Compare commits

..

56 Commits

Author SHA1 Message Date
cf4655ff6a Test quote change windows 2024-12-06 14:10:34 -05:00
e338c39eab Fixmes, fixmes everywhere 2024-12-06 14:07:09 -05:00
d1d98897dc Merge branch 'main' into move-tests-to-electon 2024-12-06 13:20:47 -05:00
98b4aaea84 2024-12-06 13:19:31 -05:00
1faad75462 Hopefully help CI flake 2024-12-06 13:12:45 -05:00
496a6c2768 A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest-8-cores) 2024-12-06 17:07:18 +00:00
5bb1bf520e Fix flake in double click editor 2024-12-06 12:01:50 -05:00
a5377f50ff A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest-8-cores) 2024-12-06 16:32:20 +00:00
2fffa7e6d8 A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest-8-cores) 2024-12-06 16:28:02 +00:00
98c50eacc5 A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest-8-cores) 2024-12-06 16:27:02 +00:00
8f5c8447ea Stop snapshots of exports (already test in e2e) 2024-12-06 11:23:36 -05:00
ded9f9b89f Upgrade to playwright 1.49.0 to fix type errors 2024-12-06 11:19:30 -05:00
086bf9b736 Merge branch 'main' into move-tests-to-electon 2024-12-06 10:56:42 -05:00
129e07f059 Fix loft test 2024-12-05 17:22:18 -05:00
137e528944 Try CI again 2024-12-05 17:16:42 -05:00
b9a07c9393 Merge branch 'main' into move-tests-to-electon 2024-12-05 17:01:37 -05:00
f18f975e1c Add an f-ton of shit because playwright doesnt want S P R E A D 2024-12-05 17:01:24 -05:00
6cd1af23e6 Look at this (photo)Graph *in the voice of Nickelback* 2024-12-05 21:47:08 +00:00
cc247ace0e Remove to-electron script 2024-12-05 16:38:08 -05:00
68bcee9298 Correct all tsc errors 2024-12-05 16:37:56 -05:00
c2500c39e6 Merge branch 'main' into move-tests-to-electon 2024-12-04 13:39:51 -05:00
38016e0137 Fix a ton of syntax changes and deflake 2 more tests (pain) 2024-12-03 20:03:54 -05:00
b263ef049a Pass segment-overlays (minor ache) and ability to switch to web if needed :) 2024-12-03 13:13:01 -05:00
1bde0e7333 Pass text-to-cad tests 2024-11-29 18:35:10 -05:00
2301cc153a Pass app header tests 2024-11-29 17:44:41 -05:00
545332ee27 Pass samples loading tests 2024-11-29 17:34:16 -05:00
725e207bd1 Pass perspective-toggle, interesting note 2024-11-29 17:21:55 -05:00
75819f880e Pass another painful spec suite: testing-settings 2024-11-29 17:01:40 -05:00
3c1c1303e1 Merge main (tests changed x_x) and pass all constraints.spec tests (pain) 2024-11-29 14:12:19 -05:00
60e62d435e Merge branch 'main' into move-tests-to-electon 2024-11-29 11:47:16 -05:00
78616f4074 Extreme time eaten by gizmo test fixes. All passing now. 2024-11-28 18:11:28 -05:00
566183ffa7 Pass camera-movement.spec tests 2024-11-28 12:54:09 -05:00
3e435bebf4 Pass network and connection tests 2024-11-28 12:44:08 -05:00
faae28b025 Pass regresion-tests.spec tests 2024-11-28 12:14:27 -05:00
2d91549a02 Pass projects, fought hard with filechooser 2024-11-28 11:49:12 -05:00
46874f4d0a Pass machine.spec tests 2024-11-27 15:59:45 -05:00
a132b35f99 Painfully fix hardcoded coordinates in point-click.spec 2024-11-27 15:56:01 -05:00
6e04760e1c Corrected a fixme in file-tree.spec! 2024-11-27 14:02:01 -05:00
082b1cad74 Pass onboarding tests 2024-11-27 11:22:36 -05:00
84ddae384e Pass file tree tests 2024-11-26 10:53:59 -05:00
7566954b13 Pass editor tests 2024-11-26 10:30:59 -05:00
4b8c37c2c8 Pass desktop export tests 2024-11-25 14:17:46 -05:00
6d69b560be Pass debug pane tests 2024-11-25 12:51:49 -05:00
aa81d616cc Completely fix code mirror text navigating for tests 2024-11-25 12:50:09 -05:00
7ee5a1c26b fmt 2024-11-22 16:09:09 -05:00
0763d1d11d Pass command bar tests 2024-11-22 16:03:18 -05:00
0f52d7b21f Pass can-sketch-on-all-planes... with ease 2024-11-22 14:45:08 -05:00
25d252787c Pass various.spec.ts, by far the hardest one 2024-11-22 14:34:32 -05:00
1bd5dc20ef Go back to fix up sketch-tests test 2024-11-21 12:00:32 -05:00
cd6b9c2166 Pass testing-selections.spec.ts 2024-11-21 10:07:47 -05:00
f79a59d415 Got testing-segment-overlays passing 2024-11-20 16:45:57 -05:00
eda97a3058 Try out 4 workers 2024-11-20 16:45:57 -05:00
5cbb9d0697 Get sketch-tests.spec.ts passing in electron 2024-11-20 16:45:57 -05:00
be3f9b7b64 Add shebang to script and add macos-14-large as a target 2024-11-20 16:45:57 -05:00
6179c66ae9 Pass the correct param to playwright-electron.sh 2024-11-20 16:45:56 -05:00
117957d7c2 Move all tests over to electron 2024-11-20 16:45:56 -05:00
665 changed files with 10849 additions and 165671 deletions

View File

@ -1,3 +1,3 @@
[codespell]
ignore-words-list: crate,everytime,inout,co-ordinate,ot,nwo,atleast,ue,afterall
skip: **/target,node_modules,build,dist,./out,**/Cargo.lock,./docs/kcl/*.md,.yarn.lock,**/yarn.lock,./openapi/*.json,./packages/codemirror-lang-kcl/test/all.test.ts,tsconfig.tsbuildinfo
ignore-words-list: crate,everytime,inout,co-ordinate,ot,nwo,absolutey,atleast,ue,afterall
skip: **/target,node_modules,build,**/Cargo.lock,./docs/kcl/*.md,.yarn.lock,**/yarn.lock,./openapi/*.json,./src/lib/machine-api.d.ts

View File

@ -5,24 +5,16 @@
},
"plugins": [
"css-modules",
"jest",
"react",
"suggest-no-throw",
"@typescript-eslint"
],
"extends": [
"react-app",
"react-app/jest",
"plugin:css-modules/recommended"
],
"rules": {
"@typescript-eslint/no-floating-promises": "error",
"@typescript-eslint/no-misused-promises": "error",
"no-restricted-globals": [
"error",
{
"name": "isNaN",
"message": "Use Number.isNaN() instead."
}
],
"semi": [
"error",
"never"

View File

@ -6,11 +6,11 @@ set -euo pipefail
if [[ ! -f "test-results/.last-run.json" ]]; then
# if no last run artifact, than run plawright normally
echo "run playwright normally"
if [[ "$3" == *ubuntu* ]]; then
if [[ "$3" == ubuntu-latest* ]]; then
xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn test:playwright:electron:ubuntu -- --shard=$1/$2 || true
elif [[ "$3" == *windows* ]]; then
elif [[ "$3" == windows-latest* ]]; then
yarn test:playwright:electron:windows -- --shard=$1/$2 || true
elif [[ "$3" == *macos* ]]; then
elif [[ "$3" == macos-14* ]]; then
yarn test:playwright:electron:macos -- --shard=$1/$2 || true
else
echo "Do not run playwright. Unable to detect os runtime."
@ -21,7 +21,7 @@ if [[ ! -f "test-results/.last-run.json" ]]; then
fi
retry=1
max_retrys=5
max_retrys=4
# retry failed tests, doing our own retries because using inbuilt playwright retries causes connection issues
while [[ $retry -le $max_retrys ]]; do
@ -30,11 +30,11 @@ while [[ $retry -le $max_retrys ]]; do
if [[ $failed_tests -gt 0 ]]; then
echo "retried=true" >>$GITHUB_OUTPUT
echo "run playwright with last failed tests and retry $retry"
if [[ "$3" == *ubuntu* ]]; then
if [[ "$3" == ubuntu-latest* ]]; then
xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn test:playwright:electron:ubuntu -- --last-failed || true
elif [[ "$3" == *windows* ]]; then
elif [[ "$3" == windows-latest* ]]; then
yarn test:playwright:electron:windows -- --last-failed || true
elif [[ "$3" == *macos* ]]; then
elif [[ "$3" == macos-14* ]]; then
yarn test:playwright:electron:macos -- --last-failed || true
else
echo "Do not run playwright. Unable to detect os runtime."

View File

@ -5,28 +5,24 @@
version: 2
updates:
- package-ecosystem: 'npm' # See documentation for possible values
directory: '/' # Location of package manifests
schedule:
interval: 'weekly'
reviewers:
- franknoirot
- irev-dev
- package-ecosystem: 'github-actions' # See documentation for possible values
directory: '/' # Location of package manifests
schedule:
interval: 'weekly'
reviewers:
- adamchalmers
- jessfraz
- package-ecosystem: 'cargo' # See documentation for possible values
directory: '/src/wasm-lib/' # Location of package manifests
schedule:
interval: 'weekly'
reviewers:
- adamchalmers
- jessfraz
groups:
serde-dependencies:
patterns:
- "serde*"
- package-ecosystem: 'npm' # See documentation for possible values
directory: '/' # Location of package manifests
schedule:
interval: 'weekly'
reviewers:
- franknoirot
- irev-dev
- package-ecosystem: 'github-actions' # See documentation for possible values
directory: '/' # Location of package manifests
schedule:
interval: 'weekly'
reviewers:
- adamchalmers
- jessfraz
- package-ecosystem: 'cargo' # See documentation for possible values
directory: '/src/wasm-lib/' # Location of package manifests
schedule:
interval: 'weekly'
reviewers:
- adamchalmers
- jessfraz

View File

@ -165,6 +165,7 @@ jobs:
- name: Build the app (release)
if: ${{ env.IS_RELEASE == 'true' || env.IS_NIGHTLY == 'true' }}
env:
PUBLISH_FOR_PULL_REQUEST: true
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
@ -172,14 +173,9 @@ jobs:
CSC_LINK: ${{ secrets.APPLE_CERTIFICATE }}
CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
CSC_KEYCHAIN: ${{ secrets.APPLE_SIGNING_IDENTITY }}
CSC_FOR_PULL_REQUEST: true
WINDOWS_CERTIFICATE_THUMBPRINT: ${{ secrets.WINDOWS_CERTIFICATE_THUMBPRINT }}
DEBUG: "electron-notarize*"
# TODO: Fix electron-notarize flakes. The logs above should help gather more data on failures
uses: nick-fields/retry@v3.0.0
with:
timeout_minutes: 10
max_attempts: 3
command: yarn electron-builder --config --publish always
run: yarn electron-builder --config --publish always
- name: List artifacts in out/
run: ls -R out
@ -233,14 +229,9 @@ jobs:
CSC_LINK: ${{ secrets.APPLE_CERTIFICATE }}
CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
CSC_KEYCHAIN: ${{ secrets.APPLE_SIGNING_IDENTITY }}
CSC_FOR_PULL_REQUEST: true
WINDOWS_CERTIFICATE_THUMBPRINT: ${{ secrets.WINDOWS_CERTIFICATE_THUMBPRINT }}
DEBUG: "electron-notarize*"
# TODO: Fix electron-notarize flakes. The logs above should help gather more data on failures
uses: nick-fields/retry@v3.0.0
with:
timeout_minutes: 10
max_attempts: 3
command: yarn electron-builder --config --publish always
run: yarn electron-builder --config --publish always
- uses: actions/upload-artifact@v4
if: ${{ env.IS_RELEASE == 'true' }}
@ -374,7 +365,7 @@ jobs:
- name: Set more complete nightly release notes
if: ${{ env.IS_NIGHTLY == 'true' }}
run: |
# Note: preferred going this way instead of a full clone in the checkout step,
# Note: prefered going this way instead of a full clone in the checkout step,
# see https://github.com/actions/checkout/issues/1471
git fetch --prune --unshallow --tags
export TAG="nightly-${VERSION}"
@ -403,10 +394,6 @@ jobs:
parent: false
destination: 'dl.kittycad.io/releases/modeling-app/nightly'
- name: Invalidate bucket cache on latest*.yml and last_download.json files
if: ${{ env.IS_NIGHTLY == 'true' }}
run: yarn files:invalidate-bucket:nightly
- name: Tag nightly commit
if: ${{ env.IS_NIGHTLY == 'true' }}
uses: actions/github-script@v7

View File

@ -2,8 +2,28 @@ on:
push:
branches:
- main
paths:
- 'src/wasm-lib/**.rs'
- 'src/wasm-lib/**.hbs'
- 'src/wasm-lib/**.gen'
- 'src/wasm-lib/**.snap'
- '**/Cargo.toml'
- '**/Cargo.lock'
- '**/rust-toolchain.toml'
- 'src/wasm-lib/**.kcl'
- .github/workflows/cargo-test.yml
pull_request:
paths:
- 'src/wasm-lib/**.rs'
- 'src/wasm-lib/**.hbs'
- 'src/wasm-lib/**.gen'
- 'src/wasm-lib/**.snap'
- '**/Cargo.toml'
- '**/Cargo.lock'
- '**/rust-toolchain.toml'
- 'src/wasm-lib/**.kcl'
- .github/workflows/cargo-test.yml
workflow_dispatch:
permissions: read-all
concurrency:
@ -51,7 +71,7 @@ jobs:
KITTYCAD_API_TOKEN: ${{secrets.KITTYCAD_API_TOKEN}}
RUST_MIN_STACK: 10485760000
- name: Upload to codecov.io
uses: codecov/codecov-action@v5
uses: codecov/codecov-action@v4
with:
token: ${{secrets.CODECOV_TOKEN}}
fail_ci_if_error: true

