Added tests

This commit is contained in:
49lf
2024-10-11 13:43:05 -04:00
parent b6a3c552c9
commit 71f7124913
6 changed files with 279 additions and 13 deletions

View File

@ -313,3 +313,45 @@ test(
await electronApp.close() await electronApp.close()
} }
) )
test(
'external change of file contents are reflected in editor',
{ tag: '@electron' },
async ({ browserName }, testInfo) => {
const PROJECT_DIR_NAME = 'lee-was-here'
const {
electronApp,
page,
dir: projectsDir,
} = await setupElectron({
testInfo,
folderSetupFn: async (dir) => {
const aProjectDir = join(dir, PROJECT_DIR_NAME)
await fsp.mkdir(aProjectDir, { recursive: true })
},
})
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
await test.step('Open the project', async () => {
await expect(page.getByText(PROJECT_DIR_NAME)).toBeVisible()
await page.getByText(PROJECT_DIR_NAME).click()
await u.waitForPageLoad()
})
await u.openFilePanel()
await u.openKclCodePanel()
await test.step('Write to file externally and check for changed content', async () => {
const content = 'ha he ho ho ha blap scap be dap'
await fsp.writeFile(
join(projectsDir, PROJECT_DIR_NAME, 'main.kcl'),
content
)
await u.editorTextMatches(content)
})
await electronApp.close()
}
)

View File

@ -960,4 +960,171 @@ _test.describe('Deleting items from the file pane', () => {
'TODO - delete folder we are in, with no main.kcl', 'TODO - delete folder we are in, with no main.kcl',
async () => {} async () => {}
) )
// Copied from tests above.
_test(
`external deletion of project navigates back home`,
{ tag: '@electron' },
async ({ browserName }, testInfo) => {
const TEST_PROJECT_NAME = 'Test Project'
const {
electronApp,
page,
dir: projectsDirName,
} = await setupElectron({
testInfo,
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()
})
await electronApp.close()
}
)
// 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 ({ browserName }, testInfo) => {
const TEST_PROJECT_NAME = 'Test Project'
const {
electronApp,
page,
dir: projectsDirName,
} = await setupElectron({
testInfo,
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')
}
)
await electronApp.close()
}
)
}) })

View File

