Rename desktop e2e scripts and tags for consistency (#7240)
* Rename desktop e2e scripts and tags for consistency * Show local command in main test step * Restore 'e2e' prefix to clarify GitHub UI * Add web script to contributor guide
This commit is contained in:
		
							
								
								
									
										12
									
								
								.github/ci-cd-scripts/playwright-electron.sh
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								.github/ci-cd-scripts/playwright-electron.sh
									
									
									
									
										vendored
									
									
								
							@ -7,11 +7,11 @@ if [[ ! -f "test-results/.last-run.json" ]]; then
 | 
			
		||||
    # If no last run artifact, than run Playwright normally
 | 
			
		||||
    echo "run playwright normally"
 | 
			
		||||
    if [[ "$3" == *ubuntu* ]]; then
 | 
			
		||||
        xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run test:playwright:electron -- --shard=$1/$2 || true
 | 
			
		||||
        xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run test:e2e:desktop -- --shard=$1/$2 || true
 | 
			
		||||
    elif [[ "$3" == *windows* ]]; then
 | 
			
		||||
        npm run test:playwright:electron -- --grep=@windows --shard=$1/$2 || true
 | 
			
		||||
        npm run test:e2e:desktop -- --grep=@windows --shard=$1/$2 || true
 | 
			
		||||
    elif [[ "$3" == *macos* ]]; then
 | 
			
		||||
        npm run test:playwright:electron -- --grep=@macos --shard=$1/$2 || true
 | 
			
		||||
        npm run test:e2e:desktop -- --grep=@macos --shard=$1/$2 || true
 | 
			
		||||
    else
 | 
			
		||||
        echo "Do not run Playwright. Unable to detect os runtime."
 | 
			
		||||
        exit 1
 | 
			
		||||
@ -31,11 +31,11 @@ while [[ $retry -le $max_retries ]]; do
 | 
			
		||||
            echo "retried=true" >>$GITHUB_OUTPUT
 | 
			
		||||
            echo "run playwright with last failed tests and retry $retry"
 | 
			
		||||
            if [[ "$3" == *ubuntu* ]]; then
 | 
			
		||||
                xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run test:playwright:electron -- --last-failed || true
 | 
			
		||||
                xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run test:e2e:desktop -- --last-failed || true
 | 
			
		||||
            elif [[ "$3" == *windows* ]]; then
 | 
			
		||||
                npm run test:playwright:electron -- --grep=@windows --last-failed || true
 | 
			
		||||
                npm run test:e2e:desktop -- --grep=@windows --last-failed || true
 | 
			
		||||
            elif [[ "$3" == *macos* ]]; then
 | 
			
		||||
                npm run test:playwright:electron -- --grep=@macos --last-failed || true
 | 
			
		||||
                npm run test:e2e:desktop -- --grep=@macos --last-failed || true
 | 
			
		||||
            else
 | 
			
		||||
                echo "Do not run playwright. Unable to detect os runtime."
 | 
			
		||||
                exit 1
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										28
									
								
								.github/workflows/e2e-tests.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										28
									
								
								.github/workflows/e2e-tests.yml
									
									
									
									
										vendored
									
									
								
							@ -20,9 +20,11 @@ permissions:
 | 
			
		||||
jobs:
 | 
			
		||||
 | 
			
		||||
  prepare-wasm:
 | 
			
		||||
 | 
			
		||||
    # separate job on Ubuntu to build or fetch the wasm blob once on the fastest runner
 | 
			
		||||
    runs-on: runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64
 | 
			
		||||
    steps:
 | 
			
		||||
 | 
			
		||||
      - uses: actions/checkout@v4
 | 
			
		||||
 | 
			
		||||
      - id: filter
 | 
			
		||||
@ -103,9 +105,10 @@ jobs:
 | 
			
		||||
    needs: [prepare-wasm]
 | 
			
		||||
 | 
			
		||||
    runs-on: runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64
 | 
			
		||||
    name: playwright:snapshots:ubuntu
 | 
			
		||||
    name: e2e:snapshots
 | 
			
		||||
 | 
			
		||||
    steps:
 | 
			
		||||
 | 
			
		||||
      - uses: actions/create-github-app-token@v1
 | 
			
		||||
        id: app-token
 | 
			
		||||
        with:
 | 
			
		||||
@ -135,7 +138,7 @@ jobs:
 | 
			
		||||
      - name: Install dependencies
 | 
			
		||||
        run: npm install
 | 
			
		||||
 | 
			
		||||
      - name: Cache browsers
 | 
			
		||||
      - name: Download browser cache
 | 
			
		||||
        uses: actions/cache@v4
 | 
			
		||||
        with:
 | 
			
		||||
          path: |
 | 
			
		||||
@ -145,7 +148,7 @@ jobs:
 | 
			
		||||
      - name: Install browsers
 | 
			
		||||
        run: npm run playwright install --with-deps
 | 
			
		||||
 | 
			
		||||
      - name: Test snapshots
 | 
			
		||||
      - name: npm run test:snapshots
 | 
			
		||||
        uses: nick-fields/retry@v3.0.2
 | 
			
		||||
        with:
 | 
			
		||||
          shell: bash
 | 
			
		||||
@ -216,6 +219,7 @@ jobs:
 | 
			
		||||
      matrix:
 | 
			
		||||
        os: [ubuntu-latest, windows-latest, macos-latest]
 | 
			
		||||
    runs-on: ${{ matrix.os }}
 | 
			
		||||
    name: e2e:web (${{ contains(matrix.os, 'ubuntu') && 'ubuntu' || (contains(matrix.os, 'windows') && 'windows' || 'macos') }})
 | 
			
		||||
    env:
 | 
			
		||||
      OS_NAME: ${{ contains(matrix.os, 'ubuntu') && 'ubuntu' || (contains(matrix.os, 'windows') && 'windows' || 'macos') }}
 | 
			
		||||
 | 
			
		||||
@ -250,7 +254,7 @@ jobs:
 | 
			
		||||
      - name: Install dependencies
 | 
			
		||||
        run: npm install
 | 
			
		||||
 | 
			
		||||
      - name: Cache browsers
 | 
			
		||||
      - name: Download browser cache
 | 
			
		||||
        uses: actions/cache@v4
 | 
			
		||||
        with:
 | 
			
		||||
          path: |
 | 
			
		||||
@ -267,7 +271,7 @@ jobs:
 | 
			
		||||
          GH_ACTIONS_AXIOM_TOKEN: ${{ secrets.GH_ACTIONS_AXIOM_TOKEN }}
 | 
			
		||||
          OS_NAME: ${{ env.OS_NAME }}
 | 
			
		||||
 | 
			
		||||
      - name: Test web
 | 
			
		||||
      - name: npm run test:e2e:web
 | 
			
		||||
        uses: nick-fields/retry@v3.0.2
 | 
			
		||||
        with:
 | 
			
		||||
          shell: bash
 | 
			
		||||
@ -336,7 +340,7 @@ jobs:
 | 
			
		||||
            shardIndex: 2
 | 
			
		||||
            shardTotal: 2
 | 
			
		||||
    runs-on: ${{ matrix.os }}
 | 
			
		||||
    name: playwright:electron:${{ contains(matrix.os, 'ubuntu') && 'ubuntu' || (contains(matrix.os, 'windows') && 'windows' || 'macos') }} (shard ${{ matrix.shardIndex }})
 | 
			
		||||
    name: e2e:desktop (${{ contains(matrix.os, 'ubuntu') && 'ubuntu' || (contains(matrix.os, 'windows') && 'windows' || 'macos') }}, shard ${{ matrix.shardIndex }})
 | 
			
		||||
    env:
 | 
			
		||||
      OS_NAME: ${{ contains(matrix.os, 'ubuntu') && 'ubuntu' || (contains(matrix.os, 'windows') && 'windows' || 'macos') }}
 | 
			
		||||
 | 
			
		||||
@ -346,7 +350,7 @@ jobs:
 | 
			
		||||
      - uses: actions/download-artifact@v4
 | 
			
		||||
        name: prepared-wasm
 | 
			
		||||
 | 
			
		||||
      - name: Copy prepared wasm
 | 
			
		||||
      - name: Copy prepared Wasm
 | 
			
		||||
        run: |
 | 
			
		||||
          ls -R prepared-wasm
 | 
			
		||||
          cp prepared-wasm/kcl_wasm_lib_bg.wasm public
 | 
			
		||||
@ -362,7 +366,7 @@ jobs:
 | 
			
		||||
        id: deps-install
 | 
			
		||||
        run: npm install
 | 
			
		||||
 | 
			
		||||
      - name: Cache browsers
 | 
			
		||||
      - name: Download browser cache
 | 
			
		||||
        uses: actions/cache@v4
 | 
			
		||||
        with:
 | 
			
		||||
          path: |
 | 
			
		||||
@ -372,9 +376,6 @@ jobs:
 | 
			
		||||
      - name: Install browsers
 | 
			
		||||
        run: npm run playwright install --with-deps
 | 
			
		||||
 | 
			
		||||
      - name: Build web
 | 
			
		||||
        run: npm run tronb:vite:dev
 | 
			
		||||
 | 
			
		||||
      - name: Start Vector
 | 
			
		||||
        if: ${{ !contains(matrix.os, 'windows') }}
 | 
			
		||||
        run: .github/ci-cd-scripts/start-vector-${{ env.OS_NAME }}.sh
 | 
			
		||||
@ -382,6 +383,9 @@ jobs:
 | 
			
		||||
          GH_ACTIONS_AXIOM_TOKEN: ${{ secrets.GH_ACTIONS_AXIOM_TOKEN }}
 | 
			
		||||
          OS_NAME: ${{ env.OS_NAME }}
 | 
			
		||||
 | 
			
		||||
      - name: Build app
 | 
			
		||||
        run: npm run tronb:vite:dev
 | 
			
		||||
 | 
			
		||||
      - uses: actions/download-artifact@v4
 | 
			
		||||
        if: ${{ !cancelled() && (success() || failure()) }}
 | 
			
		||||
        continue-on-error: true
 | 
			
		||||
@ -389,7 +393,7 @@ jobs:
 | 
			
		||||
          name: test-results-${{ env.OS_NAME }}-${{ matrix.shardIndex }}-${{ github.sha }}
 | 
			
		||||
          path: test-results/
 | 
			
		||||
 | 
			
		||||
      - name: Run playwright/electron flow (with retries)
 | 
			
		||||
      - name: npm run test:e2e:desktop
 | 
			
		||||
        id: retry
 | 
			
		||||
        if:  ${{ !cancelled() && steps.deps-install.outcome == 'success' }}
 | 
			
		||||
        uses: nick-fields/retry@v3.0.2
 | 
			
		||||
 | 
			
		||||
@ -213,14 +213,21 @@ npm run test:snapshots
 | 
			
		||||
```
 | 
			
		||||
You may use `-- --update-snapshots` as needed.
 | 
			
		||||
 | 
			
		||||
#### Electron flow tests (Chromium on Ubuntu, macOS, Windows)
 | 
			
		||||
#### Desktop tests (Electron on all platforms)
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
npm run playwright -- install chromium
 | 
			
		||||
npm run test:playwright:electron:local
 | 
			
		||||
npm run test:e2e:desktop:local
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
You may use `-- -g "my test"` to match specific test titles, or `-- path/to/file.spec.ts` for a test file.
 | 
			
		||||
 | 
			
		||||
#### Web tests (Google Chrome on all platforms)
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
npm run test:e2e:web
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### Debugger
 | 
			
		||||
 | 
			
		||||
However, if you want a debugger I recommend using VSCode and the `playwright` extension, as the above command is a cruder debugger that steps into every function call which is annoying.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										4
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								Makefile
									
									
									
									
									
								
							@ -129,9 +129,9 @@ endif
 | 
			
		||||
.PHONY: test-e2e-desktop
 | 
			
		||||
test-e2e-desktop: install build ## Run the desktop e2e tests
 | 
			
		||||
ifdef E2E_GREP
 | 
			
		||||
	npm run test:playwright:electron -- --grep="$(E2E_GREP)" --max-failures=$(E2E_FAILURES)
 | 
			
		||||
	npm run test:e2e:desktop -- --grep="$(E2E_GREP)" --max-failures=$(E2E_FAILURES)
 | 
			
		||||
else
 | 
			
		||||
	npm run test:playwright:electron -- --workers='100%'
 | 
			
		||||
	npm run test:e2e:desktop -- --workers='100%'
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
###############################################################################
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,7 @@ import * as fsp from 'fs/promises'
 | 
			
		||||
test.describe('Electron app header tests', () => {
 | 
			
		||||
  test(
 | 
			
		||||
    'Open Command Palette button has correct shortcut',
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    { tag: '@desktop' },
 | 
			
		||||
    async ({ page }, testInfo) => {
 | 
			
		||||
      await page.setBodyDimensions({ width: 1200, height: 500 })
 | 
			
		||||
 | 
			
		||||
@ -30,7 +30,7 @@ test.describe('Electron app header tests', () => {
 | 
			
		||||
 | 
			
		||||
  test(
 | 
			
		||||
    'User settings has correct shortcut',
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    { tag: '@desktop' },
 | 
			
		||||
    async ({ page, toolbar }, testInfo) => {
 | 
			
		||||
      await page.setBodyDimensions({ width: 1200, height: 500 })
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,7 @@ import { expect, test } from '@e2e/playwright/zoo-test'
 | 
			
		||||
test.describe('Authentication tests', () => {
 | 
			
		||||
  test(
 | 
			
		||||
    `The user can sign out and back in`,
 | 
			
		||||
    { tag: ['@electron'] },
 | 
			
		||||
    { tag: ['@desktop'] },
 | 
			
		||||
    async ({ page, homePage, signInPage, toolbar, tronApp }) => {
 | 
			
		||||
      if (!tronApp) {
 | 
			
		||||
        fail()
 | 
			
		||||
 | 
			
		||||
@ -275,7 +275,7 @@ middle()`)
 | 
			
		||||
 | 
			
		||||
