Compare commits
	
		
			6 Commits
		
	
	
		
			pierremtb/
			...
			lf94/speci
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| b5bcb8a5ff | |||
| 5e72da5770 | |||
| 00be11f6d6 | |||
| 4fd53d527f | |||
| 0f75d945b1 | |||
| 9cc956ddff | 
							
								
								
									
										3
									
								
								.github/workflows/build-apps.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -134,6 +134,7 @@ jobs: | ||||
|         # Windows is picky sometimes and fails on fetch. Step takes about ~30s | ||||
|         uses: nick-fields/retry@v3.0.2 | ||||
|         with: | ||||
|           shell: bash | ||||
|           timeout_minutes: 2 | ||||
|           max_attempts: 3 | ||||
|           command: yarn install | ||||
| @ -185,6 +186,7 @@ jobs: | ||||
|         # TODO: Fix electron-notarize flakes. The logs above should help gather more data on failures | ||||
|         uses: nick-fields/retry@v3.0.2 | ||||
|         with: | ||||
|           shell: bash | ||||
|           timeout_minutes: 10 | ||||
|           max_attempts: 3 | ||||
|           command: yarn tronb:package:prod | ||||
| @ -246,6 +248,7 @@ jobs: | ||||
|         # TODO: Fix electron-notarize flakes. The logs above should help gather more data on failures | ||||
|         uses: nick-fields/retry@v3.0.2 | ||||
|         with: | ||||
|           shell: bash | ||||
|           timeout_minutes: 10 | ||||
|           max_attempts: 3 | ||||
|           command: yarn tronb:package:prod | ||||
|  | ||||
							
								
								
									
										6
									
								
								.github/workflows/e2e-tests.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -51,6 +51,7 @@ jobs: | ||||