@ -9,7 +9,7 @@ import {
executorInputPath, executorInputPath,
} from './test-utils' } from './test-utils'
import { SaveSettingsPayload, SettingsLevel } from 'lib/settings/settingsTypes' import { SaveSettingsPayload, SettingsLevel } from 'lib/settings/settingsTypes'
import { SETTINGS_FILE_NAME } from 'lib/constants' import { SETTINGS_FILE_NAME, PROJECT_SETTINGS_FILE_NAME } from 'lib/constants'
import { import {
TEST_SETTINGS_KEY, TEST_SETTINGS_KEY,
TEST_SETTINGS_CORRUPTED, TEST_SETTINGS_CORRUPTED,
@ -445,6 +445,58 @@ test.describe('Testing settings', () => {
} }
) )
test(
'project settings reload on external change',
{ tag: '@electron' },
async ({ browserName }, testInfo) => {
const {
electronApp,
page,
dir: projectDirName,
} = await setupElectron({
testInfo,
})
await page.setViewportSize({ width: 1200, height: 500 })
const logoLink = page.getByTestId('app-logo')
const projectDirLink = page.getByText('Loaded from')
await test.step('Wait for project view', async () => {
await expect(projectDirLink).toBeVisible()
})
const projectLinks = page.getByTestId('project-link')
const oldCount = await projectLinks.count()
await page.getByRole('button', { name: 'New project' }).click()
await expect(projectLinks).toHaveCount(oldCount + 1)
await projectLinks.filter({ hasText: 'project-000' }).first().click()
const changeColorFs = async (color: string) => {
const tempSettingsFilePath = join(
projectDirName,
'project-000',
PROJECT_SETTINGS_FILE_NAME
)
await fsp.writeFile(
tempSettingsFilePath,
`[settings.app]\nthemeColor = "${color}"`
)
}
await test.step('Check the color is first starting as we expect', async () => {
await expect(logoLink).toHaveCSS('--primary-hue', '264.5')
})
await test.step('Check color of logo changed', async () => {
await changeColorFs('99')
await expect(logoLink).toHaveCSS('--primary-hue', '99')
})
await electronApp.close()
}
)
test( test(
`Closing settings modal should go back to the original file being viewed`, `Closing settings modal should go back to the original file being viewed`,
{ tag: '@electron' }, { tag: '@electron' },

View File

@ -266,7 +266,9 @@ const FileTreeItem = ({
: '') : '')
} }
style={{ paddingInlineStart: getIndentationCSS(level) }} style={{ paddingInlineStart: getIndentationCSS(level) }}
onClick={clickDirectory} onClick={(e) => e.currentTarget.focus()}
onClickCapture={clickDirectory}
onFocusCapture={clickDirectory}
onKeyDown={(e) => e.key === 'Enter' && e.preventDefault()} onKeyDown={(e) => e.key === 'Enter' && e.preventDefault()}
onKeyUp={handleKeyUp} onKeyUp={handleKeyUp}
> >
@ -479,7 +481,6 @@ export const FileTreeInner = ({
}: { }: {
onNavigateToFile?: () => void onNavigateToFile?: () => void
}) => { }) => {
const navigate = useNavigate()
const loaderData = useRouteLoaderData(PATHS.FILE) as IndexLoaderData const loaderData = useRouteLoaderData(PATHS.FILE) as IndexLoaderData
const { send: fileSend, context: fileContext } = useFileContext() const { send: fileSend, context: fileContext } = useFileContext()
const { send: modelingSend } = useModelingContext() const { send: modelingSend } = useModelingContext()
@ -487,11 +488,6 @@ export const FileTreeInner = ({
// Refresh the file tree when there are changes. // Refresh the file tree when there are changes.
useFileSystemWatcher( useFileSystemWatcher(
async (eventType, path) => { async (eventType, path) => {
if (eventType === 'unlinkDir' && path === loaderData?.project?.path) {
navigate(PATHS.HOME)
return
}
fileSend({ type: 'Refresh' }) fileSend({ type: 'Refresh' })
}, },
[loaderData?.project?.path, fileContext.selectedDirectory.path].filter( [loaderData?.project?.path, fileContext.selectedDirectory.path].filter(
@ -511,7 +507,7 @@ export const FileTreeInner = ({
className="overflow-auto pb-12 absolute inset-0" className="overflow-auto pb-12 absolute inset-0"
data-testid="file-pane-scroll-container" data-testid="file-pane-scroll-container"
> >
<ul className="m-0 p-0 text-sm" onClick={clickDirectory}> <ul className="m-0 p-0 text-sm" onClickCapture={clickDirectory}>
{sortProject(fileContext.project?.children || []).map((fileOrDir) => ( {sortProject(fileContext.project?.children || []).map((fileOrDir) => (
<FileTreeItem <FileTreeItem
project={fileContext.project} project={fileContext.project}

View File

@ -221,6 +221,19 @@ export const SettingsAuthProviderBase = ({
useFileSystemWatcher( useFileSystemWatcher(
async () => { async () => {
// If there is a projectPath but it no longer exists it means
// it was exterally removed. If we let the code past this condition
// execute it will recreate the directory due to code in
// loadAndValidateSettings trying to recreate files. I do not
// wish to change the behavior in case anything else uses it.
// Go home.
if (loadedProject?.project?.path) {
if (!window.electron.exists(loadedProject?.project?.path)) {
navigate(PATHS.HOME)
return
}
}
const data = await loadAndValidateSettings(loadedProject?.project?.path) const data = await loadAndValidateSettings(loadedProject?.project?.path)
settingsSend({ settingsSend({
type: 'Set all settings', type: 'Set all settings',

View File

@ -49,14 +49,12 @@ const watchFileOn = (
if (!watchers) { if (!watchers) {
watchers = new Map() watchers = new Map()
} }
console.log('watchers', watchers)
const watcher = chokidar.watch(path, { depth: 1 }) const watcher = chokidar.watch(path, { depth: 1 })
watcher.on('all', callback) watcher.on('all', callback)
watchers.set(key, { watcher, callback }) watchers.set(key, { watcher, callback })
fsWatchListeners.set(path, watchers) fsWatchListeners.set(path, watchers)
} }
const watchFileOff = (path: string, key: string) => { const watchFileOff = (path: string, key: string) => {
console.log('unmounting', path)
const watchers = fsWatchListeners.get(path) const watchers = fsWatchListeners.get(path)
if (!watchers) return if (!watchers) return
const data = watchers.get(key) const data = watchers.get(key)
@ -66,7 +64,6 @@ const watchFileOff = (path: string, key: string) => {
) )
return return
} }
console.log('watchers before remove', watchers)
const { watcher, callback } = data const { watcher, callback } = data
watcher.off('all', callback) watcher.off('all', callback)
watchers.delete(key) watchers.delete(key)
@ -75,7 +72,6 @@ const watchFileOff = (path: string, key: string) => {
} else { } else {
fsWatchListeners.set(path, watchers) fsWatchListeners.set(path, watchers)
} }
console.log('watchers after remove', watchers)
} }
const readFile = (path: string) => fs.readFile(path, 'utf-8') const readFile = (path: string) => fs.readFile(path, 'utf-8')
// It seems like from the node source code this does not actually block but also // It seems like from the node source code this does not actually block but also