* Fix: creating a dir ending with .kcl panics the app Fixes #7082 * Update snapshots * Update snapshots * Add test * tag: ['@electron', '@macos', '@windows'] --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
		
			
				
	
	
		
			1265 lines
		
	
	
		
			44 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			1265 lines
		
	
	
		
			44 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
import * as fs from 'fs'
 | 
						|
import { join } from 'path'
 | 
						|
import { FILE_EXT } from '@src/lib/constants'
 | 
						|
import * as fsp from 'fs/promises'
 | 
						|
 | 
						|
import {
 | 
						|
  createProject,
 | 
						|
  executorInputPath,
 | 
						|
  getUtils,
 | 
						|
} from '@e2e/playwright/test-utils'
 | 
						|
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' },
 | 
						|
    async ({ page, context, homePage, scene, editor, toolbar, cmdBar }) => {
 | 
						|
      await context.folderSetupFn(async (dir) => {
 | 
						|
        const bracketDir = join(dir, 'test-sample')
 | 
						|
        await fsp.mkdir(bracketDir, { recursive: true })
 | 
						|
        await fsp.copyFile(
 | 
						|
          executorInputPath('e2e-can-sketch-on-chamfer.kcl'),
 | 
						|
          join(bracketDir, 'main.kcl')
 | 
						|
        )
 | 
						|
      })
 | 
						|
 | 
						|
      const [clickObj] = scene.makeMouseHelpers(726, 272)
 | 
						|
 | 
						|
      await test.step('setup test', async () => {
 | 
						|
        await homePage.expectState({
 | 
						|
          projectCards: [
 | 
						|
            {
 | 
						|
              title: 'test-sample',
 | 
						|
              fileCount: 1,
 | 
						|
            },
 | 
						|
          ],
 | 
						|
          sortBy: 'last-modified-desc',
 | 
						|
        })
 | 
						|
        await homePage.openProject('test-sample')
 | 
						|
      })
 | 
						|
      await test.step('enter sketch mode', async () => {
 | 
						|
        await scene.connectionEstablished()
 | 
						|
        await scene.settled(cmdBar)
 | 
						|
        await clickObj()
 | 
						|
        await page.waitForTimeout(1000)
 | 
						|
        await scene.moveNoWhere()
 | 
						|
        await editor.expectState({
 | 
						|
          activeLines: [
 | 
						|
            '|>startProfile(at=[75.8,317.2])//[$startCapTag,$EndCapTag]',
 | 
						|
          ],
 | 
						|
          highlightedCode: '',
 | 
						|
          diagnostics: [],
 | 
						|
        })
 | 
						|
        await toolbar.editSketch()
 | 
						|
        await expect(toolbar.exitSketchBtn).toBeVisible()
 | 
						|
      })
 | 
						|
 | 
						|
      const fileName = 'Untitled.kcl'
 | 
						|
      await test.step('check sketch mode is exited when creating new file', async () => {
 | 
						|
        await toolbar.fileTreeBtn.click()
 | 
						|
        await toolbar.expectFileTreeState(['main.kcl'])
 | 
						|
 | 
						|
        await toolbar.createFile({ fileName, waitForToastToDisappear: true })
 | 
						|
 | 
						|
        // check we're out of sketch mode
 | 
						|
        await expect(toolbar.exitSketchBtn).not.toBeVisible()
 | 
						|
        await expect(toolbar.startSketchBtn).toBeVisible()
 | 
						|
      })
 | 
						|
      await test.step('setup for next assertion', async () => {
 | 
						|
        await toolbar.openFile('main.kcl')
 | 
						|
        await page.waitForTimeout(2000)
 | 
						|
        await clickObj()
 | 
						|
        await page.waitForTimeout(1000)
 | 
						|
        await scene.moveNoWhere()
 | 
						|
        await page.waitForTimeout(1000)
 | 
						|
        await editor.expectState({
 | 
						|
          activeLines: [
 | 
						|
            '|>startProfile(at=[75.8,317.2])//[$startCapTag,$EndCapTag]',
 | 
						|
          ],
 | 
						|
          highlightedCode: '',
 | 
						|
          diagnostics: [],
 | 
						|
        })
 | 
						|
        await toolbar.editSketch()
 | 
						|
        await expect(toolbar.exitSketchBtn).toBeVisible()
 | 
						|
        await toolbar.expectFileTreeState(['main.kcl', fileName])
 | 
						|
      })
 | 
						|
      await test.step('check sketch mode is exited when opening a different file', async () => {
 | 
						|
        await toolbar.openFile(fileName)
 | 
						|
 | 
						|
        // check we're out of sketch mode
 | 
						|
        await expect(toolbar.exitSketchBtn).not.toBeVisible()
 | 
						|
        await expect(toolbar.startSketchBtn).toBeVisible()
 | 
						|
      })
 | 
						|
    }
 | 
						|
  )
 | 
						|
})
 | 
						|
