Compare commits
90 Commits
move-tests
...
v0.35.0
Author | SHA1 | Date | |
---|---|---|---|
0c2a0a8c07 | |||
97cef4d16c | |||
9358278f7b | |||
a174e084d4 | |||
df7246897a | |||
0c9f64dd7c | |||
d2b9d3a058 | |||
7e54f08778 | |||
d9c2dd376e | |||
275a2150e7 | |||
8b8feb8d68 | |||
e21ef3f122 | |||
66834931aa | |||
06c1bcaf2e | |||
fc7df7ecbe | |||
67cb7b33bb | |||
ea74b94fac | |||
529833c63f | |||
92da86391a | |||
e7cb390db4 | |||
8a66bbbdbd | |||
3c53babb50 | |||
474acb1c68 | |||
1c941112d7 | |||
6f1d718097 | |||
36957237c0 | |||
da9cae98aa | |||
9ae025dc56 | |||
579ab23d78 | |||
4bef33e745 | |||
ac7bd28c5a | |||
d478d81156 | |||
3d27f0191b | |||
30c2acd18a | |||
a83b4b2145 | |||
70b8541038 | |||
bb51646738 | |||
c02e31a530 | |||
1d06cc7845 | |||
e0c07eecfe | |||
c5d42500fa | |||
e6e47f77f0 | |||
662c2485ac | |||
9f891deebb | |||
d08a07a1f8 | |||
872b196a86 | |||
d535a2862d | |||
63a3bc7bc6 | |||
02055a8b31 | |||
93891422f7 | |||
7193b4110a | |||
76e7d80a55 | |||
b816df21d2 | |||
3630696848 | |||
f165d19fda | |||
3dd98ae1d5 | |||
a46e0a0fe7 | |||
8f9dc06228 | |||
fa22c14723 | |||
1d39983b08 | |||
da301ba862 | |||
efe8089b08 | |||
49de3b0ac9 | |||
2b2ed470c1 | |||
96652a0c48 | |||
04e586d07b | |||
fe5f574a77 | |||
e787495ad0 | |||
8bb9be7a5e | |||
00892464e8 | |||
05ed2a3367 | |||
10cc5bce59 | |||
a32f150fc1 | |||
ac60082e67 | |||
d44dc1b21a | |||
813962ea4c | |||
738443a6ab | |||
4b6bbbe2c5 | |||
6ff8addc8b | |||
da05c38b9e | |||
191b9b71fd | |||
05163fdded | |||
7ed26e21c6 | |||
c668d40efc | |||
f38c6b90b7 | |||
7bc8bae0ec | |||
3804aca27e | |||
b127680f2f | |||
b7de8e60cf | |||
058fccb5e1 |
@ -1,3 +1,3 @@
|
|||||||
[codespell]
|
[codespell]
|
||||||
ignore-words-list: crate,everytime,inout,co-ordinate,ot,nwo,absolutey,atleast,ue,afterall
|
ignore-words-list: crate,everytime,inout,co-ordinate,ot,nwo,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
|
skip: **/target,node_modules,build,**/Cargo.lock,./docs/kcl/*.md,.yarn.lock,**/yarn.lock,./openapi/*.json,./src/lib/machine-api.d.ts,./packages/codemirror-lang-kcl/test/all.test.ts
|
||||||
|
12
.github/ci-cd-scripts/playwright-electron.sh
vendored
@ -6,11 +6,11 @@ set -euo pipefail
|
|||||||
if [[ ! -f "test-results/.last-run.json" ]]; then
|
if [[ ! -f "test-results/.last-run.json" ]]; then
|
||||||
# if no last run artifact, than run plawright normally
|
# if no last run artifact, than run plawright normally
|
||||||
echo "run playwright normally"
|
echo "run playwright normally"
|
||||||
if [[ "$3" == ubuntu-latest* ]]; then
|
if [[ "$3" == *ubuntu* ]]; then
|
||||||
xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn test:playwright:electron:ubuntu -- --shard=$1/$2 || true
|
xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn test:playwright:electron:ubuntu -- --shard=$1/$2 || true
|
||||||
elif [[ "$3" == windows-latest* ]]; then
|
elif [[ "$3" == *windows* ]]; then
|
||||||
yarn test:playwright:electron:windows -- --shard=$1/$2 || true
|
yarn test:playwright:electron:windows -- --shard=$1/$2 || true
|
||||||
elif [[ "$3" == macos-14* ]]; then
|
elif [[ "$3" == *macos* ]]; then
|
||||||
yarn test:playwright:electron:macos -- --shard=$1/$2 || true
|
yarn test:playwright:electron:macos -- --shard=$1/$2 || true
|
||||||
else
|
else
|
||||||
echo "Do not run playwright. Unable to detect os runtime."
|
echo "Do not run playwright. Unable to detect os runtime."
|
||||||
@ -30,11 +30,11 @@ while [[ $retry -le $max_retrys ]]; do
|
|||||||
if [[ $failed_tests -gt 0 ]]; then
|
if [[ $failed_tests -gt 0 ]]; then
|
||||||
echo "retried=true" >>$GITHUB_OUTPUT
|
echo "retried=true" >>$GITHUB_OUTPUT
|
||||||
echo "run playwright with last failed tests and retry $retry"
|
echo "run playwright with last failed tests and retry $retry"
|
||||||
if [[ "$3" == ubuntu-latest* ]]; then
|
if [[ "$3" == *ubuntu* ]]; then
|
||||||
xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn test:playwright:electron:ubuntu -- --last-failed || true
|
xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn test:playwright:electron:ubuntu -- --last-failed || true
|
||||||
elif [[ "$3" == windows-latest* ]]; then
|
elif [[ "$3" == *windows* ]]; then
|
||||||
yarn test:playwright:electron:windows -- --last-failed || true
|
yarn test:playwright:electron:windows -- --last-failed || true
|
||||||
elif [[ "$3" == macos-14* ]]; then
|
elif [[ "$3" == *macos* ]]; then
|
||||||
yarn test:playwright:electron:macos -- --last-failed || true
|
yarn test:playwright:electron:macos -- --last-failed || true
|
||||||
else
|
else
|
||||||
echo "Do not run playwright. Unable to detect os runtime."
|
echo "Do not run playwright. Unable to detect os runtime."
|
||||||
|
46
.github/dependabot.yml
vendored
@ -5,24 +5,28 @@
|
|||||||
|
|
||||||
version: 2
|
version: 2
|
||||||
updates:
|
updates:
|
||||||
- package-ecosystem: 'npm' # See documentation for possible values
|
- package-ecosystem: 'npm' # See documentation for possible values
|
||||||
directory: '/' # Location of package manifests
|
directory: '/' # Location of package manifests
|
||||||
schedule:
|
schedule:
|
||||||
interval: 'weekly'
|
interval: 'weekly'
|
||||||
reviewers:
|
reviewers:
|
||||||
- franknoirot
|
- franknoirot
|
||||||
- irev-dev
|
- irev-dev
|
||||||
- package-ecosystem: 'github-actions' # See documentation for possible values
|
- package-ecosystem: 'github-actions' # See documentation for possible values
|
||||||
directory: '/' # Location of package manifests
|
directory: '/' # Location of package manifests
|
||||||
schedule:
|
schedule:
|
||||||
interval: 'weekly'
|
interval: 'weekly'
|
||||||
reviewers:
|
reviewers:
|
||||||
- adamchalmers
|
- adamchalmers
|
||||||
- jessfraz
|
- jessfraz
|
||||||
- package-ecosystem: 'cargo' # See documentation for possible values
|
- package-ecosystem: 'cargo' # See documentation for possible values
|
||||||
directory: '/src/wasm-lib/' # Location of package manifests
|
directory: '/src/wasm-lib/' # Location of package manifests
|
||||||
schedule:
|
schedule:
|
||||||
interval: 'weekly'
|
interval: 'weekly'
|
||||||
reviewers:
|
reviewers:
|
||||||
- adamchalmers
|
- adamchalmers
|
||||||
- jessfraz
|
- jessfraz
|
||||||
|
groups:
|
||||||
|
serde-dependencies:
|
||||||
|
patterns:
|
||||||
|
- "serde*"
|
||||||
|
16
.github/workflows/build-apps.yml
vendored
@ -173,7 +173,13 @@ jobs:
|
|||||||
CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
||||||
CSC_KEYCHAIN: ${{ secrets.APPLE_SIGNING_IDENTITY }}
|
CSC_KEYCHAIN: ${{ secrets.APPLE_SIGNING_IDENTITY }}
|
||||||
WINDOWS_CERTIFICATE_THUMBPRINT: ${{ secrets.WINDOWS_CERTIFICATE_THUMBPRINT }}
|
WINDOWS_CERTIFICATE_THUMBPRINT: ${{ secrets.WINDOWS_CERTIFICATE_THUMBPRINT }}
|
||||||
run: yarn electron-builder --config --publish always
|
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
|
||||||
|
|
||||||
- name: List artifacts in out/
|
- name: List artifacts in out/
|
||||||
run: ls -R out
|
run: ls -R out
|
||||||
@ -228,7 +234,13 @@ jobs:
|
|||||||
CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
||||||
CSC_KEYCHAIN: ${{ secrets.APPLE_SIGNING_IDENTITY }}
|
CSC_KEYCHAIN: ${{ secrets.APPLE_SIGNING_IDENTITY }}
|
||||||
WINDOWS_CERTIFICATE_THUMBPRINT: ${{ secrets.WINDOWS_CERTIFICATE_THUMBPRINT }}
|
WINDOWS_CERTIFICATE_THUMBPRINT: ${{ secrets.WINDOWS_CERTIFICATE_THUMBPRINT }}
|
||||||
run: yarn electron-builder --config --publish always
|
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
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
if: ${{ env.IS_RELEASE == 'true' }}
|
if: ${{ env.IS_RELEASE == 'true' }}
|
||||||
|
22
.github/workflows/cargo-test.yml
vendored
@ -2,28 +2,8 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- 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:
|
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:
|
workflow_dispatch:
|
||||||
permissions: read-all
|
permissions: read-all
|
||||||
concurrency:
|
concurrency:
|
||||||
@ -71,7 +51,7 @@ jobs:
|
|||||||
KITTYCAD_API_TOKEN: ${{secrets.KITTYCAD_API_TOKEN}}
|
KITTYCAD_API_TOKEN: ${{secrets.KITTYCAD_API_TOKEN}}
|
||||||
RUST_MIN_STACK: 10485760000
|
RUST_MIN_STACK: 10485760000
|
||||||
- name: Upload to codecov.io
|
- name: Upload to codecov.io
|
||||||
uses: codecov/codecov-action@v4
|
uses: codecov/codecov-action@v5
|
||||||
with:
|
with:
|
||||||
token: ${{secrets.CODECOV_TOKEN}}
|
token: ${{secrets.CODECOV_TOKEN}}
|
||||||
fail_ci_if_error: true
|
fail_ci_if_error: true
|
||||||
|
16
.github/workflows/e2e-tests.yml
vendored
@ -34,19 +34,18 @@ jobs:
|
|||||||
- 'src/wasm-lib/**'
|
- 'src/wasm-lib/**'
|
||||||
|
|
||||||
electron:
|
electron:
|
||||||
timeout-minutes: ${{ matrix.os == 'macos-14' && 60 || 50 }}
|
timeout-minutes: 60
|
||||||
name: playwright:electron:${{ matrix.os }} ${{ matrix.shardIndex }} ${{ matrix.shardTotal }}
|
name: playwright:electron:${{ matrix.os }} ${{ matrix.shardIndex }} ${{ matrix.shardTotal }}
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest-8-cores, windows-latest-8-cores, macos-14-large]
|
# TODO: enable self-hosted-windows-8-cores once available
|
||||||
|
os: [namespace-profile-ubuntu-8-cores, namespace-profile-macos-8-cores, windows-16-cores]
|
||||||
shardIndex: [1, 2, 3, 4]
|
shardIndex: [1, 2, 3, 4]
|
||||||
shardTotal: [4]
|
shardTotal: [4]
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
needs: check-rust-changes
|
needs: check-rust-changes
|
||||||
steps:
|
steps:
|
||||||
- name: Tune GitHub-hosted runner network
|
|
||||||
uses: smorimoto/tune-github-hosted-runner-network@v1
|
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
@ -101,7 +100,8 @@ jobs:
|
|||||||
echo "/opt/homebrew/opt/gnu-sed/libexec/gnubin" >> $GITHUB_PATH
|
echo "/opt/homebrew/opt/gnu-sed/libexec/gnubin" >> $GITHUB_PATH
|
||||||
- name: Install vector
|
- name: Install vector
|
||||||
shell: bash
|
shell: bash
|
||||||
if: ${{ !startsWith(matrix.os, 'windows') }}
|
# TODO: figure out what to do with this, it's failing
|
||||||
|
if: false
|
||||||
run: |
|
run: |
|
||||||
curl --proto '=https' --tlsv1.2 -sSfL https://sh.vector.dev > /tmp/vector.sh
|
curl --proto '=https' --tlsv1.2 -sSfL https://sh.vector.dev > /tmp/vector.sh
|
||||||
chmod +x /tmp/vector.sh
|
chmod +x /tmp/vector.sh
|
||||||
@ -127,9 +127,12 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: yarn tron:package
|
run: yarn tron:package
|
||||||
- name: Run ubuntu/chrome snapshots
|
- name: Run ubuntu/chrome snapshots
|
||||||
|
if: ${{ matrix.os == 'namespace-profile-ubuntu-8-cores' && matrix.shardIndex == 1 }}
|
||||||
shell: bash
|
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: |
|
run: |
|
||||||
PLATFORM=web yarn playwright test --config=playwright.config.ts --retries="3" --update-snapshots --grep=@snapshot --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
|
PLATFORM=web yarn playwright test --config=playwright.config.ts --retries="3" --update-snapshots --grep=@snapshot --shard=1/1
|
||||||
env:
|
env:
|
||||||
CI: true
|
CI: true
|
||||||
NODE_ENV: development
|
NODE_ENV: development
|
||||||
@ -150,6 +153,7 @@ jobs:
|
|||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
run: rm -r test-results
|
run: rm -r test-results
|
||||||
- name: check for changes
|
- name: check for changes
|
||||||
|
if: ${{ matrix.os == 'namespace-profile-ubuntu-8-cores' && matrix.shardIndex == 1 }}
|
||||||
shell: bash
|
shell: bash
|
||||||
id: git-check
|
id: git-check
|
||||||
run: |
|
run: |
|
||||||
|
40
README.md
@ -337,13 +337,47 @@ For individual testing:
|
|||||||
yarn test abstractSyntaxTree -t "unexpected closed curly brace" --silent=false
|
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
|
### Rust tests
|
||||||
|
|
||||||
```bash
|
**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
|
cd src/wasm-lib
|
||||||
KITTYCAD_API_TOKEN=XXX cargo test -- --test-threads=1
|
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
|
||||||
```
|
```
|
||||||
|
|
||||||
Where `XXX` is an API token from the production engine (NOT the dev environment).
|
Where `XXX` is an API token from the production engine (NOT the dev environment).
|
||||||
|
49
docs/kcl/atan2.md
Normal file
42
docs/kcl/circleThreePoint.md
Normal file
@ -30,10 +30,12 @@ layout: manual
|
|||||||
* [`assertLessThan`](kcl/assertLessThan)
|
* [`assertLessThan`](kcl/assertLessThan)
|
||||||
* [`assertLessThanOrEq`](kcl/assertLessThanOrEq)
|
* [`assertLessThanOrEq`](kcl/assertLessThanOrEq)
|
||||||
* [`atan`](kcl/atan)
|
* [`atan`](kcl/atan)
|
||||||
|
* [`atan2`](kcl/atan2)
|
||||||
* [`bezierCurve`](kcl/bezierCurve)
|
* [`bezierCurve`](kcl/bezierCurve)
|
||||||
* [`ceil`](kcl/ceil)
|
* [`ceil`](kcl/ceil)
|
||||||
* [`chamfer`](kcl/chamfer)
|
* [`chamfer`](kcl/chamfer)
|
||||||
* [`circle`](kcl/circle)
|
* [`circle`](kcl/circle)
|
||||||
|
* [`circleThreePoint`](kcl/circleThreePoint)
|
||||||
* [`close`](kcl/close)
|
* [`close`](kcl/close)
|
||||||
* [`cm`](kcl/cm)
|
* [`cm`](kcl/cm)
|
||||||
* [`cos`](kcl/cos)
|
* [`cos`](kcl/cos)
|
||||||
@ -100,8 +102,8 @@ layout: manual
|
|||||||
* [`sin`](kcl/sin)
|
* [`sin`](kcl/sin)
|
||||||
* [`sqrt`](kcl/sqrt)
|
* [`sqrt`](kcl/sqrt)
|
||||||
* [`startProfileAt`](kcl/startProfileAt)
|
* [`startProfileAt`](kcl/startProfileAt)
|
||||||
* [`startSketchAt`](kcl/startSketchAt)
|
|
||||||
* [`startSketchOn`](kcl/startSketchOn)
|
* [`startSketchOn`](kcl/startSketchOn)
|
||||||
|
* [`sweep`](kcl/sweep)
|
||||||
* [`tan`](kcl/tan)
|
* [`tan`](kcl/tan)
|
||||||
* [`tangentToEnd`](kcl/tangentToEnd)
|
* [`tangentToEnd`](kcl/tangentToEnd)
|
||||||
* [`tangentialArc`](kcl/tangentialArc)
|
* [`tangentialArc`](kcl/tangentialArc)
|
||||||
|
@ -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")
|
- `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
|
```js
|
||||||
patternTransform(total_instances: u32, transform_function: FunctionParam, solid_set: SolidSet) -> [Solid]
|
patternTransform(total_instances: integer, transform_function: FunctionParam, solid_set: SolidSet) -> [Solid]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -43,7 +43,7 @@ patternTransform(total_instances: u32, transform_function: FunctionParam, solid_
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `total_instances` | `u32` | | Yes |
|
| `total_instances` | `integer` | | Yes |
|
||||||
| `transform_function` | `FunctionParam` | | Yes |
|
| `transform_function` | `FunctionParam` | | Yes |
|
||||||
| `solid_set` | [`SolidSet`](/docs/kcl/types/SolidSet) | A solid or a group of solids. | Yes |
|
| `solid_set` | [`SolidSet`](/docs/kcl/types/SolidSet) | A solid or a group of solids. | Yes |
|
||||||
|
|
||||||
@ -95,7 +95,8 @@ fn cube(length, center) {
|
|||||||
p2 = [l + x, l + y]
|
p2 = [l + x, l + y]
|
||||||
p3 = [l + x, -l + y]
|
p3 = [l + x, -l + y]
|
||||||
|
|
||||||
return startSketchAt(p0)
|
return startSketchOn('XY')
|
||||||
|
|> startProfileAt(p0, %)
|
||||||
|> lineTo(p1, %)
|
|> lineTo(p1, %)
|
||||||
|> lineTo(p2, %)
|
|> lineTo(p2, %)
|
||||||
|> lineTo(p3, %)
|
|> lineTo(p3, %)
|
||||||
@ -132,7 +133,8 @@ fn cube(length, center) {
|
|||||||
p2 = [l + x, l + y]
|
p2 = [l + x, l + y]
|
||||||
p3 = [l + x, -l + y]
|
p3 = [l + x, -l + y]
|
||||||
|
|
||||||
return startSketchAt(p0)
|
return startSketchOn('XY')
|
||||||
|
|> startProfileAt(p0, %)
|
||||||
|> lineTo(p1, %)
|
|> lineTo(p1, %)
|
||||||
|> lineTo(p2, %)
|
|> lineTo(p2, %)
|
||||||
|> lineTo(p3, %)
|
|> lineTo(p3, %)
|
||||||
@ -195,7 +197,8 @@ fn transform(i) {
|
|||||||
{ rotation = { angle = 45 * i } }
|
{ rotation = { angle = 45 * i } }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
startSketchAt([0, 0])
|
startSketchOn('XY')
|
||||||
|
|> startProfileAt([0, 0], %)
|
||||||
|> polygon({
|
|> polygon({
|
||||||
radius = 10,
|
radius = 10,
|
||||||
numSides = 4,
|
numSides = 4,
|
||||||
|
@ -9,7 +9,7 @@ Just like patternTransform, but works on 2D sketches not 3D solids.
|
|||||||
|
|
||||||
|
|
||||||
```js
|
```js
|
||||||
patternTransform2d(total_instances: u32, transform_function: FunctionParam, solid_set: SketchSet) -> [Sketch]
|
patternTransform2d(total_instances: integer, transform_function: FunctionParam, solid_set: SketchSet) -> [Sketch]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ patternTransform2d(total_instances: u32, transform_function: FunctionParam, soli
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `total_instances` | `u32` | | Yes |
|
| `total_instances` | `integer` | | Yes |
|
||||||
| `transform_function` | `FunctionParam` | | Yes |
|
| `transform_function` | `FunctionParam` | | Yes |
|
||||||
| `solid_set` | [`SketchSet`](/docs/kcl/types/SketchSet) | A sketch or a group of sketches. | Yes |
|
| `solid_set` | [`SketchSet`](/docs/kcl/types/SketchSet) | A sketch or a group of sketches. | Yes |
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ fn sum(arr) {
|
|||||||
|
|
||||||
/* The above is basically like this pseudo-code:
|
/* The above is basically like this pseudo-code:
|
||||||
fn sum(arr):
|
fn sum(arr):
|
||||||
let sumSoFar = 0
|
sumSoFar = 0
|
||||||
for i in arr:
|
for i in arr:
|
||||||
sumSoFar = add(sumSoFar, i)
|
sumSoFar = add(sumSoFar, i)
|
||||||
return sumSoFar */
|
return sumSoFar */
|
||||||
@ -79,10 +79,11 @@ fn decagon(radius) {
|
|||||||
stepAngle = 1 / 10 * tau()
|
stepAngle = 1 / 10 * tau()
|
||||||
|
|
||||||
// Start the decagon sketch at this point.
|
// Start the decagon sketch at this point.
|
||||||
startOfDecagonSketch = startSketchAt([cos(0) * radius, sin(0) * radius])
|
startOfDecagonSketch = startSketchOn('XY')
|
||||||
|
|> startProfileAt([cos(0) * radius, sin(0) * radius], %)
|
||||||
|
|
||||||
// Use a `reduce` to draw the remaining decagon sides.
|
// Use a `reduce` to draw the remaining decagon sides.
|
||||||
// For each number in the array 1..10, run the given function,
|
// 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.
|
// 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, fn(i, partialDecagon) {
|
||||||
// Draw one edge of the decagon.
|
// Draw one edge of the decagon.
|
||||||
@ -96,14 +97,15 @@ fn decagon(radius) {
|
|||||||
|
|
||||||
/* The `decagon` above is basically like this pseudo-code:
|
/* The `decagon` above is basically like this pseudo-code:
|
||||||
fn decagon(radius):
|
fn decagon(radius):
|
||||||
let stepAngle = (1/10) * tau()
|
stepAngle = (1/10) * tau()
|
||||||
let startOfDecagonSketch = startSketchAt([(cos(0)*radius), (sin(0) * radius)])
|
plane = startSketchOn('XY')
|
||||||
|
startOfDecagonSketch = startProfileAt([(cos(0)*radius), (sin(0) * radius)], plane)
|
||||||
|
|
||||||
// Here's the reduce part.
|
// Here's the reduce part.
|
||||||
let partialDecagon = startOfDecagonSketch
|
partialDecagon = startOfDecagonSketch
|
||||||
for i in [1..10]:
|
for i in [1..10]:
|
||||||
let x = cos(stepAngle * i) * radius
|
x = cos(stepAngle * i) * radius
|
||||||
let y = sin(stepAngle * i) * radius
|
y = sin(stepAngle * i) * radius
|
||||||
partialDecagon = lineTo([x, y], partialDecagon)
|
partialDecagon = lineTo([x, y], partialDecagon)
|
||||||
fullDecagon = partialDecagon // it's now full
|
fullDecagon = partialDecagon // it's now full
|
||||||
return fullDecagon */
|
return fullDecagon */
|
||||||
|
@ -28,7 +28,8 @@ segEnd(tag: TagIdentifier) -> [number]
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
w = 15
|
w = 15
|
||||||
cube = startSketchAt([0, 0])
|
cube = startSketchOn('XY')
|
||||||
|
|> startProfileAt([0, 0], %)
|
||||||
|> line([w, 0], %, $line1)
|
|> line([w, 0], %, $line1)
|
||||||
|> line([0, w], %, $line2)
|
|> line([0, w], %, $line2)
|
||||||
|> line([-w, 0], %, $line3)
|
|> line([-w, 0], %, $line3)
|
||||||
@ -37,7 +38,8 @@ cube = startSketchAt([0, 0])
|
|||||||
|> extrude(5, %)
|
|> extrude(5, %)
|
||||||
|
|
||||||
fn cylinder(radius, tag) {
|
fn cylinder(radius, tag) {
|
||||||
return startSketchAt([0, 0])
|
return startSketchOn('XY')
|
||||||
|
|> startProfileAt([0, 0], %)
|
||||||
|> circle({
|
|> circle({
|
||||||
radius = radius,
|
radius = radius,
|
||||||
center = segEnd(tag)
|
center = segEnd(tag)
|
||||||
|
@ -28,7 +28,8 @@ segStart(tag: TagIdentifier) -> [number]
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
w = 15
|
w = 15
|
||||||
cube = startSketchAt([0, 0])
|
cube = startSketchOn('XY')
|
||||||
|
|> startProfileAt([0, 0], %)
|
||||||
|> line([w, 0], %, $line1)
|
|> line([w, 0], %, $line1)
|
||||||
|> line([0, w], %, $line2)
|
|> line([0, w], %, $line2)
|
||||||
|> line([-w, 0], %, $line3)
|
|> line([-w, 0], %, $line3)
|
||||||
@ -37,7 +38,8 @@ cube = startSketchAt([0, 0])
|
|||||||
|> extrude(5, %)
|
|> extrude(5, %)
|
||||||
|
|
||||||
fn cylinder(radius, tag) {
|
fn cylinder(radius, tag) {
|
||||||
return startSketchAt([0, 0])
|
return startSketchOn('XY')
|
||||||
|
|> startProfileAt([0, 0], %)
|
||||||
|> circle({
|
|> circle({
|
||||||
radius = radius,
|
radius = radius,
|
||||||
center = segStart(tag)
|
center = segStart(tag)
|
||||||
|
@ -4,6 +4,8 @@ excerpt: "Start a new 2-dimensional sketch at a given point on the 'XY' plane."
|
|||||||
layout: manual
|
layout: manual
|
||||||
---
|
---
|
||||||
|
|
||||||
|
**WARNING:** This function is deprecated.
|
||||||
|
|
||||||
Start a new 2-dimensional sketch at a given point on the 'XY' plane.
|
Start a new 2-dimensional sketch at a given point on the 'XY' plane.
|
||||||
|
|
||||||
|
|
||||||
|
12875
docs/kcl/std.json
55
docs/kcl/sweep.md
Normal file
@ -13,13 +13,18 @@ Data to draw an angled line.
|
|||||||
|
|
||||||
An angle and length with explicitly named parameters
|
An angle and length with explicitly named parameters
|
||||||
|
|
||||||
[`PolarCoordsData`](/docs/kcl/types/PolarCoordsData)
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `angle` |`number`| The angle of the line (in degrees). | No |
|
||||||
|
| `length` |`number`| The length of the line. | No |
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
|
23
docs/kcl/types/CircleThreePointData.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
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 |
|
||||||
|
|
||||||
|
|
@ -12,5 +12,10 @@ 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 |
|
||||||
|
|
||||||
|
|
||||||
|
@ -329,6 +329,23 @@ 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
|
## Properties
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|
16
docs/kcl/types/ModuleId.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
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`)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
23
docs/kcl/types/SweepData.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
title: "SweepData"
|
||||||
|
excerpt: "Data for a sweep."
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
Data for a sweep.
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `path` |[`Sketch`](/docs/kcl/types/Sketch)| 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 |
|
||||||
|
|
||||||
|
|
@ -20,7 +20,7 @@ async function doBasicSketch(
|
|||||||
|
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
await u.waitForPageLoad()
|
await u.waitForPageLoad()
|
||||||
await page.waitForTimeout()
|
await page.waitForTimeout(1000)
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
|
|
||||||
// If we have the code pane open, we should see the code.
|
// 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'])
|
await doBasicSketch(page, homePage, ['code'])
|
||||||
})
|
})
|
||||||
|
|
||||||
test.fixme('code pane closed at start', async ({ page, homePage }) => {
|
test('code pane closed at start', async ({ page, homePage }) => {
|
||||||
// Load the app with the code panes
|
// Load the app with the code panes
|
||||||
await page.addInitScript(async (persistModelingContext) => {
|
await page.addInitScript(async (persistModelingContext) => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
|
@ -45,7 +45,8 @@ test.describe('Command bar tests', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Fillet from command bar', async ({ page, homePage }) => {
|
// TODO: fix this test after the electron migration
|
||||||
|
test.fixme('Fillet from command bar', async ({ page, homePage }) => {
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'persistCode',
|
'persistCode',
|
||||||
@ -80,7 +81,7 @@ test.describe('Command bar tests', () => {
|
|||||||
await page.keyboard.press('Enter') // submit
|
await page.keyboard.press('Enter') // submit
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
await expect(page.locator('.cm-activeLine')).toContainText(
|
await expect(page.locator('.cm-activeLine')).toContainText(
|
||||||
`fillet({ radius: ${KCL_DEFAULT_LENGTH}, tags: [seg01] }, %)`
|
`fillet({ radius = ${KCL_DEFAULT_LENGTH}, tags = [seg01] }, %)`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -54,6 +54,108 @@ test.describe('Editor tests', () => {
|
|||||||
|> close(%)`)
|
|> 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 ({
|
test('if you click the format button it formats your code', async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
homePage,
|
||||||
@ -453,20 +555,22 @@ test.describe('Editor tests', () => {
|
|||||||
homePage,
|
homePage,
|
||||||
}) => {
|
}) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
|
|
||||||
// check no error to begin with
|
// check no error to begin with
|
||||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
|
|
||||||
/* add the following code to the editor ($ error is not a valid line)
|
/* add the following code to the editor (~ error is not a valid line)
|
||||||
$ error
|
* the old check here used $ but this is for tags so it changed meaning.
|
||||||
|
* hopefully ~ doesn't change meaning
|
||||||
|
~ error
|
||||||
const topAng = 30
|
const topAng = 30
|
||||||
const bottomAng = 25
|
const bottomAng = 25
|
||||||
*/
|
*/
|
||||||
await u.codeLocator.click()
|
await u.codeLocator.click()
|
||||||
await page.keyboard.type('$ error')
|
await page.keyboard.type('~ error')
|
||||||
|
|
||||||
// press arrows to clear autocomplete
|
// press arrows to clear autocomplete
|
||||||
await page.keyboard.press('ArrowLeft')
|
await page.keyboard.press('ArrowLeft')
|
||||||
@ -483,10 +587,12 @@ test.describe('Editor tests', () => {
|
|||||||
|
|
||||||
// error text on hover
|
// error text on hover
|
||||||
await page.hover('.cm-lint-marker-error')
|
await page.hover('.cm-lint-marker-error')
|
||||||
await expect(page.getByText('Unexpected token: $').first()).toBeVisible()
|
await expect(
|
||||||
|
page.getByText("found unknown token '~'").first()
|
||||||
|
).toBeVisible()
|
||||||
|
|
||||||
// select the line that's causing the error and delete it
|
// 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.press('End')
|
||||||
await page.keyboard.down('Shift')
|
await page.keyboard.down('Shift')
|
||||||
await page.keyboard.press('Home')
|
await page.keyboard.press('Home')
|
||||||
|
127
e2e/playwright/feature-tree-pane.spec.ts
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
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()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
@ -92,6 +92,8 @@ test.describe('when using the file tree to', () => {
|
|||||||
`rename ${fromFile} to ${toFile}, and doesn't crash on reload and settings load`,
|
`rename ${fromFile} to ${toFile}, and doesn't crash on reload and settings load`,
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ page }, testInfo) => {
|
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 } =
|
const { panesOpen, pasteCodeInEditor, renameFile, editorTextMatches } =
|
||||||
await getUtils(page, test)
|
await getUtils(page, test)
|
||||||
|
|
||||||
@ -134,6 +136,8 @@ test.describe('when using the file tree to', () => {
|
|||||||
`create many new files of the same name, incrementing their names`,
|
`create many new files of the same name, incrementing their names`,
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ page }, testInfo) => {
|
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)
|
const { panesOpen, createNewFile } = await getUtils(page, test)
|
||||||
|
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
@ -1014,6 +1018,8 @@ 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`,
|
`open a file, change something, open a different file, hitting undo should do nothing`,
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ context, page }, testInfo) => {
|
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) => {
|
await context.folderSetupFn(async (dir) => {
|
||||||
const testDir = join(dir, 'testProject')
|
const testDir = join(dir, 'testProject')
|
||||||
await fsp.mkdir(testDir, { recursive: true })
|
await fsp.mkdir(testDir, { recursive: true })
|
||||||
@ -1082,6 +1088,8 @@ test.describe('Undo and redo do not keep history when navigating between files',
|
|||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
// Skip on windows i think the keybindings are different for redo.
|
// Skip on windows i think the keybindings are different for redo.
|
||||||
async ({ context, page }, testInfo) => {
|
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) => {
|
await context.folderSetupFn(async (dir) => {
|
||||||
const testDir = join(dir, 'testProject')
|
const testDir = join(dir, 'testProject')
|
||||||
await fsp.mkdir(testDir, { recursive: true })
|
await fsp.mkdir(testDir, { recursive: true })
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type { Page } from '@playwright/test'
|
import type { Page, Locator } from '@playwright/test'
|
||||||
import { expect } from '@playwright/test'
|
import { expect } from '@playwright/test'
|
||||||
|
|
||||||
type CmdBarSerialised =
|
type CmdBarSerialised =
|
||||||
@ -26,9 +26,11 @@ type CmdBarSerialised =
|
|||||||
|
|
||||||
export class CmdBarFixture {
|
export class CmdBarFixture {
|
||||||
public page: Page
|
public page: Page
|
||||||
|
cmdBarOpenBtn!: Locator
|
||||||
|
|
||||||
constructor(page: Page) {
|
constructor(page: Page) {
|
||||||
this.page = page
|
this.page = page
|
||||||
|
this.cmdBarOpenBtn = page.getByTestId('command-bar-open-button')
|
||||||
}
|
}
|
||||||
reConstruct = (page: Page) => {
|
reConstruct = (page: Page) => {
|
||||||
this.page = page
|
this.page = page
|
||||||
@ -116,4 +118,21 @@ export class CmdBarFixture {
|
|||||||
await this.page.keyboard.press('Enter')
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ export class EditorFixture {
|
|||||||
private diagnosticsTooltip!: Locator
|
private diagnosticsTooltip!: Locator
|
||||||
private diagnosticsGutterIcon!: Locator
|
private diagnosticsGutterIcon!: Locator
|
||||||
private codeContent!: Locator
|
private codeContent!: Locator
|
||||||
private activeLine!: Locator
|
public activeLine!: Locator
|
||||||
|
|
||||||
constructor(page: Page) {
|
constructor(page: Page) {
|
||||||
this.page = page
|
this.page = page
|
||||||
@ -147,20 +147,28 @@ export class EditorFixture {
|
|||||||
openPane() {
|
openPane() {
|
||||||
return openPane(this.page, this.paneButtonTestId)
|
return openPane(this.page, this.paneButtonTestId)
|
||||||
}
|
}
|
||||||
scrollToText(text: string) {
|
scrollToText(text: string, placeCursor?: boolean) {
|
||||||
return this.page.evaluate((scrollToText: string) => {
|
return this.page.evaluate(
|
||||||
// editorManager is available on the window object.
|
(args: { text: string; placeCursor?: boolean }) => {
|
||||||
// @ts-ignore
|
// error TS2339: Property 'docView' does not exist on type 'EditorView'.
|
||||||
let index = editorManager._editorView.docView.view.state.doc
|
// Except it does so :shrug:
|
||||||
.toString()
|
// @ts-ignore
|
||||||
.indexOf(scrollToText)
|
let index = window.editorManager._editorView?.docView.view.state.doc
|
||||||
// @ts-ignore
|
.toString()
|
||||||
editorManager._editorView.dispatch({
|
.indexOf(args.text)
|
||||||
selection: {
|
window.editorManager._editorView?.focus()
|
||||||
anchor: index,
|
window.editorManager._editorView?.dispatch({
|
||||||
},
|
selection: window.EditorSelection.create([
|
||||||
scrollIntoView: true,
|
window.EditorSelection.cursor(index),
|
||||||
})
|
]),
|
||||||
}, text)
|
effects: [
|
||||||
|
window.EditorView.scrollIntoView(
|
||||||
|
window.EditorSelection.range(index, index + 1)
|
||||||
|
),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{ text, placeCursor }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,8 @@ export class AuthenticatedApp {
|
|||||||
public readonly context: BrowserContext
|
public readonly context: BrowserContext
|
||||||
public readonly testInfo: TestInfo
|
public readonly testInfo: TestInfo
|
||||||
public readonly viewPortSize = { width: 1200, height: 500 }
|
public readonly viewPortSize = { width: 1200, height: 500 }
|
||||||
|
public electronApp: undefined | ElectronApplication
|
||||||
|
public dir: string = ''
|
||||||
|
|
||||||
constructor(context: BrowserContext, page: Page, testInfo: TestInfo) {
|
constructor(context: BrowserContext, page: Page, testInfo: TestInfo) {
|
||||||
this.context = context
|
this.context = context
|
||||||
@ -61,7 +63,7 @@ export class AuthenticatedTronApp {
|
|||||||
public page: Page
|
public page: Page
|
||||||
public context: BrowserContext
|
public context: BrowserContext
|
||||||
public readonly testInfo: TestInfo
|
public readonly testInfo: TestInfo
|
||||||
public electronApp?: ElectronApplication
|
public electronApp: ElectronApplication | undefined
|
||||||
public readonly viewPortSize = { width: 1200, height: 500 }
|
public readonly viewPortSize = { width: 1200, height: 500 }
|
||||||
public dir: string = ''
|
public dir: string = ''
|
||||||
|
|
||||||
@ -79,7 +81,7 @@ export class AuthenticatedTronApp {
|
|||||||
appSettings?: Partial<SaveSettingsPayload>
|
appSettings?: Partial<SaveSettingsPayload>
|
||||||
} = { fixtures: {} }
|
} = { fixtures: {} }
|
||||||
) {
|
) {
|
||||||
const { electronApp, page, context, dir, options } = await setupElectron({
|
const { electronApp, page, context, dir } = await setupElectron({
|
||||||
testInfo: this.testInfo,
|
testInfo: this.testInfo,
|
||||||
folderSetupFn: arg.folderSetupFn,
|
folderSetupFn: arg.folderSetupFn,
|
||||||
cleanProjectDir: arg.cleanProjectDir,
|
cleanProjectDir: arg.cleanProjectDir,
|
||||||
|
@ -218,23 +218,7 @@ export class SceneFixture {
|
|||||||
coords: { x: number; y: number },
|
coords: { x: number; y: number },
|
||||||
diff: number
|
diff: number
|
||||||
) => {
|
) => {
|
||||||
let finalValue = colour
|
await expectPixelColor(this.page, colour, coords, diff)
|
||||||
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() {
|
get gizmo() {
|
||||||
@ -251,3 +235,28 @@ export class SceneFixture {
|
|||||||
await buttonToTest.click()
|
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 }
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -1,6 +1,13 @@
|
|||||||
import type { Page, Locator } from '@playwright/test'
|
import type { Page, Locator } from '@playwright/test'
|
||||||
import { expect } from '../zoo-test'
|
import { expect } from '../zoo-test'
|
||||||
import { doAndWaitForImageDiff } from '../test-utils'
|
import {
|
||||||
|
checkIfPaneIsOpen,
|
||||||
|
closePane,
|
||||||
|
doAndWaitForImageDiff,
|
||||||
|
openPane,
|
||||||
|
} from '../test-utils'
|
||||||
|
import { SidebarType } from 'components/ModelingSidebar/ModelingPanes'
|
||||||
|
import { SIDEBAR_BUTTON_SUFFIX } from 'lib/constants'
|
||||||
|
|
||||||
export class ToolbarFixture {
|
export class ToolbarFixture {
|
||||||
public page: Page
|
public page: Page
|
||||||
@ -20,6 +27,10 @@ export class ToolbarFixture {
|
|||||||
filePane!: Locator
|
filePane!: Locator
|
||||||
exeIndicator!: Locator
|
exeIndicator!: Locator
|
||||||
treeInputField!: 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) {
|
constructor(page: Page) {
|
||||||
this.page = page
|
this.page = page
|
||||||
@ -41,6 +52,7 @@ export class ToolbarFixture {
|
|||||||
this.treeInputField = page.getByTestId('tree-input-field')
|
this.treeInputField = page.getByTestId('tree-input-field')
|
||||||
|
|
||||||
this.filePane = page.locator('#files-pane')
|
this.filePane = page.locator('#files-pane')
|
||||||
|
this.featureTreePane = page.locator('#feature-tree-pane')
|
||||||
this.fileCreateToast = page.getByText('Successfully created')
|
this.fileCreateToast = page.getByText('Successfully created')
|
||||||
this.exeIndicator = page.getByTestId('model-state-indicator-execution-done')
|
this.exeIndicator = page.getByTestId('model-state-indicator-execution-done')
|
||||||
}
|
}
|
||||||
@ -91,4 +103,76 @@ export class ToolbarFixture {
|
|||||||
await expect(this.exeIndicator).toBeVisible({ timeout: 15_000 })
|
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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import {
|
|||||||
TEST_SETTINGS_ONBOARDING_USER_MENU,
|
TEST_SETTINGS_ONBOARDING_USER_MENU,
|
||||||
} from './storageStates'
|
} from './storageStates'
|
||||||
import * as TOML from '@iarna/toml'
|
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
|
// Because onboarding relies on an app setting we need to set it as incompletel
|
||||||
// for all these tests.
|
// for all these tests.
|
||||||
@ -27,6 +28,7 @@ test.describe('Onboarding tests', () => {
|
|||||||
cleanProjectDir: true,
|
cleanProjectDir: true,
|
||||||
},
|
},
|
||||||
async ({ context, page, homePage }) => {
|
async ({ context, page, homePage }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
|
|
||||||
@ -35,10 +37,23 @@ test.describe('Onboarding tests', () => {
|
|||||||
page.getByText('Welcome to Modeling App! This')
|
page.getByText('Welcome to Modeling App! This')
|
||||||
).toBeVisible()
|
).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
|
// *and* that the code is shown in the editor
|
||||||
await expect(page.locator('.cm-content')).toContainText(
|
await expect(page.locator('.cm-content')).toContainText(
|
||||||
'// Shelf Bracket'
|
'// 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
|
||||||
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -76,6 +91,14 @@ test.describe('Onboarding tests', () => {
|
|||||||
await expect(page.locator('.cm-content')).toContainText(
|
await expect(page.locator('.cm-content')).toContainText(
|
||||||
'// Shelf Bracket'
|
'// 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)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -125,7 +148,7 @@ test.describe('Onboarding tests', () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
// There used to be old code here that checked if we stored the reset
|
// There used to be old code here that checked if we stored the reset
|
||||||
// code into localStorage but that isnt the case on desktop. It gets
|
// code into localStorage but that isn't the case on desktop. It gets
|
||||||
// saved to the file system, which we have other tests for.
|
// saved to the file system, which we have other tests for.
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -414,7 +437,7 @@ test.describe('Onboarding tests', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test(
|
test.fixme(
|
||||||
'Restarting onboarding on desktop takes one attempt',
|
'Restarting onboarding on desktop takes one attempt',
|
||||||
{
|
{
|
||||||
appSettings: {
|
appSettings: {
|
||||||
@ -486,7 +509,15 @@ test(
|
|||||||
await test.step('Confirm that the onboarding has restarted', async () => {
|
await test.step('Confirm that the onboarding has restarted', async () => {
|
||||||
await expect(tutorialProjectIndicator).toBeVisible()
|
await expect(tutorialProjectIndicator).toBeVisible()
|
||||||
await expect(tutorialModalText).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()
|
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 () => {
|
await test.step('Clear code and restart onboarding from settings', async () => {
|
||||||
|
@ -16,6 +16,8 @@ test('verify extruding circle works', async ({
|
|||||||
toolbar,
|
toolbar,
|
||||||
scene,
|
scene,
|
||||||
}) => {
|
}) => {
|
||||||
|
// TODO: fix this test on windows after the electron migration
|
||||||
|
test.skip(process.platform === 'win32', 'Skip on windows')
|
||||||
const file = await fs.readFile(
|
const file = await fs.readFile(
|
||||||
path.resolve(
|
path.resolve(
|
||||||
__dirname,
|
__dirname,
|
||||||
@ -95,6 +97,8 @@ test('verify extruding circle works', async ({
|
|||||||
})
|
})
|
||||||
|
|
||||||
test.describe('verify sketch on chamfer works', () => {
|
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 =
|
const _sketchOnAChamfer =
|
||||||
(
|
(
|
||||||
page: Page,
|
page: Page,
|
||||||
@ -760,8 +764,9 @@ const loftPointAndClickCases = [
|
|||||||
]
|
]
|
||||||
loftPointAndClickCases.forEach(({ shouldPreselect }) => {
|
loftPointAndClickCases.forEach(({ shouldPreselect }) => {
|
||||||
test(`Loft point-and-click (preselected sketches: ${shouldPreselect})`, async ({
|
test(`Loft point-and-click (preselected sketches: ${shouldPreselect})`, async ({
|
||||||
app,
|
context,
|
||||||
page,
|
page,
|
||||||
|
homePage,
|
||||||
scene,
|
scene,
|
||||||
editor,
|
editor,
|
||||||
toolbar,
|
toolbar,
|
||||||
@ -773,7 +778,11 @@ loftPointAndClickCases.forEach(({ shouldPreselect }) => {
|
|||||||
sketch002 = startSketchOn(plane001)
|
sketch002 = startSketchOn(plane001)
|
||||||
|> circle({ center = [0, 0], radius = 20 }, %)
|
|> circle({ center = [0, 0], radius = 20 }, %)
|
||||||
`
|
`
|
||||||
await app.initialise(initialCode)
|
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
|
// One dumb hardcoded screen pixel value
|
||||||
const testPoint = { x: 575, y: 200 }
|
const testPoint = { x: 575, y: 200 }
|
||||||
@ -792,7 +801,7 @@ loftPointAndClickCases.forEach(({ shouldPreselect }) => {
|
|||||||
await clickOnSketch1()
|
await clickOnSketch1()
|
||||||
await page.keyboard.down('Shift')
|
await page.keyboard.down('Shift')
|
||||||
await clickOnSketch2()
|
await clickOnSketch2()
|
||||||
await app.page.waitForTimeout(500)
|
await page.waitForTimeout(500)
|
||||||
await page.keyboard.up('Shift')
|
await page.keyboard.up('Shift')
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -859,6 +868,8 @@ shellPointAndClickCapCases.forEach(({ shouldPreselect }) => {
|
|||||||
toolbar,
|
toolbar,
|
||||||
cmdBar,
|
cmdBar,
|
||||||
}) => {
|
}) => {
|
||||||
|
// TODO: fix this test on windows after the electron migration
|
||||||
|
test.skip(process.platform === 'win32', 'Skip on windows')
|
||||||
const initialCode = `sketch001 = startSketchOn('XZ')
|
const initialCode = `sketch001 = startSketchOn('XZ')
|
||||||
|> circle({ center = [0, 0], radius = 30 }, %)
|
|> circle({ center = [0, 0], radius = 30 }, %)
|
||||||
extrude001 = extrude(30, sketch001)
|
extrude001 = extrude(30, sketch001)
|
||||||
|
@ -342,10 +342,10 @@ test(
|
|||||||
)
|
)
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'xxxxx open a file in a project works and renders, open another file in the same project with errors, it should clear the scene',
|
'open a file in a project works and renders, open another file in the same project with errors, it should clear the scene',
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ context, page }, testInfo) => {
|
async ({ context, page }, testInfo) => {
|
||||||
const { dir } = await context.folderSetupFn(async (dir) => {
|
await context.folderSetupFn(async (dir) => {
|
||||||
const bracketDir = path.join(dir, 'bracket')
|
const bracketDir = path.join(dir, 'bracket')
|
||||||
await fsp.mkdir(bracketDir, { recursive: true })
|
await fsp.mkdir(bracketDir, { recursive: true })
|
||||||
await fsp.copyFile(
|
await fsp.copyFile(
|
||||||
|
190
e2e/playwright/prompt-to-edit.spec.ts
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
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()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
@ -560,6 +560,8 @@ extrude001 = extrude(50, sketch001)
|
|||||||
page,
|
page,
|
||||||
homePage,
|
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 u = await getUtils(page)
|
||||||
|
|
||||||
// Constants and locators
|
// Constants and locators
|
||||||
@ -586,7 +588,7 @@ extrude001 = extrude(50, sketch001)
|
|||||||
timeout: 5000,
|
timeout: 5000,
|
||||||
message: 'Plane color is visible',
|
message: 'Plane color is visible',
|
||||||
})
|
})
|
||||||
.toBeLessThan(15)
|
.toBeLessThanOrEqual(15)
|
||||||
|
|
||||||
let maxZoomOuts = 10
|
let maxZoomOuts = 10
|
||||||
let middlePixelIsBackgroundColor =
|
let middlePixelIsBackgroundColor =
|
||||||
@ -604,7 +606,7 @@ extrude001 = extrude(50, sketch001)
|
|||||||
}
|
}
|
||||||
|
|
||||||
expect(middlePixelIsBackgroundColor, {
|
expect(middlePixelIsBackgroundColor, {
|
||||||
message: 'We no longer the default planes',
|
message: 'We should not see the default planes',
|
||||||
}).toBeTruthy()
|
}).toBeTruthy()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -82,19 +82,16 @@ test.describe('Sketch tests', () => {
|
|||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
await page.getByText(selectionsSnippets.startProfileAt1).click()
|
await page.getByText(selectionsSnippets.startProfileAt1).click()
|
||||||
await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled()
|
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('button', { name: 'Edit Sketch' })
|
page.getByRole('button', { name: 'Edit Sketch' })
|
||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
|
|
||||||
await page.getByText(selectionsSnippets.startProfileAt2).click()
|
await page.getByText(selectionsSnippets.startProfileAt2).click()
|
||||||
await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled()
|
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('button', { name: 'Edit Sketch' })
|
page.getByRole('button', { name: 'Edit Sketch' })
|
||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
|
|
||||||
await page.getByText(selectionsSnippets.startProfileAt3).click()
|
await page.getByText(selectionsSnippets.startProfileAt3).click()
|
||||||
await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled()
|
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('button', { name: 'Edit Sketch' })
|
page.getByRole('button', { name: 'Edit Sketch' })
|
||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
@ -637,6 +634,8 @@ test.describe('Sketch tests', () => {
|
|||||||
|> revolve({ axis = "X" }, %)`)
|
|> revolve({ axis = "X" }, %)`)
|
||||||
})
|
})
|
||||||
test('Can add multiple sketches', async ({ page, homePage }) => {
|
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 u = await getUtils(page)
|
||||||
|
|
||||||
const viewportSize = { width: 1200, height: 500 }
|
const viewportSize = { width: 1200, height: 500 }
|
||||||
@ -834,6 +833,8 @@ test.describe('Sketch tests', () => {
|
|||||||
page,
|
page,
|
||||||
homePage,
|
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
|
// this was a regression https://github.com/KittyCAD/modeling-app/issues/2832
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
@ -949,107 +950,108 @@ test.describe('Sketch tests', () => {
|
|||||||
`.replace(/\s/g, '')
|
`.replace(/\s/g, '')
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
test('empty-scene default-planes act as expected', async ({
|
// TODO: fix after electron migration is merged
|
||||||
page,
|
test.fixme(
|
||||||
homePage,
|
'empty-scene default-planes act as expected',
|
||||||
}) => {
|
async ({ page, homePage }) => {
|
||||||
/**
|
/**
|
||||||
* Tests the following things
|
* Tests the following things
|
||||||
* 1) The the planes are there on load because the scene is empty
|
* 1) The the planes are there on load because the scene is empty
|
||||||
* 2) The planes don't changes color when hovered initially
|
* 2) The planes don't changes color when hovered initially
|
||||||
* 3) Putting something in the scene makes the planes hidden
|
* 3) Putting something in the scene makes the planes hidden
|
||||||
* 4) Removing everything from the scene shows the plans again
|
* 4) Removing everything from the scene shows the plans again
|
||||||
* 3) Once "start sketch" is click, the planes do respond to hovers
|
* 3) Once "start sketch" is click, the planes do respond to hovers
|
||||||
* 4) Selecting a plan works as expected, i.e. sketch mode
|
* 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
|
* 5) Reloading the scene with something already in the scene means the planes are hidden
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
|
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
const XYPlanePoint = { x: 774, y: 116 } as const
|
const XYPlanePoint = { x: 774, y: 116 } as const
|
||||||
const unHoveredColor: [number, number, number] = [47, 47, 93]
|
const unHoveredColor: [number, number, number] = [47, 47, 93]
|
||||||
expect(
|
expect(
|
||||||
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
||||||
).toBeLessThan(8)
|
).toBeLessThan(8)
|
||||||
|
|
||||||
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y)
|
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y)
|
||||||
await page.waitForTimeout(200)
|
await page.waitForTimeout(200)
|
||||||
|
|
||||||
// color should not change for having been hovered
|
// color should not change for having been hovered
|
||||||
expect(
|
expect(
|
||||||
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
||||||
).toBeLessThan(8)
|
).toBeLessThan(8)
|
||||||
|
|
||||||
await u.openAndClearDebugPanel()
|
await u.openAndClearDebugPanel()
|
||||||
|
|
||||||
await u.codeLocator.fill(`sketch001 = startSketchOn('XY')
|
await u.codeLocator.fill(`sketch001 = startSketchOn('XY')
|
||||||
|> startProfileAt([-10, -10], %)
|
|> startProfileAt([-10, -10], %)
|
||||||
|> line([20, 0], %)
|
|> line([20, 0], %)
|
||||||
|> line([0, 20], %)
|
|> line([0, 20], %)
|
||||||
|> xLine(-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]
|
const noPlanesColor: [number, number, number] = [30, 30, 30]
|
||||||
expect(
|
expect(
|
||||||
await u.getGreatestPixDiff(XYPlanePoint, noPlanesColor)
|
await u.getGreatestPixDiff(XYPlanePoint, noPlanesColor)
|
||||||
).toBeLessThan(3)
|
).toBeLessThan(3)
|
||||||
|
|
||||||
await u.clearCommandLogs()
|
await u.clearCommandLogs()
|
||||||
await u.removeCurrentCode()
|
await u.removeCurrentCode()
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
|
||||||
await expect
|
await expect
|
||||||
.poll(() => u.getGreatestPixDiff(XYPlanePoint, unHoveredColor), {
|
.poll(() => u.getGreatestPixDiff(XYPlanePoint, unHoveredColor), {
|
||||||
timeout: 5_000,
|
timeout: 5_000,
|
||||||
})
|
})
|
||||||
.toBeLessThan(8)
|
.toBeLessThan(8)
|
||||||
|
|
||||||
// click start Sketch
|
// click start Sketch
|
||||||
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||||
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y, { steps: 50 })
|
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y, { steps: 50 })
|
||||||
const hoveredColor: [number, number, number] = [93, 93, 127]
|
const hoveredColor: [number, number, number] = [93, 93, 127]
|
||||||
// now that we're expecting the user to select a plan, it does respond to hover
|
// now that we're expecting the user to select a plan, it does respond to hover
|
||||||
await expect
|
await expect
|
||||||
.poll(() => u.getGreatestPixDiff(XYPlanePoint, hoveredColor))
|
.poll(() => u.getGreatestPixDiff(XYPlanePoint, hoveredColor))
|
||||||
.toBeLessThan(8)
|
.toBeLessThan(8)
|
||||||
|
|
||||||
await page.mouse.click(XYPlanePoint.x, XYPlanePoint.y)
|
await page.mouse.click(XYPlanePoint.x, XYPlanePoint.y)
|
||||||
await page.waitForTimeout(600)
|
await page.waitForTimeout(600)
|
||||||
|
|
||||||
await page.mouse.click(XYPlanePoint.x, XYPlanePoint.y)
|
await page.mouse.click(XYPlanePoint.x, XYPlanePoint.y)
|
||||||
await page.waitForTimeout(200)
|
await page.waitForTimeout(200)
|
||||||
await page.mouse.click(XYPlanePoint.x + 50, XYPlanePoint.y + 50)
|
await page.mouse.click(XYPlanePoint.x + 50, XYPlanePoint.y + 50)
|
||||||
await expect(u.codeLocator).toHaveText(`sketch001 = startSketchOn('XZ')
|
await expect(u.codeLocator).toHaveText(`sketch001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([11.8, 9.09], %)
|
|> startProfileAt([11.8, 9.09], %)
|
||||||
|> line([3.39, -3.39], %)
|
|> line([3.39, -3.39], %)
|
||||||
`)
|
`)
|
||||||
|
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'persistCode',
|
'persistCode',
|
||||||
`sketch001 = startSketchOn('XZ')
|
`sketch001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([11.8, 9.09], %)
|
|> startProfileAt([11.8, 9.09], %)
|
||||||
|> line([3.39, -3.39], %)
|
|> line([3.39, -3.39], %)
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
// expect there to be no planes on load since there's something in the scene
|
// expect there to be no planes on load since there's something in the scene
|
||||||
expect(
|
expect(
|
||||||
await u.getGreatestPixDiff(XYPlanePoint, noPlanesColor)
|
await u.getGreatestPixDiff(XYPlanePoint, noPlanesColor)
|
||||||
).toBeLessThan(3)
|
).toBeLessThan(3)
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
test('Can attempt to sketch on revolved face', async ({ page, homePage }) => {
|
test('Can attempt to sketch on revolved face', async ({ page, homePage }) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
@ -1321,3 +1323,85 @@ 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]}, %)',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
@ -375,6 +375,7 @@ const extrudeDefaultPlane = async (context: any, page: any, plane: string) => {
|
|||||||
await u.closeKclCodePanel()
|
await u.closeKclCodePanel()
|
||||||
await expect(page).toHaveScreenshot({
|
await expect(page).toHaveScreenshot({
|
||||||
maxDiffPixels: 100,
|
maxDiffPixels: 100,
|
||||||
|
mask: [page.getByTestId('model-state-indicator')],
|
||||||
})
|
})
|
||||||
await u.openKclCodePanel()
|
await u.openKclCodePanel()
|
||||||
}
|
}
|
||||||
@ -1168,3 +1169,109 @@ test.fixme('theme persists', async ({ page, context }) => {
|
|||||||
maxDiffPixels: 100,
|
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,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 59 KiB |
Before Width: | Height: | Size: 55 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 74 KiB |
Before Width: | Height: | Size: 65 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 66 KiB |
Before Width: | Height: | Size: 62 KiB |
After Width: | Height: | Size: 144 KiB |
After Width: | Height: | Size: 128 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 37 KiB |
@ -14,7 +14,7 @@ export const TEST_SETTINGS = {
|
|||||||
},
|
},
|
||||||
modeling: {
|
modeling: {
|
||||||
defaultUnit: 'in',
|
defaultUnit: 'in',
|
||||||
mouseControls: 'KittyCAD',
|
mouseControls: 'Zoo',
|
||||||
cameraProjection: 'perspective',
|
cameraProjection: 'perspective',
|
||||||
showDebugPanel: true,
|
showDebugPanel: true,
|
||||||
},
|
},
|
||||||
|
@ -3,6 +3,7 @@ import {
|
|||||||
BrowserContext,
|
BrowserContext,
|
||||||
TestInfo,
|
TestInfo,
|
||||||
_electron as electron,
|
_electron as electron,
|
||||||
|
ElectronApplication,
|
||||||
Locator,
|
Locator,
|
||||||
} from '@playwright/test'
|
} from '@playwright/test'
|
||||||
import { test, Page } from './zoo-test'
|
import { test, Page } from './zoo-test'
|
||||||
@ -28,130 +29,6 @@ import { isErrorWhitelisted } from './lib/console-error-whitelist'
|
|||||||
import { isArray } from 'lib/utils'
|
import { isArray } from 'lib/utils'
|
||||||
import { reportRejection } from 'lib/trap'
|
import { reportRejection } from 'lib/trap'
|
||||||
|
|
||||||
// The below is copied from playwright-core because it exports none of them :(
|
|
||||||
import { Env, BrowserContextOptions } from 'playwright-core'
|
|
||||||
import type * as channels from '@protocol/channels'
|
|
||||||
|
|
||||||
// Copied from playwright-core
|
|
||||||
function envObjectToArray(env: Env): { name: string; value: string }[] {
|
|
||||||
const result: { name: string; value: string }[] = []
|
|
||||||
for (const name in env) {
|
|
||||||
if (!Object.is(env[name], undefined))
|
|
||||||
result.push({ name, value: String(env[name]) })
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copied from playwright-core
|
|
||||||
export async function toClientCertificatesProtocol(
|
|
||||||
certs?: BrowserContextOptions['clientCertificates']
|
|
||||||
): Promise<channels.PlaywrightNewRequestParams['clientCertificates']> {
|
|
||||||
if (!certs) return undefined
|
|
||||||
|
|
||||||
const bufferizeContent = async (
|
|
||||||
value?: Buffer,
|
|
||||||
path?: string
|
|
||||||
): Promise<Buffer | undefined> => {
|
|
||||||
if (value) return value
|
|
||||||
if (path) return await fs.promises.readFile(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
return await Promise.all(
|
|
||||||
certs.map(async (cert) => ({
|
|
||||||
origin: cert.origin,
|
|
||||||
cert: await bufferizeContent(cert.cert, cert.certPath),
|
|
||||||
key: await bufferizeContent(cert.key, cert.keyPath),
|
|
||||||
pfx: await bufferizeContent(cert.pfx, cert.pfxPath),
|
|
||||||
passphrase: cert.passphrase,
|
|
||||||
}))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copied from playwright-core
|
|
||||||
function toAcceptDownloadsProtocol(acceptDownloads?: boolean) {
|
|
||||||
if (acceptDownloads === undefined) return undefined
|
|
||||||
if (acceptDownloads) return 'accept'
|
|
||||||
return 'deny'
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copied from playwright-core
|
|
||||||
function prepareRecordHarOptions(
|
|
||||||
options: BrowserContextOptions['recordHar']
|
|
||||||
): channels.RecordHarOptions | undefined {
|
|
||||||
if (!options) return
|
|
||||||
return {
|
|
||||||
path: options.path,
|
|
||||||
content: options.content || (options.omitContent ? 'omit' : undefined),
|
|
||||||
urlGlob: isString(options.urlFilter) ? options.urlFilter : undefined,
|
|
||||||
urlRegexSource: isRegExp(options.urlFilter)
|
|
||||||
? options.urlFilter.source
|
|
||||||
: undefined,
|
|
||||||
urlRegexFlags: isRegExp(options.urlFilter)
|
|
||||||
? options.urlFilter.flags
|
|
||||||
: undefined,
|
|
||||||
mode: options.mode,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copied from playwright-core
|
|
||||||
async function prepareStorageState(
|
|
||||||
options: BrowserContextOptions
|
|
||||||
): Promise<channels.BrowserNewContextParams['storageState']> {
|
|
||||||
if (typeof options.storageState !== 'string') return options.storageState
|
|
||||||
try {
|
|
||||||
return JSON.parse(await fs.promises.readFile(options.storageState, 'utf8'))
|
|
||||||
} catch (e) {
|
|
||||||
rewriteErrorMessage(
|
|
||||||
e,
|
|
||||||
`Error reading storage state from ${options.storageState}:\n` + e.message
|
|
||||||
)
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copied from playwright-core
|
|
||||||
async function prepareBrowserContextParams(
|
|
||||||
options: BrowserContextOptions
|
|
||||||
): Promise<channels.BrowserNewContextParams> {
|
|
||||||
if (options.videoSize && !options.videosPath)
|
|
||||||
throw new Error(`"videoSize" option requires "videosPath" to be specified`)
|
|
||||||
if (options.extraHTTPHeaders)
|
|
||||||
network.validateHeaders(options.extraHTTPHeaders)
|
|
||||||
const contextParams: channels.BrowserNewContextParams = {
|
|
||||||
...options,
|
|
||||||
viewport: options.viewport === null ? undefined : options.viewport,
|
|
||||||
noDefaultViewport: options.viewport === null,
|
|
||||||
extraHTTPHeaders: options.extraHTTPHeaders
|
|
||||||
? headersObjectToArray(options.extraHTTPHeaders)
|
|
||||||
: undefined,
|
|
||||||
storageState: await prepareStorageState(options),
|
|
||||||
serviceWorkers: options.serviceWorkers,
|
|
||||||
recordHar: prepareRecordHarOptions(options.recordHar),
|
|
||||||
colorScheme:
|
|
||||||
options.colorScheme === null ? 'no-override' : options.colorScheme,
|
|
||||||
reducedMotion:
|
|
||||||
options.reducedMotion === null ? 'no-override' : options.reducedMotion,
|
|
||||||
forcedColors:
|
|
||||||
options.forcedColors === null ? 'no-override' : options.forcedColors,
|
|
||||||
acceptDownloads: toAcceptDownloadsProtocol(options.acceptDownloads),
|
|
||||||
clientCertificates: await toClientCertificatesProtocol(
|
|
||||||
options.clientCertificates
|
|
||||||
),
|
|
||||||
}
|
|
||||||
if (!contextParams.recordVideo && options.videosPath) {
|
|
||||||
contextParams.recordVideo = {
|
|
||||||
dir: options.videosPath,
|
|
||||||
size: options.videoSize,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (contextParams.recordVideo && contextParams.recordVideo.dir)
|
|
||||||
contextParams.recordVideo.dir = path.resolve(
|
|
||||||
process.cwd(),
|
|
||||||
contextParams.recordVideo.dir
|
|
||||||
)
|
|
||||||
return contextParams
|
|
||||||
}
|
|
||||||
|
|
||||||
const toNormalizedCode = (text: string) => {
|
const toNormalizedCode = (text: string) => {
|
||||||
return text.replace(/\s+/g, '')
|
return text.replace(/\s+/g, '')
|
||||||
}
|
}
|
||||||
@ -1042,9 +919,9 @@ export async function setup(
|
|||||||
// await page.reload()
|
// await page.reload()
|
||||||
}
|
}
|
||||||
|
|
||||||
let electronApp = undefined
|
let electronApp: ElectronApplication | undefined = undefined
|
||||||
let context = undefined
|
let context: BrowserContext | undefined = undefined
|
||||||
let page = undefined
|
let page: Page | undefined = undefined
|
||||||
|
|
||||||
export async function setupElectron({
|
export async function setupElectron({
|
||||||
testInfo,
|
testInfo,
|
||||||
@ -1055,7 +932,12 @@ export async function setupElectron({
|
|||||||
folderSetupFn?: (projectDirName: string) => Promise<void>
|
folderSetupFn?: (projectDirName: string) => Promise<void>
|
||||||
cleanProjectDir?: boolean
|
cleanProjectDir?: boolean
|
||||||
appSettings?: Partial<SaveSettingsPayload>
|
appSettings?: Partial<SaveSettingsPayload>
|
||||||
}) {
|
}): Promise<{
|
||||||
|
electronApp: ElectronApplication
|
||||||
|
context: BrowserContext
|
||||||
|
page: Page
|
||||||
|
dir: string
|
||||||
|
}> {
|
||||||
// create or otherwise clear the folder
|
// create or otherwise clear the folder
|
||||||
const projectDirName = testInfo.outputPath('electron-test-projects-dir')
|
const projectDirName = testInfo.outputPath('electron-test-projects-dir')
|
||||||
try {
|
try {
|
||||||
@ -1122,7 +1004,7 @@ export async function setupElectron({
|
|||||||
await fsp.writeFile(tempSettingsFilePath, settingsOverrides)
|
await fsp.writeFile(tempSettingsFilePath, settingsOverrides)
|
||||||
}
|
}
|
||||||
|
|
||||||
return { electronApp, page, context, dir: projectDirName, options }
|
return { electronApp, page, context, dir: projectDirName }
|
||||||
}
|
}
|
||||||
|
|
||||||
function failOnConsoleErrors(page: Page, testInfo?: TestInfo) {
|
function failOnConsoleErrors(page: Page, testInfo?: TestInfo) {
|
||||||
|
@ -5,10 +5,16 @@ import { getUtils } from './test-utils'
|
|||||||
|
|
||||||
test.describe('Testing Camera Movement', () => {
|
test.describe('Testing Camera Movement', () => {
|
||||||
test('Can move camera reliably', async ({ page, context, homePage }) => {
|
test('Can move camera reliably', async ({ page, context, homePage }) => {
|
||||||
|
// TODO: fix this test on windows too after the electron migration
|
||||||
|
const winOrMac =
|
||||||
|
process.platform === 'win32' || process.platform === 'darwin'
|
||||||
|
// eslint-disable-next-line
|
||||||
|
test.skip(winOrMac, 'Skip on windows')
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
|
await u.waitForPageLoad()
|
||||||
await u.openAndClearDebugPanel()
|
await u.openAndClearDebugPanel()
|
||||||
await u.closeKclCodePanel()
|
await u.closeKclCodePanel()
|
||||||
|
|
||||||
@ -172,174 +178,177 @@ test.describe('Testing Camera Movement', () => {
|
|||||||
}, [0, -85, -85])
|
}, [0, -85, -85])
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Zoom should be consistent when exiting or entering sketches', async ({
|
// TODO: fix after electron migration is merged
|
||||||
page,
|
test.fixme(
|
||||||
homePage,
|
'Zoom should be consistent when exiting or entering sketches',
|
||||||
}) => {
|
async ({ page, homePage }) => {
|
||||||
// start new sketch pan and zoom before exiting, when exiting the sketch should stay in the same place
|
// start new sketch pan and zoom before exiting, when exiting the sketch should stay in the same place
|
||||||
// than zoom and pan outside of sketch mode and enter again and it should not change from where it is
|
// than zoom and pan outside of sketch mode and enter again and it should not change from where it is
|
||||||
// than again for sketching
|
// than again for sketching
|
||||||
|
|
||||||
test.skip(process.platform !== 'darwin', 'Zoom should be consistent')
|
const u = await getUtils(page)
|
||||||
const u = await getUtils(page)
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
await u.openDebugPanel()
|
await u.waitForPageLoad()
|
||||||
|
await u.openDebugPanel()
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('button', { name: 'Start Sketch' })
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
).not.toBeDisabled()
|
).not.toBeDisabled()
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('button', { name: 'Start Sketch' })
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
|
|
||||||
// click on "Start Sketch" button
|
// click on "Start Sketch" button
|
||||||
await u.clearCommandLogs()
|
await u.clearCommandLogs()
|
||||||
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||||
await page.waitForTimeout(100)
|
|
||||||
|
|
||||||
// select a plane
|
|
||||||
await page.mouse.click(700, 325)
|
|
||||||
|
|
||||||
let code = `sketch001 = startSketchOn('XY')`
|
|
||||||
await expect(u.codeLocator).toHaveText(code)
|
|
||||||
await u.closeDebugPanel()
|
|
||||||
|
|
||||||
await page.waitForTimeout(500) // TODO detect animation ending, or disable animation
|
|
||||||
|
|
||||||
// move the camera slightly
|
|
||||||
await page.keyboard.down('Shift')
|
|
||||||
await page.mouse.move(700, 300)
|
|
||||||
await page.mouse.down({ button: 'right' })
|
|
||||||
await page.mouse.move(800, 200)
|
|
||||||
await page.mouse.up({ button: 'right' })
|
|
||||||
await page.keyboard.up('Shift')
|
|
||||||
|
|
||||||
let y = 350,
|
|
||||||
x = 948
|
|
||||||
|
|
||||||
await u.canvasLocator.click({ position: { x: 783, y } })
|
|
||||||
code += `\n |> startProfileAt([8.12, -12.98], %)`
|
|
||||||
// await expect(u.codeLocator).toHaveText(code)
|
|
||||||
await u.canvasLocator.click({ position: { x, y } })
|
|
||||||
code += `\n |> line([11.18, 0], %)`
|
|
||||||
// await expect(u.codeLocator).toHaveText(code)
|
|
||||||
await u.canvasLocator.click({ position: { x, y: 275 } })
|
|
||||||
code += `\n |> line([0, 6.99], %)`
|
|
||||||
// await expect(u.codeLocator).toHaveText(code)
|
|
||||||
|
|
||||||
// click the line button
|
|
||||||
await page.getByRole('button', { name: 'line Line', exact: true }).click()
|
|
||||||
|
|
||||||
const hoverOverNothing = async () => {
|
|
||||||
// await u.canvasLocator.hover({position: {x: 700, y: 325}})
|
|
||||||
await page.mouse.move(700, 325)
|
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
await expect(page.getByTestId('hover-highlight')).not.toBeVisible({
|
|
||||||
|
// select a plane
|
||||||
|
await page.mouse.click(700, 325)
|
||||||
|
|
||||||
|
let code = `sketch001 = startSketchOn('XY')`
|
||||||
|
await expect(u.codeLocator).toHaveText(code)
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
await page.waitForTimeout(500) // TODO detect animation ending, or disable animation
|
||||||
|
|
||||||
|
// move the camera slightly
|
||||||
|
await page.keyboard.down('Shift')
|
||||||
|
await page.mouse.move(700, 300)
|
||||||
|
await page.mouse.down({ button: 'right' })
|
||||||
|
await page.mouse.move(800, 200)
|
||||||
|
await page.mouse.up({ button: 'right' })
|
||||||
|
await page.keyboard.up('Shift')
|
||||||
|
|
||||||
|
let y = 350,
|
||||||
|
x = 948
|
||||||
|
|
||||||
|
await u.canvasLocator.click({ position: { x: 783, y } })
|
||||||
|
code += `\n |> startProfileAt([8.12, -12.98], %)`
|
||||||
|
// await expect(u.codeLocator).toHaveText(code)
|
||||||
|
await u.canvasLocator.click({ position: { x, y } })
|
||||||
|
code += `\n |> line([11.18, 0], %)`
|
||||||
|
// await expect(u.codeLocator).toHaveText(code)
|
||||||
|
await u.canvasLocator.click({ position: { x, y: 275 } })
|
||||||
|
code += `\n |> line([0, 6.99], %)`
|
||||||
|
// await expect(u.codeLocator).toHaveText(code)
|
||||||
|
|
||||||
|
// click the line button
|
||||||
|
await page.getByRole('button', { name: 'line Line', exact: true }).click()
|
||||||
|
|
||||||
|
const hoverOverNothing = async () => {
|
||||||
|
// await u.canvasLocator.hover({position: {x: 700, y: 325}})
|
||||||
|
await page.mouse.move(700, 325)
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await expect(page.getByTestId('hover-highlight')).not.toBeVisible({
|
||||||
|
timeout: 10_000,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
|
||||||
|
|
||||||
|
await page.waitForTimeout(200)
|
||||||
|
// hover over horizontal line
|
||||||
|
await u.canvasLocator.hover({ position: { x: 800, y } })
|
||||||
|
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
|
||||||
|
timeout: 10_000,
|
||||||
|
})
|
||||||
|
await page.waitForTimeout(200)
|
||||||
|
|
||||||
|
await hoverOverNothing()
|
||||||
|
await page.waitForTimeout(200)
|
||||||
|
// hover over vertical line
|
||||||
|
await u.canvasLocator.hover({ position: { x, y: 325 } })
|
||||||
|
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
|
||||||
|
timeout: 10_000,
|
||||||
|
})
|
||||||
|
|
||||||
|
await hoverOverNothing()
|
||||||
|
|
||||||
|
// click exit sketch
|
||||||
|
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
||||||
|
await page.waitForTimeout(400)
|
||||||
|
|
||||||
|
await hoverOverNothing()
|
||||||
|
await page.waitForTimeout(200)
|
||||||
|
// hover over horizontal line
|
||||||
|
await page.mouse.move(858, y, { steps: 5 })
|
||||||
|
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
|
||||||
|
timeout: 10_000,
|
||||||
|
})
|
||||||
|
|
||||||
|
await hoverOverNothing()
|
||||||
|
|
||||||
|
// hover over vertical line
|
||||||
|
await page.mouse.move(x, 325)
|
||||||
|
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
|
||||||
|
timeout: 10_000,
|
||||||
|
})
|
||||||
|
|
||||||
|
await hoverOverNothing()
|
||||||
|
|
||||||
|
// hover over vertical line
|
||||||
|
await page.mouse.move(857, y)
|
||||||
|
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
|
||||||
|
timeout: 10_000,
|
||||||
|
})
|
||||||
|
// now click it
|
||||||
|
await page.mouse.click(857, y)
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Edit Sketch' })
|
||||||
|
).toBeVisible()
|
||||||
|
await hoverOverNothing()
|
||||||
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||||
|
|
||||||
|
await page.waitForTimeout(400)
|
||||||
|
|
||||||
|
x = 975
|
||||||
|
y = 468
|
||||||
|
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.mouse.move(x, 419, { steps: 5 })
|
||||||
|
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
|
||||||
|
timeout: 10_000,
|
||||||
|
})
|
||||||
|
|
||||||
|
await hoverOverNothing()
|
||||||
|
|
||||||
|
await page.mouse.move(855, y)
|
||||||
|
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
|
||||||
|
timeout: 10_000,
|
||||||
|
})
|
||||||
|
|
||||||
|
await hoverOverNothing()
|
||||||
|
|
||||||
|
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
||||||
|
await page.waitForTimeout(200)
|
||||||
|
|
||||||
|
await hoverOverNothing()
|
||||||
|
await page.waitForTimeout(200)
|
||||||
|
|
||||||
|
await page.mouse.move(x, 419)
|
||||||
|
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
|
||||||
|
timeout: 10_000,
|
||||||
|
})
|
||||||
|
|
||||||
|
await hoverOverNothing()
|
||||||
|
|
||||||
|
await page.mouse.move(855, y)
|
||||||
|
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
|
||||||
timeout: 10_000,
|
timeout: 10_000,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
)
|
||||||
await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
|
|
||||||
|
|
||||||
await page.waitForTimeout(200)
|
|
||||||
// hover over horizontal line
|
|
||||||
await u.canvasLocator.hover({ position: { x: 800, y } })
|
|
||||||
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
|
|
||||||
timeout: 10_000,
|
|
||||||
})
|
|
||||||
await page.waitForTimeout(200)
|
|
||||||
|
|
||||||
await hoverOverNothing()
|
|
||||||
await page.waitForTimeout(200)
|
|
||||||
// hover over vertical line
|
|
||||||
await u.canvasLocator.hover({ position: { x, y: 325 } })
|
|
||||||
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
|
|
||||||
timeout: 10_000,
|
|
||||||
})
|
|
||||||
|
|
||||||
await hoverOverNothing()
|
|
||||||
|
|
||||||
// click exit sketch
|
|
||||||
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
|
||||||
await page.waitForTimeout(400)
|
|
||||||
|
|
||||||
await hoverOverNothing()
|
|
||||||
await page.waitForTimeout(200)
|
|
||||||
// hover over horizontal line
|
|
||||||
await page.mouse.move(858, y, { steps: 5 })
|
|
||||||
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
|
|
||||||
timeout: 10_000,
|
|
||||||
})
|
|
||||||
|
|
||||||
await hoverOverNothing()
|
|
||||||
|
|
||||||
// hover over vertical line
|
|
||||||
await page.mouse.move(x, 325)
|
|
||||||
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
|
|
||||||
timeout: 10_000,
|
|
||||||
})
|
|
||||||
|
|
||||||
await hoverOverNothing()
|
|
||||||
|
|
||||||
// hover over vertical line
|
|
||||||
await page.mouse.move(857, y)
|
|
||||||
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
|
|
||||||
timeout: 10_000,
|
|
||||||
})
|
|
||||||
// now click it
|
|
||||||
await page.mouse.click(857, y)
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
page.getByRole('button', { name: 'Edit Sketch' })
|
|
||||||
).toBeVisible()
|
|
||||||
await hoverOverNothing()
|
|
||||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
|
||||||
|
|
||||||
await page.waitForTimeout(400)
|
|
||||||
|
|
||||||
x = 975
|
|
||||||
y = 468
|
|
||||||
|
|
||||||
await page.waitForTimeout(100)
|
|
||||||
await page.mouse.move(x, 419, { steps: 5 })
|
|
||||||
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
|
|
||||||
timeout: 10_000,
|
|
||||||
})
|
|
||||||
|
|
||||||
await hoverOverNothing()
|
|
||||||
|
|
||||||
await page.mouse.move(855, y)
|
|
||||||
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
|
|
||||||
timeout: 10_000,
|
|
||||||
})
|
|
||||||
|
|
||||||
await hoverOverNothing()
|
|
||||||
|
|
||||||
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
|
||||||
await page.waitForTimeout(200)
|
|
||||||
|
|
||||||
await hoverOverNothing()
|
|
||||||
await page.waitForTimeout(200)
|
|
||||||
|
|
||||||
await page.mouse.move(x, 419)
|
|
||||||
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
|
|
||||||
timeout: 10_000,
|
|
||||||
})
|
|
||||||
|
|
||||||
await hoverOverNothing()
|
|
||||||
|
|
||||||
await page.mouse.move(855, y)
|
|
||||||
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
|
|
||||||
timeout: 10_000,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
test(`Zoom by scroll should not fire while orbiting`, async ({
|
test(`Zoom by scroll should not fire while orbiting`, async ({
|
||||||
page,
|
|
||||||
homePage,
|
homePage,
|
||||||
|
page,
|
||||||
}) => {
|
}) => {
|
||||||
|
// TODO: fix this test on windows after the electron migration
|
||||||
|
test.skip(process.platform === 'win32', 'Skip on windows')
|
||||||
/**
|
/**
|
||||||
* Currently we only allow zooming by scroll when no other camera movement is happening,
|
* Currently we only allow zooming by scroll when no other camera movement is happening,
|
||||||
* set within cameraMouseDragGuards in cameraControls.ts,
|
* set within cameraMouseDragGuards in cameraControls.ts,
|
||||||
@ -379,6 +388,7 @@ test.describe('Testing Camera Movement', () => {
|
|||||||
|
|
||||||
await test.step(`Test setup`, async () => {
|
await test.step(`Test setup`, async () => {
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
|
await u.waitForPageLoad()
|
||||||
await u.closeKclCodePanel()
|
await u.closeKclCodePanel()
|
||||||
// This test requires the mouse controls to be set to Solidworks
|
// This test requires the mouse controls to be set to Solidworks
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
@ -474,4 +484,31 @@ test.describe('Testing Camera Movement', () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('Right-click opens context menu when not dragged', async ({
|
||||||
|
homePage,
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
|
||||||
|
await homePage.goToModelingScene()
|
||||||
|
await u.waitForPageLoad()
|
||||||
|
|
||||||
|
await test.step(`The menu should not show if we drag the mouse`, async () => {
|
||||||
|
await page.mouse.move(900, 200)
|
||||||
|
await page.mouse.down({ button: 'right' })
|
||||||
|
await page.mouse.move(900, 300)
|
||||||
|
await page.mouse.up({ button: 'right' })
|
||||||
|
|
||||||
|
await expect(page.getByTestId('view-controls-menu')).not.toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`The menu should show if we don't drag the mouse`, async () => {
|
||||||
|
await page.mouse.move(900, 200)
|
||||||
|
await page.mouse.down({ button: 'right' })
|
||||||
|
await page.mouse.up({ button: 'right' })
|
||||||
|
|
||||||
|
await expect(page.getByTestId('view-controls-menu')).toBeVisible()
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
import { test, expect } from './zoo-test'
|
import { test, expect } from './zoo-test'
|
||||||
|
import * as fsp from 'fs/promises'
|
||||||
import {
|
import {
|
||||||
getUtils,
|
getUtils,
|
||||||
TEST_COLORS,
|
TEST_COLORS,
|
||||||
pollEditorLinesSelectedLength,
|
pollEditorLinesSelectedLength,
|
||||||
|
executorInputPath,
|
||||||
} from './test-utils'
|
} from './test-utils'
|
||||||
import { XOR } from 'lib/utils'
|
import { XOR } from 'lib/utils'
|
||||||
|
import path from 'node:path'
|
||||||
|
|
||||||
test.describe('Testing constraints', () => {
|
test.describe('Testing constraints', () => {
|
||||||
test('Can constrain line length', async ({ page, homePage }) => {
|
test('Can constrain line length', async ({ page, homePage }) => {
|
||||||
@ -23,7 +25,7 @@ test.describe('Testing constraints', () => {
|
|||||||
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
await u.waitForPageLoad()
|
await u.waitForPageLoad()
|
||||||
@ -43,15 +45,16 @@ test.describe('Testing constraints', () => {
|
|||||||
await page.waitForTimeout(1000)
|
await page.waitForTimeout(1000)
|
||||||
|
|
||||||
const startXPx = 500
|
const startXPx = 500
|
||||||
await page.mouse.move(startXPx + PUR * 15, 250 - PUR * 10)
|
|
||||||
await page.keyboard.down('Shift')
|
|
||||||
await page.mouse.click(834, 244)
|
|
||||||
await page.keyboard.up('Shift')
|
|
||||||
|
|
||||||
|
await page.getByText(`line([0, 20], %)`).click()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.getByTestId('constraint-length').click()
|
||||||
|
await page.getByTestId('cmd-bar-arg-value').getByRole('textbox').fill('20')
|
||||||
await page
|
await page
|
||||||
.getByRole('button', { name: 'dimension Length', exact: true })
|
.getByRole('button', {
|
||||||
|
name: 'arrow right Continue',
|
||||||
|
})
|
||||||
.click()
|
.click()
|
||||||
await page.getByText('Add constraining value').click()
|
|
||||||
|
|
||||||
await expect(page.locator('.cm-content')).toHaveText(
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
`length001 = 20sketch001 = startSketchOn('XY') |> startProfileAt([-10, -10], %) |> line([20, 0], %) |> angledLine([90, length001], %) |> xLine(-20, %)`
|
`length001 = 20sketch001 = startSketchOn('XY') |> startProfileAt([-10, -10], %) |> line([20, 0], %) |> angledLine([90, length001], %) |> xLine(-20, %)`
|
||||||
@ -71,7 +74,7 @@ test.describe('Testing constraints', () => {
|
|||||||
await page.keyboard.press('Escape', { delay: 500 })
|
await page.keyboard.press('Escape', { delay: 500 })
|
||||||
return page.getByRole('button', { name: 'Exit Sketch' }).isVisible()
|
return page.getByRole('button', { name: 'Exit Sketch' }).isVisible()
|
||||||
})
|
})
|
||||||
.toBe(true)
|
.toBe(false)
|
||||||
})
|
})
|
||||||
test(`Remove constraints`, async ({ page, homePage }) => {
|
test(`Remove constraints`, async ({ page, homePage }) => {
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
@ -124,6 +127,8 @@ test.describe('Testing constraints', () => {
|
|||||||
await expect(page.getByTestId('segment-overlay')).toHaveCount(4)
|
await expect(page.getByTestId('segment-overlay')).toHaveCount(4)
|
||||||
})
|
})
|
||||||
test.describe('Test perpendicular distance constraint', () => {
|
test.describe('Test perpendicular distance constraint', () => {
|
||||||
|
// TODO: fix this test on windows after the electron migration
|
||||||
|
test.skip(process.platform === 'win32', 'Skip on windows')
|
||||||
const cases = [
|
const cases = [
|
||||||
{
|
{
|
||||||
testName: 'Add variable',
|
testName: 'Add variable',
|
||||||
@ -244,6 +249,8 @@ test.describe('Testing constraints', () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
test.describe('Test distance between constraint', () => {
|
test.describe('Test distance between constraint', () => {
|
||||||
|
// TODO: fix this test on windows after the electron migration
|
||||||
|
test.skip(process.platform === 'win32', 'Skip on windows')
|
||||||
const cases = [
|
const cases = [
|
||||||
{
|
{
|
||||||
testName: 'Add variable',
|
testName: 'Add variable',
|
||||||
@ -463,6 +470,8 @@ test.describe('Testing constraints', () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
test.describe('Test Angle constraint double segment selection', () => {
|
test.describe('Test Angle constraint double segment selection', () => {
|
||||||
|
// TODO: fix this test on windows after the electron migration
|
||||||
|
test.skip(process.platform === 'win32', 'Skip on windows')
|
||||||
const cases = [
|
const cases = [
|
||||||
{
|
{
|
||||||
testName: 'Add variable',
|
testName: 'Add variable',
|
||||||
@ -653,6 +662,8 @@ test.describe('Testing constraints', () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
test.describe('Test Length constraint single selection', () => {
|
test.describe('Test Length constraint single selection', () => {
|
||||||
|
// TODO: fix this test on windows after the electron migration
|
||||||
|
test.skip(process.platform === 'win32', 'Skip on windows')
|
||||||
const cases = [
|
const cases = [
|
||||||
{
|
{
|
||||||
testName: 'Length - Add variable',
|
testName: 'Length - Add variable',
|
||||||
@ -838,6 +849,8 @@ part002 = startSketchOn('XZ')
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
test.describe('Two segment - no modal constraints', () => {
|
test.describe('Two segment - no modal constraints', () => {
|
||||||
|
// TODO: fix this test on windows after the electron migration
|
||||||
|
test.skip(process.platform === 'win32', 'Skip on windows')
|
||||||
const cases = [
|
const cases = [
|
||||||
{
|
{
|
||||||
codeAfter: `|> angledLine([83, segLen(seg01)], %)`,
|
codeAfter: `|> angledLine([83, segLen(seg01)], %)`,
|
||||||
@ -998,104 +1011,162 @@ part002 = startSketchOn('XZ')
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Horizontally constrained line remains selected after applying constraint', async ({
|
test.fixme(
|
||||||
page,
|
'Horizontally constrained line remains selected after applying constraint',
|
||||||
homePage,
|
async ({ page, homePage }) => {
|
||||||
}) => {
|
test.setTimeout(70_000)
|
||||||
test.setTimeout(70_000)
|
await page.addInitScript(async () => {
|
||||||
await page.addInitScript(async () => {
|
localStorage.setItem(
|
||||||
localStorage.setItem(
|
'persistCode',
|
||||||
'persistCode',
|
`sketch001 = startSketchOn('XY')
|
||||||
`sketch001 = startSketchOn('XY')
|
|
||||||
|> startProfileAt([-1.05, -1.07], %)
|
|> startProfileAt([-1.05, -1.07], %)
|
||||||
|> line([3.79, 2.68], %, $seg01)
|
|> line([3.79, 2.68], %, $seg01)
|
||||||
|> line([3.13, -2.4], %)`
|
|> line([3.13, -2.4], %)`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await homePage.goToModelingScene()
|
||||||
|
await u.waitForPageLoad()
|
||||||
|
|
||||||
|
await page.getByText('line([3.79, 2.68], %, $seg01)').click()
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Edit Sketch' })
|
||||||
|
).toBeEnabled({ timeout: 10_000 })
|
||||||
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||||
|
|
||||||
|
// Wait for overlays to populate
|
||||||
|
await page.waitForTimeout(1000)
|
||||||
|
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
const lineBefore = await u.getSegmentBodyCoords(
|
||||||
|
`[data-overlay-index="1"]`,
|
||||||
|
0
|
||||||
)
|
)
|
||||||
})
|
expect(
|
||||||
const u = await getUtils(page)
|
await u.getGreatestPixDiff(lineBefore, TEST_COLORS.WHITE)
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
).toBeLessThan(3)
|
||||||
|
await page.mouse.move(lineBefore.x, lineBefore.y)
|
||||||
|
await page.waitForTimeout(50)
|
||||||
|
await page.mouse.click(lineBefore.x, lineBefore.y)
|
||||||
|
expect(
|
||||||
|
await u.getGreatestPixDiff(lineBefore, TEST_COLORS.BLUE)
|
||||||
|
).toBeLessThan(3)
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await page
|
||||||
await u.waitForPageLoad()
|
.getByRole('button', {
|
||||||
|
name: 'Length: open menu',
|
||||||
|
})
|
||||||
|
.click()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page
|
||||||
|
.getByRole('button', { name: 'Horizontal', exact: true })
|
||||||
|
.click()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
|
||||||
await page.getByText('line([3.79, 2.68], %, $seg01)').click()
|
await pollEditorLinesSelectedLength(page, 1)
|
||||||
await expect(page.getByRole('button', { name: 'Edit Sketch' })).toBeEnabled(
|
let activeLinesContent = await page.locator('.cm-activeLine').all()
|
||||||
{ timeout: 10_000 }
|
await expect(activeLinesContent[0]).toHaveText(`|> xLine(3.13, %)`)
|
||||||
)
|
|
||||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
|
||||||
|
|
||||||
// Wait for overlays to populate
|
// Wait for code editor to settle.
|
||||||
await page.waitForTimeout(1000)
|
await page.waitForTimeout(2000)
|
||||||
|
|
||||||
await page.waitForTimeout(100)
|
// If the overlay-angle is updated the THREE.js scene is in a good state
|
||||||
const lineBefore = await u.getSegmentBodyCoords(
|
await expect(
|
||||||
`[data-overlay-index="1"]`,
|
await page.locator('[data-overlay-index="1"]')
|
||||||
0
|
).toHaveAttribute('data-overlay-angle', '0')
|
||||||
)
|
|
||||||
expect(
|
|
||||||
await u.getGreatestPixDiff(lineBefore, TEST_COLORS.WHITE)
|
|
||||||
).toBeLessThan(3)
|
|
||||||
await page.mouse.move(lineBefore.x, lineBefore.y)
|
|
||||||
await page.waitForTimeout(50)
|
|
||||||
await page.mouse.click(lineBefore.x, lineBefore.y)
|
|
||||||
expect(
|
|
||||||
await u.getGreatestPixDiff(lineBefore, TEST_COLORS.BLUE)
|
|
||||||
).toBeLessThan(3)
|
|
||||||
|
|
||||||
await page
|
const lineAfter = await u.getSegmentBodyCoords(
|
||||||
.getByRole('button', {
|
`[data-overlay-index="1"]`,
|
||||||
name: 'Length: open menu',
|
0
|
||||||
})
|
)
|
||||||
.click()
|
|
||||||
await page.waitForTimeout(500)
|
|
||||||
await page.getByRole('button', { name: 'Horizontal', exact: true }).click()
|
|
||||||
await page.waitForTimeout(500)
|
|
||||||
|
|
||||||
await pollEditorLinesSelectedLength(page, 1)
|
const linebb = await u.getBoundingBox('[data-overlay-index="1"]')
|
||||||
let activeLinesContent = await page.locator('.cm-activeLine').all()
|
await page.mouse.move(linebb.x, linebb.y, { steps: 25 })
|
||||||
await expect(activeLinesContent[0]).toHaveText(`|> xLine(3.13, %)`)
|
await page.mouse.click(linebb.x, linebb.y)
|
||||||
|
|
||||||
// Wait for code editor to settle.
|
await expect
|
||||||
await page.waitForTimeout(2000)
|
.poll(
|
||||||
|
async () => await u.getGreatestPixDiff(lineAfter, TEST_COLORS.BLUE)
|
||||||
|
)
|
||||||
|
.toBeLessThan(3)
|
||||||
|
|
||||||
// If the overlay-angle is updated the THREE.js scene is in a good state
|
await page.waitForTimeout(500)
|
||||||
await expect(
|
|
||||||
await page.locator('[data-overlay-index="1"]')
|
|
||||||
).toHaveAttribute('data-overlay-angle', '0')
|
|
||||||
|
|
||||||
const lineAfter = await u.getSegmentBodyCoords(
|
// await expect(page.getByRole('button', { name: 'length', exact: true })).toBeVisible()
|
||||||
`[data-overlay-index="1"]`,
|
await page.waitForTimeout(200)
|
||||||
0
|
// await page.getByRole('button', { name: 'length', exact: true }).click()
|
||||||
)
|
await page.getByTestId('constraint-length').click()
|
||||||
|
|
||||||
const linebb = await u.getBoundingBox('[data-overlay-index="1"]')
|
await page
|
||||||
await page.mouse.move(linebb.x, linebb.y, { steps: 25 })
|
.getByTestId('cmd-bar-arg-value')
|
||||||
await page.mouse.click(linebb.x, linebb.y)
|
.getByRole('textbox')
|
||||||
|
.fill('10')
|
||||||
|
await page
|
||||||
|
.getByRole('button', {
|
||||||
|
name: 'arrow right Continue',
|
||||||
|
})
|
||||||
|
.click()
|
||||||
|
|
||||||
await expect
|
await pollEditorLinesSelectedLength(page, 1)
|
||||||
.poll(async () => await u.getGreatestPixDiff(lineAfter, TEST_COLORS.BLUE))
|
activeLinesContent = await page.locator('.cm-activeLine').all()
|
||||||
.toBeLessThan(3)
|
await expect(activeLinesContent[0]).toHaveText(`|> xLine(length001, %)`)
|
||||||
|
|
||||||
await page.waitForTimeout(500)
|
// checking the count of the overlays is a good proxy check that the client sketch scene is in a good state
|
||||||
|
await expect(page.getByTestId('segment-overlay')).toHaveCount(2)
|
||||||
await page
|
}
|
||||||
.getByRole('button', {
|
)
|
||||||
name: 'Length: open menu',
|
})
|
||||||
})
|
test.describe('Electron constraint tests', () => {
|
||||||
.click()
|
test(
|
||||||
// await expect(page.getByRole('button', { name: 'length', exact: true })).toBeVisible()
|
'Able to double click label to set constraint',
|
||||||
await page.waitForTimeout(200)
|
{ tag: '@electron' },
|
||||||
// await page.getByRole('button', { name: 'length', exact: true }).click()
|
async ({ page, context, homePage, scene, editor, toolbar }) => {
|
||||||
await page.getByTestId('dropdown-constraint-length').click()
|
await context.folderSetupFn(async (dir) => {
|
||||||
|
const bracketDir = path.join(dir, 'test-sample')
|
||||||
await page.getByLabel('length Value').fill('10')
|
await fsp.mkdir(bracketDir, { recursive: true })
|
||||||
await page.getByRole('button', { name: 'Add constraining value' }).click()
|
await fsp.copyFile(
|
||||||
|
executorInputPath('angled_line.kcl'),
|
||||||
await pollEditorLinesSelectedLength(page, 1)
|
path.join(bracketDir, 'main.kcl')
|
||||||
activeLinesContent = await page.locator('.cm-activeLine').all()
|
)
|
||||||
await expect(activeLinesContent[0]).toHaveText(`|> xLine(length001, %)`)
|
})
|
||||||
|
const [clickHandler] = scene.makeMouseHelpers(600, 300)
|
||||||
// checking the count of the overlays is a good proxy check that the client sketch scene is in a good state
|
|
||||||
await expect(page.getByTestId('segment-overlay')).toHaveCount(2)
|
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 test.step('Double click to constrain', async () => {
|
||||||
|
await clickHandler()
|
||||||
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||||
|
const child = page
|
||||||
|
.locator('.segment-length-label-text')
|
||||||
|
.first()
|
||||||
|
.locator('xpath=..')
|
||||||
|
await child.dblclick()
|
||||||
|
const cmdBarSubmitButton = page.getByRole('button', {
|
||||||
|
name: 'arrow right Continue',
|
||||||
|
})
|
||||||
|
await cmdBarSubmitButton.click()
|
||||||
|
await expect(page.locator('.cm-content')).toContainText(
|
||||||
|
'length001 = 15.3'
|
||||||
|
)
|
||||||
|
await expect(page.locator('.cm-content')).toContainText(
|
||||||
|
'|> angledLine([9, length001], %)'
|
||||||
|
)
|
||||||
|
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
@ -4,6 +4,8 @@ import { uuidv4 } from 'lib/utils'
|
|||||||
import { TEST_CODE_GIZMO } from './storageStates'
|
import { TEST_CODE_GIZMO } from './storageStates'
|
||||||
|
|
||||||
test.describe('Testing Gizmo', () => {
|
test.describe('Testing Gizmo', () => {
|
||||||
|
// TODO: fix this test on windows after the electron migration
|
||||||
|
test.skip(process.platform === 'win32', 'Skip on windows')
|
||||||
const cases = [
|
const cases = [
|
||||||
{
|
{
|
||||||
testDescription: 'top view',
|
testDescription: 'top view',
|
||||||
|
@ -6,7 +6,11 @@ import { uuidv4 } from 'lib/utils'
|
|||||||
import { EditorFixture } from './fixtures/editorFixture'
|
import { EditorFixture } from './fixtures/editorFixture'
|
||||||
|
|
||||||
test.describe('Testing segment overlays', () => {
|
test.describe('Testing segment overlays', () => {
|
||||||
|
// TODO: fix this test on windows after the electron migration
|
||||||
|
test.skip(process.platform === 'win32', 'Skip on windows')
|
||||||
test.describe('Hover over a segment should show its overlay, hovering over the input overlays should show its popover, clicking the input overlay should constrain/unconstrain it:\nfor the following segments', () => {
|
test.describe('Hover over a segment should show its overlay, hovering over the input overlays should show its popover, clicking the input overlay should constrain/unconstrain it:\nfor the following segments', () => {
|
||||||
|
// TODO: fix this test on mac after the electron migration
|
||||||
|
test.skip(process.platform === 'darwin', 'Skip on mac')
|
||||||
/**
|
/**
|
||||||
* Clicks on an constrained element
|
* Clicks on an constrained element
|
||||||
* @param {Page} page - The page to perform the action on
|
* @param {Page} page - The page to perform the action on
|
||||||
@ -226,7 +230,7 @@ test.describe('Testing segment overlays', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
|
|
||||||
@ -299,9 +303,10 @@ test.describe('Testing segment overlays', () => {
|
|||||||
hoverPos: { x: angledLine.x, y: angledLine.y },
|
hoverPos: { x: angledLine.x, y: angledLine.y },
|
||||||
constraintType: 'angle',
|
constraintType: 'angle',
|
||||||
expectBeforeUnconstrained:
|
expectBeforeUnconstrained:
|
||||||
'angledLine({ angle: 3 + 0, length: 32 + 0 }, %)',
|
'angledLine({ angle = 3 + 0, length = 32 + 0 }, %)',
|
||||||
expectAfterUnconstrained: 'angledLine({ angle: 3, length: 32 + 0 }, %)',
|
expectAfterUnconstrained:
|
||||||
expectFinal: 'angledLine({ angle: angle001, length: 32 + 0 }, %)',
|
'angledLine({ angle = 3, length = 32 + 0 }, %)',
|
||||||
|
expectFinal: 'angledLine({ angle = angle001, length = 32 + 0 }, %)',
|
||||||
ang: ang + 180,
|
ang: ang + 180,
|
||||||
locator: '[data-overlay-toolbar-index="1"]',
|
locator: '[data-overlay-toolbar-index="1"]',
|
||||||
})
|
})
|
||||||
@ -310,10 +315,10 @@ test.describe('Testing segment overlays', () => {
|
|||||||
hoverPos: { x: angledLine.x, y: angledLine.y },
|
hoverPos: { x: angledLine.x, y: angledLine.y },
|
||||||
constraintType: 'length',
|
constraintType: 'length',
|
||||||
expectBeforeUnconstrained:
|
expectBeforeUnconstrained:
|
||||||
'angledLine({ angle: angle001, length: 32 + 0 }, %)',
|
'angledLine({ angle = angle001, length = 32 + 0 }, %)',
|
||||||
expectAfterUnconstrained:
|
expectAfterUnconstrained:
|
||||||
'angledLine({ angle: angle001, length: 32 }, %)',
|
'angledLine({ angle = angle001, length = 32 }, %)',
|
||||||
expectFinal: 'angledLine({ angle: angle001, length: len001 }, %)',
|
expectFinal: 'angledLine({ angle = angle001, length = len001 }, %)',
|
||||||
ang: ang + 180,
|
ang: ang + 180,
|
||||||
locator: '[data-overlay-toolbar-index="1"]',
|
locator: '[data-overlay-toolbar-index="1"]',
|
||||||
})
|
})
|
||||||
@ -360,15 +365,15 @@ test.describe('Testing segment overlays', () => {
|
|||||||
locator: '[data-overlay-toolbar-index="3"]',
|
locator: '[data-overlay-toolbar-index="3"]',
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
test('for segments [yLineTo, xLine]', async ({
|
|
||||||
page,
|
// Broken on main at time of writing!
|
||||||
editor,
|
test.fixme(
|
||||||
homePage,
|
'for segments [yLineTo, xLine]',
|
||||||
}) => {
|
async ({ page, editor, homePage }) => {
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'persistCode',
|
'persistCode',
|
||||||
`yRel001 = -14
|
`yRel001 = -14
|
||||||
xRel001 = 0.5
|
xRel001 = 0.5
|
||||||
angle001 = 3
|
angle001 = 3
|
||||||
len001 = 32
|
len001 = 32
|
||||||
@ -386,59 +391,60 @@ test.describe('Testing segment overlays', () => {
|
|||||||
|> yLine(21.14 + 0, %)
|
|> yLine(21.14 + 0, %)
|
||||||
|> angledLineOfXLength({ angle = 181 + 0, length = 23.14 }, %)
|
|> angledLineOfXLength({ angle = 181 + 0, length = 23.14 }, %)
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
|
|
||||||
// wait for execution done
|
// wait for execution done
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
await page.getByText('xLine(26.04, %)').click()
|
await page.getByText('xLine(26.04, %)').click()
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||||
await page.waitForTimeout(500)
|
await page.waitForTimeout(500)
|
||||||
|
|
||||||
await expect(page.getByTestId('segment-overlay')).toHaveCount(8)
|
await expect(page.getByTestId('segment-overlay')).toHaveCount(8)
|
||||||
|
|
||||||
const clickUnconstrained = _clickUnconstrained(page, editor)
|
const clickUnconstrained = _clickUnconstrained(page, editor)
|
||||||
|
|
||||||
await page.mouse.move(700, 250)
|
await page.mouse.move(700, 250)
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
let ang = 0
|
let ang = 0
|
||||||
|
|
||||||
const yLineTo = await u.getBoundingBox(`[data-overlay-index="4"]`)
|
const yLineTo = await u.getBoundingBox(`[data-overlay-index="4"]`)
|
||||||
ang = await u.getAngle(`[data-overlay-index="4"]`)
|
ang = await u.getAngle(`[data-overlay-index="4"]`)
|
||||||
console.log('ylineTo1')
|
console.log('ylineTo1')
|
||||||
await clickUnconstrained({
|
await clickUnconstrained({
|
||||||
hoverPos: { x: yLineTo.x, y: yLineTo.y },
|
hoverPos: { x: yLineTo.x, y: yLineTo.y - 200 },
|
||||||
constraintType: 'yAbsolute',
|
constraintType: 'yAbsolute',
|
||||||
expectBeforeUnconstrained: 'yLineTo(-10.77, %, $a)',
|
expectBeforeUnconstrained: 'yLineTo(-10.77, %, $a)',
|
||||||
expectAfterUnconstrained: 'yLineTo(yAbs002, %, $a)',
|
expectAfterUnconstrained: 'yLineTo(yAbs002, %, $a)',
|
||||||
expectFinal: 'yLineTo(-10.77, %, $a)',
|
expectFinal: 'yLineTo(-10.77, %, $a)',
|
||||||
ang: ang + 180,
|
ang: ang + 180,
|
||||||
locator: '[data-overlay-toolbar-index="4"]',
|
locator: '[data-overlay-toolbar-index="4"]',
|
||||||
})
|
})
|
||||||
|
|
||||||
const xLine = await u.getBoundingBox(`[data-overlay-index="5"]`)
|
const xLine = await u.getBoundingBox(`[data-overlay-index="5"]`)
|
||||||
ang = await u.getAngle(`[data-overlay-index="5"]`)
|
ang = await u.getAngle(`[data-overlay-index="5"]`)
|
||||||
console.log('xline')
|
console.log('xline')
|
||||||
await clickUnconstrained({
|
await clickUnconstrained({
|
||||||
hoverPos: { x: xLine.x, y: xLine.y },
|
hoverPos: { x: xLine.x, y: xLine.y },
|
||||||
constraintType: 'xRelative',
|
constraintType: 'xRelative',
|
||||||
expectBeforeUnconstrained: 'xLine(26.04, %)',
|
expectBeforeUnconstrained: 'xLine(26.04, %)',
|
||||||
expectAfterUnconstrained: 'xLine(xRel002, %)',
|
expectAfterUnconstrained: 'xLine(xRel002, %)',
|
||||||
expectFinal: 'xLine(26.04, %)',
|
expectFinal: 'xLine(26.04, %)',
|
||||||
steps: 10,
|
steps: 10,
|
||||||
ang: ang + 180,
|
ang: ang + 180,
|
||||||
locator: '[data-overlay-toolbar-index="5"]',
|
locator: '[data-overlay-toolbar-index="5"]',
|
||||||
})
|
})
|
||||||
})
|
}
|
||||||
|
)
|
||||||
test('for segments [yLine, angledLineOfXLength, angledLineOfYLength]', async ({
|
test('for segments [yLine, angledLineOfXLength, angledLineOfYLength]', async ({
|
||||||
page,
|
page,
|
||||||
editor,
|
editor,
|
||||||
@ -471,7 +477,7 @@ test.describe('Testing segment overlays', () => {
|
|||||||
localStorage.setItem('disableAxis', 'true')
|
localStorage.setItem('disableAxis', 'true')
|
||||||
})
|
})
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
|
|
||||||
@ -515,11 +521,11 @@ test.describe('Testing segment overlays', () => {
|
|||||||
hoverPos: { x: angledLineOfXLength.x, y: angledLineOfXLength.y },
|
hoverPos: { x: angledLineOfXLength.x, y: angledLineOfXLength.y },
|
||||||
constraintType: 'angle',
|
constraintType: 'angle',
|
||||||
expectBeforeUnconstrained:
|
expectBeforeUnconstrained:
|
||||||
'angledLineOfXLength({ angle: 181 + 0, length: 23.14 }, %)',
|
'angledLineOfXLength({ angle = 181 + 0, length = 23.14 }, %)',
|
||||||
expectAfterUnconstrained:
|
expectAfterUnconstrained:
|
||||||
'angledLineOfXLength({ angle: -179, length: 23.14 }, %)',
|
'angledLineOfXLength({ angle = -179, length = 23.14 }, %)',
|
||||||
expectFinal:
|
expectFinal:
|
||||||
'angledLineOfXLength({ angle: angle001, length: 23.14 }, %)',
|
'angledLineOfXLength({ angle = angle001, length = 23.14 }, %)',
|
||||||
ang: ang + 180,
|
ang: ang + 180,
|
||||||
locator: '[data-overlay-toolbar-index="7"]',
|
locator: '[data-overlay-toolbar-index="7"]',
|
||||||
})
|
})
|
||||||
@ -528,11 +534,11 @@ test.describe('Testing segment overlays', () => {
|
|||||||
hoverPos: { x: angledLineOfXLength.x, y: angledLineOfXLength.y },
|
hoverPos: { x: angledLineOfXLength.x, y: angledLineOfXLength.y },
|
||||||
constraintType: 'xRelative',
|
constraintType: 'xRelative',
|
||||||
expectBeforeUnconstrained:
|
expectBeforeUnconstrained:
|
||||||
'angledLineOfXLength({ angle: angle001, length: 23.14 }, %)',
|
'angledLineOfXLength({ angle = angle001, length = 23.14 }, %)',
|
||||||
expectAfterUnconstrained:
|
expectAfterUnconstrained:
|
||||||
'angledLineOfXLength({ angle: angle001, length: xRel001 }, %)',
|
'angledLineOfXLength({ angle = angle001, length = xRel001 }, %)',
|
||||||
expectFinal:
|
expectFinal:
|
||||||
'angledLineOfXLength({ angle: angle001, length: 23.14 }, %)',
|
'angledLineOfXLength({ angle = angle001, length = 23.14 }, %)',
|
||||||
steps: 7,
|
steps: 7,
|
||||||
ang: ang + 180,
|
ang: ang + 180,
|
||||||
locator: '[data-overlay-toolbar-index="7"]',
|
locator: '[data-overlay-toolbar-index="7"]',
|
||||||
@ -547,10 +553,10 @@ test.describe('Testing segment overlays', () => {
|
|||||||
hoverPos: { x: angledLineOfYLength.x, y: angledLineOfYLength.y },
|
hoverPos: { x: angledLineOfYLength.x, y: angledLineOfYLength.y },
|
||||||
constraintType: 'angle',
|
constraintType: 'angle',
|
||||||
expectBeforeUnconstrained:
|
expectBeforeUnconstrained:
|
||||||
'angledLineOfYLength({ angle: -91, length: 19 + 0 }, %)',
|
'angledLineOfYLength({ angle = -91, length = 19 + 0 }, %)',
|
||||||
expectAfterUnconstrained:
|
expectAfterUnconstrained:
|
||||||
'angledLineOfYLength({ angle: angle002, length: 19 + 0 }, %)',
|
'angledLineOfYLength({ angle = angle002, length = 19 + 0 }, %)',
|
||||||
expectFinal: 'angledLineOfYLength({ angle: -91, length: 19 + 0 }, %)',
|
expectFinal: 'angledLineOfYLength({ angle = -91, length = 19 + 0 }, %)',
|
||||||
ang: ang + 180,
|
ang: ang + 180,
|
||||||
steps: 6,
|
steps: 6,
|
||||||
locator: '[data-overlay-toolbar-index="8"]',
|
locator: '[data-overlay-toolbar-index="8"]',
|
||||||
@ -560,10 +566,11 @@ test.describe('Testing segment overlays', () => {
|
|||||||
hoverPos: { x: angledLineOfYLength.x, y: angledLineOfYLength.y },
|
hoverPos: { x: angledLineOfYLength.x, y: angledLineOfYLength.y },
|
||||||
constraintType: 'yRelative',
|
constraintType: 'yRelative',
|
||||||
expectBeforeUnconstrained:
|
expectBeforeUnconstrained:
|
||||||
'angledLineOfYLength({ angle: -91, length: 19 + 0 }, %)',
|
'angledLineOfYLength({ angle = -91, length = 19 + 0 }, %)',
|
||||||
expectAfterUnconstrained:
|
expectAfterUnconstrained:
|
||||||
'angledLineOfYLength({ angle: -91, length: 19 }, %)',
|
'angledLineOfYLength({ angle = -91, length = 19 }, %)',
|
||||||
expectFinal: 'angledLineOfYLength({ angle: -91, length: yRel002 }, %)',
|
expectFinal:
|
||||||
|
'angledLineOfYLength({ angle = -91, length = yRel002 }, %)',
|
||||||
ang: ang + 180,
|
ang: ang + 180,
|
||||||
steps: 7,
|
steps: 7,
|
||||||
locator: '[data-overlay-toolbar-index="8"]',
|
locator: '[data-overlay-toolbar-index="8"]',
|
||||||
@ -601,7 +608,7 @@ test.describe('Testing segment overlays', () => {
|
|||||||
localStorage.setItem('disableAxis', 'true')
|
localStorage.setItem('disableAxis', 'true')
|
||||||
})
|
})
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
|
|
||||||
@ -628,9 +635,10 @@ test.describe('Testing segment overlays', () => {
|
|||||||
await clickConstrained({
|
await clickConstrained({
|
||||||
hoverPos: { x: angledLineToX.x, y: angledLineToX.y },
|
hoverPos: { x: angledLineToX.x, y: angledLineToX.y },
|
||||||
constraintType: 'angle',
|
constraintType: 'angle',
|
||||||
expectBeforeUnconstrained: 'angledLineToX({ angle: 3 + 0, to: 26 }, %)',
|
expectBeforeUnconstrained:
|
||||||
expectAfterUnconstrained: 'angledLineToX({ angle: 3, to: 26 }, %)',
|
'angledLineToX({ angle = 3 + 0, to = 26 }, %)',
|
||||||
expectFinal: 'angledLineToX({ angle: angle001, to: 26 }, %)',
|
expectAfterUnconstrained: 'angledLineToX({ angle = 3, to = 26 }, %)',
|
||||||
|
expectFinal: 'angledLineToX({ angle = angle001, to = 26 }, %)',
|
||||||
ang: ang + 180,
|
ang: ang + 180,
|
||||||
locator: '[data-overlay-toolbar-index="9"]',
|
locator: '[data-overlay-toolbar-index="9"]',
|
||||||
})
|
})
|
||||||
@ -639,10 +647,10 @@ test.describe('Testing segment overlays', () => {
|
|||||||
hoverPos: { x: angledLineToX.x, y: angledLineToX.y },
|
hoverPos: { x: angledLineToX.x, y: angledLineToX.y },
|
||||||
constraintType: 'xAbsolute',
|
constraintType: 'xAbsolute',
|
||||||
expectBeforeUnconstrained:
|
expectBeforeUnconstrained:
|
||||||
'angledLineToX({ angle: angle001, to: 26 }, %)',
|
'angledLineToX({ angle = angle001, to = 26 }, %)',
|
||||||
expectAfterUnconstrained:
|
expectAfterUnconstrained:
|
||||||
'angledLineToX({ angle: angle001, to: xAbs001 }, %)',
|
'angledLineToX({ angle = angle001, to = xAbs001 }, %)',
|
||||||
expectFinal: 'angledLineToX({ angle: angle001, to: 26 }, %)',
|
expectFinal: 'angledLineToX({ angle = angle001, to = 26 }, %)',
|
||||||
ang: ang + 180,
|
ang: ang + 180,
|
||||||
locator: '[data-overlay-toolbar-index="9"]',
|
locator: '[data-overlay-toolbar-index="9"]',
|
||||||
})
|
})
|
||||||
@ -654,10 +662,10 @@ test.describe('Testing segment overlays', () => {
|
|||||||
hoverPos: { x: angledLineToY.x, y: angledLineToY.y },
|
hoverPos: { x: angledLineToY.x, y: angledLineToY.y },
|
||||||
constraintType: 'angle',
|
constraintType: 'angle',
|
||||||
expectBeforeUnconstrained:
|
expectBeforeUnconstrained:
|
||||||
'angledLineToY({ angle: 89, to: 9.14 + 0 }, %)',
|
'angledLineToY({ angle = 89, to = 9.14 + 0 }, %)',
|
||||||
expectAfterUnconstrained:
|
expectAfterUnconstrained:
|
||||||
'angledLineToY({ angle: angle002, to: 9.14 + 0 }, %)',
|
'angledLineToY({ angle = angle002, to = 9.14 + 0 }, %)',
|
||||||
expectFinal: 'angledLineToY({ angle: 89, to: 9.14 + 0 }, %)',
|
expectFinal: 'angledLineToY({ angle = 89, to = 9.14 + 0 }, %)',
|
||||||
steps: process.platform === 'darwin' ? 8 : 9,
|
steps: process.platform === 'darwin' ? 8 : 9,
|
||||||
ang: ang + 180,
|
ang: ang + 180,
|
||||||
locator: '[data-overlay-toolbar-index="10"]',
|
locator: '[data-overlay-toolbar-index="10"]',
|
||||||
@ -667,9 +675,9 @@ test.describe('Testing segment overlays', () => {
|
|||||||
hoverPos: { x: angledLineToY.x, y: angledLineToY.y },
|
hoverPos: { x: angledLineToY.x, y: angledLineToY.y },
|
||||||
constraintType: 'yAbsolute',
|
constraintType: 'yAbsolute',
|
||||||
expectBeforeUnconstrained:
|
expectBeforeUnconstrained:
|
||||||
'angledLineToY({ angle: 89, to: 9.14 + 0 }, %)',
|
'angledLineToY({ angle = 89, to = 9.14 + 0 }, %)',
|
||||||
expectAfterUnconstrained: 'angledLineToY({ angle: 89, to: 9.14 }, %)',
|
expectAfterUnconstrained: 'angledLineToY({ angle = 89, to = 9.14 }, %)',
|
||||||
expectFinal: 'angledLineToY({ angle: 89, to: yAbs001 }, %)',
|
expectFinal: 'angledLineToY({ angle = 89, to = yAbs001 }, %)',
|
||||||
ang: ang + 180,
|
ang: ang + 180,
|
||||||
locator: '[data-overlay-toolbar-index="10"]',
|
locator: '[data-overlay-toolbar-index="10"]',
|
||||||
})
|
})
|
||||||
@ -761,7 +769,7 @@ test.describe('Testing segment overlays', () => {
|
|||||||
localStorage.setItem('disableAxis', 'true')
|
localStorage.setItem('disableAxis', 'true')
|
||||||
})
|
})
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
|
|
||||||
@ -818,7 +826,7 @@ test.describe('Testing segment overlays', () => {
|
|||||||
localStorage.setItem('disableAxis', 'true')
|
localStorage.setItem('disableAxis', 'true')
|
||||||
})
|
})
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
|
|
||||||
@ -828,7 +836,7 @@ test.describe('Testing segment overlays', () => {
|
|||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
await page
|
await page
|
||||||
.getByText('circle({ center: [1 + 0, 0], radius: 8 }, %)')
|
.getByText('circle({ center = [1 + 0, 0], radius = 8 }, %)')
|
||||||
.click()
|
.click()
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||||
@ -847,9 +855,9 @@ test.describe('Testing segment overlays', () => {
|
|||||||
hoverPos,
|
hoverPos,
|
||||||
constraintType: 'xAbsolute',
|
constraintType: 'xAbsolute',
|
||||||
expectBeforeUnconstrained:
|
expectBeforeUnconstrained:
|
||||||
'circle({ center: [1 + 0, 0], radius: 8 }, %)',
|
'circle({ center = [1 + 0, 0], radius = 8 }, %)',
|
||||||
expectAfterUnconstrained: 'circle({ center: [1, 0], radius: 8 }, %)',
|
expectAfterUnconstrained: 'circle({ center = [1, 0], radius = 8 }, %)',
|
||||||
expectFinal: 'circle({ center: [xAbs001, 0], radius: 8 }, %)',
|
expectFinal: 'circle({ center = [xAbs001, 0], radius = 8 }, %)',
|
||||||
ang: ang + 105,
|
ang: ang + 105,
|
||||||
steps: 6,
|
steps: 6,
|
||||||
locator: '[data-overlay-toolbar-index="0"]',
|
locator: '[data-overlay-toolbar-index="0"]',
|
||||||
@ -859,7 +867,7 @@ test.describe('Testing segment overlays', () => {
|
|||||||
hoverPos,
|
hoverPos,
|
||||||
constraintType: 'yAbsolute',
|
constraintType: 'yAbsolute',
|
||||||
expectBeforeUnconstrained:
|
expectBeforeUnconstrained:
|
||||||
'circle({ center: [xAbs001, 0], radius: 8 }, %)',
|
'circle({ center = [xAbs001, 0], radius = 8 }, %)',
|
||||||
expectAfterUnconstrained:
|
expectAfterUnconstrained:
|
||||||
'circle({ center = [xAbs001, yAbs001], radius = 8 }, %)',
|
'circle({ center = [xAbs001, yAbs001], radius = 8 }, %)',
|
||||||
expectFinal: 'circle({ center = [xAbs001, 0], radius = 8 }, %)',
|
expectFinal: 'circle({ center = [xAbs001, 0], radius = 8 }, %)',
|
||||||
@ -872,10 +880,10 @@ test.describe('Testing segment overlays', () => {
|
|||||||
hoverPos,
|
hoverPos,
|
||||||
constraintType: 'radius',
|
constraintType: 'radius',
|
||||||
expectBeforeUnconstrained:
|
expectBeforeUnconstrained:
|
||||||
'circle({ center: [xAbs001, 0], radius: 8 }, %)',
|
'circle({ center = [xAbs001, 0], radius = 8 }, %)',
|
||||||
expectAfterUnconstrained:
|
expectAfterUnconstrained:
|
||||||
'circle({ center: [xAbs001, 0], radius: radius001 }, %)',
|
'circle({ center = [xAbs001, 0], radius = radius001 }, %)',
|
||||||
expectFinal: 'circle({ center: [xAbs001, 0], radius: 8 }, %)',
|
expectFinal: 'circle({ center = [xAbs001, 0], radius = 8 }, %)',
|
||||||
ang: ang + 105,
|
ang: ang + 105,
|
||||||
steps: 10,
|
steps: 10,
|
||||||
locator: '[data-overlay-toolbar-index="0"]',
|
locator: '[data-overlay-toolbar-index="0"]',
|
||||||
@ -951,7 +959,7 @@ test.describe('Testing segment overlays', () => {
|
|||||||
localStorage.setItem('disableAxis', 'true')
|
localStorage.setItem('disableAxis', 'true')
|
||||||
})
|
})
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
await u.waitForPageLoad()
|
await u.waitForPageLoad()
|
||||||
@ -1003,7 +1011,7 @@ test.describe('Testing segment overlays', () => {
|
|||||||
ang = await u.getAngle(`[data-overlay-index="${10}"]`)
|
ang = await u.getAngle(`[data-overlay-index="${10}"]`)
|
||||||
await deleteSegmentSequence({
|
await deleteSegmentSequence({
|
||||||
hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y },
|
hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y },
|
||||||
codeToBeDeleted: 'angledLineToY({ angle: 89, to: 9.14 + 0 }, %)',
|
codeToBeDeleted: 'angledLineToY({ angle = 89, to = 9.14 + 0 }, %)',
|
||||||
stdLibFnName: 'angledLineToY',
|
stdLibFnName: 'angledLineToY',
|
||||||
ang: ang + 180,
|
ang: ang + 180,
|
||||||
locator: '[data-overlay-toolbar-index="10"]',
|
locator: '[data-overlay-toolbar-index="10"]',
|
||||||
@ -1013,7 +1021,7 @@ test.describe('Testing segment overlays', () => {
|
|||||||
ang = await u.getAngle(`[data-overlay-index="${9}"]`)
|
ang = await u.getAngle(`[data-overlay-index="${9}"]`)
|
||||||
await deleteSegmentSequence({
|
await deleteSegmentSequence({
|
||||||
hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y },
|
hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y },
|
||||||
codeToBeDeleted: 'angledLineToX({ angle: 3 + 0, to: 26 }, %)',
|
codeToBeDeleted: 'angledLineToX({ angle = 3 + 0, to = 26 }, %)',
|
||||||
stdLibFnName: 'angledLineToX',
|
stdLibFnName: 'angledLineToX',
|
||||||
ang: ang + 180,
|
ang: ang + 180,
|
||||||
locator: '[data-overlay-toolbar-index="9"]',
|
locator: '[data-overlay-toolbar-index="9"]',
|
||||||
@ -1024,7 +1032,7 @@ test.describe('Testing segment overlays', () => {
|
|||||||
await deleteSegmentSequence({
|
await deleteSegmentSequence({
|
||||||
hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y },
|
hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y },
|
||||||
codeToBeDeleted:
|
codeToBeDeleted:
|
||||||
'angledLineOfYLength({ angle: -91, length: 19 + 0 }, %)',
|
'angledLineOfYLength({ angle = -91, length = 19 + 0 }, %)',
|
||||||
stdLibFnName: 'angledLineOfYLength',
|
stdLibFnName: 'angledLineOfYLength',
|
||||||
ang: ang + 180,
|
ang: ang + 180,
|
||||||
locator: '[data-overlay-toolbar-index="8"]',
|
locator: '[data-overlay-toolbar-index="8"]',
|
||||||
@ -1035,7 +1043,7 @@ test.describe('Testing segment overlays', () => {
|
|||||||
await deleteSegmentSequence({
|
await deleteSegmentSequence({
|
||||||
hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y },
|
hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y },
|
||||||
codeToBeDeleted:
|
codeToBeDeleted:
|
||||||
'angledLineOfXLength({ angle: 181 + 0, length: 23.14 }, %)',
|
'angledLineOfXLength({ angle = 181 + 0, length = 23.14 }, %)',
|
||||||
stdLibFnName: 'angledLineOfXLength',
|
stdLibFnName: 'angledLineOfXLength',
|
||||||
ang: ang + 180,
|
ang: ang + 180,
|
||||||
locator: '[data-overlay-toolbar-index="7"]',
|
locator: '[data-overlay-toolbar-index="7"]',
|
||||||
@ -1118,7 +1126,7 @@ test.describe('Testing segment overlays', () => {
|
|||||||
ang = await u.getAngle(`[data-overlay-index="${1}"]`)
|
ang = await u.getAngle(`[data-overlay-index="${1}"]`)
|
||||||
await deleteSegmentSequence({
|
await deleteSegmentSequence({
|
||||||
hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y },
|
hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y },
|
||||||
codeToBeDeleted: 'angledLine({ angle: 3 + 0, length: 32 + 0 }, %)',
|
codeToBeDeleted: 'angledLine({ angle = 3 + 0, length = 32 + 0 }, %)',
|
||||||
stdLibFnName: 'angledLine',
|
stdLibFnName: 'angledLine',
|
||||||
ang: ang + 180,
|
ang: ang + 180,
|
||||||
locator: '[data-overlay-toolbar-index="1"]',
|
locator: '[data-overlay-toolbar-index="1"]',
|
||||||
@ -1177,15 +1185,31 @@ test.describe('Testing segment overlays', () => {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
await page.waitForTimeout(300)
|
await u.waitForPageLoad()
|
||||||
|
await page.waitForTimeout(1000)
|
||||||
|
|
||||||
await page.getByText(lineOfInterest).click()
|
await expect
|
||||||
await page.waitForTimeout(100)
|
.poll(async () => {
|
||||||
|
await editor.scrollToText(lineOfInterest)
|
||||||
|
await page.waitForTimeout(1000)
|
||||||
|
await page.keyboard.press('ArrowRight')
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.press('ArrowLeft')
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
try {
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Edit Sketch' })
|
||||||
|
).toBeVisible()
|
||||||
|
return true
|
||||||
|
} catch (_) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.toBe(true)
|
||||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||||
await page.waitForTimeout(500)
|
|
||||||
|
|
||||||
await expect(page.getByTestId('segment-overlay')).toHaveCount(3)
|
await expect(page.getByTestId('segment-overlay')).toHaveCount(3)
|
||||||
const segmentToDelete = await u.getBoundingBox(
|
const segmentToDelete = await u.getBoundingBox(
|
||||||
@ -1282,22 +1306,6 @@ test.describe('Testing segment overlays', () => {
|
|||||||
before: `yLineTo(-4 + 0, %, $seg01)`,
|
before: `yLineTo(-4 + 0, %, $seg01)`,
|
||||||
after: `line([0, -10], %, $seg01)`,
|
after: `line([0, -10], %, $seg01)`,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
before: `angledLineOfXLength([3 + 0, 30 + 0], %, $seg01)`,
|
|
||||||
after: `line([30, 1.57], %, $seg01)`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
before: `angledLineOfYLength([3 + 0, 1.5 + 0], %, $seg01)`,
|
|
||||||
after: `line([28.62, 1.5], %, $seg01)`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
before: `angledLineToX([3 + 0, 30 + 0], %, $seg01)`,
|
|
||||||
after: `line([25, 1.31], %, $seg01)`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
before: `angledLineToY([3 + 0, 7 + 0], %, $seg01)`,
|
|
||||||
after: `line([19.08, 1], %, $seg01)`,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
before: `angledLineOfXLength({ angle = 3 + 0, length = 30 + 0 }, %, $seg01)`,
|
before: `angledLineOfXLength({ angle = 3 + 0, length = 30 + 0 }, %, $seg01)`,
|
||||||
after: `line([30, 1.57], %, $seg01)`,
|
after: `line([30, 1.57], %, $seg01)`,
|
||||||
@ -1317,7 +1325,7 @@ test.describe('Testing segment overlays', () => {
|
|||||||
]
|
]
|
||||||
|
|
||||||
for (const { before, after } of cases) {
|
for (const { before, after } of cases) {
|
||||||
const isObj = before.includes('{ angle: 3')
|
const isObj = before.includes('{ angle = 3')
|
||||||
test(`${before.split('(')[0]}${isObj ? '-[obj-input]' : ''}`, async ({
|
test(`${before.split('(')[0]}${isObj ? '-[obj-input]' : ''}`, async ({
|
||||||
page,
|
page,
|
||||||
editor,
|
editor,
|
||||||
@ -1339,7 +1347,7 @@ test.describe('Testing segment overlays', () => {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
await u.waitForPageLoad()
|
await u.waitForPageLoad()
|
||||||
|
@ -486,12 +486,14 @@ test.describe('Testing selections', () => {
|
|||||||
await u.clearCommandLogs()
|
await u.clearCommandLogs()
|
||||||
await page.keyboard.press('Backspace')
|
await page.keyboard.press('Backspace')
|
||||||
|
|
||||||
await expect(page.getByText('Unable to delete part')).toBeVisible()
|
await expect(page.getByText('Unable to delete selection')).toBeVisible()
|
||||||
})
|
})
|
||||||
test('Hovering over 3d features highlights code, clicking puts the cursor in the right place and sends selection id to engine', async ({
|
test('Hovering over 3d features highlights code, clicking puts the cursor in the right place and sends selection id to engine', async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
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 u = await getUtils(page)
|
||||||
await page.addInitScript(async (KCL_DEFAULT_LENGTH) => {
|
await page.addInitScript(async (KCL_DEFAULT_LENGTH) => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
@ -644,7 +646,7 @@ test.describe('Testing selections', () => {
|
|||||||
await checkCodeAtHoverPosition(
|
await checkCodeAtHoverPosition(
|
||||||
'flatExtrusionFace',
|
'flatExtrusionFace',
|
||||||
flatExtrusionFace,
|
flatExtrusionFace,
|
||||||
`angledLineThatIntersects({angle:3.14,intersectTag:a,offset:0},%)extrude(5+7,%)`,
|
`angledLineThatIntersects({angle=3.14,intersectTag=a,offset=0},%)extrude(5+7,%)`,
|
||||||
'}, %)'
|
'}, %)'
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -701,19 +703,19 @@ test.describe('Testing selections', () => {
|
|||||||
await checkCodeAtHoverPosition(
|
await checkCodeAtHoverPosition(
|
||||||
'straightSegmentEdge',
|
'straightSegmentEdge',
|
||||||
straightSegmentEdge,
|
straightSegmentEdge,
|
||||||
`angledLineToY({angle:30,to:11.14},%)`,
|
`angledLineToY({angle=30,to=11.14},%)`,
|
||||||
'angledLineToY({ angle: 30, to: 11.14 }, %)'
|
'angledLineToY({ angle = 30, to = 11.14 }, %)'
|
||||||
)
|
)
|
||||||
await checkCodeAtHoverPosition(
|
await checkCodeAtHoverPosition(
|
||||||
'straightSegmentOppositeEdge',
|
'straightSegmentOppositeEdge',
|
||||||
straightSegmentOppositeEdge,
|
straightSegmentOppositeEdge,
|
||||||
`angledLineToY({angle:30,to:11.14},%)`,
|
`angledLineToY({angle=30,to=11.14},%)`,
|
||||||
'angledLineToY({ angle: 30, to: 11.14 }, %)'
|
'angledLineToY({ angle = 30, to = 11.14 }, %)'
|
||||||
)
|
)
|
||||||
await checkCodeAtHoverPosition(
|
await checkCodeAtHoverPosition(
|
||||||
'straightSegmentAdjacentEdge',
|
'straightSegmentAdjacentEdge',
|
||||||
straightSegmentAdjacentEdge,
|
straightSegmentAdjacentEdge,
|
||||||
`angledLineThatIntersects({angle:3.14,intersectTag:a,offset:0},%)`,
|
`angledLineThatIntersects({angle=3.14,intersectTag=a,offset=0},%)`,
|
||||||
'}, %)'
|
'}, %)'
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -780,14 +782,14 @@ test.describe('Testing selections', () => {
|
|||||||
await checkCodeAtHoverPosition(
|
await checkCodeAtHoverPosition(
|
||||||
'oppositeChamfer',
|
'oppositeChamfer',
|
||||||
oppositeChamfer,
|
oppositeChamfer,
|
||||||
`angledLine([segAng(rectangleSegmentA001)-90,217.26],%,$seg01)chamfer({length:30,tags:[seg01,getNextAdjacentEdge(yo),getNextAdjacentEdge(seg02),getOppositeEdge(seg01)]},%)`,
|
`angledLine([segAng(rectangleSegmentA001)-90,217.26],%,$seg01)chamfer({length=30,tags=[seg01,getNextAdjacentEdge(yo),getNextAdjacentEdge(seg02),getOppositeEdge(seg01)]},%)`,
|
||||||
'}, %)'
|
'}, %)'
|
||||||
)
|
)
|
||||||
|
|
||||||
await checkCodeAtHoverPosition(
|
await checkCodeAtHoverPosition(
|
||||||
'baseChamfer',
|
'baseChamfer',
|
||||||
baseChamfer,
|
baseChamfer,
|
||||||
`angledLine([segAng(rectangleSegmentA001)-90,217.26],%,$seg01)chamfer({length:30,tags:[seg01,getNextAdjacentEdge(yo),getNextAdjacentEdge(seg02),getOppositeEdge(seg01)]},%)`,
|
`angledLine([segAng(rectangleSegmentA001)-90,217.26],%,$seg01)chamfer({length=30,tags=[seg01,getNextAdjacentEdge(yo),getNextAdjacentEdge(seg02),getOppositeEdge(seg01)]},%)`,
|
||||||
'}, %)'
|
'}, %)'
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -818,14 +820,14 @@ test.describe('Testing selections', () => {
|
|||||||
await checkCodeAtHoverPosition(
|
await checkCodeAtHoverPosition(
|
||||||
'adjacentChamfer1',
|
'adjacentChamfer1',
|
||||||
adjacentChamfer1,
|
adjacentChamfer1,
|
||||||
`lineTo([profileStartX(%),profileStartY(%)],%,$seg02)chamfer({length:30,tags:[seg01,getNextAdjacentEdge(yo),getNextAdjacentEdge(seg02),getOppositeEdge(seg01)]},%)`,
|
`lineTo([profileStartX(%),profileStartY(%)],%,$seg02)chamfer({length=30,tags=[seg01,getNextAdjacentEdge(yo),getNextAdjacentEdge(seg02),getOppositeEdge(seg01)]},%)`,
|
||||||
'}, %)'
|
'}, %)'
|
||||||
)
|
)
|
||||||
|
|
||||||
await checkCodeAtHoverPosition(
|
await checkCodeAtHoverPosition(
|
||||||
'adjacentChamfer2',
|
'adjacentChamfer2',
|
||||||
adjacentChamfer2,
|
adjacentChamfer2,
|
||||||
`angledLine([segAng(rectangleSegmentA001),-segLen(rectangleSegmentA001)],%,$yo)chamfer({length:30,tags:[seg01,getNextAdjacentEdge(yo),getNextAdjacentEdge(seg02),getOppositeEdge(seg01)]},%)`,
|
`angledLine([segAng(rectangleSegmentA001),-segLen(rectangleSegmentA001)],%,$yo)chamfer({length=30,tags=[seg01,getNextAdjacentEdge(yo),getNextAdjacentEdge(seg02),getOppositeEdge(seg01)]},%)`,
|
||||||
'}, %)'
|
'}, %)'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -872,17 +874,15 @@ test.describe('Testing selections', () => {
|
|||||||
}
|
}
|
||||||
const clickEmpty = () => page.mouse.click(700, 460)
|
const clickEmpty = () => page.mouse.click(700, 460)
|
||||||
await selectUnExtrudable()
|
await selectUnExtrudable()
|
||||||
// expect extrude button to be disabled
|
// expect extrude button to be enabled, since we don't guard
|
||||||
await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled()
|
// until the extrude button is clicked
|
||||||
|
await expect(page.getByRole('button', { name: 'Extrude' })).toBeEnabled()
|
||||||
|
|
||||||
await clickEmpty()
|
await clickEmpty()
|
||||||
|
|
||||||
// expect active line to contain nothing
|
// expect active line to contain nothing
|
||||||
await expect(page.locator('.cm-activeLine')).toHaveText('')
|
await expect(page.locator('.cm-activeLine')).toHaveText('')
|
||||||
|
|
||||||
// and extrude to still be disabled
|
|
||||||
await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled()
|
|
||||||
|
|
||||||
const codeToAdd = `${await u.codeLocator.allInnerTexts()}
|
const codeToAdd = `${await u.codeLocator.allInnerTexts()}
|
||||||
sketch002 = startSketchOn(extrude001, $seg01)
|
sketch002 = startSketchOn(extrude001, $seg01)
|
||||||
|> startProfileAt([-12.94, 6.6], %)
|
|> startProfileAt([-12.94, 6.6], %)
|
||||||
@ -894,8 +894,9 @@ test.describe('Testing selections', () => {
|
|||||||
await u.codeLocator.fill(codeToAdd)
|
await u.codeLocator.fill(codeToAdd)
|
||||||
|
|
||||||
await selectUnExtrudable()
|
await selectUnExtrudable()
|
||||||
// expect extrude button to be disabled
|
// expect extrude button to be enabled, since we don't guard
|
||||||
await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled()
|
// until the extrude button is clicked
|
||||||
|
await expect(page.getByRole('button', { name: 'Extrude' })).toBeEnabled()
|
||||||
|
|
||||||
await clickEmpty()
|
await clickEmpty()
|
||||||
await expect(page.locator('.cm-activeLine')).toHaveText('')
|
await expect(page.locator('.cm-activeLine')).toHaveText('')
|
||||||
@ -930,11 +931,14 @@ test.describe('Testing selections', () => {
|
|||||||
const selectClose = () => page.getByText(`close(%)`).click()
|
const selectClose = () => page.getByText(`close(%)`).click()
|
||||||
const clickEmpty = () => page.mouse.click(950, 100)
|
const clickEmpty = () => page.mouse.click(950, 100)
|
||||||
|
|
||||||
// expect fillet button without any bodies in the scene
|
// Now that we don't disable toolbar buttons based on selection,
|
||||||
|
// but rather based on a "selection" step in the command palette,
|
||||||
|
// the fillet button should always be enabled with a good network connection.
|
||||||
|
// I'm not sure if this test is actually useful anymore.
|
||||||
await selectSegment()
|
await selectSegment()
|
||||||
await expect(page.getByRole('button', { name: 'Fillet' })).toBeDisabled()
|
await expect(page.getByRole('button', { name: 'Fillet' })).toBeEnabled()
|
||||||
await clickEmpty()
|
await clickEmpty()
|
||||||
await expect(page.getByRole('button', { name: 'Fillet' })).toBeDisabled()
|
await expect(page.getByRole('button', { name: 'Fillet' })).toBeEnabled()
|
||||||
|
|
||||||
// test fillet button with the body in the scene
|
// test fillet button with the body in the scene
|
||||||
const codeToAdd = `${await u.codeLocator.allInnerTexts()}
|
const codeToAdd = `${await u.codeLocator.allInnerTexts()}
|
||||||
@ -944,7 +948,7 @@ test.describe('Testing selections', () => {
|
|||||||
await selectSegment()
|
await selectSegment()
|
||||||
await expect(page.getByRole('button', { name: 'Fillet' })).toBeEnabled()
|
await expect(page.getByRole('button', { name: 'Fillet' })).toBeEnabled()
|
||||||
await selectClose()
|
await selectClose()
|
||||||
await expect(page.getByRole('button', { name: 'Fillet' })).toBeDisabled()
|
await expect(page.getByRole('button', { name: 'Fillet' })).toBeEnabled()
|
||||||
await clickEmpty()
|
await clickEmpty()
|
||||||
await expect(page.getByRole('button', { name: 'Fillet' })).toBeEnabled()
|
await expect(page.getByRole('button', { name: 'Fillet' })).toBeEnabled()
|
||||||
})
|
})
|
||||||
@ -1199,7 +1203,9 @@ test.describe('Testing selections', () => {
|
|||||||
).not.toBeDisabled()
|
).not.toBeDisabled()
|
||||||
|
|
||||||
await page.getByText(selectionsSnippets.extrudeAndEditBlocked).click()
|
await page.getByText(selectionsSnippets.extrudeAndEditBlocked).click()
|
||||||
await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled()
|
// expect extrude button to be enabled, since we don't guard
|
||||||
|
// until the extrude button is clicked
|
||||||
|
await expect(page.getByRole('button', { name: 'Extrude' })).toBeEnabled()
|
||||||
|
|
||||||
await page.getByText(selectionsSnippets.extrudeAndEditAllowed).click()
|
await page.getByText(selectionsSnippets.extrudeAndEditAllowed).click()
|
||||||
await expect(
|
await expect(
|
||||||
@ -1210,7 +1216,9 @@ test.describe('Testing selections', () => {
|
|||||||
).not.toBeDisabled()
|
).not.toBeDisabled()
|
||||||
|
|
||||||
await page.getByText(selectionsSnippets.editOnly).click()
|
await page.getByText(selectionsSnippets.editOnly).click()
|
||||||
await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled()
|
// expect extrude button to be enabled, since we don't guard
|
||||||
|
// until the extrude button is clicked
|
||||||
|
await expect(page.getByRole('button', { name: 'Extrude' })).toBeEnabled()
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('button', { name: 'Edit Sketch' })
|
page.getByRole('button', { name: 'Edit Sketch' })
|
||||||
).not.toBeDisabled()
|
).not.toBeDisabled()
|
||||||
@ -1218,7 +1226,9 @@ test.describe('Testing selections', () => {
|
|||||||
await page
|
await page
|
||||||
.getByText(selectionsSnippets.extrudeAndEditBlockedInFunction)
|
.getByText(selectionsSnippets.extrudeAndEditBlockedInFunction)
|
||||||
.click()
|
.click()
|
||||||
await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled()
|
// expect extrude button to be enabled, since we don't guard
|
||||||
|
// until the extrude button is clicked
|
||||||
|
await expect(page.getByRole('button', { name: 'Extrude' })).toBeEnabled()
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('button', { name: 'Edit Sketch' })
|
page.getByRole('button', { name: 'Edit Sketch' })
|
||||||
).not.toBeVisible()
|
).not.toBeVisible()
|
||||||
|
@ -35,7 +35,7 @@ test.describe('Testing settings', () => {
|
|||||||
|
|
||||||
// Check that the invalid settings were changed to good defaults
|
// Check that the invalid settings were changed to good defaults
|
||||||
expect(storedSettings.settings?.modeling?.defaultUnit).toBe('in')
|
expect(storedSettings.settings?.modeling?.defaultUnit).toBe('in')
|
||||||
expect(storedSettings.settings?.modeling?.mouseControls).toBe('KittyCAD')
|
expect(storedSettings.settings?.modeling?.mouseControls).toBe('Zoo')
|
||||||
expect(storedSettings.settings?.app?.projectDirectory).toBe('')
|
expect(storedSettings.settings?.app?.projectDirectory).toBe('')
|
||||||
expect(storedSettings.settings?.projects?.defaultProjectName).toBe(
|
expect(storedSettings.settings?.projects?.defaultProjectName).toBe(
|
||||||
'project-$nnn'
|
'project-$nnn'
|
||||||
@ -134,6 +134,8 @@ test.describe('Testing settings', () => {
|
|||||||
page,
|
page,
|
||||||
homePage,
|
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 u = await getUtils(page)
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
@ -492,6 +494,8 @@ test.describe('Testing settings', () => {
|
|||||||
`Closing settings modal should go back to the original file being viewed`,
|
`Closing settings modal should go back to the original file being viewed`,
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ context, page }, testInfo) => {
|
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) => {
|
await context.folderSetupFn(async (dir) => {
|
||||||
const bracketDir = join(dir, 'project-000')
|
const bracketDir = join(dir, 'project-000')
|
||||||
await fsp.mkdir(bracketDir, { recursive: true })
|
await fsp.mkdir(bracketDir, { recursive: true })
|
||||||
@ -554,6 +558,8 @@ test.describe('Testing settings', () => {
|
|||||||
|
|
||||||
test('Changing modeling default unit', async ({ page, homePage }) => {
|
test('Changing modeling default unit', async ({ page, homePage }) => {
|
||||||
await test.step(`Test setup`, async () => {
|
await test.step(`Test setup`, async () => {
|
||||||
|
// TODO: fix this test on windows after the electron migration
|
||||||
|
test.skip(process.platform === 'win32', 'Skip on windows')
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
const toastMessage = page.getByText(`Successfully created "testDefault"`)
|
const toastMessage = page.getByText(`Successfully created "testDefault"`)
|
||||||
@ -700,6 +706,8 @@ test.describe('Testing settings', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test('Changing theme in sketch mode', async ({ context, page, homePage }) => {
|
test('Changing theme in sketch mode', async ({ context, 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 u = await getUtils(page)
|
||||||
await context.addInitScript(() => {
|
await context.addInitScript(() => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
|
@ -156,13 +156,13 @@ test.describe('Text-to-CAD tests', () => {
|
|||||||
const cmdSearchBar = page.getByPlaceholder('Search commands')
|
const cmdSearchBar = page.getByPlaceholder('Search commands')
|
||||||
await expect(cmdSearchBar).toBeVisible()
|
await expect(cmdSearchBar).toBeVisible()
|
||||||
|
|
||||||
const textToCadCommand = page.getByText('Text-to-CAD')
|
const textToCadCommand = page.getByRole('option', { name: 'Text-to-CAD' })
|
||||||
await expect(textToCadCommand.first()).toBeVisible()
|
await expect(textToCadCommand.first()).toBeVisible()
|
||||||
// Click the Text-to-CAD command
|
// Click the Text-to-CAD command
|
||||||
await textToCadCommand.first().click()
|
await textToCadCommand.first().click()
|
||||||
|
|
||||||
// Enter the prompt.
|
// Enter the prompt.
|
||||||
const prompt = page.getByText('Prompt')
|
const prompt = page.getByRole('textbox', { name: 'Prompt' })
|
||||||
await expect(prompt.first()).toBeVisible()
|
await expect(prompt.first()).toBeVisible()
|
||||||
|
|
||||||
// Type the prompt.
|
// Type the prompt.
|
||||||
@ -224,13 +224,13 @@ test.describe('Text-to-CAD tests', () => {
|
|||||||
const cmdSearchBar = page.getByPlaceholder('Search commands')
|
const cmdSearchBar = page.getByPlaceholder('Search commands')
|
||||||
await expect(cmdSearchBar).toBeVisible()
|
await expect(cmdSearchBar).toBeVisible()
|
||||||
|
|
||||||
const textToCadCommand = page.getByText('Text-to-CAD')
|
const textToCadCommand = page.getByRole('option', { name: 'Text-to-CAD' })
|
||||||
await expect(textToCadCommand.first()).toBeVisible()
|
await expect(textToCadCommand.first()).toBeVisible()
|
||||||
// Click the Text-to-CAD command
|
// Click the Text-to-CAD command
|
||||||
await textToCadCommand.first().click()
|
await textToCadCommand.first().click()
|
||||||
|
|
||||||
// Enter the prompt.
|
// Enter the prompt.
|
||||||
const prompt = page.getByText('Prompt')
|
const prompt = page.getByRole('textbox', { name: 'Prompt' })
|
||||||
await expect(prompt.first()).toBeVisible()
|
await expect(prompt.first()).toBeVisible()
|
||||||
|
|
||||||
const badPrompt = 'akjsndladf lajbhflauweyfaaaljhr472iouafyvsssssss'
|
const badPrompt = 'akjsndladf lajbhflauweyfaaaljhr472iouafyvsssssss'
|
||||||
@ -314,13 +314,13 @@ test.describe('Text-to-CAD tests', () => {
|
|||||||
const cmdSearchBar = page.getByPlaceholder('Search commands')
|
const cmdSearchBar = page.getByPlaceholder('Search commands')
|
||||||
await expect(cmdSearchBar).toBeVisible()
|
await expect(cmdSearchBar).toBeVisible()
|
||||||
|
|
||||||
const textToCadCommand = page.getByText('Text-to-CAD')
|
const textToCadCommand = page.getByRole('option', { name: 'Text-to-CAD' })
|
||||||
await expect(textToCadCommand.first()).toBeVisible()
|
await expect(textToCadCommand.first()).toBeVisible()
|
||||||
// Click the Text-to-CAD command
|
// Click the Text-to-CAD command
|
||||||
await textToCadCommand.first().click()
|
await textToCadCommand.first().click()
|
||||||
|
|
||||||
// Enter the prompt.
|
// Enter the prompt.
|
||||||
const prompt = page.getByText('Prompt')
|
const prompt = page.getByRole('textbox', { name: 'Prompt' })
|
||||||
await expect(prompt.first()).toBeVisible()
|
await expect(prompt.first()).toBeVisible()
|
||||||
|
|
||||||
const badPrompt = 'akjsndladflajbhflauweyf15;'
|
const badPrompt = 'akjsndladflajbhflauweyf15;'
|
||||||
@ -392,13 +392,13 @@ test.describe('Text-to-CAD tests', () => {
|
|||||||
const cmdSearchBar = page.getByPlaceholder('Search commands')
|
const cmdSearchBar = page.getByPlaceholder('Search commands')
|
||||||
await expect(cmdSearchBar).toBeVisible()
|
await expect(cmdSearchBar).toBeVisible()
|
||||||
|
|
||||||
const textToCadCommand = page.getByText('Text-to-CAD')
|
const textToCadCommand = page.getByRole('option', { name: 'Text-to-CAD' })
|
||||||
await expect(textToCadCommand.first()).toBeVisible()
|
await expect(textToCadCommand.first()).toBeVisible()
|
||||||
// Click the Text-to-CAD command
|
// Click the Text-to-CAD command
|
||||||
await textToCadCommand.first().click()
|
await textToCadCommand.first().click()
|
||||||
|
|
||||||
// Enter the prompt.
|
// Enter the prompt.
|
||||||
const prompt = page.getByText('Prompt')
|
const prompt = page.getByRole('textbox', { name: 'Prompt' })
|
||||||
await expect(prompt.first()).toBeVisible()
|
await expect(prompt.first()).toBeVisible()
|
||||||
|
|
||||||
// Type the prompt.
|
// Type the prompt.
|
||||||
@ -604,7 +604,7 @@ async function sendPromptFromCommandBar(page: Page, promptStr: string) {
|
|||||||
await page.waitForTimeout(1000)
|
await page.waitForTimeout(1000)
|
||||||
|
|
||||||
// Enter the prompt.
|
// Enter the prompt.
|
||||||
const prompt = page.getByText('Prompt')
|
const prompt = page.getByRole('textbox', { name: 'Prompt' })
|
||||||
await expect(prompt.first()).toBeVisible()
|
await expect(prompt.first()).toBeVisible()
|
||||||
|
|
||||||
// Type the prompt.
|
// Type the prompt.
|
||||||
|
@ -185,8 +185,8 @@ export const test = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create a consistent way to resize the page across electron and web.
|
// Create a consistent way to resize the page across electron and web.
|
||||||
// (lee) I had to do everyhting in the book to make electron change its
|
// (lee) I had to do everything in the book to make electron change its
|
||||||
// damn window size. I succeded in making it consistently and reliably
|
// damn window size. I succeeded in making it consistently and reliably
|
||||||
// do it after a whole afternoon.
|
// do it after a whole afternoon.
|
||||||
tronApp.page.setBodyDimensions = async function (dims: {
|
tronApp.page.setBodyDimensions = async function (dims: {
|
||||||
width: number
|
width: number
|
||||||
@ -242,8 +242,8 @@ export const test = (
|
|||||||
// return app.reuseWindowForTest();
|
// return app.reuseWindowForTest();
|
||||||
// });
|
// });
|
||||||
|
|
||||||
await tronApp.electronApp.evaluate(({ app }, projectDirName) => {
|
await tronApp.electronApp?.evaluate(({ app }, projectDirName) => {
|
||||||
console.log('ABCDEFGHI', app.testProperty['TEST_SETTINGS_FILE_KEY'])
|
// @ts-ignore can't declaration merge see main.ts
|
||||||
app.testProperty['TEST_SETTINGS_FILE_KEY'] = projectDirName
|
app.testProperty['TEST_SETTINGS_FILE_KEY'] = projectDirName
|
||||||
}, tronApp.dir)
|
}, tronApp.dir)
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ win:
|
|||||||
# - arm64
|
# - arm64
|
||||||
signingHashAlgorithms:
|
signingHashAlgorithms:
|
||||||
- sha256
|
- sha256
|
||||||
sign: "./sign-win.js"
|
sign: "./scripts/sign-win.js"
|
||||||
publisherName: "KittyCAD Inc" # needs to be exactly like on Digicert
|
publisherName: "KittyCAD Inc" # needs to be exactly like on Digicert
|
||||||
icon: "assets/icon.ico"
|
icon: "assets/icon.ico"
|
||||||
fileAssociations:
|
fileAssociations:
|
||||||
|
8
interface.d.ts
vendored
@ -11,6 +11,13 @@ export interface IElectronAPI {
|
|||||||
open: typeof dialog.showOpenDialog
|
open: typeof dialog.showOpenDialog
|
||||||
save: typeof dialog.showSaveDialog
|
save: typeof dialog.showSaveDialog
|
||||||
openExternal: typeof shell.openExternal
|
openExternal: typeof shell.openExternal
|
||||||
|
takeElectronWindowScreenshot: ({
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
}: {
|
||||||
|
width: number
|
||||||
|
height: number
|
||||||
|
}) => Promise<string>
|
||||||
showInFolder: typeof shell.showItemInFolder
|
showInFolder: typeof shell.showItemInFolder
|
||||||
/** Require to be called first before {@link loginWithDeviceFlow} */
|
/** Require to be called first before {@link loginWithDeviceFlow} */
|
||||||
startDeviceFlow: (host: string) => Promise<string>
|
startDeviceFlow: (host: string) => Promise<string>
|
||||||
@ -80,6 +87,7 @@ export interface IElectronAPI {
|
|||||||
onUpdateError: (callback: (value: { error: Error }) => void) => Electron
|
onUpdateError: (callback: (value: { error: Error }) => void) => Electron
|
||||||
appRestart: () => void
|
appRestart: () => void
|
||||||
getArgvParsed: () => any
|
getArgvParsed: () => any
|
||||||
|
getAppTestProperty: (propertyName: string) => any
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|