test(
 | 
			
		||||
  'Opening multiple panes persists when switching projects',
 | 
			
		||||
  { tag: '@electron' },
 | 
			
		||||
  { tag: '@desktop' },
 | 
			
		||||
  async ({ context, page }, testInfo) => {
 | 
			
		||||
    // Setup multiple projects.
 | 
			
		||||
    await context.folderSetupFn(async (dir) => {
 | 
			
		||||
@ -346,7 +346,7 @@ test(
 | 
			
		||||
 | 
			
		||||
test(
 | 
			
		||||
  'external change of file contents are reflected in editor',
 | 
			
		||||
  { tag: '@electron' },
 | 
			
		||||
  { tag: '@desktop' },
 | 
			
		||||
  async ({ context, page }, testInfo) => {
 | 
			
		||||
    const PROJECT_DIR_NAME = 'lee-was-here'
 | 
			
		||||
    const { dir: projectsDir } = await context.folderSetupFn(async (dir) => {
 | 
			
		||||
 | 
			
		||||
@ -10,7 +10,7 @@ import { expect, test } from '@e2e/playwright/zoo-test'
 | 
			
		||||
 | 
			
		||||
test(
 | 
			
		||||
  'export works on the first try',
 | 
			
		||||
  { tag: ['@electron', '@macos', '@windows', '@skipLocalEngine'] },
 | 
			
		||||
  { tag: ['@desktop', '@macos', '@windows', '@skipLocalEngine'] },
 | 
			
		||||
  async ({ page, context, scene, tronApp, cmdBar }, testInfo) => {
 | 
			
		||||
    if (!tronApp) {
 | 
			
		||||
      fail()
 | 
			
		||||
 | 
			
		||||
@ -1337,7 +1337,7 @@ sketch001 = startSketchOn(XZ)
 | 
			
		||||
 | 
			
		||||
  test(
 | 
			
		||||
    `Can import a local OBJ file`,
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    { tag: '@desktop' },
 | 
			
		||||
    async ({ page, context }, testInfo) => {
 | 
			
		||||
      await context.folderSetupFn(async (dir) => {
 | 
			
		||||
        const bracketDir = join(dir, 'cube')
 | 
			
		||||
 | 
			
		||||
@ -57,7 +57,7 @@ sketch003 = startSketchOn(plane001)
 | 
			
		||||
test.describe('Feature Tree pane', () => {
 | 
			
		||||
  test(
 | 
			
		||||
    'User can go to definition and go to function definition',
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    { tag: '@desktop' },
 | 
			
		||||
    async ({ context, homePage, scene, editor, toolbar, cmdBar, page }) => {
 | 
			
		||||
      await context.folderSetupFn(async (dir) => {
 | 
			
		||||
        const bracketDir = join(dir, 'test-sample')
 | 
			
		||||
@ -150,7 +150,7 @@ test.describe('Feature Tree pane', () => {
 | 
			
		||||
 | 
			
		||||
  test(
 | 
			
		||||
    `User can edit sketch (but not on offset plane yet) from the feature tree`,
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    { tag: '@desktop' },
 | 
			
		||||
    async ({ context, homePage, scene, editor, toolbar, page }) => {
 | 
			
		||||
      await context.addInitScript((initialCode) => {
 | 
			
		||||
        localStorage.setItem('persistCode', initialCode)
 | 
			
		||||
 | 
			
		||||
@ -13,7 +13,7 @@ import { expect, test } from '@e2e/playwright/zoo-test'
 | 
			
		||||
test.describe('integrations tests', () => {
 | 
			
		||||
  test(
 | 
			
		||||
    'Creating a new file or switching file while in sketchMode should exit sketchMode',
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    { tag: '@desktop' },
 | 
			
		||||
    async ({ page, context, homePage, scene, editor, toolbar, cmdBar }) => {
 | 
			
		||||
      await context.folderSetupFn(async (dir) => {
 | 
			
		||||
        const bracketDir = join(dir, 'test-sample')
 | 
			
		||||
@ -100,7 +100,7 @@ test.describe('when using the file tree to', () => {
 | 
			
		||||
 | 
			
		||||
  test(
 | 
			
		||||
    `rename ${fromFile} to ${toFile}, and doesn't crash on reload and settings load`,
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    { tag: '@desktop' },
 | 
			
		||||
    async ({ page }, testInfo) => {
 | 
			
		||||
      const { panesOpen, pasteCodeInEditor, renameFile, editorTextMatches } =
 | 
			
		||||
        await getUtils(page, test)
 | 
			
		||||
@ -142,7 +142,7 @@ test.describe('when using the file tree to', () => {
 | 
			
		||||
 | 
			
		||||
  test(
 | 
			
		||||
    `create many new files of the same name, incrementing their names`,
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    { tag: '@desktop' },
 | 
			
		||||
    async ({ page }, testInfo) => {
 | 
			
		||||
      const { panesOpen, createNewFile } = await getUtils(page, test)
 | 
			
		||||
 | 
			
		||||
@ -174,7 +174,7 @@ test.describe('when using the file tree to', () => {
 | 
			
		||||
 | 
			
		||||
  test(
 | 
			
		||||
    'create a new file with the same name as an existing file cancels the operation',
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    { tag: '@desktop' },
 | 
			
		||||
    async ({ context, page, homePage, scene, editor, toolbar }, testInfo) => {
 | 
			
		||||
      const projectName = 'cube'
 | 
			
		||||
      const mainFile = 'main.kcl'
 | 
			
		||||
@ -240,7 +240,7 @@ test.describe('when using the file tree to', () => {
 | 
			
		||||
 | 
			
		||||
  test(
 | 
			
		||||
    `create new folders and that doesn't trigger a navigation`,
 | 
			
		||||
    { tag: ['@electron', '@macos', '@windows'] },
 | 
			
		||||
    { tag: ['@desktop', '@macos', '@windows'] },
 | 
			
		||||
    async ({ page, homePage, scene, toolbar, cmdBar }) => {
 | 
			
		||||
      await homePage.goToModelingScene()
 | 
			
		||||
      await scene.settled(cmdBar)
 | 
			
		||||
@ -260,7 +260,7 @@ test.describe('when using the file tree to', () => {
 | 
			
		||||
 | 
			
		||||
  test(
 | 
			
		||||
    'deleting all files recreates a default main.kcl with no code',
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    { tag: '@desktop' },
 | 
			
		||||
    async ({ page }, testInfo) => {
 | 
			
		||||
      const { panesOpen, pasteCodeInEditor, deleteFile, editorTextMatches } =
 | 
			
		||||
        await getUtils(page, test)
 | 
			
		||||
@ -291,7 +291,7 @@ test.describe('when using the file tree to', () => {
 | 
			
		||||
  test(
 | 
			
		||||
    'loading small file, then large, then back to small',
 | 
			
		||||
    {
 | 
			
		||||
      tag: '@electron',
 | 
			
		||||
      tag: '@desktop',
 | 
			
		||||
    },
 | 
			
		||||
    async ({ page }, testInfo) => {
 | 
			
		||||
      const {
 | 
			
		||||
@ -361,7 +361,7 @@ test.describe('when using the file tree to', () => {
 | 
			
		||||
test.describe('Renaming in the file tree', () => {
 | 
			
		||||
  test(
 | 
			
		||||
    'A file you have open',
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    { tag: '@desktop' },
 | 
			
		||||
    async ({ context, page }, testInfo) => {
 | 
			
		||||
      const { dir } = await context.folderSetupFn(async (dir) => {
 | 
			
		||||
        await fsp.mkdir(join(dir, 'Test Project'), { recursive: true })
 | 
			
		||||
@ -450,7 +450,7 @@ test.describe('Renaming in the file tree', () => {
 | 
			
		||||
 | 
			
		||||
  test(
 | 
			
		||||
    'A file you do not have open',
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    { tag: '@desktop' },
 | 
			
		||||
    async ({ context, page }, testInfo) => {
 | 
			
		||||
      const { dir } = await context.folderSetupFn(async (dir) => {
 | 
			
		||||
        await fsp.mkdir(join(dir, 'Test Project'), { recursive: true })
 | 
			
		||||
@ -536,7 +536,7 @@ test.describe('Renaming in the file tree', () => {
 | 
			
		||||
 | 
			
		||||
  test(
 | 
			
		||||
    `A folder you're not inside`,
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    { tag: '@desktop' },
 | 
			
		||||
    async ({ context, page }, testInfo) => {
 | 
			
		||||
      const { dir } = await context.folderSetupFn(async (dir) => {
 | 
			
		||||
        await fsp.mkdir(join(dir, 'Test Project'), { recursive: true })
 | 
			
		||||
@ -618,7 +618,7 @@ test.describe('Renaming in the file tree', () => {
 | 
			
		||||
 | 
			
		||||
  test(
 | 
			
		||||
    `A folder you are inside`,
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    { tag: '@desktop' },
 | 
			
		||||
    async ({ page, context }, testInfo) => {
 | 
			
		||||
      const { dir } = await context.folderSetupFn(async (dir) => {
 | 
			
		||||
        await fsp.mkdir(join(dir, 'Test Project'), { recursive: true })
 | 
			
		||||
@ -721,7 +721,7 @@ test.describe('Renaming in the file tree', () => {
 | 
			
		||||
test.describe('Deleting items from the file pane', () => {
 | 
			
		||||
  test(
 | 
			
		||||
    `delete file when main.kcl exists, navigate to main.kcl`,
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    { tag: '@desktop' },
 | 
			
		||||
    async ({ page, context }, testInfo) => {
 | 
			
		||||
      await context.folderSetupFn(async (dir) => {
 | 
			
		||||
        const testDir = join(dir, 'testProject')
 | 
			
		||||
@ -786,7 +786,7 @@ test.describe('Deleting items from the file pane', () => {
 | 
			
		||||
 | 
			
		||||
  test(
 | 
			
		||||
    `Delete folder we are not in, don't navigate`,
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    { tag: '@desktop' },
 | 
			
		||||
    async ({ context, page }, testInfo) => {
 | 
			
		||||
      await context.folderSetupFn(async (dir) => {
 | 
			
		||||
        await fsp.mkdir(join(dir, 'Test Project'), { recursive: true })
 | 
			
		||||
@ -840,7 +840,7 @@ test.describe('Deleting items from the file pane', () => {
 | 
			
		||||
 | 
			
		||||
  test(
 | 
			
		||||
    `Delete folder we are in, navigate to main.kcl`,
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    { tag: '@desktop' },
 | 
			
		||||
    async ({ context, page }, testInfo) => {
 | 
			
		||||
      await context.folderSetupFn(async (dir) => {
 | 
			
		||||
        await fsp.mkdir(join(dir, 'Test Project'), { recursive: true })
 | 
			
		||||
@ -906,7 +906,7 @@ test.describe('Deleting items from the file pane', () => {
 | 
			
		||||
  // Copied from tests above.
 | 
			
		||||
  test(
 | 
			
		||||
    `external deletion of project navigates back home`,
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    { tag: '@desktop' },
 | 
			
		||||
    async ({ context, page }, testInfo) => {
 | 
			
		||||
      const TEST_PROJECT_NAME = 'Test Project'
 | 
			
		||||
      const { dir: projectsDirName } = await context.folderSetupFn(
 | 
			
		||||
@ -970,7 +970,7 @@ test.describe('Deleting items from the file pane', () => {
 | 
			
		||||
  // Similar to the above
 | 
			
		||||
  test(
 | 
			
		||||
    `external deletion of file in sub-directory updates the file tree and recreates it on code editor typing`,
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    { tag: '@desktop' },
 | 
			
		||||
    async ({ context, page }, testInfo) => {
 | 
			
		||||
      const TEST_PROJECT_NAME = 'Test Project'
 | 
			
		||||
      const { dir: projectsDirName } = await context.folderSetupFn(
 | 
			
		||||
@ -1045,7 +1045,7 @@ test.describe('Deleting items from the file pane', () => {
 | 
			
		||||
test.describe('Undo and redo do not keep history when navigating between files', () => {
 | 
			
		||||
  test(
 | 
			
		||||
    `open a file, change something, open a different file, hitting undo should do nothing`,
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    { tag: '@desktop' },
 | 
			
		||||
    async ({ context, page }, testInfo) => {
 | 
			
		||||
      await context.folderSetupFn(async (dir) => {
 | 
			
		||||
        const testDir = join(dir, 'testProject')
 | 
			
		||||
@ -1112,7 +1112,7 @@ test.describe('Undo and redo do not keep history when navigating between files',
 | 
			
		||||
 | 
			
		||||
  test(
 | 
			
		||||
    `open a file, change something, undo it, open a different file, hitting redo should do nothing`,
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    { tag: '@desktop' },
 | 
			
		||||
    async ({ context, page }, testInfo) => {
 | 
			
		||||
      await context.folderSetupFn(async (dir) => {
 | 
			
		||||
        const testDir = join(dir, 'testProject')
 | 
			
		||||
@ -1212,7 +1212,7 @@ test.describe('Undo and redo do not keep history when navigating between files',
 | 
			
		||||
 | 
			
		||||
  test(
 | 
			
		||||
    `cloned file has an incremented name and same contents`,
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    { tag: '@desktop' },
 | 
			
		||||
    async ({ page, context, homePage }, testInfo) => {
 | 
			
		||||
      const { panesOpen, cloneFile } = await getUtils(page, test)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,7 @@ import * as fsp from 'fs/promises'
 | 
			
		||||
test.describe('Import UI tests', () => {
 | 
			
		||||
  test(
 | 
			
		||||
    'shows toast when trying to sketch on imported face, and hovering over imported geometry should NOT highlight any code',
 | 
			
		||||
    { tag: ['@electron', '@macos', '@windows'] },
 | 
			
		||||
    { tag: ['@desktop', '@macos', '@windows'] },
 | 
			
		||||
    async ({ context, page, homePage, toolbar, scene, editor, cmdBar }) => {
 | 
			
		||||
      await context.folderSetupFn(async (dir) => {
 | 
			
		||||
        const projectDir = path.join(dir, 'import-test')
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,7 @@ import { expect, test } from '@e2e/playwright/zoo-test'
 | 
			
		||||
 | 
			
		||||
test(
 | 
			
		||||
  'When machine-api server not found butt is disabled and shows the reason',
 | 
			
		||||
  { tag: '@electron' },
 | 
			
		||||
  { tag: '@desktop' },
 | 
			
		||||
  async ({ context, page, scene, cmdBar }, testInfo) => {
 | 
			
		||||
    await context.folderSetupFn(async (dir) => {
 | 
			
		||||
      const bracketDir = join(dir, 'bracket')
 | 
			
		||||
@ -43,7 +43,7 @@ test(
 | 
			
		||||
 | 
			
		||||
test(
 | 
			
		||||
  'When machine-api server not found home screen & project status shows the reason',
 | 
			
		||||
  { tag: '@electron' },
 | 
			
		||||
  { tag: '@desktop' },
 | 
			
		||||
  async ({ context, page, scene, cmdBar }, testInfo) => {
 | 
			
		||||
    await context.folderSetupFn(async (dir) => {
 | 
			
		||||
      const bracketDir = join(dir, 'bracket')
 | 
			
		||||
 | 
			
		||||
@ -13,7 +13,7 @@ import { expect, test } from '@e2e/playwright/zoo-test'
 | 
			
		||||
 */
 | 
			
		||||
test.describe(
 | 
			
		||||
  'Native file menu',
 | 
			
		||||
  { tag: ['@electron', '@macos', '@windows'] },
 | 
			
		||||
  { tag: ['@desktop', '@macos', '@windows'] },
 | 
			
		||||
  () => {
 | 
			
		||||
    test('Home page', async ({ tronApp, cmdBar, page, homePage }) => {
 | 
			
		||||
      if (!tronApp) fail()
 | 
			
		||||
 | 
			
		||||
@ -43,7 +43,7 @@ async function insertPartIntoAssembly(
 | 
			
		||||
test.describe('Point-and-click assemblies tests', () => {
 | 
			
		||||
  test(
 | 
			
		||||
    `Insert kcl parts into assembly as whole module import`,
 | 
			
		||||
    { tag: ['@electron', '@macos', '@windows'] },
 | 
			
		||||
    { tag: ['@desktop', '@macos', '@windows'] },
 | 
			
		||||
    async ({
 | 
			
		||||
      context,
 | 
			
		||||
      page,
 | 
			
		||||
@ -198,7 +198,7 @@ test.describe('Point-and-click assemblies tests', () => {
 | 
			
		||||
 | 
			
		||||
  test(
 | 
			
		||||
    `Can still translate, rotate, and delete inserted parts even with non standard code`,
 | 
			
		||||
    { tag: ['@electron', '@macos', '@windows'] },
 | 
			
		||||
    { tag: ['@desktop', '@macos', '@windows'] },
 | 
			
		||||
    async ({
 | 
			
		||||
      context,
 | 
			
		||||
      page,
 | 
			
		||||
@ -394,7 +394,7 @@ test.describe('Point-and-click assemblies tests', () => {
 | 
			
		||||
 | 
			
		||||
  test(
 | 
			
		||||
    `Insert the bracket part into an assembly and transform it`,
 | 
			
		||||
    { tag: ['@electron', '@macos', '@windows'] },
 | 
			
		||||
    { tag: ['@desktop', '@macos', '@windows'] },
 | 
			
		||||
    async ({
 | 
			
		||||
      context,
 | 
			
		||||
      page,
 | 
			
		||||
@ -585,7 +585,7 @@ test.describe('Point-and-click assemblies tests', () => {
 | 
			
		||||
 | 
			
		||||
  test(
 | 
			
		||||
    `Insert foreign parts into assembly and delete them`,
 | 
			
		||||
    { tag: ['@electron', '@macos', '@windows'] },
 | 
			
		||||
    { tag: ['@desktop', '@macos', '@windows'] },
 | 
			
		||||
    async ({
 | 
			
		||||
      context,
 | 
			
		||||
      page,
 | 
			
		||||
@ -736,7 +736,7 @@ test.describe('Point-and-click assemblies tests', () => {
 | 
			
		||||
 | 
			
		||||
  test(
 | 
			
		||||
    'Assembly gets reexecuted when imported models are updated externally',
 | 
			
		||||
    { tag: ['@electron', '@macos', '@windows'] },
 | 
			
		||||
    { tag: ['@desktop', '@macos', '@windows'] },
 | 
			
		||||
    async ({ context, page, homePage, scene, toolbar, cmdBar, tronApp }) => {
 | 
			
		||||
      if (!tronApp) {
 | 
			
		||||
        fail()
 | 
			
		||||
@ -826,7 +826,7 @@ foreign
 | 
			
		||||
 | 
			
		||||
  test(
 | 
			
		||||
    `Point-and-click clone`,
 | 
			
		||||
    { tag: ['@electron', '@macos', '@windows'] },
 | 
			
		||||
    { tag: ['@desktop', '@macos', '@windows'] },
 | 
			
		||||
    async ({
 | 
			
		||||
      context,
 | 
			
		||||
      page,
 | 
			
		||||
 | 
			
		||||
@ -17,7 +17,7 @@ import { expect, test } from '@e2e/playwright/zoo-test'
 | 
			
		||||
 | 
			
		||||
test(
 | 
			
		||||
  'projects reload if a new one is created, deleted, or renamed externally',
 | 
			
		||||
  { tag: ['@electron', '@macos', '@windows'] },
 | 
			
		||||
  { tag: ['@desktop', '@macos', '@windows'] },
 | 
			
		||||
  async ({ context, page }, testInfo) => {
 | 
			
		||||
    let externalCreatedProjectName = 'external-created-project'
 | 
			
		||||
 | 
			
		||||
@ -63,7 +63,7 @@ test(
 | 
			
		||||
 | 
			
		||||
test(
 | 
			
		||||
  'click help/keybindings from home page',
 | 
			
		||||
  { tag: '@electron' },
 | 
			
		||||
  { tag: '@desktop' },
 | 
			
		||||
  async ({ page }, testInfo) => {
 | 
			
		||||
    await page.setBodyDimensions({ width: 1200, height: 500 })
 | 
			
		||||
 | 
			
		||||
@ -81,7 +81,7 @@ test(
 | 
			
		||||
 | 
			
		||||
test(
 | 
			
		||||
  'click help/keybindings from project page',
 | 
			
		||||
  { tag: '@electron' },
 | 
			
		||||
  { tag: '@desktop' },
 | 
			
		||||
  async ({ scene, cmdBar, context, page }, testInfo) => {
 | 
			
		||||
    await context.folderSetupFn(async (dir) => {
 | 
			
		||||
      const bracketDir = path.join(dir, 'bracket')
 | 
			
		||||
@ -112,7 +112,7 @@ test(
 | 
			
		||||
 | 
			
		||||
test(
 | 
			
		||||
  'open a file in a project works and renders, open another file in different project with errors, it should clear the scene',
 | 
			
		||||
  { tag: '@electron' },
 | 
			
		||||
  { tag: '@desktop' },
 | 
			
		||||
  async ({ scene, cmdBar, context, page, editor }, testInfo) => {
 | 
			
		||||
    await context.folderSetupFn(async (dir) => {
 | 
			
		||||
      const bracketDir = path.join(dir, 'bracket')
 | 
			
		||||
@ -184,7 +184,7 @@ test(
 | 
			
		||||
 | 
			
		||||
test(
 | 
			
		||||
  'open a file in a project works and renders, open another file in different project that is empty, it should clear the scene',
 | 
			
		||||
  { tag: '@electron' },
 | 
			
		||||
  { tag: '@desktop' },
 | 
			
		||||
  async ({ scene, cmdBar, context, page }, testInfo) => {
 | 
			
		||||
    await context.folderSetupFn(async (dir) => {
 | 
			
		||||
      const bracketDir = path.join(dir, 'bracket')
 | 
			
		||||
@ -244,7 +244,7 @@ test(
 | 
			
		||||
 | 
			
		||||
test(
 | 
			
		||||
  'open a file in a project works and renders, open empty file, it should clear the scene',
 | 
			
		||||
  { tag: '@electron' },
 | 
			
		||||
  { tag: '@desktop' },
 | 
			
		||||
  async ({ context, page }, testInfo) => {
 | 
			
		||||
    await context.folderSetupFn(async (dir) => {
 | 
			
		||||
      const bracketDir = path.join(dir, 'bracket')
 | 
			
		||||
@ -310,7 +310,7 @@ test(
 | 
			
		||||
 | 
			
		||||
test(
 | 
			
		||||
  'open a file in a project works and renders, open another file in the same project with errors, it should clear the scene',
 | 
			
		||||
  { tag: '@electron' },
 | 
			
		||||
  { tag: '@desktop' },
 | 
			
		||||
  async ({ scene, cmdBar, context, page }, testInfo) => {
 | 
			
		||||
    await context.folderSetupFn(async (dir) => {
 | 
			
		||||
      const bracketDir = path.join(dir, 'bracket')
 | 
			
		||||
@ -382,7 +382,7 @@ test(
 | 
			
		||||
 | 
			
		||||
test(
 | 
			
		||||
  'when code with error first loads you get errors in console',
 | 
			
		||||
  { tag: '@electron' },
 | 
			
		||||
  { tag: '@desktop' },
 | 
			
		||||
  async ({ context, page, editor }, testInfo) => {
 | 
			
		||||
    await context.folderSetupFn(async (dir) => {
 | 
			
		||||
      await fsp.mkdir(path.join(dir, 'broken-code'), { recursive: true })
 | 
			
		||||
@ -416,7 +416,7 @@ test.describe('Can export from electron app', () => {
 | 
			
		||||
  for (const method of exportMethods) {
 | 
			
		||||
    test(
 | 
			
		||||
      `Can export using ${method}`,
 | 
			
		||||
      { tag: ['@electron', '@skipLocalEngine'] },
 | 
			
		||||
      { tag: ['@desktop', '@skipLocalEngine'] },
 | 
			
		||||
      async ({ scene, cmdBar, context, page, tronApp }, testInfo) => {
 | 
			
		||||
        if (!tronApp) {
 | 
			
		||||
          fail()
 | 
			
		||||
@ -507,7 +507,7 @@ test.describe('Can export from electron app', () => {
 | 
			
		||||
})
 | 
			
		||||
test(
 | 
			
		||||
  'Rename and delete projects, also spam arrow keys when renaming',
 | 
			
		||||
  { tag: '@electron' },
 | 
			
		||||
  { tag: '@desktop' },
 | 
			
		||||
  async ({ context, page }, testInfo) => {
 | 
			
		||||
    await context.folderSetupFn(async (dir) => {
 | 
			
		||||
      await fsp.mkdir(`${dir}/router-template-slate`, { recursive: true })
 | 
			
		||||
@ -723,7 +723,7 @@ test(
 | 
			
		||||
 | 
			
		||||
test(
 | 
			
		||||
  'pressing "delete" on home screen should do nothing',
 | 
			
		||||
  { tag: '@electron' },
 | 
			
		||||
  { tag: '@desktop' },
 | 
			
		||||
  async ({ context, page, homePage }, testInfo) => {
 | 
			
		||||
    await context.folderSetupFn(async (dir) => {
 | 
			
		||||
      await fsp.mkdir(`${dir}/router-template-slate`, { recursive: true })
 | 
			
		||||
@ -753,7 +753,7 @@ test(
 | 
			
		||||
test.describe(`Project management commands`, () => {
 | 
			
		||||
  test(
 | 
			
		||||
    `Rename from project page`,
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    { tag: '@desktop' },
 | 
			
		||||
    async ({ context, page, scene, cmdBar }, testInfo) => {
 | 
			
		||||
      const projectName = `my_project_to_rename`
 | 
			
		||||
      await context.folderSetupFn(async (dir) => {
 | 
			
		||||
@ -815,7 +815,7 @@ test.describe(`Project management commands`, () => {
 | 
			
		||||
 | 
			
		||||
  test(
 | 
			
		||||
    `Delete from project page`,
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    { tag: '@desktop' },
 | 
			
		||||
    async ({ context, page, scene, cmdBar }, testInfo) => {
 | 
			
		||||
      const projectName = `my_project_to_delete`
 | 
			
		||||
      await context.folderSetupFn(async (dir) => {
 | 
			
		||||
@ -869,7 +869,7 @@ test.describe(`Project management commands`, () => {
 | 
			
		||||
  )
 | 
			
		||||
  test(
 | 
			
		||||
    `Rename from home page`,
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    { tag: '@desktop' },
 | 
			
		||||
    async ({ context, page, homePage, scene, cmdBar }, testInfo) => {
 | 
			
		||||
      const projectName = `my_project_to_rename`
 | 
			
		||||
      await context.folderSetupFn(async (dir) => {
 | 
			
		||||
@ -927,7 +927,7 @@ test.describe(`Project management commands`, () => {
 | 
			
		||||
  )
 | 
			
		||||
  test(
 | 
			
		||||
    `Delete from home page`,
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    { tag: '@desktop' },
 | 
			
		||||
    async ({ context, page, scene, cmdBar }, testInfo) => {
 | 
			
		||||
      const projectName = `my_project_to_delete`
 | 
			
		||||
      await context.folderSetupFn(async (dir) => {
 | 
			
		||||
@ -1103,7 +1103,7 @@ test(`Create a few projects using the default project name`, async ({
 | 
			
		||||
 | 
			
		||||
test(
 | 
			
		||||
  'File in the file pane should open with a single click',
 | 
			
		||||
  { tag: '@electron' },
 | 
			
		||||
  { tag: '@desktop' },
 | 
			
		||||
  async ({ context, homePage, page }, testInfo) => {
 | 
			
		||||
    const projectName = 'router-template-slate'
 | 
			
		||||
    await context.folderSetupFn(async (dir) => {
 | 
			
		||||
@ -1145,7 +1145,7 @@ test(
 | 
			
		||||
 | 
			
		||||
test(
 | 
			
		||||
  'Nested directories in project without main.kcl do not create main.kcl',
 | 
			
		||||
  { tag: '@electron' },
 | 
			
		||||
  { tag: '@desktop' },
 | 
			
		||||
  async ({ scene, cmdBar, context, page }, testInfo) => {
 | 
			
		||||
    let testDir: string | undefined
 | 
			
		||||
    await context.folderSetupFn(async (dir) => {
 | 
			
		||||
@ -1202,7 +1202,7 @@ test(
 | 
			
		||||
 | 
			
		||||
test(
 | 
			
		||||
  'Deleting projects, can delete individual project, can still create projects after deleting all',
 | 
			
		||||
  { tag: '@electron' },
 | 
			
		||||
  { tag: '@desktop' },
 | 
			
		||||
  async ({ context, page }, testInfo) => {
 | 
			
		||||
    const projectData = [
 | 
			
		||||
      ['router-template-slate', 'cylinder.kcl'],
 | 
			
		||||
@ -1280,7 +1280,7 @@ test(
 | 
			
		||||
 | 
			
		||||
test(
 | 
			
		||||
  'Can load a file with CRLF line endings',
 | 
			
		||||
  { tag: '@electron' },
 | 
			
		||||
  { tag: '@desktop' },
 | 
			
		||||
  async ({ context, page, scene, cmdBar }, testInfo) => {
 | 
			
		||||
    await context.folderSetupFn(async (dir) => {
 | 
			
		||||
      const routerTemplateDir = path.join(dir, 'router-template-slate')
 | 
			
		||||
@ -1312,7 +1312,7 @@ test(
 | 
			
		||||
 | 
			
		||||
test(
 | 
			
		||||
  'Can sort projects on home page',
 | 
			
		||||
  { tag: '@electron' },
 | 
			
		||||
  { tag: '@desktop' },
 | 
			
		||||
  async ({ context, page }, testInfo) => {
 | 
			
		||||
    const projectData = [
 | 
			
		||||
      ['router-template-slate', 'cylinder.kcl'],
 | 
			
		||||
@ -1419,7 +1419,7 @@ test(
 | 
			
		||||
 | 
			
		||||
test(
 | 
			
		||||
  'When the project folder is empty, user can create new project and open it.',
 | 
			
		||||
  { tag: '@electron' },
 | 
			
		||||
  { tag: '@desktop' },
 | 
			
		||||
  async ({ page }, testInfo) => {
 | 
			
		||||
    const u = await getUtils(page)
 | 
			
		||||
    await page.setBodyDimensions({ width: 1200, height: 500 })
 | 
			
		||||
@ -1515,7 +1515,7 @@ extrude001 = extrude(sketch001, length = 200)`)
 | 
			
		||||
 | 
			
		||||
test(
 | 
			
		||||
  'Opening a project should successfully load the stream, (regression test that this also works when switching between projects)',
 | 
			
		||||
  { tag: '@electron' },
 | 
			
		||||
  { tag: '@desktop' },
 | 
			
		||||
  async ({ context, page, cmdBar, homePage, scene }, testInfo) => {
 | 
			
		||||
    await context.folderSetupFn(async (dir) => {
 | 
			
		||||
      await fsp.mkdir(path.join(dir, 'router-template-slate'), {
 | 
			
		||||
@ -1628,7 +1628,7 @@ test(
 | 
			
		||||
 | 
			
		||||
test(
 | 
			
		||||
  'You can change the root projects directory and nothing is lost',
 | 
			
		||||
  { tag: '@electron' },
 | 
			
		||||
  { tag: '@desktop' },
 | 
			
		||||
  async ({ context, page, tronApp, homePage }, testInfo) => {
 | 
			
		||||
    if (!tronApp) {
 | 
			
		||||
      fail()
 | 
			
		||||
@ -1735,7 +1735,7 @@ test(
 | 
			
		||||
 | 
			
		||||
test(
 | 
			
		||||
  'Search projects on desktop home',
 | 
			
		||||
  { tag: '@electron' },
 | 
			
		||||
  { tag: '@desktop' },
 | 
			
		||||
  async ({ context, page }, testInfo) => {
 | 
			
		||||
    const projectData = [
 | 
			
		||||
      ['basic bracket', 'focusrite_scarlett_mounting_bracket.kcl'],
 | 
			
		||||
@ -1791,7 +1791,7 @@ test(
 | 
			
		||||
 | 
			
		||||
test(
 | 
			
		||||
  'file pane is scrollable when there are many files',
 | 
			
		||||
  { tag: '@electron' },
 | 
			
		||||
  { tag: '@desktop' },
 | 
			
		||||
  async ({ scene, cmdBar, context, page }, testInfo) => {
 | 
			
		||||
    await context.folderSetupFn(async (dir) => {
 | 
			
		||||
      const testDir = path.join(dir, 'testProject')
 | 
			
		||||
@ -1892,7 +1892,7 @@ test(
 | 
			
		||||
 | 
			
		||||
test(
 | 
			
		||||
  'select all in code editor does not actually select all, just what is visible (regression)',
 | 
			
		||||
  { tag: '@electron' },
 | 
			
		||||
  { tag: '@desktop' },
 | 
			
		||||
  async ({ context, page }, testInfo) => {
 | 
			
		||||
    await context.folderSetupFn(async (dir) => {
 | 
			
		||||
      // rust/kcl-lib/e2e/executor/inputs/mike_stress_test.kcl
 | 
			
		||||
@ -1950,7 +1950,7 @@ test(
 | 
			
		||||
 | 
			
		||||
test(
 | 
			
		||||
  'Settings persist across restarts',
 | 
			
		||||
  { tag: '@electron' },
 | 
			
		||||
  { tag: '@desktop' },
 | 
			
		||||
  async ({ page, toolbar }, testInfo) => {
 | 
			
		||||
    await test.step('We can change a user setting like theme', async () => {
 | 
			
		||||
      await page.setBodyDimensions({ width: 1200, height: 500 })
 | 
			
		||||
@ -1982,7 +1982,7 @@ test(
 | 
			
		||||
 | 
			
		||||
test(
 | 
			
		||||
  'Original project name persist after onboarding',
 | 
			
		||||
  { tag: '@electron' },
 | 
			
		||||
  { tag: '@desktop' },
 | 
			
		||||
  async ({ page, toolbar }, testInfo) => {
 | 
			
		||||
    const nextButton = page.getByTestId('onboarding-next')
 | 
			
		||||
    await page.setBodyDimensions({ width: 1200, height: 500 })
 | 
			
		||||
@ -2022,7 +2022,7 @@ test(
 | 
			
		||||
 | 
			
		||||
test(
 | 
			
		||||
  'project name with foreign characters should open',
 | 
			
		||||
  { tag: '@electron' },
 | 
			
		||||
  { tag: '@desktop' },
 | 
			
		||||
  async ({ context, page }, testInfo) => {
 | 
			
		||||
    await context.folderSetupFn(async (dir) => {
 | 
			
		||||
      const bracketDir = path.join(dir, 'العربية')
 | 
			
		||||
@ -2067,7 +2067,7 @@ test(
 | 
			
		||||
 | 
			
		||||
test(
 | 
			
		||||
  'import from nested directory',
 | 
			
		||||
  { tag: ['@electron', '@windows', '@macos'] },
 | 
			
		||||
  { tag: ['@desktop', '@windows', '@macos'] },
 | 
			
		||||
  async ({ scene, cmdBar, context, page }) => {
 | 
			
		||||
    await context.folderSetupFn(async (dir) => {
 | 
			
		||||
      const bracketDir = path.join(dir, 'bracket')
 | 
			
		||||
 | 
			
		||||
@ -63,7 +63,7 @@ test.describe('edit with AI example snapshots', () => {
 | 
			
		||||
  test(
 | 
			
		||||
    `change colour`,
 | 
			
		||||
    // TODO this is more of a snapshot, but atm it needs to be manually run locally to update the files
 | 
			
		||||
    { tag: ['@electron'] },
 | 
			
		||||
    { tag: ['@desktop'] },
 | 
			
		||||
    async ({ context, homePage, cmdBar, editor, page, scene }) => {
 | 
			
		||||
      const project = 'test-dir'
 | 
			
		||||
      await context.folderSetupFn(async (dir) => {
 | 
			
		||||
 | 
			
		||||
@ -558,7 +558,7 @@ extrude002 = extrude(profile002, length = 150)
 | 
			
		||||
 | 
			
		||||
  test(
 | 
			
		||||
    `Network health indicator only appears in modeling view`,
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    { tag: '@desktop' },
 | 
			
		||||
    async ({ context, page }) => {
 | 
			
		||||
      await context.folderSetupFn(async (dir) => {
 | 
			
		||||
        const bracketDir = path.join(dir, 'bracket')
 | 
			
		||||
 | 
			
		||||
@ -279,7 +279,7 @@ profile001 = startProfile(sketch001, at = [12.34, -12.34])
 | 
			
		||||
 | 
			
		||||
  test(
 | 
			
		||||
    'Paused stream freezes view frame, unpause reconnect is seamless to user',
 | 
			
		||||
    { tag: ['@electron', '@skipLocalEngine'] },
 | 
			
		||||
    { tag: ['@desktop', '@skipLocalEngine'] },
 | 
			
		||||
    async ({ page, homePage, scene, cmdBar, toolbar, tronApp }) => {
 | 
			
		||||
      const networkToggle = page.getByTestId('network-toggle')
 | 
			
		||||
      const networkToggleConnectedText = page.getByText('Connected')
 | 
			
		||||
 | 
			
		||||
@ -1120,7 +1120,7 @@ part002 = startSketchOn(XZ)
 | 
			
		||||
test.describe('Electron constraint tests', () => {
 | 
			
		||||
  test(
 | 
			
		||||
    'Able to double click label to set constraint',
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    { tag: '@desktop' },
 | 
			
		||||
    async ({ page, context, homePage, scene, editor, toolbar, cmdBar }) => {
 | 
			
		||||
      await context.folderSetupFn(async (dir) => {
 | 
			
		||||
        const bracketDir = path.join(dir, 'test-sample')
 | 
			
		||||
 | 
			
		||||
@ -84,7 +84,7 @@ test.describe('Testing loading external models', () => {
 | 
			
		||||
   */
 | 
			
		||||
  test(
 | 
			
		||||
    'Desktop: should create new file by default, creates a second file with automatic unique name',
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    { tag: '@desktop' },
 | 
			
		||||
    async ({ editor, context, page, scene, cmdBar, toolbar }) => {
 | 
			
		||||
      if (runningOnWindows()) {
 | 
			
		||||
      }
 | 
			
		||||
@ -196,7 +196,7 @@ test.describe('Testing loading external models', () => {
 | 
			
		||||
  externalModelCases.map(({ modelName, deconflictedModelName, modelPath }) => {
 | 
			
		||||
    test(
 | 
			
		||||
      `Load external models from local drive - ${modelName}`,
 | 
			
		||||
      { tag: ['@electron'] },
 | 
			
		||||
      { tag: ['@desktop'] },
 | 
			
		||||
      async ({ page, homePage, scene, toolbar, cmdBar, tronApp }) => {
 | 
			
		||||
        if (!tronApp) {
 | 
			
		||||
          fail()
 | 
			
		||||
 | 
			
		||||
@ -278,7 +278,7 @@ test.describe(
 | 
			
		||||
 | 
			
		||||
    test(
 | 
			
		||||
      `Project settings override user settings on desktop`,
 | 
			
		||||
      { tag: ['@electron'] },
 | 
			
		||||
      { tag: ['@desktop'] },
 | 
			
		||||
      async ({ context, page }, testInfo) => {
 | 
			
		||||
        const projectName = 'bracket'
 | 
			
		||||
        const { dir: projectDirName } = await context.folderSetupFn(
 | 
			
		||||
@ -373,7 +373,7 @@ test.describe(
 | 
			
		||||
    test(
 | 
			
		||||
      `Load desktop app with no settings file`,
 | 
			
		||||
      {
 | 
			
		||||
        tag: '@electron',
 | 
			
		||||
        tag: '@desktop',
 | 
			
		||||
      },
 | 
			
		||||
      async ({ page }, testInfo) => {
 | 
			
		||||
        await page.setBodyDimensions({ width: 1200, height: 500 })
 | 
			
		||||
@ -393,7 +393,7 @@ test.describe(
 | 
			
		||||
    test(
 | 
			
		||||
      `Load desktop app with a settings file, but no project directory setting`,
 | 
			
		||||
      {
 | 
			
		||||
        tag: '@electron',
 | 
			
		||||
        tag: '@desktop',
 | 
			
		||||
      },
 | 
			
		||||
      async ({ context, page, tronApp }, testInfo) => {
 | 
			
		||||
        if (!tronApp) {
 | 
			
		||||
@ -425,7 +425,7 @@ test.describe(
 | 
			
		||||
    test(
 | 
			
		||||
      'user settings reload on external change, on project and modeling view',
 | 
			
		||||
      {
 | 
			
		||||
        tag: '@electron',
 | 
			
		||||
        tag: '@desktop',
 | 
			
		||||
      },
 | 
			
		||||
      async ({ context, page, tronApp }, testInfo) => {
 | 
			
		||||
        if (!tronApp) {
 | 
			
		||||
@ -486,7 +486,7 @@ test.describe(
 | 
			
		||||
 | 
			
		||||
    test(
 | 
			
		||||
      'project settings reload on external change',
 | 
			
		||||
      { tag: '@electron' },
 | 
			
		||||
      { tag: '@desktop' },
 | 
			
		||||
      async ({ context, page }, testInfo) => {
 | 
			
		||||
        const { dir: projectDirName } = await context.folderSetupFn(
 | 
			
		||||
          async () => {}
 | 
			
		||||
@ -536,7 +536,7 @@ test.describe(
 | 
			
		||||
 | 
			
		||||
    test(
 | 
			
		||||
      `Closing settings modal should go back to the original file being viewed`,
 | 
			
		||||
      { tag: '@electron' },
 | 
			
		||||
      { tag: '@desktop' },
 | 
			
		||||
      async ({ context, page }, testInfo) => {
 | 
			
		||||
        await context.folderSetupFn(async (dir) => {
 | 
			
		||||
          const bracketDir = join(dir, 'project-000')
 | 
			
		||||
 | 
			
		||||
@ -640,7 +640,7 @@ test.describe('Mocked Text-to-CAD API tests', { tag: ['@skipWin'] }, () => {
 | 
			
		||||
 | 
			
		||||
  test(
 | 
			
		||||
    'Home Page -> Text To CAD -> New Project -> Stay in home page -> Reject -> Project should be deleted',
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    { tag: '@desktop' },
 | 
			
		||||
    async ({ context, page }, testInfo) => {
 | 
			
		||||
      const projectName = 'my-project-name'
 | 
			
		||||
      const prompt = '2x2x2 cube'
 | 
			
		||||
@ -678,7 +678,7 @@ test.describe('Mocked Text-to-CAD API tests', { tag: ['@skipWin'] }, () => {
 | 
			
		||||
 | 
			
		||||
  test(
 | 
			
		||||
    'Home Page -> Text To CAD -> New Project -> Stay in home page -> Accept -> should navigate to file',
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    { tag: '@desktop' },
 | 
			
		||||
    async ({ context, page }, testInfo) => {
 | 
			
		||||
      const u = await getUtils(page)
 | 
			
		||||
      const projectName = 'my-project-name'
 | 
			
		||||
@ -724,7 +724,7 @@ test.describe('Mocked Text-to-CAD API tests', { tag: ['@skipWin'] }, () => {
 | 
			
		||||
 | 
			
		||||
  test(
 | 
			
		||||
    'Home Page -> Text To CAD -> Existing Project -> Stay in home page -> Reject -> should delete single file',
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    { tag: '@desktop' },
 | 
			
		||||
    async ({ homePage, page }, testInfo) => {
 | 
			
		||||
      const projectName = 'my-project-name'
 | 
			
		||||
      const prompt = '2x2x2 cube'
 | 
			
		||||
@ -767,7 +767,7 @@ test.describe('Mocked Text-to-CAD API tests', { tag: ['@skipWin'] }, () => {
 | 
			
		||||
 | 
			
		||||
  test(
 | 
			
		||||
    'Home Page -> Text To CAD -> Existing Project -> Stay in home page -> Accept -> should navigate to file',
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    { tag: '@desktop' },
 | 
			
		||||
    async ({ homePage, page }, testInfo) => {
 | 
			
		||||
      const u = await getUtils(page)
 | 
			
		||||
      const projectName = 'my-project-name'
 | 
			
		||||
@ -818,7 +818,7 @@ test.describe('Mocked Text-to-CAD API tests', { tag: ['@skipWin'] }, () => {
 | 
			
		||||
 | 
			
		||||
  test(
 | 
			
		||||
    'Home Page -> Text To CAD -> New Project -> Navigate to the project -> Reject -> should go to home page',
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    { tag: '@desktop' },
 | 
			
		||||
    async ({ homePage, page }, testInfo) => {
 | 
			
		||||
      const projectName = 'my-project-name'
 | 
			
		||||
      const prompt = '2x2x2 cube'
 | 
			
		||||
@ -864,7 +864,7 @@ test.describe('Mocked Text-to-CAD API tests', { tag: ['@skipWin'] }, () => {
 | 
			
		||||
 | 
			
		||||
  test(
 | 
			
		||||
    'Home Page -> Text To CAD -> New Project -> Navigate to the project -> Accept -> should stay in same file',
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    { tag: '@desktop' },
 | 
			
		||||
    async ({ homePage, page }, testInfo) => {
 | 
			
		||||
      const projectName = 'my-project-name'
 | 
			
		||||
      const prompt = '2x2x2 cube'
 | 
			
		||||
@ -907,7 +907,7 @@ test.describe('Mocked Text-to-CAD API tests', { tag: ['@skipWin'] }, () => {
 | 
			
		||||
 | 
			
		||||
  test(
 | 
			
		||||
    'Home Page -> Text To CAD -> Existing Project -> Navigate to the project -> Reject -> should load main.kcl',
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    { tag: '@desktop' },
 | 
			
		||||
    async ({ homePage, page }, testInfo) => {
 | 
			
		||||
      const u = await getUtils(page)
 | 
			
		||||
      const projectName = 'my-project-name'
 | 
			
		||||
@ -962,7 +962,7 @@ test.describe('Mocked Text-to-CAD API tests', { tag: ['@skipWin'] }, () => {
 | 
			
		||||
 | 
			
		||||
  test(
 | 
			
		||||
    'Home Page -> Text To CAD -> Existing Project -> Navigate to the project -> Accept -> should load 2x2x2-cube.kcl',
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    { tag: '@desktop' },
 | 
			
		||||
    async ({ homePage, page }, testInfo) => {
 | 
			
		||||
      const u = await getUtils(page)
 | 
			
		||||
      const projectName = 'my-project-name'
 | 
			
		||||
@ -1019,7 +1019,7 @@ test.describe('Mocked Text-to-CAD API tests', { tag: ['@skipWin'] }, () => {
 | 
			
		||||
 | 
			
		||||
  test(
 | 
			
		||||
    'Home Page -> Text To CAD -> New Project -> Navigate to different project -> Reject -> should stay in project',
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    { tag: '@desktop' },
 | 
			
		||||
    async ({ homePage, page }, testInfo) => {
 | 
			
		||||
      const u = await getUtils(page)
 | 
			
		||||
      const projectName = 'my-project-name'
 | 
			
		||||
@ -1096,7 +1096,7 @@ test.describe('Mocked Text-to-CAD API tests', { tag: ['@skipWin'] }, () => {
 | 
			
		||||
 | 
			
		||||
  test(
 | 
			
		||||
    'Home Page -> Text To CAD -> New Project -> Navigate to different project -> Accept -> should go to new project',
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    { tag: '@desktop' },
 | 
			
		||||
    async ({ page, homePage }, testInfo) => {
 | 
			
		||||
      const u = await getUtils(page)
 | 
			
		||||
      const projectName = 'my-project-name'
 | 
			
		||||
@ -1165,7 +1165,7 @@ test.describe('Mocked Text-to-CAD API tests', { tag: ['@skipWin'] }, () => {
 | 
			
		||||
 | 
			
		||||
  test(
 | 
			
		||||
    'Home Page -> Text To CAD -> Existing Project -> Navigate to different project -> Reject -> should stay in same project',
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    { tag: '@desktop' },
 | 
			
		||||
    async ({ page, homePage }, testInfo) => {
 | 
			
		||||
      const u = await getUtils(page)
 | 
			
		||||
      const projectName = 'my-project-name'
 | 
			
		||||
@ -1240,7 +1240,7 @@ test.describe('Mocked Text-to-CAD API tests', { tag: ['@skipWin'] }, () => {
 | 
			
		||||
 | 
			
		||||
  test(
 | 
			
		||||
    'Home Page -> Text To CAD -> Existing Project -> Navigate to different project -> Accept -> should navigate to new project',
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    { tag: '@desktop' },
 | 
			
		||||
    async ({ page, homePage }, testInfo) => {
 | 
			
		||||
      const u = await getUtils(page)
 | 
			
		||||
      const projectName = 'my-project-name'
 | 
			
		||||
 | 
			
		||||
@ -133,9 +133,9 @@
 | 
			
		||||
    "test:unit": "vitest run --mode development --exclude **/jest-component-unit-tests/*",
 | 
			
		||||
    "test:unit:components": "jest -c jest-component-unit-tests/jest.config.ts --rootDir jest-component-unit-tests/",
 | 
			
		||||
    "test:e2e:web": "cross-env TARGET=web NODE_ENV=development playwright test --config=playwright.config.ts --project=\"Google Chrome\" --grep=@web",
 | 
			
		||||
    "test:playwright:electron": "playwright test --config=playwright.electron.config.ts --grep-invert=@snapshot",
 | 
			
		||||
    "test:playwright:electron:local": "npm run tronb:vite:dev && playwright test --config=playwright.electron.config.ts --grep-invert=@snapshot --grep-invert=\"$(curl --silent https://test-analysis-bot.hawk-dinosaur.ts.net/projects/KittyCAD/modeling-app/tests/disabled/regex)\"",
 | 
			
		||||
    "test:playwright:electron:local-engine": "npm run tronb:vite:dev && playwright test --config=playwright.electron.config.ts --grep-invert='@snapshot|@skipLocalEngine' --grep-invert=\"$(curl --silent https://test-analysis-bot.hawk-dinosaur.ts.net/projects/KittyCAD/modeling-app/tests/disabled/regex)\"",
 | 
			
		||||
    "test:e2e:desktop": "cross-env TARGET=desktop playwright test --config=playwright.electron.config.ts --grep-invert=@snapshot",
 | 
			
		||||
    "test:e2e:desktop:local": "cross-env TARGET=desktop npm run tronb:vite:dev && playwright test --config=playwright.electron.config.ts --grep-invert=@snapshot --grep-invert=\"$(curl --silent https://test-analysis-bot.hawk-dinosaur.ts.net/projects/KittyCAD/modeling-app/tests/disabled/regex)\"",
 | 
			
		||||
    "test:e2e:desktop:local-engine": "cross-env TARGET=desktop npm run tronb:vite:dev && playwright test --config=playwright.electron.config.ts --grep-invert='@snapshot|@skipLocalEngine' --grep-invert=\"$(curl --silent https://test-analysis-bot.hawk-dinosaur.ts.net/projects/KittyCAD/modeling-app/tests/disabled/regex)\"",
 | 
			
		||||
    "test:unit:local": "npm run simpleserver:bg && npm run test:unit; kill-port 3000"
 | 
			
		||||
  },
 | 
			
		||||
  "browserslist": {
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user