test.describe('when using the file tree to', () => {
 | 
						|
  const fromFile = 'main.kcl'
 | 
						|
  const toFile = 'hello.kcl'
 | 
						|
 | 
						|
  test(
 | 
						|
    `rename ${fromFile} to ${toFile}, and doesn't crash on reload and settings load`,
 | 
						|
    { tag: '@electron' },
 | 
						|
    async ({ page }, testInfo) => {
 | 
						|
      const { panesOpen, pasteCodeInEditor, renameFile, editorTextMatches } =
 | 
						|
        await getUtils(page, test)
 | 
						|
 | 
						|
      await page.setBodyDimensions({ width: 1200, height: 500 })
 | 
						|
      page.on('console', console.log)
 | 
						|
 | 
						|
      await panesOpen(['files', 'code'])
 | 
						|
 | 
						|
      await createProject({ name: 'project-000', page })
 | 
						|
 | 
						|
      // File the main.kcl with contents
 | 
						|
      const kclCube = await fsp.readFile(
 | 
						|
        'rust/kcl-lib/e2e/executor/inputs/cube.kcl',
 | 
						|
        'utf-8'
 | 
						|
      )
 | 
						|
      await pasteCodeInEditor(kclCube)
 | 
						|
 | 
						|
      // TODO: We have a timeout of 1s between edits to write to disk. If you reload the page too quickly it won't write to disk.
 | 
						|
      await page.waitForTimeout(2000)
 | 
						|
 | 
						|
      await renameFile(fromFile, toFile)
 | 
						|
      await page.reload()
 | 
						|
 | 
						|
      await test.step('Postcondition: editor has same content as before the rename', async () => {
 | 
						|
        await editorTextMatches(kclCube)
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Postcondition: opening and closing settings works', async () => {
 | 
						|
        const settingsOpenButton = page.getByRole('link', {
 | 
						|
          name: 'settings Settings',
 | 
						|
        })
 | 
						|
        const settingsCloseButton = page.getByTestId('settings-close-button')
 | 
						|
        await settingsOpenButton.click()
 | 
						|
        await settingsCloseButton.click()
 | 
						|
      })
 | 
						|
    }
 | 
						|
  )
 | 
						|
 | 
						|
  test(
 | 
						|
    `create many new files of the same name, incrementing their names`,
 | 
						|
    { tag: '@electron' },
 | 
						|
    async ({ page }, testInfo) => {
 | 
						|
      const { panesOpen, createNewFile } = await getUtils(page, test)
 | 
						|
 | 
						|
      await page.setBodyDimensions({ width: 1200, height: 500 })
 | 
						|
      page.on('console', console.log)
 | 
						|
 | 
						|
      await panesOpen(['files'])
 | 
						|
 | 
						|
      await createProject({ name: 'project-000', page })
 | 
						|
 | 
						|
      await createNewFile('lee')
 | 
						|
      await createNewFile('lee')
 | 
						|
      await createNewFile('lee')
 | 
						|
      await createNewFile('lee')
 | 
						|
      await createNewFile('lee')
 | 
						|
 | 
						|
      await test.step('Postcondition: there are 5 new lee-*.kcl files', async () => {
 | 
						|
        await expect
 | 
						|
          .poll(() =>
 | 
						|
            page
 | 
						|
              .locator('[data-testid="file-pane-scroll-container"] button')
 | 
						|
              .filter({ hasText: /lee[-]?[0-5]?/ })
 | 
						|
              .count()
 | 
						|
          )
 | 
						|
          .toEqual(5)
 | 
						|
      })
 | 
						|
    }
 | 
						|
  )
 | 
						|
 | 
						|
  test(
 | 
						|
    'create a new file with the same name as an existing file cancels the operation',
 | 
						|
    { tag: '@electron' },
 | 
						|
    async ({ context, page, homePage, scene, editor, toolbar }, testInfo) => {
 | 
						|
      const projectName = 'cube'
 | 
						|
      const mainFile = 'main.kcl'
 | 
						|
      const secondFile = 'cylinder.kcl'
 | 
						|
      const kclCube = await fsp.readFile(executorInputPath('cube.kcl'), 'utf-8')
 | 
						|
      const kclCylinder = await fsp.readFile(
 | 
						|
        executorInputPath('cylinder.kcl'),
 | 
						|
        'utf-8'
 | 
						|
      )
 | 
						|
 | 
						|
      await context.folderSetupFn(async (dir) => {
 | 
						|
        const cubeDir = join(dir, projectName)
 | 
						|
        await fsp.mkdir(cubeDir, { recursive: true })
 | 
						|
        await fsp.copyFile(
 | 
						|
          executorInputPath('cube.kcl'),
 | 
						|
          join(cubeDir, mainFile)
 | 
						|
        )
 | 
						|
        await fsp.copyFile(
 | 
						|
          executorInputPath('cylinder.kcl'),
 | 
						|
          join(cubeDir, secondFile)
 | 
						|
        )
 | 
						|
      })
 | 
						|
 | 
						|
      const {
 | 
						|
        openFilePanel,
 | 
						|
        renameFile,
 | 
						|
        selectFile,
 | 
						|
        editorTextMatches,
 | 
						|
        waitForPageLoad,
 | 
						|
      } = await getUtils(page, test)
 | 
						|
 | 
						|
      await test.step(`Setup: Open project and navigate to ${secondFile}`, async () => {
 | 
						|
        await homePage.expectState({
 | 
						|
          projectCards: [
 | 
						|
            {
 | 
						|
              title: projectName,
 | 
						|
              fileCount: 2,
 | 
						|
            },
 | 
						|
          ],
 | 
						|
          sortBy: 'last-modified-desc',
 | 
						|
        })
 | 
						|
        await homePage.openProject(projectName)
 | 
						|
        await waitForPageLoad()
 | 
						|
        await openFilePanel()
 | 
						|
        await selectFile(secondFile)
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step(`Attempt to rename ${secondFile} to ${mainFile}`, async () => {
 | 
						|
        await renameFile(secondFile, mainFile)
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step(`Postcondition: ${mainFile} still has the original content`, async () => {
 | 
						|
        await selectFile(mainFile)
 | 
						|
        await editorTextMatches(kclCube)
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step(`Postcondition: ${secondFile} still exists with the original content`, async () => {
 | 
						|
        await selectFile(secondFile)
 | 
						|
        await editorTextMatches(kclCylinder)
 | 
						|
      })
 | 
						|
    }
 | 
						|
  )
 | 
						|
 | 
						|
  test(
 | 
						|
    `create new folders and that doesn't trigger a navigation`,
 | 
						|
    { tag: ['@electron', '@macos', '@windows'] },
 | 
						|
    async ({ page, homePage, scene, toolbar, cmdBar }) => {
 | 
						|
      await homePage.goToModelingScene()
 | 
						|
      await scene.settled(cmdBar)
 | 
						|
      await toolbar.openPane('files')
 | 
						|
      const { createNewFolder } = await getUtils(page, test)
 | 
						|
 | 
						|
      await createNewFolder('folder')
 | 
						|
 | 
						|
      await createNewFolder('folder.kcl')
 | 
						|
 | 
						|
      await test.step(`Postcondition: folders are created and we didn't navigate`, async () => {
 | 
						|
        await toolbar.expectFileTreeState(['folder', 'folder.kcl', 'main.kcl'])
 | 
						|
        await expect(toolbar.fileName).toHaveText('main.kcl')
 | 
						|
      })
 | 
						|
    }
 | 
						|
  )
 | 
						|
 | 
						|
  test(
 | 
						|
    'deleting all files recreates a default main.kcl with no code',
 | 
						|
    { tag: '@electron' },
 | 
						|
    async ({ page }, testInfo) => {
 | 
						|
      const { panesOpen, pasteCodeInEditor, deleteFile, editorTextMatches } =
 | 
						|
        await getUtils(page, test)
 | 
						|
 | 
						|
      await page.setBodyDimensions({ width: 1200, height: 500 })
 | 
						|
      page.on('console', console.log)
 | 
						|
 | 
						|
      await panesOpen(['files', 'code'])
 | 
						|
 | 
						|
      await createProject({ name: 'project-000', page })
 | 
						|
      // File the main.kcl with contents
 | 
						|
      const kclCube = await fsp.readFile(
 | 
						|
        'rust/kcl-lib/e2e/executor/inputs/cube.kcl',
 | 
						|
        'utf-8'
 | 
						|
      )
 | 
						|
      await pasteCodeInEditor(kclCube)
 | 
						|
 | 
						|
      const mainFile = 'main.kcl'
 | 
						|
 | 
						|
      await deleteFile(mainFile)
 | 
						|
 | 
						|
      await test.step(`Postcondition: ${mainFile} is recreated but has no content`, async () => {
 | 
						|
        await editorTextMatches('')
 | 
						|
      })
 | 
						|
    }
 | 
						|
  )
 | 
						|
 | 
						|
  test(
 | 
						|
    'loading small file, then large, then back to small',
 | 
						|
    {
 | 
						|
      tag: '@electron',
 | 
						|
    },
 | 
						|
    async ({ page }, testInfo) => {
 | 
						|
      const {
 | 
						|
        panesOpen,
 | 
						|
        pasteCodeInEditor,
 | 
						|
        createNewFile,
 | 
						|
        openDebugPanel,
 | 
						|
        closeDebugPanel,
 | 
						|
        expectCmdLog,
 | 
						|
      } = await getUtils(page, test)
 | 
						|
 | 
						|
      await page.setViewportSize({ width: 1200, height: 500 })
 | 
						|
      page.on('console', console.log)
 | 
						|
 | 
						|
      await panesOpen(['files', 'code'])
 | 
						|
      await createProject({ name: 'project-000', page })
 | 
						|
 | 
						|
      // Create a small file
 | 
						|
      const kclCube = await fsp.readFile(
 | 
						|
        'rust/kcl-lib/e2e/executor/inputs/cube.kcl',
 | 
						|
        'utf-8'
 | 
						|
      )
 | 
						|
      // pasted into main.kcl
 | 
						|
      await pasteCodeInEditor(kclCube)
 | 
						|
 | 
						|
      // Create a large lego file
 | 
						|
      await createNewFile('lego')
 | 
						|
      const legoFile = page.getByRole('listitem').filter({
 | 
						|
        has: page.getByRole('button', { name: 'lego.kcl' }),
 | 
						|
      })
 | 
						|
      await expect(legoFile).toBeVisible({ timeout: 60_000 })
 | 
						|
      await legoFile.click()
 | 
						|
      const kclLego = await fsp.readFile(
 | 
						|
        'rust/kcl-lib/e2e/executor/inputs/lego.kcl',
 | 
						|
        'utf-8'
 | 
						|
      )
 | 
						|
      await pasteCodeInEditor(kclLego)
 | 
						|
      const mainFile = page.getByRole('listitem').filter({
 | 
						|
        has: page.getByRole('button', { name: 'main.kcl' }),
 | 
						|
      })
 | 
						|
 | 
						|
      // Open settings and enable the debug panel
 | 
						|
      await page
 | 
						|
        .getByRole('link', {
 | 
						|
          name: 'settings Settings',
 | 
						|
        })
 | 
						|
        .click()
 | 
						|
      await page.locator('#showDebugPanel').getByText('OffOn').click()
 | 
						|
      await page.getByTestId('settings-close-button').click()
 | 
						|
 | 
						|
      await test.step('swap between small and large files', async () => {
 | 
						|
        await openDebugPanel()
 | 
						|
        // Previously created a file so we need to start back at main.kcl
 | 
						|
        await mainFile.click()
 | 
						|
        await expectCmdLog('[data-message-type="execution-done"]', 60_000)
 | 
						|
        // Click the large file
 | 
						|
        await legoFile.click()
 | 
						|
        // Once it is building, click back to the smaller file
 | 
						|
        await mainFile.click()
 | 
						|
        await expectCmdLog('[data-message-type="execution-done"]', 60_000)
 | 
						|
        await closeDebugPanel()
 | 
						|
      })
 | 
						|
    }
 | 
						|
  )
 | 
						|
})
 | 
						|
 | 
						|
test.describe('Renaming in the file tree', () => {
 | 
						|
  test(
 | 
						|
    'A file you have open',
 | 
						|
    { tag: '@electron' },
 | 
						|
    async ({ context, page }, testInfo) => {
 | 
						|
      const { dir } = await context.folderSetupFn(async (dir) => {
 | 
						|
        await fsp.mkdir(join(dir, 'Test Project'), { recursive: true })
 | 
						|
        await fsp.copyFile(
 | 
						|
          executorInputPath('basic_fillet_cube_end.kcl'),
 | 
						|
          join(dir, 'Test Project', 'main.kcl')
 | 
						|
        )
 | 
						|
        await fsp.copyFile(
 | 
						|
          executorInputPath('cylinder.kcl'),
 | 
						|
          join(dir, 'Test Project', 'fileToRename.kcl')
 | 
						|
        )
 | 
						|
      })
 | 
						|
      const u = await getUtils(page)
 | 
						|
      await page.setViewportSize({ width: 1200, height: 500 })
 | 
						|
      page.on('console', console.log)
 | 
						|
 | 
						|
      // Constants and locators
 | 
						|
      const projectLink = page.getByText('Test Project')
 | 
						|
      const projectMenuButton = page.getByTestId('project-sidebar-toggle')
 | 
						|
      const checkUnRenamedFS = () => {
 | 
						|
        const filePath = join(dir, 'Test Project', 'fileToRename.kcl')
 | 
						|
        return fs.existsSync(filePath)
 | 
						|
      }
 | 
						|
      const newFileName = 'newFileName'
 | 
						|
      const checkRenamedFS = () => {
 | 
						|
        const filePath = join(dir, 'Test Project', `${newFileName}.kcl`)
 | 
						|
        return fs.existsSync(filePath)
 | 
						|
      }
 | 
						|
 | 
						|
      const fileToRename = page
 | 
						|
        .getByRole('listitem')
 | 
						|
        .filter({ has: page.getByRole('button', { name: 'fileToRename.kcl' }) })
 | 
						|
      const renamedFile = page
 | 
						|
        .getByRole('listitem')
 | 
						|
        .filter({ has: page.getByRole('button', { name: 'newFileName.kcl' }) })
 | 
						|
      const renameMenuItem = page.getByRole('button', { name: 'Rename' })
 | 
						|
      const renameInput = page.getByPlaceholder('fileToRename.kcl')
 | 
						|
      const codeLocator = page.locator('.cm-content')
 | 
						|
 | 
						|
      await test.step('Open project and file pane', async () => {
 | 
						|
        await expect(projectLink).toBeVisible()
 | 
						|
        await projectLink.click()
 | 
						|
        await expect(projectMenuButton).toBeVisible()
 | 
						|
        await expect(projectMenuButton).toContainText('main.kcl')
 | 
						|
 | 
						|
        await u.openFilePanel()
 | 
						|
        await expect(fileToRename).toBeVisible()
 | 
						|
        expect(checkUnRenamedFS()).toBeTruthy()
 | 
						|
        expect(checkRenamedFS()).toBeFalsy()
 | 
						|
        await fileToRename.click()
 | 
						|
        await expect(projectMenuButton).toContainText('fileToRename.kcl')
 | 
						|
        await u.openKclCodePanel()
 | 
						|
        await expect(codeLocator).toContainText('circle(')
 | 
						|
        await u.closeKclCodePanel()
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Rename the file', async () => {
 | 
						|
        await fileToRename.click({ button: 'right' })
 | 
						|
        await renameMenuItem.click()
 | 
						|
        await expect(renameInput).toBeVisible()
 | 
						|
        await renameInput.fill(newFileName)
 | 
						|
        await page.keyboard.press('Enter')
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Verify the file is renamed', async () => {
 | 
						|
        await expect(fileToRename).not.toBeAttached()
 | 
						|
        await expect(renamedFile).toBeVisible()
 | 
						|
        expect(checkUnRenamedFS()).toBeFalsy()
 | 
						|
        expect(checkRenamedFS()).toBeTruthy()
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Verify we navigated', async () => {
 | 
						|
        await expect(projectMenuButton).toContainText(newFileName + FILE_EXT)
 | 
						|
        const url = page.url()
 | 
						|
        expect(url).toContain(newFileName)
 | 
						|
        await expect(projectMenuButton).not.toContainText('fileToRename.kcl')
 | 
						|
        await expect(projectMenuButton).not.toContainText('main.kcl')
 | 
						|
        expect(url).not.toContain('fileToRename.kcl')
 | 
						|
        expect(url).not.toContain('main.kcl')
 | 
						|
 | 
						|
        await u.openKclCodePanel()
 | 
						|
        await expect(codeLocator).toContainText('circle(')
 | 
						|
      })
 | 
						|
    }
 | 
						|
  )
 | 
						|
 | 
						|
  test(
 | 
						|
    'A file you do not have open',
 | 
						|
    { tag: '@electron' },
 | 
						|
    async ({ context, page }, testInfo) => {
 | 
						|
      const { dir } = await context.folderSetupFn(async (dir) => {
 | 
						|
        await fsp.mkdir(join(dir, 'Test Project'), { recursive: true })
 | 
						|
        await fsp.copyFile(
 | 
						|
          executorInputPath('basic_fillet_cube_end.kcl'),
 | 
						|
          join(dir, 'Test Project', 'main.kcl')
 | 
						|
        )
 | 
						|
        await fsp.copyFile(
 | 
						|
          executorInputPath('cylinder.kcl'),
 | 
						|
          join(dir, 'Test Project', 'fileToRename.kcl')
 | 
						|
        )
 | 
						|
      })
 | 
						|
      const u = await getUtils(page)
 | 
						|
      await page.setViewportSize({ width: 1200, height: 500 })
 | 
						|
      page.on('console', console.log)
 | 
						|
 | 
						|
      // Constants and locators
 | 
						|
      const newFileName = 'newFileName'
 | 
						|
      const checkUnRenamedFS = () => {
 | 
						|
        const filePath = join(dir, 'Test Project', 'fileToRename.kcl')
 | 
						|
        return fs.existsSync(filePath)
 | 
						|
      }
 | 
						|
      const checkRenamedFS = () => {
 | 
						|
        const filePath = join(dir, 'Test Project', `${newFileName}.kcl`)
 | 
						|
        return fs.existsSync(filePath)
 | 
						|
      }
 | 
						|
      const projectLink = page.getByText('Test Project')
 | 
						|
      const projectMenuButton = page.getByTestId('project-sidebar-toggle')
 | 
						|
      const fileToRename = page
 | 
						|
        .getByRole('listitem')
 | 
						|
        .filter({ has: page.getByRole('button', { name: 'fileToRename.kcl' }) })
 | 
						|
      const renamedFile = page.getByRole('listitem').filter({
 | 
						|
        has: page.getByRole('button', { name: newFileName + FILE_EXT }),
 | 
						|
      })
 | 
						|
      const renameMenuItem = page.getByRole('button', { name: 'Rename' })
 | 
						|
      const renameInput = page.getByPlaceholder('fileToRename.kcl')
 | 
						|
      const codeLocator = page.locator('.cm-content')
 | 
						|
 | 
						|
      await test.step('Open project and file pane', async () => {
 | 
						|
        await expect(projectLink).toBeVisible()
 | 
						|
        await projectLink.click()
 | 
						|
        await expect(projectMenuButton).toBeVisible()
 | 
						|
        await expect(projectMenuButton).toContainText('main.kcl')
 | 
						|
 | 
						|
        await u.openFilePanel()
 | 
						|
        await expect(fileToRename).toBeVisible()
 | 
						|
        expect(checkUnRenamedFS()).toBeTruthy()
 | 
						|
        expect(checkRenamedFS()).toBeFalsy()
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Rename the file', async () => {
 | 
						|
        await fileToRename.click({ button: 'right' })
 | 
						|
        await renameMenuItem.click()
 | 
						|
        await expect(renameInput).toBeVisible()
 | 
						|
        await renameInput.fill(newFileName)
 | 
						|
        await page.keyboard.press('Enter')
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Verify the file is renamed', async () => {
 | 
						|
        await expect(fileToRename).not.toBeAttached()
 | 
						|
        await expect(renamedFile).toBeVisible()
 | 
						|
        expect(checkUnRenamedFS()).toBeFalsy()
 | 
						|
        expect(checkRenamedFS()).toBeTruthy()
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Verify we have not navigated', async () => {
 | 
						|
        await expect(projectMenuButton).toContainText('main.kcl')
 | 
						|
        await expect(projectMenuButton).not.toContainText(
 | 
						|
          newFileName + FILE_EXT
 | 
						|
        )
 | 
						|
        await expect(projectMenuButton).not.toContainText('fileToRename.kcl')
 | 
						|
 | 
						|
        const url = page.url()
 | 
						|
        expect(url).toContain('main.kcl')
 | 
						|
        expect(url).not.toContain(newFileName)
 | 
						|
        expect(url).not.toContain('fileToRename.kcl')
 | 
						|
 | 
						|
        await u.openKclCodePanel()
 | 
						|
        await expect(codeLocator).toContainText('fillet(')
 | 
						|
      })
 | 
						|
    }
 | 
						|
  )
 | 
						|
 | 
						|
  test(
 | 
						|
    `A folder you're not inside`,
 | 
						|
    { tag: '@electron' },
 | 
						|
    async ({ context, page }, testInfo) => {
 | 
						|
      const { dir } = await context.folderSetupFn(async (dir) => {
 | 
						|
        await fsp.mkdir(join(dir, 'Test Project'), { recursive: true })
 | 
						|
        await fsp.mkdir(join(dir, 'Test Project', 'folderToRename'), {
 | 
						|
          recursive: true,
 | 
						|
        })
 | 
						|
        await fsp.copyFile(
 | 
						|
          executorInputPath('basic_fillet_cube_end.kcl'),
 | 
						|
          join(dir, 'Test Project', 'main.kcl')
 | 
						|
        )
 | 
						|
        await fsp.copyFile(
 | 
						|
          executorInputPath('cylinder.kcl'),
 | 
						|
          join(dir, 'Test Project', 'folderToRename', 'someFileWithin.kcl')
 | 
						|
        )
 | 
						|
      })
 | 
						|
 | 
						|
      const u = await getUtils(page)
 | 
						|
      await page.setViewportSize({ width: 1200, height: 500 })
 | 
						|
      page.on('console', console.log)
 | 
						|
 | 
						|
      // Constants and locators
 | 
						|
      const projectLink = page.getByText('Test Project')
 | 
						|
      const projectMenuButton = page.getByTestId('project-sidebar-toggle')
 | 
						|
      const folderToRename = page.getByRole('button', {
 | 
						|
        name: 'folderToRename',
 | 
						|
      })
 | 
						|
      const renamedFolder = page.getByRole('button', { name: 'newFolderName' })
 | 
						|
      const renameMenuItem = page.getByRole('button', { name: 'Rename' })
 | 
						|
      const originalFolderName = 'folderToRename'
 | 
						|
      const renameInput = page.getByPlaceholder(originalFolderName)
 | 
						|
      const newFolderName = 'newFolderName'
 | 
						|
      const checkUnRenamedFolderFS = () => {
 | 
						|
        const folderPath = join(dir, 'Test Project', originalFolderName)
 | 
						|
        return fs.existsSync(folderPath)
 | 
						|
      }
 | 
						|
      const checkRenamedFolderFS = () => {
 | 
						|
        const folderPath = join(dir, 'Test Project', newFolderName)
 | 
						|
        return fs.existsSync(folderPath)
 | 
						|
      }
 | 
						|
 | 
						|
      await test.step('Open project and file pane', async () => {
 | 
						|
        await expect(projectLink).toBeVisible()
 | 
						|
        await projectLink.click()
 | 
						|
        await expect(projectMenuButton).toBeVisible()
 | 
						|
        await expect(projectMenuButton).toContainText('main.kcl')
 | 
						|
 | 
						|
        const url = page.url()
 | 
						|
        expect(url).toContain('main.kcl')
 | 
						|
        expect(url).not.toContain('folderToRename')
 | 
						|
 | 
						|
        await u.openFilePanel()
 | 
						|
        await expect(folderToRename).toBeVisible()
 | 
						|
        expect(checkUnRenamedFolderFS()).toBeTruthy()
 | 
						|
        expect(checkRenamedFolderFS()).toBeFalsy()
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Rename the folder', async () => {
 | 
						|
        await folderToRename.click({ button: 'right' })
 | 
						|
        await expect(renameMenuItem).toBeVisible()
 | 
						|
        await renameMenuItem.click()
 | 
						|
        await expect(renameInput).toBeVisible()
 | 
						|
        await renameInput.fill(newFolderName)
 | 
						|
        await page.keyboard.press('Enter')
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Verify the folder is renamed, and no navigation occurred', async () => {
 | 
						|
        const url = page.url()
 | 
						|
        expect(url).toContain('main.kcl')
 | 
						|
        expect(url).not.toContain('folderToRename')
 | 
						|
 | 
						|
        await expect(projectMenuButton).toContainText('main.kcl')
 | 
						|
        await expect(renamedFolder).toBeVisible()
 | 
						|
        await expect(folderToRename).not.toBeAttached()
 | 
						|
        expect(checkUnRenamedFolderFS()).toBeFalsy()
 | 
						|
        expect(checkRenamedFolderFS()).toBeTruthy()
 | 
						|
      })
 | 
						|
    }
 | 
						|
  )
 | 
						|
 | 
						|
  test(
 | 
						|
    `A folder you are inside`,
 | 
						|
    { tag: '@electron' },
 | 
						|
    async ({ page, context }, testInfo) => {
 | 
						|
      const { dir } = await context.folderSetupFn(async (dir) => {
 | 
						|
        await fsp.mkdir(join(dir, 'Test Project'), { recursive: true })
 | 
						|
        await fsp.mkdir(join(dir, 'Test Project', 'folderToRename'), {
 | 
						|
          recursive: true,
 | 
						|
        })
 | 
						|
        await fsp.copyFile(
 | 
						|
          executorInputPath('basic_fillet_cube_end.kcl'),
 | 
						|
          join(dir, 'Test Project', 'main.kcl')
 | 
						|
        )
 | 
						|
        await fsp.copyFile(
 | 
						|
          executorInputPath('cylinder.kcl'),
 | 
						|
          join(dir, 'Test Project', 'folderToRename', 'someFileWithin.kcl')
 | 
						|
        )
 | 
						|
      })
 | 
						|
 | 
						|
      const u = await getUtils(page)
 | 
						|
      await page.setViewportSize({ width: 1200, height: 500 })
 | 
						|
      page.on('console', console.log)
 | 
						|
 | 
						|
      // Constants and locators
 | 
						|
      const projectLink = page.getByText('Test Project')
 | 
						|
      const projectMenuButton = page.getByTestId('project-sidebar-toggle')
 | 
						|
      const folderToRename = page.getByRole('button', {
 | 
						|
        name: 'folderToRename',
 | 
						|
      })
 | 
						|
      const renamedFolder = page.getByRole('button', { name: 'newFolderName' })
 | 
						|
      const fileWithinFolder = page.getByRole('listitem').filter({
 | 
						|
        has: page.getByRole('button', { name: 'someFileWithin.kcl' }),
 | 
						|
      })
 | 
						|
      const renameMenuItem = page.getByRole('button', { name: 'Rename' })
 | 
						|
      const originalFolderName = 'folderToRename'
 | 
						|
      const renameInput = page.getByPlaceholder(originalFolderName)
 | 
						|
      const newFolderName = 'newFolderName'
 | 
						|
      const checkUnRenamedFolderFS = () => {
 | 
						|
        const folderPath = join(dir, 'Test Project', originalFolderName)
 | 
						|
        return fs.existsSync(folderPath)
 | 
						|
      }
 | 
						|
      const checkRenamedFolderFS = () => {
 | 
						|
        const folderPath = join(dir, 'Test Project', newFolderName)
 | 
						|
        return fs.existsSync(folderPath)
 | 
						|
      }
 | 
						|
 | 
						|
      await test.step('Open project and navigate into folder', async () => {
 | 
						|
        await expect(projectLink).toBeVisible()
 | 
						|
        await projectLink.click()
 | 
						|
        await expect(projectMenuButton).toBeVisible()
 | 
						|
        await expect(projectMenuButton).toContainText('main.kcl')
 | 
						|
 | 
						|
        const url = page.url()
 | 
						|
        expect(url).toContain('main.kcl')
 | 
						|
        expect(url).not.toContain('folderToRename')
 | 
						|
 | 
						|
        await u.openFilePanel()
 | 
						|
        await expect(folderToRename).toBeVisible()
 | 
						|
        await folderToRename.click()
 | 
						|
        await expect(fileWithinFolder).toBeVisible()
 | 
						|
        await fileWithinFolder.click()
 | 
						|
 | 
						|
        await expect(projectMenuButton).toContainText('someFileWithin.kcl')
 | 
						|
        const newUrl = page.url()
 | 
						|
        expect(newUrl).toContain('folderToRename')
 | 
						|
        expect(newUrl).toContain('someFileWithin.kcl')
 | 
						|
        expect(newUrl).not.toContain('main.kcl')
 | 
						|
        expect(checkUnRenamedFolderFS()).toBeTruthy()
 | 
						|
        expect(checkRenamedFolderFS()).toBeFalsy()
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Rename the folder', async () => {
 | 
						|
        await page.waitForTimeout(1000)
 | 
						|
        await folderToRename.click({ button: 'right' })
 | 
						|
        await expect(renameMenuItem).toBeVisible()
 | 
						|
        await renameMenuItem.click()
 | 
						|
        await expect(renameInput).toBeVisible()
 | 
						|
        await renameInput.fill(newFolderName)
 | 
						|
        await page.keyboard.press('Enter')
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Verify the folder is renamed, and navigated to new path', async () => {
 | 
						|
        const urlSnippet = encodeURIComponent(
 | 
						|
          join(newFolderName, 'someFileWithin.kcl')
 | 
						|
        )
 | 
						|
        await page.waitForURL(new RegExp(urlSnippet))
 | 
						|
        await expect(projectMenuButton).toContainText('someFileWithin.kcl')
 | 
						|
        await expect(renamedFolder).toBeVisible()
 | 
						|
        await expect(folderToRename).not.toBeAttached()
 | 
						|
 | 
						|
        // URL is synchronous, so we check the other stuff first
 | 
						|
        const url = page.url()
 | 
						|
        expect(url).not.toContain('main.kcl')
 | 
						|
        expect(url).toContain(newFolderName)
 | 
						|
        expect(url).toContain('someFileWithin.kcl')
 | 
						|
        expect(checkUnRenamedFolderFS()).toBeFalsy()
 | 
						|
        expect(checkRenamedFolderFS()).toBeTruthy()
 | 
						|
      })
 | 
						|
    }
 | 
						|
  )
 | 
						|
})
 | 
						|
 | 
						|
test.describe('Deleting items from the file pane', () => {
 | 
						|
  test(
 | 
						|
    `delete file when main.kcl exists, navigate to main.kcl`,
 | 
						|
    { tag: '@electron' },
 | 
						|
    async ({ page, context }, testInfo) => {
 | 
						|
      await context.folderSetupFn(async (dir) => {
 | 
						|
        const testDir = join(dir, 'testProject')
 | 
						|
        await fsp.mkdir(testDir, { recursive: true })
 | 
						|
        await fsp.copyFile(
 | 
						|
          executorInputPath('cylinder.kcl'),
 | 
						|
          join(testDir, 'main.kcl')
 | 
						|
        )
 | 
						|
        await fsp.copyFile(
 | 
						|
          executorInputPath('basic_fillet_cube_end.kcl'),
 | 
						|
          join(testDir, 'fileToDelete.kcl')
 | 
						|
        )
 | 
						|
      })
 | 
						|
      const u = await getUtils(page)
 | 
						|
      await page.setViewportSize({ width: 1200, height: 500 })
 | 
						|
      page.on('console', console.log)
 | 
						|
 | 
						|
      // Constants and locators
 | 
						|
      const projectCard = page.getByText('testProject')
 | 
						|
      const projectMenuButton = page.getByTestId('project-sidebar-toggle')
 | 
						|
      const fileToDelete = page
 | 
						|
        .getByRole('listitem')
 | 
						|
        .filter({ has: page.getByRole('button', { name: 'fileToDelete.kcl' }) })
 | 
						|
      const deleteMenuItem = page.getByRole('button', { name: 'Delete' })
 | 
						|
      const deleteConfirmation = page.getByTestId('delete-confirmation')
 | 
						|
 | 
						|
      await test.step('Open project and navigate to fileToDelete.kcl', async () => {
 | 
						|
        await projectCard.click()
 | 
						|
        await u.waitForPageLoad()
 | 
						|
        await u.openFilePanel()
 | 
						|
 | 
						|
        await fileToDelete.click()
 | 
						|
        await u.waitForPageLoad()
 | 
						|
        await u.openKclCodePanel()
 | 
						|
        await expect(u.codeLocator).toContainText('getOppositeEdge(thing)')
 | 
						|
        await u.closeKclCodePanel()
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Delete fileToDelete.kcl', async () => {
 | 
						|
        await fileToDelete.click({ button: 'right' })
 | 
						|
        await expect(deleteMenuItem).toBeVisible()
 | 
						|
        await deleteMenuItem.click()
 | 
						|
        await expect(deleteConfirmation).toBeVisible()
 | 
						|
        await deleteConfirmation.click()
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Check deletion and navigation', async () => {
 | 
						|
        await u.waitForPageLoad()
 | 
						|
        await expect(fileToDelete).not.toBeVisible()
 | 
						|
        await u.closeFilePanel()
 | 
						|
        await u.openKclCodePanel()
 | 
						|
        await expect(u.codeLocator).toContainText('circle(')
 | 
						|
        await expect(projectMenuButton).toContainText('main.kcl')
 | 
						|
      })
 | 
						|
    }
 | 
						|
  )
 | 
						|
 | 
						|
  test.fixme(
 | 
						|
    'TODO - delete file we have open when main.kcl does not exist',
 | 
						|
    async () => {}
 | 
						|
  )
 | 
						|
 | 
						|
  test(
 | 
						|
    `Delete folder we are not in, don't navigate`,
 | 
						|
    { tag: '@electron' },
 | 
						|
    async ({ context, page }, testInfo) => {
 | 
						|
      await context.folderSetupFn(async (dir) => {
 | 
						|
        await fsp.mkdir(join(dir, 'Test Project'), { recursive: true })
 | 
						|
        await fsp.mkdir(join(dir, 'Test Project', 'folderToDelete'), {
 | 
						|
          recursive: true,
 | 
						|
        })
 | 
						|
        await fsp.copyFile(
 | 
						|
          executorInputPath('basic_fillet_cube_end.kcl'),
 | 
						|
          join(dir, 'Test Project', 'main.kcl')
 | 
						|
        )
 | 
						|
        await fsp.copyFile(
 | 
						|
          executorInputPath('cylinder.kcl'),
 | 
						|
          join(dir, 'Test Project', 'folderToDelete', 'someFileWithin.kcl')
 | 
						|
        )
 | 
						|
      })
 | 
						|
      const u = await getUtils(page)
 | 
						|
      await page.setViewportSize({ width: 1200, height: 500 })
 | 
						|
      page.on('console', console.log)
 | 
						|
 | 
						|
      // Constants and locators
 | 
						|
      const projectCard = page.getByText('Test Project')
 | 
						|
      const projectMenuButton = page.getByTestId('project-sidebar-toggle')
 | 
						|
      const folderToDelete = page.getByRole('button', {
 | 
						|
        name: 'folderToDelete',
 | 
						|
      })
 | 
						|
      const deleteMenuItem = page.getByRole('button', { name: 'Delete' })
 | 
						|
      const deleteConfirmation = page.getByTestId('delete-confirmation')
 | 
						|
 | 
						|
      await test.step('Open project and open project pane', async () => {
 | 
						|
        await projectCard.click()
 | 
						|
        await u.waitForPageLoad()
 | 
						|
        await expect(projectMenuButton).toContainText('main.kcl')
 | 
						|
        await u.closeKclCodePanel()
 | 
						|
        await u.openFilePanel()
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Delete folderToDelete', async () => {
 | 
						|
        await folderToDelete.click({ button: 'right' })
 | 
						|
        await expect(deleteMenuItem).toBeVisible()
 | 
						|
        await deleteMenuItem.click()
 | 
						|
        await expect(deleteConfirmation).toBeVisible()
 | 
						|
        await deleteConfirmation.click()
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Check deletion and no navigation', async () => {
 | 
						|
        await expect(folderToDelete).not.toBeAttached()
 | 
						|
        await expect(projectMenuButton).toContainText('main.kcl')
 | 
						|
      })
 | 
						|
    }
 | 
						|
  )
 | 
						|
 | 
						|
  test(
 | 
						|
    `Delete folder we are in, navigate to main.kcl`,
 | 
						|
    { tag: '@electron' },
 | 
						|
    async ({ context, page }, testInfo) => {
 | 
						|
      await context.folderSetupFn(async (dir) => {
 | 
						|
        await fsp.mkdir(join(dir, 'Test Project'), { recursive: true })
 | 
						|
        await fsp.mkdir(join(dir, 'Test Project', 'folderToDelete'), {
 | 
						|
          recursive: true,
 | 
						|
        })
 | 
						|
        await fsp.copyFile(
 | 
						|
          executorInputPath('basic_fillet_cube_end.kcl'),
 | 
						|
          join(dir, 'Test Project', 'main.kcl')
 | 
						|
        )
 | 
						|
        await fsp.copyFile(
 | 
						|
          executorInputPath('cylinder.kcl'),
 | 
						|
          join(dir, 'Test Project', 'folderToDelete', 'someFileWithin.kcl')
 | 
						|
        )
 | 
						|
      })
 | 
						|
      const u = await getUtils(page)
 | 
						|
      await page.setViewportSize({ width: 1200, height: 500 })
 | 
						|
      page.on('console', console.log)
 | 
						|
 | 
						|
      // Constants and locators
 | 
						|
      const projectCard = page.getByText('Test Project')
 | 
						|
      const projectMenuButton = page.getByTestId('project-sidebar-toggle')
 | 
						|
      const folderToDelete = page.getByRole('button', {
 | 
						|
        name: 'folderToDelete',
 | 
						|
      })
 | 
						|
      const fileWithinFolder = page.getByRole('listitem').filter({
 | 
						|
        has: page.getByRole('button', { name: 'someFileWithin.kcl' }),
 | 
						|
      })
 | 
						|
      const deleteMenuItem = page.getByRole('button', { name: 'Delete' })
 | 
						|
      const deleteConfirmation = page.getByTestId('delete-confirmation')
 | 
						|
 | 
						|
      await test.step('Open project and navigate into folderToDelete', async () => {
 | 
						|
        await projectCard.click()
 | 
						|
        await u.waitForPageLoad()
 | 
						|
        await expect(projectMenuButton).toContainText('main.kcl')
 | 
						|
        await u.closeKclCodePanel()
 | 
						|
        await u.openFilePanel()
 | 
						|
 | 
						|
        await folderToDelete.click()
 | 
						|
        await expect(fileWithinFolder).toBeVisible()
 | 
						|
        await fileWithinFolder.click()
 | 
						|
        await expect(projectMenuButton).toContainText('someFileWithin.kcl')
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Delete folderToDelete', async () => {
 | 
						|
        await folderToDelete.click({ button: 'right' })
 | 
						|
        await expect(deleteMenuItem).toBeVisible()
 | 
						|
        await deleteMenuItem.click()
 | 
						|
        await expect(deleteConfirmation).toBeVisible()
 | 
						|
        await deleteConfirmation.click()
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Check deletion and navigation to main.kcl', async () => {
 | 
						|
        await expect(folderToDelete).not.toBeAttached()
 | 
						|
        await expect(fileWithinFolder).not.toBeAttached()
 | 
						|
        await expect(projectMenuButton).toContainText('main.kcl')
 | 
						|
      })
 | 
						|
    }
 | 
						|
  )
 | 
						|
 | 
						|
  test.fixme('TODO - delete folder we are in, with no main.kcl', async () => {})
 | 
						|
 | 
						|
  // Copied from tests above.
 | 
						|
  test(
 | 
						|
    `external deletion of project navigates back home`,
 | 
						|
    { tag: '@electron' },
 | 
						|
    async ({ context, page }, testInfo) => {
 | 
						|
      const TEST_PROJECT_NAME = 'Test Project'
 | 
						|
      const { dir: projectsDirName } = await context.folderSetupFn(
 | 
						|
        async (dir) => {
 | 
						|
          await fsp.mkdir(join(dir, TEST_PROJECT_NAME), { recursive: true })
 | 
						|
          await fsp.mkdir(join(dir, TEST_PROJECT_NAME, 'folderToDelete'), {
 | 
						|
            recursive: true,
 | 
						|
          })
 | 
						|
          await fsp.copyFile(
 | 
						|
            executorInputPath('basic_fillet_cube_end.kcl'),
 | 
						|
            join(dir, TEST_PROJECT_NAME, 'main.kcl')
 | 
						|
          )
 | 
						|
          await fsp.copyFile(
 | 
						|
            executorInputPath('cylinder.kcl'),
 | 
						|
            join(dir, TEST_PROJECT_NAME, 'folderToDelete', 'someFileWithin.kcl')
 | 
						|
          )
 | 
						|
        }
 | 
						|
      )
 | 
						|
      const u = await getUtils(page)
 | 
						|
      await page.setViewportSize({ width: 1200, height: 500 })
 | 
						|
 | 
						|
      // Constants and locators
 | 
						|
      const projectCard = page.getByText(TEST_PROJECT_NAME)
 | 
						|
      const projectMenuButton = page.getByTestId('project-sidebar-toggle')
 | 
						|
      const folderToDelete = page.getByRole('button', {
 | 
						|
        name: 'folderToDelete',
 | 
						|
      })
 | 
						|
      const fileWithinFolder = page.getByRole('listitem').filter({
 | 
						|
        has: page.getByRole('button', { name: 'someFileWithin.kcl' }),
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Open project and navigate into folderToDelete', async () => {
 | 
						|
        await projectCard.click()
 | 
						|
        await u.waitForPageLoad()
 | 
						|
        await expect(projectMenuButton).toContainText('main.kcl')
 | 
						|
        await u.closeKclCodePanel()
 | 
						|
        await u.openFilePanel()
 | 
						|
 | 
						|
        await folderToDelete.click()
 | 
						|
        await expect(fileWithinFolder).toBeVisible()
 | 
						|
        await fileWithinFolder.click()
 | 
						|
        await expect(projectMenuButton).toContainText('someFileWithin.kcl')
 | 
						|
      })
 | 
						|
 | 
						|
      // Point of divergence. Delete the project folder and see if it goes back
 | 
						|
      // to the home view.
 | 
						|
      await test.step('Delete projectsDirName/<project-name> externally', async () => {
 | 
						|
        await fsp.rm(join(projectsDirName, TEST_PROJECT_NAME), {
 | 
						|
          recursive: true,
 | 
						|
          force: true,
 | 
						|
        })
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Check the app is back on the home view', async () => {
 | 
						|
        const projectsDirLink = page.getByText('Loaded from')
 | 
						|
        await expect(projectsDirLink).toBeVisible()
 | 
						|
      })
 | 
						|
    }
 | 
						|
  )
 | 
						|
 | 
						|
  // 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' },
 | 
						|
    async ({ context, page }, testInfo) => {
 | 
						|
      const TEST_PROJECT_NAME = 'Test Project'
 | 
						|
      const { dir: projectsDirName } = await context.folderSetupFn(
 | 
						|
        async (dir) => {
 | 
						|
          await fsp.mkdir(join(dir, TEST_PROJECT_NAME), { recursive: true })
 | 
						|
          await fsp.mkdir(join(dir, TEST_PROJECT_NAME, 'folderToDelete'), {
 | 
						|
            recursive: true,
 | 
						|
          })
 | 
						|
          await fsp.copyFile(
 | 
						|
            executorInputPath('basic_fillet_cube_end.kcl'),
 | 
						|
            join(dir, TEST_PROJECT_NAME, 'main.kcl')
 | 
						|
          )
 | 
						|
          await fsp.copyFile(
 | 
						|
            executorInputPath('cylinder.kcl'),
 | 
						|
            join(dir, TEST_PROJECT_NAME, 'folderToDelete', 'someFileWithin.kcl')
 | 
						|
          )
 | 
						|
        }
 | 
						|
      )
 | 
						|
      const u = await getUtils(page)
 | 
						|
      await page.setViewportSize({ width: 1200, height: 500 })
 | 
						|
 | 
						|
      // Constants and locators
 | 
						|
      const projectCard = page.getByText(TEST_PROJECT_NAME)
 | 
						|
      const projectMenuButton = page.getByTestId('project-sidebar-toggle')
 | 
						|
      const folderToDelete = page.getByRole('button', {
 | 
						|
        name: 'folderToDelete',
 | 
						|
      })
 | 
						|
      const fileWithinFolder = page.getByRole('listitem').filter({
 | 
						|
        has: page.getByRole('button', { name: 'someFileWithin.kcl' }),
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Open project and navigate into folderToDelete', async () => {
 | 
						|
        await projectCard.click()
 | 
						|
        await u.waitForPageLoad()
 | 
						|
        await expect(projectMenuButton).toContainText('main.kcl')
 | 
						|
 | 
						|
        await u.openFilePanel()
 | 
						|
 | 
						|
        await folderToDelete.click()
 | 
						|
        await expect(fileWithinFolder).toBeVisible()
 | 
						|
        await fileWithinFolder.click()
 | 
						|
        await expect(projectMenuButton).toContainText('someFileWithin.kcl')
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Delete projectsDirName/<project-name> externally', async () => {
 | 
						|
        await fsp.rm(
 | 
						|
          join(
 | 
						|
            projectsDirName,
 | 
						|
            TEST_PROJECT_NAME,
 | 
						|
            'folderToDelete',
 | 
						|
            'someFileWithin.kcl'
 | 
						|
          )
 | 
						|
        )
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Check the file is gone in the file tree', async () => {
 | 
						|
        await expect(
 | 
						|
          page.getByTestId('file-pane-scroll-container')
 | 
						|
        ).not.toContainText('someFileWithin.kcl')
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Check the file is back in the file tree after typing in code editor', async () => {
 | 
						|
        await u.pasteCodeInEditor('hello = 1')
 | 
						|
        await expect(
 | 
						|
          page.getByTestId('file-pane-scroll-container')
 | 
						|
        ).toContainText('someFileWithin.kcl')
 | 
						|
      })
 | 
						|
    }
 | 
						|
  )
 | 
						|
})
 | 
						|
 | 
						|
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' },
 | 
						|
    async ({ context, page }, testInfo) => {
 | 
						|
      await context.folderSetupFn(async (dir) => {
 | 
						|
        const testDir = join(dir, 'testProject')
 | 
						|
        await fsp.mkdir(testDir, { recursive: true })
 | 
						|
        await fsp.copyFile(
 | 
						|
          executorInputPath('cylinder.kcl'),
 | 
						|
          join(testDir, 'main.kcl')
 | 
						|
        )
 | 
						|
        await fsp.copyFile(
 | 
						|
          executorInputPath('basic_fillet_cube_end.kcl'),
 | 
						|
          join(testDir, 'other.kcl')
 | 
						|
        )
 | 
						|
      })
 | 
						|
      const u = await getUtils(page)
 | 
						|
      await page.setViewportSize({ width: 1200, height: 500 })
 | 
						|
      page.on('console', console.log)
 | 
						|
 | 
						|
      // Constants and locators
 | 
						|
      const projectCard = page.getByText('testProject')
 | 
						|
      const otherFile = page
 | 
						|
        .getByRole('listitem')
 | 
						|
        .filter({ has: page.getByRole('button', { name: 'other.kcl' }) })
 | 
						|
 | 
						|
      await test.step('Open project and make a change to the file', async () => {
 | 
						|
        await projectCard.click()
 | 
						|
        await u.waitForPageLoad()
 | 
						|
 | 
						|
        // Get the text in the code locator.
 | 
						|
        const originalText = await u.codeLocator.innerText()
 | 
						|
        // Click in the editor and add some new lines.
 | 
						|
        await u.codeLocator.click()
 | 
						|
 | 
						|
        await page.keyboard.type(`sketch001 = startSketchOn(XY)
 | 
						|
    some other shit`)
 | 
						|
 | 
						|
        // Ensure the content in the editor changed.
 | 
						|
        const newContent = await u.codeLocator.innerText()
 | 
						|
 | 
						|
        expect(originalText !== newContent)
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('navigate to other.kcl', async () => {
 | 
						|
        await u.openFilePanel()
 | 
						|
 | 
						|
        await otherFile.click()
 | 
						|
        await u.waitForPageLoad()
 | 
						|
        await u.openKclCodePanel()
 | 
						|
        await expect(u.codeLocator).toContainText('getOppositeEdge(thing)')
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('hit undo', async () => {
 | 
						|
        // Get the original content of the file.
 | 
						|
        const originalText = await u.codeLocator.innerText()
 | 
						|
        // Now hit undo
 | 
						|
        await page.keyboard.down('ControlOrMeta')
 | 
						|
        await page.keyboard.press('KeyZ')
 | 
						|
        await page.keyboard.up('ControlOrMeta')
 | 
						|
 | 
						|
        await page.waitForTimeout(100)
 | 
						|
        await expect(u.codeLocator).toContainText(originalText)
 | 
						|
      })
 | 
						|
    }
 | 
						|
  )
 | 
						|
 | 
						|
  test(
 | 
						|
    `open a file, change something, undo it, open a different file, hitting redo should do nothing`,
 | 
						|
    { tag: '@electron' },
 | 
						|
    async ({ context, page }, testInfo) => {
 | 
						|
      await context.folderSetupFn(async (dir) => {
 | 
						|
        const testDir = join(dir, 'testProject')
 | 
						|
        await fsp.mkdir(testDir, { recursive: true })
 | 
						|
        await fsp.copyFile(
 | 
						|
          executorInputPath('cylinder.kcl'),
 | 
						|
          join(testDir, 'main.kcl')
 | 
						|
        )
 | 
						|
        await fsp.copyFile(
 | 
						|
          executorInputPath('basic_fillet_cube_end.kcl'),
 | 
						|
          join(testDir, 'other.kcl')
 | 
						|
        )
 | 
						|
      })
 | 
						|
      const u = await getUtils(page)
 | 
						|
      await page.setViewportSize({ width: 1200, height: 500 })
 | 
						|
      page.on('console', console.log)
 | 
						|
 | 
						|
      // Constants and locators
 | 
						|
      const projectCard = page.getByText('testProject')
 | 
						|
      const otherFile = page
 | 
						|
        .getByRole('listitem')
 | 
						|
        .filter({ has: page.getByRole('button', { name: 'other.kcl' }) })
 | 
						|
 | 
						|
      const badContent = 'this shit'
 | 
						|
      await test.step('Open project and make a change to the file', async () => {
 | 
						|
        await projectCard.click()
 | 
						|
        await u.waitForPageLoad()
 | 
						|
 | 
						|
        // Get the text in the code locator.
 | 
						|
        const originalText = await u.codeLocator.innerText()
 | 
						|
        // Click in the editor and add some new lines.
 | 
						|
        await u.codeLocator.click()
 | 
						|
 | 
						|
        await page.keyboard.type(badContent)
 | 
						|
 | 
						|
        // Ensure the content in the editor changed.
 | 
						|
        const newContent = await u.codeLocator.innerText()
 | 
						|
 | 
						|
        expect(originalText !== newContent)
 | 
						|
 | 
						|
        // Now hit undo
 | 
						|
        await page.keyboard.down('ControlOrMeta')
 | 
						|
        await page.keyboard.press('KeyZ')
 | 
						|
        await page.keyboard.up('ControlOrMeta')
 | 
						|
 | 
						|
        await page.waitForTimeout(100)
 | 
						|
        await expect(u.codeLocator).toContainText(originalText)
 | 
						|
        await expect(u.codeLocator).not.toContainText(badContent)
 | 
						|
 | 
						|
        // Hit redo.
 | 
						|
        await page.keyboard.down('Shift')
 | 
						|
        await page.keyboard.down('ControlOrMeta')
 | 
						|
        await page.keyboard.press('KeyZ')
 | 
						|
        await page.keyboard.up('ControlOrMeta')
 | 
						|
        await page.keyboard.up('Shift')
 | 
						|
 | 
						|
        await page.waitForTimeout(100)
 | 
						|
        await expect(u.codeLocator).toContainText(originalText)
 | 
						|
        await expect(u.codeLocator).toContainText(badContent)
 | 
						|
 | 
						|
        // Now hit undo
 | 
						|
        await page.keyboard.down('ControlOrMeta')
 | 
						|
        await page.keyboard.press('KeyZ')
 | 
						|
        await page.keyboard.up('ControlOrMeta')
 | 
						|
 | 
						|
        await page.waitForTimeout(100)
 | 
						|
        await expect(u.codeLocator).toContainText(originalText)
 | 
						|
        await expect(u.codeLocator).not.toContainText(badContent)
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('navigate to other.kcl', async () => {
 | 
						|
        await u.openFilePanel()
 | 
						|
 | 
						|
        await otherFile.click()
 | 
						|
        await u.waitForPageLoad()
 | 
						|
        await u.openKclCodePanel()
 | 
						|
        await expect(u.codeLocator).toContainText('getOppositeEdge(thing)')
 | 
						|
        await expect(u.codeLocator).not.toContainText(badContent)
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('hit redo', async () => {
 | 
						|
        // Get the original content of the file.
 | 
						|
        const originalText = await u.codeLocator.innerText()
 | 
						|
        // Now hit redo
 | 
						|
        await page.keyboard.down('Shift')
 | 
						|
        await page.keyboard.down('ControlOrMeta')
 | 
						|
        await page.keyboard.press('KeyZ')
 | 
						|
        await page.keyboard.up('ControlOrMeta')
 | 
						|
        await page.keyboard.up('Shift')
 | 
						|
 | 
						|
        await page.waitForTimeout(100)
 | 
						|
        await expect(u.codeLocator).toContainText(originalText)
 | 
						|
        await expect(u.codeLocator).not.toContainText(badContent)
 | 
						|
      })
 | 
						|
    }
 | 
						|
  )
 | 
						|
 | 
						|
  test(
 | 
						|
    `cloned file has an incremented name and same contents`,
 | 
						|
    { tag: '@electron' },
 | 
						|
    async ({ page, context, homePage }, testInfo) => {
 | 
						|
      const { panesOpen, cloneFile } = await getUtils(page, test)
 | 
						|
 | 
						|
      const { dir } = await context.folderSetupFn(async (dir) => {
 | 
						|
        const finalDir = join(dir, 'testDefault')
 | 
						|
        await fsp.mkdir(finalDir, { recursive: true })
 | 
						|
        await fsp.copyFile(
 | 
						|
          executorInputPath('e2e-can-sketch-on-chamfer.kcl'),
 | 
						|
          join(finalDir, 'lee.kcl')
 | 
						|
        )
 | 
						|
      })
 | 
						|
 | 
						|
      const contentOriginal = await fsp.readFile(
 | 
						|
        join(dir, 'testDefault', 'lee.kcl'),
 | 
						|
        'utf-8'
 | 
						|
      )
 | 
						|
 | 
						|
      await page.setBodyDimensions({ width: 1200, height: 500 })
 | 
						|
      page.on('console', console.log)
 | 
						|
 | 
						|
      await panesOpen(['files'])
 | 
						|
      await homePage.openProject('testDefault')
 | 
						|
 | 
						|
      await cloneFile('lee.kcl')
 | 
						|
      await cloneFile('lee-1.kcl')
 | 
						|
      await cloneFile('lee-2.kcl')
 | 
						|
      await cloneFile('lee-3.kcl')
 | 
						|
      await cloneFile('lee-4.kcl')
 | 
						|
 | 
						|
      await test.step('Postcondition: there are 5 new lee-*.kcl files', async () => {
 | 
						|
        await expect(
 | 
						|
          page
 | 
						|
            .locator('[data-testid="file-pane-scroll-container"] button')
 | 
						|
            .filter({ hasText: /lee[-]?[0-5]?/ })
 | 
						|
        ).toHaveCount(5)
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Postcondition: the files have the same contents', async () => {
 | 
						|
        for (let n = 0; n < 5; n += 1) {
 | 
						|
          const content = await fsp.readFile(
 | 
						|
            join(dir, 'testDefault', `lee-${n + 1}.kcl`),
 | 
						|
            'utf-8'
 | 
						|
          )
 | 
						|
          await expect(content).toEqual(contentOriginal)
 | 
						|
        }
 | 
						|
      })
 | 
						|
    }
 | 
						|
  )
 | 
						|
})
 |