Added tests
This commit is contained in:
@ -313,3 +313,45 @@ test(
|
||||
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()
|
||||
}
|
||||
)
|
||||
|
@ -960,4 +960,171 @@ _test.describe('Deleting items from the file pane', () => {
|
||||
'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 ({ 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()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
@ -9,7 +9,7 @@ import {
|
||||
executorInputPath,
|
||||
} from './test-utils'
|
||||
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 {
|
||||
TEST_SETTINGS_KEY,
|
||||
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(
|
||||
`Closing settings modal should go back to the original file being viewed`,
|
||||
{ tag: '@electron' },
|
||||
|
@ -266,7 +266,9 @@ const FileTreeItem = ({
|
||||
: '')
|
||||
}
|
||||
style={{ paddingInlineStart: getIndentationCSS(level) }}
|
||||
onClick={clickDirectory}
|
||||
onClick={(e) => e.currentTarget.focus()}
|
||||
onClickCapture={clickDirectory}
|
||||
onFocusCapture={clickDirectory}
|
||||
onKeyDown={(e) => e.key === 'Enter' && e.preventDefault()}
|
||||
onKeyUp={handleKeyUp}
|
||||
>
|
||||
@ -479,7 +481,6 @@ export const FileTreeInner = ({
|
||||
}: {
|
||||
onNavigateToFile?: () => void
|
||||
}) => {
|
||||
const navigate = useNavigate()
|
||||
const loaderData = useRouteLoaderData(PATHS.FILE) as IndexLoaderData
|
||||
const { send: fileSend, context: fileContext } = useFileContext()
|
||||
const { send: modelingSend } = useModelingContext()
|
||||
@ -487,11 +488,6 @@ export const FileTreeInner = ({
|
||||
// Refresh the file tree when there are changes.
|
||||
useFileSystemWatcher(
|
||||
async (eventType, path) => {
|
||||
if (eventType === 'unlinkDir' && path === loaderData?.project?.path) {
|
||||
navigate(PATHS.HOME)
|
||||
return
|
||||
}
|
||||
|
||||
fileSend({ type: 'Refresh' })
|
||||
},
|
||||
[loaderData?.project?.path, fileContext.selectedDirectory.path].filter(
|
||||
@ -511,7 +507,7 @@ export const FileTreeInner = ({
|
||||
className="overflow-auto pb-12 absolute inset-0"
|
||||
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) => (
|
||||
<FileTreeItem
|
||||
project={fileContext.project}
|
||||
|
@ -221,6 +221,19 @@ export const SettingsAuthProviderBase = ({
|
||||
|
||||
useFileSystemWatcher(
|
||||
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)
|
||||
settingsSend({
|
||||
type: 'Set all settings',
|
||||
|
@ -49,14 +49,12 @@ const watchFileOn = (
|
||||
if (!watchers) {
|
||||
watchers = new Map()
|
||||
}
|
||||
console.log('watchers', watchers)
|
||||
const watcher = chokidar.watch(path, { depth: 1 })
|
||||
watcher.on('all', callback)
|
||||
watchers.set(key, { watcher, callback })
|
||||
fsWatchListeners.set(path, watchers)
|
||||
}
|
||||
const watchFileOff = (path: string, key: string) => {
|
||||
console.log('unmounting', path)
|
||||
const watchers = fsWatchListeners.get(path)
|
||||
if (!watchers) return
|
||||
const data = watchers.get(key)
|
||||
@ -66,7 +64,6 @@ const watchFileOff = (path: string, key: string) => {
|
||||
)
|
||||
return
|
||||
}
|
||||
console.log('watchers before remove', watchers)
|
||||
const { watcher, callback } = data
|
||||
watcher.off('all', callback)
|
||||
watchers.delete(key)
|
||||
@ -75,7 +72,6 @@ const watchFileOff = (path: string, key: string) => {
|
||||
} else {
|
||||
fsWatchListeners.set(path, watchers)
|
||||
}
|
||||
console.log('watchers after remove', watchers)
|
||||
}
|
||||
const readFile = (path: string) => fs.readFile(path, 'utf-8')
|
||||
// It seems like from the node source code this does not actually block but also
|
||||
|
Reference in New Issue
Block a user