Compare commits

...

59 Commits

Author SHA1 Message Date
441e18e916 Update generated test output 2025-04-23 19:36:02 -04:00
1502f923ee KCL bugfix: CallExpressionKw was not usable as math operand (#6460)
Closes https://github.com/KittyCAD/modeling-app/issues/4992
2025-04-23 21:21:57 +00:00
f03a684eec Modeling view appearance final tweaks (#6425)
* Remove bug button from LowerRightControls

There is a "report a bug" button in the help menus, both native and
lower-right corner. This is overkill, and users are not using coredump
well.

* Remove coredump from refresh UI button

* Add a "Refresh app" command to palette

* Update snapshots

* Rework "Refresh and report bug" menu item to "Report a bug"

* Add refresh button to sidebar

* Convert upper-right refresh button to Share

* Tweak styles of command button

* Make anonymous user icon same size as known user image

* Remove ModelStateIndicator

* Use hotkeyDisplay for the sidebar too

* Update snapshots

* Remove tooltip from command bar open button

* tsc, lint, and fmt
2025-04-23 15:20:45 -04:00
45e17c50e7 docs: Add docs for creating simulation tests (#6453) 2025-04-23 11:59:07 -07:00
6bf74379a7 Kwargs: hollow (#6438) 2025-04-23 18:35:33 +00:00
01c6fd53fa Fix Cargo.lock (#6452) 2025-04-23 17:39:35 +00:00
f8306c0275 Try running with 8 macOS cores again (#6420)
* Revert "Disable all macOS testing for now (#6395)"

This reverts commit fbc3251c3f.

* Try running with 8 macOS cores again
2025-04-23 13:10:09 -04:00
900ef9e18d Release KCL 63 (#6446) 2025-04-23 12:35:16 -04:00
a46186573c Kwargs: profileStart/x/y (#6439) 2025-04-23 15:21:58 +00:00
90f6c1bb04 Fix Back button on SignIn page & Add Cancel button (#6415)
* Back button on SignIn page leads to Home page
Fixes #6413

* Add regression test for the bug, and for new cancel button

* Full sign in e2e test

* Good bot

Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>

---------

Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>
2025-04-23 15:12:33 +00:00
41d946b339 Remove Spline and Text tools from sketch mode toolbar (#6427)
* Remove Spline and Text tool from sketch toolbar

* Update sketch-related snapshots

* Update snapshots
2025-04-23 13:06:51 +00:00
b7385d5f25 Turn on units of measure (BREAKING CHANGE) (#6343)
* Turn on uom checks

Signed-off-by: Nick Cameron <nrc@ncameron.org>

* Convert all lengths to mm for engine calls

Signed-off-by: Nick Cameron <nrc@ncameron.org>

---------

Signed-off-by: Nick Cameron <nrc@ncameron.org>
2025-04-22 22:58:35 +00:00
3d22f6cd66 Only save one version of tag info per epoch (#6429)
Signed-off-by: Nick Cameron <nrc@ncameron.org>
2025-04-23 09:22:52 +12:00
9730e3f5b3 KCL: Rephrase confusing error msg (#6436)
Before:

  × undefined value: memory item key `foo` is not defined

Now:

  × undefined value: `foo` is not defined
2025-04-22 20:24:53 +00:00
29d6b22d63 Restore tests that use Text-to-CAD (#6284) 2025-04-22 14:55:10 -04:00
f99e44e371 Kwargs: assert functions (#6406)
Closes https://github.com/KittyCAD/modeling-app/issues/6408
2025-04-22 17:44:52 +00:00
8be36d3d16 Update known-issues.md (#6428)
* Update known-issues.md

* Update known-issues.md

Little more description.

---------

Co-authored-by: Josh Gomez <114548659+jgomez720@users.noreply.github.com>
2025-04-22 17:18:35 +00:00
bc1742af48 Remove bold mono font (#6414)
* Remove all use of bold mono font
A few mono font points are left, mostly for numbers which look better in
monospace.

* Update snapshots
2025-04-22 15:17:47 +00:00
e4080cc184 Allow snapshot tests to report status (#6430) 2025-04-22 10:39:21 -04:00
18de6ccb59 #6417 Fix svg attribute names, add new eslint rule (#6418)
* fix svg attribute names, add new eslint rule

* tabs -> spaces
2025-04-22 09:15:38 -04:00
f00ea4cf5e Use TAB API to determine when merges are blocked (#6405)
* Use TAB API to determine when merges are blocked

* Enable a test that is known to fail
2025-04-22 09:02:02 -04:00
c5539be814 Warn if settings (or other inner attributes) are not at the top of a file (#6426)
Signed-off-by: Nick Cameron <nrc@ncameron.org>
2025-04-22 14:43:09 +12:00
f5d6a12d8c Remove unused KCL file (#6383) 2025-04-21 23:59:30 +00:00
e4e18dfd4b Coerce the result of a function call to the function's return type (#6309)
Signed-off-by: Nick Cameron <nrc@ncameron.org>
2025-04-22 11:00:53 +12:00
30ee547ce4 More consistency in 'Create project' and 'Share part via Zoo link' buttons and commands (#6378)
Polish: More consistency in 'Create project' and 'Share part via Zoo link' buttons and commands
2025-04-21 15:08:51 -04:00
f8ca6ad746 BREAKING: Change polygon to keyword args (#6385)
* Change polygon to keyword args

* Update docs

* Update generated output

* Update docs to mention the default for inscribed

* Appease clippy

* Remove tag parameter

* Update docs since removing tag

* Remove inscribed from autocomplete snippet since the default is true
2025-04-21 18:29:32 +00:00
7a90d029e1 Update onboarding interactive numbers test to use editor fixture, take 2 (#6399)
Update onboarding interactive numbers test to use editor fixture

Trying to bring it down from the leaderboard of failed tests.

Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
2025-04-21 14:34:20 +00:00
2900858171 Bump the minor group across 1 directory with 8 updates (#6411)
Bumps the minor group with 6 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [@csstools/postcss-oklab-function](https://github.com/csstools/postcss-plugins/tree/HEAD/plugins/postcss-oklab-function) | `4.0.8` | `4.0.9` |
| [@playwright/test](https://github.com/microsoft/playwright) | `1.51.1` | `1.52.0` |
| [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `22.14.0` | `22.14.1` |
| [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/tree/HEAD/packages/plugin-react) | `4.3.4` | `4.4.1` |
| [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.29.1` | `8.30.1` |
| [rollup](https://github.com/rollup/rollup) | `4.39.0` | `4.40.0` |



Updates `@csstools/postcss-oklab-function` from 4.0.8 to 4.0.9
- [Changelog](https://github.com/csstools/postcss-plugins/blob/main/plugins/postcss-oklab-function/CHANGELOG.md)
- [Commits](https://github.com/csstools/postcss-plugins/commits/HEAD/plugins/postcss-oklab-function)

Updates `@playwright/test` from 1.51.1 to 1.52.0
- [Release notes](https://github.com/microsoft/playwright/releases)
- [Commits](https://github.com/microsoft/playwright/compare/v1.51.1...v1.52.0)

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

Updates `@vitejs/plugin-react` from 4.3.4 to 4.4.1
- [Release notes](https://github.com/vitejs/vite-plugin-react/releases)
- [Changelog](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite-plugin-react/commits/plugin-react@4.4.1/packages/plugin-react)

Updates `typescript-eslint` from 8.29.1 to 8.30.1
- [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.30.1/packages/typescript-eslint)

Updates `rollup` from 4.39.0 to 4.40.0
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.39.0...v4.40.0)

Updates `@typescript-eslint/eslint-plugin` from 8.29.1 to 8.30.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.30.1/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.29.1 to 8.30.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.30.1/packages/parser)

---
updated-dependencies:
- dependency-name: "@csstools/postcss-oklab-function"
  dependency-version: 4.0.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: minor
- dependency-name: "@playwright/test"
  dependency-version: 1.52.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: minor
- dependency-name: "@types/node"
  dependency-version: 22.14.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: minor
- dependency-name: "@vitejs/plugin-react"
  dependency-version: 4.4.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: minor
- dependency-name: typescript-eslint
  dependency-version: 8.30.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: minor
- dependency-name: rollup
  dependency-version: 4.40.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: minor
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.30.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: minor
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.30.1
  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-21 12:42:14 +00:00
0dee219e46 Check for local name clash with existing variables in Insert flow (#6375)
Check for local name alias clashes with extisting var in Insert flow
Fixes #6230
2025-04-21 10:35:43 +00:00
790613e708 KCL: Convert bezierCurve to use keyword args (#6381) 2025-04-20 20:10:27 -05:00
0a35722595 Silence snapshot failures for now (#6403) 2025-04-19 10:59:53 +00:00
f30fc376ee Allow deletion of only item in AST (#6335)
* Update test to check for deletion of only operation

* Allow updateEditorWithAstAndWriteToFile to clear AST

* Update src/lang/codeManager.ts

Co-authored-by: Jonathan Tran <jonnytran@gmail.com>

* Weave `isDeleting` through to `deleteSelectionPromise`

* fmt

---------

Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
2025-04-19 05:27:23 -04:00
8c1a95833d clean up sketch mode cmd (#6397) 2025-04-18 21:15:19 -04:00
4f1ba1be01 Fix error message whitelist to use updated message (#6404) 2025-04-18 23:27:34 +00:00
90acc00369 Kwargs migration: arc/arcTo (#6334) 2025-04-18 17:40:44 -05:00
e4fe8a4440 Fix CSG commands to be included in the artifact graph (#6391)
* Fix CSG commands to be included in the artifact graph

* Update output
2025-04-18 22:35:17 +00:00
fbc3251c3f Disable all macOS testing for now (#6395) 2025-04-18 21:37:33 +00:00
a0f3f0f50e Fix KCLError to have a real message (#6396)
* Fix KCLError to have a real message

* Remove unneeded error exception
2025-04-18 17:13:09 -04:00
dad0d5488f Support any installation path for rust toolchain (#6380) 2025-04-18 20:28:38 +00:00
77773fb052 ci: Add error to console exception (#6392)
* Add error to console exception

* Ignore in codespell

* Increase retry on snapshots

* Fixme's all around

---------

Co-authored-by: Jace Browning <jacebrowning@gmail.com>
Co-authored-by: Frank Noirot <frankjohnson1993@gmail.com>
2025-04-18 15:51:13 -04:00
1240ca6f21 Revert "Update onboarding interactive numbers test to use editor fixture" (#6393)
Revert "Update onboarding interactive numbers test to use editor fixture (#6356)"

This reverts commit c77bb92da9.
2025-04-18 14:57:40 -04:00
ce3a6ad286 Revert "Remove all use of bold mono font" (#6394)
Revert "Remove all use of bold mono font (#6373)"

This reverts commit 03c4121ab7.
2025-04-18 14:57:10 -04:00
a20f420a55 Add fixmes to failing e2e tests (#6388)
* Add fixmes to bad snapshots tests

* max attempts to 5 on snapshots

* Skip revolve on windows

* Another one

* Add fixme to edit with ML snapshot test

* fmt

* Use absolute import, fix lint

* Disable another test

---------

Co-authored-by: Frank Noirot <frankjohnson1993@gmail.com>
2025-04-18 14:14:23 -04:00
03c4121ab7 Remove all use of bold mono font (#6373)
* Remove all use of bold mono font
A few mono font points are left, mostly for numbers which look better in
monospace.

* make React happy with `clipRule`
2025-04-18 14:11:20 -04:00
8c2c877817 Bump crossbeam-channel from 0.5.14 to 0.5.15 in /rust in the security group (#6354)
Bump crossbeam-channel in /rust in the security group

Bumps the security group in /rust with 1 update: [crossbeam-channel](https://github.com/crossbeam-rs/crossbeam).


Updates `crossbeam-channel` from 0.5.14 to 0.5.15
- [Release notes](https://github.com/crossbeam-rs/crossbeam/releases)
- [Changelog](https://github.com/crossbeam-rs/crossbeam/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crossbeam-rs/crossbeam/compare/crossbeam-channel-0.5.14...crossbeam-channel-0.5.15)

---
updated-dependencies:
- dependency-name: crossbeam-channel
  dependency-version: 0.5.15
  dependency-type: indirect
  dependency-group: security
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-18 14:10:54 -04:00
c77bb92da9 Update onboarding interactive numbers test to use editor fixture (#6356)
Trying to bring it down from the leaderboard of failed tests.

Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
2025-04-18 14:06:23 -04:00
bd4bad0020 allow sending async commands to engine (#6342)
* start of async

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

check at end if the async commands completed

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

run at the end of inner_run

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

set import as async

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>

add to the wasm side

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

updates

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

fmt

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

* fire

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

* flake

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

* fixup for awaiting on import

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>

* fix mock

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

* fix mock

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>

* add a test where we import then do a bunch of other stuff

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

* updates

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

* fixup to see

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

* fixups

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

* fix tests

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

* updates

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

* cross platform time

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

* fixes

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>

* another appearance tests

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

* new docs and tests

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

* updates

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

* dont loop so tight

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

* fixes

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2025-04-17 17:22:19 -07:00
0b9889e313 Double the timeout in sketch editing test (#6376) 2025-04-17 14:32:11 -04:00
f9fbaa2298 Add files via upload (#6227)
* Add files via upload

Adding parametric pc fan and bottle

* Update and rename globals.kcl to parameters.kcl

* Update fan-housing.kcl

* Update fan-housing.kcl

* Update fan.kcl

* Update motor.kcl

* Update parameters.kcl

* Update kcl-samples simulation test output

* Update main.kcl

avoiding fn imports

* Update kcl-samples simulation test output

* remove functions

* angledLine kwargs

* tangentalArc kwargs

* Update kcl-samples simulation test output

* Update housing middle

more tweaks because I just can't help myself

* Update kcl-samples simulation test output

* Update kcl-samples simulation test output

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: jgomez720 <114548659+jgomez720@users.noreply.github.com>
2025-04-17 17:46:56 +00:00
6f2d127c4f Assemblies: Set translate and rotate via point-and-click (#6167)
* 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

* WIP: Set translate and rotate via point-and-click on imports. Boilerplate code
Will eventually close #6020

* Full working loop of rotate and translate pipe mutation, including edits, only on module imports. VERY VERBOSE

* Add first e2e test for set transform. Bunch of caveats listed as TODOs

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

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

* Update to scene.settled(cmdBar)

* Add partNNN default name for alias

* Lint

* Lint

* Fix unit tests

* Add sad path insert test
Thanks @Irev-Dev for the suggestion

* Add step insert test

* Lint

* Add test for second foreign import thru file tree click

* WIP: Add point-and-click Load to copy files from outside the project into the project
Towards #6210

* Move Insert button to modeling toolbar, update menus and toolbars

* Add default value for local name alias

* Aligning tests

* Fix tests

* Add padding for filenames starting with a digit

* Lint

* Lint

* Update snapshots

* Merge branch 'main' into pierremtb/issue6210-Add-point-and-click-Load-to-copy-files-from-outside-the-project-into-the-project

* Add disabled transform subbutton

* Allow start of Transform flow from toolbar with selection

* Merge kcl-samples and local disk load into one 'Load external model' command

* Fix em tests

* Fix test

* Add test for file pick import, better input

* Fix non .kcl loading

* Lint

* Update snapshots

* Fix issue leading to test failure

* Fix clone test

* Add note

* Fix nested clone issue

* Clean up for review

* Add valueSummary for path

* Fix test after path change

* Clean up for review

* Support much wider range for transform

* Set display names

* Bug fixed itself moment...

* Add test for extrude tranform

* Oops missed a thing

* Clean up selection arg

* More tests incl for variable stuff

* Fix imports

* Add supportsTransform: true on all solids returning operations

* Fix edit flow on variables, add test

* Split transform command into translate and rotate

* Clean up and comment

* Clean up operations.ts

* Add comment

* Improve assemblies test

* Support more things

* Typo

* Fix test after unit change on import

* Last clean up for review

* Fix remaining test

---------

Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>
2025-04-17 15:44:31 +00:00
056a4d4a22 [FIX]: "Connecting to engine" overlay shows too often (#6350)
* Use the engineStreamState that's available right there silly

* fix lint

* Just look at playing/paused

---------

Co-authored-by: Jace Browning <jacebrowning@gmail.com>
2025-04-17 12:49:31 +00:00
bab79331cb #6367 Tangent snapping improvements (#6369)
* fix snapping line being culled

* make snap line more grayed out

* make snapping tolerance smaller
2025-04-17 08:26:05 -04:00
16bbc970ae fix sketch flash part 2 (#6366) 2025-04-17 08:15:54 -04:00
0f1cff316c add toast for selections we don't recognise (#6370)
* ad toast for selections we don't recognise

* remove log
2025-04-17 21:32:13 +10:00
938a2bae13 Fix to not duplicate arg labels on constrained sketches (#6337) 2025-04-17 07:16:55 +00:00
3324835b72 ci: Fix so that just commands regenerate ast output (#6338)
* Fix so that just commands regenerate ast output

* Update just command to include manifest

* Fix overwrite-sim-test kcl_samples to generate manifest

This is the command used in CI.
2025-04-17 06:03:58 +00:00
1628ea86b2 Remove pixel color checks (#6365) 2025-04-17 14:25:45 +10:00
be119248a6 put execution benchmarks behind flag (#6364)
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2025-04-16 21:23:30 -07:00
ac75181f7f fix bug and remove flash in sketch mode (#6346)
* fix bug and remove flash

* add test

* fix tests

* fix tests
2025-04-17 10:10:27 +10:00
713 changed files with 5776154 additions and 37674 deletions

View File

@ -1,3 +1,3 @@
[codespell]
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,.package-lock.json,**/package-lock.json,./openapi/*.json,./packages/codemirror-lang-kcl/test/all.test.ts,./public/kcl-samples,./rust/kcl-lib/tests/kcl_samples,tsconfig.tsbuildinfo,./src/lib/machine-api.d.ts
skip: **/target,node_modules,build,dist,./out,**/Cargo.lock,./docs/kcl/*.md,./e2e/playwright/lib/console-error-whitelist.ts,.package-lock.json,**/package-lock.json,./openapi/*.json,./packages/codemirror-lang-kcl/test/all.test.ts,./public/kcl-samples,./rust/kcl-lib/tests/kcl_samples,tsconfig.tsbuildinfo,./src/lib/machine-api.d.ts

View File

@ -19,6 +19,11 @@
"plugin:jsx-a11y/recommended",
"plugin:react-hooks/recommended"
],
"settings": {
"react": {
"version": "detect"
}
},
"rules": {
"no-array-constructor": "off", // This is wrong; use the @typescript-eslint one instead.
"@typescript-eslint/no-array-constructor": "error",
@ -61,6 +66,7 @@
"jsx-a11y/click-events-have-key-events": "off",
"jsx-a11y/no-autofocus": "off",
"jsx-a11y/no-noninteractive-element-interactions": "off",
"react/no-unknown-property": "error",
"no-restricted-globals": [
"error",
{

View File

@ -26,8 +26,8 @@ max_retries=1
# Retry failed tests, doing our own retries because using inbuilt Playwright retries causes connection issues
while [[ $retry -le $max_retries ]]; do
if [[ -f "test-results/.last-run.json" ]]; then
failed_tests=$(jq '.failedTests | length' test-results/.last-run.json)
if [[ $failed_tests -gt 0 ]]; then
status=$(jq -r '.status' test-results/.last-run.json)
if [[ "$status" == "failed" ]]; then
echo "retried=true" >>$GITHUB_OUTPUT
echo "run playwright with last failed tests and retry $retry"
if [[ "$3" == *ubuntu* ]]; then
@ -56,10 +56,11 @@ done
echo "retried=false" >>$GITHUB_OUTPUT
if [[ -f "test-results/.last-run.json" ]]; then
failed_tests=$(jq '.failedTests | length' test-results/.last-run.json)
if [[ $failed_tests -gt 0 ]]; then
# If it still fails after 3 retries, then fail the job
status=$(jq -r '.status' test-results/.last-run.json)
if [[ "$status" == "failed" ]]; then
# If it still fails after retries, then fail the job
exit 1
fi
fi
exit 0

View File

@ -50,13 +50,12 @@ jobs:
- name: Build the benchmark target(s)
run: |
cd rust
cargo codspeed build --measurement-mode walltime
cargo codspeed build
- name: Run the benchmarks
uses: CodSpeedHQ/action@v3
with:
working-directory: rust
run: cargo codspeed run
token: ${{ secrets.CODSPEED_TOKEN }}
mode: walltime
env:
KITTYCAD_API_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN }}

View File

@ -226,8 +226,8 @@ jobs:
with:
shell: bash
command: npm run test:snapshots
timeout_minutes: 30
max_attempts: 3
timeout_minutes: 5
max_attempts: 5
env:
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
snapshottoken: ${{ secrets.KITTYCAD_API_TOKEN }}
@ -285,7 +285,7 @@ jobs:
# TODO: enable namespace-profile-windows-latest once available
os:
- "runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64"
- namespace-profile-macos-6-cores
- namespace-profile-macos-8-cores
- windows-latest-8-cores
shardIndex: [1, 2, 3, 4]
shardTotal: [4]
@ -295,7 +295,7 @@ jobs:
isScheduled:
- ${{ github.event_name == 'schedule' }}
exclude:
- os: namespace-profile-macos-6-cores
- os: namespace-profile-macos-8-cores
isScheduled: true
- os: windows-latest-8-cores
isScheduled: true

View File

@ -5,6 +5,6 @@ command = "vscode-eslint-language-server"
[[language]]
name = "typescript"
auto-format = true
formatter = { command = "node_modules/.bin/prettier", args = ["--parser", "typescript"] }
language-servers = [ { name = "eslint", only-features = [ "diagnostics" ] }, "typescript-language-server" ]
formatter = { command = "./node_modules/@biomejs/biome/bin/biome", args = ["format", "--write", "--stdin-file-path=foo.ts"] }
# language-servers = [ { name = "eslint", only-features = [ "diagnostics" ] }, "typescript-language-server" ]

View File

@ -15,8 +15,8 @@ ifdef WINDOWS
CARGO ?= $(USERPROFILE)/.cargo/bin/cargo.exe
WASM_PACK ?= $(USERPROFILE)/.cargo/bin/wasm-pack.exe
else
CARGO ?= ~/.cargo/bin/cargo
WASM_PACK ?= ~/.cargo/bin/wasm-pack
CARGO ?= $(shell which cargo || echo ~/.cargo/bin/cargo)
WASM_PACK ?= $(shell which wasm-pack || echo ~/.cargo/bin/wasm-pack)
endif
.PHONY: install

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

40
docs/kcl/assertIs.md Normal file

File diff suppressed because one or more lines are too long

View File

@ -10,8 +10,10 @@ Draw a smooth, continuous, curved line segment from the current origin to the de
```js
bezierCurve(
data: BezierData,
sketch: Sketch,
control1: [number],
control2: [number],
end: [number],
tag?: TagDeclarator,
): Sketch
```
@ -21,9 +23,11 @@ bezierCurve(
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `data` | [`BezierData`](/docs/kcl/types/BezierData) | Data to draw a bezier curve. | Yes |
| `sketch` | [`Sketch`](/docs/kcl/types/Sketch) | | Yes |
| [`tag`](/docs/kcl/types/tag) | [`TagDeclarator`](/docs/kcl/types#tag-declaration) | | No |
| `sketch` | [`Sketch`](/docs/kcl/types/Sketch) | Which sketch should this path be added to? | Yes |
| `control1` | [`[number]`](/docs/kcl/types/number) | First control point for the cubic | Yes |
| `control2` | [`[number]`](/docs/kcl/types/number) | Second control point for the cubic | Yes |
| `end` | [`[number]`](/docs/kcl/types/number) | How far away (along the X and Y axes) should this line go? | Yes |
| [`tag`](/docs/kcl/types/tag) | [`TagDeclarator`](/docs/kcl/types#tag-declaration) | Create a new tag which refers to this line | No |
### Returns
@ -36,11 +40,7 @@ bezierCurve(
exampleSketch = startSketchOn(XZ)
|> startProfileAt([0, 0], %)
|> line(end = [0, 10])
|> bezierCurve({
to = [10, 10],
control1 = [5, 0],
control2 = [5, 10]
}, %)
|> bezierCurve(control1 = [5, 0], control2 = [5, 10], end = [10, 10])
|> line(endAbsolute = [10, 0])
|> close()

View File

@ -42,18 +42,10 @@ extrude(
example = startSketchOn(XZ)
|> startProfileAt([0, 0], %)
|> line(end = [10, 0])
|> arc({
angleStart = 120,
angleEnd = 0,
radius = 5
}, %)
|> arc(angleStart = 120, angleEnd = 0, radius = 5)
|> line(end = [5, 0])
|> line(end = [0, 10])
|> bezierCurve({
control1 = [-10, 0],
control2 = [2, 10],
to = [-5, 10]
}, %)
|> bezierCurve(control1 = [-10, 0], control2 = [2, 10], end = [-5, 10])
|> line(end = [-5, -2])
|> close()
|> extrude(length = 10)
@ -64,18 +56,10 @@ example = startSketchOn(XZ)
```js
exampleSketch = startSketchOn(XZ)
|> startProfileAt([-10, 0], %)
|> arc({
angleStart = 120,
angleEnd = -60,
radius = 5
}, %)
|> arc(angleStart = 120, angleEnd = -60, radius = 5)
|> line(end = [10, 0])
|> line(end = [5, 0])
|> bezierCurve({
control1 = [-3, 0],
control2 = [2, 10],
to = [-5, 10]
}, %)
|> bezierCurve(control1 = [-3, 0], control2 = [2, 10], end = [-5, 10])
|> line(end = [-4, 10])
|> line(end = [-5, -2])
|> close()
@ -88,18 +72,10 @@ example = extrude(exampleSketch, length = 10)
```js
exampleSketch = startSketchOn(XZ)
|> startProfileAt([-10, 0], %)
|> arc({
angleStart = 120,
angleEnd = -60,
radius = 5
}, %)
|> arc(angleStart = 120, angleEnd = -60, radius = 5)
|> line(end = [10, 0])
|> line(end = [5, 0])
|> bezierCurve({
control1 = [-3, 0],
control2 = [2, 10],
to = [-5, 10]
}, %)
|> bezierCurve(control1 = [-3, 0], control2 = [2, 10], end = [-5, 10])
|> line(end = [-4, 10])
|> line(end = [-5, -2])
|> close()
@ -112,18 +88,10 @@ example = extrude(exampleSketch, length = 20, symmetric = true)
```js
exampleSketch = startSketchOn(XZ)
|> startProfileAt([-10, 0], %)
|> arc({
angleStart = 120,
angleEnd = -60,
radius = 5
}, %)
|> arc(angleStart = 120, angleEnd = -60, radius = 5)
|> line(end = [10, 0])
|> line(end = [5, 0])
|> bezierCurve({
control1 = [-3, 0],
control2 = [2, 10],
to = [-5, 10]
}, %)
|> bezierCurve(control1 = [-3, 0], control2 = [2, 10], end = [-5, 10])
|> line(end = [-4, 10])
|> line(end = [-5, -2])
|> close()

View File

@ -4,8 +4,12 @@ excerpt: "Converts a number from centimeters to the current default unit."
layout: manual
---
**WARNING:** This function is deprecated.
Converts a number from centimeters to the current default unit.
*DEPRECATED* prefer using explicit numeric suffixes (e.g., `42cm`) or the `to...` conversion functions.
No matter what units the current file uses, this function will always return a number equivalent to the input in centimeters.
For example, if the current file uses inches, `fromCm(1)` will return `0.393701`. If the current file uses millimeters, `fromCm(1)` will return `10`. If the current file uses centimeters, `fromCm(1)` will return `1`.

View File

@ -4,8 +4,12 @@ excerpt: "Converts a number from feet to the current default unit."
layout: manual
---
**WARNING:** This function is deprecated.
Converts a number from feet to the current default unit.
*DEPRECATED* prefer using explicit numeric suffixes (e.g., `42ft`) or the `to...` conversion functions.
No matter what units the current file uses, this function will always return a number equivalent to the input in feet.
For example, if the current file uses inches, `fromFt(1)` will return `12`. If the current file uses millimeters, `fromFt(1)` will return `304.8`. If the current file uses feet, `fromFt(1)` will return `1`.

View File

@ -4,8 +4,12 @@ excerpt: "Converts a number from inches to the current default unit."
layout: manual
---
**WARNING:** This function is deprecated.
Converts a number from inches to the current default unit.
*DEPRECATED* prefer using explicit numeric suffixes (e.g., `42inch`) or the `to...` conversion functions.
No matter what units the current file uses, this function will always return a number equivalent to the input in inches.
For example, if the current file uses inches, `fromInches(1)` will return `1`. If the current file uses millimeters, `fromInches(1)` will return `25.4`.

View File

@ -4,8 +4,12 @@ excerpt: "Converts a number from meters to the current default unit."
layout: manual
---
**WARNING:** This function is deprecated.
Converts a number from meters to the current default unit.
*DEPRECATED* prefer using explicit numeric suffixes (e.g., `42m`) or the `to...` conversion functions.
No matter what units the current file uses, this function will always return a number equivalent to the input in meters.
For example, if the current file uses inches, `fromM(1)` will return `39.3701`. If the current file uses millimeters, `fromM(1)` will return `1000`. If the current file uses meters, `fromM(1)` will return `1`.

View File

@ -4,8 +4,12 @@ excerpt: "Converts a number from mm to the current default unit."
layout: manual
---
**WARNING:** This function is deprecated.
Converts a number from mm to the current default unit.
*DEPRECATED* prefer using explicit numeric suffixes (e.g., `42mm`) or the `to...` conversion functions.
No matter what units the current file uses, this function will always return a number equivalent to the input in millimeters.
For example, if the current file uses inches, `fromMm(1)` will return `1/25.4`. If the current file uses millimeters, `fromMm(1)` will return `1`.

View File

@ -4,8 +4,12 @@ excerpt: "Converts a number from yards to the current default unit."
layout: manual
---
**WARNING:** This function is deprecated.
Converts a number from yards to the current default unit.
*DEPRECATED* prefer using explicit numeric suffixes (e.g., `42yd`) or the `to...` conversion functions.
No matter what units the current file uses, this function will always return a number equivalent to the input in yards.
For example, if the current file uses inches, `fromYd(1)` will return `36`. If the current file uses millimeters, `fromYd(1)` will return `914.4`. If the current file uses yards, `fromYd(1)` will return `1`.

File diff suppressed because one or more lines are too long

View File

@ -22,18 +22,8 @@ layout: manual
* [`string`](kcl/types/string)
* [`tag`](kcl/types/tag)
* **std**
* [`Axis2d`](kcl/types/Axis2d)
* [`Axis3d`](kcl/types/Axis3d)
* [`END`](kcl/consts/std-END)
* [`Edge`](kcl/types/Edge)
* [`Face`](kcl/types/Face)
* [`Helix`](kcl/types/Helix)
* [`Plane`](kcl/types/Plane)
* [`Point2d`](kcl/types/Point2d)
* [`Point3d`](kcl/types/Point3d)
* [`START`](kcl/consts/std-START)
* [`Sketch`](kcl/types/Sketch)
* [`Solid`](kcl/types/Solid)
* [`X`](kcl/consts/std-X)
* [`XY`](kcl/consts/std-XY)
* [`XZ`](kcl/consts/std-XZ)
@ -48,14 +38,9 @@ layout: manual
* [`angledLineThatIntersects`](kcl/angledLineThatIntersects)
* [`appearance`](kcl/appearance)
* [`arc`](kcl/arc)
* [`arcTo`](kcl/arcTo)
* [`asin`](kcl/asin)
* [`assert`](kcl/assert)
* [`assertEqual`](kcl/assertEqual)
* [`assertGreaterThan`](kcl/assertGreaterThan)
* [`assertGreaterThanOrEq`](kcl/assertGreaterThanOrEq)
* [`assertLessThan`](kcl/assertLessThan)
* [`assertLessThanOrEq`](kcl/assertLessThanOrEq)
* [`assertIs`](kcl/assertIs)
* [`atan`](kcl/atan)
* [`atan2`](kcl/atan2)
* [`bezierCurve`](kcl/bezierCurve)
@ -66,12 +51,6 @@ layout: manual
* [`extrude`](kcl/extrude)
* [`fillet`](kcl/fillet)
* [`floor`](kcl/floor)
* [`fromCm`](kcl/fromCm)
* [`fromFt`](kcl/fromFt)
* [`fromInches`](kcl/fromInches)
* [`fromM`](kcl/fromM)
* [`fromMm`](kcl/fromMm)
* [`fromYd`](kcl/fromYd)
* [`getCommonEdge`](kcl/getCommonEdge)
* [`getNextAdjacentEdge`](kcl/getNextAdjacentEdge)
* [`getOppositeEdge`](kcl/getOppositeEdge)
@ -131,8 +110,14 @@ layout: manual
* [`sweep`](kcl/sweep)
* [`tangentToEnd`](kcl/tangentToEnd)
* [`tangentialArc`](kcl/tangentialArc)
* [`toDegrees`](kcl/toDegrees)
* [`toRadians`](kcl/toRadians)
* [`toCentimeters`](kcl/std-toCentimeters)
* [`toDegrees`](kcl/std-toDegrees)
* [`toFeet`](kcl/std-toFeet)
* [`toInches`](kcl/std-toInches)
* [`toMeters`](kcl/std-toMeters)
* [`toMillimeters`](kcl/std-toMillimeters)
* [`toRadians`](kcl/std-toRadians)
* [`toYards`](kcl/std-toYards)
* [`translate`](kcl/translate)
* [`union`](kcl/union)
* [`xLine`](kcl/xLine)
@ -153,3 +138,14 @@ layout: manual
* [`turns::QUARTER_TURN`](kcl/consts/std-turns-QUARTER_TURN)
* [`turns::THREE_QUARTER_TURN`](kcl/consts/std-turns-THREE_QUARTER_TURN)
* [`turns::ZERO`](kcl/consts/std-turns-ZERO)
* **std::types**
* [`Axis2d`](kcl/types/Axis2d)
* [`Axis3d`](kcl/types/Axis3d)
* [`Edge`](kcl/types/Edge)
* [`Face`](kcl/types/Face)
* [`Helix`](kcl/types/Helix)
* [`Plane`](kcl/types/Plane)
* [`Point2d`](kcl/types/Point2d)
* [`Point3d`](kcl/types/Point3d)
* [`Sketch`](kcl/types/Sketch)
* [`Solid`](kcl/types/Solid)

View File

@ -34,7 +34,7 @@ int(num: number): number
```js
n = int(ceil(5 / 2))
assertEqual(n, 3, 0.0001, "5/2 = 2.5, rounded up makes 3")
assert(n, isEqualTo = 3, error = "5/2 = 2.5, rounded up makes 3")
// Draw n cylinders.
startSketchOn(XZ)
|> circle(center = [0, 0], radius = 2)

View File

@ -21,4 +21,7 @@ once fixed in engine will just start working here with no language changes.
- **Chamfers**: Chamfers cannot intersect, you will get an error. Only simple
chamfer cases work currently.
- **Appearance**: Changing the appearance on a loft does not work.
- **Appearance**: Changing the appearance on a loft does not work.
Changing the appearance on an imported model does not work.
- **CSG Booleans**: Coplanar (bodies that share a plane) unions, subtractions, and intersections are not currently supported.

View File

@ -109,3 +109,98 @@ Coordinate systems:
- `zoo` (the default), forward: -Y, up: +Z, handedness: right
- `opengl`, forward: +Z, up: +Y, handedness: right
- `vulkan`, forward: +Z, up: -Y, handedness: left
### Performance
Parallelized foreign-file imports now let you overlap file reads, initialization,
and rendering. To maximize throughput, you need to understand the three distinct
stages—reading, initializing (background render start), and invocation (blocking)
—and structure your code to defer blocking operations until the end.
#### Foreign Import Execution Stages
1. **Import (Read) Stage**
```norun
import "tests/inputs/cube.step" as cube
```
- Reads the file from disk and makes its API available.
- **Does _not_** start Engine rendering or block your script.
2. **Initialization (Background Render) Stage**
```norun
import "tests/inputs/cube.step" as cube
myCube = cube // <- This line starts background rendering
```
- Invoking the imported symbol (assignment or plain call) triggers Engine rendering _in the background_.
- This kickstarts the render pipeline but doesnt block—you can continue other work while the Engine processes the model.
3. **Invocation (Blocking) Stage**
```norun
import "tests/inputs/cube.step" as cube
myCube = cube
myCube
|> translate(z=10) // <- This line blocks
```
- Any method call (e.g., `translate`, `scale`, `rotate`) waits for the background render to finish before applying transformations.
- This is the only point where your script will block.
> **Nuance:** Foreign imports differ from pure KCL modules—calling the same import symbol multiple times (e.g., `screw` twice) starts background rendering twice.
#### Best Practices
##### 1. Defer Blocking Calls
Initialize early but delay all transformations until after your heavy computation:
```norun
import "tests/inputs/cube.step" as cube // 1) Read
myCube = cube // 2) Background render starts
// --- perform other operations and calculations or setup here ---
myCube
|> translate(z=10) // 3) Blocks only here
```
##### 2. Encapsulate Imports in Modules
Keep `main.kcl` free of reads and initialization; wrap them:
```norun
// imports.kcl
import "tests/inputs/cube.step" as cube // Read only
export myCube = cube // Kick off rendering
```
```norun
// main.kcl
import myCube from "imports.kcl" // Import the initialized object
// ... computations ...
myCube
|> translate(z=10) // Blocking call at the end
```
##### 3. Avoid Immediate Method Calls
```norun
import "tests/inputs/cube.step" as cube
cube
|> translate(z=10) // Blocks immediately, negating parallelism
```
Both calling methods right on `cube` immediately or leaving an implicit import without assignment introduce blocking.
#### Future Improvements
Upcoming releases will autoanalyze dependencies and only block when truly necessary. Until then, explicit deferral and modular wrapping give you the best performance.

View File

@ -174,7 +174,7 @@ t = 0.005 // taper factor [0-1)
// Defines how to modify each layer of the vase.
// Each replica is shifted up the Z axis, and has a smoothly-varying radius
fn transform(replicaId) {
scale = r * abs(1 - (t * replicaId)) * (5 + cos(replicaId / 8))
scale = r * abs(1 - (t * replicaId)) * (5 + cos(replicaId / 8: number(rad)))
return {
translate = [0, 0, replicaId * 10],
scale = [scale, scale, 0]
@ -205,12 +205,12 @@ fn transform(i) {
}
startSketchOn(XY)
|> startProfileAt([0, 0], %)
|> polygon({
|> polygon(
radius = 10,
numSides = 4,
center = [0, 0],
inscribed = false
}, %)
inscribed = false,
)
|> extrude(length = 4)
|> patternTransform(instances = 3, transform = transform)
```

View File

@ -10,9 +10,11 @@ Create a regular polygon with the specified number of sides that is either inscr
```js
polygon(
data: PolygonData,
sketchSurfaceOrGroup: SketchOrSurface,
tag?: TagDeclarator,
radius: number,
numSides: u64,
center: [number],
inscribed?: bool,
): Sketch
```
@ -21,9 +23,11 @@ polygon(
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `data` | [`PolygonData`](/docs/kcl/types/PolygonData) | Data for drawing a polygon | Yes |
| `sketchSurfaceOrGroup` | [`SketchOrSurface`](/docs/kcl/types/SketchOrSurface) | A sketch surface or a sketch. | Yes |
| [`tag`](/docs/kcl/types/tag) | [`TagDeclarator`](/docs/kcl/types#tag-declaration) | | No |
| `sketchSurfaceOrGroup` | [`SketchOrSurface`](/docs/kcl/types/SketchOrSurface) | Plane or surface to sketch on | Yes |
| `radius` | [`number`](/docs/kcl/types/number) | The radius of the polygon | Yes |
| `numSides` | `u64` | The number of sides in the polygon | Yes |
| `center` | [`[number]`](/docs/kcl/types/number) | The center point of the polygon | Yes |
| `inscribed` | [`bool`](/docs/kcl/types/bool) | Whether the polygon is inscribed (true, the default) or circumscribed (false) about a circle with the specified radius | No |
### Returns
@ -35,12 +39,12 @@ polygon(
```js
// Create a regular hexagon inscribed in a circle of radius 10
hex = startSketchOn(XY)
|> polygon({
|> polygon(
radius = 10,
numSides = 6,
center = [0, 0],
inscribed = true
}, %)
inscribed = true,
)
example = extrude(hex, length = 5)
```
@ -50,12 +54,12 @@ example = extrude(hex, length = 5)
```js
// Create a square circumscribed around a circle of radius 5
square = startSketchOn(XY)
|> polygon({
|> polygon(
radius = 5.0,
numSides = 4,
center = [10, 10],
inscribed = false
}, %)
inscribed = false,
)
example = extrude(square, length = 5)
```

File diff suppressed because one or more lines are too long

View File

@ -9,7 +9,7 @@ Extract the provided 2-dimensional sketch's profile's origin value.
```js
profileStart(sketch: Sketch): [number]
profileStart(profile: Sketch): [number]
```
@ -17,7 +17,7 @@ profileStart(sketch: Sketch): [number]
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `sketch` | [`Sketch`](/docs/kcl/types/Sketch) | | Yes |
| `profile` | [`Sketch`](/docs/kcl/types/Sketch) | Profile whose start is being used | Yes |
### Returns

View File

@ -9,7 +9,7 @@ Extract the provided 2-dimensional sketch's profile's origin's 'x' value.
```js
profileStartX(sketch: Sketch): number
profileStartX(profile: Sketch): number
```
@ -17,7 +17,7 @@ profileStartX(sketch: Sketch): number
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `sketch` | [`Sketch`](/docs/kcl/types/Sketch) | | Yes |
| `profile` | [`Sketch`](/docs/kcl/types/Sketch) | Profile whose start is being used | Yes |
### Returns

View File

@ -9,7 +9,7 @@ Extract the provided 2-dimensional sketch's profile's origin's 'y' value.
```js
profileStartY(sketch: Sketch): number
profileStartY(profile: Sketch): number
```
@ -17,7 +17,7 @@ profileStartY(sketch: Sketch): number
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `sketch` | [`Sketch`](/docs/kcl/types/Sketch) | | Yes |
| `profile` | [`Sketch`](/docs/kcl/types/Sketch) | Profile whose start is being used | Yes |
### Returns

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

@ -53,7 +53,7 @@ rotate(
### Returns
[`SolidOrSketchOrImportedGeometry`](/docs/kcl/types/SolidOrSketchOrImportedGeometry) - Data for a solid or an imported geometry.
[`SolidOrSketchOrImportedGeometry`](/docs/kcl/types/SolidOrSketchOrImportedGeometry) - Data for a solid, sketch, or an imported geometry.
### Examples

View File

@ -37,7 +37,7 @@ scale(
### Returns
[`SolidOrSketchOrImportedGeometry`](/docs/kcl/types/SolidOrSketchOrImportedGeometry) - Data for a solid or an imported geometry.
[`SolidOrSketchOrImportedGeometry`](/docs/kcl/types/SolidOrSketchOrImportedGeometry) - Data for a solid, sketch, or an imported geometry.
### Examples

View File

@ -26,7 +26,7 @@ helix(
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `revolutions` | `number(_)` | Number of revolutions. | Yes |
| `angleStart` | `number(Angle)` | Start angle (in degrees). | Yes |
| `angleStart` | `number(Angle)` | Start angle. | Yes |
| `ccw` | [`bool`](/docs/kcl/types/bool) | Is the helix rotation counter clockwise? The default is `false`. | No |
| `radius` | `number(Length)` | Radius of the helix. | No |
| `axis` | `Axis3d | Edge` | Axis to use for the helix. | No |

View File

@ -31,7 +31,7 @@ exampleSketch = startSketchOn(XZ)
|> startProfileAt([0, 0], %)
|> angledLine(
angle = 30,
length = 3 / cos(toRadians(30)),
length = 3 / cos(30deg),
)
|> yLine(endAbsolute = 0)
|> close()

View File

@ -11,7 +11,7 @@ cartesian (x/y/z grid) coordinates.
```js
polar(
angle: number(Angle),
angle: number(rad),
length: number(Length),
): Point2d
```
@ -21,7 +21,7 @@ polar(
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `angle` | `number(Angle)` | | Yes |
| `angle` | `number(rad)` | | Yes |
| `length` | `number(Length)` | | Yes |
### Returns

View File

@ -31,7 +31,7 @@ exampleSketch = startSketchOn(XZ)
|> startProfileAt([0, 0], %)
|> angledLine(
angle = 50,
length = 15 / sin(toRadians(135)),
length = 15 / sin(135deg),
)
|> yLine(endAbsolute = 0)
|> close()

View File

@ -31,7 +31,7 @@ exampleSketch = startSketchOn(XZ)
|> startProfileAt([0, 0], %)
|> angledLine(
angle = 50,
length = 50 * tan(1/2),
length = 50 * tan((1/2): number(rad)),
)
|> yLine(endAbsolute = 0)
|> close()

View File

@ -0,0 +1,27 @@
---
title: "std::toCentimeters"
excerpt: "Convert a number to centimeters from its current units."
layout: manual
---
Convert a number to centimeters from its current units.
```js
toCentimeters(@num: number(cm)): number(cm)
```
### Arguments
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `num` | `number(cm)` | | Yes |
### Returns
`number(cm)`

44
docs/kcl/std-toDegrees.md Normal file

File diff suppressed because one or more lines are too long

27
docs/kcl/std-toFeet.md Normal file
View File

@ -0,0 +1,27 @@
---
title: "std::toFeet"
excerpt: "Convert a number to feet from its current units."
layout: manual
---
Convert a number to feet from its current units.
```js
toFeet(@num: number(ft)): number(ft)
```
### Arguments
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `num` | `number(ft)` | | Yes |
### Returns
`number(ft)`

27
docs/kcl/std-toInches.md Normal file
View File

@ -0,0 +1,27 @@
---
title: "std::toInches"
excerpt: "Convert a number to inches from its current units."
layout: manual
---
Convert a number to inches from its current units.
```js
toInches(@num: number(in)): number(in)
```
### Arguments
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `num` | `number(in)` | | Yes |
### Returns
`number(in)`

27
docs/kcl/std-toMeters.md Normal file
View File

@ -0,0 +1,27 @@
---
title: "std::toMeters"
excerpt: "Convert a number to meters from its current units."
layout: manual
---
Convert a number to meters from its current units.
```js
toMeters(@num: number(m)): number(m)
```
### Arguments
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `num` | `number(m)` | | Yes |
### Returns
`number(m)`

View File

@ -0,0 +1,27 @@
---
title: "std::toMillimeters"
excerpt: "Convert a number to millimeters from its current units."
layout: manual
---
Convert a number to millimeters from its current units.
```js
toMillimeters(@num: number(mm)): number(mm)
```
### Arguments
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `num` | `number(mm)` | | Yes |
### Returns
`number(mm)`

44
docs/kcl/std-toRadians.md Normal file

File diff suppressed because one or more lines are too long

27
docs/kcl/std-toYards.md Normal file
View File

@ -0,0 +1,27 @@
---
title: "std::toYards"
excerpt: "Converts a number to yards from its current units."
layout: manual
---
Converts a number to yards from its current units.
```js
toYards(@num: number(yd)): number(yd)
```
### Arguments
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `num` | `number(yd)` | | Yes |
### Returns
`number(yd)`

File diff suppressed because it is too large Load Diff

View File

@ -73,7 +73,7 @@ rectangleExtrude = extrude(rectangleSketch, length = 10)
```js
bottom = startSketchOn(XY)
|> startProfileAt([0, 0], %)
|> arcTo({ end = [10, 10], interior = [5, 1] }, %, $arc1)
|> arc(endAbsolute = [10, 10], interiorAbsolute = [5, 1], tag = $arc1)
|> angledLine(angle = tangentToEnd(arc1), length = 20)
|> 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

@ -33,7 +33,7 @@ translate(
### Returns
[`SolidOrSketchOrImportedGeometry`](/docs/kcl/types/SolidOrSketchOrImportedGeometry) - Data for a solid or an imported geometry.
[`SolidOrSketchOrImportedGeometry`](/docs/kcl/types/SolidOrSketchOrImportedGeometry) - Data for a solid, sketch, or an imported geometry.
### Examples

View File

@ -1,5 +1,5 @@
---
title: "std::Axis2d"
title: "std::types::Axis2d"
excerpt: "An infinite line in 2d space."
layout: manual
---

View File

@ -1,5 +1,5 @@
---
title: "std::Axis3d"
title: "std::types::Axis3d"
excerpt: "An infinite line in 3d space."
layout: manual
---

View File

@ -1,5 +1,5 @@
---
title: "std::Edge"
title: "std::types::Edge"
excerpt: "The edge of a solid."
layout: manual
---

View File

@ -1,5 +1,5 @@
---
title: "std::Face"
title: "std::types::Face"
excerpt: "A face."
layout: manual
---

View File

@ -1,5 +1,5 @@
---
title: "std::Helix"
title: "std::types::Helix"
excerpt: "A helix."
layout: manual
---

View File

@ -141,8 +141,8 @@ A base path.
|----------|------|-------------|----------|
| `type` |enum: `ArcThreePoint`| | No |
| `p1` |`[number, number]`| Point 1 of the arc (base on the end of previous segment) | No |
| `p2` |`[number, number]`| Point 2 of the arc (interior kwarg) | No |
| `p3` |`[number, number]`| Point 3 of the arc (end kwarg) | No |
| `p2` |`[number, number]`| Point 2 of the arc (interiorAbsolute kwarg) | No |
| `p3` |`[number, number]`| Point 3 of the arc (endAbsolute kwarg) | No |
| `from` |`[number, number]`| The from point. | No |
| `to` |`[number, number]`| The to point. | No |
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A unit of length. | No |

View File

@ -1,5 +1,5 @@
---
title: "std::Plane"
title: "std::types::Plane"
excerpt: "A plane."
layout: manual
---

View File

@ -1,5 +1,5 @@
---
title: "std::Point2d"
title: "std::types::Point2d"
excerpt: "A point in two dimensional space."
layout: manual
---

View File

@ -1,5 +1,5 @@
---
title: "std::Point3d"
title: "std::types::Point3d"
excerpt: "A point in three dimensional space."
layout: manual
---

File diff suppressed because one or more lines are too long

View File

@ -31,7 +31,6 @@ A sketch type.
| `xAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the plane's X axis be? | No |
| `yAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the plane's Y axis be? | No |
| `zAxis` |[`Point3d`](/docs/kcl/types/Point3d)| The z-axis (normal). | No |
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A unit of length. | No |
----

File diff suppressed because one or more lines are too long

View File

@ -1,10 +1,10 @@
---
title: "SolidOrSketchOrImportedGeometry"
excerpt: "Data for a solid or an imported geometry."
excerpt: "Data for a solid, sketch, or an imported geometry."
layout: manual
---
Data for a solid or an imported geometry.
Data for a solid, sketch, or an imported geometry.

View File

@ -12,7 +12,7 @@ The syntax for declaring a tag is `$myTag` you would use it in the following
way:
```js
startSketchOn('XZ')
startSketchOn(XZ)
|> startProfileAt(origin, %)
|> angledLine(angle = 0, length = 191.26, tag = $rectangleSegmentA001)
|> angledLine(
@ -46,7 +46,7 @@ However if the code was written like this:
```js
fn rect(origin) {
return startSketchOn('XZ')
return startSketchOn(XZ)
|> startProfileAt(origin, %)
|> angledLine(angle = 0, length = 191.26, tag = $rectangleSegmentA001)
|> angledLine(
@ -75,7 +75,7 @@ For example the following code works.
```js
fn rect(origin) {
return startSketchOn('XZ')
return startSketchOn(XZ)
|> startProfileAt(origin, %)
|> angledLine(angle = 0, length = 191.26, tag = $rectangleSegmentA001)
|> angledLine(

View File

@ -29,11 +29,11 @@ test.describe('Electron app header tests', () => {
test(
'User settings has correct shortcut',
{ tag: '@electron' },
async ({ page }, testInfo) => {
async ({ page, toolbar }, testInfo) => {
await page.setBodyDimensions({ width: 1200, height: 500 })
// Open the user sidebar menu.
await page.getByTestId('user-sidebar-toggle').click()
await toolbar.userSidebarButton.click()
// No space after "User settings" since it's textContent.
const text =

View File

@ -0,0 +1,53 @@
import { expect, test } from '@e2e/playwright/zoo-test'
// test file is for testing auth functionality
test.describe('Authentication tests', () => {
test(
`The user can sign out and back in`,
{ tag: ['@electron'] },
async ({ page, homePage, signInPage, toolbar, tronApp }) => {
if (!tronApp) {
fail()
}
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.projectSection.waitFor()
await test.step('Click on sign out and expect sign in page', async () => {
await toolbar.userSidebarButton.click()
await toolbar.signOutButton.click()
await expect(signInPage.signInButton).toBeVisible()
})
await test.step('Click on sign in and cancel, click again and expect different code', async () => {
await signInPage.signInButton.click()
await expect(signInPage.userCode).toBeVisible()
const firstUserCode = await signInPage.userCode.textContent()
await signInPage.cancelSignInButton.click()
await expect(signInPage.signInButton).toBeVisible()
await signInPage.signInButton.click()
await expect(signInPage.userCode).toBeVisible()
const secondUserCode = await signInPage.userCode.textContent()
expect(secondUserCode).not.toEqual(firstUserCode)
})
await test.step('Press back button and remain on home page', async () => {
await page.goBack()
await expect(homePage.projectSection).not.toBeVisible()
await expect(signInPage.signInButton).toBeVisible()
})
await test.step('Sign in, activate, and expect home page', async () => {
await signInPage.signInButton.click()
await expect(signInPage.userCode).toBeVisible()
const userCode = await signInPage.userCode.textContent()
expect(userCode).not.toBeNull()
await signInPage.verifyAndConfirmAuth(userCode!)
// Longer timeout than usual here for the wait on home page
await expect(homePage.projectSection).toBeVisible({ timeout: 10000 })
})
}
)
})

View File

@ -430,5 +430,17 @@ profile003 = startProfileAt([0, -4.93], sketch001)
await editor.expectEditor.not.toContain('sketch001 =')
await editor.expectEditor.not.toContain('profile002 = ')
})
await test.step(`Delete the remaining plane via feature tree`, async () => {
const operationButton = await toolbar.getFeatureTreeOperation(
'Offset Plane',
0
)
await operationButton.click({ button: 'left' })
await page.keyboard.press('Delete')
// Verify the plane code is gone, and https://github.com/KittyCAD/modeling-app/issues/5988 is fixed.
await editor.expectEditor.not.toContain('plane001 =')
})
})
})

View File

@ -178,6 +178,13 @@ export class CmdBarFixture {
return this.page.getByRole('option', options)
}
/**
* Clicks the Create new variable button for kcl input
*/
createNewVariable = async () => {
await this.page.getByRole('button', { name: 'Create new variable' }).click()
}
/**
* Captures a snapshot of the request sent to the text-to-cad API endpoint
* and saves it to a file named after the current test.

View File

@ -18,6 +18,7 @@ import type { Settings } from '@rust/kcl-lib/bindings/Settings'
import { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture'
import { EditorFixture } from '@e2e/playwright/fixtures/editorFixture'
import { HomePageFixture } from '@e2e/playwright/fixtures/homePageFixture'
import { SignInPageFixture } from '@e2e/playwright/fixtures/signInPageFixture'
import { SceneFixture } from '@e2e/playwright/fixtures/sceneFixture'
import { ToolbarFixture } from '@e2e/playwright/fixtures/toolbarFixture'
@ -66,6 +67,7 @@ export interface Fixtures {
toolbar: ToolbarFixture
scene: SceneFixture
homePage: HomePageFixture
signInPage: SignInPageFixture
}
export class ElectronZoo {
@ -387,6 +389,9 @@ const fixturesBasedOnProcessEnvPlatform = {
homePage: async ({ page }: { page: Page }, use: FnUse) => {
await use(new HomePageFixture(page))
},
signInPage: async ({ page }: { page: Page }, use: FnUse) => {
await use(new SignInPageFixture(page))
},
}
if (process.env.PLATFORM === 'web') {

View File

@ -257,6 +257,14 @@ export class SceneFixture {
await expectPixelColor(this.page, colour, coords, diff)
}
expectPixelColorNotToBe = async (
colour: [number, number, number] | [number, number, number][],
coords: { x: number; y: number },
diff: number
) => {
await expectPixelColorNotToBe(this.page, colour, coords, diff)
}
get gizmo() {
return this.page.locator('[aria-label*=gizmo]')
}
@ -278,37 +286,69 @@ function isColourArray(
return isArray(colour[0])
}
export async function expectPixelColor(
type PixelColorMatchMode = 'matches' | 'differs'
export async function checkPixelColor(
page: Page,
colour: [number, number, number] | [number, number, number][],
coords: { x: number; y: number },
diff: number
diff: number,
mode: PixelColorMatchMode
) {
let finalValue = colour
const isMatchMode = mode === 'matches'
const actionText = isMatchMode ? 'expecting' : 'not expecting'
const functionName = isMatchMode
? 'ExpectPixelColor'
: 'ExpectPixelColourNotToBe'
await expect
.poll(
async () => {
const pixel = (await getPixelRGBs(page)(coords, 1))[0]
if (!pixel) return null
finalValue = pixel
let matches
if (!isColourArray(colour)) {
return pixel.every(
matches = pixel.every(
(channel, index) => Math.abs(channel - colour[index]) < diff
)
} else {
matches = colour.some((c) =>
c.every((channel, index) => Math.abs(pixel[index] - channel) < diff)
)
}
return colour.some((c) =>
c.every((channel, index) => Math.abs(pixel[index] - channel) < diff)
)
return isMatchMode ? matches : !matches
},
{ timeout: 10_000 }
)
.toBeTruthy()
.catch((cause) => {
throw new Error(
`ExpectPixelColor: point ${JSON.stringify(
`${functionName}: point ${JSON.stringify(
coords
)} was expecting ${colour} but got ${finalValue}`,
)} was ${actionText} ${colour} but got ${finalValue}`,
{ cause }
)
})
}
export async function expectPixelColor(
page: Page,
colour: [number, number, number] | [number, number, number][],
coords: { x: number; y: number },
diff: number
) {
await checkPixelColor(page, colour, coords, diff, 'matches')
}
export async function expectPixelColorNotToBe(
page: Page,
colour: [number, number, number] | [number, number, number][],
coords: { x: number; y: number },
diff: number
) {
await checkPixelColor(page, colour, coords, diff, 'differs')
}

View File

@ -0,0 +1,48 @@
import type { Locator, Page } from '@playwright/test'
import { secrets } from '@e2e/playwright/secrets'
export class SignInPageFixture {
public page: Page
signInButton!: Locator
cancelSignInButton!: Locator
userCode!: Locator
apiBaseUrl!: string
constructor(page: Page) {
this.page = page
this.signInButton = this.page.getByTestId('sign-in-button')
this.cancelSignInButton = this.page.getByTestId('cancel-sign-in-button')
this.userCode = this.page.getByTestId('sign-in-user-code')
// TODO: set this thru env var
this.apiBaseUrl = 'https://api.dev.zoo.dev'
}
async verifyAndConfirmAuth(userCode: string) {
// Device flow: stolen from the tauri days
// https://github.com/KittyCAD/modeling-app/blob/d916c7987452e480719004e6d11fd2e595c7d0eb/e2e/tauri/specs/app.spec.ts#L19
const headers = {
Authorization: `Bearer ${secrets.token}`,
Accept: 'application/json',
'Content-Type': 'application/json',
}
const verifyUrl = `${this.apiBaseUrl}/oauth2/device/verify?user_code=${userCode}`
console.log(`GET ${verifyUrl}`)
const vr = await fetch(verifyUrl, { headers })
console.log(vr.status)
// Device flow: confirm
const confirmUrl = `${this.apiBaseUrl}/oauth2/device/confirm`
const data = JSON.stringify({ user_code: userCode })
console.log(`POST ${confirmUrl} ${data}`)
const cr = await fetch(confirmUrl, {
headers,
method: 'POST',
body: data,
})
console.log(cr.status)
}
}

View File

@ -46,6 +46,9 @@ export class ToolbarFixture {
gizmo!: Locator
gizmoDisabled!: Locator
loadButton!: Locator
/** User button for the user sidebar menu */
userSidebarButton!: Locator
signOutButton!: Locator
constructor(page: Page) {
this.page = page
@ -82,6 +85,9 @@ export class ToolbarFixture {
// element or two different elements can represent these states.
this.gizmo = page.getByTestId('gizmo')
this.gizmoDisabled = page.getByTestId('gizmo-disabled')
this.userSidebarButton = page.getByTestId('user-sidebar-toggle')
this.signOutButton = page.getByTestId('user-sidebar-sign-out')
}
get logoLink() {

View File

@ -1,6 +1,25 @@
import type { Reporter, TestCase, TestResult } from '@playwright/test/reporter'
import type {
Reporter,
TestCase,
TestResult,
FullResult,
} from '@playwright/test/reporter'
class MyAPIReporter implements Reporter {
private pendingRequests: Promise<void>[] = []
private allResults: Record<string, any>[] = []
private blockingResults: Record<string, any>[] = []
async onEnd(result: FullResult): Promise<void> {
await Promise.all(this.pendingRequests)
if (this.allResults.length > 0 && this.blockingResults.length === 0) {
result.status = 'passed'
if (!process.env.CI) {
console.error('TAB API - Marked failures as non-blocking')
}
}
}
onTestEnd(test: TestCase, result: TestResult): void {
if (!process.env.TAB_API_URL || !process.env.TAB_API_KEY) {
return
@ -20,6 +39,7 @@ class MyAPIReporter implements Reporter {
platform: process.env.RUNNER_OS || process.platform,
// Extra test and result data
annotations: test.annotations.map((a) => a.type), // e.g. 'fail' or 'fixme'
id: test.id, // computed file/test/project ID used for reruns
retry: result.retry,
tags: test.tags, // e.g. '@snapshot' or '@skipWin'
// Extra environment variables
@ -35,7 +55,7 @@ class MyAPIReporter implements Reporter {
RUNNER_ARCH: process.env.RUNNER_ARCH || null,
}
void (async () => {
const request = (async () => {
try {
const response = await fetch(`${process.env.TAB_API_URL}/api/results`, {
method: 'POST',
@ -46,18 +66,27 @@ class MyAPIReporter implements Reporter {
body: JSON.stringify(payload),
})
if (!response.ok && !process.env.CI) {
console.error(
'TAB API - Failed to send test result:',
await response.text()
)
if (response.ok) {
const result = await response.json()
this.allResults.push(result)
if (result.block) {
this.blockingResults.push(result)
}
} else {
const error = await response.json()
if (!process.env.CI) {
console.error('TAB API - Failed to send test result:', error)
}
}
} catch {
} catch (error) {
const message = error instanceof Error ? error.message : String(error)
if (!process.env.CI) {
console.error('TAB API - Unable to send test result')
console.error('TAB API - Unable to send test result:', message)
}
}
})()
this.pendingRequests.push(request)
}
}

View File

@ -63,6 +63,18 @@ export const isErrorWhitelisted = (exception: Error) => {
'e2e/playwright/can-create-sketches-on-all-planes-and-their-back-sides.spec.ts XY',
project: 'Google Chrome',
},
{
name: 'fE',
message:
'engine: Failed to wait for promise from send modeling command: JsValue("no connection to send on")',
stack: `fE: engine: Failed to wait for promise from send modeling command: JsValue("no connection to send on")
at ET (file:///home/runner/_work/modeling-app/modeling-app/.vite/renderer/main_window/assets/index-BvtRFNLF.js:49875:12)
at WGe.clearSceneAndBustCache (file:///home/runner/_work/modeling-app/modeling-app/.vite/renderer/main_window/assets/index-BvtRFNLF.js:91886:19)
at async a.onEngineConnectionOpened (file:///home/runner/_work/modeling-app/modeling-app/.vite/renderer/main_window/assets/index-BvtRFNLF.js:91483:9)`,
foundInSpec:
'e2e/playwright/testing-constraints.spec.ts is frequent but could be any spec',
project: 'Google Chrome',
},
{
name: 'RangeError',
message: 'Position 160 is out of range for changeset of length 0',

View File

@ -21,8 +21,9 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
if (!app || !app.applicationMenu) {
return false
}
const newProject =
app.applicationMenu.getMenuItemById('File.New project')
const newProject = app.applicationMenu.getMenuItemById(
'File.Create project'
)
if (!newProject) return false
newProject.click()
return true
@ -408,11 +409,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
)
.toBe(true)
})
test('Home.Help.Refresh and report a bug', async ({
tronApp,
cmdBar,
page,
}) => {
test('Home.Help.Report a bug', async ({ tronApp, cmdBar, page }) => {
if (!tronApp) fail()
// Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -423,9 +420,8 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
if (!app || !app.applicationMenu) {
return false
}
const menu = app.applicationMenu.getMenuItemById(
'Help.Refresh and report a bug'
)
const menu =
app.applicationMenu.getMenuItemById('Help.Report a bug')
if (!menu) return false
menu.click()
return true
@ -484,8 +480,9 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail()
const newProject =
app.applicationMenu.getMenuItemById('File.New project')
const newProject = app.applicationMenu.getMenuItemById(
'File.Create project'
)
if (!newProject) fail()
newProject.click()
})
@ -608,7 +605,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
const expected = 'Export'
expect(actual).toBe(expected)
})
test('Modeling.File.Share current part (via Zoo link)', async ({
test('Modeling.File.Share part via Zoo link', async ({
tronApp,
cmdBar,
page,
@ -629,10 +626,10 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
throw new Error('app or app.applicationMenu is missing')
}
const openProject = app.applicationMenu.getMenuItemById(
'File.Share current part (via Zoo link)'
'File.Share part via Zoo link'
)
if (!openProject) {
throw new Error('File.Share current part (via Zoo link)')
throw new Error('File.Share part via Zoo link')
}
openProject.click()
})
@ -2289,7 +2286,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
if (!menu) fail()
})
})
test('Modeling.Help.Refresh and report a bug', async ({
test('Modeling.Help.Report a bug', async ({
tronApp,
cmdBar,
page,
@ -2313,9 +2310,8 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
async () =>
await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) return false
const menu = app.applicationMenu.getMenuItemById(
'Help.Refresh and report a bug'
)
const menu =
app.applicationMenu.getMenuItemById('Help.Report a bug')
if (!menu) return false
menu.click()
return true

View File

@ -63,7 +63,7 @@ test.describe('Onboarding tests', () => {
{
tag: '@electron',
},
async ({ page, tronApp }) => {
async ({ page, tronApp, scene }) => {
if (!tronApp) {
fail()
}
@ -72,7 +72,6 @@ test.describe('Onboarding tests', () => {
onboarding_status: '',
},
})
const u = await getUtils(page)
const viewportSize = { width: 1200, height: 500 }
await page.setBodyDimensions(viewportSize)
@ -80,7 +79,7 @@ test.describe('Onboarding tests', () => {
await test.step(`Create a project and open to the onboarding`, async () => {
await createProject({ name: 'project-link', page })
await test.step(`Ensure the engine connection works by testing the sketch button`, async () => {
await u.waitForPageLoad()
await scene.connectionEstablished()
})
})
@ -107,12 +106,10 @@ test.describe('Onboarding tests', () => {
)
test('Code resets after confirmation', async ({
context,
page,
homePage,
tronApp,
scene,
cmdBar,
}) => {
if (!tronApp) {
fail()
@ -276,6 +273,8 @@ test.describe('Onboarding tests', () => {
page,
homePage,
tronApp,
editor,
toolbar,
}) => {
if (!tronApp) {
fail()
@ -289,7 +288,6 @@ test.describe('Onboarding tests', () => {
},
})
const u = await getUtils(page)
const badCode = `// This is bad code we shouldn't see`
await page.setBodyDimensions({ width: 1200, height: 1080 })
@ -299,18 +297,19 @@ test.describe('Onboarding tests', () => {
.poll(() => page.url())
.toContain(onboardingPaths.PARAMETRIC_MODELING)
const bracketNoNewLines = bracket.replace(/\n/g, '')
// Check the code got reset on load
await expect(page.locator('#code-pane')).toBeVisible()
await expect(u.codeLocator).toHaveText(bracketNoNewLines, {
await toolbar.openPane('code')
await editor.expectEditor.toContain(bracket, {
shouldNormalise: true,
timeout: 10_000,
})
// Mess with the code again
await u.codeLocator.selectText()
await u.codeLocator.fill(badCode)
await expect(u.codeLocator).toHaveText(badCode)
await editor.replaceCode('', badCode)
await editor.expectEditor.toContain(badCode, {
shouldNormalise: true,
timeout: 10_000,
})
// Click to the next step
await page.locator('[data-testid="onboarding-next"]').hover()
@ -320,7 +319,10 @@ test.describe('Onboarding tests', () => {
})
// Check that the code has been reset
await expect(u.codeLocator).toHaveText(bracketNoNewLines)
await editor.expectEditor.toContain(bracket, {
shouldNormalise: true,
timeout: 10_000,
})
})
// (lee) The two avatar tests are weird because even on main, we don't have
@ -329,6 +331,7 @@ test.describe('Onboarding tests', () => {
test('Avatar text updates depending on image load success', async ({
context,
page,
toolbar,
homePage,
tronApp,
}) => {
@ -360,7 +363,7 @@ test.describe('Onboarding tests', () => {
await homePage.goToModelingScene()
// Test that the text in this step is correct
const avatarLocator = page.getByTestId('user-sidebar-toggle').locator('img')
const avatarLocator = toolbar.userSidebarButton.locator('img')
const onboardingOverlayLocator = page
.getByTestId('onboarding-content')
.locator('div')
@ -402,6 +405,7 @@ test.describe('Onboarding tests', () => {
test("Avatar text doesn't mention avatar when no avatar", async ({
context,
page,
toolbar,
homePage,
tronApp,
}) => {
@ -433,7 +437,7 @@ test.describe('Onboarding tests', () => {
await homePage.goToModelingScene()
// Test that the text in this step is correct
const sidebar = page.getByTestId('user-sidebar-toggle')
const sidebar = toolbar.userSidebarButton
const avatar = sidebar.locator('img')
const onboardingOverlayLocator = page
.getByTestId('onboarding-content')
@ -462,6 +466,7 @@ test.describe('Onboarding tests', () => {
test('Restarting onboarding on desktop takes one attempt', async ({
context,
page,
toolbar,
tronApp,
}) => {
test.fixme(orRunWhenFullSuiteEnabled())
@ -500,7 +505,7 @@ test('Restarting onboarding on desktop takes one attempt', async ({
.filter({ hasText: 'Tutorial Project 00' })
const tutorialModalText = page.getByText('Welcome to Design Studio!')
const tutorialDismissButton = page.getByRole('button', { name: 'Dismiss' })
const userMenuButton = page.getByTestId('user-sidebar-toggle')
const userMenuButton = toolbar.userSidebarButton
const userMenuSettingsButton = page.getByRole('button', {
name: 'User settings',
})

View File

@ -143,8 +143,95 @@ test.describe('Point-and-click assemblies tests', () => {
await scene.settled(cmdBar)
})
await test.step('Insert a second time and expect error', async () => {
// TODO: revisit once we have clone with #6209
await test.step('Insert a second time with the same name and expect error', async () => {
await toolbar.insertButton.click()
await cmdBar.selectOption({ name: 'bracket.kcl' }).click()
await cmdBar.expectState({
stage: 'arguments',
currentArgKey: 'localName',
currentArgValue: '',
headerArguments: { Path: 'bracket.kcl', LocalName: '' },
highlightedHeaderArg: 'localName',
commandName: 'Insert',
})
await page.keyboard.insertText('bracket')
await cmdBar.progressCmdBar()
await expect(
page.getByText('This variable name is already in use')
).toBeVisible()
})
await test.step('Insert a second time with a different name and expect error', async () => {
await page.keyboard.insertText('2')
await cmdBar.progressCmdBar()
await cmdBar.expectState({
stage: 'review',
headerArguments: { Path: 'bracket.kcl', LocalName: 'bracket2' },
commandName: 'Insert',
})
await cmdBar.progressCmdBar()
await editor.expectEditor.toContain(
`
import "cylinder.kcl" as cylinder
import "bracket.kcl" as bracket
import "bracket.kcl" as bracket2
cylinder
bracket
bracket2
`,
{ shouldNormalise: true }
)
// TODO: update once we have clone() with #6209
})
}
)
test(
`Insert the bracket part into an assembly and transform it`,
{ tag: ['@electron'] },
async ({
context,
page,
homePage,
scene,
editor,
toolbar,
cmdBar,
tronApp,
}) => {
if (!tronApp) {
fail()
}
const midPoint = { x: 500, y: 250 }
const moreToTheRightPoint = { x: 900, y: 250 }
const bgColor: [number, number, number] = [30, 30, 30]
const partColor: [number, number, number] = [100, 100, 100]
const tolerance = 30
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(
path.join('public', 'kcl-samples', 'bracket', 'main.kcl'),
path.join(bracketDir, 'bracket.kcl')
),
fsp.writeFile(path.join(bracketDir, 'main.kcl'), ''),
])
})
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.openProject(projectName)
await scene.settled(cmdBar)
await toolbar.closePane('code')
})
await test.step('Insert kcl as module', async () => {
await insertPartIntoAssembly(
'bracket.kcl',
'bracket',
@ -152,19 +239,123 @@ test.describe('Point-and-click assemblies tests', () => {
cmdBar,
page
)
await toolbar.openPane('code')
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()
// 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, midPoint, tolerance)
await scene.expectPixelColor(bgColor, moreToTheRightPoint, tolerance)
})
await test.step('Set translate on module', async () => {
await toolbar.openPane('feature-tree')
const op = await toolbar.getFeatureTreeOperation('bracket', 0)
await op.click({ button: 'right' })
await page.getByTestId('context-menu-set-translate').click()
await cmdBar.expectState({
stage: 'arguments',
currentArgKey: 'x',
currentArgValue: '0',
headerArguments: {
X: '',
Y: '',
Z: '',
},
highlightedHeaderArg: 'x',
commandName: 'Translate',
})
await page.keyboard.insertText('100')
await cmdBar.progressCmdBar()
await page.keyboard.insertText('0.1')
await cmdBar.progressCmdBar()
await page.keyboard.insertText('0.2')
await cmdBar.progressCmdBar()
await cmdBar.expectState({
stage: 'review',
headerArguments: {
X: '100',
Y: '0.1',
Z: '0.2',
},
commandName: 'Translate',
})
await cmdBar.progressCmdBar()
await toolbar.closePane('feature-tree')
await toolbar.openPane('code')
await editor.expectEditor.toContain(
`
bracket
|> translate(x = 100, y = 0.1, z = 0.2)
`,
{ shouldNormalise: true }
)
// Expect translated part in the scene
await scene.expectPixelColor(bgColor, midPoint, tolerance)
await scene.expectPixelColor(partColor, moreToTheRightPoint, tolerance)
})
await test.step('Set rotate on module', async () => {
await toolbar.closePane('code')
await toolbar.openPane('feature-tree')
const op = await toolbar.getFeatureTreeOperation('bracket', 0)
await op.click({ button: 'right' })
await page.getByTestId('context-menu-set-rotate').click()
await cmdBar.expectState({
stage: 'arguments',
currentArgKey: 'roll',
currentArgValue: '0',
headerArguments: {
Roll: '',
Pitch: '',
Yaw: '',
},
highlightedHeaderArg: 'roll',
commandName: 'Rotate',
})
await page.keyboard.insertText('0.1')
await cmdBar.progressCmdBar()
await page.keyboard.insertText('0.2')
await cmdBar.progressCmdBar()
await page.keyboard.insertText('0.3')
await cmdBar.progressCmdBar()
await cmdBar.expectState({
stage: 'review',
headerArguments: {
Roll: '0.1',
Pitch: '0.2',
Yaw: '0.3',
},
commandName: 'Rotate',
})
await cmdBar.progressCmdBar()
await toolbar.closePane('feature-tree')
await toolbar.openPane('code')
await editor.expectEditor.toContain(
`
bracket
|> translate(x = 100, y = 0.1, z = 0.2)
|> rotate(roll = 0.1, pitch = 0.2, yaw = 0.3)
`,
{ shouldNormalise: true }
)
// Expect no change in the scene as the rotations are tiny
await scene.expectPixelColor(bgColor, midPoint, tolerance)
await scene.expectPixelColor(partColor, moreToTheRightPoint, tolerance)
})
}
)
@ -231,10 +422,6 @@ test.describe('Point-and-click assemblies tests', () => {
)
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)
@ -279,10 +466,6 @@ test.describe('Point-and-click assemblies tests', () => {
)
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,7 +5,10 @@ import type { Locator, Page } from '@playwright/test'
import type { EditorFixture } from '@e2e/playwright/fixtures/editorFixture'
import type { SceneFixture } from '@e2e/playwright/fixtures/sceneFixture'
import type { ToolbarFixture } from '@e2e/playwright/fixtures/toolbarFixture'
import { orRunWhenFullSuiteEnabled } from '@e2e/playwright/test-utils'
import {
orRunWhenFullSuiteEnabled,
runningOnWindows,
} from '@e2e/playwright/test-utils'
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
@ -3544,6 +3547,9 @@ tag=$rectangleSegmentC002,
toolbar,
cmdBar,
}) => {
if (runningOnWindows()) {
test.fixme(orRunWhenFullSuiteEnabled())
}
const initialCode = `sketch001 = startSketchOn(XZ)
|> startProfileAt([-102.57, 101.72], %)
|> angledLine(angle = 0, length = 202.6, tag = $rectangleSegmentA001)
@ -3835,4 +3841,469 @@ extrude001 = extrude(profile001, length = 100)
)
})
})
const translateExtrudeCases: { variables: boolean }[] = [
{
variables: false,
},
{
variables: true,
},
]
translateExtrudeCases.map(({ variables }) => {
test(`Set translate on extrude through right-click menu (variables: ${variables})`, async ({
context,
page,
homePage,
scene,
editor,
toolbar,
cmdBar,
}) => {
const initialCode = `sketch001 = startSketchOn(XZ)
profile001 = circle(sketch001, center = [0, 0], radius = 1)
extrude001 = extrude(profile001, length = 1)
`
await context.addInitScript((initialCode) => {
localStorage.setItem('persistCode', initialCode)
}, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await scene.settled(cmdBar)
// One dumb hardcoded screen pixel value
const midPoint = { x: 500, y: 250 }
const moreToTheRightPoint = { x: 800, y: 250 }
const bgColor: [number, number, number] = [50, 50, 50]
const partColor: [number, number, number] = [150, 150, 150]
const tolerance = 50
await test.step('Confirm extrude exists with default appearance', async () => {
await toolbar.closePane('code')
await scene.expectPixelColor(partColor, midPoint, tolerance)
await scene.expectPixelColor(bgColor, moreToTheRightPoint, tolerance)
})
await test.step('Set translate through command bar flow', async () => {
await toolbar.openPane('feature-tree')
const op = await toolbar.getFeatureTreeOperation('Extrude', 0)
await op.click({ button: 'right' })
await page.getByTestId('context-menu-set-translate').click()
await cmdBar.expectState({
stage: 'arguments',
currentArgKey: 'x',
currentArgValue: '0',
headerArguments: {
X: '',
Y: '',
Z: '',
},
highlightedHeaderArg: 'x',
commandName: 'Translate',
})
await page.keyboard.insertText('3')
if (variables) {
await cmdBar.createNewVariable()
}
await cmdBar.progressCmdBar()
await page.keyboard.insertText('0.1')
if (variables) {
await cmdBar.createNewVariable()
}
await cmdBar.progressCmdBar()
await page.keyboard.insertText('0.2')
if (variables) {
await cmdBar.createNewVariable()
}
await cmdBar.progressCmdBar()
await cmdBar.expectState({
stage: 'review',
headerArguments: {
X: '3',
Y: '0.1',
Z: '0.2',
},
commandName: 'Translate',
})
await cmdBar.progressCmdBar()
await toolbar.closePane('feature-tree')
})
await test.step('Confirm code and scene have changed', async () => {
await toolbar.openPane('code')
if (variables) {
await editor.expectEditor.toContain(
`
z001 = 0.2
y001 = 0.1
x001 = 3
sketch001 = startSketchOn(XZ)
profile001 = circle(sketch001, center = [0, 0], radius = 1)
extrude001 = extrude(profile001, length = 1)
|> translate(x = x001, y = y001, z = z001)
`,
{ shouldNormalise: true }
)
} else {
await editor.expectEditor.toContain(
`
sketch001 = startSketchOn(XZ)
profile001 = circle(sketch001, center = [0, 0], radius = 1)
extrude001 = extrude(profile001, length = 1)
|> translate(x = 3, y = 0.1, z = 0.2)
`,
{ shouldNormalise: true }
)
}
await scene.expectPixelColor(bgColor, midPoint, tolerance)
await scene.expectPixelColor(partColor, moreToTheRightPoint, tolerance)
})
await test.step('Edit translate', async () => {
await toolbar.openPane('feature-tree')
const op = await toolbar.getFeatureTreeOperation('Extrude', 0)
await op.click({ button: 'right' })
await page.getByTestId('context-menu-set-translate').click()
await cmdBar.expectState({
stage: 'arguments',
currentArgKey: 'z',
currentArgValue: variables ? 'z001' : '0.2',
headerArguments: {
X: '3',
Y: '0.1',
Z: '0.2',
},
highlightedHeaderArg: 'z',
commandName: 'Translate',
})
await page.keyboard.insertText('0.3')
await cmdBar.progressCmdBar()
await cmdBar.expectState({
stage: 'review',
headerArguments: {
X: '3',
Y: '0.1',
Z: '0.3',
},
commandName: 'Translate',
})
await cmdBar.progressCmdBar()
await toolbar.closePane('feature-tree')
await toolbar.openPane('code')
await editor.expectEditor.toContain(`z = 0.3`)
// Expect almost no change in scene
await scene.expectPixelColor(bgColor, midPoint, tolerance)
await scene.expectPixelColor(partColor, moreToTheRightPoint, tolerance)
})
})
})
const rotateExtrudeCases: { variables: boolean }[] = [
{
variables: false,
},
{
variables: true,
},
]
rotateExtrudeCases.map(({ variables }) => {
test(`Set rotate on extrude through right-click menu (variables: ${variables})`, async ({
context,
page,
homePage,
scene,
editor,
toolbar,
cmdBar,
}) => {
const initialCode = `sketch001 = startSketchOn(XZ)
profile001 = circle(sketch001, center = [0, 0], radius = 1)
extrude001 = extrude(profile001, length = 1)
`
await context.addInitScript((initialCode) => {
localStorage.setItem('persistCode', initialCode)
}, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await scene.settled(cmdBar)
await test.step('Set rotate through command bar flow', async () => {
await toolbar.openPane('feature-tree')
const op = await toolbar.getFeatureTreeOperation('Extrude', 0)
await op.click({ button: 'right' })
await page.getByTestId('context-menu-set-rotate').click()
await cmdBar.expectState({
stage: 'arguments',
currentArgKey: 'roll',
currentArgValue: '0',
headerArguments: {
Roll: '',
Pitch: '',
Yaw: '',
},
highlightedHeaderArg: 'roll',
commandName: 'Rotate',
})
await page.keyboard.insertText('1.1')
if (variables) {
await cmdBar.createNewVariable()
}
await cmdBar.progressCmdBar()
await page.keyboard.insertText('1.2')
if (variables) {
await cmdBar.createNewVariable()
}
await cmdBar.progressCmdBar()
await page.keyboard.insertText('1.3')
if (variables) {
await cmdBar.createNewVariable()
}
await cmdBar.progressCmdBar()
await cmdBar.expectState({
stage: 'review',
headerArguments: {
Roll: '1.1',
Pitch: '1.2',
Yaw: '1.3',
},
commandName: 'Rotate',
})
await cmdBar.progressCmdBar()
await toolbar.closePane('feature-tree')
})
await test.step('Confirm code and scene have changed', async () => {
await toolbar.openPane('code')
if (variables) {
await editor.expectEditor.toContain(
`
yaw001 = 1.3
pitch001 = 1.2
roll001 = 1.1
sketch001 = startSketchOn(XZ)
profile001 = circle(sketch001, center = [0, 0], radius = 1)
extrude001 = extrude(profile001, length = 1)
|> rotate(roll = roll001, pitch = pitch001, yaw = yaw001)
`,
{ shouldNormalise: true }
)
} else {
await editor.expectEditor.toContain(
`
sketch001 = startSketchOn(XZ)
profile001 = circle(sketch001, center = [0, 0], radius = 1)
extrude001 = extrude(profile001, length = 1)
|> rotate(roll = 1.1, pitch = 1.2, yaw = 1.3)
`,
{ shouldNormalise: true }
)
}
})
await test.step('Edit rotate', async () => {
await toolbar.openPane('feature-tree')
const op = await toolbar.getFeatureTreeOperation('Extrude', 0)
await op.click({ button: 'right' })
await page.getByTestId('context-menu-set-rotate').click()
await cmdBar.expectState({
stage: 'arguments',
currentArgKey: 'yaw',
currentArgValue: variables ? 'yaw001' : '1.3',
headerArguments: {
Roll: '1.1',
Pitch: '1.2',
Yaw: '1.3',
},
highlightedHeaderArg: 'yaw',
commandName: 'Rotate',
})
await page.keyboard.insertText('13')
await cmdBar.progressCmdBar()
await cmdBar.expectState({
stage: 'review',
headerArguments: {
Roll: '1.1',
Pitch: '1.2',
Yaw: '13',
},
commandName: 'Rotate',
})
await cmdBar.progressCmdBar()
await toolbar.closePane('feature-tree')
await toolbar.openPane('code')
await editor.expectEditor.toContain(`yaw = 13`)
})
})
})
test(`Set translate and rotate on extrude through selection`, async ({
context,
page,
homePage,
scene,
editor,
toolbar,
cmdBar,
}) => {
const initialCode = `sketch001 = startSketchOn(XZ)
profile001 = circle(sketch001, center = [0, 0], radius = 1)
extrude001 = extrude(profile001, length = 1)
`
await context.addInitScript((initialCode) => {
localStorage.setItem('persistCode', initialCode)
}, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await scene.settled(cmdBar)
// One dumb hardcoded screen pixel value
const midPoint = { x: 500, y: 250 }
const moreToTheRightPoint = { x: 800, y: 250 }
const bgColor: [number, number, number] = [50, 50, 50]
const partColor: [number, number, number] = [150, 150, 150]
const tolerance = 50
const [clickMidPoint] = scene.makeMouseHelpers(midPoint.x, midPoint.y)
const [clickMoreToTheRightPoint] = scene.makeMouseHelpers(
moreToTheRightPoint.x,
moreToTheRightPoint.y
)
await test.step('Confirm extrude exists with default appearance', async () => {
await toolbar.closePane('code')
await scene.expectPixelColor(partColor, midPoint, tolerance)
await scene.expectPixelColor(bgColor, moreToTheRightPoint, tolerance)
})
await test.step('Set translate through command bar flow', async () => {
await cmdBar.openCmdBar()
await cmdBar.chooseCommand('Translate')
await cmdBar.expectState({
stage: 'arguments',
currentArgKey: 'selection',
currentArgValue: '',
headerArguments: {
Selection: '',
X: '',
Y: '',
Z: '',
},
highlightedHeaderArg: 'selection',
commandName: 'Translate',
})
await clickMidPoint()
await cmdBar.progressCmdBar()
await cmdBar.expectState({
stage: 'arguments',
currentArgKey: 'x',
currentArgValue: '0',
headerArguments: {
Selection: '1 path',
X: '',
Y: '',
Z: '',
},
highlightedHeaderArg: 'x',
commandName: 'Translate',
})
await page.keyboard.insertText('2')
await cmdBar.progressCmdBar()
await cmdBar.progressCmdBar()
await cmdBar.progressCmdBar()
await cmdBar.expectState({
stage: 'review',
headerArguments: {
Selection: '1 path',
X: '2',
Y: '0',
Z: '0',
},
commandName: 'Translate',
})
await cmdBar.progressCmdBar()
})
await test.step('Confirm code and scene have changed', async () => {
await toolbar.openPane('code')
await editor.expectEditor.toContain(
`
sketch001 = startSketchOn(XZ)
profile001 = circle(sketch001, center = [0, 0], radius = 1)
extrude001 = extrude(profile001, length = 1)
|> translate(x = 2, y = 0, z = 0)
`,
{ shouldNormalise: true }
)
await scene.expectPixelColor(bgColor, midPoint, tolerance)
await scene.expectPixelColor(partColor, moreToTheRightPoint, tolerance)
})
await test.step('Set rotate through command bar flow', async () => {
// clear selection
await clickMidPoint()
await cmdBar.openCmdBar()
await cmdBar.chooseCommand('Rotate')
await cmdBar.expectState({
stage: 'arguments',
currentArgKey: 'selection',
currentArgValue: '',
headerArguments: {
Selection: '',
Roll: '',
Pitch: '',
Yaw: '',
},
highlightedHeaderArg: 'selection',
commandName: 'Rotate',
})
await clickMoreToTheRightPoint()
await cmdBar.progressCmdBar()
await cmdBar.expectState({
stage: 'arguments',
currentArgKey: 'roll',
currentArgValue: '0',
headerArguments: {
Selection: '1 path',
Roll: '',
Pitch: '',
Yaw: '',
},
highlightedHeaderArg: 'roll',
commandName: 'Rotate',
})
await page.keyboard.insertText('0.1')
await cmdBar.progressCmdBar()
await page.keyboard.insertText('0.2')
await cmdBar.progressCmdBar()
await page.keyboard.insertText('0.3')
await cmdBar.progressCmdBar()
await cmdBar.expectState({
stage: 'review',
headerArguments: {
Selection: '1 path',
Roll: '0.1',
Pitch: '0.2',
Yaw: '0.3',
},
commandName: 'Rotate',
})
await cmdBar.progressCmdBar()
})
await test.step('Confirm code has changed', async () => {
await toolbar.openPane('code')
await editor.expectEditor.toContain(
`
sketch001 = startSketchOn(XZ)
profile001 = circle(sketch001, center = [0, 0], radius = 1)
extrude001 = extrude(profile001, length = 1)
|> translate(x = 2, y = 0, z = 0)
|> rotate(roll = 0.1, pitch = 0.2, yaw = 0.3)
`,
{ shouldNormalise: true }
)
// No change here since the angles are super small
await scene.expectPixelColor(bgColor, midPoint, tolerance)
await scene.expectPixelColor(partColor, moreToTheRightPoint, tolerance)
})
})
})

View File

@ -400,11 +400,6 @@ test(
await expect(page.getByText('broken-code')).toBeVisible()
await page.getByText('broken-code').click()
// Gotcha: You can not use scene.settled() since the KCL code is going to fail
await expect(
page.getByTestId('model-state-indicator-playing')
).toBeAttached()
// Gotcha: Scroll to the text content in code mirror because CodeMirror lazy loads DOM content
await editor.scrollToText(
"|> line(end = [0, wallMountL], tag = 'outerEdge')"
@ -779,7 +774,9 @@ test.describe(`Project management commands`, () => {
// Constants and locators
const projectHomeLink = page.getByTestId('project-link')
const commandButton = page.getByRole('button', { name: 'Commands' })
const commandOption = page.getByRole('option', { name: 'rename project' })
const commandOption = page.getByRole('option', {
name: 'rename project',
})
const projectNameOption = page.getByRole('option', { name: projectName })
const projectRenamedName = `untitled`
// const projectMenuButton = page.getByTestId('project-sidebar-toggle')
@ -839,7 +836,9 @@ test.describe(`Project management commands`, () => {
// Constants and locators
const projectHomeLink = page.getByTestId('project-link')
const commandButton = page.getByRole('button', { name: 'Commands' })
const commandOption = page.getByRole('option', { name: 'delete project' })
const commandOption = page.getByRole('option', {
name: 'delete project',
})
const projectNameOption = page.getByRole('option', { name: projectName })
const commandWarning = page.getByText('Are you sure you want to delete?')
const commandSubmitButton = page.getByRole('button', {
@ -891,7 +890,9 @@ test.describe(`Project management commands`, () => {
// Constants and locators
const projectHomeLink = page.getByTestId('project-link')
const commandButton = page.getByRole('button', { name: 'Commands' })
const commandOption = page.getByRole('option', { name: 'rename project' })
const commandOption = page.getByRole('option', {
name: 'rename project',
})
const projectNameOption = page.getByRole('option', { name: projectName })
const projectRenamedName = `untitled`
const commandContinueButton = page.getByRole('button', {
@ -947,7 +948,9 @@ test.describe(`Project management commands`, () => {
// Constants and locators
const projectHomeLink = page.getByTestId('project-link')
const commandButton = page.getByRole('button', { name: 'Commands' })
const commandOption = page.getByRole('option', { name: 'delete project' })
const commandOption = page.getByRole('option', {
name: 'delete project',
})
const projectNameOption = page.getByRole('option', { name: projectName })
const commandWarning = page.getByText('Are you sure you want to delete?')
const commandSubmitButton = page.getByRole('button', {
@ -1962,13 +1965,13 @@ test(
test(
'Settings persist across restarts',
{ tag: '@electron' },
async ({ page, scene, cmdBar }, testInfo) => {
async ({ page, toolbar }, testInfo) => {
await test.step('We can change a user setting like theme', async () => {
await page.setBodyDimensions({ width: 1200, height: 500 })
page.on('console', console.log)
await page.getByTestId('user-sidebar-toggle').click()
await toolbar.userSidebarButton.click()
await page.getByTestId('user-settings').click()
@ -1995,7 +1998,7 @@ test(
test(
'Original project name persist after onboarding',
{ tag: '@electron' },
async ({ page }, testInfo) => {
async ({ page, toolbar }, testInfo) => {
test.fixme(orRunWhenFullSuiteEnabled())
await page.setBodyDimensions({ width: 1200, height: 500 })
@ -2007,7 +2010,7 @@ test(
})
await test.step('Should go through onboarding', async () => {
await page.getByTestId('user-sidebar-toggle').click()
await toolbar.userSidebarButton.click()
await page.getByTestId('user-settings').click()
await page.getByRole('button', { name: 'Replay Onboarding' }).click()

View File

@ -1,4 +1,5 @@
import { expect, test } from '@e2e/playwright/zoo-test'
import { orRunWhenFullSuiteEnabled } from '@e2e/playwright/test-utils'
/* eslint-disable jest/no-conditional-expect */
@ -57,6 +58,7 @@ test.describe('edit with AI example snapshots', () => {
`change colour`,
{ tag: '@snapshot' },
async ({ context, homePage, cmdBar, editor, page, scene }) => {
test.fixme(orRunWhenFullSuiteEnabled())
await context.addInitScript((file) => {
localStorage.setItem('persistCode', file)
}, file)

View File

@ -1,4 +1,3 @@
import { orRunWhenFullSuiteEnabled } from '@e2e/playwright/test-utils'
import { expect, test } from '@e2e/playwright/zoo-test'
/* eslint-disable jest/no-conditional-expect */
@ -51,7 +50,6 @@ test.describe('Prompt-to-edit tests', { tag: '@skipWin' }, () => {
page,
scene,
}) => {
test.fixme(orRunWhenFullSuiteEnabled())
await context.addInitScript((file) => {
localStorage.setItem('persistCode', file)
}, file)
@ -200,7 +198,6 @@ test.describe('Prompt-to-edit tests', { tag: '@skipWin' }, () => {
page,
scene,
}) => {
test.fixme(orRunWhenFullSuiteEnabled())
const body1CapCoords = { x: 571, y: 311 }
await context.addInitScript((file) => {
@ -260,7 +257,6 @@ test.describe('Prompt-to-edit tests', { tag: '@skipWin' }, () => {
page,
scene,
}) => {
test.fixme(orRunWhenFullSuiteEnabled())
const body1CapCoords = { x: 571, y: 311 }
const body2WallCoords = { x: 620, y: 152 }
const [clickBody1Cap] = scene.makeMouseHelpers(

View File

@ -45,11 +45,7 @@ test.describe('Sketch tests', { tag: ['@skipWin'] }, () => {
screwHole = startSketchOn(XY)
${startProfileAt1}
|> arc({
radius = screwRadius,
angleStart = 0,
angleEnd = 360
}, %)
|> arc(angleStart = 0, angleEnd = 360, radius = screwRadius)
part001 = startSketchOn(XY)
${startProfileAt2}
@ -66,11 +62,7 @@ test.describe('Sketch tests', { tag: ['@skipWin'] }, () => {
|> tangentialArc(endAbsolute = [width / 2, 0])
|> xLine(length = -width / 4 + wireRadius)
|> yLine(length = wireOffset)
|> arc({
radius = wireRadius,
angleStart = 0,
angleEnd = 180
}, %)
|> arc(angleStart = 0, angleEnd = 180, radius = wireRadius)
|> yLine(length = -wireOffset)
|> xLine(length = -width / 4)
|> close()
@ -214,15 +206,8 @@ sketch001 = startProfileAt([12.34, -12.34], sketch002)
|> startProfileAt([4.61, -14.01], %)
|> line(end = [12.73, -0.09])
|> tangentialArc(endAbsolute = [24.95, -5.38])
|> arcTo({
interior = [20.18, -1.7],
end = [11.82, -1.16]
}, %)
|> arc({
radius = 5.92,
angleStart = -89.36,
angleEnd = 135.81
}, %)
|> arc(interiorAbsolute = [20.18, -1.7], endAbsolute = [11.82, -1.16])
|> arc(angleStart = -89.36, angleEnd = 135.81, radius = 5.92)
|> close()`
)
})
@ -264,15 +249,8 @@ sketch001 = startProfileAt([12.34, -12.34], sketch002)
|> startProfileAt([4.61, -14.01], %)
|> line(end = [12.73, -0.09])
|> tangentialArc(endAbsolute = [24.95, -5.38])
|> arcTo({
interior = [20.18, -1.7],
end = [11.82, -1.16]
}, %)
|> arc({
radius = 5.92,
angleStart = -89.36,
angleEnd = 135.81
}, %)
|> arc(interiorAbsolute = [20.18, -1.7], endAbsolute = [11.82, -1.16])
|> arc(angleStart = -89.36, angleEnd = 135.81, radius = 5.92)
|> close()
`)
} else {
@ -338,7 +316,7 @@ sketch001 = startProfileAt([12.34, -12.34], sketch002)
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
}
// drag arcTo interior handle (three point arc)
// drag arcTo interiorAbsolute handle (three point arc)
const arcToHandle = await u.getBoundingBox('[data-overlay-index="2"]')
await page.mouse.move(arcToHandle.x, arcToHandle.y - 5)
await page.mouse.down()
@ -410,15 +388,8 @@ sketch001 = startProfileAt([12.34, -12.34], sketch002)
|> startProfileAt([6.44, -12.07], %)
|> line(end = [14.72, 1.97])
|> tangentialArc(endAbsolute = [26.92, -3.32])
|> arcTo({
interior = [18.11, -3.73],
end = [9.77, -3.19]
}, %)
|> arc({
radius = 3.75,
angleStart = -58.29,
angleEnd = 161.17
}, %)
|> arc(interiorAbsolute = [18.11, -3.73], endAbsolute = [9.77, -3.19])
|> arc(angleStart = -58.29, angleEnd = 161.17, radius = 3.75)
|> close()
`)
}
@ -948,7 +919,9 @@ profile001 = startProfileAt([${roundOff(scale * 69.6)}, ${roundOff(
await page.mouse.move(pointA[0] - 12, pointA[1] + 12, { steps: 10 })
const pointNotQuiteA = [pointA[0] - 7, pointA[1] + 7]
await page.mouse.move(pointNotQuiteA[0], pointNotQuiteA[1], { steps: 10 })
await page.mouse.move(pointNotQuiteA[0], pointNotQuiteA[1], {
steps: 10,
})
await page.mouse.click(pointNotQuiteA[0], pointNotQuiteA[1], {
delay: 200,
@ -1500,12 +1473,12 @@ profile002 = startProfileAt([117.2, 56.08], sketch001)
await circle3Point1()
await page.waitForTimeout(200)
await circle3Point2()
await editor.expectEditor.toContain('arcTo({')
await editor.expectEditor.toContain('arc(')
})
await test.step('equip line tool and verify three-point-arc code is removed after second click', async () => {
await toolbar.lineBtn.click()
await editor.expectEditor.not.toContain('arcTo({')
await editor.expectEditor.not.toContain('arc(')
})
const [cornerRectPoint1] = scene.makeMouseHelpers(600, 300)
@ -1542,9 +1515,9 @@ profile002 = startProfileAt([117.2, 56.08], sketch001)
await continueProfile2Clk()
await page.waitForTimeout(200)
await circle3Point1()
await editor.expectEditor.toContain('arcTo({')
await editor.expectEditor.toContain('arc(')
await toolbar.lineBtn.click()
await editor.expectEditor.not.toContain('arcTo({')
await editor.expectEditor.not.toContain('arc(')
await editor.expectEditor.toContain('profile002')
})
}
@ -1841,7 +1814,9 @@ profile003 = startProfileAt([206.63, -56.73], sketch001)
await page.waitForTimeout(300)
// Verify the three-point arc was created correctly
await editor.expectEditor.toContain(`|> arcTo(`)
await editor.expectEditor.toContain(`arc(`)
await editor.expectEditor.toContain(`interiorAbsolute`)
await editor.expectEditor.toContain(`endAbsolute`)
// Switch back to line tool to continue
await toolbar.lineBtn.click()
@ -2061,10 +2036,7 @@ profile003 = startProfileAt([206.63, -56.73], sketch001)
// Verify the first three-point arc was created correctly
await editor.expectEditor.toContain(
`profile011 = startProfileAt([13.56, -9.97], sketch001)
|> arcTo({
interior = [15.19, -6.51],
end = [19.33, -11.19]
}, %)`,
|> arc(interiorAbsolute = [15.19, -6.51], endAbsolute = [19.33, -11.19])`,
{ shouldNormalise: true }
)
@ -2081,14 +2053,8 @@ profile003 = startProfileAt([206.63, -56.73], sketch001)
// Verify the second three-point arc was created correctly
await editor.expectEditor.toContain(
` |> arcTo({
interior = [19.8, 1.7],
end = [21.7, 2.92]
}, %)
|> arcTo({
interior = [27.47, 1.42],
end = [27.57, 1.52]
}, %)`,
` |> arc(interiorAbsolute = [19.8, 1.7], endAbsolute = [21.7, 2.92])
|> arc(interiorAbsolute = [27.47, 1.42], endAbsolute = [27.57, 1.52])`,
{ shouldNormalise: true }
)
})
@ -2863,33 +2829,29 @@ test.describe(`Click based selection don't brick the app when clicked out of ran
`sketch001 = startSketchOn(XZ)
|> startProfileAt([0, 0], %)
|> line(end = [3.14, 3.14])
|> arcTo({
end = [4, 2],
interior = [1, 2]
}, %)
`
|> arc(
interiorAbsolute = [1, 2],
endAbsolute = [4, 2]
)`
)
})
await homePage.goToModelingScene()
await scene.settled(cmdBar)
const formattedArc = `arc(interiorAbsolute = [1, 2], endAbsolute = [4, 2])`
await test.step(`format the code`, async () => {
// doesn't contain condensed version
await editor.expectEditor.not.toContain(
`arcTo({ end = [4, 2], interior = [1, 2] }, %)`
)
await editor.expectEditor.not.toContain(formattedArc)
// click the code to enter sketch mode
await page.getByText(`arcTo`).click()
await page.getByText(`arc`).click()
// Format the code.
await page.locator('#code-pane button:first-child').click()
await page.locator('button:has-text("Format code")').click()
})
await test.step(`Ensure the code reformatted`, async () => {
await editor.expectEditor.toContain(
`arcTo({ end = [4, 2], interior = [1, 2] }, %)`
)
await editor.expectEditor.toContain(formattedArc)
})
const [arcClick, arcHover] = scene.makeMouseHelpers(699, 337)
@ -2900,7 +2862,7 @@ test.describe(`Click based selection don't brick the app when clicked out of ran
await editor.expectState({
activeLines: ['sketch001=startSketchOn(XZ)'],
diagnostics: [],
highlightedCode: 'arcTo({end = [4, 2], interior = [1, 2]}, %)',
highlightedCode: 'arc(interiorAbsolute = [1, 2], endAbsolute = [4, 2])',
})
})
@ -2922,7 +2884,7 @@ test.describe(`Click based selection don't brick the app when clicked out of ran
await editor.expectState({
activeLines: [],
diagnostics: [],
highlightedCode: 'arcTo({end = [4, 2], interior = [1, 2]}, %)',
highlightedCode: 'arc(interiorAbsolute = [1, 2], endAbsolute = [4, 2])',
})
})
})
@ -3036,10 +2998,7 @@ profile001 = startProfileAt([0, 0], sketch001)
|> line(end = [191.39, 191.39])
|> tangentialArc(endAbsolute = [287.08, 95.69], tag = $seg01)
|> angledLine(angle = tangentToEnd(seg01), length = 135.34)
|> arcTo({
interior = [191.39, -95.69],
end = [287.08, -95.69]
}, %, $seg02)
|> arc(interiorAbsolute = [191.39, -95.69], endAbsolute = [287.08, -95.69], tag = $seg02)
|> angledLine(angle = tangentToEnd(seg02) + turns::HALF_TURN, length = 270.67)
`.replaceAll('\n', '')
)
@ -3058,7 +3017,7 @@ test.describe('manual edits during sketch mode', () => {
}) => {
const initialCode = `myVar1 = 5
myVar2 = 6
sketch001 = startSketchOn(XZ)
profile001 = startProfileAt([106.68, 89.77], sketch001)
|> line(end = [132.34, 157.8])
@ -3069,10 +3028,8 @@ test.describe('manual edits during sketch mode', () => {
sketch002 = startSketchOn(extrude001, face = seg01)
profile002 = startProfileAt([83.39, 329.15], sketch002)
|> angledLine(angle = 0, length = 119.61, tag = $rectangleSegmentA001)
|> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 156.54, angle = -28)
|> angledLine(length = 156.54, angle = -28)
|> angledLine(
angle = segAng(rectangleSegmentA001),
length = -segLen(rectangleSegmentA001),
angle = -151,
length = 116.27,
)
@ -3089,38 +3046,43 @@ test.describe('manual edits during sketch mode', () => {
await homePage.goToModelingScene()
await scene.connectionEstablished()
await scene.settled(cmdBar)
const expectSketchOriginToBeDrawn = async () => {
await scene.expectPixelColor(TEST_COLORS.WHITE, { x: 672, y: 193 }, 15)
}
await test.step('Open feature tree and edit second sketch', async () => {
await toolbar.openFeatureTreePane()
const sketchButton = await toolbar.getFeatureTreeOperation('Sketch', 1)
await sketchButton.dblclick()
await page.waitForTimeout(700) // Wait for engine animation
await expectSketchOriginToBeDrawn()
})
await test.step('Add new variable and wait for re-execution', async () => {
await page.waitForTimeout(500) // wait for deferred execution
await editor.replaceCode('myVar2 = 6', 'myVar2 = 6\nmyVar3 = 7')
await page.waitForTimeout(2000) // wait for deferred execution
await expectSketchOriginToBeDrawn()
})
const handle1Location = { x: 843, y: 235 }
await test.step('Edit sketch by dragging handle', async () => {
await page.waitForTimeout(500)
await editor.expectEditor.toContain('length = 156.54, angle = -28')
await page.mouse.move(handle1Location.x, handle1Location.y)
await page.mouse.down()
await page.mouse.move(handle1Location.x + 50, handle1Location.y + 50, {
steps: 5,
})
await page.mouse.up()
await editor.expectEditor.toContain('length = 231.59, angle = -34')
// await page.waitForTimeout(1000) // Wait for update
await expect
.poll(
async () => {
await editor.expectEditor.toContain('length = 156.54, angle = -28')
await page.mouse.move(handle1Location.x, handle1Location.y)
await page.mouse.down()
await page.mouse.move(
handle1Location.x + 50,
handle1Location.y + 50,
{
steps: 5,
}
)
await page.mouse.up()
await editor.expectEditor.toContain('length = 231.59, angle = -34')
return true
},
{ timeout: 10_000 }
)
.toBeTruthy()
})
await test.step('Delete variables and wait for re-execution', async () => {
@ -3129,20 +3091,27 @@ test.describe('manual edits during sketch mode', () => {
await page.waitForTimeout(50)
await editor.replaceCode('myVar2 = 6', '')
await page.waitForTimeout(2000) // Wait for deferred execution
await expectSketchOriginToBeDrawn()
})
const handle2Location = { x: 872, y: 273 }
await test.step('Edit sketch again', async () => {
await editor.expectEditor.toContain('length = 231.59, angle = -34')
await page.waitForTimeout(500)
await page.mouse.move(handle2Location.x, handle2Location.y)
await page.mouse.down()
await page.mouse.move(handle2Location.x, handle2Location.y - 50, {
steps: 5,
})
await page.mouse.up()
await editor.expectEditor.toContain('length = 167.36, angle = -14')
await expect
.poll(
async () => {
await page.mouse.move(handle2Location.x, handle2Location.y)
await page.mouse.down()
await page.mouse.move(handle2Location.x, handle2Location.y - 50, {
steps: 5,
})
await page.mouse.up()
await editor.expectEditor.toContain('length = 167.36, angle = -14')
return true
},
{ timeout: 10_000 }
)
.toBeTruthy()
})
await test.step('add whole other sketch before current sketch', async () => {
@ -3154,19 +3123,27 @@ test.describe('manual edits during sketch mode', () => {
profile004 = circle(sketch003, center = [143.91, 136.89], radius = 71.63)`
)
await page.waitForTimeout(2000) // Wait for deferred execution
await expectSketchOriginToBeDrawn()
})
const handle3Location = { x: 844, y: 212 }
await test.step('edit sketch again', async () => {
await editor.expectEditor.toContain('length = 167.36, angle = -14')
await page.mouse.move(handle3Location.x, handle3Location.y)
await page.mouse.down()
await page.mouse.move(handle3Location.x, handle3Location.y + 110, {
steps: 5,
})
await page.mouse.up()
await editor.expectEditor.toContain('length = 219.2, angle = -56')
await page.waitForTimeout(500) // Wait for deferred execution
await expect
.poll(
async () => {
await editor.expectEditor.toContain('length = 167.36, angle = -14')
await page.mouse.move(handle3Location.x, handle3Location.y)
await page.mouse.down()
await page.mouse.move(handle3Location.x, handle3Location.y + 110, {
steps: 5,
})
await page.mouse.up()
await editor.expectEditor.toContain('length = 219.2, angle = -56')
return true
},
{ timeout: 10_000 }
)
.toBeTruthy()
})
// exit sketch and assert whole code
@ -3174,32 +3151,27 @@ test.describe('manual edits during sketch mode', () => {
await toolbar.exitSketch()
await editor.expectEditor.toContain(
`myVar1 = 5
sketch003 = startSketchOn(XY)
profile004 = circle(sketch003, center = [143.91, 136.89], radius = 71.63)
sketch001 = startSketchOn(XZ)
profile001 = startProfileAt([106.68, 89.77], sketch001)
|> line(end = [132.34, 157.8])
|> line(end = [67.65, -460.55], tag = $seg01)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
extrude001 = extrude(profile001, length = 500)
sketch002 = startSketchOn(extrude001, face = seg01)
profile002 = startProfileAt([83.39, 329.15], sketch002)
|> angledLine(angle = 0, length = 119.61, tag = $rectangleSegmentA001)
|> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 219.2, angle = -56)
|> angledLine(
angle = segAng(rectangleSegmentA001),
length = -segLen(rectangleSegmentA001),
angle = -151,
length = 116.27,
)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
profile003 = startProfileAt([-201.08, 254.17], sketch002)
|> line(end = [103.55, 33.32])
|> line(end = [48.8, -153.54])
`,
sketch003 = startSketchOn(XY)
profile004 = circle(sketch003, center = [143.91, 136.89], radius = 71.63)
sketch001 = startSketchOn(XZ)
profile001 = startProfileAt([106.68, 89.77], sketch001)
|> line(end = [132.34, 157.8])
|> line(end = [67.65, -460.55], tag = $seg01)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
extrude001 = extrude(profile001, length = 500)
sketch002 = startSketchOn(extrude001, face = seg01)
profile002 = startProfileAt([83.39, 329.15], sketch002)
|> angledLine(angle = 0, length = 119.61, tag = $rectangleSegmentA001)
|> angledLine(length = 219.2, angle = -56)
|> angledLine(angle = -151, length = 116.27)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
profile003 = startProfileAt([-201.08, 254.17], sketch002)
|> line(end = [103.55, 33.32])
|> line(end = [48.8, -153.54])
`,
{ shouldNormalise: true }
)
await editor.expectState({
@ -3220,7 +3192,7 @@ test.describe('manual edits during sketch mode', () => {
}) => {
const initialCode = `myVar1 = 5
myVar2 = 6
sketch001 = startSketchOn(XZ)
profile001 = startProfileAt([106.68, 89.77], sketch001)
|> line(end = [132.34, 157.8])
@ -3231,10 +3203,8 @@ test.describe('manual edits during sketch mode', () => {
sketch002 = startSketchOn(extrude001, face = seg01)
profile002 = startProfileAt([83.39, 329.15], sketch002)
|> angledLine(angle = 0, length = 119.61, tag = $rectangleSegmentA001)
|> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 156.54, angle = -28)
|> angledLine(length = 156.54, angle = -28)
|> angledLine(
angle = segAng(rectangleSegmentA001),
length = -segLen(rectangleSegmentA001),
angle = -151,
length = 116.27,
)
@ -3325,7 +3295,7 @@ test.describe('manual edits during sketch mode', () => {
)
await editor.expectState({
activeLines: [],
diagnostics: ['memoryitemkey`badBadBadFn`isnotdefined'],
diagnostics: ['`badBadBadFn`isnotdefined'],
highlightedCode: '',
})
await expect(
@ -3350,7 +3320,21 @@ test.describe('manual edits during sketch mode', () => {
// this checks sketch segments have been drawn
await verifyArrowHeadColor(arrowHeadWhite)
})
await page.waitForTimeout(100)
await test.step('make a change to the code and expect pixel color to change', async () => {
// defends against a regression where sketch would duplicate in the scene
// https://github.com/KittyCAD/modeling-app/issues/6345
await editor.replaceCode(
'startProfileAt([75.8, 317.2',
'startProfileAt([75.8, 217.2'
)
// expect not white anymore
await scene.expectPixelColorNotToBe(
TEST_COLORS.WHITE,
arrowHeadLocation,
15
)
})
}
)
})

View File

@ -49,7 +49,6 @@ test(
'exports of each format should work',
{ tag: ['@snapshot', '@skipWin', '@skipMacos'] },
async ({ page, context, scene, cmdBar, tronApp }) => {
test.fixme(orRunWhenFullSuiteEnabled())
if (!tronApp) {
fail()
}
@ -377,9 +376,6 @@ test.describe(
'extrude on default planes should be stable',
{ tag: '@snapshot' },
() => {
// FIXME: Skip on macos its being weird.
test.skip(process.platform === 'darwin', 'Skip on macos')
test('XY', async ({ page, context, cmdBar, scene }) => {
await extrudeDefaultPlane(context, page, cmdBar, scene, 'XY')
})
@ -534,9 +530,6 @@ test(
'Draft rectangles should look right',
{ tag: '@snapshot' },
async ({ page, context, cmdBar, scene }) => {
// FIXME: Skip on macos its being weird.
test.skip(process.platform === 'darwin', 'Skip on macos')
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
const PUR = 400 / 37.5 //pixeltoUnitRatio
@ -582,7 +575,6 @@ test(
'Draft circle should look right',
{ tag: '@snapshot' },
async ({ page, context, cmdBar, scene }) => {
test.fixme(orRunWhenFullSuiteEnabled())
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
const PUR = 400 / 37.5 //pixeltoUnitRatio
@ -629,9 +621,6 @@ test.describe(
'Client side scene scale should match engine scale',
{ tag: '@snapshot' },
() => {
// FIXME: Skip on macos its being weird.
test.skip(process.platform === 'darwin', 'Skip on macos')
test('Inch scale', async ({ page, cmdBar, scene }) => {
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
@ -807,9 +796,6 @@ test(
'Sketch on face with none z-up',
{ tag: '@snapshot' },
async ({ page, context, cmdBar, scene }) => {
// FIXME: Skip on macos its being weird.
test.skip(process.platform === 'darwin', 'Skip on macos')
const u = await getUtils(page)
await context.addInitScript(async (KCL_DEFAULT_LENGTH) => {
localStorage.setItem(
@ -868,9 +854,6 @@ test(
'Zoom to fit on load - solid 2d',
{ tag: '@snapshot' },
async ({ page, context, cmdBar, scene }) => {
// FIXME: Skip on macos its being weird.
test.skip(process.platform === 'darwin', 'Skip on macos')
const u = await getUtils(page)
await context.addInitScript(async () => {
localStorage.setItem(
@ -907,9 +890,6 @@ test(
'Zoom to fit on load - solid 3d',
{ tag: '@snapshot' },
async ({ page, context, cmdBar, scene }) => {
// FIXME: Skip on macos its being weird.
test.skip(process.platform === 'darwin', 'Skip on macos')
const u = await getUtils(page)
await context.addInitScript(async () => {
localStorage.setItem(
@ -1155,11 +1135,7 @@ sweepPath = startSketchOn(XZ)
sweepSketch = startSketchOn(XY)
|> startProfileAt([2, 0], %)
|> arc({
angleEnd = 360,
angleStart = 0,
radius = 2
}, %)
|> arc(angleStart = 0, angleEnd = 360, radius = 2)
|> sweep(path = sweepPath)
|> appearance(
color = "#bb00ff",
@ -1204,11 +1180,7 @@ sweepPath = startSketchOn(XZ)
sweepSketch = startSketchOn(XY)
|> startProfileAt([2, 0], %)
|> arc({
angleEnd = 360,
angleStart = 0,
radius = 2
}, %)
|> arc(angleStart = 0, angleEnd = 360, radius = 2)
|> sweep(path = sweepPath)
|> appearance(
color = "#bb00ff",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 49 KiB

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