diff --git a/e2e/playwright/editor-tests.spec.ts b/e2e/playwright/editor-tests.spec.ts index 6859d116f..795c468a9 100644 --- a/e2e/playwright/editor-tests.spec.ts +++ b/e2e/playwright/editor-tests.spec.ts @@ -1,4 +1,4 @@ -import { test, expect } from '@playwright/test' +import { test, expect } from './zoo-test' import fsp from 'fs/promises' import { uuidv4 } from 'lib/utils' import { @@ -6,1013 +6,955 @@ import { darkModePlaneColorXZ, executorInputPath, getUtils, - setup, - setupElectron, - tearDown, } from './test-utils' + import { join } from 'path' -test.beforeEach(async ({ context, page }, testInfo) => { - await setup(context, page, testInfo) -}) - -test.afterEach(async ({ page }, testInfo) => { - await tearDown(page, testInfo) -}) - test.describe('Editor tests', () => { - test('can comment out code with ctrl+/', async ({ page }) => { - const u = await getUtils(page) - await page.setViewportSize({ width: 1000, height: 500 }) - - await u.waitForAuthSkipAppStart() - - // check no error to begin with - await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible() - - await u.codeLocator.click() - await page.keyboard.type(`sketch001 = startSketchOn('XY') - |> startProfileAt([-10, -10], %) - |> line([20, 0], %) - |> line([0, 20], %) - |> line([-20, 0], %) - |> close(%)`) - - await page.keyboard.down('ControlOrMeta') - await page.keyboard.press('/') - await page.keyboard.up('ControlOrMeta') - - await expect(page.locator('.cm-content')) - .toHaveText(`sketch001 = startSketchOn('XY') - |> startProfileAt([-10, -10], %) - |> line([20, 0], %) - |> line([0, 20], %) - |> line([-20, 0], %) - // |> close(%)`) - - // uncomment the code - await page.keyboard.down('ControlOrMeta') - await page.keyboard.press('/') - await page.keyboard.up('ControlOrMeta') - - await expect(page.locator('.cm-content')) - .toHaveText(`sketch001 = startSketchOn('XY') + test('can comment out code with ctrl+/', async ({ page, homePage }) => { const u = await getUtils(page) + await page.setBodyDimensions({ width: 1000, height: 500 }) + + await homePage.goToModelingScene() + + // check no error to begin with + await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible() + + await u.codeLocator.click() + await page.keyboard.type(`sketch001 = startSketchOn('XY') |> startProfileAt([-10, -10], %) |> line([20, 0], %) |> line([0, 20], %) |> line([-20, 0], %) |> close(%)`) - }) - - test('if you click the format button it formats your code', async ({ - page, - }) => { - const u = await getUtils(page) - await page.setViewportSize({ width: 1000, height: 500 }) - - await u.waitForAuthSkipAppStart() - - // check no error to begin with - await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible() - - await u.codeLocator.click() - await page.keyboard.type(`sketch001 = startSketchOn('XY') + + await page.keyboard.down('ControlOrMeta') + await page.keyboard.press('/') + await page.keyboard.up('ControlOrMeta') + + await expect(page.locator('.cm-content')) + .toHaveText(`sketch001 = startSketchOn('XY') |> startProfileAt([-10, -10], %) |> line([20, 0], %) |> line([0, 20], %) |> line([-20, 0], %) - |> close(%)`) - await page.locator('#code-pane button:first-child').click() - await page.locator('button:has-text("Format code")').click() + // |> close(%)`) + + // uncomment the code + await page.keyboard.down('ControlOrMeta') + await page.keyboard.press('/') + await page.keyboard.up('ControlOrMeta') + + await expect(page.locator('.cm-content')) + .toHaveText(`sketch001 = startSketchOn('XY') + |> startProfileAt([-10, -10], %) + |> line([20, 0], %) + |> line([0, 20], %) + |> line([-20, 0], %) + |> close(%)`) }) - await expect(page.locator('.cm-content')) - .toHaveText(`sketch001 = startSketchOn('XY') + test('if you click the format button it formats your code', async ({ page, homePage }) => { const u = await getUtils(page) + await page.setBodyDimensions({ width: 1000, height: 500 }) + + await homePage.goToModelingScene() + + // check no error to begin with + await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible() + + await u.codeLocator.click() + await page.keyboard.type(`sketch001 = startSketchOn('XY') |> startProfileAt([-10, -10], %) |> line([20, 0], %) |> line([0, 20], %) |> line([-20, 0], %) |> close(%)`) - }) - - test('if you click the format button it formats your code and executes so lints are still there', async ({ - page, - }) => { - const u = await getUtils(page) - await page.setViewportSize({ width: 1000, height: 500 }) - - await u.waitForAuthSkipAppStart() - - // check no error to begin with - await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible() - - await u.codeLocator.click() - await page.keyboard.type(`sketch_001 = startSketchOn('XY') + await page.locator('#code-pane button:first-child').click() + await page.locator('button:has-text("Format code")').click() + + await expect(page.locator('.cm-content')) + .toHaveText(`sketch001 = startSketchOn('XY') |> startProfileAt([-10, -10], %) |> line([20, 0], %) |> line([0, 20], %) |> line([-20, 0], %) - |> close(%)`) + |> close(%)`) }) - await u.openDebugPanel() - await u.expectCmdLog('[data-message-type="execution-done"]') - await u.closeDebugPanel() - - // error in guter - await expect(page.locator('.cm-lint-marker-info').first()).toBeVisible() - - // error text on hover - await page.hover('.cm-lint-marker-info') - await expect( - page.getByText('Identifiers must be lowerCamelCase').first() - ).toBeVisible() - - await page.locator('#code-pane button:first-child').click() - await page.locator('button:has-text("Format code")').click() - - await u.openDebugPanel() - await u.expectCmdLog('[data-message-type="execution-done"]') - await u.closeDebugPanel() - - await expect(page.locator('.cm-content')) - .toHaveText(`sketch_001 = startSketchOn('XY') + test('if you click the format button it formats your code and executes so lints are still there', async ({ page, homePage }) => { const u = await getUtils(page) + await page.setBodyDimensions({ width: 1000, height: 500 }) + + await homePage.goToModelingScene() + + // check no error to begin with + await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible() + + await u.codeLocator.click() + await page.keyboard.type(`sketch_001 = startSketchOn('XY') |> startProfileAt([-10, -10], %) |> line([20, 0], %) |> line([0, 20], %) |> line([-20, 0], %) |> close(%)`) + + await u.openDebugPanel() + await u.expectCmdLog('[data-message-type="execution-done"]') + await u.closeDebugPanel() + + // error in guter + await expect(page.locator('.cm-lint-marker-info').first()).toBeVisible() + + // error text on hover + await page.hover('.cm-lint-marker-info') + await expect( + page.getByText('Identifiers must be lowerCamelCase').first() + ).toBeVisible() + + await page.locator('#code-pane button:first-child').click() + await page.locator('button:has-text("Format code")').click() + + await u.openDebugPanel() + await u.expectCmdLog('[data-message-type="execution-done"]') + await u.closeDebugPanel() + + await expect(page.locator('.cm-content')) + .toHaveText(`sketch_001 = startSketchOn('XY') + |> startProfileAt([-10, -10], %) + |> line([20, 0], %) + |> line([0, 20], %) + |> line([-20, 0], %) + |> close(%)`) + + // error in guter + await expect(page.locator('.cm-lint-marker-info').first()).toBeVisible() + + // error text on hover + await page.hover('.cm-lint-marker-info') + await expect( + page.getByText('Identifiers must be lowerCamelCase').first() + ).toBeVisible() }) - // error in guter - await expect(page.locator('.cm-lint-marker-info').first()).toBeVisible() - - // error text on hover - await page.hover('.cm-lint-marker-info') - await expect( - page.getByText('Identifiers must be lowerCamelCase').first() - ).toBeVisible() - }) - - test('fold gutters work', async ({ page }) => { - const u = await getUtils(page) - - const fullCode = `sketch001 = startSketchOn('XY') - |> startProfileAt([-10, -10], %) - |> line([20, 0], %) - |> line([0, 20], %) - |> line([-20, 0], %) - |> close(%)` - await page.addInitScript(async () => { - localStorage.setItem( - 'persistCode', - `sketch001 = startSketchOn('XY') - |> startProfileAt([-10, -10], %) - |> line([20, 0], %) - |> line([0, 20], %) - |> line([-20, 0], %) - |> close(%)` - ) - }) - await page.setViewportSize({ width: 1000, height: 500 }) - - await u.waitForAuthSkipAppStart() - - // TODO: Jess needs to fix this but you have to mod the code to get them to show - // up, its an annoying codemirror thing. - await page.locator('.cm-content').click() - await page.keyboard.press('ArrowDown') - await page.keyboard.press('ArrowDown') - await page.keyboard.press('ArrowDown') - await page.keyboard.press('ArrowDown') - await page.keyboard.press('ArrowDown') - await page.keyboard.press('Enter') - - const foldGutterFoldLine = page.locator('[title="Fold line"]') - const foldGutterUnfoldLine = page.locator('[title="Unfold line"]') - - await expect(page.locator('.cm-content')).toHaveText(fullCode) - - // check no error to begin with - await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible() - - // Make sure we have a fold gutter - await expect(foldGutterFoldLine).toBeVisible() - await expect(foldGutterUnfoldLine).not.toBeVisible() - - // Collapse the code - await foldGutterFoldLine.click() - - await expect(page.locator('.cm-content')).toHaveText( - `sketch001 = startSketchOn('XY')… ` + test('fold gutters work', async ({ page, homePage }) => { const u = await getUtils(page) + + const fullCode = `sketch001 = startSketchOn('XY') + |> startProfileAt([-10, -10], %) + |> line([20, 0], %) + |> line([0, 20], %) + |> line([-20, 0], %) + |> close(%)` + await page.addInitScript(async () => { + localStorage.setItem( + 'persistCode', + `sketch001 = startSketchOn('XY') + |> startProfileAt([-10, -10], %) + |> line([20, 0], %) + |> line([0, 20], %) + |> line([-20, 0], %) + |> close(%)` ) - await expect(page.locator('.cm-content')).not.toHaveText(fullCode) - await expect(foldGutterFoldLine).not.toBeVisible() - await expect(foldGutterUnfoldLine.nth(1)).toBeVisible() - - // Expand the code - await foldGutterUnfoldLine.nth(1).click() - await expect(page.locator('.cm-content')).toHaveText(fullCode) - - // Delete all the code. - await page.locator('.cm-content').click() - // Select all - await page.keyboard.press('ControlOrMeta+A') - await page.keyboard.press('Backspace') - - await expect(page.locator('.cm-content')).toHaveText(``) - await expect(page.locator('.cm-content')).not.toHaveText(fullCode) - - await expect(foldGutterUnfoldLine).not.toBeVisible() - await expect(foldGutterFoldLine).not.toBeVisible() }) + await page.setBodyDimensions({ width: 1000, height: 500 }) + + await homePage.goToModelingScene() + + // TODO: Jess needs to fix this but you have to mod the code to get them to show + // up, its an annoying codemirror thing. + await page.locator('.cm-content').click() + await page.keyboard.press('ArrowDown') + await page.keyboard.press('ArrowDown') + await page.keyboard.press('ArrowDown') + await page.keyboard.press('ArrowDown') + await page.keyboard.press('ArrowDown') + await page.keyboard.press('Enter') + + const foldGutterFoldLine = page.locator('[title="Fold line"]') + const foldGutterUnfoldLine = page.locator('[title="Unfold line"]') + + await expect(page.locator('.cm-content')).toHaveText(fullCode) + + // check no error to begin with + await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible() + + // Make sure we have a fold gutter + await expect(foldGutterFoldLine).toBeVisible() + await expect(foldGutterUnfoldLine).not.toBeVisible() + + // Collapse the code + await foldGutterFoldLine.click() + + await expect(page.locator('.cm-content')).toHaveText( + `sketch001 = startSketchOn('XY')… ` + ) + await expect(page.locator('.cm-content')).not.toHaveText(fullCode) + await expect(foldGutterFoldLine).not.toBeVisible() + await expect(foldGutterUnfoldLine.nth(1)).toBeVisible() + + // Expand the code + await foldGutterUnfoldLine.nth(1).click() + await expect(page.locator('.cm-content')).toHaveText(fullCode) + + // Delete all the code. + await page.locator('.cm-content').click() + // Select all + await page.keyboard.press('ControlOrMeta+A') + await page.keyboard.press('Backspace') + + await expect(page.locator('.cm-content')).toHaveText(``) + await expect(page.locator('.cm-content')).not.toHaveText(fullCode) + + await expect(foldGutterUnfoldLine).not.toBeVisible() + await expect(foldGutterFoldLine).not.toBeVisible() }) - test('hover over functions shows function description', async ({ page }) => { - const u = await getUtils(page) - await page.addInitScript(async () => { - localStorage.setItem( - 'persistCode', - `sketch001 = startSketchOn('XY') - |> startProfileAt([-10, -10], %) - |> line([20, 0], %) - |> line([0, 20], %) - |> line([-20, 0], %) - |> close(%)` - ) - }) - await page.setViewportSize({ width: 1000, height: 500 }) - - await u.waitForAuthSkipAppStart() - - // check no error to begin with - await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible() - - await u.openDebugPanel() - await u.expectCmdLog('[data-message-type="execution-done"]') - await u.closeDebugPanel() - - // focus the editor - await u.codeLocator.click() - - // Hover over the startSketchOn function - await page.getByText('startSketchOn').hover() - await expect(page.locator('.hover-tooltip')).toBeVisible() - await expect( - page.getByText( - 'Start a new 2-dimensional sketch on a specific plane or face' - ) - ).toBeVisible() - - // Hover over the line function - await page.getByText('line').first().hover() - await expect(page.locator('.hover-tooltip')).toBeVisible() - await expect(page.getByText('Draw a line')).toBeVisible() - }) - - test('if you use the format keyboard binding it formats your code', async ({ - page, - }) => { - const u = await getUtils(page) - await page.addInitScript(async () => { - localStorage.setItem( - 'persistCode', - `sketch001 = startSketchOn('XY') - |> startProfileAt([-10, -10], %) - |> line([20, 0], %) - |> line([0, 20], %) - |> line([-20, 0], %) - |> close(%)` - ) - localStorage.setItem('disableAxis', 'true') - }) - await page.setViewportSize({ width: 1000, height: 500 }) - - await u.waitForAuthSkipAppStart() - - // check no error to begin with - await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible() - - await u.openDebugPanel() - await u.expectCmdLog('[data-message-type="execution-done"]') - await u.closeDebugPanel() - - // focus the editor - await u.codeLocator.click() - - // Hit alt+shift+f to format the code - await page.keyboard.press('Alt+Shift+KeyF') - - await expect(page.locator('.cm-content')) - .toHaveText(`sketch001 = startSketchOn('XY') + test('hover over functions shows function description', async ({ page, homePage }) => { const u = await getUtils(page) + await page.addInitScript(async () => { + localStorage.setItem( + 'persistCode', + `sketch001 = startSketchOn('XY') |> startProfileAt([-10, -10], %) |> line([20, 0], %) |> line([0, 20], %) |> line([-20, 0], %) - |> close(%)`) - }) - - test('if you use the format keyboard binding it formats your code and executes so lints are shown', async ({ - page, - }) => { - const u = await getUtils(page) - await page.addInitScript(async () => { - localStorage.setItem( - 'persistCode', - `sketch_001 = startSketchOn('XY') - |> startProfileAt([-10, -10], %) - |> line([20, 0], %) - |> line([0, 20], %) - |> line([-20, 0], %) - |> close(%)` - ) - localStorage.setItem('disableAxis', 'true') - }) - await page.setViewportSize({ width: 1000, height: 500 }) - - await u.waitForAuthSkipAppStart() - - await u.openDebugPanel() - await u.expectCmdLog('[data-message-type="execution-done"]') - await u.closeDebugPanel() - - // error in guter - await expect(page.locator('.cm-lint-marker-info').first()).toBeVisible() - - // error text on hover - await page.hover('.cm-lint-marker-info') - await expect( - page.getByText('Identifiers must be lowerCamelCase').first() - ).toBeVisible() - - // focus the editor - await u.codeLocator.click() - - // Hit alt+shift+f to format the code - await page.keyboard.press('Alt+Shift+KeyF') - - await u.openDebugPanel() - await u.expectCmdLog('[data-message-type="execution-done"]') - await u.closeDebugPanel() - - await expect(page.locator('.cm-content')) - .toHaveText(`sketch_001 = startSketchOn('XY') - |> startProfileAt([-10, -10], %) - |> line([20, 0], %) - |> line([0, 20], %) - |> line([-20, 0], %) - |> close(%)`) - - // error in guter - await expect(page.locator('.cm-lint-marker-info').first()).toBeVisible() - - // error text on hover - await page.hover('.cm-lint-marker-info') - await expect( - page.getByText('Identifiers must be lowerCamelCase').first() - ).toBeVisible() - }) - - test('if you write kcl with lint errors you get lints', async ({ page }) => { - const u = await getUtils(page) - await page.setViewportSize({ width: 1000, height: 500 }) - - await u.waitForAuthSkipAppStart() - - // check no error to begin with - await expect(page.locator('.cm-lint-marker-info')).not.toBeVisible() - - await u.codeLocator.click() - await page.keyboard.type('my_snake_case_var = 5') - await page.keyboard.press('Enter') - await page.keyboard.type('myCamelCaseVar = 5') - await page.keyboard.press('Enter') - - // press arrows to clear autocomplete - await page.keyboard.press('ArrowLeft') - await page.keyboard.press('ArrowRight') - - // error in guter - await expect(page.locator('.cm-lint-marker-info').first()).toBeVisible() - - // error text on hover - await page.hover('.cm-lint-marker-info') - await expect( - page.getByText('Identifiers must be lowerCamelCase').first() - ).toBeVisible() - - // select the line that's causing the error and delete it - await page.getByText('my_snake_case_var = 5').click() - await page.keyboard.press('End') - await page.keyboard.down('Shift') - await page.keyboard.press('Home') - await page.keyboard.up('Shift') - await page.keyboard.press('Backspace') - - // wait for .cm-lint-marker-info not to be visible - await expect(page.locator('.cm-lint-marker-info')).not.toBeVisible() - }) - - test('if you fixup kcl errors you clear lints', async ({ page }) => { - 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], %) - |> close(%) - ` - ) - }) - - await page.setViewportSize({ width: 1000, height: 500 }) - - await u.waitForAuthSkipAppStart() - - // check no error to begin with - await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible() - - await u.codeLocator.click() - - await page.getByText(' |> line([2.48, 2.44], %)').click() - - await expect( - page.locator('.cm-lint-marker-error').first() - ).not.toBeVisible() - await page.keyboard.press('End') - await page.keyboard.press('Backspace') - - await expect(page.locator('.cm-lint-marker-error').first()).toBeVisible() - await page.keyboard.type(')') - await expect( - page.locator('.cm-lint-marker-error').first() - ).not.toBeVisible() - }) - - test('if you write invalid kcl you get inlined errors', async ({ page }) => { - const u = await getUtils(page) - await page.setViewportSize({ width: 1000, height: 500 }) - - await u.waitForAuthSkipAppStart() - - // check no error to begin with - await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible() - - /* add the following code to the editor ($ error is not a valid line) - $ error - topAng = 30 - bottomAng = 25 - */ - await u.codeLocator.click() - await page.keyboard.type('$ error') - - // press arrows to clear autocomplete - await page.keyboard.press('ArrowLeft') - await page.keyboard.press('ArrowRight') - - await page.keyboard.press('Enter') - await page.keyboard.type('topAng = 30') - await page.keyboard.press('Enter') - await page.keyboard.type('bottomAng = 25') - await page.keyboard.press('Enter') - - // error in gutter - await expect(page.locator('.cm-lint-marker-error')).toBeVisible() - - // error text on hover - await page.hover('.cm-lint-marker-error') - await expect( - page.getByText('Tag names must not be empty').first() - ).toBeVisible() - - // select the line that's causing the error and delete it - await page.getByText('$ error').click() - await page.keyboard.press('End') - await page.keyboard.down('Shift') - await page.keyboard.press('Home') - await page.keyboard.up('Shift') - await page.keyboard.press('Backspace') - - // wait for .cm-lint-marker-error not to be visible - await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible() - - // let's check we get an error when defining the same variable twice - await page.getByText('bottomAng = 25').click() - await page.keyboard.press('Enter') - await page.keyboard.type("// Let's define the same thing twice") - await page.keyboard.press('Enter') - await page.keyboard.type('topAng = 42') - await page.keyboard.press('ArrowLeft') - - await expect(page.locator('.cm-lint-marker-error')).toBeVisible() - await expect( - page.locator('.cm-lint-marker.cm-lint-marker-error') - ).toBeVisible() - - await page.locator('.cm-lint-marker.cm-lint-marker-error').hover() - await expect(page.locator('.cm-diagnosticText').first()).toBeVisible() - await expect( - page.getByText('Cannot redefine `topAng`').first() - ).toBeVisible() - - const secondTopAng = page.getByText('topAng').first() - await secondTopAng?.dblclick() - await page.keyboard.type('otherAng') - - await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible() - }) - - // TODO currently multiple source ranges are not supported - test.skip('error with 2 source ranges gets 2 diagnostics', async ({ - page, - }) => { - const u = await getUtils(page) - await page.addInitScript(async () => { - localStorage.setItem( - 'persistCode', - `length = .750 - width = 0.500 - height = 0.500 - dia = 4 - - fn squareHole = (l, w) => { - squareHoleSketch = startSketchOn('XY') - |> startProfileAt([-width / 2, -length / 2], %) - |> lineTo([width / 2, -length / 2], %) - |> lineTo([width / 2, length / 2], %) - |> lineTo([-width / 2, length / 2], %) - |> close(%) - return squareHoleSketch - } - ` - ) - }) - await page.setViewportSize({ width: 1000, height: 500 }) - - await u.waitForAuthSkipAppStart() - - await u.openDebugPanel() - await u.expectCmdLog('[data-message-type="execution-done"]') - await u.closeDebugPanel() - - // check no error to begin with - await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible() - - // Click on the bottom of the code editor to add a new line - await u.codeLocator.click() - await page.keyboard.press('ArrowDown') - await page.keyboard.press('ArrowDown') - await page.keyboard.press('ArrowDown') - await page.keyboard.press('ArrowDown') - await page.keyboard.press('ArrowDown') - await page.keyboard.press('ArrowDown') - await page.keyboard.press('ArrowDown') - await page.keyboard.press('ArrowDown') - await page.keyboard.press('ArrowDown') - await page.keyboard.press('ArrowDown') - await page.keyboard.press('ArrowDown') - await page.keyboard.press('ArrowDown') - await page.keyboard.press('ArrowDown') - await page.keyboard.press('Enter') - await page.keyboard.type(`extrusion = startSketchOn('XY') - |> circle({ center = [0, 0], radius = dia/2 }, %) - |> hole(squareHole(length, width, height), %) - |> extrude(height, %)`) - - // error in gutter - await expect(page.locator('.cm-lint-marker-error').first()).toBeVisible() - await page.hover('.cm-lint-marker-error:first-child') - await expect( - page.getByText('Expected 2 arguments, got 3').first() - ).toBeVisible() - - // Make sure there are two diagnostics - await expect(page.locator('.cm-lint-marker-error')).toHaveCount(2) - }) - test('if your kcl gets an error from the engine it is inlined', async ({ - page, - }) => { - const u = await getUtils(page) - await page.addInitScript(async () => { - localStorage.setItem( - 'persistCode', - `box = startSketchOn('XY') - |> startProfileAt([0, 0], %) - |> line([0, 10], %) - |> line([10, 0], %) - |> line([0, -10], %, $revolveAxis) - |> close(%) - |> extrude(10, %) - - sketch001 = startSketchOn(box, revolveAxis) - |> startProfileAt([5, 10], %) - |> line([0, -10], %) - |> line([2, 0], %) - |> line([0, -10], %) - |> close(%) - |> revolve({ - axis = revolveAxis, - angle = 90 - }, %) - ` - ) - }) - - await page.setViewportSize({ width: 1000, height: 500 }) - - await page.goto('/') - await u.waitForPageLoad() - - await expect(page.locator('.cm-lint-marker-error')).toBeVisible() - - // error text on hover - await page.hover('.cm-lint-marker-error') - const searchText = - 'sketch profile must lie entirely on one side of the revolution axis' - await expect(page.getByText(searchText)).toBeVisible() - }) - test.describe('Autocomplete works', () => { - test('with enter/click to accept the completion', async ({ page }) => { - const u = await getUtils(page) - // const PUR = 400 / 37.5 //pixeltoUnitRatio - await page.setViewportSize({ width: 1200, height: 500 }) - - await u.waitForAuthSkipAppStart() - - // tests clicking on an option, selection the first option - // and arrowing down to an option - - await u.codeLocator.click() - await page.keyboard.type('sketch001 = start') - - // expect there to be some auto complete options - // exact number depends on the KCL stdlib, so let's just check it's > 0 for now. - await expect(async () => { - const children = await page.locator('.cm-completionLabel').count() - expect(children).toBeGreaterThan(0) - }).toPass() - // this makes sure we can accept a completion with click - await page.getByText('startSketchOn').click() - await page.keyboard.type("'XZ'") - await page.keyboard.press('Tab') - await page.keyboard.press('Enter') - await page.keyboard.type(' |> startProfi') - // expect there be a single auto complete option that we can just hit enter on - await expect(page.locator('.cm-completionLabel')).toBeVisible() - await page.waitForTimeout(100) - await page.keyboard.press('Enter') // accepting the auto complete, not a new line - - await page.keyboard.press('Tab') - await page.waitForTimeout(100) - await page.keyboard.type('12') - await page.waitForTimeout(100) - await page.keyboard.press('Tab') - await page.waitForTimeout(100) - await page.keyboard.press('Tab') - await page.waitForTimeout(100) - await page.keyboard.press('Tab') - await page.waitForTimeout(100) - await page.keyboard.press('Enter') - await page.waitForTimeout(100) - await page.keyboard.type(' |> lin') - - await expect(page.locator('.cm-tooltip-autocomplete')).toBeVisible() - await page.waitForTimeout(100) - // press arrow down twice then enter to accept xLine - await page.keyboard.press('ArrowDown') - await page.keyboard.press('ArrowDown') - await page.keyboard.press('Enter') - // finish line with comment - await page.keyboard.type('5') - await page.waitForTimeout(100) - await page.keyboard.press('Tab') - await page.waitForTimeout(100) - await page.keyboard.press('Tab') - - await page.keyboard.type(' // ') - // Since we need to parse the ast to know we are in a comment we gotta hang tight. - await page.waitForTimeout(700) - await page.keyboard.type('lin ') - await page.waitForTimeout(200) - // there shouldn't be any auto complete options for 'lin' in the comment - await expect(page.locator('.cm-completionLabel')).not.toBeVisible() - - await expect(page.locator('.cm-content')) - .toHaveText(`sketch001 = startSketchOn('XZ') - |> startProfileAt([3.14, 12], %) - |> xLine(5, %) // lin`) - - // expect there to be no KCL errors - await expect(page.locator('.cm-lint-marker-error')).toHaveCount(0) - }) - - test('with tab to accept the completion', async ({ page }) => { - const u = await getUtils(page) - // const PUR = 400 / 37.5 //pixeltoUnitRatio - await page.setViewportSize({ width: 1200, height: 500 }) - - await u.waitForAuthSkipAppStart() - - // this test might be brittle as we add and remove functions - // but should also be easy to update. - // tests clicking on an option, selection the first option - // and arrowing down to an option - - await u.codeLocator.click() - await page.keyboard.type('sketch001 = startSketchO') - await page.waitForTimeout(100) - - // Make sure just hitting tab will take the only one left - await expect(page.locator('.cm-completionLabel')).toHaveCount(1) - await page.waitForTimeout(500) - await page.keyboard.press('ArrowDown') - await page.keyboard.press('Tab') - await page.waitForTimeout(500) - await page.keyboard.type("'XZ'") - await page.keyboard.press('Tab') - await page.keyboard.press('Enter') - await page.keyboard.type(' |> startProfi') - // expect there be a single auto complete option that we can just hit enter on - await expect(page.locator('.cm-completionLabel')).toBeVisible() - await page.waitForTimeout(100) - await page.keyboard.press('Tab') // accepting the auto complete, not a new line - - await page.keyboard.press('Tab') - await page.keyboard.type('12') - await page.waitForTimeout(100) - await page.keyboard.press('Tab') - await page.waitForTimeout(100) - await page.keyboard.press('Tab') - await page.waitForTimeout(100) - await page.keyboard.press('Tab') - await page.waitForTimeout(100) - await page.keyboard.press('Enter') - await page.waitForTimeout(100) - await page.keyboard.type(' |> lin') - - await expect(page.locator('.cm-tooltip-autocomplete')).toBeVisible() - await page.waitForTimeout(100) - // press arrow down twice then tab to accept xLine - await page.keyboard.press('ArrowDown') - await page.keyboard.press('ArrowDown') - await page.keyboard.press('Tab') - // finish line with comment - await page.keyboard.type('5') - await page.waitForTimeout(100) - await page.keyboard.press('Tab') - await page.waitForTimeout(100) - await page.keyboard.press('Tab') - - await page.keyboard.type(' // ') - // Since we need to parse the ast to know we are in a comment we gotta hang tight. - await page.waitForTimeout(700) - await page.keyboard.type('lin ') - await page.waitForTimeout(200) - // there shouldn't be any auto complete options for 'lin' in the comment - await expect(page.locator('.cm-completionLabel')).not.toBeVisible() - - await expect(page.locator('.cm-content')) - .toHaveText(`sketch001 = startSketchOn('XZ') - |> startProfileAt([3.14, 12], %) - |> xLine(5, %) // lin`) - }) - }) - test('Can undo a click and point extrude with ctrl+z', async ({ page }) => { - 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(%)` - ) - }) - - await page.setViewportSize({ width: 1200, height: 500 }) - - await u.waitForAuthSkipAppStart() - 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) - - await page.getByText('startProfileAt([4.61, -14.01], %)').click() - await expect(page.getByRole('button', { name: 'Extrude' })).toBeVisible() - await page.getByRole('button', { name: 'Extrude' }).click() - - 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.waitForTimeout(100) - - // expect the code to have changed - await expect(page.locator('.cm-content')).toHaveText( - `sketch001 = startSketchOn('XZ') |> startProfileAt([4.61, -14.01], %) |> line([12.73, -0.09], %) |> tangentialArcTo([24.95, -5.38], %) |> close(%)extrude001 = extrude(5, sketch001)` ) - - // Now hit undo - await page.keyboard.down('Control') - await page.keyboard.press('KeyZ') - await page.keyboard.up('Control') - - await page.waitForTimeout(100) - await expect(page.locator('.cm-content')) - .toHaveText(`sketch001 = startSketchOn('XZ') - |> startProfileAt([4.61, -14.01], %) - |> line([12.73, -0.09], %) - |> tangentialArcTo([24.95, -5.38], %) - |> close(%)`) }) + await page.setBodyDimensions({ width: 1000, height: 500 }) + + await homePage.goToModelingScene() + + // check no error to begin with + await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible() + + await u.openDebugPanel() + await u.expectCmdLog('[data-message-type="execution-done"]') + await u.closeDebugPanel() + + // focus the editor + await u.codeLocator.click() + + // Hover over the startSketchOn function + await page.getByText('startSketchOn').hover() + await expect(page.locator('.hover-tooltip')).toBeVisible() + await expect( + page.getByText( + 'Start a new 2-dimensional sketch on a specific plane or face' + ) + ).toBeVisible() + + // Hover over the line function + await page.getByText('line').first().hover() + await expect(page.locator('.hover-tooltip')).toBeVisible() + await expect(page.getByText('Draw a line')).toBeVisible() }) - test('Can undo a sketch modification with ctrl+z', async ({ page }) => { - 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], %) + test('if you use the format keyboard binding it formats your code', async ({ page, homePage }) => { const u = await getUtils(page) + await page.addInitScript(async () => { + localStorage.setItem( + 'persistCode', + `sketch001 = startSketchOn('XY') + |> startProfileAt([-10, -10], %) + |> line([20, 0], %) + |> line([0, 20], %) + |> line([-20, 0], %) + |> close(%)` + ) + localStorage.setItem('disableAxis', 'true') + }) + await page.setBodyDimensions({ width: 1000, height: 500 }) + + await homePage.goToModelingScene() + + // check no error to begin with + await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible() + + await u.openDebugPanel() + await u.expectCmdLog('[data-message-type="execution-done"]') + await u.closeDebugPanel() + + // focus the editor + await u.codeLocator.click() + + // Hit alt+shift+f to format the code + await page.keyboard.press('Alt+Shift+KeyF') + + await expect(page.locator('.cm-content')) + .toHaveText(`sketch001 = startSketchOn('XY') + |> startProfileAt([-10, -10], %) + |> line([20, 0], %) + |> line([0, 20], %) + |> line([-20, 0], %) + |> close(%)`) }) + + test('if you use the format keyboard binding it formats your code and executes so lints are shown', async ({ page, homePage }) => { const u = await getUtils(page) + await page.addInitScript(async () => { + localStorage.setItem( + 'persistCode', + `sketch_001 = startSketchOn('XY') + |> startProfileAt([-10, -10], %) + |> line([20, 0], %) + |> line([0, 20], %) + |> line([-20, 0], %) + |> close(%)` + ) + localStorage.setItem('disableAxis', 'true') + }) + await page.setBodyDimensions({ width: 1000, height: 500 }) + + await homePage.goToModelingScene() + + await u.openDebugPanel() + await u.expectCmdLog('[data-message-type="execution-done"]') + await u.closeDebugPanel() + + // error in guter + await expect(page.locator('.cm-lint-marker-info').first()).toBeVisible() + + // error text on hover + await page.hover('.cm-lint-marker-info') + await expect( + page.getByText('Identifiers must be lowerCamelCase').first() + ).toBeVisible() + + // focus the editor + await u.codeLocator.click() + + // Hit alt+shift+f to format the code + await page.keyboard.press('Alt+Shift+KeyF') + + await u.openDebugPanel() + await u.expectCmdLog('[data-message-type="execution-done"]') + await u.closeDebugPanel() + + await expect(page.locator('.cm-content')) + .toHaveText(`sketch_001 = startSketchOn('XY') + |> startProfileAt([-10, -10], %) + |> line([20, 0], %) + |> line([0, 20], %) + |> line([-20, 0], %) + |> close(%)`) + + // error in guter + await expect(page.locator('.cm-lint-marker-info').first()).toBeVisible() + + // error text on hover + await page.hover('.cm-lint-marker-info') + await expect( + page.getByText('Identifiers must be lowerCamelCase').first() + ).toBeVisible() }) + + test('if you write kcl with lint errors you get lints', async ({ page, homePage }) => { const u = await getUtils(page) + await page.setBodyDimensions({ width: 1000, height: 500 }) + + await homePage.goToModelingScene() + + // check no error to begin with + await expect(page.locator('.cm-lint-marker-info')).not.toBeVisible() + + await u.codeLocator.click() + await page.keyboard.type('my_snake_case_var = 5') + await page.keyboard.press('Enter') + await page.keyboard.type('myCamelCaseVar = 5') + await page.keyboard.press('Enter') + + // press arrows to clear autocomplete + await page.keyboard.press('ArrowLeft') + await page.keyboard.press('ArrowRight') + + // error in guter + await expect(page.locator('.cm-lint-marker-info').first()).toBeVisible() + + // error text on hover + await page.hover('.cm-lint-marker-info') + await expect( + page.getByText('Identifiers must be lowerCamelCase').first() + ).toBeVisible() + + // select the line that's causing the error and delete it + await page.getByText('my_snake_case_var = 5').click() + await page.keyboard.press('End') + await page.keyboard.down('Shift') + await page.keyboard.press('Home') + await page.keyboard.up('Shift') + await page.keyboard.press('Backspace') + + // wait for .cm-lint-marker-info not to be visible + await expect(page.locator('.cm-lint-marker-info')).not.toBeVisible() }) + + test('if you fixup kcl errors you clear lints', 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], %) |> close(%) - |> extrude(5, %)` - ) - }) + ` + ) + }) + + await page.setBodyDimensions({ width: 1000, height: 500 }) + + await homePage.goToModelingScene() + + // check no error to begin with + await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible() + + await u.codeLocator.click() + + await page.getByText(' |> line([2.48, 2.44], %)').click() + + await expect( + page.locator('.cm-lint-marker-error').first() + ).not.toBeVisible() + await page.keyboard.press('End') + await page.keyboard.press('Backspace') + + await expect(page.locator('.cm-lint-marker-error').first()).toBeVisible() + await page.keyboard.type(')') + await expect( + page.locator('.cm-lint-marker-error').first() + ).not.toBeVisible() }) - await page.setViewportSize({ width: 1200, height: 500 }) + test('if you write invalid kcl you get inlined errors', async ({ page, homePage }) => { const u = await getUtils(page) + await page.setBodyDimensions({ width: 1000, height: 500 }) + + await homePage.goToModelingScene() + + // check no error to begin with + await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible() + + /* add the following code to the editor ($ error is not a valid line) + $ error + const topAng = 30 + const bottomAng = 25 + */ + await u.codeLocator.click() + await page.keyboard.type('$ error') + + // press arrows to clear autocomplete + await page.keyboard.press('ArrowLeft') + await page.keyboard.press('ArrowRight') + + await page.keyboard.press('Enter') + await page.keyboard.type('topAng = 30') + await page.keyboard.press('Enter') + await page.keyboard.type('bottomAng = 25') + await page.keyboard.press('Enter') + + // error in guter + await expect(page.locator('.cm-lint-marker-error')).toBeVisible() + + // error text on hover + await page.hover('.cm-lint-marker-error') + await expect(page.getByText('Unexpected token: $').first()).toBeVisible() + + // select the line that's causing the error and delete it + await page.getByText('$ error').click() + await page.keyboard.press('End') + await page.keyboard.down('Shift') + await page.keyboard.press('Home') + await page.keyboard.up('Shift') + await page.keyboard.press('Backspace') + + // wait for .cm-lint-marker-error not to be visible + await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible() + + // let's check we get an error when defining the same variable twice + await page.getByText('bottomAng = 25').click() + await page.keyboard.press('Enter') + await page.keyboard.type("// Let's define the same thing twice") + await page.keyboard.press('Enter') + await page.keyboard.type('topAng = 42') + await page.keyboard.press('ArrowLeft') + + await expect(page.locator('.cm-lint-marker-error')).toBeVisible() + await expect( + page.locator('.cm-lint-marker.cm-lint-marker-error') + ).toBeVisible() + + await page.locator('.cm-lint-marker.cm-lint-marker-error').hover() + await expect(page.locator('.cm-diagnosticText').first()).toBeVisible() + await expect( + page.getByText('Cannot redefine `topAng`').first() + ).toBeVisible() + + const secondTopAng = page.getByText('topAng').first() + await secondTopAng?.dblclick() + await page.keyboard.type('otherAng') + + await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible() }) - await u.waitForAuthSkipAppStart() - 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 - // we wait so it saves the code - await page.waitForTimeout(800) - - const lineEnd = await u.getBoundingBox('[data-overlay-index="0"]') - await page.waitForTimeout(100) - await page.dragAndDrop('#stream', '#stream', { - sourcePosition: { x: lineEnd.x - 5, y: lineEnd.y }, - targetPosition: { x: lineEnd.x + dragPX, y: lineEnd.y + dragPX }, - }) - await expect(page.locator('.cm-content')).not.toHaveText(prevContent) - prevContent = await page.locator('.cm-content').innerText() - - // we wait so it saves the code - await page.waitForTimeout(800) - - // drag 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 + dragPX, - y: tangentEnd.y + dragPX, - }, - }) - 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([15.39, -2.78], %) - |> tangentialArcTo([27.6, -3.05], %) + test('error with 2 source ranges gets 2 diagnostics', async ({ page, homePage }) => { const u = await getUtils(page) + await page.addInitScript(async () => { + localStorage.setItem( + 'persistCode', + `length = .750 + width = 0.500 + height = 0.500 + dia = 4 + + fn squareHole = (l, w) => { + squareHoleSketch = startSketchOn('XY') + |> startProfileAt([-width / 2, -length / 2], %) + |> lineTo([width / 2, -length / 2], %) + |> lineTo([width / 2, length / 2], %) + |> lineTo([-width / 2, length / 2], %) |> close(%) - |> extrude(5, %) -`) - - // Hit undo - await page.keyboard.down('Control') - await page.keyboard.press('KeyZ') - await page.keyboard.up('Control') + return squareHoleSketch + } + ` + ) + }) + await page.setBodyDimensions({ width: 1000, height: 500 }) + + await homePage.goToModelingScene() + + await u.openDebugPanel() + await u.expectCmdLog('[data-message-type="execution-done"]') + await u.closeDebugPanel() + + // check no error to begin with + await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible() + + // Click on the bottom of the code editor to add a new line + await u.codeLocator.click() + await page.keyboard.press('ArrowDown') + await page.keyboard.press('ArrowDown') + await page.keyboard.press('ArrowDown') + await page.keyboard.press('ArrowDown') + await page.keyboard.press('ArrowDown') + await page.keyboard.press('ArrowDown') + await page.keyboard.press('ArrowDown') + await page.keyboard.press('ArrowDown') + await page.keyboard.press('ArrowDown') + await page.keyboard.press('ArrowDown') + await page.keyboard.press('ArrowDown') + await page.keyboard.press('ArrowDown') + await page.keyboard.press('ArrowDown') + await page.keyboard.press('Enter') + await page.keyboard.type(`extrusion = startSketchOn('XY') + |> circle({ center: [0, 0], radius: dia/2 }, %) + |> hole(squareHole(length, width, height), %) + |> extrude(height, %)`) + + // error in gutter + await expect(page.locator('.cm-lint-marker-error').first()).toBeVisible() + await page.hover('.cm-lint-marker-error:first-child') + await expect( + page.getByText('Expected 2 arguments, got 3').first() + ).toBeVisible() + + // Make sure there are two diagnostics + await expect(page.locator('.cm-lint-marker-error')).toHaveCount(2) }) + test('if your kcl gets an error from the engine it is inlined', async ({ context, page, homePage }) => { const u = await getUtils(page) + await context.addInitScript(async () => { + localStorage.setItem( + 'persistCode', + `box = startSketchOn('XY') + |> startProfileAt([0, 0], %) + |> line([0, 10], %) + |> line([10, 0], %) + |> line([0, -10], %, $revolveAxis) + |> close(%) + |> extrude(10, %) + + sketch001 = startSketchOn(box, revolveAxis) + |> startProfileAt([5, 10], %) + |> line([0, -10], %) + |> line([2, 0], %) + |> line([0, -10], %) + |> close(%) + |> revolve({ + axis: revolveAxis, + angle: 90 + }, %) + ` + ) + }) + + await page.setBodyDimensions({ width: 1000, height: 500 }) + + await homePage.goToModelingScene() + + await expect(page.locator('.cm-lint-marker-error')).toBeVisible() + + // error text on hover + await page.hover('.cm-lint-marker-error') + const searchText = + 'sketch profile must lie entirely on one side of the revolution axis' + await expect(page.getByText(searchText)).toBeVisible() }) + test.describe('Autocomplete works', () => { + test('with enter/click to accept the completion', async ({ page, homePage }) => { const u = await getUtils(page) + // const PUR = 400 / 37.5 //pixeltoUnitRatio + await page.setBodyDimensions({ width: 1200, height: 500 }) + + await homePage.goToModelingScene() + + // tests clicking on an option, selection the first option + // and arrowing down to an option + + await u.codeLocator.click() + await page.keyboard.type('sketch001 = start') + + // expect there to be some auto complete options + // exact number depends on the KCL stdlib, so let's just check it's > 0 for now. + await expect(async () => { + const children = await page.locator('.cm-completionLabel').count() + expect(children).toBeGreaterThan(0) + }).toPass() + // this makes sure we can accept a completion with click + await page.getByText('startSketchOn').click() + await page.keyboard.type("'XZ'") + await page.keyboard.press('Tab') + await page.keyboard.press('Enter') + await page.keyboard.type(' |> startProfi') + // expect there be a single auto complete option that we can just hit enter on + await expect(page.locator('.cm-completionLabel')).toBeVisible() + await page.waitForTimeout(100) + await page.keyboard.press('Enter') // accepting the auto complete, not a new line + + await page.keyboard.press('Tab') + await page.waitForTimeout(100) + await page.keyboard.type('12') + await page.waitForTimeout(100) + await page.keyboard.press('Tab') + await page.waitForTimeout(100) + await page.keyboard.press('Tab') + await page.waitForTimeout(100) + await page.keyboard.press('Tab') + await page.waitForTimeout(100) + await page.keyboard.press('Enter') + await page.waitForTimeout(100) + await page.keyboard.type(' |> lin') + + await expect(page.locator('.cm-tooltip-autocomplete')).toBeVisible() + await page.waitForTimeout(100) + // press arrow down twice then enter to accept xLine + await page.keyboard.press('ArrowDown') + await page.keyboard.press('ArrowDown') + await page.keyboard.press('Enter') + // finish line with comment + await page.keyboard.type('5') + await page.waitForTimeout(100) + await page.keyboard.press('Tab') + await page.waitForTimeout(100) + await page.keyboard.press('Tab') + + await page.keyboard.type(' // ') + // Since we need to parse the ast to know we are in a comment we gotta hang tight. + await page.waitForTimeout(700) + await page.keyboard.type('lin ') + await page.waitForTimeout(200) + // there shouldn't be any auto complete options for 'lin' in the comment + await expect(page.locator('.cm-completionLabel')).not.toBeVisible() + await expect(page.locator('.cm-content')) .toHaveText(`sketch001 = startSketchOn('XZ') - |> startProfileAt([7.12, -12.68], %) - |> line([15.39, -2.78], %) - |> tangentialArcTo([24.95, -0.38], %) - |> close(%) - |> extrude(5, %)`) - - // Hit undo again. - await page.keyboard.down('Control') - await page.keyboard.press('KeyZ') - await page.keyboard.up('Control') + |> startProfileAt([3.14, 12], %) + |> xLine(5, %) // lin`) + + // expect there to be no KCL errors + await expect(page.locator('.cm-lint-marker-error')).toHaveCount(0) }) + test('with tab to accept the completion', async ({ page, homePage }) => { const u = await getUtils(page) + // const PUR = 400 / 37.5 //pixeltoUnitRatio + await page.setBodyDimensions({ width: 1200, height: 500 }) + + await homePage.goToModelingScene() + + // this test might be brittle as we add and remove functions + // but should also be easy to update. + // tests clicking on an option, selection the first option + // and arrowing down to an option + + await u.codeLocator.click() + await page.keyboard.type('sketch001 = startSketchO') + await page.waitForTimeout(100) + + // Make sure just hitting tab will take the only one left + await expect(page.locator('.cm-completionLabel')).toHaveCount(1) + await page.waitForTimeout(500) + await page.keyboard.press('ArrowDown') + await page.keyboard.press('Tab') + await page.waitForTimeout(500) + await page.keyboard.type("'XZ'") + await page.keyboard.press('Tab') + await page.keyboard.press('Enter') + await page.keyboard.type(' |> startProfi') + // expect there be a single auto complete option that we can just hit enter on + await expect(page.locator('.cm-completionLabel')).toBeVisible() + await page.waitForTimeout(100) + await page.keyboard.press('Tab') // accepting the auto complete, not a new line + + await page.keyboard.press('Tab') + await page.keyboard.type('12') + await page.waitForTimeout(100) + await page.keyboard.press('Tab') + await page.waitForTimeout(100) + await page.keyboard.press('Tab') + await page.waitForTimeout(100) + await page.keyboard.press('Tab') + await page.waitForTimeout(100) + await page.keyboard.press('Enter') + await page.waitForTimeout(100) + await page.keyboard.type(' |> lin') + + await expect(page.locator('.cm-tooltip-autocomplete')).toBeVisible() + await page.waitForTimeout(100) + // press arrow down twice then tab to accept xLine + await page.keyboard.press('ArrowDown') + await page.keyboard.press('ArrowDown') + await page.keyboard.press('Tab') + // finish line with comment + await page.keyboard.type('5') + await page.waitForTimeout(100) + await page.keyboard.press('Tab') + await page.waitForTimeout(100) + await page.keyboard.press('Tab') + + await page.keyboard.type(' // ') + // Since we need to parse the ast to know we are in a comment we gotta hang tight. + await page.waitForTimeout(700) + await page.keyboard.type('lin ') + await page.waitForTimeout(200) + // there shouldn't be any auto complete options for 'lin' in the comment + await expect(page.locator('.cm-completionLabel')).not.toBeVisible() + await expect(page.locator('.cm-content')) .toHaveText(`sketch001 = startSketchOn('XZ') - |> startProfileAt([7.12, -12.68], %) + |> startProfileAt([3.14, 12], %) + |> xLine(5, %) // lin`) }) + }) + test('Can undo a click and point extrude with ctrl+z', async ({ page, context, homePage }) => { const u = await getUtils(page) + await context.addInitScript(async () => { + localStorage.setItem( + 'persistCode', + `sketch001 = startSketchOn('XZ') + |> startProfileAt([4.61, -14.01], %) + |> line([12.73, -0.09], %) + |> tangentialArcTo([24.95, -5.38], %) + |> close(%)` + ) + }) + + await page.setBodyDimensions({ width: 1200, height: 500 }) + + 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) + + await page.getByText('startProfileAt([4.61, -14.01], %)').click() + await expect(page.getByRole('button', { name: 'Extrude' })).toBeVisible() + await page.getByRole('button', { name: 'Extrude' }).click() + + 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.waitForTimeout(100) + + // expect the code to have changed + await expect(page.locator('.cm-content')).toHaveText( + `sketch001 = startSketchOn('XZ') |> startProfileAt([4.61, -14.01], %) |> line([12.73, -0.09], %) |> tangentialArcTo([24.95, -5.38], %) |> close(%)extrude001 = extrude(5, sketch001)` + ) + + // Now hit undo + await page.keyboard.down('Control') + await page.keyboard.press('KeyZ') + await page.keyboard.up('Control') + + await page.waitForTimeout(100) + await expect(page.locator('.cm-content')) + .toHaveText(`sketch001 = startSketchOn('XZ') + |> startProfileAt([4.61, -14.01], %) + |> line([12.73, -0.09], %) + |> tangentialArcTo([24.95, -5.38], %) + |> close(%)`) }) + + test('Can undo a sketch modification with ctrl+z', 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, %) -`) + |> extrude(5, %)` + ) + }) + + await page.setBodyDimensions({ width: 1200, height: 500 }) + + 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 = [1200 / 2, 500 / 2] + + 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) - // Hit undo again. - await page.keyboard.down('Control') - await page.keyboard.press('KeyZ') - await page.keyboard.up('Control') - - await page.waitForTimeout(100) - await expect(page.locator('.cm-content')) - .toHaveText(`sketch001 = startSketchOn('XZ') - |> startProfileAt([4.61, -10.01], %) - |> line([12.73, -0.09], %) + // drag startProfileAt handle + await page.dragAndDrop('#stream', '#stream', { + sourcePosition: { x: startPX[0] + 68, y: startPX[1] + 147 }, + targetPosition: { x: startPX[0] + dragPX, y: startPX[1] + dragPX }, + }) + await page.waitForTimeout(100) + await expect(page.locator('.cm-content')).not.toHaveText(prevContent) + prevContent = await page.locator('.cm-content').innerText() + + // drag line handle + // we wait so it saves the code + await page.waitForTimeout(800) + + const lineEnd = await u.getBoundingBox('[data-overlay-index="0"]') + await page.waitForTimeout(100) + await page.dragAndDrop('#stream', '#stream', { + sourcePosition: { x: lineEnd.x - 5, y: lineEnd.y }, + targetPosition: { x: lineEnd.x + dragPX, y: lineEnd.y + dragPX }, + }) + await expect(page.locator('.cm-content')).not.toHaveText(prevContent) + prevContent = await page.locator('.cm-content').innerText() + + + // we wait so it saves the code + await page.waitForTimeout(800) + + // drag 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 + dragPX, + y: tangentEnd.y + dragPX, + }, + }) + 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([2.71, -2.71], %) + |> line([15.4, -2.78], %) + |> tangentialArcTo([27.6, -3.05], %) + |> close(%) + |> extrude(5, %) + `) + + // Hit undo + await page.keyboard.down('Control') + await page.keyboard.press('KeyZ') + await page.keyboard.up('Control') + + await expect(page.locator('.cm-content')) + .toHaveText(`sketch001 = startSketchOn('XZ') + |> startProfileAt([2.71, -2.71], %) + |> line([15.4, -2.78], %) |> tangentialArcTo([24.95, -0.38], %) |> close(%) |> extrude(5, %)`) - }) + + // Hit undo again. + await page.keyboard.down('Control') + await page.keyboard.press('KeyZ') + await page.keyboard.up('Control') + + await expect(page.locator('.cm-content')) + .toHaveText(`sketch001 = startSketchOn('XZ') + |> startProfileAt([2.71, -2.71], %) + |> line([12.73, -0.09], %) + |> tangentialArcTo([24.95, -0.38], %) + |> close(%) + |> extrude(5, %) + `) + + // Hit undo again. + await page.keyboard.down('Control') + await page.keyboard.press('KeyZ') + await page.keyboard.up('Control') + + await page.waitForTimeout(100) + await expect(page.locator('.cm-content')) + .toHaveText(`sketch001 = startSketchOn('XZ') + |> startProfileAt([4.61, -10.01], %) + |> line([12.73, -0.09], %) + |> tangentialArcTo([24.95, -0.38], %) + |> close(%) + |> extrude(5, %)`) }) test.fixme( `Can use the import stdlib function on a local OBJ file`, { tag: '@electron' }, - async ({ browserName }, testInfo) => { - const { electronApp, page } = await setupElectron({ - testInfo, - folderSetupFn: async (dir) => { - const bracketDir = join(dir, 'cube') - await fsp.mkdir(bracketDir, { recursive: true }) - await fsp.copyFile( - executorInputPath('cube.obj'), - join(bracketDir, 'cube.obj') - ) - await fsp.writeFile(join(bracketDir, 'main.kcl'), '') - }, + async ({ page, context }, testInfo) => { + await context.folderSetupFn(async (dir) => { + const bracketDir = join(dir, 'cube') + await fsp.mkdir(bracketDir, { recursive: true }) + await fsp.copyFile( + executorInputPath('cube.obj'), + join(bracketDir, 'cube.obj') + ) + await fsp.writeFile(join(bracketDir, 'main.kcl'), '') }) + const viewportSize = { width: 1200, height: 500 } - await page.setViewportSize(viewportSize) + await page.setBodyDimensions(viewportSize) // Locators and constants const u = await getUtils(page) @@ -1070,8 +1012,6 @@ test.describe('Editor tests', () => { }) .toBeGreaterThan(15) }) - - await electronApp.close() } ) })