diff --git a/e2e/playwright/basic-sketch.spec.ts b/e2e/playwright/basic-sketch.spec.ts index afb55990d..6595a39d9 100644 --- a/e2e/playwright/basic-sketch.spec.ts +++ b/e2e/playwright/basic-sketch.spec.ts @@ -9,7 +9,11 @@ import { HomePageFixture } from './fixtures/homePageFixture' test.setTimeout(120000) -async function doBasicSketch(page: Page, homePage: HomePageFixture, openPanes: string[]) { +async function doBasicSketch( + page: Page, + homePage: HomePageFixture, + openPanes: string[] +) { const u = await getUtils(page) await page.setBodyDimensions({ width: 1200, height: 500 }) const PUR = 400 / 37.5 //pixeltoUnitRatio @@ -139,16 +143,19 @@ async function doBasicSketch(page: Page, homePage: HomePageFixture, openPanes: s } test.describe('Basic sketch', () => { - test('code pane open at start', async ({ page, homePage }) => { // Skip on windows it is being weird. + test('code pane open at start', async ({ page, homePage }) => { + // Skip on windows it is being weird. await doBasicSketch(page, homePage, ['code']) }) - test('code pane closed at start', async ({ page, homePage }) => { // Load the app with the code panes - await page.addInitScript(async (persistModelingContext) => { - localStorage.setItem( - persistModelingContext, - JSON.stringify({ openPanes: [] }) - ) - }, PERSIST_MODELING_CONTEXT) - await doBasicSketch(page, homePage, []) }) + test('code pane closed at start', async ({ page, homePage }) => { + // Load the app with the code panes + await page.addInitScript(async (persistModelingContext) => { + localStorage.setItem( + persistModelingContext, + JSON.stringify({ openPanes: [] }) + ) + }, PERSIST_MODELING_CONTEXT) + await doBasicSketch(page, homePage, []) + }) }) diff --git a/e2e/playwright/can-create-sketches-on-all-planes-and-their-back-sides.spec.ts b/e2e/playwright/can-create-sketches-on-all-planes-and-their-back-sides.spec.ts index 75c816ecc..5fefbb6e4 100644 --- a/e2e/playwright/can-create-sketches-on-all-planes-and-their-back-sides.spec.ts +++ b/e2e/playwright/can-create-sketches-on-all-planes-and-their-back-sides.spec.ts @@ -79,12 +79,13 @@ test.describe('Can create sketches on all planes and their back sides', () => { } test('XY', async ({ page, homePage }) => { await sketchOnPlaneAndBackSideTest( - page, - homePage, - 'XY', - { x: 600, y: 388 } // red plane - // { x: 600, y: 400 }, // red plane // clicks grid helper and that causes problems, should fix so that these coords work too. - ) }) + page, + homePage, + 'XY', + { x: 600, y: 388 } // red plane + // { x: 600, y: 400 }, // red plane // clicks grid helper and that causes problems, should fix so that these coords work too. + ) + }) test('YZ', async ({ page, homePage }) => { await sketchOnPlaneAndBackSideTest(page, homePage, 'YZ', { x: 700, y: 250 }) // green plane @@ -95,11 +96,17 @@ test.describe('Can create sketches on all planes and their back sides', () => { }) test('-XY', async ({ page, homePage }) => { - await sketchOnPlaneAndBackSideTest(page, homePage, '-XY', { x: 600, y: 118 }) // back of red plane + await sketchOnPlaneAndBackSideTest(page, homePage, '-XY', { + x: 600, + y: 118, + }) // back of red plane }) test('-YZ', async ({ page, homePage }) => { - await sketchOnPlaneAndBackSideTest(page, homePage, '-YZ', { x: 700, y: 219 }) // back of green plan + await sketchOnPlaneAndBackSideTest(page, homePage, '-YZ', { + x: 700, + y: 219, + }) // back of green plan }) test('-XZ', async ({ page, homePage }) => { diff --git a/e2e/playwright/code-pane-and-errors.spec.ts b/e2e/playwright/code-pane-and-errors.spec.ts index 39e136af0..0f30e02fe 100644 --- a/e2e/playwright/code-pane-and-errors.spec.ts +++ b/e2e/playwright/code-pane-and-errors.spec.ts @@ -1,22 +1,23 @@ import { test, expect } from './zoo-test' -import { - getUtils, - executorInputPath, -} from './test-utils' +import { getUtils, executorInputPath } from './test-utils' import { join } from 'path' import { bracket } from 'lib/exampleKcl' import { TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW } from './storageStates' import fsp from 'fs/promises' test.describe('Code pane and errors', () => { - test('Typing KCL errors induces a badge on the code pane button', async ({ page, homePage }) => { const u = await getUtils(page) - - // Load the app with the working starter code - await page.addInitScript(() => { - localStorage.setItem( - 'persistCode', - `// Extruded Triangle + test('Typing KCL errors induces a badge on the code pane button', async ({ + page, + homePage, + }) => { + const u = await getUtils(page) + + // Load the app with the working starter code + await page.addInitScript(() => { + localStorage.setItem( + 'persistCode', + `// Extruded Triangle sketch001 = startSketchOn('XZ') |> startProfileAt([0, 0], %) |> line([10, 0], %) @@ -24,191 +25,207 @@ test.describe('Code pane and errors', () => { |> lineTo([profileStartX(%), profileStartY(%)], %) |> close(%) extrude001 = extrude(5, sketch001)` - ) + ) + }) + + await page.setBodyDimensions({ width: 1200, height: 500 }) + await homePage.goToModelingScene() + + // wait for execution done + await u.openDebugPanel() + await u.expectCmdLog('[data-message-type="execution-done"]') + await u.closeDebugPanel() + + // Ensure no badge is present + const codePaneButtonHolder = page.locator('#code-button-holder') + await expect(codePaneButtonHolder).not.toContainText('notification') + + // Delete a character to break the KCL + await u.openKclCodePanel() + await page.getByText('extrude(').click() + await page.keyboard.press('Backspace') + + // Ensure that a badge appears on the button + await expect(codePaneButtonHolder).toContainText('notification') }) - - await page.setBodyDimensions({ width: 1200, height: 500 }) - await homePage.goToModelingScene() - - // wait for execution done - await u.openDebugPanel() - await u.expectCmdLog('[data-message-type="execution-done"]') - await u.closeDebugPanel() - - // Ensure no badge is present - const codePaneButtonHolder = page.locator('#code-button-holder') - await expect(codePaneButtonHolder).not.toContainText('notification') - - // Delete a character to break the KCL - await u.openKclCodePanel() - await page.getByText('extrude(').click() - await page.keyboard.press('Backspace') - - // Ensure that a badge appears on the button - await expect(codePaneButtonHolder).toContainText('notification') }) - test('Opening and closing the code pane will consistently show error diagnostics', async ({ page, homePage }) => { - - const u = await getUtils(page) - - // Load the app with the working starter code - await page.addInitScript((code) => { - localStorage.setItem('persistCode', code) - }, bracket) - - await page.setBodyDimensions({ width: 1200, height: 900 }) - await homePage.goToModelingScene() - - // wait for execution done - await u.openDebugPanel() - await u.expectCmdLog('[data-message-type="execution-done"]') - await u.closeDebugPanel() - - // Ensure we have no errors in the gutter. - await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible() - - // Ensure no badge is present - const codePaneButton = page.getByRole('button', { name: 'KCL Code pane' }) - const codePaneButtonHolder = page.locator('#code-button-holder') - await expect(codePaneButtonHolder).not.toContainText('notification') - - // Delete a character to break the KCL - await u.openKclCodePanel() - await page.getByText('thickness, bracketLeg1Sketch)').click() - await page.keyboard.press('Backspace') - - // Ensure that a badge appears on the button - await expect(codePaneButtonHolder).toContainText('notification') - - // Ensure we have an error diagnostic. - await expect(page.locator('.cm-lint-marker-error')).toBeVisible() - - // error text on hover - await page.hover('.cm-lint-marker-error') - await expect(page.locator('.cm-tooltip').first()).toBeVisible() - - // Close the code pane - await codePaneButton.click() - - await page.waitForTimeout(500) - - // Ensure that a badge appears on the button - await expect(codePaneButtonHolder).toContainText('notification') - // Ensure we have no errors in the gutter. - await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible() - - // Open the code pane - await u.openKclCodePanel() - - // Ensure that a badge appears on the button - await expect(codePaneButtonHolder).toContainText('notification') - - // Ensure we have an error diagnostic. - await expect(page.locator('.cm-lint-marker-error')).toBeVisible() - - // error text on hover - await page.hover('.cm-lint-marker-error') - await expect(page.locator('.cm-tooltip').first()).toBeVisible() }) + test('Opening and closing the code pane will consistently show error diagnostics', async ({ + page, + homePage, + }) => { + const u = await getUtils(page) - test('When error is not in view you can click the badge to scroll to it', async ({ page, homePage, context }) => { const u = await getUtils(page) - - // Load the app with the working starter code - await context.addInitScript((code) => { - localStorage.setItem('persistCode', code) - }, TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW) - - await page.setBodyDimensions({ width: 1200, height: 500 }) - await homePage.goToModelingScene() - - await page.waitForTimeout(1000) - - // Ensure badge is present - const codePaneButtonHolder = page.locator('#code-button-holder') - await expect(codePaneButtonHolder).toContainText('notification') - - // Ensure we have no errors in the gutter, since error out of view. - await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible() - - // Click the badge. - const badge = page.locator('#code-badge') - await expect(badge).toBeVisible() - await badge.click() - - // Ensure we have an error diagnostic. - await expect(page.locator('.cm-lint-marker-error').first()).toBeVisible() - - // Hover over the error to see the error message - await page.hover('.cm-lint-marker-error') - await expect( - page - .getByText( - 'Modeling command failed: [ApiError { error_code: InternalEngine, message: "Solid3D revolve failed: sketch profile must lie entirely on one side of the revolution axis" }]' - ) - .first() - ).toBeVisible() }) + // Load the app with the working starter code + await page.addInitScript((code) => { + localStorage.setItem('persistCode', code) + }, bracket) - test('When error is not in view WITH LINTS you can click the badge to scroll to it', async ({ context, page, homePage }) => { const u = await getUtils(page) - - // Load the app with the working starter code - await context.addInitScript((code) => { - localStorage.setItem('persistCode', code) - }, TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW) - - await page.setBodyDimensions({ width: 1200, height: 500 }) - await homePage.goToModelingScene() - - await page.waitForTimeout(1000) - - // Ensure badge is present - const codePaneButtonHolder = page.locator('#code-button-holder') - await expect(codePaneButtonHolder).toContainText('notification') - - // Ensure we have no errors in the gutter, since error out of view. - await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible() - - // click in the editor to focus it - await page.locator('.cm-content').click() - - await page.waitForTimeout(500) - - // go to the start of the editor and enter more text which will trigger - // a lint error. - // GO to the start of the editor. - await page.keyboard.press('ArrowUp') - await page.keyboard.press('ArrowUp') - await page.keyboard.press('ArrowUp') - await page.keyboard.press('ArrowUp') - await page.keyboard.press('ArrowUp') - await page.keyboard.press('ArrowUp') - await page.keyboard.press('ArrowUp') - await page.keyboard.press('ArrowUp') - await page.keyboard.press('ArrowUp') - await page.keyboard.press('ArrowUp') - await page.keyboard.press('Home') - await page.keyboard.type('foo_bar = 1') - await page.waitForTimeout(500) - await page.keyboard.press('Enter') - - // ensure we have a lint error - await expect(page.locator('.cm-lint-marker-info').first()).toBeVisible() - - // Click the badge. - const badge = page.locator('#code-badge') - await expect(badge).toBeVisible() - await badge.click() - - // Ensure we have an error diagnostic. - await expect(page.locator('.cm-lint-marker-error').first()).toBeVisible() - - // Hover over the error to see the error message - await page.hover('.cm-lint-marker-error') - await expect( - page - .getByText( - 'sketch profile must lie entirely on one side of the revolution axis' - ) - .first() - ).toBeVisible() }) + await page.setBodyDimensions({ width: 1200, height: 900 }) + await homePage.goToModelingScene() + + // wait for execution done + await u.openDebugPanel() + await u.expectCmdLog('[data-message-type="execution-done"]') + await u.closeDebugPanel() + + // Ensure we have no errors in the gutter. + await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible() + + // Ensure no badge is present + const codePaneButton = page.getByRole('button', { name: 'KCL Code pane' }) + const codePaneButtonHolder = page.locator('#code-button-holder') + await expect(codePaneButtonHolder).not.toContainText('notification') + + // Delete a character to break the KCL + await u.openKclCodePanel() + await page.getByText('thickness, bracketLeg1Sketch)').click() + await page.keyboard.press('Backspace') + + // Ensure that a badge appears on the button + await expect(codePaneButtonHolder).toContainText('notification') + + // Ensure we have an error diagnostic. + await expect(page.locator('.cm-lint-marker-error')).toBeVisible() + + // error text on hover + await page.hover('.cm-lint-marker-error') + await expect(page.locator('.cm-tooltip').first()).toBeVisible() + + // Close the code pane + await codePaneButton.click() + + await page.waitForTimeout(500) + + // Ensure that a badge appears on the button + await expect(codePaneButtonHolder).toContainText('notification') + // Ensure we have no errors in the gutter. + await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible() + + // Open the code pane + await u.openKclCodePanel() + + // Ensure that a badge appears on the button + await expect(codePaneButtonHolder).toContainText('notification') + + // Ensure we have an error diagnostic. + await expect(page.locator('.cm-lint-marker-error')).toBeVisible() + + // error text on hover + await page.hover('.cm-lint-marker-error') + await expect(page.locator('.cm-tooltip').first()).toBeVisible() + }) + + test('When error is not in view you can click the badge to scroll to it', async ({ + page, + homePage, + context, + }) => { + const u = await getUtils(page) + + // Load the app with the working starter code + await context.addInitScript((code) => { + localStorage.setItem('persistCode', code) + }, TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW) + + await page.setBodyDimensions({ width: 1200, height: 500 }) + await homePage.goToModelingScene() + + await page.waitForTimeout(1000) + + // Ensure badge is present + const codePaneButtonHolder = page.locator('#code-button-holder') + await expect(codePaneButtonHolder).toContainText('notification') + + // Ensure we have no errors in the gutter, since error out of view. + await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible() + + // Click the badge. + const badge = page.locator('#code-badge') + await expect(badge).toBeVisible() + await badge.click() + + // Ensure we have an error diagnostic. + await expect(page.locator('.cm-lint-marker-error').first()).toBeVisible() + + // Hover over the error to see the error message + await page.hover('.cm-lint-marker-error') + await expect( + page + .getByText( + 'Modeling command failed: [ApiError { error_code: InternalEngine, message: "Solid3D revolve failed: sketch profile must lie entirely on one side of the revolution axis" }]' + ) + .first() + ).toBeVisible() + }) + + test('When error is not in view WITH LINTS you can click the badge to scroll to it', async ({ + context, + page, + homePage, + }) => { + const u = await getUtils(page) + + // Load the app with the working starter code + await context.addInitScript((code) => { + localStorage.setItem('persistCode', code) + }, TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW) + + await page.setBodyDimensions({ width: 1200, height: 500 }) + await homePage.goToModelingScene() + + await page.waitForTimeout(1000) + + // Ensure badge is present + const codePaneButtonHolder = page.locator('#code-button-holder') + await expect(codePaneButtonHolder).toContainText('notification') + + // Ensure we have no errors in the gutter, since error out of view. + await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible() + + // click in the editor to focus it + await page.locator('.cm-content').click() + + await page.waitForTimeout(500) + + // go to the start of the editor and enter more text which will trigger + // a lint error. + // GO to the start of the editor. + await page.keyboard.press('ArrowUp') + await page.keyboard.press('ArrowUp') + await page.keyboard.press('ArrowUp') + await page.keyboard.press('ArrowUp') + await page.keyboard.press('ArrowUp') + await page.keyboard.press('ArrowUp') + await page.keyboard.press('ArrowUp') + await page.keyboard.press('ArrowUp') + await page.keyboard.press('ArrowUp') + await page.keyboard.press('ArrowUp') + await page.keyboard.press('Home') + await page.keyboard.type('foo_bar = 1') + await page.waitForTimeout(500) + await page.keyboard.press('Enter') + + // ensure we have a lint error + await expect(page.locator('.cm-lint-marker-info').first()).toBeVisible() + + // Click the badge. + const badge = page.locator('#code-badge') + await expect(badge).toBeVisible() + await badge.click() + + // Ensure we have an error diagnostic. + await expect(page.locator('.cm-lint-marker-error').first()).toBeVisible() + + // Hover over the error to see the error message + await page.hover('.cm-lint-marker-error') + await expect( + page + .getByText( + 'sketch profile must lie entirely on one side of the revolution axis' + ) + .first() + ).toBeVisible() + }) }) test( @@ -287,7 +304,7 @@ test( { tag: '@electron' }, async ({ context, browserName, page }, testInfo) => { const PROJECT_DIR_NAME = 'lee-was-here' - const { dir: projectsDir, } = await context.folderSetupFn(async (dir) => { + const { dir: projectsDir } = await context.folderSetupFn(async (dir) => { const aProjectDir = join(dir, PROJECT_DIR_NAME) await fsp.mkdir(aProjectDir, { recursive: true }) }) diff --git a/e2e/playwright/command-bar-tests.spec.ts b/e2e/playwright/command-bar-tests.spec.ts index 0d2c533a4..d2ca92178 100644 --- a/e2e/playwright/command-bar-tests.spec.ts +++ b/e2e/playwright/command-bar-tests.spec.ts @@ -5,46 +5,52 @@ import { KCL_DEFAULT_LENGTH } from 'lib/constants' import { normalizeLineEndings } from 'lib/codeEditor' test.describe('Command bar tests', () => { - test('Extrude from command bar selects extrude line after', async ({ page, homePage }) => { await page.addInitScript(async () => { - localStorage.setItem( - 'persistCode', - `sketch001 = startSketchOn('XY') + test('Extrude from command bar selects extrude line after', async ({ + page, + homePage, + }) => { + await page.addInitScript(async () => { + localStorage.setItem( + 'persistCode', + `sketch001 = startSketchOn('XY') |> startProfileAt([-10, -10], %) |> line([20, 0], %) |> line([0, 20], %) |> xLine(-20, %) |> close(%) ` + ) + }) + + const u = await getUtils(page) + await page.setBodyDimensions({ width: 1200, height: 500 }) + + await homePage.goToModelingScene() + + await u.openDebugPanel() + await u.expectCmdLog('[data-message-type="execution-done"]') + await u.closeDebugPanel() + + // Click the line of code for xLine. + await page.getByText(`close(%)`).click() // TODO remove this and reinstate // await topHorzSegmentClick() + await page.waitForTimeout(100) + + await page.getByRole('button', { name: 'Extrude' }).click() + await page.waitForTimeout(200) + await page.keyboard.press('Enter') + await page.waitForTimeout(200) + await page.keyboard.press('Enter') + await page.waitForTimeout(200) + await expect(page.locator('.cm-activeLine')).toHaveText( + `extrude001 = extrude(${KCL_DEFAULT_LENGTH}, sketch001)` ) }) - - const u = await getUtils(page) - await page.setBodyDimensions({ width: 1200, height: 500 }) - - await homePage.goToModelingScene() - - await u.openDebugPanel() - await u.expectCmdLog('[data-message-type="execution-done"]') - await u.closeDebugPanel() - - // Click the line of code for xLine. - await page.getByText(`close(%)`).click() // TODO remove this and reinstate // await topHorzSegmentClick() - await page.waitForTimeout(100) - - await page.getByRole('button', { name: 'Extrude' }).click() - await page.waitForTimeout(200) - await page.keyboard.press('Enter') - await page.waitForTimeout(200) - await page.keyboard.press('Enter') - await page.waitForTimeout(200) - await expect(page.locator('.cm-activeLine')).toHaveText( - `extrude001 = extrude(${KCL_DEFAULT_LENGTH}, sketch001)` - ) }) - test('Fillet from command bar', async ({ page, homePage }) => { await page.addInitScript(async () => { - localStorage.setItem( - 'persistCode', - `sketch001 = startSketchOn('XY') + test('Fillet from command bar', async ({ page, homePage }) => { + await page.addInitScript(async () => { + localStorage.setItem( + 'persistCode', + `sketch001 = startSketchOn('XY') |> startProfileAt([-5, -5], %) |> line([0, 10], %) |> line([10, 0], %) @@ -52,158 +58,170 @@ test.describe('Command bar tests', () => { |> lineTo([profileStartX(%), profileStartY(%)], %) |> close(%) extrude001 = extrude(-10, sketch001)` + ) + }) + + const u = await getUtils(page) + await page.setBodyDimensions({ width: 1000, height: 500 }) + await homePage.goToModelingScene() + await u.openDebugPanel() + await u.expectCmdLog('[data-message-type="execution-done"]') + await u.closeDebugPanel() + + const selectSegment = () => page.getByText(`line([0, -10], %)`).click() + + await selectSegment() + await page.waitForTimeout(100) + await page.getByRole('button', { name: 'Fillet' }).click() + await page.waitForTimeout(100) + await page.keyboard.press('Enter') // skip selection + await page.waitForTimeout(100) + await page.keyboard.press('Enter') // accept default radius + await page.waitForTimeout(100) + await page.keyboard.press('Enter') // submit + await page.waitForTimeout(100) + await expect(page.locator('.cm-activeLine')).toContainText( + `fillet({ radius: ${KCL_DEFAULT_LENGTH}, tags: [seg01] }, %)` ) }) - - const u = await getUtils(page) - await page.setBodyDimensions({ width: 1000, height: 500 }) - await homePage.goToModelingScene() - await u.openDebugPanel() - await u.expectCmdLog('[data-message-type="execution-done"]') - await u.closeDebugPanel() - - const selectSegment = () => page.getByText(`line([0, -10], %)`).click() - - await selectSegment() - await page.waitForTimeout(100) - await page.getByRole('button', { name: 'Fillet' }).click() - await page.waitForTimeout(100) - await page.keyboard.press('Enter') // skip selection - await page.waitForTimeout(100) - await page.keyboard.press('Enter') // accept default radius - await page.waitForTimeout(100) - await page.keyboard.press('Enter') // submit - await page.waitForTimeout(100) - await expect(page.locator('.cm-activeLine')).toContainText( - `fillet({ radius: ${KCL_DEFAULT_LENGTH}, tags: [seg01] }, %)` - ) }) - test('Command bar can change a setting, and switch back and forth between arguments', async ({ page, homePage }) => { const u = await getUtils(page) - await page.setBodyDimensions({ width: 1200, height: 500 }) - await homePage.goToModelingScene() - - const commandBarButton = page.getByRole('button', { name: 'Commands' }) - const cmdSearchBar = page.getByPlaceholder('Search commands') - const commandName = 'debug panel' - const commandOption = page.getByRole('option', { - name: commandName, - exact: false, + test('Command bar can change a setting, and switch back and forth between arguments', async ({ + page, + homePage, + }) => { + const u = await getUtils(page) + await page.setBodyDimensions({ width: 1200, height: 500 }) + await homePage.goToModelingScene() + + const commandBarButton = page.getByRole('button', { name: 'Commands' }) + const cmdSearchBar = page.getByPlaceholder('Search commands') + const commandName = 'debug panel' + const commandOption = page.getByRole('option', { + name: commandName, + exact: false, + }) + const commandLevelArgButton = page.getByRole('button', { name: 'level' }) + const commandThemeArgButton = page.getByRole('button', { name: 'value' }) + const paneSelector = page.getByRole('button', { name: 'debug panel' }) + // This selector changes after we set the setting + let commandOptionInput = page.getByPlaceholder('On') + + await expect( + page.getByRole('button', { name: 'Start Sketch' }) + ).not.toBeDisabled() + + // First try opening the command bar and closing it + await page + .getByRole('button', { name: 'Commands', exact: false }) + .or(page.getByRole('button', { name: '⌘K' })) + .click() + + await expect(cmdSearchBar).toBeVisible() + await page.keyboard.press('Escape') + await expect(cmdSearchBar).not.toBeVisible() + + // Now try the same, but with the keyboard shortcut, check focus + await page.keyboard.press('ControlOrMeta+K') + await expect(cmdSearchBar).toBeVisible() + await expect(cmdSearchBar).toBeFocused() + + // Try typing in the command bar + await cmdSearchBar.fill(commandName) + await expect(commandOption).toBeVisible() + await commandOption.click() + const toggleInput = page.getByPlaceholder('On') + await expect(toggleInput).toBeVisible() + await expect(toggleInput).toBeFocused() + // Select On + await page.keyboard.press('ArrowDown') + await page.keyboard.press('ArrowDown') + await expect(page.getByRole('option', { name: 'Off' })).toHaveAttribute( + 'data-headlessui-state', + 'active' + ) + await page.keyboard.press('Enter') + + // Check the toast appeared + await expect( + page.getByText(`Set show debug panel to "false" for this project`) + ).toBeVisible() + // Check that the visibility changed + await expect(paneSelector).not.toBeVisible() + + commandOptionInput = page.locator('[id="option-input"]') + + // Test case for https://github.com/KittyCAD/modeling-app/issues/2882 + await commandBarButton.click() + await cmdSearchBar.focus() + await cmdSearchBar.fill(commandName) + await commandOption.click() + await expect(commandThemeArgButton).toBeDisabled() + await commandOptionInput.focus() + await commandOptionInput.fill('on') + await commandLevelArgButton.click() + await expect(commandLevelArgButton).toBeDisabled() + + // Test case for https://github.com/KittyCAD/modeling-app/issues/2881 + await commandThemeArgButton.click() + await expect(commandThemeArgButton).toBeDisabled() + await expect(commandLevelArgButton).toHaveText('level: project') }) - const commandLevelArgButton = page.getByRole('button', { name: 'level' }) - const commandThemeArgButton = page.getByRole('button', { name: 'value' }) - const paneSelector = page.getByRole('button', { name: 'debug panel' }) - // This selector changes after we set the setting - let commandOptionInput = page.getByPlaceholder('On') - - await expect( - page.getByRole('button', { name: 'Start Sketch' }) - ).not.toBeDisabled() - - // First try opening the command bar and closing it - await page - .getByRole('button', { name: 'Commands', exact: false }) - .or(page.getByRole('button', { name: '⌘K' })) - .click() - - await expect(cmdSearchBar).toBeVisible() - await page.keyboard.press('Escape') - await expect(cmdSearchBar).not.toBeVisible() - - // Now try the same, but with the keyboard shortcut, check focus - await page.keyboard.press('ControlOrMeta+K') - await expect(cmdSearchBar).toBeVisible() - await expect(cmdSearchBar).toBeFocused() - - // Try typing in the command bar - await cmdSearchBar.fill(commandName) - await expect(commandOption).toBeVisible() - await commandOption.click() - const toggleInput = page.getByPlaceholder('On') - await expect(toggleInput).toBeVisible() - await expect(toggleInput).toBeFocused() - // Select On - await page.keyboard.press('ArrowDown') - await page.keyboard.press('ArrowDown') - await expect(page.getByRole('option', { name: 'Off' })).toHaveAttribute( - 'data-headlessui-state', - 'active' - ) - await page.keyboard.press('Enter') - - // Check the toast appeared - await expect( - page.getByText(`Set show debug panel to "false" for this project`) - ).toBeVisible() - // Check that the visibility changed - await expect(paneSelector).not.toBeVisible() - - commandOptionInput = page.locator('[id="option-input"]') - - // Test case for https://github.com/KittyCAD/modeling-app/issues/2882 - await commandBarButton.click() - await cmdSearchBar.focus() - await cmdSearchBar.fill(commandName) - await commandOption.click() - await expect(commandThemeArgButton).toBeDisabled() - await commandOptionInput.focus() - await commandOptionInput.fill('on') - await commandLevelArgButton.click() - await expect(commandLevelArgButton).toBeDisabled() - - // Test case for https://github.com/KittyCAD/modeling-app/issues/2881 - await commandThemeArgButton.click() - await expect(commandThemeArgButton).toBeDisabled() - await expect(commandLevelArgButton).toHaveText('level: project') }) - test('Command bar keybinding works from code editor and can change a setting', async ({ page, homePage }) => { const u = await getUtils(page) - await page.setBodyDimensions({ width: 1200, height: 500 }) - await homePage.goToModelingScene() - - await expect( - page.getByRole('button', { name: 'Start Sketch' }) - ).not.toBeDisabled() - - // Put the cursor in the code editor - await page.locator('.cm-content').click() - - // Now try the same, but with the keyboard shortcut, check focus - await page.keyboard.press('ControlOrMeta+K') - - let cmdSearchBar = page.getByPlaceholder('Search commands') - await expect(cmdSearchBar).toBeVisible() - await expect(cmdSearchBar).toBeFocused() - - // Try typing in the command bar - await cmdSearchBar.fill('theme') - const themeOption = page.getByRole('option', { - name: 'Settings · app · theme', + test('Command bar keybinding works from code editor and can change a setting', async ({ + page, + homePage, + }) => { + const u = await getUtils(page) + await page.setBodyDimensions({ width: 1200, height: 500 }) + await homePage.goToModelingScene() + + await expect( + page.getByRole('button', { name: 'Start Sketch' }) + ).not.toBeDisabled() + + // Put the cursor in the code editor + await page.locator('.cm-content').click() + + // Now try the same, but with the keyboard shortcut, check focus + await page.keyboard.press('ControlOrMeta+K') + + let cmdSearchBar = page.getByPlaceholder('Search commands') + await expect(cmdSearchBar).toBeVisible() + await expect(cmdSearchBar).toBeFocused() + + // Try typing in the command bar + await cmdSearchBar.fill('theme') + const themeOption = page.getByRole('option', { + name: 'Settings · app · theme', + }) + await expect(themeOption).toBeVisible() + await themeOption.click() + const themeInput = page.getByPlaceholder('dark') + await expect(themeInput).toBeVisible() + await expect(themeInput).toBeFocused() + // Select dark theme + await page.keyboard.press('ArrowDown') + await page.keyboard.press('ArrowDown') + await page.keyboard.press('ArrowDown') + await expect(page.getByRole('option', { name: 'system' })).toHaveAttribute( + 'data-headlessui-state', + 'active' + ) + await page.keyboard.press('Enter') + + // Check the toast appeared + await expect( + page.getByText(`Set theme to "system" as a user default`) + ).toBeVisible() + // Check that the theme changed + await expect(page.locator('body')).not.toHaveClass(`body-bg dark`) }) - await expect(themeOption).toBeVisible() - await themeOption.click() - const themeInput = page.getByPlaceholder('dark') - await expect(themeInput).toBeVisible() - await expect(themeInput).toBeFocused() - // Select dark theme - await page.keyboard.press('ArrowDown') - await page.keyboard.press('ArrowDown') - await page.keyboard.press('ArrowDown') - await expect(page.getByRole('option', { name: 'system' })).toHaveAttribute( - 'data-headlessui-state', - 'active' - ) - await page.keyboard.press('Enter') - - // Check the toast appeared - await expect( - page.getByText(`Set theme to "system" as a user default`) - ).toBeVisible() - // Check that the theme changed - await expect(page.locator('body')).not.toHaveClass(`body-bg dark`) }) - test('Can extrude from the command bar', async ({ page, homePage }) => { await page.addInitScript(async () => { - localStorage.setItem( - 'persistCode', - `distance = sqrt(20) + test('Can extrude from the command bar', async ({ page, homePage }) => { + await page.addInitScript(async () => { + localStorage.setItem( + 'persistCode', + `distance = sqrt(20) sketch001 = startSketchOn('XZ') |> startProfileAt([-6.95, 10.98], %) |> line([25.1, 0.41], %) @@ -211,117 +229,123 @@ test.describe('Command bar tests', () => { |> line([-23.44, 0.52], %) |> close(%) ` + ) + }) + + const u = await getUtils(page) + await page.setBodyDimensions({ width: 1200, height: 500 }) + + await homePage.goToModelingScene() + + // Make sure the stream is up + await u.openDebugPanel() + await u.expectCmdLog('[data-message-type="execution-done"]') + + await expect( + page.getByRole('button', { name: 'Start Sketch' }) + ).not.toBeDisabled() + await u.clearCommandLogs() + await page.getByRole('button', { name: 'Extrude' }).isEnabled() + + let cmdSearchBar = page.getByPlaceholder('Search commands') + await page.keyboard.press('ControlOrMeta+K') + await expect(cmdSearchBar).toBeVisible() + + // Search for extrude command and choose it + await page.getByRole('option', { name: 'Extrude' }).click() + + // Assert that we're on the selection step + await expect(page.getByRole('button', { name: 'selection' })).toBeDisabled() + // Select a face + await page.mouse.move(700, 200) + await page.mouse.click(700, 200) + + // Assert that we're on the distance step + await expect( + page.getByRole('button', { name: 'distance', exact: false }) + ).toBeDisabled() + + // Assert that the an alternative variable name is chosen, + // since the default variable name is already in use (distance) + await page.getByRole('button', { name: 'Create new variable' }).click() + await expect(page.getByPlaceholder('Variable name')).toHaveValue( + 'distance001' + ) + + const continueButton = page.getByRole('button', { name: 'Continue' }) + const submitButton = page.getByRole('button', { name: 'Submit command' }) + await continueButton.click() + + // Review step and argument hotkeys + await expect(submitButton).toBeEnabled() + await expect(submitButton).toBeFocused() + await submitButton.press('Backspace') + + // Assert we're back on the distance step + await expect( + page.getByRole('button', { name: 'distance', exact: false }) + ).toBeDisabled() + + await continueButton.click() + await submitButton.click() + + await u.waitForCmdReceive('extrude') + + await expect(page.locator('.cm-content')).toContainText( + 'extrude001 = extrude(distance001, sketch001)' ) }) - - const u = await getUtils(page) - await page.setBodyDimensions({ width: 1200, height: 500 }) - - await homePage.goToModelingScene() - - // Make sure the stream is up - await u.openDebugPanel() - await u.expectCmdLog('[data-message-type="execution-done"]') - - await expect( - page.getByRole('button', { name: 'Start Sketch' }) - ).not.toBeDisabled() - await u.clearCommandLogs() - await page.getByRole('button', { name: 'Extrude' }).isEnabled() - - let cmdSearchBar = page.getByPlaceholder('Search commands') - await page.keyboard.press('ControlOrMeta+K') - await expect(cmdSearchBar).toBeVisible() - - // Search for extrude command and choose it - await page.getByRole('option', { name: 'Extrude' }).click() - - // Assert that we're on the selection step - await expect(page.getByRole('button', { name: 'selection' })).toBeDisabled() - // Select a face - await page.mouse.move(700, 200) - await page.mouse.click(700, 200) - - // Assert that we're on the distance step - await expect( - page.getByRole('button', { name: 'distance', exact: false }) - ).toBeDisabled() - - // Assert that the an alternative variable name is chosen, - // since the default variable name is already in use (distance) - await page.getByRole('button', { name: 'Create new variable' }).click() - await expect(page.getByPlaceholder('Variable name')).toHaveValue( - 'distance001' - ) - - const continueButton = page.getByRole('button', { name: 'Continue' }) - const submitButton = page.getByRole('button', { name: 'Submit command' }) - await continueButton.click() - - // Review step and argument hotkeys - await expect(submitButton).toBeEnabled() - await expect(submitButton).toBeFocused() - await submitButton.press('Backspace') - - // Assert we're back on the distance step - await expect( - page.getByRole('button', { name: 'distance', exact: false }) - ).toBeDisabled() - - await continueButton.click() - await submitButton.click() - - await u.waitForCmdReceive('extrude') - await expect(page.locator('.cm-content')).toContainText('extrude001 = extrude(distance001, sketch001)') + test('Can switch between sketch tools via command bar', async ({ + page, + homePage, + }) => { + const u = await getUtils(page) + await page.setBodyDimensions({ width: 1200, height: 500 }) + await homePage.goToModelingScene() - }) + const sketchButton = page.getByRole('button', { name: 'Start Sketch' }) + const cmdBarButton = page.getByRole('button', { name: 'Commands' }) + const rectangleToolCommand = page.getByRole('option', { + name: 'rectangle', + }) + const rectangleToolButton = page.getByRole('button', { + name: 'rectangle Corner rectangle', + }) + const lineToolCommand = page.getByRole('option', { + name: 'Line', + }) + const lineToolButton = page.getByRole('button', { + name: 'line Line', + exact: true, + }) + const arcToolCommand = page.getByRole('option', { name: 'Tangential Arc' }) + const arcToolButton = page.getByRole('button', { + name: 'arc Tangential Arc', + }) - test('Can switch between sketch tools via command bar', async ({ page, homePage }) => { const u = await getUtils(page) - await page.setBodyDimensions({ width: 1200, height: 500 }) - await homePage.goToModelingScene() - - const sketchButton = page.getByRole('button', { name: 'Start Sketch' }) - const cmdBarButton = page.getByRole('button', { name: 'Commands' }) - const rectangleToolCommand = page.getByRole('option', { - name: 'rectangle', + // Start a sketch + await sketchButton.click() + await page.mouse.click(700, 200) + + // Switch between sketch tools via the command bar + await expect(lineToolButton).toHaveAttribute('aria-pressed', 'true') + await cmdBarButton.click() + await rectangleToolCommand.click() + await expect(rectangleToolButton).toHaveAttribute('aria-pressed', 'true') + await cmdBarButton.click() + await lineToolCommand.click() + await expect(lineToolButton).toHaveAttribute('aria-pressed', 'true') + + // Click in the scene a couple times to draw a line + // so tangential arc is valid + await page.mouse.click(700, 200) + await page.mouse.move(700, 300, { steps: 5 }) + await page.mouse.click(700, 300) + + // switch to tangential arc via command bar + await cmdBarButton.click() + await arcToolCommand.click() + await expect(arcToolButton).toHaveAttribute('aria-pressed', 'true') }) - const rectangleToolButton = page.getByRole('button', { - name: 'rectangle Corner rectangle', - }) - const lineToolCommand = page.getByRole('option', { - name: 'Line', - }) - const lineToolButton = page.getByRole('button', { - name: 'line Line', - exact: true, - }) - const arcToolCommand = page.getByRole('option', { name: 'Tangential Arc' }) - const arcToolButton = page.getByRole('button', { - name: 'arc Tangential Arc', - }) - - // Start a sketch - await sketchButton.click() - await page.mouse.click(700, 200) - - // Switch between sketch tools via the command bar - await expect(lineToolButton).toHaveAttribute('aria-pressed', 'true') - await cmdBarButton.click() - await rectangleToolCommand.click() - await expect(rectangleToolButton).toHaveAttribute('aria-pressed', 'true') - await cmdBarButton.click() - await lineToolCommand.click() - await expect(lineToolButton).toHaveAttribute('aria-pressed', 'true') - - // Click in the scene a couple times to draw a line - // so tangential arc is valid - await page.mouse.click(700, 200) - await page.mouse.move(700, 300, { steps: 5 }) - await page.mouse.click(700, 300) - - // switch to tangential arc via command bar - await cmdBarButton.click() - await arcToolCommand.click() - await expect(arcToolButton).toHaveAttribute('aria-pressed', 'true') }) }) diff --git a/e2e/playwright/copilot-ghost-test.spec.ts b/e2e/playwright/copilot-ghost-test.spec.ts index e2afe8f7c..ae84339f0 100644 --- a/e2e/playwright/copilot-ghost-test.spec.ts +++ b/e2e/playwright/copilot-ghost-test.spec.ts @@ -1,23 +1,16 @@ -import { test, expect } from '@playwright/test' -import { getUtils, setup, tearDown } from './test-utils' +import { test, expect } from './zoo-test' +import { getUtils } from './test-utils' -test.beforeEach(async ({ context, page }, testInfo) => { - await setup(context, page, testInfo) -}) - -test.afterEach(async ({ page }, testInfo) => { - await tearDown(page, testInfo) -}) test.describe('Copilot ghost text', () => { // eslint-disable-next-line jest/valid-title test.skip(true, 'Needs to get covered again') - test('completes code in empty file', async ({ page }) => { + test('completes code in empty file', async ({ page, homePage }) => { const u = await getUtils(page) // const PUR = 400 / 37.5 //pixeltoUnitRatio - await page.setViewportSize({ width: 1200, height: 500 }) + await page.setBodyDimensions({ width: 1200, height: 500 }) - await u.waitForAuthSkipAppStart() + await homePage.goToModelingScene() await u.codeLocator.click() await expect(page.locator('.cm-content')).toHaveText(``) @@ -55,9 +48,9 @@ test.describe('Copilot ghost text', () => { }) => { const u = await getUtils(page) // const PUR = 400 / 37.5 //pixeltoUnitRatio - await page.setViewportSize({ width: 1200, height: 500 }) + await page.setBodyDimensions({ width: 1200, height: 500 }) - await u.waitForAuthSkipAppStart() + await homePage.goToModelingScene() await u.codeLocator.click() await expect(page.locator('.cm-content')).toHaveText(``) @@ -101,12 +94,13 @@ test.describe('Copilot ghost text', () => { test('copilot disabled in sketch mode after selecting plane', async ({ page, + homePage, }) => { const u = await getUtils(page) // const PUR = 400 / 37.5 //pixeltoUnitRatio - await page.setViewportSize({ width: 1200, height: 500 }) + await page.setBodyDimensions({ width: 1200, height: 500 }) - await u.waitForAuthSkipAppStart() + await homePage.goToModelingScene() await u.codeLocator.click() await expect(page.locator('.cm-content')).toHaveText(``) @@ -184,12 +178,12 @@ test.describe('Copilot ghost text', () => { await expect(page.locator('.cm-ghostText')).not.toBeVisible() }) - test('ArrowUp in code rejects the suggestion', async ({ page }) => { + test('ArrowUp in code rejects the suggestion', async ({ page, homePage }) => { const u = await getUtils(page) // const PUR = 400 / 37.5 //pixeltoUnitRatio - await page.setViewportSize({ width: 1200, height: 500 }) + await page.setBodyDimensions({ width: 1200, height: 500 }) - await u.waitForAuthSkipAppStart() + await homePage.goToModelingScene() await u.codeLocator.click() await expect(page.locator('.cm-content')).toHaveText(``) @@ -212,12 +206,15 @@ test.describe('Copilot ghost text', () => { await expect(page.locator('.cm-content')).toHaveText(``) }) - test('ArrowDown in code rejects the suggestion', async ({ page }) => { + test('ArrowDown in code rejects the suggestion', async ({ + page, + homePage, + }) => { const u = await getUtils(page) // const PUR = 400 / 37.5 //pixeltoUnitRatio - await page.setViewportSize({ width: 1200, height: 500 }) + await page.setBodyDimensions({ width: 1200, height: 500 }) - await u.waitForAuthSkipAppStart() + await homePage.goToModelingScene() await u.codeLocator.click() await expect(page.locator('.cm-content')).toHaveText(``) @@ -240,12 +237,15 @@ test.describe('Copilot ghost text', () => { await expect(page.locator('.cm-content')).toHaveText(``) }) - test('ArrowLeft in code rejects the suggestion', async ({ page }) => { + test('ArrowLeft in code rejects the suggestion', async ({ + page, + homePage, + }) => { const u = await getUtils(page) // const PUR = 400 / 37.5 //pixeltoUnitRatio - await page.setViewportSize({ width: 1200, height: 500 }) + await page.setBodyDimensions({ width: 1200, height: 500 }) - await u.waitForAuthSkipAppStart() + await homePage.goToModelingScene() await u.codeLocator.click() await expect(page.locator('.cm-content')).toHaveText(``) @@ -268,12 +268,15 @@ test.describe('Copilot ghost text', () => { await expect(page.locator('.cm-content')).toHaveText(``) }) - test('ArrowRight in code rejects the suggestion', async ({ page }) => { + test('ArrowRight in code rejects the suggestion', async ({ + page, + homePage, + }) => { const u = await getUtils(page) // const PUR = 400 / 37.5 //pixeltoUnitRatio - await page.setViewportSize({ width: 1200, height: 500 }) + await page.setBodyDimensions({ width: 1200, height: 500 }) - await u.waitForAuthSkipAppStart() + await homePage.goToModelingScene() await u.codeLocator.click() await expect(page.locator('.cm-content')).toHaveText(``) @@ -296,12 +299,12 @@ test.describe('Copilot ghost text', () => { await expect(page.locator('.cm-content')).toHaveText(``) }) - test('Enter in code scoots it down', async ({ page }) => { + test('Enter in code scoots it down', async ({ page, homePage }) => { const u = await getUtils(page) // const PUR = 400 / 37.5 //pixeltoUnitRatio - await page.setViewportSize({ width: 1200, height: 500 }) + await page.setBodyDimensions({ width: 1200, height: 500 }) - await u.waitForAuthSkipAppStart() + await homePage.goToModelingScene() await u.codeLocator.click() await expect(page.locator('.cm-content')).toHaveText(``) @@ -326,12 +329,15 @@ test.describe('Copilot ghost text', () => { ) }) - test('Ctrl+shift+z in code rejects the suggestion', async ({ page }) => { + test('Ctrl+shift+z in code rejects the suggestion', async ({ + page, + homePage, + }) => { const u = await getUtils(page) // const PUR = 400 / 37.5 //pixeltoUnitRatio - await page.setViewportSize({ width: 1200, height: 500 }) + await page.setBodyDimensions({ width: 1200, height: 500 }) - await u.waitForAuthSkipAppStart() + await homePage.goToModelingScene() await u.codeLocator.click() await expect(page.locator('.cm-content')).toHaveText(``) @@ -360,12 +366,13 @@ test.describe('Copilot ghost text', () => { test('Ctrl+z in code rejects the suggestion and undos the last code', async ({ page, + homePage, }) => { const u = await getUtils(page) // const PUR = 400 / 37.5 //pixeltoUnitRatio - await page.setViewportSize({ width: 1200, height: 500 }) + await page.setBodyDimensions({ width: 1200, height: 500 }) - await u.waitForAuthSkipAppStart() + await homePage.goToModelingScene() await page.waitForTimeout(800) await u.codeLocator.click() @@ -420,98 +427,107 @@ test.describe('Copilot ghost text', () => { await expect(page.locator('.cm-ghostText').first()).not.toBeVisible() // TODO when we make codemirror a widget, we can test this. - //await expect(page.locator('.cm-content')).toHaveText(``) - }) + //await expect(page.locator('.cm-content')).toHaveText(``) }) - test('delete in code rejects the suggestion', async ({ page }) => { - const u = await getUtils(page) - // const PUR = 400 / 37.5 //pixeltoUnitRatio - await page.setViewportSize({ width: 1200, height: 500 }) + test('delete in code rejects the suggestion', async ({ + page, + homePage, + }) => { + const u = await getUtils(page) + // const PUR = 400 / 37.5 //pixeltoUnitRatio + await page.setBodyDimensions({ width: 1200, height: 500 }) - await u.waitForAuthSkipAppStart() + await homePage.goToModelingScene() - await u.codeLocator.click() - await expect(page.locator('.cm-content')).toHaveText(``) + await u.codeLocator.click() + await expect(page.locator('.cm-content')).toHaveText(``) - await expect(page.locator('.cm-ghostText')).not.toBeVisible() - await page.waitForTimeout(500) - await page.keyboard.press('Enter') - await page.keyboard.press('Enter') - await page.keyboard.press('Enter') - await expect(page.locator('.cm-ghostText').first()).toBeVisible() - await expect(page.locator('.cm-content')).toHaveText( - `fn cube = (pos, scale) => { sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)` - ) - await expect(page.locator('.cm-ghostText').first()).toHaveText( - `fn cube = (pos, scale) => {` - ) + await expect(page.locator('.cm-ghostText')).not.toBeVisible() + await page.waitForTimeout(500) + await page.keyboard.press('Enter') + await page.keyboard.press('Enter') + await page.keyboard.press('Enter') + await expect(page.locator('.cm-ghostText').first()).toBeVisible() + await expect(page.locator('.cm-content')).toHaveText( + `fn cube = (pos, scale) => { sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)` + ) + await expect(page.locator('.cm-ghostText').first()).toHaveText( + `fn cube = (pos, scale) => {` + ) - // Going elsewhere in the code should hide the ghost text. - await page.keyboard.press('Delete') - await expect(page.locator('.cm-ghostText').first()).not.toBeVisible() + // Going elsewhere in the code should hide the ghost text. + await page.keyboard.press('Delete') + await expect(page.locator('.cm-ghostText').first()).not.toBeVisible() - await expect(page.locator('.cm-content')).toHaveText(``) - }) + await expect(page.locator('.cm-content')).toHaveText(``) + }) - test('backspace in code rejects the suggestion', async ({ page }) => { - const u = await getUtils(page) - // const PUR = 400 / 37.5 //pixeltoUnitRatio - await page.setViewportSize({ width: 1200, height: 500 }) + test('backspace in code rejects the suggestion', async ({ + page, + homePage, + }) => { + const u = await getUtils(page) + // const PUR = 400 / 37.5 //pixeltoUnitRatio + await page.setBodyDimensions({ width: 1200, height: 500 }) - await u.waitForAuthSkipAppStart() + await homePage.goToModelingScene() - await u.codeLocator.click() - await expect(page.locator('.cm-content')).toHaveText(``) + await u.codeLocator.click() + await expect(page.locator('.cm-content')).toHaveText(``) - await expect(page.locator('.cm-ghostText')).not.toBeVisible() - await page.waitForTimeout(500) - await page.keyboard.press('Enter') - await page.keyboard.press('Enter') - await page.keyboard.press('Enter') - await expect(page.locator('.cm-ghostText').first()).toBeVisible() - await expect(page.locator('.cm-content')).toHaveText( - `fn cube = (pos, scale) => { sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)` - ) - await expect(page.locator('.cm-ghostText').first()).toHaveText( - `fn cube = (pos, scale) => {` - ) + await expect(page.locator('.cm-ghostText')).not.toBeVisible() + await page.waitForTimeout(500) + await page.keyboard.press('Enter') + await page.keyboard.press('Enter') + await page.keyboard.press('Enter') + await expect(page.locator('.cm-ghostText').first()).toBeVisible() + await expect(page.locator('.cm-content')).toHaveText( + `fn cube = (pos, scale) => { sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)` + ) + await expect(page.locator('.cm-ghostText').first()).toHaveText( + `fn cube = (pos, scale) => {` + ) - // Going elsewhere in the code should hide the ghost text. - await page.keyboard.press('Backspace') - await expect(page.locator('.cm-ghostText').first()).not.toBeVisible() + // Going elsewhere in the code should hide the ghost text. + await page.keyboard.press('Backspace') + await expect(page.locator('.cm-ghostText').first()).not.toBeVisible() - await expect(page.locator('.cm-content')).toHaveText(``) - }) + await expect(page.locator('.cm-content')).toHaveText(``) + }) - test('focus outside code pane rejects the suggestion', async ({ page }) => { - const u = await getUtils(page) - // const PUR = 400 / 37.5 //pixeltoUnitRatio - await page.setViewportSize({ width: 1200, height: 500 }) + test('focus outside code pane rejects the suggestion', async ({ + page, + homePage, + }) => { + const u = await getUtils(page) + // const PUR = 400 / 37.5 //pixeltoUnitRatio + await page.setBodyDimensions({ width: 1200, height: 500 }) - await u.waitForAuthSkipAppStart() + await homePage.goToModelingScene() - await u.codeLocator.click() - await expect(page.locator('.cm-content')).toHaveText(``) + await u.codeLocator.click() + await expect(page.locator('.cm-content')).toHaveText(``) - await expect(page.locator('.cm-ghostText')).not.toBeVisible() - await page.waitForTimeout(500) - await page.keyboard.press('Enter') - await expect(page.locator('.cm-ghostText').first()).toBeVisible() - await expect(page.locator('.cm-content')).toHaveText( - `fn cube = (pos, scale) => { sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)` - ) - await expect(page.locator('.cm-ghostText').first()).toHaveText( - `fn cube = (pos, scale) => {` - ) + await expect(page.locator('.cm-ghostText')).not.toBeVisible() + await page.waitForTimeout(500) + await page.keyboard.press('Enter') + await expect(page.locator('.cm-ghostText').first()).toBeVisible() + await expect(page.locator('.cm-content')).toHaveText( + `fn cube = (pos, scale) => { sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)` + ) + await expect(page.locator('.cm-ghostText').first()).toHaveText( + `fn cube = (pos, scale) => {` + ) - // Going outside the editor should hide the ghost text. - await page.mouse.move(0, 0) - await page - .getByRole('button', { name: 'Start Sketch' }) - .waitFor({ state: 'visible' }) - await page.getByRole('button', { name: 'Start Sketch' }).click() - await expect(page.locator('.cm-ghostText').first()).not.toBeVisible() + // Going outside the editor should hide the ghost text. + await page.mouse.move(0, 0) + await page + .getByRole('button', { name: 'Start Sketch' }) + .waitFor({ state: 'visible' }) + await page.getByRole('button', { name: 'Start Sketch' }).click() + await expect(page.locator('.cm-ghostText').first()).not.toBeVisible() - await expect(page.locator('.cm-content')).toHaveText(``) + await expect(page.locator('.cm-content')).toHaveText(``) + }) }) }) diff --git a/e2e/playwright/fixtures/fixtureSetup.ts b/e2e/playwright/fixtures/fixtureSetup.ts index b43f0de1f..427eb7a85 100644 --- a/e2e/playwright/fixtures/fixtureSetup.ts +++ b/e2e/playwright/fixtures/fixtureSetup.ts @@ -65,7 +65,7 @@ export class AuthenticatedTronApp { public readonly testInfo: TestInfo public electronApp?: ElectronApplication public readonly viewPortSize = { width: 1200, height: 500 } - public dir: string = "" + public dir: string = '' constructor(context: BrowserContext, page: Page, testInfo: TestInfo) { this._page = page diff --git a/e2e/playwright/sketch-tests.spec.ts b/e2e/playwright/sketch-tests.spec.ts index 03e6f342a..d207120c1 100644 --- a/e2e/playwright/sketch-tests.spec.ts +++ b/e2e/playwright/sketch-tests.spec.ts @@ -12,22 +12,24 @@ import { } from './test-utils' import { uuidv4, roundOff } from 'lib/utils' - - test.describe('Sketch tests', () => { - test('multi-sketch file shows multiple Edit Sketch buttons', async ({ page, context, - homePage }) => { const u = await getUtils(page) - const selectionsSnippets = { - startProfileAt1: - '|> startProfileAt([-width / 4 + screwRadius, height / 2], %)', - startProfileAt2: '|> startProfileAt([-width / 2, 0], %)', - startProfileAt3: '|> startProfileAt([0, thickness], %)', - } - await context.addInitScript( - async ({ startProfileAt1, startProfileAt2, startProfileAt3 }: any) => { - localStorage.setItem( - 'persistCode', - ` + test('multi-sketch file shows multiple Edit Sketch buttons', async ({ + page, + context, + homePage, + }) => { + const u = await getUtils(page) + const selectionsSnippets = { + startProfileAt1: + '|> startProfileAt([-width / 4 + screwRadius, height / 2], %)', + startProfileAt2: '|> startProfileAt([-width / 2, 0], %)', + startProfileAt3: '|> startProfileAt([0, thickness], %)', + } + await context.addInitScript( + async ({ startProfileAt1, startProfileAt2, startProfileAt3 }: any) => { + localStorage.setItem( + 'persistCode', + ` width = 20 height = 10 thickness = 5 @@ -68,108 +70,119 @@ test.describe('Sketch tests', () => { |> close(%) |> extrude(-height, %) ` - ) - }, - selectionsSnippets - ) - await page.setBodyDimensions({ width: 1200, height: 500 }) - - await homePage.goToModelingScene() - - // wait for execution done - await u.openDebugPanel() - await u.expectCmdLog('[data-message-type="execution-done"]') - await u.closeDebugPanel() - - await page.getByText(selectionsSnippets.startProfileAt1).click() - await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled() - await expect( - page.getByRole('button', { name: 'Edit Sketch' }) - ).toBeVisible() - - await page.getByText(selectionsSnippets.startProfileAt2).click() - await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled() - await expect( - page.getByRole('button', { name: 'Edit Sketch' }) - ).toBeVisible() - - await page.getByText(selectionsSnippets.startProfileAt3).click() - await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled() - await expect( - page.getByRole('button', { name: 'Edit Sketch' }) - ).toBeVisible() }) - test('Can delete most of a sketch and the line tool will still work', async ({ page, center, - homePage }) => { const u = await getUtils(page) - await page.addInitScript(async () => { - localStorage.setItem( - 'persistCode', - `sketch001 = startSketchOn('XZ') + ) + }, + selectionsSnippets + ) + await page.setBodyDimensions({ width: 1200, height: 500 }) + + await homePage.goToModelingScene() + + // wait for execution done + await u.openDebugPanel() + await u.expectCmdLog('[data-message-type="execution-done"]') + await u.closeDebugPanel() + + await page.getByText(selectionsSnippets.startProfileAt1).click() + await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled() + await expect( + page.getByRole('button', { name: 'Edit Sketch' }) + ).toBeVisible() + + await page.getByText(selectionsSnippets.startProfileAt2).click() + await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled() + await expect( + page.getByRole('button', { name: 'Edit Sketch' }) + ).toBeVisible() + + await page.getByText(selectionsSnippets.startProfileAt3).click() + await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled() + await expect( + page.getByRole('button', { name: 'Edit Sketch' }) + ).toBeVisible() + }) + test('Can delete most of a sketch and the line tool will still work', async ({ + page, + center, + homePage, + }) => { + const u = await getUtils(page) + await page.addInitScript(async () => { + localStorage.setItem( + 'persistCode', + `sketch001 = startSketchOn('XZ') |> startProfileAt([4.61, -14.01], %) |> xLine(12.73, %) |> tangentialArcTo([24.95, -5.38], %)` - ) - }) - - await homePage.goToModelingScene() - - await expect(async () => { + ) + }) + + await homePage.goToModelingScene() + + await expect(async () => { + await page.getByText('tangentialArcTo([24.95, -5.38], %)').click() + await expect( + page.getByRole('button', { name: 'Edit Sketch' }) + ).toBeEnabled({ timeout: 1000 }) + 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('tangentialArcTo([24.95, -5.38], %)').click() - await expect( - page.getByRole('button', { name: 'Edit Sketch' }) - ).toBeEnabled({ timeout: 1000 }) - 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('tangentialArcTo([24.95, -5.38], %)').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(100) - - await expect(async () => { - await page.mouse.move(700, 200, { step: 25 }) - await page.mouse.click(700, 200) - - await expect.poll(u.crushKclCodeIntoOneLineAndThenMaybeSome, { timeout: 1000 }) - .toBe(`sketch001 = startSketchOn('XZ') + 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(100) + + await expect(async () => { + await page.mouse.move(700, 200, { step: 25 }) + await page.mouse.click(700, 200) + + await expect + .poll(u.crushKclCodeIntoOneLineAndThenMaybeSome, { timeout: 1000 }) + .toBe( + `sketch001 = startSketchOn('XZ') |> startProfileAt([4.61,-14.01], %) |> yLine(15.95, %) -`.replaceAll(' ', '').replaceAll("\n", '')) - }).toPass({ timeout: 40_000, intervals: [1_000] }) - +` + .replaceAll(' ', '') + .replaceAll('\n', '') + ) + }).toPass({ timeout: 40_000, intervals: [1_000] }) }) - test('Can exit selection of face', async ({ page, homePage }) => { // Load the app with the code panes - await page.addInitScript(async () => { - localStorage.setItem('persistCode', ``) + test('Can exit selection of face', async ({ page, homePage }) => { + // Load the app with the code panes + await page.addInitScript(async () => { + localStorage.setItem('persistCode', ``) + }) + + const u = await getUtils(page) + await page.setBodyDimensions({ width: 1200, height: 500 }) + + await homePage.goToModelingScene() + + await page.getByRole('button', { name: 'Start Sketch' }).click() + await expect( + page.getByRole('button', { name: 'Exit Sketch' }) + ).toBeVisible() + + await expect(page.getByText('select a plane or face')).toBeVisible() + + await page.keyboard.press('Escape') + await expect( + page.getByRole('button', { name: 'Start Sketch' }) + ).toBeVisible() }) - - const u = await getUtils(page) - await page.setBodyDimensions({ width: 1200, height: 500 }) - - await homePage.goToModelingScene() - - await page.getByRole('button', { name: 'Start Sketch' }).click() - await expect( - page.getByRole('button', { name: 'Exit Sketch' }) - ).toBeVisible() - - await expect(page.getByText('select a plane or face')).toBeVisible() - - await page.keyboard.press('Escape') - await expect( - page.getByRole('button', { name: 'Start Sketch' }) - ).toBeVisible() }) test.describe('Can edit segments by dragging their handles', () => { const doEditSegmentsByDraggingHandle = async ( page: Page, @@ -304,397 +317,418 @@ test.describe('Sketch tests', () => { |> line([1.97, 2.06], %) |> close(%)`) } - test('code pane open at start-handles', async ({ page, homePage }) => { // Load the app with the code panes + test('code pane open at start-handles', async ({ page, homePage }) => { + // Load the app with the code panes + await page.addInitScript(async () => { + localStorage.setItem( + 'store', + JSON.stringify({ + state: { + openPanes: ['code'], + }, + version: 0, + }) + ) + }) + await doEditSegmentsByDraggingHandle(page, homePage, ['code']) + }) + + test('code pane closed at start-handles', async ({ page, homePage }) => { + // Load the app with the code panes + await page.addInitScript(async (persistModelingContext) => { + localStorage.setItem( + persistModelingContext, + JSON.stringify({ openPanes: [] }) + ) + }, PERSIST_MODELING_CONTEXT) + await doEditSegmentsByDraggingHandle(page, homePage, []) + }) + }) + + test('Can edit a circle center and radius by dragging its handles', async ({ + page, + homePage, + }) => { + const u = await getUtils(page) await page.addInitScript(async () => { localStorage.setItem( - 'store', - JSON.stringify({ - state: { - openPanes: ['code'], - }, - version: 0, - }) - ) - }) - await doEditSegmentsByDraggingHandle(page, homePage, ['code']) }) - - test('code pane closed at start-handles', async ({ page, homePage }) => { // Load the app with the code panes - await page.addInitScript(async (persistModelingContext) => { - localStorage.setItem( - persistModelingContext, - JSON.stringify({ openPanes: [] }) - ) - }, PERSIST_MODELING_CONTEXT) - await doEditSegmentsByDraggingHandle(page, homePage, []) }) - }) - - test('Can edit a circle center and radius by dragging its handles', async ({ page, homePage }) => { const u = await getUtils(page) - await page.addInitScript(async () => { - localStorage.setItem( - 'persistCode', - `sketch001 = startSketchOn('XZ') + 'persistCode', + `sketch001 = startSketchOn('XZ') |> circle({ center: [4.61, -5.01], radius: 8 }, %)` - ) - }) - - await homePage.goToModelingScene() - - await expect( - page.getByRole('button', { name: 'Start Sketch' }) - ).not.toBeDisabled() - - 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 = [667, 325] - - const dragPX = 40 - - await page - .getByText('circle({ center: [4.61, -5.01], radius: 8 }, %)') - .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(1) - - await test.step('drag circle center handle', async () => { - await page.dragAndDrop('#stream', '#stream', { - sourcePosition: { x: startPX[0], y: startPX[1] }, - targetPosition: { x: startPX[0] + dragPX, y: startPX[1] - dragPX }, + ) + }) + + await homePage.goToModelingScene() + + await expect( + page.getByRole('button', { name: 'Start Sketch' }) + ).not.toBeDisabled() + + 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 expect(page.locator('.cm-content')).not.toHaveText(prevContent) - prevContent = await page.locator('.cm-content').innerText() - }) - - await test.step('drag circle radius handle', async () => { - await page.waitForTimeout(100) - - 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 * 2, y: lineEnd.y + dragPX }, + await u.sendCustomCmd({ + type: 'modeling_cmd_req', + cmd_id: uuidv4(), + cmd: { + type: 'default_camera_get_settings', + }, }) - await expect(page.locator('.cm-content')).not.toHaveText(prevContent) - prevContent = await page.locator('.cm-content').innerText() - }) - - // expect the code to have changed - await expect(page.locator('.cm-content')) - .toHaveText(`sketch001 = startSketchOn('XZ') + await page.waitForTimeout(100) + + const startPX = [667, 325] + + const dragPX = 40 + + await page + .getByText('circle({ center: [4.61, -5.01], radius: 8 }, %)') + .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(1) + + await test.step('drag circle center handle', async () => { + await page.dragAndDrop('#stream', '#stream', { + sourcePosition: { x: startPX[0], y: startPX[1] }, + 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() + }) + + await test.step('drag circle radius handle', async () => { + await page.waitForTimeout(100) + + 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 * 2, y: lineEnd.y + dragPX }, + }) + await expect(page.locator('.cm-content')).not.toHaveText(prevContent) + prevContent = await page.locator('.cm-content').innerText() + }) + + // expect the code to have changed + await expect(page.locator('.cm-content')) + .toHaveText(`sketch001 = startSketchOn('XZ') |> circle({ center: [7.26, -2.37], radius: 11.44 }, %) - `) }) - test('Can edit a sketch that has been extruded in the same pipe', async ({ page, homePage }) => { const u = await getUtils(page) - await page.addInitScript(async () => { - localStorage.setItem( - 'persistCode', - `sketch001 = startSketchOn('XZ') + `) + }) + test('Can edit a sketch that has been extruded in the same pipe', async ({ + page, + homePage, + }) => { + const u = await getUtils(page) + await page.addInitScript(async () => { + localStorage.setItem( + 'persistCode', + `sketch001 = startSketchOn('XZ') |> startProfileAt([4.61, -10.01], %) |> line([12.73, -0.09], %) |> tangentialArcTo([24.95, -0.38], %) |> close(%) |> extrude(5, %)` - ) - }) - - await homePage.goToModelingScene() - - await expect( - page.getByRole('button', { name: 'Start Sketch' }) - ).not.toBeDisabled() - - 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 = [665, 397] - - const dragPX = 40 - - await page.getByText('startProfileAt([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(2) - - // drag startProfieAt handle - await page.dragAndDrop('#stream', '#stream', { - sourcePosition: { x: startPX[0], y: startPX[1] }, - 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() - - // drag line handle - await page.waitForTimeout(100) - - const lineEnd = await u.getBoundingBox('[data-overlay-index="0"]') - await page.dragAndDrop('#stream', '#stream', { - sourcePosition: { x: lineEnd.x - 15, y: lineEnd.y }, - targetPosition: { x: lineEnd.x, y: lineEnd.y + 15 }, - }) - await page.waitForTimeout(100) - await expect(page.locator('.cm-content')).not.toHaveText(prevContent) - prevContent = await page.locator('.cm-content').innerText() - - // drag tangentialArcTo 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, - y: tangentEnd.y - 15, - }, - }) - await page.waitForTimeout(100) - await expect(page.locator('.cm-content')).not.toHaveText(prevContent) - - // expect the code to have changed - await expect(page.locator('.cm-content')) - .toHaveText(`sketch001 = startSketchOn('XZ') + ) + }) + + await homePage.goToModelingScene() + + await expect( + page.getByRole('button', { name: 'Start Sketch' }) + ).not.toBeDisabled() + + 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 = [665, 397] + + const dragPX = 40 + + await page.getByText('startProfileAt([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(2) + + // drag startProfieAt handle + await page.dragAndDrop('#stream', '#stream', { + sourcePosition: { x: startPX[0], y: startPX[1] }, + 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() + + // drag line handle + await page.waitForTimeout(100) + + const lineEnd = await u.getBoundingBox('[data-overlay-index="0"]') + await page.dragAndDrop('#stream', '#stream', { + sourcePosition: { x: lineEnd.x - 15, y: lineEnd.y }, + targetPosition: { x: lineEnd.x, y: lineEnd.y + 15 }, + }) + await page.waitForTimeout(100) + await expect(page.locator('.cm-content')).not.toHaveText(prevContent) + prevContent = await page.locator('.cm-content').innerText() + + // drag tangentialArcTo 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, + y: tangentEnd.y - 15, + }, + }) + await page.waitForTimeout(100) + await expect(page.locator('.cm-content')).not.toHaveText(prevContent) + + // expect the code to have changed + await expect(page.locator('.cm-content')) + .toHaveText(`sketch001 = startSketchOn('XZ') |> startProfileAt([7.12, -12.68], %) |> line([12.68, -1.09], %) |> tangentialArcTo([24.89, 0.68], %) |> close(%) |> extrude(5, %) - `) }) + `) + }) - test('Can edit a sketch that has been revolved in the same pipe', async ({ page, homePage }) => { const u = await getUtils(page) - await page.addInitScript(async () => { - localStorage.setItem( - 'persistCode', - `sketch001 = startSketchOn('XZ') + test('Can edit a sketch that has been revolved in the same pipe', async ({ + page, + homePage, + }) => { + const u = await getUtils(page) + await page.addInitScript(async () => { + localStorage.setItem( + 'persistCode', + `sketch001 = startSketchOn('XZ') |> startProfileAt([4.61, -14.01], %) |> line([12.73, -0.09], %) |> tangentialArcTo([24.95, -5.38], %) |> close(%) |> revolve({ axis: "X",}, %)` - ) - }) - - await homePage.goToModelingScene() - - await expect( - page.getByRole('button', { name: 'Start Sketch' }) - ).not.toBeDisabled() - - 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 = [665, 458] - - const dragPX = 30 - - await page.getByText('startProfileAt([4.61, -14.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() - - const step5 = { steps: 5 } - - await expect(page.getByTestId('segment-overlay')).toHaveCount(2) - - // drag startProfieAt handle - await page.mouse.move(startPX[0], startPX[1]) - await page.mouse.down() - await page.mouse.move(startPX[0] + dragPX, startPX[1] - dragPX, step5) - await page.mouse.up() - - await expect(page.locator('.cm-content')).not.toHaveText(prevContent) - prevContent = await page.locator('.cm-content').innerText() - - // drag line handle - await page.waitForTimeout(100) - - const lineEnd = await u.getBoundingBox('[data-overlay-index="0"]') - await page.mouse.move(lineEnd.x - 5, lineEnd.y) - await page.mouse.down() - await page.mouse.move(lineEnd.x + dragPX, lineEnd.y - dragPX, step5) - await page.mouse.up() - await page.waitForTimeout(100) - await expect(page.locator('.cm-content')).not.toHaveText(prevContent) - prevContent = await page.locator('.cm-content').innerText() - - // drag tangentialArcTo handle - const tangentEnd = await u.getBoundingBox('[data-overlay-index="1"]') - await page.mouse.move(tangentEnd.x, tangentEnd.y - 5) - await page.mouse.down() - await page.mouse.move(tangentEnd.x + dragPX, tangentEnd.y - dragPX, step5) - await page.mouse.up() - await page.waitForTimeout(100) - await expect(page.locator('.cm-content')).not.toHaveText(prevContent) - - // expect the code to have changed - await expect(page.locator('.cm-content')) - .toHaveText(`sketch001 = startSketchOn('XZ') + ) + }) + + await homePage.goToModelingScene() + + await expect( + page.getByRole('button', { name: 'Start Sketch' }) + ).not.toBeDisabled() + + 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 = [665, 458] + + const dragPX = 30 + + await page.getByText('startProfileAt([4.61, -14.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() + + const step5 = { steps: 5 } + + await expect(page.getByTestId('segment-overlay')).toHaveCount(2) + + // drag startProfieAt handle + await page.mouse.move(startPX[0], startPX[1]) + await page.mouse.down() + await page.mouse.move(startPX[0] + dragPX, startPX[1] - dragPX, step5) + await page.mouse.up() + + await expect(page.locator('.cm-content')).not.toHaveText(prevContent) + prevContent = await page.locator('.cm-content').innerText() + + // drag line handle + await page.waitForTimeout(100) + + const lineEnd = await u.getBoundingBox('[data-overlay-index="0"]') + await page.mouse.move(lineEnd.x - 5, lineEnd.y) + await page.mouse.down() + await page.mouse.move(lineEnd.x + dragPX, lineEnd.y - dragPX, step5) + await page.mouse.up() + await page.waitForTimeout(100) + await expect(page.locator('.cm-content')).not.toHaveText(prevContent) + prevContent = await page.locator('.cm-content').innerText() + + // drag tangentialArcTo handle + const tangentEnd = await u.getBoundingBox('[data-overlay-index="1"]') + await page.mouse.move(tangentEnd.x, tangentEnd.y - 5) + await page.mouse.down() + await page.mouse.move(tangentEnd.x + dragPX, tangentEnd.y - dragPX, step5) + await page.mouse.up() + await page.waitForTimeout(100) + await expect(page.locator('.cm-content')).not.toHaveText(prevContent) + + // expect the code to have changed + await expect(page.locator('.cm-content')) + .toHaveText(`sketch001 = startSketchOn('XZ') |> startProfileAt([6.44, -12.07], %) |> line([14.72, 1.97], %) |> tangentialArcTo([24.95, -5.38], %) |> line([1.97, 2.06], %) |> close(%) - |> revolve({ axis: "X" }, %)`) }) - test('Can add multiple sketches', async ({ page, homePage }) => { const u = await getUtils(page) - - const viewportSize = { width: 1200, height: 500 } - await page.setBodyDimensions(viewportSize) - - await homePage.goToModelingScene() - await u.openDebugPanel() - - const center = { x: viewportSize.width / 2, y: viewportSize.height / 2 } - const { toSU, toU, click00r } = getMovementUtils({ center, page }) - - 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 u.doAndWaitForImageDiff( - () => page.getByRole('button', { name: 'Start Sketch' }).click(), - 200 - ) - - let codeStr = "sketch001 = startSketchOn('XY')" - - await page.mouse.click(center.x, viewportSize.height * 0.55) - await expect(u.codeLocator).toHaveText(codeStr) - await u.closeDebugPanel() - await page.waitForTimeout(500) // TODO detect animation ending, or disable animation - - await click00r(0, 0) - codeStr += ` |> startProfileAt(${toSU([0, 0])}, %)` - await expect(u.codeLocator).toHaveText(codeStr) - - await click00r(50, 0) - await page.waitForTimeout(100) - codeStr += ` |> xLine(${toU(50, 0)[0]}, %)` - await expect(u.codeLocator).toHaveText(codeStr) - - await click00r(0, 50) - codeStr += ` |> yLine(${toU(0, 50)[1]}, %)` - await expect(u.codeLocator).toHaveText(codeStr) - - await click00r(-50, 0) - codeStr += ` |> xLine(${toU(-50, 0)[0]}, %)` - await expect(u.codeLocator).toHaveText(codeStr) - - // exit the sketch, reset relative clicker - await click00r(undefined, undefined) - await u.openAndClearDebugPanel() - await page.getByRole('button', { name: 'Exit Sketch' }).click() - await u.expectCmdLog('[data-message-type="execution-done"]') - await page.waitForTimeout(250) - await u.clearCommandLogs() - - // start a new sketch - await page.getByRole('button', { name: 'Start Sketch' }).click() - - // when exiting the sketch above the camera is still looking down at XY, - // so selecting the plane again is a bit easier. - await page.mouse.click(center.x + 200, center.y + 100) - await page.waitForTimeout(600) // TODO detect animation ending, or disable animation - codeStr += "sketch002 = startSketchOn('XY')" - await expect(u.codeLocator).toHaveText(codeStr) - await u.closeDebugPanel() - - await click00r(30, 0) - codeStr += ` |> startProfileAt([2.03, 0], %)` - await expect(u.codeLocator).toHaveText(codeStr) - - // TODO: I couldn't use `toSU` here because of some rounding error causing - // it to be off by 0.01 - await click00r(30, 0) - codeStr += ` |> xLine(2.04, %)` - await expect(u.codeLocator).toHaveText(codeStr) - - await click00r(0, 30) - codeStr += ` |> yLine(-2.03, %)` - await expect(u.codeLocator).toHaveText(codeStr) - - await click00r(-30, 0) - codeStr += ` |> xLine(-2.04, %)` - await expect(u.codeLocator).toHaveText(codeStr) - - await click00r(undefined, undefined) - await u.openAndClearDebugPanel() - await page.getByRole('button', { name: 'Exit Sketch' }).click() - await u.expectCmdLog('[data-message-type="execution-done"]') - await u.updateCamPosition([100, 100, 100]) - await u.clearCommandLogs() }) + |> revolve({ axis: "X" }, %)`) + }) + test('Can add multiple sketches', async ({ page, homePage }) => { + const u = await getUtils(page) + + const viewportSize = { width: 1200, height: 500 } + await page.setBodyDimensions(viewportSize) + + await homePage.goToModelingScene() + await u.openDebugPanel() + + const center = { x: viewportSize.width / 2, y: viewportSize.height / 2 } + const { toSU, toU, click00r } = getMovementUtils({ center, page }) + + 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 u.doAndWaitForImageDiff( + () => page.getByRole('button', { name: 'Start Sketch' }).click(), + 200 + ) + + let codeStr = "sketch001 = startSketchOn('XY')" + + await page.mouse.click(center.x, viewportSize.height * 0.55) + await expect(u.codeLocator).toHaveText(codeStr) + await u.closeDebugPanel() + await page.waitForTimeout(500) // TODO detect animation ending, or disable animation + + await click00r(0, 0) + codeStr += ` |> startProfileAt(${toSU([0, 0])}, %)` + await expect(u.codeLocator).toHaveText(codeStr) + + await click00r(50, 0) + await page.waitForTimeout(100) + codeStr += ` |> xLine(${toU(50, 0)[0]}, %)` + await expect(u.codeLocator).toHaveText(codeStr) + + await click00r(0, 50) + codeStr += ` |> yLine(${toU(0, 50)[1]}, %)` + await expect(u.codeLocator).toHaveText(codeStr) + + await click00r(-50, 0) + codeStr += ` |> xLine(${toU(-50, 0)[0]}, %)` + await expect(u.codeLocator).toHaveText(codeStr) + + // exit the sketch, reset relative clicker + await click00r(undefined, undefined) + await u.openAndClearDebugPanel() + await page.getByRole('button', { name: 'Exit Sketch' }).click() + await u.expectCmdLog('[data-message-type="execution-done"]') + await page.waitForTimeout(250) + await u.clearCommandLogs() + + // start a new sketch + await page.getByRole('button', { name: 'Start Sketch' }).click() + + // when exiting the sketch above the camera is still looking down at XY, + // so selecting the plane again is a bit easier. + await page.mouse.click(center.x + 200, center.y + 100) + await page.waitForTimeout(600) // TODO detect animation ending, or disable animation + codeStr += "sketch002 = startSketchOn('XY')" + await expect(u.codeLocator).toHaveText(codeStr) + await u.closeDebugPanel() + + await click00r(30, 0) + codeStr += ` |> startProfileAt([2.03, 0], %)` + await expect(u.codeLocator).toHaveText(codeStr) + + // TODO: I couldn't use `toSU` here because of some rounding error causing + // it to be off by 0.01 + await click00r(30, 0) + codeStr += ` |> xLine(2.04, %)` + await expect(u.codeLocator).toHaveText(codeStr) + + await click00r(0, 30) + codeStr += ` |> yLine(-2.03, %)` + await expect(u.codeLocator).toHaveText(codeStr) + + await click00r(-30, 0) + codeStr += ` |> xLine(-2.04, %)` + await expect(u.codeLocator).toHaveText(codeStr) + + await click00r(undefined, undefined) + await u.openAndClearDebugPanel() + await page.getByRole('button', { name: 'Exit Sketch' }).click() + await u.expectCmdLog('[data-message-type="execution-done"]') + await u.updateCamPosition([100, 100, 100]) + await u.clearCommandLogs() + }) test.describe('Snap to close works (at any scale)', () => { const doSnapAtDifferentScales = async ( page: any, @@ -785,17 +819,25 @@ test.describe('Sketch tests', () => { await u.expectCmdLog('[data-message-type="execution-done"]') await u.removeCurrentCode() } - test('[0, 100, 100]', async ({ page, homePage }) => { await homePage.goToModelingScene() - await doSnapAtDifferentScales(page, [0, 100, 100], 0.01) }) + test('[0, 100, 100]', async ({ page, homePage }) => { + await homePage.goToModelingScene() + await doSnapAtDifferentScales(page, [0, 100, 100], 0.01) + }) - test('[0, 10000, 10000]', async ({ page, homePage }) => { await homePage.goToModelingScene() - await doSnapAtDifferentScales(page, [0, 10000, 10000]) }) + test('[0, 10000, 10000]', async ({ page, homePage }) => { + await homePage.goToModelingScene() + await doSnapAtDifferentScales(page, [0, 10000, 10000]) + }) }) - test('exiting a close extrude, has the extrude button enabled ready to go', async ({ page, homePage }) => { // this was a regression https://github.com/KittyCAD/modeling-app/issues/2832 - await page.addInitScript(async () => { - localStorage.setItem( - 'persistCode', - `sketch001 = startSketchOn('XZ') + test('exiting a close extrude, has the extrude button enabled ready to go', async ({ + page, + homePage, + }) => { + // this was a regression https://github.com/KittyCAD/modeling-app/issues/2832 + await page.addInitScript(async () => { + localStorage.setItem( + 'persistCode', + `sketch001 = startSketchOn('XZ') |> startProfileAt([-0.45, 0.87], %) |> line([1.32, 0.38], %) |> line([1.02, -1.32], %, $seg01) @@ -803,52 +845,57 @@ test.describe('Sketch tests', () => { |> lineTo([profileStartX(%), profileStartY(%)], %) |> close(%) ` + ) + }) + + const u = await getUtils(page) + await page.setBodyDimensions({ width: 1200, height: 500 }) + + await homePage.goToModelingScene() + + // wait for execution done + await u.openDebugPanel() + await u.expectCmdLog('[data-message-type="execution-done"]') + await u.closeDebugPanel() + + // click "line([1.32, 0.38], %)" + await page.getByText(`line([1.32, 0.38], %)`).click() + await page.waitForTimeout(100) + await expect(page.getByRole('button', { name: 'Edit Sketch' })).toBeEnabled( + { timeout: 10_000 } ) + // click edit sketch + await page.getByRole('button', { name: 'Edit Sketch' }).click() + await page.waitForTimeout(600) // wait for animation + + // exit sketch + await page.getByRole('button', { name: 'Exit Sketch' }).click() + + // expect extrude button to be enabled + await expect( + page.getByRole('button', { name: 'Extrude' }) + ).not.toBeDisabled() + + // click extrude + await page.getByRole('button', { name: 'Extrude' }).click() + + // sketch selection should already have been made. "Selection: 1 face" only show up when the selection has been made already + // otherwise the cmdbar would be waiting for a selection. + await expect( + page.getByRole('button', { name: 'selection : 1 face', exact: false }) + ).toBeVisible({ + timeout: 10_000, + }) }) - - const u = await getUtils(page) - await page.setBodyDimensions({ width: 1200, height: 500 }) - - await homePage.goToModelingScene() - - // wait for execution done - await u.openDebugPanel() - await u.expectCmdLog('[data-message-type="execution-done"]') - await u.closeDebugPanel() - - // click "line([1.32, 0.38], %)" - await page.getByText(`line([1.32, 0.38], %)`).click() - await page.waitForTimeout(100) - await expect(page.getByRole('button', { name: 'Edit Sketch' })).toBeEnabled( - { timeout: 10_000 } - ) - // click edit sketch - await page.getByRole('button', { name: 'Edit Sketch' }).click() - await page.waitForTimeout(600) // wait for animation - - // exit sketch - await page.getByRole('button', { name: 'Exit Sketch' }).click() - - // expect extrude button to be enabled - await expect( - page.getByRole('button', { name: 'Extrude' }) - ).not.toBeDisabled() - - // click extrude - await page.getByRole('button', { name: 'Extrude' }).click() - - // sketch selection should already have been made. "Selection: 1 face" only show up when the selection has been made already - // otherwise the cmdbar would be waiting for a selection. - await expect( - page.getByRole('button', { name: 'selection : 1 face', exact: false }) - ).toBeVisible({ - timeout: 10_000, - }) }) - test("Existing sketch with bad code delete user's code", async ({ page, homePage }) => { // this was a regression https://github.com/KittyCAD/modeling-app/issues/2832 - await page.addInitScript(async () => { - localStorage.setItem( - 'persistCode', - `sketch001 = startSketchOn('XZ') + test("Existing sketch with bad code delete user's code", async ({ + page, + homePage, + }) => { + // this was a regression https://github.com/KittyCAD/modeling-app/issues/2832 + await page.addInitScript(async () => { + localStorage.setItem( + 'persistCode', + `sketch001 = startSketchOn('XZ') |> startProfileAt([-0.45, 0.87], %) |> line([1.32, 0.38], %) |> line([1.02, -1.32], %, $seg01) @@ -857,38 +904,38 @@ test.describe('Sketch tests', () => { |> close(%) extrude001 = extrude(5, sketch001) ` - ) - }) - - const u = await getUtils(page) - await page.setBodyDimensions({ width: 1200, height: 500 }) - - await homePage.goToModelingScene() - - await u.openDebugPanel() - await u.expectCmdLog('[data-message-type="execution-done"]') - await u.closeDebugPanel() - - await page.getByRole('button', { name: 'Start Sketch' }).click() - - await page.mouse.click(622, 355) - - await page.waitForTimeout(800) - await page.getByText(`END')`).click() - await page.keyboard.press('End') - await page.keyboard.press('Enter') - await page.keyboard.type(' |>', { delay: 100 }) - await page.waitForTimeout(100) - await expect(page.locator('.cm-lint-marker-error')).toBeVisible() - - await page.getByRole('button', { name: 'Exit Sketch' }).click() - - await expect( - page.getByRole('button', { name: 'Start Sketch' }) - ).toBeVisible() - - await expect((await u.codeLocator.innerText()).replace(/\s/g, '')).toBe( - `sketch001 = startSketchOn('XZ') + ) + }) + + const u = await getUtils(page) + await page.setBodyDimensions({ width: 1200, height: 500 }) + + await homePage.goToModelingScene() + + await u.openDebugPanel() + await u.expectCmdLog('[data-message-type="execution-done"]') + await u.closeDebugPanel() + + await page.getByRole('button', { name: 'Start Sketch' }).click() + + await page.mouse.click(622, 355) + + await page.waitForTimeout(800) + await page.getByText(`END')`).click() + await page.keyboard.press('End') + await page.keyboard.press('Enter') + await page.keyboard.type(' |>', { delay: 100 }) + await page.waitForTimeout(100) + await expect(page.locator('.cm-lint-marker-error')).toBeVisible() + + await page.getByRole('button', { name: 'Exit Sketch' }).click() + + await expect( + page.getByRole('button', { name: 'Start Sketch' }) + ).toBeVisible() + + await expect((await u.codeLocator.innerText()).replace(/\s/g, '')).toBe( + `sketch001 = startSketchOn('XZ') |> startProfileAt([-0.45, 0.87], %) |> line([1.32, 0.38], %) |> line([1.02, -1.32], %, $seg01) @@ -899,111 +946,118 @@ test.describe('Sketch tests', () => { sketch002 = startSketchOn(extrude001, 'END') |> `.replace(/\s/g, '') - ) }) - test('empty-scene default-planes act as expected', async ({ page, homePage }) => { /** - * Tests the following things - * 1) The the planes are there on load because the scene is empty - * 2) The planes don't changes color when hovered initially - * 3) Putting something in the scene makes the planes hidden - * 4) Removing everything from the scene shows the plans again - * 3) Once "start sketch" is click, the planes do respond to hovers - * 4) Selecting a plan works as expected, i.e. sketch mode - * 5) Reloading the scene with something already in the scene means the planes are hidden - */ - - const u = await getUtils(page) - await homePage.goToModelingScene() - - await u.openDebugPanel() - await u.expectCmdLog('[data-message-type="execution-done"]') - await u.closeDebugPanel() - - const XYPlanePoint = { x: 774, y: 116 } as const - const unHoveredColor: [number, number, number] = [47, 47, 93] - expect( - await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor) - ).toBeLessThan(8) - - await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y) - await page.waitForTimeout(200) - - // color should not change for having been hovered - expect( - await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor) - ).toBeLessThan(8) - - await u.openAndClearDebugPanel() - - await u.codeLocator.fill(`sketch001 = startSketchOn('XY') + ) + }) + test('empty-scene default-planes act as expected', async ({ + page, + homePage, + }) => { + /** + * Tests the following things + * 1) The the planes are there on load because the scene is empty + * 2) The planes don't changes color when hovered initially + * 3) Putting something in the scene makes the planes hidden + * 4) Removing everything from the scene shows the plans again + * 3) Once "start sketch" is click, the planes do respond to hovers + * 4) Selecting a plan works as expected, i.e. sketch mode + * 5) Reloading the scene with something already in the scene means the planes are hidden + */ + + const u = await getUtils(page) + await homePage.goToModelingScene() + + await u.openDebugPanel() + await u.expectCmdLog('[data-message-type="execution-done"]') + await u.closeDebugPanel() + + const XYPlanePoint = { x: 774, y: 116 } as const + const unHoveredColor: [number, number, number] = [47, 47, 93] + expect( + await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor) + ).toBeLessThan(8) + + await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y) + await page.waitForTimeout(200) + + // color should not change for having been hovered + expect( + await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor) + ).toBeLessThan(8) + + await u.openAndClearDebugPanel() + + await u.codeLocator.fill(`sketch001 = startSketchOn('XY') |> startProfileAt([-10, -10], %) |> line([20, 0], %) |> line([0, 20], %) |> xLine(-20, %) `) - - await u.expectCmdLog('[data-message-type="execution-done"]') - - const noPlanesColor: [number, number, number] = [30, 30, 30] - expect( - await u.getGreatestPixDiff(XYPlanePoint, noPlanesColor) - ).toBeLessThan(3) - - await u.clearCommandLogs() - await u.removeCurrentCode() - await u.expectCmdLog('[data-message-type="execution-done"]') - - await expect - .poll(() => u.getGreatestPixDiff(XYPlanePoint, unHoveredColor), { - timeout: 5_000, - }) - .toBeLessThan(8) - - // click start Sketch - await page.getByRole('button', { name: 'Start Sketch' }).click() - await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y, { steps: 50 }) - const hoveredColor: [number, number, number] = [93, 93, 127] - // now that we're expecting the user to select a plan, it does respond to hover - await expect - .poll(() => u.getGreatestPixDiff(XYPlanePoint, hoveredColor)) - .toBeLessThan(8) - - await page.mouse.click(XYPlanePoint.x, XYPlanePoint.y) - await page.waitForTimeout(600) - - await page.mouse.click(XYPlanePoint.x, XYPlanePoint.y) - await page.waitForTimeout(200) - await page.mouse.click(XYPlanePoint.x + 50, XYPlanePoint.y + 50) - await expect(u.codeLocator).toHaveText(`sketch001 = startSketchOn('XZ') + + await u.expectCmdLog('[data-message-type="execution-done"]') + + const noPlanesColor: [number, number, number] = [30, 30, 30] + expect( + await u.getGreatestPixDiff(XYPlanePoint, noPlanesColor) + ).toBeLessThan(3) + + await u.clearCommandLogs() + await u.removeCurrentCode() + await u.expectCmdLog('[data-message-type="execution-done"]') + + await expect + .poll(() => u.getGreatestPixDiff(XYPlanePoint, unHoveredColor), { + timeout: 5_000, + }) + .toBeLessThan(8) + + // click start Sketch + await page.getByRole('button', { name: 'Start Sketch' }).click() + await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y, { steps: 50 }) + const hoveredColor: [number, number, number] = [93, 93, 127] + // now that we're expecting the user to select a plan, it does respond to hover + await expect + .poll(() => u.getGreatestPixDiff(XYPlanePoint, hoveredColor)) + .toBeLessThan(8) + + await page.mouse.click(XYPlanePoint.x, XYPlanePoint.y) + await page.waitForTimeout(600) + + await page.mouse.click(XYPlanePoint.x, XYPlanePoint.y) + await page.waitForTimeout(200) + await page.mouse.click(XYPlanePoint.x + 50, XYPlanePoint.y + 50) + await expect(u.codeLocator).toHaveText(`sketch001 = startSketchOn('XZ') |> startProfileAt([11.8, 9.09], %) |> line([3.39, -3.39], %) `) - - await page.addInitScript(async () => { - localStorage.setItem( - 'persistCode', - `sketch001 = startSketchOn('XZ') + + await page.addInitScript(async () => { + localStorage.setItem( + 'persistCode', + `sketch001 = startSketchOn('XZ') |> startProfileAt([11.8, 9.09], %) |> line([3.39, -3.39], %) ` - ) - }) - - await u.openDebugPanel() - await u.expectCmdLog('[data-message-type="execution-done"]') - await u.closeDebugPanel() - - // expect there to be no planes on load since there's something in the scene - expect( - await u.getGreatestPixDiff(XYPlanePoint, noPlanesColor) - ).toBeLessThan(3) }) + ) + }) - test('Can attempt to sketch on revolved face', async ({ page, homePage }) => { const u = await getUtils(page) - await page.setBodyDimensions({ width: 1200, height: 500 }) - - await page.addInitScript(async () => { - localStorage.setItem( - 'persistCode', - `lugHeadLength = 0.25 + await u.openDebugPanel() + await u.expectCmdLog('[data-message-type="execution-done"]') + await u.closeDebugPanel() + + // expect there to be no planes on load since there's something in the scene + expect( + await u.getGreatestPixDiff(XYPlanePoint, noPlanesColor) + ).toBeLessThan(3) + }) + + test('Can attempt to sketch on revolved face', async ({ page, homePage }) => { + const u = await getUtils(page) + await page.setBodyDimensions({ width: 1200, height: 500 }) + + await page.addInitScript(async () => { + localStorage.setItem( + 'persistCode', + `lugHeadLength = 0.25 lugDiameter = 0.5 lugLength = 2 @@ -1020,49 +1074,54 @@ test.describe('Sketch tests', () => { } lug([0, 0], 10, .5, "XY")` - ) - }) - - await homePage.goToModelingScene() - - await u.openDebugPanel() - await u.expectCmdLog('[data-message-type="execution-done"]') - await u.closeDebugPanel() - - /*** - * Test Plan - * Start the sketch mode - * Click the middle of the screen which should click the top face that is revolved - * Wait till you see the line tool be enabled - * Wait till you see the exit sketch enabled - * - * This is supposed to test that you are allowed to go into sketch mode to sketch on a revolved face - */ - - await page.getByRole('button', { name: 'Start Sketch' }).click() - - await expect(async () => { - await page.mouse.click(600, 250) - await page.waitForTimeout(1000) - await expect( - page.getByRole('button', { name: 'Exit Sketch' }) - ).toBeVisible() - await expect( - page.getByRole('button', { name: 'line Line', exact: true }) - ).toHaveAttribute('aria-pressed', 'true') - }).toPass({ timeout: 40_000, intervals: [1_000] }) }) + ) + }) - test('Can sketch on face when user defined function was used in the sketch', async ({ page, homePage }) => { const u = await getUtils(page) - await page.setBodyDimensions({ width: 1200, height: 500 }) - - // Checking for a regression that performs a sketch when a user defined function - // is declared at the top of the file and used in the sketch that is being drawn on. - // fn in2mm is declared at the top of the file and used rail which does a an extrusion with the function. - - await page.addInitScript(async () => { - localStorage.setItem( - 'persistCode', - `fn in2mm = (inches) => { + await homePage.goToModelingScene() + + await u.openDebugPanel() + await u.expectCmdLog('[data-message-type="execution-done"]') + await u.closeDebugPanel() + + /*** + * Test Plan + * Start the sketch mode + * Click the middle of the screen which should click the top face that is revolved + * Wait till you see the line tool be enabled + * Wait till you see the exit sketch enabled + * + * This is supposed to test that you are allowed to go into sketch mode to sketch on a revolved face + */ + + await page.getByRole('button', { name: 'Start Sketch' }).click() + + await expect(async () => { + await page.mouse.click(600, 250) + await page.waitForTimeout(1000) + await expect( + page.getByRole('button', { name: 'Exit Sketch' }) + ).toBeVisible() + await expect( + page.getByRole('button', { name: 'line Line', exact: true }) + ).toHaveAttribute('aria-pressed', 'true') + }).toPass({ timeout: 40_000, intervals: [1_000] }) + }) + + test('Can sketch on face when user defined function was used in the sketch', async ({ + page, + homePage, + }) => { + const u = await getUtils(page) + await page.setBodyDimensions({ width: 1200, height: 500 }) + + // Checking for a regression that performs a sketch when a user defined function + // is declared at the top of the file and used in the sketch that is being drawn on. + // fn in2mm is declared at the top of the file and used rail which does a an extrusion with the function. + + await page.addInitScript(async () => { + localStorage.setItem( + 'persistCode', + `fn in2mm = (inches) => { return inches * 25.4 } @@ -1102,47 +1161,48 @@ test.describe('Sketch tests', () => { ], %) |> close(%) |> extrude(in2mm(2), %)` - ) + ) + }) + + const center = { x: 600, y: 250 } + const rectangleSize = 20 + await homePage.goToModelingScene() + + // Start a sketch + await page.getByRole('button', { name: 'Start Sketch' }).click() + + // Click the top face of this rail + await page.mouse.click(center.x, center.y) + await page.waitForTimeout(1000) + + // Draw a rectangle + // top left + await page.mouse.click(center.x - rectangleSize, center.y - rectangleSize) + await page.waitForTimeout(250) + // top right + await page.mouse.click(center.x + rectangleSize, center.y - rectangleSize) + await page.waitForTimeout(250) + + // bottom right + await page.mouse.click(center.x + rectangleSize, center.y + rectangleSize) + await page.waitForTimeout(250) + + // bottom left + await page.mouse.click(center.x - rectangleSize, center.y + rectangleSize) + await page.waitForTimeout(250) + + // top left + await page.mouse.click(center.x - rectangleSize, center.y - rectangleSize) + await page.waitForTimeout(250) + + // exit sketch + await page.getByRole('button', { name: 'Exit Sketch' }).click() + + // Check execution is done + await u.openDebugPanel() + await u.expectCmdLog('[data-message-type="execution-done"]') + await u.closeDebugPanel() }) - - const center = { x: 600, y: 250 } - const rectangleSize = 20 - await homePage.goToModelingScene() - - // Start a sketch - await page.getByRole('button', { name: 'Start Sketch' }).click() - - // Click the top face of this rail - await page.mouse.click(center.x, center.y) - await page.waitForTimeout(1000) - - // Draw a rectangle - // top left - await page.mouse.click(center.x - rectangleSize, center.y - rectangleSize) - await page.waitForTimeout(250) - // top right - await page.mouse.click(center.x + rectangleSize, center.y - rectangleSize) - await page.waitForTimeout(250) - - // bottom right - await page.mouse.click(center.x + rectangleSize, center.y + rectangleSize) - await page.waitForTimeout(250) - - // bottom left - await page.mouse.click(center.x - rectangleSize, center.y + rectangleSize) - await page.waitForTimeout(250) - - // top left - await page.mouse.click(center.x - rectangleSize, center.y - rectangleSize) - await page.waitForTimeout(250) - - // exit sketch - await page.getByRole('button', { name: 'Exit Sketch' }).click() - - // Check execution is done - await u.openDebugPanel() - await u.expectCmdLog('[data-message-type="execution-done"]') - await u.closeDebugPanel() }) }) test.describe('Sketch mode should be toleratant to syntax errors', () => { @@ -1213,49 +1273,50 @@ test.describe('Sketch mode should be toleratant to syntax errors', () => { }) test.describe(`Sketching with offset planes`, () => { - test( - `Can select an offset plane to sketch on`, - async ({ context, page, scene, toolbar, editor, homePage }) => { - // We seed the scene with a single offset plane - await context.addInitScript(() => { - localStorage.setItem( - 'persistCode', - `offsetPlane001 = offsetPlane("XY", 10)` - ) - }) + test(`Can select an offset plane to sketch on`, async ({ + context, + page, + scene, + toolbar, + editor, + homePage, + }) => { + // We seed the scene with a single offset plane + await context.addInitScript(() => { + localStorage.setItem( + 'persistCode', + `offsetPlane001 = offsetPlane("XY", 10)` + ) + }) - homePage.goToModelingScene() + homePage.goToModelingScene() - const [planeClick, planeHover] = scene.makeMouseHelpers(650, 200) + const [planeClick, planeHover] = scene.makeMouseHelpers(650, 200) - await test.step(`Start sketching on the offset plane`, async () => { - await toolbar.startSketchPlaneSelection() + await test.step(`Start sketching on the offset plane`, async () => { + await toolbar.startSketchPlaneSelection() - await test.step(`Hovering should highlight code`, async () => { - await planeHover() - await editor.expectState({ - activeLines: [`offsetPlane001=offsetPlane("XY",10)`], - diagnostics: [], - highlightedCode: 'offsetPlane("XY", 10)', - }) + await test.step(`Hovering should highlight code`, async () => { + await planeHover() + await editor.expectState({ + activeLines: [`offsetPlane001=offsetPlane("XY",10)`], + diagnostics: [], + highlightedCode: 'offsetPlane("XY", 10)', }) - - await test.step( - `Clicking should select the plane and enter sketch mode`, - async () => { - await planeClick() - // Have to wait for engine-side animation to finish - await page.waitForTimeout(600) - await expect(toolbar.lineBtn).toBeEnabled() - await editor.expectEditor.toContain('startSketchOn(offsetPlane001)') - await editor.expectState({ - activeLines: [`offsetPlane001=offsetPlane("XY",10)`], - diagnostics: [], - highlightedCode: '', - }) - } - ) }) - } - ) + + await test.step(`Clicking should select the plane and enter sketch mode`, async () => { + await planeClick() + // Have to wait for engine-side animation to finish + await page.waitForTimeout(600) + await expect(toolbar.lineBtn).toBeEnabled() + await editor.expectEditor.toContain('startSketchOn(offsetPlane001)') + await editor.expectState({ + activeLines: [`offsetPlane001=offsetPlane("XY",10)`], + diagnostics: [], + highlightedCode: '', + }) + }) + }) + }) }) diff --git a/e2e/playwright/test-utils.ts b/e2e/playwright/test-utils.ts index 1312284c7..f843503b0 100644 --- a/e2e/playwright/test-utils.ts +++ b/e2e/playwright/test-utils.ts @@ -418,7 +418,7 @@ export async function getUtils(page: Page, test_?: typeof test) { codeLocator: page.locator('.cm-content'), crushKclCodeIntoOneLineAndThenMaybeSome: async () => { const code = await page.locator('.cm-content').innerText() - return code.replaceAll(' ', '').replaceAll("\n", '') + return code.replaceAll(' ', '').replaceAll('\n', '') }, normalisedEditorCode: async () => { const code = await page.locator('.cm-content').innerText() @@ -687,14 +687,16 @@ const moveDownloadedFileTo = async (page: Page, toLocation: string) => { const downloadDir = path.resolve( page.TEST_SETTINGS_FILE_KEY, - "downloads-during-playwright" + 'downloads-during-playwright' ) // Expect there to be at least one file - expect.poll(async () => { - const files = await fsp.readdir(downloadDir) - return files.length - }).toBe(1) + expect + .poll(async () => { + const files = await fsp.readdir(downloadDir) + return files.length + }) + .toBe(1) // Go through the downloads dir and move files to new location const files = await fsp.readdir(downloadDir) @@ -832,7 +834,13 @@ export async function setup( testInfo?: TestInfo ) { await context.addInitScript( - async ({ token, settingsKey, settings, IS_PLAYWRIGHT_KEY, PLAYWRIGHT_TEST_DIR }) => { + async ({ + token, + settingsKey, + settings, + IS_PLAYWRIGHT_KEY, + PLAYWRIGHT_TEST_DIR, + }) => { localStorage.clear() localStorage.setItem('TOKEN_PERSIST_KEY', token) localStorage.setItem('persistCode', ``) diff --git a/e2e/playwright/testing-selections.spec.ts b/e2e/playwright/testing-selections.spec.ts index 1847ccc2b..b5e4c5560 100644 --- a/e2e/playwright/testing-selections.spec.ts +++ b/e2e/playwright/testing-selections.spec.ts @@ -1,261 +1,268 @@ import { test, expect } from './zoo-test' -import { commonPoints, getUtils, } from './test-utils' +import { commonPoints, getUtils } from './test-utils' import { Coords2d } from 'lang/std/sketch' import { KCL_DEFAULT_LENGTH } from 'lib/constants' import { uuidv4 } from 'lib/utils' test.describe('Testing selections', () => { test.setTimeout(90_000) - test('Selections work on fresh and edited sketch', { tag: ['@skipWin'] }, async ({ page, homePage }) => { // Skip on windows its being weird. - test.skip(process.platform === 'win32', 'Skip on windows') - - // tests mapping works on fresh sketch and edited sketch - // tests using hovers which is the same as selections, because if - // source ranges are wrong, hovers won't work - const u = await getUtils(page) - const PUR = 400 / 37.5 //pixeltoUnitRatio - await page.setBodyDimensions({ width: 1200, height: 500 }) - - await homePage.goToModelingScene() - await u.openDebugPanel() - - const yAxisClick = () => - test.step('Click on Y axis', async () => { - await page.mouse.move(600, 200, { steps: 5 }) - await page.mouse.click(600, 200) - await page.waitForTimeout(100) - }) - const xAxisClickAfterExitingSketch = () => - test.step(`Click on X axis after exiting sketch, which shifts it at the moment`, async () => { - await page.mouse.click(639, 278) - await page.waitForTimeout(100) - }) - const emptySpaceHover = () => - test.step('Hover over empty space', async () => { - await page.mouse.move(700, 143, { steps: 5 }) - await expect(page.locator('.hover-highlight')).not.toBeVisible() - }) - const emptySpaceClick = () => - test.step(`Click in empty space`, async () => { - await page.mouse.click(700, 143) - await expect(page.locator('.cm-line').last()).toHaveClass( - /cm-activeLine/ - ) - }) - const topHorzSegmentClick = () => - page.mouse - .click(startXPx, 500 - PUR * 20) - .then(() => page.waitForTimeout(100)) - const bottomHorzSegmentClick = () => - page.mouse - .click(startXPx + PUR * 10, 500 - PUR * 10) - .then(() => page.waitForTimeout(100)) - - await u.clearCommandLogs() - await expect( - page.getByRole('button', { name: 'Start Sketch' }) - ).not.toBeDisabled() - await page.getByRole('button', { name: 'Start Sketch' }).click() - - // select a plane - await page.mouse.click(700, 200) - await page.waitForTimeout(700) // wait for animation - - const startXPx = 600 - await u.closeDebugPanel() - await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10) - await expect(page.locator('.cm-content')) - .toHaveText(`sketch001 = startSketchOn('XZ') + test( + 'Selections work on fresh and edited sketch', + { tag: ['@skipWin'] }, + async ({ page, homePage }) => { + // Skip on windows its being weird. + test.skip(process.platform === 'win32', 'Skip on windows') + + // tests mapping works on fresh sketch and edited sketch + // tests using hovers which is the same as selections, because if + // source ranges are wrong, hovers won't work + const u = await getUtils(page) + const PUR = 400 / 37.5 //pixeltoUnitRatio + await page.setBodyDimensions({ width: 1200, height: 500 }) + + await homePage.goToModelingScene() + await u.openDebugPanel() + + const yAxisClick = () => + test.step('Click on Y axis', async () => { + await page.mouse.move(600, 200, { steps: 5 }) + await page.mouse.click(600, 200) + await page.waitForTimeout(100) + }) + const xAxisClickAfterExitingSketch = () => + test.step(`Click on X axis after exiting sketch, which shifts it at the moment`, async () => { + await page.mouse.click(639, 278) + await page.waitForTimeout(100) + }) + const emptySpaceHover = () => + test.step('Hover over empty space', async () => { + await page.mouse.move(700, 143, { steps: 5 }) + await expect(page.locator('.hover-highlight')).not.toBeVisible() + }) + const emptySpaceClick = () => + test.step(`Click in empty space`, async () => { + await page.mouse.click(700, 143) + await expect(page.locator('.cm-line').last()).toHaveClass( + /cm-activeLine/ + ) + }) + const topHorzSegmentClick = () => + page.mouse + .click(startXPx, 500 - PUR * 20) + .then(() => page.waitForTimeout(100)) + const bottomHorzSegmentClick = () => + page.mouse + .click(startXPx + PUR * 10, 500 - PUR * 10) + .then(() => page.waitForTimeout(100)) + + await u.clearCommandLogs() + await expect( + page.getByRole('button', { name: 'Start Sketch' }) + ).not.toBeDisabled() + await page.getByRole('button', { name: 'Start Sketch' }).click() + + // select a plane + await page.mouse.click(700, 200) + await page.waitForTimeout(700) // wait for animation + + const startXPx = 600 + await u.closeDebugPanel() + await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10) + await expect(page.locator('.cm-content')) + .toHaveText(`sketch001 = startSketchOn('XZ') |> startProfileAt(${commonPoints.startAt}, %)`) - - await page.waitForTimeout(100) - await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10) - - await expect(page.locator('.cm-content')) - .toHaveText(`sketch001 = startSketchOn('XZ') + + await page.waitForTimeout(100) + await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10) + + await expect(page.locator('.cm-content')) + .toHaveText(`sketch001 = startSketchOn('XZ') |> startProfileAt(${commonPoints.startAt}, %) |> xLine(${commonPoints.num1}, %)`) - - await page.waitForTimeout(100) - await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20) - await expect(page.locator('.cm-content')) - .toHaveText(`sketch001 = startSketchOn('XZ') + + await page.waitForTimeout(100) + await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20) + await expect(page.locator('.cm-content')) + .toHaveText(`sketch001 = startSketchOn('XZ') |> startProfileAt(${commonPoints.startAt}, %) |> xLine(${commonPoints.num1}, %) |> yLine(${commonPoints.num1 + 0.01}, %)`) - await page.waitForTimeout(100) - await page.mouse.click(startXPx, 500 - PUR * 20) - await expect(page.locator('.cm-content')) - .toHaveText(`sketch001 = startSketchOn('XZ') + await page.waitForTimeout(100) + await page.mouse.click(startXPx, 500 - PUR * 20) + await expect(page.locator('.cm-content')) + .toHaveText(`sketch001 = startSketchOn('XZ') |> startProfileAt(${commonPoints.startAt}, %) |> xLine(${commonPoints.num1}, %) |> yLine(${commonPoints.num1 + 0.01}, %) |> xLine(${commonPoints.num2 * -1}, %)`) - - // deselect line tool - await page.getByRole('button', { name: 'line Line', exact: true }).click() - - await u.closeDebugPanel() - const selectionSequence = async () => { - await expect(page.getByTestId('hover-highlight')).not.toBeVisible() - - await page.waitForTimeout(100) - await page.mouse.move(startXPx + PUR * 15, 500 - PUR * 10) - - await expect(page.getByTestId('hover-highlight').first()).toBeVisible() - // bg-yellow-300/70 is more brittle than hover-highlight, but is closer to the user experience - // and will be an easy fix if it breaks because we change the colour - await expect(page.locator('.bg-yellow-300\\/70')).toBeVisible() - // check mousing off, than mousing onto another line - await page.mouse.move(startXPx + PUR * 10, 500 - PUR * 15) // mouse off - await expect(page.getByTestId('hover-highlight')).not.toBeVisible() - await page.mouse.move(startXPx + PUR * 10, 500 - PUR * 20) // mouse onto another line - await expect(page.getByTestId('hover-highlight').first()).toBeVisible() - - // now check clicking works including axis - - // click a segment hold shift and click an axis, see that a relevant constraint is enabled - const constrainButton = page.getByRole('button', { - name: 'Length: open menu', - }) - const absXButton = page.getByRole('button', { name: 'Absolute X' }) - - await test.step(`Select a segment and an axis, see that a relevant constraint is enabled`, async () => { - await topHorzSegmentClick() - await page.keyboard.down('Shift') - await constrainButton.click() - await expect(absXButton).toBeDisabled() - await page.waitForTimeout(100) - await yAxisClick() - await page.keyboard.up('Shift') - await constrainButton.click() - await absXButton.and(page.locator(':not([disabled])')).waitFor() - await expect(absXButton).not.toBeDisabled() - }) - - await emptySpaceClick() - await page.waitForTimeout(100) - - await test.step(`Same selection but click the axis first`, async () => { - await yAxisClick() - await constrainButton.click() - await expect(absXButton).toBeDisabled() - await page.keyboard.down('Shift') - await page.waitForTimeout(100) - await topHorzSegmentClick() - await page.waitForTimeout(100) - - await page.keyboard.up('Shift') - await constrainButton.click() - await expect(absXButton).not.toBeDisabled() - }) - - // clear selection by clicking on nothing - await emptySpaceClick() - - // check the same selection again by putting cursor in code first then selecting axis - await test.step(`Same selection but code selection then axis`, async () => { - await page - .getByText(` |> xLine(${commonPoints.num2 * -1}, %)`) - .click() - await page.keyboard.down('Shift') - await constrainButton.click() - await expect(absXButton).toBeDisabled() - await page.waitForTimeout(100) - await yAxisClick() - await page.keyboard.up('Shift') - await constrainButton.click() - await expect(absXButton).not.toBeDisabled() - }) - - // clear selection by clicking on nothing - await emptySpaceClick() - - // select segment in editor than another segment in scene and check there are two cursors - // TODO change this back to shift click in the scene, not cmd click in the editor - await bottomHorzSegmentClick() - - await expect(page.locator('.cm-cursor')).toHaveCount(1) - - await page.keyboard.down( - process.platform === 'linux' ? 'Control' : 'Meta' - ) - await page.waitForTimeout(100) - await page.getByText(` |> xLine(${commonPoints.num2 * -1}, %)`).click() - - await expect(page.locator('.cm-cursor')).toHaveCount(2) - await page.waitForTimeout(500) - await page.keyboard.up( - process.platform === 'linux' ? 'Control' : 'Meta' - ) - - // clear selection by clicking on nothing - await emptySpaceClick() - } - - await test.step(`Test hovering and selecting on fresh sketch`, async () => { - await selectionSequence() - }) - - // hovering in fresh sketch worked, lets try exiting and re-entering - await u.openAndClearDebugPanel() - await page.getByRole('button', { name: 'Exit Sketch' }).click() - await page.waitForTimeout(200) - // wait for execution done - - await u.expectCmdLog('[data-message-type="execution-done"]') - await u.closeDebugPanel() - - // select a line, this verifies that sketches in the scene can be selected outside of sketch mode - await topHorzSegmentClick() - await xAxisClickAfterExitingSketch() - await page.waitForTimeout(100) - await emptySpaceHover() - - // enter sketch again - await u.doAndWaitForCmd( - () => page.getByRole('button', { name: 'Edit Sketch' }).click(), - 'default_camera_get_settings' - ) - - await page.waitForTimeout(450) // wait for animation - - await u.openAndClearDebugPanel() - await u.sendCustomCmd({ - type: 'modeling_cmd_req', - cmd_id: uuidv4(), - cmd: { - type: 'default_camera_look_at', - center: { x: 0, y: 0, z: 0 }, - vantage: { x: 0, y: -1378.01, 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) - - await emptySpaceClick() - - await u.closeDebugPanel() - - await test.step(`Test hovering and selecting on edited sketch`, async () => { - await selectionSequence() - }) }) - test('Solids should be select and deletable', async ({ page, homePage }) => { test.setTimeout(90_000) - const u = await getUtils(page) - await page.addInitScript(async () => { - localStorage.setItem( - 'persistCode', - `sketch001 = startSketchOn('XZ') + // deselect line tool + await page.getByRole('button', { name: 'line Line', exact: true }).click() + + await u.closeDebugPanel() + const selectionSequence = async () => { + await expect(page.getByTestId('hover-highlight')).not.toBeVisible() + + await page.waitForTimeout(100) + await page.mouse.move(startXPx + PUR * 15, 500 - PUR * 10) + + await expect(page.getByTestId('hover-highlight').first()).toBeVisible() + // bg-yellow-300/70 is more brittle than hover-highlight, but is closer to the user experience + // and will be an easy fix if it breaks because we change the colour + await expect(page.locator('.bg-yellow-300\\/70')).toBeVisible() + // check mousing off, than mousing onto another line + await page.mouse.move(startXPx + PUR * 10, 500 - PUR * 15) // mouse off + await expect(page.getByTestId('hover-highlight')).not.toBeVisible() + await page.mouse.move(startXPx + PUR * 10, 500 - PUR * 20) // mouse onto another line + await expect(page.getByTestId('hover-highlight').first()).toBeVisible() + + // now check clicking works including axis + + // click a segment hold shift and click an axis, see that a relevant constraint is enabled + const constrainButton = page.getByRole('button', { + name: 'Length: open menu', + }) + const absXButton = page.getByRole('button', { name: 'Absolute X' }) + + await test.step(`Select a segment and an axis, see that a relevant constraint is enabled`, async () => { + await topHorzSegmentClick() + await page.keyboard.down('Shift') + await constrainButton.click() + await expect(absXButton).toBeDisabled() + await page.waitForTimeout(100) + await yAxisClick() + await page.keyboard.up('Shift') + await constrainButton.click() + await absXButton.and(page.locator(':not([disabled])')).waitFor() + await expect(absXButton).not.toBeDisabled() + }) + + await emptySpaceClick() + await page.waitForTimeout(100) + + await test.step(`Same selection but click the axis first`, async () => { + await yAxisClick() + await constrainButton.click() + await expect(absXButton).toBeDisabled() + await page.keyboard.down('Shift') + await page.waitForTimeout(100) + await topHorzSegmentClick() + await page.waitForTimeout(100) + + await page.keyboard.up('Shift') + await constrainButton.click() + await expect(absXButton).not.toBeDisabled() + }) + + // clear selection by clicking on nothing + await emptySpaceClick() + + // check the same selection again by putting cursor in code first then selecting axis + await test.step(`Same selection but code selection then axis`, async () => { + await page + .getByText(` |> xLine(${commonPoints.num2 * -1}, %)`) + .click() + await page.keyboard.down('Shift') + await constrainButton.click() + await expect(absXButton).toBeDisabled() + await page.waitForTimeout(100) + await yAxisClick() + await page.keyboard.up('Shift') + await constrainButton.click() + await expect(absXButton).not.toBeDisabled() + }) + + // clear selection by clicking on nothing + await emptySpaceClick() + + // select segment in editor than another segment in scene and check there are two cursors + // TODO change this back to shift click in the scene, not cmd click in the editor + await bottomHorzSegmentClick() + + await expect(page.locator('.cm-cursor')).toHaveCount(1) + + await page.keyboard.down( + process.platform === 'linux' ? 'Control' : 'Meta' + ) + await page.waitForTimeout(100) + await page.getByText(` |> xLine(${commonPoints.num2 * -1}, %)`).click() + + await expect(page.locator('.cm-cursor')).toHaveCount(2) + await page.waitForTimeout(500) + await page.keyboard.up( + process.platform === 'linux' ? 'Control' : 'Meta' + ) + + // clear selection by clicking on nothing + await emptySpaceClick() + } + + await test.step(`Test hovering and selecting on fresh sketch`, async () => { + await selectionSequence() + }) + + // hovering in fresh sketch worked, lets try exiting and re-entering + await u.openAndClearDebugPanel() + await page.getByRole('button', { name: 'Exit Sketch' }).click() + await page.waitForTimeout(200) + // wait for execution done + + await u.expectCmdLog('[data-message-type="execution-done"]') + await u.closeDebugPanel() + + // select a line, this verifies that sketches in the scene can be selected outside of sketch mode + await topHorzSegmentClick() + await xAxisClickAfterExitingSketch() + await page.waitForTimeout(100) + await emptySpaceHover() + + // enter sketch again + await u.doAndWaitForCmd( + () => page.getByRole('button', { name: 'Edit Sketch' }).click(), + 'default_camera_get_settings' + ) + + await page.waitForTimeout(450) // wait for animation + + await u.openAndClearDebugPanel() + await u.sendCustomCmd({ + type: 'modeling_cmd_req', + cmd_id: uuidv4(), + cmd: { + type: 'default_camera_look_at', + center: { x: 0, y: 0, z: 0 }, + vantage: { x: 0, y: -1378.01, 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) + + await emptySpaceClick() + + await u.closeDebugPanel() + + await test.step(`Test hovering and selecting on edited sketch`, async () => { + await selectionSequence() + }) + } + ) + + test('Solids should be select and deletable', async ({ page, homePage }) => { + test.setTimeout(90_000) + const u = await getUtils(page) + await page.addInitScript(async () => { + localStorage.setItem( + 'persistCode', + `sketch001 = startSketchOn('XZ') |> startProfileAt([-79.26, 95.04], %) |> line([112.54, 127.64], %, $seg02) |> line([170.36, -121.61], %, $seg01) @@ -316,70 +323,70 @@ test.describe('Testing selections', () => { |> close(%) rev = revolve({ axis: 'y' }, part009) ` + ) + }, KCL_DEFAULT_LENGTH) + await page.setBodyDimensions({ width: 1000, height: 500 }) + + await homePage.goToModelingScene() + + await u.openDebugPanel() + await u.expectCmdLog('[data-message-type="execution-done"]') + await u.closeDebugPanel() + + await u.openAndClearDebugPanel() + await u.sendCustomCmd({ + type: 'modeling_cmd_req', + cmd_id: uuidv4(), + cmd: { + type: 'default_camera_look_at', + vantage: { x: 1139.49, y: -7053, z: 8597.31 }, + center: { x: -2206.68, y: -1298.36, z: 60 }, + 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 revolve = { x: 646, y: 248 } + const parentExtrude = { x: 915, y: 133 } + const solid2d = { x: 770, y: 167 } + + // DELETE REVOLVE + await page.mouse.click(revolve.x, revolve.y) + await page.waitForTimeout(100) + await expect(page.locator('.cm-activeLine')).toHaveText( + '|> line([0, -pipeLength], %)' ) - }, KCL_DEFAULT_LENGTH) - await page.setBodyDimensions({ width: 1000, height: 500 }) - - await homePage.goToModelingScene() - - await u.openDebugPanel() - await u.expectCmdLog('[data-message-type="execution-done"]') - await u.closeDebugPanel() - - await u.openAndClearDebugPanel() - await u.sendCustomCmd({ - type: 'modeling_cmd_req', - cmd_id: uuidv4(), - cmd: { - type: 'default_camera_look_at', - vantage: { x: 1139.49, y: -7053, z: 8597.31 }, - center: { x: -2206.68, y: -1298.36, z: 60 }, - 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 revolve = { x: 646, y: 248 } - const parentExtrude = { x: 915, y: 133 } - const solid2d = { x: 770, y: 167 } - - // DELETE REVOLVE - await page.mouse.click(revolve.x, revolve.y) - await page.waitForTimeout(100) - await expect(page.locator('.cm-activeLine')).toHaveText( - '|> line([0, -pipeLength], %)' - ) - await u.clearCommandLogs() - await page.keyboard.press('Backspace') - await u.expectCmdLog('[data-message-type="execution-done"]', 10_000) - await page.waitForTimeout(200) - - await expect(u.codeLocator).not.toContainText( - `rev = revolve({ axis: 'y' }, part009)` - ) - - // DELETE PARENT EXTRUDE - await page.mouse.click(parentExtrude.x, parentExtrude.y) - await page.waitForTimeout(100) - await expect(page.locator('.cm-activeLine')).toHaveText( - '|> line([170.36, -121.61], %, $seg01)' - ) - await u.clearCommandLogs() - await page.keyboard.press('Backspace') - await u.expectCmdLog('[data-message-type="execution-done"]', 10_000) - await page.waitForTimeout(200) - await expect(u.codeLocator).not.toContainText( - `extrude001 = extrude(50, sketch001)` - ) - await expect(u.codeLocator).toContainText(`sketch005 = startSketchOn({ + await u.clearCommandLogs() + await page.keyboard.press('Backspace') + await u.expectCmdLog('[data-message-type="execution-done"]', 10_000) + await page.waitForTimeout(200) + + await expect(u.codeLocator).not.toContainText( + `rev = revolve({ axis: 'y' }, part009)` + ) + + // DELETE PARENT EXTRUDE + await page.mouse.click(parentExtrude.x, parentExtrude.y) + await page.waitForTimeout(100) + await expect(page.locator('.cm-activeLine')).toHaveText( + '|> line([170.36, -121.61], %, $seg01)' + ) + await u.clearCommandLogs() + await page.keyboard.press('Backspace') + await u.expectCmdLog('[data-message-type="execution-done"]', 10_000) + await page.waitForTimeout(200) + await expect(u.codeLocator).not.toContainText( + `extrude001 = extrude(50, sketch001)` + ) + await expect(u.codeLocator).toContainText(`sketch005 = startSketchOn({ plane: { origin: { x: 0, y: -50, z: 0 }, x_axis: { x: 1, y: 0, z: 0 }, @@ -387,7 +394,7 @@ test.describe('Testing selections', () => { z_axis: { x: 0, y: -1, z: 0 } } })`) - await expect(u.codeLocator).toContainText(`sketch003 = startSketchOn({ + await expect(u.codeLocator).toContainText(`sketch003 = startSketchOn({ plane: { origin: { x: 116.53, y: 0, z: 163.25 }, x_axis: { x: -0.81, y: 0, z: 0.58 }, @@ -395,7 +402,7 @@ test.describe('Testing selections', () => { z_axis: { x: 0.58, y: 0, z: 0.81 } } })`) - await expect(u.codeLocator).toContainText(`sketch002 = startSketchOn({ + await expect(u.codeLocator).toContainText(`sketch002 = startSketchOn({ plane: { origin: { x: -91.74, y: 0, z: 80.89 }, x_axis: { x: -0.66, y: 0, z: -0.75 }, @@ -403,23 +410,28 @@ test.describe('Testing selections', () => { z_axis: { x: -0.75, y: 0, z: 0.66 } } })`) - - // DELETE SOLID 2D - await page.mouse.click(solid2d.x, solid2d.y) - await page.waitForTimeout(100) - await expect(page.locator('.cm-activeLine')).toHaveText( - '|> startProfileAt([23.24, 136.52], %)' - ) - await u.clearCommandLogs() - await page.keyboard.press('Backspace') - await u.expectCmdLog('[data-message-type="execution-done"]', 10_000) - await page.waitForTimeout(200) - await expect(u.codeLocator).not.toContainText(`sketch005 = startSketchOn({`) }) - test("Deleting solid that the AST mod can't handle results in a toast message", async ({ page, homePage }) => { const u = await getUtils(page) - await page.addInitScript(async () => { - localStorage.setItem( - 'persistCode', - `sketch001 = startSketchOn('XZ') + + // DELETE SOLID 2D + await page.mouse.click(solid2d.x, solid2d.y) + await page.waitForTimeout(100) + await expect(page.locator('.cm-activeLine')).toHaveText( + '|> startProfileAt([23.24, 136.52], %)' + ) + await u.clearCommandLogs() + await page.keyboard.press('Backspace') + await u.expectCmdLog('[data-message-type="execution-done"]', 10_000) + await page.waitForTimeout(200) + await expect(u.codeLocator).not.toContainText(`sketch005 = startSketchOn({`) + }) + test("Deleting solid that the AST mod can't handle results in a toast message", async ({ + page, + homePage, + }) => { + const u = await getUtils(page) + await page.addInitScript(async () => { + localStorage.setItem( + 'persistCode', + `sketch001 = startSketchOn('XZ') |> startProfileAt([-79.26, 95.04], %) |> line([112.54, 127.64], %, $seg02) |> line([170.36, -121.61], %, $seg01) @@ -434,52 +446,57 @@ test.describe('Testing selections', () => { |> lineTo([profileStartX(%), profileStartY(%)], %) |> close(%) ` + ) + }, KCL_DEFAULT_LENGTH) + await page.setBodyDimensions({ width: 1000, height: 500 }) + + await homePage.goToModelingScene() + + await u.openDebugPanel() + await u.expectCmdLog('[data-message-type="execution-done"]', 10_000) + await u.closeDebugPanel() + + await u.openAndClearDebugPanel() + await u.sendCustomCmd({ + type: 'modeling_cmd_req', + cmd_id: uuidv4(), + cmd: { + type: 'default_camera_look_at', + vantage: { x: 1139.49, y: -7053, z: 8597.31 }, + center: { x: -2206.68, y: -1298.36, z: 60 }, + 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) + + // attempt delete + await page.mouse.click(930, 139) + await page.waitForTimeout(100) + await expect(page.locator('.cm-activeLine')).toHaveText( + '|> line([170.36, -121.61], %, $seg01)' ) - }, KCL_DEFAULT_LENGTH) - await page.setBodyDimensions({ width: 1000, height: 500 }) - - await homePage.goToModelingScene() - - await u.openDebugPanel() - await u.expectCmdLog('[data-message-type="execution-done"]', 10_000) - await u.closeDebugPanel() - - await u.openAndClearDebugPanel() - await u.sendCustomCmd({ - type: 'modeling_cmd_req', - cmd_id: uuidv4(), - cmd: { - type: 'default_camera_look_at', - vantage: { x: 1139.49, y: -7053, z: 8597.31 }, - center: { x: -2206.68, y: -1298.36, z: 60 }, - up: { x: 0, y: 0, z: 1 }, - }, + await u.clearCommandLogs() + await page.keyboard.press('Backspace') + + await expect(page.getByText('Unable to delete part')).toBeVisible() }) - await page.waitForTimeout(100) - await u.sendCustomCmd({ - type: 'modeling_cmd_req', - cmd_id: uuidv4(), - cmd: { - type: 'default_camera_get_settings', - }, - }) - await page.waitForTimeout(100) - - // attempt delete - await page.mouse.click(930, 139) - await page.waitForTimeout(100) - await expect(page.locator('.cm-activeLine')).toHaveText( - '|> line([170.36, -121.61], %, $seg01)' - ) - await u.clearCommandLogs() - await page.keyboard.press('Backspace') - - await expect(page.getByText('Unable to delete part')).toBeVisible() }) - test('Hovering over 3d features highlights code, clicking puts the cursor in the right place and sends selection id to engine', async ({ page, homePage }) => { const u = await getUtils(page) - await page.addInitScript(async (KCL_DEFAULT_LENGTH) => { - localStorage.setItem( - 'persistCode', - `part001 = startSketchOn('XZ') + test('Hovering over 3d features highlights code, clicking puts the cursor in the right place and sends selection id to engine', async ({ + page, + homePage, + }) => { + const u = await getUtils(page) + await page.addInitScript(async (KCL_DEFAULT_LENGTH) => { + localStorage.setItem( + 'persistCode', + `part001 = startSketchOn('XZ') |> startProfileAt([20, 0], %) |> line([7.13, 4 + 0], %) |> angledLine({ angle: 3 + 0, length: 3.14 + 0 }, %) @@ -500,210 +517,210 @@ test.describe('Testing selections', () => { |> close(%) |> extrude(5 + 7, %) ` - ) - }, KCL_DEFAULT_LENGTH) - await page.setBodyDimensions({ width: 1000, height: 500 }) - - await homePage.goToModelingScene() - - // wait for execution done - await u.openDebugPanel() - await u.expectCmdLog('[data-message-type="execution-done"]') - await u.closeDebugPanel() - - 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) - await u.closeDebugPanel() - - const extrusionTopCap: Coords2d = [800, 240] - const flatExtrusionFace: Coords2d = [960, 160] - const tangentialArcTo: Coords2d = [840, 160] - const close: Coords2d = [720, 200] - const nothing: Coords2d = [600, 200] - const closeEdge: Coords2d = [744, 233] - const closeAdjacentEdge: Coords2d = [743, 277] - const closeOppositeEdge: Coords2d = [687, 169] - - const tangentialArcEdge: Coords2d = [811, 142] - const tangentialArcOppositeEdge: Coords2d = [820, 180] - const tangentialArcAdjacentEdge: Coords2d = [688, 123] - - const straightSegmentEdge: Coords2d = [819, 369] - const straightSegmentOppositeEdge: Coords2d = [822, 368] - const straightSegmentAdjacentEdge: Coords2d = [893, 165] - - await page.mouse.move(nothing[0], nothing[1]) - await page.mouse.click(nothing[0], nothing[1]) - - await expect(page.getByTestId('hover-highlight')).not.toBeVisible() - await page.waitForTimeout(200) - - const checkCodeAtHoverPosition = async ( - name = '', - coord: Coords2d, - highlightCode: string, - activeLine = highlightCode - ) => { - await test.step(`test selection for: ${name}`, async () => { - const highlightedLocator = page.getByTestId('hover-highlight') - const activeLineLocator = page.locator('.cm-activeLine') - - await test.step(`hover should highlight correct code, clicking should put the cursor in the right place, and send selection to engine`, async () => { - await expect(async () => { - await page.mouse.move(nothing[0], nothing[1]) - await page.mouse.move(coord[0], coord[1]) - await expect(highlightedLocator.first()).toBeVisible() + ) + }, KCL_DEFAULT_LENGTH) + await page.setBodyDimensions({ width: 1000, height: 500 }) + + await homePage.goToModelingScene() + + // wait for execution done + await u.openDebugPanel() + await u.expectCmdLog('[data-message-type="execution-done"]') + await u.closeDebugPanel() + + 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) + await u.closeDebugPanel() + + const extrusionTopCap: Coords2d = [800, 240] + const flatExtrusionFace: Coords2d = [960, 160] + const tangentialArcTo: Coords2d = [840, 160] + const close: Coords2d = [720, 200] + const nothing: Coords2d = [600, 200] + const closeEdge: Coords2d = [744, 233] + const closeAdjacentEdge: Coords2d = [743, 277] + const closeOppositeEdge: Coords2d = [687, 169] + + const tangentialArcEdge: Coords2d = [811, 142] + const tangentialArcOppositeEdge: Coords2d = [820, 180] + const tangentialArcAdjacentEdge: Coords2d = [688, 123] + + const straightSegmentEdge: Coords2d = [819, 369] + const straightSegmentOppositeEdge: Coords2d = [822, 368] + const straightSegmentAdjacentEdge: Coords2d = [893, 165] + + await page.mouse.move(nothing[0], nothing[1]) + await page.mouse.click(nothing[0], nothing[1]) + + await expect(page.getByTestId('hover-highlight')).not.toBeVisible() + await page.waitForTimeout(200) + + const checkCodeAtHoverPosition = async ( + name = '', + coord: Coords2d, + highlightCode: string, + activeLine = highlightCode + ) => { + await test.step(`test selection for: ${name}`, async () => { + const highlightedLocator = page.getByTestId('hover-highlight') + const activeLineLocator = page.locator('.cm-activeLine') + + await test.step(`hover should highlight correct code, clicking should put the cursor in the right place, and send selection to engine`, async () => { + await expect(async () => { + await page.mouse.move(nothing[0], nothing[1]) + await page.mouse.move(coord[0], coord[1]) + await expect(highlightedLocator.first()).toBeVisible() + await expect + .poll(async () => { + let textContents = await highlightedLocator.allTextContents() + const textContentsStr = textContents + .join('') + .replace(/\s+/g, '') + console.log(textContentsStr) + return textContentsStr + }) + .toBe(highlightCode) + await page.mouse.move(nothing[0], nothing[1]) + }).toPass({ timeout: 40_000, intervals: [500] }) + }) + await test.step(`click should put the cursor in the right place`, async () => { + // await page.mouse.move(nothing[0], nothing[1], { steps: 5 }) + // await expect(highlightedLocator.first()).not.toBeVisible() + await page.mouse.click(coord[0], coord[1]) await expect .poll(async () => { - let textContents = await highlightedLocator.allTextContents() - const textContentsStr = textContents - .join('') - .replace(/\s+/g, '') - console.log(textContentsStr) - return textContentsStr + const activeLines = await activeLineLocator.allInnerTexts() + return activeLines.join('') }) - .toBe(highlightCode) - await page.mouse.move(nothing[0], nothing[1]) - }).toPass({ timeout: 40_000, intervals: [500] }) + .toContain(activeLine) + // check pixels near the click location are yellow + }) + await test.step(`check the engine agrees with selections`, async () => { + // ultimately the only way we know if the engine agrees with the selection from the FE + // perspective is if it highlights the pixels near where we clicked yellow. + await expect + .poll(async () => { + const RGBs = await u.getPixelRGBs({ x: coord[0], y: coord[1] }, 3) + for (const rgb of RGBs) { + const [r, g, b] = rgb + const RGAverage = (r + g) / 2 + const isRedGreenSameIsh = Math.abs(r - g) < 3 + const isBlueLessThanRG = RGAverage - b > 45 + const isYellowy = isRedGreenSameIsh && isBlueLessThanRG + if (isYellowy) return true + } + return false + }) + .toBeTruthy() + await page.mouse.click(nothing[0], nothing[1]) + }) }) - await test.step(`click should put the cursor in the right place`, async () => { - // await page.mouse.move(nothing[0], nothing[1], { steps: 5 }) - // await expect(highlightedLocator.first()).not.toBeVisible() - await page.mouse.click(coord[0], coord[1]) - await expect - .poll(async () => { - const activeLines = await activeLineLocator.allInnerTexts() - return activeLines.join('') - }) - .toContain(activeLine) - // check pixels near the click location are yellow - }) - await test.step(`check the engine agrees with selections`, async () => { - // ultimately the only way we know if the engine agrees with the selection from the FE - // perspective is if it highlights the pixels near where we clicked yellow. - await expect - .poll(async () => { - const RGBs = await u.getPixelRGBs({ x: coord[0], y: coord[1] }, 3) - for (const rgb of RGBs) { - const [r, g, b] = rgb - const RGAverage = (r + g) / 2 - const isRedGreenSameIsh = Math.abs(r - g) < 3 - const isBlueLessThanRG = RGAverage - b > 45 - const isYellowy = isRedGreenSameIsh && isBlueLessThanRG - if (isYellowy) return true - } - return false - }) - .toBeTruthy() - await page.mouse.click(nothing[0], nothing[1]) - }) - }) - } - - await checkCodeAtHoverPosition( - 'extrusionTopCap', - extrusionTopCap, - 'startProfileAt([20,0],%)', - 'startProfileAt([20, 0], %)' - ) - await checkCodeAtHoverPosition( - 'flatExtrusionFace', - flatExtrusionFace, - `angledLineThatIntersects({angle:3.14,intersectTag:a,offset:0},%)extrude(5+7,%)`, - '}, %)' - ) - - await checkCodeAtHoverPosition( - 'tangentialArcTo', - tangentialArcTo, - 'tangentialArcTo([13.14+0,13.14],%)extrude(5+7,%)', - 'tangentialArcTo([13.14 + 0, 13.14], %)' - ) - await checkCodeAtHoverPosition( - 'tangentialArcEdge', - tangentialArcEdge, - `tangentialArcTo([13.14+0,13.14],%)`, - 'tangentialArcTo([13.14 + 0, 13.14], %)' - ) - await checkCodeAtHoverPosition( - 'tangentialArcOppositeEdge', - tangentialArcOppositeEdge, - `tangentialArcTo([13.14+0,13.14],%)`, - 'tangentialArcTo([13.14 + 0, 13.14], %)' - ) - await checkCodeAtHoverPosition( - 'tangentialArcAdjacentEdge', - tangentialArcAdjacentEdge, - `tangentialArcTo([13.14+0,13.14],%)`, - 'tangentialArcTo([13.14 + 0, 13.14], %)' - ) - - await checkCodeAtHoverPosition( - 'close', - close, - 'close(%)extrude(5+7,%)', - 'close(%)' - ) - await checkCodeAtHoverPosition( - 'closeEdge', - closeEdge, - `close(%)`, - 'close(%)' - ) - await checkCodeAtHoverPosition( - 'closeAdjacentEdge', - closeAdjacentEdge, - `close(%)`, - 'close(%)' - ) - await checkCodeAtHoverPosition( - 'closeOppositeEdge', - closeOppositeEdge, - `close(%)`, - 'close(%)' - ) - - await checkCodeAtHoverPosition( - 'straightSegmentEdge', - straightSegmentEdge, - `angledLineToY({angle:30,to:11.14},%)`, - 'angledLineToY({ angle: 30, to: 11.14 }, %)' - ) - await checkCodeAtHoverPosition( - 'straightSegmentOppositeEdge', - straightSegmentOppositeEdge, - `angledLineToY({angle:30,to:11.14},%)`, - 'angledLineToY({ angle: 30, to: 11.14 }, %)' - ) - await checkCodeAtHoverPosition( - 'straightSegmentAdjacentEdge', - straightSegmentAdjacentEdge, - `angledLineThatIntersects({angle:3.14,intersectTag:a,offset:0},%)`, - '}, %)' - ) - - await page.waitForTimeout(200) - - await u.removeCurrentCode() - await u.codeLocator.fill(`sketch001 = startSketchOn('XZ') + } + + await checkCodeAtHoverPosition( + 'extrusionTopCap', + extrusionTopCap, + 'startProfileAt([20,0],%)', + 'startProfileAt([20, 0], %)' + ) + await checkCodeAtHoverPosition( + 'flatExtrusionFace', + flatExtrusionFace, + `angledLineThatIntersects({angle:3.14,intersectTag:a,offset:0},%)extrude(5+7,%)`, + '}, %)' + ) + + await checkCodeAtHoverPosition( + 'tangentialArcTo', + tangentialArcTo, + 'tangentialArcTo([13.14+0,13.14],%)extrude(5+7,%)', + 'tangentialArcTo([13.14 + 0, 13.14], %)' + ) + await checkCodeAtHoverPosition( + 'tangentialArcEdge', + tangentialArcEdge, + `tangentialArcTo([13.14+0,13.14],%)`, + 'tangentialArcTo([13.14 + 0, 13.14], %)' + ) + await checkCodeAtHoverPosition( + 'tangentialArcOppositeEdge', + tangentialArcOppositeEdge, + `tangentialArcTo([13.14+0,13.14],%)`, + 'tangentialArcTo([13.14 + 0, 13.14], %)' + ) + await checkCodeAtHoverPosition( + 'tangentialArcAdjacentEdge', + tangentialArcAdjacentEdge, + `tangentialArcTo([13.14+0,13.14],%)`, + 'tangentialArcTo([13.14 + 0, 13.14], %)' + ) + + await checkCodeAtHoverPosition( + 'close', + close, + 'close(%)extrude(5+7,%)', + 'close(%)' + ) + await checkCodeAtHoverPosition( + 'closeEdge', + closeEdge, + `close(%)`, + 'close(%)' + ) + await checkCodeAtHoverPosition( + 'closeAdjacentEdge', + closeAdjacentEdge, + `close(%)`, + 'close(%)' + ) + await checkCodeAtHoverPosition( + 'closeOppositeEdge', + closeOppositeEdge, + `close(%)`, + 'close(%)' + ) + + await checkCodeAtHoverPosition( + 'straightSegmentEdge', + straightSegmentEdge, + `angledLineToY({angle:30,to:11.14},%)`, + 'angledLineToY({ angle: 30, to: 11.14 }, %)' + ) + await checkCodeAtHoverPosition( + 'straightSegmentOppositeEdge', + straightSegmentOppositeEdge, + `angledLineToY({angle:30,to:11.14},%)`, + 'angledLineToY({ angle: 30, to: 11.14 }, %)' + ) + await checkCodeAtHoverPosition( + 'straightSegmentAdjacentEdge', + straightSegmentAdjacentEdge, + `angledLineThatIntersects({angle:3.14,intersectTag:a,offset:0},%)`, + '}, %)' + ) + + await page.waitForTimeout(200) + + await u.removeCurrentCode() + await u.codeLocator.fill(`sketch001 = startSketchOn('XZ') |> startProfileAt([75.8, 317.2], %) // [$startCapTag, $EndCapTag] |> angledLine([0, 268.43], %, $rectangleSegmentA001) |> angledLine([ @@ -727,95 +744,100 @@ test.describe('Testing selections', () => { ] }, %) `) - await expect( - page.getByTestId('model-state-indicator-execution-done') - ).toBeVisible() - - await u.openAndClearDebugPanel() - await u.sendCustomCmd({ - type: 'modeling_cmd_req', - cmd_id: uuidv4(), - cmd: { - type: 'default_camera_look_at', - vantage: { x: 16118, y: -1654, z: 5855 }, - center: { x: 4915, y: -3893, z: 4874 }, - up: { x: 0, y: 0, z: 1 }, - }, + await expect( + page.getByTestId('model-state-indicator-execution-done') + ).toBeVisible() + + await u.openAndClearDebugPanel() + await u.sendCustomCmd({ + type: 'modeling_cmd_req', + cmd_id: uuidv4(), + cmd: { + type: 'default_camera_look_at', + vantage: { x: 16118, y: -1654, z: 5855 }, + center: { x: 4915, y: -3893, z: 4874 }, + 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) + await u.closeDebugPanel() + + await page.mouse.click(nothing[0], nothing[1]) + + const oppositeChamfer: Coords2d = [577, 230] + const baseChamfer: Coords2d = [726, 258] + const adjacentChamfer1: Coords2d = [653, 99] + const adjacentChamfer2: Coords2d = [653, 430] + + await checkCodeAtHoverPosition( + 'oppositeChamfer', + oppositeChamfer, + `angledLine([segAng(rectangleSegmentA001)-90,217.26],%,$seg01)chamfer({length:30,tags:[seg01,getNextAdjacentEdge(yo),getNextAdjacentEdge(seg02),getOppositeEdge(seg01)]},%)`, + '}, %)' + ) + + await checkCodeAtHoverPosition( + 'baseChamfer', + baseChamfer, + `angledLine([segAng(rectangleSegmentA001)-90,217.26],%,$seg01)chamfer({length:30,tags:[seg01,getNextAdjacentEdge(yo),getNextAdjacentEdge(seg02),getOppositeEdge(seg01)]},%)`, + '}, %)' + ) + + await u.openAndClearDebugPanel() + await u.sendCustomCmd({ + type: 'modeling_cmd_req', + cmd_id: uuidv4(), + cmd: { + type: 'default_camera_look_at', + vantage: { x: -6414, y: 160, z: 6145 }, + center: { x: 5919, y: 1236, z: 5251 }, + 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) + await u.closeDebugPanel() + + await page.mouse.click(nothing[0], nothing[1]) + + await checkCodeAtHoverPosition( + 'adjacentChamfer1', + adjacentChamfer1, + `lineTo([profileStartX(%),profileStartY(%)],%,$seg02)chamfer({length:30,tags:[seg01,getNextAdjacentEdge(yo),getNextAdjacentEdge(seg02),getOppositeEdge(seg01)]},%)`, + '}, %)' + ) + + await checkCodeAtHoverPosition( + 'adjacentChamfer2', + adjacentChamfer2, + `angledLine([segAng(rectangleSegmentA001),-segLen(rectangleSegmentA001)],%,$yo)chamfer({length:30,tags:[seg01,getNextAdjacentEdge(yo),getNextAdjacentEdge(seg02),getOppositeEdge(seg01)]},%)`, + '}, %)' + ) }) - await page.waitForTimeout(100) - await u.sendCustomCmd({ - type: 'modeling_cmd_req', - cmd_id: uuidv4(), - cmd: { - type: 'default_camera_get_settings', - }, - }) - await page.waitForTimeout(100) - await u.closeDebugPanel() - - await page.mouse.click(nothing[0], nothing[1]) - - const oppositeChamfer: Coords2d = [577, 230] - const baseChamfer: Coords2d = [726, 258] - const adjacentChamfer1: Coords2d = [653, 99] - const adjacentChamfer2: Coords2d = [653, 430] - - await checkCodeAtHoverPosition( - 'oppositeChamfer', - oppositeChamfer, - `angledLine([segAng(rectangleSegmentA001)-90,217.26],%,$seg01)chamfer({length:30,tags:[seg01,getNextAdjacentEdge(yo),getNextAdjacentEdge(seg02),getOppositeEdge(seg01)]},%)`, - '}, %)' - ) - - await checkCodeAtHoverPosition( - 'baseChamfer', - baseChamfer, - `angledLine([segAng(rectangleSegmentA001)-90,217.26],%,$seg01)chamfer({length:30,tags:[seg01,getNextAdjacentEdge(yo),getNextAdjacentEdge(seg02),getOppositeEdge(seg01)]},%)`, - '}, %)' - ) - - await u.openAndClearDebugPanel() - await u.sendCustomCmd({ - type: 'modeling_cmd_req', - cmd_id: uuidv4(), - cmd: { - type: 'default_camera_look_at', - vantage: { x: -6414, y: 160, z: 6145 }, - center: { x: 5919, y: 1236, z: 5251 }, - 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) - await u.closeDebugPanel() - - await page.mouse.click(nothing[0], nothing[1]) - - await checkCodeAtHoverPosition( - 'adjacentChamfer1', - adjacentChamfer1, - `lineTo([profileStartX(%),profileStartY(%)],%,$seg02)chamfer({length:30,tags:[seg01,getNextAdjacentEdge(yo),getNextAdjacentEdge(seg02),getOppositeEdge(seg01)]},%)`, - '}, %)' - ) - - await checkCodeAtHoverPosition( - 'adjacentChamfer2', - adjacentChamfer2, - `angledLine([segAng(rectangleSegmentA001),-segLen(rectangleSegmentA001)],%,$yo)chamfer({length:30,tags:[seg01,getNextAdjacentEdge(yo),getNextAdjacentEdge(seg02),getOppositeEdge(seg01)]},%)`, - '}, %)' - ) }) - test("Extrude button should be disabled if there's no extrudable geometry when nothing is selected", async ({ page, homePage }) => { const u = await getUtils(page) - await page.addInitScript(async () => { - localStorage.setItem( - 'persistCode', - `sketch001 = startSketchOn('XZ') + test("Extrude button should be disabled if there's no extrudable geometry when nothing is selected", async ({ + page, + homePage, + }) => { + const u = await getUtils(page) + await page.addInitScript(async () => { + localStorage.setItem( + 'persistCode', + `sketch001 = startSketchOn('XZ') |> startProfileAt([3.29, 7.86], %) |> line([2.48, 2.44], %) |> line([2.66, 1.17], %) @@ -830,32 +852,32 @@ test.describe('Testing selections', () => { |> close(%) extrude001 = extrude(10, sketch001) ` - ) - }) - await page.setBodyDimensions({ width: 1000, height: 500 }) - - await homePage.goToModelingScene() - - // wait for execution done - await u.openDebugPanel() - await u.expectCmdLog('[data-message-type="execution-done"]') - await u.closeDebugPanel() - - const selectUnExtrudable = () => - page.getByText(`line([4.99, -0.46], %, $seg01)`).click() - const clickEmpty = () => page.mouse.click(700, 460) - await selectUnExtrudable() - // expect extrude button to be disabled - await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled() - - await clickEmpty() - - // expect active line to contain nothing - await expect(page.locator('.cm-activeLine')).toHaveText('') - // and extrude to still be disabled - await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled() - - const codeToAdd = `${await u.codeLocator.allInnerTexts()} + ) + }) + await page.setBodyDimensions({ width: 1000, height: 500 }) + + await homePage.goToModelingScene() + + // wait for execution done + await u.openDebugPanel() + await u.expectCmdLog('[data-message-type="execution-done"]') + await u.closeDebugPanel() + + const selectUnExtrudable = () => + page.getByText(`line([4.99, -0.46], %, $seg01)`).click() + const clickEmpty = () => page.mouse.click(700, 460) + await selectUnExtrudable() + // expect extrude button to be disabled + await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled() + + await clickEmpty() + + // expect active line to contain nothing + await expect(page.locator('.cm-activeLine')).toHaveText('') + // and extrude to still be disabled + await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled() + + const codeToAdd = `${await u.codeLocator.allInnerTexts()} sketch002 = startSketchOn(extrude001, $seg01) |> startProfileAt([-12.94, 6.6], %) |> line([2.45, -0.2], %) @@ -863,60 +885,63 @@ test.describe('Testing selections', () => { |> lineTo([profileStartX(%), profileStartY(%)], %) |> close(%) ` - await u.codeLocator.fill(codeToAdd) - - await selectUnExtrudable() - // expect extrude button to be disabled - await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled() - - await clickEmpty() - await expect(page.locator('.cm-activeLine')).toHaveText('') - // there's not extrudable geometry, so button should be enabled - await expect( - page.getByRole('button', { name: 'Extrude' }) - ).not.toBeDisabled() }) + await u.codeLocator.fill(codeToAdd) - test('Fillet button states test', async ({ page, homePage }) => { const u = await getUtils(page) - await page.addInitScript(async () => { - localStorage.setItem( - 'persistCode', - `sketch001 = startSketchOn('XZ') + await selectUnExtrudable() + // expect extrude button to be disabled + await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled() + + await clickEmpty() + await expect(page.locator('.cm-activeLine')).toHaveText('') + // there's not extrudable geometry, so button should be enabled + await expect( + page.getByRole('button', { name: 'Extrude' }) + ).not.toBeDisabled() + }) + + test('Fillet button states test', async ({ page, homePage }) => { + const u = await getUtils(page) + await page.addInitScript(async () => { + localStorage.setItem( + 'persistCode', + `sketch001 = startSketchOn('XZ') |> startProfileAt([-5, -5], %) |> line([0, 10], %) |> line([10, 0], %) |> line([0, -10], %) |> lineTo([profileStartX(%), profileStartY(%)], %) |> close(%)` - ) - }) - - await page.setBodyDimensions({ width: 1000, height: 500 }) - await homePage.goToModelingScene() - await u.openDebugPanel() - await u.expectCmdLog('[data-message-type="execution-done"]') - await u.closeDebugPanel() - - const selectSegment = () => page.getByText(`line([10, 0], %)`).click() - const selectClose = () => page.getByText(`close(%)`).click() - const clickEmpty = () => page.mouse.click(950, 100) - - // expect fillet button without any bodies in the scene - await selectSegment() - await expect(page.getByRole('button', { name: 'Fillet' })).toBeDisabled() - await clickEmpty() - await expect(page.getByRole('button', { name: 'Fillet' })).toBeDisabled() - - // test fillet button with the body in the scene - const codeToAdd = `${await u.codeLocator.allInnerTexts()} + ) + }) + + await page.setBodyDimensions({ width: 1000, height: 500 }) + await homePage.goToModelingScene() + await u.openDebugPanel() + await u.expectCmdLog('[data-message-type="execution-done"]') + await u.closeDebugPanel() + + const selectSegment = () => page.getByText(`line([10, 0], %)`).click() + const selectClose = () => page.getByText(`close(%)`).click() + const clickEmpty = () => page.mouse.click(950, 100) + + // expect fillet button without any bodies in the scene + await selectSegment() + await expect(page.getByRole('button', { name: 'Fillet' })).toBeDisabled() + await clickEmpty() + await expect(page.getByRole('button', { name: 'Fillet' })).toBeDisabled() + + // test fillet button with the body in the scene + const codeToAdd = `${await u.codeLocator.allInnerTexts()} extrude001 = extrude(10, sketch001)` - await u.codeLocator.clear() - await u.codeLocator.fill(codeToAdd) - await selectSegment() - await expect(page.getByRole('button', { name: 'Fillet' })).toBeEnabled() - await selectClose() - await expect(page.getByRole('button', { name: 'Fillet' })).toBeDisabled() - await clickEmpty() - await expect(page.getByRole('button', { name: 'Fillet' })).toBeEnabled() }) + await u.codeLocator.clear() + await u.codeLocator.fill(codeToAdd) + await selectSegment() + await expect(page.getByRole('button', { name: 'Fillet' })).toBeEnabled() + await selectClose() + await expect(page.getByRole('button', { name: 'Fillet' })).toBeDisabled() + await clickEmpty() + await expect(page.getByRole('button', { name: 'Fillet' })).toBeEnabled() + }) const removeAfterFirstParenthesis = (inputString: string) => { const index = inputString.indexOf('(') @@ -926,25 +951,29 @@ test.describe('Testing selections', () => { 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 }) => { const cases = [ - { - pos: [694, 185], - expectedCode: 'line([74.36, 130.4], %, $seg01)', - }, - { - pos: [816, 244], - expectedCode: 'angledLine([segAng(seg01), yo], %)', - }, - { - pos: [1107, 161], - expectedCode: 'tangentialArcTo([167.95, -28.85], %)', - }, - ] as const - await page.addInitScript( - async ({ cases }) => { - localStorage.setItem( - 'persistCode', - `yo = 79 + test('Testing selections (and hovers) work on sketches when NOT in sketch mode', async ({ + page, + homePage, + }) => { + const cases = [ + { + pos: [694, 185], + expectedCode: 'line([74.36, 130.4], %, $seg01)', + }, + { + pos: [816, 244], + expectedCode: 'angledLine([segAng(seg01), yo], %)', + }, + { + pos: [1107, 161], + expectedCode: 'tangentialArcTo([167.95, -28.85], %)', + }, + ] as const + await page.addInitScript( + async ({ cases }) => { + localStorage.setItem( + 'persistCode', + `yo = 79 part001 = startSketchOn('XZ') |> startProfileAt([-7.54, -26.74], %) |> ${cases[0].expectedCode} @@ -952,47 +981,52 @@ test.describe('Testing selections', () => { |> ${cases[1].expectedCode} |> line([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 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 ) - }, - { cases } - ) - const u = await getUtils(page) - await page.setBodyDimensions({ width: 1200, height: 500 }) - - await homePage.goToModelingScene() - 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 }, - }, + // 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 + ) + } }) - 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 - ) - } }) - test("Hovering and selection of extruded faces works, and is not overridden shortly after user's click", async ({ page, homePage }) => { await page.addInitScript(async () => { - localStorage.setItem( - 'persistCode', - `sketch001 = startSketchOn('XZ') + test("Hovering and selection of extruded faces works, and is not overridden shortly after user's click", async ({ + page, + homePage, + }) => { + await page.addInitScript(async () => { + localStorage.setItem( + 'persistCode', + `sketch001 = startSketchOn('XZ') |> startProfileAt([-79.26, 95.04], %) |> line([112.54, 127.64], %) |> line([170.36, -121.61], %, $seg01) @@ -1000,108 +1034,113 @@ test.describe('Testing selections', () => { |> close(%) extrude001 = extrude(50, sketch001) ` + ) + }) + const u = await getUtils(page) + await page.setBodyDimensions({ width: 1200, height: 500 }) + + await homePage.goToModelingScene() + 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([170.36, -121.61], %, $seg01)` + + const cap = { x: 594, y: 283 } + const capText = `startProfileAt([-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(100) + 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(200) + 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(200) + 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(300) + 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(200) + 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(200) + 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) }) - const u = await getUtils(page) - await page.setBodyDimensions({ width: 1200, height: 500 }) - - await homePage.goToModelingScene() - 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([170.36, -121.61], %, $seg01)` - - const cap = { x: 594, y: 283 } - const capText = `startProfileAt([-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(100) - 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(200) - 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(200) - 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(300) - 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(200) - 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(200) - 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 }) => { const u = await getUtils(page) - const selectionsSnippets = { - extrudeAndEditBlocked: '|> startProfileAt([10.81, 32.99], %)', - extrudeAndEditBlockedInFunction: '|> startProfileAt(pos, %)', - extrudeAndEditAllowed: '|> startProfileAt([15.72, 4.7], %)', - editOnly: '|> startProfileAt([15.79, -14.6], %)', - } - await page.addInitScript( - async ({ - extrudeAndEditBlocked, - extrudeAndEditBlockedInFunction, - extrudeAndEditAllowed, - editOnly, - }: any) => { - localStorage.setItem( - 'persistCode', - `part001 = startSketchOn('XZ') + test("Various pipe expressions should and shouldn't allow edit and or extrude", async ({ + page, + homePage, + }) => { + const u = await getUtils(page) + const selectionsSnippets = { + extrudeAndEditBlocked: '|> startProfileAt([10.81, 32.99], %)', + extrudeAndEditBlockedInFunction: '|> startProfileAt(pos, %)', + extrudeAndEditAllowed: '|> startProfileAt([15.72, 4.7], %)', + editOnly: '|> startProfileAt([15.79, -14.6], %)', + } + await page.addInitScript( + async ({ + extrudeAndEditBlocked, + extrudeAndEditBlockedInFunction, + extrudeAndEditAllowed, + editOnly, + }: any) => { + localStorage.setItem( + 'persistCode', + `part001 = startSketchOn('XZ') ${extrudeAndEditBlocked} |> line([25.96, 2.93], %) |> line([5.25, -5.72], %) @@ -1135,127 +1174,133 @@ test.describe('Testing selections', () => { yohey([15.79, -34.6]) ` - ) - }, - selectionsSnippets - ) - await page.setBodyDimensions({ width: 1200, height: 1000 }) - - await homePage.goToModelingScene() - - // wait for execution done - await u.openDebugPanel() - await u.expectCmdLog('[data-message-type="execution-done"]') - await u.closeDebugPanel() - - // wait for start sketch as a proxy for the stream being ready - await expect( - page.getByRole('button', { name: 'Start Sketch' }) - ).not.toBeDisabled() - - await page.getByText(selectionsSnippets.extrudeAndEditBlocked).click() - await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled() - - await page.getByText(selectionsSnippets.extrudeAndEditAllowed).click() - await expect( - page.getByRole('button', { name: 'Extrude' }) - ).not.toBeDisabled() - await expect( - page.getByRole('button', { name: 'Edit Sketch' }) - ).not.toBeDisabled() - - await page.getByText(selectionsSnippets.editOnly).click() - await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled() - await expect( - page.getByRole('button', { name: 'Edit Sketch' }) - ).not.toBeDisabled() - - await page - .getByText(selectionsSnippets.extrudeAndEditBlockedInFunction) - .click() - await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled() - await expect( - page.getByRole('button', { name: 'Edit Sketch' }) - ).not.toBeVisible() }) + ) + }, + selectionsSnippets + ) + await page.setBodyDimensions({ width: 1200, height: 1000 }) - test('Deselecting line tool should mean nothing happens on click', async ({ page, homePage }) => { /** - * If the line tool is clicked when the state is 'No Points' it will exit Sketch mode. - * This is the same exact workflow as pressing ESC. - * - * To continue to test this workflow, we now enter sketch mode and place a single point before exiting the line tool. - */ - const u = await getUtils(page) - await page.setBodyDimensions({ width: 1200, height: 500 }) - - await homePage.goToModelingScene() - await u.openDebugPanel() - - 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 u.doAndWaitForImageDiff( - () => page.getByRole('button', { name: 'Start Sketch' }).click(), - 200 - ) - - // Clicks the XZ Plane in the page - await page.mouse.click(700, 200) - - await expect(page.locator('.cm-content')).toHaveText( - `sketch001 = startSketchOn('XZ')` - ) - - await page.waitForTimeout(600) - - // Place a point because the line tool will exit if no points are pressed - await page.mouse.click(650, 200) - await page.waitForTimeout(600) - - // Code before exiting the tool - let previousCodeContent = await page.locator('.cm-content').innerText() - - // deselect the line tool by clicking it - await page.getByRole('button', { name: 'line Line', exact: true }).click() - - await page.mouse.click(700, 200) - await page.waitForTimeout(100) - await page.mouse.click(700, 250) - await page.waitForTimeout(100) - await page.mouse.click(750, 200) - await page.waitForTimeout(100) - - // expect no change - await expect(page.locator('.cm-content')).toHaveText(previousCodeContent) - - // select line tool again - await page.getByRole('button', { name: 'line Line', exact: true }).click() - - await u.closeDebugPanel() - - // line tool should work as expected again - await page.mouse.click(700, 200) - await expect(page.locator('.cm-content')).not.toHaveText( - previousCodeContent - ) - previousCodeContent = await page.locator('.cm-content').innerText() - - await page.waitForTimeout(100) - await page.mouse.click(700, 300) - await expect(page.locator('.cm-content')).not.toHaveText( - previousCodeContent - ) - previousCodeContent = await page.locator('.cm-content').innerText() - - await page.waitForTimeout(100) - await page.mouse.click(750, 300) - await expect(page.locator('.cm-content')).not.toHaveText( - previousCodeContent - ) - previousCodeContent = await page.locator('.cm-content').innerText() }) + await homePage.goToModelingScene() + + // wait for execution done + await u.openDebugPanel() + await u.expectCmdLog('[data-message-type="execution-done"]') + await u.closeDebugPanel() + + // wait for start sketch as a proxy for the stream being ready + await expect( + page.getByRole('button', { name: 'Start Sketch' }) + ).not.toBeDisabled() + + await page.getByText(selectionsSnippets.extrudeAndEditBlocked).click() + await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled() + + await page.getByText(selectionsSnippets.extrudeAndEditAllowed).click() + await expect( + page.getByRole('button', { name: 'Extrude' }) + ).not.toBeDisabled() + await expect( + page.getByRole('button', { name: 'Edit Sketch' }) + ).not.toBeDisabled() + + await page.getByText(selectionsSnippets.editOnly).click() + await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled() + await expect( + page.getByRole('button', { name: 'Edit Sketch' }) + ).not.toBeDisabled() + + await page + .getByText(selectionsSnippets.extrudeAndEditBlockedInFunction) + .click() + await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled() + await expect( + page.getByRole('button', { name: 'Edit Sketch' }) + ).not.toBeVisible() + }) + + test('Deselecting line tool should mean nothing happens on click', async ({ + page, + homePage, + }) => { + /** + * If the line tool is clicked when the state is 'No Points' it will exit Sketch mode. + * This is the same exact workflow as pressing ESC. + * + * To continue to test this workflow, we now enter sketch mode and place a single point before exiting the line tool. + */ + const u = await getUtils(page) + await page.setBodyDimensions({ width: 1200, height: 500 }) + + await homePage.goToModelingScene() + await u.openDebugPanel() + + 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 u.doAndWaitForImageDiff( + () => page.getByRole('button', { name: 'Start Sketch' }).click(), + 200 + ) + + // Clicks the XZ Plane in the page + await page.mouse.click(700, 200) + + await expect(page.locator('.cm-content')).toHaveText( + `sketch001 = startSketchOn('XZ')` + ) + + await page.waitForTimeout(600) + + // Place a point because the line tool will exit if no points are pressed + await page.mouse.click(650, 200) + await page.waitForTimeout(600) + + // Code before exiting the tool + let previousCodeContent = await page.locator('.cm-content').innerText() + + // deselect the line tool by clicking it + await page.getByRole('button', { name: 'line Line', exact: true }).click() + + await page.mouse.click(700, 200) + await page.waitForTimeout(100) + await page.mouse.click(700, 250) + await page.waitForTimeout(100) + await page.mouse.click(750, 200) + await page.waitForTimeout(100) + + // expect no change + await expect(page.locator('.cm-content')).toHaveText(previousCodeContent) + + // select line tool again + await page.getByRole('button', { name: 'line Line', exact: true }).click() + + await u.closeDebugPanel() + + // line tool should work as expected again + await page.mouse.click(700, 200) + await expect(page.locator('.cm-content')).not.toHaveText( + previousCodeContent + ) + previousCodeContent = await page.locator('.cm-content').innerText() + + await page.waitForTimeout(100) + await page.mouse.click(700, 300) + await expect(page.locator('.cm-content')).not.toHaveText( + previousCodeContent + ) + previousCodeContent = await page.locator('.cm-content').innerText() + + await page.waitForTimeout(100) + await page.mouse.click(750, 300) + await expect(page.locator('.cm-content')).not.toHaveText( + previousCodeContent + ) + previousCodeContent = await page.locator('.cm-content').innerText() + }) }) diff --git a/e2e/playwright/various.spec.ts b/e2e/playwright/various.spec.ts index 3b4767cd4..3098f89bc 100644 --- a/e2e/playwright/various.spec.ts +++ b/e2e/playwright/various.spec.ts @@ -4,42 +4,44 @@ import { doExport, getUtils, makeTemplate } from './test-utils' test.fixme('Units menu', async ({ page, homePage }) => { const u = await getUtils(page) -await page.setBodyDimensions({ width: 1200, height: 500 }) -await homePage.goToModelingScene() + await page.setBodyDimensions({ width: 1200, height: 500 }) + await homePage.goToModelingScene() -const unitsMenuButton = page.getByRole('button', { - name: 'Current Units', - exact: false, + const unitsMenuButton = page.getByRole('button', { + name: 'Current Units', + exact: false, + }) + await expect(unitsMenuButton).toBeVisible() + await expect(unitsMenuButton).toContainText('in') + + await unitsMenuButton.click() + const millimetersButton = page.getByRole('button', { name: 'Millimeters' }) + + await expect(millimetersButton).toBeVisible() + await millimetersButton.click() + + // Look out for the toast message + const toastMessage = page.getByText( + `Set default unit to "mm" for this project` + ) + await expect(toastMessage).toBeVisible() + + // Verify that the popover has closed + await expect(millimetersButton).not.toBeAttached() + + // Verify that the button label has updated + await expect(unitsMenuButton).toContainText('mm') }) -await expect(unitsMenuButton).toBeVisible() -await expect(unitsMenuButton).toContainText('in') -await unitsMenuButton.click() -const millimetersButton = page.getByRole('button', { name: 'Millimeters' }) - -await expect(millimetersButton).toBeVisible() -await millimetersButton.click() - -// Look out for the toast message -const toastMessage = page.getByText( - `Set default unit to "mm" for this project` -) -await expect(toastMessage).toBeVisible() - -// Verify that the popover has closed -await expect(millimetersButton).not.toBeAttached() - -// Verify that the button label has updated -await expect(unitsMenuButton).toContainText('mm') }) - -test('Successful export shows a success toast', async ({ page, homePage }) => { // FYI this test doesn't work with only engine running locally -// And you will need to have the KittyCAD CLI installed -const u = await getUtils(page) -await page.addInitScript(async () => { - ;(window as any).playwrightSkipFilePicker = true - localStorage.setItem( - 'persistCode', - `topAng = 25 +test('Successful export shows a success toast', async ({ page, homePage }) => { + // FYI this test doesn't work with only engine running locally + // And you will need to have the KittyCAD CLI installed + const u = await getUtils(page) + await page.addInitScript(async () => { + ;(window as any).playwrightSkipFilePicker = true + localStorage.setItem( + 'persistCode', + `topAng = 25 bottomAng = 35 baseLen = 3.5 baseHeight = 1 @@ -77,158 +79,172 @@ part001 = startSketchOn('-XZ') |> xLineTo(ZERO, %) |> close(%) |> extrude(4, %)` + ) + }) + await page.setBodyDimensions({ width: 1200, height: 500 }) + + await homePage.goToModelingScene() + await u.openDebugPanel() + await u.expectCmdLog('[data-message-type="execution-done"]') + await u.waitForCmdReceive('extrude') + await page.waitForTimeout(1000) + await u.clearAndCloseDebugPanel() + + await doExport( + { + type: 'gltf', + storage: 'embedded', + presentation: 'pretty', + }, + page ) }) -await page.setBodyDimensions({ width: 1200, height: 500 }) -await homePage.goToModelingScene() -await u.openDebugPanel() -await u.expectCmdLog('[data-message-type="execution-done"]') -await u.waitForCmdReceive('extrude') -await page.waitForTimeout(1000) -await u.clearAndCloseDebugPanel() - -await doExport( - { - type: 'gltf', - storage: 'embedded', - presentation: 'pretty', - }, - page -) - -}) - -test('Paste should not work unless an input is focused', async ({ page, browserName, homePage }) => { // To run this test locally, uncomment Firefox in playwright.config.ts -test.skip( - browserName !== 'firefox', - "This bug is really Firefox-only, which we don't run in CI." -) -const u = await getUtils(page) -await page.setBodyDimensions({ width: 1200, height: 500 }) -await homePage.goToModelingScene() -await page - .getByRole('button', { name: 'Start Sketch' }) - .waitFor({ state: 'visible' }) - -const codeEditorText = page.locator('.cm-content') -const pasteContent = `// was this pasted?` -const typeContent = `// this should be typed` - -// Load text into the clipboard -await page.evaluate((t) => navigator.clipboard.writeText(t), pasteContent) - -// Focus the text editor -await codeEditorText.focus() - -// Show that we can type into it -await page.keyboard.type(typeContent) -await page.keyboard.press('Enter') - -// Paste without the code pane focused -await codeEditorText.blur() -await page.keyboard.press('ControlOrMeta+KeyV') - -// Show that the paste didn't work but typing did -await expect(codeEditorText).not.toContainText(pasteContent) -await expect(codeEditorText).toContainText(typeContent) - -// Paste with the code editor focused -// Following this guidance: https://github.com/microsoft/playwright/issues/8114 -await codeEditorText.focus() -await page.keyboard.press('ControlOrMeta+KeyV') -await expect( - await page.evaluate( - () => document.querySelector('.cm-content')?.textContent +test('Paste should not work unless an input is focused', async ({ + page, + browserName, + homePage, +}) => { + // To run this test locally, uncomment Firefox in playwright.config.ts + test.skip( + browserName !== 'firefox', + "This bug is really Firefox-only, which we don't run in CI." ) -).toContain(pasteContent) }) + const u = await getUtils(page) + await page.setBodyDimensions({ width: 1200, height: 500 }) + await homePage.goToModelingScene() + await page + .getByRole('button', { name: 'Start Sketch' }) + .waitFor({ state: 'visible' }) -test('Keyboard shortcuts can be viewed through the help menu', async ({ page, homePage }) => { const u = await getUtils(page) -await page.setBodyDimensions({ width: 1200, height: 500 }) -await homePage.goToModelingScene() + const codeEditorText = page.locator('.cm-content') + const pasteContent = `// was this pasted?` + const typeContent = `// this should be typed` -await page.waitForURL('file:///**', { waitUntil: 'domcontentloaded' }) -await page - .getByRole('button', { name: 'Start Sketch' }) - .waitFor({ state: 'visible' }) + // Load text into the clipboard + await page.evaluate((t) => navigator.clipboard.writeText(t), pasteContent) -// Open the help menu -await page.getByRole('button', { name: 'Help and resources' }).click() + // Focus the text editor + await codeEditorText.focus() -// Open the keyboard shortcuts -await page.getByRole('button', { name: 'Keyboard Shortcuts' }).click() + // Show that we can type into it + await page.keyboard.type(typeContent) + await page.keyboard.press('Enter') -// Verify the URL and that you can see a list of shortcuts -await expect.poll(() => page.url()).toContain('?tab=keybindings') -await expect( - page.getByRole('heading', { name: 'Enter Sketch Mode' }) -).toBeAttached() + // Paste without the code pane focused + await codeEditorText.blur() + await page.keyboard.press('ControlOrMeta+KeyV') + + // Show that the paste didn't work but typing did + await expect(codeEditorText).not.toContainText(pasteContent) + await expect(codeEditorText).toContainText(typeContent) + + // Paste with the code editor focused + // Following this guidance: https://github.com/microsoft/playwright/issues/8114 + await codeEditorText.focus() + await page.keyboard.press('ControlOrMeta+KeyV') + await expect( + await page.evaluate( + () => document.querySelector('.cm-content')?.textContent + ) + ).toContain(pasteContent) }) -test('First escape in tool pops you out of tool, second exits sketch mode', async ({ page, homePage }) => { // Wait for the app to be ready for use -const u = await getUtils(page) -await page.setBodyDimensions({ width: 1200, height: 500 }) +test('Keyboard shortcuts can be viewed through the help menu', async ({ + page, + homePage, +}) => { + const u = await getUtils(page) + await page.setBodyDimensions({ width: 1200, height: 500 }) + await homePage.goToModelingScene() -await homePage.goToModelingScene() -await u.openDebugPanel() -await u.expectCmdLog('[data-message-type="execution-done"]') -await u.closeDebugPanel() + await page.waitForURL('file:///**', { waitUntil: 'domcontentloaded' }) + await page + .getByRole('button', { name: 'Start Sketch' }) + .waitFor({ state: 'visible' }) -const lineButton = page.getByRole('button', { - name: 'line Line', - exact: true, -}) -const arcButton = page.getByRole('button', { - name: 'arc Tangential Arc', - exact: true, + // Open the help menu + await page.getByRole('button', { name: 'Help and resources' }).click() + + // Open the keyboard shortcuts + await page.getByRole('button', { name: 'Keyboard Shortcuts' }).click() + + // Verify the URL and that you can see a list of shortcuts + await expect.poll(() => page.url()).toContain('?tab=keybindings') + await expect( + page.getByRole('heading', { name: 'Enter Sketch Mode' }) + ).toBeAttached() }) -// Test these hotkeys perform actions when -// focus is on the canvas -await page.mouse.move(600, 250) -await page.mouse.click(600, 250) +test('First escape in tool pops you out of tool, second exits sketch mode', async ({ + page, + homePage, +}) => { + // Wait for the app to be ready for use + const u = await getUtils(page) + await page.setBodyDimensions({ width: 1200, height: 500 }) -// Start a sketch -await page.keyboard.press('s') -await page.mouse.move(800, 300) -await page.mouse.click(800, 300) -await page.waitForTimeout(1000) -await expect(lineButton).toBeVisible() -await expect(lineButton).toHaveAttribute('aria-pressed', 'true') + await homePage.goToModelingScene() + await u.openDebugPanel() + await u.expectCmdLog('[data-message-type="execution-done"]') + await u.closeDebugPanel() -// Draw a line -await page.mouse.move(700, 200, { steps: 5 }) -await page.mouse.click(700, 200) -await page.mouse.move(800, 250, { steps: 5 }) -await page.mouse.click(800, 250) -// Unequip line tool -await page.keyboard.press('Escape') -// Make sure we didn't pop out of sketch mode. -await expect(page.getByRole('button', { name: 'Exit Sketch' })).toBeVisible() -await expect(lineButton).not.toHaveAttribute('aria-pressed', 'true') -// Equip arc tool -await page.keyboard.press('a') -await expect(arcButton).toHaveAttribute('aria-pressed', 'true') -await page.mouse.move(1000, 100, { steps: 5 }) -await page.mouse.click(1000, 100) -await page.keyboard.press('Escape') -await page.keyboard.press('l') -await expect(lineButton).toHaveAttribute('aria-pressed', 'true') + const lineButton = page.getByRole('button', { + name: 'line Line', + exact: true, + }) + const arcButton = page.getByRole('button', { + name: 'arc Tangential Arc', + exact: true, + }) -// Do not close the sketch. -// On close it will exit sketch mode. + // Test these hotkeys perform actions when + // focus is on the canvas + await page.mouse.move(600, 250) + await page.mouse.click(600, 250) -// Unequip line tool -await page.keyboard.press('Escape') -await expect(lineButton).toHaveAttribute('aria-pressed', 'false') -await expect(arcButton).toHaveAttribute('aria-pressed', 'false') -// Make sure we didn't pop out of sketch mode. -await expect(page.getByRole('button', { name: 'Exit Sketch' })).toBeVisible() -// Exit sketch -await page.keyboard.press('Escape') -await expect( - page.getByRole('button', { name: 'Exit Sketch' }) -).not.toBeVisible() }) + // Start a sketch + await page.keyboard.press('s') + await page.mouse.move(800, 300) + await page.mouse.click(800, 300) + await page.waitForTimeout(1000) + await expect(lineButton).toBeVisible() + await expect(lineButton).toHaveAttribute('aria-pressed', 'true') + + // Draw a line + await page.mouse.move(700, 200, { steps: 5 }) + await page.mouse.click(700, 200) + await page.mouse.move(800, 250, { steps: 5 }) + await page.mouse.click(800, 250) + // Unequip line tool + await page.keyboard.press('Escape') + // Make sure we didn't pop out of sketch mode. + await expect(page.getByRole('button', { name: 'Exit Sketch' })).toBeVisible() + await expect(lineButton).not.toHaveAttribute('aria-pressed', 'true') + // Equip arc tool + await page.keyboard.press('a') + await expect(arcButton).toHaveAttribute('aria-pressed', 'true') + await page.mouse.move(1000, 100, { steps: 5 }) + await page.mouse.click(1000, 100) + await page.keyboard.press('Escape') + await page.keyboard.press('l') + await expect(lineButton).toHaveAttribute('aria-pressed', 'true') + + // Do not close the sketch. + // On close it will exit sketch mode. + + // Unequip line tool + await page.keyboard.press('Escape') + await expect(lineButton).toHaveAttribute('aria-pressed', 'false') + await expect(arcButton).toHaveAttribute('aria-pressed', 'false') + // Make sure we didn't pop out of sketch mode. + await expect(page.getByRole('button', { name: 'Exit Sketch' })).toBeVisible() + // Exit sketch + await page.keyboard.press('Escape') + await expect( + page.getByRole('button', { name: 'Exit Sketch' }) + ).not.toBeVisible() +}) test.fixme( 'Basic default modeling and sketch hotkeys work', @@ -412,37 +428,38 @@ test.fixme( ) test('Delete key does not navigate back', async ({ page, homePage }) => { + await page.setBodyDimensions({ width: 1200, height: 500 }) + await homePage.goToModelingScene() -await page.setBodyDimensions({ width: 1200, height: 500 }) -await homePage.goToModelingScene(); + await page.waitForURL('file:///**', { waitUntil: 'domcontentloaded' }) -await page.waitForURL('file:///**', { waitUntil: 'domcontentloaded' }) + const settingsButton = page.getByRole('link', { + name: 'Settings', + exact: false, + }) + const settingsCloseButton = page.getByTestId('settings-close-button') -const settingsButton = page.getByRole('link', { - name: 'Settings', - exact: false, + await settingsButton.click() + await expect.poll(() => page.url()).toContain('/settings') + + // Make sure that delete doesn't go back from settings + await page.keyboard.press('Delete') + await expect.poll(() => page.url()).toContain('/settings') + + // Now close the settings and try delete again, + // make sure it doesn't go back to settings + await settingsCloseButton.click() + await page.keyboard.press('Delete') + await expect.poll(() => page.url()).not.toContain('/settings') }) -const settingsCloseButton = page.getByTestId('settings-close-button') -await settingsButton.click() -await expect.poll(() => page.url()).toContain('/settings') - -// Make sure that delete doesn't go back from settings -await page.keyboard.press('Delete') -await expect.poll(() => page.url()).toContain('/settings') - -// Now close the settings and try delete again, -// make sure it doesn't go back to settings -await settingsCloseButton.click() -await page.keyboard.press('Delete') -await expect.poll(() => page.url()).not.toContain('/settings') }) - -test('Sketch on face', async ({ page, homePage }) => { test.setTimeout(90_000) -const u = await getUtils(page) -await page.addInitScript(async () => { - localStorage.setItem( - 'persistCode', - `sketch001 = startSketchOn('XZ') +test('Sketch on face', async ({ page, homePage }) => { + test.setTimeout(90_000) + const u = await getUtils(page) + await page.addInitScript(async () => { + localStorage.setItem( + 'persistCode', + `sketch001 = startSketchOn('XZ') |> startProfileAt([3.29, 7.86], %) |> line([2.48, 2.44], %) |> line([2.66, 1.17], %) @@ -456,126 +473,127 @@ await page.addInitScript(async () => { |> line([-17.67, 0.85], %) |> close(%) extrude001 = extrude(5 + 7, sketch001)` + ) + }) + + await page.setBodyDimensions({ width: 1200, height: 500 }) + + await homePage.goToModelingScene() + + // wait for execution done + await u.openDebugPanel() + await u.expectCmdLog('[data-message-type="execution-done"]') + await u.closeDebugPanel() + + await expect( + page.getByRole('button', { name: 'Start Sketch' }) + ).not.toBeDisabled() + + await page.getByRole('button', { name: 'Start Sketch' }).click() + await page.waitForTimeout(300) + + let previousCodeContent = await page.locator('.cm-content').innerText() + + 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.setBodyDimensions({ width: 1200, height: 500 }) + const firstClickPosition = [612, 238] + const secondClickPosition = [661, 242] + const thirdClickPosition = [609, 267] -await homePage.goToModelingScene() + await page.mouse.click(firstClickPosition[0], firstClickPosition[1]) + await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent) + previousCodeContent = await page.locator('.cm-content').innerText() -// wait for execution done -await u.openDebugPanel() -await u.expectCmdLog('[data-message-type="execution-done"]') -await u.closeDebugPanel() + 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 expect( - page.getByRole('button', { name: 'Start Sketch' }) -).not.toBeDisabled() + 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.getByRole('button', { name: 'Start Sketch' }).click() -await page.waitForTimeout(300) + 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() -let previousCodeContent = await page.locator('.cm-content').innerText() - -await u.openAndClearDebugPanel() -await u.doAndWaitForCmd( - () => page.mouse.click(625, 165), - 'default_camera_get_settings', - true -) -await page.waitForTimeout(150) -await u.closeDebugPanel() - -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, seg01) + await expect.poll(u.normalisedEditorCode).toContain( + u.normalisedCode(`sketch002 = startSketchOn(extrude001, seg01) |> startProfileAt([-12.94, 6.6], %) |> line([2.45, -0.2], %) |> line([-2.6, -1.25], %) |> lineTo([profileStartX(%), profileStartY(%)], %) |> close(%) `) -) + ) -await u.openAndClearDebugPanel() -await page.getByRole('button', { name: 'Exit Sketch' }).click() -await u.expectCmdLog('[data-message-type="execution-done"]') + 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 u.updateCamPosition([1049, 239, 686]) + await u.closeDebugPanel() -await page.getByText('startProfileAt([-12').click() -await expect(page.getByRole('button', { name: 'Edit Sketch' })).toBeVisible() -await page.getByRole('button', { name: 'Edit Sketch' }).click() -await page.waitForTimeout(400) -await page.waitForTimeout(150) -await page.setBodyDimensions({ width: 1200, height: 1200 }) -await u.openAndClearDebugPanel() -await u.updateCamPosition([452, -152, 1166]) -await u.closeDebugPanel() -await page.waitForTimeout(200) + await page.getByText('startProfileAt([-12').click() + await expect(page.getByRole('button', { name: 'Edit Sketch' })).toBeVisible() + await page.getByRole('button', { name: 'Edit Sketch' }).click() + await page.waitForTimeout(400) + await page.waitForTimeout(150) + await page.setBodyDimensions({ 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 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, seg01) + const result = makeTemplate`sketch002 = startSketchOn(extrude001, seg01) |> startProfileAt([-12.83, 6.7], %) |> line([${[2.28, 2.35]}, -${0.07}], %) |> line([-3.05, -1.47], %) |> lineTo([profileStartX(%), profileStartY(%)], %) |> close(%)` -await expect(page.locator('.cm-content')).toHaveText(result.regExp) + 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"]') + // exit sketch + await u.openAndClearDebugPanel() + await page.getByRole('button', { name: 'Exit Sketch' }).click() + await u.expectCmdLog('[data-message-type="execution-done"]') -await page.getByText('startProfileAt([-12').click() + await page.getByText('startProfileAt([-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.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 expect(page.getByTestId('command-bar')).toBeVisible() + await page.waitForTimeout(100) -await page.getByRole('button', { name: 'arrow right Continue' }).click() -await page.waitForTimeout(100) -await expect(page.getByText('Confirm Extrude')).toBeVisible() -await page.getByRole('button', { name: 'checkmark Submit command' }).click() + await page.getByRole('button', { name: 'arrow right Continue' }).click() + await page.waitForTimeout(100) + await expect(page.getByText('Confirm Extrude')).toBeVisible() + await page.getByRole('button', { name: 'checkmark Submit command' }).click() -const result2 = result.genNext` + const result2 = result.genNext` const sketch002 = extrude(${[5, 5]} + 7, sketch002)` -await expect(page.locator('.cm-content')).toHaveText(result2.regExp) }) + await expect(page.locator('.cm-content')).toHaveText(result2.regExp) +}) diff --git a/e2e/playwright/zoo-test.ts b/e2e/playwright/zoo-test.ts index 57202a05d..fc6dd5776 100644 --- a/e2e/playwright/zoo-test.ts +++ b/e2e/playwright/zoo-test.ts @@ -10,7 +10,6 @@ export { expect, Page, BrowserContext, TestInfo } from '@playwright/test' // switch between web and electron if needed. const pwTestFnWithFixtures = playwrightTestFn.extend(fixtures) - // In JavaScript you cannot replace a function's body only (despite functions // are themselves objects, which you'd expect a body property or something...) // So we must redefine the function and then re-attach properties. @@ -61,7 +60,7 @@ export function test(desc, objOrFn, fnMaybe) { // We need to expose this in order for some tests that require folder // creation. Before they used to do this by their own electronSetup({...}) // calls. - tronApp.context.folderSetupFn = function(fn) { + tronApp.context.folderSetupFn = function (fn) { return fn(tronApp.dir).then(() => ({ dir: tronApp.dir })) } diff --git a/src/lib/exportSave.ts b/src/lib/exportSave.ts index 7f5e2e872..0824ec9c6 100644 --- a/src/lib/exportSave.ts +++ b/src/lib/exportSave.ts @@ -20,7 +20,7 @@ const save_ = async (file: ModelingAppFile, toastId: string) => { // Skip file picker, save to the test dir downloads directory const downloadDir = window.electron.join( window.electron.process.env.TEST_SETTINGS_FILE_KEY, - "downloads-during-playwright", + 'downloads-during-playwright' ) await window.electron.mkdir(downloadDir, { recursive: true }) await window.electron.writeFile(