View File

@ -1,7 +1,7 @@
name: E2E Tests
on:
push:
branches: [ main ]
branches: [ main, pierremtb/move-tests-to-electron ]
pull_request:
branches: [ main ]
@ -34,18 +34,19 @@ jobs:
- 'src/wasm-lib/**'
electron:
timeout-minutes: 60
timeout-minutes: ${{ matrix.os == 'macos-14' && 60 || 50 }}
name: playwright:electron:${{ matrix.os }} ${{ matrix.shardIndex }} ${{ matrix.shardTotal }}
strategy:
fail-fast: false
matrix:
# TODO: enable self-hosted-windows-8-cores once available
os: [namespace-profile-ubuntu-8-cores, namespace-profile-macos-8-cores, windows-16-cores]
os: [ubuntu-latest-8-cores, windows-latest-8-cores, macos-14-large]
shardIndex: [1, 2, 3, 4]
shardTotal: [4]
runs-on: ${{ matrix.os }}
needs: check-rust-changes
steps:
- name: Tune GitHub-hosted runner network
uses: smorimoto/tune-github-hosted-runner-network@v1
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
@ -100,8 +101,7 @@ jobs:
echo "/opt/homebrew/opt/gnu-sed/libexec/gnubin" >> $GITHUB_PATH
- name: Install vector
shell: bash
# TODO: figure out what to do with this, it's failing
if: false
if: ${{ !startsWith(matrix.os, 'windows') }}
run: |
curl --proto '=https' --tlsv1.2 -sSfL https://sh.vector.dev > /tmp/vector.sh
chmod +x /tmp/vector.sh
@ -127,12 +127,9 @@ jobs:
shell: bash
run: yarn tron:package
- name: Run ubuntu/chrome snapshots
if: ${{ matrix.os == 'namespace-profile-ubuntu-8-cores' && matrix.shardIndex == 1 }}
shell: bash
# TODO: break this in its own job, for now it's not slowing down the overall execution as ubuntu is the quickest,
# but we could do better. This forces a large 1/1 shard of all 20 snapshot tests that runs in about 3 minutes.
run: |
PLATFORM=web yarn playwright test --config=playwright.config.ts --retries="3" --update-snapshots --grep=@snapshot --shard=1/1
PLATFORM=web yarn playwright test --config=playwright.config.ts --retries="3" --update-snapshots --grep=@snapshot --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
env:
CI: true
NODE_ENV: development
@ -153,7 +150,6 @@ jobs:
continue-on-error: true
run: rm -r test-results
- name: check for changes
if: ${{ matrix.os == 'namespace-profile-ubuntu-8-cores' && matrix.shardIndex == 1 }}
shell: bash
id: git-check
run: |

View File

@ -126,7 +126,11 @@ jobs:
destination: 'dl.kittycad.io/releases/modeling-app'
- name: Invalidate bucket cache on latest*.yml and last_download.json files
run: yarn files:invalidate-bucket
run: |
gcloud compute url-maps invalidate-cdn-cache dl-url-map --path="/releases/modeling-app/last_download.json" --async
gcloud compute url-maps invalidate-cdn-cache dl-url-map --path="/releases/modeling-app/latest-linux-arm64.yml" --async
gcloud compute url-maps invalidate-cdn-cache dl-url-map --path="/releases/modeling-app/latest-mac.yml" --async
gcloud compute url-maps invalidate-cdn-cache dl-url-map --path="/releases/modeling-app/latest.yml" --async
- name: Upload release files to Github
if: ${{ github.event_name == 'release' }}

2
.nvmrc
View File

@ -1 +1 @@
v22.12.0
v21.7.3

View File