|         node-version-file: '.nvmrc' | ||||
|         cache: 'yarn' | ||||
|     - name: Install dependencies | ||||
|       id: deps-install | ||||
|       shell: bash | ||||
|       run: yarn | ||||
|     - name: Cache Playwright Browsers | ||||
| @ -133,7 +134,7 @@ jobs: | ||||
|       # TODO: break this in its own job, for now it's not slowing down the overall execution as ubuntu is the quickest, | ||||
|       # but we could do better. This forces a large 1/1 shard of all 20 snapshot tests that runs in about 3 minutes. | ||||
|       run: | | ||||
|         PLATFORM=web yarn playwright test --config=playwright.config.ts --retries="3" --update-snapshots --grep=@snapshot --trace=on --shard=1/1 | ||||
|         yarn test:snapshots | ||||
|       env: | ||||
|         CI: true | ||||
|         NODE_ENV: development | ||||
| @ -193,9 +194,10 @@ jobs: | ||||
|         path: test-results/ | ||||
|     - name: Run playwright/electron flow (with retries) | ||||
|       id: retry | ||||
|       if: ${{ !cancelled() && (success() || failure()) }} | ||||
|       if: ${{ !cancelled() && steps.deps-install.outcome == 'success' }} | ||||
|       uses: nick-fields/retry@v3.0.2 | ||||
|       with: | ||||
|         shell: bash | ||||
|         command: .github/ci-cd-scripts/playwright-electron.sh ${{matrix.shardIndex}} ${{matrix.shardTotal}} ${{matrix.os}} | ||||
|         timeout_minutes: 30 | ||||
|         max_attempts: 25 | ||||
|  | ||||
| @ -1,12 +1,14 @@ | ||||
| import type { Page, Locator } from '@playwright/test' | ||||
| import { expect } from '@playwright/test' | ||||
| import { expect } from '../zoo-test' | ||||
| import { isArray, uuidv4 } from 'lib/utils' | ||||
| import { CmdBarFixture } from './cmdBarFixture' | ||||
| import { | ||||
|   closeDebugPanel, | ||||
|   doAndWaitForImageDiff, | ||||
|   getPixelRGBs, | ||||
|   openAndClearDebugPanel, | ||||
|   sendCustomCmd, | ||||
|   getUtils, | ||||
| } from '../test-utils' | ||||
|  | ||||
| type MouseParams = { | ||||
| @ -40,9 +42,13 @@ export class SceneFixture { | ||||
|   public page: Page | ||||
|   public streamWrapper!: Locator | ||||
|   public loadingIndicator!: Locator | ||||
|   public networkToggleConnected!: Locator | ||||
|   public startEditSketchBtn!: Locator | ||||
|  | ||||
|   get exeIndicator() { | ||||
|     return this.page.getByTestId('model-state-indicator-execution-done') | ||||
|     return this.page | ||||
|       .getByTestId('model-state-indicator-execution-done') | ||||
|       .or(this.page.getByTestId('model-state-indicator-receive-reliable')) | ||||
|   } | ||||
|  | ||||
|   constructor(page: Page) { | ||||
| @ -70,7 +76,11 @@ export class SceneFixture { | ||||
|     this.page = page | ||||
|  | ||||
|     this.streamWrapper = page.getByTestId('stream') | ||||
|     this.networkToggleConnected = page.getByTestId('network-toggle-ok') | ||||
|     this.loadingIndicator = this.streamWrapper.getByTestId('loading') | ||||
|     this.startEditSketchBtn = page | ||||
|       .getByRole('button', { name: 'Start Sketch' }) | ||||
|       .or(page.getByRole('button', { name: 'Edit Sketch' })) | ||||
|   } | ||||
|  | ||||
|   makeMouseHelpers = ( | ||||
| @ -229,6 +239,27 @@ export class SceneFixture { | ||||
|     await expect(this.exeIndicator).toBeVisible({ timeout: 30000 }) | ||||
|   } | ||||
|  | ||||
|   connectionEstablished = async () => { | ||||
|     const timeout = 30000 | ||||
|     await expect(this.networkToggleConnected).toBeVisible({ timeout }) | ||||
|   } | ||||
|  | ||||
|   settled = async (cmdBar: CmdBarFixture) => { | ||||
|     const u = await getUtils(this.page) | ||||
|  | ||||
|     await cmdBar.openCmdBar() | ||||
|     await cmdBar.chooseCommand('Settings · app · show debug panel') | ||||
|     await cmdBar.selectOption({ name: 'on' }).click() | ||||
|  | ||||
|     await u.openDebugPanel() | ||||
|     await u.expectCmdLog('[data-message-type="execution-done"]') | ||||
|     await u.clearAndCloseDebugPanel() | ||||
|  | ||||
|     await this.waitForExecutionDone() | ||||
|     await expect(this.startEditSketchBtn).not.toBeDisabled() | ||||
|     await expect(this.startEditSketchBtn).toBeVisible() | ||||
|   } | ||||
|  | ||||
|   expectPixelColor = async ( | ||||
|     colour: [number, number, number] | [number, number, number][], | ||||
|     coords: { x: number; y: number }, | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| import { test, expect } from '@playwright/test' | ||||
| import { test, expect } from './zoo-test' | ||||
| import { secrets } from './secrets' | ||||
| import { Paths, doExport, getUtils } from './test-utils' | ||||
| import { Models } from '@kittycad/lib' | ||||
| @ -13,27 +13,10 @@ import { | ||||
|   TEST_SETTINGS_KEY, | ||||
| } from './storageStates' | ||||
| import * as TOML from '@iarna/toml' | ||||
| import { SceneFixture } from './fixtures/sceneFixture' | ||||
| import { CmdBarFixture } from './fixtures/cmdBarFixture' | ||||
|  | ||||
| test.beforeEach(async ({ page }) => { | ||||
|   // reducedMotion kills animations, which speeds up tests and reduces flakiness | ||||
|   await page.emulateMedia({ reducedMotion: 'reduce' }) | ||||
|  | ||||
|   // set the default settings | ||||
|   await page.addInitScript( | ||||
|     async ({ token, settingsKey, settings, IS_PLAYWRIGHT_KEY }) => { | ||||
|       localStorage.setItem('TOKEN_PERSIST_KEY', token) | ||||
|       localStorage.setItem('persistCode', ``) | ||||
|       localStorage.setItem(settingsKey, settings) | ||||
|       localStorage.setItem(IS_PLAYWRIGHT_KEY, 'true') | ||||
|     }, | ||||
|     { | ||||
|       token: secrets.token, | ||||
|       settingsKey: TEST_SETTINGS_KEY, | ||||
|       settings: TOML.stringify({ settings: TEST_SETTINGS }), | ||||
|       IS_PLAYWRIGHT_KEY: IS_PLAYWRIGHT_KEY, | ||||
|     } | ||||
|   ) | ||||
|  | ||||
| test.beforeEach(async ({ page, context }) => { | ||||
|   // Make the user avatar image always 404 | ||||
|   // so we see the fallback menu icon for all snapshot tests | ||||
|   await page.route('https://lh3.googleusercontent.com/**', async (route) => { | ||||
| @ -45,6 +28,14 @@ test.beforeEach(async ({ page }) => { | ||||
|   }) | ||||
| }) | ||||
|  | ||||
| // Help engine-manager: tear shit down. | ||||
| test.afterEach(async ({ page }) => { | ||||
|   await page.evaluate(() => { | ||||
|     // @ts-expect-error | ||||
|     window.tearDown() | ||||
|   }) | ||||
| }) | ||||
|  | ||||
| test.setTimeout(60_000) | ||||
|  | ||||
| // We test this end to end already - getting this to work on web just to take | ||||
| @ -54,7 +45,7 @@ test.setTimeout(60_000) | ||||
| test.skip( | ||||
|   'exports of each format should work', | ||||
|   { tag: ['@snapshot', '@skipWin', '@skipMacos'] }, | ||||
|   async ({ page, context }) => { | ||||
|   async ({ page, context, scene, cmdBar }) => { | ||||
|     // 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) | ||||
| @ -106,11 +97,8 @@ part001 = startSketchOn('-XZ') | ||||
|  | ||||
|     await u.waitForAuthSkipAppStart() | ||||
|  | ||||
|     await u.openDebugPanel() | ||||
|     await u.expectCmdLog('[data-message-type="execution-done"]') | ||||
|     await u.waitForCmdReceive('extrude') | ||||
|     await page.waitForTimeout(1000) | ||||
|     await u.clearAndCloseDebugPanel() | ||||
|     await scene.connectionEstablished() | ||||
|     await scene.settled(cmdBar) | ||||
|  | ||||
|     const axisDirectionPair: Models['AxisDirectionPair_type'] = { | ||||
|       axis: 'z', | ||||
| @ -313,8 +301,25 @@ part001 = startSketchOn('-XZ') | ||||
|   } | ||||
| ) | ||||
|  | ||||
| const extrudeDefaultPlane = async (context: any, page: any, plane: string) => { | ||||
|   await context.addInitScript(async () => { | ||||
| const extrudeDefaultPlane = async ( | ||||
|   context: any, | ||||
|   page: any, | ||||
|   cmdBar: CmdBarFixture, | ||||
|   scene: SceneFixture, | ||||
|   plane: string | ||||
| ) => { | ||||
|   const code = `part001 = startSketchOn('${plane}') | ||||
|   |> startProfileAt([7.00, 4.40], %) | ||||
|   |> line(end = [6.60, -0.20]) | ||||
|   |> line(end = [2.80, 5.00]) | ||||
|   |> line(end = [-5.60, 4.40]) | ||||
|   |> line(end = [-5.40, -3.80]) | ||||
|   |> close() | ||||
|   |> extrude(length = 10.00) | ||||
| ` | ||||
|  | ||||
|   // This probably does absolutely nothing based on my trip through here. | ||||
|   await page.addInitScript(async () => { | ||||
|     localStorage.setItem( | ||||
|       'SETTINGS_PERSIST_KEY', | ||||
|       JSON.stringify({ | ||||
| @ -331,41 +336,17 @@ const extrudeDefaultPlane = async (context: any, page: any, plane: string) => { | ||||
|     ) | ||||
|   }) | ||||
|  | ||||
|   const code = `part001 = startSketchOn('${plane}') | ||||
|   |> startProfileAt([7.00, 4.40], %) | ||||
|   |> line(end = [6.60, -0.20]) | ||||
|   |> line(end = [2.80, 5.00]) | ||||
|   |> line(end = [-5.60, 4.40]) | ||||
|   |> line(end = [-5.40, -3.80]) | ||||
|   |> close() | ||||
|   |> extrude(length = 10.00) | ||||
| ` | ||||
|   await page.addInitScript(async (code: string) => { | ||||
|     localStorage.setItem('persistCode', code) | ||||
|   }) | ||||
|   }, code) | ||||
|  | ||||
|   const u = await getUtils(page) | ||||
|   await page.setViewportSize({ width: 1200, height: 500 }) | ||||
|  | ||||
|   await u.waitForAuthSkipAppStart() | ||||
|   await scene.connectionEstablished() | ||||
|   await scene.settled(cmdBar) | ||||
|  | ||||
|   // wait for execution done | ||||
|   await u.openDebugPanel() | ||||
|   await u.expectCmdLog('[data-message-type="execution-done"]') | ||||
|   await u.clearAndCloseDebugPanel() | ||||
|   await page.waitForTimeout(200) | ||||
|   // clear code | ||||
|   await u.removeCurrentCode() | ||||
|   await u.openAndClearDebugPanel() | ||||
|   await u.doAndWaitForImageDiff( | ||||
|     () => page.locator('.cm-content').fill(code), | ||||
|     200 | ||||
|   ) | ||||
|   // wait for execution done | ||||
|   await u.expectCmdLog('[data-message-type="execution-done"]') | ||||
|   await u.clearAndCloseDebugPanel() | ||||
|  | ||||
|   await u.closeKclCodePanel() | ||||
|   await expect(page).toHaveScreenshot({ | ||||
|     maxDiffPixels: 100, | ||||
|     mask: [page.getByTestId('model-state-indicator')], | ||||
| @ -380,28 +361,28 @@ test.describe( | ||||
|     // FIXME: Skip on macos its being weird. | ||||
|     test.skip(process.platform === 'darwin', 'Skip on macos') | ||||
|  | ||||
|     test('XY', async ({ page, context }) => { | ||||
|       await extrudeDefaultPlane(context, page, 'XY') | ||||
|     test('XY', async ({ page, context, cmdBar, scene }) => { | ||||
|       await extrudeDefaultPlane(context, page, cmdBar, scene, 'XY') | ||||
|     }) | ||||
|  | ||||
|     test('XZ', async ({ page, context }) => { | ||||
|       await extrudeDefaultPlane(context, page, 'XZ') | ||||
|     test('XZ', async ({ page, context, cmdBar, scene }) => { | ||||
|       await extrudeDefaultPlane(context, page, cmdBar, scene, 'XZ') | ||||
|     }) | ||||
|  | ||||
|     test('YZ', async ({ page, context }) => { | ||||
|       await extrudeDefaultPlane(context, page, 'YZ') | ||||
|     test('YZ', async ({ page, context, cmdBar, scene }) => { | ||||
|       await extrudeDefaultPlane(context, page, cmdBar, scene, 'YZ') | ||||
|     }) | ||||
|  | ||||
|     test('-XY', async ({ page, context }) => { | ||||
|       await extrudeDefaultPlane(context, page, '-XY') | ||||
|     test('-XY', async ({ page, context, cmdBar, scene }) => { | ||||
|       await extrudeDefaultPlane(context, page, cmdBar, scene, '-XY') | ||||
|     }) | ||||
|  | ||||
|     test('-XZ', async ({ page, context }) => { | ||||
|       await extrudeDefaultPlane(context, page, '-XZ') | ||||
|     test('-XZ', async ({ page, context, cmdBar, scene }) => { | ||||
|       await extrudeDefaultPlane(context, page, cmdBar, scene, '-XZ') | ||||
|     }) | ||||
|  | ||||
|     test('-YZ', async ({ page, context }) => { | ||||
|       await extrudeDefaultPlane(context, page, '-YZ') | ||||
|     test('-YZ', async ({ page, context, cmdBar, scene }) => { | ||||
|       await extrudeDefaultPlane(context, page, cmdBar, scene, '-YZ') | ||||
|     }) | ||||
|   } | ||||
| ) | ||||
| @ -409,7 +390,7 @@ test.describe( | ||||
| test( | ||||
|   'Draft segments should look right', | ||||
|   { tag: '@snapshot' }, | ||||
|   async ({ page, context }) => { | ||||
|   async ({ page, context, scene, cmdBar }) => { | ||||
|     // FIXME: Skip on macos its being weird. | ||||
|     test.skip(process.platform === 'darwin', 'Skip on macos') | ||||
|  | ||||
| @ -418,17 +399,9 @@ test( | ||||
|     const PUR = 400 / 37.5 //pixeltoUnitRatio | ||||
|     await u.waitForAuthSkipAppStart() | ||||
|  | ||||
|     await u.openDebugPanel() | ||||
|  | ||||
|     await expect( | ||||
|       page.getByRole('button', { name: 'Start Sketch' }) | ||||
|     ).not.toBeDisabled() | ||||
|     await expect( | ||||
|       page.getByRole('button', { name: 'Start Sketch' }) | ||||
|     ).toBeVisible() | ||||
|     await scene.connectionEstablished() | ||||
|  | ||||
|     // click on "Start Sketch" button | ||||
|     await u.clearCommandLogs() | ||||
|     await u.doAndWaitForImageDiff( | ||||
|       () => page.getByRole('button', { name: 'Start Sketch' }).click(), | ||||
|       200 | ||||
| @ -448,8 +421,9 @@ test( | ||||
|     await expect(page.locator('.cm-content')).toHaveText(code) | ||||
|     await page.waitForTimeout(100) | ||||
|  | ||||
|     await u.closeDebugPanel() | ||||
|     await page.mouse.move(startXPx + PUR * 20, 500 - PUR * 10) | ||||
|  | ||||
|     await page.waitForTimeout(500) | ||||
|     await expect(page).toHaveScreenshot({ | ||||
|       maxDiffPixels: 100, | ||||
|       mask: [page.getByTestId('model-state-indicator')], | ||||
| @ -458,7 +432,7 @@ test( | ||||
|     const lineEndClick = () => | ||||
|       page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10) | ||||
|     await lineEndClick() | ||||
|     await page.waitForTimeout(100) | ||||
|     await page.waitForTimeout(500) | ||||
|  | ||||
|     code += ` | ||||
|   |> xLine(7.25, %)` | ||||
| @ -469,18 +443,16 @@ test( | ||||
|       .click() | ||||
|  | ||||
|     // click on the end of the profile to continue it | ||||
|     await page.waitForTimeout(300) | ||||
|     await page.waitForTimeout(500) | ||||
|     await lineEndClick() | ||||
|     await page.waitForTimeout(100) | ||||
|     await page.waitForTimeout(500) | ||||
|  | ||||
|     // click to continue profile | ||||
|     await page.mouse.move(813, 392, { steps: 10 }) | ||||
|     await page.waitForTimeout(100) | ||||
|     await page.waitForTimeout(500) | ||||
|  | ||||
|     await page.mouse.move(startXPx + PUR * 30, 500 - PUR * 20, { steps: 10 }) | ||||
|  | ||||
|     await page.waitForTimeout(1000) | ||||
|  | ||||
|     await expect(page).toHaveScreenshot({ | ||||
|       maxDiffPixels: 100, | ||||
|       mask: [page.getByTestId('model-state-indicator')], | ||||
| @ -491,7 +463,7 @@ test( | ||||
| test( | ||||
|   'Draft rectangles should look right', | ||||
|   { tag: '@snapshot' }, | ||||
|   async ({ page, context }) => { | ||||
|   async ({ page, context, cmdBar, scene }) => { | ||||
|     // FIXME: Skip on macos its being weird. | ||||
|     test.skip(process.platform === 'darwin', 'Skip on macos') | ||||
|  | ||||
| @ -500,17 +472,10 @@ test( | ||||
|     const PUR = 400 / 37.5 //pixeltoUnitRatio | ||||
|  | ||||
|     await u.waitForAuthSkipAppStart() | ||||
|     await u.openDebugPanel() | ||||
|  | ||||
|     await expect( | ||||
|       page.getByRole('button', { name: 'Start Sketch' }) | ||||
|     ).not.toBeDisabled() | ||||
|     await expect( | ||||
|       page.getByRole('button', { name: 'Start Sketch' }) | ||||
|     ).toBeVisible() | ||||
|     await scene.connectionEstablished() | ||||
|  | ||||
|     // click on "Start Sketch" button | ||||
|     await u.clearCommandLogs() | ||||
|     await u.doAndWaitForImageDiff( | ||||
|       () => page.getByRole('button', { name: 'Start Sketch' }).click(), | ||||
|       200 | ||||
| @ -523,8 +488,8 @@ test( | ||||
|       `sketch001 = startSketchOn('XZ')` | ||||
|     ) | ||||
|  | ||||
|     await page.waitForTimeout(500) // TODO detect animation ending, or disable animation | ||||
|     await u.closeDebugPanel() | ||||
|     // Wait for camera animation | ||||
|     await page.waitForTimeout(2000) | ||||
|  | ||||
|     const startXPx = 600 | ||||
|  | ||||
| @ -548,7 +513,7 @@ test( | ||||
| test( | ||||
|   'Draft circle should look right', | ||||
|   { tag: '@snapshot' }, | ||||
|   async ({ page, context }) => { | ||||
|   async ({ page, context, cmdBar, scene }) => { | ||||
|     // FIXME: Skip on macos its being weird. | ||||
|     // test.skip(process.platform === 'darwin', 'Skip on macos') | ||||
|  | ||||
| @ -557,17 +522,9 @@ test( | ||||
|     const PUR = 400 / 37.5 //pixeltoUnitRatio | ||||
|  | ||||
|     await u.waitForAuthSkipAppStart() | ||||
|     await u.openDebugPanel() | ||||
|  | ||||
|     await expect( | ||||
|       page.getByRole('button', { name: 'Start Sketch' }) | ||||
|     ).not.toBeDisabled() | ||||
|     await expect( | ||||
|       page.getByRole('button', { name: 'Start Sketch' }) | ||||
|     ).toBeVisible() | ||||
|     await scene.connectionEstablished() | ||||
|  | ||||
|     // click on "Start Sketch" button | ||||
|     await u.clearCommandLogs() | ||||
|     await u.doAndWaitForImageDiff( | ||||
|       () => page.getByRole('button', { name: 'Start Sketch' }).click(), | ||||
|       200 | ||||
| @ -580,8 +537,8 @@ test( | ||||
|       `sketch001 = startSketchOn('XZ')` | ||||
|     ) | ||||
|  | ||||
|     await page.waitForTimeout(500) // TODO detect animation ending, or disable animation | ||||
|     await u.closeDebugPanel() | ||||
|     // Wait for camera animation | ||||
|     await page.waitForTimeout(2000) | ||||
|  | ||||
|     const startXPx = 600 | ||||
|  | ||||
| @ -611,23 +568,15 @@ test.describe( | ||||
|     // FIXME: Skip on macos its being weird. | ||||
|     test.skip(process.platform === 'darwin', 'Skip on macos') | ||||
|  | ||||
|     test('Inch scale', async ({ page }) => { | ||||
|     test('Inch scale', async ({ page, cmdBar, scene }) => { | ||||
|       const u = await getUtils(page) | ||||
|       await page.setViewportSize({ width: 1200, height: 500 }) | ||||
|       const PUR = 400 / 37.5 //pixeltoUnitRatio | ||||
|  | ||||
|       await u.waitForAuthSkipAppStart() | ||||
|       await u.openDebugPanel() | ||||
|  | ||||
|       await expect( | ||||
|         page.getByRole('button', { name: 'Start Sketch' }) | ||||
|       ).not.toBeDisabled() | ||||
|       await expect( | ||||
|         page.getByRole('button', { name: 'Start Sketch' }) | ||||
|       ).toBeVisible() | ||||
|       await scene.connectionEstablished() | ||||
|  | ||||
|       // click on "Start Sketch" button | ||||
|       await u.clearCommandLogs() | ||||
|       await u.doAndWaitForImageDiff( | ||||
|         () => page.getByRole('button', { name: 'Start Sketch' }).click(), | ||||
|         200 | ||||
| @ -639,7 +588,8 @@ test.describe( | ||||
|       let code = `sketch001 = startSketchOn('XZ')` | ||||
|       await expect(page.locator('.cm-content')).toHaveText(code) | ||||
|  | ||||
|       await page.waitForTimeout(600) // TODO detect animation ending, or disable animation | ||||
|       // Wait for camera animation | ||||
|       await page.waitForTimeout(2000) | ||||
|  | ||||
|       const startXPx = 600 | ||||
|       await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10) | ||||
| @ -647,8 +597,6 @@ test.describe( | ||||
|       await expect(u.codeLocator).toHaveText(code) | ||||
|       await page.waitForTimeout(100) | ||||
|  | ||||
|       await u.closeDebugPanel() | ||||
|  | ||||
|       await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10) | ||||
|       await page.waitForTimeout(100) | ||||
|  | ||||
| @ -683,17 +631,12 @@ test.describe( | ||||
|         mask: [page.getByTestId('model-state-indicator')], | ||||
|       }) | ||||
|  | ||||
|       // exit sketch | ||||
|       await u.openAndClearDebugPanel() | ||||
|       await u.doAndWaitForImageDiff( | ||||
|         () => page.getByRole('button', { name: 'Exit Sketch' }).click(), | ||||
|         200 | ||||
|       ) | ||||
|  | ||||
|       // wait for execution done | ||||
|       await u.expectCmdLog('[data-message-type="execution-done"]') | ||||
|       await u.clearAndCloseDebugPanel() | ||||
|       await page.waitForTimeout(300) | ||||
|       await scene.settled(cmdBar) | ||||
|  | ||||
|       // second screen shot should look almost identical, i.e. scale should be the same. | ||||
|       await expect(page).toHaveScreenshot({ | ||||
| @ -702,8 +645,8 @@ test.describe( | ||||
|       }) | ||||
|     }) | ||||
|  | ||||
|     test('Millimeter scale', async ({ page }) => { | ||||
|       await page.addInitScript( | ||||
|     test('Millimeter scale', async ({ page, context, cmdBar, scene }) => { | ||||
|       await context.addInitScript( | ||||
|         async ({ settingsKey, settings }) => { | ||||
|           localStorage.setItem(settingsKey, settings) | ||||
|         }, | ||||
| @ -725,17 +668,10 @@ test.describe( | ||||
|       const PUR = 400 / 37.5 //pixeltoUnitRatio | ||||
|  | ||||
|       await u.waitForAuthSkipAppStart() | ||||
|       await u.openDebugPanel() | ||||
|  | ||||
|       await expect( | ||||
|         page.getByRole('button', { name: 'Start Sketch' }) | ||||
|       ).not.toBeDisabled() | ||||
|       await expect( | ||||
|         page.getByRole('button', { name: 'Start Sketch' }) | ||||
|       ).toBeVisible() | ||||
|       await scene.connectionEstablished() | ||||
|       await scene.settled(cmdBar) | ||||
|  | ||||
|       // click on "Start Sketch" button | ||||
|       await u.clearCommandLogs() | ||||
|       await u.doAndWaitForImageDiff( | ||||
|         () => page.getByRole('button', { name: 'Start Sketch' }).click(), | ||||
|         200 | ||||
| @ -747,7 +683,8 @@ test.describe( | ||||
|       let code = `sketch001 = startSketchOn('XZ')` | ||||
|       await expect(u.codeLocator).toHaveText(code) | ||||
|  | ||||
|       await page.waitForTimeout(600) // TODO detect animation ending, or disable animation | ||||
|       // Wait for camera animation | ||||
|       await page.waitForTimeout(2000) | ||||
|  | ||||
|       const startXPx = 600 | ||||
|       await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10) | ||||
| @ -755,8 +692,6 @@ test.describe( | ||||
|       await expect(u.codeLocator).toHaveText(code) | ||||
|       await page.waitForTimeout(100) | ||||
|  | ||||
|       await u.closeDebugPanel() | ||||
|  | ||||
|       await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10) | ||||
|       await page.waitForTimeout(100) | ||||
|  | ||||
| @ -790,16 +725,12 @@ test.describe( | ||||
|       }) | ||||
|  | ||||
|       // exit sketch | ||||
|       await u.openAndClearDebugPanel() | ||||
|       await u.doAndWaitForImageDiff( | ||||
|         () => page.getByRole('button', { name: 'Exit Sketch' }).click(), | ||||
|         200 | ||||
|       ) | ||||
|  | ||||
|       // wait for execution done | ||||
|       await u.expectCmdLog('[data-message-type="execution-done"]') | ||||
|       await u.clearAndCloseDebugPanel() | ||||
|       await page.waitForTimeout(300) | ||||
|       await scene.settled(cmdBar) | ||||
|  | ||||
|       // second screen shot should look almost identical, i.e. scale should be the same. | ||||
|       await expect(page).toHaveScreenshot({ | ||||
| @ -812,7 +743,7 @@ test.describe( | ||||
| test( | ||||
|   'Sketch on face with none z-up', | ||||
|   { tag: '@snapshot' }, | ||||
|   async ({ page, context }) => { | ||||
|   async ({ page, context, cmdBar, scene }) => { | ||||
|     // FIXME: Skip on macos its being weird. | ||||
|     test.skip(process.platform === 'darwin', 'Skip on macos') | ||||
|  | ||||
| @ -840,12 +771,8 @@ part002 = startSketchOn(part001, seg01) | ||||
|  | ||||
|     await u.waitForAuthSkipAppStart() | ||||
|  | ||||
|     await u.openDebugPanel() | ||||
|     // wait for execution done | ||||
|     await expect( | ||||
|       page.locator('[data-message-type="execution-done"]') | ||||
|     ).toHaveCount(1, { timeout: 10_000 }) | ||||
|     await u.closeDebugPanel() | ||||
|     await scene.connectionEstablished() | ||||
|     await scene.settled(cmdBar) | ||||
|  | ||||
|     // Wait for the second extrusion to appear | ||||
|     // TODO: Find a way to truly know that the objects have finished | ||||
| @ -877,7 +804,7 @@ part002 = startSketchOn(part001, seg01) | ||||
| test( | ||||
|   'Zoom to fit on load - solid 2d', | ||||
|   { tag: '@snapshot' }, | ||||
|   async ({ page, context }) => { | ||||
|   async ({ page, context, cmdBar, scene }) => { | ||||
|     // FIXME: Skip on macos its being weird. | ||||
|     test.skip(process.platform === 'darwin', 'Skip on macos') | ||||
|  | ||||
| @ -899,12 +826,8 @@ test( | ||||
|  | ||||
|     await u.waitForAuthSkipAppStart() | ||||
|  | ||||
|     await u.openDebugPanel() | ||||
|     // wait for execution done | ||||
|     await expect( | ||||
|       page.locator('[data-message-type="execution-done"]') | ||||
|     ).toHaveCount(1) | ||||
|     await u.closeDebugPanel() | ||||
|     await scene.connectionEstablished() | ||||
|     await scene.settled(cmdBar) | ||||
|  | ||||
|     // Wait for the second extrusion to appear | ||||
|     // TODO: Find a way to truly know that the objects have finished | ||||
| @ -920,7 +843,7 @@ test( | ||||
| test( | ||||
|   'Zoom to fit on load - solid 3d', | ||||
|   { tag: '@snapshot' }, | ||||
|   async ({ page, context }) => { | ||||
|   async ({ page, context, cmdBar, scene }) => { | ||||
|     // FIXME: Skip on macos its being weird. | ||||
|     test.skip(process.platform === 'darwin', 'Skip on macos') | ||||
|  | ||||
| @ -943,12 +866,8 @@ test( | ||||
|  | ||||
|     await u.waitForAuthSkipAppStart() | ||||
|  | ||||
|     await u.openDebugPanel() | ||||
|     // wait for execution done | ||||
|     await expect( | ||||
|       page.locator('[data-message-type="execution-done"]') | ||||
|     ).toHaveCount(1) | ||||
|     await u.closeDebugPanel() | ||||
|     await scene.connectionEstablished() | ||||
|     await scene.settled(cmdBar) | ||||
|  | ||||
|     // Wait for the second extrusion to appear | ||||
|     // TODO: Find a way to truly know that the objects have finished | ||||
| @ -965,7 +884,11 @@ test.describe('Grid visibility', { tag: '@snapshot' }, () => { | ||||
|   // FIXME: Skip on macos its being weird. | ||||
|   // test.skip(process.platform === 'darwin', 'Skip on macos') | ||||
|  | ||||
|   test('Grid turned off to on via command bar', async ({ page }) => { | ||||
|   test('Grid turned off to on via command bar', async ({ | ||||
|     page, | ||||
|     cmdBar, | ||||
|     scene, | ||||
|   }) => { | ||||
|     const u = await getUtils(page) | ||||
|     const stream = page.getByTestId('stream') | ||||
|     const mask = [ | ||||
| @ -978,12 +901,9 @@ test.describe('Grid visibility', { tag: '@snapshot' }, () => { | ||||
|     await page.goto('/') | ||||
|     await u.waitForAuthSkipAppStart() | ||||
|  | ||||
|     await u.openDebugPanel() | ||||
|     // wait for execution done | ||||
|     await expect( | ||||
|       page.locator('[data-message-type="execution-done"]') | ||||
|     ).toHaveCount(1) | ||||
|     await u.closeDebugPanel() | ||||
|     await scene.connectionEstablished() | ||||
|     await scene.settled(cmdBar) | ||||
|  | ||||
|     await u.closeKclCodePanel() | ||||
|     // TODO: Find a way to truly know that the objects have finished | ||||
|     // rendering, because an execution-done message is not sufficient. | ||||
| @ -1033,7 +953,7 @@ test.describe('Grid visibility', { tag: '@snapshot' }, () => { | ||||
|     }) | ||||
|   }) | ||||
|  | ||||
|   test('Grid turned off', async ({ page }) => { | ||||
|   test('Grid turned off', async ({ page, cmdBar, scene }) => { | ||||
|     const u = await getUtils(page) | ||||
|     const stream = page.getByTestId('stream') | ||||
|     const mask = [ | ||||
| @ -1046,12 +966,9 @@ test.describe('Grid visibility', { tag: '@snapshot' }, () => { | ||||
|     await page.goto('/') | ||||
|     await u.waitForAuthSkipAppStart() | ||||
|  | ||||
|     await u.openDebugPanel() | ||||
|     // wait for execution done | ||||
|     await expect( | ||||
|       page.locator('[data-message-type="execution-done"]') | ||||
|     ).toHaveCount(1) | ||||
|     await u.closeDebugPanel() | ||||
|     await scene.connectionEstablished() | ||||
|     await scene.settled(cmdBar) | ||||
|  | ||||
|     await u.closeKclCodePanel() | ||||
|     // TODO: Find a way to truly know that the objects have finished | ||||
|     // rendering, because an execution-done message is not sufficient. | ||||
| @ -1063,8 +980,8 @@ test.describe('Grid visibility', { tag: '@snapshot' }, () => { | ||||
|     }) | ||||
|   }) | ||||
|  | ||||
|   test('Grid turned on', async ({ page }) => { | ||||
|     await page.addInitScript( | ||||
|   test('Grid turned on', async ({ page, context, cmdBar, scene }) => { | ||||
|     await context.addInitScript( | ||||
|       async ({ settingsKey, settings }) => { | ||||
|         localStorage.setItem(settingsKey, settings) | ||||
|       }, | ||||
| @ -1094,12 +1011,9 @@ test.describe('Grid visibility', { tag: '@snapshot' }, () => { | ||||
|     await page.goto('/') | ||||
|     await u.waitForAuthSkipAppStart() | ||||
|  | ||||
|     await u.openDebugPanel() | ||||
|     // wait for execution done | ||||
|     await expect( | ||||
|       page.locator('[data-message-type="execution-done"]') | ||||
|     ).toHaveCount(1) | ||||
|     await u.closeDebugPanel() | ||||
|     await scene.connectionEstablished() | ||||
|     await scene.settled(cmdBar) | ||||
|  | ||||
|     await u.closeKclCodePanel() | ||||
|     // TODO: Find a way to truly know that the objects have finished | ||||
|     // rendering, because an execution-done message is not sufficient. | ||||
| @ -1179,7 +1093,7 @@ test.fixme('theme persists', async ({ page, context }) => { | ||||
| }) | ||||
|  | ||||
| test.describe('code color goober', { tag: '@snapshot' }, () => { | ||||
|   test('code color goober', async ({ page, context }) => { | ||||
|   test('code color goober', async ({ page, context, scene, cmdBar }) => { | ||||
|     const u = await getUtils(page) | ||||
|     await context.addInitScript(async () => { | ||||
|       localStorage.setItem( | ||||
| @ -1213,19 +1127,22 @@ sweepSketch = startSketchOn('XY') | ||||
|     }) | ||||
|  | ||||
|     await page.setViewportSize({ width: 1200, height: 1000 }) | ||||
|  | ||||
|     await u.waitForAuthSkipAppStart() | ||||
|  | ||||
|     await u.openDebugPanel() | ||||
|     await u.expectCmdLog('[data-message-type="execution-done"]') | ||||
|     await u.clearAndCloseDebugPanel() | ||||
|     await scene.connectionEstablished() | ||||
|     await scene.settled(cmdBar) | ||||
|  | ||||
|     await expect(page, 'expect small color widget').toHaveScreenshot({ | ||||
|       maxDiffPixels: 100, | ||||
|     }) | ||||
|   }) | ||||
|  | ||||
|   test('code color goober opening window', async ({ page, context }) => { | ||||
|   test('code color goober opening window', async ({ | ||||
|     page, | ||||
|     context, | ||||
|     scene, | ||||
|     cmdBar, | ||||
|   }) => { | ||||
|     const u = await getUtils(page) | ||||
|     await context.addInitScript(async () => { | ||||
|       localStorage.setItem( | ||||
| @ -1259,12 +1176,10 @@ sweepSketch = startSketchOn('XY') | ||||
|     }) | ||||
|  | ||||
|     await page.setViewportSize({ width: 1200, height: 1000 }) | ||||
|  | ||||
|     await u.waitForAuthSkipAppStart() | ||||
|  | ||||
|     await u.openDebugPanel() | ||||
|     await u.expectCmdLog('[data-message-type="execution-done"]') | ||||
|     await u.clearAndCloseDebugPanel() | ||||
|     await scene.connectionEstablished() | ||||
|     await scene.settled(cmdBar) | ||||
|  | ||||
|     await expect(page.locator('.cm-css-color-picker-wrapper')).toBeVisible() | ||||
|  | ||||
|  | ||||
| Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 53 KiB | 
| Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB | 
| Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 59 KiB | 
| Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB | 
| Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 48 KiB | 
| Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 45 KiB | 
| Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 42 KiB | 
| Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 45 KiB | 
| Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 76 KiB | 
| Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB | 
| Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB | 
| Before Width: | Height: | Size: 145 KiB After Width: | Height: | Size: 145 KiB | 
| Before Width: | Height: | Size: 129 KiB After Width: | Height: | Size: 128 KiB | 
| Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 69 KiB | 
| Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 75 KiB | 
| Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 69 KiB | 
| Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 72 KiB | 
| Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 73 KiB | 
| Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 68 KiB | 
| @ -118,6 +118,7 @@ | ||||
|     "tronb:package:prod": "yarn tronb:vite:prod && electron-builder --config electron-builder.yml --publish always", | ||||
|     "test-setup": "yarn install && yarn build:wasm", | ||||
|     "test": "vitest --mode development", | ||||
|     "test:snapshots": "PLATFORM=web NODE_ENV=development yarn playwright test --config=playwright.config.ts --update-snapshots --grep=@snapshot --trace=on --shard=1/1", | ||||
|     "test:unit": "vitest run --mode development --exclude **/kclSamples.test.ts", | ||||
|     "test:unit:kcl-samples": "vitest run --mode development ./src/lang/kclSamples.test.ts", | ||||
|     "test:playwright:electron": "playwright test --config=playwright.electron.config.ts --grep-invert='@snapshot'", | ||||
|  | ||||
| @ -19,21 +19,25 @@ export interface ActionIconProps extends React.PropsWithChildren { | ||||
|   bgClassName?: string | ||||
|   iconClassName?: string | ||||
|   size?: keyof typeof iconSizes | ||||
|   'data-testid'?: string | ||||
| } | ||||
|  | ||||
| export const ActionIcon = ({ | ||||
|   icon = faCircleExclamation, | ||||
|   className, | ||||
|   bgClassName, | ||||
|   iconClassName, | ||||
|   size = 'md', | ||||
|   children, | ||||
| }: ActionIconProps) => { | ||||
| export const ActionIcon = (props: ActionIconProps) => { | ||||
|   const { | ||||
|     icon = faCircleExclamation, | ||||
|     className, | ||||
|     bgClassName, | ||||
|     iconClassName, | ||||
|     size = 'md', | ||||
|     children, | ||||
|   } = props | ||||
|  | ||||
|   const computedIconClassName = `h-auto text-inherit dark:text-current group-disabled:text-chalkboard-60 group-disabled:text-chalkboard-60 ${iconClassName}` | ||||
|   const computedBgClassName = `bg-chalkboard-20 dark:bg-chalkboard-80 group-disabled:bg-chalkboard-30 dark:group-disabled:bg-chalkboard-80 ${bgClassName}` | ||||
|  | ||||
|   return ( | ||||
|     <div | ||||
|       data-testid={props['data-testid']} | ||||
|       className={ | ||||
|         `w-fit self-stretch inline-grid place-content-center ${className} ` + | ||||
|         computedBgClassName | ||||
|  | ||||
| @ -99,6 +99,9 @@ export const NetworkHealthIndicator = () => { | ||||
|       > | ||||
|         <ActionIcon | ||||
|           icon={overallConnectionStateIcon[overallState]} | ||||
|           data-testid={`network-toggle-${ | ||||
|             overallState == NetworkHealthState.Ok ? 'ok' : 'other' | ||||
|           }`} | ||||
|           className="p-1" | ||||
|           iconClassName={overallConnectionStateColor[overallState].icon} | ||||
|           bgClassName={ | ||||
|  | ||||
