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: '@desktop' }, 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: '@desktop' }, 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: '@desktop' }, 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: '@desktop' }, 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: ['@desktop', '@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: '@desktop' }, 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: '@desktop', }, 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: '@desktop' }, 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: '@desktop' }, 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: '@desktop' }, 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: '@desktop' }, 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: '@desktop' }, 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: '@desktop' }, 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: '@desktop' }, 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: '@desktop' }, 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/ 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: '@desktop' }, 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/ 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: '@desktop' }, 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: '@desktop' }, 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: '@desktop' }, 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) } }) } ) })