diff --git a/e2e/playwright/command-bar-tests.spec.ts b/e2e/playwright/command-bar-tests.spec.ts index 8cd115009..8f6b00a7c 100644 --- a/e2e/playwright/command-bar-tests.spec.ts +++ b/e2e/playwright/command-bar-tests.spec.ts @@ -4,7 +4,6 @@ import * as fsp from 'fs/promises' import { executorInputPath, getUtils } from '@e2e/playwright/test-utils' import { expect, test } from '@e2e/playwright/zoo-test' -import { expectPixelColor } from '@e2e/playwright/fixtures/sceneFixture' test.describe('Command bar tests', () => { test('Extrude from command bar selects extrude line after', async ({ @@ -515,47 +514,6 @@ test.describe('Command bar tests', () => { }) }) - test( - `Zoom to fit to shared model on web`, - { tag: ['@web'] }, - async ({ page, scene }) => { - if (process.env.TARGET !== 'web') { - // This test is web-only - // TODO: re-enable on CI as part of a new @web test suite - return - } - await test.step(`Prepare and navigate to home page with query params`, async () => { - // a quad in the top left corner of the XZ plane (which is out of the current view) - const code = `sketch001 = startSketchOn(XZ) -profile001 = startProfile(sketch001, at = [-484.34, 484.95]) - |> yLine(length = -69.1) - |> xLine(length = 66.84) - |> yLine(length = 71.37) - |> line(endAbsolute = [profileStartX(%), profileStartY(%)]) - |> close() -` - const targetURL = `?create-file=true&name=test&units=mm&code=${encodeURIComponent(btoa(code))}&ask-open-desktop=true` - await page.goto(page.url() + targetURL) - expect(page.url()).toContain(targetURL) - }) - - await test.step(`Submit the command`, async () => { - await page.getByTestId('continue-to-web-app-button').click() - - await scene.connectionEstablished() - - // This makes SystemIOMachineActors.createKCLFile run after EngineStream/firstPlay - await page.waitForTimeout(3000) - - await page.getByTestId('command-bar-submit').click() - }) - - await test.step(`Ensure we created the project and are in the modeling scene`, async () => { - await expectPixelColor(page, [252, 252, 252], { x: 600, y: 260 }, 8) - }) - } - ) - test(`Can add and edit a named parameter or constant`, async ({ page, homePage, diff --git a/e2e/playwright/snapshot-tests.spec.ts-snapshots/Client-side-scene-scale-should-match-engine-scale-Inch-scale-1-Google-Chrome-linux.png b/e2e/playwright/snapshot-tests.spec.ts-snapshots/Client-side-scene-scale-should-match-engine-scale-Inch-scale-1-Google-Chrome-linux.png index 4d00a06fe..8169515bc 100644 Binary files a/e2e/playwright/snapshot-tests.spec.ts-snapshots/Client-side-scene-scale-should-match-engine-scale-Inch-scale-1-Google-Chrome-linux.png and b/e2e/playwright/snapshot-tests.spec.ts-snapshots/Client-side-scene-scale-should-match-engine-scale-Inch-scale-1-Google-Chrome-linux.png differ diff --git a/e2e/playwright/snapshot-tests.spec.ts-snapshots/Client-side-scene-scale-should-match-engine-scale-Millimeter-scale-1-Google-Chrome-linux.png b/e2e/playwright/snapshot-tests.spec.ts-snapshots/Client-side-scene-scale-should-match-engine-scale-Millimeter-scale-1-Google-Chrome-linux.png index 13512d16d..2813e518e 100644 Binary files a/e2e/playwright/snapshot-tests.spec.ts-snapshots/Client-side-scene-scale-should-match-engine-scale-Millimeter-scale-1-Google-Chrome-linux.png and b/e2e/playwright/snapshot-tests.spec.ts-snapshots/Client-side-scene-scale-should-match-engine-scale-Millimeter-scale-1-Google-Chrome-linux.png differ diff --git a/e2e/playwright/snapshot-tests.spec.ts-snapshots/Draft-segments-should-look-right-2-Google-Chrome-linux.png b/e2e/playwright/snapshot-tests.spec.ts-snapshots/Draft-segments-should-look-right-2-Google-Chrome-linux.png index cb0ee415b..e7c016cf6 100644 Binary files a/e2e/playwright/snapshot-tests.spec.ts-snapshots/Draft-segments-should-look-right-2-Google-Chrome-linux.png and b/e2e/playwright/snapshot-tests.spec.ts-snapshots/Draft-segments-should-look-right-2-Google-Chrome-linux.png differ diff --git a/e2e/playwright/temporary-workspace.spec.ts b/e2e/playwright/temporary-workspace.spec.ts new file mode 100644 index 000000000..1aff27635 --- /dev/null +++ b/e2e/playwright/temporary-workspace.spec.ts @@ -0,0 +1,117 @@ +import { expect, test } from '@e2e/playwright/zoo-test' +import { stringToBase64 } from '@src/lib/base64' + +test.describe('Temporary workspace', () => { + test( + 'Opening a share link creates a temporary environment that is not saved', + { tag: ['@web'] }, + async ({ page, editor, scene, cmdBar, homePage }) => { + await test.step('Pre-condition: editor is empty', async () => { + await homePage.goToModelingScene() + await scene.settled(cmdBar) + await editor.expectEditor.toContain('') + }) + + await test.step('Go to share link, check new content present, make a change', async () => { + const code = `sketch001 = startSketchOn(XY) + profile001 = startProfile(sketch001, at = [-124.89, -186.4]) + |> line(end = [391.31, 444.04]) + |> line(end = [96.21, -493.07]) + |> line(endAbsolute = [profileStartX(%), profileStartY(%)]) + |> close() + extrude001 = extrude(profile001, length = 5) +` + + const codeQueryParam = encodeURIComponent(stringToBase64(code)) + const targetURL = `?create-file=true&browser=test&code=${codeQueryParam}&ask-open-desktop=true` + await page.goto(page.url() + targetURL) + await expect.poll(() => page.url()).toContain(targetURL) + const button = page.getByRole('button', { name: 'Continue to web app' }) + await button.click() + + await editor.expectEditor.toContain(code, { shouldNormalise: true }) + await editor.scrollToText('-124.89', true) + await page.keyboard.press('9') + await page.keyboard.press('9') + }) + + await test.step('Post-condition: empty editor once again (original state)', async () => { + await homePage.goToModelingScene() + await scene.settled(cmdBar) + const code = await page.evaluate(() => + window.localStorage.getItem('persistCode') + ) + await expect(code).toContain('') + }) + } + ) + + test( + 'Opening a sample link creates a temporary environment that is not saved', + { tag: ['@web'] }, + async ({ page, editor, scene, cmdBar, homePage }) => { + await test.step('Pre-condition: editor is empty', async () => { + await homePage.goToModelingScene() + await scene.settled(cmdBar) + await editor.expectEditor.toContain('') + }) + + await test.step('Load sample, make an edit', async () => { + await page.goto( + `${page.url()}/?cmd=add-kcl-file-to-project&groupId=application&projectName=browser&source=kcl-samples&sample=brake-rotor/main.kcl` + ) + + await editor.scrollToText('114.3', true) + await page.keyboard.press('9') + await page.keyboard.press('9') + }) + + await test.step('Post-condition: empty editor once again (original state)', async () => { + await homePage.goToModelingScene() + await scene.settled(cmdBar) + const code = await page.evaluate(() => + window.localStorage.getItem('persistCode') + ) + await expect(code).toContain('') + }) + } + ) + + test( + 'Hitting save will save the temporary workspace', + { tag: ['@web'] }, + async ({ page, editor, scene, cmdBar, homePage }) => { + const buttonSaveTemporaryWorkspace = page.getByTestId('tws-save') + + await test.step('Pre-condition: editor is empty', async () => { + await homePage.goToModelingScene() + await scene.settled(cmdBar) + await editor.expectEditor.toContain('') + }) + + await test.step('Load sample, make an edit, *save*', async () => { + await page.goto( + `${page.url()}/?cmd=add-kcl-file-to-project&groupId=application&projectName=browser&source=kcl-samples&sample=brake-rotor/main.kcl` + ) + await homePage.goToModelingScene() + await scene.settled(cmdBar) + + await editor.scrollToText('114.3') + await editor.replaceCode('114.3', '999.9133') + await editor.expectEditor.toContain('999.9133') + + await buttonSaveTemporaryWorkspace.click() + await expect(buttonSaveTemporaryWorkspace).not.toBeVisible() + + await editor.expectEditor.toContain('999.9133') + }) + + await test.step('Post-condition: has the edits in localStorage', async () => { + const code = await page.evaluate(() => + window.localStorage.getItem('persistCode') + ) + await expect(code).toContain('999.9133') + }) + } + ) +}) diff --git a/src/Toolbar.tsx b/src/Toolbar.tsx index e825859ff..f9c295f42 100644 --- a/src/Toolbar.tsx +++ b/src/Toolbar.tsx @@ -25,7 +25,7 @@ import type { ToolbarModeName, } from '@src/lib/toolbar' import { isToolbarItemResolvedDropdown, toolbarConfig } from '@src/lib/toolbar' -import { commandBarActor } from '@src/lib/singletons' +import { codeManager, commandBarActor } from '@src/lib/singletons' import { filterEscHotkey } from '@src/lib/hotkeyWrapper' export function Toolbar({ @@ -40,6 +40,12 @@ export function Toolbar({ 'bg-chalkboard-transparent dark:bg-transparent disabled:bg-transparent dark:disabled:bg-transparent enabled:hover:bg-chalkboard-10 dark:enabled:hover:bg-chalkboard-100 pressed:!bg-primary pressed:enabled:hover:!text-chalkboard-10' const buttonBorderClassName = '!border-transparent' + const isInTemporaryWorkspace = codeManager.isBufferMode + + const onClickSave = () => { + codeManager.exitFromTemporaryWorkspaceMode() + } + const sketchPathId = useMemo(() => { if ( isCursorInFunctionDefinition( @@ -385,11 +391,27 @@ export function Toolbar({ ) })} - {state.matches('Sketch no face') && ( -
Select a plane or face to start sketching
-Select a plane or face to start sketching
+