diff --git a/Makefile b/Makefile index 042daaecd9..69112d1b0f 100644 --- a/Makefile +++ b/Makefile @@ -113,6 +113,8 @@ run-desktop: install build ## Start the desktop app ############################################################################### # TEST +PW_ARGS ?= + E2E_GREP ?= E2E_WORKERS ?= E2E_FAILURES ?= 1 @@ -138,17 +140,17 @@ test-e2e: test-e2e-$(TARGET) .PHONY: test-e2e-web test-e2e-web: install build ## Run the web e2e tests ifdef E2E_GREP - npm run test:e2e:web -- --headed --grep="$(E2E_GREP)" --max-failures=$(E2E_FAILURES) + npm run test:e2e:web -- --headed --grep="$(E2E_GREP)" --max-failures=$(E2E_FAILURES) $(PW_ARGS) else - npm run test:e2e:web -- --headed --workers='100%' + npm run test:e2e:web -- --headed --workers='100%' $(PW_ARGS) endif .PHONY: test-e2e-desktop test-e2e-desktop: install build ## Run the desktop e2e tests ifdef E2E_GREP - npm run test:e2e:desktop -- --grep="$(E2E_GREP)" --max-failures=$(E2E_FAILURES) + npm run test:e2e:desktop -- --grep="$(E2E_GREP)" --max-failures=$(E2E_FAILURES) $(PW_ARGS) else - npm run test:e2e:desktop -- --workers='100%' + npm run test:e2e:desktop -- --workers='100%' $(PW_ARGS) endif .PHONY: test-snapshots diff --git a/e2e/playwright/basic-sketch.spec.ts b/e2e/playwright/basic-sketch.spec.ts deleted file mode 100644 index 944b97746f..0000000000 --- a/e2e/playwright/basic-sketch.spec.ts +++ /dev/null @@ -1,201 +0,0 @@ -import type { Page } from '@playwright/test' - -import type { HomePageFixture } from '@e2e/playwright/fixtures/homePageFixture' -import type { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture' -import type { SceneFixture } from '@e2e/playwright/fixtures/sceneFixture' -import type { EditorFixture } from '@e2e/playwright/fixtures/editorFixture' -import { - PERSIST_MODELING_CONTEXT, - TEST_COLORS, - commonPoints, - getUtils, -} from '@e2e/playwright/test-utils' -import { expect, test } from '@e2e/playwright/zoo-test' - -test.setTimeout(120000) - -async function doBasicSketch( - page: Page, - openPanes: string[], - fixtures: { - homePage: HomePageFixture - cmdBar: CmdBarFixture - scene: SceneFixture - editor: EditorFixture - } -) { - const { cmdBar, scene, homePage, editor } = fixtures - const u = await getUtils(page) - await page.setBodyDimensions({ width: 1200, height: 500 }) - const PUR = 400 / 37.5 //pixeltoUnitRatio - - await homePage.goToModelingScene() - await scene.settled(cmdBar) - await u.waitForPageLoad() - await page.waitForTimeout(1000) - await u.openDebugPanel() - - // If we have the code pane open, we should see the code. - if (openPanes.includes('code')) { - await expect(u.codeLocator).toHaveText(``) - } else { - // Ensure we don't see the code. - await expect(u.codeLocator).not.toBeVisible() - } - - await expect( - page.getByRole('button', { name: 'Start Sketch' }) - ).not.toBeDisabled() - await expect(page.getByRole('button', { name: 'Start Sketch' })).toBeVisible() - - // click on "Start Sketch" button - await u.clearCommandLogs() - await page.getByRole('button', { name: 'Start Sketch' }).click() - await page.waitForTimeout(100) - - // select a plane - await page.mouse.click(700, 200) - - if (openPanes.includes('code')) { - await expect(u.codeLocator).toHaveText(`sketch001 = startSketchOn(XZ)`) - } - await u.closeDebugPanel() - - // wait for line button to have aria-pressed as proxy for sketch mode - await expect - .poll(async () => page.getByTestId('line').getAttribute('aria-pressed'), { - timeout: 10_000, - }) - .toBe('true') - await page.waitForTimeout(200) - - const startXPx = 600 - await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10) - if (openPanes.includes('code')) { - await expect(u.codeLocator).toContainText( - `sketch001 = startSketchOn(XZ)profile001 = startProfile(sketch001, at = ${commonPoints.startAt})` - ) - } - await page.waitForTimeout(500) - await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10) - await page.waitForTimeout(500) - - if (openPanes.includes('code')) { - await expect( - u.codeLocator - ).toHaveText(`sketch001 = startSketchOn(XZ)profile001 = startProfile(sketch001, at = ${commonPoints.startAt}) - |> xLine(length = ${commonPoints.num1})`) - } - await page.waitForTimeout(500) - await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20) - if (openPanes.includes('code')) { - await expect( - u.codeLocator - ).toHaveText(`sketch001 = startSketchOn(XZ)profile001 = startProfile(sketch001, at = ${ - commonPoints.startAt - }) - |> xLine(length = ${commonPoints.num1}) - |> yLine(length = ${commonPoints.num1 + 0.01})`) - } else { - await page.waitForTimeout(500) - } - await page.waitForTimeout(200) - await page.mouse.click(startXPx, 500 - PUR * 20) - if (openPanes.includes('code')) { - await expect( - u.codeLocator - ).toHaveText(`@settings(defaultLengthUnit = in)sketch001 = startSketchOn(XZ)profile001 = startProfile(sketch001, ${ - commonPoints.startAt - }) - |> xLine(length = ${commonPoints.num1}) - |> yLine(length = ${commonPoints.num1 + 0.01}) - |> xLine(length = ${commonPoints.num2 * -1})`) - } - - // deselect line tool - const btnLine = page.getByTestId('line') - const btnLineAriaPressed = await btnLine.getAttribute('aria-pressed') - if (btnLineAriaPressed === 'true') { - await btnLine.click() - } - - await page.waitForTimeout(100) - - const line1 = await u.getSegmentBodyCoords(`[data-overlay-index="${0}"]`, 0) - if (openPanes.includes('code')) { - await expect - .poll(async () => u.getGreatestPixDiff(line1, TEST_COLORS.WHITE)) - .toBeLessThan(3) - await page.waitForTimeout(100) - await expect - .poll(async () => u.getGreatestPixDiff(line1, [249, 249, 249])) - .toBeLessThan(3) - await page.waitForTimeout(100) - } - - // click between first two clicks to get center of the line - await page.mouse.click(startXPx + PUR * 15, 500 - PUR * 10) - await page.waitForTimeout(100) - - if (openPanes.includes('code')) { - expect(await u.getGreatestPixDiff(line1, TEST_COLORS.BLUE)).toBeLessThan(3) - } - - // hold down shift - await page.keyboard.down('Shift') - await page.waitForTimeout(100) - - // click between the latest two clicks to get center of the line - await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 20) - await page.waitForTimeout(100) - - // selected two lines therefore there should be two cursors - if (openPanes.includes('code')) { - await expect(page.locator('.cm-cursor')).toHaveCount(2) - await page.waitForTimeout(100) - } - - await page.getByRole('button', { name: 'constraints: open menu' }).click() - await page.getByRole('button', { name: 'Equal Length' }).click() - - // Open the code pane. - await u.openKclCodePanel() - await editor.expectEditor.toContain( - `@settings(defaultLengthUnit = in)sketch001 = startSketchOn(XZ)profile001 = startProfile(sketch001, at = ${ - commonPoints.startAt - }) - |> xLine(length = ${commonPoints.num1}, tag = $seg01) - |> yLine(length = ${commonPoints.num1 + 0.01}) - |> xLine(length = -segLen(seg01))`, - { shouldNormalise: true } - ) -} - -test.describe('Basic sketch', () => { - test('code pane open at start', async ({ - page, - homePage, - cmdBar, - scene, - editor, - }) => { - await doBasicSketch(page, ['code'], { cmdBar, scene, homePage, editor }) - }) - - test('code pane closed at start', async ({ - page, - homePage, - cmdBar, - scene, - editor, - }) => { - // Load the app with the code panes - await page.addInitScript(async (persistModelingContext) => { - localStorage.setItem( - persistModelingContext, - JSON.stringify({ openPanes: [] }) - ) - }, PERSIST_MODELING_CONTEXT) - await doBasicSketch(page, [], { cmdBar, scene, homePage, editor }) - }) -}) diff --git a/e2e/playwright/editor-tests.spec.ts b/e2e/playwright/editor-tests.spec.ts index 03ae5494d5..64af062ff2 100644 --- a/e2e/playwright/editor-tests.spec.ts +++ b/e2e/playwright/editor-tests.spec.ts @@ -625,6 +625,11 @@ a1 = startSketchOn(offsetPlane(XY, offset = 10)) await scene.connectionEstablished() + // Wait for highlighting to kick in, a good proxy that the LSP is ready. + await expect + .poll(() => page.evaluate(() => document.querySelector('.ͼu') !== null)) + .toBe(true) + // Expect the signature help to NOT be visible await expect(page.locator('.cm-signature-tooltip')).not.toBeVisible() @@ -1177,107 +1182,47 @@ sketch001 = startSketchOn(XZ) editor, scene, cmdBar, + toolbar, }) => { - const u = await getUtils(page) - await page.addInitScript(async () => { - localStorage.setItem( - 'persistCode', - `@settings(defaultLengthUnit=in) -sketch001 = startSketchOn(XZ) - |> startProfile(at = [4.61, -10.01]) - |> line(end = [12.73, -0.09]) - |> tangentialArc(endAbsolute = [24.95, -0.38]) - |> close() - |> extrude(length = 5)` - ) - }) - - await page.setBodyDimensions({ width: 1200, height: 500 }) + const ogCode = `sketch001 = startSketchOn(XZ) +profile001 = startProfile(sketch001, at = [0, 0]) + |> xLine(length = 10) + |> yLine(length = 10) + |> xLine(length = -10) + |> line(endAbsolute = [profileStartX(%), profileStartY(%)]) + |> close()` + await page.addInitScript(async (code) => { + localStorage.setItem('persistCode', code) + }, ogCode) await homePage.goToModelingScene() await scene.settled(cmdBar) - await page.waitForTimeout(100) - await u.openAndClearDebugPanel() - await u.sendCustomCmd({ - type: 'modeling_cmd_req', - cmd_id: uuidv4(), - cmd: { - type: 'default_camera_look_at', - vantage: { x: 0, y: -1250, z: 580 }, - center: { x: 0, y: 0, z: 0 }, - up: { x: 0, y: 0, z: 1 }, - }, - }) - await page.waitForTimeout(100) - await u.sendCustomCmd({ - type: 'modeling_cmd_req', - cmd_id: uuidv4(), - cmd: { - type: 'default_camera_get_settings', - }, - }) - await page.waitForTimeout(100) - - const startPX = [1200 / 2, 500 / 2] - - const dragPX = 40 - - await page.getByText('startProfile(at = [4.61, -10.01])').click() - await expect( - page.getByRole('button', { name: 'Edit Sketch' }) - ).toBeVisible() - await page.getByRole('button', { name: 'Edit Sketch' }).click() - await page.waitForTimeout(400) - let prevContent = await page.locator('.cm-content').innerText() - - await expect(page.getByTestId('segment-overlay')).toHaveCount(3) - - // drag startProfileAt handle - await page.dragAndDrop('#stream', '#stream', { - sourcePosition: { x: startPX[0] + 68, y: startPX[1] + 147 }, - targetPosition: { x: startPX[0] + dragPX, y: startPX[1] + dragPX }, - }) - await page.waitForTimeout(100) - await expect(page.locator('.cm-content')).not.toHaveText(prevContent) - prevContent = await page.locator('.cm-content').innerText() + let prevContent = await editor.getCurrentCode() + await toolbar.editSketch() - // drag line handle - // we wait so it saves the code - await page.waitForTimeout(800) + // first sketch modification + await editor.selectText( + 'line(endAbsolute = [profileStartX(%), profileStartY(%)])' + ) + await editor.closePane() + await page.keyboard.press('Delete') + await editor.expectEditor.not.toContain(prevContent) + prevContent = await editor.getCurrentCode() - const lineEnd = await u.getBoundingBox('[data-overlay-index="0"]') - await page.waitForTimeout(100) - await page.dragAndDrop('#stream', '#stream', { - sourcePosition: { x: lineEnd.x - 5, y: lineEnd.y }, - targetPosition: { x: lineEnd.x + dragPX, y: lineEnd.y + dragPX }, - }) - await expect(page.locator('.cm-content')).not.toHaveText(prevContent) - prevContent = await page.locator('.cm-content').innerText() - - // we wait so it saves the code - await page.waitForTimeout(800) - - // drag tangentialArc handle - const tangentEnd = await u.getBoundingBox('[data-overlay-index="1"]') - await page.dragAndDrop('#stream', '#stream', { - sourcePosition: { x: tangentEnd.x + 10, y: tangentEnd.y - 5 }, - targetPosition: { - x: tangentEnd.x + dragPX, - y: tangentEnd.y + dragPX, - }, - }) - await page.waitForTimeout(100) - await expect(page.locator('.cm-content')).not.toHaveText(prevContent) + // second sketch modification + await editor.selectText('xLine(length = -10)') + await editor.closePane() + await page.keyboard.press('Delete') + await editor.expectEditor.not.toContain(prevContent) // expect the code to have changed await editor.expectEditor.toContain( `sketch001 = startSketchOn(XZ) - |> startProfile(at = [5.36, -5.36]) - |> line(end = [12.73, -0.09]) - |> tangentialArc(endAbsolute = [24.95, -0.38]) - |> close() - |> extrude(length = 5)`, +profile001 = startProfile(sketch001, at = [0, 0]) + |> xLine(length = 10) + |> yLine(length = 10) + |> close()`, { shouldNormalise: true } ) @@ -1286,46 +1231,25 @@ sketch001 = startSketchOn(XZ) await page.keyboard.press('KeyZ') await page.keyboard.up('Control') + await editor.openPane() await editor.expectEditor.toContain( `sketch001 = startSketchOn(XZ) - |> startProfile(at = [2.71, -2.71]) - |> line(end = [12.73, -0.09]) - |> tangentialArc(endAbsolute = [24.95, -0.38]) - |> close() - |> extrude(length = 5)`, +profile001 = startProfile(sketch001, at = [0, 0]) + |> xLine(length = 10) + |> yLine(length = 10) + |> xLine(length = -10) + |> close()`, { shouldNormalise: true } ) // Hit undo again. + await editor.closePane() await page.keyboard.down('Control') await page.keyboard.press('KeyZ') await page.keyboard.up('Control') - await editor.expectEditor.toContain( - `sketch001 = startSketchOn(XZ) - |> startProfile(at = [4.61, -10.01]) - |> line(end = [12.73, -0.09]) - |> tangentialArc(endAbsolute = [24.95, -0.38]) - |> close() - |> extrude(length = 5)`, - { shouldNormalise: true } - ) - - // Hit undo again. - await page.keyboard.down('Control') - await page.keyboard.press('KeyZ') - await page.keyboard.up('Control') - - await page.waitForTimeout(100) - await editor.expectEditor.toContain( - `sketch001 = startSketchOn(XZ) - |> startProfile(at = [4.61, -10.01]) - |> line(end = [12.73, -0.09]) - |> tangentialArc(endAbsolute = [24.95, -0.38]) - |> close() - |> extrude(length = 5)`, - { shouldNormalise: true } - ) + await editor.openPane() + await editor.expectEditor.toContain(ogCode, { shouldNormalise: true }) }) test( diff --git a/e2e/playwright/fixtures/sceneFixture.ts b/e2e/playwright/fixtures/sceneFixture.ts index b75f500aab..fd2556908c 100644 --- a/e2e/playwright/fixtures/sceneFixture.ts +++ b/e2e/playwright/fixtures/sceneFixture.ts @@ -85,7 +85,8 @@ export class SceneFixture { convertPagePositionToStream = async ( x: number, y: number, - format: 'pixels' | 'ratio' | undefined = 'pixels' + format: 'pixels' | 'ratio' | undefined = 'pixels', + relativeToStream = false ) => { const viewportSize = this.page.viewportSize() const streamBoundingBox = await this.streamWrapper.boundingBox() @@ -99,11 +100,11 @@ export class SceneFixture { const resolvedX = (x / (format === 'pixels' ? viewportSize.width : 1)) * streamBoundingBox.width + - streamBoundingBox.x + (relativeToStream ? 0 : streamBoundingBox.x) const resolvedY = (y / (format === 'pixels' ? viewportSize.height : 1)) * streamBoundingBox.height + - streamBoundingBox.y + (relativeToStream ? 0 : streamBoundingBox.y) const resolvedPoint = { x: Math.round(resolvedX), @@ -203,12 +204,14 @@ export class SceneFixture { const resolvedToPoint = await this.convertPagePositionToStream( x, y, - format + format, + true ) const resolvedFromPoint = await this.convertPagePositionToStream( dragToParams.fromPoint.x, dragToParams.fromPoint.y, - format + format, + true ) if (debug) { console.log({ @@ -239,12 +242,14 @@ export class SceneFixture { const resolvedFromPoint = await this.convertPagePositionToStream( x, y, - format + format, + true ) const resolvedToPoint = await this.convertPagePositionToStream( dragFromParams.toPoint.x, dragFromParams.toPoint.y, - format + format, + true ) if (debug) { console.log({ diff --git a/e2e/playwright/point-click.spec.ts b/e2e/playwright/point-click.spec.ts index bc1b1f33a1..1b5e1c8c14 100644 --- a/e2e/playwright/point-click.spec.ts +++ b/e2e/playwright/point-click.spec.ts @@ -1419,27 +1419,13 @@ extrude001 = extrude(sketch001, length = -12) const secondFilletDeclaration = `fillet(radius=5,tags=[getCommonEdge(faces=[seg01,capStart001])],)` // Locators - const firstEdgeLocation = { x: 600, y: 193 } + // TODO: find a way to not have hardcoded pixel values for sweepEdges 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) => { @@ -1447,30 +1433,13 @@ extrude001 = extrude(sketch001, length = -12) }, 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) + 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 editor.selectText( + 'line(endAbsolute = [profileStartX(%), profileStartY(%)])' ) }) @@ -1523,10 +1492,6 @@ extrude001 = extrude(sketch001, length = -12) }) }) - 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, @@ -1599,17 +1564,7 @@ extrude001 = extrude(sketch001, length = -12) }) 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 () => { @@ -1659,14 +1614,6 @@ extrude001 = extrude(sketch001, length = -12) }) }) - 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 @@ -1701,9 +1648,7 @@ extrude001 = extrude(sketch001, length = -12) 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 }) }) }) @@ -1798,19 +1743,6 @@ fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg02)]) 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) => { @@ -1819,16 +1751,6 @@ fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg02)]) 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 @@ -1849,18 +1771,6 @@ fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg02)]) 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', @@ -1880,18 +1790,6 @@ fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg02)]) 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 () => { @@ -1913,13 +1811,6 @@ fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg02)]) 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 () => { @@ -1938,13 +1829,6 @@ fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg02)]) standaloneUnassignedFilletDeclaration ) }) - await test.step('Verify standalone unassigned fillet is deleted but piped is not (in the scene)', async () => { - await scene.expectPixelColor( - edgeColorWhite, - standaloneFilletEdgeLocation, - lowTolerance - ) - }) }) }) }) @@ -1973,16 +1857,8 @@ extrude001 = extrude(profile001, length = 5) 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 + // TODO: find a way to select sweepEdges in a different way + const edgeLocation = { x: 649, y: 283 } // Setup await test.step(`Initial test setup`, async () => { @@ -1991,28 +1867,18 @@ extrude001 = extrude(profile001, length = 5) }, 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) + await scene.settled(cmdBar) }) // Test await test.step('Select edges and apply oversized fillet', async () => { await test.step(`Select the edge`, async () => { - await scene.expectPixelColor(edgeColorWhite, edgeLocation, lowTolerance) + await toolbar.closePane('code') 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 () => { diff --git a/e2e/playwright/projects.spec.ts b/e2e/playwright/projects.spec.ts index 4ba871d526..797380c8d8 100644 --- a/e2e/playwright/projects.spec.ts +++ b/e2e/playwright/projects.spec.ts @@ -293,7 +293,9 @@ test( test( 'when code with error first loads you get errors in console', - { tag: '@desktop' }, + { + tag: '@desktop', + }, async ({ context, page, editor }, testInfo) => { await context.folderSetupFn(async (dir) => { await fsp.mkdir(path.join(dir, 'broken-code'), { recursive: true }) @@ -324,7 +326,9 @@ test( test( 'Rename and delete projects, also spam arrow keys when renaming', - { tag: '@desktop' }, + { + tag: '@desktop', + }, async ({ context, page }, testInfo) => { await context.folderSetupFn(async (dir) => { await fsp.mkdir(`${dir}/router-template-slate`, { recursive: true }) @@ -540,7 +544,9 @@ test( test( 'pressing "delete" on home screen should do nothing', - { tag: '@desktop' }, + { + tag: '@desktop', + }, async ({ context, page, homePage }, testInfo) => { await context.folderSetupFn(async (dir) => { await fsp.mkdir(`${dir}/router-template-slate`, { recursive: true }) @@ -911,7 +917,9 @@ test( test( 'Nested directories in project without main.kcl do not create main.kcl', - { tag: '@desktop' }, + { + tag: '@desktop', + }, async ({ scene, cmdBar, context, page }, testInfo) => { let testDir: string | undefined await context.folderSetupFn(async (dir) => { @@ -968,7 +976,9 @@ test( test( 'Deleting projects, can delete individual project, can still create projects after deleting all', - { tag: '@desktop' }, + { + tag: '@desktop', + }, async ({ context, page }, testInfo) => { const projectData = [ ['router-template-slate', 'cylinder.kcl'], @@ -1046,7 +1056,9 @@ test( test( 'Can load a file with CRLF line endings', - { tag: '@desktop' }, + { + tag: '@desktop', + }, async ({ context, page, scene, cmdBar }, testInfo) => { await context.folderSetupFn(async (dir) => { const routerTemplateDir = path.join(dir, 'router-template-slate') @@ -1078,7 +1090,9 @@ test( test( 'Can sort projects on home page', - { tag: '@desktop' }, + { + tag: '@desktop', + }, async ({ context, page }, testInfo) => { const projectData = [ ['router-template-slate', 'cylinder.kcl'], @@ -1185,7 +1199,9 @@ test( test( 'When the project folder is empty, user can create new project and open it.', - { tag: '@desktop' }, + { + tag: '@desktop', + }, async ({ page }, testInfo) => { const u = await getUtils(page) await page.setBodyDimensions({ width: 1200, height: 500 }) @@ -1281,7 +1297,9 @@ extrude001 = extrude(sketch001, length = 200)`) test( 'You can change the root projects directory and nothing is lost', - { tag: '@desktop' }, + { + tag: '@desktop', + }, async ({ context, page, tronApp, homePage }, testInfo) => { if (!tronApp) { fail() @@ -1388,7 +1406,9 @@ test( test( 'Search projects on desktop home', - { tag: '@desktop' }, + { + tag: '@desktop', + }, async ({ context, page }, testInfo) => { const projectData = [ ['basic bracket', 'focusrite_scarlett_mounting_bracket.kcl'], @@ -1444,7 +1464,9 @@ test( test( 'file pane is scrollable when there are many files', - { tag: '@desktop' }, + { + tag: '@desktop', + }, async ({ scene, cmdBar, context, page }, testInfo) => { await context.folderSetupFn(async (dir) => { const testDir = path.join(dir, 'testProject') @@ -1546,7 +1568,9 @@ test( test( 'select all in code editor does not actually select all, just what is visible (regression)', - { tag: '@desktop' }, + { + tag: '@desktop', + }, async ({ context, page }, testInfo) => { await context.folderSetupFn(async (dir) => { // rust/kcl-lib/e2e/executor/inputs/mike_stress_test.kcl @@ -1604,7 +1628,9 @@ test( test( 'Settings persist across restarts', - { tag: '@desktop' }, + { + tag: '@desktop', + }, async ({ page, toolbar }, testInfo) => { await test.step('We can change a user setting like theme', async () => { await page.setBodyDimensions({ width: 1200, height: 500 }) @@ -1636,7 +1662,9 @@ test( test( 'Original project name persist after onboarding', - { tag: '@desktop' }, + { + tag: '@desktop', + }, async ({ page, toolbar }, testInfo) => { const nextButton = page.getByTestId('onboarding-next') await page.setBodyDimensions({ width: 1200, height: 500 }) diff --git a/e2e/playwright/regression-tests.spec.ts b/e2e/playwright/regression-tests.spec.ts index 126326e900..51e404211b 100644 --- a/e2e/playwright/regression-tests.spec.ts +++ b/e2e/playwright/regression-tests.spec.ts @@ -816,12 +816,12 @@ washer = extrude(washerSketch, length = thicknessMax)` await circleCenterClick() // this number will be different if the scale is not set correctly for inches await editor.expectEditor.toContain( - 'circle(sketch001, center = [0.06, -0.06]' + 'circle(sketch001, center = [0.04, -0.06]' ) await circleRadiusClick() await editor.expectEditor.toContain( - 'circle(sketch001, center = [0.06, -0.06], radius = 0.18' + 'circle(sketch001, center = [0.04, -0.06], radius = 0.12' ) }) diff --git a/e2e/playwright/sketch-tests.spec.ts b/e2e/playwright/sketch-tests.spec.ts index 7fcb82ffed..121f6f4a03 100644 --- a/e2e/playwright/sketch-tests.spec.ts +++ b/e2e/playwright/sketch-tests.spec.ts @@ -97,74 +97,6 @@ test.describe('Sketch tests', () => { page.getByRole('button', { name: 'Edit Sketch' }) ).toBeVisible() }) - test('Can delete most of a sketch and the line tool will still work', async ({ - page, - scene, - homePage, - cmdBar, - }) => { - const u = await getUtils(page) - await page.addInitScript(async () => { - localStorage.setItem( - 'persistCode', - `@settings(defaultLengthUnit = in) -sketch001 = startSketchOn(XZ) - |> startProfile(at = [2.61, -4.01]) - |> xLine(length = 8.73) - |> tangentialArc(endAbsolute = [8.33, -1.31])` - ) - }) - - await homePage.goToModelingScene() - await scene.settled(cmdBar) - - await scene.expectPixelColor(TEST_COLORS.WHITE, { x: 587, y: 270 }, 15) - - await expect(async () => { - await page.mouse.click(700, 200) - await page.getByText('tangentialArc(endAbsolute = [8.33, -1.31])').click() - await expect( - page.getByRole('button', { name: 'Edit Sketch' }) - ).toBeEnabled({ timeout: 2000 }) - await page.getByRole('button', { name: 'Edit Sketch' }).click() - }).toPass({ timeout: 40_000, intervals: [1_000] }) - - await page.waitForTimeout(600) // wait for animation - - await page.getByText('tangentialArc(endAbsolute = [8.33, -1.31])').click() - await page.keyboard.press('End') - await page.keyboard.down('Shift') - await page.keyboard.press('ArrowUp') - await page.keyboard.press('Home') - await page.keyboard.up('Shift') - await page.keyboard.press('Backspace') - await u.openDebugPanel() - await u.expectCmdLog('[data-message-type="execution-done"]', 10_000) - await page.waitForTimeout(100) - - await page.getByRole('button', { name: 'line Line', exact: true }).click() - await page.waitForTimeout(500) - // click start profileAt handle to continue profile - await page.mouse.click(702, 406, { delay: 500 }) - await page.waitForTimeout(100) - await page.mouse.move(800, 150) - - await expect(async () => { - // click to add segment - await page.mouse.click(700, 200) - - await expect - .poll(u.normalisedEditorCode, { timeout: 1000 }) - .toBe(`@settings(defaultLengthUnit = in) - - -sketch002 = startSketchOn(XZ) -sketch001 = startProfile(sketch002, at = [12.34, -12.34]) - |> yLine(length = 12.34) - -`) - }).toPass({ timeout: 5_000, intervals: [1_000] }) - }) test('Can exit selection of face', async ({ page, homePage }) => { // Load the app with the code panes @@ -930,10 +862,10 @@ sketch001 = startSketchOn(XZ) const code = `@settings(defaultLengthUnit = in) sketch001 = startSketchOn(-XZ) -profile001 = startProfile(sketch001, at = [${roundOff(scale * 69.6)}, ${roundOff( +profile001 = startProfile(sketch001, at = [${roundOff(scale * 77.11)}, ${roundOff( scale * 34.8 )}]) - |> xLine(length = ${roundOff(scale * 139.19)}) + |> xLine(length = ${roundOff(scale * 154.22)}) |> yLine(length = -${roundOff(scale * 139.2)}) |> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> close()` @@ -1503,20 +1435,17 @@ sketch001 = startSketchOn(XZ) await homePage.goToModelingScene() await toolbar.waitForFeatureTreeToBeBuilt() await scene.settled(cmdBar) - - await (await toolbar.getFeatureTreeOperation('Sketch', 0)).dblclick() - - await page.waitForTimeout(1000) - - await page.mouse.move(1200, 139) - await page.mouse.down() - await page.mouse.move(870, 250) - await page.mouse.up() + await toolbar.editSketch() + const [dragToDifferentPoint] = scene.makeDragHelpers(1000, 177, { + debug: true, + }) + await dragToDifferentPoint({ + fromPoint: { x: 1400, y: 177 }, + }) await page.waitForTimeout(200) - await editor.expectEditor.toContain( - `tangentialArc(angle = 234.01deg, radius = 4.08)`, + `tangentialArc(angle = 187.46deg, radius = 3.97)`, { shouldNormalise: true } ) }) @@ -1527,12 +1456,8 @@ sketch001 = startSketchOn(XZ) homePage, cmdBar, editor, + toolbar, }) => { - const u = await getUtils(page) - - const viewportSize = { width: 1100, height: 750 } - await page.setBodyDimensions(viewportSize) - await page.addInitScript(async () => { localStorage.setItem( 'persistCode', @@ -1550,21 +1475,15 @@ profile001 = startProfile(sketch001, at = [0, 0]) await homePage.goToModelingScene() await scene.settled(cmdBar) - // Enter sketch mode - await page.mouse.dblclick(654, 450) - await page.waitForTimeout(1000) + await toolbar.editSketch() // Select the third line - await page.mouse.click(918, 90) - await page.waitForTimeout(1000) + await editor.selectText('line(end = [-22.0, 12.0])') + await editor.closePane() // Delete with backspace await page.keyboard.press('Delete') - // Wait for engine re-execution to complete - await u.openDebugPanel() - await u.expectCmdLog('[data-message-type="execution-done"]', 10_000) - // Validate the editor code no longer contains the deleted line await editor.expectEditor.toContain( `sketch001 = startSketchOn(XZ) @@ -1845,49 +1764,55 @@ tangentialArc(end = [-10.82, 144.95])` scene, toolbar, editor, + cmdBar, page, homePage, }) => { - await page.setBodyDimensions({ width: 1000, height: 500 }) - await homePage.goToModelingScene() - await scene.connectionEstablished() - await expect( - page.getByRole('button', { name: 'Start Sketch' }) - ).not.toBeDisabled() - - const [selectXZPlane] = scene.makeMouseHelpers(650, 150) - - await toolbar.startSketchPlaneSelection() - await selectXZPlane() - // timeout wait for engine animation is unavoidable - await page.waitForTimeout(600) - await editor.expectEditor.toContain(`sketch001 = startSketchOn(XZ)`) - - const [startProfile1] = scene.makeMouseHelpers(568, 110) - const [segment1Clk] = scene.makeMouseHelpers(701, 118) - const [segment2Clk] = scene.makeMouseHelpers(745, 189) - - await test.step('add two segments', async () => { - await startProfile1() - await editor.expectEditor.toContain( - `profile001 = startProfile(sketch001, at = [4.61, 9.49])` + await page.addInitScript(async () => { + localStorage.setItem( + 'persistCode', + `sketch001 = startSketchOn(XZ) +profile001 = startProfile(sketch001, at=[0, 0]) + |> angledLine(angle=45deg, length=1in) + |> angledLine(angle=180deg, length=0.5in) +` ) - await segment1Clk() - await editor.expectEditor.toContain(`|> line(end`) - await segment2Clk() - await editor.expectEditor.toContain(`|> line(end = [2.98, -4.81])`) }) + await homePage.goToModelingScene() + await scene.settled(cmdBar) + await toolbar.editSketch(0) await test.step('delete all profiles', async () => { await editor.replaceCode('', 'sketch001 = startSketchOn(XZ)\n') - await page.waitForTimeout(600) // wait for deferred execution + }) + + await test.step('wait for execution', async () => { + // TODO: there is a gap between deleting the code and the re-execution during which + // there seems to be no signal to the system that we are in a "dirty" state awaiting re-execution. + // Need a better signal to the system (and by extension Playwright) that a re-execution is coming, + // because if the user (or test) equips a new tool and draws with it in this state, the tool will + // be unequipped and the code will be half-reset when execution completes. + // await expect(toolbar.exitSketchBtn).toBeDisabled() + // await expect(toolbar.exitSketchBtn).toBeEnabled() + // TODO: the trick above doesn't seem to work anymore, still need a better signal + await page.waitForTimeout(2000) }) await test.step('equip circle and draw it', async () => { await toolbar.circleBtn.click() - await page.mouse.click(700, 200) - await page.mouse.click(750, 200) - await editor.expectEditor.toContain('circle(sketch001, center = [') + const [circleCenterClick] = scene.makeMouseHelpers(0.5, 0.5, { + format: 'ratio', + }) + const [circlePerimeterClick] = scene.makeMouseHelpers(0.75, 0.75, { + format: 'ratio', + }) + await expect(toolbar.circleBtn).toHaveAttribute('aria-pressed', 'true') + await circleCenterClick() + await circlePerimeterClick() + await editor.expectEditor.not.toContain('profile001 = angledLine(') + await editor.expectEditor.toContain( + 'profile001 = circle(sketch001, center = [' + ) }) }) test('Can add multiple profiles to a sketch (all tool types)', async ({ diff --git a/e2e/playwright/snapshot-tests.spec.ts-snapshots/Client-side-scene-scale-should-match-engine-scale-Inch-scale-1-Google-Chrome-linux.png b/e2e/playwright/snapshot-tests.spec.ts-snapshots/Client-side-scene-scale-should-match-engine-scale-Inch-scale-1-Google-Chrome-linux.png index 934bb3a82a..d5f8994ef4 100644 Binary files a/e2e/playwright/snapshot-tests.spec.ts-snapshots/Client-side-scene-scale-should-match-engine-scale-Inch-scale-1-Google-Chrome-linux.png and b/e2e/playwright/snapshot-tests.spec.ts-snapshots/Client-side-scene-scale-should-match-engine-scale-Inch-scale-1-Google-Chrome-linux.png differ diff --git a/e2e/playwright/snapshot-tests.spec.ts-snapshots/Client-side-scene-scale-should-match-engine-scale-Inch-scale-2-Google-Chrome-linux.png b/e2e/playwright/snapshot-tests.spec.ts-snapshots/Client-side-scene-scale-should-match-engine-scale-Inch-scale-2-Google-Chrome-linux.png index f4338306ae..9328c6a5a9 100644 Binary files a/e2e/playwright/snapshot-tests.spec.ts-snapshots/Client-side-scene-scale-should-match-engine-scale-Inch-scale-2-Google-Chrome-linux.png and b/e2e/playwright/snapshot-tests.spec.ts-snapshots/Client-side-scene-scale-should-match-engine-scale-Inch-scale-2-Google-Chrome-linux.png differ diff --git a/e2e/playwright/snapshot-tests.spec.ts-snapshots/Client-side-scene-scale-should-match-engine-scale-Millimeter-scale-1-Google-Chrome-linux.png b/e2e/playwright/snapshot-tests.spec.ts-snapshots/Client-side-scene-scale-should-match-engine-scale-Millimeter-scale-1-Google-Chrome-linux.png index 449b960344..78357ca48f 100644 Binary files a/e2e/playwright/snapshot-tests.spec.ts-snapshots/Client-side-scene-scale-should-match-engine-scale-Millimeter-scale-1-Google-Chrome-linux.png and b/e2e/playwright/snapshot-tests.spec.ts-snapshots/Client-side-scene-scale-should-match-engine-scale-Millimeter-scale-1-Google-Chrome-linux.png differ diff --git a/e2e/playwright/snapshot-tests.spec.ts-snapshots/Client-side-scene-scale-should-match-engine-scale-Millimeter-scale-2-Google-Chrome-linux.png b/e2e/playwright/snapshot-tests.spec.ts-snapshots/Client-side-scene-scale-should-match-engine-scale-Millimeter-scale-2-Google-Chrome-linux.png index f4338306ae..ee6fc7bc01 100644 Binary files a/e2e/playwright/snapshot-tests.spec.ts-snapshots/Client-side-scene-scale-should-match-engine-scale-Millimeter-scale-2-Google-Chrome-linux.png and b/e2e/playwright/snapshot-tests.spec.ts-snapshots/Client-side-scene-scale-should-match-engine-scale-Millimeter-scale-2-Google-Chrome-linux.png differ diff --git a/e2e/playwright/snapshot-tests.spec.ts-snapshots/Draft-circle-should-look-right-1-Google-Chrome-linux.png b/e2e/playwright/snapshot-tests.spec.ts-snapshots/Draft-circle-should-look-right-1-Google-Chrome-linux.png index e868bce543..ab8af3551e 100644 Binary files a/e2e/playwright/snapshot-tests.spec.ts-snapshots/Draft-circle-should-look-right-1-Google-Chrome-linux.png and b/e2e/playwright/snapshot-tests.spec.ts-snapshots/Draft-circle-should-look-right-1-Google-Chrome-linux.png differ diff --git a/e2e/playwright/snapshot-tests.spec.ts-snapshots/Draft-rectangles-should-look-right-1-Google-Chrome-linux.png b/e2e/playwright/snapshot-tests.spec.ts-snapshots/Draft-rectangles-should-look-right-1-Google-Chrome-linux.png index bc2944594f..870df8829c 100644 Binary files a/e2e/playwright/snapshot-tests.spec.ts-snapshots/Draft-rectangles-should-look-right-1-Google-Chrome-linux.png and b/e2e/playwright/snapshot-tests.spec.ts-snapshots/Draft-rectangles-should-look-right-1-Google-Chrome-linux.png differ diff --git a/e2e/playwright/snapshot-tests.spec.ts-snapshots/Grid-visibility-Grid-turned-off-1-Google-Chrome-linux.png b/e2e/playwright/snapshot-tests.spec.ts-snapshots/Grid-visibility-Grid-turned-off-1-Google-Chrome-linux.png index 4568b344c0..69232e15c5 100644 Binary files a/e2e/playwright/snapshot-tests.spec.ts-snapshots/Grid-visibility-Grid-turned-off-1-Google-Chrome-linux.png and b/e2e/playwright/snapshot-tests.spec.ts-snapshots/Grid-visibility-Grid-turned-off-1-Google-Chrome-linux.png differ diff --git a/e2e/playwright/snapshot-tests.spec.ts-snapshots/Grid-visibility-Grid-turned-off-to-on-via-command-bar-1-Google-Chrome-linux.png b/e2e/playwright/snapshot-tests.spec.ts-snapshots/Grid-visibility-Grid-turned-off-to-on-via-command-bar-1-Google-Chrome-linux.png index eb124323ff..29ded0e889 100644 Binary files a/e2e/playwright/snapshot-tests.spec.ts-snapshots/Grid-visibility-Grid-turned-off-to-on-via-command-bar-1-Google-Chrome-linux.png and b/e2e/playwright/snapshot-tests.spec.ts-snapshots/Grid-visibility-Grid-turned-off-to-on-via-command-bar-1-Google-Chrome-linux.png differ diff --git a/e2e/playwright/snapshot-tests.spec.ts-snapshots/Grid-visibility-Grid-turned-on-1-Google-Chrome-linux.png b/e2e/playwright/snapshot-tests.spec.ts-snapshots/Grid-visibility-Grid-turned-on-1-Google-Chrome-linux.png index ee2f24304c..62b194721e 100644 Binary files a/e2e/playwright/snapshot-tests.spec.ts-snapshots/Grid-visibility-Grid-turned-on-1-Google-Chrome-linux.png and b/e2e/playwright/snapshot-tests.spec.ts-snapshots/Grid-visibility-Grid-turned-on-1-Google-Chrome-linux.png differ diff --git a/e2e/playwright/snapshot-tests.spec.ts-snapshots/Sketch-on-face-with-none-z-up-1-Google-Chrome-linux.png b/e2e/playwright/snapshot-tests.spec.ts-snapshots/Sketch-on-face-with-none-z-up-1-Google-Chrome-linux.png index 7d069310b5..9f86f44001 100644 Binary files a/e2e/playwright/snapshot-tests.spec.ts-snapshots/Sketch-on-face-with-none-z-up-1-Google-Chrome-linux.png and b/e2e/playwright/snapshot-tests.spec.ts-snapshots/Sketch-on-face-with-none-z-up-1-Google-Chrome-linux.png differ diff --git a/e2e/playwright/snapshot-tests.spec.ts-snapshots/Zoom-to-fit-on-load---solid-2d-1-Google-Chrome-linux.png b/e2e/playwright/snapshot-tests.spec.ts-snapshots/Zoom-to-fit-on-load---solid-2d-1-Google-Chrome-linux.png index 542e07aca8..c78f356b43 100644 Binary files a/e2e/playwright/snapshot-tests.spec.ts-snapshots/Zoom-to-fit-on-load---solid-2d-1-Google-Chrome-linux.png and b/e2e/playwright/snapshot-tests.spec.ts-snapshots/Zoom-to-fit-on-load---solid-2d-1-Google-Chrome-linux.png differ diff --git a/e2e/playwright/snapshot-tests.spec.ts-snapshots/Zoom-to-fit-on-load---solid-3d-1-Google-Chrome-linux.png b/e2e/playwright/snapshot-tests.spec.ts-snapshots/Zoom-to-fit-on-load---solid-3d-1-Google-Chrome-linux.png index f07a042d74..bfa8f12666 100644 Binary files a/e2e/playwright/snapshot-tests.spec.ts-snapshots/Zoom-to-fit-on-load---solid-3d-1-Google-Chrome-linux.png and b/e2e/playwright/snapshot-tests.spec.ts-snapshots/Zoom-to-fit-on-load---solid-3d-1-Google-Chrome-linux.png differ diff --git a/e2e/playwright/snapshot-tests.spec.ts-snapshots/code-color-goober-code-color-goober-1-Google-Chrome-linux.png b/e2e/playwright/snapshot-tests.spec.ts-snapshots/code-color-goober-code-color-goober-1-Google-Chrome-linux.png index 653d96eb93..cdf1e37d99 100644 Binary files a/e2e/playwright/snapshot-tests.spec.ts-snapshots/code-color-goober-code-color-goober-1-Google-Chrome-linux.png and b/e2e/playwright/snapshot-tests.spec.ts-snapshots/code-color-goober-code-color-goober-1-Google-Chrome-linux.png differ diff --git a/e2e/playwright/snapshot-tests.spec.ts-snapshots/code-color-goober-code-color-goober-opening-window-1-Google-Chrome-linux.png b/e2e/playwright/snapshot-tests.spec.ts-snapshots/code-color-goober-code-color-goober-opening-window-1-Google-Chrome-linux.png index 178ecfe95e..9bab370d4e 100644 Binary files a/e2e/playwright/snapshot-tests.spec.ts-snapshots/code-color-goober-code-color-goober-opening-window-1-Google-Chrome-linux.png and b/e2e/playwright/snapshot-tests.spec.ts-snapshots/code-color-goober-code-color-goober-opening-window-1-Google-Chrome-linux.png differ diff --git a/e2e/playwright/snapshot-tests.spec.ts-snapshots/code-color-goober-code-color-goober-works-with-single-quotes-1-Google-Chrome-linux.png b/e2e/playwright/snapshot-tests.spec.ts-snapshots/code-color-goober-code-color-goober-works-with-single-quotes-1-Google-Chrome-linux.png index e9ae75e864..ec37bb89e4 100644 Binary files a/e2e/playwright/snapshot-tests.spec.ts-snapshots/code-color-goober-code-color-goober-works-with-single-quotes-1-Google-Chrome-linux.png and b/e2e/playwright/snapshot-tests.spec.ts-snapshots/code-color-goober-code-color-goober-works-with-single-quotes-1-Google-Chrome-linux.png differ diff --git a/e2e/playwright/snapshot-tests.spec.ts-snapshots/extrude-on-default-planes-should-be-stable--XY-1-Google-Chrome-linux.png b/e2e/playwright/snapshot-tests.spec.ts-snapshots/extrude-on-default-planes-should-be-stable--XY-1-Google-Chrome-linux.png index d07541bb3a..89e2c20f84 100644 Binary files a/e2e/playwright/snapshot-tests.spec.ts-snapshots/extrude-on-default-planes-should-be-stable--XY-1-Google-Chrome-linux.png and b/e2e/playwright/snapshot-tests.spec.ts-snapshots/extrude-on-default-planes-should-be-stable--XY-1-Google-Chrome-linux.png differ diff --git a/e2e/playwright/snapshot-tests.spec.ts-snapshots/extrude-on-default-planes-should-be-stable--XZ-1-Google-Chrome-linux.png b/e2e/playwright/snapshot-tests.spec.ts-snapshots/extrude-on-default-planes-should-be-stable--XZ-1-Google-Chrome-linux.png index 3ed9ceb4c3..5b98742a9c 100644 Binary files a/e2e/playwright/snapshot-tests.spec.ts-snapshots/extrude-on-default-planes-should-be-stable--XZ-1-Google-Chrome-linux.png and b/e2e/playwright/snapshot-tests.spec.ts-snapshots/extrude-on-default-planes-should-be-stable--XZ-1-Google-Chrome-linux.png differ diff --git a/e2e/playwright/snapshot-tests.spec.ts-snapshots/extrude-on-default-planes-should-be-stable--YZ-1-Google-Chrome-linux.png b/e2e/playwright/snapshot-tests.spec.ts-snapshots/extrude-on-default-planes-should-be-stable--YZ-1-Google-Chrome-linux.png index 706abcbaac..21fd328c0a 100644 Binary files a/e2e/playwright/snapshot-tests.spec.ts-snapshots/extrude-on-default-planes-should-be-stable--YZ-1-Google-Chrome-linux.png and b/e2e/playwright/snapshot-tests.spec.ts-snapshots/extrude-on-default-planes-should-be-stable--YZ-1-Google-Chrome-linux.png differ diff --git a/e2e/playwright/snapshot-tests.spec.ts-snapshots/extrude-on-default-planes-should-be-stable-XY-1-Google-Chrome-linux.png b/e2e/playwright/snapshot-tests.spec.ts-snapshots/extrude-on-default-planes-should-be-stable-XY-1-Google-Chrome-linux.png index af376714c8..fd803fb5eb 100644 Binary files a/e2e/playwright/snapshot-tests.spec.ts-snapshots/extrude-on-default-planes-should-be-stable-XY-1-Google-Chrome-linux.png and b/e2e/playwright/snapshot-tests.spec.ts-snapshots/extrude-on-default-planes-should-be-stable-XY-1-Google-Chrome-linux.png differ diff --git a/e2e/playwright/snapshot-tests.spec.ts-snapshots/extrude-on-default-planes-should-be-stable-XZ-1-Google-Chrome-linux.png b/e2e/playwright/snapshot-tests.spec.ts-snapshots/extrude-on-default-planes-should-be-stable-XZ-1-Google-Chrome-linux.png index 47c4e13ec3..6b7cf86b8f 100644 Binary files a/e2e/playwright/snapshot-tests.spec.ts-snapshots/extrude-on-default-planes-should-be-stable-XZ-1-Google-Chrome-linux.png and b/e2e/playwright/snapshot-tests.spec.ts-snapshots/extrude-on-default-planes-should-be-stable-XZ-1-Google-Chrome-linux.png differ diff --git a/e2e/playwright/snapshot-tests.spec.ts-snapshots/extrude-on-default-planes-should-be-stable-YZ-1-Google-Chrome-linux.png b/e2e/playwright/snapshot-tests.spec.ts-snapshots/extrude-on-default-planes-should-be-stable-YZ-1-Google-Chrome-linux.png index 9dca19b817..718d44a7fe 100644 Binary files a/e2e/playwright/snapshot-tests.spec.ts-snapshots/extrude-on-default-planes-should-be-stable-YZ-1-Google-Chrome-linux.png and b/e2e/playwright/snapshot-tests.spec.ts-snapshots/extrude-on-default-planes-should-be-stable-YZ-1-Google-Chrome-linux.png differ diff --git a/e2e/playwright/test-network-and-connection-issues.spec.ts b/e2e/playwright/test-network-and-connection-issues.spec.ts index 8bf2b3e2f3..10f6ccd49a 100644 --- a/e2e/playwright/test-network-and-connection-issues.spec.ts +++ b/e2e/playwright/test-network-and-connection-issues.spec.ts @@ -242,8 +242,9 @@ test.describe('Test network related behaviors', () => { sketch001 = startSketchOn(XZ) -profile001 = startProfile(sketch001, at = [12.34, -12.34]) +profile001 = startProfile(sketch001, at = [-12.34, -12.34]) |> xLine(length = 12.34) +profile002 = startProfile(sketch001, at = [12.34, -12.34]) |> line(end = [-12.34, 12.34]) `) @@ -256,8 +257,9 @@ profile001 = startProfile(sketch001, at = [12.34, -12.34]) sketch001 = startSketchOn(XZ) -profile001 = startProfile(sketch001, at = [12.34, -12.34]) +profile001 = startProfile(sketch001, at = [-12.34, -12.34]) |> xLine(length = 12.34) +profile002 = startProfile(sketch001, at = [12.34, -12.34]) |> line(end = [-12.34, 12.34]) |> xLine(length = -12.34) diff --git a/e2e/playwright/test-utils.ts b/e2e/playwright/test-utils.ts index e5aa1aeb1e..723cc110e8 100644 --- a/e2e/playwright/test-utils.ts +++ b/e2e/playwright/test-utils.ts @@ -19,6 +19,9 @@ const NODE_ENV = process.env.NODE_ENV || 'development' dotenv.config({ path: [`.env.${NODE_ENV}.local`, `.env.${NODE_ENV}`] }) export const token = process.env.VITE_KITTYCAD_API_TOKEN || '' +/** A string version of a RegExp to get a number that may include a decimal point */ +export const NUMBER_REGEXP = '\\d+(\\.\\d+)?' + import type { ProjectConfiguration } from '@rust/kcl-lib/bindings/ProjectConfiguration' import type { ElectronZoo } from '@e2e/playwright/fixtures/fixtureSetup' @@ -58,8 +61,8 @@ export const PERSIST_MODELING_CONTEXT = 'persistModelingContext' export const deg = (Math.PI * 2) / 360 export const commonPoints = { - startAt: '[7.19, -9.7]', - num1: 7.25, + startAt: '[-12.99, -10.63]', + num1: 8.2, num2: 14.44, /** The Y-value of a common lineTo move we perform in tests */ num3: -2.44, diff --git a/e2e/playwright/testing-camera-movement.spec.ts b/e2e/playwright/testing-camera-movement.spec.ts index dd87364344..625ae7f095 100644 --- a/e2e/playwright/testing-camera-movement.spec.ts +++ b/e2e/playwright/testing-camera-movement.spec.ts @@ -253,7 +253,7 @@ test.describe('Testing Camera Movement', () => { await page.waitForTimeout(100) await page.mouse.up({ button: 'right' }) }, - afterPosition: [27.07, -43.66, 108.68], + afterPosition: [47.27, -15.48, 109.43], beforePosition: initialCamPosition, page, scene, diff --git a/e2e/playwright/testing-constraints.spec.ts b/e2e/playwright/testing-constraints.spec.ts index 17a93f69b1..94552f632e 100644 --- a/e2e/playwright/testing-constraints.spec.ts +++ b/e2e/playwright/testing-constraints.spec.ts @@ -4,6 +4,7 @@ import * as fsp from 'fs/promises' import { getUtils, + NUMBER_REGEXP, pollEditorLinesSelectedLength, } from '@e2e/playwright/test-utils' import { expect, test } from '@e2e/playwright/zoo-test' @@ -403,8 +404,10 @@ test.describe('Testing constraints', () => { } else { await scene.clickXAxis() } + // await page.waitForTimeout(120_000) await page.keyboard.down('Shift') await page.waitForTimeout(100) + console.log('clicking line', line3) await page.mouse.click(line3.x, line3.y) await page.waitForTimeout(100) await page.keyboard.up('Shift') @@ -455,46 +458,48 @@ test.describe('Testing constraints', () => { testName: 'Add variable', addVariable: true, axisSelect: false, - value: 'segAng(seg01) + angle001', + /** must be a regex-escaped string */ + value: 'segAng\\(seg01\\) \\+ angle001', }, { testName: 'No variable', addVariable: false, axisSelect: false, - value: 'segAng(seg01) + 22.69', + /** must be a regex-escaped string */ + value: `segAng\\(seg01\\) \\+ ${NUMBER_REGEXP}`, }, { testName: 'Add variable, selecting axis', addVariable: true, axisSelect: true, + /** must be a regex-escaped string */ value: 'turns::QUARTER_TURN - angle001', }, { testName: 'No variable, selecting axis', addVariable: false, axisSelect: true, + /** must be a regex-escaped string */ value: 'turns::QUARTER_TURN - 7', }, ] as const for (const { testName, addVariable, value, axisSelect } of cases) { - test(`${testName}`, async ({ page, homePage, scene, cmdBar }) => { + test(`${testName}`, async ({ + page, + homePage, + scene, + cmdBar, + toolbar, + }) => { await page.addInitScript(async () => { localStorage.setItem( 'persistCode', `@settings(defaultLengthUnit = in) - yo = 5 - part001 = startSketchOn(XZ) - |> startProfile(at = [-7.54, -26.74]) - |> line(end = [74.36, 130.4]) - |> line(end = [78.92, -120.11]) - |> line(end = [9.16, 77.79]) - |> line(end = [51.19, 48.97]) - part002 = startSketchOn(XZ) - |> startProfile(at = [299.05, 231.45]) - |> xLine(length = -425.34, tag = $seg_what) - |> yLine(length = -264.06) - |> xLine(length = segLen(seg_what)) - |> line(endAbsolute = [profileStartX(%), profileStartY(%)])` +sketch001 = startSketchOn(XZ) +profile001 = startProfile(sketch001, at = [-70, -10]) + |> line(end = [75, 50]) + |> line(end = [25, -100]) + |> line(end = [10, 80])` ) }) const u = await getUtils(page) @@ -502,12 +507,7 @@ test.describe('Testing constraints', () => { await homePage.goToModelingScene() await scene.settled(cmdBar) - - await page.getByText('line(end = [74.36, 130.4])').click() - await page.getByRole('button', { name: 'Edit Sketch' }).click() - - // Wait for overlays to populate - await page.waitForTimeout(1000) + await toolbar.editSketch(0) const [line1, line3] = await Promise.all([ u.getBoundingBox(`[data-overlay-index="${1}"]`), @@ -515,7 +515,7 @@ test.describe('Testing constraints', () => { ]) if (axisSelect) { - await page.mouse.click(600, 130) + await scene.clickYAxis() } else { await page.mouse.click(line1.x, line1.y) } @@ -543,8 +543,10 @@ test.describe('Testing constraints', () => { // checking activeLines assures the cursors are where they should be const codeAfter = [ - '|> line(end = [74.36, 130.4], tag = $seg01)', - `|> angledLine(angle = ${value}, length = 78.33)`, + '|> line(end = [75, 50], tag = $seg01)', + new RegExp( + `\\|> angledLine\\(angle = ${value}, length = ${NUMBER_REGEXP}\\)` + ), ] if (axisSelect) codeAfter.shift() @@ -560,7 +562,7 @@ test.describe('Testing constraints', () => { ) // checking the count of the overlays is a good proxy check that the client sketch scene is in a good state - await expect(page.getByTestId('segment-overlay')).toHaveCount(5) + await expect(page.getByTestId('segment-overlay')).toHaveCount(4) }) } }) @@ -918,34 +920,33 @@ part002 = startSketchOn(XZ) test.describe('Axis & segment - no modal constraints', () => { const cases = [ { - codeAfter: `|> line(endAbsolute = [154.9, turns::ZERO])`, - axisClick: { x: 950, y: 250 }, + codeAfter: /\|> line\(endAbsolute = \[\d+(\.\d+)?, turns::ZERO/, + axisClick: { x: 0.75, y: 0.5 }, constraintName: 'Snap To X', }, { - codeAfter: `|> line(endAbsolute = [turns::ZERO, 61.34])`, - axisClick: { x: 600, y: 150 }, + codeAfter: /\|> line\(endAbsolute = \[turns::ZERO/, + axisClick: { x: 0.5, y: 0.75 }, constraintName: 'Snap To Y', }, ] as const for (const { codeAfter, constraintName, axisClick } of cases) { - test(`${constraintName}`, async ({ page, homePage, scene, cmdBar }) => { + test(`${constraintName}`, async ({ + page, + homePage, + scene, + cmdBar, + toolbar, + }) => { await page.addInitScript(async () => { localStorage.setItem( 'persistCode', `@settings(defaultLengthUnit = in) - yo = 5 - part001 = startSketchOn(XZ) - |> startProfile(at = [-7.54, -26.74]) - |> line(end = [74.36, 130.4]) - |> line(end = [78.92, -120.11]) - |> line(end = [9.16, 77.79]) - part002 = startSketchOn(XZ) - |> startProfile(at = [299.05, 231.45]) - |> xLine(length = -425.34, tag = $seg_what) - |> yLine(length = -264.06) - |> xLine(length = segLen(seg_what)) - |> line(endAbsolute = [profileStartX(%), profileStartY(%)])` +sketch001 = startSketchOn(XZ) +profile001 = startProfile(sketch001, at = [-47.54, -26.74]) + |> line(end = [74.36, 100.4]) + |> line(end = [78.92, -100.11]) + |> line(end = [9.16, 77.79])` ) }) const u = await getUtils(page) @@ -953,13 +954,13 @@ part002 = startSketchOn(XZ) await homePage.goToModelingScene() await scene.settled(cmdBar) + await toolbar.editSketch(0) - await page.getByText('line(end = [74.36, 130.4])').click() - await page.getByRole('button', { name: 'Edit Sketch' }).click() - - // Wait for overlays to populate - await page.waitForTimeout(1000) - + const [convertedAxisClick] = scene.makeMouseHelpers( + axisClick.x, + axisClick.y, + { format: 'ratio' } + ) const line3 = await u.getBoundingBox(`[data-overlay-index="${3}"]`) // select segment and axis by holding down shift @@ -967,7 +968,7 @@ part002 = startSketchOn(XZ) await page.waitForTimeout(100) await page.keyboard.down('Shift') await page.waitForTimeout(100) - await page.mouse.click(axisClick.x, axisClick.y) + await convertedAxisClick() await page.waitForTimeout(100) await page.keyboard.up('Shift') await page.waitForTimeout(100) diff --git a/e2e/playwright/testing-segment-overlays.spec.ts b/e2e/playwright/testing-segment-overlays.spec.ts index 6ad98f0002..1b63f2f7de 100644 --- a/e2e/playwright/testing-segment-overlays.spec.ts +++ b/e2e/playwright/testing-segment-overlays.spec.ts @@ -545,7 +545,7 @@ part001 = startSketchOn(XZ) 'persistCode', `@settings(defaultLengthUnit = in) part001 = startSketchOn(XZ) - |> startProfile(at = [5, 6]) + |> startProfile(at = [-5, 6]) |> ${lineToBeDeleted} |> line(end = [-10, -15]) |> angledLine(angle = -176, length = segLen(seg01))` diff --git a/e2e/playwright/testing-selections.spec.ts b/e2e/playwright/testing-selections.spec.ts index 840715bd2a..b55256f7e2 100644 --- a/e2e/playwright/testing-selections.spec.ts +++ b/e2e/playwright/testing-selections.spec.ts @@ -476,191 +476,63 @@ part001 = startSketchOn(XZ) ).not.toBeDisabled() }) - const removeAfterFirstParenthesis = (inputString: string) => { - const index = inputString.indexOf('(') - if (index !== -1) { - return inputString.substring(0, index) - } - return inputString // return the original string if '(' is not found - } - - test('Testing selections (and hovers) work on sketches when NOT in sketch mode', async ({ - page, - homePage, - scene, - cmdBar, - }) => { - const cases = [ - { - pos: [694, 185], - expectedCode: 'line(end = [74.36, 130.4], tag = $seg01)', - }, - { - pos: [816, 244], - expectedCode: 'angledLine(angle = segAng(seg01), length = yo)', - }, - { - pos: [1107, 161], - expectedCode: 'tangentialArc(endAbsolute = [167.95, -28.85])', - }, - ] as const - await page.addInitScript( - async ({ cases }) => { - localStorage.setItem( - 'persistCode', - `@settings(defaultLengthUnit = in) + test( + 'Testing selections (and hovers) work on sketches when NOT in sketch mode', + { tag: '@web' }, + async ({ page, homePage, scene, cmdBar }) => { + const cases = [ + { + pos: [0.31, 0.5], + expectedCode: 'line(end = [74.36, 130.4], tag = $seg01)', + }, + { + pos: [0.448, 0.557], + expectedCode: 'angledLine(angle = segAng(seg01), length = yo)', + }, + { + pos: [0.753, 0.5], + expectedCode: 'tangentialArc(endAbsolute = [167.95, -28.85])', + }, + ] as const + await page.addInitScript( + async ({ cases }) => { + localStorage.setItem( + 'persistCode', + `@settings(defaultLengthUnit = in) yo = 79 part001 = startSketchOn(XZ) - |> startProfile(at = [-7.54, -26.74]) + |> startProfile(at = [-40.54, -26.74]) |> ${cases[0].expectedCode} |> line(end = [-3.19, -138.43]) |> ${cases[1].expectedCode} |> line(end = [41.19, 28.97 + 5]) |> ${cases[2].expectedCode}` - ) - }, - { cases } - ) - const u = await getUtils(page) - await page.setBodyDimensions({ width: 1200, height: 500 }) - - await homePage.goToModelingScene() - await scene.settled(cmdBar) - await u.openAndClearDebugPanel() - - await u.sendCustomCmd({ - type: 'modeling_cmd_req', - cmd_id: uuidv4(), - cmd: { - type: 'default_camera_look_at', - vantage: { x: -449, y: -7503, z: 99 }, - center: { x: -449, y: 0, z: 99 }, - up: { x: 0, y: 0, z: 1 }, - }, - }) - await u.waitForCmdReceive('default_camera_look_at') - await u.clearAndCloseDebugPanel() - - // end setup, now test hover and selects - for (const { pos, expectedCode } of cases) { - // hover over segment, check it's content - await page.mouse.move(pos[0], pos[1], { steps: 5 }) - await expect(page.getByTestId('hover-highlight').first()).toBeVisible() - await expect(page.getByTestId('hover-highlight').first()).toHaveText( - expectedCode - ) - // hover over segment, click it and check the cursor has move to the right place - await page.mouse.click(pos[0], pos[1]) - await expect(page.locator('.cm-activeLine')).toHaveText( - '|> ' + expectedCode + ) + }, + { cases } ) + await page.setBodyDimensions({ width: 1200, height: 500 }) + await homePage.goToModelingScene() + await scene.settled(cmdBar) + + // end setup, now test hover and selects + for (const { pos, expectedCode } of cases) { + const [click, hover] = scene.makeMouseHelpers(pos[0], pos[1], { + format: 'ratio', + steps: 5, + }) + // hover over segment, check it's content + await hover() + await expect(page.getByTestId('hover-highlight').first()).toBeVisible() + await expect(page.getByTestId('hover-highlight').first()).toHaveText( + expectedCode + ) + // hover over segment, click it and check the cursor has move to the right place + await click() + await expect(page.locator('.cm-activeLine')).toContainText(expectedCode) + } } - }) - test("Hovering and selection of extruded faces works, and is not overridden shortly after user's click", async ({ - page, - homePage, - scene, - cmdBar, - }) => { - await page.addInitScript(async () => { - localStorage.setItem( - 'persistCode', - `@settings(defaultLengthUnit = in) - sketch001 = startSketchOn(XZ) - |> startProfile(at = [-79.26, 95.04]) - |> line(end = [112.54, 127.64]) - |> line(end = [170.36, -121.61], tag = $seg01) - |> line(endAbsolute = [profileStartX(%), profileStartY(%)]) - |> close() - extrude001 = extrude(sketch001, length = 50) - ` - ) - }) - const u = await getUtils(page) - await page.setBodyDimensions({ width: 1200, height: 500 }) - - await homePage.goToModelingScene() - await scene.settled(cmdBar) - await u.openAndClearDebugPanel() - - await u.sendCustomCmd({ - type: 'modeling_cmd_req', - cmd_id: uuidv4(), - cmd: { - type: 'default_camera_look_at', - vantage: { x: 6615, y: -9505, z: 10344 }, - center: { x: 1579, y: -635, z: 4035 }, - up: { x: 0, y: 0, z: 1 }, - }, - }) - await u.waitForCmdReceive('default_camera_look_at') - await u.clearAndCloseDebugPanel() - - await page.waitForTimeout(1000) - - let noHoverColor: [number, number, number] = [92, 92, 92] - let hoverColor: [number, number, number] = [127, 127, 127] - let selectColor: [number, number, number] = [155, 155, 105] - - const extrudeWall = { x: 670, y: 275 } - const extrudeText = `line(end = [170.36, -121.61], tag = $seg01)` - - const cap = { x: 594, y: 283 } - const capText = `startProfile(at = [-79.26, 95.04])` - - const nothing = { x: 946, y: 229 } - - await expect - .poll(() => u.getGreatestPixDiff(extrudeWall, noHoverColor)) - .toBeLessThan(15) - await page.mouse.move(nothing.x, nothing.y) - await page.waitForTimeout(1000) - await page.mouse.move(extrudeWall.x, extrudeWall.y) - await expect(page.getByTestId('hover-highlight').first()).toBeVisible() - await expect(page.getByTestId('hover-highlight').first()).toContainText( - removeAfterFirstParenthesis(extrudeText) - ) - await page.waitForTimeout(1000) - await expect( - await u.getGreatestPixDiff(extrudeWall, hoverColor) - ).toBeLessThan(15) - await page.mouse.click(extrudeWall.x, extrudeWall.y) - await expect(page.locator('.cm-activeLine')).toHaveText(`|> ${extrudeText}`) - await page.waitForTimeout(1000) - await expect( - await u.getGreatestPixDiff(extrudeWall, selectColor) - ).toBeLessThan(15) - await page.waitForTimeout(1000) - // check color stays there, i.e. not overridden (this was a bug previously) - await expect( - await u.getGreatestPixDiff(extrudeWall, selectColor) - ).toBeLessThan(15) - - await page.mouse.move(nothing.x, nothing.y) - await page.waitForTimeout(1000) - await expect(page.getByTestId('hover-highlight')).not.toBeVisible() - - // because of shading, color is not exact everywhere on the face - noHoverColor = [115, 115, 115] - hoverColor = [145, 145, 145] - selectColor = [168, 168, 120] - - await expect(await u.getGreatestPixDiff(cap, noHoverColor)).toBeLessThan(15) - await page.mouse.move(cap.x, cap.y) - await expect(page.getByTestId('hover-highlight').first()).toBeVisible() - await expect(page.getByTestId('hover-highlight').first()).toContainText( - removeAfterFirstParenthesis(capText) - ) - await page.waitForTimeout(1000) - await expect(await u.getGreatestPixDiff(cap, hoverColor)).toBeLessThan(15) - await page.mouse.click(cap.x, cap.y) - await expect(page.locator('.cm-activeLine')).toHaveText(`|> ${capText}`) - await page.waitForTimeout(1000) - await expect(await u.getGreatestPixDiff(cap, selectColor)).toBeLessThan(15) - await page.waitForTimeout(1000) - // check color stays there, i.e. not overridden (this was a bug previously) - await expect(await u.getGreatestPixDiff(cap, selectColor)).toBeLessThan(15) - }) + ) test("Various pipe expressions should and shouldn't allow edit and or extrude", async ({ page, homePage, diff --git a/e2e/playwright/various.spec.ts b/e2e/playwright/various.spec.ts index a22568fc58..4532487b3d 100644 --- a/e2e/playwright/various.spec.ts +++ b/e2e/playwright/various.spec.ts @@ -1,4 +1,4 @@ -import { doExport, getUtils, makeTemplate } from '@e2e/playwright/test-utils' +import { doExport, getUtils } from '@e2e/playwright/test-utils' import { expect, test } from '@e2e/playwright/zoo-test' test('Units menu', async ({ page, homePage }) => { @@ -443,149 +443,3 @@ test('Delete key does not navigate back', async ({ page, homePage }) => { await page.keyboard.press('Delete') await expect.poll(() => page.url()).not.toContain('/settings') }) - -test('Sketch on face', async ({ page, homePage, scene, cmdBar, toolbar }) => { - test.setTimeout(90_000) - const u = await getUtils(page) - await page.addInitScript(async () => { - localStorage.setItem( - 'persistCode', - `@settings(defaultLengthUnit = in) -sketch001 = startSketchOn(XZ) -|> startProfile(at = [3.29, 7.86]) -|> line(end = [2.48, 2.44]) -|> line(end = [2.66, 1.17]) -|> line(end = [3.75, 0.46]) -|> line(end = [4.99, -0.46]) -|> line(end = [3.3, -2.12]) -|> line(end = [2.16, -3.33]) -|> line(end = [0.85, -3.08]) -|> line(end = [-0.18, -3.36]) -|> line(end = [-3.86, -2.73]) -|> line(end = [-17.67, 0.85]) -|> close() -extrude001 = extrude(sketch001, length = 5 + 7)` - ) - }) - - await page.setBodyDimensions({ width: 1200, height: 500 }) - - await homePage.goToModelingScene() - await scene.connectionEstablished() - await scene.settled(cmdBar) - - let previousCodeContent = await page.locator('.cm-content').innerText() - - await toolbar.startSketchThenCallbackThenWaitUntilReady(async () => { - await u.openAndClearDebugPanel() - await u.doAndWaitForCmd( - () => page.mouse.click(625, 165), - 'default_camera_get_settings', - true - ) - await page.waitForTimeout(150) - await u.closeDebugPanel() - }) - await page.waitForTimeout(300) - - const firstClickPosition = [612, 238] - const secondClickPosition = [661, 242] - const thirdClickPosition = [609, 267] - - await page.mouse.click(firstClickPosition[0], firstClickPosition[1]) - await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent) - previousCodeContent = await page.locator('.cm-content').innerText() - - await page.waitForTimeout(100) - await page.mouse.click(secondClickPosition[0], secondClickPosition[1]) - await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent) - previousCodeContent = await page.locator('.cm-content').innerText() - - await page.waitForTimeout(100) - await page.mouse.click(thirdClickPosition[0], thirdClickPosition[1]) - await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent) - previousCodeContent = await page.locator('.cm-content').innerText() - - await page.waitForTimeout(100) - await page.mouse.click(firstClickPosition[0], firstClickPosition[1]) - await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent) - previousCodeContent = await page.locator('.cm-content').innerText() - - await expect.poll(u.normalisedEditorCode).toContain( - u.normalisedCode(`sketch002 = startSketchOn(extrude001, face = seg01) -profile001 = startProfile(sketch002, at = [-12.34, 12.34]) - |> line(end = [12.34, -12.34]) - |> line(end = [-12.34, -12.34]) - |> line(endAbsolute = [profileStartX(%), profileStartY(%)]) - |> close() -`) - ) - - await u.openAndClearDebugPanel() - await page.getByRole('button', { name: 'Exit Sketch' }).click() - await u.expectCmdLog('[data-message-type="execution-done"]') - - await u.updateCamPosition([1049, 239, 686]) - await u.closeDebugPanel() - - await page.getByText('startProfile(sketch002, at = [-12').click() - await expect(page.getByRole('button', { name: 'Edit Sketch' })).toBeVisible() - await page.getByRole('button', { name: 'Edit Sketch' }).click() - await page.waitForTimeout(500) - await page.setViewportSize({ width: 1200, height: 1200 }) - await u.openAndClearDebugPanel() - await u.updateCamPosition([452, -152, 1166]) - await u.closeDebugPanel() - await page.waitForTimeout(200) - - const pointToDragFirst = [787, 565] - await page.mouse.move(pointToDragFirst[0], pointToDragFirst[1]) - await page.mouse.down() - await page.mouse.move(pointToDragFirst[0] - 20, pointToDragFirst[1], { - steps: 5, - }) - await page.mouse.up() - await page.waitForTimeout(100) - await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent) - previousCodeContent = await page.locator('.cm-content').innerText() - - const result = makeTemplate`sketch002 = startSketchOn(extrude001, face = seg01) -|> startProfile(at = [-12.83, 6.7]) -|> line(end = [${[2.28, 2.35]}, -${0.07}]) -|> line(end = [-3.05, -1.47]) -|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) -|> close()` - - await expect(page.locator('.cm-content')).toHaveText(result.regExp) - - // exit sketch - await u.openAndClearDebugPanel() - await page.getByRole('button', { name: 'Exit Sketch' }).click() - await u.expectCmdLog('[data-message-type="execution-done"]') - - await page.getByText('startProfile(sketch002, at = [-12').click() - - await expect(page.getByRole('button', { name: 'Extrude' })).not.toBeDisabled() - await page.waitForTimeout(100) - await page.getByRole('button', { name: 'Extrude' }).click() - - await expect(page.getByTestId('command-bar')).toBeVisible() - await page.waitForTimeout(100) - - await cmdBar.progressCmdBar() - await cmdBar.progressCmdBar() - await cmdBar.submit() - - const result2 = result.genNext` -const sketch002 = extrude(sketch002, length = ${[5, 5]} + 7)` - await expect(page.locator('.cm-content')).toHaveText(result2.regExp) -}) - -test.fixme( - `Opening a share link in the web isn't blocked by the web warning banner`, - async () => { - // This test is not able to be run right now since we don't have a web-only setup for Playwright. - // @franknoirot can implement it when that testing infra is set up. It should be a test to cover the fix from - // modeling-app issue #6172. - } -) diff --git a/src/App.tsx b/src/App.tsx index 7540b1579c..fb4bdb14a4 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -269,54 +269,58 @@ export function App() { - - - - {/* */} -
- - +
+ +
+ + +
+ + +
+
- - */} + + ...defaultLocalStatusBarItems, + ]} + /> + ) } diff --git a/src/Toolbar.tsx b/src/Toolbar.tsx index c9748a3b6b..ffe8ecd65c 100644 --- a/src/Toolbar.tsx +++ b/src/Toolbar.tsx @@ -207,7 +207,7 @@ export function Toolbar({
    { if (this.camera instanceof PerspectiveCamera) { - this.camera.aspect = window.innerWidth / window.innerHeight + this.camera.aspect = + this.domElement.clientWidth / this.domElement.clientHeight } else if (this.camera instanceof OrthographicCamera) { - const aspect = window.innerWidth / window.innerHeight + const aspect = this.domElement.clientWidth / this.domElement.clientHeight + this.camera.left = -ORTHOGRAPHIC_CAMERA_SIZE * aspect this.camera.right = ORTHOGRAPHIC_CAMERA_SIZE * aspect this.camera.top = ORTHOGRAPHIC_CAMERA_SIZE @@ -596,7 +597,9 @@ export class CameraControls { const { x: px, y: py, z: pz } = this.camera.position const { x: qx, y: qy, z: qz, w: qw } = this.camera.quaternion const oldCamUp = this.camera.up.clone() - const aspect = window.innerWidth / window.innerHeight + const aspect = + this.engineCommandManager.streamDimensions.width / + this.engineCommandManager.streamDimensions.height this.lastPerspectiveFov = this.camera.fov const { z_near, z_far } = calculateNearFarFromFOV(this.lastPerspectiveFov) this.camera = new OrthographicCamera( @@ -634,7 +637,8 @@ export class CameraControls { const previousCamUp = this.camera.up.clone() this.camera = new PerspectiveCamera( this.lastPerspectiveFov, - window.innerWidth / window.innerHeight, + this.engineCommandManager.streamDimensions.width / + this.engineCommandManager.streamDimensions.height, z_near, z_far ) diff --git a/src/clientSideScene/ClientSideSceneComp.tsx b/src/clientSideScene/ClientSideSceneComp.tsx index 910f2bfec5..622c07b9bf 100644 --- a/src/clientSideScene/ClientSideSceneComp.tsx +++ b/src/clientSideScene/ClientSideSceneComp.tsx @@ -132,6 +132,19 @@ export const ClientSideScene = ({ // eslint-disable-next-line react-hooks/exhaustive-deps -- TODO: blanket-ignored fix me! }, []) + useEffect(() => { + const canvas = canvasRef.current + if (!canvas) return + const observer = new ResizeObserver(() => { + sceneInfra.onWindowResize() + sceneInfra.camControls.onWindowResize() + }) + observer.observe(canvas) + return () => { + observer.disconnect() + } + }, []) + let cursor = 'default' if (state.matches('Sketch')) { if ( diff --git a/src/clientSideScene/sceneInfra.ts b/src/clientSideScene/sceneInfra.ts index 789611d875..442aec7239 100644 --- a/src/clientSideScene/sceneInfra.ts +++ b/src/clientSideScene/sceneInfra.ts @@ -232,8 +232,8 @@ export class SceneInfra { _angle = typeof angle === 'number' ? angle : getAngle(from, to) } - const x = (vector.x * 0.5 + 0.5) * window.innerWidth - const y = (-vector.y * 0.5 + 0.5) * window.innerHeight + const x = (vector.x * 0.5 + 0.5) * this.renderer.domElement.clientWidth + const y = (-vector.y * 0.5 + 0.5) * this.renderer.domElement.clientHeight const pathToNodeString = JSON.stringify(group.userData.pathToNode) return { type: 'set-one', @@ -444,6 +444,11 @@ export class SceneInfra { private _lastUnprocessedMouseEvent: MouseEvent | undefined onMouseMove = async (mouseEvent: MouseEvent) => { + if (!(mouseEvent.currentTarget instanceof HTMLElement)) { + console.error('unexpected targetless event') + return + } + if (this.mouseMoveThrottling) { // Throttle mouse move events to help with performance. // Without this a new call to executeAstMock() is made by SceneEntities/onDragSegment() while the @@ -456,9 +461,10 @@ export class SceneInfra { this._processingMouseMove = true } - this.currentMouseVector.x = (mouseEvent.clientX / window.innerWidth) * 2 - 1 + this.currentMouseVector.x = + (mouseEvent.offsetX / mouseEvent.currentTarget.clientWidth) * 2 - 1 this.currentMouseVector.y = - -(mouseEvent.clientY / window.innerHeight) * 2 + 1 + -(mouseEvent.offsetY / mouseEvent.currentTarget.clientHeight) * 2 + 1 const planeIntersectPoint = this.getPlaneIntersectPoint() const intersects = this.raycastRing() @@ -599,8 +605,14 @@ export class SceneInfra { // Check the ring points for (let i = 0; i < rayRingCount; i++) { const angle = (i / rayRingCount) * Math.PI * 2 - const offsetX = ((pixelRadius * Math.cos(angle)) / window.innerWidth) * 2 - const offsetY = ((pixelRadius * Math.sin(angle)) / window.innerHeight) * 2 + const offsetX = + ((pixelRadius * Math.cos(angle)) / + this.renderer.domElement.clientWidth) * + 2 + const offsetY = + ((pixelRadius * Math.sin(angle)) / + this.renderer.domElement.clientHeight) * + 2 const ringVector = new Vector2( mouseDownVector.x + offsetX, mouseDownVector.y - offsetY @@ -624,8 +636,14 @@ export class SceneInfra { } onMouseDown = (event: MouseEvent) => { - this.currentMouseVector.x = (event.clientX / window.innerWidth) * 2 - 1 - this.currentMouseVector.y = -(event.clientY / window.innerHeight) * 2 + 1 + if (!(event.currentTarget instanceof HTMLElement)) { + console.error('unexpected targetless event') + return + } + this.currentMouseVector.x = + (event.offsetX / event.currentTarget.clientWidth) * 2 - 1 + this.currentMouseVector.y = + -(event.offsetY / event.currentTarget.clientHeight) * 2 + 1 const mouseDownVector = this.currentMouseVector.clone() const intersect = this.raycastRing()[0] @@ -643,9 +661,14 @@ export class SceneInfra { } onMouseUp = async (mouseEvent: MouseEvent) => { - this.currentMouseVector.x = (mouseEvent.clientX / window.innerWidth) * 2 - 1 + if (!(mouseEvent.currentTarget instanceof HTMLElement)) { + console.error('unexpected targetless event') + return + } + this.currentMouseVector.x = + (mouseEvent.offsetX / mouseEvent.currentTarget.clientWidth) * 2 - 1 this.currentMouseVector.y = - -(mouseEvent.clientY / window.innerHeight) * 2 + 1 + -(mouseEvent.offsetY / mouseEvent.currentTarget.clientHeight) * 2 + 1 const planeIntersectPoint = this.getPlaneIntersectPoint() const intersects = this.raycastRing() diff --git a/src/components/CustomIcon.tsx b/src/components/CustomIcon.tsx index 0779a2d32d..b3289b4877 100644 --- a/src/components/CustomIcon.tsx +++ b/src/components/CustomIcon.tsx @@ -1361,6 +1361,19 @@ const CustomIconMap = { /> ), + sixDots: ( + + + + ), sketch: ( { + // Will cause a useEffect loop if not checked for. + if (engineStreamState.context.containerElementRef.current !== null) return + engineStreamActor.send({ + type: EngineStreamTransition.SetContainerElementRef, + containerElementRef: { current: videoWrapperRef.current }, + }) + // eslint-disable-next-line react-hooks/exhaustive-deps -- TODO: blanket-ignored fix me! + }, [videoWrapperRef.current, engineStreamState]) + useEffect(() => { // Will cause a useEffect loop if not checked for. if (engineStreamState.context.videoRef.current !== null) return @@ -303,6 +313,8 @@ export const EngineStream = (props: { // But if the user resizes, and we're stopped or paused, then we want // to try to restart the stream! + const wrapper = engineStreamState.context.containerElementRef?.current + if (!wrapper) return const video = engineStreamState.context.videoRef?.current if (!video) return const canvas = engineStreamState.context.canvasRef?.current @@ -317,8 +329,8 @@ export const EngineStream = (props: { last.current = Date.now() if ( - (Math.abs(video.width - window.innerWidth) > 4 || - Math.abs(video.height - window.innerHeight) > 4) && + (Math.abs(video.width - wrapper.clientWidth) > 4 || + Math.abs(video.height - wrapper.clientHeight) > 4) && engineStreamState.matches(EngineStreamState.Playing) ) { timeoutStart.current = Date.now() @@ -327,7 +339,7 @@ export const EngineStream = (props: { }) }) - observer.observe(document.body) + observer.observe(wrapper) return () => { observer.disconnect() @@ -570,7 +582,7 @@ export const EngineStream = (props: { // eslint-disable-next-line jsx-a11y/no-static-element-interactions
    Connecting and setting up scene... diff --git a/src/components/ModelingSidebar/ModelingPane.module.css b/src/components/ModelingSidebar/ModelingPane.module.css index 201a02d4a7..d425441608 100644 --- a/src/components/ModelingSidebar/ModelingPane.module.css +++ b/src/components/ModelingSidebar/ModelingPane.module.css @@ -1,23 +1,27 @@ .panel { - @apply relative z-0 rounded-r max-w-full flex-auto; + @apply relative z-0 max-w-full flex-auto; display: grid; grid-template-rows: auto 1fr; - @apply bg-chalkboard-10/50 focus-within:bg-chalkboard-10/90 backdrop-blur-sm border border-chalkboard-30; + @apply bg-chalkboard-10 border-chalkboard-30; scroll-margin-block-start: 41px; } +.panel:not(:first-child) { + @apply border-t; +} + .header::before, .header::-webkit-details-marker { display: none; } :global(.dark) .panel { - @apply bg-chalkboard-100/50 focus-within:bg-chalkboard-100/90 backdrop-blur-[3px] border-chalkboard-80; + @apply bg-chalkboard-100 border-chalkboard-80; } .header { - @apply z-10 relative rounded-tr; - @apply flex h-[41px] items-center justify-between gap-2 px-2; + @apply z-10 relative; + @apply flex items-center justify-between gap-2 py-1 px-2; @apply text-xs select-none text-chalkboard-90; @apply bg-chalkboard-10 border-b border-chalkboard-30; } diff --git a/src/components/ModelingSidebar/ModelingPane.tsx b/src/components/ModelingSidebar/ModelingPane.tsx index 4bacb861a0..64fc4dc9af 100644 --- a/src/components/ModelingSidebar/ModelingPane.tsx +++ b/src/components/ModelingSidebar/ModelingPane.tsx @@ -32,7 +32,6 @@ export const ModelingPaneHeader = ({ {icon && ( + ), }} > -
    +
      -
        = 1 ? 'pr-0.5' : '') - } - > +
          {filteredPanes.map((pane) => ( 0 && ( <>
          -