Compare commits

...

74 Commits

Author SHA1 Message Date
e8a82ea85e Clean up for PR 2025-04-11 09:47:45 -04:00
c1894edaed Remove sha1, add rfc3161TimeStampServer 2025-04-11 08:40:24 -04:00
8c28f34238 Back to publisherName = certificateSubjectName = KittyCAD Inc 2025-04-11 07:40:38 -04:00
d2340628a8 WIP 2025-04-11 07:05:22 -04:00
a1f5cdd690 Update signingHashAlgorithms to include sha1 2025-04-11 05:18:56 -04:00
d1d8d0a82c Use smctl windows certsync to update the windows store in build-apps (attempt) 2025-04-11 05:03:33 -04:00
f76b328136 WIP messing with Get-ChildItem -Recurse Cert 2025-04-11 04:59:45 -04:00
a13548da17 WIP messing with Get-ChildItem -Recurse Cert 2025-04-11 04:55:18 -04:00
65f4b0f239 WIP messing with Get-ChildItem -Recurse Cert 2025-04-11 04:54:05 -04:00
dbcc0bd3b4 WIP messing with Get-ChildItem -Recurse Cert 2025-04-11 04:43:35 -04:00
472b3618ac shell: pwsh 2025-04-11 04:00:47 -04:00
43e89e8bae Add back smksp_registrar.exe list and smctl.exe keypair ls 2025-04-10 19:39:56 -04:00
94a9e01301 Add certificateSha1 2025-04-10 19:02:01 -04:00
3980a1caf8 WIP 2025-04-10 18:58:46 -04:00
d4f23f8469 Trying to follow https://github.com/electron-userland/electron-builder/issues/7605#issuecomment-2257861622 2025-04-10 18:55:46 -04:00
9143c6f08a Test from https://github.com/electron-userland/electron-builder/issues/7605#issuecomment-2257861622 2025-04-10 18:49:23 -04:00
1d4456c458 Loosing my mind 2025-04-10 18:39:13 -04:00
c6fbb4fc63 Fix typo 2025-04-10 18:36:17 -04:00
b7c8d6c185 Copied line by line from docs in dummy script 2025-04-10 17:58:48 -04:00
f23aa5e642 Add dummy pierre-test-windows-code-sign, plus console log on normal script 2025-04-10 17:36:34 -04:00
8bb26c9b89 Remove stdio param for hopefully more logs 2025-04-10 16:56:54 -04:00
0d7aebdee9 DEBUG=electron-builder to get more logs (maybe) 2025-04-10 16:40:11 -04:00
ad333c2055 Try certutil.exe -csp "DigiCert Software Trust Manager KSP", forget yarn cache for now 2025-04-10 16:25:12 -04:00
3559df0c5e Add smksp_registrar.exe register which is somewhere else in their docs. Plus comment 2025-04-10 16:00:53 -04:00
e2dda07829 Update from docs, back to inherit 2025-04-10 15:33:19 -04:00
ea585cb5d6 Trying to get more logs 2025-04-10 15:04:18 -04:00
8af9af2aa7 Another try with the right things commented out 2025-04-10 14:22:30 -04:00
f0ba35c0b2 WIP: Updater on Nightly on Windows failed
Fixes #6256
2025-04-10 14:09:55 -04:00
970cf7f017 skip unreliable AI tests (#6252)
skip unrealiable AI tests
2025-04-10 12:10:57 -04:00
d125efcd60 Bidirectional extrude/revolve (#6154)
* extend extrude endpoint

* revolve and mocks

* add bounds check to revolve

* kcl examples of new args

* update to 110

* fix mock

* move example to prelude

* change to camelCase

* new prelude tests

* extend just file

* missed change

* change to XY

* redo sim tests

* review changes

* redo markdown
2025-04-10 10:46:10 -04:00
4664427832 Add a sidebar action to create a share link (#6233) 2025-04-10 00:27:22 +00:00
d84b9cc875 Remove flaky pixel check from prompt-to-edit test (#6225)
remove flaky pixel check
2025-04-10 00:06:38 +00:00
0c6b6bf5d5 Fix bad merge with some KCL snapshots (#6239)
Caused by admin merge in #5803
2025-04-09 16:43:56 -04:00
d275995dfe KCL: Angled line should use keyword args (#5803)
We continue migrating KCL stdlib functions to use keyword arguments. Next up is the `angledLine` family of functions (except `angledLineThatIntersects, which will be a quick follow-up).

Before vs. after:

`angledLine({angle = 90, length = 3}, %, $edge)`
  => `angledLine(angle = 90, length = 3, tag = $edge)`

`angledLineOfXLength({angle = 90, length = 3}, %, $edge)`
  => `angledLine(angle = 90, lengthX = 3, tag = $edge)`

`angledLineOfYLength({angle = 90, length = 3}, %, $edge)`
  => `angledLine(angle = 90, lengthY = 3, tag = $edge)`

`angledLineToX({angle = 90, length = 3}, %, $edge)`
  => `angledLine(angle = 90, endAbsoluteX = 3, tag = $edge)`

`angledLineToY({angle = 90, length = 3}, %, $edge)`
  => `angledLine(angle = 90, endAbsoluteY = 3, tag = $edge)`
2025-04-09 14:55:15 -05:00
b03ca30379 Cleanup spatula example. (#6232)
* Cleanup spatula example. Naming better variables and removing unused tags

* Update kcl-samples simulation test output

* remove unused tag

* fix spacing

* Update kcl-samples simulation test output

* cleanup comments

* Update kcl-samples simulation test output

* re-add spacing

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Greg Sweeney <greg@kittycad.io>
Co-authored-by: jgomez720 <114548659+jgomez720@users.noreply.github.com>
2025-04-09 15:21:05 -04:00
e5f23a49b4 #4469 Improve toolbar ux (#5841)
* Improve ActionButtonDropdown selection

* center rectangle icon fixed

* ignore Esc key when displaying hotkeys

* add ability to escape 3 point circle tool

* remove focus from ActionButton, ActionButtonDropdown

* remove focus outline from buttons

* remember lastly selected multi action item

* Add tests for toolbar buttons

* fix sketch-tests by turning toolbar dropdown arrays into an object with an id - this got broken because dropdown now remember the last selected option so we cant rely on cant reference the first option in tests

* update other tests with open menu click
2025-04-09 14:32:52 +02:00
e78100eaac Assemblies: UX improvements around foreign file imports (#6159)
* 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

* Add default value for local name alias

* Aligning tests

* Fix tests

* Add padding for filenames starting with a digit

---------

Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>
2025-04-09 07:47:57 -04:00
ae9d8be4e4 Don't apply numeric adjustments in arithmetic yet (#6222)
Don't apply numeric adjustments yet

Signed-off-by: Nick Cameron <nrc@ncameron.org>
2025-04-09 14:16:58 +12:00
e4f73a6d5c Continue running tests with weak network (#6223) 2025-04-09 02:13:38 +00:00
a31fd608cf Improve hovers for KCL std lib fns (#6208)
Signed-off-by: Nick Cameron <nrc@ncameron.org>
2025-04-09 12:15:12 +12:00
a98d5aa2fb Fix to not filter out empty modules from the Feature Tree (#6224) 2025-04-08 23:56:27 +00:00
4c6ef841bb Handle PowerShell vs. Git Bash on Windows (#6207) 2025-04-08 23:47:56 +00:00
997f539a8c More numeric type propagations (#6221)
Last few numeric type propagations

Signed-off-by: Nick Cameron <nrc@ncameron.org>
2025-04-09 11:46:54 +12:00
83f74faaf7 fix nix (#6213)
Signed-off-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: Adam Sunderland <adam@kittycad.io>
2025-04-08 19:06:47 +00:00
0bb5797377 Fix unclickable state while opening share link in browser (#6201)
* Fix unclickable state, don't show warning if query present

* Leave a note about need for a web test

* Fix browser share links by waiting for network connection

* Don't worry about engineConnection on home route
2025-04-08 13:10:49 -04:00
c3097aa334 fix missing extension (#6205)
* fix missing extension

* fix e2e

* better name
2025-04-08 15:41:48 +00:00
2cd0fcc9a7 #6157 Dispatch wheel event to camControls when hovering segment label (#6158)
dispatch wheel event to camControls when hovering segment label
2025-04-08 11:12:12 -04:00
5b653fb558 Change unit conversion functions to operate on input rather than return a conversion factor (#6181)
Signed-off-by: Nick Cameron <nrc@ncameron.org>
2025-04-08 02:21:23 +00:00
6a5b23f848 Bump the minor group with 7 updates (#6191)
Bumps the minor group with 7 updates:

| Package | From | To |
| --- | --- | --- |
| [three](https://github.com/mrdoob/three.js) | `0.174.0` | `0.175.0` |
| [@types/three](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/three) | `0.174.0` | `0.175.0` |
| [@electron-forge/cli](https://github.com/electron/forge) | `7.7.0` | `7.8.0` |
| [@electron-forge/plugin-fuses](https://github.com/electron/forge) | `7.7.0` | `7.8.0` |
| [@electron-forge/plugin-vite](https://github.com/electron/forge/tree/HEAD/packages/plugin/vite) | `7.7.0` | `7.8.0` |
| [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `22.13.14` | `22.14.0` |
| [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.26.1` | `8.29.0` |


Updates `three` from 0.174.0 to 0.175.0
- [Release notes](https://github.com/mrdoob/three.js/releases)
- [Commits](https://github.com/mrdoob/three.js/commits)

Updates `@types/three` from 0.174.0 to 0.175.0
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/three)

Updates `@electron-forge/cli` from 7.7.0 to 7.8.0
- [Release notes](https://github.com/electron/forge/releases)
- [Changelog](https://github.com/electron/forge/blob/main/CHANGELOG.md)
- [Commits](https://github.com/electron/forge/compare/v7.7.0...v7.8.0)

Updates `@electron-forge/plugin-fuses` from 7.7.0 to 7.8.0
- [Release notes](https://github.com/electron/forge/releases)
- [Changelog](https://github.com/electron/forge/blob/main/CHANGELOG.md)
- [Commits](https://github.com/electron/forge/compare/v7.7.0...v7.8.0)

Updates `@electron-forge/plugin-vite` from 7.7.0 to 7.8.0
- [Release notes](https://github.com/electron/forge/releases)
- [Changelog](https://github.com/electron/forge/blob/main/CHANGELOG.md)
- [Commits](https://github.com/electron/forge/commits/v7.8.0/packages/plugin/vite)

Updates `@types/node` from 22.13.14 to 22.14.0
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `@types/three` from 0.174.0 to 0.175.0
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/three)

Updates `typescript-eslint` from 8.26.1 to 8.29.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.29.0/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: three
  dependency-version: 0.175.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: minor
- dependency-name: "@types/three"
  dependency-version: 0.175.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: minor
- dependency-name: "@electron-forge/cli"
  dependency-version: 7.8.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: minor
- dependency-name: "@electron-forge/plugin-fuses"
  dependency-version: 7.8.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: minor
- dependency-name: "@electron-forge/plugin-vite"
  dependency-version: 7.8.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: minor
- dependency-name: "@types/node"
  dependency-version: 22.14.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: minor
- dependency-name: "@types/three"
  dependency-version: 0.175.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: minor
- dependency-name: typescript-eslint
  dependency-version: 8.29.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-07 22:24:57 +00:00
a480783ee8 Quick app rename typo fix in settings.md (#6198) 2025-04-07 21:09:27 +00:00
bc0f5b5787 Add point-and-click Insert from local project files (#6129)
* 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

* @lrev-Dev's suggestion to remove a comment

Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>

* Update to scene.settled(cmdBar)

* Lint

---------

Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>
2025-04-07 20:28:11 +00:00
962eb0e376 Install and start Vector on macOS CI runners (#6147)
* Install vector on macOS

* Include platform information
2025-04-07 20:22:27 +00:00
ababe24b97 Implement polar std function in KCL (#6180)
Signed-off-by: Nick Cameron <nrc@ncameron.org>
2025-04-07 15:16:43 -05:00
1bf7a2abd4 Bump typescript from 5.8.2 to 5.8.3 in /packages/codemirror-lsp-client in the patch group (#6188)
Bump typescript in /packages/codemirror-lsp-client in the patch group

Bumps the patch group in /packages/codemirror-lsp-client with 1 update: [typescript](https://github.com/microsoft/TypeScript).


Updates `typescript` from 5.8.2 to 5.8.3
- [Release notes](https://github.com/microsoft/TypeScript/releases)
- [Changelog](https://github.com/microsoft/TypeScript/blob/main/azure-pipelines.release-publish.yml)
- [Commits](https://github.com/microsoft/TypeScript/commits)

---
updated-dependencies:
- dependency-name: typescript
  dependency-version: 5.8.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-07 20:12:36 +00:00
0ea4f94e8f Bump @types/node from 22.13.13 to 22.14.0 in /packages/codemirror-lsp-client in the minor group (#6189)
Bump @types/node in /packages/codemirror-lsp-client in the minor group

Bumps the minor group in /packages/codemirror-lsp-client with 1 update: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node).


Updates `@types/node` from 22.13.13 to 22.14.0
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 22.14.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-07 17:50:09 +00:00
6cc6130b6a Bump the major group in /packages/codemirror-lang-kcl with 2 updates (#6194)
Bumps the major group in /packages/codemirror-lang-kcl with 2 updates: [vite-tsconfig-paths](https://github.com/aleclarson/vite-tsconfig-paths) and [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest).


Updates `vite-tsconfig-paths` from 4.3.2 to 5.1.4
- [Release notes](https://github.com/aleclarson/vite-tsconfig-paths/releases)
- [Commits](https://github.com/aleclarson/vite-tsconfig-paths/compare/v4.3.2...v5.1.4)

Updates `vitest` from 2.1.9 to 3.1.1
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Commits](https://github.com/vitest-dev/vitest/commits/v3.1.1/packages/vitest)

---
updated-dependencies:
- dependency-name: vite-tsconfig-paths
  dependency-version: 5.1.4
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: major
- dependency-name: vitest
  dependency-version: 3.1.1
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-07 17:44:10 +00:00
8294903cd0 Bump taiki-e/install-action from 2.49.30 to 2.49.45 in the patch group (#6185)
Bumps the patch group with 1 update: [taiki-e/install-action](https://github.com/taiki-e/install-action).


Updates `taiki-e/install-action` from 2.49.30 to 2.49.45
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.49.30...v2.49.45)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-version: 2.49.45
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-07 17:43:36 +00:00
7b7379c3dd Bump the patch group with 6 updates (#6186)
Bumps the patch group with 6 updates:

| Package | From | To |
| --- | --- | --- |
| [@kittycad/lib](https://github.com/KittyCAD/kittycad.ts) | `2.0.26` | `2.0.28` |
| [@lezer/generator](https://github.com/lezer-parser/generator) | `1.7.2` | `1.7.3` |
| [@types/wicg-file-system-access](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/wicg-file-system-access) | `2023.10.5` | `2023.10.6` |
| [@types/ws](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/ws) | `8.18.0` | `8.18.1` |
| [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) | `7.37.4` | `7.37.5` |
| [typescript](https://github.com/microsoft/TypeScript) | `5.8.2` | `5.8.3` |


Updates `@kittycad/lib` from 2.0.26 to 2.0.28
- [Release notes](https://github.com/KittyCAD/kittycad.ts/releases)
- [Commits](https://github.com/KittyCAD/kittycad.ts/compare/v2.0.26...v2.0.28)

Updates `@lezer/generator` from 1.7.2 to 1.7.3
- [Changelog](https://github.com/lezer-parser/generator/blob/main/CHANGELOG.md)
- [Commits](https://github.com/lezer-parser/generator/compare/1.7.2...1.7.3)

Updates `@types/wicg-file-system-access` from 2023.10.5 to 2023.10.6
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/wicg-file-system-access)

Updates `@types/ws` from 8.18.0 to 8.18.1
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/ws)

Updates `eslint-plugin-react` from 7.37.4 to 7.37.5
- [Release notes](https://github.com/jsx-eslint/eslint-plugin-react/releases)
- [Changelog](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jsx-eslint/eslint-plugin-react/compare/v7.37.4...v7.37.5)

Updates `typescript` from 5.8.2 to 5.8.3
- [Release notes](https://github.com/microsoft/TypeScript/releases)
- [Changelog](https://github.com/microsoft/TypeScript/blob/main/azure-pipelines.release-publish.yml)
- [Commits](https://github.com/microsoft/TypeScript/commits)

---
updated-dependencies:
- dependency-name: "@kittycad/lib"
  dependency-version: 2.0.28
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch
- dependency-name: "@lezer/generator"
  dependency-version: 1.7.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: patch
- dependency-name: "@types/wicg-file-system-access"
  dependency-version: 2023.10.6
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: patch
- dependency-name: "@types/ws"
  dependency-version: 8.18.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: patch
- dependency-name: eslint-plugin-react
  dependency-version: 7.37.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: patch
- dependency-name: typescript
  dependency-version: 5.8.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-07 17:37:29 +00:00
9acdf1e42f Bump the patch group in /rust/kcl-language-server with 3 updates (#6183)
Bumps the patch group in /rust/kcl-language-server with 3 updates: [@vscode/vsce](https://github.com/Microsoft/vsce), [esbuild](https://github.com/evanw/esbuild) and [typescript](https://github.com/microsoft/TypeScript).


Updates `@vscode/vsce` from 3.3.0 to 3.3.2
- [Release notes](https://github.com/Microsoft/vsce/releases)
- [Commits](https://github.com/Microsoft/vsce/compare/v3.3.0...v3.3.2)

Updates `esbuild` from 0.25.1 to 0.25.2
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.25.1...v0.25.2)

Updates `typescript` from 5.8.2 to 5.8.3
- [Release notes](https://github.com/microsoft/TypeScript/releases)
- [Changelog](https://github.com/microsoft/TypeScript/blob/main/azure-pipelines.release-publish.yml)
- [Commits](https://github.com/microsoft/TypeScript/commits)

---
updated-dependencies:
- dependency-name: "@vscode/vsce"
  dependency-version: 3.3.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: patch
- dependency-name: esbuild
  dependency-version: 0.25.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: patch
- dependency-name: typescript
  dependency-version: 5.8.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-07 17:03:31 +00:00
5dd95d27de Bump the patch group in /packages/codemirror-lang-kcl with 2 updates (#6193)
Bumps the patch group in /packages/codemirror-lang-kcl with 2 updates: [@codemirror/state](https://github.com/codemirror/state) and [@lezer/generator](https://github.com/lezer-parser/generator).


Updates `@codemirror/state` from 6.5.0 to 6.5.2
- [Changelog](https://github.com/codemirror/state/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codemirror/state/compare/6.5.0...6.5.2)

Updates `@lezer/generator` from 1.7.2 to 1.7.3
- [Changelog](https://github.com/lezer-parser/generator/blob/main/CHANGELOG.md)
- [Commits](https://github.com/lezer-parser/generator/compare/1.7.2...1.7.3)

---
updated-dependencies:
- dependency-name: "@codemirror/state"
  dependency-version: 6.5.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch
- dependency-name: "@lezer/generator"
  dependency-version: 1.7.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-07 16:48:48 +00:00
1016b4c879 Remove unnecessary timeouts waiting for command bar (#6199) 2025-04-07 16:46:16 +00:00
df6f571294 Stream handling / Stream idle mode v2; a ton of network related changes (ping; scene indicator -> stream indicator, stream resizing (even on pause)) (#5312)
* Add back stream idle mode

* Shut up codespell

* Correct serialization; only expose at user level

* cargo fmt

* tsc lint fmt

* Move engineStreamMachine as a global actor; tons of more work

* Fix up everything after bumping kittycad/lib

* Remove camera sync

* Use pause/play iconology

* Add back better ping indicator

* wip

* Fix streamIdleMode checkbox being wonky

* yarn fmt

* Massive extinction event for waitForExecutionDone; try to stop projects view switching from crashing

* Clear diagnostics when unmounting code editor!

* wip

* Rework initial root projects dir + deflake many projects tests

* More e2e fixes

* Deflake revolve some revolve tests

* Fix the rest of the mfing tests

* yarn fmt

* yarn lint

* yarn tsc

* Fix tsc after rebase

* wip

* less flaky point and click

* wip

* Fixup after rebase

* Fix more tests

* Fix 2 more

* Fix up named-views tests

* yarn fmt lint tsc

* Fix up new changes

* Get rid of 1 cyclic dependency

* Fix another cyclic mfer!

* fmt

* fmt tsc

* Fix zoom to fit being frigged

* a new list of circular deps

* Remove NetworkHealthIndicator test that was shit

* Fix the bad reload repeat issue kevin started on

* Fix zoom to fit at the right moments...

* Fix cache count numbers in editor test

* Remove a test race - poll window info.

* Qualify fail function

* Try something

* Use scene.connectionEstablished

* Hopefully fix snapshots at least

* Add app console.log

* Fix native menu tests more

* tsc lint

* Fix camera failure

* Try again

* Test attempt number 15345203, action!

* Add back old window detection heuristic

* Remove firstWindow to complete the work of 2342d04fe2

* Tweak some tests for MacOS

* Tweak "set appearance" test for MacOS

Revert this if it messes up any other platform's color checks!

* Are you serious? This was all that needed formatting?

* More color tweaks

Local MacOS and CI MacOS don't agree

* Fixes on apperance e2e test for stream idle branch (#6168)

pierremtb/stream-idle-revamp-appearance-fixes

* Another apperance fix

* Skip one native menu test to make stream idle green (#6169)

* pierremtb/stream-idle-revamp-more-fixes

* Fix lint

* Update snapshot for test_generate_settings_docs

---------

Co-authored-by: lee-at-zoo-corp <lee@zoo.dev>
Co-authored-by: Frank Noirot <frankjohnson1993@gmail.com>
Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
Co-authored-by: Pierre Jacquier <pierre@zoo.dev>
2025-04-07 07:08:31 -04:00
be05dd7ba1 More propagation of numeric types (#6177)
Signed-off-by: Nick Cameron <nrc@ncameron.org>
2025-04-07 19:02:41 +12:00
bc22d888ee Apply type-directed coercions to arguments in calls of user functions (#6179)
Signed-off-by: Nick Cameron <nrc@ncameron.org>
2025-04-07 18:02:46 +12:00
e6ae89ebf9 Erase comment start positions from snapshot tests (#6178)
Signed-off-by: Nick Cameron <nrc@ncameron.org>
2025-04-07 17:42:15 +12:00
e7b23e1638 Implement coercion of numeric types for ascription and arithmetic (off by default) (#6175)
Signed-off-by: Nick Cameron <nrc@ncameron.org>
2025-04-07 16:13:15 +12:00
ce7a967f5f Reduce the number of reps in the add_lots test (#6174)
Signed-off-by: Nick Cameron <nrc@ncameron.org>
2025-04-06 22:41:48 -04:00
38446b5b2a take things off the batch in a more safe way (#6171)
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2025-04-06 03:54:32 +00:00
48e1a8ed02 attempt to import win-ca on windows (#6136)
* attempt to import win-ca on windows

otherwise we won't see system x509 cert authorities, which leaves people
high and dry if they're using an enterprise mitm tls box (😢)
2025-04-05 16:30:48 +00:00
a059166b9c Upgrade e2e-tests windows runner from 4 cores to 8 (#6166)
* Change e2e-tests runner to windows-latest-8-cores

* Clean up for PR
2025-04-05 08:44:50 -07:00
086a2b851d Follow-up fixes after bearing sample rename (#6164) 2025-04-05 11:00:47 +00:00
8e4c5fb24d Add test for #5799: "Only showing axis planes when there are no errors" (#6007)
* first pass at adding test for Only showing axis planes when there are no errors

* fix test Only show axis planes when there are no errors

* PR feedback
2025-04-04 23:41:09 +02:00
6993893600 Wait for export button to make test more reliable (#6143) 2025-04-04 16:53:57 -04:00
bfdf8babed sketching on a mirror2d thats been extruded fixed! (#6149)
* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* snap

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fixes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* add sample

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fixes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* snap

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2025-04-04 12:55:21 -07:00
708 changed files with 92430 additions and 90338 deletions

View File

@ -1,3 +1,3 @@
[codespell] [codespell]
ignore-words-list: crate,everytime,inout,co-ordinate,ot,nwo,atleast,ue,afterall,ser ignore-words-list: crate,everytime,inout,co-ordinate,ot,nwo,atleast,ue,afterall,ser,fromM,FromM
skip: **/target,node_modules,build,dist,./out,**/Cargo.lock,./docs/kcl/*.md,.yarn.lock,**/yarn.lock,./openapi/*.json,./packages/codemirror-lang-kcl/test/all.test.ts,./public/kcl-samples,./rust/kcl-lib/tests/kcl_samples,tsconfig.tsbuildinfo skip: **/target,node_modules,build,dist,./out,**/Cargo.lock,./docs/kcl/*.md,.yarn.lock,**/yarn.lock,./openapi/*.json,./packages/codemirror-lang-kcl/test/all.test.ts,./public/kcl-samples,./rust/kcl-lib/tests/kcl_samples,tsconfig.tsbuildinfo

24
.github/ci-cd-scripts/start-vector-macos.sh vendored Executable file
View File

@ -0,0 +1,24 @@
#!/bin/bash
set -euo pipefail
# Install vector
brew tap vectordotdev/brew && brew install vector
# Configure vector
mkdir -p /tmp/vector
cp .github/workflows/vector.toml /tmp/vector.toml
sed -i '' "s#OS_NAME#${OS_NAME}#g" /tmp/vector.toml
sed -i '' "s#GITHUB_WORKFLOW#${GITHUB_WORKFLOW}#g" /tmp/vector.toml
sed -i '' "s#GITHUB_REPOSITORY#${GITHUB_REPOSITORY}#g" /tmp/vector.toml
sed -i '' "s#GITHUB_SHA#${GITHUB_SHA}#g" /tmp/vector.toml
sed -i '' "s#GITHUB_REF_NAME#${GITHUB_REF_NAME}#g" /tmp/vector.toml
sed -i '' "s#GH_ACTIONS_AXIOM_TOKEN#${GH_ACTIONS_AXIOM_TOKEN}#g" /tmp/vector.toml
# Display settings
echo
echo 'Vector config:'
cat /tmp/vector.toml
echo
# Start in the background
$(brew --prefix)/opt/vector/bin/vector --config /tmp/vector.toml &

24
.github/ci-cd-scripts/start-vector-ubuntu.sh vendored Executable file
View File

@ -0,0 +1,24 @@
#!/bin/bash
set -euo pipefail
# Install vector
curl --proto '=https' --tlsv1.2 -sSfL https://sh.vector.dev | bash -s -- -y
# Configure vector
mkdir -p /tmp/vector
cp .github/workflows/vector.toml /tmp/vector.toml
sed -i "s#OS_NAME#${OS_NAME}#g" /tmp/vector.toml
sed -i "s#GITHUB_WORKFLOW#${GITHUB_WORKFLOW}#g" /tmp/vector.toml
sed -i "s#GITHUB_REPOSITORY#${GITHUB_REPOSITORY}#g" /tmp/vector.toml
sed -i "s#GITHUB_SHA#${GITHUB_SHA}#g" /tmp/vector.toml
sed -i "s#GITHUB_REF_NAME#${GITHUB_REF_NAME}#g" /tmp/vector.toml
sed -i "s#GH_ACTIONS_AXIOM_TOKEN#${GH_ACTIONS_AXIOM_TOKEN}#g" /tmp/vector.toml
# Display settings
echo
echo 'Vector config:'
cat /tmp/vector.toml
echo
# Start in background
${HOME}/.vector/bin/vector --config /tmp/vector.toml &

View File

@ -24,7 +24,7 @@ jobs:
uses: actions-rust-lang/setup-rust-toolchain@v1 uses: actions-rust-lang/setup-rust-toolchain@v1
with: with:
cache: false # Configured below. cache: false # Configured below.
- uses: taiki-e/install-action@37bdc826eaedac215f638a96472df572feab0f9b - uses: taiki-e/install-action@d4635f2de61c8b8104d59cd4aede2060638378cc
with: with:
tool: wasm-pack tool: wasm-pack
- name: Rust Cache - name: Rust Cache

View File

@ -10,7 +10,8 @@ on:
- 'nightly-v[0-9]+.[0-9]+.[0-9]+' - 'nightly-v[0-9]+.[0-9]+.[0-9]+'
env: env:
IS_RELEASE: ${{ github.ref_type == 'tag' && startsWith(github.ref_name, 'v') }} # IS_RELEASE: ${{ github.ref_type == 'tag' && startsWith(github.ref_name, 'v') }}
IS_RELEASE: true
IS_NIGHTLY: ${{ github.ref_type == 'tag' && startsWith(github.ref_name, 'nightly-v') }} IS_NIGHTLY: ${{ github.ref_type == 'tag' && startsWith(github.ref_name, 'nightly-v') }}
concurrency: concurrency:
@ -77,7 +78,7 @@ jobs:
with: with:
cache: false # Configured below. cache: false # Configured below.
- uses: taiki-e/install-action@37bdc826eaedac215f638a96472df572feab0f9b - uses: taiki-e/install-action@d4635f2de61c8b8104d59cd4aede2060638378cc
if: ${{ steps.wasm.outputs.should-build-wasm == 'true' }} if: ${{ steps.wasm.outputs.should-build-wasm == 'true' }}
with: with:
tool: wasm-pack tool: wasm-pack
@ -99,11 +100,11 @@ jobs:
yarn files:set-version yarn files:set-version
yarn files:flip-to-nightly yarn files:flip-to-nightly
- name: Set release version # - name: Set release version
if: ${{ env.IS_RELEASE == 'true' }} # if: ${{ env.IS_RELEASE == 'true' }}
run: | # run: |
export VERSION=${GITHUB_REF_NAME#v} # export VERSION=${GITHUB_REF_NAME#v}
yarn files:set-version # yarn files:set-version
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
with: with:
@ -183,30 +184,32 @@ jobs:
max_attempts: 3 max_attempts: 3
command: yarn install command: yarn install
# Next steps are from Digicert docs at
# https://docs.digicert.com/en/digicert-keylocker/ci-cd-integrations/scripts/github/scripts-for-signing-using-ksp-library-on-github.html#ksp-signing-using-github-action-488726
- name: Prepare certificate and variables (Windows only) - name: Prepare certificate and variables (Windows only)
if: ${{ (env.IS_RELEASE == 'true' || env.IS_NIGHTLY == 'true') && matrix.os == 'windows-2022' }} if: ${{ (env.IS_RELEASE == 'true' || env.IS_NIGHTLY == 'true') && matrix.os == 'windows-2022' }}
run: | run: |
echo "${{secrets.SM_CLIENT_CERT_FILE_B64 }}" | base64 --decode > /d/Certificate_pkcs12.p12 CERTIFICATE_PATH=$RUNNER_TEMP/certificate.p12
cat /d/Certificate_pkcs12.p12 echo "$SM_CLIENT_CERT_FILE_B64" | base64 --decode > $CERTIFICATE_PATH
echo "::set-output name=version::${GITHUB_REF#refs/tags/v}" echo "SM_CLIENT_CERT_FILE=$CERTIFICATE_PATH" >> "$GITHUB_ENV"
echo "SM_HOST=${{ secrets.SM_HOST }}" >> "$GITHUB_ENV" echo "SM_HOST=${{ secrets.SM_HOST }}" >> "$GITHUB_ENV"
echo "SM_API_KEY=${{ secrets.SM_API_KEY }}" >> "$GITHUB_ENV" echo "SM_API_KEY=${{ secrets.SM_API_KEY }}" >> "$GITHUB_ENV"
echo "SM_CLIENT_CERT_FILE=D:\\Certificate_pkcs12.p12" >> "$GITHUB_ENV"
echo "SM_CLIENT_CERT_PASSWORD=${{ secrets.SM_CLIENT_CERT_PASSWORD }}" >> "$GITHUB_ENV" echo "SM_CLIENT_CERT_PASSWORD=${{ secrets.SM_CLIENT_CERT_PASSWORD }}" >> "$GITHUB_ENV"
echo "C:\Program Files (x86)\Windows Kits\10\App Certification Kit" >> $GITHUB_PATH echo "C:\Program Files (x86)\Windows Kits\10\App Certification Kit" >> $GITHUB_PATH
echo "C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools" >> $GITHUB_PATH echo "C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools" >> $GITHUB_PATH
echo "C:\Program Files\DigiCert\DigiCert One Signing Manager Tools" >> $GITHUB_PATH echo "C:\Program Files\DigiCert\DigiCert Keylocker Tools" >> $GITHUB_PATH
shell: bash shell: bash
- name: Setup certicate with SSM KSP (Windows only) - name: Setup certicate with SSM KSP (Windows only)
if: ${{ (env.IS_RELEASE == 'true' || env.IS_NIGHTLY == 'true') && matrix.os == 'windows-2022' }} if: ${{ (env.IS_RELEASE == 'true' || env.IS_NIGHTLY == 'true') && matrix.os == 'windows-2022' }}
run: | run: |
curl -X GET https://one.digicert.com/signingmanager/api-ui/v1/releases/smtools-windows-x64.msi/download -H "x-api-key:%SM_API_KEY%" -o smtools-windows-x64.msi curl -X GET https://one.digicert.com/signingmanager/api-ui/v1/releases/Keylockertools-windows-x64.msi/download -H "x-api-key:%SM_API_KEY%" -o Keylockertools-windows-x64.msi
msiexec /i smtools-windows-x64.msi /quiet /qn msiexec /i Keylockertools-windows-x64.msi /quiet /qn
smksp_registrar.exe list smksp_registrar.exe list
smctl.exe keypair ls smctl.exe keypair ls
C:\Windows\System32\certutil.exe -csp "DigiCert Signing Manager KSP" -key -user C:\Windows\System32\certutil.exe -csp "DigiCert Signing Manager KSP" -key -user
smksp_cert_sync.exe smksp_cert_sync.exe
smctl windows certsync
shell: cmd shell: cmd
- name: Build the app (debug) - name: Build the app (debug)
@ -225,8 +228,8 @@ jobs:
CSC_LINK: ${{ secrets.APPLE_CERTIFICATE }} CSC_LINK: ${{ secrets.APPLE_CERTIFICATE }}
CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
CSC_KEYCHAIN: ${{ secrets.APPLE_SIGNING_IDENTITY }} CSC_KEYCHAIN: ${{ secrets.APPLE_SIGNING_IDENTITY }}
WINDOWS_CERTIFICATE_THUMBPRINT: ${{ secrets.WINDOWS_CERTIFICATE_THUMBPRINT }} # DEBUG: "electron-notarize*"
DEBUG: "electron-notarize*" DEBUG: electron-builder
# TODO: Fix electron-notarize flakes. The logs above should help gather more data on failures # TODO: Fix electron-notarize flakes. The logs above should help gather more data on failures
uses: nick-fields/retry@v3.0.2 uses: nick-fields/retry@v3.0.2
with: with:

View File

@ -34,20 +34,11 @@ jobs:
uses: actions-rust-lang/setup-rust-toolchain@v1 uses: actions-rust-lang/setup-rust-toolchain@v1
with: with:
cache: false # Configured below. cache: false # Configured below.
- name: Install vector - name: Start Vector
run: | run: .github/ci-cd-scripts/start-vector-ubuntu.sh
curl --proto '=https' --tlsv1.2 -sSfL https://sh.vector.dev > /tmp/vector.sh env:
chmod +x /tmp/vector.sh GH_ACTIONS_AXIOM_TOKEN: ${{ secrets.GH_ACTIONS_AXIOM_TOKEN }}
/tmp/vector.sh -y -no-modify-path OS_NAME: ${{ env.OS_NAME }}
mkdir -p /tmp/vector
cp .github/workflows/vector.toml /tmp/vector.toml
sed -i "s#GITHUB_WORKFLOW#${GITHUB_WORKFLOW}#g" /tmp/vector.toml
sed -i "s#GITHUB_REPOSITORY#${GITHUB_REPOSITORY}#g" /tmp/vector.toml
sed -i "s#GITHUB_SHA#${GITHUB_SHA}#g" /tmp/vector.toml
sed -i "s#GITHUB_REF_NAME#${GITHUB_REF_NAME}#g" /tmp/vector.toml
sed -i "s#GH_ACTIONS_AXIOM_TOKEN#${{secrets.GH_ACTIONS_AXIOM_TOKEN}}#g" /tmp/vector.toml
cat /tmp/vector.toml
${HOME}/.vector/bin/vector --config /tmp/vector.toml &
- uses: taiki-e/install-action@cargo-llvm-cov - uses: taiki-e/install-action@cargo-llvm-cov
- uses: taiki-e/install-action@nextest - uses: taiki-e/install-action@nextest
- name: Install just - name: Install just

View File

@ -140,7 +140,7 @@ jobs:
with: with:
cache: false # Configured below. cache: false # Configured below.
- uses: taiki-e/install-action@37bdc826eaedac215f638a96472df572feab0f9b - uses: taiki-e/install-action@d4635f2de61c8b8104d59cd4aede2060638378cc
if: ${{ needs.conditions.outputs.should-run == 'true' && steps.wasm.outputs.should-build-wasm == 'true' }} if: ${{ needs.conditions.outputs.should-run == 'true' && steps.wasm.outputs.should-build-wasm == 'true' }}
with: with:
tool: wasm-pack tool: wasm-pack
@ -281,7 +281,7 @@ jobs:
os: os:
- "runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64" - "runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64"
- namespace-profile-macos-8-cores - namespace-profile-macos-8-cores
- windows-latest - windows-latest-8-cores
shardIndex: [1, 2, 3, 4] shardIndex: [1, 2, 3, 4]
shardTotal: [4] shardTotal: [4]
# Disable macos and windows tests on hourly e2e tests since we only care # Disable macos and windows tests on hourly e2e tests since we only care
@ -292,7 +292,7 @@ jobs:
exclude: exclude:
- os: namespace-profile-macos-8-cores - os: namespace-profile-macos-8-cores
isScheduled: true isScheduled: true
- os: windows-latest - os: windows-latest-8-cores
isScheduled: true isScheduled: true
# TODO: add ref here for main and latest release tag # TODO: add ref here for main and latest release tag
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
@ -339,22 +339,12 @@ jobs:
if: needs.conditions.outputs.should-run == 'true' if: needs.conditions.outputs.should-run == 'true'
run: yarn tronb:vite:dev run: yarn tronb:vite:dev
- name: Install vector - name: Start Vector
if: ${{ needs.conditions.outputs.should-run == 'true' && contains(matrix.os, 'ubuntu') }} if: ${{ needs.conditions.outputs.should-run == 'true' && !contains(matrix.os, 'windows') }}
shell: bash run: .github/ci-cd-scripts/start-vector-${{ env.OS_NAME }}.sh
run: | env:
curl --proto '=https' --tlsv1.2 -sSfL https://sh.vector.dev > /tmp/vector.sh GH_ACTIONS_AXIOM_TOKEN: ${{ secrets.GH_ACTIONS_AXIOM_TOKEN }}
chmod +x /tmp/vector.sh OS_NAME: ${{ env.OS_NAME }}
/tmp/vector.sh -y -no-modify-path
mkdir -p /tmp/vector
cp .github/workflows/vector.toml /tmp/vector.toml
sed -i "s#GITHUB_WORKFLOW#${GITHUB_WORKFLOW}#g" /tmp/vector.toml
sed -i "s#GITHUB_REPOSITORY#${GITHUB_REPOSITORY}#g" /tmp/vector.toml
sed -i "s#GITHUB_SHA#${GITHUB_SHA}#g" /tmp/vector.toml
sed -i "s#GITHUB_REF_NAME#${GITHUB_REF_NAME}#g" /tmp/vector.toml
sed -i "s#GH_ACTIONS_AXIOM_TOKEN#${{secrets.GH_ACTIONS_AXIOM_TOKEN}}#g" /tmp/vector.toml
cat /tmp/vector.toml
${HOME}/.vector/bin/vector --config /tmp/vector.toml &
- uses: actions/download-artifact@v4 - uses: actions/download-artifact@v4
if: ${{ needs.conditions.outputs.should-run == 'true' && !cancelled() && (success() || failure()) }} if: ${{ needs.conditions.outputs.should-run == 'true' && !cancelled() && (success() || failure()) }}

View File

@ -51,7 +51,7 @@ jobs:
with: with:
cache: false # Configured below. cache: false # Configured below.
- uses: taiki-e/install-action@37bdc826eaedac215f638a96472df572feab0f9b - uses: taiki-e/install-action@d4635f2de61c8b8104d59cd4aede2060638378cc
with: with:
tool: wasm-pack tool: wasm-pack
@ -191,7 +191,7 @@ jobs:
cache: 'yarn' cache: 'yarn'
- run: yarn install - run: yarn install
- uses: taiki-e/install-action@37bdc826eaedac215f638a96472df572feab0f9b - uses: taiki-e/install-action@d4635f2de61c8b8104d59cd4aede2060638378cc
with: with:
tool: wasm-pack tool: wasm-pack
@ -236,7 +236,7 @@ jobs:
cache: 'yarn' cache: 'yarn'
- run: yarn install - run: yarn install
- uses: taiki-e/install-action@37bdc826eaedac215f638a96472df572feab0f9b - uses: taiki-e/install-action@d4635f2de61c8b8104d59cd4aede2060638378cc
with: with:
tool: wasm-pack tool: wasm-pack

View File

@ -8,6 +8,7 @@ include = ["/tmp/github-actions.log"]
type = "remap" type = "remap"
inputs = [ "github-actions-file" ] inputs = [ "github-actions-file" ]
source = ''' source = '''
.platform = "OS_NAME"
.action = "GITHUB_WORKFLOW" .action = "GITHUB_WORKFLOW"
.repo = "GITHUB_REPOSITORY" .repo = "GITHUB_REPOSITORY"
.sha = "GITHUB_SHA" .sha = "GITHUB_SHA"

View File

@ -5,8 +5,15 @@ all: install build check
# INSTALL # INSTALL
ifeq ($(OS),Windows_NT) ifeq ($(OS),Windows_NT)
CARGO ?= ~/.cargo/bin/cargo.exe export WINDOWS := true
WASM_PACK ?= ~/.cargo/bin/wasm-pack.exe ifndef MSYSTEM
export POWERSHELL := true
endif
endif
ifdef WINDOWS
CARGO ?= $(USERPROFILE)/.cargo/bin/cargo.exe
WASM_PACK ?= $(USERPROFILE)/.cargo/bin/wasm-pack.exe
else else
CARGO ?= ~/.cargo/bin/cargo CARGO ?= ~/.cargo/bin/cargo
WASM_PACK ?= ~/.cargo/bin/wasm-pack WASM_PACK ?= ~/.cargo/bin/wasm-pack
@ -17,21 +24,21 @@ install: node_modules/.yarn-integrity $(CARGO) $(WASM_PACK) ## Install dependenc
node_modules/.yarn-integrity: package.json yarn.lock node_modules/.yarn-integrity: package.json yarn.lock
yarn install yarn install
ifeq ($(OS),Windows_NT) ifdef POWERSHELL
@ type nul > $@ @ type nul > $@
else else
@ touch $@ @ touch $@
endif endif
$(CARGO): $(CARGO):
ifeq ($(OS),Windows_NT) ifdef WINDOWS
yarn install:rust:windows yarn install:rust:windows
else else
yarn install:rust yarn install:rust
endif endif
$(WASM_PACK): $(WASM_PACK):
ifeq ($(OS),Windows_NT) ifdef WINDOWS
yarn install:wasm-pack:cargo yarn install:wasm-pack:cargo
else else
yarn install:wasm-pack:sh yarn install:wasm-pack:sh
@ -57,7 +64,7 @@ build-web: install public/kcl_wasm_lib_bg.wasm build/index.html
build-desktop: install public/kcl_wasm_lib_bg.wasm .vite/build/main.js build-desktop: install public/kcl_wasm_lib_bg.wasm .vite/build/main.js
public/kcl_wasm_lib_bg.wasm: $(CARGO_SOURCES) $(RUST_SOURCES) public/kcl_wasm_lib_bg.wasm: $(CARGO_SOURCES) $(RUST_SOURCES)
ifeq ($(OS),Windows_NT) ifdef WINDOWS
yarn build:wasm:dev:windows yarn build:wasm:dev:windows
else else
yarn build:wasm:dev yarn build:wasm:dev
@ -140,8 +147,8 @@ endif
.PHONY: clean .PHONY: clean
clean: ## Delete all artifacts clean: ## Delete all artifacts
ifeq ($(OS),Windows_NT) ifdef POWERSHELL
git clean --force -d -X git clean --force -d -x --exclude=.env* --exclude=**/*.env
else else
rm -rf .vite/ build/ rm -rf .vite/ build/
rm -rf trace.zip playwright-report/ test-results/ rm -rf trace.zip playwright-report/ test-results/
@ -152,7 +159,7 @@ endif
.PHONY: help .PHONY: help
help: install help: install
ifeq ($(OS),Windows_NT) ifdef POWERSHELL
@ powershell -Command "Get-Content $(MAKEFILE_LIST) | Select-String -Pattern '^[^\s]+:.*##\s.*$$' | ForEach-Object { $$line = $$_.Line -split ':.*?##\s+'; Write-Host -NoNewline $$line[0].PadRight(30) -ForegroundColor Cyan; Write-Host $$line[1] }" @ powershell -Command "Get-Content $(MAKEFILE_LIST) | Select-String -Pattern '^[^\s]+:.*##\s.*$$' | ForEach-Object { $$line = $$_.Line -split ':.*?##\s+'; Write-Host -NoNewline $$line[0].PadRight(30) -ForegroundColor Cyan; Write-Host $$line[1] }"
else else
@ grep -E '^[^[:space:]]+:.*## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' @ grep -E '^[^[:space:]]+:.*## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'

View File

@ -36,9 +36,9 @@ myAngle = -120
sketch001 = startSketchOn(XZ) sketch001 = startSketchOn(XZ)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> line(end = [8, 0]) |> line(end = [8, 0])
|> angledLine({ angle = abs(myAngle), length = 5 }, %) |> angledLine(angle = abs(myAngle), length = 5)
|> line(end = [-5, 0]) |> line(end = [-5, 0])
|> angledLine({ angle = myAngle, length = 5 }, %) |> angledLine(angle = myAngle, length = 5)
|> close() |> close()
baseExtrusion = extrude(sketch001, length = 5) baseExtrusion = extrude(sketch001, length = 5)

View File

@ -33,10 +33,7 @@ acos(num: number): number
```js ```js
sketch001 = startSketchOn(XZ) sketch001 = startSketchOn(XZ)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> angledLine({ |> angledLine(angle = toDegrees(acos(0.5)), length = 10)
angle = toDegrees(acos(0.5)),
length = 10
}, %)
|> line(end = [5, 0]) |> line(end = [5, 0])
|> line(endAbsolute = [12, 0]) |> line(endAbsolute = [12, 0])
|> close() |> close()

View File

@ -36,7 +36,7 @@ angleToMatchLengthX(
sketch001 = startSketchOn(XZ) sketch001 = startSketchOn(XZ)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> line(end = [2, 5], tag = $seg01) |> line(end = [2, 5], tag = $seg01)
|> angledLineToX([-angleToMatchLengthX(seg01, 7, %), 10], %) |> angledLine(angle = -angleToMatchLengthX(seg01, 7, %), endAbsoluteX = 10)
|> close() |> close()
extrusion = extrude(sketch001, length = 5) extrusion = extrude(sketch001, length = 5)

View File

@ -36,10 +36,7 @@ angleToMatchLengthY(
sketch001 = startSketchOn(XZ) sketch001 = startSketchOn(XZ)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> line(end = [1, 2], tag = $seg01) |> line(end = [1, 2], tag = $seg01)
|> angledLine({ |> angledLine(angle = angleToMatchLengthY(seg01, 15, %), length = 5)
angle = angleToMatchLengthY(seg01, 15, %),
length = 5
}, %)
|> yLine(endAbsolute = 0) |> yLine(endAbsolute = 0)
|> close() |> close()

View File

@ -10,8 +10,13 @@ Draw a line segment relative to the current origin using the polar measure of so
```js ```js
angledLine( angledLine(
data: AngledLineData,
sketch: Sketch, sketch: Sketch,
angle: number,
length?: number,
lengthX?: number,
lengthY?: number,
endAbsoluteX?: number,
endAbsoluteY?: number,
tag?: TagDeclarator, tag?: TagDeclarator,
): Sketch ): Sketch
``` ```
@ -21,9 +26,14 @@ angledLine(
| Name | Type | Description | Required | | Name | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `data` | [`AngledLineData`](/docs/kcl/types/AngledLineData) | Data to draw an angled line. | Yes | | `sketch` | [`Sketch`](/docs/kcl/types/Sketch) | Which sketch should this path be added to? | Yes |
| `sketch` | [`Sketch`](/docs/kcl/types/Sketch) | | Yes | | `angle` | [`number`](/docs/kcl/types/number) | Which angle should the line be drawn at? | Yes |
| [`tag`](/docs/kcl/types/tag) | [`TagDeclarator`](/docs/kcl/types#tag-declaration) | | No | | `length` | [`number`](/docs/kcl/types/number) | Draw the line this distance along the given angle. Only one of `length`, `lengthX`, `lengthY`, `lengthAbsoluteEndX`, `lengthAbsoluteEndY` can be given. | No |
| `lengthX` | [`number`](/docs/kcl/types/number) | Draw the line this distance along the X axis. Only one of `length`, `lengthX`, `lengthY`, `lengthAbsoluteEndX`, `lengthAbsoluteEndY` can be given. | No |
| `lengthY` | [`number`](/docs/kcl/types/number) | Draw the line this distance along the Y axis. Only one of `length`, `lengthX`, `lengthY`, `lengthAbsoluteEndX`, `lengthAbsoluteEndY` can be given. | No |
| `endAbsoluteX` | [`number`](/docs/kcl/types/number) | Draw the line along the given angle until it reaches this point along the X axis. Only one of `length`, `lengthX`, `lengthY`, `lengthAbsoluteEndX`, `lengthAbsoluteEndY` can be given. | No |
| `endAbsoluteY` | [`number`](/docs/kcl/types/number) | Draw the line along the given angle until it reaches this point along the Y axis. Only one of `length`, `lengthX`, `lengthY`, `lengthAbsoluteEndX`, `lengthAbsoluteEndY` can be given. | No |
| [`tag`](/docs/kcl/types/tag) | [`TagDeclarator`](/docs/kcl/types#tag-declaration) | Create a new tag which refers to this line | No |
### Returns ### Returns
@ -36,7 +46,7 @@ angledLine(
exampleSketch = startSketchOn(XZ) exampleSketch = startSketchOn(XZ)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> yLine(endAbsolute = 15) |> yLine(endAbsolute = 15)
|> angledLine({ angle = 30, length = 15 }, %) |> angledLine(angle = 30, length = 15)
|> line(end = [8, -10]) |> line(end = [8, -10])
|> yLine(endAbsolute = 0) |> yLine(endAbsolute = 0)
|> close() |> close()

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -33,10 +33,7 @@ asin(num: number): number
```js ```js
sketch001 = startSketchOn(XZ) sketch001 = startSketchOn(XZ)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> angledLine({ |> angledLine(angle = toDegrees(asin(0.5)), length = 20)
angle = toDegrees(asin(0.5)),
length = 20
}, %)
|> yLine(endAbsolute = 0) |> yLine(endAbsolute = 0)
|> close() |> close()

View File

@ -33,10 +33,7 @@ atan(num: number): number
```js ```js
sketch001 = startSketchOn(XZ) sketch001 = startSketchOn(XZ)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> angledLine({ |> angledLine(angle = toDegrees(atan(1.25)), length = 20)
angle = toDegrees(atan(1.25)),
length = 20
}, %)
|> yLine(endAbsolute = 0) |> yLine(endAbsolute = 0)
|> close() |> close()

View File

@ -37,10 +37,7 @@ atan2(
```js ```js
sketch001 = startSketchOn(XZ) sketch001 = startSketchOn(XZ)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> angledLine({ |> angledLine(angle = toDegrees(atan2(1.25, 2)), length = 20)
angle = toDegrees(atan2(1.25, 2)),
length = 20
}, %)
|> yLine(endAbsolute = 0) |> yLine(endAbsolute = 0)
|> close() |> close()

View File

@ -17,10 +17,10 @@ std::math::E: number = 2.71828182845904523536028747135266250_
```js ```js
exampleSketch = startSketchOn(XZ) exampleSketch = startSketchOn(XZ)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> angledLine({ |> angledLine(
angle = 30, angle = 30,
length = 2 * E ^ 2, length = 2 * E ^ 2,
}, %) )
|> yLine(endAbsolute = 0) |> yLine(endAbsolute = 0)
|> close() |> close()

View File

@ -17,10 +17,10 @@ std::math::TAU: number = 6.28318530717958647692528676655900577_
```js ```js
exampleSketch = startSketchOn(XZ) exampleSketch = startSketchOn(XZ)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> angledLine({ |> angledLine(
angle = 50, angle = 50,
length = 10 * TAU, length = 10 * TAU,
}, %) )
|> yLine(endAbsolute = 0) |> yLine(endAbsolute = 0)
|> close() |> close()

View File

@ -30,7 +30,7 @@ e(): number
```js ```js
exampleSketch = startSketchOn(XZ) exampleSketch = startSketchOn(XZ)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> angledLine({ angle = 30, length = 2 * e() ^ 2 }, %) |> angledLine(angle = 30, length = 2 * e() ^ 2)
|> yLine(endAbsolute = 0) |> yLine(endAbsolute = 0)
|> close() |> close()

File diff suppressed because one or more lines are too long

45
docs/kcl/fromCm.md Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -30,10 +30,10 @@ getNextAdjacentEdge(tag: TagIdentifier): Uuid
exampleSketch = startSketchOn(XZ) exampleSketch = startSketchOn(XZ)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> line(end = [10, 0]) |> line(end = [10, 0])
|> angledLine({ angle = 60, length = 10 }, %) |> angledLine(angle = 60, length = 10)
|> angledLine({ angle = 120, length = 10 }, %) |> angledLine(angle = 120, length = 10)
|> line(end = [-10, 0]) |> line(end = [-10, 0])
|> angledLine({ angle = 240, length = 10 }, %, $referenceEdge) |> angledLine(angle = 240, length = 10, tag = $referenceEdge)
|> close() |> close()
example = extrude(exampleSketch, length = 5) example = extrude(exampleSketch, length = 5)

View File

@ -30,10 +30,10 @@ getOppositeEdge(tag: TagIdentifier): Uuid
exampleSketch = startSketchOn(XZ) exampleSketch = startSketchOn(XZ)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> line(end = [10, 0]) |> line(end = [10, 0])
|> angledLine({ angle = 60, length = 10 }, %) |> angledLine(angle = 60, length = 10)
|> angledLine({ angle = 120, length = 10 }, %) |> angledLine(angle = 120, length = 10)
|> line(end = [-10, 0]) |> line(end = [-10, 0])
|> angledLine({ angle = 240, length = 10 }, %, $referenceEdge) |> angledLine(angle = 240, length = 10, tag = $referenceEdge)
|> close() |> close()
example = extrude(exampleSketch, length = 5) example = extrude(exampleSketch, length = 5)

View File

@ -30,10 +30,10 @@ getPreviousAdjacentEdge(tag: TagIdentifier): Uuid
exampleSketch = startSketchOn(XZ) exampleSketch = startSketchOn(XZ)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> line(end = [10, 0]) |> line(end = [10, 0])
|> angledLine({ angle = 60, length = 10 }, %) |> angledLine(angle = 60, length = 10)
|> angledLine({ angle = 120, length = 10 }, %) |> angledLine(angle = 120, length = 10)
|> line(end = [-10, 0]) |> line(end = [-10, 0])
|> angledLine({ angle = 240, length = 10 }, %, $referenceEdge) |> angledLine(angle = 240, length = 10, tag = $referenceEdge)
|> close() |> close()
example = extrude(exampleSketch, length = 5) example = extrude(exampleSketch, length = 5)

File diff suppressed because one or more lines are too long

View File

@ -43,11 +43,7 @@ layout: manual
* [`angleToMatchLengthX`](kcl/angleToMatchLengthX) * [`angleToMatchLengthX`](kcl/angleToMatchLengthX)
* [`angleToMatchLengthY`](kcl/angleToMatchLengthY) * [`angleToMatchLengthY`](kcl/angleToMatchLengthY)
* [`angledLine`](kcl/angledLine) * [`angledLine`](kcl/angledLine)
* [`angledLineOfXLength`](kcl/angledLineOfXLength)
* [`angledLineOfYLength`](kcl/angledLineOfYLength)
* [`angledLineThatIntersects`](kcl/angledLineThatIntersects) * [`angledLineThatIntersects`](kcl/angledLineThatIntersects)
* [`angledLineToX`](kcl/angledLineToX)
* [`angledLineToY`](kcl/angledLineToY)
* [`appearance`](kcl/appearance) * [`appearance`](kcl/appearance)
* [`arc`](kcl/arc) * [`arc`](kcl/arc)
* [`arcTo`](kcl/arcTo) * [`arcTo`](kcl/arcTo)
@ -65,11 +61,15 @@ layout: manual
* [`chamfer`](kcl/chamfer) * [`chamfer`](kcl/chamfer)
* [`circleThreePoint`](kcl/circleThreePoint) * [`circleThreePoint`](kcl/circleThreePoint)
* [`close`](kcl/close) * [`close`](kcl/close)
* [`cm`](kcl/cm)
* [`extrude`](kcl/extrude) * [`extrude`](kcl/extrude)
* [`fillet`](kcl/fillet) * [`fillet`](kcl/fillet)
* [`floor`](kcl/floor) * [`floor`](kcl/floor)
* [`ft`](kcl/ft) * [`fromCm`](kcl/fromCm)
* [`fromFt`](kcl/fromFt)
* [`fromInches`](kcl/fromInches)
* [`fromM`](kcl/fromM)
* [`fromMm`](kcl/fromMm)
* [`fromYd`](kcl/fromYd)
* [`getCommonEdge`](kcl/getCommonEdge) * [`getCommonEdge`](kcl/getCommonEdge)
* [`getNextAdjacentEdge`](kcl/getNextAdjacentEdge) * [`getNextAdjacentEdge`](kcl/getNextAdjacentEdge)
* [`getOppositeEdge`](kcl/getOppositeEdge) * [`getOppositeEdge`](kcl/getOppositeEdge)
@ -77,7 +77,6 @@ layout: manual
* [`helix`](kcl/std-helix) * [`helix`](kcl/std-helix)
* [`hole`](kcl/hole) * [`hole`](kcl/hole)
* [`hollow`](kcl/hollow) * [`hollow`](kcl/hollow)
* [`inch`](kcl/inch)
* [`lastSegX`](kcl/lastSegX) * [`lastSegX`](kcl/lastSegX)
* [`lastSegY`](kcl/lastSegY) * [`lastSegY`](kcl/lastSegY)
* [`legAngX`](kcl/legAngX) * [`legAngX`](kcl/legAngX)
@ -89,11 +88,9 @@ layout: manual
* [`log`](kcl/log) * [`log`](kcl/log)
* [`log10`](kcl/log10) * [`log10`](kcl/log10)
* [`log2`](kcl/log2) * [`log2`](kcl/log2)
* [`m`](kcl/m)
* [`map`](kcl/map) * [`map`](kcl/map)
* [`max`](kcl/max) * [`max`](kcl/max)
* [`min`](kcl/min) * [`min`](kcl/min)
* [`mm`](kcl/mm)
* [`offsetPlane`](kcl/offsetPlane) * [`offsetPlane`](kcl/offsetPlane)
* [`patternCircular2d`](kcl/patternCircular2d) * [`patternCircular2d`](kcl/patternCircular2d)
* [`patternCircular3d`](kcl/patternCircular3d) * [`patternCircular3d`](kcl/patternCircular3d)
@ -101,7 +98,6 @@ layout: manual
* [`patternLinear3d`](kcl/patternLinear3d) * [`patternLinear3d`](kcl/patternLinear3d)
* [`patternTransform`](kcl/patternTransform) * [`patternTransform`](kcl/patternTransform)
* [`patternTransform2d`](kcl/patternTransform2d) * [`patternTransform2d`](kcl/patternTransform2d)
* [`polar`](kcl/polar)
* [`polygon`](kcl/polygon) * [`polygon`](kcl/polygon)
* [`pop`](kcl/pop) * [`pop`](kcl/pop)
* [`pow`](kcl/pow) * [`pow`](kcl/pow)
@ -137,12 +133,12 @@ layout: manual
* [`translate`](kcl/translate) * [`translate`](kcl/translate)
* [`xLine`](kcl/xLine) * [`xLine`](kcl/xLine)
* [`yLine`](kcl/yLine) * [`yLine`](kcl/yLine)
* [`yd`](kcl/yd)
* **std::math** * **std::math**
* [`E`](kcl/consts/std-math-E) * [`E`](kcl/consts/std-math-E)
* [`PI`](kcl/consts/std-math-PI) * [`PI`](kcl/consts/std-math-PI)
* [`TAU`](kcl/consts/std-math-TAU) * [`TAU`](kcl/consts/std-math-TAU)
* [`cos`](kcl/std-math-cos) * [`cos`](kcl/std-math-cos)
* [`polar`](kcl/std-math-polar)
* [`sin`](kcl/std-math-sin) * [`sin`](kcl/std-math-sin)
* [`tan`](kcl/std-math-tan) * [`tan`](kcl/std-math-tan)
* **std::sketch** * **std::sketch**

View File

@ -33,10 +33,7 @@ max(args: [number]): number
```js ```js
exampleSketch = startSketchOn(XZ) exampleSketch = startSketchOn(XZ)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> angledLine({ |> angledLine(angle = 70, length = max(15, 31, 4, 13, 22))
angle = 70,
length = max(15, 31, 4, 13, 22)
}, %)
|> line(end = [20, 0]) |> line(end = [20, 0])
|> close() |> close()

View File

@ -33,10 +33,7 @@ min(args: [number]): number
```js ```js
exampleSketch = startSketchOn(XZ) exampleSketch = startSketchOn(XZ)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> angledLine({ |> angledLine(angle = 70, length = min(15, 31, 4, 13, 22))
angle = 70,
length = min(15, 31, 4, 13, 22)
}, %)
|> line(end = [20, 0]) |> line(end = [20, 0])
|> close() |> close()

File diff suppressed because one or more lines are too long

View File

@ -37,7 +37,7 @@ pow(
```js ```js
exampleSketch = startSketchOn(XZ) exampleSketch = startSketchOn(XZ)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> angledLine({ angle = 50, length = pow(5, 2) }, %) |> angledLine(angle = 50, length = pow(5, 2))
|> yLine(endAbsolute = 0) |> yLine(endAbsolute = 0)
|> close() |> close()

View File

@ -29,11 +29,8 @@ profileStart(sketch: Sketch): [number]
```js ```js
sketch001 = startSketchOn(XY) sketch001 = startSketchOn(XY)
|> startProfileAt([5, 2], %) |> startProfileAt([5, 2], %)
|> angledLine({ angle = 120, length = 50 }, %, $seg01) |> angledLine(angle = 120, length = 50, tag = $seg01)
|> angledLine({ |> angledLine(angle = segAng(seg01) + 120, length = 50)
angle = segAng(seg01) + 120,
length = 50
}, %)
|> line(end = profileStart(%)) |> line(end = profileStart(%))
|> close() |> close()
|> extrude(length = 20) |> extrude(length = 20)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -78,7 +78,7 @@ assertEqual(sum, 6, 0.00001, "1 + 2 + 3 summed is 6")
```js ```js
// Declare a function that sketches a decagon. // Declare a function that sketches a decagon.
fn decagon(radius) { fn decagon(radius) {
// Each side of the decagon is turned this many degrees from the previous angle. // Each side of the decagon is turned this many radians from the previous angle.
stepAngle = 1 / 10 * TAU stepAngle = 1 / 10 * TAU
// Start the decagon sketch at this point. // Start the decagon sketch at this point.

View File

@ -151,15 +151,9 @@ cube
sketch001 = startSketchOn(XY) sketch001 = startSketchOn(XY)
rectangleSketch = startProfileAt([-200, 23.86], sketch001) rectangleSketch = startProfileAt([-200, 23.86], sketch001)
|> angledLine([0, 73.47], %, $rectangleSegmentA001) |> angledLine(angle = 0, length = 73.47, tag = $rectangleSegmentA001)
|> angledLine([ |> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 50.61)
segAng(rectangleSegmentA001) - 90, |> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001))
50.61
], %)
|> angledLine([
segAng(rectangleSegmentA001),
-segLen(rectangleSegmentA001)
], %)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close() |> close()

View File

@ -85,15 +85,9 @@ cube
sketch001 = startSketchOn(XY) sketch001 = startSketchOn(XY)
rectangleSketch = startProfileAt([-200, 23.86], sketch001) rectangleSketch = startProfileAt([-200, 23.86], sketch001)
|> angledLine([0, 73.47], %, $rectangleSegmentA001) |> angledLine(angle = 0, length = 73.47, tag = $rectangleSegmentA001)
|> angledLine([ |> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 50.61)
segAng(rectangleSegmentA001) - 90, |> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001))
50.61
], %)
|> angledLine([
segAng(rectangleSegmentA001),
-segLen(rectangleSegmentA001)
], %)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close() |> close()

View File

@ -32,9 +32,9 @@ exampleSketch = startSketchOn(XZ)
|> line(end = [10, 0]) |> line(end = [10, 0])
|> line(end = [5, 10], tag = $seg01) |> line(end = [5, 10], tag = $seg01)
|> line(end = [-10, 0]) |> line(end = [-10, 0])
|> angledLine([segAng(seg01), 10], %) |> angledLine(angle = segAng(seg01), length = 10)
|> line(end = [-10, 0]) |> line(end = [-10, 0])
|> angledLine([segAng(seg01), -15], %) |> angledLine(angle = segAng(seg01), length = -15)
|> close() |> close()
example = extrude(exampleSketch, length = 4) example = extrude(exampleSketch, length = 4)

View File

@ -29,9 +29,9 @@ segLen(tag: TagIdentifier): number
```js ```js
exampleSketch = startSketchOn(XZ) exampleSketch = startSketchOn(XZ)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> angledLine({ angle = 60, length = 10 }, %, $thing) |> angledLine(angle = 60, length = 10, tag = $thing)
|> tangentialArc({ offset = -120, radius = 5 }, %) |> tangentialArc({ offset = -120, radius = 5 }, %)
|> angledLine({ angle = -60, length = segLen(thing) }, %) |> angledLine(angle = -60, length = segLen(thing))
|> close() |> close()
example = extrude(exampleSketch, length = 5) example = extrude(exampleSketch, length = 5)

View File

@ -6,7 +6,7 @@ layout: manual
# KCL Settings # KCL Settings
There are three levels of settings available in the KittyCAD Design Studiolication: There are three levels of settings available in Zoo Design Studio:
1. [User Settings](/docs/kcl/settings/user): Global settings that apply to all projects, stored in `user.toml` 1. [User Settings](/docs/kcl/settings/user): Global settings that apply to all projects, stored in `user.toml`
2. [Project Settings](/docs/kcl/settings/project): Settings specific to a project, stored in `project.toml` 2. [Project Settings](/docs/kcl/settings/project): Settings specific to a project, stored in `project.toml`
@ -14,7 +14,7 @@ There are three levels of settings available in the KittyCAD Design Studiolicati
## Configuration Files ## Configuration Files
The KittyCAD Design Studio uses TOML files for configuration: Zoo Design Studio uses TOML files for configuration:
* **User Settings**: `user.toml` - See [complete documentation](/docs/kcl/settings/user) * **User Settings**: `user.toml` - See [complete documentation](/docs/kcl/settings/user)
* **Project Settings**: `project.toml` - See [complete documentation](/docs/kcl/settings/project) * **Project Settings**: `project.toml` - See [complete documentation](/docs/kcl/settings/project)

View File

@ -96,7 +96,7 @@ Permanently dismiss the banner warning to download the desktop app. This setting
##### stream_idle_mode ##### stream_idle_mode
When the user is idle, and this is true, the stream will be torn down. When the user is idle, teardown the stream after some time.
**Default:** None **Default:** None

View File

@ -33,7 +33,7 @@ sqrt(num: number): number
```js ```js
exampleSketch = startSketchOn(XZ) exampleSketch = startSketchOn(XZ)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> angledLine({ angle = 50, length = sqrt(2500) }, %) |> angledLine(angle = 50, length = sqrt(2500))
|> yLine(endAbsolute = 0) |> yLine(endAbsolute = 0)
|> close() |> close()

View File

@ -9,7 +9,15 @@ Create a helix.
```js ```js
helix(revolutions: number(_), angleStart: number(deg), ccw?: bool, radius?: number(mm), axis?: Axis3d | Edge, length?: number(mm), cylinder?: Solid): Helix helix(
revolutions: number(_),
angleStart: number(deg),
ccw?: bool,
radius?: number(mm),
axis?: Axis3d | Edge,
length?: number(mm),
cylinder?: Solid,
): Helix
``` ```

View File

@ -29,10 +29,10 @@ cos(@num: number(rad)): number(_)
```js ```js
exampleSketch = startSketchOn(XZ) exampleSketch = startSketchOn(XZ)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> angledLine({ |> angledLine(
angle = 30, angle = 30,
length = 3 / cos(toRadians(30)), length = 3 / cos(toRadians(30)),
}, %) )
|> yLine(endAbsolute = 0) |> yLine(endAbsolute = 0)
|> close() |> close()

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -29,10 +29,10 @@ tan(@num: number(rad)): number(_)
```js ```js
exampleSketch = startSketchOn(XZ) exampleSketch = startSketchOn(XZ)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> angledLine({ |> angledLine(
angle = 50, angle = 50,
length = 50 * tan(1/2), length = 50 * tan(1/2),
}, %) )
|> yLine(endAbsolute = 0) |> yLine(endAbsolute = 0)
|> close() |> close()

File diff suppressed because one or more lines are too long

View File

@ -6,10 +6,16 @@ layout: manual
Construct a 2-dimensional circle, of the specified radius, centered atthe provided (x, y) origin point. Construct a 2-dimensional circle, of the specified radius, centered at
the provided (x, y) origin point.
```js ```js
circle(@sketch_or_surface: Sketch | Plane | Face, center: Point2d, radius: number, tag?: tag): Sketch circle(
@sketch_or_surface: Sketch | Plane | Face,
center: Point2d,
radius: number,
tag?: tag,
): Sketch
``` ```

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -91,15 +91,9 @@ springSketch = startSketchOn(YZ)
sketch001 = startSketchOn(XY) sketch001 = startSketchOn(XY)
rectangleSketch = startProfileAt([-200, 23.86], sketch001) rectangleSketch = startProfileAt([-200, 23.86], sketch001)
|> angledLine([0, 73.47], %, $rectangleSegmentA001) |> angledLine(angle = 0, length = 73.47, tag = $rectangleSegmentA001)
|> angledLine([ |> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 50.61)
segAng(rectangleSegmentA001) - 90, |> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001))
50.61
], %)
|> angledLine([
segAng(rectangleSegmentA001),
-segLen(rectangleSegmentA001)
], %)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close() |> close()

View File

@ -32,10 +32,7 @@ pillSketch = startSketchOn(XZ)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> line(end = [20, 0]) |> line(end = [20, 0])
|> tangentialArcToRelative([0, 10], %, $arc1) |> tangentialArcToRelative([0, 10], %, $arc1)
|> angledLine({ |> angledLine(angle = tangentToEnd(arc1), length = 20)
angle = tangentToEnd(arc1),
length = 20
}, %)
|> tangentialArcToRelative([0, -10], %) |> tangentialArcToRelative([0, -10], %)
|> close() |> close()
@ -50,10 +47,7 @@ pillSketch = startSketchOn(XZ)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> line(end = [0, 20]) |> line(end = [0, 20])
|> tangentialArcTo([10, 20], %, $arc1) |> tangentialArcTo([10, 20], %, $arc1)
|> angledLine({ |> angledLine(angle = tangentToEnd(arc1), length = 20)
angle = tangentToEnd(arc1),
length = 20
}, %)
|> tangentialArcToRelative([-10, 0], %) |> tangentialArcToRelative([-10, 0], %)
|> close() |> close()
@ -66,10 +60,7 @@ pillExtrude = extrude(pillSketch, length = 10)
rectangleSketch = startSketchOn(XZ) rectangleSketch = startSketchOn(XZ)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> line(end = [10, 0], tag = $seg1) |> line(end = [10, 0], tag = $seg1)
|> angledLine({ |> angledLine(angle = tangentToEnd(seg1), length = 10)
angle = tangentToEnd(seg1),
length = 10
}, %)
|> line(end = [0, 10]) |> line(end = [0, 10])
|> line(end = [-20, 0]) |> line(end = [-20, 0])
|> close() |> close()
@ -83,7 +74,7 @@ rectangleExtrude = extrude(rectangleSketch, length = 10)
bottom = startSketchOn(XY) bottom = startSketchOn(XY)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> arcTo({ end = [10, 10], interior = [5, 1] }, %, $arc1) |> arcTo({ end = [10, 10], interior = [5, 1] }, %, $arc1)
|> angledLine([tangentToEnd(arc1), 20], %) |> angledLine(angle = tangentToEnd(arc1), length = 20)
|> close() |> close()
``` ```
@ -95,7 +86,7 @@ circSketch = startSketchOn(XY)
triangleSketch = startSketchOn(XY) triangleSketch = startSketchOn(XY)
|> startProfileAt([-5, 0], %) |> startProfileAt([-5, 0], %)
|> angledLine([tangentToEnd(circ), 10], %) |> angledLine(angle = tangentToEnd(circ), length = 10)
|> line(end = [-15, 0]) |> line(end = [-15, 0])
|> close() |> close()
``` ```

View File

@ -35,9 +35,9 @@ tangentialArc(
```js ```js
exampleSketch = startSketchOn(XZ) exampleSketch = startSketchOn(XZ)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> angledLine({ angle = 60, length = 10 }, %) |> angledLine(angle = 60, length = 10)
|> tangentialArc({ radius = 10, offset = -120 }, %) |> tangentialArc({ radius = 10, offset = -120 }, %)
|> angledLine({ angle = -60, length = 10 }, %) |> angledLine(angle = -60, length = 10)
|> close() |> close()
example = extrude(exampleSketch, length = 10) example = extrude(exampleSketch, length = 10)

View File

@ -35,7 +35,7 @@ tangentialArcTo(
```js ```js
exampleSketch = startSketchOn(XZ) exampleSketch = startSketchOn(XZ)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> angledLine({ angle = 60, length = 10 }, %) |> angledLine(angle = 60, length = 10)
|> tangentialArcTo([15, 15], %) |> tangentialArcTo([15, 15], %)
|> line(end = [10, -15]) |> line(end = [10, -15])
|> close() |> close()

View File

@ -35,7 +35,7 @@ tangentialArcToRelative(
```js ```js
exampleSketch = startSketchOn(XZ) exampleSketch = startSketchOn(XZ)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> angledLine({ angle = 45, length = 10 }, %) |> angledLine(angle = 45, length = 10)
|> tangentialArcToRelative([0, -10], %) |> tangentialArcToRelative([0, -10], %)
|> line(end = [-10, 0]) |> line(end = [-10, 0])
|> close() |> close()

View File

@ -30,7 +30,7 @@ tau(): number
```js ```js
exampleSketch = startSketchOn(XZ) exampleSketch = startSketchOn(XZ)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> angledLine({ angle = 50, length = 10 * tau() }, %) |> angledLine(angle = 50, length = 10 * tau())
|> yLine(endAbsolute = 0) |> yLine(endAbsolute = 0)
|> close() |> close()

View File

@ -33,10 +33,7 @@ toDegrees(num: number): number
```js ```js
exampleSketch = startSketchOn(XZ) exampleSketch = startSketchOn(XZ)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> angledLine({ |> angledLine(angle = 50, length = 70 * cos(toDegrees(pi() / 4)))
angle = 50,
length = 70 * cos(toDegrees(pi() / 4))
}, %)
|> yLine(endAbsolute = 0) |> yLine(endAbsolute = 0)
|> close() |> close()

View File

@ -33,10 +33,7 @@ toRadians(num: number): number
```js ```js
exampleSketch = startSketchOn(XZ) exampleSketch = startSketchOn(XZ)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> angledLine({ |> angledLine(angle = 50, length = 70 * cos(toRadians(45)))
angle = 50,
length = 70 * cos(toRadians(45))
}, %)
|> yLine(endAbsolute = 0) |> yLine(endAbsolute = 0)
|> close() |> close()

View File

@ -86,15 +86,9 @@ cube
sketch001 = startSketchOn(XY) sketch001 = startSketchOn(XY)
rectangleSketch = startProfileAt([-200, 23.86], sketch001) rectangleSketch = startProfileAt([-200, 23.86], sketch001)
|> angledLine([0, 73.47], %, $rectangleSegmentA001) |> angledLine(angle = 0, length = 73.47, tag = $rectangleSegmentA001)
|> angledLine([ |> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 50.61)
segAng(rectangleSegmentA001) - 90, |> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001))
50.61
], %)
|> angledLine([
segAng(rectangleSegmentA001),
-segLen(rectangleSegmentA001)
], %)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close() |> close()

View File

@ -184,15 +184,17 @@ way:
```norun ```norun
startSketchOn('XZ') startSketchOn('XZ')
|> startProfileAt(origin, %) |> startProfileAt(origin, %)
|> angledLine({angle = 0, length = 191.26}, %, $rectangleSegmentA001) |> angledLine(angle = 0, length = 191.26, tag = $rectangleSegmentA001)
|> angledLine({ |> angledLine(
angle = segAng(rectangleSegmentA001) - 90, angle = segAng(rectangleSegmentA001) - 90,
length = 196.99, length = 196.99,
}, %, $rectangleSegmentB001) tag = $rectangleSegmentB001,
|> angledLine({ )
|> angledLine(
angle = segAng(rectangleSegmentA001), angle = segAng(rectangleSegmentA001),
length = -segLen(rectangleSegmentA001), length = -segLen(rectangleSegmentA001),
}, %, $rectangleSegmentC001) tag = $rectangleSegmentC001,
)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close() |> close()
``` ```
@ -217,15 +219,17 @@ However if the code was written like this:
fn rect(origin) { fn rect(origin) {
return startSketchOn('XZ') return startSketchOn('XZ')
|> startProfileAt(origin, %) |> startProfileAt(origin, %)
|> angledLine({angle = 0, length = 191.26}, %, $rectangleSegmentA001) |> angledLine(angle = 0, length = 191.26, tag = $rectangleSegmentA001)
|> angledLine({ |> angledLine(
angle = segAng(rectangleSegmentA001) - 90, angle = segAng(rectangleSegmentA001) - 90,
length = 196.99 length = 196.99,
}, %, $rectangleSegmentB001) tag = $rectangleSegmentB001,
|> angledLine({ )
|> angledLine(
angle = segAng(rectangleSegmentA001), angle = segAng(rectangleSegmentA001),
length = -segLen(rectangleSegmentA001) length = -segLen(rectangleSegmentA001)
}, %, $rectangleSegmentC001) tag = $rectangleSegmentC001,
)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close() |> close()
} }
@ -245,15 +249,17 @@ For example the following code works.
fn rect(origin) { fn rect(origin) {
return startSketchOn('XZ') return startSketchOn('XZ')
|> startProfileAt(origin, %) |> startProfileAt(origin, %)
|> angledLine({angle = 0, length = 191.26}, %, $rectangleSegmentA001) |> angledLine(angle = 0, length = 191.26, tag = $rectangleSegmentA001)
|> angledLine({ |> angledLine(
angle = segAng(rectangleSegmentA001) - 90, angle = segAng(rectangleSegmentA001) - 90,
length = 196.99 length = 196.99,
}, %, $rectangleSegmentB001) tag = $rectangleSegmentB001,
|> angledLine({ )
|> angledLine(
angle = segAng(rectangleSegmentA001), angle = segAng(rectangleSegmentA001),
length = -segLen(rectangleSegmentA001) length = -segLen(rectangleSegmentA001)
}, %, $rectangleSegmentC001) tag = $rectangleSegmentC001,
)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close() |> close()
} }

View File

@ -1,22 +0,0 @@
---
title: "PolarCoordsData"
excerpt: "Data for polar coordinates."
layout: manual
---
Data for polar coordinates.
**Type:** `object`
## Properties
| Property | Type | Description | Required |
|----------|------|-------------|----------|
| `angle` |[`number`](/docs/kcl/types/number)| The angle of the line (in degrees). | No |
| `length` |[`number`](/docs/kcl/types/number)| The length of the line. | No |

View File

@ -14,15 +14,17 @@ way:
```js ```js
startSketchOn('XZ') startSketchOn('XZ')
|> startProfileAt(origin, %) |> startProfileAt(origin, %)
|> angledLine({angle = 0, length = 191.26}, %, $rectangleSegmentA001) |> angledLine(angle = 0, length = 191.26, tag = $rectangleSegmentA001)
|> angledLine({ |> angledLine(
angle = segAng(rectangleSegmentA001) - 90, angle = segAng(rectangleSegmentA001) - 90,
length = 196.99, length = 196.99,
}, %, $rectangleSegmentB001) tag = $rectangleSegmentB001,
|> angledLine({ )
|> angledLine(
angle = segAng(rectangleSegmentA001), angle = segAng(rectangleSegmentA001),
length = -segLen(rectangleSegmentA001), length = -segLen(rectangleSegmentA001),
}, %, $rectangleSegmentC001) tag = $rectangleSegmentC001,
)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close() |> close()
``` ```
@ -46,15 +48,16 @@ However if the code was written like this:
fn rect(origin) { fn rect(origin) {
return startSketchOn('XZ') return startSketchOn('XZ')
|> startProfileAt(origin, %) |> startProfileAt(origin, %)
|> angledLine({angle = 0, length = 191.26}, %, $rectangleSegmentA001) |> angledLine(angle = 0, length = 191.26, tag = $rectangleSegmentA001)
|> angledLine({ |> angledLine(
angle = segAng(rectangleSegmentA001) - 90, angle = segAng(rectangleSegmentA001) - 90,
length = 196.99 length = 196.99,
}, %, $rectangleSegmentB001) tag = $rectangleSegmentB001)
|> angledLine({ |> angledLine(
angle = segAng(rectangleSegmentA001), angle = segAng(rectangleSegmentA001),
length = -segLen(rectangleSegmentA001) length = -segLen(rectangleSegmentA001),
}, %, $rectangleSegmentC001) tag = $rectangleSegmentC001
)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close() |> close()
} }
@ -74,15 +77,15 @@ For example the following code works.
fn rect(origin) { fn rect(origin) {
return startSketchOn('XZ') return startSketchOn('XZ')
|> startProfileAt(origin, %) |> startProfileAt(origin, %)
|> angledLine({angle = 0, length = 191.26}, %, $rectangleSegmentA001) |> angledLine(angle = 0, length = 191.26, tag = $rectangleSegmentA001)
|> angledLine({ |> angledLine(
angle = segAng(rectangleSegmentA001) - 90, angle = segAng(rectangleSegmentA001) - 90,
length = 196.99 length = 196.99
}, %, $rectangleSegmentB001) , %, $rectangleSegmentB001)
|> angledLine({ |> angledLine(
angle = segAng(rectangleSegmentA001), angle = segAng(rectangleSegmentA001),
length = -segLen(rectangleSegmentA001) length = -segLen(rectangleSegmentA001)
}, %, $rectangleSegmentC001) , %, $rectangleSegmentC001)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close() |> close()
} }

View File

@ -38,10 +38,10 @@ xLine(
exampleSketch = startSketchOn(XZ) exampleSketch = startSketchOn(XZ)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> xLine(length = 15) |> xLine(length = 15)
|> angledLine({ angle = 80, length = 15 }, %) |> angledLine(angle = 80, length = 15)
|> line(end = [8, -10]) |> line(end = [8, -10])
|> xLine(length = 10) |> xLine(length = 10)
|> angledLine({ angle = 120, length = 30 }, %) |> angledLine(angle = 120, length = 30)
|> xLine(length = -15) |> xLine(length = -15)
|> close() |> close()

View File

@ -38,7 +38,7 @@ yLine(
exampleSketch = startSketchOn(XZ) exampleSketch = startSketchOn(XZ)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> yLine(length = 15) |> yLine(length = 15)
|> angledLine({ angle = 30, length = 15 }, %) |> angledLine(angle = 30, length = 15)
|> line(end = [8, -10]) |> line(end = [8, -10])
|> yLine(length = -5) |> yLine(length = -5)
|> close() |> close()

View File

@ -137,7 +137,7 @@ async function doBasicSketch(
await page.waitForTimeout(100) await page.waitForTimeout(100)
} }
await page.getByRole('button', { name: 'Length: open menu' }).click() await page.getByRole('button', { name: 'constraints: open menu' }).click()
await page.getByRole('button', { name: 'Equal Length' }).click() await page.getByRole('button', { name: 'Equal Length' }).click()
// Open the code pane. // Open the code pane.

View File

@ -38,7 +38,7 @@ test.describe('Point and click for boolean workflows', () => {
path.resolve( path.resolve(
__dirname, __dirname,
'../../', '../../',
'./rust/kcl-lib/e2e/executor/inputs/boolean-setup-with' './rust/kcl-lib/e2e/executor/inputs/boolean-setup-with-sketch-on-faces.kcl'
), ),
'utf-8' 'utf-8'
) )
@ -46,8 +46,6 @@ test.describe('Point and click for boolean workflows', () => {
localStorage.setItem('persistCode', file) localStorage.setItem('persistCode', file)
}, file) }, file)
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone()
await scene.settled(cmdBar) await scene.settled(cmdBar)
// Test coordinates for selection - these might need adjustment based on actual scene layout // Test coordinates for selection - these might need adjustment based on actual scene layout
@ -77,6 +75,7 @@ test.describe('Point and click for boolean workflows', () => {
// Select first object in the scene, expect there to be a pixel diff from the selection color change // Select first object in the scene, expect there to be a pixel diff from the selection color change
await clickFirstObject({ pixelDiff: 50 }) await clickFirstObject({ pixelDiff: 50 })
await page.waitForTimeout(1000)
// For subtract, we need to proceed to the next step before selecting the second object // For subtract, we need to proceed to the next step before selecting the second object
if (operationName !== 'subtract') { if (operationName !== 'subtract') {
@ -87,6 +86,8 @@ test.describe('Point and click for boolean workflows', () => {
// Select second object // Select second object
await clickSecondObject({ pixelDiff: 50 }) await clickSecondObject({ pixelDiff: 50 })
await page.waitForTimeout(1000)
// Confirm the operation in the command bar // Confirm the operation in the command bar
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()

View File

@ -4,6 +4,7 @@ import { uuidv4 } from '@src/lib/utils'
import type { HomePageFixture } from '@e2e/playwright/fixtures/homePageFixture' import type { HomePageFixture } from '@e2e/playwright/fixtures/homePageFixture'
import type { SceneFixture } from '@e2e/playwright/fixtures/sceneFixture' import type { SceneFixture } from '@e2e/playwright/fixtures/sceneFixture'
import type { ToolbarFixture } from '@e2e/playwright/fixtures/toolbarFixture'
import { getUtils } from '@e2e/playwright/test-utils' import { getUtils } from '@e2e/playwright/test-utils'
import { expect, test } from '@e2e/playwright/zoo-test' import { expect, test } from '@e2e/playwright/zoo-test'
@ -15,6 +16,7 @@ test.describe(
page: Page, page: Page,
homePage: HomePageFixture, homePage: HomePageFixture,
scene: SceneFixture, scene: SceneFixture,
toolbar: ToolbarFixture,
plane: string, plane: string,
clickCoords: { x: number; y: number } clickCoords: { x: number; y: number }
) => { ) => {
@ -59,9 +61,12 @@ test.describe(
await u.sendCustomCmd(updateCamCommand) await u.sendCustomCmd(updateCamCommand)
await u.closeDebugPanel() await u.closeDebugPanel()
await page.mouse.click(clickCoords.x, clickCoords.y) await page.mouse.click(clickCoords.x, clickCoords.y)
await page.waitForTimeout(600) // wait for animation await page.waitForTimeout(600) // wait for animation
await toolbar.waitUntilSketchingReady()
await expect( await expect(
page.getByRole('button', { name: 'line Line', exact: true }) page.getByRole('button', { name: 'line Line', exact: true })
).toBeVisible() ).toBeVisible()
@ -117,11 +122,12 @@ test.describe(
] ]
for (const config of planeConfigs) { for (const config of planeConfigs) {
test(config.plane, async ({ page, homePage, scene }) => { test(config.plane, async ({ page, homePage, scene, toolbar }) => {
await sketchOnPlaneAndBackSideTest( await sketchOnPlaneAndBackSideTest(
page, page,
homePage, homePage,
scene, scene,
toolbar,
config.plane, config.plane,
config.coords config.coords
) )

View File

@ -15,6 +15,7 @@ test.describe('Code pane and errors', { tag: ['@skipWin'] }, () => {
page, page,
homePage, homePage,
scene, scene,
cmdBar,
}) => { }) => {
const u = await getUtils(page) const u = await getUtils(page)
@ -36,7 +37,7 @@ extrude001 = extrude(sketch001, length = 5)`
await page.setBodyDimensions({ width: 1200, height: 500 }) await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.settled(cmdBar)
// Ensure no badge is present // Ensure no badge is present
const codePaneButtonHolder = page.locator('#code-button-holder') const codePaneButtonHolder = page.locator('#code-button-holder')
@ -171,6 +172,8 @@ extrude001 = extrude(sketch001, length = 5)`
context, context,
page, page,
homePage, homePage,
scene,
cmdBar,
}) => { }) => {
// Load the app with the working starter code // Load the app with the working starter code
await context.addInitScript((code) => { await context.addInitScript((code) => {
@ -180,9 +183,7 @@ extrude001 = extrude(sketch001, length = 5)`
await page.setBodyDimensions({ width: 1200, height: 500 }) await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
// FIXME: await scene.waitForExecutionDone() does not work. It still fails. await scene.settled(cmdBar)
// I needed to increase this timeout to get this to pass.
await page.waitForTimeout(10000)
// Ensure badge is present // Ensure badge is present
const codePaneButtonHolder = page.locator('#code-button-holder') const codePaneButtonHolder = page.locator('#code-button-holder')

View File

@ -317,9 +317,13 @@ test.describe('Command bar tests', { tag: ['@skipWin'] }, () => {
test('Can switch between sketch tools via command bar', async ({ test('Can switch between sketch tools via command bar', async ({
page, page,
homePage, homePage,
scene,
cmdBar,
toolbar,
}) => { }) => {
await page.setBodyDimensions({ width: 1200, height: 500 }) await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.settled(cmdBar)
const sketchButton = page.getByRole('button', { name: 'Start Sketch' }) const sketchButton = page.getByRole('button', { name: 'Start Sketch' })
const cmdBarButton = page.getByRole('button', { name: 'Commands' }) const cmdBarButton = page.getByRole('button', { name: 'Commands' })
@ -343,7 +347,9 @@ test.describe('Command bar tests', { tag: ['@skipWin'] }, () => {
// Start a sketch // Start a sketch
await sketchButton.click() await sketchButton.click()
await page.mouse.click(700, 200) await page.mouse.click(700, 200)
await toolbar.waitUntilSketchingReady()
// Switch between sketch tools via the command bar // Switch between sketch tools via the command bar
await expect(lineToolButton).toHaveAttribute('aria-pressed', 'true') await expect(lineToolButton).toHaveAttribute('aria-pressed', 'true')

View File

@ -11,7 +11,7 @@ import { expect, test } from '@e2e/playwright/zoo-test'
test( test(
'export works on the first try', 'export works on the first try',
{ tag: ['@electron', '@skipLocalEngine'] }, { tag: ['@electron', '@skipLocalEngine'] },
async ({ page, context, scene, tronApp }, testInfo) => { async ({ page, context, scene, tronApp, cmdBar }, testInfo) => {
if (!tronApp) { if (!tronApp) {
fail() fail()
} }
@ -32,53 +32,41 @@ test(
}) })
await page.setBodyDimensions({ width: 1200, height: 500 }) await page.setBodyDimensions({ width: 1200, height: 500 })
page.on('console', console.log)
await test.step('on open of project', async () => { await test.step('on open of project', async () => {
await expect(page.getByText(`bracket`)).toBeVisible() // Open the project
const projectName = page.getByText(`bracket`)
await expect(projectName).toBeVisible()
await projectName.click()
await scene.settled(cmdBar)
// open the project // Expect zero errors in gutter
await page.getByText(`bracket`).click()
// expect zero errors in guter
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible() await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
// export the model // Click the export button
const exportButton = page.getByTestId('export-pane-button') const exportButton = page.getByTestId('export-pane-button')
await expect(exportButton).toBeVisible() await expect(exportButton).toBeVisible()
await scene.waitForExecutionDone()
const gltfOption = page.getByText('glTF')
const submitButton = page.getByText('Confirm Export')
const exportingToastMessage = page.getByText(`Exporting...`)
const errorToastMessage = page.getByText(`Error while exporting`)
const engineErrorToastMessage = page.getByText(`Nothing to export`)
const alreadyExportingToastMessage = page.getByText(`Already exporting`)
// The open file's name is `main.kcl`, so the export file name should be `main.gltf`
const exportFileName = `main.gltf`
// Click the export button
await exportButton.click() await exportButton.click()
// Select the first format option
const gltfOption = cmdBar.selectOption({ name: 'glTF' })
const exportFileName = `main.gltf` // source file is named `main.kcl`
await expect(gltfOption).toBeVisible() await expect(gltfOption).toBeVisible()
await expect(page.getByText('STL')).toBeVisible()
await page.keyboard.press('Enter') await page.keyboard.press('Enter')
// Click the checkbox // Click the checkbox
const submitButton = page.getByText('Confirm Export')
await expect(submitButton).toBeVisible() await expect(submitButton).toBeVisible()
await page.waitForTimeout(500)
await page.keyboard.press('Enter') await page.keyboard.press('Enter')
// Find the toast.
// Look out for the toast message // Look out for the toast message
const exportingToastMessage = page.getByText(`Exporting...`)
const alreadyExportingToastMessage = page.getByText(`Already exporting`)
await expect(exportingToastMessage).toBeVisible() await expect(exportingToastMessage).toBeVisible()
await expect(alreadyExportingToastMessage).not.toBeVisible() await expect(alreadyExportingToastMessage).not.toBeVisible()
// Expect it to succeed. // Expect it to succeed
const errorToastMessage = page.getByText(`Error while exporting`)
const engineErrorToastMessage = page.getByText(`Nothing to export`)
await expect(errorToastMessage).not.toBeVisible() await expect(errorToastMessage).not.toBeVisible()
await expect(engineErrorToastMessage).not.toBeVisible() await expect(engineErrorToastMessage).not.toBeVisible()
@ -86,6 +74,7 @@ test(
await expect(successToastMessage).toBeVisible() await expect(successToastMessage).toBeVisible()
await expect(exportingToastMessage).not.toBeVisible() await expect(exportingToastMessage).not.toBeVisible()
// Check for the exported file
const firstFileFullPath = path.resolve( const firstFileFullPath = path.resolve(
getPlaywrightDownloadDir(tronApp.projectDirName), getPlaywrightDownloadDir(tronApp.projectDirName),
exportFileName exportFileName
@ -112,60 +101,50 @@ test(
const u = await getUtils(page) const u = await getUtils(page)
await u.openFilePanel() await u.openFilePanel()
// Click on the other file
const otherKclButton = page.getByRole('button', { name: 'other.kcl' }) const otherKclButton = page.getByRole('button', { name: 'other.kcl' })
// Click the file
await otherKclButton.click() await otherKclButton.click()
// Close the file pane // Close the file pane
await u.closeFilePanel() await u.closeFilePanel()
await scene.settled(cmdBar)
// FIXME: await scene.waitForExecutionDone() does not work. The modeling indicator stays in -receive-reliable and not execution done // Expect zero errors in gutter
await page.waitForTimeout(10000)
// expect zero errors in guter
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible() await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
// export the model // Click the export button
const exportButton = page.getByTestId('export-pane-button') const exportButton = page.getByTestId('export-pane-button')
await expect(exportButton).toBeVisible() await expect(exportButton).toBeVisible()
const gltfOption = page.getByText('glTF')
const submitButton = page.getByText('Confirm Export')
const exportingToastMessage = page.getByText(`Exporting...`)
const errorToastMessage = page.getByText(`Error while exporting`)
const engineErrorToastMessage = page.getByText(`Nothing to export`)
const alreadyExportingToastMessage = page.getByText(`Already exporting`)
// The open file's name is `other.kcl`, so the export file name should be `other.gltf`
const exportFileName = `other.gltf`
// Click the export button
await exportButton.click() await exportButton.click()
// Select the first format option
const gltfOption = cmdBar.selectOption({ name: 'glTF' })
const exportFileName = `other.gltf` // source file is named `other.kcl`
await expect(gltfOption).toBeVisible() await expect(gltfOption).toBeVisible()
await expect(page.getByText('STL')).toBeVisible()
await page.keyboard.press('Enter') await page.keyboard.press('Enter')
// Click the checkbox // Click the checkbox
const submitButton = page.getByText('Confirm Export')
await expect(submitButton).toBeVisible() await expect(submitButton).toBeVisible()
await page.keyboard.press('Enter') await page.keyboard.press('Enter')
// Find the toast.
// Look out for the toast message // Look out for the toast message
const exportingToastMessage = page.getByText(`Exporting...`)
const alreadyExportingToastMessage = page.getByText(`Already exporting`)
await expect(exportingToastMessage).toBeVisible() await expect(exportingToastMessage).toBeVisible()
await expect(alreadyExportingToastMessage).not.toBeVisible()
// Expect it to succeed
const errorToastMessage = page.getByText(`Error while exporting`)
const engineErrorToastMessage = page.getByText(`Nothing to export`)
await expect(errorToastMessage).not.toBeVisible()
await expect(engineErrorToastMessage).not.toBeVisible()
const successToastMessage = page.getByText(`Exported successfully`) const successToastMessage = page.getByText(`Exported successfully`)
await test.step('Check the success toast message shows and nothing else', async () => await expect(successToastMessage).toBeVisible()
Promise.all([ await expect(exportingToastMessage).not.toBeVisible()
expect(alreadyExportingToastMessage).not.toBeVisible(),
expect(errorToastMessage).not.toBeVisible(),
expect(engineErrorToastMessage).not.toBeVisible(),
expect(successToastMessage).toBeVisible(),
expect(exportingToastMessage).not.toBeVisible(),
]))
// Check for the exported file=
const secondFileFullPath = path.resolve( const secondFileFullPath = path.resolve(
getPlaywrightDownloadDir(tronApp.projectDirName), getPlaywrightDownloadDir(tronApp.projectDirName),
exportFileName exportFileName

View File

@ -78,12 +78,14 @@ sketch001 = startSketchOn(XY)
// Ensure we execute the first time. // Ensure we execute the first time.
await u.openDebugPanel() await u.openDebugPanel()
await expect( await expect
page.locator('[data-receive-command-type="scene_clear_all"]') .poll(() =>
).toHaveCount(1) page.locator('[data-receive-command-type="scene_clear_all"]').count()
await expect( )
page.locator('[data-message-type="execution-done"]') .toBe(1)
).toHaveCount(2) await expect
.poll(() => page.locator('[data-message-type="execution-done"]').count())
.toBe(2)
// Add whitespace to the end of the code. // Add whitespace to the end of the code.
await u.codeLocator.click() await u.codeLocator.click()
@ -110,12 +112,14 @@ sketch001 = startSketchOn(XY)
test('ensure we use the cache, and do not clear on append', async ({ test('ensure we use the cache, and do not clear on append', async ({
homePage, homePage,
page, page,
scene,
cmdBar,
}) => { }) => {
const u = await getUtils(page) const u = await getUtils(page)
await page.setBodyDimensions({ width: 1000, height: 500 }) await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await u.waitForPageLoad() await scene.settled(cmdBar)
await u.codeLocator.click() await u.codeLocator.click()
await page.keyboard.type(`sketch001 = startSketchOn(XY) await page.keyboard.type(`sketch001 = startSketchOn(XY)
@ -499,7 +503,7 @@ sketch_001 = startSketchOn(XY)
await page.keyboard.press('ArrowLeft') await page.keyboard.press('ArrowLeft')
await page.keyboard.press('ArrowRight') await page.keyboard.press('ArrowRight')
await scene.waitForExecutionDone() await scene.connectionEstablished()
// error in guter // error in guter
await expect(page.locator('.cm-lint-marker-info').first()).toBeVisible() await expect(page.locator('.cm-lint-marker-info').first()).toBeVisible()
@ -1315,4 +1319,85 @@ sketch001 = startSketchOn(XZ)
const element = page.locator('[data-overlay-index="1"]') const element = page.locator('[data-overlay-index="1"]')
await expect(element).toHaveAttribute('data-overlay-visible', 'true') await expect(element).toHaveAttribute('data-overlay-visible', 'true')
}) })
test(`Only show axis planes when there are no errors`, async ({
page,
homePage,
scene,
cmdBar,
}) => {
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`sketch001 = startSketchOn(XZ)
profile001 = circle(sketch001, center = [-100.0, -100.0], radius = 50.0)
sketch002 = startSketchOn(XZ)
profile002 = circle(sketch002, center = [-100.0, 100.0], radius = 50.0)
extrude001 = extrude(profile002, length = 0)` // length = 0 is causing the error
)
})
const viewportSize = { width: 1200, height: 800 }
await page.setBodyDimensions(viewportSize)
await homePage.goToModelingScene()
await scene.connectionEstablished()
await scene.settled(cmdBar)
await scene.expectPixelColor(
TEST_COLORS.DARK_MODE_BKGD,
// This is a position where the blue part of the axis plane is visible if its rendered
{ x: viewportSize.width * 0.75, y: viewportSize.height * 0.2 },
15
)
})
test(`test-toolbar-buttons`, async ({
page,
homePage,
toolbar,
scene,
cmdBar,
}) => {
await test.step('Load an empty file', async () => {
await page.addInitScript(async () => {
localStorage.setItem('persistCode', '')
})
await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene()
// wait until scene is ready to be interacted with
await scene.connectionEstablished()
await scene.settled(cmdBar)
})
await test.step('Test toolbar button correct selection', async () => {
await toolbar.expectToolbarMode.toBe('modeling')
await toolbar.startSketchPlaneSelection()
// Click on a default plane
await page.mouse.click(700, 200)
// tools cannot be selected immediately, couldn't find an event to await instead.
await page.waitForTimeout(1000)
await toolbar.selectCenterRectangle()
await expect(page.getByTestId('center-rectangle')).toHaveAttribute(
'aria-pressed',
'true'
)
})
await test.step('Test Toolbar dropdown remembering last selection', async () => {
// Select another tool
await page.getByTestId('circle-center').click()
// center-rectangle should still be the active option in the rectangle dropdown
await expect(page.getByTestId('center-rectangle')).toBeVisible()
})
})
}) })

View File

@ -19,7 +19,7 @@ length001 = timesFive(1) * 5
sketch001 = startSketchOn(XZ) sketch001 = startSketchOn(XZ)
|> startProfileAt([20, 10], %) |> startProfileAt([20, 10], %)
|> line(end = [10, 10]) |> line(end = [10, 10])
|> angledLine([-45, length001], %) |> angledLine(angle = -45, length = length001)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close() |> close()
revolve001 = revolve(sketch001, axis = X) revolve001 = revolve(sketch001, axis = X)
@ -38,15 +38,9 @@ extrude001 = extrude(sketch002, length = 10)
const FEATURE_TREE_SKETCH_CODE = `sketch001 = startSketchOn(XZ) const FEATURE_TREE_SKETCH_CODE = `sketch001 = startSketchOn(XZ)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> angledLine([0, 4], %, $rectangleSegmentA001) |> angledLine(angle = 0, length = 4, tag = $rectangleSegmentA001)
|> angledLine([ |> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 2, tag = $rectangleSegmentB001)
segAng(rectangleSegmentA001) - 90, |> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001), tag = $rectangleSegmentC001)
2
], %, $rectangleSegmentB001)
|> angledLine([
segAng(rectangleSegmentA001),
-segLen(rectangleSegmentA001)
], %, $rectangleSegmentC001)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close(%) |> close(%)
extrude001 = extrude(sketch001, length = 10) extrude001 = extrude(sketch001, length = 10)
@ -64,7 +58,7 @@ test.describe('Feature Tree pane', () => {
test( test(
'User can go to definition and go to function definition', 'User can go to definition and go to function definition',
{ tag: '@electron' }, { tag: '@electron' },
async ({ context, homePage, scene, editor, toolbar }) => { async ({ context, homePage, scene, editor, toolbar, cmdBar, page }) => {
await context.folderSetupFn(async (dir) => { await context.folderSetupFn(async (dir) => {
const bracketDir = join(dir, 'test-sample') const bracketDir = join(dir, 'test-sample')
await fsp.mkdir(bracketDir, { recursive: true }) await fsp.mkdir(bracketDir, { recursive: true })
@ -86,9 +80,13 @@ test.describe('Feature Tree pane', () => {
sortBy: 'last-modified-desc', sortBy: 'last-modified-desc',
}) })
await homePage.openProject('test-sample') await homePage.openProject('test-sample')
await scene.waitForExecutionDone() await scene.connectionEstablished()
await editor.closePane() await scene.settled(cmdBar)
await toolbar.openFeatureTreePane() await toolbar.openFeatureTreePane()
await expect
.poll(() => page.getByText('Feature tree').count())
.toBeGreaterThan(1)
}) })
async function testViewSource({ async function testViewSource({
@ -254,7 +252,7 @@ test.describe('Feature Tree pane', () => {
sortBy: 'last-modified-desc', sortBy: 'last-modified-desc',
}) })
await homePage.openProject('test-sample') await homePage.openProject('test-sample')
await scene.waitForExecutionDone() await scene.settled(cmdBar)
await toolbar.openFeatureTreePane() await toolbar.openFeatureTreePane()
}) })
@ -339,7 +337,7 @@ test.describe('Feature Tree pane', () => {
sortBy: 'last-modified-desc', sortBy: 'last-modified-desc',
}) })
await homePage.openProject('test-sample') await homePage.openProject('test-sample')
await scene.waitForExecutionDone() await scene.settled(cmdBar)
await toolbar.openFeatureTreePane() await toolbar.openFeatureTreePane()
}) })
@ -414,8 +412,7 @@ profile003 = startProfileAt([0, -4.93], sketch001)
const planeColor: [number, number, number] = [74, 74, 74] const planeColor: [number, number, number] = [74, 74, 74]
await homePage.openProject('test-sample') await homePage.openProject('test-sample')
// FIXME: @lf94 has a better way to verify execution completion, in a PR rn await scene.settled(cmdBar)
await scene.waitForExecutionDone()
await test.step(`Verify we see the sketch`, async () => { await test.step(`Verify we see the sketch`, async () => {
await scene.expectPixelColor(sketchColor, testPoint, 10) await scene.expectPixelColor(sketchColor, testPoint, 10)

View File

@ -47,6 +47,7 @@ test.describe('integrations tests', () => {
await scene.connectionEstablished() await scene.connectionEstablished()
await scene.settled(cmdBar) await scene.settled(cmdBar)
await clickObj() await clickObj()
await page.waitForTimeout(1000)
await scene.moveNoWhere() await scene.moveNoWhere()
await editor.expectState({ await editor.expectState({
activeLines: [ activeLines: [
@ -72,11 +73,11 @@ test.describe('integrations tests', () => {
}) })
await test.step('setup for next assertion', async () => { await test.step('setup for next assertion', async () => {
await toolbar.openFile('main.kcl') await toolbar.openFile('main.kcl')
await page.waitForTimeout(1000)
await scene.settled(cmdBar)
await clickObj() await clickObj()
await page.waitForTimeout(1000)
await scene.moveNoWhere() await scene.moveNoWhere()
await page.waitForTimeout(1000)
await editor.expectState({ await editor.expectState({
activeLines: [ activeLines: [
'|>startProfileAt([75.8,317.2],%)//[$startCapTag,$EndCapTag]', '|>startProfileAt([75.8,317.2],%)//[$startCapTag,$EndCapTag]',
@ -89,7 +90,7 @@ test.describe('integrations tests', () => {
await toolbar.expectFileTreeState(['main.kcl', fileName]) await toolbar.expectFileTreeState(['main.kcl', fileName])
}) })
await test.step('check sketch mode is exited when opening a different file', async () => { await test.step('check sketch mode is exited when opening a different file', async () => {
await toolbar.openFile(fileName, { wait: false }) await toolbar.openFile(fileName)
// check we're out of sketch mode // check we're out of sketch mode
await expect(toolbar.exitSketchBtn).not.toBeVisible() await expect(toolbar.exitSketchBtn).not.toBeVisible()

View File

@ -112,10 +112,7 @@ export class CmdBarFixture {
* and assumes we are past the `pickCommand` step. * and assumes we are past the `pickCommand` step.
*/ */
progressCmdBar = async (shouldFuzzProgressMethod = true) => { progressCmdBar = async (shouldFuzzProgressMethod = true) => {
// FIXME: Progressing the command bar is a race condition. We have an async useEffect that reports the final state via useCalculateKclExpression. If this does not run quickly enough, it will not "fail" the continue because you can press continue if the state is not ready. E2E tests do not know this. await this.page.waitForTimeout(2000)
// Wait 1250ms to assume the await executeAst of the KCL input field is finished
await this.page.waitForTimeout(1250)
if (shouldFuzzProgressMethod || Math.random() > 0.5) {
const arrowButton = this.page.getByRole('button', { const arrowButton = this.page.getByRole('button', {
name: 'arrow right Continue', name: 'arrow right Continue',
}) })
@ -126,9 +123,6 @@ export class CmdBarFixture {
.getByRole('button', { name: 'checkmark Submit command' }) .getByRole('button', { name: 'checkmark Submit command' })
.click() .click()
} }
} else {
await this.page.keyboard.press('Enter')
}
} }
// Added data-testid to the command bar buttons // Added data-testid to the command bar buttons

View File

@ -39,7 +39,8 @@ export class AuthenticatedApp {
} }
async initialise(code = '') { async initialise(code = '') {
await setup(this.context, this.page, this.testInfo) const testDir = this.testInfo.outputPath('electron-test-projects-dir')
await setup(this.context, this.page, testDir, this.testInfo)
const u = await getUtils(this.page) const u = await getUtils(this.page)
await this.page.addInitScript(async (code) => { await this.page.addInitScript(async (code) => {
@ -102,11 +103,11 @@ export class ElectronZoo {
return resolve(undefined) return resolve(undefined)
} }
if (Date.now() - timeA > 10000) { if (Date.now() - timeA > 3000) {
return resolve(undefined) return resolve(undefined)
} }
setTimeout(checkDisconnected, 0) setTimeout(checkDisconnected, 1)
} }
checkDisconnected() checkDisconnected()
}) })
@ -128,11 +129,9 @@ export class ElectronZoo {
const that = this const that = this
const options = { const options = {
timeout: 120000,
args: ['.', '--no-sandbox'], args: ['.', '--no-sandbox'],
env: { env: {
...process.env, ...process.env,
TEST_SETTINGS_FILE_KEY: this.projectDirName,
IS_PLAYWRIGHT: 'true', IS_PLAYWRIGHT: 'true',
}, },
...(process.env.ELECTRON_OVERRIDE_DIST_PATH ...(process.env.ELECTRON_OVERRIDE_DIST_PATH
@ -200,7 +199,14 @@ export class ElectronZoo {
await this.context.tracing.startChunk() await this.context.tracing.startChunk()
await setup(this.context, this.page, testInfo) // THIS IS ABSOLUTELY NECESSARY TO CHANGE THE PROJECT DIRECTORY BETWEEN
// TESTS BECAUSE OF THE ELECTRON INSTANCE REUSE.
await this.electron?.evaluate(({ app }, projectDirName) => {
// @ts-ignore can't declaration merge see main.ts
app.testProperty['TEST_SETTINGS_FILE_KEY'] = projectDirName
}, this.projectDirName)
await setup(this.context, this.page, this.projectDirName, testInfo)
await this.cleanProjectDir() await this.cleanProjectDir()
@ -250,11 +256,6 @@ export class ElectronZoo {
// return app.reuseWindowForTest(); // return app.reuseWindowForTest();
// }); // });
await this.electron?.evaluate(({ app }, projectDirName) => {
// @ts-ignore can't declaration merge see main.ts
app.testProperty['TEST_SETTINGS_FILE_KEY'] = projectDirName
}, this.projectDirName)
// Always start at the root view // Always start at the root view
await this.page.goto(this.firstUrl) await this.page.goto(this.firstUrl)
@ -278,8 +279,9 @@ export class ElectronZoo {
// Not a problem if it already exists. // Not a problem if it already exists.
} }
const tempSettingsFilePath = path.join( const tempSettingsFilePath = path.resolve(
this.projectDirName, this.projectDirName,
'..',
SETTINGS_FILE_NAME SETTINGS_FILE_NAME
) )

View File

@ -43,21 +43,15 @@ type DragFromHandler = (
export class SceneFixture { export class SceneFixture {
public page: Page public page: Page
public streamWrapper!: Locator public streamWrapper!: Locator
public loadingIndicator!: Locator
public networkToggleConnected!: Locator public networkToggleConnected!: Locator
public startEditSketchBtn!: Locator public startEditSketchBtn!: Locator
get exeIndicator() {
return this.page
.getByTestId('model-state-indicator-execution-done')
.or(this.page.getByTestId('model-state-indicator-receive-reliable'))
}
constructor(page: Page) { constructor(page: Page) {
this.page = page this.page = page
this.streamWrapper = page.getByTestId('stream') this.streamWrapper = page.getByTestId('stream')
this.networkToggleConnected = page.getByTestId('network-toggle-ok') this.networkToggleConnected = page
this.loadingIndicator = this.streamWrapper.getByTestId('loading') .getByTestId('network-toggle-ok')
.or(page.getByTestId('network-toggle-other'))
this.startEditSketchBtn = page this.startEditSketchBtn = page
.getByRole('button', { name: 'Start Sketch' }) .getByRole('button', { name: 'Start Sketch' })
.or(page.getByRole('button', { name: 'Edit Sketch' })) .or(page.getByRole('button', { name: 'Edit Sketch' }))
@ -231,10 +225,6 @@ export class SceneFixture {
} }
} }
waitForExecutionDone = async () => {
await expect(this.exeIndicator).toBeVisible({ timeout: 30000 })
}
connectionEstablished = async () => { connectionEstablished = async () => {
const timeout = 30000 const timeout = 30000
await expect(this.networkToggleConnected).toBeVisible({ timeout }) await expect(this.networkToggleConnected).toBeVisible({ timeout })
@ -243,6 +233,9 @@ export class SceneFixture {
settled = async (cmdBar: CmdBarFixture) => { settled = async (cmdBar: CmdBarFixture) => {
const u = await getUtils(this.page) const u = await getUtils(this.page)
await expect(this.startEditSketchBtn).not.toBeDisabled()
await expect(this.startEditSketchBtn).toBeVisible()
await cmdBar.openCmdBar() await cmdBar.openCmdBar()
await cmdBar.chooseCommand('Settings · app · show debug panel') await cmdBar.chooseCommand('Settings · app · show debug panel')
await cmdBar.selectOption({ name: 'on' }).click() await cmdBar.selectOption({ name: 'on' }).click()
@ -250,10 +243,6 @@ export class SceneFixture {
await u.openDebugPanel() await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]') await u.expectCmdLog('[data-message-type="execution-done"]')
await u.closeDebugPanel() await u.closeDebugPanel()
await this.waitForExecutionDone()
await expect(this.startEditSketchBtn).not.toBeDisabled()
await expect(this.startEditSketchBtn).toBeVisible()
} }
expectPixelColor = async ( expectPixelColor = async (

View File

@ -44,6 +44,7 @@ export class ToolbarFixture {
featureTreePane!: Locator featureTreePane!: Locator
gizmo!: Locator gizmo!: Locator
gizmoDisabled!: Locator gizmoDisabled!: Locator
insertButton!: Locator
constructor(page: Page) { constructor(page: Page) {
this.page = page this.page = page
@ -78,18 +79,14 @@ export class ToolbarFixture {
// element or two different elements can represent these states. // element or two different elements can represent these states.
this.gizmo = page.getByTestId('gizmo') this.gizmo = page.getByTestId('gizmo')
this.gizmoDisabled = page.getByTestId('gizmo-disabled') this.gizmoDisabled = page.getByTestId('gizmo-disabled')
this.insertButton = page.getByTestId('insert-pane-button')
} }
get logoLink() { get logoLink() {
return this.page.getByTestId('app-logo') return this.page.getByTestId('app-logo')
} }
get exeIndicator() {
return this.page
.getByTestId('model-state-indicator-receive-reliable')
.or(this.page.getByTestId('model-state-indicator-execution-done'))
}
startSketchPlaneSelection = async () => startSketchPlaneSelection = async () =>
doAndWaitForImageDiff(this.page, () => this.startSketchBtn.click(), 500) doAndWaitForImageDiff(this.page, () => this.startSketchBtn.click(), 500)
@ -165,20 +162,14 @@ export class ToolbarFixture {
} }
} }
/** /**
* Opens file by it's name and waits for execution to finish * Opens file by it's name
*/ */
openFile = async ( openFile = async (fileName: string) => {
fileName: string,
{ wait }: { wait?: boolean } = { wait: true }
) => {
await this.filePane.getByText(fileName).click() await this.filePane.getByText(fileName).click()
if (wait) {
await expect(this.exeIndicator).toBeVisible({ timeout: 15_000 })
}
} }
selectCenterRectangle = async () => { selectCenterRectangle = async () => {
await this.page await this.page
.getByRole('button', { name: 'caret down Corner rectangle:' }) .getByRole('button', { name: 'caret down rectangles:' })
.click() .click()
await expect( await expect(
this.page.getByTestId('dropdown-center-rectangle') this.page.getByTestId('dropdown-center-rectangle')
@ -187,7 +178,7 @@ export class ToolbarFixture {
} }
selectBoolean = async (operation: 'union' | 'subtract' | 'intersect') => { selectBoolean = async (operation: 'union' | 'subtract' | 'intersect') => {
await this.page await this.page
.getByRole('button', { name: 'caret down Union: open menu' }) .getByRole('button', { name: 'caret down booleans: open menu' })
.click() .click()
const operationTestId = `dropdown-boolean-${operation}` const operationTestId = `dropdown-boolean-${operation}`
await expect(this.page.getByTestId(operationTestId)).toBeVisible() await expect(this.page.getByTestId(operationTestId)).toBeVisible()
@ -195,25 +186,19 @@ export class ToolbarFixture {
} }
selectCircleThreePoint = async () => { selectCircleThreePoint = async () => {
await this.page await this.page.getByRole('button', { name: 'caret down circles:' }).click()
.getByRole('button', { name: 'caret down Center circle:' })
.click()
await expect( await expect(
this.page.getByTestId('dropdown-circle-three-points') this.page.getByTestId('dropdown-circle-three-points')
).toBeVisible() ).toBeVisible()
await this.page.getByTestId('dropdown-circle-three-points').click() await this.page.getByTestId('dropdown-circle-three-points').click()
} }
selectArc = async () => { selectArc = async () => {
await this.page await this.page.getByRole('button', { name: 'caret down arcs:' }).click()
.getByRole('button', { name: 'caret down Tangential Arc:' })
.click()
await expect(this.page.getByTestId('dropdown-arc')).toBeVisible() await expect(this.page.getByTestId('dropdown-arc')).toBeVisible()
await this.page.getByTestId('dropdown-arc').click() await this.page.getByTestId('dropdown-arc').click()
} }
selectThreePointArc = async () => { selectThreePointArc = async () => {
await this.page await this.page.getByRole('button', { name: 'caret down arcs:' }).click()
.getByRole('button', { name: 'caret down Tangential Arc:' })
.click()
await expect( await expect(
this.page.getByTestId('dropdown-three-point-arc') this.page.getByTestId('dropdown-three-point-arc')
).toBeVisible() ).toBeVisible()

View File

@ -10,6 +10,7 @@ test.describe('Import UI tests', () => {
toolbar, toolbar,
scene, scene,
editor, editor,
cmdBar,
}) => { }) => {
await context.folderSetupFn(async (dir) => { await context.folderSetupFn(async (dir) => {
const projectDir = path.join(dir, 'import-test') const projectDir = path.join(dir, 'import-test')
@ -20,15 +21,9 @@ test.describe('Import UI tests', () => {
path.join(projectDir, 'toBeImported.kcl'), path.join(projectDir, 'toBeImported.kcl'),
`sketch001 = startSketchOn(XZ) `sketch001 = startSketchOn(XZ)
profile001 = startProfileAt([281.54, 305.81], sketch001) profile001 = startProfileAt([281.54, 305.81], sketch001)
|> angledLine([0, 123.43], %, $rectangleSegmentA001) |> angledLine(angle = 0, length = 123.43, tag = $rectangleSegmentA001)
|> angledLine([ |> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 85.99)
segAng(rectangleSegmentA001) - 90, |> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001))
85.99
], %)
|> angledLine([
segAng(rectangleSegmentA001),
-segLen(rectangleSegmentA001)
], %)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close() |> close()
extrude(profile001, length = 100)` extrude(profile001, length = 100)`
@ -43,15 +38,9 @@ importedCube
sketch001 = startSketchOn(XZ) sketch001 = startSketchOn(XZ)
profile001 = startProfileAt([-134.53, -56.17], sketch001) profile001 = startProfileAt([-134.53, -56.17], sketch001)
|> angledLine([0, 79.05], %, $rectangleSegmentA001) |> angledLine(angle = 0, length = 79.05, tag = $rectangleSegmentA001)
|> angledLine([ |> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 76.28)
segAng(rectangleSegmentA001) - 90, |> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001), tag = $seg01)
76.28
], %)
|> angledLine([
segAng(rectangleSegmentA001),
-segLen(rectangleSegmentA001)
], %, $seg01)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg02) |> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg02)
|> close() |> close()
extrude001 = extrude(profile001, length = 100) extrude001 = extrude(profile001, length = 100)
@ -61,7 +50,7 @@ sketch002 = startSketchOn(extrude001, seg01)`
}) })
await homePage.openProject('import-test') await homePage.openProject('import-test')
await scene.waitForExecutionDone() await scene.settled(cmdBar)
await scene.moveCameraTo( await scene.moveCameraTo(
{ {

View File

@ -7,7 +7,7 @@ import { expect, test } from '@e2e/playwright/zoo-test'
test( test(
'When machine-api server not found butt is disabled and shows the reason', 'When machine-api server not found butt is disabled and shows the reason',
{ tag: '@electron' }, { tag: '@electron' },
async ({ context, page }, testInfo) => { async ({ context, page, scene, cmdBar }, testInfo) => {
await context.folderSetupFn(async (dir) => { await context.folderSetupFn(async (dir) => {
const bracketDir = join(dir, 'bracket') const bracketDir = join(dir, 'bracket')
await fsp.mkdir(bracketDir, { recursive: true }) await fsp.mkdir(bracketDir, { recursive: true })
@ -23,10 +23,7 @@ test(
await page.getByText('bracket').click() await page.getByText('bracket').click()
await expect(page.getByTestId('loading')).toBeAttached() await scene.settled(cmdBar)
await expect(page.getByTestId('loading')).not.toBeAttached({
timeout: 20_000,
})
const notFoundText = 'Machine API server was not discovered' const notFoundText = 'Machine API server was not discovered'
await expect(page.getByText(notFoundText).first()).not.toBeVisible() await expect(page.getByText(notFoundText).first()).not.toBeVisible()
@ -47,7 +44,7 @@ test(
test( test(
'When machine-api server not found home screen & project status shows the reason', 'When machine-api server not found home screen & project status shows the reason',
{ tag: '@electron' }, { tag: '@electron' },
async ({ context, page }, testInfo) => { async ({ context, page, scene, cmdBar }, testInfo) => {
await context.folderSetupFn(async (dir) => { await context.folderSetupFn(async (dir) => {
const bracketDir = join(dir, 'bracket') const bracketDir = join(dir, 'bracket')
await fsp.mkdir(bracketDir, { recursive: true }) await fsp.mkdir(bracketDir, { recursive: true })
@ -71,10 +68,7 @@ test(
await page.getByText('bracket').click() await page.getByText('bracket').click()
await expect(page.getByTestId('loading')).toBeAttached() await scene.settled(cmdBar)
await expect(page.getByTestId('loading')).not.toBeAttached({
timeout: 20_000,
})
await expect(page.getByText(notFoundText).nth(1)).not.toBeVisible() await expect(page.getByText(notFoundText).nth(1)).not.toBeVisible()

View File

@ -61,6 +61,7 @@ function tomlStringOverWriteNamedViewUuids(toml: string): string {
} }
test.describe('Named view tests', () => { test.describe('Named view tests', () => {
test.skip() // TODO: Jace is working on these
test('Verify project.toml is not created', async ({ page }, testInfo) => { test('Verify project.toml is not created', async ({ page }, testInfo) => {
// Create project and load it // Create project and load it
const projectName = 'named-views' const projectName = 'named-views'
@ -88,7 +89,7 @@ test.describe('Named view tests', () => {
// Create and load project // Create and load project
await createProject({ name: projectName, page }) await createProject({ name: projectName, page })
await scene.waitForExecutionDone() await scene.settled(cmdBar)
// Create named view // Create named view
const projectDirName = testInfo.outputPath('electron-test-projects-dir') const projectDirName = testInfo.outputPath('electron-test-projects-dir')
@ -110,14 +111,17 @@ test.describe('Named view tests', () => {
expect(exists).toBe(true) expect(exists).toBe(true)
}).toPass() }).toPass()
await expect(async () => {
// Read project.toml into memory // Read project.toml into memory
let tomlString = await fsp.readFile(tempProjectSettingsFilePath, 'utf-8') let tomlString = await fsp.readFile(tempProjectSettingsFilePath, 'utf-8')
// Rewrite the uuids in the named views to match snapshot otherwise they will be randomly generated from rust and break // Rewrite the uuids in the named views to match snapshot otherwise they will be randomly generated from rust and break
tomlString = tomlStringOverWriteNamedViewUuids(tomlString) tomlString = tomlStringOverWriteNamedViewUuids(tomlString)
// Write the entire tomlString to a snapshot. // Write the entire tomlString to a snapshot.
// There are many key/value pairs to check this is a safer match. // There are many key/value pairs to check this is a safer match.
expect(tomlString).toMatchSnapshot('verify-named-view-gets-created') expect(tomlString).toMatchSnapshot('verify-named-view-gets-created')
}).toPass()
}) })
test('Verify named view gets deleted', async ({ test('Verify named view gets deleted', async ({
cmdBar, cmdBar,
@ -130,7 +134,7 @@ test.describe('Named view tests', () => {
// Create project and go into the project // Create project and go into the project
await createProject({ name: projectName, page }) await createProject({ name: projectName, page })
await scene.waitForExecutionDone() await scene.settled(cmdBar)
// Create a new named view // Create a new named view
await cmdBar.openCmdBar() await cmdBar.openCmdBar()
@ -152,6 +156,7 @@ test.describe('Named view tests', () => {
expect(exists).toBe(true) expect(exists).toBe(true)
}).toPass() }).toPass()
await expect(async () => {
// Read project.toml into memory // Read project.toml into memory
let tomlString = await fsp.readFile(tempProjectSettingsFilePath, 'utf-8') let tomlString = await fsp.readFile(tempProjectSettingsFilePath, 'utf-8')
// Rewrite the uuids in the named views to match snapshot otherwise they will be randomly generated from rust and break // Rewrite the uuids in the named views to match snapshot otherwise they will be randomly generated from rust and break
@ -160,6 +165,7 @@ test.describe('Named view tests', () => {
// Write the entire tomlString to a snapshot. // Write the entire tomlString to a snapshot.
// There are many key/value pairs to check this is a safer match. // There are many key/value pairs to check this is a safer match.
expect(tomlString).toMatchSnapshot('verify-named-view-gets-created') expect(tomlString).toMatchSnapshot('verify-named-view-gets-created')
}).toPass()
// Delete a named view // Delete a named view
await cmdBar.openCmdBar() await cmdBar.openCmdBar()
@ -167,14 +173,16 @@ test.describe('Named view tests', () => {
cmdBar.selectOption({ name: myNamedView2 }) cmdBar.selectOption({ name: myNamedView2 })
await cmdBar.progressCmdBar(false) await cmdBar.progressCmdBar(false)
await expect(async () => {
// Read project.toml into memory again since we deleted a named view // Read project.toml into memory again since we deleted a named view
tomlString = await fsp.readFile(tempProjectSettingsFilePath, 'utf-8') let tomlString = await fsp.readFile(tempProjectSettingsFilePath, 'utf-8')
// Rewrite the uuids in the named views to match snapshot otherwise they will be randomly generated from rust and break // Rewrite the uuids in the named views to match snapshot otherwise they will be randomly generated from rust and break
tomlString = tomlStringOverWriteNamedViewUuids(tomlString) tomlString = tomlStringOverWriteNamedViewUuids(tomlString)
// // Write the entire tomlString to a snapshot. // // Write the entire tomlString to a snapshot.
// // There are many key/value pairs to check this is a safer match. // // There are many key/value pairs to check this is a safer match.
expect(tomlString).toMatchSnapshot('verify-named-view-gets-deleted') expect(tomlString).toMatchSnapshot('verify-named-view-gets-deleted')
}).toPass()
}) })
test('Verify named view gets loaded', async ({ test('Verify named view gets loaded', async ({
cmdBar, cmdBar,
@ -186,7 +194,7 @@ test.describe('Named view tests', () => {
// Create project and go into the project // Create project and go into the project
await createProject({ name: projectName, page }) await createProject({ name: projectName, page })
await scene.waitForExecutionDone() await scene.settled(cmdBar)
// Create a new named view // Create a new named view
await cmdBar.openCmdBar() await cmdBar.openCmdBar()
@ -208,6 +216,7 @@ test.describe('Named view tests', () => {
expect(exists).toBe(true) expect(exists).toBe(true)
}).toPass() }).toPass()
await expect(async () => {
// Read project.toml into memory // Read project.toml into memory
let tomlString = await fsp.readFile(tempProjectSettingsFilePath, 'utf-8') let tomlString = await fsp.readFile(tempProjectSettingsFilePath, 'utf-8')
// Rewrite the uuids in the named views to match snapshot otherwise they will be randomly generated from rust and break // Rewrite the uuids in the named views to match snapshot otherwise they will be randomly generated from rust and break
@ -216,6 +225,7 @@ test.describe('Named view tests', () => {
// Write the entire tomlString to a snapshot. // Write the entire tomlString to a snapshot.
// There are many key/value pairs to check this is a safer match. // There are many key/value pairs to check this is a safer match.
expect(tomlString).toMatchSnapshot('verify-named-view-gets-created') expect(tomlString).toMatchSnapshot('verify-named-view-gets-created')
}).toPass()
// Create a load a named view // Create a load a named view
await cmdBar.openCmdBar() await cmdBar.openCmdBar()
@ -239,7 +249,7 @@ test.describe('Named view tests', () => {
// Create and load project // Create and load project
await createProject({ name: projectName, page }) await createProject({ name: projectName, page })
await scene.waitForExecutionDone() await scene.settled(cmdBar)
// Create named view // Create named view
const projectDirName = testInfo.outputPath('electron-test-projects-dir') const projectDirName = testInfo.outputPath('electron-test-projects-dir')
@ -282,6 +292,7 @@ test.describe('Named view tests', () => {
expect(exists).toBe(true) expect(exists).toBe(true)
}).toPass() }).toPass()
await expect(async () => {
// Read project.toml into memory // Read project.toml into memory
let tomlString = await fsp.readFile(tempProjectSettingsFilePath, 'utf-8') let tomlString = await fsp.readFile(tempProjectSettingsFilePath, 'utf-8')
// Rewrite the uuids in the named views to match snapshot otherwise they will be randomly generated from rust and break // Rewrite the uuids in the named views to match snapshot otherwise they will be randomly generated from rust and break
@ -290,5 +301,6 @@ test.describe('Named view tests', () => {
// Write the entire tomlString to a snapshot. // Write the entire tomlString to a snapshot.
// There are many key/value pairs to check this is a safer match. // There are many key/value pairs to check this is a safer match.
expect(tomlString).toMatchSnapshot('verify-two-named-view-gets-created') expect(tomlString).toMatchSnapshot('verify-two-named-view-gets-created')
}).toPass()
}) })
}) })

View File

@ -0,0 +1,16 @@
[settings]
modeling = { }
text_editor = { }
command_bar = { }
[settings.app.named_views.0656fb1a-9640-473e-b334-591dc70c0138]
name = "uuid1"
eye_offset = 1_378.0059
fov_y = 45
is_ortho = false
ortho_scale_enabled = true
ortho_scale_factor = 1.6
pivot_position = [ 0, 0, 0 ]
pivot_rotation = [ 0.5380994, 0.0, 0.0, 0.8428814 ]
world_coord_system = "right_handed_up_z"
version = 1

View File

@ -0,0 +1,28 @@
[settings]
modeling = { }
text_editor = { }
command_bar = { }
[settings.app.named_views.0656fb1a-9640-473e-b334-591dc70c0138]
name = "uuid1"
eye_offset = 1_378.0059
fov_y = 45
is_ortho = false
ortho_scale_enabled = true
ortho_scale_factor = 1.6
pivot_position = [ 0, 0, 0 ]
pivot_rotation = [ 0.5380994, 0.0, 0.0, 0.8428814 ]
world_coord_system = "right_handed_up_z"
version = 1
[settings.app.named_views.c810cf04-c6cc-4a4a-8b11-17bf445dcab7]
name = "uuid2"
eye_offset = 1_378.0059
fov_y = 45
is_ortho = false
ortho_scale_enabled = true
ortho_scale_factor = 1.6
pivot_position = [ 1_826.5239, 0.0, 0.0 ]
pivot_rotation = [ 0.5380994, 0.0, 0.0, 0.8428814 ]
world_coord_system = "right_handed_up_z"
version = 1

View File

@ -1,4 +1,5 @@
import { throwTronAppMissing } from '@e2e/playwright/lib/electron-helpers' import { throwTronAppMissing } from '@e2e/playwright/lib/electron-helpers'
import { orRunWhenFullSuiteEnabled } from '@e2e/playwright/test-utils'
import { expect, test } from '@e2e/playwright/zoo-test' import { expect, test } from '@e2e/playwright/zoo-test'
/** /**
@ -6,19 +7,28 @@ import { expect, test } from '@e2e/playwright/zoo-test'
* Test file menu actions that trigger something in the frontend * Test file menu actions that trigger something in the frontend
*/ */
test.describe('Native file menu', { tag: ['@electron'] }, () => { test.describe('Native file menu', { tag: ['@electron'] }, () => {
test.skip() // TODO: Reimplement native file menu tests
test.describe('Home page', () => { test.describe('Home page', () => {
test.describe('File role', () => { test.describe('File role', () => {
test('Home.File.Create project', async ({ tronApp, cmdBar, page }) => { test('Home.File.Create project', async ({ tronApp, cmdBar, page }) => {
if (!tronApp) fail() if (!tronApp) fail()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
await expect
.poll(
async () =>
await tronApp.electron.evaluate(async ({ app }) => { await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail() if (!app || !app.applicationMenu) {
return false
}
const newProject = const newProject =
app.applicationMenu.getMenuItemById('File.New project') app.applicationMenu.getMenuItemById('File.New project')
if (!newProject) fail() if (!newProject) return false
newProject.click() newProject.click()
return true
}) })
)
.toBe(true)
// Check that the command bar is opened // Check that the command bar is opened
await expect(cmdBar.cmdBarElement).toBeVisible() await expect(cmdBar.cmdBarElement).toBeVisible()
// Check the placeholder project name exists // Check the placeholder project name exists
@ -32,13 +42,21 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
if (!tronApp) fail() if (!tronApp) fail()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
await expect
.poll(
async () =>
await tronApp.electron.evaluate(async ({ app }) => { await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail() if (!app || !app.applicationMenu) return false
const openProject = const openProject =
app.applicationMenu.getMenuItemById('File.Open project') app.applicationMenu.getMenuItemById('File.Open project')
if (!openProject) fail() if (!openProject) {
return false
}
openProject.click() openProject.click()
return true
}) })
)
.toBe(true)
// Check that the command bar is opened // Check that the command bar is opened
await expect(cmdBar.cmdBarElement).toBeVisible() await expect(cmdBar.cmdBarElement).toBeVisible()
// Check the placeholder project name exists // Check the placeholder project name exists
@ -56,14 +74,23 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
if (!tronApp) fail() if (!tronApp) fail()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
await expect
.poll(
async () =>
await tronApp.electron.evaluate(async ({ app }) => { await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail() console.log(app)
if (!app || !app.applicationMenu) {
return false
}
const userSettings = app.applicationMenu.getMenuItemById( const userSettings = app.applicationMenu.getMenuItemById(
'File.Preferences.User settings' 'File.Preferences.User settings'
) )
if (!userSettings) fail() if (!userSettings) return false
userSettings.click() userSettings.click()
return true
}) })
)
.toBe(true)
const settings = page.getByTestId('settings-dialog-panel') const settings = page.getByTestId('settings-dialog-panel')
await expect(settings).toBeVisible() await expect(settings).toBeVisible()
// You are viewing the user tab // You are viewing the user tab
@ -77,17 +104,27 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
cmdBar, cmdBar,
page, page,
}) => { }) => {
if (!tronApp) fail() if (!tronApp) {
fail()
}
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
await expect
.poll(
async () =>
await tronApp.electron.evaluate(async ({ app }) => { await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail() if (!app || !app.applicationMenu) return false
const keybindings = app.applicationMenu.getMenuItemById( const keybindings = app.applicationMenu.getMenuItemById(
'File.Preferences.Keybindings' 'File.Preferences.Keybindings'
) )
if (!keybindings) fail() if (!keybindings) {
return false
}
keybindings.click() keybindings.click()
return true
}) })
)
.toBe(true)
const settings = page.getByTestId('settings-dialog-panel') const settings = page.getByTestId('settings-dialog-panel')
await expect(settings).toBeVisible() await expect(settings).toBeVisible()
// You are viewing the keybindings tab // You are viewing the keybindings tab
@ -102,14 +139,22 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
if (!tronApp) fail() if (!tronApp) fail()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
await expect
.poll(
async () =>
await tronApp.electron.evaluate(async ({ app }) => { await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail() if (!app || !app.applicationMenu) {
return false
}
const menu = app.applicationMenu.getMenuItemById( const menu = app.applicationMenu.getMenuItemById(
'File.Preferences.User default units' 'File.Preferences.User default units'
) )
if (!menu) fail() if (!menu) return false
menu.click() menu.click()
return true
}) })
)
.toBe(true)
const settings = page.getByTestId('settings-dialog-panel') const settings = page.getByTestId('settings-dialog-panel')
await expect(settings).toBeVisible() await expect(settings).toBeVisible()
const defaultUnit = settings.locator('#defaultUnit') const defaultUnit = settings.locator('#defaultUnit')
@ -119,14 +164,22 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
if (!tronApp) fail() if (!tronApp) fail()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
await expect
.poll(
async () =>
await tronApp.electron.evaluate(async ({ app }) => { await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail() if (!app || !app.applicationMenu) return false
const menu = app.applicationMenu.getMenuItemById( const menu = app.applicationMenu.getMenuItemById(
'File.Preferences.Theme' 'File.Preferences.Theme'
) )
if (!menu) fail() if (!menu) {
return false
}
menu.click() menu.click()
return true
}) })
)
.toBe(true)
// Check that the command bar is opened // Check that the command bar is opened
await expect(cmdBar.cmdBarElement).toBeVisible() await expect(cmdBar.cmdBarElement).toBeVisible()
// Check the placeholder project name exists // Check the placeholder project name exists
@ -144,14 +197,22 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
if (!tronApp) fail() if (!tronApp) fail()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
await expect
.poll(
async () =>
await tronApp.electron.evaluate(async ({ app }) => { await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail() if (!app || !app.applicationMenu) {
return false
}
const menu = app.applicationMenu.getMenuItemById( const menu = app.applicationMenu.getMenuItemById(
'File.Preferences.Theme color' 'File.Preferences.Theme color'
) )
if (!menu) fail() if (!menu) return false
menu.click() menu.click()
return true
}) })
)
.toBe(true)
const settings = page.getByTestId('settings-dialog-panel') const settings = page.getByTestId('settings-dialog-panel')
await expect(settings).toBeVisible() await expect(settings).toBeVisible()
const defaultUnit = settings.locator('#themeColor') const defaultUnit = settings.locator('#themeColor')
@ -165,13 +226,22 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
if (!tronApp) fail() if (!tronApp) fail()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
await expect
.poll(
async () =>
await tronApp.electron.evaluate(async ({ app }) => { await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail() if (!app || !app.applicationMenu) return false
const menu = app.applicationMenu.getMenuItemById('File.Sign out') const menu =
if (!menu) fail() app.applicationMenu.getMenuItemById('File.Sign out')
if (!menu) {
return false
}
// FIXME: Add back when you can actually sign out // FIXME: Add back when you can actually sign out
// menu.click() // menu.click()
return true
}) })
)
.toBe(true)
// FIXME: When signing out during E2E the page is not bound correctly. // FIXME: When signing out during E2E the page is not bound correctly.
// It cannot find the button // It cannot find the button
// const signIn = page.getByTestId('sign-in-button') // const signIn = page.getByTestId('sign-in-button')
@ -184,14 +254,22 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
if (!tronApp) fail() if (!tronApp) fail()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
await expect
.poll(
async () =>
await tronApp.electron.evaluate(async ({ app }) => { await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail() if (!app || !app.applicationMenu) {
return false
}
const menu = app.applicationMenu.getMenuItemById( const menu = app.applicationMenu.getMenuItemById(
'Edit.Rename project' 'Edit.Rename project'
) )
if (!menu) fail() if (!menu) return false
menu.click() menu.click()
return true
}) })
)
.toBe(true)
// Check the placeholder project name exists // Check the placeholder project name exists
const actual = await cmdBar.cmdBarElement const actual = await cmdBar.cmdBarElement
.getByTestId('command-name') .getByTestId('command-name')
@ -203,20 +281,27 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
if (!tronApp) fail() if (!tronApp) fail()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
await expect
.poll(
async () =>
await tronApp.electron.evaluate(async ({ app }) => { await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail() if (!app || !app.applicationMenu) return false
const menu = app.applicationMenu.getMenuItemById( const menu = app.applicationMenu.getMenuItemById(
'Edit.Delete project' 'Edit.Delete project'
) )
if (!menu) fail() if (!menu) {
return false
}
menu.click() menu.click()
return true
}) })
)
.toBe(true)
// Check the placeholder project name exists // Check the placeholder project name exists
const actual = await cmdBar.cmdBarElement const actual = async () =>
.getByTestId('command-name') cmdBar.cmdBarElement.getByTestId('command-name').textContent()
.textContent()
const expected = 'Delete project' const expected = 'Delete project'
expect(actual).toBe(expected) await expect.poll(async () => await actual()).toBe(expected)
}) })
test('Home.Edit.Change project directory', async ({ test('Home.Edit.Change project directory', async ({
tronApp, tronApp,
@ -226,14 +311,22 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
if (!tronApp) fail() if (!tronApp) fail()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
await expect
.poll(
async () =>
await tronApp.electron.evaluate(async ({ app }) => { await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail() if (!app || !app.applicationMenu) {
return false
}
const menu = app.applicationMenu.getMenuItemById( const menu = app.applicationMenu.getMenuItemById(
'Edit.Change project directory' 'Edit.Change project directory'
) )
if (!menu) fail() if (!menu) return false
menu.click() menu.click()
return true
}) })
)
.toBe(true)
const settings = page.getByTestId('settings-dialog-panel') const settings = page.getByTestId('settings-dialog-panel')
await expect(settings).toBeVisible() await expect(settings).toBeVisible()
const projectDirectory = settings.locator('#projectDirectory') const projectDirectory = settings.locator('#projectDirectory')
@ -249,14 +342,22 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
if (!tronApp) fail() if (!tronApp) fail()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
await expect
.poll(
async () =>
await tronApp.electron.evaluate(async ({ app }) => { await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail() if (!app || !app.applicationMenu) return false
const menu = app.applicationMenu.getMenuItemById( const menu = app.applicationMenu.getMenuItemById(
'View.Command Palette...' 'View.Command Palette...'
) )
if (!menu) fail() if (!menu) {
return false
}
menu.click() menu.click()
return true
}) })
)
.toBe(true)
// Check the placeholder project name exists // Check the placeholder project name exists
const actual = cmdBar.cmdBarElement.getByTestId('cmd-bar-search') const actual = cmdBar.cmdBarElement.getByTestId('cmd-bar-search')
await expect(actual).toBeVisible() await expect(actual).toBeVisible()
@ -267,14 +368,22 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
if (!tronApp) fail() if (!tronApp) fail()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
await expect
.poll(
async () =>
await tronApp.electron.evaluate(async ({ app }) => { await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail() if (!app || !app.applicationMenu) {
return false
}
const menu = app.applicationMenu.getMenuItemById( const menu = app.applicationMenu.getMenuItemById(
'Help.Show all commands' 'Help.Show all commands'
) )
if (!menu) fail() if (!menu) return false
menu.click() menu.click()
return true
}) })
)
.toBe(true)
// Check the placeholder project name exists // Check the placeholder project name exists
const actual = cmdBar.cmdBarElement.getByTestId('cmd-bar-search') const actual = cmdBar.cmdBarElement.getByTestId('cmd-bar-search')
await expect(actual).toBeVisible() await expect(actual).toBeVisible()
@ -283,13 +392,21 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
if (!tronApp) fail() if (!tronApp) fail()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
await expect
.poll(
async () =>
await tronApp.electron.evaluate(async ({ app }) => { await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail() if (!app || !app.applicationMenu) return false
const menu = app.applicationMenu.getMenuItemById( const menu = app.applicationMenu.getMenuItemById(
'Help.KCL code samples' 'Help.KCL code samples'
) )
if (!menu) fail() if (!menu) {
return false
}
return true
}) })
)
.toBe(true)
}) })
test('Home.Help.Refresh and report a bug', async ({ test('Home.Help.Refresh and report a bug', async ({
tronApp, tronApp,
@ -299,14 +416,22 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
if (!tronApp) fail() if (!tronApp) fail()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
await expect
.poll(
async () =>
await tronApp.electron.evaluate(async ({ app }) => { await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail() if (!app || !app.applicationMenu) {
return false
}
const menu = app.applicationMenu.getMenuItemById( const menu = app.applicationMenu.getMenuItemById(
'Help.Refresh and report a bug' 'Help.Refresh and report a bug'
) )
if (!menu) fail() if (!menu) return false
menu.click() menu.click()
return true
}) })
)
.toBe(true)
// Core dump and refresh magic number timeout // Core dump and refresh magic number timeout
await page.waitForTimeout(7000) await page.waitForTimeout(7000)
const actual = page.getByText( const actual = page.getByText(
@ -318,14 +443,22 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
if (!tronApp) fail() if (!tronApp) fail()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
await expect
.poll(
async () =>
await tronApp.electron.evaluate(async ({ app }) => { await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail() if (!app || !app.applicationMenu) return false
const menu = app.applicationMenu.getMenuItemById( const menu = app.applicationMenu.getMenuItemById(
'Help.Reset onboarding' 'Help.Reset onboarding'
) )
if (!menu) fail() if (!menu) {
return false
}
menu.click() menu.click()
return true
}) })
)
.toBe(true)
const actual = page.getByText( const actual = page.getByText(
`This is a hardware design tool that lets you edit visually, with code, or both. It's powered by the KittyCAD Design API, the first API created for anyone to build hardware design tools.` `This is a hardware design tool that lets you edit visually, with code, or both. It's powered by the KittyCAD Design API, the first API created for anyone to build hardware design tools.`
@ -345,7 +478,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
}) => { }) => {
if (!tronApp) fail() if (!tronApp) fail()
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -377,7 +510,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -413,7 +546,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -438,6 +571,43 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
const expected = 'Open sample' const expected = 'Open sample'
expect(actual).toBe(expected) expect(actual).toBe(expected)
}) })
test('Modeling.File.Insert from project file', async ({
tronApp,
cmdBar,
page,
homePage,
scene,
}) => {
if (!tronApp) {
throwTronAppMissing()
return
}
await homePage.goToModelingScene()
await scene.settled(cmdBar)
// Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) {
throw new Error('app or app.applicationMenu is missing')
}
const openProject = app.applicationMenu.getMenuItemById(
'File.Insert from project file'
)
if (!openProject) {
throw new Error('File.Insert from project file')
}
openProject.click()
})
// Check that the command bar is opened
await expect(cmdBar.cmdBarElement).toBeVisible()
// Check the placeholder project name exists
const actual = await cmdBar.cmdBarElement
.getByTestId('command-name')
.textContent()
const expected = 'Insert'
expect(actual).toBe(expected)
})
test('Modeling.File.Export current part', async ({ test('Modeling.File.Export current part', async ({
tronApp, tronApp,
cmdBar, cmdBar,
@ -450,7 +620,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -487,7 +657,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -523,7 +693,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -560,7 +730,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -596,7 +766,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -630,7 +800,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -663,7 +833,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -700,7 +870,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -733,21 +903,28 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
await expect
.poll(
async () =>
await tronApp.electron.evaluate(async ({ app }) => { await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) { if (!app || !app.applicationMenu) {
throw new Error('app or app.applicationMenu is missing') throw new Error('app or app.applicationMenu is missing')
} }
const menu = app.applicationMenu.getMenuItemById('File.Sign out') const menu =
app.applicationMenu.getMenuItemById('File.Sign out')
if (!menu) { if (!menu) {
throw new Error('File.Sign out') throw new Error('File.Sign out')
} }
// FIXME: Add back when you can actually sign out // FIXME: Add back when you can actually sign out
// menu.click() // menu.click()
return true
}) })
)
.toBe(true)
// FIXME: When signing out during E2E the page is not bound correctly. // FIXME: When signing out during E2E the page is not bound correctly.
// It cannot find the button // It cannot find the button
// const signIn = page.getByTestId('sign-in-button') // const signIn = page.getByTestId('sign-in-button')
@ -767,7 +944,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -802,7 +979,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -837,7 +1014,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -867,7 +1044,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -902,7 +1079,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -937,7 +1114,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
await tronApp.electron.evaluate(async ({ app }) => { await tronApp.electron.evaluate(async ({ app }) => {
@ -971,7 +1148,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -1003,7 +1180,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -1039,7 +1216,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -1075,7 +1252,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -1112,7 +1289,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -1141,7 +1318,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -1170,7 +1347,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -1199,7 +1376,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -1228,7 +1405,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -1257,7 +1434,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -1286,7 +1463,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -1315,7 +1492,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -1344,7 +1521,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -1381,7 +1558,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -1418,7 +1595,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -1455,7 +1632,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -1488,7 +1665,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -1521,7 +1698,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -1554,7 +1731,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -1587,7 +1764,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -1621,7 +1798,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -1658,7 +1835,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -1695,7 +1872,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -1733,7 +1910,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -1770,7 +1947,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -1807,7 +1984,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -1844,7 +2021,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -1881,7 +2058,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -1918,7 +2095,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -1956,7 +2133,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -1994,7 +2171,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -2032,7 +2209,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -2071,7 +2248,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -2099,7 +2276,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -2119,25 +2296,33 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
scene, scene,
toolbar, toolbar,
}) => { }) => {
// TODO: this test has been dead dead on the idle stream branch
test.fixme(orRunWhenFullSuiteEnabled())
if (!tronApp) { if (!tronApp) {
throwTronAppMissing() throwTronAppMissing()
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
await expect
.poll(
async () =>
await tronApp.electron.evaluate(async ({ app }) => { await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail() if (!app || !app.applicationMenu) return false
const menu = app.applicationMenu.getMenuItemById( const menu = app.applicationMenu.getMenuItemById(
'Help.Refresh and report a bug' 'Help.Refresh and report a bug'
) )
if (!menu) fail() if (!menu) return false
menu.click() menu.click()
return true
}) })
)
.toBe(true)
// Core dump and refresh magic number timeout // Core dump and refresh magic number timeout
await scene.waitForExecutionDone() await scene.connectionEstablished()
await expect(toolbar.startSketchBtn).toBeVisible() await expect(toolbar.startSketchBtn).toBeVisible()
}) })
test('Modeling.Help.Reset onboarding', async ({ test('Modeling.Help.Reset onboarding', async ({
@ -2152,7 +2337,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
return return
} }
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await page.waitForTimeout(100) // wait for createModelingPageMenu() to run

View File

@ -0,0 +1,292 @@
import * as fsp from 'fs/promises'
import path from 'path'
import type { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture'
import type { ToolbarFixture } from '@e2e/playwright/fixtures/toolbarFixture'
import {
executorInputPath,
getUtils,
testsInputPath,
} from '@e2e/playwright/test-utils'
import { expect, test } from '@e2e/playwright/zoo-test'
import type { Page } from '@playwright/test'
async function insertPartIntoAssembly(
path: string,
alias: string,
toolbar: ToolbarFixture,
cmdBar: CmdBarFixture,
page: Page
) {
await toolbar.insertButton.click()
await cmdBar.selectOption({ name: path }).click()
await cmdBar.expectState({
stage: 'arguments',
currentArgKey: 'localName',
currentArgValue: '',
headerArguments: { Path: path, LocalName: '' },
highlightedHeaderArg: 'localName',
commandName: 'Insert',
})
await page.keyboard.insertText(alias)
await cmdBar.progressCmdBar()
await cmdBar.expectState({
stage: 'review',
headerArguments: { Path: path, LocalName: alias },
commandName: 'Insert',
})
await cmdBar.progressCmdBar()
}
// 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 parts into assembly as whole module import`,
{ tag: ['@electron'] },
async ({
context,
page,
homePage,
scene,
editor,
toolbar,
cmdBar,
tronApp,
}) => {
if (!tronApp) {
fail()
}
const midPoint = { x: 500, y: 250 }
const partPoint = { x: midPoint.x + 30, y: midPoint.y - 30 } // mid point, just off top right
const defaultPlanesColor: [number, number, number] = [180, 220, 180]
const partColor: [number, number, number] = [100, 100, 100]
const tolerance = 50
const u = await getUtils(page)
const gizmo = page.locator('[aria-label*=gizmo]')
const resetCameraButton = page.getByRole('button', { name: 'Reset view' })
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.kcl'),
path.join(bracketDir, 'cylinder.kcl')
),
fsp.copyFile(
executorInputPath('e2e-can-sketch-on-chamfer.kcl'),
path.join(bracketDir, 'bracket.kcl')
),
fsp.copyFile(
testsInputPath('cube.step'),
path.join(bracketDir, 'cube.step')
),
fsp.writeFile(path.join(bracketDir, 'main.kcl'), ''),
])
})
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.openProject(projectName)
await scene.settled(cmdBar)
await toolbar.closePane('code')
await scene.expectPixelColor(defaultPlanesColor, midPoint, tolerance)
})
await test.step('Insert kcl as first part as module', async () => {
await insertPartIntoAssembly(
'cylinder.kcl',
'cylinder',
toolbar,
cmdBar,
page
)
await toolbar.openPane('code')
await editor.expectEditor.toContain(
`
import "cylinder.kcl" as cylinder
cylinder
`,
{ shouldNormalise: true }
)
await scene.settled(cmdBar)
// Check scene for changes
await toolbar.closePane('code')
await u.doAndWaitForCmd(async () => {
await gizmo.click({ button: 'right' })
await resetCameraButton.click()
}, 'zoom_to_fit')
await toolbar.closePane('debug')
await scene.expectPixelColor(partColor, partPoint, tolerance)
await toolbar.openPane('code')
})
await test.step('Insert kcl second part as module', async () => {
await insertPartIntoAssembly(
'bracket.kcl',
'bracket',
toolbar,
cmdBar,
page
)
await editor.expectEditor.toContain(
`
import "cylinder.kcl" as cylinder
import "bracket.kcl" as bracket
cylinder
bracket
`,
{ shouldNormalise: true }
)
await scene.settled(cmdBar)
})
await test.step('Insert a second time and expect error', async () => {
// TODO: revisit once we have clone with #6209
await insertPartIntoAssembly(
'bracket.kcl',
'bracket',
toolbar,
cmdBar,
page
)
await editor.expectEditor.toContain(
`
import "cylinder.kcl" as cylinder
import "bracket.kcl" as bracket
import "bracket.kcl" as bracket
cylinder
bracket
bracket
`,
{ shouldNormalise: true }
)
await scene.settled(cmdBar)
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
})
}
)
test(
`Insert foreign parts into assembly as whole module import`,
{ tag: ['@electron'] },
async ({
context,
page,
homePage,
scene,
editor,
toolbar,
cmdBar,
tronApp,
}) => {
if (!tronApp) {
fail()
}
const midPoint = { x: 500, y: 250 }
const partPoint = { x: midPoint.x + 30, y: midPoint.y - 30 } // mid point, just off top right
const defaultPlanesColor: [number, number, number] = [180, 220, 180]
const partColor: [number, number, number] = [150, 150, 150]
const tolerance = 50
const complexPlmFileName = 'cube_Complex-PLM_Name_-001.sldprt'
const camelCasedSolidworksFileName = 'cubeComplexPLMName001'
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(
testsInputPath('cube.step'),
path.join(bracketDir, 'cube.step')
),
fsp.copyFile(
testsInputPath('cube.sldprt'),
path.join(bracketDir, complexPlmFileName)
),
fsp.writeFile(path.join(bracketDir, 'main.kcl'), ''),
])
})
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.openProject(projectName)
await scene.settled(cmdBar)
await toolbar.closePane('code')
await scene.expectPixelColor(defaultPlanesColor, midPoint, tolerance)
})
await test.step('Insert step part as module', async () => {
await insertPartIntoAssembly('cube.step', 'cube', toolbar, cmdBar, page)
await toolbar.openPane('code')
await editor.expectEditor.toContain(
`
import "cube.step" as cube
cube
`,
{ shouldNormalise: true }
)
await scene.settled(cmdBar)
// TODO: remove this once #5780 is fixed
await page.reload()
await scene.settled(cmdBar)
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
await toolbar.closePane('code')
await scene.expectPixelColor(partColor, partPoint, tolerance)
})
await test.step('Insert second step part by clicking', async () => {
await toolbar.openPane('files')
await toolbar.expectFileTreeState([
complexPlmFileName,
'cube.step',
'main.kcl',
])
await toolbar.openFile(complexPlmFileName)
// Go through the ToastInsert prompt
await page.getByText('Insert into my current file').click()
// Check getPathFilenameInVariableCase output
const parsedValueFromFile =
await cmdBar.currentArgumentInput.inputValue()
expect(parsedValueFromFile).toEqual(camelCasedSolidworksFileName)
// Continue on with the flow
await page.keyboard.insertText('cubeSw')
await cmdBar.progressCmdBar()
await cmdBar.expectState({
stage: 'review',
headerArguments: { Path: complexPlmFileName, LocalName: 'cubeSw' },
commandName: 'Insert',
})
await cmdBar.progressCmdBar()
await toolbar.closePane('files')
await toolbar.openPane('code')
await editor.expectEditor.toContain(
`
import "cube.step" as cube
import "${complexPlmFileName}" as cubeSw
cube
cubeSw
`,
{ shouldNormalise: true }
)
await scene.settled(cmdBar)
// TODO: remove this once #5780 is fixed
await page.reload()
await scene.settled(cmdBar)
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
await toolbar.closePane('code')
await scene.expectPixelColor(partColor, partPoint, tolerance)
})
}
)
})

View File

@ -5,13 +5,14 @@ import path from 'node:path'
import type { EditorFixture } from '@e2e/playwright/fixtures/editorFixture' import type { EditorFixture } from '@e2e/playwright/fixtures/editorFixture'
import type { SceneFixture } from '@e2e/playwright/fixtures/sceneFixture' import type { SceneFixture } from '@e2e/playwright/fixtures/sceneFixture'
import type { ToolbarFixture } from '@e2e/playwright/fixtures/toolbarFixture' import type { ToolbarFixture } from '@e2e/playwright/fixtures/toolbarFixture'
import { getUtils, orRunWhenFullSuiteEnabled } from '@e2e/playwright/test-utils' import { orRunWhenFullSuiteEnabled } from '@e2e/playwright/test-utils'
import { expect, test } from '@e2e/playwright/zoo-test' import { expect, test } from '@e2e/playwright/zoo-test'
// test file is for testing point an click code gen functionality that's not sketch mode related // test file is for testing point an click code gen functionality that's not sketch mode related
test.describe('Point-and-click tests', () => { test.describe('Point-and-click tests', () => {
test('verify extruding circle works', async ({ test('verify extruding circle works', async ({
page,
context, context,
homePage, homePage,
cmdBar, cmdBar,
@ -30,8 +31,9 @@ test.describe('Point-and-click tests', () => {
await context.addInitScript((file) => { await context.addInitScript((file) => {
localStorage.setItem('persistCode', file) localStorage.setItem('persistCode', file)
}, file) }, file)
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
const [clickCircle, moveToCircle] = scene.makeMouseHelpers(582, 217) const [clickCircle, moveToCircle] = scene.makeMouseHelpers(582, 217)
@ -72,7 +74,6 @@ test.describe('Point-and-click tests', () => {
await test.step('do extrude flow and check extrude code is added to editor', async () => { await test.step('do extrude flow and check extrude code is added to editor', async () => {
await toolbar.extrudeButton.click() await toolbar.extrudeButton.click()
await cmdBar.expectState({ await cmdBar.expectState({
stage: 'arguments', stage: 'arguments',
currentArgKey: 'distance', currentArgKey: 'distance',
@ -186,6 +187,7 @@ test.describe('Point-and-click tests', () => {
editor, editor,
toolbar, toolbar,
scene, scene,
cmdBar,
}) => { }) => {
const file = await fs.readFile( const file = await fs.readFile(
path.resolve( path.resolve(
@ -200,9 +202,7 @@ test.describe('Point-and-click tests', () => {
}, file) }, file)
await page.setBodyDimensions({ width: 1000, height: 500 }) await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await expect( await scene.settled(cmdBar)
page.getByTestId('model-state-indicator-receive-reliable')
).toBeVisible()
const sketchOnAChamfer = _sketchOnAChamfer(page, editor, toolbar, scene) const sketchOnAChamfer = _sketchOnAChamfer(page, editor, toolbar, scene)
@ -210,7 +210,7 @@ test.describe('Point-and-click tests', () => {
clickCoords: { x: 570, y: 220 }, clickCoords: { x: 570, y: 220 },
cameraPos: { x: 16020, y: -2000, z: 10500 }, cameraPos: { x: 16020, y: -2000, z: 10500 },
cameraTarget: { x: -150, y: -4500, z: -80 }, cameraTarget: { x: -150, y: -4500, z: -80 },
beforeChamferSnippet: `angledLine([segAng(rectangleSegmentA001)-90,217.26],%,$seg01) beforeChamferSnippet: `angledLine(angle=segAng(rectangleSegmentA001)-90,length=217.26,tag=$seg01)
chamfer(length = 30,tags = [ chamfer(length = 30,tags = [
seg01, seg01,
getNextAdjacentEdge(yo), getNextAdjacentEdge(yo),
@ -223,9 +223,9 @@ test.describe('Point-and-click tests', () => {
'sketch002 = startSketchOn(extrude001, seg03)', 'sketch002 = startSketchOn(extrude001, seg03)',
afterRectangle1stClickSnippet: afterRectangle1stClickSnippet:
'startProfileAt([205.96, 254.59], sketch002)', 'startProfileAt([205.96, 254.59], sketch002)',
afterRectangle2ndClickSnippet: `angledLine([0,11.39],%,$rectangleSegmentA002) afterRectangle2ndClickSnippet: `angledLine(angle=0,length=11.39,tag=$rectangleSegmentA002)
|>angledLine([segAng(rectangleSegmentA002)-90,105.26],%) |>angledLine(angle=segAng(rectangleSegmentA002)-90,length=105.26)
|>angledLine([segAng(rectangleSegmentA002),-segLen(rectangleSegmentA002)],%) |>angledLine(angle=segAng(rectangleSegmentA002),length=-segLen(rectangleSegmentA002))
|>line(endAbsolute=[profileStartX(%),profileStartY(%)]) |>line(endAbsolute=[profileStartX(%),profileStartY(%)])
|>close()`, |>close()`,
}) })
@ -234,10 +234,7 @@ test.describe('Point-and-click tests', () => {
clickCoords: { x: 690, y: 250 }, clickCoords: { x: 690, y: 250 },
cameraPos: { x: 16020, y: -2000, z: 10500 }, cameraPos: { x: 16020, y: -2000, z: 10500 },
cameraTarget: { x: -150, y: -4500, z: -80 }, cameraTarget: { x: -150, y: -4500, z: -80 },
beforeChamferSnippet: `angledLine([ beforeChamferSnippet: `angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 217.26, tag = $seg01)chamfer(
segAng(rectangleSegmentA001) - 90,
217.26
], %, $seg01)chamfer(
length = 30, length = 30,
tags = [ tags = [
seg01, seg01,
@ -250,9 +247,9 @@ test.describe('Point-and-click tests', () => {
'sketch003 = startSketchOn(extrude001, seg04)', 'sketch003 = startSketchOn(extrude001, seg04)',
afterRectangle1stClickSnippet: afterRectangle1stClickSnippet:
'startProfileAt([-209.64, 255.28], sketch003)', 'startProfileAt([-209.64, 255.28], sketch003)',
afterRectangle2ndClickSnippet: `angledLine([0,11.56],%,$rectangleSegmentA003) afterRectangle2ndClickSnippet: `angledLine(angle=0,length=11.56,tag=$rectangleSegmentA003)
|>angledLine([segAng(rectangleSegmentA003)-90,106.84],%) |>angledLine(angle=segAng(rectangleSegmentA003)-90,length=106.84)
|>angledLine([segAng(rectangleSegmentA003),-segLen(rectangleSegmentA003)],%) |>angledLine(angle=segAng(rectangleSegmentA003),length=-segLen(rectangleSegmentA003))
|>line(endAbsolute=[profileStartX(%),profileStartY(%)]) |>line(endAbsolute=[profileStartX(%),profileStartY(%)])
|>close()`, |>close()`,
}) })
@ -261,7 +258,7 @@ test.describe('Point-and-click tests', () => {
clickCoords: { x: 677, y: 87 }, clickCoords: { x: 677, y: 87 },
cameraPos: { x: -6200, y: 1500, z: 6200 }, cameraPos: { x: -6200, y: 1500, z: 6200 },
cameraTarget: { x: 8300, y: 1100, z: 4800 }, cameraTarget: { x: 8300, y: 1100, z: 4800 },
beforeChamferSnippet: `angledLine([0, 268.43], %, $rectangleSegmentA001)chamfer( beforeChamferSnippet: `angledLine(angle = 0, length = 268.43, tag = $rectangleSegmentA001)chamfer(
length = 30, length = 30,
tags = [ tags = [
getNextAdjacentEdge(yo), getNextAdjacentEdge(yo),
@ -272,9 +269,9 @@ test.describe('Point-and-click tests', () => {
'sketch004 = startSketchOn(extrude001, seg05)', 'sketch004 = startSketchOn(extrude001, seg05)',
afterRectangle1stClickSnippet: afterRectangle1stClickSnippet:
'startProfileAt([82.57, 322.96], sketch004)', 'startProfileAt([82.57, 322.96], sketch004)',
afterRectangle2ndClickSnippet: `angledLine([0,11.16],%,$rectangleSegmentA004) afterRectangle2ndClickSnippet: `angledLine(angle=0,length=11.16,tag=$rectangleSegmentA004)
|>angledLine([segAng(rectangleSegmentA004)-90,103.07],%) |>angledLine(angle=segAng(rectangleSegmentA004)-90,length=103.07)
|>angledLine([segAng(rectangleSegmentA004),-segLen(rectangleSegmentA004)],%) |>angledLine(angle=segAng(rectangleSegmentA004),length=-segLen(rectangleSegmentA004))
|>line(endAbsolute=[profileStartX(%),profileStartY(%)]) |>line(endAbsolute=[profileStartX(%),profileStartY(%)])
|>close()`, |>close()`,
}) })
@ -290,9 +287,9 @@ test.describe('Point-and-click tests', () => {
'sketch005 = startSketchOn(extrude001, seg06)', 'sketch005 = startSketchOn(extrude001, seg06)',
afterRectangle1stClickSnippet: afterRectangle1stClickSnippet:
'startProfileAt([-23.43, 19.69], sketch005)', 'startProfileAt([-23.43, 19.69], sketch005)',
afterRectangle2ndClickSnippet: `angledLine([0,9.1],%,$rectangleSegmentA005) afterRectangle2ndClickSnippet: `angledLine(angle=0,length=9.1,tag=$rectangleSegmentA005)
|>angledLine([segAng(rectangleSegmentA005)-90,84.07],%) |>angledLine(angle=segAng(rectangleSegmentA005)-90,length=84.07)
|>angledLine([segAng(rectangleSegmentA005),-segLen(rectangleSegmentA005)],%) |>angledLine(angle=segAng(rectangleSegmentA005),length=-segLen(rectangleSegmentA005))
|>line(endAbsolute=[profileStartX(%),profileStartY(%)]) |>line(endAbsolute=[profileStartX(%),profileStartY(%)])
|>close()`, |>close()`,
}) })
@ -302,15 +299,9 @@ test.describe('Point-and-click tests', () => {
`@settings(defaultLengthUnit = in) `@settings(defaultLengthUnit = in)
sketch001 = startSketchOn(XZ) sketch001 = startSketchOn(XZ)
|> startProfileAt([75.8, 317.2], %) // [$startCapTag, $EndCapTag] |> startProfileAt([75.8, 317.2], %) // [$startCapTag, $EndCapTag]
|> angledLine([0, 268.43], %, $rectangleSegmentA001) |> angledLine(angle = 0, length = 268.43, tag = $rectangleSegmentA001)
|> angledLine([ |> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 217.26, tag = $seg01)
segAng(rectangleSegmentA001) - 90, |> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001), tag = $yo)
217.26
], %, $seg01)
|> angledLine([
segAng(rectangleSegmentA001),
-segLen(rectangleSegmentA001)
], %, $yo)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg02) |> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg02)
|> close() |> close()
extrude001 = extrude(sketch001, length = 100) extrude001 = extrude(sketch001, length = 100)
@ -320,48 +311,30 @@ extrude001 = extrude(sketch001, length = 100)
|> chamfer(length = 30, tags = [getNextAdjacentEdge(yo)], tag = $seg06) |> chamfer(length = 30, tags = [getNextAdjacentEdge(yo)], tag = $seg06)
sketch005 = startSketchOn(extrude001, seg06) sketch005 = startSketchOn(extrude001, seg06)
profile004=startProfileAt([-23.43,19.69], sketch005) profile004=startProfileAt([-23.43,19.69], sketch005)
|> angledLine([0, 9.1], %, $rectangleSegmentA005) |> angledLine(angle = 0, length = 9.1, tag = $rectangleSegmentA005)
|> angledLine([segAng(rectangleSegmentA005) - 90, 84.07], %) |> angledLine(angle = segAng(rectangleSegmentA005) - 90, length = 84.07)
|> angledLine([segAng(rectangleSegmentA005), -segLen(rectangleSegmentA005)], %) |> angledLine(angle = segAng(rectangleSegmentA005), length = -segLen(rectangleSegmentA005))
|> line(endAbsolute=[profileStartX(%), profileStartY(%)]) |> line(endAbsolute=[profileStartX(%), profileStartY(%)])
|> close() |> close()
sketch004 = startSketchOn(extrude001, seg05) sketch004 = startSketchOn(extrude001, seg05)
profile003 = startProfileAt([82.57, 322.96], sketch004) profile003 = startProfileAt([82.57, 322.96], sketch004)
|> angledLine([0, 11.16], %, $rectangleSegmentA004) |> angledLine(angle = 0, length = 11.16, tag = $rectangleSegmentA004)
|> angledLine([ |> angledLine(angle = segAng(rectangleSegmentA004) - 90, length = 103.07)
segAng(rectangleSegmentA004) - 90, |> angledLine(angle = segAng(rectangleSegmentA004), length = -segLen(rectangleSegmentA004))
103.07
], %)
|> angledLine([
segAng(rectangleSegmentA004),
-segLen(rectangleSegmentA004)
], %)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close() |> close()
sketch003 = startSketchOn(extrude001, seg04) sketch003 = startSketchOn(extrude001, seg04)
profile002 = startProfileAt([-209.64, 255.28], sketch003) profile002 = startProfileAt([-209.64, 255.28], sketch003)
|> angledLine([0, 11.56], %, $rectangleSegmentA003) |> angledLine(angle = 0, length = 11.56, tag = $rectangleSegmentA003)
|> angledLine([ |> angledLine(angle = segAng(rectangleSegmentA003) - 90, length = 106.84)
segAng(rectangleSegmentA003) - 90, |> angledLine(angle = segAng(rectangleSegmentA003), length = -segLen(rectangleSegmentA003))
106.84
], %)
|> angledLine([
segAng(rectangleSegmentA003),
-segLen(rectangleSegmentA003)
], %)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close() |> close()
sketch002 = startSketchOn(extrude001, seg03) sketch002 = startSketchOn(extrude001, seg03)
profile001 = startProfileAt([205.96, 254.59], sketch002) profile001 = startProfileAt([205.96, 254.59], sketch002)
|> angledLine([0, 11.39], %, $rectangleSegmentA002) |> angledLine(angle = 0, length = 11.39, tag = $rectangleSegmentA002)
|> angledLine([ |> angledLine(angle = segAng(rectangleSegmentA002) - 90, length = 105.26)
segAng(rectangleSegmentA002) - 90, |> angledLine(angle = segAng(rectangleSegmentA002), length = -segLen(rectangleSegmentA002))
105.26
], %)
|> angledLine([
segAng(rectangleSegmentA002),
-segLen(rectangleSegmentA002)
], %)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close() |> close()
`, `,
@ -377,6 +350,7 @@ profile001 = startProfileAt([205.96, 254.59], sketch002)
editor, editor,
toolbar, toolbar,
scene, scene,
cmdBar,
}) => { }) => {
const file = await fs.readFile( const file = await fs.readFile(
path.resolve( path.resolve(
@ -392,7 +366,7 @@ profile001 = startProfileAt([205.96, 254.59], sketch002)
await page.setBodyDimensions({ width: 1000, height: 500 }) await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.settled(cmdBar)
const sketchOnAChamfer = _sketchOnAChamfer(page, editor, toolbar, scene) const sketchOnAChamfer = _sketchOnAChamfer(page, editor, toolbar, scene)
@ -400,7 +374,7 @@ profile001 = startProfileAt([205.96, 254.59], sketch002)
clickCoords: { x: 570, y: 220 }, clickCoords: { x: 570, y: 220 },
cameraPos: { x: 16020, y: -2000, z: 10500 }, cameraPos: { x: 16020, y: -2000, z: 10500 },
cameraTarget: { x: -150, y: -4500, z: -80 }, cameraTarget: { x: -150, y: -4500, z: -80 },
beforeChamferSnippet: `angledLine([segAng(rectangleSegmentA001)-90,217.26],%,$seg01) beforeChamferSnippet: `angledLine(angle=segAng(rectangleSegmentA001)-90,length=217.26,tag=$seg01)
chamfer(extrude001,length=30,tags=[ chamfer(extrude001,length=30,tags=[
seg01, seg01,
getNextAdjacentEdge(yo), getNextAdjacentEdge(yo),
@ -412,9 +386,9 @@ profile001 = startProfileAt([205.96, 254.59], sketch002)
'sketch002 = startSketchOn(extrude001, seg03)', 'sketch002 = startSketchOn(extrude001, seg03)',
afterRectangle1stClickSnippet: afterRectangle1stClickSnippet:
'startProfileAt([205.96, 254.59], sketch002)', 'startProfileAt([205.96, 254.59], sketch002)',
afterRectangle2ndClickSnippet: `angledLine([0,11.39],%,$rectangleSegmentA002) afterRectangle2ndClickSnippet: `angledLine(angle=0,length=11.39,tag=$rectangleSegmentA002)
|>angledLine([segAng(rectangleSegmentA002)-90,105.26],%) |>angledLine(angle=segAng(rectangleSegmentA002)-90,length=105.26)
|>angledLine([segAng(rectangleSegmentA002),-segLen(rectangleSegmentA002)],%) |>angledLine(angle=segAng(rectangleSegmentA002),length=-segLen(rectangleSegmentA002))
|>line(endAbsolute=[profileStartX(%),profileStartY(%)]) |>line(endAbsolute=[profileStartX(%),profileStartY(%)])
|>close()`, |>close()`,
}) })
@ -422,15 +396,9 @@ profile001 = startProfileAt([205.96, 254.59], sketch002)
`@settings(defaultLengthUnit = in) `@settings(defaultLengthUnit = in)
sketch001 = startSketchOn(XZ) sketch001 = startSketchOn(XZ)
|> startProfileAt([75.8, 317.2], %) |> startProfileAt([75.8, 317.2], %)
|> angledLine([0, 268.43], %, $rectangleSegmentA001) |> angledLine(angle = 0, length = 268.43, tag = $rectangleSegmentA001)
|> angledLine([ |> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 217.26, tag = $seg01)
segAng(rectangleSegmentA001) - 90, |> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001), tag = $yo)
217.26
], %, $seg01)
|> angledLine([
segAng(rectangleSegmentA001),
-segLen(rectangleSegmentA001)
], %, $yo)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg02) |> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg02)
|> close() |> close()
extrude001 = extrude(sketch001, length = 100) extrude001 = extrude(sketch001, length = 100)
@ -450,15 +418,9 @@ chamf = chamfer(
) )
sketch002 = startSketchOn(extrude001, seg03) sketch002 = startSketchOn(extrude001, seg03)
profile001 = startProfileAt([205.96, 254.59], sketch002) profile001 = startProfileAt([205.96, 254.59], sketch002)
|> angledLine([0, 11.39], %, $rectangleSegmentA002) |> angledLine(angle = 0, length = 11.39, tag = $rectangleSegmentA002)
|> angledLine([ |> angledLine(angle = segAng(rectangleSegmentA002) - 90, length = 105.26)
segAng(rectangleSegmentA002) - 90, |> angledLine(angle = segAng(rectangleSegmentA002), length = -segLen(rectangleSegmentA002))
105.26
], %)
|> angledLine([
segAng(rectangleSegmentA002),
-segLen(rectangleSegmentA002)
], %)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close() |> close()
`, `,
@ -479,6 +441,7 @@ profile001 = startProfileAt([205.96, 254.59], sketch002)
await page.setBodyDimensions(viewPortSize) await page.setBodyDimensions(viewPortSize)
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.connectionEstablished()
// Constants and locators // Constants and locators
// These are mappings from screenspace to KCL coordinates, // These are mappings from screenspace to KCL coordinates,
@ -537,8 +500,7 @@ profile001 = startProfileAt([205.96, 254.59], sketch002)
await toolbar.startSketchPlaneSelection() await toolbar.startSketchPlaneSelection()
await moveToXzPlane() await moveToXzPlane()
await clickOnXzPlane() await clickOnXzPlane()
// timeout wait for engine animation is unavoidable await toolbar.waitUntilSketchingReady()
await page.waitForTimeout(600)
await editor.expectEditor.toContain(expectedCodeSnippets.sketchOnXzPlane) await editor.expectEditor.toContain(expectedCodeSnippets.sketchOnXzPlane)
}) })
await test.step(`Place a point a few pixels off the middle, verify it still snaps to 0,0`, async () => { await test.step(`Place a point a few pixels off the middle, verify it still snaps to 0,0`, async () => {
@ -580,9 +542,8 @@ profile001 = startProfileAt([205.96, 254.59], sketch002)
editor, editor,
toolbar, toolbar,
scene, scene,
cmdBar,
}) => { }) => {
const u = await getUtils(page)
const initialCode = `closedSketch = startSketchOn(XZ) const initialCode = `closedSketch = startSketchOn(XZ)
|> circle(center = [8, 5], radius = 2) |> circle(center = [8, 5], radius = 2)
openSketch = startSketchOn(XY) openSketch = startSketchOn(XY)
@ -599,8 +560,6 @@ openSketch = startSketchOn(XY)
}, initialCode) }, initialCode)
await homePage.goToModelingScene() await homePage.goToModelingScene()
await u.waitForPageLoad()
await page.waitForTimeout(1000)
const pointInsideCircle = { const pointInsideCircle = {
x: viewPortSize.width * 0.63, x: viewPortSize.width * 0.63,
@ -625,15 +584,16 @@ openSketch = startSketchOn(XY)
const exitSketch = async () => { const exitSketch = async () => {
await test.step(`Exit sketch mode`, async () => { await test.step(`Exit sketch mode`, async () => {
await toolbar.exitSketchBtn.click() await toolbar.exitSketchBtn.click()
await expect(toolbar.exitSketchBtn).not.toBeVisible()
await expect(toolbar.startSketchBtn).toBeEnabled() await expect(toolbar.startSketchBtn).toBeEnabled()
}) })
} }
await test.step(`Double-click on the closed sketch`, async () => { await test.step(`Double-click on the closed sketch`, async () => {
await scene.settled(cmdBar)
await moveToCircle() await moveToCircle()
await page.waitForTimeout(1000)
await dblClickCircle() await dblClickCircle()
await expect(toolbar.startSketchBtn).not.toBeVisible() await page.waitForTimeout(1000)
await expect(toolbar.exitSketchBtn).toBeVisible() await expect(toolbar.exitSketchBtn).toBeVisible()
await editor.expectState({ await editor.expectState({
activeLines: [`|>circle(center=[8,5],radius=2)`], activeLines: [`|>circle(center=[8,5],radius=2)`],
@ -670,7 +630,6 @@ openSketch = startSketchOn(XY)
// There is a full execution after exiting sketch that clears the scene. // There is a full execution after exiting sketch that clears the scene.
await page.waitForTimeout(500) await page.waitForTimeout(500)
await dblClickOpenPath() await dblClickOpenPath()
await expect(toolbar.startSketchBtn).not.toBeVisible()
await expect(toolbar.exitSketchBtn).toBeVisible() await expect(toolbar.exitSketchBtn).toBeVisible()
// Wait for enter sketch mode to complete // Wait for enter sketch mode to complete
await page.waitForTimeout(500) await page.waitForTimeout(500)
@ -1031,6 +990,9 @@ openSketch = startSketchOn(XY)
}) })
await test.step(`Go through the command bar flow`, async () => { await test.step(`Go through the command bar flow`, async () => {
await toolbar.offsetPlaneButton.click() await toolbar.offsetPlaneButton.click()
await expect
.poll(() => page.getByText('Please select one').count())
.toBe(1)
await cmdBar.expectState({ await cmdBar.expectState({
stage: 'arguments', stage: 'arguments',
currentArgKey: 'plane', currentArgKey: 'plane',
@ -1088,6 +1050,7 @@ openSketch = startSketchOn(XY)
const expectedLine = `axis=X,` const expectedLine = `axis=X,`
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.connectionEstablished()
await test.step(`Go through the command bar flow`, async () => { await test.step(`Go through the command bar flow`, async () => {
await toolbar.helixButton.click() await toolbar.helixButton.click()
@ -1106,6 +1069,7 @@ openSketch = startSketchOn(XY)
commandName: 'Helix', commandName: 'Helix',
}) })
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await expect.poll(() => page.getByText('Axis').count()).toBe(6)
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
@ -1233,6 +1197,7 @@ openSketch = startSketchOn(XY)
}, initialCode) }, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 }) await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.settled(cmdBar)
await test.step(`Go through the command bar flow`, async () => { await test.step(`Go through the command bar flow`, async () => {
await toolbar.closePane('code') await toolbar.closePane('code')
@ -1252,15 +1217,22 @@ openSketch = startSketchOn(XY)
commandName: 'Helix', commandName: 'Helix',
}) })
await cmdBar.selectOption({ name: 'Edge' }).click() await cmdBar.selectOption({ name: 'Edge' }).click()
await expect
.poll(() => page.getByText('Please select one').count())
.toBe(1)
await clickOnEdge() await clickOnEdge()
await page.waitForTimeout(1000)
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await page.waitForTimeout(1000)
await cmdBar.argumentInput.focus() await cmdBar.argumentInput.focus()
await page.waitForTimeout(1000)
await page.keyboard.insertText('20') await page.keyboard.insertText('20')
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await page.keyboard.insertText('0') await page.keyboard.insertText('0')
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await page.keyboard.insertText('1') await page.keyboard.insertText('1')
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await page.keyboard.insertText('100')
await cmdBar.expectState({ await cmdBar.expectState({
stage: 'review', stage: 'review',
headerArguments: { headerArguments: {
@ -1274,6 +1246,7 @@ openSketch = startSketchOn(XY)
commandName: 'Helix', commandName: 'Helix',
}) })
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await page.waitForTimeout(1000)
}) })
await test.step(`Confirm code is added to the editor, scene has changed`, async () => { await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
@ -1369,7 +1342,7 @@ extrude001 = extrude(profile001, length = 100)
}, initialCode) }, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 }) await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.settled(cmdBar)
// One dumb hardcoded screen pixel value // One dumb hardcoded screen pixel value
const testPoint = { x: 620, y: 257 } const testPoint = { x: 620, y: 257 }
@ -1530,6 +1503,9 @@ extrude001 = extrude(profile001, length = 100)
if (!shouldPreselect) { if (!shouldPreselect) {
await test.step(`Go through the command bar flow without preselected sketches`, async () => { await test.step(`Go through the command bar flow without preselected sketches`, async () => {
await toolbar.loftButton.click() await toolbar.loftButton.click()
await expect
.poll(() => page.getByText('Please select one').count())
.toBe(1)
await cmdBar.expectState({ await cmdBar.expectState({
stage: 'arguments', stage: 'arguments',
currentArgKey: 'selection', currentArgKey: 'selection',
@ -1579,6 +1555,7 @@ extrude001 = extrude(profile001, length = 100)
page, page,
homePage, homePage,
scene, scene,
cmdBar,
}) => { }) => {
const initialCode = `sketch001 = startSketchOn(XZ) const initialCode = `sketch001 = startSketchOn(XZ)
|> circle(center = [0, 0], radius = 30) |> circle(center = [0, 0], radius = 30)
@ -1592,7 +1569,7 @@ loft001 = loft([sketch001, sketch002])
}, initialCode) }, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 }) await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.settled(cmdBar)
// One dumb hardcoded screen pixel value // One dumb hardcoded screen pixel value
const testPoint = { x: 575, y: 200 } const testPoint = { x: 575, y: 200 }
@ -1655,15 +1632,9 @@ sketch002 = startSketchOn(XZ)
initialCode: `@settings(defaultLengthUnit = in) initialCode: `@settings(defaultLengthUnit = in)
sketch001 = startSketchOn(YZ) sketch001 = startSketchOn(YZ)
profile001 = startProfileAt([-400, -400], sketch001) profile001 = startProfileAt([-400, -400], sketch001)
|> angledLine([0, 800], %, $rectangleSegmentA001) |> angledLine(angle = 0, length = 800, tag = $rectangleSegmentA001)
|> angledLine([ |> angledLine(angle = segAng(rectangleSegmentA001) + 90, length = 800)
segAng(rectangleSegmentA001) + 90, |> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001))
800
], %)
|> angledLine([
segAng(rectangleSegmentA001),
-segLen(rectangleSegmentA001)
], %)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close() |> close()
sketch002 = startSketchOn(XZ) sketch002 = startSketchOn(XZ)
@ -1687,7 +1658,7 @@ sketch002 = startSketchOn(XZ)
}, initialCode) }, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 }) await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.settled(cmdBar)
// One dumb hardcoded screen pixel value // One dumb hardcoded screen pixel value
const [clickOnSketch1] = scene.makeMouseHelpers(testPoint.x, testPoint.y) const [clickOnSketch1] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
@ -1707,6 +1678,9 @@ sketch002 = startSketchOn(XZ)
await test.step(`Go through the command bar flow`, async () => { await test.step(`Go through the command bar flow`, async () => {
await toolbar.sweepButton.click() await toolbar.sweepButton.click()
await expect
.poll(() => page.getByText('Please select one').count())
.toBe(1)
await cmdBar.expectState({ await cmdBar.expectState({
commandName: 'Sweep', commandName: 'Sweep',
currentArgKey: 'target', currentArgKey: 'target',
@ -1826,7 +1800,7 @@ sketch002 = startSketchOn(XZ)
}, initialCode) }, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 }) await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.settled(cmdBar)
// One dumb hardcoded screen pixel value // One dumb hardcoded screen pixel value
const testPoint = { x: 700, y: 250 } const testPoint = { x: 700, y: 250 }
@ -1843,6 +1817,9 @@ sketch002 = startSketchOn(XZ)
await test.step(`Go through the command bar flow and fail validation with a toast`, async () => { await test.step(`Go through the command bar flow and fail validation with a toast`, async () => {
await toolbar.sweepButton.click() await toolbar.sweepButton.click()
await expect
.poll(() => page.getByText('Please select one').count())
.toBe(1)
await cmdBar.expectState({ await cmdBar.expectState({
commandName: 'Sweep', commandName: 'Sweep',
currentArgKey: 'target', currentArgKey: 'target',
@ -2059,6 +2036,9 @@ extrude001 = extrude(sketch001, length = -12)
await test.step(`Open fillet UI without selecting edges`, async () => { await test.step(`Open fillet UI without selecting edges`, async () => {
await page.waitForTimeout(100) await page.waitForTimeout(100)
await toolbar.filletButton.click() await toolbar.filletButton.click()
await expect
.poll(() => page.getByText('Please select one').count())
.toBe(1)
await cmdBar.expectState({ await cmdBar.expectState({
stage: 'arguments', stage: 'arguments',
currentArgKey: 'selection', currentArgKey: 'selection',
@ -2184,6 +2164,7 @@ extrude001 = extrude(sketch001, length = -12)
homePage, homePage,
scene, scene,
toolbar, toolbar,
cmdBar,
}) => { }) => {
const initialCode = `sketch001 = startSketchOn(XY) const initialCode = `sketch001 = startSketchOn(XY)
profile001 = circle( profile001 = circle(
@ -2200,7 +2181,7 @@ fillet001 = fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg01)])
}, initialCode) }, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 }) await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.settled(cmdBar)
await test.step('Double-click in feature tree and expect error toast', async () => { await test.step('Double-click in feature tree and expect error toast', async () => {
await toolbar.openPane('feature-tree') await toolbar.openPane('feature-tree')
@ -2521,7 +2502,7 @@ extrude001 = extrude(sketch001, length = -12)
}, initialCode) }, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 }) await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.settled(cmdBar)
}) })
// Test 1: Command bar flow with preselected edges // Test 1: Command bar flow with preselected edges
@ -2554,6 +2535,7 @@ extrude001 = extrude(sketch001, length = -12)
stage: 'arguments', stage: 'arguments',
}) })
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await page.waitForTimeout(1000)
await cmdBar.expectState({ await cmdBar.expectState({
commandName: 'Chamfer', commandName: 'Chamfer',
highlightedHeaderArg: 'length', highlightedHeaderArg: 'length',
@ -2565,7 +2547,10 @@ extrude001 = extrude(sketch001, length = -12)
}, },
stage: 'arguments', stage: 'arguments',
}) })
await cmdBar.argumentInput.focus()
await page.waitForTimeout(1000)
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await page.waitForTimeout(1000)
await cmdBar.expectState({ await cmdBar.expectState({
commandName: 'Chamfer', commandName: 'Chamfer',
headerArguments: { headerArguments: {
@ -2649,6 +2634,9 @@ extrude001 = extrude(sketch001, length = -12)
await test.step(`Open chamfer UI without selecting edges`, async () => { await test.step(`Open chamfer UI without selecting edges`, async () => {
await page.waitForTimeout(100) await page.waitForTimeout(100)
await toolbar.chamferButton.click() await toolbar.chamferButton.click()
await expect
.poll(() => page.getByText('Please select one').count())
.toBe(1)
await cmdBar.expectState({ await cmdBar.expectState({
stage: 'arguments', stage: 'arguments',
currentArgKey: 'selection', currentArgKey: 'selection',
@ -2771,6 +2759,7 @@ extrude001 = extrude(sketch001, length = -12)
scene, scene,
editor, editor,
toolbar, toolbar,
cmdBar,
}) => { }) => {
// Code samples // Code samples
const initialCode = `@settings(defaultLengthUnit = in) const initialCode = `@settings(defaultLengthUnit = in)
@ -2814,7 +2803,7 @@ chamfer04 = chamfer(extrude001, length = 5, tags = [getOppositeEdge(seg02)])
}, initialCode) }, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 }) await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.settled(cmdBar)
// verify modeling scene is loaded // verify modeling scene is loaded
await scene.expectPixelColor( await scene.expectPixelColor(
@ -2936,9 +2925,11 @@ extrude001 = extrude(sketch001, length = 30)
await context.addInitScript((initialCode) => { await context.addInitScript((initialCode) => {
localStorage.setItem('persistCode', initialCode) localStorage.setItem('persistCode', initialCode)
}, initialCode) }, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 }) await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
// One dumb hardcoded screen pixel value // One dumb hardcoded screen pixel value
const testPoint = { x: 575, y: 200 } const testPoint = { x: 575, y: 200 }
@ -2955,6 +2946,9 @@ extrude001 = extrude(sketch001, length = 30)
if (!shouldPreselect) { if (!shouldPreselect) {
await test.step(`Go through the command bar flow without preselected faces`, async () => { await test.step(`Go through the command bar flow without preselected faces`, async () => {
await toolbar.shellButton.click() await toolbar.shellButton.click()
await expect
.poll(() => page.getByText('Please select one').count())
.toBe(1)
await cmdBar.expectState({ await cmdBar.expectState({
stage: 'arguments', stage: 'arguments',
currentArgKey: 'selection', currentArgKey: 'selection',
@ -3015,7 +3009,6 @@ extrude001 = extrude(sketch001, length = 30)
}) })
await test.step('Edit shell via feature tree selection works', async () => { await test.step('Edit shell via feature tree selection works', async () => {
await toolbar.closePane('code')
await toolbar.openPane('feature-tree') await toolbar.openPane('feature-tree')
const operationButton = await toolbar.getFeatureTreeOperation( const operationButton = await toolbar.getFeatureTreeOperation(
'Shell', 'Shell',
@ -3044,7 +3037,6 @@ extrude001 = extrude(sketch001, length = 30)
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await toolbar.closePane('feature-tree') await toolbar.closePane('feature-tree')
await scene.expectPixelColor([150, 150, 150], testPoint, 15) await scene.expectPixelColor([150, 150, 150], testPoint, 15)
await toolbar.openPane('code')
await editor.expectEditor.toContain(editedShellDeclaration) await editor.expectEditor.toContain(editedShellDeclaration)
await editor.expectState({ await editor.expectState({
diagnostics: [], diagnostics: [],
@ -3079,7 +3071,7 @@ extrude001 = extrude(sketch001, length = 40)
}, initialCode) }, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 }) await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.settled(cmdBar)
// One dumb hardcoded screen pixel value // One dumb hardcoded screen pixel value
const testPoint = { x: 580, y: 180 } const testPoint = { x: 580, y: 180 }
@ -3097,6 +3089,9 @@ extrude001 = extrude(sketch001, length = 40)
await test.step(`Go through the command bar flow, selecting a wall and keeping default thickness`, async () => { await test.step(`Go through the command bar flow, selecting a wall and keeping default thickness`, async () => {
await toolbar.shellButton.click() await toolbar.shellButton.click()
await expect
.poll(() => page.getByText('Please select one').count())
.toBe(1)
await cmdBar.expectState({ await cmdBar.expectState({
stage: 'arguments', stage: 'arguments',
currentArgKey: 'selection', currentArgKey: 'selection',
@ -3108,6 +3103,9 @@ extrude001 = extrude(sketch001, length = 40)
highlightedHeaderArg: 'selection', highlightedHeaderArg: 'selection',
commandName: 'Shell', commandName: 'Shell',
}) })
await expect
.poll(() => page.getByText('Please select one').count())
.toBe(1)
await clickOnCap() await clickOnCap()
await page.keyboard.down('Shift') await page.keyboard.down('Shift')
await clickOnWall() await clickOnWall()
@ -3116,6 +3114,7 @@ extrude001 = extrude(sketch001, length = 40)
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await page.waitForTimeout(500) await page.waitForTimeout(500)
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await page.waitForTimeout(500)
await cmdBar.expectState({ await cmdBar.expectState({
stage: 'review', stage: 'review',
headerArguments: { headerArguments: {
@ -3124,7 +3123,9 @@ extrude001 = extrude(sketch001, length = 40)
}, },
commandName: 'Shell', commandName: 'Shell',
}) })
await page.waitForTimeout(500)
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await page.waitForTimeout(500)
}) })
await test.step(`Confirm code is added to the editor, scene has changed`, async () => { await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
@ -3139,7 +3140,6 @@ extrude001 = extrude(sketch001, length = 40)
}) })
await test.step('Edit shell via feature tree selection works', async () => { await test.step('Edit shell via feature tree selection works', async () => {
await editor.closePane()
const operationButton = await toolbar.getFeatureTreeOperation('Shell', 0) const operationButton = await toolbar.getFeatureTreeOperation('Shell', 0)
await operationButton.dblclick({ button: 'left' }) await operationButton.dblclick({ button: 'left' })
await cmdBar.expectState({ await cmdBar.expectState({
@ -3154,6 +3154,7 @@ extrude001 = extrude(sketch001, length = 40)
}) })
await page.keyboard.insertText('1') await page.keyboard.insertText('1')
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await page.waitForTimeout(500)
await cmdBar.expectState({ await cmdBar.expectState({
stage: 'review', stage: 'review',
headerArguments: { headerArguments: {
@ -3164,7 +3165,6 @@ extrude001 = extrude(sketch001, length = 40)
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await toolbar.closePane('feature-tree') await toolbar.closePane('feature-tree')
await scene.expectPixelColor([150, 150, 150], testPoint, 15) await scene.expectPixelColor([150, 150, 150], testPoint, 15)
await toolbar.openPane('code')
await editor.expectEditor.toContain(editedShellDeclaration) await editor.expectEditor.toContain(editedShellDeclaration)
await editor.expectState({ await editor.expectState({
diagnostics: [], diagnostics: [],
@ -3218,7 +3218,7 @@ extrude002 = extrude(sketch002, length = 50)
}, initialCode) }, initialCode)
await page.setBodyDimensions({ width: 1200, height: 500 }) await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.settled(cmdBar)
// One dumb hardcoded screen pixel value // One dumb hardcoded screen pixel value
const testPoint = { x: 580, y: 320 } const testPoint = { x: 580, y: 320 }
@ -3243,12 +3243,13 @@ extrude002 = extrude(sketch002, length = 50)
highlightedHeaderArg: 'selection', highlightedHeaderArg: 'selection',
commandName: 'Shell', commandName: 'Shell',
}) })
await expect
.poll(() => page.getByText('Please select one').count())
.toBe(1)
await clickOnCap() await clickOnCap()
await page.waitForTimeout(500) await page.waitForTimeout(1000)
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await page.waitForTimeout(500)
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await page.waitForTimeout(500)
await cmdBar.expectState({ await cmdBar.expectState({
stage: 'review', stage: 'review',
headerArguments: { headerArguments: {
@ -3306,7 +3307,7 @@ profile001 = startProfileAt([-20, 20], sketch001)
}, initialCode) }, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 }) await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.settled(cmdBar)
await toolbar.openPane('feature-tree') await toolbar.openPane('feature-tree')
// One dumb hardcoded screen pixel value // One dumb hardcoded screen pixel value
@ -3386,7 +3387,7 @@ sweep001 = sweep(sketch001, path = sketch002)
}, initialCode) }, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 }) await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.settled(cmdBar)
// One dumb hardcoded screen pixel value // One dumb hardcoded screen pixel value
const testPoint = { x: 500, y: 250 } const testPoint = { x: 500, y: 250 }
@ -3399,6 +3400,9 @@ sweep001 = sweep(sketch001, path = sketch002)
await test.step(`Go through the Shell flow and fail validation with a toast`, async () => { await test.step(`Go through the Shell flow and fail validation with a toast`, async () => {
await toolbar.shellButton.click() await toolbar.shellButton.click()
await expect
.poll(() => page.getByText('Please select one').count())
.toBe(1)
await cmdBar.expectState({ await cmdBar.expectState({
stage: 'arguments', stage: 'arguments',
currentArgKey: 'selection', currentArgKey: 'selection',
@ -3433,26 +3437,29 @@ sweep001 = sweep(sketch001, path = sketch002)
const initialCode = ` const initialCode = `
sketch001 = startSketchOn(XZ) sketch001 = startSketchOn(XZ)
|> startProfileAt([-100.0, 100.0], %) |> startProfileAt([-100.0, 100.0], %)
|> angledLine([0, 200.0], %, $rectangleSegmentA001) |> angledLine(angle = 0, length = 200.0, tag = $rectangleSegmentA001)
|> angledLine([segAng(rectangleSegmentA001) - 90, 200], %, $rectangleSegmentB001) |> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 200, tag = $rectangleSegmentB001)
|> angledLine([ |> angledLine(
segAng(rectangleSegmentA001), angle=segAng(rectangleSegmentA001),
-segLen(rectangleSegmentA001) length=-segLen(rectangleSegmentA001),
], %, $rectangleSegmentC001) tag=$rectangleSegmentC001,
)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close() |> close()
extrude001 = extrude(sketch001, length = 200) extrude001 = extrude(sketch001, length = 200)
sketch002 = startSketchOn(extrude001, rectangleSegmentA001) sketch002 = startSketchOn(extrude001, rectangleSegmentA001)
|> startProfileAt([-66.77, 84.81], %) |> startProfileAt([-66.77, 84.81], %)
|> angledLine([180, 27.08], %, $rectangleSegmentA002) |> angledLine(angle = 180, length = 27.08, tag = $rectangleSegmentA002)
|> angledLine([ |> angledLine(
segAng(rectangleSegmentA002) - 90, angle=segAng(rectangleSegmentA002) - 90,
27.8 length=27.8,
], %, $rectangleSegmentB002) tag=$rectangleSegmentB002,
|> angledLine([ )
segAng(rectangleSegmentA002), |> angledLine(
-segLen(rectangleSegmentA002) angle=segAng(rectangleSegmentA002),
], %, $rectangleSegmentC002) length=-segLen(rectangleSegmentA002),
tag=$rectangleSegmentC002,
)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close() |> close()
` `
@ -3462,12 +3469,13 @@ segAng(rectangleSegmentA002),
}, initialCode) }, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 }) await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.settled(cmdBar)
// select line of code // select line of code
const codeToSelecton = `segAng(rectangleSegmentA002) - 90,` const codeToSelection = `segAng(rectangleSegmentA002) - 90,`
// revolve // revolve
await page.getByText(codeToSelecton).click() await editor.scrollToText(codeToSelection)
await page.getByText(codeToSelection).click()
await toolbar.revolveButton.click() await toolbar.revolveButton.click()
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
@ -3521,35 +3529,32 @@ segAng(rectangleSegmentA002),
}) => { }) => {
const initialCode = `sketch001 = startSketchOn(XZ) const initialCode = `sketch001 = startSketchOn(XZ)
|> startProfileAt([-102.57, 101.72], %) |> startProfileAt([-102.57, 101.72], %)
|> angledLine([0, 202.6], %, $rectangleSegmentA001) |> angledLine(angle = 0, length = 202.6, tag = $rectangleSegmentA001)
|> angledLine([ |> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 202.6, tag = $rectangleSegmentB001)
segAng(rectangleSegmentA001) - 90, |> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001), tag = $rectangleSegmentC001)
202.6
], %, $rectangleSegmentB001)
|> angledLine([
segAng(rectangleSegmentA001),
-segLen(rectangleSegmentA001)
], %, $rectangleSegmentC001)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close() |> close()
extrude001 = extrude(sketch001, length = 50) extrude001 = extrude(sketch001, length = 50)
sketch002 = startSketchOn(extrude001, rectangleSegmentA001) sketch002 = startSketchOn(extrude001, rectangleSegmentA001)
|> circle(center = [-11.34, 10.0], radius = 8.69) |> circle(center = [-11.34, 10.0], radius = 8.69)
` `
await context.addInitScript((initialCode) => { await context.addInitScript((initialCode) => {
localStorage.setItem('persistCode', initialCode) localStorage.setItem('persistCode', initialCode)
}, initialCode) }, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 }) await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
await scene.settled(cmdBar)
// select line of code // select line of code
const codeToSelecton = `center = [-11.34, 10.0]` const codeToSelection = `center = [-11.34, 10.0]`
// revolve // revolve
await page.getByText(codeToSelecton).click() await editor.scrollToText(codeToSelection)
await page.getByText(codeToSelection).click()
await toolbar.revolveButton.click() await toolbar.revolveButton.click()
await page.getByText('Edge', { exact: true }).click() await page.getByText('Edge', { exact: true }).click()
const lineCodeToSelection = `|> angledLine([0, 202.6], %, $rectangleSegmentA001)` const lineCodeToSelection = `angledLine(angle = 0, length = 202.6, tag = $rectangleSegmentA001)`
await page.getByText(lineCodeToSelection).click() await page.getByText(lineCodeToSelection).click()
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
@ -3595,6 +3600,7 @@ sketch002 = startSketchOn(extrude001, rectangleSegmentA001)
await editor.expectEditor.toContain( await editor.expectEditor.toContain(
newCodeToFind.replace('angle = 360', 'angle = angle001') newCodeToFind.replace('angle = 360', 'angle = angle001')
) )
expect(editor.expectEditor.toContain(newCodeToFind)).toBeTruthy()
}) })
test('revolve sketch circle around line segment from startProfileAt sketch', async ({ test('revolve sketch circle around line segment from startProfileAt sketch', async ({
context, context,
@ -3605,22 +3611,23 @@ sketch002 = startSketchOn(extrude001, rectangleSegmentA001)
toolbar, toolbar,
cmdBar, cmdBar,
}) => { }) => {
const initialCode = `sketch002 = startSketchOn(XY) const initialCode = `
sketch002 = startSketchOn(XY)
|> startProfileAt([-2.02, 1.79], %) |> startProfileAt([-2.02, 1.79], %)
|> xLine(length = 2.6) |> xLine(length = 2.6)
sketch001 = startSketchOn(-XY) sketch001 = startSketchOn('-XY')
|> startProfileAt([-0.48, 1.25], %) |> startProfileAt([-0.48, 1.25], %)
|> angledLine([0, 2.38], %, $rectangleSegmentA001) |> angledLine(angle = 0, length = 2.38, tag = $rectangleSegmentA001)
|> angledLine([segAng(rectangleSegmentA001) - 90, 2.4], %, $rectangleSegmentB001) |> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 2.4, tag = $rectangleSegmentB001)
|> angledLine([ |> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001), tag = $rectangleSegmentC001)
segAng(rectangleSegmentA001),
-segLen(rectangleSegmentA001)
], %, $rectangleSegmentC001)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close() |> close()
extrude001 = extrude(sketch001, length = 5) extrude001 = extrude(sketch001, length = 5)
sketch003 = startSketchOn(extrude001, 'START') sketch003 = startSketchOn(extrude001, 'START')
|> circle(center = [-0.69, 0.56], radius = 0.28) |> circle(
center = [-0.69, 0.56],
radius = 0.28
)
` `
await context.addInitScript((initialCode) => { await context.addInitScript((initialCode) => {
@ -3628,15 +3635,20 @@ sketch003 = startSketchOn(extrude001, 'START')
}, initialCode) }, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 }) await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.connectionEstablished()
await scene.settled(cmdBar)
// select line of code // select line of code
const codeToSelecton = `center = [-0.69, 0.56]` const codeToSelection = `center = [-0.69, 0.56]`
// revolve // revolve
await page.getByText(codeToSelecton).click()
await toolbar.revolveButton.click() await toolbar.revolveButton.click()
await page.waitForTimeout(1000)
await editor.scrollToText(codeToSelection)
await page.getByText(codeToSelection).click()
await expect.poll(() => page.getByText('AxisOrEdge').count()).toBe(2)
await page.getByText('Edge', { exact: true }).click() await page.getByText('Edge', { exact: true }).click()
const lineCodeToSelection = `|> xLine(length = 2.6)` const lineCodeToSelection = `length = 2.6`
await editor.scrollToText(lineCodeToSelection)
await page.getByText(lineCodeToSelection).click() await page.getByText(lineCodeToSelection).click()
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
@ -3703,21 +3715,22 @@ extrude001 = extrude(profile001, length = 100)
}, initialCode) }, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 }) await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.settled(cmdBar)
// One dumb hardcoded screen pixel value // One dumb hardcoded screen pixel value
const testPoint = { x: 500, y: 250 } const testPoint = { x: 500, y: 250 }
const initialColor: [number, number, number] = [123, 123, 123] const initialColor: [number, number, number] = [123, 123, 123]
const tolerance = 50
await test.step(`Confirm extrude exists with default appearance`, async () => { await test.step(`Confirm extrude exists with default appearance`, async () => {
await toolbar.closePane('code') await toolbar.closePane('code')
await scene.expectPixelColor(initialColor, testPoint, 15) await scene.expectPixelColor(initialColor, testPoint, tolerance)
}) })
async function setApperanceAndCheck( async function setApperanceAndCheck(
option: string, option: string,
hex: string, hex: string,
shapeColor: [number, number, number] shapeColor?: [number, number, number]
) { ) {
await toolbar.openPane('feature-tree') await toolbar.openPane('feature-tree')
const enterAppearanceFlow = async (stepName: string) => const enterAppearanceFlow = async (stepName: string) =>
@ -3766,7 +3779,9 @@ extrude001 = extrude(profile001, length = 100)
}) })
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await toolbar.closePane('feature-tree') await toolbar.closePane('feature-tree')
await scene.expectPixelColor(shapeColor, testPoint, 10) if (shapeColor) {
await scene.expectPixelColor(shapeColor, testPoint, tolerance)
}
await toolbar.openPane('code') await toolbar.openPane('code')
if (hex === 'default') { if (hex === 'default') {
const anyAppearanceDeclaration = `|> appearance(` const anyAppearanceDeclaration = `|> appearance(`
@ -3785,16 +3800,17 @@ extrude001 = extrude(profile001, length = 100)
} }
await test.step(`Go through the Set Appearance flow for all options`, async () => { await test.step(`Go through the Set Appearance flow for all options`, async () => {
await setApperanceAndCheck('Red', '#FF0000', [180, 0, 0]) await setApperanceAndCheck('Red', '#FF0000', [180, 30, 30])
await setApperanceAndCheck('Green', '#00FF00', [0, 180, 0]) // Not checking the scene color every time cause that's not really deterministic. Red seems reliable though
await setApperanceAndCheck('Blue', '#0000FF', [0, 0, 180]) await setApperanceAndCheck('Green', '#00FF00')
await setApperanceAndCheck('Turquoise', '#00FFFF', [0, 180, 180]) await setApperanceAndCheck('Blue', '#0000FF')
await setApperanceAndCheck('Purple', '#FF00FF', [180, 0, 180]) await setApperanceAndCheck('Turquoise', '#00FFFF')
await setApperanceAndCheck('Yellow', '#FFFF00', [180, 180, 0]) await setApperanceAndCheck('Purple', '#FF00FF')
await setApperanceAndCheck('Black', '#000000', [0, 0, 0]) await setApperanceAndCheck('Yellow', '#FFFF00')
await setApperanceAndCheck('Dark Grey', '#080808', [0x33, 0x33, 0x33]) await setApperanceAndCheck('Black', '#000000')
await setApperanceAndCheck('Light Grey', '#D3D3D3', [176, 176, 176]) await setApperanceAndCheck('Dark Grey', '#080808')
await setApperanceAndCheck('White', '#FFFFFF', [184, 184, 184]) await setApperanceAndCheck('Light Grey', '#D3D3D3')
await setApperanceAndCheck('White', '#FFFFFF')
await setApperanceAndCheck( await setApperanceAndCheck(
'Default (clear appearance)', 'Default (clear appearance)',
'default', 'default',

View File

@ -83,7 +83,7 @@ test(
test( test(
'click help/keybindings from project page', 'click help/keybindings from project page',
{ tag: '@electron' }, { tag: '@electron' },
async ({ context, page }, testInfo) => { async ({ scene, cmdBar, context, page }, testInfo) => {
await context.folderSetupFn(async (dir) => { await context.folderSetupFn(async (dir) => {
const bracketDir = path.join(dir, 'bracket') const bracketDir = path.join(dir, 'bracket')
await fsp.mkdir(bracketDir, { recursive: true }) await fsp.mkdir(bracketDir, { recursive: true })
@ -95,17 +95,11 @@ test(
await page.setBodyDimensions({ width: 1200, height: 500 }) await page.setBodyDimensions({ width: 1200, height: 500 })
page.on('console', console.log)
// expect to see the text bracket // expect to see the text bracket
await expect(page.getByText('bracket')).toBeVisible() await expect(page.getByText('bracket')).toBeVisible()
await page.getByText('bracket').click() await page.getByText('bracket').click()
await expect(page.getByTestId('loading')).toBeAttached() await scene.settled(cmdBar)
await expect(page.getByTestId('loading')).not.toBeAttached({
timeout: 20_000,
})
// click ? button // click ? button
await page.getByTestId('help-button').click() await page.getByTestId('help-button').click()
@ -120,7 +114,7 @@ test(
test( test(
'open a file in a project works and renders, open another file in different project with errors, it should clear the scene', 'open a file in a project works and renders, open another file in different project with errors, it should clear the scene',
{ tag: '@electron' }, { tag: '@electron' },
async ({ context, page, editor }, testInfo) => { async ({ scene, cmdBar, context, page, editor }, testInfo) => {
await context.folderSetupFn(async (dir) => { await context.folderSetupFn(async (dir) => {
const bracketDir = path.join(dir, 'bracket') const bracketDir = path.join(dir, 'bracket')
await fsp.mkdir(bracketDir, { recursive: true }) await fsp.mkdir(bracketDir, { recursive: true })
@ -149,24 +143,7 @@ test(
await page.getByText('bracket').click() await page.getByText('bracket').click()
await expect(page.getByTestId('loading')).toBeAttached() await scene.settled(cmdBar)
await expect(page.getByTestId('loading')).not.toBeAttached({
timeout: 20_000,
})
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).toBeEnabled({
timeout: 20_000,
})
// gray at this pixel means the stream has loaded in the most
// user way we can verify it (pixel color)
await expect
.poll(() => u.getGreatestPixDiff(pointOnModel, [110, 110, 110]), {
timeout: 10_000,
})
.toBeLessThan(20)
}) })
await test.step('Clicking the logo takes us back to the projects page / home', async () => { await test.step('Clicking the logo takes us back to the projects page / home', async () => {
@ -209,7 +186,7 @@ test(
test( test(
'open a file in a project works and renders, open another file in different project that is empty, it should clear the scene', 'open a file in a project works and renders, open another file in different project that is empty, it should clear the scene',
{ tag: '@electron' }, { tag: '@electron' },
async ({ context, page }, testInfo) => { async ({ scene, cmdBar, context, page }, testInfo) => {
await context.folderSetupFn(async (dir) => { await context.folderSetupFn(async (dir) => {
const bracketDir = path.join(dir, 'bracket') const bracketDir = path.join(dir, 'bracket')
await fsp.mkdir(bracketDir, { recursive: true }) await fsp.mkdir(bracketDir, { recursive: true })
@ -235,24 +212,7 @@ test(
await page.getByText('bracket').click() await page.getByText('bracket').click()
await expect(page.getByTestId('loading')).toBeAttached() await scene.settled(cmdBar)
await expect(page.getByTestId('loading')).not.toBeAttached({
timeout: 20_000,
})
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).toBeEnabled({
timeout: 20_000,
})
// gray at this pixel means the stream has loaded in the most
// user way we can verify it (pixel color)
await expect
.poll(() => u.getGreatestPixDiff(pointOnModel, [125, 125, 125]), {
timeout: 10_000,
})
.toBeLessThan(15)
}) })
await test.step('Clicking the logo takes us back to the projects page / home', async () => { await test.step('Clicking the logo takes us back to the projects page / home', async () => {
@ -352,7 +312,7 @@ test(
test( test(
'open a file in a project works and renders, open another file in the same project with errors, it should clear the scene', 'open a file in a project works and renders, open another file in the same project with errors, it should clear the scene',
{ tag: '@electron' }, { tag: '@electron' },
async ({ context, page }, testInfo) => { async ({ scene, cmdBar, context, page }, testInfo) => {
if (runningOnWindows()) { if (runningOnWindows()) {
test.fixme(orRunWhenFullSuiteEnabled()) test.fixme(orRunWhenFullSuiteEnabled())
} }
@ -380,10 +340,7 @@ test(
await page.getByText('bracket').click() await page.getByText('bracket').click()
await expect(page.getByTestId('loading')).toBeAttached() await scene.settled(cmdBar)
await expect(page.getByTestId('loading')).not.toBeAttached({
timeout: 20_000,
})
await expect( await expect(
page.getByRole('button', { name: 'Start Sketch' }) page.getByRole('button', { name: 'Start Sketch' })
@ -443,10 +400,10 @@ test(
await expect(page.getByText('broken-code')).toBeVisible() await expect(page.getByText('broken-code')).toBeVisible()
await page.getByText('broken-code').click() await page.getByText('broken-code').click()
// Gotcha: You can not use scene.waitForExecutionDone() since the KCL code is going to fail // Gotcha: You can not use scene.settled() since the KCL code is going to fail
await expect(page.getByTestId('loading')).not.toBeAttached({ await expect(
timeout: 20_000, page.getByTestId('model-state-indicator-playing')
}) ).toBeAttached()
// Gotcha: Scroll to the text content in code mirror because CodeMirror lazy loads DOM content // Gotcha: Scroll to the text content in code mirror because CodeMirror lazy loads DOM content
await editor.scrollToText( await editor.scrollToText(
@ -469,7 +426,7 @@ test.describe('Can export from electron app', () => {
test( test(
`Can export using ${method}`, `Can export using ${method}`,
{ tag: ['@electron', '@skipLocalEngine'] }, { tag: ['@electron', '@skipLocalEngine'] },
async ({ context, page, tronApp }, testInfo) => { async ({ scene, cmdBar, context, page, tronApp }, testInfo) => {
if (!tronApp) { if (!tronApp) {
fail() fail()
} }
@ -499,10 +456,7 @@ test.describe('Can export from electron app', () => {
await page.getByText('bracket').click() await page.getByText('bracket').click()
await expect(page.getByTestId('loading')).toBeAttached() await scene.settled(cmdBar)
await expect(page.getByTestId('loading')).not.toBeAttached({
timeout: 20_000,
})
await expect( await expect(
page.getByRole('button', { name: 'Start Sketch' }) page.getByRole('button', { name: 'Start Sketch' })
@ -812,7 +766,7 @@ test.describe(`Project management commands`, () => {
test( test(
`Rename from project page`, `Rename from project page`,
{ tag: '@electron' }, { tag: '@electron' },
async ({ context, page }, testInfo) => { async ({ context, page, scene, cmdBar }, testInfo) => {
const projectName = `my_project_to_rename` const projectName = `my_project_to_rename`
await context.folderSetupFn(async (dir) => { await context.folderSetupFn(async (dir) => {
await fsp.mkdir(`${dir}/${projectName}`, { recursive: true }) await fsp.mkdir(`${dir}/${projectName}`, { recursive: true })
@ -821,7 +775,6 @@ test.describe(`Project management commands`, () => {
`${dir}/${projectName}/main.kcl` `${dir}/${projectName}/main.kcl`
) )
}) })
const u = await getUtils(page)
// Constants and locators // Constants and locators
const projectHomeLink = page.getByTestId('project-link') const projectHomeLink = page.getByTestId('project-link')
@ -843,7 +796,7 @@ test.describe(`Project management commands`, () => {
page.on('console', console.log) page.on('console', console.log)
await projectHomeLink.click() await projectHomeLink.click()
await u.waitForPageLoad() await scene.settled(cmdBar)
}) })
await test.step(`Run rename command via command palette`, async () => { await test.step(`Run rename command via command palette`, async () => {
@ -882,7 +835,6 @@ test.describe(`Project management commands`, () => {
`${dir}/${projectName}/main.kcl` `${dir}/${projectName}/main.kcl`
) )
}) })
const u = await getUtils(page)
// Constants and locators // Constants and locators
const projectHomeLink = page.getByTestId('project-link') const projectHomeLink = page.getByTestId('project-link')
@ -900,9 +852,9 @@ test.describe(`Project management commands`, () => {
await page.setBodyDimensions({ width: 1200, height: 500 }) await page.setBodyDimensions({ width: 1200, height: 500 })
page.on('console', console.log) page.on('console', console.log)
await page.waitForTimeout(3000)
await projectHomeLink.click() await projectHomeLink.click()
await u.waitForPageLoad()
await scene.connectionEstablished()
await scene.settled(cmdBar) await scene.settled(cmdBar)
}) })
@ -926,7 +878,7 @@ test.describe(`Project management commands`, () => {
test( test(
`Rename from home page`, `Rename from home page`,
{ tag: '@electron' }, { tag: '@electron' },
async ({ context, page, homePage }, testInfo) => { async ({ context, page, homePage, scene, cmdBar }, testInfo) => {
const projectName = `my_project_to_rename` const projectName = `my_project_to_rename`
await context.folderSetupFn(async (dir) => { await context.folderSetupFn(async (dir) => {
await fsp.mkdir(`${dir}/${projectName}`, { recursive: true }) await fsp.mkdir(`${dir}/${projectName}`, { recursive: true })
@ -982,7 +934,7 @@ test.describe(`Project management commands`, () => {
test( test(
`Delete from home page`, `Delete from home page`,
{ tag: '@electron' }, { tag: '@electron' },
async ({ context, page }, testInfo) => { async ({ context, page, scene, cmdBar }, testInfo) => {
const projectName = `my_project_to_delete` const projectName = `my_project_to_delete`
await context.folderSetupFn(async (dir) => { await context.folderSetupFn(async (dir) => {
await fsp.mkdir(`${dir}/${projectName}`, { recursive: true }) await fsp.mkdir(`${dir}/${projectName}`, { recursive: true })
@ -1033,6 +985,7 @@ test.describe(`Project management commands`, () => {
homePage, homePage,
toolbar, toolbar,
cmdBar, cmdBar,
scene,
}) => { }) => {
const projectName = 'test-project' const projectName = 'test-project'
await test.step(`Setup`, async () => { await test.step(`Setup`, async () => {
@ -1072,10 +1025,11 @@ test.describe(`Project management commands`, () => {
}) })
await cmdBar.argumentInput.fill(projectName) await cmdBar.argumentInput.fill(projectName)
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await scene.settled(cmdBar)
await toolbar.logoLink.click()
}) })
await test.step(`Check the project was created with a non-colliding name`, async () => { await test.step(`Check the project was created with a non-colliding name`, async () => {
await toolbar.logoLink.click()
await homePage.expectState({ await homePage.expectState({
projectCards: [ projectCards: [
{ {
@ -1106,10 +1060,11 @@ test.describe(`Project management commands`, () => {
}) })
await cmdBar.argumentInput.fill(projectName) await cmdBar.argumentInput.fill(projectName)
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await scene.settled(cmdBar)
await toolbar.logoLink.click()
}) })
await test.step(`Check the second project was created with a non-colliding name`, async () => { await test.step(`Check the second project was created with a non-colliding name`, async () => {
await toolbar.logoLink.click()
await homePage.expectState({ await homePage.expectState({
projectCards: [ projectCards: [
{ {
@ -1195,7 +1150,7 @@ test(
test( test(
'Nested directories in project without main.kcl do not create main.kcl', 'Nested directories in project without main.kcl do not create main.kcl',
{ tag: '@electron' }, { tag: '@electron' },
async ({ context, page }, testInfo) => { async ({ scene, cmdBar, context, page }, testInfo) => {
let testDir: string | undefined let testDir: string | undefined
await context.folderSetupFn(async (dir) => { await context.folderSetupFn(async (dir) => {
await fsp.mkdir(path.join(dir, 'router-template-slate', 'nested'), { await fsp.mkdir(path.join(dir, 'router-template-slate', 'nested'), {
@ -1218,10 +1173,7 @@ test(
await test.step('Open the project', async () => { await test.step('Open the project', async () => {
await page.getByText('router-template-slate').click() await page.getByText('router-template-slate').click()
await expect(page.getByTestId('loading')).toBeAttached() await scene.settled(cmdBar)
await expect(page.getByTestId('loading')).not.toBeAttached({
timeout: 20_000,
})
// It actually loads. // It actually loads.
await expect(u.codeLocator).toContainText('mounting bracket') await expect(u.codeLocator).toContainText('mounting bracket')
@ -1334,7 +1286,7 @@ test(
test( test(
'Can load a file with CRLF line endings', 'Can load a file with CRLF line endings',
{ tag: '@electron' }, { tag: '@electron' },
async ({ context, page }, testInfo) => { async ({ context, page, scene, cmdBar }, testInfo) => {
if (runningOnWindows()) { if (runningOnWindows()) {
test.fixme(orRunWhenFullSuiteEnabled()) test.fixme(orRunWhenFullSuiteEnabled())
} }
@ -1357,13 +1309,8 @@ test(
const u = await getUtils(page) const u = await getUtils(page)
await page.setBodyDimensions({ width: 1200, height: 500 }) await page.setBodyDimensions({ width: 1200, height: 500 })
page.on('console', console.log)
await page.getByText('router-template-slate').click() await page.getByText('router-template-slate').click()
await expect(page.getByTestId('loading')).toBeAttached() await scene.settled(cmdBar)
await expect(page.getByTestId('loading')).not.toBeAttached({
timeout: 20_000,
})
await expect(u.codeLocator).toContainText('routerDiameter') await expect(u.codeLocator).toContainText('routerDiameter')
await expect(u.codeLocator).toContainText('templateGap') await expect(u.codeLocator).toContainText('templateGap')
@ -1578,7 +1525,7 @@ extrude001 = extrude(sketch001, length = 200)`)
test( test(
'Opening a project should successfully load the stream, (regression test that this also works when switching between projects)', 'Opening a project should successfully load the stream, (regression test that this also works when switching between projects)',
{ tag: '@electron' }, { tag: '@electron' },
async ({ context, page, cmdBar, homePage }, testInfo) => { async ({ context, page, cmdBar, homePage, scene }, testInfo) => {
await context.folderSetupFn(async (dir) => { await context.folderSetupFn(async (dir) => {
await fsp.mkdir(path.join(dir, 'router-template-slate'), { await fsp.mkdir(path.join(dir, 'router-template-slate'), {
recursive: true, recursive: true,
@ -1607,13 +1554,10 @@ test(
path.join(dir, 'bracket', 'main.kcl') path.join(dir, 'bracket', 'main.kcl')
) )
}) })
const u = await getUtils(page)
await page.setBodyDimensions({ width: 1200, height: 500 }) await page.setBodyDimensions({ width: 1200, height: 500 })
page.on('console', console.log) page.on('console', console.log)
const pointOnModel = { x: 630, y: 280 }
await test.step('Opening the bracket project via command palette should load the stream', async () => { await test.step('Opening the bracket project via command palette should load the stream', async () => {
await homePage.expectState({ await homePage.expectState({
projectCards: [ projectCards: [
@ -1647,15 +1591,7 @@ test(
stage: 'commandBarClosed', stage: 'commandBarClosed',
}) })
await u.waitForPageLoad() await scene.settled(cmdBar)
// gray at this pixel means the stream has loaded in the most
// user way we can verify it (pixel color)
await expect
.poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
timeout: 10_000,
})
.toBeLessThan(15)
}) })
await test.step('Clicking the logo takes us back to the projects page / home', async () => { await test.step('Clicking the logo takes us back to the projects page / home', async () => {
@ -1672,15 +1608,7 @@ test(
await page.getByText('router-template-slate').click() await page.getByText('router-template-slate').click()
await u.waitForPageLoad() await scene.settled(cmdBar)
// gray at this pixel means the stream has loaded in the most
// user way we can verify it (pixel color)
await expect
.poll(() => u.getGreatestPixDiff(pointOnModel, [143, 143, 143]), {
timeout: 10_000,
})
.toBeLessThan(15)
}) })
await test.step('The projects on the home page should still be normal', async () => { await test.step('The projects on the home page should still be normal', async () => {
@ -1733,8 +1661,6 @@ test(
}) })
await page.setBodyDimensions({ width: 1200, height: 500 }) await page.setBodyDimensions({ width: 1200, height: 500 })
page.on('console', console.log)
// we'll grab this from the settings on screen before we switch // we'll grab this from the settings on screen before we switch
let originalProjectDirName: string let originalProjectDirName: string
const newProjectDirName = testInfo.outputPath( const newProjectDirName = testInfo.outputPath(
@ -1875,7 +1801,7 @@ test(
test( test(
'file pane is scrollable when there are many files', 'file pane is scrollable when there are many files',
{ tag: '@electron' }, { tag: '@electron' },
async ({ context, page }, testInfo) => { async ({ scene, cmdBar, context, page }, testInfo) => {
await context.folderSetupFn(async (dir) => { await context.folderSetupFn(async (dir) => {
const testDir = path.join(dir, 'testProject') const testDir = path.join(dir, 'testProject')
await fsp.mkdir(testDir, { recursive: true }) await fsp.mkdir(testDir, { recursive: true })
@ -1954,10 +1880,8 @@ test(
await test.step('setup, open file pane', async () => { await test.step('setup, open file pane', async () => {
await page.getByText('testProject').click() await page.getByText('testProject').click()
await expect(page.getByTestId('loading')).toBeAttached()
await expect(page.getByTestId('loading')).not.toBeAttached({ await scene.settled(cmdBar)
timeout: 20_000,
})
await page.getByTestId('files-pane-button').click() await page.getByTestId('files-pane-button').click()
}) })

View File

@ -41,15 +41,13 @@ sketch002 = startSketchOn(XZ)
extrude002 = extrude(sketch002, length = 50) extrude002 = extrude(sketch002, length = 50)
sketch003 = startSketchOn(XY) sketch003 = startSketchOn(XY)
|> startProfileAt([52.92, 157.81], %) |> startProfileAt([52.92, 157.81], %)
|> angledLine([0, 176.4], %, $rectangleSegmentA001) |> angledLine(angle = 0, length = 176.4, tag = $rectangleSegmentA001)
|> angledLine([ |> angledLine(
segAng(rectangleSegmentA001) - 90, angle = segAng(rectangleSegmentA001) - 90,
53.4 length = 53.4,
], %, $rectangleSegmentB001) tag = $rectangleSegmentB001,
|> angledLine([ )
segAng(rectangleSegmentA001), |> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001), tag = $rectangleSegmentC001)
-segLen(rectangleSegmentA001)
], %, $rectangleSegmentC001)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close() |> close()
extrude003 = extrude(sketch003, length = 20) extrude003 = extrude(sketch003, length = 20)
@ -63,7 +61,7 @@ test.describe('edit with AI example snapshots', () => {
localStorage.setItem('persistCode', file) localStorage.setItem('persistCode', file)
}, file) }, file)
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.settled(cmdBar)
const body1CapCoords = { x: 571, y: 351 } const body1CapCoords = { x: 571, y: 351 }
const [clickBody1Cap] = scene.makeMouseHelpers( const [clickBody1Cap] = scene.makeMouseHelpers(
@ -74,7 +72,6 @@ test.describe('edit with AI example snapshots', () => {
const submittingToast = page.getByText('Submitting to Text-to-CAD API...') const submittingToast = page.getByText('Submitting to Text-to-CAD API...')
await test.step('wait for scene to load select body and check selection came through', async () => { await test.step('wait for scene to load select body and check selection came through', async () => {
await scene.expectPixelColor([134, 134, 134], body1CapCoords, 15)
await clickBody1Cap() await clickBody1Cap()
await scene.expectPixelColor(yellow, body1CapCoords, 20) await scene.expectPixelColor(yellow, body1CapCoords, 20)
await editor.expectState({ await editor.expectState({

View File

@ -22,15 +22,9 @@ sketch002 = startSketchOn(XZ)
extrude002 = extrude(sketch002, length = 50) extrude002 = extrude(sketch002, length = 50)
sketch003 = startSketchOn(XY) sketch003 = startSketchOn(XY)
|> startProfileAt([52.92, 157.81], %) |> startProfileAt([52.92, 157.81], %)
|> angledLine([0, 176.4], %, $rectangleSegmentA001) |> angledLine(angle = 0, length = 176.4, tag = $rectangleSegmentA001)
|> angledLine([ |> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 53.4, tag = $rectangleSegmentB001)
segAng(rectangleSegmentA001) - 90, |> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001), tag = $rectangleSegmentC001)
53.4
], %, $rectangleSegmentB001)
|> angledLine([
segAng(rectangleSegmentA001),
-segLen(rectangleSegmentA001)
], %, $rectangleSegmentC001)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close() |> close()
extrude003 = extrude(sketch003, length = 20) extrude003 = extrude(sketch003, length = 20)
@ -57,11 +51,12 @@ test.describe('Prompt-to-edit tests', { tag: '@skipWin' }, () => {
page, page,
scene, scene,
}) => { }) => {
test.fixme(orRunWhenFullSuiteEnabled())
await context.addInitScript((file) => { await context.addInitScript((file) => {
localStorage.setItem('persistCode', file) localStorage.setItem('persistCode', file)
}, file) }, file)
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.settled(cmdBar)
const body1CapCoords = { x: 571, y: 311 } const body1CapCoords = { x: 571, y: 311 }
const greenCheckCoords = { x: 565, y: 305 } const greenCheckCoords = { x: 565, y: 305 }
@ -156,7 +151,7 @@ test.describe('Prompt-to-edit tests', { tag: '@skipWin' }, () => {
localStorage.setItem('persistCode', file) localStorage.setItem('persistCode', file)
}, file) }, file)
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.settled(cmdBar)
const body1CapCoords = { x: 571, y: 311 } const body1CapCoords = { x: 571, y: 311 }
const [clickBody1Cap] = scene.makeMouseHelpers( const [clickBody1Cap] = scene.makeMouseHelpers(
@ -212,7 +207,7 @@ test.describe('Prompt-to-edit tests', { tag: '@skipWin' }, () => {
localStorage.setItem('persistCode', file) localStorage.setItem('persistCode', file)
}, file) }, file)
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.settled(cmdBar)
const submittingToast = page.getByText('Submitting to Text-to-CAD API...') const submittingToast = page.getByText('Submitting to Text-to-CAD API...')
const successToast = page.getByText('Prompt to edit successful') const successToast = page.getByText('Prompt to edit successful')
@ -265,6 +260,7 @@ test.describe('Prompt-to-edit tests', { tag: '@skipWin' }, () => {
page, page,
scene, scene,
}) => { }) => {
test.fixme(orRunWhenFullSuiteEnabled())
const body1CapCoords = { x: 571, y: 311 } const body1CapCoords = { x: 571, y: 311 }
const body2WallCoords = { x: 620, y: 152 } const body2WallCoords = { x: 620, y: 152 }
const [clickBody1Cap] = scene.makeMouseHelpers( const [clickBody1Cap] = scene.makeMouseHelpers(
@ -281,7 +277,7 @@ test.describe('Prompt-to-edit tests', { tag: '@skipWin' }, () => {
localStorage.setItem('persistCode', file) localStorage.setItem('persistCode', file)
}, file) }, file)
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.waitForExecutionDone() await scene.settled(cmdBar)
const submittingToast = page.getByText('Submitting to Text-to-CAD API...') const submittingToast = page.getByText('Submitting to Text-to-CAD API...')
const successToast = page.getByText('Prompt to edit successful') const successToast = page.getByText('Prompt to edit successful')

Some files were not shown because too many files have changed in this diff Show More