Compare commits
48 Commits
jtran/fix-
...
move-tests
Author | SHA1 | Date | |
---|---|---|---|
ff64367729 | |||
c440bbc0fe | |||
c2bb81ad1f | |||
82ec52b317 | |||
f5a14166a1 | |||
6ac8cef180 | |||
f0c8dbd5a2 | |||
c20ce60c9a | |||
f2b8e66952 | |||
cdd73e952a | |||
edbff28296 | |||
006fcd7490 | |||
4795de789d | |||
1a04a4dcfb | |||
119cacf6a8 | |||
12b00aca34 | |||
5c56f94fbb | |||
329e60dda8 | |||
2844f4c4d6 | |||
0c48006793 | |||
534d1ddecc | |||
4f5766d423 | |||
253556d6c7 | |||
10019a180d | |||
728314ccda | |||
7e229099a0 | |||
da217b6c1a | |||
ff4e1a6c73 | |||
a7c2548645 | |||
ccfc592b62 | |||
b3cc43ae2f | |||
f061a9a15f | |||
2b92f00d3c | |||
4b0823dbbf | |||
08a4016fda | |||
cb56fc7555 | |||
d7d822cc11 | |||
8e450378c3 | |||
fd39fcdb25 | |||
24011dd100 | |||
a9b78fb2a4 | |||
765e27c02b | |||
05baf9884d | |||
1ced492deb | |||
804124b07f | |||
b3112025b9 | |||
898b4ed016 | |||
4b21a5b667 |
@ -1,3 +1,3 @@
|
|||||||
[codespell]
|
[codespell]
|
||||||
ignore-words-list: crate,everytime,inout,co-ordinate,ot,nwo,atleast,ue,afterall
|
ignore-words-list: crate,everytime,inout,co-ordinate,ot,nwo,absolutey,atleast,ue,afterall
|
||||||
skip: **/target,node_modules,build,dist,./out,**/Cargo.lock,./docs/kcl/*.md,.yarn.lock,**/yarn.lock,./openapi/*.json,./packages/codemirror-lang-kcl/test/all.test.ts,tsconfig.tsbuildinfo
|
skip: **/target,node_modules,build,**/Cargo.lock,./docs/kcl/*.md,.yarn.lock,**/yarn.lock,./openapi/*.json,./src/lib/machine-api.d.ts
|
||||||
|
12
.eslintrc
@ -5,24 +5,16 @@
|
|||||||
},
|
},
|
||||||
"plugins": [
|
"plugins": [
|
||||||
"css-modules",
|
"css-modules",
|
||||||
"jest",
|
|
||||||
"react",
|
|
||||||
"suggest-no-throw",
|
"suggest-no-throw",
|
||||||
"@typescript-eslint"
|
|
||||||
],
|
],
|
||||||
"extends": [
|
"extends": [
|
||||||
|
"react-app",
|
||||||
|
"react-app/jest",
|
||||||
"plugin:css-modules/recommended"
|
"plugin:css-modules/recommended"
|
||||||
],
|
],
|
||||||
"rules": {
|
"rules": {
|
||||||
"@typescript-eslint/no-floating-promises": "error",
|
"@typescript-eslint/no-floating-promises": "error",
|
||||||
"@typescript-eslint/no-misused-promises": "error",
|
"@typescript-eslint/no-misused-promises": "error",
|
||||||
"no-restricted-globals": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"name": "isNaN",
|
|
||||||
"message": "Use Number.isNaN() instead."
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"semi": [
|
"semi": [
|
||||||
"error",
|
"error",
|
||||||
"never"
|
"never"
|
||||||
|
14
.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* ]]; then
|
if [[ "$3" == ubuntu-latest* ]]; 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* ]]; then
|
elif [[ "$3" == windows-latest* ]]; then
|
||||||
yarn test:playwright:electron:windows -- --shard=$1/$2 || true
|
yarn test:playwright:electron:windows -- --shard=$1/$2 || true
|
||||||
elif [[ "$3" == *macos* ]]; then
|
elif [[ "$3" == macos-14* ]]; then
|
||||||
yarn test:playwright:electron:macos -- --shard=$1/$2 || true
|
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."
|
||||||
@ -21,7 +21,7 @@ if [[ ! -f "test-results/.last-run.json" ]]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
retry=1
|
retry=1
|
||||||
max_retrys=5
|
max_retrys=4
|
||||||
|
|
||||||
# retry failed tests, doing our own retries because using inbuilt playwright retries causes connection issues
|
# retry failed tests, doing our own retries because using inbuilt playwright retries causes connection issues
|
||||||
while [[ $retry -le $max_retrys ]]; do
|
while [[ $retry -le $max_retrys ]]; do
|
||||||
@ -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* ]]; then
|
if [[ "$3" == ubuntu-latest* ]]; 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* ]]; then
|
elif [[ "$3" == windows-latest* ]]; then
|
||||||
yarn test:playwright:electron:windows -- --last-failed || true
|
yarn test:playwright:electron:windows -- --last-failed || true
|
||||||
elif [[ "$3" == *macos* ]]; then
|
elif [[ "$3" == macos-14* ]]; then
|
||||||
yarn test:playwright:electron:macos -- --last-failed || true
|
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,28 +5,24 @@
|
|||||||
|
|
||||||
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,13 +173,7 @@ 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 }}
|
||||||
DEBUG: "electron-notarize*"
|
run: yarn electron-builder --config --publish always
|
||||||
# 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
|
||||||
@ -234,13 +228,7 @@ 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 }}
|
||||||
DEBUG: "electron-notarize*"
|
run: yarn electron-builder --config --publish always
|
||||||
# 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,8 +2,28 @@ 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:
|
||||||
@ -51,7 +71,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@v5
|
uses: codecov/codecov-action@v4
|
||||||
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,18 +34,19 @@ jobs:
|
|||||||
- 'src/wasm-lib/**'
|
- 'src/wasm-lib/**'
|
||||||
|
|
||||||
electron:
|
electron:
|
||||||
timeout-minutes: 60
|
timeout-minutes: ${{ matrix.os == 'macos-14' && 60 || 50 }}
|
||||||
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:
|
||||||
# TODO: enable self-hosted-windows-8-cores once available
|
os: [ubuntu-latest-8-cores, windows-latest-8-cores, macos-14-large]
|
||||||
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:
|
||||||
@ -100,8 +101,7 @@ 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
|
||||||
# TODO: figure out what to do with this, it's failing
|
if: ${{ !startsWith(matrix.os, 'windows') }}
|
||||||
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,12 +127,9 @@ 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=1/1
|
PLATFORM=web yarn playwright test --config=playwright.config.ts --retries="3" --update-snapshots --grep=@snapshot --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
|
||||||
env:
|
env:
|
||||||
CI: true
|
CI: true
|
||||||
NODE_ENV: development
|
NODE_ENV: development
|
||||||
@ -153,7 +150,6 @@ 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: |
|
||||||
|
38
README.md
@ -337,47 +337,13 @@ 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
|
||||||
|
|
||||||
**Dependencies**
|
|
||||||
|
|
||||||
- `KITTYCAD_API_TOKEN`
|
|
||||||
- `cargo-nextest`
|
|
||||||
- `just`
|
|
||||||
|
|
||||||
#### Setting KITTYCAD_API_TOKEN
|
|
||||||
Use the production zoo.dev token, set this environment variable before running the tests
|
|
||||||
|
|
||||||
#### Installing cargonextest
|
|
||||||
|
|
||||||
```
|
|
||||||
cd src/wasm-lib
|
|
||||||
cargo search cargo-nextest
|
|
||||||
cargo install cargo-nextest
|
|
||||||
```
|
|
||||||
|
|
||||||
#### just
|
|
||||||
install [`just`](https://github.com/casey/just?tab=readme-ov-file#pre-built-binaries)
|
|
||||||
|
|
||||||
#### Running the tests
|
|
||||||
|
|
||||||
```bash
|
```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
|
cd src/wasm-lib
|
||||||
just test
|
KITTYCAD_API_TOKEN=XXX cargo test -- --test-threads=1
|
||||||
```
|
|
||||||
|
|
||||||
```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).
|
||||||
|
@ -24,5 +24,3 @@ once fixed in engine will just start working here with no language changes.
|
|||||||
chamfer cases work currently.
|
chamfer cases work currently.
|
||||||
|
|
||||||
- **Appearance**: Changing the appearance on a loft does not work.
|
- **Appearance**: Changing the appearance on a loft does not work.
|
||||||
|
|
||||||
- **Helix**: Currently sweeping a helix does not work.
|
|
||||||
|
@ -30,12 +30,10 @@ 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)
|
||||||
@ -48,7 +46,6 @@ layout: manual
|
|||||||
* [`getOppositeEdge`](kcl/getOppositeEdge)
|
* [`getOppositeEdge`](kcl/getOppositeEdge)
|
||||||
* [`getPreviousAdjacentEdge`](kcl/getPreviousAdjacentEdge)
|
* [`getPreviousAdjacentEdge`](kcl/getPreviousAdjacentEdge)
|
||||||
* [`helix`](kcl/helix)
|
* [`helix`](kcl/helix)
|
||||||
* [`helixRevolutions`](kcl/helixRevolutions)
|
|
||||||
* [`hole`](kcl/hole)
|
* [`hole`](kcl/hole)
|
||||||
* [`hollow`](kcl/hollow)
|
* [`hollow`](kcl/hollow)
|
||||||
* [`import`](kcl/import)
|
* [`import`](kcl/import)
|
||||||
@ -82,7 +79,6 @@ layout: manual
|
|||||||
* [`pi`](kcl/pi)
|
* [`pi`](kcl/pi)
|
||||||
* [`polar`](kcl/polar)
|
* [`polar`](kcl/polar)
|
||||||
* [`polygon`](kcl/polygon)
|
* [`polygon`](kcl/polygon)
|
||||||
* [`pop`](kcl/pop)
|
|
||||||
* [`pow`](kcl/pow)
|
* [`pow`](kcl/pow)
|
||||||
* [`profileStart`](kcl/profileStart)
|
* [`profileStart`](kcl/profileStart)
|
||||||
* [`profileStartX`](kcl/profileStartX)
|
* [`profileStartX`](kcl/profileStartX)
|
||||||
@ -104,8 +100,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: integer, transform_function: FunctionParam, solid_set: SolidSet) -> [Solid]
|
patternTransform(total_instances: u32, transform_function: FunctionParam, solid_set: SolidSet) -> [Solid]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -43,7 +43,7 @@ patternTransform(total_instances: integer, transform_function: FunctionParam, so
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `total_instances` | `integer` | | Yes |
|
| `total_instances` | `u32` | | 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,8 +95,7 @@ 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 startSketchOn('XY')
|
return startSketchAt(p0)
|
||||||
|> startProfileAt(p0, %)
|
|
||||||
|> lineTo(p1, %)
|
|> lineTo(p1, %)
|
||||||
|> lineTo(p2, %)
|
|> lineTo(p2, %)
|
||||||
|> lineTo(p3, %)
|
|> lineTo(p3, %)
|
||||||
@ -133,8 +132,7 @@ 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 startSketchOn('XY')
|
return startSketchAt(p0)
|
||||||
|> startProfileAt(p0, %)
|
|
||||||
|> lineTo(p1, %)
|
|> lineTo(p1, %)
|
||||||
|> lineTo(p2, %)
|
|> lineTo(p2, %)
|
||||||
|> lineTo(p3, %)
|
|> lineTo(p3, %)
|
||||||
@ -197,8 +195,7 @@ fn transform(i) {
|
|||||||
{ rotation = { angle = 45 * i } }
|
{ rotation = { angle = 45 * i } }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
startSketchOn('XY')
|
startSketchAt([0, 0])
|
||||||
|> 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: integer, transform_function: FunctionParam, solid_set: SketchSet) -> [Sketch]
|
patternTransform2d(total_instances: u32, transform_function: FunctionParam, solid_set: SketchSet) -> [Sketch]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ patternTransform2d(total_instances: integer, transform_function: FunctionParam,
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `total_instances` | `integer` | | Yes |
|
| `total_instances` | `u32` | | 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):
|
||||||
sumSoFar = 0
|
let sumSoFar = 0
|
||||||
for i in arr:
|
for i in arr:
|
||||||
sumSoFar = add(sumSoFar, i)
|
sumSoFar = add(sumSoFar, i)
|
||||||
return sumSoFar */
|
return sumSoFar */
|
||||||
@ -79,11 +79,10 @@ 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 = startSketchOn('XY')
|
startOfDecagonSketch = startSketchAt([cos(0) * radius, sin(0) * radius])
|
||||||
|> 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.
|
||||||
@ -97,15 +96,14 @@ 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):
|
||||||
stepAngle = (1/10) * tau()
|
let stepAngle = (1/10) * tau()
|
||||||
plane = startSketchOn('XY')
|
let startOfDecagonSketch = startSketchAt([(cos(0)*radius), (sin(0) * radius)])
|
||||||
startOfDecagonSketch = startProfileAt([(cos(0)*radius), (sin(0) * radius)], plane)
|
|
||||||
|
|
||||||
// Here's the reduce part.
|
// Here's the reduce part.
|
||||||
partialDecagon = startOfDecagonSketch
|
let partialDecagon = startOfDecagonSketch
|
||||||
for i in [1..10]:
|
for i in [1..10]:
|
||||||
x = cos(stepAngle * i) * radius
|
let x = cos(stepAngle * i) * radius
|
||||||
y = sin(stepAngle * i) * radius
|
let 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,8 +28,7 @@ segEnd(tag: TagIdentifier) -> [number]
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
w = 15
|
w = 15
|
||||||
cube = startSketchOn('XY')
|
cube = startSketchAt([0, 0])
|
||||||
|> 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)
|
||||||
@ -38,8 +37,7 @@ cube = startSketchOn('XY')
|
|||||||
|> extrude(5, %)
|
|> extrude(5, %)
|
||||||
|
|
||||||
fn cylinder(radius, tag) {
|
fn cylinder(radius, tag) {
|
||||||
return startSketchOn('XY')
|
return startSketchAt([0, 0])
|
||||||
|> startProfileAt([0, 0], %)
|
|
||||||
|> circle({
|
|> circle({
|
||||||
radius = radius,
|
radius = radius,
|
||||||
center = segEnd(tag)
|
center = segEnd(tag)
|
||||||
|
@ -28,8 +28,7 @@ segStart(tag: TagIdentifier) -> [number]
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
w = 15
|
w = 15
|
||||||
cube = startSketchOn('XY')
|
cube = startSketchAt([0, 0])
|
||||||
|> 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)
|
||||||
@ -38,8 +37,7 @@ cube = startSketchOn('XY')
|
|||||||
|> extrude(5, %)
|
|> extrude(5, %)
|
||||||
|
|
||||||
fn cylinder(radius, tag) {
|
fn cylinder(radius, tag) {
|
||||||
return startSketchOn('XY')
|
return startSketchAt([0, 0])
|
||||||
|> startProfileAt([0, 0], %)
|
|
||||||
|> circle({
|
|> circle({
|
||||||
radius = radius,
|
radius = radius,
|
||||||
center = segStart(tag)
|
center = segStart(tag)
|
||||||
|
@ -4,8 +4,6 @@ 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.
|
||||||
|
|
||||||
|
|
||||||
|
19650
docs/kcl/std.json
@ -13,18 +13,13 @@ Data to draw an angled line.
|
|||||||
|
|
||||||
An angle and length with explicitly named parameters
|
An angle and length with explicitly named parameters
|
||||||
|
|
||||||
**Type:** `object`
|
[`PolarCoordsData`](/docs/kcl/types/PolarCoordsData)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `angle` |`number`| The angle of the line (in degrees). | No |
|
|
||||||
| `length` |`number`| The length of the line. | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
|
@ -1,42 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Axis3dOrEdgeReference"
|
|
||||||
excerpt: "A 3D axis or tagged edge."
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
A 3D axis or tagged edge.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**This schema accepts any of the following:**
|
|
||||||
|
|
||||||
3D axis and origin.
|
|
||||||
|
|
||||||
[`AxisAndOrigin3d`](/docs/kcl/types/AxisAndOrigin3d)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
Tagged edge.
|
|
||||||
|
|
||||||
[`EdgeReference`](/docs/kcl/types/EdgeReference)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: "AxisAndOrigin2d"
|
title: "AxisAndOrigin"
|
||||||
excerpt: "A 2D axis and origin."
|
excerpt: "Axis and origin."
|
||||||
layout: manual
|
layout: manual
|
||||||
---
|
---
|
||||||
|
|
||||||
A 2D axis and origin.
|
Axis and origin.
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,105 +0,0 @@
|
|||||||
---
|
|
||||||
title: "AxisAndOrigin3d"
|
|
||||||
excerpt: "A 3D axis and origin."
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
A 3D axis and origin.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**This schema accepts exactly one of the following:**
|
|
||||||
|
|
||||||
X-axis.
|
|
||||||
|
|
||||||
**enum:** `X`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
Y-axis.
|
|
||||||
|
|
||||||
**enum:** `Y`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
Z-axis.
|
|
||||||
|
|
||||||
**enum:** `Z`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
Flip the X-axis.
|
|
||||||
|
|
||||||
**enum:** `-X`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
Flip the Y-axis.
|
|
||||||
|
|
||||||
**enum:** `-Y`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
Flip the Z-axis.
|
|
||||||
|
|
||||||
**enum:** `-Z`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `custom` |`object`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,19 +1,19 @@
|
|||||||
---
|
---
|
||||||
title: "Axis2dOrEdgeReference"
|
title: "AxisOrEdgeReference"
|
||||||
excerpt: "A 2D axis or tagged edge."
|
excerpt: "Axis or tagged edge."
|
||||||
layout: manual
|
layout: manual
|
||||||
---
|
---
|
||||||
|
|
||||||
A 2D axis or tagged edge.
|
Axis or tagged edge.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**This schema accepts any of the following:**
|
**This schema accepts any of the following:**
|
||||||
|
|
||||||
2D axis and origin.
|
Axis and origin.
|
||||||
|
|
||||||
[`AxisAndOrigin2d`](/docs/kcl/types/AxisAndOrigin2d)
|
[`AxisAndOrigin`](/docs/kcl/types/AxisAndOrigin)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
|||||||
---
|
|
||||||
title: "CircleThreePointData"
|
|
||||||
excerpt: "Data for drawing a 3-point circle"
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
Data for drawing a 3-point circle
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `p1` |`[number, number]`| Point one for circle derivation. | No |
|
|
||||||
| `p2` |`[number, number]`| Point two for circle derivation. | No |
|
|
||||||
| `p3` |`[number, number]`| Point three for circle derivation. | No |
|
|
||||||
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Helix"
|
|
||||||
excerpt: "A helix."
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
A helix.
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `value` |`string`| The id of the helix. | No |
|
|
||||||
| `revolutions` |`number`| Number of revolutions. | No |
|
|
||||||
| `angleStart` |`number`| Start angle (in degrees). | No |
|
|
||||||
| `ccw` |`boolean`| Is the helix rotation counter clockwise? | No |
|
|
||||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
|
||||||
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: "HelixData"
|
title: "HelixData"
|
||||||
excerpt: "Data for a helix."
|
excerpt: "Data for helices."
|
||||||
layout: manual
|
layout: manual
|
||||||
---
|
---
|
||||||
|
|
||||||
Data for a helix.
|
Data for helices.
|
||||||
|
|
||||||
**Type:** `object`
|
**Type:** `object`
|
||||||
|
|
||||||
@ -19,8 +19,6 @@ Data for a helix.
|
|||||||
| `revolutions` |`number`| Number of revolutions. | No |
|
| `revolutions` |`number`| Number of revolutions. | No |
|
||||||
| `angleStart` |`number`| Start angle (in degrees). | No |
|
| `angleStart` |`number`| Start angle (in degrees). | No |
|
||||||
| `ccw` |`boolean`| Is the helix rotation counter clockwise? The default is `false`. | No |
|
| `ccw` |`boolean`| Is the helix rotation counter clockwise? The default is `false`. | No |
|
||||||
| `length` |`number`| Length of the helix. | No |
|
| `length` |`number`| Length of the helix. If this argument is not provided, the height of the solid is used. | No |
|
||||||
| `radius` |`number`| Radius of the helix. | No |
|
|
||||||
| `axis` |[`Axis3dOrEdgeReference`](/docs/kcl/types/Axis3dOrEdgeReference)| Axis to use as mirror. | No |
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
---
|
|
||||||
title: "HelixRevolutionsData"
|
|
||||||
excerpt: "Data for helix revolutions."
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
Data for helix revolutions.
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `revolutions` |`number`| Number of revolutions. | No |
|
|
||||||
| `angleStart` |`number`| Start angle (in degrees). | No |
|
|
||||||
| `ccw` |`boolean`| Is the helix rotation counter clockwise? The default is `false`. | No |
|
|
||||||
| `length` |`number`| Length of the helix. If this argument is not provided, the height of the solid is used. | No |
|
|
||||||
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
|||||||
---
|
|
||||||
title: "HelixValue"
|
|
||||||
excerpt: "A helix."
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
A helix.
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `value` |`string`| The id of the helix. | No |
|
|
||||||
| `revolutions` |`number`| Number of revolutions. | No |
|
|
||||||
| `angleStart` |`number`| Start angle (in degrees). | No |
|
|
||||||
| `ccw` |`boolean`| Is the helix rotation counter clockwise? | No |
|
|
||||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
|
||||||
|
|
||||||
|
|
@ -12,10 +12,5 @@ KCL value for an optional parameter which was not given an argument. (remember,
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -285,27 +285,6 @@ An solid is a collection of extrude surfaces.
|
|||||||
| `value` |`[` [`Solid`](/docs/kcl/types/Solid) `]`| | No |
|
| `value` |`[` [`Solid`](/docs/kcl/types/Solid) `]`| | No |
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
A helix.
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: [`Helix`](/docs/kcl/types/Helix)| | No |
|
|
||||||
| `value` |`string`| The id of the helix. | No |
|
|
||||||
| `revolutions` |`number`| Number of revolutions. | No |
|
|
||||||
| `angleStart` |`number`| Start angle (in degrees). | No |
|
|
||||||
| `ccw` |`boolean`| Is the helix rotation counter clockwise? | No |
|
|
||||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
Data for an imported geometry.
|
Data for an imported geometry.
|
||||||
|
|
||||||
@ -350,23 +329,6 @@ Data for an imported geometry.
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `Module`| | No |
|
|
||||||
| `value` |[`ModuleId`](/docs/kcl/types/ModuleId)| Any KCL value. | No |
|
|
||||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
## Properties
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|
@ -16,6 +16,6 @@ Data for a mirror.
|
|||||||
|
|
||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `axis` |[`Axis2dOrEdgeReference`](/docs/kcl/types/Axis2dOrEdgeReference)| Axis to use as mirror. | No |
|
| `axis` |[`AxisOrEdgeReference`](/docs/kcl/types/AxisOrEdgeReference)| Axis to use as mirror. | No |
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
---
|
|
||||||
title: "ModuleId"
|
|
||||||
excerpt: "Identifier of a source file. Uses a u32 to keep the size small."
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
Identifier of a source file. Uses a u32 to keep the size small.
|
|
||||||
|
|
||||||
**Type:** `integer` (`uint32`)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -17,7 +17,7 @@ Data for revolution surfaces.
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `angle` |`number` (**maximum:** 360.0) (**minimum:** -360.0)| Angle to revolve (in degrees). Default is 360. | No |
|
| `angle` |`number` (**maximum:** 360.0) (**minimum:** -360.0)| Angle to revolve (in degrees). Default is 360. | No |
|
||||||
| `axis` |[`Axis2dOrEdgeReference`](/docs/kcl/types/Axis2dOrEdgeReference)| Axis of revolution. | No |
|
| `axis` |[`AxisOrEdgeReference`](/docs/kcl/types/AxisOrEdgeReference)| Axis of revolution. | No |
|
||||||
| `tolerance` |`number`| Tolerance for the revolve operation. | No |
|
| `tolerance` |`number`| Tolerance for the revolve operation. | No |
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
---
|
|
||||||
title: "SweepData"
|
|
||||||
excerpt: "Data for a sweep."
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
Data for a sweep.
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `path` |[`SweepPath`](/docs/kcl/types/SweepPath)| The path to sweep along. | No |
|
|
||||||
| `sectional` |`boolean`| If true, the sweep will be broken up into sub-sweeps (extrusions, revolves, sweeps) based on the trajectory path components. | No |
|
|
||||||
| `tolerance` |`number`| Tolerance for the sweep operation. | No |
|
|
||||||
|
|
||||||
|
|
@ -1,42 +0,0 @@
|
|||||||
---
|
|
||||||
title: "SweepPath"
|
|
||||||
excerpt: "A path to sweep along."
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
A path to sweep along.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**This schema accepts any of the following:**
|
|
||||||
|
|
||||||
A path to sweep along.
|
|
||||||
|
|
||||||
[`Sketch`](/docs/kcl/types/Sketch)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
A path to sweep along.
|
|
||||||
|
|
||||||
[`Helix`](/docs/kcl/types/Helix)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -20,7 +20,7 @@ async function doBasicSketch(
|
|||||||
|
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
await u.waitForPageLoad()
|
await u.waitForPageLoad()
|
||||||
await page.waitForTimeout(1000)
|
await page.waitForTimeout()
|
||||||
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('code pane closed at start', async ({ page, homePage }) => {
|
test.fixme('code pane closed at start', async ({ page, homePage }) => {
|
||||||
// Load the app with the code panes
|
// Load the app with the code panes
|
||||||
await page.addInitScript(async (persistModelingContext) => {
|
await page.addInitScript(async (persistModelingContext) => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
|
@ -45,8 +45,7 @@ test.describe('Command bar tests', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
// TODO: fix this test after the electron migration
|
test('Fillet from command bar', async ({ page, homePage }) => {
|
||||||
test.fixme('Fillet from command bar', async ({ page, homePage }) => {
|
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'persistCode',
|
'persistCode',
|
||||||
@ -81,7 +80,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,108 +54,6 @@ 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,
|
||||||
@ -555,22 +453,20 @@ test.describe('Editor tests', () => {
|
|||||||
homePage,
|
homePage,
|
||||||
}) => {
|
}) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1000, 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)
|
||||||
* the old check here used $ but this is for tags so it changed meaning.
|
$ error
|
||||||
* 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')
|
||||||
@ -587,12 +483,10 @@ 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(
|
await expect(page.getByText('Unexpected token: $').first()).toBeVisible()
|
||||||
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')
|
||||||
|
@ -1,127 +0,0 @@
|
|||||||
import { test, expect } from './zoo-test'
|
|
||||||
import * as fsp from 'fs/promises'
|
|
||||||
import { join } from 'path'
|
|
||||||
|
|
||||||
const FEATURE_TREE_EXAMPLE_CODE = `export fn timesFive(x) {
|
|
||||||
return 5 * x
|
|
||||||
}
|
|
||||||
export fn triangle() {
|
|
||||||
return startSketchOn('XZ')
|
|
||||||
|> startProfileAt([0, 0], %)
|
|
||||||
|> xLine(10, %)
|
|
||||||
|> line([-10, -5], %)
|
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|
||||||
|> close(%)
|
|
||||||
}
|
|
||||||
|
|
||||||
length001 = timesFive(1) * 5
|
|
||||||
sketch001 = startSketchOn('XZ')
|
|
||||||
|> startProfileAt([20, 10], %)
|
|
||||||
|> line([10, 10], %)
|
|
||||||
|> angledLine([-45, length001], %)
|
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|
||||||
|> close(%)
|
|
||||||
revolve001 = revolve({ axis = "X" }, sketch001)
|
|
||||||
triangle()
|
|
||||||
|> extrude(30, %)
|
|
||||||
plane001 = offsetPlane('XY', 10)
|
|
||||||
sketch002 = startSketchOn(plane001)
|
|
||||||
|> startProfileAt([-20, 0], %)
|
|
||||||
|> line([5, -15], %)
|
|
||||||
|> xLine(-10, %)
|
|
||||||
|> lineTo([-40, 0], %)
|
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|
||||||
|> close(%)
|
|
||||||
extrude001 = extrude(10, sketch002)
|
|
||||||
`
|
|
||||||
|
|
||||||
test.describe('Feature Tree pane', () => {
|
|
||||||
test(
|
|
||||||
'User can go to definition and go to function definition',
|
|
||||||
{ tag: '@electron' },
|
|
||||||
async ({ context, homePage, scene, editor, toolbar }) => {
|
|
||||||
await context.folderSetupFn(async (dir) => {
|
|
||||||
const bracketDir = join(dir, 'test-sample')
|
|
||||||
await fsp.mkdir(bracketDir, { recursive: true })
|
|
||||||
await fsp.writeFile(
|
|
||||||
join(bracketDir, 'main.kcl'),
|
|
||||||
FEATURE_TREE_EXAMPLE_CODE,
|
|
||||||
'utf-8'
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step('setup test', async () => {
|
|
||||||
await homePage.expectState({
|
|
||||||
projectCards: [
|
|
||||||
{
|
|
||||||
title: 'test-sample',
|
|
||||||
fileCount: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
sortBy: 'last-modified-desc',
|
|
||||||
})
|
|
||||||
await homePage.openProject('test-sample')
|
|
||||||
await scene.waitForExecutionDone()
|
|
||||||
await editor.closePane()
|
|
||||||
await toolbar.openFeatureTreePane()
|
|
||||||
})
|
|
||||||
|
|
||||||
async function testViewSource({
|
|
||||||
operationName,
|
|
||||||
operationIndex,
|
|
||||||
expectedActiveLine,
|
|
||||||
}: {
|
|
||||||
operationName: string
|
|
||||||
operationIndex: number
|
|
||||||
expectedActiveLine: string
|
|
||||||
}) {
|
|
||||||
await test.step(`Go to definition of the ${operationName}`, async () => {
|
|
||||||
await toolbar.viewSourceOnOperation(operationName, operationIndex)
|
|
||||||
await editor.expectState({
|
|
||||||
highlightedCode: '',
|
|
||||||
diagnostics: [],
|
|
||||||
activeLines: [expectedActiveLine],
|
|
||||||
})
|
|
||||||
await expect(
|
|
||||||
editor.activeLine.first(),
|
|
||||||
`${operationName} code should be scrolled into view`
|
|
||||||
).toBeVisible()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
await testViewSource({
|
|
||||||
operationName: 'Offset Plane',
|
|
||||||
operationIndex: 0,
|
|
||||||
expectedActiveLine: "plane001 = offsetPlane('XY', 10)",
|
|
||||||
})
|
|
||||||
await testViewSource({
|
|
||||||
operationName: 'Extrude',
|
|
||||||
operationIndex: 1,
|
|
||||||
expectedActiveLine: 'extrude001 = extrude(10, sketch002)',
|
|
||||||
})
|
|
||||||
await testViewSource({
|
|
||||||
operationName: 'Revolve',
|
|
||||||
operationIndex: 0,
|
|
||||||
expectedActiveLine: 'revolve001 = revolve({ axis = "X" }, sketch001)',
|
|
||||||
})
|
|
||||||
await testViewSource({
|
|
||||||
operationName: 'Triangle',
|
|
||||||
operationIndex: 0,
|
|
||||||
expectedActiveLine: 'triangle()',
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step('Go to definition on the triangle function', async () => {
|
|
||||||
await toolbar.goToDefinitionOnOperation('Triangle', 0)
|
|
||||||
await editor.expectState({
|
|
||||||
highlightedCode: '',
|
|
||||||
diagnostics: [],
|
|
||||||
activeLines: ['export fn triangle() {'],
|
|
||||||
})
|
|
||||||
await expect(
|
|
||||||
editor.activeLine.first(),
|
|
||||||
'Triangle function definition should be scrolled into view'
|
|
||||||
).toBeVisible()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
)
|
|
||||||
})
|
|
@ -92,8 +92,6 @@ test.describe('when using the file tree to', () => {
|
|||||||
`rename ${fromFile} to ${toFile}, and doesn't crash on reload and settings load`,
|
`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)
|
||||||
|
|
||||||
@ -136,8 +134,6 @@ 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 })
|
||||||
@ -1018,8 +1014,6 @@ test.describe('Undo and redo do not keep history when navigating between files',
|
|||||||
`open a file, change something, open a different file, hitting undo should do nothing`,
|
`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 })
|
||||||
@ -1088,8 +1082,6 @@ 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, Locator } from '@playwright/test'
|
import type { Page } from '@playwright/test'
|
||||||
import { expect } from '@playwright/test'
|
import { expect } from '@playwright/test'
|
||||||
|
|
||||||
type CmdBarSerialised =
|
type CmdBarSerialised =
|
||||||
@ -26,11 +26,9 @@ 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
|
||||||
@ -118,21 +116,4 @@ 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
|
||||||
public activeLine!: Locator
|
private activeLine!: Locator
|
||||||
|
|
||||||
constructor(page: Page) {
|
constructor(page: Page) {
|
||||||
this.page = page
|
this.page = page
|
||||||
@ -147,28 +147,20 @@ export class EditorFixture {
|
|||||||
openPane() {
|
openPane() {
|
||||||
return openPane(this.page, this.paneButtonTestId)
|
return openPane(this.page, this.paneButtonTestId)
|
||||||
}
|
}
|
||||||
scrollToText(text: string, placeCursor?: boolean) {
|
scrollToText(text: string) {
|
||||||
return this.page.evaluate(
|
return this.page.evaluate((scrollToText: string) => {
|
||||||
(args: { text: string; placeCursor?: boolean }) => {
|
// editorManager is available on the window object.
|
||||||
// error TS2339: Property 'docView' does not exist on type 'EditorView'.
|
// @ts-ignore
|
||||||
// Except it does so :shrug:
|
let index = editorManager._editorView.docView.view.state.doc
|
||||||
// @ts-ignore
|
.toString()
|
||||||
let index = window.editorManager._editorView?.docView.view.state.doc
|
.indexOf(scrollToText)
|
||||||
.toString()
|
// @ts-ignore
|
||||||
.indexOf(args.text)
|
editorManager._editorView.dispatch({
|
||||||
window.editorManager._editorView?.focus()
|
selection: {
|
||||||
window.editorManager._editorView?.dispatch({
|
anchor: index,
|
||||||
selection: window.EditorSelection.create([
|
},
|
||||||
window.EditorSelection.cursor(index),
|
scrollIntoView: true,
|
||||||
]),
|
})
|
||||||
effects: [
|
}, text)
|
||||||
window.EditorView.scrollIntoView(
|
|
||||||
window.EditorSelection.range(index, index + 1)
|
|
||||||
),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
},
|
|
||||||
{ text, placeCursor }
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,8 +21,6 @@ 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
|
||||||
@ -63,7 +61,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 | undefined
|
public electronApp?: ElectronApplication
|
||||||
public readonly viewPortSize = { width: 1200, height: 500 }
|
public readonly viewPortSize = { width: 1200, height: 500 }
|
||||||
public dir: string = ''
|
public dir: string = ''
|
||||||
|
|
||||||
@ -81,7 +79,7 @@ export class AuthenticatedTronApp {
|
|||||||
appSettings?: Partial<SaveSettingsPayload>
|
appSettings?: Partial<SaveSettingsPayload>
|
||||||
} = { fixtures: {} }
|
} = { fixtures: {} }
|
||||||
) {
|
) {
|
||||||
const { electronApp, page, context, dir } = await setupElectron({
|
const { electronApp, page, context, dir, options } = await setupElectron({
|
||||||
testInfo: this.testInfo,
|
testInfo: this.testInfo,
|
||||||
folderSetupFn: arg.folderSetupFn,
|
folderSetupFn: arg.folderSetupFn,
|
||||||
cleanProjectDir: arg.cleanProjectDir,
|
cleanProjectDir: arg.cleanProjectDir,
|
||||||
|
@ -36,8 +36,7 @@ type DragFromHandler = (
|
|||||||
|
|
||||||
export class SceneFixture {
|
export class SceneFixture {
|
||||||
public page: Page
|
public page: Page
|
||||||
public streamWrapper!: Locator
|
|
||||||
public loadingIndicator!: Locator
|
|
||||||
private exeIndicator!: Locator
|
private exeIndicator!: Locator
|
||||||
|
|
||||||
constructor(page: Page) {
|
constructor(page: Page) {
|
||||||
@ -65,8 +64,6 @@ export class SceneFixture {
|
|||||||
this.page = page
|
this.page = page
|
||||||
|
|
||||||
this.exeIndicator = page.getByTestId('model-state-indicator-execution-done')
|
this.exeIndicator = page.getByTestId('model-state-indicator-execution-done')
|
||||||
this.streamWrapper = page.getByTestId('stream')
|
|
||||||
this.loadingIndicator = this.streamWrapper.getByTestId('loading')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
makeMouseHelpers = (
|
makeMouseHelpers = (
|
||||||
@ -221,7 +218,23 @@ export class SceneFixture {
|
|||||||
coords: { x: number; y: number },
|
coords: { x: number; y: number },
|
||||||
diff: number
|
diff: number
|
||||||
) => {
|
) => {
|
||||||
await expectPixelColor(this.page, colour, coords, diff)
|
let finalValue = colour
|
||||||
|
await expect
|
||||||
|
.poll(async () => {
|
||||||
|
const pixel = (await getPixelRGBs(this.page)(coords, 1))[0]
|
||||||
|
if (!pixel) return null
|
||||||
|
finalValue = pixel
|
||||||
|
return pixel.every(
|
||||||
|
(channel, index) => Math.abs(channel - colour[index]) < diff
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.toBeTruthy()
|
||||||
|
.catch((cause) => {
|
||||||
|
throw new Error(
|
||||||
|
`ExpectPixelColor: expecting ${colour} got ${finalValue}`,
|
||||||
|
{ cause }
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
get gizmo() {
|
get gizmo() {
|
||||||
@ -238,28 +251,3 @@ 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,20 +1,12 @@
|
|||||||
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 {
|
import { doAndWaitForImageDiff } from '../test-utils'
|
||||||
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
|
||||||
|
|
||||||
extrudeButton!: Locator
|
extrudeButton!: Locator
|
||||||
loftButton!: Locator
|
loftButton!: Locator
|
||||||
sweepButton!: Locator
|
|
||||||
shellButton!: Locator
|
shellButton!: Locator
|
||||||
offsetPlaneButton!: Locator
|
offsetPlaneButton!: Locator
|
||||||
startSketchBtn!: Locator
|
startSketchBtn!: Locator
|
||||||
@ -28,10 +20,6 @@ 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,7 +29,6 @@ export class ToolbarFixture {
|
|||||||
this.page = page
|
this.page = page
|
||||||
this.extrudeButton = page.getByTestId('extrude')
|
this.extrudeButton = page.getByTestId('extrude')
|
||||||
this.loftButton = page.getByTestId('loft')
|
this.loftButton = page.getByTestId('loft')
|
||||||
this.sweepButton = page.getByTestId('sweep')
|
|
||||||
this.shellButton = page.getByTestId('shell')
|
this.shellButton = page.getByTestId('shell')
|
||||||
this.offsetPlaneButton = page.getByTestId('plane-offset')
|
this.offsetPlaneButton = page.getByTestId('plane-offset')
|
||||||
this.startSketchBtn = page.getByTestId('sketch')
|
this.startSketchBtn = page.getByTestId('sketch')
|
||||||
@ -54,7 +41,6 @@ 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')
|
||||||
}
|
}
|
||||||
@ -105,76 +91,4 @@ 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,7 +11,6 @@ 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.
|
||||||
@ -28,7 +27,6 @@ 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()
|
||||||
|
|
||||||
@ -37,23 +35,10 @@ 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
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -91,14 +76,6 @@ 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)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -148,7 +125,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 isn't the case on desktop. It gets
|
// code into localStorage but that isnt the case on desktop. It gets
|
||||||
// saved to the file system, which we have other tests for.
|
// saved to the file system, which we have other tests for.
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -437,7 +414,7 @@ test.describe('Onboarding tests', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test.fixme(
|
test(
|
||||||
'Restarting onboarding on desktop takes one attempt',
|
'Restarting onboarding on desktop takes one attempt',
|
||||||
{
|
{
|
||||||
appSettings: {
|
appSettings: {
|
||||||
@ -509,15 +486,7 @@ test.fixme(
|
|||||||
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,8 +16,6 @@ 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,
|
||||||
@ -97,8 +95,6 @@ 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,
|
||||||
@ -756,17 +752,6 @@ test(`Offset plane point-and-click`, async ({
|
|||||||
})
|
})
|
||||||
await scene.expectPixelColor([74, 74, 74], testPoint, 15)
|
await scene.expectPixelColor([74, 74, 74], testPoint, 15)
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step('Delete offset plane via feature tree selection', async () => {
|
|
||||||
await editor.closePane()
|
|
||||||
const operationButton = await toolbar.getFeatureTreeOperation(
|
|
||||||
'Offset Plane',
|
|
||||||
0
|
|
||||||
)
|
|
||||||
await operationButton.click({ button: 'left' })
|
|
||||||
await page.keyboard.press('Backspace')
|
|
||||||
await scene.expectPixelColor([50, 51, 96], testPoint, 15)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const loftPointAndClickCases = [
|
const loftPointAndClickCases = [
|
||||||
@ -775,9 +760,8 @@ 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 ({
|
||||||
context,
|
app,
|
||||||
page,
|
page,
|
||||||
homePage,
|
|
||||||
scene,
|
scene,
|
||||||
editor,
|
editor,
|
||||||
toolbar,
|
toolbar,
|
||||||
@ -789,11 +773,7 @@ loftPointAndClickCases.forEach(({ shouldPreselect }) => {
|
|||||||
sketch002 = startSketchOn(plane001)
|
sketch002 = startSketchOn(plane001)
|
||||||
|> circle({ center = [0, 0], radius = 20 }, %)
|
|> circle({ center = [0, 0], radius = 20 }, %)
|
||||||
`
|
`
|
||||||
await context.addInitScript((initialCode) => {
|
await app.initialise(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 }
|
||||||
@ -812,7 +792,7 @@ loftPointAndClickCases.forEach(({ shouldPreselect }) => {
|
|||||||
await clickOnSketch1()
|
await clickOnSketch1()
|
||||||
await page.keyboard.down('Shift')
|
await page.keyboard.down('Shift')
|
||||||
await clickOnSketch2()
|
await clickOnSketch2()
|
||||||
await page.waitForTimeout(500)
|
await app.page.waitForTimeout(500)
|
||||||
await page.keyboard.up('Shift')
|
await page.keyboard.up('Shift')
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -862,173 +842,6 @@ loftPointAndClickCases.forEach(({ shouldPreselect }) => {
|
|||||||
})
|
})
|
||||||
await scene.expectPixelColor([89, 89, 89], testPoint, 15)
|
await scene.expectPixelColor([89, 89, 89], testPoint, 15)
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step('Delete loft via feature tree selection', async () => {
|
|
||||||
await editor.closePane()
|
|
||||||
const operationButton = await toolbar.getFeatureTreeOperation('Loft', 0)
|
|
||||||
await operationButton.click({ button: 'left' })
|
|
||||||
await page.keyboard.press('Backspace')
|
|
||||||
await scene.expectPixelColor([254, 254, 254], testPoint, 15)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
// TODO: merge with above test. Right now we're not able to delete a loft
|
|
||||||
// right after creation via selection for some reason, so we go with a new instance
|
|
||||||
test('Loft and offset plane deletion via selection', async ({
|
|
||||||
context,
|
|
||||||
page,
|
|
||||||
homePage,
|
|
||||||
scene,
|
|
||||||
}) => {
|
|
||||||
const initialCode = `sketch001 = startSketchOn('XZ')
|
|
||||||
|> circle({ center = [0, 0], radius = 30 }, %)
|
|
||||||
plane001 = offsetPlane('XZ', 50)
|
|
||||||
sketch002 = startSketchOn(plane001)
|
|
||||||
|> circle({ center = [0, 0], radius = 20 }, %)
|
|
||||||
loft001 = loft([sketch001, sketch002])
|
|
||||||
`
|
|
||||||
await context.addInitScript((initialCode) => {
|
|
||||||
localStorage.setItem('persistCode', initialCode)
|
|
||||||
}, initialCode)
|
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
|
||||||
await homePage.goToModelingScene()
|
|
||||||
|
|
||||||
// One dumb hardcoded screen pixel value
|
|
||||||
const testPoint = { x: 575, y: 200 }
|
|
||||||
const [clickOnSketch1] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
|
||||||
const [clickOnSketch2] = scene.makeMouseHelpers(testPoint.x, testPoint.y + 80)
|
|
||||||
|
|
||||||
await test.step(`Delete loft`, async () => {
|
|
||||||
// Check for loft
|
|
||||||
await scene.expectPixelColor([89, 89, 89], testPoint, 15)
|
|
||||||
await clickOnSketch1()
|
|
||||||
await expect(page.locator('.cm-activeLine')).toHaveText(`
|
|
||||||
|> circle({ center = [0, 0], radius = 30 }, %)
|
|
||||||
`)
|
|
||||||
await page.keyboard.press('Backspace')
|
|
||||||
// Check for sketch 1
|
|
||||||
await scene.expectPixelColor([254, 254, 254], testPoint, 15)
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step('Delete sketch002', async () => {
|
|
||||||
await page.waitForTimeout(1000)
|
|
||||||
await clickOnSketch2()
|
|
||||||
await expect(page.locator('.cm-activeLine')).toHaveText(`
|
|
||||||
|> circle({ center = [0, 0], radius = 20 }, %)
|
|
||||||
`)
|
|
||||||
await page.keyboard.press('Backspace')
|
|
||||||
// Check for plane001
|
|
||||||
await scene.expectPixelColor([228, 228, 228], testPoint, 15)
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step('Delete plane001', async () => {
|
|
||||||
await page.waitForTimeout(1000)
|
|
||||||
await clickOnSketch2()
|
|
||||||
await expect(page.locator('.cm-activeLine')).toHaveText(`
|
|
||||||
plane001 = offsetPlane('XZ', 50)
|
|
||||||
`)
|
|
||||||
await page.keyboard.press('Backspace')
|
|
||||||
// Check for sketch 1
|
|
||||||
await scene.expectPixelColor([254, 254, 254], testPoint, 15)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
test(`Sweep point-and-click`, async ({
|
|
||||||
context,
|
|
||||||
page,
|
|
||||||
homePage,
|
|
||||||
scene,
|
|
||||||
editor,
|
|
||||||
toolbar,
|
|
||||||
cmdBar,
|
|
||||||
}) => {
|
|
||||||
const initialCode = `sketch001 = startSketchOn('YZ')
|
|
||||||
|> circle({
|
|
||||||
center = [0, 0],
|
|
||||||
radius = 500
|
|
||||||
}, %)
|
|
||||||
sketch002 = startSketchOn('XZ')
|
|
||||||
|> startProfileAt([0, 0], %)
|
|
||||||
|> xLine(-500, %)
|
|
||||||
|> tangentialArcTo([-2000, 500], %)
|
|
||||||
`
|
|
||||||
await context.addInitScript((initialCode) => {
|
|
||||||
localStorage.setItem('persistCode', initialCode)
|
|
||||||
}, initialCode)
|
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
|
||||||
await homePage.goToModelingScene()
|
|
||||||
await scene.waitForExecutionDone()
|
|
||||||
|
|
||||||
// One dumb hardcoded screen pixel value
|
|
||||||
const testPoint = { x: 700, y: 250 }
|
|
||||||
const [clickOnSketch1] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
|
||||||
const [clickOnSketch2] = scene.makeMouseHelpers(testPoint.x - 50, testPoint.y)
|
|
||||||
const sweepDeclaration = 'sweep001 = sweep({ path = sketch002 }, sketch001)'
|
|
||||||
|
|
||||||
await test.step(`Look for sketch001`, async () => {
|
|
||||||
await toolbar.closePane('code')
|
|
||||||
await scene.expectPixelColor([53, 53, 53], testPoint, 15)
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step(`Go through the command bar flow`, async () => {
|
|
||||||
await toolbar.sweepButton.click()
|
|
||||||
await cmdBar.expectState({
|
|
||||||
commandName: 'Sweep',
|
|
||||||
currentArgKey: 'profile',
|
|
||||||
currentArgValue: '',
|
|
||||||
headerArguments: {
|
|
||||||
Path: '',
|
|
||||||
Profile: '',
|
|
||||||
},
|
|
||||||
highlightedHeaderArg: 'profile',
|
|
||||||
stage: 'arguments',
|
|
||||||
})
|
|
||||||
await clickOnSketch1()
|
|
||||||
await cmdBar.expectState({
|
|
||||||
commandName: 'Sweep',
|
|
||||||
currentArgKey: 'path',
|
|
||||||
currentArgValue: '',
|
|
||||||
headerArguments: {
|
|
||||||
Path: '',
|
|
||||||
Profile: '1 face',
|
|
||||||
},
|
|
||||||
highlightedHeaderArg: 'path',
|
|
||||||
stage: 'arguments',
|
|
||||||
})
|
|
||||||
await clickOnSketch2()
|
|
||||||
await cmdBar.expectState({
|
|
||||||
commandName: 'Sweep',
|
|
||||||
headerArguments: {
|
|
||||||
Path: '1 face',
|
|
||||||
Profile: '1 face',
|
|
||||||
},
|
|
||||||
stage: 'review',
|
|
||||||
})
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
|
|
||||||
await scene.expectPixelColor([135, 64, 73], testPoint, 15)
|
|
||||||
await toolbar.openPane('code')
|
|
||||||
await editor.expectEditor.toContain(sweepDeclaration)
|
|
||||||
await editor.expectState({
|
|
||||||
diagnostics: [],
|
|
||||||
activeLines: [sweepDeclaration],
|
|
||||||
highlightedCode: '',
|
|
||||||
})
|
|
||||||
await toolbar.closePane('code')
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step('Delete sweep via feature tree selection', async () => {
|
|
||||||
await toolbar.openPane('feature-tree')
|
|
||||||
await page.waitForTimeout(500)
|
|
||||||
const operationButton = await toolbar.getFeatureTreeOperation('Sweep', 0)
|
|
||||||
await operationButton.click({ button: 'left' })
|
|
||||||
await page.keyboard.press('Backspace')
|
|
||||||
await page.waitForTimeout(500)
|
|
||||||
await toolbar.closePane('feature-tree')
|
|
||||||
await scene.expectPixelColor([53, 53, 53], testPoint, 15)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -1046,8 +859,6 @@ 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)
|
||||||
@ -1208,104 +1019,4 @@ extrude001 = extrude(40, sketch001)
|
|||||||
})
|
})
|
||||||
await scene.expectPixelColor([49, 49, 49], testPoint, 15)
|
await scene.expectPixelColor([49, 49, 49], testPoint, 15)
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step('Delete shell via feature tree selection', async () => {
|
|
||||||
await editor.closePane()
|
|
||||||
const operationButton = await toolbar.getFeatureTreeOperation('Shell', 0)
|
|
||||||
await operationButton.click({ button: 'left' })
|
|
||||||
await page.keyboard.press('Backspace')
|
|
||||||
await scene.expectPixelColor([99, 99, 99], testPoint, 15)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
const shellSketchOnFacesCases = [
|
|
||||||
`sketch001 = startSketchOn('XZ')
|
|
||||||
|> circle({ center = [0, 0], radius = 100 }, %)
|
|
||||||
|> extrude(100, %)
|
|
||||||
|
|
||||||
sketch002 = startSketchOn(sketch001, 'END')
|
|
||||||
|> circle({ center = [0, 0], radius = 50 }, %)
|
|
||||||
|> extrude(50, %)
|
|
||||||
`,
|
|
||||||
`sketch001 = startSketchOn('XZ')
|
|
||||||
|> circle({ center = [0, 0], radius = 100 }, %)
|
|
||||||
extrude001 = extrude(100, sketch001)
|
|
||||||
|
|
||||||
sketch002 = startSketchOn(extrude001, 'END')
|
|
||||||
|> circle({ center = [0, 0], radius = 50 }, %)
|
|
||||||
extrude002 = extrude(50, sketch002)
|
|
||||||
`,
|
|
||||||
]
|
|
||||||
shellSketchOnFacesCases.forEach((initialCode, index) => {
|
|
||||||
const hasExtrudesInPipe = index === 0
|
|
||||||
test(`Shell point-and-click sketch on face (extrudes in pipes: ${hasExtrudesInPipe})`, async ({
|
|
||||||
context,
|
|
||||||
page,
|
|
||||||
homePage,
|
|
||||||
scene,
|
|
||||||
editor,
|
|
||||||
toolbar,
|
|
||||||
cmdBar,
|
|
||||||
}) => {
|
|
||||||
await context.addInitScript((initialCode) => {
|
|
||||||
localStorage.setItem('persistCode', initialCode)
|
|
||||||
}, initialCode)
|
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
|
||||||
await homePage.goToModelingScene()
|
|
||||||
await scene.waitForExecutionDone()
|
|
||||||
|
|
||||||
// One dumb hardcoded screen pixel value
|
|
||||||
const testPoint = { x: 550, y: 295 }
|
|
||||||
const [clickOnCap] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
|
||||||
const shellDeclaration = `shell001 = shell({ faces = ['end'], thickness = 5 }, ${
|
|
||||||
hasExtrudesInPipe ? 'sketch002' : 'extrude002'
|
|
||||||
})`
|
|
||||||
|
|
||||||
await test.step(`Look for the grey of the shape`, async () => {
|
|
||||||
await toolbar.closePane('code')
|
|
||||||
await scene.expectPixelColor([128, 128, 128], testPoint, 15)
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step(`Go through the command bar flow, selecting a cap and keeping default thickness`, async () => {
|
|
||||||
await toolbar.shellButton.click()
|
|
||||||
await cmdBar.expectState({
|
|
||||||
stage: 'arguments',
|
|
||||||
currentArgKey: 'selection',
|
|
||||||
currentArgValue: '',
|
|
||||||
headerArguments: {
|
|
||||||
Selection: '',
|
|
||||||
Thickness: '',
|
|
||||||
},
|
|
||||||
highlightedHeaderArg: 'selection',
|
|
||||||
commandName: 'Shell',
|
|
||||||
})
|
|
||||||
await clickOnCap()
|
|
||||||
await page.waitForTimeout(500)
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
await page.waitForTimeout(500)
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
await page.waitForTimeout(500)
|
|
||||||
await cmdBar.expectState({
|
|
||||||
stage: 'review',
|
|
||||||
headerArguments: {
|
|
||||||
Selection: '1 cap',
|
|
||||||
Thickness: '5',
|
|
||||||
},
|
|
||||||
commandName: 'Shell',
|
|
||||||
})
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
|
|
||||||
await toolbar.openPane('code')
|
|
||||||
await editor.expectEditor.toContain(shellDeclaration)
|
|
||||||
await editor.expectState({
|
|
||||||
diagnostics: [],
|
|
||||||
activeLines: [shellDeclaration],
|
|
||||||
highlightedCode: '',
|
|
||||||
})
|
|
||||||
await toolbar.closePane('code')
|
|
||||||
await scene.expectPixelColor([73, 73, 73], testPoint, 15)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
@ -115,7 +115,7 @@ test(
|
|||||||
)
|
)
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'open a file in a project works and renders, open another file in different project with errors, it should clear the scene',
|
'yyyyyyyyy open a file in a project works and renders, open another file in different project with errors, it should clear the scene',
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ context, page }, testInfo) => {
|
async ({ context, page }, testInfo) => {
|
||||||
await context.folderSetupFn(async (dir) => {
|
await context.folderSetupFn(async (dir) => {
|
||||||
@ -199,7 +199,7 @@ test(
|
|||||||
)
|
)
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'open a file in a project works and renders, open another file in different project that is empty, it should clear the scene',
|
'aaayyyyyyyy open a file in a project works and renders, open another file in different project that is empty, it should clear the scene',
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ context, page }, testInfo) => {
|
async ({ context, page }, testInfo) => {
|
||||||
await context.folderSetupFn(async (dir) => {
|
await context.folderSetupFn(async (dir) => {
|
||||||
@ -276,7 +276,7 @@ test(
|
|||||||
)
|
)
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'open a file in a project works and renders, open empty file, it should clear the scene',
|
'nooooooooooooo open a file in a project works and renders, open empty file, it should clear the scene',
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ context, page }, testInfo) => {
|
async ({ context, page }, testInfo) => {
|
||||||
await context.folderSetupFn(async (dir) => {
|
await context.folderSetupFn(async (dir) => {
|
||||||
@ -342,10 +342,10 @@ test(
|
|||||||
)
|
)
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'open a file in a project works and renders, open another file in the same project with errors, it should clear the scene',
|
'xxxxx 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) => {
|
||||||
await context.folderSetupFn(async (dir) => {
|
const { 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(
|
||||||
@ -1885,48 +1885,3 @@ test.fixme(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
test(
|
|
||||||
'project name with foreign characters should open',
|
|
||||||
{ tag: '@electron' },
|
|
||||||
async ({ context, page }, testInfo) => {
|
|
||||||
await context.folderSetupFn(async (dir) => {
|
|
||||||
const bracketDir = path.join(dir, 'اَلْعَرَبِيَّةُ')
|
|
||||||
await fsp.mkdir(bracketDir, { recursive: true })
|
|
||||||
await fsp.copyFile(
|
|
||||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
|
||||||
path.join(bracketDir, 'main.kcl')
|
|
||||||
)
|
|
||||||
|
|
||||||
await fsp.writeFile(path.join(bracketDir, 'empty.kcl'), '')
|
|
||||||
})
|
|
||||||
|
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
|
||||||
const u = await getUtils(page)
|
|
||||||
|
|
||||||
page.on('console', console.log)
|
|
||||||
|
|
||||||
const pointOnModel = { x: 630, y: 280 }
|
|
||||||
|
|
||||||
await test.step('Opening the اَلْعَرَبِيَّةُ project should load the stream', async () => {
|
|
||||||
// expect to see the text bracket
|
|
||||||
await expect(page.getByText('اَلْعَرَبِيَّةُ')).toBeVisible()
|
|
||||||
|
|
||||||
await page.getByText('اَلْعَرَبِيَّةُ').click()
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
page.getByRole('button', { name: 'Start Sketch' })
|
|
||||||
).toBeEnabled({
|
|
||||||
timeout: 20_000,
|
|
||||||
})
|
|
||||||
|
|
||||||
// gray at this pixel means the stream has loaded in the most
|
|
||||||
// user way we can verify it (pixel color)
|
|
||||||
await expect
|
|
||||||
.poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
|
|
||||||
timeout: 10_000,
|
|
||||||
})
|
|
||||||
.toBeLessThan(15)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
@ -1,190 +0,0 @@
|
|||||||
import { test, expect } from './zoo-test'
|
|
||||||
|
|
||||||
/* eslint-disable jest/no-conditional-expect */
|
|
||||||
|
|
||||||
const file = `sketch001 = startSketchOn('XZ')
|
|
||||||
profile001 = startProfileAt([57.81, 250.51], sketch001)
|
|
||||||
|> line([121.13, 56.63], %, $seg02)
|
|
||||||
|> line([83.37, -34.61], %, $seg01)
|
|
||||||
|> line([19.66, -116.4], %)
|
|
||||||
|> line([-221.8, -41.69], %)
|
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|
||||||
|> close(%)
|
|
||||||
extrude001 = extrude(200, profile001)
|
|
||||||
sketch002 = startSketchOn('XZ')
|
|
||||||
|> startProfileAt([-73.64, -42.89], %)
|
|
||||||
|> xLine(173.71, %)
|
|
||||||
|> line([-22.12, -94.4], %)
|
|
||||||
|> xLine(-156.98, %)
|
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|
||||||
|> close(%)
|
|
||||||
extrude002 = extrude(50, sketch002)
|
|
||||||
sketch003 = startSketchOn('XY')
|
|
||||||
|> startProfileAt([52.92, 157.81], %)
|
|
||||||
|> angledLine([0, 176.4], %, $rectangleSegmentA001)
|
|
||||||
|> angledLine([
|
|
||||||
segAng(rectangleSegmentA001) - 90,
|
|
||||||
53.4
|
|
||||||
], %, $rectangleSegmentB001)
|
|
||||||
|> angledLine([
|
|
||||||
segAng(rectangleSegmentA001),
|
|
||||||
-segLen(rectangleSegmentA001)
|
|
||||||
], %, $rectangleSegmentC001)
|
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|
||||||
|> close(%)
|
|
||||||
extrude003 = extrude(20, sketch003)
|
|
||||||
`
|
|
||||||
|
|
||||||
test.describe('Check the happy path, for basic changing color', () => {
|
|
||||||
const cases = [
|
|
||||||
{
|
|
||||||
desc: 'User accepts change',
|
|
||||||
shouldReject: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: 'User rejects change',
|
|
||||||
shouldReject: true,
|
|
||||||
},
|
|
||||||
] as const
|
|
||||||
for (const { desc, shouldReject } of cases) {
|
|
||||||
test(`${desc}`, async ({
|
|
||||||
context,
|
|
||||||
homePage,
|
|
||||||
cmdBar,
|
|
||||||
editor,
|
|
||||||
page,
|
|
||||||
scene,
|
|
||||||
}) => {
|
|
||||||
await context.addInitScript((file) => {
|
|
||||||
localStorage.setItem('persistCode', file)
|
|
||||||
}, file)
|
|
||||||
await homePage.goToModelingScene()
|
|
||||||
|
|
||||||
const body1CapCoords = { x: 571, y: 351 }
|
|
||||||
const greenCheckCoords = { x: 565, y: 345 }
|
|
||||||
const body2WallCoords = { x: 609, y: 153 }
|
|
||||||
const [clickBody1Cap] = scene.makeMouseHelpers(
|
|
||||||
body1CapCoords.x,
|
|
||||||
body1CapCoords.y
|
|
||||||
)
|
|
||||||
const yellow: [number, number, number] = [179, 179, 131]
|
|
||||||
const green: [number, number, number] = [108, 152, 75]
|
|
||||||
const notGreen: [number, number, number] = [132, 132, 132]
|
|
||||||
const body2NotGreen: [number, number, number] = [88, 88, 88]
|
|
||||||
const submittingToast = page.getByText('Submitting to Text-to-CAD API...')
|
|
||||||
const successToast = page.getByText('Prompt to edit successful')
|
|
||||||
const acceptBtn = page.getByRole('button', { name: 'checkmark Accept' })
|
|
||||||
const rejectBtn = page.getByRole('button', { name: 'close Reject' })
|
|
||||||
|
|
||||||
await test.step('wait for scene to load select body and check selection came through', async () => {
|
|
||||||
await scene.expectPixelColor([134, 134, 134], body1CapCoords, 15)
|
|
||||||
await clickBody1Cap()
|
|
||||||
await scene.expectPixelColor(yellow, body1CapCoords, 20)
|
|
||||||
await editor.expectState({
|
|
||||||
highlightedCode: '',
|
|
||||||
activeLines: ['|>startProfileAt([-73.64,-42.89],%)'],
|
|
||||||
diagnostics: [],
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step('fire off edit prompt', async () => {
|
|
||||||
await cmdBar.openCmdBar('promptToEdit')
|
|
||||||
// being specific about the color with a hex means asserting pixel color is more stable
|
|
||||||
await page
|
|
||||||
.getByTestId('cmd-bar-arg-value')
|
|
||||||
.fill('make this neon green please, use #39FF14')
|
|
||||||
await page.waitForTimeout(100)
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
await expect(submittingToast).toBeVisible()
|
|
||||||
await expect(submittingToast).not.toBeVisible({ timeout: 2 * 60_000 }) // can take a while
|
|
||||||
await expect(successToast).toBeVisible()
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step('verify initial change', async () => {
|
|
||||||
await scene.expectPixelColor(green, greenCheckCoords, 15)
|
|
||||||
await scene.expectPixelColor(body2NotGreen, body2WallCoords, 15)
|
|
||||||
await editor.expectEditor.toContain('appearance({')
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!shouldReject) {
|
|
||||||
await test.step('check accept works and can be "undo"ed', async () => {
|
|
||||||
await acceptBtn.click()
|
|
||||||
await expect(successToast).not.toBeVisible()
|
|
||||||
|
|
||||||
await scene.expectPixelColor(green, greenCheckCoords, 15)
|
|
||||||
await editor.expectEditor.toContain('appearance({')
|
|
||||||
|
|
||||||
// ctrl-z works after accepting
|
|
||||||
await page.keyboard.down('ControlOrMeta')
|
|
||||||
await page.keyboard.press('KeyZ')
|
|
||||||
await page.keyboard.up('ControlOrMeta')
|
|
||||||
await editor.expectEditor.not.toContain('appearance({')
|
|
||||||
await scene.expectPixelColor(notGreen, greenCheckCoords, 15)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
await test.step('check reject works', async () => {
|
|
||||||
await rejectBtn.click()
|
|
||||||
await expect(successToast).not.toBeVisible()
|
|
||||||
|
|
||||||
await scene.expectPixelColor(notGreen, greenCheckCoords, 15)
|
|
||||||
await editor.expectEditor.not.toContain('appearance({')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
test.describe('bad path', () => {
|
|
||||||
test(`bad edit prompt`, async ({
|
|
||||||
context,
|
|
||||||
homePage,
|
|
||||||
cmdBar,
|
|
||||||
editor,
|
|
||||||
toolbar,
|
|
||||||
page,
|
|
||||||
scene,
|
|
||||||
}) => {
|
|
||||||
await context.addInitScript((file) => {
|
|
||||||
localStorage.setItem('persistCode', file)
|
|
||||||
}, file)
|
|
||||||
await homePage.goToModelingScene()
|
|
||||||
|
|
||||||
const body1CapCoords = { x: 571, y: 351 }
|
|
||||||
const [clickBody1Cap] = scene.makeMouseHelpers(
|
|
||||||
body1CapCoords.x,
|
|
||||||
body1CapCoords.y
|
|
||||||
)
|
|
||||||
const yellow: [number, number, number] = [179, 179, 131]
|
|
||||||
const submittingToast = page.getByText('Submitting to Text-to-CAD API...')
|
|
||||||
const failToast = page.getByText(
|
|
||||||
'Failed to edit your KCL code, please try again with a different prompt or selection'
|
|
||||||
)
|
|
||||||
|
|
||||||
await test.step('wait for scene to load and select body', async () => {
|
|
||||||
await scene.expectPixelColor([134, 134, 134], body1CapCoords, 15)
|
|
||||||
|
|
||||||
await clickBody1Cap()
|
|
||||||
await scene.expectPixelColor(yellow, body1CapCoords, 20)
|
|
||||||
|
|
||||||
await editor.expectState({
|
|
||||||
highlightedCode: '',
|
|
||||||
activeLines: ['|>startProfileAt([-73.64,-42.89],%)'],
|
|
||||||
diagnostics: [],
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step('fire of bad prompt', async () => {
|
|
||||||
await cmdBar.openCmdBar('promptToEdit')
|
|
||||||
await page
|
|
||||||
.getByTestId('cmd-bar-arg-value')
|
|
||||||
.fill('ansheusha asnthuatshoeuhtaoetuhthaeu laughs in dvorak')
|
|
||||||
await page.waitForTimeout(100)
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
await expect(submittingToast).toBeVisible()
|
|
||||||
})
|
|
||||||
await test.step('check fail toast appeared', async () => {
|
|
||||||
await expect(submittingToast).not.toBeVisible({ timeout: 2 * 60_000 }) // can take a while
|
|
||||||
await expect(failToast).toBeVisible()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
@ -560,8 +560,6 @@ 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
|
||||||
@ -588,7 +586,7 @@ extrude001 = extrude(50, sketch001)
|
|||||||
timeout: 5000,
|
timeout: 5000,
|
||||||
message: 'Plane color is visible',
|
message: 'Plane color is visible',
|
||||||
})
|
})
|
||||||
.toBeLessThanOrEqual(15)
|
.toBeLessThan(15)
|
||||||
|
|
||||||
let maxZoomOuts = 10
|
let maxZoomOuts = 10
|
||||||
let middlePixelIsBackgroundColor =
|
let middlePixelIsBackgroundColor =
|
||||||
@ -606,7 +604,7 @@ extrude001 = extrude(50, sketch001)
|
|||||||
}
|
}
|
||||||
|
|
||||||
expect(middlePixelIsBackgroundColor, {
|
expect(middlePixelIsBackgroundColor, {
|
||||||
message: 'We should not see the default planes',
|
message: 'We no longer the default planes',
|
||||||
}).toBeTruthy()
|
}).toBeTruthy()
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -614,38 +612,6 @@ extrude001 = extrude(50, sketch001)
|
|||||||
await expect(gizmo).toBeVisible()
|
await expect(gizmo).toBeVisible()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test(`Refreshing the app doesn't cause the stream to pause on long-executing files`, async ({
|
|
||||||
context,
|
|
||||||
homePage,
|
|
||||||
scene,
|
|
||||||
toolbar,
|
|
||||||
viewport,
|
|
||||||
}) => {
|
|
||||||
await context.folderSetupFn(async (dir) => {
|
|
||||||
const legoDir = path.join(dir, 'lego')
|
|
||||||
await fsp.mkdir(legoDir, { recursive: true })
|
|
||||||
await fsp.copyFile(
|
|
||||||
executorInputPath('lego.kcl'),
|
|
||||||
path.join(legoDir, 'main.kcl')
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step(`Test setup`, async () => {
|
|
||||||
await homePage.openProject('lego')
|
|
||||||
await toolbar.closePane('code')
|
|
||||||
})
|
|
||||||
await test.step(`Waiting for the loading spinner to disappear`, async () => {
|
|
||||||
await scene.loadingIndicator.waitFor({ state: 'detached' })
|
|
||||||
})
|
|
||||||
await test.step(`The part should start loading quickly, not waiting until execution is complete`, async () => {
|
|
||||||
await scene.expectPixelColor(
|
|
||||||
[143, 143, 143],
|
|
||||||
{ x: (viewport?.width ?? 1200) / 2, y: (viewport?.height ?? 500) / 2 },
|
|
||||||
15
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
async function clickExportButton(page: Page) {
|
async function clickExportButton(page: Page) {
|
||||||
|
@ -39,8 +39,8 @@ test.describe('Sketch tests', () => {
|
|||||||
${startProfileAt1}
|
${startProfileAt1}
|
||||||
|> arc({
|
|> arc({
|
||||||
radius = screwRadius,
|
radius = screwRadius,
|
||||||
angleStart = 0,
|
angle_start = 0,
|
||||||
angleEnd = 360
|
angle_end = 360
|
||||||
}, %)
|
}, %)
|
||||||
|
|
||||||
part001 = startSketchOn('XY')
|
part001 = startSketchOn('XY')
|
||||||
@ -60,8 +60,8 @@ test.describe('Sketch tests', () => {
|
|||||||
|> yLine(wireOffset, %)
|
|> yLine(wireOffset, %)
|
||||||
|> arc({
|
|> arc({
|
||||||
radius = wireRadius,
|
radius = wireRadius,
|
||||||
angleStart = 0,
|
angle_start = 0,
|
||||||
angleEnd = 180
|
angle_end = 180
|
||||||
}, %)
|
}, %)
|
||||||
|> yLine(-wireOffset, %)
|
|> yLine(-wireOffset, %)
|
||||||
|> xLine(-width / 4, %)
|
|> xLine(-width / 4, %)
|
||||||
@ -82,16 +82,19 @@ 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()
|
||||||
@ -634,8 +637,6 @@ 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 }
|
||||||
@ -833,8 +834,6 @@ 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(
|
||||||
@ -950,108 +949,107 @@ test.describe('Sketch tests', () => {
|
|||||||
`.replace(/\s/g, '')
|
`.replace(/\s/g, '')
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
// TODO: fix after electron migration is merged
|
test('empty-scene default-planes act as expected', async ({
|
||||||
test.fixme(
|
page,
|
||||||
'empty-scene default-planes act as expected',
|
homePage,
|
||||||
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)
|
||||||
@ -1323,85 +1321,3 @@ test.describe(`Sketching with offset planes`, () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// Regression test for https://github.com/KittyCAD/modeling-app/issues/4891
|
|
||||||
test.describe(`Click based selection don't brick the app when clicked out of range after format using cache`, () => {
|
|
||||||
test(`Can select a line that reformmed after entering sketch mode`, async ({
|
|
||||||
context,
|
|
||||||
page,
|
|
||||||
scene,
|
|
||||||
toolbar,
|
|
||||||
editor,
|
|
||||||
homePage,
|
|
||||||
}) => {
|
|
||||||
// We seed the scene with a single offset plane
|
|
||||||
await context.addInitScript(() => {
|
|
||||||
localStorage.setItem(
|
|
||||||
'persistCode',
|
|
||||||
`sketch001 = startSketchOn('XZ')
|
|
||||||
|> startProfileAt([0, 0], %)
|
|
||||||
|> line([3.14, 3.14], %)
|
|
||||||
|> arcTo({
|
|
||||||
end = [4, 2],
|
|
||||||
interior = [1, 2]
|
|
||||||
}, %)
|
|
||||||
`
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
|
||||||
await scene.waitForExecutionDone()
|
|
||||||
|
|
||||||
await test.step(`format the code`, async () => {
|
|
||||||
// doesn't contain condensed version
|
|
||||||
await editor.expectEditor.not.toContain(
|
|
||||||
`arcTo({ end = [4, 2], interior = [1, 2] }, %)`
|
|
||||||
)
|
|
||||||
// click the code to enter sketch mode
|
|
||||||
await page.getByText(`arcTo`).click()
|
|
||||||
// Format the code.
|
|
||||||
await page.locator('#code-pane button:first-child').click()
|
|
||||||
await page.locator('button:has-text("Format code")').click()
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step(`Ensure the code reformatted`, async () => {
|
|
||||||
await editor.expectEditor.toContain(
|
|
||||||
`arcTo({ end = [4, 2], interior = [1, 2] }, %)`
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
const [arcClick, arcHover] = scene.makeMouseHelpers(699, 337)
|
|
||||||
await test.step('Ensure we can hover the arc', async () => {
|
|
||||||
await arcHover()
|
|
||||||
|
|
||||||
// Check that the code is highlighted
|
|
||||||
await editor.expectState({
|
|
||||||
activeLines: ["sketch001=startSketchOn('XZ')"],
|
|
||||||
diagnostics: [],
|
|
||||||
highlightedCode: 'arcTo({end = [4, 2], interior = [1, 2]}, %)',
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step('reset the selection', async () => {
|
|
||||||
// Move the mouse out of the way
|
|
||||||
await page.mouse.move(655, 337)
|
|
||||||
|
|
||||||
await editor.expectState({
|
|
||||||
activeLines: ["sketch001=startSketchOn('XZ')"],
|
|
||||||
diagnostics: [],
|
|
||||||
highlightedCode: '',
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step('Ensure we can click the arc', async () => {
|
|
||||||
await arcClick()
|
|
||||||
|
|
||||||
// Check that the code is highlighted
|
|
||||||
await editor.expectState({
|
|
||||||
activeLines: [],
|
|
||||||
diagnostics: [],
|
|
||||||
highlightedCode: 'arcTo({end = [4, 2], interior = [1, 2]}, %)',
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
@ -375,7 +375,6 @@ 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()
|
||||||
}
|
}
|
||||||
@ -1169,109 +1168,3 @@ 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: 54 KiB After Width: | Height: | Size: 52 KiB |
After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 49 KiB |
After Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 58 KiB |
After Width: | Height: | Size: 55 KiB |
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 47 KiB |
After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
After Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 41 KiB |
After Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 49 KiB |
After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 74 KiB |
After Width: | Height: | Size: 65 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 65 KiB |
After Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 144 KiB |
Before Width: | Height: | Size: 128 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 37 KiB |
After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 40 KiB |
After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 39 KiB |
After Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 38 KiB |
After Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 42 KiB |