-
Notifications
You must be signed in to change notification settings - Fork 102
Add point-and-click Insert from local project files #6129
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
9001006
3afcb33
e00f6c2
2bae5ff
79fd435
2d08d47
1bb37fc
9371860
69a0198
58c87ac
9e32ce3
5d8f7a5
7b7d8b4
eaf1f09
8052af8
efcf164
8fa8b16
5f11d61
4698339
a932339
c5e18ba
a699e94
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,115 @@ | ||
| import * as fsp from 'fs/promises' | ||
| import path from 'path' | ||
|
|
||
| import { executorInputPath } from '@e2e/playwright/test-utils' | ||
| import { test } from '@e2e/playwright/zoo-test' | ||
|
|
||
| // test file is for testing point an click code gen functionality that's assemblies related | ||
| test.describe('Point-and-click assemblies tests', () => { | ||
| test( | ||
| `Insert kcl part into assembly as whole module import`, | ||
| { tag: ['@electron'] }, | ||
| async ({ | ||
| context, | ||
| page, | ||
| homePage, | ||
| scene, | ||
| editor, | ||
| toolbar, | ||
| cmdBar, | ||
| tronApp, | ||
| }) => { | ||
| if (!tronApp) { | ||
| fail() | ||
| } | ||
|
|
||
| // One dumb hardcoded screen pixel value | ||
| const testPoint = { x: 575, y: 200 } | ||
| const initialColor: [number, number, number] = [50, 50, 50] | ||
| const partColor: [number, number, number] = [150, 150, 150] | ||
| const tolerance = 50 | ||
|
|
||
| await test.step('Setup parts and expect empty assembly scene', async () => { | ||
| const projectName = 'assembly' | ||
| await context.folderSetupFn(async (dir) => { | ||
| const bracketDir = path.join(dir, projectName) | ||
| await fsp.mkdir(bracketDir, { recursive: true }) | ||
| await Promise.all([ | ||
| fsp.copyFile( | ||
| executorInputPath('cylinder-inches.kcl'), | ||
| path.join(bracketDir, 'cylinder.kcl') | ||
| ), | ||
| fsp.copyFile( | ||
| executorInputPath('e2e-can-sketch-on-chamfer.kcl'), | ||
| path.join(bracketDir, 'bracket.kcl') | ||
| ), | ||
| fsp.writeFile(path.join(bracketDir, 'main.kcl'), ''), | ||
| ]) | ||
| }) | ||
| await page.setBodyDimensions({ width: 1000, height: 500 }) | ||
| await homePage.openProject(projectName) | ||
| await scene.settled(cmdBar) | ||
| await scene.expectPixelColor(initialColor, testPoint, tolerance) | ||
| }) | ||
|
|
||
| await test.step('Insert first part into the assembly', async () => { | ||
| await toolbar.insertButton.click() | ||
| await cmdBar.selectOption({ name: 'cylinder.kcl' }).click() | ||
| await cmdBar.expectState({ | ||
| stage: 'arguments', | ||
| currentArgKey: 'localName', | ||
| currentArgValue: '', | ||
| headerArguments: { Path: 'cylinder.kcl', LocalName: '' }, | ||
| highlightedHeaderArg: 'localName', | ||
| commandName: 'Insert', | ||
| }) | ||
| await page.keyboard.insertText('cylinder') | ||
| await cmdBar.progressCmdBar() | ||
| await cmdBar.expectState({ | ||
| stage: 'review', | ||
| headerArguments: { Path: 'cylinder.kcl', LocalName: 'cylinder' }, | ||
| commandName: 'Insert', | ||
| }) | ||
| await cmdBar.progressCmdBar() | ||
| await editor.expectEditor.toContain( | ||
| ` | ||
| import "cylinder.kcl" as cylinder | ||
| cylinder | ||
| `, | ||
| { shouldNormalise: true } | ||
| ) | ||
| await scene.expectPixelColor(partColor, testPoint, tolerance) | ||
| }) | ||
|
|
||
| await test.step('Insert second part into the assembly', async () => { | ||
| await toolbar.insertButton.click() | ||
| await cmdBar.selectOption({ name: 'bracket.kcl' }).click() | ||
| await cmdBar.expectState({ | ||
| stage: 'arguments', | ||
| currentArgKey: 'localName', | ||
| currentArgValue: '', | ||
| headerArguments: { Path: 'bracket.kcl', LocalName: '' }, | ||
| highlightedHeaderArg: 'localName', | ||
| commandName: 'Insert', | ||
| }) | ||
| await page.keyboard.insertText('bracket') | ||
| await cmdBar.progressCmdBar() | ||
| await cmdBar.expectState({ | ||
| stage: 'review', | ||
| headerArguments: { Path: 'bracket.kcl', LocalName: 'bracket' }, | ||
| commandName: 'Insert', | ||
| }) | ||
| await cmdBar.progressCmdBar() | ||
| await editor.expectEditor.toContain( | ||
| ` | ||
| import "cylinder.kcl" as cylinder | ||
| import "bracket.kcl" as bracket | ||
| cylinder | ||
| bracket | ||
| `, | ||
| { shouldNormalise: true } | ||
| ) | ||
| }) | ||
| } | ||
| ) | ||
| }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -459,10 +459,30 @@ export const FileMachineProvider = ({ | |
| name: sample.title, | ||
| })), | ||
| }, | ||
| specialPropsForInsertCommand: { | ||
| providedOptions: (isDesktop() && project?.children | ||
| ? project.children | ||
| : [] | ||
| ).flatMap((v) => { | ||
| // TODO: add support for full tree traversal when KCL support subdir imports | ||
| const relativeFilePath = v.path.replace( | ||
| project?.path + window.electron.sep, | ||
| '' | ||
| ) | ||
| const isDirectory = v.children | ||
| const isCurrentFile = v.path === file?.path | ||
| return isDirectory || isCurrentFile | ||
| ? [] | ||
| : { | ||
| name: relativeFilePath, | ||
| value: relativeFilePath, | ||
| } | ||
| }), | ||
| }, | ||
| }).filter( | ||
| (command) => kclSamples.length || command.name !== 'open-kcl-example' | ||
| ), | ||
| [codeManager, kclManager, send, kclSamples] | ||
| [codeManager, kclManager, send, kclSamples, project, file] | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This right here is the reason why this command was added in
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @nadr0 is in the process of excavating the projectMachine out of React. When that is done, then the inputs will be available anywhere the appMachine is, and it should be easier to get into a modeling command definition. But I think this is understandable as an approach!
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok phew that's awesome! |
||
| ) | ||
|
|
||
| useEffect(() => { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,6 +17,7 @@ import { | |
| EXECUTION_TYPE_NONE, | ||
| EXECUTION_TYPE_REAL, | ||
| } from '@src/lib/constants' | ||
| import type { Selections } from '@src/lib/selections' | ||
|
|
||
| /** | ||
| * Updates the complete modeling state: | ||
|
|
@@ -52,15 +53,23 @@ export async function updateModelingState( | |
| }, | ||
| options?: { | ||
| focusPath?: Array<PathToNode> | ||
| skipUpdateAst?: boolean | ||
| } | ||
| ): Promise<void> { | ||
| // Step 1: Update AST without executing (prepare selections) | ||
| const updatedAst = await dependencies.kclManager.updateAst( | ||
| ast, | ||
| // false == mock execution. Is this what we want? | ||
| false, // Execution handled separately for error resilience | ||
| options | ||
| ) | ||
| let updatedAst: { | ||
| newAst: Node<Program> | ||
| selections?: Selections | ||
| } = { newAst: ast } | ||
| // TODO: understand why this skip flag is needed for insertAstMod. | ||
| // It's unclear why we double casts the AST | ||
|
Comment on lines
+63
to
+64
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This took a lot of time from a few of us to debug. Thanks a lot to @franknoirot @nadr0 @lee-at-zoo-corp The solution below is not perfect but helps unblocking us without changing the code path too much. |
||
| if (!options?.skipUpdateAst) { | ||
| // Step 1: Update AST without executing (prepare selections) | ||
| updatedAst = await dependencies.kclManager.updateAst( | ||
| ast, | ||
| false, // Execution handled separately for error resilience | ||
| options | ||
| ) | ||
| } | ||
|
|
||
| // Step 2: Update the code editor and save file | ||
| await dependencies.codeManager.updateEditorWithAstAndWriteToFile( | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be abstracted into one of the fixtures, and just be a oneLiner, or maybe part of "goToModelingScene"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I might leave this with @nadr0 as a separate issue if that's cool. This pattern only exists in
e2e/playwright/native-file-menu.spec.ts, not sure how much it should be generalized.But I agree with the sentiment yes!