Compare commits

...

87 Commits

Author SHA1 Message Date
6e2274a6c4 Bump @tweenjs/tween.js from 23.1.2 to 25.0.0
Bumps [@tweenjs/tween.js](https://github.com/tweenjs/tween.js) from 23.1.2 to 25.0.0.
- [Release notes](https://github.com/tweenjs/tween.js/releases)
- [Commits](https://github.com/tweenjs/tween.js/compare/v23.1.2...v25.0.0)

---
updated-dependencies:
- dependency-name: "@tweenjs/tween.js"
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-12 03:47:59 +00:00
4b9f71c994 Update machine-api spec (#3346)
* YOYO NEW API SPEC!

* New machine-api types

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-08-11 18:04:30 -07:00
e86a5622c8 the printer slicer expects mm (#3341)
* the printer slicer expects mm

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

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* empty

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-08-09 18:05:18 -07:00
a503d1ce50 Use named constants for settings URL query params (#3333) 2024-08-09 02:47:25 -04:00
11a94cc99e set global test timeout using the Playwright config (#3330)
set global test timeout

Co-authored-by: ryanrosello-og <ry@zoo.dev>
2024-08-08 10:49:16 +00:00
e295f82495 Cut release v0.24.10 (#3323) 2024-08-08 12:01:12 +10:00
0b9cf2dc21 Revert "Revert "Revert "Cut release v0.24.10" (#3313)" (#3321)" (#3322)
This reverts commit eb58507e93.
2024-08-08 10:50:44 +10:00
eb58507e93 Revert "Revert "Cut release v0.24.10" (#3313)" (#3321)
This reverts commit 308a0fb06e.
2024-08-08 10:47:05 +10:00
ca28a5f549 Fix settings derp and app start project theme loading (#3320)
* Fix settings opening and messing up everything

* Set settings aggressively for startup project theme loading

* fmt

---------

Co-authored-by: Lee <lee@Lees-Mac-mini.local>
2024-08-08 10:44:33 +10:00
6f4bbdb79e Fix tanArcTo (#3318)
* updated tangentArc math

* Add a test case showing tan arc then xLineTo

* Fix compile errors

* Tweaking the math

* Use + on angles

* atan2 outputs radians, not degrees

* Track ccw and center of all tan arcs

* re-sequenced atan2 arcTan

* Remove print statements

* Update the test

* Update kittycad in tauri

* New arc fields

---------

Co-authored-by: Jordan Noone <jordan@kittycad.io>
2024-08-07 18:35:41 -05:00
6773dbe7ff set Playwright actionTimeout to 15 seconds (#3115)
* set playwright actionTimeout to 15 seconds

* enabled screenshot on failure and html reporter to easily see individual test duration

* extend timeout when waiting for console

* removed duplicated html reporter

---------

Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>
Co-authored-by: ryanrosello-og <ry@zoo.dev>
2024-08-08 09:19:07 +10:00
acfc2b47fa increase windows stream ping interval (#3317) 2024-08-08 07:45:42 +10:00
9dc7ff9797 Fix type of sketchGroup function (#3316) 2024-08-07 15:15:22 -04:00
308a0fb06e Revert "Cut release v0.24.10" (#3313)
Revert "Cut release v0.24.10 (#3309)"

This reverts commit 214ae6f512.
2024-08-07 11:47:21 +00:00
214ae6f512 Cut release v0.24.10 (#3309)
Co-authored-by: Frank Noirot <frank@zoo.dev>
2024-08-07 07:13:45 -04:00
8d54fec589 fix test: Basic sketch › code pane open at start (#3188)
* added 1 extra hard coded timeout

* stabilize test code pane open at start

* re-apply changes

---------

Co-authored-by: ryanrosello-og <ry@zoo.dev>
2024-08-07 21:10:58 +10:00
4bfbecd3e7 Don't set firstRender from within a routeLoader (#3311) 2024-08-07 06:21:15 -04:00
dff3848a00 Switch projects fix (#3310)
* switch projects fix

* add comment
2024-08-07 19:47:23 +10:00
f875efab1b Split large (flow-tests) spec file into individual spec file per test suite (#3297)
* split flow tests spec file
---------

Co-authored-by: ryanrosello-og <ry@zoo.dev>
2024-08-07 19:27:32 +10:00
3f082c8222 Persist theme - Reload everything on a disconnect (#3250)
* Reload everything on a disconnect

* fix unit-integration tests

* Further improvements to connection manager; persist theme across reconnects

* Fix up artifactGraph.test

* Actually pass the callback

* Kurt hmmm (#3308)

* kurts attempts

* we're almost sane

* get tests working, praise be

---------

Co-authored-by: 49lf <ircsurfer33@gmail.com>

* typo

---------

Co-authored-by: Kurt Hutten Irev-Dev <k.hutten@protonmail.ch>
2024-08-07 17:11:57 +10:00
e1c45bdb33 Start to rework some of our kcl docs (#3222)
* Start to rework some of our kcl docs

Signed-off-by: Paul R. Tagliamonte <paul@zoo.dev>
2024-08-06 20:27:26 -04:00
5cb5dbd689 artifactGraph snapshot stability (#3305)
* artifactgraph snapshot stability

* clean up

* tweak

* Look at this (photo)Graph *in the voice of Nickelback*

* trigger ci

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-08-07 10:15:08 +10:00
0a8881bc69 badge scale on hover (#3298) 2024-08-07 07:29:16 +10:00
be9438160e bisect docs (#3304) 2024-08-07 06:27:23 +10:00
77b565f781 Add a search bar to the projects/home page (#3301)
* Add a search bar to the projects/home page

* Better hotkey config

* Look at this (photo)Graph *in the voice of Nickelback*

* Re-run CI

* Look at this (photo)Graph *in the voice of Nickelback*

* Re-run CI

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-08-06 16:19:30 -04:00
c843dfad95 Add a little dropdown arrow menu to gizmo with view settings (#3300) 2024-08-06 10:01:55 -04:00
a3ff0a45eb Disable build-test-web's tests on release or nightly build (#3296) 2024-08-06 05:50:49 -04:00
4617ad0fed Cut release v0.24.9 (#3295) 2024-08-06 05:30:34 -04:00
5fa51a2f92 Revert "Cut release v0.24.9"
This reverts commit 4218777afb.
2024-08-06 17:34:11 +10:00
4218777afb Cut release v0.24.9 2024-08-06 17:32:39 +10:00
8b1b462e29 Revert "Cut release v0.24.9 (#3284)" (#3294)
This reverts commit 1c778bf373.
2024-08-06 17:32:18 +10:00
2bc99ba39b reset camera on empty scene (#3293)
* reset camera on empty scene

* tweak numbers

* number tweak again
2024-08-06 17:05:46 +10:00
ffe0da6dcd don't allow edit on sketches with no variable declaration (#3292)
don't allow edit on sketches with no variable decleration
2024-08-06 06:17:30 +00:00
d27afb8c74 Improve docs for x/yLine and x/yLineTo (#3285)
* Improve docs for xLineTo and yLineTo

* Improve docs for xLine and yLine
2024-08-06 14:26:19 +10:00
1c778bf373 Cut release v0.24.9 (#3284)
* Cut release v0.24.9

* Look at this (photo)Graph *in the voice of Nickelback*

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-08-06 13:13:34 +10:00
5df8a943a9 Easy KCL tests with no engine/visuals (#3282)
This new test framework, `no_visuals`, is for testing KCL programs via asserts, not via twenty-twenty visual tests. This is useful for unit-testing small fragments of KCL.

It's easy! All you need to do is:
- Write a KCL file
- Save it under `tests/executor/inputs/no_visuals/foo.kcl`
- Open `no_visuals.rs` and add `gen_test!(foo);`
2024-08-06 02:44:49 +00:00
ab729dbcdb Bump google-github-actions/upload-cloud-storage from 2.1.0 to 2.1.1 (#3272)
Bumps [google-github-actions/upload-cloud-storage](https://github.com/google-github-actions/upload-cloud-storage) from 2.1.0 to 2.1.1.
- [Release notes](https://github.com/google-github-actions/upload-cloud-storage/releases)
- [Changelog](https://github.com/google-github-actions/upload-cloud-storage/blob/main/CHANGELOG.md)
- [Commits](https://github.com/google-github-actions/upload-cloud-storage/compare/v2.1.0...v2.1.1)

---
updated-dependencies:
- dependency-name: google-github-actions/upload-cloud-storage
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-05 10:13:00 -07:00
84865eaed0 Add assertEqual function to KCL stdlib (#3279)
Takes a tolerance, because floating-point imprecision.
2024-08-05 16:31:58 +00:00
543e809739 Add "report a bug" mention to user menu onboarding step (#3278)
* Mention "report a bug" in user menu onboarding step

* Add to a test so we match QAWolf a bit more

* Re-run CI
2024-08-05 08:24:19 -04:00
61b669cf4e github actions Playwright shard execution (#3199)
* add @snapshot tag to all snapshot-tests

* set workers to 1 on CI

* try sharding on google chrome only

* add retry when navigating to the app (on CI)

* reduce ubuntu-cores to 2

* revert runner back to 8 cores

* re-add retry + enable macos

* Reduce timeouts to 30 minutes

* ensure retry is triggered

* removed sharding when retrying failures

* added --pass-with-no-tests

* ensure failure occurs

* revert failure

* use smaller sized runners

* revert back to supported runner size

* revert to og version

* minimize changes

* yarn fmt

* ensure failure

* undo failure

---------

Co-authored-by: ryanrosello-og <ry@zoo.dev>
2024-08-05 21:30:16 +10:00
75f1aaa824 camera breaks on extrude zoom to fit (#3276) 2024-08-05 16:08:30 +10:00
f4848d7dea Jump to error not lint (#3271)
* dont jump to lints

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

* updates

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

* fix

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-08-05 02:44:33 +00:00
a0167f6ba6 Coplanar sketch should have diagnostic error. (#3269)
* artifactMapTweak

* typo
2024-08-05 02:04:53 +00:00
e5a26c42e6 Cut release v0.24.8 (#3263) 2024-08-05 10:32:16 +10:00
9c87b124d9 Jump to error code on badge click (#3262)
* add function to scroll to view

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

* scroll into view on click

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

* add test for jump to code with error

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-08-04 22:59:04 +00:00
21389c089d apply fillets before a shell (#3261)
* add test for fillet and shell

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

* apply fillets before a shell

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-08-04 15:37:40 -07:00
29f57be8c1 editor repaints any errors when rendered (#3260)
* editor repaints any errors when rendered

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

* Update src/lang/KclSingleton.ts

* fix test

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

* fix typo

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>
2024-08-04 15:16:34 -07:00
cd55f07619 Bump serde_json from 1.0.121 to 1.0.122 in /src/wasm-lib (#3235)
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.121 to 1.0.122.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.121...v1.0.122)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-04 12:57:54 -07:00
baf7d3dd9d Add print button (#3133)
* add print button

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

* cleanup

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

* generate more types

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

* add a github action to generate machine api-types

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

* fix

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

* New machine-api types

* actually print on the real machine

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

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* add more

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

* New machine-api types

* get the current machine

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

* New machine-api types

* know when error

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>

* fmt

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

* add fmt

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

* New machine-api types

* empty

* empty

* update machine api

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

* New machine-api types

* empty

* New machine-api types

* emptuy

* no circular deps

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

* New machine-api types

* remove recursive dep

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Jess Frazelle <jessfraz@users.noreply.github.com>
2024-08-04 04:51:30 +00:00
54a9a50969 fix bug when engine returns an error on websocket export (#3256)
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-08-04 02:24:06 +00:00
2830c750fa remove unused timeout (#3254) 2024-08-03 23:10:04 +00:00
d3160cd85a Update machine-api spec (#3253)
YOYO NEW API SPEC!

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-08-03 16:07:42 -07:00
fd1b4c3a32 fix snapshot tests, don't let them silently fail (#3228)
fix snapshots, don't let them silently fail
2024-08-03 22:29:28 +00:00
b0a41c31ac Update machine-api spec (#3252)
YOYO NEW API SPEC!

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-08-03 15:25:02 -07:00
5825ba575c test for default planes in empty scene (#3249)
* test for default planes in empty scene

* fmt

* skit if webkit

* fmt
2024-08-03 21:34:56 +10:00
e5bec2140e ArtifactGraph reThink (PART 3) (#3140)
* adjust engine connection to opt out of webRTC connection

* refactor start and test setup

* add env to unit test

* spell config update

* fix beforeAll order bug

* initial integration of new artifact map with tests passing

* remove old artifact map and clean up

* graph artifact map

* have graph commited

* have graph commited

* remove bad file

* install playwright

* fmt

* commit permissions

* typo

* flesh out tests more

* Look at this (photo)Graph *in the voice of Nickelback*

* multi highlight

* redo image logic

* add in solid 2d data into artifactMap

* fix snapshots

* stabiles graph images

* Look at this (photo)Graph *in the voice of Nickelback*

* tweak tests

* rename blend to edgeCut

* Look at this (photo)Graph *in the voice of Nickelback*

* fix playw tests

* start of artifact map rename to graph

* rename file

* rename test

* rename clearup

* comments

* docs

* docs proof read

* few tweaks here and there

* typos

* delete get parent logic

* nit, combine if statements

* remove unused param

* fix silly test bug

* rename surfId to sufaceId

* rename types

* update comments

* add comment

* add extra check

* Look at this (photo)Graph *in the voice of Nickelback*

* pull out merge artifact function

* update comments

* fix test

* fmt

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-08-03 18:08:51 +10:00
7bf6bc3048 Fix computed properties of KCL objects (#3246)
* Fix computed properties of KCL objects

Fixes https://github.com/KittyCAD/modeling-app/issues/3201

* Incorporate Jon's feedback
2024-08-02 22:24:00 -07:00
22f9df73ed Update machine-api spec (#3247)
YOYO NEW API SPEC!

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-08-02 21:08:20 -07:00
834472e0a6 Update machine-api spec (#3244)
YOYO NEW API SPEC!

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-08-02 16:13:49 -07:00
bcdf6e314f Cut release v0.24.7 (#3243) 2024-08-02 18:12:58 -04:00
55e9845ade Update machine-api spec (#3242)
YOYO NEW API SPEC!

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-08-02 14:10:52 -07:00
d61cf882c1 show default planes on empty scene (#3237)
* show default planes on empty sceen

* fmt

* remove log

* fix silly click listener bug

* delete old stuff

* test tweak

* Revert "test tweak"

This reverts commit e9cb4ac4b5.

---------

Co-authored-by: Paul Tagliamonte <paul@zoo.dev>
2024-08-02 14:05:35 -07:00
874d19cbfd Re-get the openPanes from localStorage when navigating between projects (#3241)
* Re-get the openPanes from localStorage when navigating between projects

* fmt
2024-08-02 15:39:05 -04:00
9dcc955760 Regression fix: restarting onboarding in desktop app required two attempts (#3240)
* Fixed onboarding modal issue, revealed race

* Remove logs

* Make common reset onboarding code path
2024-08-02 15:38:39 -04:00
9b594efe53 Have links clickable within tooltips without clicking content below them (#3204)
* Have links clickable within tooltips without clicking content below them

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* Re-run CI

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* Re-run CI

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-08-02 12:25:57 -04:00
7b9f40c4cb Fix link to keybindings tab in help menu on Windows (#3236) 2024-08-02 10:25:42 -04:00
81b79da90f fix cryptic error (#3234)
* fix cryptic error

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

* Update types.rs

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-08-01 19:40:22 -07:00
2ad5a880fa rm error pane show badge on code (#3233)
* rm error pane show badge on code

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

* fix playwirght

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

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* empty

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-08-01 19:40:16 -07:00
b57a9ba54c open file with url encoded space (#3231)
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-08-01 17:53:42 -07:00
b32f5c1d4e add html report to playwright artifact (#3229)
add htlm report to playwright artifact
2024-08-01 22:09:40 +00:00
b6d4cc7a4e Update machine-api spec (#3226)
YOYO NEW API SPEC!

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-08-01 14:49:01 -07:00
43a34b191e Upgrade to clap 4.5.13 to fix build error (#3223) 2024-08-01 17:03:05 +00:00
19a93e8deb Cut release v0.24.6 (#3214) 2024-08-01 09:47:25 -04:00
b8c623e1ec sure up test (#3220) 2024-08-01 12:30:08 +00:00
4006c28479 make test fail fast (#3218) 2024-08-01 11:56:59 +00:00
8c932fdb8d unpause vid in next event loop (#3219)
* unpause vid in next event loop

* fmt
2024-08-01 10:53:44 +00:00
a74c715c01 fix 'Engine disconnect & reconnect in sketch mode' test (#3215)
* fix 'Engine disconnect & reconnect in sketch mode' test

* tweak

* tweak 2
2024-08-01 18:39:24 +10:00
1ac39d95f2 Bug fix: prevent KCL error due to colliding AST execution on project switch (#3205)
* Only run "Execute AST" action if defaultUnit setting changes

* A little more logging and catching anywhere we call video.play()
2024-08-01 05:40:14 +00:00
41b1ec94fa Bump clap from 4.5.11 to 4.5.13 in /src/wasm-lib (#3210)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.11 to 4.5.13.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.11...v4.5.13)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-31 22:18:40 -07:00
525c803888 Bump toml from 0.8.17 to 0.8.19 in /src/wasm-lib (#3212)
Bumps [toml](https://github.com/toml-rs/toml) from 0.8.17 to 0.8.19.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v0.8.17...toml-v0.8.19)

---
updated-dependencies:
- dependency-name: toml
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-31 22:18:29 -07:00
2ee1c78aad Add a system for badges to appear on pane buttons (#3208) 2024-08-01 13:29:24 +10:00
dc21034b86 Move KCL programs into their own files (#3200)
* Move KCL programs into their own files

* Move even more
2024-07-31 15:07:56 -05:00
1684786659 Update machine-api spec (#3202)
YOYO NEW API SPEC!

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-07-31 11:19:36 -07:00
12505b4398 Bump toml from 0.8.16 to 0.8.17 in /src/wasm-lib (#3198)
Bumps [toml](https://github.com/toml-rs/toml) from 0.8.16 to 0.8.17.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v0.8.16...toml-v0.8.17)

---
updated-dependencies:
- dependency-name: toml
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-31 11:08:07 -07:00
115f2fdea2 Simplify the KCL stdlib test codegen (#3196)
This will ensure that the KCL snapshot tests all use the same logic, whether they're in `tests/executor/main.rs` or in the KCL stdlib modules.
2024-07-31 09:54:46 -05:00
0df28abc4b Update machine-api spec (#3197)
YOYO NEW API SPEC!

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-07-30 19:57:57 -07:00
1e07ea4986 Use better names for engine/KCL tests (#3193)
'serial_test' isn't actually accurate. Two of these tests run in parallel
now. So I renamed it 'kcl_test' as that's what it's actually doing.

In the nextest config, I changed the label from 'serial-integration' to
'uses-engine' because the former isn't true, and also doesn't explain
_why_ it's being limited. The new name explains why we're limiting the
number of tests that can run in parallel.
2024-07-30 16:12:01 -05:00
275 changed files with 21663 additions and 12563 deletions

View File

@ -1,3 +1,3 @@
[codespell]
ignore-words-list: crate,everytime,inout,co-ordinate,ot,nwo,absolutey,atleast,ue
ignore-words-list: crate,everytime,inout,co-ordinate,ot,nwo,absolutey,atleast,ue,afterall
skip: **/target,node_modules,build,**/Cargo.lock,./docs/kcl/*.md,./src-tauri/gen/schemas

View File

@ -20,6 +20,11 @@ concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
permissions:
contents: write
pull-requests: write
actions: read
jobs:
check-format:
runs-on: 'ubuntu-latest'
@ -84,8 +89,43 @@ jobs:
- run: yarn build:wasm
- run: yarn simpleserver:ci
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
- name: Install Chromium Browser
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
run: yarn playwright install chromium --with-deps
- name: run unit tests
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
run: yarn test:nowatch
env:
VITE_KC_DEV_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
- name: check for changes
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
id: git-check
run: |
git add src/lang/std/artifactMapGraphs
if git status src/lang/std/artifactMapGraphs | grep -q "Changes to be committed"
then echo "modified=true" >> $GITHUB_OUTPUT
else echo "modified=false" >> $GITHUB_OUTPUT
fi
- name: Commit changes, if any
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' && steps.git-check.outputs.modified == 'true' }}
run: |
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git remote set-url origin https://${{ github.actor }}:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git
git fetch origin
echo ${{ github.head_ref }}
git checkout ${{ github.head_ref }}
# TODO when webkit works on ubuntu remove the os part of the commit message
git commit -am "Look at this (photo)Graph *in the voice of Nickelback*" || true
git push
git push origin ${{ github.head_ref }}
- run: yarn test:nowatch
prepare-json-files:
@ -495,7 +535,7 @@ jobs:
project_id: kittycadapi
- name: Upload release files to public bucket
uses: google-github-actions/upload-cloud-storage@v2.1.0
uses: google-github-actions/upload-cloud-storage@v2.1.1
with:
path: artifact
glob: '*/Zoo*'
@ -503,13 +543,13 @@ jobs:
destination: ${{ env.BUCKET_DIR }}/${{ env.VERSION }}
- name: Upload update endpoint to public bucket
uses: google-github-actions/upload-cloud-storage@v2.1.0
uses: google-github-actions/upload-cloud-storage@v2.1.1
with:
path: last_update.json
destination: ${{ env.BUCKET_DIR }}
- name: Upload download endpoint to public bucket
uses: google-github-actions/upload-cloud-storage@v2.1.0
uses: google-github-actions/upload-cloud-storage@v2.1.1
with:
path: last_download.json
destination: ${{ env.BUCKET_DIR }}

View File

@ -0,0 +1,49 @@
name: generate machine-api types
on:
pull_request:
paths:
- 'openapi/machine-api.json'
- '.github/workflows/generate-machine-api-types.yml'
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
permissions:
contents: write
jobs:
generate:
runs-on: 'ubuntu-latest'
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'yarn'
- run: yarn install
- run: yarn generate:machine-api
- run: yarn fmt
- name: check for changes
id: git-check
run: |
git add .
if git status | grep -q "Changes to be committed"
then echo "modified=true" >> $GITHUB_OUTPUT
else echo "modified=false" >> $GITHUB_OUTPUT
fi
- name: Commit changes, if any
if: steps.git-check.outputs.modified == 'true'
run: |
git add .
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git remote set-url origin https://${{ github.actor }}:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git
git fetch origin
echo ${{ github.head_ref }}
git checkout ${{ github.head_ref }}
git commit -am "New machine-api types" || true
git push
git push origin ${{ github.head_ref }}

View File

@ -34,8 +34,13 @@ jobs:
- 'src/wasm-lib/**'
playwright-ubuntu:
timeout-minutes: 60
timeout-minutes: 30
runs-on: ubuntu-latest-8-cores
strategy:
fail-fast: false
matrix:
shardIndex: [1, 2, 3, 4]
shardTotal: [4]
needs: check-rust-changes
steps:
- name: Tune GitHub-hosted runner network
@ -106,13 +111,19 @@ jobs:
- name: build web
run: yarn build:local
- name: Run ubuntu/chrome snapshots
continue-on-error: true
run: |
yarn playwright test --project="Google Chrome" --update-snapshots e2e/playwright/snapshot-tests.spec.ts
yarn playwright test --project="Google Chrome" --retries="3" --update-snapshots --grep=@snapshot --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
env:
CI: true
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
snapshottoken: ${{ secrets.KITTYCAD_API_TOKEN }}
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report-ubuntu-snapshot-${{ matrix.shardIndex }}-${{ github.sha }}
path: playwright-report/
retention-days: 30
overwrite: true
- name: Clean up test-results
if: always()
continue-on-error: true
@ -143,7 +154,7 @@ jobs:
- uses: actions/upload-artifact@v4
if: steps.git-check.outputs.modified == 'true'
with:
name: playwright-report-ubuntu-${{ github.sha }}
name: playwright-report-ubuntu-${{ matrix.shardIndex }}-${{ github.sha }}
path: playwright-report/
retention-days: 30
# if have previous run results, use them
@ -151,7 +162,7 @@ jobs:
if: always()
continue-on-error: true
with:
name: test-results-ubuntu-${{ github.sha }}
name: test-results-ubuntu-${{ matrix.shardIndex }}-${{ github.sha }}
path: test-results/
- name: Run ubuntu/chrome flow (with retries)
id: retry
@ -160,7 +171,7 @@ jobs:
if [[ ! -f "test-results/.last-run.json" ]]; then
# if no last run artifact, than run plawright normally
echo "run playwright normally"
yarn playwright test --project="Google Chrome" e2e/playwright/flow-tests.spec.ts || true
yarn playwright test --project="Google Chrome" --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} --grep-invert=@snapshot || true
# # send to axiom
node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
fi
@ -175,7 +186,7 @@ jobs:
if [[ $failed_tests -gt 0 ]]; then
echo "retried=true" >>$GITHUB_OUTPUT
echo "run playwright with last failed tests and retry $retry"
yarn playwright test --project="Google Chrome" --last-failed e2e/playwright/flow-tests.spec.ts || true
yarn playwright test --project="Google Chrome" --last-failed --grep-invert=@snapshot || true
# send to axiom
node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
retry=$((retry + 1))
@ -210,21 +221,26 @@ jobs:
- uses: actions/upload-artifact@v4
if: always()
with:
name: test-results-ubuntu-${{ github.sha }}
name: test-results-ubuntu-${{ matrix.shardIndex }}-${{ github.sha }}
path: test-results/
retention-days: 30
overwrite: true
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report-ubuntu-${{ github.sha }}
name: playwright-report-ubuntu-${{ matrix.shardIndex }}-${{ github.sha }}
path: playwright-report/
retention-days: 30
overwrite: true
playwright-macos:
timeout-minutes: 60
runs-on: macos-14-large
timeout-minutes: 30
runs-on: macos-14
strategy:
fail-fast: false
matrix:
shardIndex: [1, 2, 3, 4]
shardTotal: [4]
needs: check-rust-changes
steps:
- name: Tune GitHub-hosted runner network
@ -300,7 +316,7 @@ jobs:
if: ${{ always() }}
continue-on-error: true
with:
name: test-results-macos-${{ github.sha }}
name: test-results-macos-${{ matrix.shardIndex }}-${{ github.sha }}
path: test-results/
- name: Run macos/safari flow (with retries)
id: retry
@ -309,7 +325,7 @@ jobs:
if [[ ! -f "test-results/.last-run.json" ]]; then
# if no last run artifact, than run plawright normally
echo "run playwright normally"
yarn playwright test --project="webkit" e2e/playwright/flow-tests.spec.ts || true
yarn playwright test --project="webkit" --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} --grep-invert=@snapshot || true
# # send to axiom
node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
fi
@ -324,7 +340,7 @@ jobs:
if [[ $failed_tests -gt 0 ]]; then
echo "retried=true" >>$GITHUB_OUTPUT
echo "run playwright with last failed tests and retry $retry"
yarn playwright test --project="webkit" --last-failed e2e/playwright/flow-tests.spec.ts || true
yarn playwright test --project="webkit" --last-failed --grep-invert=@snapshot || true
# send to axiom
node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
retry=$((retry + 1))
@ -354,15 +370,14 @@ jobs:
- uses: actions/upload-artifact@v4
if: ${{ always() }}
with:
name: test-results-macos-${{ github.sha }}
name: test-results-macos-${{ matrix.shardIndex }}-${{ github.sha }}
path: test-results/
retention-days: 30
overwrite: true
- uses: actions/upload-artifact@v4
if: ${{ always() }}
with:
name: playwright-report-macos-${{ github.sha }}
name: playwright-report-macos-${{ matrix.shardIndex }}-${{ github.sha }}
path: playwright-report/
retention-days: 30
overwrite: true

2
.gitignore vendored
View File

@ -39,6 +39,7 @@ src/wasm-lib/grackle/test_json_output
e2e/playwright/playwright-secrets.env
e2e/playwright/temp1.png
e2e/playwright/temp2.png
e2e/playwright/temp3.png
# exports from snapshot-tests.spec.ts "exports of each format should work"
e2e/playwright/export-snapshots/*
!e2e/playwright/export-snapshots/*.png
@ -48,6 +49,7 @@ e2e/playwright/export-snapshots/*
/playwright-report/
/blob-report/
/playwright/.cache/
/src/lang/std/artifactMapCache
## generated files

View File

@ -110,6 +110,17 @@ Note that these became separate apps on Macos, so make sure you open the right o
<img width="1232" alt="image (1)" src="https://user-images.githubusercontent.com/29681384/211947073-e76b4933-bef5-4636-bc4d-e930ac8e290f.png">
## Checking out commits / Bisecting
Which commands from setup are one off vs need to be run every time?
The following will need to be run when checking out a new commit and guarantees the build is not stale:
```bash
yarn install
yarn build:wasm-dev # or yarn build:wasm for slower but more production-like build
yarn start # or yarn build:local && yarn serve for slower but more production-like build
```
## Before submitting a PR
Before you submit a contribution PR to this repo, please ensure that:

View File

@ -1,10 +1,10 @@
---
title: "abs"
excerpt: "Computes the absolute value of a number."
excerpt: "Compute the absolute value of a number."
layout: manual
---
Computes the absolute value of a number.
Compute the absolute value of a number.

View File

@ -1,10 +1,10 @@
---
title: "acos"
excerpt: "Computes the arccosine of a number (in radians)."
excerpt: "Compute the arccosine of a number (in radians)."
layout: manual
---
Computes the arccosine of a number (in radians).
Compute the arccosine of a number (in radians).

View File

@ -1,10 +1,10 @@
---
title: "angleToMatchLengthX"
excerpt: "Returns the angle to match the given length for x."
excerpt: "Compute the angle (in degrees) in o"
layout: manual
---
Returns the angle to match the given length for x.
Compute the angle (in degrees) in o
@ -253,6 +253,10 @@ const extrusion = extrude(5, sketch001)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -458,6 +462,10 @@ const extrusion = extrude(5, sketch001)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.

View File

@ -257,6 +257,10 @@ const extrusion = extrude(5, sketch001)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -462,6 +466,10 @@ const extrusion = extrude(5, sketch001)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.

View File

@ -1,12 +1,12 @@
---
title: "angledLine"
excerpt: "Draw an angled line."
excerpt: "Draw a line segment relative to the current origin using the polar"
layout: manual
---
Draw an angled line.
Draw a line segment relative to the current origin using the polar
measure of some angle and distance.
```js
angledLine(data: AngledLineData, sketch_group: SketchGroup, tag?: TagDeclarator) -> SketchGroup
@ -172,6 +172,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -377,6 +381,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -584,6 +592,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -789,6 +801,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.

View File

@ -1,12 +1,12 @@
---
title: "angledLineOfXLength"
excerpt: "Draw an angled line of a given x length."
excerpt: "Create a line segment from the current 2-dimensional sketch origin"
layout: manual
---
Draw an angled line of a given x length.
Create a line segment from the current 2-dimensional sketch origin
along some angle (in degrees) for some relative length in the 'x' dimension.
```js
angledLineOfXLength(data: AngledLineData, sketch_group: SketchGroup, tag?: TagDeclarator) -> SketchGroup
@ -171,6 +171,10 @@ const extrusion = extrude(10, sketch001)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -376,6 +380,10 @@ const extrusion = extrude(10, sketch001)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -583,6 +591,10 @@ const extrusion = extrude(10, sketch001)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -788,6 +800,10 @@ const extrusion = extrude(10, sketch001)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.

View File

@ -1,12 +1,12 @@
---
title: "angledLineOfYLength"
excerpt: "Draw an angled line of a given y length."
excerpt: "Create a line segment from the current 2-dimensional sketch origin"
layout: manual
---
Draw an angled line of a given y length.
Create a line segment from the current 2-dimensional sketch origin
along some angle (in degrees) for some relative length in the 'y' dimension.
```js
angledLineOfYLength(data: AngledLineData, sketch_group: SketchGroup, tag?: TagDeclarator) -> SketchGroup
@ -173,6 +173,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -378,6 +382,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -585,6 +593,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -790,6 +802,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.

View File

@ -1,12 +1,12 @@
---
title: "angledLineThatIntersects"
excerpt: "Draw an angled line that intersects with a given line."
excerpt: "Draw an angled line from the current origin, constructing a line segment"
layout: manual
---
Draw an angled line that intersects with a given line.
Draw an angled line from the current origin, constructing a line segment
such that the newly created line intersects the desired target line segment.
```js
angledLineThatIntersects(data: AngledLineThatIntersectsData, sketch_group: SketchGroup, tag?: TagDeclarator) -> SketchGroup
@ -265,6 +265,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -470,6 +474,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -677,6 +685,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -882,6 +894,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.

View File

@ -1,12 +1,12 @@
---
title: "angledLineToX"
excerpt: "Draw an angled line to a given x coordinate."
excerpt: "Create a line segment from the current 2-dimensional sketch origin"
layout: manual
---
Draw an angled line to a given x coordinate.
Create a line segment from the current 2-dimensional sketch origin
along some angle (in degrees) for some length, ending at the provided value in the 'x' dimension.
```js
angledLineToX(data: AngledLineToData, sketch_group: SketchGroup, tag?: TagDeclarator) -> SketchGroup
@ -170,6 +170,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -375,6 +379,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -582,6 +590,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -787,6 +799,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.

View File

@ -1,12 +1,12 @@
---
title: "angledLineToY"
excerpt: "Draw an angled line to a given y coordinate."
excerpt: "Create a line segment from the current 2-dimensional sketch origin"
layout: manual
---
Draw an angled line to a given y coordinate.
Create a line segment from the current 2-dimensional sketch origin
along some angle (in degrees) for some length, ending at the provided value in the 'y' dimension.
```js
angledLineToY(data: AngledLineToData, sketch_group: SketchGroup, tag?: TagDeclarator) -> SketchGroup
@ -170,6 +170,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -375,6 +379,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -582,6 +590,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -787,6 +799,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.

View File

@ -1,12 +1,14 @@
---
title: "arc"
excerpt: "Draw an arc."
excerpt: "Starting at the current sketch's origin, draw a curved line segment along"
layout: manual
---
Draw an arc.
Starting at the current sketch's origin, draw a curved line segment along
an imaginary circle of the specified radius.
The arc is constructed such that the current position of the sketch is placed along an imaginary circle of the specified radius, at angleStart degrees. The resulting arc is the segment of the imaginary circle from that origin point to angleEnd, radius away from the center of the imaginary circle.
Unless this makes a lot of sense and feels like what you're looking for to construct your shape, you're likely looking for tangentialArc.
```js
arc(data: ArcData, sketch_group: SketchGroup, tag?: TagDeclarator) -> SketchGroup
@ -181,6 +183,10 @@ const exampleSketch = startSketchOn('XZ')
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -386,6 +392,10 @@ const exampleSketch = startSketchOn('XZ')
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -593,6 +603,10 @@ const exampleSketch = startSketchOn('XZ')
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -798,6 +812,10 @@ const exampleSketch = startSketchOn('XZ')
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.

View File

@ -1,10 +1,10 @@
---
title: "asin"
excerpt: "Computes the arcsine of a number (in radians)."
excerpt: "Compute the arcsine of a number (in radians)."
layout: manual
---
Computes the arcsine of a number (in radians).
Compute the arcsine of a number (in radians).

37
docs/kcl/assertEqual.md Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,10 +1,10 @@
---
title: "atan"
excerpt: "Computes the arctangent of a number (in radians)."
excerpt: "Compute the arctangent of a number (in radians)."
layout: manual
---
Computes the arctangent of a number (in radians).
Compute the arctangent of a number (in radians).

View File

@ -1,12 +1,12 @@
---
title: "bezierCurve"
excerpt: "Draw a bezier curve."
excerpt: "Draw a smooth, continuous, curved line segment from the current origin to"
layout: manual
---
Draw a bezier curve.
Draw a smooth, continuous, curved line segment from the current origin to
the desired (x, y), using a number of control points to shape the curve's shape.
```js
bezierCurve(data: BezierData, sketch_group: SketchGroup, tag?: TagDeclarator) -> SketchGroup
@ -176,6 +176,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -381,6 +385,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -588,6 +596,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -793,6 +805,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.

View File

@ -1,10 +1,10 @@
---
title: "ceil"
excerpt: "Computes the smallest integer greater than or equal to a number."
excerpt: "Compute the smallest integer greater than or equal to a number."
layout: manual
---
Computes the smallest integer greater than or equal to a number.
Compute the smallest integer greater than or equal to a number.

View File

@ -1,12 +1,12 @@
---
title: "chamfer"
excerpt: "Create chamfers on tagged paths."
excerpt: "Cut a straight transitional edge along a tagged path."
layout: manual
---
Create chamfers on tagged paths.
Cut a straight transitional edge along a tagged path.
Chamfer is similar in function and use to a fillet, except a fillet will blend the transition along an edge, rather than cut a sharp, straight transitional edge.
```js
chamfer(data: ChamferData, extrude_group: ExtrudeGroup, tag?: TagDeclarator) -> ExtrudeGroup
@ -398,6 +398,10 @@ const mountingPlate = extrude(thickness, mountingPlateSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -798,6 +802,10 @@ const mountingPlate = extrude(thickness, mountingPlateSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.

View File

@ -1,12 +1,12 @@
---
title: "circle"
excerpt: "Sketch a circle."
excerpt: "Construct a 2-dimensional circle, of the specified radius, centered at"
layout: manual
---
Sketch a circle.
Construct a 2-dimensional circle, of the specified radius, centered at
the provided (x, y) origin point.
```js
circle(center: [number], radius: number, sketch_surface_or_group: SketchSurfaceOrGroup, tag?: TagDeclarator) -> SketchGroup
@ -169,6 +169,10 @@ const example = extrude(5, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -541,6 +545,10 @@ const example = extrude(5, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -748,6 +756,10 @@ const example = extrude(5, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -953,6 +965,10 @@ const example = extrude(5, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.

View File

@ -1,12 +1,12 @@
---
title: "close"
excerpt: "Close the current sketch."
excerpt: "Construct a line segment from the current origin back to the profile's"
layout: manual
---
Close the current sketch.
Construct a line segment from the current origin back to the profile's
origin, ensuring the resulting 2-dimensional sketch is not open-ended.
```js
close(sketch_group: SketchGroup, tag?: TagDeclarator) -> SketchGroup
@ -171,6 +171,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -376,6 +380,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -583,6 +591,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -788,6 +800,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.

View File

@ -1,10 +1,10 @@
---
title: "cos"
excerpt: "Computes the cosine of a number (in radians)."
excerpt: "Compute the cosine of a number (in radians)."
layout: manual
---
Computes the cosine of a number (in radians).
Compute the cosine of a number (in radians).

View File

@ -1,12 +1,12 @@
---
title: "extrude"
excerpt: "Extrudes by a given amount."
excerpt: "Extend a 2-dimensional sketch through a third dimension in order to"
layout: manual
---
Extrudes by a given amount.
Extend a 2-dimensional sketch through a third dimension in order to
create new 3-dimensional volume, or if extruded into an existing volume, cut into an existing solid.
```js
extrude(length: number, sketch_group_set: SketchGroupSet) -> ExtrudeGroupSet
@ -196,6 +196,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -402,6 +406,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -728,6 +736,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.

View File

@ -1,12 +1,12 @@
---
title: "fillet"
excerpt: "Create fillets on tagged paths."
excerpt: "Blend a transitional edge along a tagged path, smoothing the sharp edge."
layout: manual
---
Create fillets on tagged paths.
Blend a transitional edge along a tagged path, smoothing the sharp edge.
Fillet is similar in function and use to a chamfer, except a chamfer will cut a sharp transition along an edge while fillet will smoothly blend the transition.
```js
fillet(data: FilletData, extrude_group: ExtrudeGroup, tag?: TagDeclarator) -> ExtrudeGroup
@ -398,6 +398,10 @@ const mountingPlate = extrude(thickness, mountingPlateSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -798,6 +802,10 @@ const mountingPlate = extrude(thickness, mountingPlateSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.

View File

@ -1,10 +1,10 @@
---
title: "floor"
excerpt: "Computes the largest integer less than or equal to a number."
excerpt: "Compute the largest integer less than or equal to a number."
layout: manual
---
Computes the largest integer less than or equal to a number.
Compute the largest integer less than or equal to a number.

View File

@ -299,6 +299,10 @@ const part001 = startSketchOn('XY')
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -690,6 +694,10 @@ const part001 = startSketchOn('XY')
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.

View File

@ -1,10 +1,10 @@
---
title: "hole"
excerpt: "Use a sketch to cut a hole in another sketch."
excerpt: "Use a 2-dimensional sketch to cut a hole in another 2-dimensional sketch."
layout: manual
---
Use a sketch to cut a hole in another sketch.
Use a 2-dimensional sketch to cut a hole in another 2-dimensional sketch.
@ -182,6 +182,10 @@ const example = extrude(1, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -388,6 +392,10 @@ const example = extrude(1, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -586,6 +594,10 @@ const example = extrude(1, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -791,6 +803,10 @@ const example = extrude(1, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -989,6 +1005,10 @@ const example = extrude(1, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -1194,6 +1214,10 @@ const example = extrude(1, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.

View File

@ -6,8 +6,8 @@ layout: manual
Import a CAD file.
For formats lacking unit data (STL, OBJ, PLY), the default import unit is millimeters. Otherwise you can specify the unit by passing in the options parameter. If you import a gltf file, we will try to find the bin file and import it as well.
Import paths are relative to the current project directory. This only works in the desktop app not in browser.
For formats lacking unit data (such as STL, OBJ, or PLY files), the default unit of measurement is millimeters. Alternatively you may specify the unit by passing your desired measurement unit in the options parameter. When importing a GLTF file, the bin file will be imported as well. Import paths are relative to the current project directory.
Note: The import command currently only works when using the native Modeling App.
```js
import(file_path: String, options?: ImportFormat) -> ImportedGeometry

View File

@ -21,6 +21,7 @@ layout: manual
* [`arc`](kcl/arc)
* [`asin`](kcl/asin)
* [`assert`](kcl/assert)
* [`assertEqual`](kcl/assertEqual)
* [`assertGreaterThan`](kcl/assertGreaterThan)
* [`assertGreaterThanOrEq`](kcl/assertGreaterThanOrEq)
* [`assertLessThan`](kcl/assertLessThan)

View File

@ -1,10 +1,10 @@
---
title: "int"
excerpt: "Converts a number to an integer."
excerpt: "Convert a number to an integer."
layout: manual
---
Converts a number to an integer.
Convert a number to an integer.
Callers should use floor(), ceil(), or other rounding function first if they care about how numbers with fractional parts are converted. If the number has a fractional part, it's truncated, moving the number towards zero.
If the number is NaN or has a magnitude, either positive or negative, that is too large to fit into the internal integer representation, the result is a runtime error.

File diff suppressed because one or more lines are too long

View File

@ -1,12 +1,12 @@
---
title: "lastSegY"
excerpt: "Returns the last segment of y."
excerpt: "Extract the 'y' axis value of the last line segment in the provided 2-d"
layout: manual
---
Returns the last segment of y.
Extract the 'y' axis value of the last line segment in the provided 2-d
sketch.
```js
lastSegY(sketch_group: SketchGroup) -> number
@ -162,6 +162,10 @@ const example = extrude(5, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -367,6 +371,10 @@ const example = extrude(5, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.

View File

@ -1,10 +1,10 @@
---
title: "legAngX"
excerpt: "Returns the angle of the given leg for x."
excerpt: "Compute the angle of the given leg for x."
layout: manual
---
Returns the angle of the given leg for x.
Compute the angle of the given leg for x.

View File

@ -1,10 +1,10 @@
---
title: "legAngY"
excerpt: "Returns the angle of the given leg for y."
excerpt: "Compute the angle of the given leg for y."
layout: manual
---
Returns the angle of the given leg for y.
Compute the angle of the given leg for y.

View File

@ -1,10 +1,10 @@
---
title: "legLen"
excerpt: "Returns the length of the given leg."
excerpt: "Compute the length of the given leg."
layout: manual
---
Returns the length of the given leg.
Compute the length of the given leg.

View File

@ -1,12 +1,12 @@
---
title: "line"
excerpt: "Draw a line."
excerpt: "Draw a line relative to the current origin to a specified (x, y) away"
layout: manual
---
Draw a line.
Draw a line relative to the current origin to a specified (x, y) away
from the current position.
```js
line(delta: [number], sketch_group: SketchGroup, tag?: TagDeclarator) -> SketchGroup
@ -175,6 +175,10 @@ const example = extrude(5, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -380,6 +384,10 @@ const example = extrude(5, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -587,6 +595,10 @@ const example = extrude(5, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -792,6 +804,10 @@ const example = extrude(5, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.

View File

@ -1,10 +1,10 @@
---
title: "lineTo"
excerpt: "Draw a line to a point."
excerpt: "Draw a line from the current origin to some absolute (x, y) point."
layout: manual
---
Draw a line to a point.
Draw a line from the current origin to some absolute (x, y) point.
@ -162,6 +162,10 @@ const example = extrude(5, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -367,6 +371,10 @@ const example = extrude(5, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -574,6 +582,10 @@ const example = extrude(5, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -779,6 +791,10 @@ const example = extrude(5, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.

View File

@ -1,10 +1,10 @@
---
title: "ln"
excerpt: "Computes the natural logarithm of the number."
excerpt: "Compute the natural logarithm of the number."
layout: manual
---
Computes the natural logarithm of the number.
Compute the natural logarithm of the number.

View File

@ -1,10 +1,10 @@
---
title: "log"
excerpt: "Computes the logarithm of the number with respect to an arbitrary base."
excerpt: "Compute the logarithm of the number with respect to an arbitrary base."
layout: manual
---
Computes the logarithm of the number with respect to an arbitrary base.
Compute the logarithm of the number with respect to an arbitrary base.
The result might not be correctly rounded owing to implementation details; `log2()` can produce more accurate results for base 2, and `log10()` can produce more accurate results for base 10.

View File

@ -1,10 +1,10 @@
---
title: "log10"
excerpt: "Computes the base 10 logarithm of the number."
excerpt: "Compute the base 10 logarithm of the number."
layout: manual
---
Computes the base 10 logarithm of the number.
Compute the base 10 logarithm of the number.

View File

@ -1,10 +1,10 @@
---
title: "log2"
excerpt: "Computes the base 2 logarithm of the number."
excerpt: "Compute the base 2 logarithm of the number."
layout: manual
---
Computes the base 2 logarithm of the number.
Compute the base 2 logarithm of the number.

View File

@ -1,10 +1,10 @@
---
title: "max"
excerpt: "Computes the maximum of the given arguments."
excerpt: "Compute the maximum of the given arguments."
layout: manual
---
Computes the maximum of the given arguments.
Compute the maximum of the given arguments.

View File

@ -1,10 +1,10 @@
---
title: "min"
excerpt: "Computes the minimum of the given arguments."
excerpt: "Compute the minimum of the given arguments."
layout: manual
---
Computes the minimum of the given arguments.
Compute the minimum of the given arguments.

View File

@ -1,12 +1,12 @@
---
title: "patternCircular2d"
excerpt: "A circular pattern on a 2D sketch."
excerpt: "Repeat a 2-dimensional sketch some number of times along a partial or"
layout: manual
---
A circular pattern on a 2D sketch.
Repeat a 2-dimensional sketch some number of times along a partial or
complete circle some specified number of times. Each object may additionally be rotated along the circle, ensuring orentation of the solid with respect to the center of the circle is maintained.
```js
patternCircular2d(data: CircularPattern2dData, sketch_group_set: SketchGroupSet) -> [SketchGroup]
@ -180,6 +180,10 @@ const example = extrude(1, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -386,6 +390,10 @@ const example = extrude(1, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.

View File

@ -1,12 +1,12 @@
---
title: "patternCircular3d"
excerpt: "A circular pattern on a 3D model."
excerpt: "Repeat a 3-dimensional solid some number of times along a partial or"
layout: manual
---
A circular pattern on a 3D model.
Repeat a 3-dimensional solid some number of times along a partial or
complete circle some specified number of times. Each object may additionally be rotated along the circle, ensuring orentation of the solid with respect to the center of the circle is maintained.
```js
patternCircular3d(data: CircularPattern3dData, extrude_group_set: ExtrudeGroupSet) -> [ExtrudeGroup]
@ -304,6 +304,10 @@ const example = extrude(-5, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.

View File

@ -1,12 +1,12 @@
---
title: "patternLinear2d"
excerpt: "A linear pattern on a 2D sketch."
excerpt: "Repeat a 2-dimensional sketch along some dimension, with a dynamic amount"
layout: manual
---
A linear pattern on a 2D sketch.
Repeat a 2-dimensional sketch along some dimension, with a dynamic amount
of distance between each repetition, some specified number of times.
```js
patternLinear2d(data: LinearPattern2dData, sketch_group_set: SketchGroupSet) -> [SketchGroup]
@ -173,6 +173,10 @@ const example = extrude(1, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -379,6 +383,10 @@ const example = extrude(1, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.

View File

@ -1,12 +1,12 @@
---
title: "patternLinear3d"
excerpt: "A linear pattern on a 3D model."
excerpt: "Repeat a 3-dimensional solid along a linear path, with a dynamic amount"
layout: manual
---
A linear pattern on a 3D model.
Repeat a 3-dimensional solid along a linear path, with a dynamic amount
of distance between each repetition, some specified number of times.
```js
patternLinear3d(data: LinearPattern3dData, extrude_group_set: ExtrudeGroupSet) -> [ExtrudeGroup]
@ -302,6 +302,10 @@ const example = extrude(1, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.

View File

@ -1,12 +1,12 @@
---
title: "patternTransform"
excerpt: "A linear pattern on a 3D solid."
excerpt: "Repeat a 3-dimensional solid by successively applying a transformation (such"
layout: manual
---
A linear pattern on a 3D solid.
Repeat a 3-dimensional solid by successively applying a transformation (such
Each repetition of the pattern can be transformed (e.g. scaled, translated, hidden, etc).
as rotation, scale, translation, visibility) on each repetition.
```js
patternTransform(num_repetitions: u32, transform_function: FunctionParam, extrude_group_set: ExtrudeGroupSet) -> [ExtrudeGroup]
@ -304,6 +304,10 @@ let vase = layer()
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.

View File

@ -1,12 +1,12 @@
---
title: "polar"
excerpt: "Convert from polar/sphere coordinates to cartesian coordinates."
excerpt: "Convert polar/sphere (azimuth, elevation, distance) coordinates to"
layout: manual
---
Convert from polar/sphere coordinates to cartesian coordinates.
Convert polar/sphere (azimuth, elevation, distance) coordinates to
cartesian (x/y/z grid) coordinates.
```js
polar(data: PolarCoordsData) -> [number]

View File

@ -1,10 +1,10 @@
---
title: "pow"
excerpt: "Computes the number to a power."
excerpt: "Compute the number to a power."
layout: manual
---
Computes the number to a power.
Compute the number to a power.

View File

@ -1,12 +1,12 @@
---
title: "profileStart"
excerpt: ""
excerpt: "Extract the provided 2-dimensional sketch group's profile's origin"
layout: manual
---
Extract the provided 2-dimensional sketch group's profile's origin
value.
```js
profileStart(sketch_group: SketchGroup) -> [number]
@ -163,6 +163,10 @@ const sketch001 = startSketchOn('XY')
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -368,6 +372,10 @@ const sketch001 = startSketchOn('XY')
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.

View File

@ -1,12 +1,12 @@
---
title: "profileStartX"
excerpt: ""
excerpt: "Extract the provided 2-dimensional sketch group's profile's origin's 'x'"
layout: manual
---
Extract the provided 2-dimensional sketch group's profile's origin's 'x'
value.
```js
profileStartX(sketch_group: SketchGroup) -> number
@ -158,6 +158,10 @@ const sketch001 = startSketchOn('XY')
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -363,6 +367,10 @@ const sketch001 = startSketchOn('XY')
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.

View File

@ -1,12 +1,12 @@
---
title: "profileStartY"
excerpt: ""
excerpt: "Extract the provided 2-dimensional sketch group's profile's origin's 'y'"
layout: manual
---
Extract the provided 2-dimensional sketch group's profile's origin's 'y'
value.
```js
profileStartY(sketch_group: SketchGroup) -> number
@ -157,6 +157,10 @@ const sketch001 = startSketchOn('XY')
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -362,6 +366,10 @@ const sketch001 = startSketchOn('XY')
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.

View File

@ -1,12 +1,12 @@
---
title: "revolve"
excerpt: "Revolve a sketch around an axis."
excerpt: "Rotate a sketch around some provided axis, creating a solid from its extent."
layout: manual
---
Revolve a sketch around an axis.
Rotate a sketch around some provided axis, creating a solid from its extent.
This, like extrude, is able to create a 3-dimensional solid from a 2-dimensional sketch. However, unlike extrude, this creates a solid by using the extent of the sketch as its revolved around an axis rather than using the extent of the sketch linearly translated through a third dimension.
```js
revolve(data: RevolveData, sketch_group: SketchGroup) -> ExtrudeGroup
@ -379,6 +379,10 @@ uuid |
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -584,6 +588,10 @@ uuid |
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -907,6 +915,10 @@ uuid |
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.

View File

@ -1,10 +1,10 @@
---
title: "segAng"
excerpt: "Returns the angle of the segment."
excerpt: "Compute the angle (in degrees) of the provided line segment."
layout: manual
---
Returns the angle of the segment.
Compute the angle (in degrees) of the provided line segment.

View File

@ -1,10 +1,10 @@
---
title: "segEndX"
excerpt: "Returns the segment end of x."
excerpt: "Compute the ending point of the provided line segment along the 'x' axis."
layout: manual
---
Returns the segment end of x.
Compute the ending point of the provided line segment along the 'x' axis.

View File

@ -1,10 +1,10 @@
---
title: "segEndY"
excerpt: "Returns the segment end of y."
excerpt: "Compute the ending point of the provided line segment along the 'y' axis."
layout: manual
---
Returns the segment end of y.
Compute the ending point of the provided line segment along the 'y' axis.

View File

@ -1,10 +1,10 @@
---
title: "segLen"
excerpt: "Returns the length of the segment."
excerpt: "Compute the length of the provided line segment."
layout: manual
---
Returns the length of the segment.
Compute the length of the provided line segment.

View File

@ -1,12 +1,12 @@
---
title: "shell"
excerpt: "Shell a solid."
excerpt: "Remove volume from a 3-dimensional shape such that a wall of the"
layout: manual
---
Shell a solid.
Remove volume from a 3-dimensional shape such that a wall of the
provided thickness remains, taking volume starting at the provided face, leaving it open in that direction.
```js
shell(data: ShellData, extrude_group: ExtrudeGroup) -> ExtrudeGroup
@ -386,6 +386,10 @@ shell({ faces: ['end'], thickness: 0.25 }, firstSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -777,6 +781,10 @@ shell({ faces: ['end'], thickness: 0.25 }, firstSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.

View File

@ -1,10 +1,10 @@
---
title: "sin"
excerpt: "Computes the sine of a number (in radians)."
excerpt: "Compute the sine of a number (in radians)."
layout: manual
---
Computes the sine of a number (in radians).
Compute the sine of a number (in radians).

View File

@ -1,10 +1,10 @@
---
title: "sqrt"
excerpt: "Computes the square root of a number."
excerpt: "Compute the square root of a number."
layout: manual
---
Computes the square root of a number.
Compute the square root of a number.

View File

@ -1,10 +1,10 @@
---
title: "startProfileAt"
excerpt: "Start a profile at a given point."
excerpt: "Start a new profile at a given point."
layout: manual
---
Start a profile at a given point.
Start a new profile at a given point.
@ -241,6 +241,10 @@ const example = extrude(5, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -540,6 +544,10 @@ const example = extrude(5, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -745,6 +753,10 @@ const example = extrude(5, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.

View File

@ -1,10 +1,10 @@
---
title: "startSketchAt"
excerpt: "Start a sketch at a given point on the 'XY' plane."
excerpt: "Start a new 2-dimensional sketch at a given point on the 'XY' plane."
layout: manual
---
Start a sketch at a given point on the 'XY' plane.
Start a new 2-dimensional sketch at a given point on the 'XY' plane.
@ -188,6 +188,10 @@ const example = extrude(5, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -393,6 +397,10 @@ const example = extrude(5, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.

View File

@ -1,10 +1,10 @@
---
title: "startSketchOn"
excerpt: "Start a sketch on a specific plane or face."
excerpt: "Start a new 2-dimensional sketch on a specific plane or face."
layout: manual
---
Start a sketch on a specific plane or face.
Start a new 2-dimensional sketch on a specific plane or face.
@ -310,6 +310,10 @@ const a1 = startSketchOn({
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -722,6 +726,10 @@ const a1 = startSketchOn({
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,10 @@
---
title: "tan"
excerpt: "Computes the tangent of a number (in radians)."
excerpt: "Compute the tangent of a number (in radians)."
layout: manual
---
Computes the tangent of a number (in radians).
Compute the tangent of a number (in radians).

View File

@ -1,12 +1,13 @@
---
title: "tangentialArc"
excerpt: "Draw an arc."
excerpt: "Starting at the current sketch's origin, draw a curved line segment along"
layout: manual
---
Draw an arc.
Starting at the current sketch's origin, draw a curved line segment along
some part of an imaginary circle of the specified radius.
The arc is constructed such that the last line segment is placed tangent to the imaginary circle of the specified radius. The resulting arc is the segment of the imaginary circle from that tangent point for 'offset' degrees along the imaginary circle.
```js
tangentialArc(data: TangentialArcData, sketch_group: SketchGroup, tag?: TagDeclarator) -> SketchGroup
@ -171,6 +172,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -376,6 +381,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -583,6 +592,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -788,6 +801,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.

View File

@ -1,12 +1,12 @@
---
title: "tangentialArcTo"
excerpt: "Draw an arc."
excerpt: "Starting at the current sketch's origin, draw a curved line segment along"
layout: manual
---
Draw an arc.
Starting at the current sketch's origin, draw a curved line segment along
some part of an imaginary circle until it reaches the desired (x, y) coordinates.
```js
tangentialArcTo(to: [number], sketch_group: SketchGroup, tag?: TagDeclarator) -> SketchGroup
@ -162,6 +162,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -367,6 +371,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -574,6 +582,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -779,6 +791,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.

View File

@ -1,12 +1,12 @@
---
title: "xLine"
excerpt: "Draw a line on the x-axis."
excerpt: "Draw a line relative to the current origin to a specified distance away"
layout: manual
---
Draw a line on the x-axis.
Draw a line relative to the current origin to a specified distance away
from the current position along the 'x' axis.
```js
xLine(length: number, sketch_group: SketchGroup, tag?: TagDeclarator) -> SketchGroup
@ -165,6 +165,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -370,6 +374,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -577,6 +585,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -782,6 +794,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.

View File

@ -1,12 +1,12 @@
---
title: "xLineTo"
excerpt: "Draw a line to a point on the x-axis."
excerpt: "Draw a line parallel to the X axis, that ends at the given X."
layout: manual
---
Draw a line to a point on the x-axis.
Draw a line parallel to the X axis, that ends at the given X.
E.g. if the previous line ended at (1, 1), then xLineTo(4) draws a line from (1, 1) to (4, 1)
```js
xLineTo(to: number, sketch_group: SketchGroup, tag?: TagDeclarator) -> SketchGroup
@ -165,6 +165,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -370,6 +374,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -577,6 +585,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -782,6 +794,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.

View File

@ -1,12 +1,12 @@
---
title: "yLine"
excerpt: "Draw a line on the y-axis."
excerpt: "Draw a line relative to the current origin to a specified distance away"
layout: manual
---
Draw a line on the y-axis.
Draw a line relative to the current origin to a specified distance away
from the current position along the 'y' axis.
```js
yLine(length: number, sketch_group: SketchGroup, tag?: TagDeclarator) -> SketchGroup
@ -163,6 +163,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -368,6 +372,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -575,6 +583,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -780,6 +792,10 @@ const example = extrude(10, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.

View File

@ -1,12 +1,12 @@
---
title: "yLineTo"
excerpt: "Draw a line to a point on the y-axis."
excerpt: "Draw a line parallel to the Y axis, that ends at the given Y."
layout: manual
---
Draw a line to a point on the y-axis.
Draw a line parallel to the Y axis, that ends at the given Y.
E.g. if the previous line ended at (1, 1), then yLineTo(4) draws a line from (1, 1) to (1, 4)
```js
yLineTo(to: number, sketch_group: SketchGroup, tag?: TagDeclarator) -> SketchGroup
@ -161,6 +161,10 @@ const example = extrude(5, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -366,6 +370,10 @@ const example = extrude(5, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -573,6 +581,10 @@ const example = extrude(5, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.
@ -778,6 +790,10 @@ const example = extrude(5, exampleSketch)
type: "TangentialArcTo",
} |
{
// arc's direction
ccw: string,
// the arc's center
center: [number, number],
// The from point.
from: [number, number],
// The tag of the path.

View File

@ -0,0 +1,153 @@
import { test, expect, Page } from '@playwright/test'
import {
getUtils,
TEST_COLORS,
setup,
tearDown,
commonPoints,
PERSIST_MODELING_CONTEXT,
} from './test-utils'
test.beforeEach(async ({ context, page }) => {
await setup(context, page)
})
test.afterEach(async ({ page }, testInfo) => {
await tearDown(page, testInfo)
})
test.setTimeout(120000)
async function doBasicSketch(page: Page, openPanes: string[]) {
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
const PUR = 400 / 37.5 //pixeltoUnitRatio
await u.waitForAuthSkipAppStart()
await u.openDebugPanel()
// If we have the code pane open, we should see the code.
if (openPanes.includes('code')) {
await expect(u.codeLocator).toHaveText(``)
} else {
// Ensure we don't see the code.
await expect(u.codeLocator).not.toBeVisible()
}
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled()
await expect(page.getByRole('button', { name: 'Start Sketch' })).toBeVisible()
// click on "Start Sketch" button
await u.clearCommandLogs()
await page.getByRole('button', { name: 'Start Sketch' }).click()
await page.waitForTimeout(100)
// select a plane
await page.mouse.click(700, 200)
if (openPanes.includes('code')) {
await expect(u.codeLocator).toHaveText(
`const sketch001 = startSketchOn('XZ')`
)
}
await u.closeDebugPanel()
await page.waitForTimeout(1000) // TODO detect animation ending, or disable animation
const startXPx = 600
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
if (openPanes.includes('code')) {
await expect(u.codeLocator)
.toHaveText(`const sketch001 = startSketchOn('XZ')
|> startProfileAt(${commonPoints.startAt}, %)`)
}
await page.waitForTimeout(500)
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
await page.waitForTimeout(500)
if (openPanes.includes('code')) {
await expect(u.codeLocator)
.toHaveText(`const sketch001 = startSketchOn('XZ')
|> startProfileAt(${commonPoints.startAt}, %)
|> line([${commonPoints.num1}, 0], %)`)
}
await page.waitForTimeout(500)
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
if (openPanes.includes('code')) {
await expect(u.codeLocator)
.toHaveText(`const sketch001 = startSketchOn('XZ')
|> startProfileAt(${commonPoints.startAt}, %)
|> line([${commonPoints.num1}, 0], %)
|> line([0, ${commonPoints.num1 + 0.01}], %)`)
} else {
await page.waitForTimeout(500)
}
await page.mouse.click(startXPx, 500 - PUR * 20)
if (openPanes.includes('code')) {
await expect(u.codeLocator)
.toHaveText(`const sketch001 = startSketchOn('XZ')
|> startProfileAt(${commonPoints.startAt}, %)
|> line([${commonPoints.num1}, 0], %)
|> line([0, ${commonPoints.num1 + 0.01}], %)
|> line([-${commonPoints.num2}, 0], %)`)
}
// deselect line tool
await page.getByRole('button', { name: 'Line', exact: true }).click()
await page.waitForTimeout(500)
const line1 = await u.getSegmentBodyCoords(`[data-overlay-index="${0}"]`, 0)
if (openPanes.includes('code')) {
expect(await u.getGreatestPixDiff(line1, TEST_COLORS.WHITE)).toBeLessThan(3)
await expect(
await u.getGreatestPixDiff(line1, [249, 249, 249])
).toBeLessThan(3)
}
// click between first two clicks to get center of the line
await page.mouse.click(startXPx + PUR * 15, 500 - PUR * 10)
await page.waitForTimeout(100)
if (openPanes.includes('code')) {
expect(await u.getGreatestPixDiff(line1, TEST_COLORS.BLUE)).toBeLessThan(3)
await expect(await u.getGreatestPixDiff(line1, [0, 0, 255])).toBeLessThan(3)
}
// hold down shift
await page.keyboard.down('Shift')
// click between the latest two clicks to get center of the line
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 20)
// selected two lines therefore there should be two cursors
if (openPanes.includes('code')) {
await expect(page.locator('.cm-cursor')).toHaveCount(2)
}
await page.getByRole('button', { name: 'Length: open menu' }).click()
await page.getByRole('button', { name: 'Equal Length' }).click()
// Open the code pane.
await u.openKclCodePanel()
await expect(u.codeLocator).toHaveText(`const sketch001 = startSketchOn('XZ')
|> startProfileAt(${commonPoints.startAt}, %)
|> line([${commonPoints.num1}, 0], %, $seg01)
|> line([0, ${commonPoints.num1 + 0.01}], %)
|> angledLine([180, segLen(seg01)], %)`)
}
test.describe('Basic sketch', () => {
test('code pane open at start', async ({ page }) => {
await doBasicSketch(page, ['code'])
})
test('code pane closed at start', async ({ page }) => {
// Load the app with the code panes
await page.addInitScript(async (persistModelingContext) => {
localStorage.setItem(
persistModelingContext,
JSON.stringify({ openPanes: [] })
)
}, PERSIST_MODELING_CONTEXT)
await doBasicSketch(page, [])
})
})

View File

@ -0,0 +1,111 @@
import { test, expect } from '@playwright/test'
import { getUtils, setup, tearDown } from './test-utils'
import { EngineCommand } from 'lang/std/artifactGraph'
import { uuidv4 } from 'lib/utils'
test.beforeEach(async ({ context, page }) => {
await setup(context, page)
})
test.afterEach(async ({ page }, testInfo) => {
await tearDown(page, testInfo)
})
test.describe('Can create sketches on all planes and their back sides', () => {
const sketchOnPlaneAndBackSideTest = async (
page: any,
plane: string,
clickCoords: { x: number; y: number }
) => {
const u = await getUtils(page)
const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
await u.openDebugPanel()
const coord =
plane === '-XY' || plane === '-YZ' || plane === 'XZ' ? -100 : 100
const camCommand: EngineCommand = {
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_look_at',
center: { x: 0, y: 0, z: 0 },
vantage: { x: coord, y: coord, z: coord },
up: { x: 0, y: 0, z: 1 },
},
}
const updateCamCommand: EngineCommand = {
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_get_settings',
},
}
const code = `const sketch001 = startSketchOn('${plane}')
|> startProfileAt([0.9, -1.22], %)`
await u.openDebugPanel()
await u.clearCommandLogs()
await page.getByRole('button', { name: 'Start Sketch' }).click()
await u.sendCustomCmd(camCommand)
await page.waitForTimeout(100)
await u.sendCustomCmd(updateCamCommand)
await u.closeDebugPanel()
await page.mouse.click(clickCoords.x, clickCoords.y)
await page.waitForTimeout(300) // wait for animation
await expect(
page.getByRole('button', { name: 'Line', exact: true })
).toBeVisible()
// draw a line
const startXPx = 600
await u.closeDebugPanel()
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
await expect(page.locator('.cm-content')).toHaveText(code)
await page.getByRole('button', { name: 'Line', exact: true }).click()
await u.openAndClearDebugPanel()
await page.getByRole('button', { name: 'Exit Sketch' }).click()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.clearCommandLogs()
await u.removeCurrentCode()
}
test('XY', async ({ page }) => {
await sketchOnPlaneAndBackSideTest(
page,
'XY',
{ x: 600, y: 388 } // red plane
// { x: 600, y: 400 }, // red plane // clicks grid helper and that causes problems, should fix so that these coords work too.
)
})
test('YZ', async ({ page }) => {
await sketchOnPlaneAndBackSideTest(page, 'YZ', { x: 700, y: 250 }) // green plane
})
test('XZ', async ({ page }) => {
await sketchOnPlaneAndBackSideTest(page, '-XZ', { x: 700, y: 80 }) // blue plane
})
test('-XY', async ({ page }) => {
await sketchOnPlaneAndBackSideTest(page, '-XY', { x: 600, y: 118 }) // back of red plane
})
test('-YZ', async ({ page }) => {
await sketchOnPlaneAndBackSideTest(page, '-YZ', { x: 700, y: 219 }) // back of green plane
})
test('-XZ', async ({ page }) => {
await sketchOnPlaneAndBackSideTest(page, 'XZ', { x: 700, y: 427 }) // back of blue plane
})
})

View File

@ -0,0 +1,219 @@
import { test, expect } from '@playwright/test'
import { getUtils, setup, tearDown } from './test-utils'
import { bracket } from 'lib/exampleKcl'
import { TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW } from './storageStates'
test.beforeEach(async ({ context, page }) => {
await setup(context, page)
})
test.afterEach(async ({ page }, testInfo) => {
await tearDown(page, testInfo)
})
test.describe('Code pane and errors', () => {
test('Typing KCL errors induces a badge on the code pane button', async ({
page,
}) => {
const u = await getUtils(page)
// Load the app with the working starter code
await page.addInitScript((code) => {
localStorage.setItem('persistCode', code)
}, bracket)
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
// wait for execution done
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.closeDebugPanel()
// Ensure no badge is present
const codePaneButtonHolder = page.locator('#code-button-holder')
await expect(codePaneButtonHolder).not.toContainText('notification')
// Delete a character to break the KCL
await u.openKclCodePanel()
await page.getByText('extrude(').click()
await page.keyboard.press('Backspace')
// Ensure that a badge appears on the button
await expect(codePaneButtonHolder).toContainText('notification')
})
test('Opening and closing the code pane will consistently show error diagnostics', async ({
page,
}) => {
const u = await getUtils(page)
// Load the app with the working starter code
await page.addInitScript((code) => {
localStorage.setItem('persistCode', code)
}, bracket)
await page.setViewportSize({ width: 1200, height: 900 })
await u.waitForAuthSkipAppStart()
// wait for execution done
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.closeDebugPanel()
// Ensure we have no errors in the gutter.
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
// Ensure no badge is present
const codePaneButton = page.getByRole('button', { name: 'KCL Code pane' })
const codePaneButtonHolder = page.locator('#code-button-holder')
await expect(codePaneButtonHolder).not.toContainText('notification')
// Delete a character to break the KCL
await u.openKclCodePanel()
await page.getByText('extrude(').click()
await page.keyboard.press('Backspace')
// Ensure that a badge appears on the button
await expect(codePaneButtonHolder).toContainText('notification')
// Ensure we have an error diagnostic.
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
// error text on hover
await page.hover('.cm-lint-marker-error')
await expect(page.getByText('Unexpected token').first()).toBeVisible()
// Close the code pane
await codePaneButton.click()
await page.waitForTimeout(500)
// Ensure that a badge appears on the button
await expect(codePaneButtonHolder).toContainText('notification')
// Ensure we have no errors in the gutter.
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
// Open the code pane
await u.openKclCodePanel()
// Ensure that a badge appears on the button
await expect(codePaneButtonHolder).toContainText('notification')
// Ensure we have an error diagnostic.
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
// error text on hover
await page.hover('.cm-lint-marker-error')
await expect(page.getByText('Unexpected token').first()).toBeVisible()
})
test('When error is not in view you can click the badge to scroll to it', async ({
page,
}) => {
const u = await getUtils(page)
// Load the app with the working starter code
await page.addInitScript((code) => {
localStorage.setItem('persistCode', code)
}, TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW)
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
await page.waitForTimeout(1000)
// Ensure badge is present
const codePaneButtonHolder = page.locator('#code-button-holder')
await expect(codePaneButtonHolder).toContainText('notification')
// Ensure we have no errors in the gutter, since error out of view.
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
// Click the badge.
const badge = page.locator('#code-badge')
await expect(badge).toBeVisible()
await badge.click()
// Ensure we have an error diagnostic.
await expect(page.locator('.cm-lint-marker-error').first()).toBeVisible()
// Hover over the error to see the error message
await page.hover('.cm-lint-marker-error')
await expect(
page
.getByText(
'sketch profile must lie entirely on one side of the revolution axis'
)
.first()
).toBeVisible()
})
test('When error is not in view WITH LINTS you can click the badge to scroll to it', async ({
page,
}) => {
const u = await getUtils(page)
// Load the app with the working starter code
await page.addInitScript((code) => {
localStorage.setItem('persistCode', code)
}, TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW)
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
await page.waitForTimeout(1000)
// Ensure badge is present
const codePaneButtonHolder = page.locator('#code-button-holder')
await expect(codePaneButtonHolder).toContainText('notification')
// Ensure we have no errors in the gutter, since error out of view.
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
// click in the editor to focus it
await page.locator('.cm-content').click()
await page.waitForTimeout(500)
// go to the start of the editor and enter more text which will trigger
// a lint error.
// GO to the start of the editor.
await page.keyboard.press('ArrowUp')
await page.keyboard.press('ArrowUp')
await page.keyboard.press('ArrowUp')
await page.keyboard.press('ArrowUp')
await page.keyboard.press('ArrowUp')
await page.keyboard.press('ArrowUp')
await page.keyboard.press('ArrowUp')
await page.keyboard.press('ArrowUp')
await page.keyboard.press('ArrowUp')
await page.keyboard.press('ArrowUp')
await page.keyboard.press('Home')
await page.keyboard.type('const foo_bar = 1')
await page.waitForTimeout(500)
await page.keyboard.press('Enter')
// ensure we have a lint error
await expect(page.locator('.cm-lint-marker-info').first()).toBeVisible()
// Click the badge.
const badge = page.locator('#code-badge')
await expect(badge).toBeVisible()
await badge.click()
// Ensure we have an error diagnostic.
await expect(page.locator('.cm-lint-marker-error').first()).toBeVisible()
// Hover over the error to see the error message
await page.hover('.cm-lint-marker-error')
await expect(
page
.getByText(
'sketch profile must lie entirely on one side of the revolution axis'
)
.first()
).toBeVisible()
})
})

View File

@ -0,0 +1,363 @@
import { test, expect } from '@playwright/test'
import { getUtils, setup, tearDown } from './test-utils'
import { KCL_DEFAULT_LENGTH } from 'lib/constants'
test.beforeEach(async ({ context, page }) => {
await setup(context, page)
})
test.afterEach(async ({ page }, testInfo) => {
await tearDown(page, testInfo)
})
test.describe('Command bar tests', () => {
test('Extrude from command bar selects extrude line after', async ({
page,
}) => {
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const sketch001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
|> xLine(-20, %)
|> close(%)
`
)
})
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.closeDebugPanel()
// Click the line of code for xLine.
await page.getByText(`close(%)`).click() // TODO remove this and reinstate // await topHorzSegmentClick()
await page.waitForTimeout(100)
await page.getByRole('button', { name: 'Extrude' }).click()
await page.waitForTimeout(100)
await page.keyboard.press('Enter')
await page.waitForTimeout(100)
await page.keyboard.press('Enter')
await page.waitForTimeout(100)
await expect(page.locator('.cm-activeLine')).toHaveText(
`const extrude001 = extrude(${KCL_DEFAULT_LENGTH}, sketch001)`
)
})
test('Fillet from command bar', async ({ page }) => {
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const sketch001 = startSketchOn('XY')
|> startProfileAt([-5, -5], %)
|> line([0, 10], %)
|> line([10, 0], %)
|> line([0, -10], %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
const extrude001 = extrude(-10, sketch001)`
)
})
const u = await getUtils(page)
await page.setViewportSize({ width: 1000, height: 500 })
await u.waitForAuthSkipAppStart()
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.closeDebugPanel()
const selectSegment = () => page.getByText(`line([0, -10], %)`).click()
await selectSegment()
await page.waitForTimeout(100)
await page.getByRole('button', { name: 'Fillet' }).click()
await page.waitForTimeout(100)
await page.keyboard.press('Enter')
await page.waitForTimeout(100)
await page.keyboard.press('Enter')
await page.waitForTimeout(100)
await expect(page.locator('.cm-activeLine')).toContainText(
`fillet({ radius: ${KCL_DEFAULT_LENGTH}, tags: [seg01] }, %)`
)
})
test('Command bar can change a setting, and switch back and forth between arguments', async ({
page,
}) => {
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
const commandBarButton = page.getByRole('button', { name: 'Commands' })
const cmdSearchBar = page.getByPlaceholder('Search commands')
const themeOption = page.getByRole('option', {
name: 'theme',
exact: false,
})
const commandLevelArgButton = page.getByRole('button', { name: 'level' })
const commandThemeArgButton = page.getByRole('button', { name: 'value' })
// This selector changes after we set the setting
let commandOptionInput = page.getByPlaceholder('Select an option')
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled()
// First try opening the command bar and closing it
await page
.getByRole('button', { name: 'Commands', exact: false })
.or(page.getByRole('button', { name: '⌘K' }))
.click()
await expect(cmdSearchBar).toBeVisible()
await page.keyboard.press('Escape')
await expect(cmdSearchBar).not.toBeVisible()
// Now try the same, but with the keyboard shortcut, check focus
await page.keyboard.press('Meta+K')
await expect(cmdSearchBar).toBeVisible()
await expect(cmdSearchBar).toBeFocused()
// Try typing in the command bar
await cmdSearchBar.fill('theme')
await expect(themeOption).toBeVisible()
await themeOption.click()
const themeInput = page.getByPlaceholder('Select an option')
await expect(themeInput).toBeVisible()
await expect(themeInput).toBeFocused()
// Select dark theme
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await expect(page.getByRole('option', { name: 'system' })).toHaveAttribute(
'data-headlessui-state',
'active'
)
await page.keyboard.press('Enter')
// Check the toast appeared
await expect(
page.getByText(`Set theme to "system" for this project`)
).toBeVisible()
// Check that the theme changed
await expect(page.locator('body')).not.toHaveClass(`body-bg dark`)
commandOptionInput = page.getByPlaceholder('system')
// Test case for https://github.com/KittyCAD/modeling-app/issues/2882
await commandBarButton.click()
await cmdSearchBar.focus()
await cmdSearchBar.fill('theme')
await themeOption.click()
await expect(commandThemeArgButton).toBeDisabled()
await commandOptionInput.focus()
await commandOptionInput.fill('lig')
await commandLevelArgButton.click()
await expect(commandLevelArgButton).toBeDisabled()
// Test case for https://github.com/KittyCAD/modeling-app/issues/2881
await commandThemeArgButton.click()
await expect(commandThemeArgButton).toBeDisabled()
await expect(commandLevelArgButton).toHaveText('level: project')
})
test('Command bar keybinding works from code editor and can change a setting', async ({
page,
}) => {
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled()
// Put the cursor in the code editor
await page.locator('.cm-content').click()
// Now try the same, but with the keyboard shortcut, check focus
await page.keyboard.press('Meta+K')
let cmdSearchBar = page.getByPlaceholder('Search commands')
await expect(cmdSearchBar).toBeVisible()
await expect(cmdSearchBar).toBeFocused()
// Try typing in the command bar
await cmdSearchBar.fill('theme')
const themeOption = page.getByRole('option', {
name: 'Settings · app · theme',
})
await expect(themeOption).toBeVisible()
await themeOption.click()
const themeInput = page.getByPlaceholder('Select an option')
await expect(themeInput).toBeVisible()
await expect(themeInput).toBeFocused()
// Select dark theme
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await expect(page.getByRole('option', { name: 'system' })).toHaveAttribute(
'data-headlessui-state',
'active'
)
await page.keyboard.press('Enter')
// Check the toast appeared
await expect(
page.getByText(`Set theme to "system" for this project`)
).toBeVisible()
// Check that the theme changed
await expect(page.locator('body')).not.toHaveClass(`body-bg dark`)
})
test('Can extrude from the command bar', async ({ page }) => {
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const distance = sqrt(20)
const sketch001 = startSketchOn('XZ')
|> startProfileAt([-6.95, 10.98], %)
|> line([25.1, 0.41], %)
|> line([0.73, -20.93], %)
|> line([-23.44, 0.52], %)
|> close(%)
`
)
})
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
// Make sure the stream is up
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled()
await u.clearCommandLogs()
await page.getByRole('button', { name: 'Extrude' }).isEnabled()
let cmdSearchBar = page.getByPlaceholder('Search commands')
await page.keyboard.press('Meta+K')
await expect(cmdSearchBar).toBeVisible()
// Search for extrude command and choose it
await page.getByRole('option', { name: 'Extrude' }).click()
// Assert that we're on the selection step
await expect(page.getByRole('button', { name: 'selection' })).toBeDisabled()
// Select a face
await page.mouse.move(700, 200)
await page.mouse.click(700, 200)
// Assert that we're on the distance step
await expect(
page.getByRole('button', { name: 'distance', exact: false })
).toBeDisabled()
// Assert that the an alternative variable name is chosen,
// since the default variable name is already in use (distance)
await page.getByRole('button', { name: 'Create new variable' }).click()
await expect(page.getByPlaceholder('Variable name')).toHaveValue(
'distance001'
)
const continueButton = page.getByRole('button', { name: 'Continue' })
const submitButton = page.getByRole('button', { name: 'Submit command' })
await continueButton.click()
// Review step and argument hotkeys
await expect(submitButton).toBeEnabled()
await expect(submitButton).toBeFocused()
await submitButton.press('Backspace')
// Assert we're back on the distance step
await expect(
page.getByRole('button', { name: 'distance', exact: false })
).toBeDisabled()
await continueButton.click()
await submitButton.click()
// Check that the code was updated
await u.waitForCmdReceive('extrude')
// Unfortunately this indentation seems to matter for the test
await expect(page.locator('.cm-content')).toHaveText(
`const distance = sqrt(20)
const distance001 = ${KCL_DEFAULT_LENGTH}
const sketch001 = startSketchOn('XZ')
|> startProfileAt([-6.95, 10.98], %)
|> line([25.1, 0.41], %)
|> line([0.73, -20.93], %)
|> line([-23.44, 0.52], %)
|> close(%)
const extrude001 = extrude(distance001, sketch001)`.replace(
/(\r\n|\n|\r)/gm,
''
) // remove newlines
)
})
test('Can switch between sketch tools via command bar', async ({ page }) => {
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
const sketchButton = page.getByRole('button', { name: 'Start Sketch' })
const cmdBarButton = page.getByRole('button', { name: 'Commands' })
const rectangleToolCommand = page.getByRole('option', {
name: 'rectangle',
})
const rectangleToolButton = page.getByRole('button', {
name: 'Corner rectangle',
exact: true,
})
const lineToolCommand = page.getByRole('option', {
name: 'Line',
})
const lineToolButton = page.getByRole('button', {
name: 'Line',
exact: true,
})
const arcToolCommand = page.getByRole('option', { name: 'Tangential Arc' })
const arcToolButton = page.getByRole('button', {
name: 'Tangential Arc',
exact: true,
})
// Start a sketch
await sketchButton.click()
await page.mouse.click(700, 200)
// Switch between sketch tools via the command bar
await expect(lineToolButton).toHaveAttribute('aria-pressed', 'true')
await cmdBarButton.click()
await rectangleToolCommand.click()
await expect(rectangleToolButton).toHaveAttribute('aria-pressed', 'true')
await cmdBarButton.click()
await lineToolCommand.click()
await expect(lineToolButton).toHaveAttribute('aria-pressed', 'true')
// Click in the scene a couple times to draw a line
// so tangential arc is valid
await page.mouse.click(700, 200)
await page.mouse.move(700, 300, { steps: 5 })
await page.mouse.click(700, 300)
// switch to tangential arc via command bar
await cmdBarButton.click()
await arcToolCommand.click()
await expect(arcToolButton).toHaveAttribute('aria-pressed', 'true')
})
})

View File

@ -0,0 +1,519 @@
import { test, expect } from '@playwright/test'
import { getUtils, setup, tearDown } from './test-utils'
test.beforeEach(async ({ context, page }) => {
await setup(context, page)
})
test.afterEach(async ({ page }, testInfo) => {
await tearDown(page, testInfo)
})
test.describe('Copilot ghost text', () => {
test.skip(true, 'Needs to get covered again')
test('completes code in empty file', async ({ page }) => {
const u = await getUtils(page)
// const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
await u.codeLocator.click()
await expect(page.locator('.cm-content')).toHaveText(``)
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
await page.waitForTimeout(500)
await page.keyboard.press('Enter')
await expect(page.locator('.cm-ghostText').first()).toBeVisible()
await expect(page.locator('.cm-content')).toHaveText(
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
)
await expect(page.locator('.cm-ghostText').first()).toHaveText(
`fn cube = (pos, scale) => {`
)
// We should be able to hit Tab to accept the completion.
await page.keyboard.press('Tab')
await expect(page.locator('.cm-content')).toHaveText(
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
)
// Hit enter a few times.
await page.keyboard.press('Enter')
await page.keyboard.press('Enter')
await expect(page.locator('.cm-content')).toHaveText(
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %) `
)
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
})
test.skip('copilot disabled in sketch mode no select plane', async ({
page,
}) => {
const u = await getUtils(page)
// const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
await u.codeLocator.click()
await expect(page.locator('.cm-content')).toHaveText(``)
// Click sketch mode.
await page.getByRole('button', { name: 'Start Sketch' }).click()
await u.codeLocator.click()
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
await page.waitForTimeout(500)
await page.keyboard.press('Enter')
await page.waitForTimeout(500)
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
await expect(page.locator('.cm-content')).toHaveText(``)
// Exit sketch mode.
await page.getByRole('button', { name: 'Exit Sketch' }).click()
await page.waitForTimeout(500)
await u.codeLocator.click()
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
await page.waitForTimeout(500)
await page.keyboard.press('Enter')
await page.waitForTimeout(500)
await page.keyboard.press('Enter')
await expect(page.locator('.cm-content')).toHaveText(
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
)
await expect(page.locator('.cm-ghostText').first()).toHaveText(
`fn cube = (pos, scale) => {`
)
// We should be able to hit Tab to accept the completion.
await page.keyboard.press('Tab')
await expect(page.locator('.cm-content')).toContainText(
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
)
})
test('copilot disabled in sketch mode after selecting plane', async ({
page,
}) => {
const u = await getUtils(page)
// const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
await u.codeLocator.click()
await expect(page.locator('.cm-content')).toHaveText(``)
// Click sketch mode.
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled()
await page.getByRole('button', { name: 'Start Sketch' }).click()
// select a plane
await page.mouse.click(700, 200)
await page.waitForTimeout(700) // wait for animation
await u.codeLocator.click()
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
await page.waitForTimeout(500)
await page.keyboard.press('Enter')
await page.waitForTimeout(500)
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
await expect(page.locator('.cm-content')).toHaveText(
`const sketch001 = startSketchOn('XZ')`
)
// Escape to exit the tool.
await u.openDebugPanel()
await u.closeDebugPanel()
await page.keyboard.press('Escape')
await page.waitForTimeout(500)
await u.codeLocator.click()
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
await page.waitForTimeout(500)
await page.keyboard.press('Enter')
await page.waitForTimeout(500)
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
await expect(page.locator('.cm-content')).toHaveText(
`const sketch001 = startSketchOn('XZ')`
)
// Escape again to exit sketch mode.
await u.openDebugPanel()
await u.closeDebugPanel()
await page.keyboard.press('Escape')
await page.waitForTimeout(500)
await u.codeLocator.click()
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
await page.waitForTimeout(500)
await page.keyboard.press('Enter')
await page.waitForTimeout(500)
await page.keyboard.press('Enter')
await expect(page.locator('.cm-content')).toHaveText(
`const sketch001 = startSketchOn('XZ')fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
)
await expect(page.locator('.cm-ghostText').first()).toHaveText(
`fn cube = (pos, scale) => {`
)
// We should be able to hit Tab to accept the completion.
await page.keyboard.press('Tab')
await expect(page.locator('.cm-content')).toHaveText(
`const sketch001 = startSketchOn('XZ')fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
)
// Hit enter a few times.
await page.keyboard.press('Enter')
await page.keyboard.press('Enter')
await expect(page.locator('.cm-content')).toHaveText(
`const sketch001 = startSketchOn('XZ')fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %) `
)
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
})
test('ArrowUp in code rejects the suggestion', async ({ page }) => {
const u = await getUtils(page)
// const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
await u.codeLocator.click()
await expect(page.locator('.cm-content')).toHaveText(``)
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
await page.waitForTimeout(500)
await page.keyboard.press('Enter')
await expect(page.locator('.cm-ghostText').first()).toBeVisible()
await expect(page.locator('.cm-content')).toHaveText(
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
)
await expect(page.locator('.cm-ghostText').first()).toHaveText(
`fn cube = (pos, scale) => {`
)
// Going elsewhere in the code should hide the ghost text.
await page.keyboard.press('ArrowUp')
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
await expect(page.locator('.cm-content')).toHaveText(``)
})
test('ArrowDown in code rejects the suggestion', async ({ page }) => {
const u = await getUtils(page)
// const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
await u.codeLocator.click()
await expect(page.locator('.cm-content')).toHaveText(``)
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
await page.waitForTimeout(500)
await page.keyboard.press('Enter')
await expect(page.locator('.cm-ghostText').first()).toBeVisible()
await expect(page.locator('.cm-content')).toHaveText(
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
)
await expect(page.locator('.cm-ghostText').first()).toHaveText(
`fn cube = (pos, scale) => {`
)
// Going elsewhere in the code should hide the ghost text.
await page.keyboard.press('ArrowDown')
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
await expect(page.locator('.cm-content')).toHaveText(``)
})
test('ArrowLeft in code rejects the suggestion', async ({ page }) => {
const u = await getUtils(page)
// const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
await u.codeLocator.click()
await expect(page.locator('.cm-content')).toHaveText(``)
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
await page.waitForTimeout(500)
await page.keyboard.press('Enter')
await expect(page.locator('.cm-ghostText').first()).toBeVisible()
await expect(page.locator('.cm-content')).toHaveText(
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
)
await expect(page.locator('.cm-ghostText').first()).toHaveText(
`fn cube = (pos, scale) => {`
)
// Going elsewhere in the code should hide the ghost text.
await page.keyboard.press('ArrowLeft')
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
await expect(page.locator('.cm-content')).toHaveText(``)
})
test('ArrowRight in code rejects the suggestion', async ({ page }) => {
const u = await getUtils(page)
// const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
await u.codeLocator.click()
await expect(page.locator('.cm-content')).toHaveText(``)
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
await page.waitForTimeout(500)
await page.keyboard.press('Enter')
await expect(page.locator('.cm-ghostText').first()).toBeVisible()
await expect(page.locator('.cm-content')).toHaveText(
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
)
await expect(page.locator('.cm-ghostText').first()).toHaveText(
`fn cube = (pos, scale) => {`
)
// Going elsewhere in the code should hide the ghost text.
await page.keyboard.press('ArrowRight')
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
await expect(page.locator('.cm-content')).toHaveText(``)
})
test('Enter in code scoots it down', async ({ page }) => {
const u = await getUtils(page)
// const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
await u.codeLocator.click()
await expect(page.locator('.cm-content')).toHaveText(``)
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
await page.waitForTimeout(500)
await page.keyboard.press('Enter')
await expect(page.locator('.cm-ghostText').first()).toBeVisible()
await expect(page.locator('.cm-content')).toHaveText(
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
)
await expect(page.locator('.cm-ghostText').first()).toHaveText(
`fn cube = (pos, scale) => {`
)
// Going elsewhere in the code should hide the ghost text.
await page.keyboard.press('Enter')
await expect(page.locator('.cm-ghostText').first()).toBeVisible()
await expect(page.locator('.cm-content')).toHaveText(
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
)
})
test('Ctrl+shift+z in code rejects the suggestion', async ({ page }) => {
const u = await getUtils(page)
// const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
const CtrlKey = process.platform === 'darwin' ? 'Meta' : 'Control'
await u.codeLocator.click()
await expect(page.locator('.cm-content')).toHaveText(``)
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
await page.waitForTimeout(500)
await page.keyboard.press('Enter')
await expect(page.locator('.cm-ghostText').first()).toBeVisible()
await expect(page.locator('.cm-content')).toHaveText(
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
)
await expect(page.locator('.cm-ghostText').first()).toHaveText(
`fn cube = (pos, scale) => {`
)
// Going elsewhere in the code should hide the ghost text.
await page.keyboard.down(CtrlKey)
await page.keyboard.down('Shift')
await page.keyboard.press('KeyZ')
await page.keyboard.up(CtrlKey)
await page.keyboard.up('Shift')
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
await expect(page.locator('.cm-content')).toHaveText(``)
})
test('Ctrl+z in code rejects the suggestion and undos the last code', async ({
page,
}) => {
const u = await getUtils(page)
// const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
const CtrlKey = process.platform === 'darwin' ? 'Meta' : 'Control'
await page.waitForTimeout(800)
await u.codeLocator.click()
await expect(page.locator('.cm-content')).toHaveText(``)
await page.keyboard.type('{thing: "blah"}', { delay: 0 })
await expect(page.locator('.cm-content')).toHaveText(`{thing: "blah"}`)
// We wanna make sure the code saves.
await page.waitForTimeout(800)
// Ctrl+z
await page.keyboard.down(CtrlKey)
await page.keyboard.press('KeyZ')
await page.keyboard.up(CtrlKey)
await expect(page.locator('.cm-content')).toHaveText(``)
// Ctrl+shift+z
await page.keyboard.down(CtrlKey)
await page.keyboard.down('Shift')
await page.keyboard.press('KeyZ')
await page.keyboard.up(CtrlKey)
await page.keyboard.up('Shift')
await expect(page.locator('.cm-content')).toHaveText(`{thing: "blah"}`)
// We wanna make sure the code saves.
await page.waitForTimeout(800)
await page.waitForTimeout(500)
await page.keyboard.press('Enter')
await expect(page.locator('.cm-ghostText').first()).toBeVisible()
await expect(page.locator('.cm-content')).toHaveText(
`{thing: "blah"}fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
)
await expect(page.locator('.cm-ghostText').first()).toHaveText(
`fn cube = (pos, scale) => {`
)
// Once for the enter.
await page.keyboard.down(CtrlKey)
await page.keyboard.press('KeyZ')
await page.keyboard.up(CtrlKey)
// Once for the text.
await page.keyboard.down(CtrlKey)
await page.keyboard.press('KeyZ')
await page.keyboard.up(CtrlKey)
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
// TODO when we make codemirror a widget, we can test this.
//await expect(page.locator('.cm-content')).toHaveText(``)
})
test('delete in code rejects the suggestion', async ({ page }) => {
const u = await getUtils(page)
// const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
await u.codeLocator.click()
await expect(page.locator('.cm-content')).toHaveText(``)
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
await page.waitForTimeout(500)
await page.keyboard.press('Enter')
await page.keyboard.press('Enter')
await page.keyboard.press('Enter')
await expect(page.locator('.cm-ghostText').first()).toBeVisible()
await expect(page.locator('.cm-content')).toHaveText(
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
)
await expect(page.locator('.cm-ghostText').first()).toHaveText(
`fn cube = (pos, scale) => {`
)
// Going elsewhere in the code should hide the ghost text.
await page.keyboard.press('Delete')
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
await expect(page.locator('.cm-content')).toHaveText(``)
})
test('backspace in code rejects the suggestion', async ({ page }) => {
const u = await getUtils(page)
// const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
await u.codeLocator.click()
await expect(page.locator('.cm-content')).toHaveText(``)
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
await page.waitForTimeout(500)
await page.keyboard.press('Enter')
await page.keyboard.press('Enter')
await page.keyboard.press('Enter')
await expect(page.locator('.cm-ghostText').first()).toBeVisible()
await expect(page.locator('.cm-content')).toHaveText(
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
)
await expect(page.locator('.cm-ghostText').first()).toHaveText(
`fn cube = (pos, scale) => {`
)
// Going elsewhere in the code should hide the ghost text.
await page.keyboard.press('Backspace')
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
await expect(page.locator('.cm-content')).toHaveText(``)
})
test('focus outside code pane rejects the suggestion', async ({ page }) => {
const u = await getUtils(page)
// const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
await u.codeLocator.click()
await expect(page.locator('.cm-content')).toHaveText(``)
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
await page.waitForTimeout(500)
await page.keyboard.press('Enter')
await expect(page.locator('.cm-ghostText').first()).toBeVisible()
await expect(page.locator('.cm-content')).toHaveText(
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
)
await expect(page.locator('.cm-ghostText').first()).toHaveText(
`fn cube = (pos, scale) => {`
)
// Going outside the editor should hide the ghost text.
await page.mouse.move(0, 0)
await page
.getByRole('button', { name: 'Start Sketch' })
.waitFor({ state: 'visible' })
await page.getByRole('button', { name: 'Start Sketch' }).click()
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
await expect(page.locator('.cm-content')).toHaveText(``)
})
})

View File

@ -0,0 +1,867 @@
import { test, expect } from '@playwright/test'
import { uuidv4 } from 'lib/utils'
import { getUtils, setup, tearDown } from './test-utils'
test.beforeEach(async ({ context, page }) => {
await setup(context, page)
})
test.afterEach(async ({ page }, testInfo) => {
await tearDown(page, testInfo)
})
test.describe('Editor tests', () => {
test('can comment out code with ctrl+/', async ({ page }) => {
const u = await getUtils(page)
await page.setViewportSize({ width: 1000, height: 500 })
await u.waitForAuthSkipAppStart()
const CtrlKey = process.platform === 'darwin' ? 'Meta' : 'Control'
// check no error to begin with
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
await u.codeLocator.click()
await page.keyboard.type(`const sketch001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
|> line([-20, 0], %)
|> close(%)`)
await page.keyboard.down(CtrlKey)
await page.keyboard.press('/')
await page.keyboard.up(CtrlKey)
await expect(page.locator('.cm-content'))
.toHaveText(`const sketch001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
|> line([-20, 0], %)
// |> close(%)`)
// uncomment the code
await page.keyboard.down(CtrlKey)
await page.keyboard.press('/')
await page.keyboard.up(CtrlKey)
await expect(page.locator('.cm-content'))
.toHaveText(`const sketch001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
|> line([-20, 0], %)
|> close(%)`)
})
test('if you click the format button it formats your code', async ({
page,
}) => {
const u = await getUtils(page)
await page.setViewportSize({ width: 1000, height: 500 })
await u.waitForAuthSkipAppStart()
// check no error to begin with
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
await u.codeLocator.click()
await page.keyboard.type(`const sketch001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
|> line([-20, 0], %)
|> close(%)`)
await page.locator('#code-pane button:first-child').click()
await page.locator('button:has-text("Format code")').click()
await expect(page.locator('.cm-content'))
.toHaveText(`const sketch001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
|> line([-20, 0], %)
|> close(%)`)
})
test('fold gutters work', async ({ page }) => {
const u = await getUtils(page)
const fullCode = `const sketch001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
|> line([-20, 0], %)
|> close(%)`
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const sketch001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
|> line([-20, 0], %)
|> close(%)`
)
})
await page.setViewportSize({ width: 1000, height: 500 })
await u.waitForAuthSkipAppStart()
// TODO: Jess needs to fix this but you have to mod the code to get them to show
// up, its an annoying codemirror thing.
await page.locator('.cm-content').click()
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('Enter')
const foldGutterFoldLine = page.locator('[title="Fold line"]')
const foldGutterUnfoldLine = page.locator('[title="Unfold line"]')
await expect(page.locator('.cm-content')).toHaveText(fullCode)
// check no error to begin with
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
// Make sure we have a fold gutter
await expect(foldGutterFoldLine).toBeVisible()
await expect(foldGutterUnfoldLine).not.toBeVisible()
// Collapse the code
await foldGutterFoldLine.click()
await expect(page.locator('.cm-content')).toHaveText(
`const sketch001 = startSketchOn('XY')… `
)
await expect(page.locator('.cm-content')).not.toHaveText(fullCode)
await expect(foldGutterFoldLine).not.toBeVisible()
await expect(foldGutterUnfoldLine.nth(1)).toBeVisible()
// Expand the code
await foldGutterUnfoldLine.nth(1).click()
await expect(page.locator('.cm-content')).toHaveText(fullCode)
// Delete all the code.
await page.locator('.cm-content').click()
// Select all
await page.keyboard.press('Control+A')
await page.keyboard.press('Backspace')
await page.keyboard.press('Meta+A')
await page.keyboard.press('Backspace')
await expect(page.locator('.cm-content')).toHaveText(``)
await expect(page.locator('.cm-content')).not.toHaveText(fullCode)
await expect(foldGutterUnfoldLine).not.toBeVisible()
await expect(foldGutterFoldLine).not.toBeVisible()
})
test('hover over functions shows function description', async ({ page }) => {
const u = await getUtils(page)
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const sketch001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
|> line([-20, 0], %)
|> close(%)`
)
})
await page.setViewportSize({ width: 1000, height: 500 })
await u.waitForAuthSkipAppStart()
// check no error to begin with
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.closeDebugPanel()
// focus the editor
await u.codeLocator.click()
// Hover over the startSketchOn function
await page.getByText('startSketchOn').hover()
await expect(page.locator('.hover-tooltip')).toBeVisible()
await expect(
page.getByText(
'Start a new 2-dimensional sketch on a specific plane or face'
)
).toBeVisible()
// Hover over the line function
await page.getByText('line').first().hover()
await expect(page.locator('.hover-tooltip')).toBeVisible()
await expect(page.getByText('Draw a line')).toBeVisible()
})
test('if you use the format keyboard binding it formats your code', async ({
page,
}) => {
const u = await getUtils(page)
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const sketch001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
|> line([-20, 0], %)
|> close(%)`
)
localStorage.setItem('disableAxis', 'true')
})
await page.setViewportSize({ width: 1000, height: 500 })
await u.waitForAuthSkipAppStart()
// check no error to begin with
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.closeDebugPanel()
// focus the editor
await u.codeLocator.click()
// Hit alt+shift+f to format the code
await page.keyboard.press('Alt+Shift+KeyF')
await expect(page.locator('.cm-content'))
.toHaveText(`const sketch001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
|> line([-20, 0], %)
|> close(%)`)
})
test('if you write kcl with lint errors you get lints', async ({ page }) => {
const u = await getUtils(page)
await page.setViewportSize({ width: 1000, height: 500 })
await u.waitForAuthSkipAppStart()
// check no error to begin with
await expect(page.locator('.cm-lint-marker-info')).not.toBeVisible()
await u.codeLocator.click()
await page.keyboard.type('const my_snake_case_var = 5')
await page.keyboard.press('Enter')
await page.keyboard.type('const myCamelCaseVar = 5')
await page.keyboard.press('Enter')
// press arrows to clear autocomplete
await page.keyboard.press('ArrowLeft')
await page.keyboard.press('ArrowRight')
// error in guter
await expect(page.locator('.cm-lint-marker-info').first()).toBeVisible()
// error text on hover
await page.hover('.cm-lint-marker-info')
await expect(
page.getByText('Identifiers must be lowerCamelCase').first()
).toBeVisible()
// select the line that's causing the error and delete it
await page.getByText('const my_snake_case_var = 5').click()
await page.keyboard.press('End')
await page.keyboard.down('Shift')
await page.keyboard.press('Home')
await page.keyboard.up('Shift')
await page.keyboard.press('Backspace')
// wait for .cm-lint-marker-info not to be visible
await expect(page.locator('.cm-lint-marker-info')).not.toBeVisible()
})
test('if you fixup kcl errors you clear lints', async ({ page }) => {
const u = await getUtils(page)
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const sketch001 = startSketchOn('XZ')
|> startProfileAt([3.29, 7.86], %)
|> line([2.48, 2.44], %)
|> line([2.66, 1.17], %)
|> close(%)
`
)
})
await page.setViewportSize({ width: 1000, height: 500 })
await u.waitForAuthSkipAppStart()
// check no error to begin with
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
await u.codeLocator.click()
await page.getByText(' |> line([2.48, 2.44], %)').click()
await expect(
page.locator('.cm-lint-marker-error').first()
).not.toBeVisible()
await page.keyboard.press('End')
await page.keyboard.press('Backspace')
await expect(page.locator('.cm-lint-marker-error').first()).toBeVisible()
await page.keyboard.type(')')
await expect(
page.locator('.cm-lint-marker-error').first()
).not.toBeVisible()
})
test('if you write invalid kcl you get inlined errors', async ({ page }) => {
const u = await getUtils(page)
await page.setViewportSize({ width: 1000, height: 500 })
await u.waitForAuthSkipAppStart()
// check no error to begin with
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
/* add the following code to the editor ($ error is not a valid line)
$ error
const topAng = 30
const bottomAng = 25
*/
await u.codeLocator.click()
await page.keyboard.type('$ error')
// press arrows to clear autocomplete
await page.keyboard.press('ArrowLeft')
await page.keyboard.press('ArrowRight')
await page.keyboard.press('Enter')
await page.keyboard.type('const topAng = 30')
await page.keyboard.press('Enter')
await page.keyboard.type('const bottomAng = 25')
await page.keyboard.press('Enter')
// error in guter
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
// error text on hover
await page.hover('.cm-lint-marker-error')
await expect(page.getByText('Unexpected token').first()).toBeVisible()
// select the line that's causing the error and delete it
await page.getByText('$ error').click()
await page.keyboard.press('End')
await page.keyboard.down('Shift')
await page.keyboard.press('Home')
await page.keyboard.up('Shift')
await page.keyboard.press('Backspace')
// wait for .cm-lint-marker-error not to be visible
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
// let's check we get an error when defining the same variable twice
await page.getByText('const bottomAng = 25').click()
await page.keyboard.press('Enter')
await page.keyboard.type("// Let's define the same thing twice")
await page.keyboard.press('Enter')
await page.keyboard.type('const topAng = 42')
await page.keyboard.press('ArrowLeft')
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
await expect(
page.locator('.cm-lint-marker.cm-lint-marker-error')
).toBeVisible()
await page.locator('.cm-lint-marker.cm-lint-marker-error').hover()
await expect(page.locator('.cm-diagnosticText').first()).toBeVisible()
await expect(
page.getByText('Cannot redefine `topAng`').first()
).toBeVisible()
const secondTopAng = page.getByText('topAng').first()
await secondTopAng?.dblclick()
await page.keyboard.type('otherAng')
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
})
test('error with 2 source ranges gets 2 diagnostics', async ({ page }) => {
const u = await getUtils(page)
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const length = .750
const width = 0.500
const height = 0.500
const dia = 4
fn squareHole = (l, w) => {
const squareHoleSketch = startSketchOn('XY')
|> startProfileAt([-width / 2, -length / 2], %)
|> lineTo([width / 2, -length / 2], %)
|> lineTo([width / 2, length / 2], %)
|> lineTo([-width / 2, length / 2], %)
|> close(%)
return squareHoleSketch
}
`
)
})
await page.setViewportSize({ width: 1000, height: 500 })
await u.waitForAuthSkipAppStart()
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.closeDebugPanel()
// check no error to begin with
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
// Click on the bottom of the code editor to add a new line
await u.codeLocator.click()
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('Enter')
await page.keyboard.type(`const extrusion = startSketchOn('XY')
|> circle([0, 0], dia/2, %)
|> hole(squareHole(length, width, height), %)
|> extrude(height, %)`)
// error in gutter
await expect(page.locator('.cm-lint-marker-error').first()).toBeVisible()
await page.hover('.cm-lint-marker-error:first-child')
await expect(
page.getByText('Expected 2 arguments, got 3').first()
).toBeVisible()
// Make sure there are two diagnostics
await expect(page.locator('.cm-lint-marker-error')).toHaveCount(2)
})
test('if your kcl gets an error from the engine it is inlined', async ({
page,
}) => {
const u = await getUtils(page)
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const box = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> line([0, 10], %)
|> line([10, 0], %)
|> line([0, -10], %, $revolveAxis)
|> close(%)
|> extrude(10, %)
const sketch001 = startSketchOn(box, revolveAxis)
|> startProfileAt([5, 10], %)
|> line([0, -10], %)
|> line([2, 0], %)
|> line([0, -10], %)
|> close(%)
|> revolve({
axis: revolveAxis,
angle: 90
}, %)
`
)
})
await page.setViewportSize({ width: 1000, height: 500 })
await page.goto('/')
await u.waitForPageLoad()
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
// error text on hover
await page.hover('.cm-lint-marker-error')
const searchText =
'sketch profile must lie entirely on one side of the revolution axis'
await expect(page.getByText(searchText)).toBeVisible()
})
test.describe('Autocomplete works', () => {
test('with enter/click to accept the completion', async ({ page }) => {
const u = await getUtils(page)
// const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
// this test might be brittle as we add and remove functions
// but should also be easy to update.
// tests clicking on an option, selection the first option
// and arrowing down to an option
await u.codeLocator.click()
await page.keyboard.type('const sketch001 = start')
// expect there to be six auto complete options
await expect(page.locator('.cm-completionLabel')).toHaveCount(8)
// this makes sure we can accept a completion with click
await page.getByText('startSketchOn').click()
await page.keyboard.type("'XZ'")
await page.keyboard.press('Tab')
await page.keyboard.press('Enter')
await page.keyboard.type(' |> startProfi')
// expect there be a single auto complete option that we can just hit enter on
await expect(page.locator('.cm-completionLabel')).toBeVisible()
await page.waitForTimeout(100)
await page.keyboard.press('Enter') // accepting the auto complete, not a new line
await page.keyboard.press('Tab')
await page.waitForTimeout(100)
await page.keyboard.type('12')
await page.waitForTimeout(100)
await page.keyboard.press('Tab')
await page.waitForTimeout(100)
await page.keyboard.press('Tab')
await page.waitForTimeout(100)
await page.keyboard.press('Tab')
await page.waitForTimeout(100)
await page.keyboard.press('Enter')
await page.waitForTimeout(100)
await page.keyboard.type(' |> lin')
await expect(page.locator('.cm-tooltip-autocomplete')).toBeVisible()
await page.waitForTimeout(100)
// press arrow down twice then enter to accept xLine
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('Enter')
// finish line with comment
await page.keyboard.type('5')
await page.waitForTimeout(100)
await page.keyboard.press('Tab')
await page.waitForTimeout(100)
await page.keyboard.press('Tab')
await page.keyboard.type(' // ')
// Since we need to parse the ast to know we are in a comment we gotta hang tight.
await page.waitForTimeout(700)
await page.keyboard.type('lin ')
await page.waitForTimeout(200)
// there shouldn't be any auto complete options for 'lin' in the comment
await expect(page.locator('.cm-completionLabel')).not.toBeVisible()
await expect(page.locator('.cm-content'))
.toHaveText(`const sketch001 = startSketchOn('XZ')
|> startProfileAt([3.14, 12], %)
|> xLine(5, %) // lin`)
})
test('with tab to accept the completion', async ({ page }) => {
const u = await getUtils(page)
// const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
// this test might be brittle as we add and remove functions
// but should also be easy to update.
// tests clicking on an option, selection the first option
// and arrowing down to an option
await u.codeLocator.click()
await page.keyboard.type('const sketch001 = startSketchO')
await page.waitForTimeout(100)
// Make sure just hitting tab will take the only one left
await expect(page.locator('.cm-completionLabel')).toHaveCount(1)
await page.waitForTimeout(500)
await page.keyboard.press('ArrowDown')
await page.keyboard.press('Tab')
await page.waitForTimeout(500)
await page.keyboard.type("'XZ'")
await page.keyboard.press('Tab')
await page.keyboard.press('Enter')
await page.keyboard.type(' |> startProfi')
// expect there be a single auto complete option that we can just hit enter on
await expect(page.locator('.cm-completionLabel')).toBeVisible()
await page.waitForTimeout(100)
await page.keyboard.press('Tab') // accepting the auto complete, not a new line
await page.keyboard.press('Tab')
await page.keyboard.type('12')
await page.waitForTimeout(100)
await page.keyboard.press('Tab')
await page.waitForTimeout(100)
await page.keyboard.press('Tab')
await page.waitForTimeout(100)
await page.keyboard.press('Tab')
await page.waitForTimeout(100)
await page.keyboard.press('Enter')
await page.waitForTimeout(100)
await page.keyboard.type(' |> lin')
await expect(page.locator('.cm-tooltip-autocomplete')).toBeVisible()
await page.waitForTimeout(100)
// press arrow down twice then tab to accept xLine
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('Tab')
// finish line with comment
await page.keyboard.type('5')
await page.waitForTimeout(100)
await page.keyboard.press('Tab')
await page.waitForTimeout(100)
await page.keyboard.press('Tab')
await page.keyboard.type(' // ')
// Since we need to parse the ast to know we are in a comment we gotta hang tight.
await page.waitForTimeout(700)
await page.keyboard.type('lin ')
await page.waitForTimeout(200)
// there shouldn't be any auto complete options for 'lin' in the comment
await expect(page.locator('.cm-completionLabel')).not.toBeVisible()
await expect(page.locator('.cm-content'))
.toHaveText(`const sketch001 = startSketchOn('XZ')
|> startProfileAt([3.14, 12], %)
|> xLine(5, %) // lin`)
})
})
test('Can undo a click and point extrude with ctrl+z', async ({ page }) => {
const u = await getUtils(page)
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const sketch001 = startSketchOn('XZ')
|> startProfileAt([4.61, -14.01], %)
|> line([12.73, -0.09], %)
|> tangentialArcTo([24.95, -5.38], %)
|> close(%)`
)
})
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled()
await page.waitForTimeout(100)
await u.openAndClearDebugPanel()
await u.sendCustomCmd({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_look_at',
vantage: { x: 0, y: -1250, z: 580 },
center: { x: 0, y: 0, z: 0 },
up: { x: 0, y: 0, z: 1 },
},
})
await page.waitForTimeout(100)
await u.sendCustomCmd({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_get_settings',
},
})
await page.waitForTimeout(100)
const startPX = [665, 458]
const dragPX = 40
await page.getByText('startProfileAt([4.61, -14.01], %)').click()
await expect(page.getByRole('button', { name: 'Extrude' })).toBeVisible()
await page.getByRole('button', { name: 'Extrude' }).click()
await expect(page.getByTestId('command-bar')).toBeVisible()
await page.waitForTimeout(100)
await page.keyboard.press('Enter')
await page.waitForTimeout(100)
await expect(page.getByText('Confirm Extrude')).toBeVisible()
await page.keyboard.press('Enter')
await page.waitForTimeout(100)
// expect the code to have changed
await expect(page.locator('.cm-content')).toHaveText(
`const sketch001 = startSketchOn('XZ') |> startProfileAt([4.61, -14.01], %) |> line([12.73, -0.09], %) |> tangentialArcTo([24.95, -5.38], %) |> close(%)const extrude001 = extrude(5, sketch001)`
)
// Now hit undo
await page.keyboard.down('Control')
await page.keyboard.press('KeyZ')
await page.keyboard.up('Control')
await page.waitForTimeout(100)
await expect(page.locator('.cm-content'))
.toHaveText(`const sketch001 = startSketchOn('XZ')
|> startProfileAt([4.61, -14.01], %)
|> line([12.73, -0.09], %)
|> tangentialArcTo([24.95, -5.38], %)
|> close(%)`)
})
// failing for the same reason as "Can edit a sketch that has been extruded in the same pipe"
// please fix together
test.fixme('Can undo a sketch modification with ctrl+z', async ({ page }) => {
const u = await getUtils(page)
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const sketch001 = startSketchOn('XZ')
|> startProfileAt([4.61, -14.01], %)
|> line([12.73, -0.09], %)
|> tangentialArcTo([24.95, -5.38], %)
|> close(%)
|> extrude(5, %)`
)
})
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled()
await page.waitForTimeout(100)
await u.openAndClearDebugPanel()
await u.sendCustomCmd({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_look_at',
vantage: { x: 0, y: -1250, z: 580 },
center: { x: 0, y: 0, z: 0 },
up: { x: 0, y: 0, z: 1 },
},
})
await page.waitForTimeout(100)
await u.sendCustomCmd({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_get_settings',
},
})
await page.waitForTimeout(100)
const startPX = [665, 458]
const dragPX = 40
await page.getByText('startProfileAt([4.61, -14.01], %)').click()
await expect(
page.getByRole('button', { name: 'Edit Sketch' })
).toBeVisible()
await page.getByRole('button', { name: 'Edit Sketch' }).click()
await page.waitForTimeout(400)
let prevContent = await page.locator('.cm-content').innerText()
await expect(page.getByTestId('segment-overlay')).toHaveCount(2)
// drag startProfieAt handle
await page.dragAndDrop('#stream', '#stream', {
sourcePosition: { x: startPX[0], y: startPX[1] },
targetPosition: { x: startPX[0] + dragPX, y: startPX[1] + dragPX },
})
await page.waitForTimeout(100)
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
prevContent = await page.locator('.cm-content').innerText()
// drag line handle
// we wait so it saves the code
await page.waitForTimeout(800)
const lineEnd = await u.getBoundingBox('[data-overlay-index="0"]')
await page.waitForTimeout(100)
await page.dragAndDrop('#stream', '#stream', {
sourcePosition: { x: lineEnd.x - 5, y: lineEnd.y },
targetPosition: { x: lineEnd.x + dragPX, y: lineEnd.y + dragPX },
})
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
prevContent = await page.locator('.cm-content').innerText()
// we wait so it saves the code
await page.waitForTimeout(800)
// drag tangentialArcTo handle
const tangentEnd = await u.getBoundingBox('[data-overlay-index="1"]')
await page.dragAndDrop('#stream', '#stream', {
sourcePosition: { x: tangentEnd.x, y: tangentEnd.y - 5 },
targetPosition: {
x: tangentEnd.x + dragPX,
y: tangentEnd.y + dragPX,
},
})
await page.waitForTimeout(100)
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
// expect the code to have changed
await expect(page.locator('.cm-content'))
.toHaveText(`const sketch001 = startSketchOn('XZ')
|> startProfileAt([7.12, -16.82], %)
|> line([15.4, -2.74], %)
|> tangentialArcTo([24.95, -5.38], %)
|> line([2.65, -2.69], %)
|> close(%)
|> extrude(5, %)`)
// Hit undo
await page.keyboard.down('Control')
await page.keyboard.press('KeyZ')
await page.keyboard.up('Control')
await expect(page.locator('.cm-content'))
.toHaveText(`const sketch001 = startSketchOn('XZ')
|> startProfileAt([7.12, -16.82], %)
|> line([15.4, -2.74], %)
|> tangentialArcTo([24.95, -5.38], %)
|> close(%)
|> extrude(5, %)`)
// Hit undo again.
await page.keyboard.down('Control')
await page.keyboard.press('KeyZ')
await page.keyboard.up('Control')
await expect(page.locator('.cm-content'))
.toHaveText(`const sketch001 = startSketchOn('XZ')
|> startProfileAt([7.12, -16.82], %)
|> line([12.73, -0.09], %)
|> tangentialArcTo([24.95, -5.38], %)
|> close(%)
|> extrude(5, %)`)
// Hit undo again.
await page.keyboard.down('Control')
await page.keyboard.press('KeyZ')
await page.keyboard.up('Control')
await page.waitForTimeout(100)
await expect(page.locator('.cm-content'))
.toHaveText(`const sketch001 = startSketchOn('XZ')
|> startProfileAt([4.61, -14.01], %)
|> line([12.73, -0.09], %)
|> tangentialArcTo([24.95, -5.38], %)
|> close(%)
|> extrude(5, %)`)
})
})

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,341 @@
import { test, expect } from '@playwright/test'
import { getUtils, setup, tearDown } from './test-utils'
import { bracket } from 'lib/exampleKcl'
import { onboardingPaths } from 'routes/Onboarding/paths'
import {
TEST_SETTINGS_KEY,
TEST_SETTINGS_ONBOARDING_START,
TEST_SETTINGS_ONBOARDING_EXPORT,
TEST_SETTINGS_ONBOARDING_PARAMETRIC_MODELING,
TEST_SETTINGS_ONBOARDING_USER_MENU,
} from './storageStates'
import * as TOML from '@iarna/toml'
test.beforeEach(async ({ context, page }) => {
await setup(context, page)
})
test.afterEach(async ({ page }, testInfo) => {
await tearDown(page, testInfo)
})
test.describe('Onboarding tests', () => {
test('Onboarding code is shown in the editor', async ({ page }) => {
const u = await getUtils(page)
// Override beforeEach test setup
await page.addInitScript(
async ({ settingsKey }) => {
// Give no initial code, so that the onboarding start is shown immediately
localStorage.removeItem('persistCode')
localStorage.removeItem(settingsKey)
},
{ settingsKey: TEST_SETTINGS_KEY }
)
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
// Test that the onboarding pane loaded
await expect(page.getByText('Welcome to Modeling App! This')).toBeVisible()
// *and* that the code is shown in the editor
await expect(page.locator('.cm-content')).toContainText('// Shelf Bracket')
})
test('Code resets after confirmation', async ({ page }) => {
const initialCode = `const sketch001 = startSketchOn('XZ')`
// Load the page up with some code so we see the confirmation warning
// when we go to replay onboarding
await page.addInitScript((code) => {
localStorage.setItem('persistCode', code)
}, initialCode)
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
// Replay the onboarding
await page.getByRole('link', { name: 'Settings' }).last().click()
const replayButton = page.getByRole('button', { name: 'Replay onboarding' })
await expect(replayButton).toBeVisible()
await replayButton.click()
// Ensure we see the warning, and that the code has not yet updated
await expect(
page.getByText('Replaying onboarding resets your code')
).toBeVisible()
await expect(page.locator('.cm-content')).toHaveText(initialCode)
const nextButton = page.getByTestId('onboarding-next')
await expect(nextButton).toBeVisible()
await nextButton.click()
// Ensure we see the introduction and that the code has been reset
await expect(page.getByText('Welcome to Modeling App!')).toBeVisible()
await expect(page.locator('.cm-content')).toContainText('// Shelf Bracket')
// Ensure we persisted the code to local storage.
// Playwright's addInitScript method unfortunately will reset
// this code if we try reloading the page as a test,
// so this is our best way to test persistence afaik.
expect(
await page.evaluate(() => {
return localStorage.getItem('persistCode')
})
).toContain('// Shelf Bracket')
})
test('Click through each onboarding step', async ({ page }) => {
const u = await getUtils(page)
// Override beforeEach test setup
await page.addInitScript(
async ({ settingsKey, settings }) => {
// Give no initial code, so that the onboarding start is shown immediately
localStorage.setItem('persistCode', '')
localStorage.setItem(settingsKey, settings)
},
{
settingsKey: TEST_SETTINGS_KEY,
settings: TOML.stringify({ settings: TEST_SETTINGS_ONBOARDING_START }),
}
)
await page.setViewportSize({ width: 1200, height: 1080 })
await u.waitForAuthSkipAppStart()
// Test that the onboarding pane loaded
await expect(page.getByText('Welcome to Modeling App! This')).toBeVisible()
const nextButton = page.getByTestId('onboarding-next')
while ((await nextButton.innerText()) !== 'Finish') {
await expect(nextButton).toBeVisible()
await nextButton.click()
}
// Finish the onboarding
await expect(nextButton).toBeVisible()
await nextButton.click()
// Test that the onboarding pane is gone
await expect(page.getByTestId('onboarding-content')).not.toBeVisible()
await expect(page.url()).not.toContain('onboarding')
})
test('Onboarding redirects and code updating', async ({ page }) => {
const u = await getUtils(page)
// Override beforeEach test setup
await page.addInitScript(
async ({ settingsKey, settings }) => {
// Give some initial code, so we can test that it's cleared
localStorage.setItem('persistCode', 'const sigmaAllow = 15000')
localStorage.setItem(settingsKey, settings)
},
{
settingsKey: TEST_SETTINGS_KEY,
settings: TOML.stringify({ settings: TEST_SETTINGS_ONBOARDING_EXPORT }),
}
)
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
// Test that the redirect happened
await expect(page.url().split(':3000').slice(-1)[0]).toBe(
`/file/%2Fbrowser%2Fmain.kcl/onboarding/export`
)
// Test that you come back to this page when you refresh
await page.reload()
await expect(page.url().split(':3000').slice(-1)[0]).toBe(
`/file/%2Fbrowser%2Fmain.kcl/onboarding/export`
)
// Test that the onboarding pane loaded
const title = page.locator('[data-testid="onboarding-content"]')
await expect(title).toBeAttached()
// Test that the code changes when you advance to the next step
await page.locator('[data-testid="onboarding-next"]').click()
await expect(page.locator('.cm-content')).toHaveText('')
// Test that the code is not empty when you click on the next step
await page.locator('[data-testid="onboarding-next"]').click()
await expect(page.locator('.cm-content')).toHaveText(/.+/)
})
test('Onboarding code gets reset to demo on Interactive Numbers step', async ({
page,
}) => {
test.skip(
process.platform === 'darwin',
"Skip on macOS, because Playwright isn't behaving the same as the actual browser"
)
const u = await getUtils(page)
const badCode = `// This is bad code we shouldn't see`
// Override beforeEach test setup
await page.addInitScript(
async ({ settingsKey, settings, badCode }) => {
localStorage.setItem('persistCode', badCode)
localStorage.setItem(settingsKey, settings)
},
{
settingsKey: TEST_SETTINGS_KEY,
settings: TOML.stringify({
settings: TEST_SETTINGS_ONBOARDING_PARAMETRIC_MODELING,
}),
badCode,
}
)
await page.setViewportSize({ width: 1200, height: 1080 })
await u.waitForAuthSkipAppStart()
await page.waitForURL('**' + onboardingPaths.PARAMETRIC_MODELING, {
waitUntil: 'domcontentloaded',
})
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, {
timeout: 10_000,
})
// Mess with the code again
await u.codeLocator.selectText()
await u.codeLocator.fill(badCode)
await expect(u.codeLocator).toHaveText(badCode)
// Click to the next step
await page.locator('[data-testid="onboarding-next"]').click()
await page.waitForURL('**' + onboardingPaths.INTERACTIVE_NUMBERS, {
waitUntil: 'domcontentloaded',
})
// Check that the code has been reset
await expect(u.codeLocator).toHaveText(bracketNoNewLines)
})
test('Avatar text updates depending on image load success', async ({
page,
}) => {
// Override beforeEach test setup
await page.addInitScript(
async ({ settingsKey, settings }) => {
localStorage.setItem(settingsKey, settings)
},
{
settingsKey: TEST_SETTINGS_KEY,
settings: TOML.stringify({
settings: TEST_SETTINGS_ONBOARDING_USER_MENU,
}),
}
)
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
await page.waitForURL('**/file/**', { waitUntil: 'domcontentloaded' })
// Test that the text in this step is correct
const avatarLocator = await page
.getByTestId('user-sidebar-toggle')
.locator('img')
const onboardingOverlayLocator = await page
.getByTestId('onboarding-content')
.locator('div')
.nth(1)
// Expect the avatar to be visible and for the text to reference it
await expect(avatarLocator).toBeVisible()
await expect(onboardingOverlayLocator).toBeVisible()
await expect(onboardingOverlayLocator).toContainText('your avatar')
// This is to force the avatar to 404.
// For our test image (only triggers locally. on CI, it's Kurt's /
// gravatar image )
await page.route('/cat.jpg', async (route) => {
await route.fulfill({
status: 404,
contentType: 'text/plain',
body: 'Not Found!',
})
})
// 404 the CI avatar image
await page.route('https://lh3.googleusercontent.com/**', async (route) => {
await route.fulfill({
status: 404,
contentType: 'text/plain',
body: 'Not Found!',
})
})
await page.reload({ waitUntil: 'domcontentloaded' })
// Now expect the text to be different
await expect(avatarLocator).not.toBeVisible()
await expect(onboardingOverlayLocator).toBeVisible()
await expect(onboardingOverlayLocator).toContainText('the menu button')
})
test("Avatar text doesn't mention avatar when no avatar", async ({
page,
}) => {
// Override beforeEach test setup
await page.addInitScript(
async ({ settingsKey, settings }) => {
localStorage.setItem(settingsKey, settings)
localStorage.setItem('FORCE_NO_IMAGE', 'FORCE_NO_IMAGE')
},
{
settingsKey: TEST_SETTINGS_KEY,
settings: TOML.stringify({
settings: TEST_SETTINGS_ONBOARDING_USER_MENU,
}),
}
)
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
await page.waitForURL('**/file/**', { waitUntil: 'domcontentloaded' })
// Test that the text in this step is correct
const sidebar = page.getByTestId('user-sidebar-toggle')
const avatar = sidebar.locator('img')
const onboardingOverlayLocator = page
.getByTestId('onboarding-content')
.locator('div')
.nth(1)
// Expect the avatar to be visible and for the text to reference it
await expect(avatar).not.toBeVisible()
await expect(onboardingOverlayLocator).toBeVisible()
await expect(onboardingOverlayLocator).toContainText('the menu button')
// Test we mention what else is in this menu for https://github.com/KittyCAD/modeling-app/issues/2939
// which doesn't deserver its own full test spun up
const userMenuFeatures = [
'manage your account',
'report a bug',
'request a feature',
'sign out',
]
for (const feature of userMenuFeatures) {
await expect(onboardingOverlayLocator).toContainText(feature)
}
})
})

View File

@ -0,0 +1,226 @@
import { test, expect } from '@playwright/test'
import { getUtils, setup, tearDown } from './test-utils'
test.beforeEach(async ({ context, page }) => {
await setup(context, page)
})
test.afterEach(async ({ page }, testInfo) => {
await tearDown(page, testInfo)
})
test.describe('Regression tests', () => {
// bugs we found that don't fit neatly into other categories
test('bad model has inline error #3251', async ({ page }) => {
// because the model has `line([0,0]..` it is valid code, but the model is invalid
// regression test for https://github.com/KittyCAD/modeling-app/issues/3251
// Since the bad model also found as issue with the artifact graph, which in tern blocked the editor diognostics
const u = await getUtils(page)
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const sketch2 = startSketchOn("XY")
const sketch001 = startSketchAt([-0, -0])
|> line([0, 0], %)
|> line([-4.84, -5.29], %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)`
)
})
await page.setViewportSize({ width: 1000, height: 500 })
await u.waitForAuthSkipAppStart()
// error in guter
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
// error text on hover
await page.hover('.cm-lint-marker-error')
// this is a cryptic error message, fact that all the lines are co-linear from the `line([0,0])` is the issue why
// the close doesn't work
// when https://github.com/KittyCAD/modeling-app/issues/3268 is closed
// this test will need updating
const crypticErrorText = `ApiError`
await expect(page.getByText(crypticErrorText).first()).toBeVisible()
})
test('executes on load', async ({ page }) => {
const u = await getUtils(page)
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const sketch001 = startSketchOn('-XZ')
|> startProfileAt([-6.95, 4.98], %)
|> line([25.1, 0.41], %)
|> line([0.73, -14.93], %)
|> line([-23.44, 0.52], %)`
)
})
await page.setViewportSize({ width: 1000, height: 500 })
await u.waitForAuthSkipAppStart()
// expand variables section
const variablesTabButton = page.getByTestId('variables-pane-button')
await variablesTabButton.click()
// can find sketch001 in the variables summary (pretty-json-container, makes sure we're not looking in the code editor)
// sketch001 only shows up in the variables summary if it's been executed
await page.waitForFunction(() => {
const variablesElement = document.querySelector(
'.pretty-json-container'
) as HTMLDivElement
return variablesElement.innerHTML.includes('sketch001')
})
await expect(
page.locator('.pretty-json-container >> text=sketch001')
).toBeVisible()
})
test('re-executes', async ({ page }) => {
const u = await getUtils(page)
await page.addInitScript(async () => {
localStorage.setItem('persistCode', `const myVar = 5`)
})
await page.setViewportSize({ width: 1000, height: 500 })
await u.waitForAuthSkipAppStart()
const variablesTabButton = page.getByTestId('variables-pane-button')
await variablesTabButton.click()
// expect to see "myVar:5"
await expect(
page.locator('.pretty-json-container >> text=myVar:5')
).toBeVisible()
// change 5 to 67
await page.getByText('const myVar').click()
await page.keyboard.press('End')
await page.keyboard.press('Backspace')
await page.keyboard.type('67')
await expect(
page.locator('.pretty-json-container >> text=myVar:67')
).toBeVisible()
})
test('ProgramMemory can be serialised', async ({ page }) => {
const u = await getUtils(page)
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const part = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> line([0, 1], %)
|> line([1, 0], %)
|> line([0, -1], %)
|> close(%)
|> extrude(1, %)
|> patternLinear3d({
axis: [1, 0, 1],
repetitions: 3,
distance: 6
}, %)`
)
})
await page.setViewportSize({ width: 1000, height: 500 })
const messages: string[] = []
// Listen for all console events and push the message text to an array
page.on('console', (message) => messages.push(message.text()))
await u.waitForAuthSkipAppStart()
// wait for execution done
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
const forbiddenMessages = ['cannot serialize tagged newtype variant']
forbiddenMessages.forEach((forbiddenMessage) => {
messages.forEach((message) => {
expect(message).not.toContain(forbiddenMessage)
})
})
})
test('ensure the Zoo logo is not a link in browser app', async ({ page }) => {
const u = await getUtils(page)
await page.setViewportSize({ width: 1000, height: 500 })
await u.waitForAuthSkipAppStart()
const zooLogo = page.locator('[data-testid="app-logo"]')
// Make sure it's not a link
await expect(zooLogo).not.toHaveAttribute('href')
})
test('Position _ Is Out Of Range... regression test', async ({ page }) => {
const u = await getUtils(page)
// const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.setViewportSize({ width: 1200, height: 500 })
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const exampleSketch = startSketchOn("XZ")
|> startProfileAt([0, 0], %)
|> angledLine({ angle: 50, length: 45 }, %)
|> yLineTo(0, %)
|> close(%)
|>
const example = extrude(5, exampleSketch)
shell({ faces: ['end'], thickness: 0.25 }, exampleSketch)`
)
})
await expect(async () => {
await page.goto('/')
await u.waitForPageLoad()
// error in guter
await expect(page.locator('.cm-lint-marker-error')).toBeVisible({
timeout: 1_000,
})
await page.waitForTimeout(200)
// expect it still to be there (sometimes it just clears for a bit?)
await expect(page.locator('.cm-lint-marker-error')).toBeVisible({
timeout: 1_000,
})
}).toPass({ timeout: 40_000, intervals: [1_000] })
// error text on hover
await page.hover('.cm-lint-marker-error')
await expect(page.getByText('Unexpected token').first()).toBeVisible()
// Okay execution finished, let's start editing text below the error.
await u.codeLocator.click()
// Go to the end of the editor
// This bug happens when there is a diagnostic in the editor and you try to
// edit text below it.
// Or delete a huge chunk of text and then try to edit below it.
await page.keyboard.press('End')
await page.keyboard.down('Shift')
await page.keyboard.press('ArrowUp')
await page.keyboard.press('ArrowUp')
await page.keyboard.press('ArrowUp')
await page.keyboard.press('ArrowUp')
await page.keyboard.press('ArrowUp')
await page.keyboard.press('End')
await page.keyboard.up('Shift')
await page.keyboard.press('Backspace')
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
await page.keyboard.press('Enter')
await page.keyboard.press('Enter')
await page.keyboard.type('thing: "blah"', { delay: 100 })
await page.keyboard.press('Enter')
await page.keyboard.press('ArrowLeft')
await expect(page.locator('.cm-content'))
.toContainText(`const exampleSketch = startSketchOn("XZ")
|> startProfileAt([0, 0], %)
|> angledLine({ angle: 50, length: 45 }, %)
|> yLineTo(0, %)
|> close(%)
thing: "blah"`)
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
})
})

View File

@ -0,0 +1,959 @@
import { test, expect, Page } from '@playwright/test'
import {
getMovementUtils,
getUtils,
PERSIST_MODELING_CONTEXT,
setup,
tearDown,
} from './test-utils'
import { uuidv4, roundOff } from 'lib/utils'
test.beforeEach(async ({ context, page }) => {
await setup(context, page)
})
test.afterEach(async ({ page }, testInfo) => {
await tearDown(page, testInfo)
})
test.describe('Sketch tests', () => {
test('multi-sketch file shows multiple Edit Sketch buttons', async ({
page,
context,
}) => {
const u = await getUtils(page)
const selectionsSnippets = {
startProfileAt1:
'|> startProfileAt([-width / 4 + screwRadius, height / 2], %)',
startProfileAt2: '|> startProfileAt([-width / 2, 0], %)',
startProfileAt3: '|> startProfileAt([0, thickness], %)',
}
await context.addInitScript(
async ({ startProfileAt1, startProfileAt2, startProfileAt3 }: any) => {
localStorage.setItem(
'persistCode',
`
const width = 20
const height = 10
const thickness = 5
const screwRadius = 3
const wireRadius = 2
const wireOffset = 0.5
const screwHole = startSketchOn('XY')
${startProfileAt1}
|> arc({
radius: screwRadius,
angle_start: 0,
angle_end: 360
}, %)
const part001 = startSketchOn('XY')
${startProfileAt2}
|> xLine(width * .5, %)
|> yLine(height, %)
|> xLine(-width * .5, %)
|> close(%)
|> hole(screwHole, %)
|> extrude(thickness, %)
const part002 = startSketchOn('-XZ')
${startProfileAt3}
|> xLine(width / 4, %)
|> tangentialArcTo([width / 2, 0], %)
|> xLine(-width / 4 + wireRadius, %)
|> yLine(wireOffset, %)
|> arc({
radius: wireRadius,
angle_start: 0,
angle_end: 180
}, %)
|> yLine(-wireOffset, %)
|> xLine(-width / 4, %)
|> close(%)
|> extrude(-height, %)
`
)
},
selectionsSnippets
)
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
// wait for execution done
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.closeDebugPanel()
await page.getByText(selectionsSnippets.startProfileAt1).click()
await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled()
await expect(
page.getByRole('button', { name: 'Edit Sketch' })
).toBeVisible()
await page.getByText(selectionsSnippets.startProfileAt2).click()
await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled()
await expect(
page.getByRole('button', { name: 'Edit Sketch' })
).toBeVisible()
await page.getByText(selectionsSnippets.startProfileAt3).click()
await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled()
await expect(
page.getByRole('button', { name: 'Edit Sketch' })
).toBeVisible()
})
test('Can delete most of a sketch and the line tool will still work', async ({
page,
}) => {
const u = await getUtils(page)
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const sketch001 = startSketchOn('XZ')
|> startProfileAt([4.61, -14.01], %)
|> line([12.73, -0.09], %)
|> tangentialArcTo([24.95, -5.38], %)`
)
})
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
await expect(async () => {
await page.mouse.click(700, 200)
await page.getByText('tangentialArcTo([24.95, -5.38], %)').click()
await expect(
page.getByRole('button', { name: 'Edit Sketch' })
).toBeEnabled({ timeout: 1000 })
await page.getByRole('button', { name: 'Edit Sketch' }).click()
}).toPass({ timeout: 40_000, intervals: [1_000] })
await page.waitForTimeout(600) // wait for animation
await page.getByText('tangentialArcTo([24.95, -5.38], %)').click()
await page.keyboard.press('End')
await page.keyboard.down('Shift')
await page.keyboard.press('ArrowUp')
await page.keyboard.press('Home')
await page.keyboard.up('Shift')
await page.keyboard.press('Backspace')
await u.openAndClearDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]', 10_000)
await page.waitForTimeout(100)
await page.getByRole('button', { name: 'Line', exact: true }).click()
await page.waitForTimeout(100)
await page.mouse.click(700, 200)
await expect(page.locator('.cm-content')).toHaveText(
`const sketch001 = startSketchOn('XZ')
|> startProfileAt([4.61, -14.01], %)
|> line([0.31, 16.47], %)`
)
})
test('Can exit selection of face', async ({ page }) => {
// Load the app with the code panes
await page.addInitScript(async () => {
localStorage.setItem('persistCode', ``)
})
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
await page.getByRole('button', { name: 'Start Sketch' }).click()
await expect(
page.getByRole('button', { name: 'Exit Sketch' })
).toBeVisible()
await expect(page.getByText('select a plane or face')).toBeVisible()
await page.keyboard.press('Escape')
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).toBeVisible()
})
test.describe('Can edit segments by dragging their handles', () => {
const doEditSegmentsByDraggingHandle = async (
page: Page,
openPanes: string[]
) => {
// Load the app with the code panes
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const sketch001 = startSketchOn('XZ')
|> startProfileAt([4.61, -14.01], %)
|> line([12.73, -0.09], %)
|> tangentialArcTo([24.95, -5.38], %)
|> close(%)`
)
})
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled()
await page.waitForTimeout(100)
await u.openAndClearDebugPanel()
await u.sendCustomCmd({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_look_at',
vantage: { x: 0, y: -1250, z: 580 },
center: { x: 0, y: 0, z: 0 },
up: { x: 0, y: 0, z: 1 },
},
})
await page.waitForTimeout(100)
await u.sendCustomCmd({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_get_settings',
},
})
await page.waitForTimeout(100)
await u.closeDebugPanel()
// If we have the code pane open, we should see the code.
if (openPanes.includes('code')) {
await expect(u.codeLocator)
.toHaveText(`const sketch001 = startSketchOn('XZ')
|> startProfileAt([4.61, -14.01], %)
|> line([12.73, -0.09], %)
|> tangentialArcTo([24.95, -5.38], %)
|> close(%)`)
} else {
// Ensure we don't see the code.
await expect(u.codeLocator).not.toBeVisible()
}
const startPX = [665, 458]
const dragPX = 30
let prevContent = ''
if (openPanes.includes('code')) {
await page.getByText('startProfileAt([4.61, -14.01], %)').click()
} else {
// Wait for the render.
await page.waitForTimeout(1000)
// Select the sketch
await page.mouse.click(700, 370)
}
await expect(
page.getByRole('button', { name: 'Edit Sketch' })
).toBeVisible()
await page.getByRole('button', { name: 'Edit Sketch' }).click()
await page.waitForTimeout(400)
if (openPanes.includes('code')) {
prevContent = await page.locator('.cm-content').innerText()
}
const step5 = { steps: 5 }
await expect(page.getByTestId('segment-overlay')).toHaveCount(2)
// drag startProfieAt handle
await page.mouse.move(startPX[0], startPX[1])
await page.mouse.down()
await page.mouse.move(startPX[0] + dragPX, startPX[1] - dragPX, step5)
await page.mouse.up()
if (openPanes.includes('code')) {
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
prevContent = await page.locator('.cm-content').innerText()
}
// drag line handle
await page.waitForTimeout(100)
const lineEnd = await u.getBoundingBox('[data-overlay-index="0"]')
await page.mouse.move(lineEnd.x - 5, lineEnd.y)
await page.mouse.down()
await page.mouse.move(lineEnd.x + dragPX, lineEnd.y - dragPX, step5)
await page.mouse.up()
await page.waitForTimeout(100)
if (openPanes.includes('code')) {
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
prevContent = await page.locator('.cm-content').innerText()
}
// drag tangentialArcTo handle
const tangentEnd = await u.getBoundingBox('[data-overlay-index="1"]')
await page.mouse.move(tangentEnd.x, tangentEnd.y - 5)
await page.mouse.down()
await page.mouse.move(tangentEnd.x + dragPX, tangentEnd.y - dragPX, step5)
await page.mouse.up()
await page.waitForTimeout(100)
if (openPanes.includes('code')) {
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
}
// Open the code pane
await u.openKclCodePanel()
// expect the code to have changed
await expect(page.locator('.cm-content'))
.toHaveText(`const sketch001 = startSketchOn('XZ')
|> startProfileAt([6.44, -12.07], %)
|> line([14.72, 1.97], %)
|> tangentialArcTo([24.95, -5.38], %)
|> line([1.97, 2.06], %)
|> close(%)`)
}
test('code pane open at start-handles', async ({ page }) => {
// Load the app with the code panes
await page.addInitScript(async () => {
localStorage.setItem(
'store',
JSON.stringify({
state: {
openPanes: ['code'],
},
version: 0,
})
)
})
await doEditSegmentsByDraggingHandle(page, ['code'])
})
test('code pane closed at start-handles', async ({ page }) => {
// Load the app with the code panes
await page.addInitScript(async (persistModelingContext) => {
localStorage.setItem(
persistModelingContext,
JSON.stringify({ openPanes: [] })
)
}, PERSIST_MODELING_CONTEXT)
await doEditSegmentsByDraggingHandle(page, [])
})
})
// failing for the same reason as "Can undo a sketch modification with ctrl+z"
// please fix together
test.fixme(
'Can edit a sketch that has been extruded in the same pipe',
async ({ page }) => {
const u = await getUtils(page)
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const sketch001 = startSketchOn('XZ')
|> startProfileAt([4.61, -14.01], %)
|> line([12.73, -0.09], %)
|> tangentialArcTo([24.95, -5.38], %)
|> close(%)
|> extrude(5, %)`
)
})
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled()
await page.waitForTimeout(100)
await u.openAndClearDebugPanel()
await u.sendCustomCmd({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_look_at',
vantage: { x: 0, y: -1250, z: 580 },
center: { x: 0, y: 0, z: 0 },
up: { x: 0, y: 0, z: 1 },
},
})
await page.waitForTimeout(100)
await u.sendCustomCmd({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_get_settings',
},
})
await page.waitForTimeout(100)
const startPX = [665, 458]
const dragPX = 40
await page.getByText('startProfileAt([4.61, -14.01], %)').click()
await expect(
page.getByRole('button', { name: 'Edit Sketch' })
).toBeVisible()
await page.getByRole('button', { name: 'Edit Sketch' }).click()
await page.waitForTimeout(400)
let prevContent = await page.locator('.cm-content').innerText()
await expect(page.getByTestId('segment-overlay')).toHaveCount(2)
// drag startProfieAt handle
await page.dragAndDrop('#stream', '#stream', {
sourcePosition: { x: startPX[0], y: startPX[1] },
targetPosition: { x: startPX[0] + dragPX, y: startPX[1] + dragPX },
})
await page.waitForTimeout(100)
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
prevContent = await page.locator('.cm-content').innerText()
// drag line handle
await page.waitForTimeout(100)
const lineEnd = await u.getBoundingBox('[data-overlay-index="0"]')
await page.waitForTimeout(100)
await page.dragAndDrop('#stream', '#stream', {
sourcePosition: { x: lineEnd.x - 5, y: lineEnd.y },
targetPosition: { x: lineEnd.x + dragPX, y: lineEnd.y + dragPX },
})
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
prevContent = await page.locator('.cm-content').innerText()
// drag tangentialArcTo handle
const tangentEnd = await u.getBoundingBox('[data-overlay-index="1"]')
await page.dragAndDrop('#stream', '#stream', {
sourcePosition: { x: tangentEnd.x, y: tangentEnd.y - 5 },
targetPosition: {
x: tangentEnd.x + dragPX,
y: tangentEnd.y + dragPX,
},
})
await page.waitForTimeout(100)
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
// expect the code to have changed
await expect(page.locator('.cm-content'))
.toHaveText(`const sketch001 = startSketchOn('XZ')
|> startProfileAt([7.12, -16.82], %)
|> line([15.4, -2.74], %)
|> tangentialArcTo([24.95, -5.38], %)
|> line([2.65, -2.69], %)
|> close(%)
|> extrude(5, %)`)
}
)
test('Can edit a sketch that has been revolved in the same pipe', async ({
page,
}) => {
const u = await getUtils(page)
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const sketch001 = startSketchOn('XZ')
|> startProfileAt([4.61, -14.01], %)
|> line([12.73, -0.09], %)
|> tangentialArcTo([24.95, -5.38], %)
|> close(%)
|> revolve({ axis: "X",}, %)`
)
})
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled()
await page.waitForTimeout(100)
await u.openAndClearDebugPanel()
await u.sendCustomCmd({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_look_at',
vantage: { x: 0, y: -1250, z: 580 },
center: { x: 0, y: 0, z: 0 },
up: { x: 0, y: 0, z: 1 },
},
})
await page.waitForTimeout(100)
await u.sendCustomCmd({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_get_settings',
},
})
await page.waitForTimeout(100)
const startPX = [665, 458]
const dragPX = 30
await page.getByText('startProfileAt([4.61, -14.01], %)').click()
await expect(
page.getByRole('button', { name: 'Edit Sketch' })
).toBeVisible()
await page.getByRole('button', { name: 'Edit Sketch' }).click()
await page.waitForTimeout(400)
let prevContent = await page.locator('.cm-content').innerText()
const step5 = { steps: 5 }
await expect(page.getByTestId('segment-overlay')).toHaveCount(2)
// drag startProfieAt handle
await page.mouse.move(startPX[0], startPX[1])
await page.mouse.down()
await page.mouse.move(startPX[0] + dragPX, startPX[1] - dragPX, step5)
await page.mouse.up()
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
prevContent = await page.locator('.cm-content').innerText()
// drag line handle
await page.waitForTimeout(100)
const lineEnd = await u.getBoundingBox('[data-overlay-index="0"]')
await page.mouse.move(lineEnd.x - 5, lineEnd.y)
await page.mouse.down()
await page.mouse.move(lineEnd.x + dragPX, lineEnd.y - dragPX, step5)
await page.mouse.up()
await page.waitForTimeout(100)
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
prevContent = await page.locator('.cm-content').innerText()
// drag tangentialArcTo handle
const tangentEnd = await u.getBoundingBox('[data-overlay-index="1"]')
await page.mouse.move(tangentEnd.x, tangentEnd.y - 5)
await page.mouse.down()
await page.mouse.move(tangentEnd.x + dragPX, tangentEnd.y - dragPX, step5)
await page.mouse.up()
await page.waitForTimeout(100)
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
// expect the code to have changed
await expect(page.locator('.cm-content'))
.toHaveText(`const sketch001 = startSketchOn('XZ')
|> startProfileAt([6.44, -12.07], %)
|> line([14.72, 1.97], %)
|> tangentialArcTo([24.95, -5.38], %)
|> line([1.97, 2.06], %)
|> close(%)
|> revolve({ axis: "X" }, %)`)
})
test('Can add multiple sketches', async ({ page }) => {
test.skip(process.platform === 'darwin', 'Can add multiple sketches')
const u = await getUtils(page)
const viewportSize = { width: 1200, height: 500 }
await page.setViewportSize(viewportSize)
await u.waitForAuthSkipAppStart()
await u.openDebugPanel()
const center = { x: viewportSize.width / 2, y: viewportSize.height / 2 }
const { toSU, click00r } = getMovementUtils({ center, page })
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled()
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).toBeVisible()
// click on "Start Sketch" button
await u.clearCommandLogs()
await u.doAndWaitForImageDiff(
() => page.getByRole('button', { name: 'Start Sketch' }).click(),
200
)
let codeStr = "const sketch001 = startSketchOn('XY')"
await page.mouse.click(center.x, viewportSize.height * 0.55)
await expect(u.codeLocator).toHaveText(codeStr)
await u.closeDebugPanel()
await page.waitForTimeout(500) // TODO detect animation ending, or disable animation
await click00r(0, 0)
codeStr += ` |> startProfileAt(${toSU([0, 0])}, %)`
await expect(u.codeLocator).toHaveText(codeStr)
await click00r(50, 0)
await page.waitForTimeout(100)
codeStr += ` |> line(${toSU([50, 0])}, %)`
await expect(u.codeLocator).toHaveText(codeStr)
await click00r(0, 50)
codeStr += ` |> line(${toSU([0, 50])}, %)`
await expect(u.codeLocator).toHaveText(codeStr)
await click00r(-50, 0)
codeStr += ` |> line(${toSU([-50, 0])}, %)`
await expect(u.codeLocator).toHaveText(codeStr)
// exit the sketch, reset relative clicker
click00r(undefined, undefined)
await u.openAndClearDebugPanel()
await page.getByRole('button', { name: 'Exit Sketch' }).click()
await u.expectCmdLog('[data-message-type="execution-done"]')
await page.waitForTimeout(250)
await u.clearCommandLogs()
// start a new sketch
await page.getByRole('button', { name: 'Start Sketch' }).click()
// when exiting the sketch above the camera is still looking down at XY,
// so selecting the plane again is a bit easier.
await page.mouse.click(center.x + 200, center.y + 100)
await page.waitForTimeout(600) // TODO detect animation ending, or disable animation
codeStr += "const sketch002 = startSketchOn('XY')"
await expect(u.codeLocator).toHaveText(codeStr)
await u.closeDebugPanel()
await click00r(30, 0)
codeStr += ` |> startProfileAt([1.53, 0], %)`
await expect(u.codeLocator).toHaveText(codeStr)
await click00r(30, 0)
codeStr += ` |> line([1.53, 0], %)`
await expect(u.codeLocator).toHaveText(codeStr)
await click00r(0, 30)
codeStr += ` |> line([0, -1.53], %)`
await expect(u.codeLocator).toHaveText(codeStr)
await click00r(-30, 0)
codeStr += ` |> line([-1.53, 0], %)`
await expect(u.codeLocator).toHaveText(codeStr)
await click00r(undefined, undefined)
await u.openAndClearDebugPanel()
await page.getByRole('button', { name: 'Exit Sketch' }).click()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.updateCamPosition([100, 100, 100])
await u.clearCommandLogs()
})
test.describe('Snap to close works (at any scale)', () => {
const doSnapAtDifferentScales = async (
page: any,
camPos: [number, number, number],
scale = 1
) => {
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
await u.openDebugPanel()
const code = `const sketch001 = startSketchOn('-XZ')
|> startProfileAt([${roundOff(scale * 69.6)}, ${roundOff(scale * 34.8)}], %)
|> line([${roundOff(scale * 139.19)}, 0], %)
|> line([0, -${roundOff(scale * 139.2)}], %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)`
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled()
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).toBeVisible()
await u.clearCommandLogs()
await page.getByRole('button', { name: 'Start Sketch' }).click()
await page.waitForTimeout(100)
await u.openAndClearDebugPanel()
await u.updateCamPosition(camPos)
await u.closeDebugPanel()
await page.mouse.move(0, 0)
// select a plane
await page.mouse.move(700, 200, { steps: 10 })
await page.mouse.click(700, 200, { delay: 200 })
await expect(page.locator('.cm-content')).toHaveText(
`const sketch001 = startSketchOn('-XZ')`
)
let prevContent = await page.locator('.cm-content').innerText()
const pointA = [700, 200]
const pointB = [900, 200]
const pointC = [900, 400]
// draw three lines
await page.waitForTimeout(500)
await page.mouse.move(pointA[0], pointA[1], { steps: 10 })
await page.mouse.click(pointA[0], pointA[1], { delay: 200 })
await page.waitForTimeout(100)
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
prevContent = await page.locator('.cm-content').innerText()
await page.mouse.move(pointB[0], pointB[1], { steps: 10 })
await page.mouse.click(pointB[0], pointB[1], { delay: 200 })
await page.waitForTimeout(100)
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
prevContent = await page.locator('.cm-content').innerText()
await page.mouse.move(pointC[0], pointC[1], { steps: 10 })
await page.mouse.click(pointC[0], pointC[1], { delay: 200 })
await page.waitForTimeout(100)
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
prevContent = await page.locator('.cm-content').innerText()
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.click(pointNotQuiteA[0], pointNotQuiteA[1], {
delay: 200,
})
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
prevContent = await page.locator('.cm-content').innerText()
await expect(page.locator('.cm-content')).toHaveText(code)
// Assert the tool was unequipped
await expect(
page.getByRole('button', { name: 'Line', exact: true })
).not.toHaveAttribute('aria-pressed', 'true')
// exit sketch
await u.openAndClearDebugPanel()
await page.getByRole('button', { name: 'Exit Sketch' }).click()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.removeCurrentCode()
}
test('[0, 100, 100]', async ({ page }) => {
await doSnapAtDifferentScales(page, [0, 100, 100], 0.01)
})
test('[0, 10000, 10000]', async ({ page }) => {
await doSnapAtDifferentScales(page, [0, 10000, 10000])
})
})
test('exiting a close extrude, has the extrude button enabled ready to go', async ({
page,
}) => {
// this was a regression https://github.com/KittyCAD/modeling-app/issues/2832
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const sketch001 = startSketchOn('XZ')
|> startProfileAt([-0.45, 0.87], %)
|> line([1.32, 0.38], %)
|> line([1.02, -1.32], %, $seg01)
|> line([-1.01, -0.77], %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
`
)
})
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
// click "line([1.32, 0.38], %)"
await page.getByText(`line([1.32, 0.38], %)`).click()
await page.waitForTimeout(100)
// click edit sketch
await page.getByRole('button', { name: 'Edit Sketch' }).click()
await page.waitForTimeout(600) // wait for animation
// exit sketch
await page.getByRole('button', { name: 'Exit Sketch' }).click()
// expect extrude button to be enabled
await expect(
page.getByRole('button', { name: 'Extrude' })
).not.toBeDisabled()
// click extrude
await page.getByRole('button', { name: 'Extrude' }).click()
// sketch selection should already have been made. "Selection: 1 face" only show up when the selection has been made already
// otherwise the cmdbar would be waiting for a selection.
await expect(
page.getByRole('button', { name: 'selection : 1 face', exact: false })
).toBeVisible()
})
test("Existing sketch with bad code delete user's code", async ({ page }) => {
// this was a regression https://github.com/KittyCAD/modeling-app/issues/2832
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const sketch001 = startSketchOn('XZ')
|> startProfileAt([-0.45, 0.87], %)
|> line([1.32, 0.38], %)
|> line([1.02, -1.32], %, $seg01)
|> line([-1.01, -0.77], %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
const extrude001 = extrude(5, sketch001)
`
)
})
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.closeDebugPanel()
await page.getByRole('button', { name: 'Start Sketch' }).click()
await page.mouse.click(622, 355)
await page.waitForTimeout(800)
await page.getByText(`END')`).click()
await page.keyboard.press('End')
await page.keyboard.press('Enter')
await page.keyboard.type(' |>', { delay: 100 })
await page.waitForTimeout(100)
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
await page.getByRole('button', { name: 'Exit Sketch' }).click()
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).toBeVisible()
await expect((await u.codeLocator.innerText()).replace(/\s/g, '')).toBe(
`const sketch001 = startSketchOn('XZ')
|> startProfileAt([-0.45, 0.87], %)
|> line([1.32, 0.38], %)
|> line([1.02, -1.32], %, $seg01)
|> line([-1.01, -0.77], %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
const extrude001 = extrude(5, sketch001)
const sketch002 = startSketchOn(extrude001, 'END')
|>
`.replace(/\s/g, '')
)
})
test('empty-scene default-planes act as expected', async ({
page,
browserName,
}) => {
test.skip(
browserName === 'webkit',
'Skip on Safari until `window.tearDown` is working there'
)
/**
* Tests the following things
* 1) The the planes are there on load because the scene is empty
* 2) The planes don't changes color when hovered initially
* 3) Putting something in the scene makes the planes hidden
* 4) Removing everything from the scene shows the plans again
* 3) Once "start sketch" is click, the planes do respond to hovers
* 4) Selecting a plan works as expected, i.e. sketch mode
* 5) Reloading the scene with something already in the scene means the planes are hidden
*/
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.closeDebugPanel()
const XYPlanePoint = { x: 774, y: 116 } as const
const unHoveredColor: [number, number, number] = [47, 47, 93]
expect(
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
).toBeLessThan(8)
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y)
await page.waitForTimeout(200)
// color should not change for having been hovered
expect(
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
).toBeLessThan(8)
await u.openAndClearDebugPanel()
await u.codeLocator.fill(`const sketch001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
|> xLine(-20, %)
`)
await u.expectCmdLog('[data-message-type="execution-done"]')
const noPlanesColor: [number, number, number] = [30, 30, 30]
expect(
await u.getGreatestPixDiff(XYPlanePoint, noPlanesColor)
).toBeLessThan(3)
await u.clearCommandLogs()
await u.removeCurrentCode()
await u.expectCmdLog('[data-message-type="execution-done"]')
await expect
.poll(() => u.getGreatestPixDiff(XYPlanePoint, unHoveredColor), {
timeout: 5_000,
})
.toBeLessThan(8)
// click start Sketch
await page.getByRole('button', { name: 'Start Sketch' }).click()
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y, { steps: 5 })
const hoveredColor: [number, number, number] = [93, 93, 127]
// now that we're expecting the user to select a plan, it does respond to hover
await expect
.poll(() => u.getGreatestPixDiff(XYPlanePoint, hoveredColor))
.toBeLessThan(8)
await page.mouse.click(XYPlanePoint.x, XYPlanePoint.y)
await page.waitForTimeout(600)
await page.mouse.click(XYPlanePoint.x, XYPlanePoint.y)
await page.waitForTimeout(200)
await page.mouse.click(XYPlanePoint.x + 50, XYPlanePoint.y + 50)
await expect(u.codeLocator)
.toHaveText(`const sketch001 = startSketchOn('XZ')
|> startProfileAt([11.8, 9.09], %)
|> line([3.39, -3.39], %)
`)
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const sketch001 = startSketchOn('XZ')
|> startProfileAt([11.8, 9.09], %)
|> line([3.39, -3.39], %)
`
)
})
await page.reload()
await u.waitForAuthSkipAppStart()
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.closeDebugPanel()
// expect there to be no planes on load since there's something in the scene
expect(
await u.getGreatestPixDiff(XYPlanePoint, noPlanesColor)
).toBeLessThan(3)
})
})

View File

@ -42,7 +42,10 @@ test.beforeEach(async ({ page }) => {
test.setTimeout(60_000)
test('exports of each format should work', async ({ page, context }) => {
test(
'exports of each format should work',
{ tag: '@snapshot' },
async ({ page, context }) => {
// FYI this test doesn't work with only engine running locally
// And you will need to have the KittyCAD CLI installed
const u = await getUtils(page)
@ -65,7 +68,7 @@ const part001 = startSketchOn('-XZ')
angle: topAng,
to: totalHeightHalf,
}, %, $seg04)
|> xLineTo(totalLen, %, $seg03')
|> xLineTo(totalLen, %, $seg03)
|> yLine(-armThick, %, $seg01)
|> angledLineThatIntersects({
angle: HALF_TURN,
@ -296,7 +299,8 @@ const part001 = startSketchOn('-XZ')
console.log(`snapshot failed for ${modelPath}`)
}
}
})
}
)
const extrudeDefaultPlane = async (context: any, page: any, plane: string) => {
await context.addInitScript(async () => {
@ -357,7 +361,10 @@ const extrudeDefaultPlane = async (context: any, page: any, plane: string) => {
await u.openKclCodePanel()
}
test.describe('extrude on default planes should be stable', () => {
test.describe(
'extrude on default planes should be stable',
{ tag: '@snapshot' },
() => {
test('XY', async ({ page, context }) => {
await extrudeDefaultPlane(context, page, 'XY')
})
@ -381,9 +388,13 @@ test.describe('extrude on default planes should be stable', () => {
test('-YZ', async ({ page, context }) => {
await extrudeDefaultPlane(context, page, '-YZ')
})
})
}
)
test('Draft segments should look right', async ({ page, context }) => {
test(
'Draft segments should look right',
{ tag: '@snapshot' },
async ({ page, context }) => {
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
const PUR = 400 / 37.5 //pixeltoUnitRatio
@ -394,7 +405,9 @@ test('Draft segments should look right', async ({ page, context }) => {
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled()
await expect(page.getByRole('button', { name: 'Start Sketch' })).toBeVisible()
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).toBeVisible()
// click on "Start Sketch" button
await u.clearCommandLogs()
@ -442,9 +455,13 @@ test('Draft segments should look right', async ({ page, context }) => {
await expect(page).toHaveScreenshot({
maxDiffPixels: 100,
})
})
}
)
test('Draft rectangles should look right', async ({ page, context }) => {
test(
'Draft rectangles should look right',
{ tag: '@snapshot' },
async ({ page, context }) => {
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
const PUR = 400 / 37.5 //pixeltoUnitRatio
@ -455,7 +472,9 @@ test('Draft rectangles should look right', async ({ page, context }) => {
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled()
await expect(page.getByRole('button', { name: 'Start Sketch' })).toBeVisible()
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).toBeVisible()
// click on "Start Sketch" button
await u.clearCommandLogs()
@ -490,9 +509,13 @@ test('Draft rectangles should look right', async ({ page, context }) => {
await expect(page).toHaveScreenshot({
maxDiffPixels: 100,
})
})
}
)
test.describe('Client side scene scale should match engine scale', () => {
test.describe(
'Client side scene scale should match engine scale',
{ tag: '@snapshot' },
() => {
test('Inch scale', async ({ page }) => {
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
@ -680,23 +703,27 @@ test.describe('Client side scene scale should match engine scale', () => {
maxDiffPixels: 100,
})
})
})
}
)
test('Sketch on face with none z-up', async ({ page, context }) => {
test(
'Sketch on face with none z-up',
{ tag: '@snapshot' },
async ({ page, context }) => {
const u = await getUtils(page)
await context.addInitScript(async (KCL_DEFAULT_LENGTH) => {
localStorage.setItem(
'persistCode',
`const part001 = startSketchOn('-XZ')
|> startProfileAt([1.4, 2.47], %)
|> line([9.31, 10.55], %, 'seg01')
|> line([9.31, 10.55], %, $seg01)
|> line([11.91, -10.42], %)
|> close(%)
|> extrude(${KCL_DEFAULT_LENGTH}, %)
const part002 = startSketchOn(part001, 'seg01')
const part002 = startSketchOn(part001, seg01)
|> startProfileAt([8, 8], %)
|> line([4.68, 3.05], %)
|> line([0, -7.79], %, 'seg02')
|> line([0, -7.79], %)
|> close(%)
|> extrude(${KCL_DEFAULT_LENGTH}, %)
`
@ -711,7 +738,7 @@ const part002 = startSketchOn(part001, 'seg01')
// wait for execution done
await expect(
page.locator('[data-message-type="execution-done"]')
).toHaveCount(2)
).toHaveCount(1, { timeout: 10_000 })
await u.closeDebugPanel()
// Wait for the second extrusion to appear
@ -728,7 +755,9 @@ const part002 = startSketchOn(part001, 'seg01')
// click at 641, 135
await page.mouse.click(641, 135)
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
await expect(page.locator('.cm-content')).not.toHaveText(
previousCodeContent
)
previousCodeContent = await page.locator('.cm-content').innerText()
await page.waitForTimeout(300)
@ -736,9 +765,13 @@ const part002 = startSketchOn(part001, 'seg01')
await expect(page).toHaveScreenshot({
maxDiffPixels: 100,
})
})
}
)
test('Zoom to fit on load - solid 2d', async ({ page, context }) => {
test(
'Zoom to fit on load - solid 2d',
{ tag: '@snapshot' },
async ({ page, context }) => {
const u = await getUtils(page)
await context.addInitScript(async () => {
localStorage.setItem(
@ -761,7 +794,7 @@ test('Zoom to fit on load - solid 2d', async ({ page, context }) => {
// wait for execution done
await expect(
page.locator('[data-message-type="execution-done"]')
).toHaveCount(2)
).toHaveCount(1)
await u.closeDebugPanel()
// Wait for the second extrusion to appear
@ -772,9 +805,13 @@ test('Zoom to fit on load - solid 2d', async ({ page, context }) => {
await expect(page).toHaveScreenshot({
maxDiffPixels: 100,
})
})
}
)
test('Zoom to fit on load - solid 3d', async ({ page, context }) => {
test(
'Zoom to fit on load - solid 3d',
{ tag: '@snapshot' },
async ({ page, context }) => {
const u = await getUtils(page)
await context.addInitScript(async () => {
localStorage.setItem(
@ -798,7 +835,7 @@ test('Zoom to fit on load - solid 3d', async ({ page, context }) => {
// wait for execution done
await expect(
page.locator('[data-message-type="execution-done"]')
).toHaveCount(2)
).toHaveCount(1)
await u.closeDebugPanel()
// Wait for the second extrusion to appear
@ -809,9 +846,10 @@ test('Zoom to fit on load - solid 3d', async ({ page, context }) => {
await expect(page).toHaveScreenshot({
maxDiffPixels: 100,
})
})
}
)
test.describe('Grid visibility', () => {
test.describe('Grid visibility', { tag: '@snapshot' }, () => {
test('Grid turned off', async ({ page }) => {
const u = await getUtils(page)
const stream = page.getByTestId('stream')
@ -829,7 +867,7 @@ test.describe('Grid visibility', () => {
// wait for execution done
await expect(
page.locator('[data-message-type="execution-done"]')
).toHaveCount(2)
).toHaveCount(1)
await u.closeDebugPanel()
await u.closeKclCodePanel()
// TODO: Find a way to truly know that the objects have finished
@ -877,7 +915,7 @@ test.describe('Grid visibility', () => {
// wait for execution done
await expect(
page.locator('[data-message-type="execution-done"]')
).toHaveCount(2)
).toHaveCount(1)
await u.closeDebugPanel()
await u.closeKclCodePanel()
// TODO: Find a way to truly know that the objects have finished

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

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