import fs from 'node:fs/promises' import path from 'node:path' import type { Locator, Page } from '@playwright/test' import type { EditorFixture } from '@e2e/playwright/fixtures/editorFixture' import type { SceneFixture } from '@e2e/playwright/fixtures/sceneFixture' import type { ToolbarFixture } from '@e2e/playwright/fixtures/toolbarFixture' import { expect, test } from '@e2e/playwright/zoo-test' import { bracket } from '@e2e/playwright/fixtures/bracket' import type { CmdBarSerialised } from '@e2e/playwright/fixtures/cmdBarFixture' // test file is for testing point an click code gen functionality that's not sketch mode related test.describe('Point-and-click tests', () => { test('verify extruding circle works', async ({ page, context, homePage, cmdBar, editor, toolbar, scene, }) => { const file = await fs.readFile( path.resolve( __dirname, '../../', './rust/kcl-lib/e2e/executor/inputs/test-circle-extrude.kcl' ), 'utf-8' ) await context.addInitScript((file) => { localStorage.setItem('persistCode', file) }, file) await homePage.goToModelingScene() await scene.connectionEstablished() const [clickCircle, moveToCircle] = scene.makeMouseHelpers(582, 217) await test.step('because there is sweepable geometry, verify extrude is enable when nothing is selected', async () => { // FIXME: Do not click, clicking removes the activeLines in future checks // await scene.clickNoWhere() await expect(toolbar.extrudeButton).toBeEnabled() }) await test.step('check code model connection works and that button is still enable once circle is selected ', async () => { await moveToCircle() const circleSnippet = 'circle(center = [318.33, 168.1], radius = 182.8)' await editor.expectState({ activeLines: ['sketch002=startSketchOn(XZ)'], highlightedCode: circleSnippet, diagnostics: [], }) await test.step('check code model connection works and that button is still enable once circle is selected ', async () => { await moveToCircle() const circleSnippet = 'circle(center = [318.33, 168.1], radius = 182.8)' await editor.expectState({ activeLines: ['sketch002=startSketchOn(XZ)'], highlightedCode: circleSnippet, diagnostics: [], }) await clickCircle() await editor.expectState({ activeLines: ['|>' + circleSnippet], highlightedCode: circleSnippet, diagnostics: [], }) await expect(toolbar.extrudeButton).toBeEnabled() }) await expect(toolbar.extrudeButton).toBeEnabled() }) await test.step('do extrude flow and check extrude code is added to editor', async () => { await toolbar.extrudeButton.click() await cmdBar.expectState({ stage: 'arguments', currentArgKey: 'sketches', currentArgValue: '', headerArguments: { Profiles: '', Length: '' }, highlightedHeaderArg: 'Profiles', commandName: 'Extrude', }) await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'arguments', currentArgKey: 'length', currentArgValue: '5', headerArguments: { Profiles: '1 profile', Length: '' }, highlightedHeaderArg: 'length', commandName: 'Extrude', }) await cmdBar.progressCmdBar() const expectString = 'extrude001 = extrude(sketch001, length = 5)' await editor.expectEditor.not.toContain(expectString) await cmdBar.expectState({ stage: 'review', headerArguments: { Profiles: '1 profile', Length: '5' }, commandName: 'Extrude', }) await cmdBar.progressCmdBar() await editor.expectEditor.toContain(expectString) }) }) test('Verify in-pipe extrudes in bracket can be edited', async ({ tronApp, context, editor, homePage, page, scene, toolbar, cmdBar, }) => { await context.addInitScript((initialCode) => { localStorage.setItem('persistCode', initialCode) }, bracket) await homePage.goToModelingScene() await scene.settled(cmdBar) await test.step(`Edit first extrude via feature tree`, async () => { await (await toolbar.getFeatureTreeOperation('Extrude', 0)).dblclick() await cmdBar.expectState({ stage: 'arguments', currentArgKey: 'length', currentArgValue: 'width', headerArguments: { Length: '5', }, highlightedHeaderArg: 'length', commandName: 'Extrude', }) await page.keyboard.insertText('width - 0.001in') await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'review', headerArguments: { Length: '4.999in', }, commandName: 'Extrude', }) await cmdBar.progressCmdBar() await editor.expectEditor.toContain('extrude(length = width - 0.001in)') }) await test.step(`Edit second extrude via feature tree`, async () => { await (await toolbar.getFeatureTreeOperation('Extrude', 1)).dblclick() await cmdBar.expectState({ stage: 'arguments', currentArgKey: 'length', currentArgValue: '-thickness - .01', headerArguments: { Length: '-0.3949', }, highlightedHeaderArg: 'length', commandName: 'Extrude', }) await page.keyboard.insertText('-thickness - .01 - 0.001') await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'review', headerArguments: { Length: '-0.3959', }, commandName: 'Extrude', }) await cmdBar.progressCmdBar() await editor.expectEditor.toContain( 'extrude(length = -thickness - .01 - 0.001)' ) }) await test.step(`Edit third extrude via feature tree`, async () => { await (await toolbar.getFeatureTreeOperation('Extrude', 2)).dblclick() await cmdBar.expectState({ stage: 'arguments', currentArgKey: 'length', currentArgValue: '-thickness - 0.1', headerArguments: { Length: '-0.4849', }, highlightedHeaderArg: 'length', commandName: 'Extrude', }) await page.keyboard.insertText('-thickness - 0.1 - 0.001') await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'review', headerArguments: { Length: '-0.4859', }, commandName: 'Extrude', }) await cmdBar.progressCmdBar() await editor.expectEditor.toContain( 'extrude(length = -thickness - 0.1 - 0.001)' ) }) }) test.describe('verify sketch on chamfer works', () => { const _sketchOnAChamfer = ( page: Page, editor: EditorFixture, toolbar: ToolbarFixture, scene: SceneFixture ) => async ({ clickCoords, cameraPos, cameraTarget, beforeChamferSnippet, afterChamferSelectSnippet, afterRectangle1stClickSnippet, afterRectangle2ndClickSnippet, beforeChamferSnippetEnd, }: { clickCoords: { x: number; y: number } cameraPos: { x: number; y: number; z: number } cameraTarget: { x: number; y: number; z: number } beforeChamferSnippet: string afterChamferSelectSnippet: string afterRectangle1stClickSnippet: string afterRectangle2ndClickSnippet: string beforeChamferSnippetEnd?: string }) => { const [clickChamfer] = scene.makeMouseHelpers( clickCoords.x, clickCoords.y ) const [rectangle1stClick] = scene.makeMouseHelpers(573, 149) const [rectangle2ndClick, rectangle2ndMove] = scene.makeMouseHelpers( 598, 380, { steps: 5 } ) await scene.moveCameraTo(cameraPos, cameraTarget) await test.step('check chamfer selection changes cursor position', async () => { await toolbar.waitForFeatureTreeToBeBuilt() await expect(async () => { // sometimes initial click doesn't register await clickChamfer() // await editor.expectActiveLinesToBe([beforeChamferSnippet.slice(-5)]) await editor.expectActiveLinesToBe([ beforeChamferSnippetEnd || beforeChamferSnippet.slice(-5), ]) }).toPass({ timeout: 15_000, intervals: [500] }) }) await test.step('starting a new and selecting a chamfer should animate to the new sketch and possible break up the initial chamfer if it had one than more tag', async () => { await toolbar.startSketchPlaneSelection() await clickChamfer() // timeout wait for engine animation is unavoidable await page.waitForTimeout(1000) await editor.expectEditor.toContain(afterChamferSelectSnippet) }) await test.step('make sure a basic sketch can be added', async () => { await toolbar.rectangleBtn.click() await rectangle1stClick() await editor.expectEditor.toContain(afterRectangle1stClickSnippet) await rectangle2ndMove({ pixelDiff: 50, }) await rectangle2ndClick() await editor.expectEditor.toContain(afterRectangle2ndClickSnippet, { shouldNormalise: true, }) }) await test.step('Clean up so that `_sketchOnAChamfer` util can be called again', async () => { await toolbar.exitSketch() }) await test.step('Check there is no errors after code created in previous steps executes', async () => { await editor.expectState({ activeLines: ['@settings(defaultLengthUnit = in)'], highlightedCode: '', diagnostics: [], }) await toolbar.waitForFeatureTreeToBeBuilt() }) } test('works on all edge selections and can break up multi edges in a chamfer array', async ({ context, page, homePage, editor, toolbar, scene, cmdBar, }) => { const file = await fs.readFile( path.resolve( __dirname, '../../', './rust/kcl-lib/e2e/executor/inputs/e2e-can-sketch-on-chamfer.kcl' ), 'utf-8' ) await context.addInitScript((file) => { localStorage.setItem('persistCode', file) }, file) await page.setBodyDimensions({ width: 1000, height: 500 }) await homePage.goToModelingScene() await scene.settled(cmdBar) const sketchOnAChamfer = _sketchOnAChamfer(page, editor, toolbar, scene) await sketchOnAChamfer({ clickCoords: { x: 570, y: 220 }, cameraPos: { x: 16020, y: -2000, z: 10500 }, cameraTarget: { x: -150, y: -4500, z: -80 }, beforeChamferSnippet: `angledLine(angle=segAng(rectangleSegmentA001)-90,length=217.26,tag=$seg01) chamfer(length = 30,tags = [ seg01, getNextAdjacentEdge(yo), getNextAdjacentEdge(seg02), getOppositeEdge(seg01) ], )`, afterChamferSelectSnippet: 'sketch002 = startSketchOn(extrude001, face = seg03)', afterRectangle1stClickSnippet: 'startProfile(sketch002, at = [205.96, 254.59])', afterRectangle2ndClickSnippet: `angledLine(angle=0,length=11.39,tag=$rectangleSegmentA002) |>angledLine(angle=segAng(rectangleSegmentA002)-90,length=105.26) |>angledLine(angle=segAng(rectangleSegmentA002),length=-segLen(rectangleSegmentA002)) |>line(endAbsolute=[profileStartX(%),profileStartY(%)]) |>close()`, }) await sketchOnAChamfer({ clickCoords: { x: 690, y: 250 }, cameraPos: { x: 16020, y: -2000, z: 10500 }, cameraTarget: { x: -150, y: -4500, z: -80 }, beforeChamferSnippet: `angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 217.26, tag = $seg01)chamfer( length = 30, tags = [ seg01, getNextAdjacentEdge(yo), getNextAdjacentEdge(seg02) ] )`, afterChamferSelectSnippet: 'sketch003 = startSketchOn(extrude001, face = seg04)', afterRectangle1stClickSnippet: 'startProfile(sketch003, at = [-209.64, 255.28])', afterRectangle2ndClickSnippet: `angledLine(angle=0,length=11.56,tag=$rectangleSegmentA003) |>angledLine(angle=segAng(rectangleSegmentA003)-90,length=106.84) |>angledLine(angle=segAng(rectangleSegmentA003),length=-segLen(rectangleSegmentA003)) |>line(endAbsolute=[profileStartX(%),profileStartY(%)]) |>close()`, }) await sketchOnAChamfer({ clickCoords: { x: 677, y: 87 }, cameraPos: { x: -6200, y: 1500, z: 6200 }, cameraTarget: { x: 8300, y: 1100, z: 4800 }, beforeChamferSnippet: `angledLine(angle = 0, length = 268.43, tag = $rectangleSegmentA001)chamfer( length = 30, tags = [ getNextAdjacentEdge(yo), getNextAdjacentEdge(seg02) ] )`, afterChamferSelectSnippet: 'sketch004 = startSketchOn(extrude001, face = seg05)', afterRectangle1stClickSnippet: 'startProfile(sketch004, at = [82.57, 322.96])', afterRectangle2ndClickSnippet: `angledLine(angle=0,length=11.16,tag=$rectangleSegmentA004) |>angledLine(angle=segAng(rectangleSegmentA004)-90,length=103.07) |>angledLine(angle=segAng(rectangleSegmentA004),length=-segLen(rectangleSegmentA004)) |>line(endAbsolute=[profileStartX(%),profileStartY(%)]) |>close()`, }) /// last one await sketchOnAChamfer({ clickCoords: { x: 620, y: 300 }, cameraPos: { x: -1100, y: -7700, z: 1600 }, cameraTarget: { x: 1450, y: 670, z: 4000 }, beforeChamferSnippet: `chamfer(length = 30, tags = [getNextAdjacentEdge(yo)])`, beforeChamferSnippetEnd: '|> chamfer(length = 30, tags = [getNextAdjacentEdge(yo)])', afterChamferSelectSnippet: 'sketch005 = startSketchOn(extrude001, face = seg06)', afterRectangle1stClickSnippet: 'startProfile(sketch005, at = [-23.43, 19.69])', afterRectangle2ndClickSnippet: `angledLine(angle=0,length=9.1,tag=$rectangleSegmentA005) |>angledLine(angle=segAng(rectangleSegmentA005)-90,length=84.07) |>angledLine(angle=segAng(rectangleSegmentA005),length=-segLen(rectangleSegmentA005)) |>line(endAbsolute=[profileStartX(%),profileStartY(%)]) |>close()`, }) await test.step('verify at the end of the test that final code is what is expected', async () => { await editor.expectEditor.toContain( `@settings(defaultLengthUnit = in) sketch001 = startSketchOn(XZ) |> startProfile(at = [75.8, 317.2]) // [$startCapTag, $EndCapTag] |> angledLine(angle = 0, length = 268.43, tag = $rectangleSegmentA001) |> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 217.26, tag = $seg01) |> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001), tag = $yo) |> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg02) |> close() extrude001 = extrude(sketch001, length = 100) |> chamfer(length = 30, tags = [getOppositeEdge(seg01)], tag = $seg03) |> chamfer(length = 30, tags = [seg01], tag = $seg04) |> chamfer(length = 30, tags = [getNextAdjacentEdge(seg02)], tag = $seg05) |> chamfer(length = 30, tags = [getNextAdjacentEdge(yo)], tag = $seg06) sketch002 = startSketchOn(extrude001, face = seg03) profile001 = startProfile(sketch002, at = [205.96, 254.59]) |> angledLine(angle = 0, length = 11.39, tag = $rectangleSegmentA002) |> angledLine(angle = segAng(rectangleSegmentA002) - 90, length = 105.26) |> angledLine(angle = segAng(rectangleSegmentA002), length = -segLen(rectangleSegmentA002)) |> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> close() sketch003 = startSketchOn(extrude001, face = seg04) profile002 = startProfile(sketch003, at = [-209.64, 255.28]) |> angledLine(angle = 0, length = 11.56, tag = $rectangleSegmentA003) |> angledLine(angle = segAng(rectangleSegmentA003) - 90, length = 106.84) |> angledLine(angle = segAng(rectangleSegmentA003), length = -segLen(rectangleSegmentA003)) |> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> close() sketch004 = startSketchOn(extrude001, face = seg05) profile003 = startProfile(sketch004, at = [82.57, 322.96]) |> angledLine(angle = 0, length = 11.16, tag = $rectangleSegmentA004) |> angledLine(angle = segAng(rectangleSegmentA004) - 90, length = 103.07) |> angledLine(angle = segAng(rectangleSegmentA004), length = -segLen(rectangleSegmentA004)) |> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> close() sketch005 = startSketchOn(extrude001, face = seg06) profile004 = startProfile(sketch005, at = [-23.43, 19.69]) |> angledLine(angle = 0, length = 9.1, tag = $rectangleSegmentA005) |> angledLine(angle = segAng(rectangleSegmentA005) - 90, length = 84.07) |> angledLine(angle = segAng(rectangleSegmentA005), length = -segLen(rectangleSegmentA005)) |> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> close() `, { shouldNormalise: true } ) }) }) test('Works on chamfers that are not in a pipeExpression can break up multi edges in a chamfer array', async ({ context, page, homePage, editor, toolbar, scene, cmdBar, }) => { const file = await fs.readFile( path.resolve( __dirname, '../../', './rust/kcl-lib/e2e/executor/inputs/e2e-can-sketch-on-chamfer-no-pipeExpr.kcl' ), 'utf-8' ) await context.addInitScript((file) => { localStorage.setItem('persistCode', file) }, file) await page.setBodyDimensions({ width: 1000, height: 500 }) await homePage.goToModelingScene() await scene.settled(cmdBar) const sketchOnAChamfer = _sketchOnAChamfer(page, editor, toolbar, scene) await sketchOnAChamfer({ clickCoords: { x: 570, y: 220 }, cameraPos: { x: 16020, y: -2000, z: 10500 }, cameraTarget: { x: -150, y: -4500, z: -80 }, beforeChamferSnippet: `angledLine(angle=segAng(rectangleSegmentA001)-90,length=217.26,tag=$seg01) chamfer(extrude001,length=30,tags=[ seg01, getNextAdjacentEdge(yo), getNextAdjacentEdge(seg02), getOppositeEdge(seg01), ])`, beforeChamferSnippetEnd: ')', afterChamferSelectSnippet: 'sketch002 = startSketchOn(extrude001, face = seg03)', afterRectangle1stClickSnippet: 'startProfile(sketch002, at = [205.96, 254.59])', afterRectangle2ndClickSnippet: `angledLine(angle=0,length=11.39,tag=$rectangleSegmentA002) |>angledLine(angle=segAng(rectangleSegmentA002)-90,length=105.26) |>angledLine(angle=segAng(rectangleSegmentA002),length=-segLen(rectangleSegmentA002)) |>line(endAbsolute=[profileStartX(%),profileStartY(%)]) |>close()`, }) await editor.expectEditor.toContain( `@settings(defaultLengthUnit = in) sketch001 = startSketchOn(XZ) |> startProfile(at = [75.8, 317.2]) |> angledLine(angle = 0, length = 268.43, tag = $rectangleSegmentA001) |> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 217.26, tag = $seg01) |> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001), tag = $yo) |> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg02) |> close() extrude001 = extrude(sketch001, length = 100) chamf = chamfer( extrude001, length = 30, tags = [getOppositeEdge(seg01)], tag = $seg03, ) |> chamfer( length = 30, tags = [ seg01, getNextAdjacentEdge(yo), getNextAdjacentEdge(seg02) ], ) sketch002 = startSketchOn(extrude001, face = seg03) profile001 = startProfile(sketch002, at = [205.96, 254.59]) |> angledLine(angle = 0, length = 11.39, tag = $rectangleSegmentA002) |> angledLine(angle = segAng(rectangleSegmentA002) - 90, length = 105.26) |> angledLine(angle = segAng(rectangleSegmentA002), length = -segLen(rectangleSegmentA002)) |> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> close() `, { shouldNormalise: true } ) }) }) test(`Verify axis, origin, and horizontal snapping`, async ({ page, homePage, editor, toolbar, scene, }) => { const viewPortSize = { width: 1200, height: 500 } await page.setBodyDimensions(viewPortSize) await homePage.goToModelingScene() await scene.connectionEstablished() // Constants and locators // These are mappings from screenspace to KCL coordinates, // until we merge in our coordinate system helpers const xzPlane = [ viewPortSize.width * 0.65, viewPortSize.height * 0.3, ] as const const originSloppy = { screen: [ viewPortSize.width / 2 + 3, // 3px off the center of the screen viewPortSize.height / 2, ], kcl: [0, 0], } as const const xAxisSloppy = { screen: [ viewPortSize.width * 0.75, viewPortSize.height / 2 - 3, // 3px off the X-axis ], kcl: [20.34, 0], } as const const offYAxis = { screen: [ viewPortSize.width * 0.6, // Well off the Y-axis, out of snapping range viewPortSize.height * 0.3, ], kcl: [8.14, 6.78], } as const const yAxisSloppy = { screen: [ viewPortSize.width / 2 + 5, // 5px off the Y-axis viewPortSize.height * 0.3, ], kcl: [0, 6.78], } as const const [clickOnXzPlane, moveToXzPlane] = scene.makeMouseHelpers(...xzPlane) const [clickOriginSloppy] = scene.makeMouseHelpers(...originSloppy.screen) const [clickXAxisSloppy, moveXAxisSloppy] = scene.makeMouseHelpers( ...xAxisSloppy.screen ) const [dragToOffYAxis, dragFromOffAxis] = scene.makeDragHelpers( ...offYAxis.screen ) const expectedCodeSnippets = { sketchOnXzPlane: `sketch001 = startSketchOn(XZ)`, pointAtOrigin: `startProfile(sketch001, at = [${originSloppy.kcl[0]}, ${originSloppy.kcl[1]}])`, segmentOnXAxis: `xLine(length = ${xAxisSloppy.kcl[0]})`, afterSegmentDraggedOffYAxis: `startProfile(sketch001, at = [${offYAxis.kcl[0]}, ${offYAxis.kcl[1]}])`, afterSegmentDraggedOnYAxis: `startProfile(sketch001, at = [${yAxisSloppy.kcl[0]}, ${yAxisSloppy.kcl[1]}])`, } await test.step(`Start a sketch on the XZ plane`, async () => { await editor.closePane() await toolbar.startSketchPlaneSelection() await moveToXzPlane() await clickOnXzPlane() await toolbar.waitUntilSketchingReady() await editor.expectEditor.toContain(expectedCodeSnippets.sketchOnXzPlane) }) await test.step(`Place a point a few pixels off the middle, verify it still snaps to 0,0`, async () => { await clickOriginSloppy() await editor.expectEditor.toContain(expectedCodeSnippets.pointAtOrigin) }) await test.step(`Add a segment on x-axis after moving the mouse a bit, verify it snaps`, async () => { await moveXAxisSloppy() await clickXAxisSloppy() await editor.expectEditor.toContain(expectedCodeSnippets.segmentOnXAxis) }) await test.step(`Unequip line tool`, async () => { await toolbar.lineBtn.click() await expect(toolbar.lineBtn).not.toHaveAttribute('aria-pressed', 'true') }) await test.step(`Drag the origin point up and to the right, verify it's past snapping`, async () => { await dragToOffYAxis({ fromPoint: { x: originSloppy.screen[0], y: originSloppy.screen[1] }, }) await editor.expectEditor.toContain( expectedCodeSnippets.afterSegmentDraggedOffYAxis ) }) await test.step(`Drag the origin point left to the y-axis, verify it snaps back`, async () => { await dragFromOffAxis({ toPoint: { x: yAxisSloppy.screen[0], y: yAxisSloppy.screen[1] }, }) await editor.expectEditor.toContain( expectedCodeSnippets.afterSegmentDraggedOnYAxis ) }) await editor.page.waitForTimeout(1000) }) test(`Verify user can double-click to edit a sketch`, async ({ context, page, homePage, editor, toolbar, scene, cmdBar, }) => { const initialCode = `closedSketch = startSketchOn(XZ) |> circle(center = [8, 5], radius = 2) openSketch = startSketchOn(XY) |> startProfile(at = [-5, 0]) |> line(endAbsolute = [0, 5]) |> xLine(length = 5) |> tangentialArc(endAbsolute = [10, 0]) ` const viewPortSize = { width: 1000, height: 500 } await page.setBodyDimensions(viewPortSize) await context.addInitScript((code) => { localStorage.setItem('persistCode', code) }, initialCode) await homePage.goToModelingScene() const pointInsideCircle = { x: viewPortSize.width * 0.63, y: viewPortSize.height * 0.5, } const pointOnPathAfterSketching = { x: viewPortSize.width * 0.65, y: viewPortSize.height * 0.5, } // eslint-disable-next-line @typescript-eslint/no-unused-vars const [_clickOpenPath, moveToOpenPath, dblClickOpenPath] = scene.makeMouseHelpers( pointOnPathAfterSketching.x, pointOnPathAfterSketching.y ) // eslint-disable-next-line @typescript-eslint/no-unused-vars const [_clickCircle, moveToCircle, dblClickCircle] = scene.makeMouseHelpers( pointInsideCircle.x, pointInsideCircle.y ) const exitSketch = async () => { await test.step(`Exit sketch mode`, async () => { await toolbar.exitSketchBtn.click() await expect(toolbar.startSketchBtn).toBeEnabled() }) } await test.step(`Double-click on the closed sketch`, async () => { await scene.settled(cmdBar) await moveToCircle() await page.waitForTimeout(1000) await dblClickCircle() await page.waitForTimeout(1000) await expect(toolbar.exitSketchBtn).toBeVisible() await editor.expectState({ activeLines: [`|>circle(center=[8,5],radius=2)`], highlightedCode: 'circle(center=[8,5],radius=2)', diagnostics: [], }) }) await page.waitForTimeout(1000) await exitSketch() await page.waitForTimeout(1000) // Drag the sketch line out of the axis view which blocks the click await page.dragAndDrop('#stream', '#stream', { sourcePosition: { x: viewPortSize.width * 0.7, y: viewPortSize.height * 0.5, }, targetPosition: { x: viewPortSize.width * 0.7, y: viewPortSize.height * 0.4, }, }) await page.waitForTimeout(500) await test.step(`Double-click on the open sketch`, async () => { await moveToOpenPath() await scene.expectPixelColor( [250, 250, 250], pointOnPathAfterSketching, 15 ) // There is a full execution after exiting sketch that clears the scene. await page.waitForTimeout(500) await dblClickOpenPath() await expect(toolbar.exitSketchBtn).toBeVisible() // Wait for enter sketch mode to complete await page.waitForTimeout(500) await editor.expectState({ activeLines: [`|>tangentialArc(endAbsolute=[10,0])`], highlightedCode: 'tangentialArc(endAbsolute=[10,0])', diagnostics: [], }) }) }) test(`Shift-click to select and deselect edges and faces`, async ({ context, page, homePage, scene, }) => { // Code samples const initialCode = `sketch001 = startSketchOn(XY) |> startProfile(at = [-12, -6]) |> line(end = [0, 12]) |> line(end = [24, 0]) |> line(end = [0, -12]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> close() |> extrude(%, length = -12)` // Locators const upperEdgeLocation = { x: 600, y: 192 } const lowerEdgeLocation = { x: 600, y: 383 } const faceLocation = { x: 630, y: 290 } // Click helpers const [clickOnUpperEdge] = scene.makeMouseHelpers( upperEdgeLocation.x, upperEdgeLocation.y ) const [clickOnLowerEdge] = scene.makeMouseHelpers( lowerEdgeLocation.x, lowerEdgeLocation.y ) const [clickOnFace] = scene.makeMouseHelpers(faceLocation.x, faceLocation.y) // Colors const edgeColorWhite: [number, number, number] = [220, 220, 220] // varies from 192 to 255 const edgeColorYellow: [number, number, number] = [251, 251, 40] // vaies from 12 to 67 const faceColorGray: [number, number, number] = [168, 168, 168] const faceColorYellow: [number, number, number] = [155, 155, 155] const tolerance = 40 const timeout = 150 // Setup await test.step(`Initial test setup`, async () => { await context.addInitScript((initialCode) => { localStorage.setItem('persistCode', initialCode) }, initialCode) await page.setBodyDimensions({ width: 1000, height: 500 }) await homePage.goToModelingScene() // Wait for the scene and stream to load await scene.expectPixelColor(faceColorGray, faceLocation, tolerance) }) await test.step('Select and deselect a single edge', async () => { await test.step('Click the edge', async () => { await scene.expectPixelColor( edgeColorWhite, upperEdgeLocation, tolerance ) await clickOnUpperEdge() await scene.expectPixelColor( edgeColorYellow, upperEdgeLocation, tolerance ) }) await test.step('Shift-click the same edge to deselect', async () => { await page.keyboard.down('Shift') await page.waitForTimeout(timeout) await clickOnUpperEdge() await page.waitForTimeout(timeout) await page.keyboard.up('Shift') await scene.expectPixelColor( edgeColorWhite, upperEdgeLocation, tolerance ) }) }) await test.step('Select and deselect multiple objects', async () => { await test.step('Select both edges and the face', async () => { await test.step('Select the upper edge', async () => { await scene.expectPixelColor( edgeColorWhite, upperEdgeLocation, tolerance ) await clickOnUpperEdge() await scene.expectPixelColor( edgeColorYellow, upperEdgeLocation, tolerance ) }) await test.step('Select the lower edge (Shift-click)', async () => { await scene.expectPixelColor( edgeColorWhite, lowerEdgeLocation, tolerance ) await page.keyboard.down('Shift') await page.waitForTimeout(timeout) await clickOnLowerEdge() await page.waitForTimeout(timeout) await page.keyboard.up('Shift') await scene.expectPixelColor( edgeColorYellow, lowerEdgeLocation, tolerance ) }) await test.step('Select the face (Shift-click)', async () => { await scene.expectPixelColor(faceColorGray, faceLocation, tolerance) await page.keyboard.down('Shift') await page.waitForTimeout(timeout) await clickOnFace() await page.waitForTimeout(timeout) await page.keyboard.up('Shift') await scene.expectPixelColor(faceColorYellow, faceLocation, tolerance) }) }) await test.step('Deselect them one by one', async () => { await test.step('Deselect the face (Shift-click)', async () => { await scene.expectPixelColor(faceColorYellow, faceLocation, tolerance) await page.keyboard.down('Shift') await page.waitForTimeout(timeout) await clickOnFace() await page.waitForTimeout(timeout) await page.keyboard.up('Shift') await scene.expectPixelColor(faceColorGray, faceLocation, tolerance) }) await test.step('Deselect the lower edge (Shift-click)', async () => { await scene.expectPixelColor( edgeColorYellow, lowerEdgeLocation, tolerance ) await page.keyboard.down('Shift') await page.waitForTimeout(timeout) await clickOnLowerEdge() await page.waitForTimeout(timeout) await page.keyboard.up('Shift') await scene.expectPixelColor( edgeColorWhite, lowerEdgeLocation, tolerance ) }) await test.step('Deselect the upper edge (Shift-click)', async () => { await scene.expectPixelColor( edgeColorYellow, upperEdgeLocation, tolerance ) await page.keyboard.down('Shift') await page.waitForTimeout(timeout) await clickOnUpperEdge() await page.waitForTimeout(timeout) await page.keyboard.up('Shift') await scene.expectPixelColor( edgeColorWhite, upperEdgeLocation, tolerance ) }) }) }) }) test(`Shift-click to select and deselect sketch segments`, async ({ page, homePage, scene, editor, }) => { // Locators const firstPointLocation = { x: 200, y: 100 } const secondPointLocation = { x: 800, y: 100 } const thirdPointLocation = { x: 800, y: 400 } const firstSegmentLocation = { x: 750, y: 100 } const secondSegmentLocation = { x: 800, y: 150 } const planeLocation = { x: 700, y: 200 } // Click helpers const [clickFirstPoint] = scene.makeMouseHelpers( firstPointLocation.x, firstPointLocation.y ) const [clickSecondPoint] = scene.makeMouseHelpers( secondPointLocation.x, secondPointLocation.y ) const [clickThirdPoint] = scene.makeMouseHelpers( thirdPointLocation.x, thirdPointLocation.y ) const [clickFirstSegment] = scene.makeMouseHelpers( firstSegmentLocation.x, firstSegmentLocation.y ) const [clickSecondSegment] = scene.makeMouseHelpers( secondSegmentLocation.x, secondSegmentLocation.y ) const [clickPlane] = scene.makeMouseHelpers( planeLocation.x, planeLocation.y ) // Colors const edgeColorWhite: [number, number, number] = [220, 220, 220] const edgeColorBlue: [number, number, number] = [20, 20, 200] const backgroundColor: [number, number, number] = [30, 30, 30] const tolerance = 40 const timeout = 150 // Setup await test.step(`Initial test setup`, async () => { await page.setBodyDimensions({ width: 1000, height: 500 }) await homePage.goToModelingScene() // Wait for the scene and stream to load await scene.expectPixelColor( backgroundColor, secondPointLocation, tolerance ) }) await test.step('Select and deselect a single sketch segment', async () => { await test.step('Get into sketch mode', async () => { await editor.closePane() await page.waitForTimeout(timeout) await page.getByRole('button', { name: 'Start Sketch' }).click() await page.waitForTimeout(timeout) await clickPlane() await page.waitForTimeout(1000) }) await test.step('Draw sketch', async () => { await clickFirstPoint() await page.waitForTimeout(timeout) await clickSecondPoint() await page.waitForTimeout(timeout) await clickThirdPoint() await page.waitForTimeout(timeout) }) await test.step('Deselect line tool', async () => { const btnLine = page.getByTestId('line') const btnLineAriaPressed = await btnLine.getAttribute('aria-pressed') if (btnLineAriaPressed === 'true') { await btnLine.click() } await page.waitForTimeout(timeout) }) await test.step('Select the first segment', async () => { await page.waitForTimeout(timeout) await clickFirstSegment() await page.waitForTimeout(timeout) await scene.expectPixelColor( edgeColorBlue, firstSegmentLocation, tolerance ) await scene.expectPixelColor( edgeColorWhite, secondSegmentLocation, tolerance ) }) await test.step('Select the second segment (Shift-click)', async () => { await page.keyboard.down('Shift') await page.waitForTimeout(timeout) await clickSecondSegment() await page.waitForTimeout(timeout) await page.keyboard.up('Shift') await scene.expectPixelColor( edgeColorBlue, firstSegmentLocation, tolerance ) await scene.expectPixelColor( edgeColorBlue, secondSegmentLocation, tolerance ) }) await test.step('Deselect the first segment', async () => { await page.keyboard.down('Shift') await page.waitForTimeout(timeout) await clickFirstSegment() await page.waitForTimeout(timeout) await page.keyboard.up('Shift') await scene.expectPixelColor( edgeColorWhite, firstSegmentLocation, tolerance ) await scene.expectPixelColor( edgeColorBlue, secondSegmentLocation, tolerance ) }) await test.step('Deselect the second segment', async () => { await page.keyboard.down('Shift') await page.waitForTimeout(timeout) await clickSecondSegment() await page.waitForTimeout(timeout) await page.keyboard.up('Shift') await scene.expectPixelColor( edgeColorWhite, firstSegmentLocation, tolerance ) await scene.expectPixelColor( edgeColorWhite, secondSegmentLocation, tolerance ) }) }) }) test(`Offset plane point-and-click`, async ({ context, page, homePage, scene, editor, toolbar, cmdBar, }) => { // One dumb hardcoded screen pixel value const testPoint = { x: 700, y: 200 } // TODO: replace the testPoint selection with a feature tree click once that's supported #7544 const [clickOnXzPlane] = scene.makeMouseHelpers(testPoint.x, testPoint.y) const expectedOutput = `plane001 = offsetPlane(XZ, offset = 5)` await homePage.goToModelingScene() await scene.settled(cmdBar) await test.step(`Look for the blue of the XZ plane`, async () => { //await scene.expectPixelColor([50, 51, 96], testPoint, 15) // FIXME }) await test.step(`Go through the command bar flow`, async () => { await toolbar.offsetPlaneButton.click() await expect .poll(() => page.getByText('Please select one').count()) .toBe(1) await cmdBar.expectState({ stage: 'arguments', currentArgKey: 'plane', currentArgValue: '', headerArguments: { Plane: '', Distance: '' }, highlightedHeaderArg: 'plane', commandName: 'Offset plane', }) await clickOnXzPlane() await cmdBar.expectState({ stage: 'arguments', currentArgKey: 'distance', currentArgValue: '5', headerArguments: { Plane: '1 plane', Distance: '' }, highlightedHeaderArg: 'distance', commandName: 'Offset plane', }) await cmdBar.progressCmdBar() }) await test.step(`Confirm code is added to the editor, scene has changed`, async () => { await editor.expectEditor.toContain(expectedOutput) await editor.expectState({ diagnostics: [], activeLines: [expectedOutput], highlightedCode: '', }) await scene.expectPixelColor([74, 74, 74], testPoint, 15) }) await test.step('Delete offset plane via feature tree selection', async () => { await editor.closePane() const operationButton = await toolbar.getFeatureTreeOperation( 'Offset Plane', 0 ) await operationButton.click({ button: 'left' }) await page.keyboard.press('Delete') //await scene.expectPixelColor([50, 51, 96], testPoint, 15) // FIXME }) }) const initialCmdBarStateHelix: CmdBarSerialised = { stage: 'arguments', currentArgKey: 'mode', currentArgValue: '', headerArguments: { Mode: '', AngleStart: '', Revolutions: '', Radius: '', }, highlightedHeaderArg: 'mode', commandName: 'Helix', } test('Helix point-and-click on default axis', async ({ context, page, homePage, scene, editor, toolbar, cmdBar, }) => { const expectedOutput = `helix001 = helix( axis = X, radius = 5, length = 5, revolutions = 1, angleStart = 270,)` const expectedLine = `axis=X,` await homePage.goToModelingScene() await scene.connectionEstablished() await test.step(`Go through the command bar flow`, async () => { await toolbar.helixButton.click() await cmdBar.expectState(initialCmdBarStateHelix) await cmdBar.progressCmdBar() await expect.poll(() => page.getByText('Axis').count()).toBe(6) await cmdBar.progressCmdBar() await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'arguments', currentArgKey: 'angleStart', highlightedHeaderArg: 'angleStart', currentArgValue: '360', headerArguments: { Mode: 'Axis', Axis: 'X', Revolutions: '1', AngleStart: '', Length: '', Radius: '', }, commandName: 'Helix', }) await cmdBar.currentArgumentInput.locator('.cm-content').fill('270') await cmdBar.progressCmdBar() await cmdBar.progressCmdBar() await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'review', headerArguments: { Mode: 'Axis', Axis: 'X', AngleStart: '270', Revolutions: '1', Length: '5', Radius: '5', }, commandName: 'Helix', }) await cmdBar.submit() }) await test.step(`Confirm code is added to the editor, scene has changed`, async () => { await editor.expectEditor.toContain(expectedOutput) await editor.expectState({ diagnostics: [], activeLines: [expectedLine], highlightedCode: '', }) }) await test.step(`Edit helix through the feature tree`, async () => { await editor.closePane() const operationButton = await toolbar.getFeatureTreeOperation('Helix', 0) await operationButton.dblclick() const initialInput = '5' const newInput = '50' await cmdBar.expectState({ commandName: 'Helix', stage: 'arguments', currentArgKey: 'length', currentArgValue: '5', headerArguments: { Axis: 'X', AngleStart: '270', Revolutions: '1', Radius: '5', Length: initialInput, }, highlightedHeaderArg: 'length', }) await page.keyboard.insertText(newInput) await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'review', headerArguments: { Axis: 'X', AngleStart: '270', Revolutions: '1', Radius: '5', Length: newInput, }, commandName: 'Helix', }) await cmdBar.submit() await toolbar.closeFeatureTreePane() await editor.openPane() await editor.expectEditor.toContain('length = ' + newInput) }) await test.step('Delete helix via feature tree selection', async () => { await editor.closePane() const operationButton = await toolbar.getFeatureTreeOperation('Helix', 0) await operationButton.click({ button: 'left' }) await page.keyboard.press('Delete') await scene.settled(cmdBar) await editor.expectEditor.not.toContain('helix') await expect( await toolbar.getFeatureTreeOperation('Helix', 0) ).not.toBeVisible() }) }) test(`Helix point-and-click around segment`, async ({ context, page, homePage, scene, editor, toolbar, cmdBar, }) => { const initialCode = `sketch001 = startSketchOn(XZ) profile001 = startProfile(sketch001, at = [0, 0]) |> yLine(length = 100) |> line(endAbsolute = [100, 0]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> close()` await context.addInitScript((initialCode) => { localStorage.setItem('persistCode', initialCode) }, initialCode) await page.setBodyDimensions({ width: 1000, height: 500 }) await homePage.goToModelingScene() await scene.settled(cmdBar) await test.step(`Go through the command bar flow`, async () => { await toolbar.closePane('code') await toolbar.helixButton.click() await cmdBar.expectState(initialCmdBarStateHelix) await cmdBar.selectOption({ name: 'Edge' }).click() await editor.selectText('yLine(length = 100)') await cmdBar.progressCmdBar() await page.keyboard.insertText('1') await cmdBar.progressCmdBar() await page.keyboard.insertText('2') await cmdBar.progressCmdBar() await page.keyboard.insertText('3') await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'review', headerArguments: { Mode: 'Edge', Edge: `1 segment`, AngleStart: '2', Revolutions: '1', Radius: '3', }, commandName: 'Helix', }) await cmdBar.submit() await scene.settled(cmdBar) }) await test.step(`Confirm code is added to the editor, scene has changed`, async () => { await toolbar.openPane('code') await editor.expectEditor.toContain( ` helix001 = helix( axis = seg01, radius = 3, revolutions = 1, angleStart = 2, )`, { shouldNormalise: true } ) await toolbar.closePane('code') }) }) test(`Helix point-and-click around sweepEdge with edit and delete flows`, async ({ context, page, homePage, scene, editor, toolbar, cmdBar, }) => { const initialCode = `sketch001 = startSketchOn(XZ) profile001 = startProfile(sketch001, at = [0, 0]) |> yLine(length = 100) |> line(endAbsolute = [100, 0]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> close() extrude001 = extrude(profile001, length = 100)` // One dumb hardcoded screen pixel value to click on the sweepEdge, can't think of another way? const testPoint = { x: 564, y: 364 } const [clickOnEdge] = scene.makeMouseHelpers(testPoint.x, testPoint.y) await context.addInitScript((initialCode) => { localStorage.setItem('persistCode', initialCode) }, initialCode) await page.setBodyDimensions({ width: 1000, height: 500 }) await homePage.goToModelingScene() await scene.settled(cmdBar) await test.step(`Go through the command bar flow`, async () => { await toolbar.closePane('code') await toolbar.helixButton.click() await cmdBar.expectState(initialCmdBarStateHelix) await cmdBar.selectOption({ name: 'Edge' }).click() await expect .poll(() => page.getByText('Please select one').count()) .toBe(1) await clickOnEdge() await cmdBar.progressCmdBar() await cmdBar.argumentInput.focus() await page.keyboard.insertText('20') await cmdBar.progressCmdBar() await page.keyboard.insertText('0') await cmdBar.progressCmdBar() await page.keyboard.insertText('1') await cmdBar.progressCmdBar() await page.keyboard.insertText('100') await cmdBar.expectState({ stage: 'review', headerArguments: { Mode: 'Edge', Edge: `1 sweepEdge`, AngleStart: '0', Revolutions: '20', Radius: '1', }, commandName: 'Helix', }) await cmdBar.submit() await scene.settled(cmdBar) }) await test.step(`Confirm code is added to the editor, scene has changed`, async () => { await toolbar.openPane('code') await editor.expectEditor.toContain( ` helix001 = helix( axis = getOppositeEdge(seg01), radius = 1, revolutions = 20, angleStart = 0, )`, { shouldNormalise: true } ) await toolbar.closePane('code') }) await test.step(`Edit helix through the feature tree`, async () => { await toolbar.openPane('feature-tree') const operationButton = await toolbar.getFeatureTreeOperation('Helix', 0) await operationButton.dblclick() const initialInput = '1' const newInput = '5' await cmdBar.expectState({ commandName: 'Helix', stage: 'arguments', currentArgKey: 'radius', currentArgValue: initialInput, headerArguments: { AngleStart: '0', Revolutions: '20', Radius: initialInput, }, highlightedHeaderArg: 'radius', }) await page.keyboard.insertText(newInput) await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'review', headerArguments: { AngleStart: '0', Revolutions: '20', Radius: newInput, }, commandName: 'Helix', }) await cmdBar.clickOptionalArgument('ccw') await cmdBar.expectState({ commandName: 'Helix', stage: 'arguments', currentArgKey: 'CounterClockWise', currentArgValue: '', headerArguments: { AngleStart: '0', Revolutions: '20', Radius: newInput, CounterClockWise: '', }, highlightedHeaderArg: 'CounterClockWise', }) await cmdBar.selectOption({ name: 'True' }).click() await cmdBar.expectState({ stage: 'review', headerArguments: { AngleStart: '0', Revolutions: '20', Radius: newInput, CounterClockWise: '', }, commandName: 'Helix', }) await cmdBar.submit() await toolbar.closePane('feature-tree') await toolbar.openPane('code') await editor.expectEditor.toContain( ` helix001 = helix( axis = getOppositeEdge(seg01), radius = 5, revolutions = 20, angleStart = 0, ccw = true, )`, { shouldNormalise: true } ) await toolbar.closePane('code') }) await test.step('Delete helix via feature tree selection', async () => { await toolbar.openPane('feature-tree') const operationButton = await toolbar.getFeatureTreeOperation('Helix', 0) await operationButton.click({ button: 'left' }) await page.keyboard.press('Delete') await editor.expectEditor.not.toContain('helix') await expect( await toolbar.getFeatureTreeOperation('Helix', 0) ).not.toBeVisible() }) }) test('Helix point-and-click on cylinder', async ({ context, page, homePage, scene, editor, toolbar, cmdBar, }) => { const initialCode = `sketch001 = startSketchOn(XY) profile001 = circle( sketch001, center = [0, 0], radius = 100, tag = $seg01, ) extrude001 = extrude(profile001, length = 100) ` await context.addInitScript((initialCode) => { localStorage.setItem('persistCode', initialCode) }, initialCode) await page.setBodyDimensions({ width: 1000, height: 500 }) await homePage.goToModelingScene() await scene.settled(cmdBar) // One dumb hardcoded screen pixel value const testPoint = { x: 620, y: 257 } const [clickOnWall] = scene.makeMouseHelpers(testPoint.x, testPoint.y) const expectedOutput = `helix001 = helix(cylinder = extrude001, revolutions = 1, angleStart = 360)` const expectedEditedOutput = `helix001 = helix(cylinder = extrude001, revolutions = 1, angleStart = 10)` await test.step(`Go through the command bar flow`, async () => { await toolbar.helixButton.click() await cmdBar.expectState(initialCmdBarStateHelix) await cmdBar.selectOption({ name: 'Cylinder' }).click() await cmdBar.expectState({ stage: 'arguments', currentArgKey: 'cylinder', currentArgValue: '', headerArguments: { Mode: 'Cylinder', Cylinder: '', AngleStart: '', Revolutions: '', }, highlightedHeaderArg: 'cylinder', commandName: 'Helix', }) await clickOnWall() await cmdBar.progressCmdBar() await cmdBar.progressCmdBar() await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'review', headerArguments: { Mode: 'Cylinder', Cylinder: '1 face', AngleStart: '360', Revolutions: '1', }, commandName: 'Helix', }) await cmdBar.submit() }) await test.step(`Confirm code is added to the editor, scene has changed`, async () => { await editor.expectEditor.toContain(expectedOutput) await editor.expectState({ diagnostics: [], activeLines: [expectedOutput], highlightedCode: '', }) }) await test.step(`Edit helix through the feature tree`, async () => { await editor.closePane() const operationButton = await toolbar.getFeatureTreeOperation('Helix', 0) await operationButton.dblclick() await cmdBar.expectState({ commandName: 'Helix', stage: 'arguments', currentArgKey: 'angleStart', currentArgValue: '360', headerArguments: { AngleStart: '360', Revolutions: '1', }, highlightedHeaderArg: 'angleStart', }) await page.keyboard.insertText('10') await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'review', headerArguments: { AngleStart: '10', Revolutions: '1', }, commandName: 'Helix', }) await cmdBar.progressCmdBar() await toolbar.closePane('feature-tree') await toolbar.openPane('code') await editor.expectEditor.toContain(expectedEditedOutput) await editor.closePane() }) await test.step('Delete helix via feature tree selection', async () => { await toolbar.openPane('feature-tree') const operationButton = await toolbar.getFeatureTreeOperation('Helix', 0) await operationButton.click({ button: 'left' }) await page.keyboard.press('Delete') await toolbar.closePane('feature-tree') await toolbar.openPane('code') await editor.expectEditor.not.toContain(expectedEditedOutput) }) }) const loftPointAndClickCases = [ { shouldPreselect: true }, { shouldPreselect: false }, ] loftPointAndClickCases.forEach(({ shouldPreselect }) => { test(`Loft point-and-click (preselected sketches: ${shouldPreselect})`, async ({ context, page, homePage, scene, editor, toolbar, cmdBar, }) => { const initialCode = `sketch001 = startSketchOn(XZ) |> circle(center = [0, 0], radius = 30) plane001 = offsetPlane(XZ, offset = 50) sketch002 = startSketchOn(plane001) |> circle(center = [0, 0], radius = 20) ` await context.addInitScript((initialCode) => { localStorage.setItem('persistCode', initialCode) }, initialCode) await page.setBodyDimensions({ width: 1000, height: 500 }) await homePage.goToModelingScene() // One dumb hardcoded screen pixel value const testPoint = { x: 575, y: 200 } const [clickOnSketch1] = scene.makeMouseHelpers(testPoint.x, testPoint.y) const [clickOnSketch2] = scene.makeMouseHelpers( testPoint.x, testPoint.y + 80 ) const loftDeclaration = 'loft001 = loft([sketch001, sketch002])' const editedLoftDeclaration = 'loft001 = loft([sketch001, sketch002], vDegree = 3)' await test.step(`Look for the white of the sketch001 shape`, async () => { await scene.expectPixelColor([254, 254, 254], testPoint, 15) }) async function selectSketches() { await clickOnSketch1() await page.keyboard.down('Shift') await clickOnSketch2() await page.waitForTimeout(500) await page.keyboard.up('Shift') } if (!shouldPreselect) { await test.step(`Go through the command bar flow without preselected sketches`, async () => { await toolbar.loftButton.click() await expect .poll(() => page.getByText('Please select one').count()) .toBe(1) await cmdBar.expectState({ stage: 'arguments', currentArgKey: 'sketches', currentArgValue: '', headerArguments: { Profiles: '' }, highlightedHeaderArg: 'Profiles', commandName: 'Loft', }) await selectSketches() await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'review', headerArguments: { Profiles: '2 profiles' }, commandName: 'Loft', }) await cmdBar.submit() }) } else { await test.step(`Preselect the two sketches`, async () => { await selectSketches() }) await test.step(`Go through the command bar flow with preselected sketches`, async () => { await toolbar.loftButton.click() await cmdBar.expectState({ stage: 'arguments', currentArgKey: 'sketches', currentArgValue: '', headerArguments: { Profiles: '' }, highlightedHeaderArg: 'Profiles', commandName: 'Loft', }) await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'review', headerArguments: { Profiles: '2 profiles' }, commandName: 'Loft', }) await cmdBar.submit() }) } await test.step(`Confirm code is added to the editor, scene has changed`, async () => { await editor.expectEditor.toContain(loftDeclaration) await editor.expectState({ diagnostics: [], activeLines: [loftDeclaration], highlightedCode: '', }) await scene.expectPixelColor([89, 89, 89], testPoint, 15) }) await test.step('Go through the edit flow via feature tree', async () => { await toolbar.openPane('feature-tree') const op = await toolbar.getFeatureTreeOperation('Loft', 0) await op.dblclick() await cmdBar.expectState({ stage: 'review', headerArguments: {}, commandName: 'Loft', }) await cmdBar.clickOptionalArgument('vDegree') await cmdBar.expectState({ stage: 'arguments', currentArgKey: 'vDegree', currentArgValue: '', headerArguments: { VDegree: '', }, highlightedHeaderArg: 'vDegree', commandName: 'Loft', }) await page.keyboard.insertText('3') await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'review', headerArguments: { VDegree: '3', }, commandName: 'Loft', }) await cmdBar.submit() await editor.expectEditor.toContain(editedLoftDeclaration) }) await test.step('Delete loft via feature tree selection', async () => { await editor.closePane() const operationButton = await toolbar.getFeatureTreeOperation('Loft', 0) await operationButton.click({ button: 'left' }) await page.keyboard.press('Delete') await scene.expectPixelColor([254, 254, 254], testPoint, 15) }) }) }) const sweepCases = [ { targetType: 'circle', testPoint: { x: 700, y: 250 }, initialCode: `@settings(defaultLengthUnit = in) sketch001 = startSketchOn(YZ) profile001 = circle(sketch001, center = [0, 0], radius = 500) sketch002 = startSketchOn(XZ) profile002 = startProfile(sketch002, at = [0, 0]) |> xLine(length = -500) |> tangentialArc(endAbsolute = [-2000, 500])`, }, { targetType: 'rectangle', testPoint: { x: 710, y: 255 }, initialCode: `@settings(defaultLengthUnit = in) sketch001 = startSketchOn(YZ) profile001 = startProfile(sketch001, at = [-400, -400]) |> angledLine(angle = 0, length = 800, tag = $rectangleSegmentA001) |> angledLine(angle = segAng(rectangleSegmentA001) + 90, length = 800) |> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001)) |> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> close() sketch002 = startSketchOn(XZ) profile002 = startProfile(sketch002, at = [0, 0]) |> xLine(length = -500) |> tangentialArc(endAbsolute = [-2000, 500])`, }, ] sweepCases.map(({ initialCode, targetType, testPoint }) => { test(`Sweep point-and-click ${targetType}`, async ({ context, page, homePage, scene, editor, toolbar, cmdBar, }) => { await context.addInitScript((initialCode) => { localStorage.setItem('persistCode', initialCode) }, initialCode) await page.setBodyDimensions({ width: 1000, height: 500 }) await homePage.goToModelingScene() await scene.settled(cmdBar) // One dumb hardcoded screen pixel value const [clickOnSketch1] = scene.makeMouseHelpers(testPoint.x, testPoint.y) const [clickOnSketch2] = scene.makeMouseHelpers( testPoint.x - 50, testPoint.y ) const sweepDeclaration = 'sweep001 = sweep(profile001, path = profile002)' const editedSweepDeclaration = 'sweep001 = sweep(profile001, path = profile002, sectional = true)' await test.step(`Look for sketch001`, async () => { await toolbar.closePane('code') await scene.expectPixelColor([53, 53, 53], testPoint, 15) }) await test.step(`Go through the command bar flow`, async () => { await toolbar.sweepButton.click() await expect .poll(() => page.getByText('Please select one').count()) .toBe(1) await cmdBar.expectState({ commandName: 'Sweep', currentArgKey: 'sketches', currentArgValue: '', headerArguments: { Profiles: '', Path: '', }, highlightedHeaderArg: 'Profiles', stage: 'arguments', }) await clickOnSketch1() await cmdBar.progressCmdBar() await cmdBar.expectState({ commandName: 'Sweep', currentArgKey: 'path', currentArgValue: '', headerArguments: { Profiles: '1 profile', Path: '', }, highlightedHeaderArg: 'path', stage: 'arguments', }) await clickOnSketch2() await cmdBar.expectState({ commandName: 'Sweep', currentArgKey: 'path', currentArgValue: '', headerArguments: { Profiles: '1 profile', Path: '', }, highlightedHeaderArg: 'path', stage: 'arguments', }) await cmdBar.progressCmdBar() await cmdBar.expectState({ commandName: 'Sweep', headerArguments: { Profiles: '1 profile', Path: '1 segment', }, stage: 'review', }) // Confirm we can submit from the review step with just `Enter` await cmdBar.progressCmdBar(true) await cmdBar.expectState({ stage: 'commandBarClosed', }) }) await test.step(`Confirm code is added to the editor, scene has changed`, async () => { await toolbar.openPane('code') await editor.expectEditor.toContain(sweepDeclaration) await scene.expectPixelColor([120, 120, 120], testPoint, 40) await toolbar.closePane('code') }) await test.step('Edit sweep via feature tree selection works', async () => { await toolbar.openPane('feature-tree') const operationButton = await toolbar.getFeatureTreeOperation( 'Sweep', 0 ) await operationButton.dblclick({ button: 'left' }) await page .getByRole('button', { name: 'sectional', exact: false }) .click() await cmdBar.expectState({ commandName: 'Sweep', currentArgKey: 'sectional', currentArgValue: '', headerArguments: { Sectional: '', }, highlightedHeaderArg: 'sectional', stage: 'arguments', }) await cmdBar.selectOption({ name: 'True' }).click() await cmdBar.expectState({ commandName: 'Sweep', headerArguments: { Sectional: '', }, stage: 'review', }) await cmdBar.progressCmdBar() await toolbar.closePane('feature-tree') await toolbar.openPane('code') await editor.expectEditor.toContain(editedSweepDeclaration) await toolbar.closePane('code') }) await test.step('Delete sweep via feature tree selection', async () => { await toolbar.openPane('feature-tree') await page.waitForTimeout(500) const operationButton = await toolbar.getFeatureTreeOperation( 'Sweep', 0 ) await operationButton.click({ button: 'left' }) await page.keyboard.press('Delete') await page.waitForTimeout(500) await toolbar.closePane('feature-tree') await scene.expectPixelColor([53, 53, 53], testPoint, 15) }) }) }) test(`Sweep point-and-click helix`, async ({ context, page, homePage, scene, editor, toolbar, cmdBar, }) => { const circleCode = `circle(sketch001, center = [0, -1], radius = .1)` const initialCode = `helix001 = helix( axis = X, radius = 1, length = 10, revolutions = 10, angleStart = 0, ccw = false, ) sketch001 = startSketchOn(XZ) profile001 = ${circleCode}` const sweepDeclaration = 'sweep001 = sweep(profile001, path = helix001)' const editedSweepDeclaration = `sweep001 = sweep(profile001, path = helix001, relativeTo = 'sketchPlane')` await context.addInitScript((initialCode) => { localStorage.setItem('persistCode', initialCode) }, initialCode) await homePage.goToModelingScene() await scene.settled(cmdBar) await test.step(`Add sweep through the command bar flow`, async () => { await toolbar.openPane('feature-tree') await toolbar.sweepButton.click() await cmdBar.expectState({ commandName: 'Sweep', currentArgKey: 'sketches', currentArgValue: '', headerArguments: { Profiles: '', Path: '', }, highlightedHeaderArg: 'Profiles', stage: 'arguments', }) await editor.scrollToText(circleCode) await page.getByText(circleCode).click() await cmdBar.progressCmdBar() await cmdBar.expectState({ commandName: 'Sweep', currentArgKey: 'path', currentArgValue: '', headerArguments: { Profiles: '1 profile', Path: '', }, highlightedHeaderArg: 'path', stage: 'arguments', }) const helix = await toolbar.getFeatureTreeOperation('Helix', 0) await helix.click() await cmdBar.expectState({ commandName: 'Sweep', currentArgKey: 'path', currentArgValue: '', headerArguments: { Profiles: '1 profile', Path: '', }, highlightedHeaderArg: 'path', stage: 'arguments', }) await cmdBar.progressCmdBar() await cmdBar.expectState({ commandName: 'Sweep', headerArguments: { Profiles: '1 profile', Path: '1 helix', }, stage: 'review', }) await cmdBar.progressCmdBar(true) await editor.expectEditor.toContain(sweepDeclaration) }) await test.step('Go through the edit flow via feature tree', async () => { await toolbar.openPane('feature-tree') const op = await toolbar.getFeatureTreeOperation('Sweep', 0) await op.dblclick() await cmdBar.expectState({ stage: 'review', headerArguments: {}, commandName: 'Sweep', }) await cmdBar.clickOptionalArgument('relativeTo') await cmdBar.expectState({ stage: 'arguments', currentArgKey: 'relativeTo', currentArgValue: '', headerArguments: { RelativeTo: '', }, highlightedHeaderArg: 'relativeTo', commandName: 'Sweep', }) await cmdBar.selectOption({ name: 'sketchPlane' }).click() await cmdBar.expectState({ stage: 'review', headerArguments: { RelativeTo: 'sketchPlane', }, commandName: 'Sweep', }) await cmdBar.submit() await editor.expectEditor.toContain(editedSweepDeclaration) }) await test.step('Delete sweep via feature tree selection', async () => { const sweep = await toolbar.getFeatureTreeOperation('Sweep', 0) await sweep.click() await page.keyboard.press('Delete') await editor.expectEditor.not.toContain(editedSweepDeclaration) }) }) test(`Fillet point-and-click`, async ({ context, page, homePage, scene, editor, toolbar, cmdBar, }) => { // Code samples const initialCode = `sketch001 = startSketchOn(XY) |> startProfile(at = [-12, -6]) |> line(end = [0, 12]) |> line(end = [24, 0]) |> line(end = [0, -12]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> close() extrude001 = extrude(sketch001, length = -12) ` const firstFilletDeclaration = `fillet(radius=5,tags=[getCommonEdge(faces=[seg01,capEnd001])],)` const secondFilletDeclaration = `fillet(radius=5,tags=[getCommonEdge(faces=[seg01,capStart001])],)` // Locators const firstEdgeLocation = { x: 600, y: 193 } const secondEdgeLocation = { x: 600, y: 383 } const bodyLocation = { x: 630, y: 290 } const [clickOnFirstEdge] = scene.makeMouseHelpers( firstEdgeLocation.x, firstEdgeLocation.y ) const [clickOnSecondEdge] = scene.makeMouseHelpers( secondEdgeLocation.x, secondEdgeLocation.y ) // Colors const edgeColorWhite: [number, number, number] = [248, 248, 248] const edgeColorYellow: [number, number, number] = [251, 251, 40] // Mac:B=67 Ubuntu:B=12 const bodyColor: [number, number, number] = [155, 155, 155] const filletColor: [number, number, number] = [127, 127, 127] const backgroundColor: [number, number, number] = [30, 30, 30] const lowTolerance = 20 const highTolerance = 70 // TODO: understand why I needed that for edgeColorYellow on macos (local) // Setup await test.step(`Initial test setup`, async () => { await context.addInitScript((initialCode) => { localStorage.setItem('persistCode', initialCode) }, initialCode) await page.setBodyDimensions({ width: 1000, height: 500 }) await homePage.goToModelingScene() // verify modeling scene is loaded await scene.expectPixelColor( backgroundColor, secondEdgeLocation, lowTolerance ) // wait for stream to load await scene.expectPixelColor(bodyColor, bodyLocation, highTolerance) }) // Test 1: Command bar flow with preselected edges await test.step(`Select first edge`, async () => { await scene.expectPixelColor( edgeColorWhite, firstEdgeLocation, lowTolerance ) await clickOnFirstEdge() await scene.expectPixelColor( edgeColorYellow, firstEdgeLocation, highTolerance // Ubuntu color mismatch can require high tolerance ) }) await test.step(`Apply fillet to the preselected edge`, async () => { await page.waitForTimeout(100) await toolbar.filletButton.click() await cmdBar.expectState({ commandName: 'Fillet', highlightedHeaderArg: 'selection', currentArgKey: 'selection', currentArgValue: '', headerArguments: { Selection: '', Radius: '', }, stage: 'arguments', }) await cmdBar.progressCmdBar() await cmdBar.expectState({ commandName: 'Fillet', highlightedHeaderArg: 'radius', currentArgKey: 'radius', currentArgValue: '5', headerArguments: { Selection: '1 segment', Radius: '', }, stage: 'arguments', }) await cmdBar.progressCmdBar() await cmdBar.expectState({ commandName: 'Fillet', headerArguments: { Selection: '1 segment', Radius: '5', }, stage: 'review', }) await cmdBar.progressCmdBar() }) await test.step(`Confirm code is added to the editor`, async () => { await editor.expectEditor.toContain(firstFilletDeclaration, { shouldNormalise: true, }) await editor.expectState({ diagnostics: [], activeLines: [')'], highlightedCode: '', }) }) await test.step(`Confirm scene has changed`, async () => { await scene.expectPixelColor(filletColor, firstEdgeLocation, lowTolerance) }) // Test 1.1: Edit fillet (segment type) async function editFillet( featureTreeIndex: number, oldValue: string, newValue: string ) { await toolbar.openPane('feature-tree') const operationButton = await toolbar.getFeatureTreeOperation( 'Fillet', featureTreeIndex ) await operationButton.dblclick({ button: 'left' }) await cmdBar.expectState({ commandName: 'Fillet', currentArgKey: 'radius', currentArgValue: oldValue, headerArguments: { Radius: oldValue, }, highlightedHeaderArg: 'radius', stage: 'arguments', }) await page.keyboard.insertText(newValue) await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'review', headerArguments: { Radius: newValue, }, commandName: 'Fillet', }) await cmdBar.progressCmdBar() await toolbar.closePane('feature-tree') } await test.step('Edit fillet via feature tree selection works', async () => { const firstFilletFeatureTreeIndex = 0 const editedRadius = '1' await editFillet(firstFilletFeatureTreeIndex, '5', editedRadius) await editor.expectEditor.toContain( firstFilletDeclaration.replace('radius=5', 'radius=' + editedRadius), { shouldNormalise: true } ) // Edit back to original radius await editFillet(firstFilletFeatureTreeIndex, editedRadius, '5') await editor.expectEditor.toContain(firstFilletDeclaration, { shouldNormalise: true, }) }) // Test 2: Command bar flow without preselected edges await test.step(`Open fillet UI without selecting edges`, async () => { await page.waitForTimeout(100) await toolbar.filletButton.click() await expect .poll(() => page.getByText('Please select one').count()) .toBe(1) await cmdBar.expectState({ stage: 'arguments', currentArgKey: 'selection', currentArgValue: '', headerArguments: { Selection: '', Radius: '', }, highlightedHeaderArg: 'selection', commandName: 'Fillet', }) }) await test.step(`Select second edge`, async () => { await scene.expectPixelColor( edgeColorWhite, secondEdgeLocation, lowTolerance ) await clickOnSecondEdge() await scene.expectPixelColor( edgeColorYellow, secondEdgeLocation, highTolerance // Ubuntu color mismatch can require high tolerance ) }) await test.step(`Apply fillet to the second edge`, async () => { await cmdBar.expectState({ commandName: 'Fillet', highlightedHeaderArg: 'selection', currentArgKey: 'selection', currentArgValue: '', headerArguments: { Selection: '', Radius: '', }, stage: 'arguments', }) await cmdBar.progressCmdBar() await cmdBar.expectState({ commandName: 'Fillet', highlightedHeaderArg: 'radius', currentArgKey: 'radius', currentArgValue: '5', headerArguments: { Selection: '1 sweepEdge', Radius: '', }, stage: 'arguments', }) await cmdBar.progressCmdBar() await cmdBar.expectState({ commandName: 'Fillet', headerArguments: { Selection: '1 sweepEdge', Radius: '5', }, stage: 'review', }) await cmdBar.progressCmdBar() }) await test.step(`Confirm code is added to the editor`, async () => { await editor.expectEditor.toContain(secondFilletDeclaration, { shouldNormalise: true, }) await editor.expectState({ diagnostics: [], activeLines: [')'], highlightedCode: '', }) }) await test.step(`Confirm scene has changed`, async () => { await scene.expectPixelColor( backgroundColor, secondEdgeLocation, lowTolerance ) }) // Test 2.1: Edit fillet (edgeSweep type) await test.step('Edit fillet via feature tree selection works', async () => { const secondFilletFeatureTreeIndex = 1 const editedRadius = '2' await editFillet(secondFilletFeatureTreeIndex, '5', editedRadius) await editor.expectEditor.toContain( secondFilletDeclaration.replace('radius=5', 'radius=' + editedRadius), { shouldNormalise: true } ) // Edit back to original radius await editFillet(secondFilletFeatureTreeIndex, editedRadius, '5') await editor.expectEditor.toContain(secondFilletDeclaration, { shouldNormalise: true, }) }) // Test 3: Delete fillets await test.step('Delete fillet via feature tree selection', async () => { await test.step('Open Feature Tree Pane', async () => { await toolbar.openPane('feature-tree') await page.waitForTimeout(500) }) await test.step('Delete fillet via feature tree selection', async () => { await editor.expectEditor.toContain(secondFilletDeclaration, { shouldNormalise: true, }) const operationButton = await toolbar.getFeatureTreeOperation( 'Fillet', 1 ) await operationButton.click({ button: 'left' }) await page.keyboard.press('Delete') await page.waitForTimeout(500) await scene.expectPixelColor(edgeColorWhite, secondEdgeLocation, 15) // deleted await editor.expectEditor.not.toContain(secondFilletDeclaration) await scene.expectPixelColor(filletColor, firstEdgeLocation, 15) // stayed }) }) }) test(`Fillet point-and-click edit standalone expression`, async ({ context, page, homePage, scene, editor, toolbar, cmdBar, }) => { const initialCode = `sketch001 = startSketchOn(XY) profile001 = circle( sketch001, center = [0, 0], radius = 100, tag = $seg01, ) extrude001 = extrude(profile001, length = 100) fillet001 = fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg01)]) ` await test.step(`Initial test setup`, async () => { await context.addInitScript((initialCode) => { localStorage.setItem('persistCode', initialCode) }, initialCode) await page.setBodyDimensions({ width: 1000, height: 500 }) await homePage.goToModelingScene() await scene.settled(cmdBar) }) await test.step('Edit fillet', async () => { await toolbar.openPane('feature-tree') await toolbar.closePane('code') const operationButton = await toolbar.getFeatureTreeOperation('Fillet', 0) await operationButton.dblclick({ button: 'left' }) await cmdBar.expectState({ commandName: 'Fillet', currentArgKey: 'radius', currentArgValue: '5', headerArguments: { Radius: '5', }, highlightedHeaderArg: 'radius', stage: 'arguments', }) await page.keyboard.insertText('20') await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'review', headerArguments: { Radius: '20', }, commandName: 'Fillet', }) await cmdBar.progressCmdBar() }) await test.step('Confirm changes', async () => { await toolbar.openPane('code') await toolbar.closePane('feature-tree') await editor.expectEditor.toContain('radius = 20') }) }) test(`Fillet point-and-click delete`, async ({ context, page, homePage, scene, editor, toolbar, cmdBar, }) => { // Code samples const initialCode = `sketch001 = startSketchOn(XY) |> startProfile(at = [-12, -6]) |> line(end = [0, 12]) |> line(end = [24, 0], tag = $seg02) |> line(end = [0, -12]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg01) |> close() extrude001 = extrude(sketch001, length = -12) |> fillet(radius = 5, tags = [seg01]) // fillet01 |> fillet(radius = 5, tags = [seg02]) // fillet02 fillet03 = fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg01)]) fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg02)]) ` const firstPipedFilletDeclaration = 'fillet(radius = 5, tags = [seg01])' const secondPipedFilletDeclaration = 'fillet(radius = 5, tags = [seg02])' const standaloneAssignedFilletDeclaration = 'fillet03 = fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg01)])' const standaloneUnassignedFilletDeclaration = 'fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg02)])' // Locators const pipedFilletEdgeLocation = { x: 600, y: 193 } const standaloneFilletEdgeLocation = { x: 600, y: 383 } const bodyLocation = { x: 630, y: 290 } // Colors const edgeColorWhite: [number, number, number] = [248, 248, 248] const bodyColor: [number, number, number] = [155, 155, 155] const filletColor: [number, number, number] = [127, 127, 127] const backgroundColor: [number, number, number] = [30, 30, 30] const lowTolerance = 20 const highTolerance = 40 // Setup await test.step(`Initial test setup`, async () => { await context.addInitScript((initialCode) => { localStorage.setItem('persistCode', initialCode) }, initialCode) await page.setBodyDimensions({ width: 1000, height: 500 }) await homePage.goToModelingScene() await scene.settled(cmdBar) // verify modeling scene is loaded await scene.expectPixelColor( backgroundColor, standaloneFilletEdgeLocation, lowTolerance ) // wait for stream to load await scene.expectPixelColor(bodyColor, bodyLocation, highTolerance) }) // Test await test.step('Delete fillet via feature tree selection', async () => { await test.step('Open Feature Tree Pane', async () => { await toolbar.openPane('feature-tree') await scene.settled(cmdBar) }) await test.step('Delete piped fillet via feature tree selection', async () => { await test.step('Verify all fillets are present in the editor', async () => { await editor.expectEditor.toContain(firstPipedFilletDeclaration) await editor.expectEditor.toContain(secondPipedFilletDeclaration) await editor.expectEditor.toContain( standaloneAssignedFilletDeclaration ) await editor.expectEditor.toContain( standaloneUnassignedFilletDeclaration ) }) await test.step('Verify test fillets are present in the scene', async () => { await scene.expectPixelColor( filletColor, pipedFilletEdgeLocation, lowTolerance ) await scene.expectPixelColor( backgroundColor, standaloneFilletEdgeLocation, lowTolerance ) }) await test.step('Delete piped fillet', async () => { const operationButton = await toolbar.getFeatureTreeOperation( 'Fillet', 0 ) await operationButton.click({ button: 'left' }) await page.keyboard.press('Delete') await scene.settled(cmdBar) }) await test.step('Verify piped fillet is deleted but other fillets are not (in the editor)', async () => { await editor.expectEditor.not.toContain(firstPipedFilletDeclaration) await editor.expectEditor.toContain(secondPipedFilletDeclaration) await editor.expectEditor.toContain( standaloneAssignedFilletDeclaration ) await editor.expectEditor.toContain( standaloneUnassignedFilletDeclaration ) }) await test.step('Verify piped fillet is deleted but non-piped is not (in the scene)', async () => { await scene.expectPixelColor( edgeColorWhite, // you see edge because fillet is deleted pipedFilletEdgeLocation, lowTolerance ) await scene.expectPixelColor( backgroundColor, // you see background because fillet is not deleted standaloneFilletEdgeLocation, lowTolerance ) }) }) await test.step('Delete standalone assigned fillet via feature tree selection', async () => { await test.step('Delete standalone assigned fillet', async () => { const operationButton = await toolbar.getFeatureTreeOperation( 'Fillet', 1 ) await operationButton.click({ button: 'left' }) await page.keyboard.press('Delete') await scene.settled(cmdBar) }) await test.step('Verify standalone assigned fillet is deleted but other two fillets are not (in the editor)', async () => { await editor.expectEditor.toContain(secondPipedFilletDeclaration) await editor.expectEditor.not.toContain( standaloneAssignedFilletDeclaration ) await editor.expectEditor.toContain( standaloneUnassignedFilletDeclaration ) }) await test.step('Verify standalone assigned fillet is deleted but piped is not (in the scene)', async () => { await scene.expectPixelColor( edgeColorWhite, standaloneFilletEdgeLocation, lowTolerance ) }) }) await test.step('Delete standalone unassigned fillet via feature tree selection', async () => { await test.step('Delete standalone unassigned fillet', async () => { const operationButton = await toolbar.getFeatureTreeOperation( 'Fillet', 1 ) await operationButton.click({ button: 'left' }) await page.keyboard.press('Delete') await scene.settled(cmdBar) }) await test.step('Verify standalone unassigned fillet is deleted but other fillet is not (in the editor)', async () => { await editor.expectEditor.toContain(secondPipedFilletDeclaration) await editor.expectEditor.not.toContain( standaloneUnassignedFilletDeclaration ) }) await test.step('Verify standalone unassigned fillet is deleted but piped is not (in the scene)', async () => { await scene.expectPixelColor( edgeColorWhite, standaloneFilletEdgeLocation, lowTolerance ) }) }) }) }) test(`Fillet with large radius should update code even if engine fails`, async ({ context, page, homePage, scene, editor, toolbar, cmdBar, }) => { // Create a cube with small edges that will cause some fillets to fail const initialCode = `sketch001 = startSketchOn(XY) profile001 = startProfile(sketch001, at = [0, 0]) |> yLine(length = -1) |> xLine(length = -10) |> yLine(length = 10) |> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> close() extrude001 = extrude(profile001, length = 5) ` const taggedSegment1 = `xLine(length = -10, tag = $seg01)` const taggedSegment2 = `yLine(length = -1, tag = $seg02)` const filletExpression = `fillet(radius = 1000, tags = [getCommonEdge(faces = [seg01, seg02])])` // Locators const edgeLocation = { x: 659, y: 313 } const bodyLocation = { x: 594, y: 313 } // Colors const edgeColorWhite: [number, number, number] = [248, 248, 248] const edgeColorYellow: [number, number, number] = [251, 251, 120] // Mac:B=251,251,90 Ubuntu:240,241,180, Windows:240,241,180 const backgroundColor: [number, number, number] = [30, 30, 30] const bodyColor: [number, number, number] = [155, 155, 155] const lowTolerance = 20 const highTolerance = 70 // Setup await test.step(`Initial test setup`, async () => { await context.addInitScript((initialCode) => { localStorage.setItem('persistCode', initialCode) }, initialCode) await page.setBodyDimensions({ width: 1000, height: 500 }) await homePage.goToModelingScene() // verify modeling scene is loaded await scene.expectPixelColor(backgroundColor, edgeLocation, lowTolerance) // wait for stream to load await scene.expectPixelColor(bodyColor, bodyLocation, highTolerance) }) // Test await test.step('Select edges and apply oversized fillet', async () => { await test.step(`Select the edge`, async () => { await scene.expectPixelColor(edgeColorWhite, edgeLocation, lowTolerance) const [clickOnTheEdge] = scene.makeMouseHelpers( edgeLocation.x, edgeLocation.y ) await clickOnTheEdge() await scene.expectPixelColor( edgeColorYellow, edgeLocation, highTolerance // Ubuntu color mismatch can require high tolerance ) }) await test.step(`Apply fillet`, async () => { await page.waitForTimeout(100) await toolbar.filletButton.click() await cmdBar.expectState({ commandName: 'Fillet', highlightedHeaderArg: 'selection', currentArgKey: 'selection', currentArgValue: '', headerArguments: { Selection: '', Radius: '', }, stage: 'arguments', }) await cmdBar.progressCmdBar() await cmdBar.expectState({ commandName: 'Fillet', highlightedHeaderArg: 'radius', currentArgKey: 'radius', currentArgValue: '5', headerArguments: { Selection: '1 sweepEdge', Radius: '', }, stage: 'arguments', }) // Set a large radius (1000) await cmdBar.currentArgumentInput.locator('.cm-content').fill('1000') await cmdBar.progressCmdBar() await cmdBar.expectState({ commandName: 'Fillet', headerArguments: { Selection: '1 sweepEdge', Radius: '1000', }, stage: 'review', }) // Apply fillet with large radius await cmdBar.progressCmdBar() }) }) await test.step('Verify code is updated regardless of execution errors', async () => { await editor.expectEditor.toContain(taggedSegment1) await editor.expectEditor.toContain(taggedSegment2) await editor.expectEditor.toContain(filletExpression) }) }) test(`Chamfer point-and-click`, async ({ context, page, homePage, scene, editor, toolbar, cmdBar, }) => { // Code samples const initialCode = `@settings(defaultLengthUnit = in) sketch001 = startSketchOn(XY) |> startProfile(at = [-12, -6]) |> line(end = [0, 12]) |> line(end = [24, 0]) |> line(end = [0, -12]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> close() extrude001 = extrude(sketch001, length = -12) ` const firstChamferDeclaration = `chamfer(length=5,tags=[getCommonEdge(faces=[seg01,capEnd001])],)` const secondChamferDeclaration = `chamfer(length=5,tags=[getCommonEdge(faces=[seg01,capStart001])],)` // Locators const firstEdgeLocation = { x: 600, y: 193 } const secondEdgeLocation = { x: 600, y: 383 } const [clickOnFirstEdge] = scene.makeMouseHelpers( firstEdgeLocation.x, firstEdgeLocation.y ) const [clickOnSecondEdge] = scene.makeMouseHelpers( secondEdgeLocation.x, secondEdgeLocation.y ) // Colors const edgeColorWhite: [number, number, number] = [248, 248, 248] const edgeColorYellow: [number, number, number] = [251, 251, 40] // Mac:B=67 Ubuntu:B=12 const chamferColor: [number, number, number] = [168, 168, 168] const backgroundColor: [number, number, number] = [30, 30, 30] const lowTolerance = 20 const highTolerance = 70 // TODO: understand why I needed that for edgeColorYellow on macos (local) // Setup await test.step(`Initial test setup`, async () => { await context.addInitScript((initialCode) => { localStorage.setItem('persistCode', initialCode) }, initialCode) await page.setBodyDimensions({ width: 1000, height: 500 }) await homePage.goToModelingScene() await scene.settled(cmdBar) }) // Test 1: Command bar flow with preselected edges await test.step(`Select first edge`, async () => { await scene.expectPixelColor( edgeColorWhite, firstEdgeLocation, lowTolerance ) await clickOnFirstEdge() await scene.expectPixelColor( edgeColorYellow, firstEdgeLocation, highTolerance // Ubuntu color mismatch can require high tolerance ) }) await test.step(`Apply chamfer to the preselected edge`, async () => { await page.waitForTimeout(100) await toolbar.chamferButton.click() await cmdBar.expectState({ commandName: 'Chamfer', highlightedHeaderArg: 'selection', currentArgKey: 'selection', currentArgValue: '', headerArguments: { Selection: '', Length: '', }, stage: 'arguments', }) await cmdBar.progressCmdBar() await page.waitForTimeout(1000) await cmdBar.expectState({ commandName: 'Chamfer', highlightedHeaderArg: 'length', currentArgKey: 'length', currentArgValue: '5', headerArguments: { Selection: '1 segment', Length: '', }, stage: 'arguments', }) await cmdBar.argumentInput.focus() await page.waitForTimeout(1000) await cmdBar.progressCmdBar() await page.waitForTimeout(1000) await cmdBar.expectState({ commandName: 'Chamfer', headerArguments: { Selection: '1 segment', Length: '5', }, stage: 'review', }) await cmdBar.progressCmdBar() }) await test.step(`Confirm code is added to the editor`, async () => { await editor.expectEditor.toContain(firstChamferDeclaration, { shouldNormalise: true, }) await editor.expectState({ diagnostics: [], activeLines: [')'], highlightedCode: '', }) }) await test.step(`Confirm scene has changed`, async () => { await scene.expectPixelColor( chamferColor, firstEdgeLocation, lowTolerance ) }) // Test 1.1: Edit sweep async function editChamfer( featureTreeIndex: number, oldValue: string, newValue: string ) { await toolbar.openPane('feature-tree') const operationButton = await toolbar.getFeatureTreeOperation( 'Chamfer', featureTreeIndex ) await operationButton.dblclick({ button: 'left' }) await cmdBar.expectState({ commandName: 'Chamfer', currentArgKey: 'length', currentArgValue: oldValue, headerArguments: { Length: oldValue, }, highlightedHeaderArg: 'length', stage: 'arguments', }) await page.keyboard.insertText(newValue) await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'review', headerArguments: { Length: newValue, }, commandName: 'Chamfer', }) await cmdBar.progressCmdBar() await toolbar.closePane('feature-tree') } await test.step('Edit chamfer via feature tree selection works', async () => { const firstChamferFeatureTreeIndex = 0 const editedLength = '1' await editChamfer(firstChamferFeatureTreeIndex, '5', editedLength) await editor.expectEditor.toContain( firstChamferDeclaration.replace('length=5', 'length=' + editedLength), { shouldNormalise: true } ) // Edit back to original radius await editChamfer(firstChamferFeatureTreeIndex, editedLength, '5') await editor.expectEditor.toContain(firstChamferDeclaration, { shouldNormalise: true, }) }) // Test 2: Command bar flow without preselected edges await test.step(`Open chamfer UI without selecting edges`, async () => { await page.waitForTimeout(100) await toolbar.chamferButton.click() await expect .poll(() => page.getByText('Please select one').count()) .toBe(1) await cmdBar.expectState({ stage: 'arguments', currentArgKey: 'selection', currentArgValue: '', headerArguments: { Selection: '', Length: '', }, highlightedHeaderArg: 'selection', commandName: 'Chamfer', }) }) await test.step(`Select second edge`, async () => { await scene.expectPixelColor( edgeColorWhite, secondEdgeLocation, lowTolerance ) await clickOnSecondEdge() await scene.expectPixelColor( edgeColorYellow, secondEdgeLocation, highTolerance // Ubuntu color mismatch can require high tolerance ) }) await test.step(`Apply chamfer to the second edge`, async () => { await cmdBar.expectState({ commandName: 'Chamfer', highlightedHeaderArg: 'selection', currentArgKey: 'selection', currentArgValue: '', headerArguments: { Selection: '', Length: '', }, stage: 'arguments', }) await cmdBar.progressCmdBar() await cmdBar.expectState({ commandName: 'Chamfer', highlightedHeaderArg: 'length', currentArgKey: 'length', currentArgValue: '5', headerArguments: { Selection: '1 sweepEdge', Length: '', }, stage: 'arguments', }) await cmdBar.progressCmdBar() await cmdBar.expectState({ commandName: 'Chamfer', headerArguments: { Selection: '1 sweepEdge', Length: '5', }, stage: 'review', }) await cmdBar.progressCmdBar() }) await test.step(`Confirm code is added to the editor`, async () => { await editor.expectEditor.toContain(secondChamferDeclaration, { shouldNormalise: true, }) await editor.expectState({ diagnostics: [], activeLines: [')'], highlightedCode: '', }) }) await test.step(`Confirm scene has changed`, async () => { await scene.expectPixelColor( backgroundColor, secondEdgeLocation, lowTolerance ) }) // Test 2.1: Edit chamfer (edgeSweep type) await test.step('Edit chamfer via feature tree selection works', async () => { const secondChamferFeatureTreeIndex = 1 const editedLength = '2' await editChamfer(secondChamferFeatureTreeIndex, '5', editedLength) await editor.expectEditor.toContain( secondChamferDeclaration.replace('length=5', 'length=' + editedLength), { shouldNormalise: true } ) // Edit back to original length await editChamfer(secondChamferFeatureTreeIndex, editedLength, '5') await editor.expectEditor.toContain(secondChamferDeclaration, { shouldNormalise: true, }) }) // Test 3: Delete chamfer via feature tree selection await test.step('Open Feature Tree Pane', async () => { await toolbar.openPane('feature-tree') await page.waitForTimeout(500) }) await test.step('Delete chamfer via feature tree selection', async () => { const operationButton = await toolbar.getFeatureTreeOperation( 'Chamfer', 1 ) await operationButton.click({ button: 'left' }) await page.keyboard.press('Delete') await page.waitForTimeout(500) await scene.expectPixelColor(edgeColorWhite, secondEdgeLocation, 15) // deleted await scene.expectPixelColor(chamferColor, firstEdgeLocation, 15) // stayed }) }) test(`Chamfer point-and-click delete`, async ({ context, page, homePage, scene, editor, toolbar, cmdBar, }) => { // Code samples const initialCode = `@settings(defaultLengthUnit = in) sketch001 = startSketchOn(XY) |> startProfile(at = [-12, -6]) |> line(end = [0, 12]) |> line(end = [24, 0], tag = $seg02) |> line(end = [0, -12]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg01) |> close() extrude001 = extrude(sketch001, length = -12) |> chamfer(length = 5, tags = [seg01]) // chamfer01 |> chamfer(length = 5, tags = [seg02]) // chamfer02 chamfer03 = chamfer(extrude001, length = 5, tags = [getOppositeEdge(seg01)]) chamfer(extrude001, length = 5, tags = [getOppositeEdge(seg02)]) ` const firstPipedChamferDeclaration = 'chamfer(length = 5, tags = [seg01])' const secondPipedChamferDeclaration = 'chamfer(length = 5, tags = [seg02])' const standaloneAssignedChamferDeclaration = 'chamfer03 = chamfer(extrude001, length = 5, tags = [getOppositeEdge(seg01)])' const standaloneUnassignedChamferDeclaration = 'chamfer(extrude001, length = 5, tags = [getOppositeEdge(seg02)])' // Locators const pipedChamferEdgeLocation = { x: 600, y: 193 } const standaloneChamferEdgeLocation = { x: 600, y: 383 } const bodyLocation = { x: 630, y: 290 } // Colors const edgeColorWhite: [number, number, number] = [248, 248, 248] const bodyColor: [number, number, number] = [155, 155, 155] const chamferColor: [number, number, number] = [168, 168, 168] const backgroundColor: [number, number, number] = [30, 30, 30] const lowTolerance = 20 const highTolerance = 40 // Setup await test.step(`Initial test setup`, async () => { await context.addInitScript((initialCode) => { localStorage.setItem('persistCode', initialCode) }, initialCode) await page.setBodyDimensions({ width: 1000, height: 500 }) await homePage.goToModelingScene() await scene.settled(cmdBar) // verify modeling scene is loaded await scene.expectPixelColor( backgroundColor, standaloneChamferEdgeLocation, lowTolerance ) // wait for stream to load await scene.expectPixelColor(bodyColor, bodyLocation, highTolerance) }) // Test await test.step('Delete chamfer via feature tree selection', async () => { await test.step('Open Feature Tree Pane', async () => { await toolbar.openPane('feature-tree') await scene.settled(cmdBar) }) await test.step('Delete piped chamfer via feature tree selection', async () => { await test.step('Verify all chamfers are present in the editor', async () => { await editor.expectEditor.toContain(firstPipedChamferDeclaration) await editor.expectEditor.toContain(secondPipedChamferDeclaration) await editor.expectEditor.toContain( standaloneAssignedChamferDeclaration ) await editor.expectEditor.toContain( standaloneUnassignedChamferDeclaration ) }) await test.step('Verify test chamfers are present in the scene', async () => { await scene.expectPixelColor( chamferColor, pipedChamferEdgeLocation, lowTolerance ) await scene.expectPixelColor( backgroundColor, standaloneChamferEdgeLocation, lowTolerance ) }) await test.step('Delete piped chamfer', async () => { const operationButton = await toolbar.getFeatureTreeOperation( 'Chamfer', 0 ) await operationButton.click({ button: 'left' }) await page.keyboard.press('Delete') await scene.settled(cmdBar) }) await test.step('Verify piped chamfer is deleted but other chamfers are not (in the editor)', async () => { await editor.expectEditor.not.toContain(firstPipedChamferDeclaration) await editor.expectEditor.toContain(secondPipedChamferDeclaration) await editor.expectEditor.toContain( standaloneAssignedChamferDeclaration ) await editor.expectEditor.toContain( standaloneUnassignedChamferDeclaration ) }) await test.step('Verify piped chamfer is deleted but non-piped is not (in the scene)', async () => { await scene.expectPixelColor( edgeColorWhite, // you see edge color because chamfer is deleted pipedChamferEdgeLocation, lowTolerance ) await scene.expectPixelColor( backgroundColor, // you see background color instead of edge because it's chamfered standaloneChamferEdgeLocation, lowTolerance ) }) }) await test.step('Delete standalone assigned chamfer via feature tree selection', async () => { await test.step('Delete standalone assigned chamfer', async () => { const operationButton = await toolbar.getFeatureTreeOperation( 'Chamfer', 1 ) await operationButton.click({ button: 'left' }) await page.keyboard.press('Delete') await scene.settled(cmdBar) }) await test.step('Verify standalone assigned chamfer is deleted but other two chamfers are not (in the editor)', async () => { await editor.expectEditor.toContain(secondPipedChamferDeclaration) await editor.expectEditor.not.toContain( standaloneAssignedChamferDeclaration ) await editor.expectEditor.toContain( standaloneUnassignedChamferDeclaration ) }) await test.step('Verify standalone assigned chamfer is deleted but piped is not (in the scene)', async () => { await scene.expectPixelColor( edgeColorWhite, standaloneChamferEdgeLocation, lowTolerance ) }) }) await test.step('Delete standalone unassigned chamfer via feature tree selection', async () => { await test.step('Delete standalone unassigned chamfer', async () => { const operationButton = await toolbar.getFeatureTreeOperation( 'Chamfer', 1 ) await operationButton.click({ button: 'left' }) await page.keyboard.press('Delete') await scene.settled(cmdBar) }) await test.step('Verify standalone unassigned chamfer is deleted but piped chamfer is not (in the editor)', async () => { await editor.expectEditor.toContain(secondPipedChamferDeclaration) await editor.expectEditor.not.toContain( standaloneUnassignedChamferDeclaration ) }) await test.step('Verify standalone unassigned chamfer is deleted but piped is not (in the scene)', async () => { await scene.expectPixelColor( edgeColorWhite, standaloneChamferEdgeLocation, lowTolerance ) }) }) }) }) const shellPointAndClickCapCases = [ { shouldPreselect: true }, { shouldPreselect: false }, ] shellPointAndClickCapCases.forEach(({ shouldPreselect }) => { test(`Shell point-and-click cap (preselected sketches: ${shouldPreselect})`, async ({ context, page, homePage, scene, editor, toolbar, cmdBar, }) => { const initialCode = `@settings(defaultLengthUnit = in) sketch001 = startSketchOn(XZ) |> circle(center = [0, 0], radius = 30) extrude001 = extrude(sketch001, length = 30) ` await context.addInitScript((initialCode) => { localStorage.setItem('persistCode', initialCode) }, initialCode) await page.setBodyDimensions({ width: 1000, height: 500 }) await homePage.goToModelingScene() await scene.connectionEstablished() // One dumb hardcoded screen pixel value const testPoint = { x: 575, y: 200 } const [clickOnCap] = scene.makeMouseHelpers(testPoint.x, testPoint.y) const shellDeclaration = 'shell001 = shell(extrude001, faces = [END], thickness = 5)' const editedShellDeclaration = 'shell001 = shell(extrude001, faces = [END], thickness = 2)' await test.step(`Look for the grey of the shape`, async () => { await scene.expectPixelColor([127, 127, 127], testPoint, 15) }) if (!shouldPreselect) { await test.step(`Go through the command bar flow without preselected faces`, async () => { await toolbar.shellButton.click() await expect .poll(() => page.getByText('Please select one').count()) .toBe(1) await cmdBar.expectState({ stage: 'arguments', currentArgKey: 'selection', currentArgValue: '', headerArguments: { Selection: '', Thickness: '', }, highlightedHeaderArg: 'selection', commandName: 'Shell', }) await clickOnCap() await page.waitForTimeout(500) await cmdBar.progressCmdBar() await page.waitForTimeout(500) await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'review', headerArguments: { Selection: '1 cap', Thickness: '5', }, commandName: 'Shell', }) await cmdBar.progressCmdBar() }) } else { await test.step(`Preselect the cap`, async () => { await clickOnCap() await page.waitForTimeout(500) }) await test.step(`Go through the command bar flow with a preselected face (cap)`, async () => { await toolbar.shellButton.click() await cmdBar.progressCmdBar() await page.waitForTimeout(500) await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'review', headerArguments: { Selection: '1 cap', Thickness: '5', }, commandName: 'Shell', }) await cmdBar.progressCmdBar() }) } await test.step(`Confirm code is added to the editor, scene has changed`, async () => { await editor.expectEditor.toContain(shellDeclaration) await editor.expectState({ diagnostics: [], activeLines: [shellDeclaration], highlightedCode: '', }) await scene.expectPixelColor([146, 146, 146], testPoint, 15) }) await test.step('Edit shell via feature tree selection works', async () => { await toolbar.openPane('feature-tree') const operationButton = await toolbar.getFeatureTreeOperation( 'Shell', 0 ) await operationButton.dblclick() await cmdBar.expectState({ stage: 'arguments', currentArgKey: 'thickness', currentArgValue: '5', headerArguments: { Thickness: '5', }, highlightedHeaderArg: 'thickness', commandName: 'Shell', }) await page.keyboard.insertText('2') await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'review', headerArguments: { Thickness: '2', }, commandName: 'Shell', }) await cmdBar.progressCmdBar() await toolbar.closePane('feature-tree') await scene.expectPixelColor([150, 150, 150], testPoint, 15) await editor.expectEditor.toContain(editedShellDeclaration) await editor.expectState({ diagnostics: [], activeLines: [editedShellDeclaration], highlightedCode: '', }) }) }) }) test('Shell point-and-click wall', async ({ context, page, homePage, scene, editor, toolbar, cmdBar, }) => { const initialCode = `@settings(defaultLengthUnit = in) sketch001 = startSketchOn(XY) |> startProfile(at = [-20, 20]) |> xLine(length = 40) |> yLine(length = -60) |> xLine(length = -40) |> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> close() extrude001 = extrude(sketch001, length = 40) ` await context.addInitScript((initialCode) => { localStorage.setItem('persistCode', initialCode) }, initialCode) await page.setBodyDimensions({ width: 1000, height: 500 }) await homePage.goToModelingScene() await scene.settled(cmdBar) // One dumb hardcoded screen pixel value const testPoint = { x: 580, y: 180 } const [clickOnCap] = scene.makeMouseHelpers(testPoint.x, testPoint.y) const [clickOnWall] = scene.makeMouseHelpers(testPoint.x, testPoint.y + 70) const mutatedCode = 'xLine(length = -40, tag = $seg01)' const shellDeclaration = 'shell001 = shell(extrude001, faces = [END, seg01], thickness = 5)' const editedShellDeclaration = 'shell001 = shell(extrude001, faces = [END, seg01], thickness = 1)' await test.step(`Look for the grey of the shape`, async () => { await scene.expectPixelColor([99, 99, 99], testPoint, 15) }) await test.step(`Go through the command bar flow, selecting a wall and keeping default thickness`, async () => { await toolbar.shellButton.click() await expect .poll(() => page.getByText('Please select one').count()) .toBe(1) await cmdBar.expectState({ stage: 'arguments', currentArgKey: 'selection', currentArgValue: '', headerArguments: { Selection: '', Thickness: '', }, highlightedHeaderArg: 'selection', commandName: 'Shell', }) await expect .poll(() => page.getByText('Please select one').count()) .toBe(1) await clickOnCap() await page.keyboard.down('Shift') await clickOnWall() await page.waitForTimeout(500) await page.keyboard.up('Shift') await cmdBar.progressCmdBar() await page.waitForTimeout(500) await cmdBar.progressCmdBar() await page.waitForTimeout(500) await cmdBar.expectState({ stage: 'review', headerArguments: { Selection: '1 cap, 1 face', Thickness: '5', }, commandName: 'Shell', }) await page.waitForTimeout(500) await cmdBar.progressCmdBar() await page.waitForTimeout(500) }) await test.step(`Confirm code is added to the editor, scene has changed`, async () => { await editor.expectEditor.toContain(mutatedCode) await editor.expectEditor.toContain(shellDeclaration) await editor.expectState({ diagnostics: [], activeLines: [shellDeclaration], highlightedCode: '', }) await scene.expectPixelColor([49, 49, 49], testPoint, 15) }) await test.step('Edit shell via feature tree selection works', async () => { const operationButton = await toolbar.getFeatureTreeOperation('Shell', 0) await operationButton.dblclick({ button: 'left' }) await cmdBar.expectState({ stage: 'arguments', currentArgKey: 'thickness', currentArgValue: '5', headerArguments: { Thickness: '5', }, highlightedHeaderArg: 'thickness', commandName: 'Shell', }) await page.keyboard.insertText('1') await cmdBar.progressCmdBar() await page.waitForTimeout(500) await cmdBar.expectState({ stage: 'review', headerArguments: { Thickness: '1', }, commandName: 'Shell', }) await cmdBar.progressCmdBar() await toolbar.closePane('feature-tree') await scene.expectPixelColor([150, 150, 150], testPoint, 15) await editor.expectEditor.toContain(editedShellDeclaration) await editor.expectState({ diagnostics: [], activeLines: [editedShellDeclaration], highlightedCode: '', }) }) await test.step('Delete shell via feature tree selection', async () => { await editor.closePane() const operationButton = await toolbar.getFeatureTreeOperation('Shell', 0) await operationButton.click({ button: 'left' }) await page.keyboard.press('Delete') await scene.expectPixelColor([99, 99, 99], testPoint, 15) }) }) const shellSketchOnFacesCases = [ `@settings(defaultLengthUnit = in) sketch001 = startSketchOn(XZ) |> circle(center = [0, 0], radius = 100) |> extrude(length = 100) sketch002 = startSketchOn(sketch001, face = 'END') |> circle(center = [0, 0], radius = 50) |> extrude(length = 50) `, `@settings(defaultLengthUnit = in) sketch001 = startSketchOn(XZ) |> circle(center = [0, 0], radius = 100) extrude001 = extrude(sketch001, length = 100) sketch002 = startSketchOn(extrude001, face = 'END') |> circle(center = [0, 0], radius = 50) extrude002 = extrude(sketch002, length = 50) `, ] shellSketchOnFacesCases.forEach((initialCode, index) => { const hasExtrudesInPipe = index === 0 test(`Shell point-and-click sketch on face (extrudes in pipes: ${hasExtrudesInPipe})`, async ({ context, page, homePage, scene, editor, toolbar, cmdBar, }) => { await context.addInitScript((initialCode) => { localStorage.setItem('persistCode', initialCode) }, initialCode) await page.setBodyDimensions({ width: 1200, height: 500 }) await homePage.goToModelingScene() await scene.settled(cmdBar) // One dumb hardcoded screen pixel value const testPoint = { x: 580, y: 320 } const [clickOnCap] = scene.makeMouseHelpers(testPoint.x, testPoint.y) const shellTarget = hasExtrudesInPipe ? 'sketch002' : 'extrude002' const shellDeclaration = `shell001 = shell(${shellTarget}, faces = [END], thickness = 5)` await test.step(`Look for the grey of the shape`, async () => { await scene.expectPixelColor([113, 113, 113], testPoint, 15) }) await test.step(`Go through the command bar flow, selecting a cap and keeping default thickness`, async () => { await toolbar.shellButton.click() await cmdBar.expectState({ stage: 'arguments', currentArgKey: 'selection', currentArgValue: '', headerArguments: { Selection: '', Thickness: '', }, highlightedHeaderArg: 'selection', commandName: 'Shell', }) await expect .poll(() => page.getByText('Please select one').count()) .toBe(1) await clickOnCap() await page.waitForTimeout(1000) await cmdBar.progressCmdBar() await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'review', headerArguments: { Selection: '1 cap', Thickness: '5', }, commandName: 'Shell', }) await cmdBar.progressCmdBar() }) await test.step(`Confirm code is added to the editor, scene has changed`, async () => { await toolbar.openPane('code') await editor.expectEditor.toContain(shellDeclaration) await editor.expectState({ diagnostics: [], activeLines: [shellDeclaration], highlightedCode: '', }) await toolbar.closePane('code') await scene.expectPixelColor([80, 80, 80], testPoint, 15) }) }) }) const shellPointAndClickDeletionCases = [ { shouldUseKeyboard: true }, { shouldUseKeyboard: false }, ] shellPointAndClickDeletionCases.forEach(({ shouldUseKeyboard }) => { test(`Shell point-and-click deletion (shouldUseKeyboard: ${shouldUseKeyboard})`, async ({ context, page, homePage, scene, editor, toolbar, cmdBar, }) => { const sketchCode = `sketch001 = startSketchOn(XY) profile001 = startProfile(sketch001, at = [-20, 20]) |> xLine(length = 40) |> yLine(length = -60) |> xLine(length = -40) |> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> close() ` const extrudeCode = `extrude001 = extrude(profile001, length = 40) ` const shellCode = `shell001 = shell(extrude001, faces = [END], thickness = 5) ` const initialCode = sketchCode + extrudeCode + shellCode await context.addInitScript((initialCode) => { localStorage.setItem('persistCode', initialCode) }, initialCode) await page.setBodyDimensions({ width: 1000, height: 500 }) await homePage.goToModelingScene() await scene.settled(cmdBar) await toolbar.openPane('feature-tree') // One dumb hardcoded screen pixel value const testPoint = { x: 590, y: 400 } const extrudeColor: [number, number, number] = [100, 100, 100] const sketchColor: [number, number, number] = [140, 140, 140] const defaultPlaneColor: [number, number, number] = [88, 44, 45] const deleteOperation = async (operationButton: Locator) => { if (shouldUseKeyboard) { await operationButton.click({ button: 'left' }) await page.keyboard.press('Delete') } else { await operationButton.click({ button: 'right' }) const editButton = page.getByTestId('context-menu-delete') await editButton.click() } } await test.step(`Look for the grey of the extrude shape`, async () => { await scene.expectPixelColor(extrudeColor, testPoint, 20) }) await test.step('Delete shell and confirm deletion', async () => { const operationButton = await toolbar.getFeatureTreeOperation( 'Shell', 0 ) await deleteOperation(operationButton) await scene.expectPixelColor(extrudeColor, testPoint, 20) await editor.expectEditor.not.toContain(shellCode) }) await test.step('Delete extrude and confirm deletion', async () => { const operationButton = await toolbar.getFeatureTreeOperation( 'Extrude', 0 ) await deleteOperation(operationButton) await editor.expectEditor.not.toContain(extrudeCode) await scene.expectPixelColor(sketchColor, testPoint, 20) }) await test.step('Delete sketch and confirm empty scene', async () => { const operationButton = await toolbar.getFeatureTreeOperation( 'Sketch', 0 ) await deleteOperation(operationButton) await editor.expectEditor.toContain('') // Cannot use test point anymore because the camera's position has been // reset and the rest of the test doesn't need to change just to check // if the scene is cleared. // Check that the scene is cleared await scene.expectPixelColor(defaultPlaneColor, { x: 574, y: 342 }, 20) }) }) }) test.describe('Revolve point and click workflows', () => { test('Base case workflow, auto spam continue in command bar', async ({ context, page, homePage, scene, editor, toolbar, cmdBar, }) => { const initialCode = ` sketch001 = startSketchOn(XZ) |> startProfile(at = [-100.0, 100.0]) |> angledLine(angle = 0, length = 200.0, tag = $rectangleSegmentA001) |> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 200, tag = $rectangleSegmentB001) |> angledLine( angle=segAng(rectangleSegmentA001), length=-segLen(rectangleSegmentA001), tag=$rectangleSegmentC001, ) |> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> close() extrude001 = extrude(sketch001, length = 200) sketch002 = startSketchOn(extrude001, face = rectangleSegmentA001) |> startProfile(at = [-66.77, 84.81]) |> angledLine(angle = 180, length = 27.08, tag = $rectangleSegmentA002) |> angledLine( angle=segAng(rectangleSegmentA002) - 90, length=27.8, tag=$rectangleSegmentB002, ) |> angledLine( angle=segAng(rectangleSegmentA002), length=-segLen(rectangleSegmentA002), tag=$rectangleSegmentC002, ) |> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> close() ` await context.addInitScript((initialCode) => { localStorage.setItem('persistCode', initialCode) }, initialCode) await page.setBodyDimensions({ width: 1000, height: 500 }) await homePage.goToModelingScene() await scene.settled(cmdBar) // select line of code const codeToSelection = `startProfile(at = [-66.77, 84.81])` // revolve await editor.scrollToText(codeToSelection) await page.getByText(codeToSelection).click() await toolbar.revolveButton.click() await cmdBar.progressCmdBar() await cmdBar.progressCmdBar() await cmdBar.progressCmdBar() await cmdBar.progressCmdBar() await cmdBar.progressCmdBar() const newCodeToFind = `revolve001 = revolve(sketch002, angle = 360, axis = X)` expect(editor.expectEditor.toContain(newCodeToFind)).toBeTruthy() // Edit flow const newAngle = '90' await toolbar.openPane('feature-tree') const operationButton = await toolbar.getFeatureTreeOperation( 'Revolve', 0 ) await operationButton.dblclick({ button: 'left' }) await cmdBar.expectState({ commandName: 'Revolve', currentArgKey: 'angle', currentArgValue: '360', headerArguments: { Angle: '360', }, highlightedHeaderArg: 'angle', stage: 'arguments', }) await page.keyboard.insertText(newAngle) await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'review', headerArguments: { Angle: newAngle, }, commandName: 'Revolve', }) await cmdBar.progressCmdBar() await toolbar.closePane('feature-tree') await editor.expectEditor.toContain( newCodeToFind.replace('angle = 360', 'angle = ' + newAngle) ) }) test('revolve surface around edge from an extruded solid2d', async ({ context, page, homePage, scene, editor, toolbar, cmdBar, }) => { const initialCode = `sketch001 = startSketchOn(XZ) |> startProfile(at = [-102.57, 101.72]) |> angledLine(angle = 0, length = 202.6, tag = $rectangleSegmentA001) |> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 202.6, tag = $rectangleSegmentB001) |> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001), tag = $rectangleSegmentC001) |> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> close() extrude001 = extrude(sketch001, length = 50) sketch002 = startSketchOn(extrude001, face = rectangleSegmentA001) |> circle(center = [-11.34, 10.0], radius = 8.69) ` await context.addInitScript((initialCode) => { localStorage.setItem('persistCode', initialCode) }, initialCode) await page.setBodyDimensions({ width: 1000, height: 500 }) await homePage.goToModelingScene() await scene.connectionEstablished() await scene.settled(cmdBar) // select line of code const codeToSelection = `center = [-11.34, 10.0]` // revolve await editor.scrollToText(codeToSelection) await page.getByText(codeToSelection).click() await toolbar.revolveButton.click() await cmdBar.progressCmdBar() await page.getByText('Edge', { exact: true }).click() const lineCodeToSelection = `angledLine(angle = 0, length = 202.6, tag = $rectangleSegmentA001)` await page.getByText(lineCodeToSelection).click() await cmdBar.progressCmdBar() await cmdBar.progressCmdBar() await cmdBar.progressCmdBar() const newCodeToFind = `revolve001 = revolve(sketch002, angle = 360, axis = rectangleSegmentA001)` await editor.expectEditor.toContain(newCodeToFind) // Edit flow const newAngle = '180' await toolbar.openPane('feature-tree') const operationButton = await toolbar.getFeatureTreeOperation( 'Revolve', 0 ) await operationButton.dblclick({ button: 'left' }) await cmdBar.expectState({ commandName: 'Revolve', currentArgKey: 'angle', currentArgValue: '360', headerArguments: { Angle: '360', }, highlightedHeaderArg: 'angle', stage: 'arguments', }) await page.keyboard.insertText(newAngle) await cmdBar.variableCheckbox.click() await expect(page.getByPlaceholder('Variable name')).toHaveValue( 'angle001' ) await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'review', headerArguments: { Angle: newAngle, }, commandName: 'Revolve', }) await cmdBar.progressCmdBar() await toolbar.closePane('feature-tree') await editor.expectEditor.toContain('angle001 = ' + newAngle) await editor.expectEditor.toContain( newCodeToFind.replace('angle = 360', 'angle = angle001') ) expect(editor.expectEditor.toContain(newCodeToFind)).toBeTruthy() }) test('revolve sketch circle around line segment from startProfileAt sketch', async ({ context, page, homePage, scene, editor, toolbar, cmdBar, }) => { const initialCode = ` sketch002 = startSketchOn(XY) |> startProfile(at = [-2.02, 1.79]) |> xLine(length = 2.6) sketch001 = startSketchOn(-XY) |> startProfile(at = [-0.48, 1.25]) |> angledLine(angle = 0, length = 2.38, tag = $rectangleSegmentA001) |> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 2.4, tag = $rectangleSegmentB001) |> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001), tag = $rectangleSegmentC001) |> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> close() extrude001 = extrude(sketch001, length = 5) sketch003 = startSketchOn(extrude001, face = 'START') |> circle( center = [-0.69, 0.56], radius = 0.28 ) ` await context.addInitScript((initialCode) => { localStorage.setItem('persistCode', initialCode) }, initialCode) await page.setBodyDimensions({ width: 1000, height: 500 }) await homePage.goToModelingScene() await scene.connectionEstablished() await scene.settled(cmdBar) // select line of code const codeToSelection = `center = [-0.69, 0.56]` // revolve await toolbar.revolveButton.click() await page.waitForTimeout(1000) await editor.scrollToText(codeToSelection) await page.getByText(codeToSelection).click() await cmdBar.progressCmdBar() await expect.poll(() => page.getByText('AxisOrEdge').count()).toBe(2) await page.getByText('Edge', { exact: true }).click() const lineCodeToSelection = `length = 2.6` await editor.scrollToText(lineCodeToSelection) await page.getByText(lineCodeToSelection).click() await cmdBar.progressCmdBar() await cmdBar.progressCmdBar() await cmdBar.progressCmdBar() const newCodeToFind = `revolve001 = revolve(sketch003, angle = 360, axis = seg01)` expect(editor.expectEditor.toContain(newCodeToFind)).toBeTruthy() // Edit flow const newAngle = '270' const newAngle2 = '5' const editedCodeToFind = `revolve001 = revolve(sketch003, angle = ${newAngle}, axis = seg01, bidirectionalAngle = ${newAngle2}, )` await toolbar.openPane('feature-tree') const operationButton = await toolbar.getFeatureTreeOperation( 'Revolve', 0 ) await operationButton.dblclick({ button: 'left' }) await cmdBar.expectState({ commandName: 'Revolve', currentArgKey: 'angle', currentArgValue: '360', headerArguments: { Angle: '360', }, highlightedHeaderArg: 'angle', stage: 'arguments', }) await page.keyboard.insertText(newAngle) await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'review', headerArguments: { Angle: newAngle, }, commandName: 'Revolve', }) await cmdBar.clickOptionalArgument('bidirectionalAngle') await cmdBar.expectState({ commandName: 'Revolve', currentArgKey: 'bidirectionalAngle', currentArgValue: '', headerArguments: { Angle: newAngle, BidirectionalAngle: '', }, highlightedHeaderArg: 'bidirectionalAngle', stage: 'arguments', }) await page.keyboard.insertText(newAngle2) await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'review', headerArguments: { Angle: newAngle, BidirectionalAngle: newAngle2, }, commandName: 'Revolve', }) await cmdBar.submit() await toolbar.closePane('feature-tree') await editor.expectEditor.toContain(editedCodeToFind, { shouldNormalise: true, }) }) }) test(`Set appearance`, async ({ context, page, homePage, scene, editor, toolbar, cmdBar, }) => { const initialCode = `@settings(defaultLengthUnit = in) sketch001 = startSketchOn(XZ) profile001 = circle( sketch001, center = [0, 0], radius = 100 ) extrude001 = extrude(profile001, length = 100) ` await context.addInitScript((initialCode) => { localStorage.setItem('persistCode', initialCode) }, initialCode) await page.setBodyDimensions({ width: 1000, height: 500 }) await homePage.goToModelingScene() await scene.settled(cmdBar) // One dumb hardcoded screen pixel value const testPoint = { x: 500, y: 250 } const initialColor: [number, number, number] = [123, 123, 123] const tolerance = 50 await test.step(`Confirm extrude exists with default appearance`, async () => { await toolbar.closePane('code') await scene.expectPixelColor(initialColor, testPoint, tolerance) }) async function setAppearanceAndCheck( option: string, hex: string, shapeColor?: [number, number, number] ) { await toolbar.openPane('feature-tree') const enterAppearanceFlow = async (stepName: string) => test.step(stepName, async () => { const operationButton = await toolbar.getFeatureTreeOperation( 'Extrude', 0 ) await operationButton.click({ button: 'right' }) const menuButton = page.getByTestId('context-menu-set-appearance') await menuButton.click() await cmdBar.expectState({ commandName: 'Appearance', currentArgKey: 'color', currentArgValue: '', headerArguments: { Color: '', }, highlightedHeaderArg: 'color', stage: 'arguments', }) }) await enterAppearanceFlow(`Open Set Appearance flow`) await test.step(`Validate hidden argument "nodeToEdit" can't be reached with Backspace`, async () => { await page.keyboard.press('Shift+Backspace') await cmdBar.expectState({ stage: 'pickCommand', }) await page.keyboard.press('Escape') await cmdBar.expectState({ stage: 'commandBarClosed', }) }) await enterAppearanceFlow(`Restart Appearance flow`) const item = page.getByText(option, { exact: true }) await item.click() await cmdBar.expectState({ commandName: 'Appearance', headerArguments: { Color: hex, }, stage: 'review', }) await cmdBar.progressCmdBar() await toolbar.closePane('feature-tree') if (shapeColor) { await scene.expectPixelColor(shapeColor, testPoint, tolerance) } await toolbar.openPane('code') if (hex === 'default') { const anyAppearanceDeclaration = `|> appearance(` await editor.expectEditor.not.toContain(anyAppearanceDeclaration) } else { const declaration = `|> appearance(%, color = '${hex}')` await editor.expectEditor.toContain(declaration) // TODO: fix selection range after appearance update // await editor.expectState({ // diagnostics: [], // activeLines: [declaration], // highlightedCode: '', // }) } await toolbar.closePane('code') } await test.step(`Go through the Set Appearance flow for all options`, async () => { await setAppearanceAndCheck('Red', '#FF0000', [180, 30, 30]) // Not checking the scene color every time cause that's not really deterministic. Red seems reliable though await setAppearanceAndCheck('Green', '#00FF00') await setAppearanceAndCheck('Blue', '#0000FF') await setAppearanceAndCheck('Turquoise', '#00FFFF') await setAppearanceAndCheck('Purple', '#FF00FF') await setAppearanceAndCheck('Yellow', '#FFFF00') await setAppearanceAndCheck('Black', '#000000') await setAppearanceAndCheck('Dark Grey', '#080808') await setAppearanceAndCheck('Light Grey', '#D3D3D3') await setAppearanceAndCheck('White', '#FFFFFF') await setAppearanceAndCheck( 'Default (clear appearance)', 'default', initialColor ) }) }) const translateExtrudeCases: { variables: boolean }[] = [ { variables: false, }, { variables: true, }, ] translateExtrudeCases.map(({ variables }) => { test(`Set translate on extrude through right-click menu (variables: ${variables})`, async ({ context, page, homePage, scene, editor, toolbar, cmdBar, }) => { const initialCode = `sketch001 = startSketchOn(XZ) profile001 = circle(sketch001, center = [0, 0], radius = 1) extrude001 = extrude(profile001, length = 1) ` await context.addInitScript((initialCode) => { localStorage.setItem('persistCode', initialCode) }, initialCode) await page.setBodyDimensions({ width: 1000, height: 500 }) await homePage.goToModelingScene() await scene.settled(cmdBar) // One dumb hardcoded screen pixel value const midPoint = { x: 500, y: 250 } const moreToTheRightPoint = { x: 800, y: 250 } const bgColor: [number, number, number] = [50, 50, 50] const partColor: [number, number, number] = [150, 150, 150] const tolerance = 50 await test.step('Confirm extrude exists with default appearance', async () => { await toolbar.closePane('code') await scene.expectPixelColor(partColor, midPoint, tolerance) await scene.expectPixelColor(bgColor, moreToTheRightPoint, tolerance) }) await test.step('Set translate through command bar flow', async () => { await toolbar.openPane('feature-tree') const op = await toolbar.getFeatureTreeOperation('Extrude', 0) await op.click({ button: 'right' }) await page.getByTestId('context-menu-set-translate').click() await cmdBar.expectState({ stage: 'arguments', currentArgKey: 'x', currentArgValue: '0', headerArguments: { X: '', Y: '', Z: '', }, highlightedHeaderArg: 'x', commandName: 'Translate', }) await page.keyboard.insertText('3') if (variables) { await cmdBar.createNewVariable() } await cmdBar.progressCmdBar() await page.keyboard.insertText('0.1') if (variables) { await cmdBar.createNewVariable() } await cmdBar.progressCmdBar() await page.keyboard.insertText('0.2') if (variables) { await cmdBar.createNewVariable() } await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'review', headerArguments: { X: '3', Y: '0.1', Z: '0.2', }, commandName: 'Translate', }) await cmdBar.progressCmdBar() await toolbar.closePane('feature-tree') }) await test.step('Confirm code and scene have changed', async () => { await toolbar.openPane('code') if (variables) { await editor.expectEditor.toContain( ` z001 = 0.2 y001 = 0.1 x001 = 3 sketch001 = startSketchOn(XZ) profile001 = circle(sketch001, center = [0, 0], radius = 1) extrude001 = extrude(profile001, length = 1) |> translate(x = x001, y = y001, z = z001) `, { shouldNormalise: true } ) } else { await editor.expectEditor.toContain( ` sketch001 = startSketchOn(XZ) profile001 = circle(sketch001, center = [0, 0], radius = 1) extrude001 = extrude(profile001, length = 1) |> translate(x = 3, y = 0.1, z = 0.2) `, { shouldNormalise: true } ) } await scene.expectPixelColor(bgColor, midPoint, tolerance) await scene.expectPixelColor(partColor, moreToTheRightPoint, tolerance) }) await test.step('Edit translate', async () => { await toolbar.openPane('feature-tree') const op = await toolbar.getFeatureTreeOperation('Extrude', 0) await op.click({ button: 'right' }) await page.getByTestId('context-menu-set-translate').click() await cmdBar.expectState({ stage: 'arguments', currentArgKey: 'z', currentArgValue: variables ? 'z001' : '0.2', headerArguments: { X: '3', Y: '0.1', Z: '0.2', }, highlightedHeaderArg: 'z', commandName: 'Translate', }) await page.keyboard.insertText('0.3') await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'review', headerArguments: { X: '3', Y: '0.1', Z: '0.3', }, commandName: 'Translate', }) await cmdBar.progressCmdBar() await toolbar.closePane('feature-tree') await toolbar.openPane('code') await editor.expectEditor.toContain(`z = 0.3`) // Expect almost no change in scene await scene.expectPixelColor(bgColor, midPoint, tolerance) await scene.expectPixelColor(partColor, moreToTheRightPoint, tolerance) }) }) }) const rotateExtrudeCases: { variables: boolean }[] = [ { variables: false, }, { variables: true, }, ] rotateExtrudeCases.map(({ variables }) => { test(`Set rotate on extrude through right-click menu (variables: ${variables})`, async ({ context, page, homePage, scene, editor, toolbar, cmdBar, }) => { const initialCode = `sketch001 = startSketchOn(XZ) profile001 = circle(sketch001, center = [0, 0], radius = 1) extrude001 = extrude(profile001, length = 1) ` await context.addInitScript((initialCode) => { localStorage.setItem('persistCode', initialCode) }, initialCode) await page.setBodyDimensions({ width: 1000, height: 500 }) await homePage.goToModelingScene() await scene.settled(cmdBar) await test.step('Set rotate through command bar flow', async () => { await toolbar.openPane('feature-tree') const op = await toolbar.getFeatureTreeOperation('Extrude', 0) await op.click({ button: 'right' }) await page.getByTestId('context-menu-set-rotate').click() await cmdBar.expectState({ stage: 'arguments', currentArgKey: 'roll', currentArgValue: '0', headerArguments: { Roll: '', Pitch: '', Yaw: '', }, highlightedHeaderArg: 'roll', commandName: 'Rotate', }) await page.keyboard.insertText('1.1') if (variables) { await cmdBar.createNewVariable() } await cmdBar.progressCmdBar() await page.keyboard.insertText('1.2') if (variables) { await cmdBar.createNewVariable() } await cmdBar.progressCmdBar() await page.keyboard.insertText('1.3') if (variables) { await cmdBar.createNewVariable() } await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'review', headerArguments: { Roll: '1.1', Pitch: '1.2', Yaw: '1.3', }, commandName: 'Rotate', }) await cmdBar.progressCmdBar() await toolbar.closePane('feature-tree') }) await test.step('Confirm code and scene have changed', async () => { await toolbar.openPane('code') if (variables) { await editor.expectEditor.toContain( ` yaw001 = 1.3 pitch001 = 1.2 roll001 = 1.1 sketch001 = startSketchOn(XZ) profile001 = circle(sketch001, center = [0, 0], radius = 1) extrude001 = extrude(profile001, length = 1) |> rotate(roll = roll001, pitch = pitch001, yaw = yaw001) `, { shouldNormalise: true } ) } else { await editor.expectEditor.toContain( ` sketch001 = startSketchOn(XZ) profile001 = circle(sketch001, center = [0, 0], radius = 1) extrude001 = extrude(profile001, length = 1) |> rotate(roll = 1.1, pitch = 1.2, yaw = 1.3) `, { shouldNormalise: true } ) } }) await test.step('Edit rotate', async () => { await toolbar.openPane('feature-tree') const op = await toolbar.getFeatureTreeOperation('Extrude', 0) await op.click({ button: 'right' }) await page.getByTestId('context-menu-set-rotate').click() await cmdBar.expectState({ stage: 'arguments', currentArgKey: 'yaw', currentArgValue: variables ? 'yaw001' : '1.3', headerArguments: { Roll: '1.1', Pitch: '1.2', Yaw: '1.3', }, highlightedHeaderArg: 'yaw', commandName: 'Rotate', }) await page.keyboard.insertText('13') await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'review', headerArguments: { Roll: '1.1', Pitch: '1.2', Yaw: '13', }, commandName: 'Rotate', }) await cmdBar.progressCmdBar() await toolbar.closePane('feature-tree') await toolbar.openPane('code') await editor.expectEditor.toContain(`yaw = 13`) }) }) }) test(`Set translate and rotate on extrude through selection`, async ({ context, page, homePage, scene, editor, toolbar, cmdBar, }) => { const initialCode = `sketch001 = startSketchOn(XZ) profile001 = circle(sketch001, center = [0, 0], radius = 1) extrude001 = extrude(profile001, length = 1) ` await context.addInitScript((initialCode) => { localStorage.setItem('persistCode', initialCode) }, initialCode) await page.setBodyDimensions({ width: 1000, height: 500 }) await homePage.goToModelingScene() await scene.settled(cmdBar) // One dumb hardcoded screen pixel value const midPoint = { x: 500, y: 250 } const moreToTheRightPoint = { x: 800, y: 250 } const bgColor: [number, number, number] = [50, 50, 50] const partColor: [number, number, number] = [150, 150, 150] const tolerance = 50 const [clickMidPoint] = scene.makeMouseHelpers(midPoint.x, midPoint.y) const [clickMoreToTheRightPoint] = scene.makeMouseHelpers( moreToTheRightPoint.x, moreToTheRightPoint.y ) await test.step('Confirm extrude exists with default appearance', async () => { await toolbar.closePane('code') await scene.expectPixelColor(partColor, midPoint, tolerance) await scene.expectPixelColor(bgColor, moreToTheRightPoint, tolerance) }) await test.step('Set translate through command bar flow', async () => { await cmdBar.openCmdBar() await cmdBar.chooseCommand('Translate') await cmdBar.expectState({ stage: 'arguments', currentArgKey: 'selection', currentArgValue: '', headerArguments: { Selection: '', X: '', Y: '', Z: '', }, highlightedHeaderArg: 'selection', commandName: 'Translate', }) await clickMidPoint() await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'arguments', currentArgKey: 'x', currentArgValue: '0', headerArguments: { Selection: '1 path', X: '', Y: '', Z: '', }, highlightedHeaderArg: 'x', commandName: 'Translate', }) await page.keyboard.insertText('2') await cmdBar.progressCmdBar() await cmdBar.progressCmdBar() await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'review', headerArguments: { Selection: '1 path', X: '2', Y: '0', Z: '0', }, commandName: 'Translate', }) await cmdBar.progressCmdBar() }) await test.step('Confirm code and scene have changed', async () => { await toolbar.openPane('code') await editor.expectEditor.toContain( ` sketch001 = startSketchOn(XZ) profile001 = circle(sketch001, center = [0, 0], radius = 1) extrude001 = extrude(profile001, length = 1) |> translate(x = 2, y = 0, z = 0) `, { shouldNormalise: true } ) await scene.expectPixelColor(bgColor, midPoint, tolerance) await scene.expectPixelColor(partColor, moreToTheRightPoint, tolerance) }) await test.step('Set rotate through command bar flow', async () => { // clear selection await clickMidPoint() await cmdBar.openCmdBar() await cmdBar.chooseCommand('Rotate') await cmdBar.expectState({ stage: 'arguments', currentArgKey: 'selection', currentArgValue: '', headerArguments: { Selection: '', Roll: '', Pitch: '', Yaw: '', }, highlightedHeaderArg: 'selection', commandName: 'Rotate', }) await clickMoreToTheRightPoint() await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'arguments', currentArgKey: 'roll', currentArgValue: '0', headerArguments: { Selection: '1 path', Roll: '', Pitch: '', Yaw: '', }, highlightedHeaderArg: 'roll', commandName: 'Rotate', }) await page.keyboard.insertText('0.1') await cmdBar.progressCmdBar() await page.keyboard.insertText('0.2') await cmdBar.progressCmdBar() await page.keyboard.insertText('0.3') await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'review', headerArguments: { Selection: '1 path', Roll: '0.1', Pitch: '0.2', Yaw: '0.3', }, commandName: 'Rotate', }) await cmdBar.progressCmdBar() }) await test.step('Confirm code has changed', async () => { await toolbar.openPane('code') await editor.expectEditor.toContain( ` sketch001 = startSketchOn(XZ) profile001 = circle(sketch001, center = [0, 0], radius = 1) extrude001 = extrude(profile001, length = 1) |> translate(x = 2, y = 0, z = 0) |> rotate(roll = 0.1, pitch = 0.2, yaw = 0.3) `, { shouldNormalise: true } ) // No change here since the angles are super small await scene.expectPixelColor(bgColor, midPoint, tolerance) await scene.expectPixelColor(partColor, moreToTheRightPoint, tolerance) }) }) test('Point-and-click Clone extrude through selection', async ({ context, page, homePage, scene, editor, toolbar, cmdBar, }) => { const initialCode = `sketch001 = startSketchOn(XZ) profile001 = circle(sketch001, center = [0, 0], radius = 1) extrude001 = extrude(profile001, length = 1) ` await context.addInitScript((initialCode) => { localStorage.setItem('persistCode', initialCode) }, initialCode) await page.setBodyDimensions({ width: 1000, height: 500 }) await homePage.goToModelingScene() await scene.settled(cmdBar) // One dumb hardcoded screen pixel value const midPoint = { x: 500, y: 250 } const [clickMidPoint] = scene.makeMouseHelpers(midPoint.x, midPoint.y) await test.step('Clone through command bar flow', async () => { await toolbar.closePane('code') await cmdBar.openCmdBar() await cmdBar.chooseCommand('Clone a solid or sketch') await cmdBar.expectState({ stage: 'arguments', currentArgKey: 'selection', currentArgValue: '', headerArguments: { Selection: '', VariableName: '', }, highlightedHeaderArg: 'selection', commandName: 'Clone', }) await clickMidPoint() await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'arguments', currentArgKey: 'variableName', currentArgValue: '', headerArguments: { Selection: '1 path', VariableName: '', }, highlightedHeaderArg: 'variableName', commandName: 'Clone', }) await page.keyboard.insertText('yoyoyo') await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'review', headerArguments: { Selection: '1 path', VariableName: 'yoyoyo', }, commandName: 'Clone', }) await cmdBar.progressCmdBar() // Expect changes await toolbar.openPane('code') await editor.expectEditor.toContain( ` sketch001 = startSketchOn(XZ) profile001 = circle(sketch001, center = [0, 0], radius = 1) extrude001 = extrude(profile001, length = 1) yoyoyo = clone(extrude001) `, { shouldNormalise: true } ) }) }) const multiProfileSweepsCode = `sketch001 = startSketchOn(XY) profile001 = circle(sketch001, center = [3, 0], radius = 1) profile002 = circle(sketch001, center = [6, 0], radius = 1) path001 = startProfile(sketch001, at = [0, 0]) |> yLine(length = 2) ` const profile001Point = { x: 470, y: 270 } const profile002Point = { x: 670, y: 270 } test('Point-and-click multi-profile sweeps: extrude', async ({ context, page, homePage, scene, editor, toolbar, cmdBar, }) => { await context.addInitScript((initialCode) => { localStorage.setItem('persistCode', initialCode) }, multiProfileSweepsCode) await page.setBodyDimensions({ width: 1000, height: 500 }) await homePage.goToModelingScene() await scene.settled(cmdBar) await test.step('Select through scene', async () => { // Unfortunately can't select thru code for multi profile yet const [clickProfile001Point] = scene.makeMouseHelpers( profile001Point.x, profile001Point.y ) const [clickProfile002Point] = scene.makeMouseHelpers( profile002Point.x, profile002Point.y ) await toolbar.closePane('code') await clickProfile001Point() await page.keyboard.down('Shift') await clickProfile002Point() await page.waitForTimeout(500) await page.keyboard.up('Shift') }) await test.step('Go through command bar flow', async () => { await toolbar.extrudeButton.click() await cmdBar.expectState({ stage: 'arguments', currentArgKey: 'sketches', currentArgValue: '', headerArguments: { Profiles: '', Length: '', }, highlightedHeaderArg: 'Profiles', commandName: 'Extrude', }) await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'arguments', currentArgKey: 'length', currentArgValue: '5', headerArguments: { Profiles: '2 profiles', Length: '', }, highlightedHeaderArg: 'length', commandName: 'Extrude', }) await page.keyboard.insertText('1') await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'review', headerArguments: { Profiles: '2 profiles', Length: '1', }, commandName: 'Extrude', }) await cmdBar.progressCmdBar() await scene.settled(cmdBar) await toolbar.openPane('code') await editor.expectEditor.toContain( `extrude001 = extrude([profile001, profile002], length = 1)`, { shouldNormalise: true } ) await editor.closePane() }) await test.step('Delete extrude via feature tree selection', async () => { const op = await toolbar.getFeatureTreeOperation('Extrude', 0) await op.click({ button: 'right' }) await page.getByTestId('context-menu-delete').click() await scene.settled(cmdBar) await toolbar.closePane('feature-tree') await toolbar.openPane('code') await editor.expectEditor.not.toContain( `extrude001 = extrude([profile001, profile002], length = 1)`, { shouldNormalise: true } ) }) }) test('Point-and-click multi-profile sweeps: sweep', async ({ context, page, homePage, scene, editor, toolbar, cmdBar, }) => { await context.addInitScript((initialCode) => { localStorage.setItem('persistCode', initialCode) }, multiProfileSweepsCode) await page.setBodyDimensions({ width: 1000, height: 500 }) await homePage.goToModelingScene() await scene.settled(cmdBar) await test.step('Select through scene', async () => { // Unfortunately can't select thru code for multi profile yet const [clickProfile001Point] = scene.makeMouseHelpers( profile001Point.x, profile001Point.y ) const [clickProfile002Point] = scene.makeMouseHelpers( profile002Point.x, profile002Point.y ) await toolbar.closePane('code') await clickProfile001Point() await page.keyboard.down('Shift') await clickProfile002Point() await page.waitForTimeout(500) await page.keyboard.up('Shift') }) await test.step('Go through command bar flow', async () => { await toolbar.sweepButton.click() await cmdBar.expectState({ stage: 'arguments', currentArgKey: 'sketches', currentArgValue: '', headerArguments: { Profiles: '', Path: '', }, highlightedHeaderArg: 'Profiles', commandName: 'Sweep', }) await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'arguments', currentArgKey: 'path', currentArgValue: '', headerArguments: { Profiles: '2 profiles', Path: '', }, highlightedHeaderArg: 'path', commandName: 'Sweep', }) await toolbar.openPane('code') await page.getByText('yLine(length = 2)').click() await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'review', headerArguments: { Profiles: '2 profiles', Path: '1 segment', }, commandName: 'Sweep', }) await cmdBar.progressCmdBar() await scene.settled(cmdBar) await toolbar.openPane('code') await editor.expectEditor.toContain( `sweep001 = sweep([profile001, profile002], path = path001)`, { shouldNormalise: true } ) }) await test.step('Delete sweep via feature tree selection', async () => { await editor.closePane() const op = await toolbar.getFeatureTreeOperation('Sweep', 0) await op.click({ button: 'right' }) await page.getByTestId('context-menu-delete').click() await scene.settled(cmdBar) await editor.expectEditor.not.toContain( `sweep001 = sweep([profile001, profile002], path = path001)`, { shouldNormalise: true } ) }) }) test('Point-and-click multi-profile sweeps: revolve', async ({ context, page, homePage, scene, editor, toolbar, cmdBar, }) => { await context.addInitScript((initialCode) => { localStorage.setItem('persistCode', initialCode) }, multiProfileSweepsCode) await page.setBodyDimensions({ width: 1000, height: 500 }) await homePage.goToModelingScene() await scene.settled(cmdBar) await test.step('Select through scene', async () => { // Unfortunately can't select thru code for multi profile yet const [clickProfile001Point] = scene.makeMouseHelpers( profile001Point.x, profile001Point.y ) const [clickProfile002Point] = scene.makeMouseHelpers( profile002Point.x, profile002Point.y ) await toolbar.closePane('code') await clickProfile001Point() await page.keyboard.down('Shift') await clickProfile002Point() await page.waitForTimeout(500) await page.keyboard.up('Shift') }) await test.step('Go through command bar flow', async () => { await toolbar.closePane('code') await toolbar.revolveButton.click() await cmdBar.expectState({ stage: 'arguments', currentArgKey: 'sketches', currentArgValue: '', headerArguments: { Profiles: '', AxisOrEdge: '', Angle: '', }, highlightedHeaderArg: 'Profiles', commandName: 'Revolve', }) await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'arguments', currentArgKey: 'axisOrEdge', currentArgValue: '', headerArguments: { Profiles: '2 profiles', AxisOrEdge: '', Angle: '', }, highlightedHeaderArg: 'axisOrEdge', commandName: 'Revolve', }) await cmdBar.selectOption({ name: 'Edge' }).click() await toolbar.openPane('code') await page.getByText('yLine(length = 2)').click() await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'arguments', currentArgKey: 'angle', currentArgValue: '360', headerArguments: { Profiles: '2 profiles', AxisOrEdge: 'Edge', Edge: '1 segment', Angle: '', }, highlightedHeaderArg: 'angle', commandName: 'Revolve', }) await page.keyboard.insertText('180') await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'review', headerArguments: { Profiles: '2 profiles', AxisOrEdge: 'Edge', Edge: '1 segment', Angle: '180', }, commandName: 'Revolve', }) await cmdBar.progressCmdBar() await scene.settled(cmdBar) await editor.expectEditor.toContain(`yLine(length = 2, tag = $seg01)`, { shouldNormalise: true, }) await editor.expectEditor.toContain( `revolve001 = revolve([profile001, profile002], angle=180, axis=seg01)`, { shouldNormalise: true } ) }) await test.step('Delete revolve via feature tree selection', async () => { await editor.closePane() const op = await toolbar.getFeatureTreeOperation('Revolve', 0) await op.click({ button: 'right' }) await page.getByTestId('context-menu-delete').click() await scene.settled(cmdBar) await editor.expectEditor.not.toContain( `revolve001 = revolve([profile001, profile002], axis = XY, angle = 180)`, { shouldNormalise: true } ) }) }) test(`Point and click codemods can't run on KCL errors`, async ({ context, page, homePage, scene, editor, toolbar, cmdBar, }) => { const badCode = `sketch001 = startSketchOn(XZ) profile001 = circle(sketch001, center = [0, 0], radius = 1) extrude001 = extrude(profile001 length = 1)` await context.addInitScript((initialCode) => { localStorage.setItem('persistCode', initialCode) }, badCode) await page.setBodyDimensions({ width: 1000, height: 500 }) await homePage.goToModelingScene() await scene.connectionEstablished() await test.step(`Start Sketch is disabled`, async () => { await expect(toolbar.startSketchBtn).not.toBeEnabled() await editor.expectEditor.toContain(badCode, { shouldNormalise: true }) }) await test.step(`Helix is disabled`, async () => { await expect(toolbar.helixButton).not.toBeEnabled() await editor.expectEditor.toContain(badCode, { shouldNormalise: true }) }) }) test('Point-and-click extrude with optional args', async ({ context, page, homePage, scene, editor, toolbar, cmdBar, }) => { const squareProfileCode = `length001 = 100 sketch001 = startSketchOn(XY) profile001 = startProfile(sketch001, at = [0, 0]) |> yLine(length = length001) |> xLine(length = length001) |> yLine(length = -length001) |> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> close() ` await context.addInitScript((initialCode) => { localStorage.setItem('persistCode', initialCode) }, squareProfileCode) await homePage.goToModelingScene() await scene.settled(cmdBar) await test.step('Select through code', async () => { await editor.selectText('startProfile(sketch001, at = [0, 0])') }) await test.step('Go through command bar flow', async () => { await toolbar.extrudeButton.click() await cmdBar.expectState({ stage: 'arguments', currentArgKey: 'sketches', currentArgValue: '', headerArguments: { Profiles: '', Length: '', }, highlightedHeaderArg: 'Profiles', commandName: 'Extrude', }) await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'arguments', currentArgKey: 'length', currentArgValue: '5', headerArguments: { Profiles: '1 profile', Length: '', }, highlightedHeaderArg: 'length', commandName: 'Extrude', }) await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'review', headerArguments: { Profiles: '1 profile', Length: '5', }, commandName: 'Extrude', }) await cmdBar.clickOptionalArgument('bidirectionalLength') await cmdBar.expectState({ stage: 'arguments', currentArgKey: 'bidirectionalLength', currentArgValue: '', headerArguments: { Profiles: '1 profile', Length: '5', BidirectionalLength: '', }, highlightedHeaderArg: 'bidirectionalLength', commandName: 'Extrude', }) await page.keyboard.insertText('10') await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'review', headerArguments: { Profiles: '1 profile', Length: '5', BidirectionalLength: '10', }, commandName: 'Extrude', }) await cmdBar.submit() }) await test.step('Check that the code has changed', async () => { await scene.settled(cmdBar) await editor.expectEditor.toContain( `extrude001 = extrude(profile001, length = 5, bidirectionalLength = 10)`, { shouldNormalise: true } ) }) await test.step('Go through the edit flow via feature tree', async () => { await toolbar.openPane('feature-tree') const op = await toolbar.getFeatureTreeOperation('Extrude', 0) await op.dblclick() await cmdBar.expectState({ stage: 'arguments', currentArgKey: 'length', currentArgValue: '5', headerArguments: { Length: '5', BidirectionalLength: '10', }, highlightedHeaderArg: 'length', commandName: 'Extrude', }) await page.keyboard.insertText('10') await cmdBar.progressCmdBar() await page.getByRole('button', { name: 'BidirectionalLength' }).click() await cmdBar.expectState({ stage: 'arguments', currentArgKey: 'bidirectionalLength', currentArgValue: '10', headerArguments: { Length: '10', BidirectionalLength: '10', }, highlightedHeaderArg: 'bidirectionalLength', commandName: 'Extrude', }) await page.keyboard.insertText('20') await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'review', headerArguments: { Length: '10', BidirectionalLength: '20', }, commandName: 'Extrude', }) await cmdBar.submit() }) await test.step('Check that the code has changed again', async () => { await scene.settled(cmdBar) await toolbar.closePane('feature-tree') await toolbar.openPane('code') await editor.expectEditor.toContain( `extrude001 = extrude(profile001, length = 10, bidirectionalLength = 20)`, { shouldNormalise: true } ) }) }) })