Assemblies: Load outside files into project via point-and-click (#6217)
* WIP: Add point-and-click Import for geometry Will eventually fix #6120 Right now the whole loop is there but the codemod doesn't work yet * Better pathToNOde, log on non-working cm dispatch call * Add workaround to updateModelingState not working * Back to updateModelingState with a skip flag * Better todo * Change working from Import to Insert, cleanups * Sister command in kclCommands to populate file options * Improve path selector * Unsure: move importAstMod to kclCommands onSubmit 😶 * Add e2e test * Clean up for review * Add native file menu entry and test * No await yo lint said so * WIP: UX improvements around foreign file imports Fixes #6152 * @lrev-Dev's suggestion to remove a comment Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch> * Update to scene.settled(cmdBar) * Add partNNN default name for alias * Lint * Lint * Fix unit tests * Add sad path insert test Thanks @Irev-Dev for the suggestion * Add step insert test * Lint * Add test for second foreign import thru file tree click * WIP: Add point-and-click Load to copy files from outside the project into the project Towards #6210 * Move Insert button to modeling toolbar, update menus and toolbars * Add default value for local name alias * Aligning tests * Fix tests * Add padding for filenames starting with a digit * Lint * Lint * Update snapshots * Merge branch 'main' into pierremtb/issue6210-Add-point-and-click-Load-to-copy-files-from-outside-the-project-into-the-project * Add disabled transform subbutton * Merge kcl-samples and local disk load into one 'Load external model' command * Fix em tests * Fix test * Add test for file pick import, better input * Fix non .kcl loading * Lint * Update snapshots * Fix issue leading to test failure * Fix clone test * Add note * Fix nested clone issue * Clean up for review * Add valueSummary for path * Fix test after path change * Clean up for review * Update src/lib/kclCommands.ts Thanks @franknoirot! Co-authored-by: Frank Noirot <frank@zoo.dev> * Improve path input arg * Fix tests * Merge branch 'main' into pierremtb/issue6210-Add-point-and-click-Load-to-copy-files-from-outside-the-project-into-the-project * Fix path header not showing and improve tests * Clean up --------- Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch> Co-authored-by: Frank Noirot <frank@zoo.dev>
This commit is contained in:
@ -3,14 +3,18 @@ import { FILE_EXT } from '@src/lib/constants'
|
||||
import * as fsp from 'fs/promises'
|
||||
import { join } from 'path'
|
||||
|
||||
import type { CmdBarSerialised } from '@e2e/playwright/fixtures/cmdBarFixture'
|
||||
import type { ElectronZoo } from '@e2e/playwright/fixtures/fixtureSetup'
|
||||
import {
|
||||
executorInputPath,
|
||||
getUtils,
|
||||
orRunWhenFullSuiteEnabled,
|
||||
runningOnWindows,
|
||||
testsInputPath,
|
||||
} from '@e2e/playwright/test-utils'
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
|
||||
test.describe('Testing in-app sample loading', () => {
|
||||
test.describe('Testing loading external models', () => {
|
||||
/**
|
||||
* Note this test implicitly depends on the KCL sample "parametric-bearing-pillow-block",
|
||||
* its title, and its units settings. https://github.com/KittyCAD/kcl-samples/blob/main/parametric-bearing-pillow-block/main.kcl
|
||||
@ -39,7 +43,7 @@ test.describe('Testing in-app sample loading', () => {
|
||||
}
|
||||
const commandBarButton = page.getByRole('button', { name: 'Commands' })
|
||||
const samplesCommandOption = page.getByRole('option', {
|
||||
name: 'Open Sample',
|
||||
name: 'Load external model',
|
||||
})
|
||||
const commandSampleOption = page.getByRole('option', {
|
||||
name: newSample.title,
|
||||
@ -83,7 +87,7 @@ test.describe('Testing in-app sample loading', () => {
|
||||
test(
|
||||
'Desktop: should create new file by default, optionally overwrite',
|
||||
{ tag: '@electron' },
|
||||
async ({ editor, context, page, scene, cmdBar }, testInfo) => {
|
||||
async ({ editor, context, page, scene, cmdBar, toolbar }) => {
|
||||
if (runningOnWindows()) {
|
||||
test.fixme(orRunWhenFullSuiteEnabled())
|
||||
}
|
||||
@ -106,20 +110,12 @@ test.describe('Testing in-app sample loading', () => {
|
||||
title: '100mm Gear Rack',
|
||||
}
|
||||
const projectCard = page.getByRole('link', { name: 'bracket' })
|
||||
const commandBarButton = page.getByRole('button', { name: 'Commands' })
|
||||
const commandOption = page.getByRole('option', { name: 'Open Sample' })
|
||||
const commandSampleOption = (name: string) =>
|
||||
page.getByRole('option', {
|
||||
name,
|
||||
exact: true,
|
||||
})
|
||||
const commandMethodArgButton = page.getByRole('button', {
|
||||
name: 'Method',
|
||||
})
|
||||
const commandMethodOption = page.getByRole('option', {
|
||||
name: 'Overwrite',
|
||||
})
|
||||
const newFileWarning = page.getByText('Create a new file from sample?')
|
||||
const overwriteWarning = page.getByText(
|
||||
'Overwrite current file with sample?'
|
||||
)
|
||||
@ -129,6 +125,18 @@ test.describe('Testing in-app sample loading', () => {
|
||||
page.getByRole('listitem').filter({
|
||||
has: page.getByRole('button', { name }),
|
||||
})
|
||||
const defaultLoadCmdBarState: CmdBarSerialised = {
|
||||
commandName: 'Load external model',
|
||||
currentArgKey: 'source',
|
||||
currentArgValue: '',
|
||||
headerArguments: {
|
||||
Method: 'newFile',
|
||||
Sample: '',
|
||||
Source: '',
|
||||
},
|
||||
highlightedHeaderArg: 'source',
|
||||
stage: 'arguments',
|
||||
}
|
||||
|
||||
await test.step(`Test setup`, async () => {
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
@ -147,14 +155,12 @@ test.describe('Testing in-app sample loading', () => {
|
||||
})
|
||||
|
||||
await test.step(`Load a KCL sample with the command palette`, async () => {
|
||||
await commandBarButton.click()
|
||||
await page.waitForTimeout(1000)
|
||||
await commandOption.click()
|
||||
await page.waitForTimeout(1000)
|
||||
await commandSampleOption(sampleOne.title).click()
|
||||
await toolbar.loadButton.click()
|
||||
await cmdBar.expectState(defaultLoadCmdBarState)
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.selectOption({ name: sampleOne.title }).click()
|
||||
await expect(overwriteWarning).not.toBeVisible()
|
||||
await expect(newFileWarning).toBeVisible()
|
||||
await confirmButton.click()
|
||||
await cmdBar.progressCmdBar()
|
||||
await page.waitForTimeout(1000)
|
||||
})
|
||||
|
||||
@ -165,21 +171,15 @@ test.describe('Testing in-app sample loading', () => {
|
||||
})
|
||||
|
||||
await test.step(`Now overwrite the current file`, async () => {
|
||||
await commandBarButton.click()
|
||||
await page.waitForTimeout(1000)
|
||||
await commandOption.click()
|
||||
await page.waitForTimeout(1000)
|
||||
await commandSampleOption(sampleTwo.title).click()
|
||||
await page.waitForTimeout(1000)
|
||||
await toolbar.loadButton.click()
|
||||
await cmdBar.expectState(defaultLoadCmdBarState)
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.selectOption({ name: sampleTwo.title }).click()
|
||||
await commandMethodArgButton.click()
|
||||
await page.waitForTimeout(1000)
|
||||
await commandMethodOption.click()
|
||||
await page.waitForTimeout(1000)
|
||||
await expect(commandMethodArgButton).toContainText('overwrite')
|
||||
await expect(newFileWarning).not.toBeVisible()
|
||||
await expect(overwriteWarning).toBeVisible()
|
||||
await confirmButton.click()
|
||||
await page.waitForTimeout(1000)
|
||||
})
|
||||
|
||||
await test.step(`Ensure we overwrote the current file without navigating`, async () => {
|
||||
@ -200,4 +200,96 @@ test.describe('Testing in-app sample loading', () => {
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
const externalModelCases = [
|
||||
{
|
||||
modelName: 'cylinder.kcl',
|
||||
deconflictedModelName: 'cylinder-1.kcl',
|
||||
modelPath: executorInputPath('cylinder.kcl'),
|
||||
},
|
||||
{
|
||||
modelName: 'cube.step',
|
||||
deconflictedModelName: 'cube-1.step',
|
||||
modelPath: testsInputPath('cube.step'),
|
||||
},
|
||||
]
|
||||
externalModelCases.map(({ modelName, deconflictedModelName, modelPath }) => {
|
||||
test(
|
||||
`Load external models from local drive - ${modelName}`,
|
||||
{ tag: ['@electron'] },
|
||||
async ({ page, homePage, scene, toolbar, cmdBar, tronApp }) => {
|
||||
if (!tronApp) {
|
||||
fail()
|
||||
}
|
||||
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
await scene.settled(cmdBar)
|
||||
const modelFileContent = await fsp.readFile(modelPath, 'utf-8')
|
||||
const { editorTextMatches } = await getUtils(page, test)
|
||||
|
||||
async function loadExternalFileThroughCommandBar(tronApp: ElectronZoo) {
|
||||
await toolbar.loadButton.click()
|
||||
await cmdBar.expectState({
|
||||
commandName: 'Load external model',
|
||||
currentArgKey: 'source',
|
||||
currentArgValue: '',
|
||||
headerArguments: {
|
||||
Method: 'newFile',
|
||||
Sample: '',
|
||||
Source: '',
|
||||
},
|
||||
highlightedHeaderArg: 'source',
|
||||
stage: 'arguments',
|
||||
})
|
||||
await cmdBar.selectOption({ name: 'Local Drive' }).click()
|
||||
|
||||
// Mock the file picker selection
|
||||
const handleFile = tronApp.electron.evaluate(
|
||||
async ({ dialog }, filePaths) => {
|
||||
dialog.showOpenDialog = () =>
|
||||
Promise.resolve({ canceled: false, filePaths })
|
||||
},
|
||||
[modelPath]
|
||||
)
|
||||
await page.getByTestId('cmd-bar-arg-file-button').click()
|
||||
await handleFile
|
||||
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.expectState({
|
||||
commandName: 'Load external model',
|
||||
headerArguments: {
|
||||
Source: 'local',
|
||||
Path: modelName,
|
||||
},
|
||||
stage: 'review',
|
||||
})
|
||||
await cmdBar.progressCmdBar()
|
||||
}
|
||||
|
||||
await test.step('Load the external model from local drive', async () => {
|
||||
await loadExternalFileThroughCommandBar(tronApp)
|
||||
// TODO: I think the files pane should auto open?
|
||||
await toolbar.openPane('files')
|
||||
await toolbar.expectFileTreeState([modelName, 'main.kcl'])
|
||||
if (modelName.endsWith('.kcl')) {
|
||||
await editorTextMatches(modelFileContent)
|
||||
}
|
||||
})
|
||||
|
||||
await test.step('Load the same external model, except deconflicted name', async () => {
|
||||
await loadExternalFileThroughCommandBar(tronApp)
|
||||
await toolbar.openPane('files')
|
||||
await toolbar.expectFileTreeState([
|
||||
deconflictedModelName,
|
||||
modelName,
|
||||
'main.kcl',
|
||||
])
|
||||
if (modelName.endsWith('.kcl')) {
|
||||
await editorTextMatches(modelFileContent)
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
|
Reference in New Issue
Block a user