@ -1,43 +0,0 @@
# Setting Up Zoo Modeling App
Compared to other CAD software, getting Zoo Modeling App up and running is quick and straightforward across platforms. It's about 100MB to download and is quick to install.
## Windows
1. Download the [Zoo Modeling App installer](https://zoo.dev/modeling-app/download) for Windows and for your processor type.
2. Once downloaded, run the installer `Zoo Modeling App-{version}-{arch}-win.exe` which should take a few seconds.
3. The installation happens at `C:\Program Files\Zoo Modeling App`. A shortcut in the start menu is also created so you can run the app easily by clicking on it.
## macOS
1. Download the [Zoo Modeling App installer](https://zoo.dev/modeling-app/download) for macOS and for your processor type.
2. Once downloaded, open the disk image `Zoo Modeling App-{version}-{arch}-mac.dmg` and drag the applications to your `Applications` directory.
3. You can then open your `Applications` directory and double-click on `Zoo Modeling App` to open.
## Linux
1. Download the [Zoo Modeling App installer](https://zoo.dev/modeling-app/download) for Linux and for your processor type.
2. Install the dependencies needed to run the [AppImage format](https://appimage.org/).
- On Ubuntu, install the FUSE library with these commands in a terminal.
```bash
sudo apt update
sudo apt install libfuse2
```
- Optionally, follow [these steps](https://github.com/probonopd/go-appimage/blob/master/src/appimaged/README.md#initial-setup) to install `appimaged`. It is a daemon that makes interacting with AppImage files more seamless.
- Once installed, copy the downloaded `Zoo Modeling App-{version}-{arch}-linux.AppImage` to the directory of your choice, for instance `~/Applications`.
- `appimaged` should automatically find it and make it executable. If not, run:
```bash
chmod a+x ~/Applications/Zoo\ Modeling\ App-{version}-{arch}-linux.AppImage
```
3. You can double-click on the AppImage to run it, or in a terminal with this command:
```bash
~/Applications/Zoo\ Modeling\ App-{version}-{arch}-linux.AppImage
```

View File

@ -337,47 +337,13 @@ For individual testing:
yarn test abstractSyntaxTree -t "unexpected closed curly brace" --silent=false
```
Which will run our suite of [Vitest unit](https://vitest.dev/) and [React Testing Library E2E](https://testing-library.com/docs/react-testing-library/intro) tests, in interactive mode by default.
Which will run our suite of [Vitest unit](https://vitest.dev/) and [React Testing Library E2E](https://testing-library.com/docs/react-testing-library/intro/) tests, in interactive mode by default.
### Rust tests
**Dependencies**
- `KITTYCAD_API_TOKEN`
- `cargo-nextest`
- `just`
#### Setting KITTYCAD_API_TOKEN
Use the production zoo.dev token, set this environment variable before running the tests
#### Installing cargonextest
```
cd src/wasm-lib
cargo search cargo-nextest
cargo install cargo-nextest
```
#### just
install [`just`](https://github.com/casey/just?tab=readme-ov-file#pre-built-binaries)
#### Running the tests
```bash
# With just
# Make sure KITTYCAD_API_TOKEN=<prod zoo.dev token> is set
# Make sure you installed cargo-nextest
# Make sure you installed just
cd src/wasm-lib
just test
```
```bash
# Without just
# Make sure KITTYCAD_API_TOKEN=<prod zoo.dev token> is set
# Make sure you installed cargo-nextest
cd src/wasm-lib
export RUST_BRACKTRACE="full" && cargo nextest run --workspace --test-threads=1
KITTYCAD_API_TOKEN=XXX cargo test -- --test-threads=1
```
Where `XXX` is an API token from the production engine (NOT the dev environment).

View File

@ -22,7 +22,3 @@ once fixed in engine will just start working here with no language changes.
- **Chamfers**: Chamfers cannot intersect, you will get an error. Only simple
chamfer cases work currently.
- **Appearance**: Changing the appearance on a loft does not work.
- **Helix**: Currently sweeping a helix does not work.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -19,7 +19,6 @@ layout: manual
* [`angledLineThatIntersects`](kcl/angledLineThatIntersects)
* [`angledLineToX`](kcl/angledLineToX)
* [`angledLineToY`](kcl/angledLineToY)
* [`appearance`](kcl/appearance)
* [`arc`](kcl/arc)
* [`arcTo`](kcl/arcTo)
* [`asin`](kcl/asin)
@ -30,12 +29,10 @@ layout: manual
* [`assertLessThan`](kcl/assertLessThan)
* [`assertLessThanOrEq`](kcl/assertLessThanOrEq)
* [`atan`](kcl/atan)
* [`atan2`](kcl/atan2)
* [`bezierCurve`](kcl/bezierCurve)
* [`ceil`](kcl/ceil)
* [`chamfer`](kcl/chamfer)
* [`circle`](kcl/circle)
* [`circleThreePoint`](kcl/circleThreePoint)
* [`close`](kcl/close)
* [`cm`](kcl/cm)
* [`cos`](kcl/cos)
@ -48,7 +45,6 @@ layout: manual
* [`getOppositeEdge`](kcl/getOppositeEdge)
* [`getPreviousAdjacentEdge`](kcl/getPreviousAdjacentEdge)
* [`helix`](kcl/helix)
* [`helixRevolutions`](kcl/helixRevolutions)
* [`hole`](kcl/hole)
* [`hollow`](kcl/hollow)
* [`import`](kcl/import)
@ -82,7 +78,6 @@ layout: manual
* [`pi`](kcl/pi)
* [`polar`](kcl/polar)
* [`polygon`](kcl/polygon)
* [`pop`](kcl/pop)
* [`pow`](kcl/pow)
* [`profileStart`](kcl/profileStart)
* [`profileStartX`](kcl/profileStartX)
@ -104,8 +99,8 @@ layout: manual
* [`sin`](kcl/sin)
* [`sqrt`](kcl/sqrt)
* [`startProfileAt`](kcl/startProfileAt)
* [`startSketchAt`](kcl/startSketchAt)
* [`startSketchOn`](kcl/startSketchOn)
* [`sweep`](kcl/sweep)
* [`tan`](kcl/tan)
* [`tangentToEnd`](kcl/tangentToEnd)
* [`tangentialArc`](kcl/tangentialArc)

File diff suppressed because one or more lines are too long

View File

@ -45,7 +45,7 @@ circles = map([1..3], drawCircle)
```js
r = 10 // radius
// Call `map`, using an anonymous function instead of a named one.
circles = map([1..3], fn(id) {
circles = map([1..3], (id) {
return startSketchOn("XY")
|> circle({ center = [id * 2 * r, 0], radius = r }, %)
})

File diff suppressed because one or more lines are too long

View File

@ -35,7 +35,7 @@ The transform function returns a transform object. All properties of the object
- `rotation.origin` (either "local" i.e. rotate around its own center, "global" i.e. rotate around the scene's center, or a 3D point, defaults to "local")
```js
patternTransform(total_instances: integer, transform_function: FunctionParam, solid_set: SolidSet) -> [Solid]
patternTransform(total_instances: u32, transform_function: FunctionParam, solid_set: SolidSet) -> [Solid]
```
@ -43,7 +43,7 @@ patternTransform(total_instances: integer, transform_function: FunctionParam, so
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `total_instances` | `integer` | | Yes |
| `total_instances` | `u32` | | Yes |
| `transform_function` | `FunctionParam` | | Yes |
| `solid_set` | [`SolidSet`](/docs/kcl/types/SolidSet) | A solid or a group of solids. | Yes |
@ -95,8 +95,7 @@ fn cube(length, center) {
p2 = [l + x, l + y]
p3 = [l + x, -l + y]
return startSketchOn('XY')
|> startProfileAt(p0, %)
return startSketchAt(p0)
|> lineTo(p1, %)
|> lineTo(p2, %)
|> lineTo(p3, %)
@ -133,8 +132,7 @@ fn cube(length, center) {
p2 = [l + x, l + y]
p3 = [l + x, -l + y]
return startSketchOn('XY')
|> startProfileAt(p0, %)
return startSketchAt(p0)
|> lineTo(p1, %)
|> lineTo(p2, %)
|> lineTo(p3, %)
@ -197,8 +195,7 @@ fn transform(i) {
{ rotation = { angle = 45 * i } }
]
}
startSketchOn('XY')
|> startProfileAt([0, 0], %)
startSketchAt([0, 0])
|> polygon({
radius = 10,
numSides = 4,

View File

@ -9,7 +9,7 @@ Just like patternTransform, but works on 2D sketches not 3D solids.
```js
patternTransform2d(total_instances: integer, transform_function: FunctionParam, solid_set: SketchSet) -> [Sketch]
patternTransform2d(total_instances: u32, transform_function: FunctionParam, solid_set: SketchSet) -> [Sketch]
```
@ -17,7 +17,7 @@ patternTransform2d(total_instances: integer, transform_function: FunctionParam,
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `total_instances` | `integer` | | Yes |
| `total_instances` | `u32` | | Yes |
| `transform_function` | `FunctionParam` | | Yes |
| `solid_set` | [`SketchSet`](/docs/kcl/types/SketchSet) | A sketch or a group of sketches. | Yes |

File diff suppressed because one or more lines are too long

View File

@ -43,7 +43,7 @@ fn sum(arr) {
/* The above is basically like this pseudo-code:
fn sum(arr):
sumSoFar = 0
let sumSoFar = 0
for i in arr:
sumSoFar = add(sumSoFar, i)
return sumSoFar */
@ -61,7 +61,7 @@ assertEqual(sum([1, 2, 3]), 6, 0.00001, "1 + 2 + 3 summed is 6")
// an anonymous `add` function as its parameter, instead of declaring a
// named function outside.
arr = [1, 2, 3]
sum = reduce(arr, 0, fn(i, result_so_far) {
sum = reduce(arr, 0, (i, result_so_far) {
return i + result_so_far
})
@ -79,13 +79,12 @@ fn decagon(radius) {
stepAngle = 1 / 10 * tau()
// Start the decagon sketch at this point.
startOfDecagonSketch = startSketchOn('XY')
|> startProfileAt([cos(0) * radius, sin(0) * radius], %)
startOfDecagonSketch = startSketchAt([cos(0) * radius, sin(0) * radius])
// Use a `reduce` to draw the remaining decagon sides.
// For each number in the array 1..10, run the given function,
// Use a `reduce` to draw the remaining decagon sides.
// For each number in the array 1..10, run the given function,
// which takes a partially-sketched decagon and adds one more edge to it.
fullDecagon = reduce([1..10], startOfDecagonSketch, fn(i, partialDecagon) {
fullDecagon = reduce([1..10], startOfDecagonSketch, (i, partialDecagon) {
// Draw one edge of the decagon.
x = cos(stepAngle * i) * radius
y = sin(stepAngle * i) * radius
@ -97,15 +96,14 @@ fn decagon(radius) {
/* The `decagon` above is basically like this pseudo-code:
fn decagon(radius):
stepAngle = (1/10) * tau()
plane = startSketchOn('XY')
startOfDecagonSketch = startProfileAt([(cos(0)*radius), (sin(0) * radius)], plane)
let stepAngle = (1/10) * tau()
let startOfDecagonSketch = startSketchAt([(cos(0)*radius), (sin(0) * radius)])
// Here's the reduce part.
partialDecagon = startOfDecagonSketch
let partialDecagon = startOfDecagonSketch
for i in [1..10]:
x = cos(stepAngle * i) * radius
y = sin(stepAngle * i) * radius
let x = cos(stepAngle * i) * radius
let y = sin(stepAngle * i) * radius
partialDecagon = lineTo([x, y], partialDecagon)
fullDecagon = partialDecagon // it's now full
return fullDecagon */

File diff suppressed because one or more lines are too long

View File

@ -28,8 +28,7 @@ segEnd(tag: TagIdentifier) -> [number]
```js
w = 15
cube = startSketchOn('XY')
|> startProfileAt([0, 0], %)
cube = startSketchAt([0, 0])
|> line([w, 0], %, $line1)
|> line([0, w], %, $line2)
|> line([-w, 0], %, $line3)
@ -38,8 +37,7 @@ cube = startSketchOn('XY')
|> extrude(5, %)
fn cylinder(radius, tag) {
return startSketchOn('XY')
|> startProfileAt([0, 0], %)
return startSketchAt([0, 0])
|> circle({
radius = radius,
center = segEnd(tag)

View File

@ -28,8 +28,7 @@ segStart(tag: TagIdentifier) -> [number]
```js
w = 15
cube = startSketchOn('XY')
|> startProfileAt([0, 0], %)
cube = startSketchAt([0, 0])
|> line([w, 0], %, $line1)
|> line([0, w], %, $line2)
|> line([-w, 0], %, $line3)
@ -38,8 +37,7 @@ cube = startSketchOn('XY')
|> extrude(5, %)
fn cylinder(radius, tag) {
return startSketchOn('XY')
|> startProfileAt([0, 0], %)
return startSketchAt([0, 0])
|> circle({
radius = radius,
center = segStart(tag)

View File

@ -4,8 +4,6 @@ excerpt: "Start a new 2-dimensional sketch at a given point on the 'XY' plane."
layout: manual
---
**WARNING:** This function is deprecated.
Start a new 2-dimensional sketch at a given point on the 'XY' plane.

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -13,18 +13,13 @@ Data to draw an angled line.
An angle and length with explicitly named parameters
**Type:** `object`
[`PolarCoordsData`](/docs/kcl/types/PolarCoordsData)
## Properties
| Property | Type | Description | Required |
|----------|------|-------------|----------|
| `angle` |`number`| The angle of the line (in degrees). | No |
| `length` |`number`| The length of the line. | No |
----

View File

@ -1,23 +0,0 @@
---
title: "AppearanceData"
excerpt: "Data for appearance."
layout: manual
---
Data for appearance.
**Type:** `object`
## Properties
| Property | Type | Description | Required |
|----------|------|-------------|----------|
| `color` |`string`| Color of the new material, a hex string like "#ff0000". | No |
| `metalness` |`number` (**maximum:** 100.0)| Metalness of the new material, a percentage like 95.7. | No |
| `roughness` |`number` (**maximum:** 100.0)| Roughness of the new material, a percentage like 95.7. | No |

View File

@ -1,42 +0,0 @@
---
title: "Axis3dOrEdgeReference"
excerpt: "A 3D axis or tagged edge."
layout: manual
---
A 3D axis or tagged edge.
**This schema accepts any of the following:**
3D axis and origin.
[`AxisAndOrigin3d`](/docs/kcl/types/AxisAndOrigin3d)
----
Tagged edge.
[`EdgeReference`](/docs/kcl/types/EdgeReference)
----

View File

@ -1,10 +1,10 @@
---
title: "AxisAndOrigin2d"
excerpt: "A 2D axis and origin."
title: "AxisAndOrigin"
excerpt: "Axis and origin."
layout: manual
---
A 2D axis and origin.
Axis and origin.

View File

@ -1,105 +0,0 @@
---
title: "AxisAndOrigin3d"
excerpt: "A 3D axis and origin."
layout: manual
---
A 3D axis and origin.
**This schema accepts exactly one of the following:**
X-axis.
**enum:** `X`
----
Y-axis.
**enum:** `Y`
----
Z-axis.
**enum:** `Z`
----
Flip the X-axis.
**enum:** `-X`
----
Flip the Y-axis.
**enum:** `-Y`
----
Flip the Z-axis.
**enum:** `-Z`
----
**Type:** `object`
## Properties
| Property | Type | Description | Required |
|----------|------|-------------|----------|
| `custom` |`object`| | No |
----

View File

@ -1,19 +1,19 @@
---
title: "Axis2dOrEdgeReference"
excerpt: "A 2D axis or tagged edge."
title: "AxisOrEdgeReference"
excerpt: "Axis or tagged edge."
layout: manual
---
A 2D axis or tagged edge.
Axis or tagged edge.
**This schema accepts any of the following:**
2D axis and origin.
Axis and origin.
[`AxisAndOrigin2d`](/docs/kcl/types/AxisAndOrigin2d)
[`AxisAndOrigin`](/docs/kcl/types/AxisAndOrigin)

View File

@ -1,23 +0,0 @@
---
title: "CircleThreePointData"
excerpt: "Data for drawing a 3-point circle"
layout: manual
---
Data for drawing a 3-point circle
**Type:** `object`
## Properties
| Property | Type | Description | Required |
|----------|------|-------------|----------|
| `p1` |`[number, number]`| Point one for circle derivation. | No |
| `p2` |`[number, number]`| Point two for circle derivation. | No |
| `p3` |`[number, number]`| Point three for circle derivation. | No |

View File

@ -1,25 +0,0 @@
---
title: "Helix"
excerpt: "A helix."
layout: manual
---
A helix.
**Type:** `object`
## Properties
| Property | Type | Description | Required |
|----------|------|-------------|----------|
| `value` |`string`| The id of the helix. | No |
| `revolutions` |`number`| Number of revolutions. | No |
| `angleStart` |`number`| Start angle (in degrees). | No |
| `ccw` |`boolean`| Is the helix rotation counter clockwise? | No |
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |

View File

@ -1,10 +1,10 @@
---
title: "HelixData"
excerpt: "Data for a helix."
excerpt: "Data for helices."
layout: manual
---
Data for a helix.
Data for helices.
**Type:** `object`
@ -19,8 +19,6 @@ Data for a helix.
| `revolutions` |`number`| Number of revolutions. | No |
| `angleStart` |`number`| Start angle (in degrees). | No |
| `ccw` |`boolean`| Is the helix rotation counter clockwise? The default is `false`. | No |
| `length` |`number`| Length of the helix. | No |
| `radius` |`number`| Radius of the helix. | No |
| `axis` |[`Axis3dOrEdgeReference`](/docs/kcl/types/Axis3dOrEdgeReference)| Axis to use as mirror. | No |
| `length` |`number`| Length of the helix. If this argument is not provided, the height of the solid is used. | No |

View File

@ -1,24 +0,0 @@
---
title: "HelixRevolutionsData"
excerpt: "Data for helix revolutions."
layout: manual
---
Data for helix revolutions.
**Type:** `object`
## Properties
| Property | Type | Description | Required |
|----------|------|-------------|----------|
| `revolutions` |`number`| Number of revolutions. | No |
| `angleStart` |`number`| Start angle (in degrees). | No |
| `ccw` |`boolean`| Is the helix rotation counter clockwise? The default is `false`. | No |
| `length` |`number`| Length of the helix. If this argument is not provided, the height of the solid is used. | No |

View File

@ -1,25 +0,0 @@
---
title: "HelixValue"
excerpt: "A helix."
layout: manual
---
A helix.
**Type:** `object`
## Properties
| Property | Type | Description | Required |
|----------|------|-------------|----------|
| `value` |`string`| The id of the helix. | No |
| `revolutions` |`number`| Number of revolutions. | No |
| `angleStart` |`number`| Start angle (in degrees). | No |
| `ccw` |`boolean`| Is the helix rotation counter clockwise? | No |
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |

View File

@ -12,10 +12,5 @@ KCL value for an optional parameter which was not given an argument. (remember,
## Properties
| Property | Type | Description | Required |
|----------|------|-------------|----------|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |

View File

@ -285,27 +285,6 @@ An solid is a collection of extrude surfaces.
| `value` |`[` [`Solid`](/docs/kcl/types/Solid) `]`| | No |
----
A helix.
**Type:** `object`
## Properties
| Property | Type | Description | Required |
|----------|------|-------------|----------|
| `type` |enum: [`Helix`](/docs/kcl/types/Helix)| | No |
| `value` |`string`| The id of the helix. | No |
| `revolutions` |`number`| Number of revolutions. | No |
| `angleStart` |`number`| Start angle (in degrees). | No |
| `ccw` |`boolean`| Is the helix rotation counter clockwise? | No |
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
----
Data for an imported geometry.
@ -350,23 +329,6 @@ Data for an imported geometry.
## Properties
| Property | Type | Description | Required |
|----------|------|-------------|----------|
| `type` |enum: `Module`| | No |
| `value` |[`ModuleId`](/docs/kcl/types/ModuleId)| Any KCL value. | No |
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
----
**Type:** `object`
## Properties
| Property | Type | Description | Required |

View File

@ -16,6 +16,6 @@ Data for a mirror.
| Property | Type | Description | Required |
|----------|------|-------------|----------|
| `axis` |[`Axis2dOrEdgeReference`](/docs/kcl/types/Axis2dOrEdgeReference)| Axis to use as mirror. | No |
| `axis` |[`AxisOrEdgeReference`](/docs/kcl/types/AxisOrEdgeReference)| Axis to use as mirror. | No |

View File

@ -1,16 +0,0 @@
---
title: "ModuleId"
excerpt: "Identifier of a source file. Uses a u32 to keep the size small."
layout: manual
---
Identifier of a source file. Uses a u32 to keep the size small.
**Type:** `integer` (`uint32`)

View File

@ -17,7 +17,7 @@ Data for revolution surfaces.
| Property | Type | Description | Required |
|----------|------|-------------|----------|
| `angle` |`number` (**maximum:** 360.0) (**minimum:** -360.0)| Angle to revolve (in degrees). Default is 360. | No |
| `axis` |[`Axis2dOrEdgeReference`](/docs/kcl/types/Axis2dOrEdgeReference)| Axis of revolution. | No |
| `axis` |[`AxisOrEdgeReference`](/docs/kcl/types/AxisOrEdgeReference)| Axis of revolution. | No |
| `tolerance` |`number`| Tolerance for the revolve operation. | No |

View File

@ -1,23 +0,0 @@
---
title: "SweepData"
excerpt: "Data for a sweep."
layout: manual
---
Data for a sweep.
**Type:** `object`
## Properties
| Property | Type | Description | Required |
|----------|------|-------------|----------|
| `path` |[`SweepPath`](/docs/kcl/types/SweepPath)| The path to sweep along. | No |
| `sectional` |`boolean`| If true, the sweep will be broken up into sub-sweeps (extrusions, revolves, sweeps) based on the trajectory path components. | No |
| `tolerance` |`number`| Tolerance for the sweep operation. | No |

View File

@ -1,42 +0,0 @@
---
title: "SweepPath"
excerpt: "A path to sweep along."
layout: manual
---
A path to sweep along.
**This schema accepts any of the following:**
A path to sweep along.
[`Sketch`](/docs/kcl/types/Sketch)
----
A path to sweep along.
[`Helix`](/docs/kcl/types/Helix)
----

View File

@ -20,7 +20,7 @@ async function doBasicSketch(
await homePage.goToModelingScene()
await u.waitForPageLoad()
await page.waitForTimeout(1000)
await page.waitForTimeout()
await u.openDebugPanel()
// If we have the code pane open, we should see the code.
@ -149,7 +149,7 @@ test.describe('Basic sketch', () => {
await doBasicSketch(page, homePage, ['code'])
})
test('code pane closed at start', async ({ page, homePage }) => {
test.fixme('code pane closed at start', async ({ page, homePage }) => {
// Load the app with the code panes
await page.addInitScript(async (persistModelingContext) => {
localStorage.setItem(

View File

@ -49,7 +49,7 @@ test.describe('Code pane and errors', () => {
await expect(codePaneButtonHolder).toContainText('notification')
})
test.skip('Opening and closing the code pane will consistently show error diagnostics', async ({
test('Opening and closing the code pane will consistently show error diagnostics', async ({
page,
homePage,
editor,
@ -120,45 +120,46 @@ test.describe('Code pane and errors', () => {
await expect(page.locator('.cm-tooltip').first()).toBeVisible()
})
test.fixme(
'When error is not in view you can click the badge to scroll to it',
async ({ page, homePage, context }) => {
// Load the app with the working starter code
await context.addInitScript((code) => {
localStorage.setItem('persistCode', code)
}, TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW)
test.fixme('When error is not in view you can click the badge to scroll to it', async ({
page,
homePage,
context,
}) => {
// Load the app with the working starter code
await context.addInitScript((code) => {
localStorage.setItem('persistCode', code)
}, TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW)
await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene()
await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene()
await page.waitForTimeout(1000)
await page.waitForTimeout(1000)
// Ensure badge is present
const codePaneButtonHolder = page.locator('#code-button-holder')
await expect(codePaneButtonHolder).toContainText('notification')
// 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()
// 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()
// 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()
// 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(
'Modeling command failed: [ApiError { error_code: InternalEngine, message: "Solid3D revolve failed: sketch profile must lie entirely on one side of the revolution axis" }]'
)
.first()
).toBeVisible()
}
)
// Hover over the error to see the error message
await page.hover('.cm-lint-marker-error')
await expect(
page
.getByText(
'Modeling command failed: [ApiError { error_code: InternalEngine, message: "Solid3D revolve failed: 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 ({
context,

View File

@ -45,8 +45,7 @@ test.describe('Command bar tests', () => {
)
})
// TODO: fix this test after the electron migration
test.fixme('Fillet from command bar', async ({ page, homePage }) => {
test('Fillet from command bar', async ({ page, homePage }) => {
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',

View File

@ -54,108 +54,6 @@ test.describe('Editor tests', () => {
|> close(%)`)
})
test('ensure we use the cache, and do not re-execute', async ({
homePage,
page,
}) => {
const u = await getUtils(page)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await u.waitForPageLoad()
await u.codeLocator.click()
await page.keyboard.type(`sketch001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
|> line([-20, 0], %)
|> close(%)`)
// Ensure we execute the first time.
await u.openDebugPanel()
await expect(
page.locator('[data-receive-command-type="scene_clear_all"]')
).toHaveCount(1)
await expect(
page.locator('[data-message-type="execution-done"]')
).toHaveCount(2)
// Add whitespace to the end of the code.
await u.codeLocator.click()
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(' ')
await page.keyboard.press('Enter')
await page.keyboard.type(' ')
// Ensure we don't execute the second time.
await u.openDebugPanel()
// Make sure we didn't clear the scene.
await expect(
page.locator('[data-message-type="execution-done"]')
).toHaveCount(3)
await expect(
page.locator('[data-receive-command-type="scene_clear_all"]')
).toHaveCount(1)
})
test('ensure we use the cache, and do not clear on append', async ({
homePage,
page,
}) => {
const u = await getUtils(page)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await u.waitForPageLoad()
await u.codeLocator.click()
await page.keyboard.type(`sketch001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
|> line([-20, 0], %)
|> close(%)`)
// Ensure we execute the first time.
await u.openDebugPanel()
await expect(
page.locator('[data-receive-command-type="scene_clear_all"]')
).toHaveCount(1)
await expect(
page.locator('[data-message-type="execution-done"]')
).toHaveCount(2)
// Add whitespace to the end of the code.
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('End')
await page.keyboard.press('Enter')
await page.keyboard.press('Enter')
await page.keyboard.type('const x = 1')
await page.keyboard.press('Enter')
await u.openDebugPanel()
await expect(
page.locator('[data-message-type="execution-done"]')
).toHaveCount(3)
await expect(
page.locator('[data-receive-command-type="scene_clear_all"]')
).toHaveCount(1)
})
test('if you click the format button it formats your code', async ({
page,
homePage,
@ -555,22 +453,20 @@ test.describe('Editor tests', () => {
homePage,
}) => {
const u = await getUtils(page)
await page.setBodyDimensions({ width: 1200, height: 500 })
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
// 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)
* the old check here used $ but this is for tags so it changed meaning.
* hopefully ~ doesn't change meaning
~ error
/* 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')
await page.keyboard.type('$ error')
// press arrows to clear autocomplete
await page.keyboard.press('ArrowLeft')
@ -587,12 +483,10 @@ test.describe('Editor tests', () => {
// error text on hover
await page.hover('.cm-lint-marker-error')
await expect(
page.getByText("found unknown token '~'").first()
).toBeVisible()
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.getByText('$ error').click()
await page.keyboard.press('End')
await page.keyboard.down('Shift')
await page.keyboard.press('Home')
@ -628,14 +522,15 @@ test.describe('Editor tests', () => {
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
})
test.fixme(
'error with 2 source ranges gets 2 diagnostics',
async ({ page, homePage }) => {
const u = await getUtils(page)
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`length = .750
test.fixme('error with 2 source ranges gets 2 diagnostics', async ({
page,
homePage,
}) => {
const u = await getUtils(page)
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`length = .750
width = 0.500
height = 0.500
dia = 4
@ -650,53 +545,52 @@ test.describe('Editor tests', () => {
return squareHoleSketch
}
`
)
})
await page.setBodyDimensions({ width: 1000, height: 500 })
)
})
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await u.waitForPageLoad()
await page.waitForTimeout(1000)
await homePage.goToModelingScene()
await u.waitForPageLoad()
await page.waitForTimeout(1000)
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.closeDebugPanel()
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()
// 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(`extrusion = startSketchOn('XY')
// 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(`extrusion = startSketchOn('XY')
|> circle({ center: [0, 0], radius: 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()
// 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)
}
)
// 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 ({
context,
page,

View File

@ -1,127 +0,0 @@
import { test, expect } from './zoo-test'
import * as fsp from 'fs/promises'
import { join } from 'path'
const FEATURE_TREE_EXAMPLE_CODE = `export fn timesFive(x) {
return 5 * x
}
export fn triangle() {
return startSketchOn('XZ')
|> startProfileAt([0, 0], %)
|> xLine(10, %)
|> line([-10, -5], %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
}
length001 = timesFive(1) * 5
sketch001 = startSketchOn('XZ')
|> startProfileAt([20, 10], %)
|> line([10, 10], %)
|> angledLine([-45, length001], %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
revolve001 = revolve({ axis = "X" }, sketch001)
triangle()
|> extrude(30, %)
plane001 = offsetPlane('XY', 10)
sketch002 = startSketchOn(plane001)
|> startProfileAt([-20, 0], %)
|> line([5, -15], %)
|> xLine(-10, %)
|> lineTo([-40, 0], %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
extrude001 = extrude(10, sketch002)
`
test.describe('Feature Tree pane', () => {
test(
'User can go to definition and go to function definition',
{ tag: '@electron' },
async ({ context, homePage, scene, editor, toolbar }) => {
await context.folderSetupFn(async (dir) => {
const bracketDir = join(dir, 'test-sample')
await fsp.mkdir(bracketDir, { recursive: true })
await fsp.writeFile(
join(bracketDir, 'main.kcl'),
FEATURE_TREE_EXAMPLE_CODE,
'utf-8'
)
})
await test.step('setup test', async () => {
await homePage.expectState({
projectCards: [
{
title: 'test-sample',
fileCount: 1,
},
],
sortBy: 'last-modified-desc',
})
await homePage.openProject('test-sample')
await scene.waitForExecutionDone()
await editor.closePane()
await toolbar.openFeatureTreePane()
})
async function testViewSource({
operationName,
operationIndex,
expectedActiveLine,
}: {
operationName: string
operationIndex: number
expectedActiveLine: string
}) {
await test.step(`Go to definition of the ${operationName}`, async () => {
await toolbar.viewSourceOnOperation(operationName, operationIndex)
await editor.expectState({
highlightedCode: '',
diagnostics: [],
activeLines: [expectedActiveLine],
})
await expect(
editor.activeLine.first(),
`${operationName} code should be scrolled into view`
).toBeVisible()
})
}
await testViewSource({
operationName: 'Offset Plane',
operationIndex: 0,
expectedActiveLine: "plane001 = offsetPlane('XY', 10)",
})
await testViewSource({
operationName: 'Extrude',
operationIndex: 1,
expectedActiveLine: 'extrude001 = extrude(10, sketch002)',
})
await testViewSource({
operationName: 'Revolve',
operationIndex: 0,
expectedActiveLine: 'revolve001 = revolve({ axis = "X" }, sketch001)',
})
await testViewSource({
operationName: 'Triangle',
operationIndex: 0,
expectedActiveLine: 'triangle()',
})
await test.step('Go to definition on the triangle function', async () => {
await toolbar.goToDefinitionOnOperation('Triangle', 0)
await editor.expectState({
highlightedCode: '',
diagnostics: [],
activeLines: ['export fn triangle() {'],
})
await expect(
editor.activeLine.first(),
'Triangle function definition should be scrolled into view'
).toBeVisible()
})
}
)
})

View File

@ -92,8 +92,6 @@ test.describe('when using the file tree to', () => {
`rename ${fromFile} to ${toFile}, and doesn't crash on reload and settings load`,
{ tag: '@electron' },
async ({ page }, testInfo) => {
// TODO: fix this test on windows after the electron migration
test.skip(process.platform === 'win32', 'Skip on windows')
const { panesOpen, pasteCodeInEditor, renameFile, editorTextMatches } =
await getUtils(page, test)
@ -136,8 +134,6 @@ test.describe('when using the file tree to', () => {
`create many new files of the same name, incrementing their names`,
{ tag: '@electron' },
async ({ page }, testInfo) => {
// TODO: fix this test on windows after the electron migration
test.skip(process.platform === 'win32', 'Skip on windows')
const { panesOpen, createNewFile } = await getUtils(page, test)
await page.setBodyDimensions({ width: 1200, height: 500 })
@ -1018,8 +1014,6 @@ test.describe('Undo and redo do not keep history when navigating between files',
`open a file, change something, open a different file, hitting undo should do nothing`,
{ tag: '@electron' },
async ({ context, page }, testInfo) => {
// TODO: fix this test on windows after the electron migration
test.skip(process.platform === 'win32', 'Skip on windows')
await context.folderSetupFn(async (dir) => {
const testDir = join(dir, 'testProject')
await fsp.mkdir(testDir, { recursive: true })
@ -1088,8 +1082,6 @@ test.describe('Undo and redo do not keep history when navigating between files',
{ tag: '@electron' },
// Skip on windows i think the keybindings are different for redo.
async ({ context, page }, testInfo) => {
// TODO: fix this test on windows after the electron migration
test.skip(process.platform === 'win32', 'Skip on windows')
await context.folderSetupFn(async (dir) => {
const testDir = join(dir, 'testProject')
await fsp.mkdir(testDir, { recursive: true })

View File

@ -1,4 +1,4 @@
import type { Page, Locator } from '@playwright/test'
import type { Page } from '@playwright/test'
import { expect } from '@playwright/test'
type CmdBarSerialised =
@ -26,11 +26,9 @@ type CmdBarSerialised =
export class CmdBarFixture {
public page: Page
cmdBarOpenBtn!: Locator
constructor(page: Page) {
this.page = page
this.cmdBarOpenBtn = page.getByTestId('command-bar-open-button')
}
reConstruct = (page: Page) => {
this.page = page
@ -118,21 +116,4 @@ export class CmdBarFixture {
await this.page.keyboard.press('Enter')
}
}
openCmdBar = async (selectCmd?: 'promptToEdit') => {
// TODO why does this button not work in electron tests?
// await this.cmdBarOpenBtn.click()
await this.page.keyboard.down('ControlOrMeta')
await this.page.keyboard.press('KeyK')
await this.page.keyboard.up('ControlOrMeta')
await expect(this.page.getByPlaceholder('Search commands')).toBeVisible()
if (selectCmd === 'promptToEdit') {
const promptEditCommand = this.page.getByText(
'Use Zoo AI to edit your kcl'
)
await expect(promptEditCommand.first()).toBeVisible()
await promptEditCommand.first().scrollIntoViewIfNeeded()
await promptEditCommand.first().click()
}
}
}

View File

@ -20,7 +20,7 @@ export class EditorFixture {
private diagnosticsTooltip!: Locator
private diagnosticsGutterIcon!: Locator
private codeContent!: Locator
public activeLine!: Locator
private activeLine!: Locator
constructor(page: Page) {
this.page = page
@ -29,7 +29,7 @@ export class EditorFixture {
reConstruct = (page: Page) => {
this.page = page
this.codeContent = page.locator('.cm-content[data-language="kcl"]')
this.codeContent = page.locator('.cm-content')
this.diagnosticsTooltip = page.locator('.cm-tooltip-lint')
this.diagnosticsGutterIcon = page.locator('.cm-lint-marker-error')
this.activeLine = this.page.locator('.cm-activeLine')
@ -147,28 +147,20 @@ export class EditorFixture {
openPane() {
return openPane(this.page, this.paneButtonTestId)
}
scrollToText(text: string, placeCursor?: boolean) {
return this.page.evaluate(
(args: { text: string; placeCursor?: boolean }) => {
// error TS2339: Property 'docView' does not exist on type 'EditorView'.
// Except it does so :shrug:
// @ts-ignore
let index = window.editorManager._editorView?.docView.view.state.doc
.toString()
.indexOf(args.text)
window.editorManager._editorView?.focus()
window.editorManager._editorView?.dispatch({
selection: window.EditorSelection.create([
window.EditorSelection.cursor(index),
]),
effects: [
window.EditorView.scrollIntoView(
window.EditorSelection.range(index, index + 1)
),
],
})
},
{ text, placeCursor }
)
scrollToText(text: string) {
return this.page.evaluate((scrollToText: string) => {
// editorManager is available on the window object.
// @ts-ignore
let index = editorManager._editorView.docView.view.state.doc
.toString()
.indexOf(scrollToText)
// @ts-ignore
editorManager._editorView.dispatch({
selection: {
anchor: index,
},
scrollIntoView: true,
})
}, text)
}
}

View File

@ -21,8 +21,6 @@ export class AuthenticatedApp {
public readonly context: BrowserContext
public readonly testInfo: TestInfo
public readonly viewPortSize = { width: 1200, height: 500 }
public electronApp: undefined | ElectronApplication
public dir: string = ''
constructor(context: BrowserContext, page: Page, testInfo: TestInfo) {
this.context = context
@ -63,7 +61,7 @@ export class AuthenticatedTronApp {
public page: Page
public context: BrowserContext
public readonly testInfo: TestInfo
public electronApp: ElectronApplication | undefined
public electronApp?: ElectronApplication
public readonly viewPortSize = { width: 1200, height: 500 }
public dir: string = ''
@ -98,6 +96,8 @@ export class AuthenticatedTronApp {
// Setup localStorage, addCookies, reload
await setup(this.context, this.page, this.testInfo)
await page.setViewportSize(this.viewPortSize)
for (const key of unsafeTypedKeys(arg.fixtures)) {
const fixture = arg.fixtures[key]
if (

View File

@ -36,8 +36,7 @@ type DragFromHandler = (
export class SceneFixture {
public page: Page
public streamWrapper!: Locator
public loadingIndicator!: Locator
private exeIndicator!: Locator
constructor(page: Page) {
@ -65,8 +64,6 @@ export class SceneFixture {
this.page = page
this.exeIndicator = page.getByTestId('model-state-indicator-execution-done')
this.streamWrapper = page.getByTestId('stream')
this.loadingIndicator = this.streamWrapper.getByTestId('loading')
}
makeMouseHelpers = (
@ -221,7 +218,23 @@ export class SceneFixture {
coords: { x: number; y: number },
diff: number
) => {
await expectPixelColor(this.page, colour, coords, diff)
let finalValue = colour
await expect
.poll(async () => {
const pixel = (await getPixelRGBs(this.page)(coords, 1))[0]
if (!pixel) return null
finalValue = pixel
return pixel.every(
(channel, index) => Math.abs(channel - colour[index]) < diff
)
})
.toBeTruthy()
.catch((cause) => {
throw new Error(
`ExpectPixelColor: expecting ${colour} got ${finalValue}`,
{ cause }
)
})
}
get gizmo() {
@ -238,28 +251,3 @@ export class SceneFixture {
await buttonToTest.click()
}
}
export async function expectPixelColor(
page: Page,
colour: [number, number, number],
coords: { x: number; y: number },
diff: number
) {
let finalValue = colour
await expect
.poll(async () => {
const pixel = (await getPixelRGBs(page)(coords, 1))[0]
if (!pixel) return null
finalValue = pixel
return pixel.every(
(channel, index) => Math.abs(channel - colour[index]) < diff
)
})
.toBeTruthy()
.catch((cause) => {
throw new Error(
`ExpectPixelColor: expecting ${colour} got ${finalValue}`,
{ cause }
)
})
}

View File

@ -1,21 +1,12 @@
import type { Page, Locator } from '@playwright/test'
import { expect } from '../zoo-test'
import {
checkIfPaneIsOpen,
closePane,
doAndWaitForImageDiff,
openPane,
} from '../test-utils'
import { SidebarType } from 'components/ModelingSidebar/ModelingPanes'
import { SIDEBAR_BUTTON_SUFFIX } from 'lib/constants'
import { doAndWaitForImageDiff } from '../test-utils'
export class ToolbarFixture {
public page: Page
extrudeButton!: Locator
loftButton!: Locator
sweepButton!: Locator
shellButton!: Locator
offsetPlaneButton!: Locator
startSketchBtn!: Locator
lineBtn!: Locator
@ -28,10 +19,6 @@ export class ToolbarFixture {
filePane!: Locator
exeIndicator!: Locator
treeInputField!: Locator
/** The sidebar button for the Feature Tree pane */
featureTreeId = 'feature-tree' as const
/** The pane element for the Feature Tree */
featureTreePane!: Locator
constructor(page: Page) {
this.page = page
@ -41,8 +28,6 @@ export class ToolbarFixture {
this.page = page
this.extrudeButton = page.getByTestId('extrude')
this.loftButton = page.getByTestId('loft')
this.sweepButton = page.getByTestId('sweep')
this.shellButton = page.getByTestId('shell')
this.offsetPlaneButton = page.getByTestId('plane-offset')
this.startSketchBtn = page.getByTestId('sketch')
this.lineBtn = page.getByTestId('line')
@ -54,7 +39,6 @@ export class ToolbarFixture {
this.treeInputField = page.getByTestId('tree-input-field')
this.filePane = page.locator('#files-pane')
this.featureTreePane = page.locator('#feature-tree-pane')
this.fileCreateToast = page.getByText('Successfully created')
this.exeIndicator = page.getByTestId('model-state-indicator-execution-done')
}
@ -105,76 +89,4 @@ export class ToolbarFixture {
await expect(this.exeIndicator).toBeVisible({ timeout: 15_000 })
}
}
async closePane(paneId: SidebarType) {
return closePane(this.page, paneId + SIDEBAR_BUTTON_SUFFIX)
}
async openPane(paneId: SidebarType) {
return openPane(this.page, paneId + SIDEBAR_BUTTON_SUFFIX)
}
async checkIfPaneIsOpen(paneId: SidebarType) {
return checkIfPaneIsOpen(this.page, paneId + SIDEBAR_BUTTON_SUFFIX)
}
async openFeatureTreePane() {
return this.openPane(this.featureTreeId)
}
async closeFeatureTreePane() {
await this.closePane(this.featureTreeId)
}
async checkIfFeatureTreePaneIsOpen() {
return this.checkIfPaneIsOpen(this.featureTreeId)
}
/**
* Get a specific operation button from the Feature Tree pane
*/
async getFeatureTreeOperation(operationName: string, operationIndex: number) {
await this.openFeatureTreePane()
await expect(this.featureTreePane).toBeVisible()
return this.featureTreePane
.getByRole('button', {
name: operationName,
})
.nth(operationIndex)
}
/**
* View source on a specific operation in the Feature Tree pane.
* @param operationName The name of the operation type
* @param operationIndex The index out of operations of this type
*/
async viewSourceOnOperation(operationName: string, operationIndex: number) {
const operationButton = await this.getFeatureTreeOperation(
operationName,
operationIndex
)
const viewSourceMenuButton = this.page.getByRole('button', {
name: 'View KCL source code',
})
await operationButton.click({ button: 'right' })
await expect(viewSourceMenuButton).toBeVisible()
await viewSourceMenuButton.click()
}
/**
* Go to definition on a specific operation in the Feature Tree pane
*/
async goToDefinitionOnOperation(
operationName: string,
operationIndex: number
) {
const operationButton = await this.getFeatureTreeOperation(
operationName,
operationIndex
)
const goToDefinitionMenuButton = this.page.getByRole('button', {
name: 'View function definition',
})
await operationButton.click({ button: 'right' })
await expect(goToDefinitionMenuButton).toBeVisible()
await goToDefinitionMenuButton.click()
}
}

View File

@ -11,7 +11,6 @@ import {
TEST_SETTINGS_ONBOARDING_USER_MENU,
} from './storageStates'
import * as TOML from '@iarna/toml'
import { expectPixelColor } from './fixtures/sceneFixture'
// Because onboarding relies on an app setting we need to set it as incompletel
// for all these tests.
@ -28,7 +27,6 @@ test.describe('Onboarding tests', () => {
cleanProjectDir: true,
},
async ({ context, page, homePage }) => {
const u = await getUtils(page)
await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene()
@ -37,23 +35,10 @@ test.describe('Onboarding tests', () => {
page.getByText('Welcome to Modeling App! This')
).toBeVisible()
// 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'
)
// Make sure the model loaded
const XYPlanePoint = { x: 774, y: 116 } as const
const modelColor: [number, number, number] = [45, 45, 45]
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y)
expect(await u.getGreatestPixDiff(XYPlanePoint, modelColor)).toBeLessThan(
8
)
}
)
@ -91,14 +76,6 @@ test.describe('Onboarding tests', () => {
await expect(page.locator('.cm-content')).toContainText(
'// Shelf Bracket'
)
// TODO: jess make less shit
// Make sure the model loaded
//const XYPlanePoint = { x: 986, y: 522 } as const
//const modelColor: [number, number, number] = [76, 76, 76]
//await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y)
//await expectPixelColor(page, modelColor, XYPlanePoint, 8)
})
}
)
@ -148,7 +125,7 @@ test.describe('Onboarding tests', () => {
)
// There used to be old code here that checked if we stored the reset
// code into localStorage but that isn't the case on desktop. It gets
// code into localStorage but that isnt the case on desktop. It gets
// saved to the file system, which we have other tests for.
}
)
@ -437,7 +414,7 @@ test.describe('Onboarding tests', () => {
)
})
test.fixme(
test(
'Restarting onboarding on desktop takes one attempt',
{
appSettings: {
@ -509,15 +486,7 @@ test.fixme(
await test.step('Confirm that the onboarding has restarted', async () => {
await expect(tutorialProjectIndicator).toBeVisible()
await expect(tutorialModalText).toBeVisible()
// Make sure the model loaded
const XYPlanePoint = { x: 988, y: 523 } as const
const modelColor: [number, number, number] = [76, 76, 76]
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y)
await expectPixelColor(page, modelColor, XYPlanePoint, 8)
await tutorialDismissButton.click()
// Make sure model still there.
await expectPixelColor(page, modelColor, XYPlanePoint, 8)
})
await test.step('Clear code and restart onboarding from settings', async () => {

View File

@ -16,8 +16,6 @@ test('verify extruding circle works', async ({
toolbar,
scene,
}) => {
// TODO: fix this test on windows after the electron migration
test.skip(process.platform === 'win32', 'Skip on windows')
const file = await fs.readFile(
path.resolve(
__dirname,
@ -97,8 +95,6 @@ test('verify extruding circle works', async ({
})
test.describe('verify sketch on chamfer works', () => {
// TODO: fix this test on windows after the electron migration
test.skip(process.platform === 'win32', 'Skip on windows')
const _sketchOnAChamfer =
(
page: Page,
@ -756,17 +752,6 @@ test(`Offset plane point-and-click`, async ({
})
await scene.expectPixelColor([74, 74, 74], testPoint, 15)
})
await test.step('Delete offset plane via feature tree selection', async () => {
await editor.closePane()
const operationButton = await toolbar.getFeatureTreeOperation(
'Offset Plane',
0
)
await operationButton.click({ button: 'left' })
await page.keyboard.press('Backspace')
await scene.expectPixelColor([50, 51, 96], testPoint, 15)
})
})
const loftPointAndClickCases = [
@ -776,24 +761,29 @@ const loftPointAndClickCases = [
loftPointAndClickCases.forEach(({ shouldPreselect }) => {
test(`Loft point-and-click (preselected sketches: ${shouldPreselect})`, async ({
context,
page,
homePage,
page,
scene,
editor,
toolbar,
cmdBar,
}) => {
const u = await getUtils(page)
const initialCode = `sketch001 = startSketchOn('XZ')
|> circle({ center = [0, 0], radius = 30 }, %)
plane001 = offsetPlane('XZ', 50)
sketch002 = startSketchOn(plane001)
|> circle({ center = [0, 0], radius = 20 }, %)
`
await context.addInitScript((initialCode) => {
localStorage.setItem('persistCode', initialCode)
}, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 })
await context.addInitScript((code) => {
localStorage.setItem('persistCode', code)
}, initialCode)
await homePage.goToModelingScene()
await u.waitForPageLoad()
await page.waitForTimeout(1000)
// One dumb hardcoded screen pixel value
const testPoint = { x: 575, y: 200 }
@ -862,450 +852,5 @@ loftPointAndClickCases.forEach(({ shouldPreselect }) => {
})
await scene.expectPixelColor([89, 89, 89], testPoint, 15)
})
await test.step('Delete loft via feature tree selection', async () => {
await editor.closePane()
const operationButton = await toolbar.getFeatureTreeOperation('Loft', 0)
await operationButton.click({ button: 'left' })
await page.keyboard.press('Backspace')
await scene.expectPixelColor([254, 254, 254], testPoint, 15)
})
})
})
// TODO: merge with above test. Right now we're not able to delete a loft
// right after creation via selection for some reason, so we go with a new instance
test('Loft and offset plane deletion via selection', async ({
context,
page,
homePage,
scene,
}) => {
const initialCode = `sketch001 = startSketchOn('XZ')
|> circle({ center = [0, 0], radius = 30 }, %)
plane001 = offsetPlane('XZ', 50)
sketch002 = startSketchOn(plane001)
|> circle({ center = [0, 0], radius = 20 }, %)
loft001 = loft([sketch001, sketch002])
`
await context.addInitScript((initialCode) => {
localStorage.setItem('persistCode', initialCode)
}, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
// One dumb hardcoded screen pixel value
const testPoint = { x: 575, y: 200 }
const [clickOnSketch1] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
const [clickOnSketch2] = scene.makeMouseHelpers(testPoint.x, testPoint.y + 80)
await test.step(`Delete loft`, async () => {
// Check for loft
await scene.expectPixelColor([89, 89, 89], testPoint, 15)
await clickOnSketch1()
await expect(page.locator('.cm-activeLine')).toHaveText(`
|> circle({ center = [0, 0], radius = 30 }, %)
`)
await page.keyboard.press('Backspace')
// Check for sketch 1
await scene.expectPixelColor([254, 254, 254], testPoint, 15)
})
await test.step('Delete sketch002', async () => {
await page.waitForTimeout(1000)
await clickOnSketch2()
await expect(page.locator('.cm-activeLine')).toHaveText(`
|> circle({ center = [0, 0], radius = 20 }, %)
`)
await page.keyboard.press('Backspace')
// Check for plane001
await scene.expectPixelColor([228, 228, 228], testPoint, 15)
})
await test.step('Delete plane001', async () => {
await page.waitForTimeout(1000)
await clickOnSketch2()
await expect(page.locator('.cm-activeLine')).toHaveText(`
plane001 = offsetPlane('XZ', 50)
`)
await page.keyboard.press('Backspace')
// Check for sketch 1
await scene.expectPixelColor([254, 254, 254], testPoint, 15)
})
})
test(`Sweep point-and-click`, async ({
context,
page,
homePage,
scene,
editor,
toolbar,
cmdBar,
}) => {
const initialCode = `sketch001 = startSketchOn('YZ')
|> circle({
center = [0, 0],
radius = 500
}, %)
sketch002 = startSketchOn('XZ')
|> startProfileAt([0, 0], %)
|> xLine(-500, %)
|> tangentialArcTo([-2000, 500], %)
`
await context.addInitScript((initialCode) => {
localStorage.setItem('persistCode', initialCode)
}, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
// One dumb hardcoded screen pixel value
const testPoint = { x: 700, y: 250 }
const [clickOnSketch1] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
const [clickOnSketch2] = scene.makeMouseHelpers(testPoint.x - 50, testPoint.y)
const sweepDeclaration = 'sweep001 = sweep({ path = sketch002 }, sketch001)'
await test.step(`Look for sketch001`, async () => {
await toolbar.closePane('code')
await scene.expectPixelColor([53, 53, 53], testPoint, 15)
})
await test.step(`Go through the command bar flow`, async () => {
await toolbar.sweepButton.click()
await cmdBar.expectState({
commandName: 'Sweep',
currentArgKey: 'profile',
currentArgValue: '',
headerArguments: {
Path: '',
Profile: '',
},
highlightedHeaderArg: 'profile',
stage: 'arguments',
})
await clickOnSketch1()
await cmdBar.expectState({
commandName: 'Sweep',
currentArgKey: 'path',
currentArgValue: '',
headerArguments: {
Path: '',
Profile: '1 face',
},
highlightedHeaderArg: 'path',
stage: 'arguments',
})
await clickOnSketch2()
await cmdBar.expectState({
commandName: 'Sweep',
headerArguments: {
Path: '1 face',
Profile: '1 face',
},
stage: 'review',
})
await cmdBar.progressCmdBar()
})
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
await scene.expectPixelColor([135, 64, 73], testPoint, 15)
await toolbar.openPane('code')
await editor.expectEditor.toContain(sweepDeclaration)
await editor.expectState({
diagnostics: [],
activeLines: [sweepDeclaration],
highlightedCode: '',
})
await toolbar.closePane('code')
})
await test.step('Delete sweep via feature tree selection', async () => {
await toolbar.openPane('feature-tree')
await page.waitForTimeout(500)
const operationButton = await toolbar.getFeatureTreeOperation('Sweep', 0)
await operationButton.click({ button: 'left' })
await page.keyboard.press('Backspace')
await page.waitForTimeout(500)
await toolbar.closePane('feature-tree')
await scene.expectPixelColor([53, 53, 53], testPoint, 15)
})
})
const shellPointAndClickCapCases = [
{ shouldPreselect: true },
{ shouldPreselect: false },
]
shellPointAndClickCapCases.forEach(({ shouldPreselect }) => {
test(`Shell point-and-click cap (preselected sketches: ${shouldPreselect})`, async ({
context,
page,
homePage,
scene,
editor,
toolbar,
cmdBar,
}) => {
// TODO: fix this test on windows after the electron migration
test.skip(process.platform === 'win32', 'Skip on windows')
const initialCode = `sketch001 = startSketchOn('XZ')
|> circle({ center = [0, 0], radius = 30 }, %)
extrude001 = extrude(30, sketch001)
`
await context.addInitScript((initialCode) => {
localStorage.setItem('persistCode', initialCode)
}, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
// One dumb hardcoded screen pixel value
const testPoint = { x: 575, y: 200 }
const [clickOnCap] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
const shellDeclaration =
"shell001 = shell({ faces = ['end'], thickness = 5 }, extrude001)"
await test.step(`Look for the grey of the shape`, async () => {
await scene.expectPixelColor([127, 127, 127], testPoint, 15)
})
if (!shouldPreselect) {
await test.step(`Go through the command bar flow without preselected faces`, async () => {
await toolbar.shellButton.click()
await cmdBar.expectState({
stage: 'arguments',
currentArgKey: 'selection',
currentArgValue: '',
headerArguments: {
Selection: '',
Thickness: '',
},
highlightedHeaderArg: 'selection',
commandName: 'Shell',
})
await clickOnCap()
await page.waitForTimeout(500)
await cmdBar.progressCmdBar()
await cmdBar.progressCmdBar()
await cmdBar.expectState({
stage: 'review',
headerArguments: {
Selection: '1 cap',
Thickness: '5',
},
commandName: 'Shell',
})
await cmdBar.progressCmdBar()
})
} else {
await test.step(`Preselect the cap`, async () => {
await clickOnCap()
await page.waitForTimeout(500)
})
await test.step(`Go through the command bar flow with a preselected face (cap)`, async () => {
await toolbar.shellButton.click()
await cmdBar.progressCmdBar()
await cmdBar.progressCmdBar()
await cmdBar.expectState({
stage: 'review',
headerArguments: {
Selection: '1 cap',
Thickness: '5',
},
commandName: 'Shell',
})
await cmdBar.progressCmdBar()
})
}
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
await editor.expectEditor.toContain(shellDeclaration)
await editor.expectState({
diagnostics: [],
activeLines: [shellDeclaration],
highlightedCode: '',
})
await scene.expectPixelColor([146, 146, 146], testPoint, 15)
})
})
})
test('Shell point-and-click wall', async ({
context,
page,
homePage,
scene,
editor,
toolbar,
cmdBar,
}) => {
const initialCode = `sketch001 = startSketchOn('XY')
|> startProfileAt([-20, 20], %)
|> xLine(40, %)
|> yLine(-60, %)
|> xLine(-40, %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
extrude001 = extrude(40, sketch001)
`
await context.addInitScript((initialCode) => {
localStorage.setItem('persistCode', initialCode)
}, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
// One dumb hardcoded screen pixel value
const testPoint = { x: 580, y: 180 }
const [clickOnCap] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
const [clickOnWall] = scene.makeMouseHelpers(testPoint.x, testPoint.y + 70)
const mutatedCode = 'xLine(-40, %, $seg01)'
const shellDeclaration =
"shell001 = shell({ faces = ['end', seg01], thickness = 5}, extrude001)"
const formattedOutLastLine = '}, extrude001)'
await test.step(`Look for the grey of the shape`, async () => {
await scene.expectPixelColor([99, 99, 99], testPoint, 15)
})
await test.step(`Go through the command bar flow, selecting a wall and keeping default thickness`, async () => {
await toolbar.shellButton.click()
await cmdBar.expectState({
stage: 'arguments',
currentArgKey: 'selection',
currentArgValue: '',
headerArguments: {
Selection: '',
Thickness: '',
},
highlightedHeaderArg: 'selection',
commandName: 'Shell',
})
await clickOnCap()
await page.keyboard.down('Shift')
await clickOnWall()
await page.waitForTimeout(500)
await page.keyboard.up('Shift')
await cmdBar.progressCmdBar()
await cmdBar.progressCmdBar()
await cmdBar.expectState({
stage: 'review',
headerArguments: {
Selection: '1 cap, 1 face',
Thickness: '5',
},
commandName: 'Shell',
})
await cmdBar.progressCmdBar()
})
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
await editor.expectEditor.toContain(mutatedCode)
await editor.expectEditor.toContain(shellDeclaration)
await editor.expectState({
diagnostics: [],
activeLines: [formattedOutLastLine],
highlightedCode: '',
})
await scene.expectPixelColor([49, 49, 49], testPoint, 15)
})
await test.step('Delete shell via feature tree selection', async () => {
await editor.closePane()
const operationButton = await toolbar.getFeatureTreeOperation('Shell', 0)
await operationButton.click({ button: 'left' })
await page.keyboard.press('Backspace')
await scene.expectPixelColor([99, 99, 99], testPoint, 15)
})
})
const shellSketchOnFacesCases = [
`sketch001 = startSketchOn('XZ')
|> circle({ center = [0, 0], radius = 100 }, %)
|> extrude(100, %)
sketch002 = startSketchOn(sketch001, 'END')
|> circle({ center = [0, 0], radius = 50 }, %)
|> extrude(50, %)
`,
`sketch001 = startSketchOn('XZ')
|> circle({ center = [0, 0], radius = 100 }, %)
extrude001 = extrude(100, sketch001)
sketch002 = startSketchOn(extrude001, 'END')
|> circle({ center = [0, 0], radius = 50 }, %)
extrude002 = extrude(50, sketch002)
`,
]
shellSketchOnFacesCases.forEach((initialCode, index) => {
const hasExtrudesInPipe = index === 0
test(`Shell point-and-click sketch on face (extrudes in pipes: ${hasExtrudesInPipe})`, async ({
context,
page,
homePage,
scene,
editor,
toolbar,
cmdBar,
}) => {
await context.addInitScript((initialCode) => {
localStorage.setItem('persistCode', initialCode)
}, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
// One dumb hardcoded screen pixel value
const testPoint = { x: 550, y: 295 }
const [clickOnCap] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
const shellDeclaration = `shell001 = shell({ faces = ['end'], thickness = 5 }, ${
hasExtrudesInPipe ? 'sketch002' : 'extrude002'
})`
await test.step(`Look for the grey of the shape`, async () => {
await toolbar.closePane('code')
await scene.expectPixelColor([128, 128, 128], testPoint, 15)
})
await test.step(`Go through the command bar flow, selecting a cap and keeping default thickness`, async () => {
await toolbar.shellButton.click()
await cmdBar.expectState({
stage: 'arguments',
currentArgKey: 'selection',
currentArgValue: '',
headerArguments: {
Selection: '',
Thickness: '',
},
highlightedHeaderArg: 'selection',
commandName: 'Shell',
})
await clickOnCap()
await page.waitForTimeout(500)
await cmdBar.progressCmdBar()
await page.waitForTimeout(500)
await cmdBar.progressCmdBar()
await page.waitForTimeout(500)
await cmdBar.expectState({
stage: 'review',
headerArguments: {
Selection: '1 cap',
Thickness: '5',
},
commandName: 'Shell',
})
await cmdBar.progressCmdBar()
})
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
await toolbar.openPane('code')
await editor.expectEditor.toContain(shellDeclaration)
await editor.expectState({
diagnostics: [],
activeLines: [shellDeclaration],
highlightedCode: '',
})
await toolbar.closePane('code')
await scene.expectPixelColor([73, 73, 73], testPoint, 15)
})
})
})

View File

@ -114,308 +114,6 @@ test(
}
)
test(
'open a file in a project works and renders, open another file in different project with errors, it should clear the scene',
{ tag: '@electron' },
async ({ context, page }, testInfo) => {
await context.folderSetupFn(async (dir) => {
const bracketDir = path.join(dir, 'bracket')
await fsp.mkdir(bracketDir, { recursive: true })
await fsp.copyFile(
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
path.join(bracketDir, 'main.kcl')
)
const errorDir = path.join(dir, 'broken-code')
await fsp.mkdir(errorDir, { recursive: true })
await fsp.copyFile(
executorInputPath('broken-code-test.kcl'),
path.join(errorDir, 'main.kcl')
)
})
await page.setBodyDimensions({ width: 1200, height: 500 })
const u = await getUtils(page)
page.on('console', console.log)
const pointOnModel = { x: 630, y: 280 }
await test.step('Opening the bracket project should load the stream', async () => {
// expect to see the text bracket
await expect(page.getByText('bracket')).toBeVisible()
await page.getByText('bracket').click()
await expect(page.getByTestId('loading')).toBeAttached()
await expect(page.getByTestId('loading')).not.toBeAttached({
timeout: 20_000,
})
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).toBeEnabled({
timeout: 20_000,
})
// gray at this pixel means the stream has loaded in the most
// user way we can verify it (pixel color)
await expect
.poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
timeout: 10_000,
})
.toBeLessThan(15)
})
await test.step('Clicking the logo takes us back to the projects page / home', async () => {
await page.getByTestId('app-logo').click()
await expect(page.getByRole('link', { name: 'bracket' })).toBeVisible()
await expect(page.getByText('broken-code')).toBeVisible()
await expect(page.getByText('bracket')).toBeVisible()
await expect(page.getByText('New Project')).toBeVisible()
})
await test.step('opening broken code project should clear the scene and show the error', async () => {
// Go back home.
await expect(page.getByText('broken-code')).toBeVisible()
await page.getByText('broken-code').click()
// error in guter
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
// error text on hover
await page.hover('.cm-lint-marker-error')
const crypticErrorText = `Expected a tag declarator`
await expect(page.getByText(crypticErrorText).first()).toBeVisible()
// black pixel means the scene has been cleared.
await expect
.poll(() => u.getGreatestPixDiff(pointOnModel, [30, 30, 30]), {
timeout: 10_000,
})
.toBeLessThan(15)
})
}
)
test(
'open a file in a project works and renders, open another file in different project that is empty, it should clear the scene',
{ tag: '@electron' },
async ({ context, page }, testInfo) => {
await context.folderSetupFn(async (dir) => {
const bracketDir = path.join(dir, 'bracket')
await fsp.mkdir(bracketDir, { recursive: true })
await fsp.copyFile(
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
path.join(bracketDir, 'main.kcl')
)
const emptyDir = path.join(dir, 'empty')
await fsp.mkdir(emptyDir, { recursive: true })
await fsp.writeFile(path.join(emptyDir, 'main.kcl'), '')
})
await page.setBodyDimensions({ width: 1200, height: 500 })
const u = await getUtils(page)
page.on('console', console.log)
const pointOnModel = { x: 630, y: 280 }
await test.step('Opening the bracket project should load the stream', async () => {
// expect to see the text bracket
await expect(page.getByText('bracket')).toBeVisible()
await page.getByText('bracket').click()
await expect(page.getByTestId('loading')).toBeAttached()
await expect(page.getByTestId('loading')).not.toBeAttached({
timeout: 20_000,
})
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).toBeEnabled({
timeout: 20_000,
})
// gray at this pixel means the stream has loaded in the most
// user way we can verify it (pixel color)
await expect
.poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
timeout: 10_000,
})
.toBeLessThan(15)
})
await test.step('Clicking the logo takes us back to the projects page / home', async () => {
await page.getByTestId('app-logo').click()
await expect(page.getByRole('link', { name: 'bracket' })).toBeVisible()
await expect(page.getByText('empty')).toBeVisible()
await expect(page.getByText('bracket')).toBeVisible()
await expect(page.getByText('New Project')).toBeVisible()
})
await test.step('opening empty code project should clear the scene', async () => {
// Go back home.
await expect(page.getByText('empty')).toBeVisible()
await page.getByText('empty').click()
// Ensure the code is empty.
await expect(u.codeLocator).toContainText('')
expect(u.codeLocator.innerHTML.length).toBeLessThan(2)
// planes colors means the scene has been cleared.
await expect
.poll(() => u.getGreatestPixDiff(pointOnModel, [92, 53, 53]), {
timeout: 10_000,
})
.toBeLessThan(15)
})
}
)
test(
'open a file in a project works and renders, open empty file, it should clear the scene',
{ tag: '@electron' },
async ({ context, page }, testInfo) => {
await context.folderSetupFn(async (dir) => {
const bracketDir = path.join(dir, 'bracket')
await fsp.mkdir(bracketDir, { recursive: true })
await fsp.copyFile(
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
path.join(bracketDir, 'main.kcl')
)
await fsp.writeFile(path.join(bracketDir, 'empty.kcl'), '')
})
await page.setBodyDimensions({ width: 1200, height: 500 })
const u = await getUtils(page)
page.on('console', console.log)
const pointOnModel = { x: 630, y: 280 }
await test.step('Opening the bracket project should load the stream', async () => {
// expect to see the text bracket
await expect(page.getByText('bracket')).toBeVisible()
await page.getByText('bracket').click()
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).toBeEnabled({
timeout: 20_000,
})
// gray at this pixel means the stream has loaded in the most
// user way we can verify it (pixel color)
await expect
.poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
timeout: 10_000,
})
.toBeLessThan(15)
})
await test.step('creating a empty file should clear the scene', async () => {
// open the file pane.
await page.getByTestId('files-pane-button').click()
// OPen the other file.
const file = page.getByRole('button', { name: 'empty.kcl' })
await expect(file).toBeVisible()
await file.click()
// planes colors means the scene has been cleared.
await expect
.poll(() => u.getGreatestPixDiff(pointOnModel, [92, 53, 53]), {
timeout: 10_000,
})
.toBeLessThan(15)
// Ensure the code is empty.
await expect(u.codeLocator).toContainText('')
expect(u.codeLocator.innerHTML.length).toBeLessThan(2)
})
}
)
test(
'open a file in a project works and renders, open another file in the same project with errors, it should clear the scene',
{ tag: '@electron' },
async ({ context, page }, testInfo) => {
await context.folderSetupFn(async (dir) => {
const bracketDir = path.join(dir, 'bracket')
await fsp.mkdir(bracketDir, { recursive: true })
await fsp.copyFile(
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
path.join(bracketDir, 'main.kcl')
)
await fsp.copyFile(
executorInputPath('broken-code-test.kcl'),
path.join(bracketDir, 'broken-code-test.kcl')
)
})
await page.setBodyDimensions({ width: 1200, height: 500 })
const u = await getUtils(page)
const pointOnModel = { x: 630, y: 280 }
await test.step('Opening the bracket project should load the stream', async () => {
// expect to see the text bracket
await expect(page.getByText('bracket')).toBeVisible()
await page.getByText('bracket').click()
await expect(page.getByTestId('loading')).toBeAttached()
await expect(page.getByTestId('loading')).not.toBeAttached({
timeout: 20_000,
})
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).toBeEnabled({
timeout: 20_000,
})
// gray at this pixel means the stream has loaded in the most
// user way we can verify it (pixel color)
await expect
.poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
timeout: 10_000,
})
.toBeLessThan(15)
})
await test.step('opening broken code file should clear the scene and show the error', async () => {
// open the file pane.
await page.getByTestId('files-pane-button').click()
// Open the other file.
const file = page.getByRole('button', { name: 'broken-code-test.kcl' })
await expect(file).toBeVisible()
await file.click()
// error in guter
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
// error text on hover
await page.hover('.cm-lint-marker-error')
const crypticErrorText = `Expected a tag declarator`
await expect(page.getByText(crypticErrorText).first()).toBeVisible()
// black pixel means the scene has been cleared.
await expect
.poll(() => u.getGreatestPixDiff(pointOnModel, [30, 30, 30]), {
timeout: 10_000,
})
.toBeLessThan(15)
})
}
)
test(
'when code with error first loads you get errors in console',
{ tag: '@electron' },
@ -990,7 +688,7 @@ test.describe(`Project management commands`, () => {
test(
'File in the file pane should open with a single click',
{ tag: '@electron' },
async ({ context, homePage, page }, testInfo) => {
async ({ context, page }, testInfo) => {
const projectName = 'router-template-slate'
await context.folderSetupFn(async (dir) => {
await fsp.mkdir(`${dir}/${projectName}`, { recursive: true })
@ -1003,14 +701,16 @@ test(
`${dir}/${projectName}/otherThingToClickOn.kcl`
)
})
const u = await getUtils(page)
await page.setBodyDimensions({ width: 1200, height: 500 })
page.on('console', console.log)
await page.getByText(projectName).click()
await u.waitForPageLoad()
await expect(page.getByTestId('loading')).toBeAttached()
await expect(page.getByTestId('loading')).not.toBeAttached({
timeout: 20_000,
})
await expect(u.codeLocator).toContainText('routerDiameter')
await expect(u.codeLocator).toContainText('templateGap')
@ -1885,48 +1585,3 @@ test.fixme(
})
}
)
test(
'project name with foreign characters should open',
{ tag: '@electron' },
async ({ context, page }, testInfo) => {
await context.folderSetupFn(async (dir) => {
const bracketDir = path.join(dir, 'اَلْعَرَبِيَّةُ')
await fsp.mkdir(bracketDir, { recursive: true })
await fsp.copyFile(
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
path.join(bracketDir, 'main.kcl')
)
await fsp.writeFile(path.join(bracketDir, 'empty.kcl'), '')
})
await page.setBodyDimensions({ width: 1200, height: 500 })
const u = await getUtils(page)
page.on('console', console.log)
const pointOnModel = { x: 630, y: 280 }
await test.step('Opening the اَلْعَرَبِيَّةُ project should load the stream', async () => {
// expect to see the text bracket
await expect(page.getByText('اَلْعَرَبِيَّةُ')).toBeVisible()
await page.getByText('اَلْعَرَبِيَّةُ').click()
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).toBeEnabled({
timeout: 20_000,
})
// gray at this pixel means the stream has loaded in the most
// user way we can verify it (pixel color)
await expect
.poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
timeout: 10_000,
})
.toBeLessThan(15)
})
}
)

View File

@ -1,190 +0,0 @@
import { test, expect } from './zoo-test'
/* eslint-disable jest/no-conditional-expect */
const file = `sketch001 = startSketchOn('XZ')
profile001 = startProfileAt([57.81, 250.51], sketch001)
|> line([121.13, 56.63], %, $seg02)
|> line([83.37, -34.61], %, $seg01)
|> line([19.66, -116.4], %)
|> line([-221.8, -41.69], %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
extrude001 = extrude(200, profile001)
sketch002 = startSketchOn('XZ')
|> startProfileAt([-73.64, -42.89], %)
|> xLine(173.71, %)
|> line([-22.12, -94.4], %)
|> xLine(-156.98, %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
extrude002 = extrude(50, sketch002)
sketch003 = startSketchOn('XY')
|> startProfileAt([52.92, 157.81], %)
|> angledLine([0, 176.4], %, $rectangleSegmentA001)
|> angledLine([
segAng(rectangleSegmentA001) - 90,
53.4
], %, $rectangleSegmentB001)
|> angledLine([
segAng(rectangleSegmentA001),
-segLen(rectangleSegmentA001)
], %, $rectangleSegmentC001)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
extrude003 = extrude(20, sketch003)
`
test.describe('Check the happy path, for basic changing color', () => {
const cases = [
{
desc: 'User accepts change',
shouldReject: false,
},
{
desc: 'User rejects change',
shouldReject: true,
},
] as const
for (const { desc, shouldReject } of cases) {
test(`${desc}`, async ({
context,
homePage,
cmdBar,
editor,
page,
scene,
}) => {
await context.addInitScript((file) => {
localStorage.setItem('persistCode', file)
}, file)
await homePage.goToModelingScene()
const body1CapCoords = { x: 571, y: 351 }
const greenCheckCoords = { x: 565, y: 345 }
const body2WallCoords = { x: 609, y: 153 }
const [clickBody1Cap] = scene.makeMouseHelpers(
body1CapCoords.x,
body1CapCoords.y
)
const yellow: [number, number, number] = [179, 179, 131]
const green: [number, number, number] = [108, 152, 75]
const notGreen: [number, number, number] = [132, 132, 132]
const body2NotGreen: [number, number, number] = [88, 88, 88]
const submittingToast = page.getByText('Submitting to Text-to-CAD API...')
const successToast = page.getByText('Prompt to edit successful')
const acceptBtn = page.getByRole('button', { name: 'checkmark Accept' })
const rejectBtn = page.getByRole('button', { name: 'close Reject' })
await test.step('wait for scene to load select body and check selection came through', async () => {
await scene.expectPixelColor([134, 134, 134], body1CapCoords, 15)
await clickBody1Cap()
await scene.expectPixelColor(yellow, body1CapCoords, 20)
await editor.expectState({
highlightedCode: '',
activeLines: ['|>startProfileAt([-73.64,-42.89],%)'],
diagnostics: [],
})
})
await test.step('fire off edit prompt', async () => {
await cmdBar.openCmdBar('promptToEdit')
// being specific about the color with a hex means asserting pixel color is more stable
await page
.getByTestId('cmd-bar-arg-value')
.fill('make this neon green please, use #39FF14')
await page.waitForTimeout(100)
await cmdBar.progressCmdBar()
await expect(submittingToast).toBeVisible()
await expect(submittingToast).not.toBeVisible({ timeout: 2 * 60_000 }) // can take a while
await expect(successToast).toBeVisible()
})
await test.step('verify initial change', async () => {
await scene.expectPixelColor(green, greenCheckCoords, 15)
await scene.expectPixelColor(body2NotGreen, body2WallCoords, 15)
await editor.expectEditor.toContain('appearance({')
})
if (!shouldReject) {
await test.step('check accept works and can be "undo"ed', async () => {
await acceptBtn.click()
await expect(successToast).not.toBeVisible()
await scene.expectPixelColor(green, greenCheckCoords, 15)
await editor.expectEditor.toContain('appearance({')
// ctrl-z works after accepting
await page.keyboard.down('ControlOrMeta')
await page.keyboard.press('KeyZ')
await page.keyboard.up('ControlOrMeta')
await editor.expectEditor.not.toContain('appearance({')
await scene.expectPixelColor(notGreen, greenCheckCoords, 15)
})
} else {
await test.step('check reject works', async () => {
await rejectBtn.click()
await expect(successToast).not.toBeVisible()
await scene.expectPixelColor(notGreen, greenCheckCoords, 15)
await editor.expectEditor.not.toContain('appearance({')
})
}
})
}
})
test.describe('bad path', () => {
test(`bad edit prompt`, async ({
context,
homePage,
cmdBar,
editor,
toolbar,
page,
scene,
}) => {
await context.addInitScript((file) => {
localStorage.setItem('persistCode', file)
}, file)
await homePage.goToModelingScene()
const body1CapCoords = { x: 571, y: 351 }
const [clickBody1Cap] = scene.makeMouseHelpers(
body1CapCoords.x,
body1CapCoords.y
)
const yellow: [number, number, number] = [179, 179, 131]
const submittingToast = page.getByText('Submitting to Text-to-CAD API...')
const failToast = page.getByText(
'Failed to edit your KCL code, please try again with a different prompt or selection'
)
await test.step('wait for scene to load and select body', async () => {
await scene.expectPixelColor([134, 134, 134], body1CapCoords, 15)
await clickBody1Cap()
await scene.expectPixelColor(yellow, body1CapCoords, 20)
await editor.expectState({
highlightedCode: '',
activeLines: ['|>startProfileAt([-73.64,-42.89],%)'],
diagnostics: [],
})
})
await test.step('fire of bad prompt', async () => {
await cmdBar.openCmdBar('promptToEdit')
await page
.getByTestId('cmd-bar-arg-value')
.fill('ansheusha asnthuatshoeuhtaoetuhthaeu laughs in dvorak')
await page.waitForTimeout(100)
await cmdBar.progressCmdBar()
await expect(submittingToast).toBeVisible()
})
await test.step('check fail toast appeared', async () => {
await expect(submittingToast).not.toBeVisible({ timeout: 2 * 60_000 }) // can take a while
await expect(failToast).toBeVisible()
})
})
})

View File

@ -560,12 +560,10 @@ extrude001 = extrude(50, sketch001)
page,
homePage,
}) => {
// TODO: fix this test on windows after the electron migration
test.skip(process.platform === 'win32', 'Skip on windows')
const u = await getUtils(page)
// Constants and locators
const planeColor: [number, number, number] = [170, 220, 170]
const planeColor: [number, number, number] = [161, 220, 155]
const bgColor: [number, number, number] = [27, 27, 27]
const middlePixelIsColor = async (color: [number, number, number]) => {
return u.getGreatestPixDiff({ x: 600, y: 250 }, color)
@ -588,7 +586,7 @@ extrude001 = extrude(50, sketch001)
timeout: 5000,
message: 'Plane color is visible',
})
.toBeLessThanOrEqual(15)
.toBeLessThan(15)
let maxZoomOuts = 10
let middlePixelIsBackgroundColor =
@ -606,7 +604,7 @@ extrude001 = extrude(50, sketch001)
}
expect(middlePixelIsBackgroundColor, {
message: 'We should not see the default planes',
message: 'We no longer the default planes',
}).toBeTruthy()
})
@ -614,38 +612,6 @@ extrude001 = extrude(50, sketch001)
await expect(gizmo).toBeVisible()
})
})
test(`Refreshing the app doesn't cause the stream to pause on long-executing files`, async ({
context,
homePage,
scene,
toolbar,
viewport,
}) => {
await context.folderSetupFn(async (dir) => {
const legoDir = path.join(dir, 'lego')
await fsp.mkdir(legoDir, { recursive: true })
await fsp.copyFile(
executorInputPath('lego.kcl'),
path.join(legoDir, 'main.kcl')
)
})
await test.step(`Test setup`, async () => {
await homePage.openProject('lego')
await toolbar.closePane('code')
})
await test.step(`Waiting for the loading spinner to disappear`, async () => {
await scene.loadingIndicator.waitFor({ state: 'detached' })
})
await test.step(`The part should start loading quickly, not waiting until execution is complete`, async () => {
await scene.expectPixelColor(
[143, 143, 143],
{ x: (viewport?.width ?? 1200) / 2, y: (viewport?.height ?? 500) / 2 },
15
)
})
})
})
async function clickExportButton(page: Page) {

View File

@ -39,8 +39,8 @@ test.describe('Sketch tests', () => {
${startProfileAt1}
|> arc({
radius = screwRadius,
angleStart = 0,
angleEnd = 360
angle_start = 0,
angle_end = 360
}, %)
part001 = startSketchOn('XY')
@ -60,8 +60,8 @@ test.describe('Sketch tests', () => {
|> yLine(wireOffset, %)
|> arc({
radius = wireRadius,
angleStart = 0,
angleEnd = 180
angle_start = 0,
angle_end = 180
}, %)
|> yLine(-wireOffset, %)
|> xLine(-width / 4, %)
@ -82,16 +82,19 @@ test.describe('Sketch tests', () => {
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()
@ -634,8 +637,6 @@ test.describe('Sketch tests', () => {
|> revolve({ axis = "X" }, %)`)
})
test('Can add multiple sketches', async ({ page, homePage }) => {
// TODO: fix this test on windows after the electron migration
test.skip(process.platform === 'win32', 'Skip on windows')
const u = await getUtils(page)
const viewportSize = { width: 1200, height: 500 }
@ -833,8 +834,6 @@ test.describe('Sketch tests', () => {
page,
homePage,
}) => {
// TODO: fix this test on windows after the electron migration
test.skip(process.platform === 'win32', 'Skip on windows')
// this was a regression https://github.com/KittyCAD/modeling-app/issues/2832
await page.addInitScript(async () => {
localStorage.setItem(
@ -950,108 +949,211 @@ test.describe('Sketch tests', () => {
`.replace(/\s/g, '')
)
})
// TODO: fix after electron migration is merged
test.fixme(
'empty-scene default-planes act as expected',
async ({ page, homePage }) => {
/**
* 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 homePage.goToModelingScene()
/* TODO: once we fix bug turn on.
test('empty-scene default-planes act as expected when spaces in file', async ({
page,
browserName,
}) => {
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.closeDebugPanel()
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
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 u.waitForAuthSkipAppStart()
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y)
await page.waitForTimeout(200)
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.closeDebugPanel()
// color should not change for having been hovered
expect(
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
).toBeLessThan(8)
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 u.openAndClearDebugPanel()
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y)
await page.waitForTimeout(200)
await u.codeLocator.fill(`sketch001 = startSketchOn('XY')
// color should not change for having been hovered
expect(
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
).toBeLessThan(8)
await u.openAndClearDebugPanel()
// Fill with spaces
await u.codeLocator.fill(`
`)
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.closeDebugPanel()
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)
})
test('empty-scene default-planes act as expected when only code comments in file', async ({
page,
browserName,
}) => {
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()
// Fill with spaces
await u.codeLocator.fill(`// this is a code comments ya nerds
`)
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.closeDebugPanel()
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)
})*/
test('empty-scene default-planes act as expected', async ({
page,
homePage,
}) => {
/**
* 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 homePage.goToModelingScene()
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(`sketch001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
|> xLine(-20, %)
`)
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.expectCmdLog('[data-message-type="execution-done"]')
const noPlanesColor: [number, number, number] = [30, 30, 30]
expect(
await u.getGreatestPixDiff(XYPlanePoint, noPlanesColor)
).toBeLessThan(3)
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 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)
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: 50 })
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)
// click start Sketch
await page.getByRole('button', { name: 'Start Sketch' }).click()
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y, { steps: 50 })
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(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(`sketch001 = startSketchOn('XZ')
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(`sketch001 = startSketchOn('XZ')
|> startProfileAt([11.8, 9.09], %)
|> line([3.39, -3.39], %)
`)
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`sketch001 = startSketchOn('XZ')
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`sketch001 = startSketchOn('XZ')
|> startProfileAt([11.8, 9.09], %)
|> line([3.39, -3.39], %)
`
)
})
)
})
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.closeDebugPanel()
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)
}
)
// expect there to be no planes on load since there's something in the scene
expect(
await u.getGreatestPixDiff(XYPlanePoint, noPlanesColor)
).toBeLessThan(3)
})
test('Can attempt to sketch on revolved face', async ({ page, homePage }) => {
const u = await getUtils(page)
@ -1323,85 +1425,3 @@ test.describe(`Sketching with offset planes`, () => {
})
})
})
// Regression test for https://github.com/KittyCAD/modeling-app/issues/4891
test.describe(`Click based selection don't brick the app when clicked out of range after format using cache`, () => {
test(`Can select a line that reformmed after entering sketch mode`, async ({
context,
page,
scene,
toolbar,
editor,
homePage,
}) => {
// We seed the scene with a single offset plane
await context.addInitScript(() => {
localStorage.setItem(
'persistCode',
`sketch001 = startSketchOn('XZ')
|> startProfileAt([0, 0], %)
|> line([3.14, 3.14], %)
|> arcTo({
end = [4, 2],
interior = [1, 2]
}, %)
`
)
})
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
await test.step(`format the code`, async () => {
// doesn't contain condensed version
await editor.expectEditor.not.toContain(
`arcTo({ end = [4, 2], interior = [1, 2] }, %)`
)
// click the code to enter sketch mode
await page.getByText(`arcTo`).click()
// Format the code.
await page.locator('#code-pane button:first-child').click()
await page.locator('button:has-text("Format code")').click()
})
await test.step(`Ensure the code reformatted`, async () => {
await editor.expectEditor.toContain(
`arcTo({ end = [4, 2], interior = [1, 2] }, %)`
)
})
const [arcClick, arcHover] = scene.makeMouseHelpers(699, 337)
await test.step('Ensure we can hover the arc', async () => {
await arcHover()
// Check that the code is highlighted
await editor.expectState({
activeLines: ["sketch001=startSketchOn('XZ')"],
diagnostics: [],
highlightedCode: 'arcTo({end = [4, 2], interior = [1, 2]}, %)',
})
})
await test.step('reset the selection', async () => {
// Move the mouse out of the way
await page.mouse.move(655, 337)
await editor.expectState({
activeLines: ["sketch001=startSketchOn('XZ')"],
diagnostics: [],
highlightedCode: '',
})
})
await test.step('Ensure we can click the arc', async () => {
await arcClick()
// Check that the code is highlighted
await editor.expectState({
activeLines: [],
diagnostics: [],
highlightedCode: 'arcTo({end = [4, 2], interior = [1, 2]}, %)',
})
})
})
})

View File

@ -47,6 +47,7 @@ test.beforeEach(async ({ page }) => {
test.setTimeout(60_000)
// We test this end to end already - getting this to work on web just to take
// a snapshot of it feels weird. I'd rather our regular tests fail.
// The primary failure is doExport now relies on the filesystem. We can follow
@ -375,7 +376,6 @@ const extrudeDefaultPlane = async (context: any, page: any, plane: string) => {
await u.closeKclCodePanel()
await expect(page).toHaveScreenshot({
maxDiffPixels: 100,
mask: [page.getByTestId('model-state-indicator')],
})
await u.openKclCodePanel()
}
@ -955,75 +955,7 @@ test(
test.describe('Grid visibility', { tag: '@snapshot' }, () => {
// FIXME: Skip on macos its being weird.
// test.skip(process.platform === 'darwin', 'Skip on macos')
test('Grid turned off to on via command bar', async ({ page }) => {
const u = await getUtils(page)
const stream = page.getByTestId('stream')
const mask = [
page.locator('#app-header'),
page.locator('#sidebar-top-ribbon'),
page.locator('#sidebar-bottom-ribbon'),
]
await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/')
await u.waitForAuthSkipAppStart()
await u.openDebugPanel()
// wait for execution done
await expect(
page.locator('[data-message-type="execution-done"]')
).toHaveCount(1)
await u.closeDebugPanel()
await u.closeKclCodePanel()
// TODO: Find a way to truly know that the objects have finished
// rendering, because an execution-done message is not sufficient.
await page.waitForTimeout(1000)
// Open the command bar.
await page
.getByRole('button', { name: 'Commands', exact: false })
.or(page.getByRole('button', { name: '⌘K' }))
.click()
const commandName = 'show scale grid'
const commandOption = page.getByRole('option', {
name: commandName,
exact: false,
})
const cmdSearchBar = page.getByPlaceholder('Search commands')
// This selector changes after we set the setting
await cmdSearchBar.fill(commandName)
await expect(commandOption).toBeVisible()
await commandOption.click()
const toggleInput = page.getByPlaceholder('Off')
await expect(toggleInput).toBeVisible()
await expect(toggleInput).toBeFocused()
// Select On
await page.keyboard.press('ArrowDown')
await expect(page.getByRole('option', { name: 'Off' })).toHaveAttribute(
'data-headlessui-state',
'active selected'
)
await page.keyboard.press('ArrowUp')
await expect(page.getByRole('option', { name: 'On' })).toHaveAttribute(
'data-headlessui-state',
'active'
)
await page.keyboard.press('Enter')
// Check the toast appeared
await expect(
page.getByText(`Set show scale grid to "true" as a user default`)
).toBeVisible()
await expect(stream).toHaveScreenshot({
maxDiffPixels: 100,
mask,
})
})
test.skip(process.platform === 'darwin', 'Skip on macos')
test('Grid turned off', async ({ page }) => {
const u = await getUtils(page)
@ -1169,109 +1101,3 @@ test.fixme('theme persists', async ({ page, context }) => {
maxDiffPixels: 100,
})
})
test.describe('code color goober', { tag: '@snapshot' }, () => {
test('code color goober', async ({ page, context }) => {
const u = await getUtils(page)
await context.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`// Create a pipe using a sweep.
// Create a path for the sweep.
sweepPath = startSketchOn('XZ')
|> startProfileAt([0.05, 0.05], %)
|> line([0, 7], %)
|> tangentialArc({ offset = 90, radius = 5 }, %)
|> line([-3, 0], %)
|> tangentialArc({ offset = -90, radius = 5 }, %)
|> line([0, 7], %)
sweepSketch = startSketchOn('XY')
|> startProfileAt([2, 0], %)
|> arc({
angleEnd = 360,
angleStart = 0,
radius = 2
}, %)
|> sweep({
path = sweepPath,
}, %)
|> appearance({
color = "#bb00ff",
metalness = 90,
roughness = 90
}, %)
`
)
})
await page.setViewportSize({ width: 1200, height: 1000 })
await u.waitForAuthSkipAppStart()
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.clearAndCloseDebugPanel()
await expect(page, 'expect small color widget').toHaveScreenshot({
maxDiffPixels: 100,
})
})
test('code color goober opening window', async ({ page, context }) => {
const u = await getUtils(page)
await context.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`// Create a pipe using a sweep.
// Create a path for the sweep.
sweepPath = startSketchOn('XZ')
|> startProfileAt([0.05, 0.05], %)
|> line([0, 7], %)
|> tangentialArc({ offset = 90, radius = 5 }, %)
|> line([-3, 0], %)
|> tangentialArc({ offset = -90, radius = 5 }, %)
|> line([0, 7], %)
sweepSketch = startSketchOn('XY')
|> startProfileAt([2, 0], %)
|> arc({
angleEnd = 360,
angleStart = 0,
radius = 2
}, %)
|> sweep({
path = sweepPath,
}, %)
|> appearance({
color = "#bb00ff",
metalness = 90,
roughness = 90
}, %)
`
)
})
await page.setViewportSize({ width: 1200, height: 1000 })
await u.waitForAuthSkipAppStart()
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.clearAndCloseDebugPanel()
await expect(page.locator('.cm-css-color-picker-wrapper')).toBeVisible()
// Click the color widget
await page.locator('.cm-css-color-picker-wrapper input').click()
await expect(
page,
'expect small color widget to have window open'
).toHaveScreenshot({
maxDiffPixels: 100,
})
})
})

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 42 KiB

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