Compare commits
45 Commits
Author | SHA1 | Date | |
---|---|---|---|
872b196a86 | |||
d535a2862d | |||
63a3bc7bc6 | |||
02055a8b31 | |||
93891422f7 | |||
7193b4110a | |||
76e7d80a55 | |||
b816df21d2 | |||
3630696848 | |||
f165d19fda | |||
3dd98ae1d5 | |||
a46e0a0fe7 | |||
8f9dc06228 | |||
fa22c14723 | |||
1d39983b08 | |||
da301ba862 | |||
efe8089b08 | |||
49de3b0ac9 | |||
2b2ed470c1 | |||
96652a0c48 | |||
04e586d07b | |||
fe5f574a77 | |||
e787495ad0 | |||
8bb9be7a5e | |||
00892464e8 | |||
05ed2a3367 | |||
10cc5bce59 | |||
a32f150fc1 | |||
ac60082e67 | |||
d44dc1b21a | |||
813962ea4c | |||
738443a6ab | |||
4b6bbbe2c5 | |||
6ff8addc8b | |||
da05c38b9e | |||
191b9b71fd | |||
05163fdded | |||
7ed26e21c6 | |||
c668d40efc | |||
f38c6b90b7 | |||
7bc8bae0ec | |||
3804aca27e | |||
b127680f2f | |||
b7de8e60cf | |||
058fccb5e1 |
@ -1,59 +0,0 @@
|
||||
# bash strict mode
|
||||
set -euo pipefail
|
||||
|
||||
if [[ ! -f "test-results/.last-run.json" ]]; then
|
||||
# if no last run artifact, than run plawright normally
|
||||
echo "run playwright normally"
|
||||
if [[ "$3" == ubuntu-latest* ]]; then
|
||||
yarn test:playwright:browser:chrome:ubuntu -- --shard=$1/$2 || true
|
||||
elif [[ "$3" == windows-latest* ]]; then
|
||||
yarn test:playwright:browser:chrome:windows -- --shard=$1/$2 || true
|
||||
else
|
||||
echo "Do not run playwright. Unable to detect os runtime."
|
||||
exit 1
|
||||
fi
|
||||
# # send to axiom
|
||||
node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
|
||||
fi
|
||||
|
||||
retry=1
|
||||
max_retrys=4
|
||||
|
||||
# retry failed tests, doing our own retries because using inbuilt playwright retries causes connection issues
|
||||
while [[ $retry -le $max_retrys ]]; do
|
||||
if [[ -f "test-results/.last-run.json" ]]; then
|
||||
failed_tests=$(jq '.failedTests | length' test-results/.last-run.json)
|
||||
if [[ $failed_tests -gt 0 ]]; then
|
||||
echo "retried=true" >>$GITHUB_OUTPUT
|
||||
echo "run playwright with last failed tests and retry $retry"
|
||||
if [[ "$3" == ubuntu-latest* ]]; then
|
||||
yarn test:playwright:browser:chrome:ubuntu -- --last-failed || true
|
||||
elif [[ "$3" == windows-latest* ]]; then
|
||||
yarn test:playwright:browser:chrome:windows -- --last-failed || true
|
||||
else
|
||||
echo "Do not run playwright. Unable to detect os runtime."
|
||||
exit 1
|
||||
fi
|
||||
# send to axiom
|
||||
node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
|
||||
retry=$((retry + 1))
|
||||
else
|
||||
echo "retried=false" >>$GITHUB_OUTPUT
|
||||
exit 0
|
||||
fi
|
||||
else
|
||||
echo "retried=false" >>$GITHUB_OUTPUT
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
|
||||
echo "retried=false" >>$GITHUB_OUTPUT
|
||||
|
||||
if [[ -f "test-results/.last-run.json" ]]; then
|
||||
failed_tests=$(jq '.failedTests | length' test-results/.last-run.json)
|
||||
if [[ $failed_tests -gt 0 ]]; then
|
||||
# if it still fails after 3 retrys, then fail the job
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
exit 0
|
20
.github/ci-cd-scripts/playwright-electron.sh
vendored
@ -1,15 +1,17 @@
|
||||
#!/bin/bash
|
||||
|
||||
# bash strict mode
|
||||
set -euo pipefail
|
||||
|
||||
if [[ ! -f "test-results/.last-run.json" ]]; then
|
||||
# if no last run artifact, than run plawright normally
|
||||
echo "run playwright normally"
|
||||
if [[ "$1" == ubuntu-latest* ]]; then
|
||||
xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn test:playwright:electron:ubuntu || true
|
||||
elif [[ "$1" == windows-latest* ]]; then
|
||||
yarn test:playwright:electron:windows || true
|
||||
elif [[ "$1" == macos-14* ]]; then
|
||||
yarn test:playwright:electron:macos || true
|
||||
if [[ "$3" == *ubuntu* ]]; then
|
||||
xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn test:playwright:electron:ubuntu -- --shard=$1/$2 || true
|
||||
elif [[ "$3" == *windows* ]]; then
|
||||
yarn test:playwright:electron:windows -- --shard=$1/$2 || true
|
||||
elif [[ "$3" == *macos* ]]; then
|
||||
yarn test:playwright:electron:macos -- --shard=$1/$2 || true
|
||||
else
|
||||
echo "Do not run playwright. Unable to detect os runtime."
|
||||
exit 1
|
||||
@ -28,11 +30,11 @@ while [[ $retry -le $max_retrys ]]; do
|
||||
if [[ $failed_tests -gt 0 ]]; then
|
||||
echo "retried=true" >>$GITHUB_OUTPUT
|
||||
echo "run playwright with last failed tests and retry $retry"
|
||||
if [[ "$1" == ubuntu-latest* ]]; then
|
||||
if [[ "$3" == *ubuntu* ]]; then
|
||||
xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn test:playwright:electron:ubuntu -- --last-failed || true
|
||||
elif [[ "$1" == windows-latest* ]]; then
|
||||
elif [[ "$3" == *windows* ]]; then
|
||||
yarn test:playwright:electron:windows -- --last-failed || true
|
||||
elif [[ "$1" == macos-14* ]]; then
|
||||
elif [[ "$3" == *macos* ]]; then
|
||||
yarn test:playwright:electron:macos -- --last-failed || true
|
||||
else
|
||||
echo "Do not run playwright. Unable to detect os runtime."
|
||||
|
22
.github/workflows/cargo-test.yml
vendored
@ -2,28 +2,8 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'src/wasm-lib/**.rs'
|
||||
- 'src/wasm-lib/**.hbs'
|
||||
- 'src/wasm-lib/**.gen'
|
||||
- 'src/wasm-lib/**.snap'
|
||||
- '**/Cargo.toml'
|
||||
- '**/Cargo.lock'
|
||||
- '**/rust-toolchain.toml'
|
||||
- 'src/wasm-lib/**.kcl'
|
||||
- .github/workflows/cargo-test.yml
|
||||
|
||||
pull_request:
|
||||
paths:
|
||||
- 'src/wasm-lib/**.rs'
|
||||
- 'src/wasm-lib/**.hbs'
|
||||
- 'src/wasm-lib/**.gen'
|
||||
- 'src/wasm-lib/**.snap'
|
||||
- '**/Cargo.toml'
|
||||
- '**/Cargo.lock'
|
||||
- '**/rust-toolchain.toml'
|
||||
- 'src/wasm-lib/**.kcl'
|
||||
- .github/workflows/cargo-test.yml
|
||||
workflow_dispatch:
|
||||
permissions: read-all
|
||||
concurrency:
|
||||
@ -71,7 +51,7 @@ jobs:
|
||||
KITTYCAD_API_TOKEN: ${{secrets.KITTYCAD_API_TOKEN}}
|
||||
RUST_MIN_STACK: 10485760000
|
||||
- name: Upload to codecov.io
|
||||
uses: codecov/codecov-action@v4
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
token: ${{secrets.CODECOV_TOKEN}}
|
||||
fail_ci_if_error: true
|
||||
|
164
.github/workflows/e2e-tests.yml
vendored
@ -18,6 +18,7 @@ permissions:
|
||||
jobs:
|
||||
|
||||
check-rust-changes:
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
rust-changed: ${{ steps.filter.outputs.rust }}
|
||||
@ -33,20 +34,20 @@ jobs:
|
||||
rust:
|
||||
- 'src/wasm-lib/**'
|
||||
|
||||
browser:
|
||||
timeout-minutes: ${{ matrix.os == 'macos-14' && 60 || 50 }}
|
||||
name: playwright:browser:${{ matrix.os }} ${{ matrix.shardIndex }} ${{ matrix.shardTotal }}
|
||||
electron:
|
||||
if: github.event.pull_request.draft == false
|
||||
timeout-minutes: 60
|
||||
name: playwright:electron:${{ matrix.os }} ${{ matrix.shardIndex }} ${{ matrix.shardTotal }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest-8-cores, windows-latest-8-cores]
|
||||
# TODO: enable self-hosted-windows-8-cores once available
|
||||
os: [namespace-profile-ubuntu-8-cores, namespace-profile-macos-8-cores, windows-16-cores]
|
||||
shardIndex: [1, 2, 3, 4]
|
||||
shardTotal: [4]
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: check-rust-changes
|
||||
steps:
|
||||
- name: Tune GitHub-hosted runner network
|
||||
uses: smorimoto/tune-github-hosted-runner-network@v1
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
@ -101,7 +102,8 @@ jobs:
|
||||
echo "/opt/homebrew/opt/gnu-sed/libexec/gnubin" >> $GITHUB_PATH
|
||||
- name: Install vector
|
||||
shell: bash
|
||||
if: ${{ !startsWith(matrix.os, 'windows') }}
|
||||
# TODO: figure out what to do with this, it's failing
|
||||
if: false
|
||||
run: |
|
||||
curl --proto '=https' --tlsv1.2 -sSfL https://sh.vector.dev > /tmp/vector.sh
|
||||
chmod +x /tmp/vector.sh
|
||||
@ -123,13 +125,13 @@ jobs:
|
||||
if: steps.download-wasm.outcome == 'failure'
|
||||
shell: bash
|
||||
run: yarn build:wasm
|
||||
- name: build web
|
||||
run: yarn build:local
|
||||
- name: build electron
|
||||
shell: bash
|
||||
run: yarn tron:package
|
||||
- name: Run ubuntu/chrome snapshots
|
||||
shell: bash
|
||||
run: |
|
||||
yarn playwright test --project="Google Chrome" --config=playwright.ci.config.ts --retries="3" --update-snapshots --grep=@snapshot --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
|
||||
PLATFORM=web yarn playwright test --config=playwright.config.ts --retries="3" --update-snapshots --grep=@snapshot --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
|
||||
env:
|
||||
CI: true
|
||||
NODE_ENV: development
|
||||
@ -186,12 +188,12 @@ jobs:
|
||||
with:
|
||||
name: test-results-${{ matrix.os }}-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||
path: test-results/
|
||||
- name: Run playwright/chrome flow (with retries)
|
||||
- name: Run playwright/electron flow (with retries)
|
||||
id: retry
|
||||
if: ${{ !cancelled() && (success() || failure()) }}
|
||||
shell: bash
|
||||
run: |
|
||||
.github/ci-cd-scripts/playwright-browser-chrome.sh ${{matrix.shardIndex}} ${{matrix.shardTotal}} ${{matrix.os}}
|
||||
.github/ci-cd-scripts/playwright-electron.sh ${{matrix.shardIndex}} ${{matrix.shardTotal}} ${{matrix.os}}
|
||||
env:
|
||||
CI: true
|
||||
FAIL_ON_CONSOLE_ERRORS: true
|
||||
@ -199,11 +201,6 @@ jobs:
|
||||
VITE_KC_DEV_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
VITE_KC_SKIP_AUTH: true
|
||||
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
- name: send to axiom
|
||||
if: always()
|
||||
shell: bash
|
||||
run: |
|
||||
node playwrightProcess.mjs | tee /tmp/github-actions.log
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
@ -221,136 +218,3 @@ jobs:
|
||||
retention-days: 30
|
||||
overwrite: true
|
||||
|
||||
|
||||
electron:
|
||||
name: playwright:electron:${{matrix.os}}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest-8-cores, windows-latest-8-cores, macos-14-large]
|
||||
timeout-minutes: 60
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: check-rust-changes
|
||||
steps:
|
||||
- name: Tune GitHub-hosted runner network
|
||||
uses: smorimoto/tune-github-hosted-runner-network@v1
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
- uses: KittyCAD/action-install-cli@main
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
run: yarn
|
||||
- name: Cache Playwright Browsers
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/ms-playwright/
|
||||
key: ${{ runner.os }}-playwright-${{ hashFiles('yarn.lock') }}
|
||||
- name: Install Playwright Browsers
|
||||
shell: bash
|
||||
run: yarn playwright install chromium --with-deps
|
||||
- name: Download Wasm Cache
|
||||
id: download-wasm
|
||||
if: needs.check-rust-changes.outputs.rust-changed == 'false'
|
||||
uses: dawidd6/action-download-artifact@v7
|
||||
continue-on-error: true
|
||||
with:
|
||||
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||
name: wasm-bundle
|
||||
workflow: build-and-store-wasm.yml
|
||||
branch: main
|
||||
path: src/wasm-lib/pkg
|
||||
- name: copy wasm blob
|
||||
if: needs.check-rust-changes.outputs.rust-changed == 'false'
|
||||
shell: bash
|
||||
run: cp src/wasm-lib/pkg/wasm_lib_bg.wasm public
|
||||
continue-on-error: true
|
||||
- name: Setup Rust
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
- name: Cache Wasm (because rust diff)
|
||||
if: needs.check-rust-changes.outputs.rust-changed == 'true'
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: './src/wasm-lib'
|
||||
- name: OR Cache Wasm (because wasm cache failed)
|
||||
if: steps.download-wasm.outcome == 'failure'
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: './src/wasm-lib'
|
||||
- name: install good sed
|
||||
if: ${{ startsWith(matrix.os, 'macos') }}
|
||||
shell: bash
|
||||
run: |
|
||||
brew install gnu-sed
|
||||
echo "/opt/homebrew/opt/gnu-sed/libexec/gnubin" >> $GITHUB_PATH
|
||||
- name: Install vector
|
||||
if: ${{ startsWith(matrix.os, 'ubuntu') }}
|
||||
shell: bash
|
||||
run: |
|
||||
curl --proto '=https' --tlsv1.2 -sSfL https://sh.vector.dev > /tmp/vector.sh
|
||||
chmod +x /tmp/vector.sh
|
||||
/tmp/vector.sh -y -no-modify-path
|
||||
mkdir -p /tmp/vector
|
||||
cp .github/workflows/vector.toml /tmp/vector.toml
|
||||
sed -i "s#GITHUB_WORKFLOW#${GITHUB_WORKFLOW}#g" /tmp/vector.toml
|
||||
sed -i "s#GITHUB_REPOSITORY#${GITHUB_REPOSITORY}#g" /tmp/vector.toml
|
||||
sed -i "s#GITHUB_SHA#${GITHUB_SHA}#g" /tmp/vector.toml
|
||||
sed -i "s#GITHUB_REF_NAME#${GITHUB_REF_NAME}#g" /tmp/vector.toml
|
||||
sed -i "s#GH_ACTIONS_AXIOM_TOKEN#${{secrets.GH_ACTIONS_AXIOM_TOKEN}}#g" /tmp/vector.toml
|
||||
cat /tmp/vector.toml
|
||||
${HOME}/.vector/bin/vector --config /tmp/vector.toml &
|
||||
- name: Build Wasm (because rust diff)
|
||||
if: needs.check-rust-changes.outputs.rust-changed == 'true'
|
||||
shell: bash
|
||||
run: yarn build:wasm
|
||||
- name: OR Build Wasm (because wasm cache failed)
|
||||
if: steps.download-wasm.outcome == 'failure'
|
||||
shell: bash
|
||||
run: yarn build:wasm
|
||||
- name: build electron
|
||||
shell: bash
|
||||
run: yarn tron:package
|
||||
- uses: actions/download-artifact@v4
|
||||
if: ${{ !cancelled() && (success() || failure()) }}
|
||||
continue-on-error: true
|
||||
with:
|
||||
name: test-results-electron-${{ matrix.os }}-${{ github.sha }}
|
||||
path: test-results/
|
||||
- name: Run electron tests (with retries)
|
||||
id: retry
|
||||
if: ${{ !cancelled() && (success() || failure()) }}
|
||||
shell: bash
|
||||
run: |
|
||||
.github/ci-cd-scripts/playwright-electron.sh ${{ matrix.os }}
|
||||
env:
|
||||
CI: true
|
||||
FAIL_ON_CONSOLE_ERRORS: true
|
||||
NODE_ENV: development
|
||||
VITE_KC_DEV_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
VITE_KC_SKIP_AUTH: true
|
||||
IS_UBUNTU: ${{ startsWith(matrix.os, 'ubuntu') && 'true' || 'false' }}
|
||||
#DEBUG: 'pw:browser*'
|
||||
- name: send to axiom
|
||||
if: ${{ !cancelled() && (success() || failure()) && !startsWith(matrix.os, 'windows') }}
|
||||
shell: bash
|
||||
run: |
|
||||
node playwrightProcess.mjs | tee /tmp/github-actions.log
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: ${{ !cancelled() && (success() || failure()) }}
|
||||
with:
|
||||
name: test-results-electron-${{ matrix.os }}-${{ github.sha }}
|
||||
path: test-results/
|
||||
include-hidden-files: true
|
||||
retention-days: 30
|
||||
overwrite: true
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: ${{ !cancelled() && (success() || failure()) }}
|
||||
with:
|
||||
name: playwright-report-electron-${{ matrix.os }}-${{ github.sha }}
|
||||
path: playwright-report/
|
||||
include-hidden-files: true
|
||||
retention-days: 30
|
||||
overwrite: true
|
||||
|
17
README.md
@ -388,23 +388,6 @@ yarn test:unit:local
|
||||
|
||||
#### E2E Tests
|
||||
|
||||
**Playwright Browser**
|
||||
|
||||
These E2E tests run in a browser (without electron).
|
||||
There are tests that are skipped if they are ran in a windows OS or Linux OS. We can use playwright tags to implement test skipping.
|
||||
|
||||
Breaking down the command `yarn test:playwright:browser:chrome:windows`
|
||||
- The application is `playwright`
|
||||
- The runtime is a `browser`
|
||||
- The specific `browser` is `chrome`
|
||||
- The test should run in a `windows` environment. It will skip tests that are broken or flaky in the windows OS.
|
||||
|
||||
```
|
||||
yarn test:playwright:browser:chrome
|
||||
yarn test:playwright:browser:chrome:windows
|
||||
yarn test:playwright:browser:chrome:ubuntu
|
||||
```
|
||||
|
||||
**Playwright Electron**
|
||||
|
||||
These E2E tests run in electron. There are tests that are skipped if they are ran in a windows, linux, or macos environment. We can use playwright tags to implement test skipping.
|
||||
|
49
docs/kcl/atan2.md
Normal file
@ -30,6 +30,7 @@ layout: manual
|
||||
* [`assertLessThan`](kcl/assertLessThan)
|
||||
* [`assertLessThanOrEq`](kcl/assertLessThanOrEq)
|
||||
* [`atan`](kcl/atan)
|
||||
* [`atan2`](kcl/atan2)
|
||||
* [`bezierCurve`](kcl/bezierCurve)
|
||||
* [`ceil`](kcl/ceil)
|
||||
* [`chamfer`](kcl/chamfer)
|
||||
@ -100,8 +101,8 @@ layout: manual
|
||||
* [`sin`](kcl/sin)
|
||||
* [`sqrt`](kcl/sqrt)
|
||||
* [`startProfileAt`](kcl/startProfileAt)
|
||||
* [`startSketchAt`](kcl/startSketchAt)
|
||||
* [`startSketchOn`](kcl/startSketchOn)
|
||||
* [`sweep`](kcl/sweep)
|
||||
* [`tan`](kcl/tan)
|
||||
* [`tangentToEnd`](kcl/tangentToEnd)
|
||||
* [`tangentialArc`](kcl/tangentialArc)
|
||||
|
@ -35,7 +35,7 @@ The transform function returns a transform object. All properties of the object
|
||||
- `rotation.origin` (either "local" i.e. rotate around its own center, "global" i.e. rotate around the scene's center, or a 3D point, defaults to "local")
|
||||
|
||||
```js
|
||||
patternTransform(total_instances: u32, transform_function: FunctionParam, solid_set: SolidSet) -> [Solid]
|
||||
patternTransform(total_instances: integer, transform_function: FunctionParam, solid_set: SolidSet) -> [Solid]
|
||||
```
|
||||
|
||||
|
||||
@ -43,7 +43,7 @@ patternTransform(total_instances: u32, transform_function: FunctionParam, solid_
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `total_instances` | `u32` | | Yes |
|
||||
| `total_instances` | `integer` | | Yes |
|
||||
| `transform_function` | `FunctionParam` | | Yes |
|
||||
| `solid_set` | [`SolidSet`](/docs/kcl/types/SolidSet) | A solid or a group of solids. | Yes |
|
||||
|
||||
@ -95,7 +95,8 @@ fn cube(length, center) {
|
||||
p2 = [l + x, l + y]
|
||||
p3 = [l + x, -l + y]
|
||||
|
||||
return startSketchAt(p0)
|
||||
return startSketchOn('XY')
|
||||
|> startProfileAt(p0, %)
|
||||
|> lineTo(p1, %)
|
||||
|> lineTo(p2, %)
|
||||
|> lineTo(p3, %)
|
||||
@ -132,7 +133,8 @@ fn cube(length, center) {
|
||||
p2 = [l + x, l + y]
|
||||
p3 = [l + x, -l + y]
|
||||
|
||||
return startSketchAt(p0)
|
||||
return startSketchOn('XY')
|
||||
|> startProfileAt(p0, %)
|
||||
|> lineTo(p1, %)
|
||||
|> lineTo(p2, %)
|
||||
|> lineTo(p3, %)
|
||||
@ -195,7 +197,8 @@ fn transform(i) {
|
||||
{ rotation = { angle = 45 * i } }
|
||||
]
|
||||
}
|
||||
startSketchAt([0, 0])
|
||||
startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> polygon({
|
||||
radius = 10,
|
||||
numSides = 4,
|
||||
|
@ -9,7 +9,7 @@ Just like patternTransform, but works on 2D sketches not 3D solids.
|
||||
|
||||
|
||||
```js
|
||||
patternTransform2d(total_instances: u32, transform_function: FunctionParam, solid_set: SketchSet) -> [Sketch]
|
||||
patternTransform2d(total_instances: integer, transform_function: FunctionParam, solid_set: SketchSet) -> [Sketch]
|
||||
```
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@ patternTransform2d(total_instances: u32, transform_function: FunctionParam, soli
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `total_instances` | `u32` | | Yes |
|
||||
| `total_instances` | `integer` | | Yes |
|
||||
| `transform_function` | `FunctionParam` | | 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:
|
||||
fn sum(arr):
|
||||
let sumSoFar = 0
|
||||
sumSoFar = 0
|
||||
for i in arr:
|
||||
sumSoFar = add(sumSoFar, i)
|
||||
return sumSoFar */
|
||||
@ -79,10 +79,11 @@ fn decagon(radius) {
|
||||
stepAngle = 1 / 10 * tau()
|
||||
|
||||
// Start the decagon sketch at this point.
|
||||
startOfDecagonSketch = startSketchAt([cos(0) * radius, sin(0) * radius])
|
||||
startOfDecagonSketch = startSketchOn('XY')
|
||||
|> startProfileAt([cos(0) * radius, sin(0) * radius], %)
|
||||
|
||||
// Use a `reduce` to draw the remaining decagon sides.
|
||||
// For each number in the array 1..10, run the given function,
|
||||
// Use a `reduce` to draw the remaining decagon sides.
|
||||
// For each number in the array 1..10, run the given function,
|
||||
// which takes a partially-sketched decagon and adds one more edge to it.
|
||||
fullDecagon = reduce([1..10], startOfDecagonSketch, fn(i, partialDecagon) {
|
||||
// Draw one edge of the decagon.
|
||||
@ -96,14 +97,15 @@ fn decagon(radius) {
|
||||
|
||||
/* The `decagon` above is basically like this pseudo-code:
|
||||
fn decagon(radius):
|
||||
let stepAngle = (1/10) * tau()
|
||||
let startOfDecagonSketch = startSketchAt([(cos(0)*radius), (sin(0) * radius)])
|
||||
stepAngle = (1/10) * tau()
|
||||
plane = startSketchOn('XY')
|
||||
startOfDecagonSketch = startProfileAt([(cos(0)*radius), (sin(0) * radius)], plane)
|
||||
|
||||
// Here's the reduce part.
|
||||
let partialDecagon = startOfDecagonSketch
|
||||
partialDecagon = startOfDecagonSketch
|
||||
for i in [1..10]:
|
||||
let x = cos(stepAngle * i) * radius
|
||||
let y = sin(stepAngle * i) * radius
|
||||
x = cos(stepAngle * i) * radius
|
||||
y = sin(stepAngle * i) * radius
|
||||
partialDecagon = lineTo([x, y], partialDecagon)
|
||||
fullDecagon = partialDecagon // it's now full
|
||||
return fullDecagon */
|
||||
|
@ -28,7 +28,8 @@ segEnd(tag: TagIdentifier) -> [number]
|
||||
|
||||
```js
|
||||
w = 15
|
||||
cube = startSketchAt([0, 0])
|
||||
cube = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([w, 0], %, $line1)
|
||||
|> line([0, w], %, $line2)
|
||||
|> line([-w, 0], %, $line3)
|
||||
@ -37,7 +38,8 @@ cube = startSketchAt([0, 0])
|
||||
|> extrude(5, %)
|
||||
|
||||
fn cylinder(radius, tag) {
|
||||
return startSketchAt([0, 0])
|
||||
return startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> circle({
|
||||
radius = radius,
|
||||
center = segEnd(tag)
|
||||
|
@ -28,7 +28,8 @@ segStart(tag: TagIdentifier) -> [number]
|
||||
|
||||
```js
|
||||
w = 15
|
||||
cube = startSketchAt([0, 0])
|
||||
cube = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([w, 0], %, $line1)
|
||||
|> line([0, w], %, $line2)
|
||||
|> line([-w, 0], %, $line3)
|
||||
@ -37,7 +38,8 @@ cube = startSketchAt([0, 0])
|
||||
|> extrude(5, %)
|
||||
|
||||
fn cylinder(radius, tag) {
|
||||
return startSketchAt([0, 0])
|
||||
return startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> circle({
|
||||
radius = radius,
|
||||
center = segStart(tag)
|
||||
|
@ -4,6 +4,8 @@ excerpt: "Start a new 2-dimensional sketch at a given point on the 'XY' plane."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
**WARNING:** This function is deprecated.
|
||||
|
||||
Start a new 2-dimensional sketch at a given point on the 'XY' plane.
|
||||
|
||||
|
||||
|
8759
docs/kcl/std.json
55
docs/kcl/sweep.md
Normal file
@ -13,13 +13,18 @@ Data to draw an angled line.
|
||||
|
||||
An angle and length with explicitly named parameters
|
||||
|
||||
[`PolarCoordsData`](/docs/kcl/types/PolarCoordsData)
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `angle` |`number`| The angle of the line (in degrees). | No |
|
||||
| `length` |`number`| The length of the line. | No |
|
||||
|
||||
|
||||
----
|
||||
|
@ -12,5 +12,10 @@ KCL value for an optional parameter which was not given an argument. (remember,
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||
|
||||
|
||||
|
@ -329,6 +329,23 @@ Data for an imported geometry.
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `Module`| | No |
|
||||
| `value` |[`ModuleId`](/docs/kcl/types/ModuleId)| Any KCL value. | No |
|
||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|
16
docs/kcl/types/ModuleId.md
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
title: "ModuleId"
|
||||
excerpt: "Identifier of a source file. Uses a u32 to keep the size small."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
Identifier of a source file. Uses a u32 to keep the size small.
|
||||
|
||||
**Type:** `integer` (`uint32`)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
23
docs/kcl/types/SweepData.md
Normal file
@ -0,0 +1,23 @@
|
||||
---
|
||||
title: "SweepData"
|
||||
excerpt: "Data for a sweep."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
Data for a sweep.
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `path` |[`Sketch`](/docs/kcl/types/Sketch)| The path to sweep along. | No |
|
||||
| `sectional` |`boolean`| If true, the sweep will be broken up into sub-sweeps (extrusions, revolves, sweeps) based on the trajectory path components. | No |
|
||||
| `tolerance` |`number`| Tolerance for the sweep operation. | No |
|
||||
|
||||
|
@ -1,22 +1,11 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
|
||||
import { setupElectron, tearDown } from './test-utils'
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
import { test, expect } from './zoo-test'
|
||||
|
||||
test.describe('Electron app header tests', () => {
|
||||
test(
|
||||
'Open Command Palette button has correct shortcut',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async () => {},
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
async ({ page }, testInfo) => {
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
// No space before the shortcut since it checks textContent.
|
||||
let text
|
||||
@ -34,21 +23,14 @@ test.describe('Electron app header tests', () => {
|
||||
const commandsButton = page.getByRole('button', { name: 'Commands' })
|
||||
await expect(commandsButton).toBeVisible()
|
||||
await expect(commandsButton).toHaveText(text)
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'User settings has correct shortcut',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async () => {},
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
async ({ page }, testInfo) => {
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
// Open the user sidebar menu.
|
||||
await page.getByTestId('user-sidebar-toggle').click()
|
||||
@ -59,8 +41,6 @@ test.describe('Electron app header tests', () => {
|
||||
const userSettingsButton = page.getByTestId('user-settings')
|
||||
await expect(userSettingsButton).toBeVisible()
|
||||
await expect(userSettingsButton).toHaveText(text)
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
@ -1,29 +1,26 @@
|
||||
import { test, expect, Page } from '@playwright/test'
|
||||
import { test, expect, Page } from './zoo-test'
|
||||
import {
|
||||
getUtils,
|
||||
TEST_COLORS,
|
||||
setup,
|
||||
tearDown,
|
||||
commonPoints,
|
||||
PERSIST_MODELING_CONTEXT,
|
||||
} from './test-utils'
|
||||
|
||||
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||
await setup(context, page, testInfo)
|
||||
})
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
import { HomePageFixture } from './fixtures/homePageFixture'
|
||||
|
||||
test.setTimeout(120000)
|
||||
|
||||
async function doBasicSketch(page: Page, openPanes: string[]) {
|
||||
async function doBasicSketch(
|
||||
page: Page,
|
||||
homePage: HomePageFixture,
|
||||
openPanes: string[]
|
||||
) {
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
await page.waitForTimeout(1000)
|
||||
await u.openDebugPanel()
|
||||
|
||||
// If we have the code pane open, we should see the code.
|
||||
@ -148,13 +145,11 @@ async function doBasicSketch(page: Page, openPanes: string[]) {
|
||||
}
|
||||
|
||||
test.describe('Basic sketch', () => {
|
||||
test('code pane open at start', { tag: ['@skipWin'] }, async ({ page }) => {
|
||||
// Skip on windows it is being weird.
|
||||
test.skip(process.platform === 'win32', 'Skip on windows')
|
||||
await doBasicSketch(page, ['code'])
|
||||
test.fixme('code pane open at start', async ({ page, homePage }) => {
|
||||
await doBasicSketch(page, homePage, ['code'])
|
||||
})
|
||||
|
||||
test('code pane closed at start', async ({ page }) => {
|
||||
test.fixme('code pane closed at start', async ({ page, homePage }) => {
|
||||
// Load the app with the code panes
|
||||
await page.addInitScript(async (persistModelingContext) => {
|
||||
localStorage.setItem(
|
||||
@ -162,6 +157,6 @@ test.describe('Basic sketch', () => {
|
||||
JSON.stringify({ openPanes: [] })
|
||||
)
|
||||
}, PERSIST_MODELING_CONTEXT)
|
||||
await doBasicSketch(page, [])
|
||||
await doBasicSketch(page, homePage, [])
|
||||
})
|
||||
})
|
||||
|
@ -1,27 +1,21 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { getUtils, setup, tearDown } from './test-utils'
|
||||
import { test, expect, Page } from './zoo-test'
|
||||
import { HomePageFixture } from './fixtures/homePageFixture'
|
||||
import { getUtils } from './test-utils'
|
||||
import { EngineCommand } from 'lang/std/artifactGraph'
|
||||
import { uuidv4 } from 'lib/utils'
|
||||
|
||||
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||
await setup(context, page, testInfo)
|
||||
})
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
|
||||
test.describe('Can create sketches on all planes and their back sides', () => {
|
||||
const sketchOnPlaneAndBackSideTest = async (
|
||||
page: any,
|
||||
page: Page,
|
||||
homePage: HomePageFixture,
|
||||
plane: string,
|
||||
clickCoords: { x: number; y: number }
|
||||
) => {
|
||||
const u = await getUtils(page)
|
||||
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.openDebugPanel()
|
||||
|
||||
const coord =
|
||||
@ -83,32 +77,39 @@ test.describe('Can create sketches on all planes and their back sides', () => {
|
||||
await u.clearCommandLogs()
|
||||
await u.removeCurrentCode()
|
||||
}
|
||||
test('XY', async ({ page }) => {
|
||||
test('XY', async ({ page, homePage }) => {
|
||||
await sketchOnPlaneAndBackSideTest(
|
||||
page,
|
||||
homePage,
|
||||
'XY',
|
||||
{ x: 600, y: 388 } // red plane
|
||||
// { x: 600, y: 400 }, // red plane // clicks grid helper and that causes problems, should fix so that these coords work too.
|
||||
)
|
||||
})
|
||||
|
||||
test('YZ', async ({ page }) => {
|
||||
await sketchOnPlaneAndBackSideTest(page, 'YZ', { x: 700, y: 250 }) // green plane
|
||||
test('YZ', async ({ page, homePage }) => {
|
||||
await sketchOnPlaneAndBackSideTest(page, homePage, 'YZ', { x: 700, y: 250 }) // green plane
|
||||
})
|
||||
|
||||
test('XZ', async ({ page }) => {
|
||||
await sketchOnPlaneAndBackSideTest(page, '-XZ', { x: 700, y: 80 }) // blue plane
|
||||
test('XZ', async ({ page, homePage }) => {
|
||||
await sketchOnPlaneAndBackSideTest(page, homePage, '-XZ', { x: 700, y: 80 }) // blue plane
|
||||
})
|
||||
|
||||
test('-XY', async ({ page }) => {
|
||||
await sketchOnPlaneAndBackSideTest(page, '-XY', { x: 600, y: 118 }) // back of red plane
|
||||
test('-XY', async ({ page, homePage }) => {
|
||||
await sketchOnPlaneAndBackSideTest(page, homePage, '-XY', {
|
||||
x: 600,
|
||||
y: 118,
|
||||
}) // back of red plane
|
||||
})
|
||||
|
||||
test('-YZ', async ({ page }) => {
|
||||
await sketchOnPlaneAndBackSideTest(page, '-YZ', { x: 700, y: 219 }) // back of green plane
|
||||
test('-YZ', async ({ page, homePage }) => {
|
||||
await sketchOnPlaneAndBackSideTest(page, homePage, '-YZ', {
|
||||
x: 700,
|
||||
y: 219,
|
||||
}) // back of green plan
|
||||
})
|
||||
|
||||
test('-XZ', async ({ page }) => {
|
||||
await sketchOnPlaneAndBackSideTest(page, 'XZ', { x: 700, y: 427 }) // back of blue plane
|
||||
test('-XZ', async ({ page, homePage }) => {
|
||||
await sketchOnPlaneAndBackSideTest(page, homePage, 'XZ', { x: 700, y: 427 }) // back of blue plane
|
||||
})
|
||||
})
|
||||
|
@ -1,28 +1,15 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { test, expect } from './zoo-test'
|
||||
|
||||
import {
|
||||
getUtils,
|
||||
setup,
|
||||
setupElectron,
|
||||
tearDown,
|
||||
executorInputPath,
|
||||
} from './test-utils'
|
||||
import { getUtils, executorInputPath } from './test-utils'
|
||||
import { join } from 'path'
|
||||
import { bracket } from 'lib/exampleKcl'
|
||||
import { TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW } from './storageStates'
|
||||
import fsp from 'fs/promises'
|
||||
|
||||
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||
await setup(context, page, testInfo)
|
||||
})
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
|
||||
test.describe('Code pane and errors', () => {
|
||||
test('Typing KCL errors induces a badge on the code pane button', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
@ -31,18 +18,18 @@ test.describe('Code pane and errors', () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`// Extruded Triangle
|
||||
sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([10, 0], %)
|
||||
|> line([-5, 10], %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(5, sketch001)`
|
||||
sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([10, 0], %)
|
||||
|> line([-5, 10], %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(5, sketch001)`
|
||||
)
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// wait for execution done
|
||||
await u.openDebugPanel()
|
||||
@ -62,11 +49,11 @@ extrude001 = extrude(5, sketch001)`
|
||||
await expect(codePaneButtonHolder).toContainText('notification')
|
||||
})
|
||||
|
||||
test('Opening and closing the code pane will consistently show error diagnostics', async ({
|
||||
test.skip('Opening and closing the code pane will consistently show error diagnostics', async ({
|
||||
page,
|
||||
homePage,
|
||||
editor,
|
||||
}) => {
|
||||
await page.goto('http://localhost:3000')
|
||||
|
||||
const u = await getUtils(page)
|
||||
|
||||
// Load the app with the working starter code
|
||||
@ -74,8 +61,8 @@ extrude001 = extrude(5, sketch001)`
|
||||
localStorage.setItem('persistCode', code)
|
||||
}, bracket)
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 900 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1200, height: 900 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// wait for execution done
|
||||
await u.openDebugPanel()
|
||||
@ -91,8 +78,9 @@ extrude001 = extrude(5, sketch001)`
|
||||
await expect(codePaneButtonHolder).not.toContainText('notification')
|
||||
|
||||
// Delete a character to break the KCL
|
||||
await u.openKclCodePanel()
|
||||
await page.getByText('thickness, bracketLeg1Sketch)').click()
|
||||
await editor.openPane()
|
||||
await editor.scrollToText('thickness, bracketLeg1Sketch)')
|
||||
await page.getByText('extrude(thickness, bracketLeg1Sketch)').click()
|
||||
await page.keyboard.press('Backspace')
|
||||
|
||||
// Ensure that a badge appears on the button
|
||||
@ -116,7 +104,10 @@ extrude001 = extrude(5, sketch001)`
|
||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||
|
||||
// Open the code pane
|
||||
await u.openKclCodePanel()
|
||||
await editor.openPane()
|
||||
|
||||
// Go to our problematic code again (missing closing paren!)
|
||||
await editor.scrollToText('extrude(thickness, bracketLeg1Sketch')
|
||||
|
||||
// Ensure that a badge appears on the button
|
||||
await expect(codePaneButtonHolder).toContainText('notification')
|
||||
@ -129,59 +120,58 @@ extrude001 = extrude(5, sketch001)`
|
||||
await expect(page.locator('.cm-tooltip').first()).toBeVisible()
|
||||
})
|
||||
|
||||
test('When error is not in view you can click the badge to scroll to it', async ({
|
||||
page,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
test.fixme(
|
||||
'When error is not in view you can click the badge to scroll to it',
|
||||
async ({ page, homePage, context }) => {
|
||||
// Load the app with the working starter code
|
||||
await context.addInitScript((code) => {
|
||||
localStorage.setItem('persistCode', code)
|
||||
}, TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW)
|
||||
|
||||
// Load the app with the working starter code
|
||||
await page.addInitScript((code) => {
|
||||
localStorage.setItem('persistCode', code)
|
||||
}, TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW)
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
await page.waitForTimeout(1000)
|
||||
// Ensure badge is present
|
||||
const codePaneButtonHolder = page.locator('#code-button-holder')
|
||||
await expect(codePaneButtonHolder).toContainText('notification')
|
||||
|
||||
// Ensure badge is present
|
||||
const codePaneButtonHolder = page.locator('#code-button-holder')
|
||||
await expect(codePaneButtonHolder).toContainText('notification')
|
||||
// Ensure we have no errors in the gutter, since error out of view.
|
||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||
|
||||
// Ensure we have no errors in the gutter, since error out of view.
|
||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||
// Click the badge.
|
||||
const badge = page.locator('#code-badge')
|
||||
await expect(badge).toBeVisible()
|
||||
await badge.click()
|
||||
|
||||
// Click the badge.
|
||||
const badge = page.locator('#code-badge')
|
||||
await expect(badge).toBeVisible()
|
||||
await badge.click()
|
||||
// Ensure we have an error diagnostic.
|
||||
await expect(page.locator('.cm-lint-marker-error').first()).toBeVisible()
|
||||
|
||||
// Ensure we have an error diagnostic.
|
||||
await expect(page.locator('.cm-lint-marker-error').first()).toBeVisible()
|
||||
|
||||
// Hover over the error to see the error message
|
||||
await page.hover('.cm-lint-marker-error')
|
||||
await expect(
|
||||
page
|
||||
.getByText(
|
||||
'sketch profile must lie entirely on one side of the revolution axis'
|
||||
)
|
||||
.first()
|
||||
).toBeVisible()
|
||||
})
|
||||
// Hover over the error to see the error message
|
||||
await page.hover('.cm-lint-marker-error')
|
||||
await expect(
|
||||
page
|
||||
.getByText(
|
||||
'Modeling command failed: [ApiError { error_code: InternalEngine, message: "Solid3D revolve failed: sketch profile must lie entirely on one side of the revolution axis" }]'
|
||||
)
|
||||
.first()
|
||||
).toBeVisible()
|
||||
}
|
||||
)
|
||||
|
||||
test('When error is not in view WITH LINTS you can click the badge to scroll to it', async ({
|
||||
context,
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
// Load the app with the working starter code
|
||||
await page.addInitScript((code) => {
|
||||
await context.addInitScript((code) => {
|
||||
localStorage.setItem('persistCode', code)
|
||||
}, TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW)
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
@ -241,32 +231,29 @@ extrude001 = extrude(5, sketch001)`
|
||||
test(
|
||||
'Opening multiple panes persists when switching projects',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
async ({ context, page }, testInfo) => {
|
||||
// Setup multiple projects.
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
const routerTemplateDir = join(dir, 'router-template-slate')
|
||||
const bracketDir = join(dir, 'bracket')
|
||||
await Promise.all([
|
||||
fsp.mkdir(routerTemplateDir, { recursive: true }),
|
||||
fsp.mkdir(bracketDir, { recursive: true }),
|
||||
])
|
||||
await Promise.all([
|
||||
fsp.copyFile(
|
||||
executorInputPath('router-template-slate.kcl'),
|
||||
join(routerTemplateDir, 'main.kcl')
|
||||
),
|
||||
fsp.copyFile(
|
||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||
join(bracketDir, 'main.kcl')
|
||||
),
|
||||
])
|
||||
},
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
const routerTemplateDir = join(dir, 'router-template-slate')
|
||||
const bracketDir = join(dir, 'bracket')
|
||||
await Promise.all([
|
||||
fsp.mkdir(routerTemplateDir, { recursive: true }),
|
||||
fsp.mkdir(bracketDir, { recursive: true }),
|
||||
])
|
||||
await Promise.all([
|
||||
fsp.copyFile(
|
||||
executorInputPath('router-template-slate.kcl'),
|
||||
join(routerTemplateDir, 'main.kcl')
|
||||
),
|
||||
fsp.copyFile(
|
||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||
join(bracketDir, 'main.kcl')
|
||||
),
|
||||
])
|
||||
})
|
||||
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await test.step('Opening the bracket project should load', async () => {
|
||||
await expect(page.getByText('bracket')).toBeVisible()
|
||||
@ -309,30 +296,21 @@ test(
|
||||
await expect(page.locator('#variables-pane')).toBeVisible()
|
||||
await expect(page.locator('#logs-pane')).toBeVisible()
|
||||
})
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'external change of file contents are reflected in editor',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
async ({ context, page }, testInfo) => {
|
||||
const PROJECT_DIR_NAME = 'lee-was-here'
|
||||
const {
|
||||
electronApp,
|
||||
page,
|
||||
dir: projectsDir,
|
||||
} = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
const aProjectDir = join(dir, PROJECT_DIR_NAME)
|
||||
await fsp.mkdir(aProjectDir, { recursive: true })
|
||||
},
|
||||
const { dir: projectsDir } = await context.folderSetupFn(async (dir) => {
|
||||
const aProjectDir = join(dir, PROJECT_DIR_NAME)
|
||||
await fsp.mkdir(aProjectDir, { recursive: true })
|
||||
})
|
||||
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await test.step('Open the project', async () => {
|
||||
await expect(page.getByText(PROJECT_DIR_NAME)).toBeVisible()
|
||||
@ -351,7 +329,5 @@ test(
|
||||
)
|
||||
await u.editorTextMatches(content)
|
||||
})
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
@ -1,37 +1,30 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { test, expect } from './zoo-test'
|
||||
|
||||
import { getUtils, setup, tearDown } from './test-utils'
|
||||
import { getUtils } from './test-utils'
|
||||
import { KCL_DEFAULT_LENGTH } from 'lib/constants'
|
||||
|
||||
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||
await setup(context, page, testInfo)
|
||||
})
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
|
||||
test.describe('Command bar tests', () => {
|
||||
test('Extrude from command bar selects extrude line after', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, -10], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([0, 20], %)
|
||||
|> xLine(-20, %)
|
||||
|> close(%)
|
||||
`
|
||||
|> startProfileAt([-10, -10], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([0, 20], %)
|
||||
|> xLine(-20, %)
|
||||
|> close(%)
|
||||
`
|
||||
)
|
||||
})
|
||||
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
@ -52,24 +45,25 @@ test.describe('Command bar tests', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('Fillet from command bar', async ({ page }) => {
|
||||
// TODO: fix this test after the electron migration
|
||||
test.fixme('Fillet from command bar', async ({ page, homePage }) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-5, -5], %)
|
||||
|> line([0, 10], %)
|
||||
|> line([10, 0], %)
|
||||
|> line([0, -10], %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(-10, sketch001)`
|
||||
|> startProfileAt([-5, -5], %)
|
||||
|> line([0, 10], %)
|
||||
|> line([10, 0], %)
|
||||
|> line([0, -10], %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(-10, sketch001)`
|
||||
)
|
||||
})
|
||||
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
await u.closeDebugPanel()
|
||||
@ -93,10 +87,10 @@ extrude001 = extrude(-10, sketch001)`
|
||||
|
||||
test('Command bar can change a setting, and switch back and forth between arguments', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
const commandBarButton = page.getByRole('button', { name: 'Commands' })
|
||||
const cmdSearchBar = page.getByPlaceholder('Search commands')
|
||||
@ -153,7 +147,7 @@ extrude001 = extrude(-10, sketch001)`
|
||||
// Check that the visibility changed
|
||||
await expect(paneSelector).not.toBeVisible()
|
||||
|
||||
commandOptionInput = page.getByPlaceholder('off')
|
||||
commandOptionInput = page.locator('[id="option-input"]')
|
||||
|
||||
// Test case for https://github.com/KittyCAD/modeling-app/issues/2882
|
||||
await commandBarButton.click()
|
||||
@ -174,10 +168,10 @@ extrude001 = extrude(-10, sketch001)`
|
||||
|
||||
test('Command bar keybinding works from code editor and can change a setting', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Start Sketch' })
|
||||
@ -221,25 +215,25 @@ extrude001 = extrude(-10, sketch001)`
|
||||
await expect(page.locator('body')).not.toHaveClass(`body-bg dark`)
|
||||
})
|
||||
|
||||
test('Can extrude from the command bar', async ({ page }) => {
|
||||
test('Can extrude from the command bar', async ({ page, homePage }) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`distance = sqrt(20)
|
||||
sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([-6.95, 10.98], %)
|
||||
|> line([25.1, 0.41], %)
|
||||
|> line([0.73, -20.93], %)
|
||||
|> line([-23.44, 0.52], %)
|
||||
|> close(%)
|
||||
`
|
||||
sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([-6.95, 10.98], %)
|
||||
|> line([25.1, 0.41], %)
|
||||
|> line([0.73, -20.93], %)
|
||||
|> line([-23.44, 0.52], %)
|
||||
|> close(%)
|
||||
`
|
||||
)
|
||||
})
|
||||
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// Make sure the stream is up
|
||||
await u.openDebugPanel()
|
||||
@ -293,26 +287,19 @@ extrude001 = extrude(-10, sketch001)`
|
||||
await continueButton.click()
|
||||
await submitButton.click()
|
||||
|
||||
// Check that the code was updated
|
||||
await u.waitForCmdReceive('extrude')
|
||||
// Unfortunately this indentation seems to matter for the test
|
||||
await expect(page.locator('.cm-content')).toHaveText(
|
||||
`distance = sqrt(20)
|
||||
distance001 = ${KCL_DEFAULT_LENGTH}
|
||||
sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([-6.95, 10.98], %)
|
||||
|> line([25.1, 0.41], %)
|
||||
|> line([0.73, -20.93], %)
|
||||
|> line([-23.44, 0.52], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(distance001, sketch001)`.replace(/(\r\n|\n|\r)/gm, '') // remove newlines
|
||||
|
||||
await expect(page.locator('.cm-content')).toContainText(
|
||||
'extrude001 = extrude(distance001, sketch001)'
|
||||
)
|
||||
})
|
||||
|
||||
test('Can switch between sketch tools via command bar', async ({ page }) => {
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
test('Can switch between sketch tools via command bar', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
const sketchButton = page.getByRole('button', { name: 'Start Sketch' })
|
||||
const cmdBarButton = page.getByRole('button', { name: 'Commands' })
|
||||
|
@ -1,23 +1,16 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { getUtils, setup, tearDown } from './test-utils'
|
||||
import { test, expect } from './zoo-test'
|
||||
import { getUtils } from './test-utils'
|
||||
|
||||
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||
await setup(context, page, testInfo)
|
||||
})
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
test.describe('Copilot ghost text', () => {
|
||||
// eslint-disable-next-line jest/valid-title
|
||||
test.skip(true, 'Needs to get covered again')
|
||||
|
||||
test('completes code in empty file', async ({ page }) => {
|
||||
test('completes code in empty file', async ({ page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await u.codeLocator.click()
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
@ -52,12 +45,13 @@ test.describe('Copilot ghost text', () => {
|
||||
|
||||
test.skip('copilot disabled in sketch mode no select plane', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await u.codeLocator.click()
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
@ -101,12 +95,13 @@ test.describe('Copilot ghost text', () => {
|
||||
|
||||
test('copilot disabled in sketch mode after selecting plane', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await u.codeLocator.click()
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
@ -184,12 +179,12 @@ test.describe('Copilot ghost text', () => {
|
||||
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
||||
})
|
||||
|
||||
test('ArrowUp in code rejects the suggestion', async ({ page }) => {
|
||||
test('ArrowUp in code rejects the suggestion', async ({ page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await u.codeLocator.click()
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
@ -212,12 +207,15 @@ test.describe('Copilot ghost text', () => {
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
})
|
||||
|
||||
test('ArrowDown in code rejects the suggestion', async ({ page }) => {
|
||||
test('ArrowDown in code rejects the suggestion', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await u.codeLocator.click()
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
@ -240,12 +238,15 @@ test.describe('Copilot ghost text', () => {
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
})
|
||||
|
||||
test('ArrowLeft in code rejects the suggestion', async ({ page }) => {
|
||||
test('ArrowLeft in code rejects the suggestion', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await u.codeLocator.click()
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
@ -268,12 +269,15 @@ test.describe('Copilot ghost text', () => {
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
})
|
||||
|
||||
test('ArrowRight in code rejects the suggestion', async ({ page }) => {
|
||||
test('ArrowRight in code rejects the suggestion', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await u.codeLocator.click()
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
@ -296,12 +300,12 @@ test.describe('Copilot ghost text', () => {
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
})
|
||||
|
||||
test('Enter in code scoots it down', async ({ page }) => {
|
||||
test('Enter in code scoots it down', async ({ page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await u.codeLocator.click()
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
@ -326,12 +330,15 @@ test.describe('Copilot ghost text', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('Ctrl+shift+z in code rejects the suggestion', async ({ page }) => {
|
||||
test('Ctrl+shift+z in code rejects the suggestion', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await u.codeLocator.click()
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
@ -360,12 +367,13 @@ test.describe('Copilot ghost text', () => {
|
||||
|
||||
test('Ctrl+z in code rejects the suggestion and undos the last code', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await page.waitForTimeout(800)
|
||||
await u.codeLocator.click()
|
||||
@ -420,98 +428,107 @@ test.describe('Copilot ghost text', () => {
|
||||
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
||||
|
||||
// TODO when we make codemirror a widget, we can test this.
|
||||
//await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
})
|
||||
//await expect(page.locator('.cm-content')).toHaveText(``) })
|
||||
|
||||
test('delete in code rejects the suggestion', async ({ page }) => {
|
||||
const u = await getUtils(page)
|
||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
test('delete in code rejects the suggestion', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await u.codeLocator.click()
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
await u.codeLocator.click()
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
|
||||
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
||||
await page.waitForTimeout(500)
|
||||
await page.keyboard.press('Enter')
|
||||
await page.keyboard.press('Enter')
|
||||
await page.keyboard.press('Enter')
|
||||
await expect(page.locator('.cm-ghostText').first()).toBeVisible()
|
||||
await expect(page.locator('.cm-content')).toHaveText(
|
||||
`fn cube = (pos, scale) => { sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
|
||||
)
|
||||
await expect(page.locator('.cm-ghostText').first()).toHaveText(
|
||||
`fn cube = (pos, scale) => {`
|
||||
)
|
||||
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
||||
await page.waitForTimeout(500)
|
||||
await page.keyboard.press('Enter')
|
||||
await page.keyboard.press('Enter')
|
||||
await page.keyboard.press('Enter')
|
||||
await expect(page.locator('.cm-ghostText').first()).toBeVisible()
|
||||
await expect(page.locator('.cm-content')).toHaveText(
|
||||
`fn cube = (pos, scale) => { sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
|
||||
)
|
||||
await expect(page.locator('.cm-ghostText').first()).toHaveText(
|
||||
`fn cube = (pos, scale) => {`
|
||||
)
|
||||
|
||||
// Going elsewhere in the code should hide the ghost text.
|
||||
await page.keyboard.press('Delete')
|
||||
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
||||
// Going elsewhere in the code should hide the ghost text.
|
||||
await page.keyboard.press('Delete')
|
||||
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
||||
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
})
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
})
|
||||
|
||||
test('backspace in code rejects the suggestion', async ({ page }) => {
|
||||
const u = await getUtils(page)
|
||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
test('backspace in code rejects the suggestion', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await u.codeLocator.click()
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
await u.codeLocator.click()
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
|
||||
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
||||
await page.waitForTimeout(500)
|
||||
await page.keyboard.press('Enter')
|
||||
await page.keyboard.press('Enter')
|
||||
await page.keyboard.press('Enter')
|
||||
await expect(page.locator('.cm-ghostText').first()).toBeVisible()
|
||||
await expect(page.locator('.cm-content')).toHaveText(
|
||||
`fn cube = (pos, scale) => { sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
|
||||
)
|
||||
await expect(page.locator('.cm-ghostText').first()).toHaveText(
|
||||
`fn cube = (pos, scale) => {`
|
||||
)
|
||||
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
||||
await page.waitForTimeout(500)
|
||||
await page.keyboard.press('Enter')
|
||||
await page.keyboard.press('Enter')
|
||||
await page.keyboard.press('Enter')
|
||||
await expect(page.locator('.cm-ghostText').first()).toBeVisible()
|
||||
await expect(page.locator('.cm-content')).toHaveText(
|
||||
`fn cube = (pos, scale) => { sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
|
||||
)
|
||||
await expect(page.locator('.cm-ghostText').first()).toHaveText(
|
||||
`fn cube = (pos, scale) => {`
|
||||
)
|
||||
|
||||
// Going elsewhere in the code should hide the ghost text.
|
||||
await page.keyboard.press('Backspace')
|
||||
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
||||
// Going elsewhere in the code should hide the ghost text.
|
||||
await page.keyboard.press('Backspace')
|
||||
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
||||
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
})
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
})
|
||||
|
||||
test('focus outside code pane rejects the suggestion', async ({ page }) => {
|
||||
const u = await getUtils(page)
|
||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
test('focus outside code pane rejects the suggestion', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await u.codeLocator.click()
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
await u.codeLocator.click()
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
|
||||
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
||||
await page.waitForTimeout(500)
|
||||
await page.keyboard.press('Enter')
|
||||
await expect(page.locator('.cm-ghostText').first()).toBeVisible()
|
||||
await expect(page.locator('.cm-content')).toHaveText(
|
||||
`fn cube = (pos, scale) => { sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
|
||||
)
|
||||
await expect(page.locator('.cm-ghostText').first()).toHaveText(
|
||||
`fn cube = (pos, scale) => {`
|
||||
)
|
||||
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
||||
await page.waitForTimeout(500)
|
||||
await page.keyboard.press('Enter')
|
||||
await expect(page.locator('.cm-ghostText').first()).toBeVisible()
|
||||
await expect(page.locator('.cm-content')).toHaveText(
|
||||
`fn cube = (pos, scale) => { sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
|
||||
)
|
||||
await expect(page.locator('.cm-ghostText').first()).toHaveText(
|
||||
`fn cube = (pos, scale) => {`
|
||||
)
|
||||
|
||||
// Going outside the editor should hide the ghost text.
|
||||
await page.mouse.move(0, 0)
|
||||
await page
|
||||
.getByRole('button', { name: 'Start Sketch' })
|
||||
.waitFor({ state: 'visible' })
|
||||
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
||||
// Going outside the editor should hide the ghost text.
|
||||
await page.mouse.move(0, 0)
|
||||
await page
|
||||
.getByRole('button', { name: 'Start Sketch' })
|
||||
.waitFor({ state: 'visible' })
|
||||
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
||||
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -1,14 +1,6 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { test, expect } from './zoo-test'
|
||||
|
||||
import { getUtils, setup, tearDown } from './test-utils'
|
||||
|
||||
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||
await setup(context, page, testInfo)
|
||||
})
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
import { getUtils } from './test-utils'
|
||||
|
||||
function countNewlines(input: string): number {
|
||||
let count = 0
|
||||
@ -24,13 +16,14 @@ test.describe('Debug pane', () => {
|
||||
test('Artifact IDs in the artifact graph are stable across code edits', async ({
|
||||
page,
|
||||
context,
|
||||
homePage,
|
||||
}) => {
|
||||
const code = `sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([1, 1], %)
|
||||
`
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([1, 1], %)
|
||||
`
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
const tree = page.getByTestId('debug-feature-tree')
|
||||
const segment = tree.locator('li', {
|
||||
@ -39,7 +32,7 @@ test.describe('Debug pane', () => {
|
||||
})
|
||||
|
||||
await test.step('Test setup', async () => {
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.openKclCodePanel()
|
||||
await u.openDebugPanel()
|
||||
// Set the code in the code editor.
|
||||
|
@ -1,39 +1,31 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { join } from 'path'
|
||||
import { test, expect } from './zoo-test'
|
||||
import path from 'path'
|
||||
import {
|
||||
getUtils,
|
||||
setupElectron,
|
||||
tearDown,
|
||||
executorInputPath,
|
||||
getPlaywrightDownloadDir,
|
||||
} from './test-utils'
|
||||
import fsp from 'fs/promises'
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
|
||||
test(
|
||||
'export works on the first try',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
const bracketDir = join(dir, 'bracket')
|
||||
await Promise.all([fsp.mkdir(bracketDir, { recursive: true })])
|
||||
await Promise.all([
|
||||
fsp.copyFile(
|
||||
executorInputPath('router-template-slate.kcl'),
|
||||
join(bracketDir, 'other.kcl')
|
||||
),
|
||||
fsp.copyFile(
|
||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||
join(bracketDir, 'main.kcl')
|
||||
),
|
||||
])
|
||||
},
|
||||
async ({ page, context }, testInfo) => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
const bracketDir = path.join(dir, 'bracket')
|
||||
await Promise.all([fsp.mkdir(bracketDir, { recursive: true })])
|
||||
await Promise.all([
|
||||
fsp.copyFile(
|
||||
executorInputPath('router-template-slate.kcl'),
|
||||
path.join(bracketDir, 'other.kcl')
|
||||
),
|
||||
fsp.copyFile(
|
||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||
path.join(bracketDir, 'main.kcl')
|
||||
),
|
||||
])
|
||||
})
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
page.on('console', console.log)
|
||||
|
||||
@ -93,12 +85,16 @@ test(
|
||||
await expect(successToastMessage).toBeVisible()
|
||||
await expect(exportingToastMessage).not.toBeVisible()
|
||||
|
||||
const firstFileFullPath = path.resolve(
|
||||
getPlaywrightDownloadDir(page),
|
||||
exportFileName
|
||||
)
|
||||
await test.step('Check the export size', async () => {
|
||||
await expect
|
||||
.poll(
|
||||
async () => {
|
||||
try {
|
||||
const outputGltf = await fsp.readFile(exportFileName)
|
||||
const outputGltf = await fsp.readFile(firstFileFullPath)
|
||||
return outputGltf.byteLength
|
||||
} catch (e) {
|
||||
return 0
|
||||
@ -107,9 +103,6 @@ test(
|
||||
{ timeout: 15_000 }
|
||||
)
|
||||
.toBeGreaterThan(300_000)
|
||||
|
||||
// clean up exported file
|
||||
await fsp.rm(exportFileName)
|
||||
})
|
||||
})
|
||||
|
||||
@ -170,12 +163,16 @@ test(
|
||||
expect(exportingToastMessage).not.toBeVisible(),
|
||||
]))
|
||||
|
||||
const secondFileFullPath = path.resolve(
|
||||
getPlaywrightDownloadDir(page),
|
||||
exportFileName
|
||||
)
|
||||
await test.step('Check the export size', async () => {
|
||||
await expect
|
||||
.poll(
|
||||
async () => {
|
||||
try {
|
||||
const outputGltf = await fsp.readFile(exportFileName)
|
||||
const outputGltf = await fsp.readFile(secondFileFullPath)
|
||||
return outputGltf.byteLength
|
||||
} catch (e) {
|
||||
return 0
|
||||
@ -184,13 +181,7 @@ test(
|
||||
{ timeout: 15_000 }
|
||||
)
|
||||
.toBeGreaterThan(100_000)
|
||||
|
||||
// clean up exported file
|
||||
await fsp.rm(exportFileName)
|
||||
})
|
||||
await electronApp.close()
|
||||
})
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { test, expect } from './zoo-test'
|
||||
import fsp from 'fs/promises'
|
||||
import { uuidv4 } from 'lib/utils'
|
||||
import {
|
||||
@ -6,37 +6,27 @@ import {
|
||||
darkModePlaneColorXZ,
|
||||
executorInputPath,
|
||||
getUtils,
|
||||
setup,
|
||||
setupElectron,
|
||||
tearDown,
|
||||
} from './test-utils'
|
||||
|
||||
import { join } from 'path'
|
||||
|
||||
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||
await setup(context, page, testInfo)
|
||||
})
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
|
||||
test.describe('Editor tests', () => {
|
||||
test('can comment out code with ctrl+/', async ({ page }) => {
|
||||
test('can comment out code with ctrl+/', async ({ page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// check no error to begin with
|
||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||
|
||||
await u.codeLocator.click()
|
||||
await page.keyboard.type(`sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, -10], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([0, 20], %)
|
||||
|> line([-20, 0], %)
|
||||
|> close(%)`)
|
||||
|> startProfileAt([-10, -10], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([0, 20], %)
|
||||
|> line([-20, 0], %)
|
||||
|> close(%)`)
|
||||
|
||||
await page.keyboard.down('ControlOrMeta')
|
||||
await page.keyboard.press('/')
|
||||
@ -44,11 +34,11 @@ test.describe('Editor tests', () => {
|
||||
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, -10], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([0, 20], %)
|
||||
|> line([-20, 0], %)
|
||||
// |> close(%)`)
|
||||
|> startProfileAt([-10, -10], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([0, 20], %)
|
||||
|> line([-20, 0], %)
|
||||
// |> close(%)`)
|
||||
|
||||
// uncomment the code
|
||||
await page.keyboard.down('ControlOrMeta')
|
||||
@ -57,23 +47,22 @@ test.describe('Editor tests', () => {
|
||||
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, -10], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([0, 20], %)
|
||||
|> line([-20, 0], %)
|
||||
|> close(%)`)
|
||||
|> startProfileAt([-10, -10], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([0, 20], %)
|
||||
|> line([-20, 0], %)
|
||||
|> close(%)`)
|
||||
})
|
||||
|
||||
test('if you click the format button it formats your code', async ({
|
||||
test('ensure we use the cache, and do not re-execute', async ({
|
||||
homePage,
|
||||
page,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
|
||||
// check no error to begin with
|
||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await u.codeLocator.click()
|
||||
await page.keyboard.type(`sketch001 = startSketchOn('XY')
|
||||
@ -82,36 +71,141 @@ test.describe('Editor tests', () => {
|
||||
|> line([0, 20], %)
|
||||
|> line([-20, 0], %)
|
||||
|> close(%)`)
|
||||
await page.locator('#code-pane button:first-child').click()
|
||||
await page.locator('button:has-text("Format code")').click()
|
||||
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn('XY')
|
||||
// Ensure we execute the first time.
|
||||
await u.openDebugPanel()
|
||||
await expect(
|
||||
page.locator('[data-receive-command-type="scene_clear_all"]')
|
||||
).toHaveCount(1)
|
||||
await expect(
|
||||
page.locator('[data-message-type="execution-done"]')
|
||||
).toHaveCount(2)
|
||||
|
||||
// Add whitespace to the end of the code.
|
||||
await u.codeLocator.click()
|
||||
await page.keyboard.press('ArrowUp')
|
||||
await page.keyboard.press('ArrowUp')
|
||||
await page.keyboard.press('ArrowUp')
|
||||
await page.keyboard.press('ArrowUp')
|
||||
await page.keyboard.press('Home')
|
||||
await page.keyboard.type(' ')
|
||||
await page.keyboard.press('Enter')
|
||||
await page.keyboard.type(' ')
|
||||
|
||||
// Ensure we don't execute the second time.
|
||||
await u.openDebugPanel()
|
||||
// Make sure we didn't clear the scene.
|
||||
await expect(
|
||||
page.locator('[data-message-type="execution-done"]')
|
||||
).toHaveCount(3)
|
||||
await expect(
|
||||
page.locator('[data-receive-command-type="scene_clear_all"]')
|
||||
).toHaveCount(1)
|
||||
})
|
||||
|
||||
test('ensure we use the cache, and do not clear on append', async ({
|
||||
homePage,
|
||||
page,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await u.codeLocator.click()
|
||||
await page.keyboard.type(`sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, -10], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([0, 20], %)
|
||||
|> line([-20, 0], %)
|
||||
|> close(%)`)
|
||||
|
||||
// Ensure we execute the first time.
|
||||
await u.openDebugPanel()
|
||||
await expect(
|
||||
page.locator('[data-receive-command-type="scene_clear_all"]')
|
||||
).toHaveCount(1)
|
||||
await expect(
|
||||
page.locator('[data-message-type="execution-done"]')
|
||||
).toHaveCount(2)
|
||||
|
||||
// Add whitespace to the end of the code.
|
||||
await u.codeLocator.click()
|
||||
await page.keyboard.press('ArrowDown')
|
||||
await page.keyboard.press('ArrowDown')
|
||||
await page.keyboard.press('ArrowDown')
|
||||
await page.keyboard.press('ArrowDown')
|
||||
await page.keyboard.press('ArrowDown')
|
||||
await page.keyboard.press('ArrowDown')
|
||||
await page.keyboard.press('ArrowDown')
|
||||
await page.keyboard.press('ArrowDown')
|
||||
await page.keyboard.press('ArrowDown')
|
||||
await page.keyboard.press('End')
|
||||
await page.keyboard.press('Enter')
|
||||
await page.keyboard.press('Enter')
|
||||
await page.keyboard.type('const x = 1')
|
||||
await page.keyboard.press('Enter')
|
||||
|
||||
await u.openDebugPanel()
|
||||
await expect(
|
||||
page.locator('[data-message-type="execution-done"]')
|
||||
).toHaveCount(3)
|
||||
await expect(
|
||||
page.locator('[data-receive-command-type="scene_clear_all"]')
|
||||
).toHaveCount(1)
|
||||
})
|
||||
|
||||
test('if you click the format button it formats your code', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// check no error to begin with
|
||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||
|
||||
await u.codeLocator.click()
|
||||
await page.keyboard.type(`sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, -10], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([0, 20], %)
|
||||
|> line([-20, 0], %)
|
||||
|> close(%)`)
|
||||
await page.locator('#code-pane button:first-child').click()
|
||||
await page.locator('button:has-text("Format code")').click()
|
||||
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, -10], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([0, 20], %)
|
||||
|> line([-20, 0], %)
|
||||
|> close(%)`)
|
||||
})
|
||||
|
||||
test('if you click the format button it formats your code and executes so lints are still there', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// check no error to begin with
|
||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||
|
||||
await u.codeLocator.click()
|
||||
await page.keyboard.type(`sketch_001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, -10], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([0, 20], %)
|
||||
|> line([-20, 0], %)
|
||||
|> close(%)`)
|
||||
|> startProfileAt([-10, -10], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([0, 20], %)
|
||||
|> line([-20, 0], %)
|
||||
|> close(%)`)
|
||||
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
@ -135,11 +229,11 @@ test.describe('Editor tests', () => {
|
||||
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch_001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, -10], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([0, 20], %)
|
||||
|> line([-20, 0], %)
|
||||
|> close(%)`)
|
||||
|> startProfileAt([-10, -10], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([0, 20], %)
|
||||
|> line([-20, 0], %)
|
||||
|> close(%)`)
|
||||
|
||||
// error in guter
|
||||
await expect(page.locator('.cm-lint-marker-info').first()).toBeVisible()
|
||||
@ -151,29 +245,27 @@ test.describe('Editor tests', () => {
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
test('fold gutters work', async ({ page }) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
test('fold gutters work', async ({ page, homePage }) => {
|
||||
const fullCode = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, -10], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([0, 20], %)
|
||||
|> line([-20, 0], %)
|
||||
|> close(%)`
|
||||
|> startProfileAt([-10, -10], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([0, 20], %)
|
||||
|> line([-20, 0], %)
|
||||
|> close(%)`
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, -10], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([0, 20], %)
|
||||
|> line([-20, 0], %)
|
||||
|> close(%)`
|
||||
|> startProfileAt([-10, -10], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([0, 20], %)
|
||||
|> line([-20, 0], %)
|
||||
|> close(%)`
|
||||
)
|
||||
})
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// TODO: Jess needs to fix this but you have to mod the code to get them to show
|
||||
// up, its an annoying codemirror thing.
|
||||
@ -224,22 +316,25 @@ test.describe('Editor tests', () => {
|
||||
await expect(foldGutterFoldLine).not.toBeVisible()
|
||||
})
|
||||
|
||||
test('hover over functions shows function description', async ({ page }) => {
|
||||
test('hover over functions shows function description', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, -10], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([0, 20], %)
|
||||
|> line([-20, 0], %)
|
||||
|> close(%)`
|
||||
|> startProfileAt([-10, -10], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([0, 20], %)
|
||||
|> line([-20, 0], %)
|
||||
|> close(%)`
|
||||
)
|
||||
})
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// check no error to begin with
|
||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||
@ -268,23 +363,24 @@ test.describe('Editor tests', () => {
|
||||
|
||||
test('if you use the format keyboard binding it formats your code', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, -10], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([0, 20], %)
|
||||
|> line([-20, 0], %)
|
||||
|> close(%)`
|
||||
|> startProfileAt([-10, -10], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([0, 20], %)
|
||||
|> line([-20, 0], %)
|
||||
|> close(%)`
|
||||
)
|
||||
localStorage.setItem('disableAxis', 'true')
|
||||
})
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// check no error to begin with
|
||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||
@ -301,32 +397,33 @@ test.describe('Editor tests', () => {
|
||||
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, -10], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([0, 20], %)
|
||||
|> line([-20, 0], %)
|
||||
|> close(%)`)
|
||||
|> startProfileAt([-10, -10], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([0, 20], %)
|
||||
|> line([-20, 0], %)
|
||||
|> close(%)`)
|
||||
})
|
||||
|
||||
test('if you use the format keyboard binding it formats your code and executes so lints are shown', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`sketch_001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, -10], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([0, 20], %)
|
||||
|> line([-20, 0], %)
|
||||
|> close(%)`
|
||||
|> startProfileAt([-10, -10], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([0, 20], %)
|
||||
|> line([-20, 0], %)
|
||||
|> close(%)`
|
||||
)
|
||||
localStorage.setItem('disableAxis', 'true')
|
||||
})
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
@ -353,11 +450,11 @@ test.describe('Editor tests', () => {
|
||||
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch_001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, -10], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([0, 20], %)
|
||||
|> line([-20, 0], %)
|
||||
|> close(%)`)
|
||||
|> startProfileAt([-10, -10], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([0, 20], %)
|
||||
|> line([-20, 0], %)
|
||||
|> close(%)`)
|
||||
|
||||
// error in guter
|
||||
await expect(page.locator('.cm-lint-marker-info').first()).toBeVisible()
|
||||
@ -369,11 +466,14 @@ test.describe('Editor tests', () => {
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
test('if you write kcl with lint errors you get lints', async ({ page }) => {
|
||||
test('if you write kcl with lint errors you get lints', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// check no error to begin with
|
||||
await expect(page.locator('.cm-lint-marker-info')).not.toBeVisible()
|
||||
@ -409,23 +509,26 @@ test.describe('Editor tests', () => {
|
||||
await expect(page.locator('.cm-lint-marker-info')).not.toBeVisible()
|
||||
})
|
||||
|
||||
test('if you fixup kcl errors you clear lints', async ({ page }) => {
|
||||
test('if you fixup kcl errors you clear lints', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([3.29, 7.86], %)
|
||||
|> line([2.48, 2.44], %)
|
||||
|> line([2.66, 1.17], %)
|
||||
|> close(%)
|
||||
`
|
||||
|> startProfileAt([3.29, 7.86], %)
|
||||
|> line([2.48, 2.44], %)
|
||||
|> line([2.66, 1.17], %)
|
||||
|> close(%)
|
||||
`
|
||||
)
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// check no error to begin with
|
||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||
@ -447,22 +550,27 @@ test.describe('Editor tests', () => {
|
||||
).not.toBeVisible()
|
||||
})
|
||||
|
||||
test('if you write invalid kcl you get inlined errors', async ({ page }) => {
|
||||
test('if you write invalid kcl you get inlined errors', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// check no error to begin with
|
||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||
|
||||
/* add the following code to the editor ($ error is not a valid line)
|
||||
$ error
|
||||
topAng = 30
|
||||
bottomAng = 25
|
||||
*/
|
||||
/* add the following code to the editor (~ error is not a valid line)
|
||||
* the old check here used $ but this is for tags so it changed meaning.
|
||||
* hopefully ~ doesn't change meaning
|
||||
~ error
|
||||
const topAng = 30
|
||||
const bottomAng = 25
|
||||
*/
|
||||
await u.codeLocator.click()
|
||||
await page.keyboard.type('$ error')
|
||||
await page.keyboard.type('~ error')
|
||||
|
||||
// press arrows to clear autocomplete
|
||||
await page.keyboard.press('ArrowLeft')
|
||||
@ -474,17 +582,17 @@ test.describe('Editor tests', () => {
|
||||
await page.keyboard.type('bottomAng = 25')
|
||||
await page.keyboard.press('Enter')
|
||||
|
||||
// error in gutter
|
||||
// error in guter
|
||||
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
|
||||
|
||||
// error text on hover
|
||||
await page.hover('.cm-lint-marker-error')
|
||||
await expect(
|
||||
page.getByText('Tag names must not be empty').first()
|
||||
page.getByText("found unknown token '~'").first()
|
||||
).toBeVisible()
|
||||
|
||||
// select the line that's causing the error and delete it
|
||||
await page.getByText('$ error').click()
|
||||
await page.getByText('~ error').click()
|
||||
await page.keyboard.press('End')
|
||||
await page.keyboard.down('Shift')
|
||||
await page.keyboard.press('Home')
|
||||
@ -520,106 +628,108 @@ test.describe('Editor tests', () => {
|
||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||
})
|
||||
|
||||
// TODO currently multiple source ranges are not supported
|
||||
test.skip('error with 2 source ranges gets 2 diagnostics', async ({
|
||||
page,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`length = .750
|
||||
width = 0.500
|
||||
height = 0.500
|
||||
dia = 4
|
||||
test.fixme(
|
||||
'error with 2 source ranges gets 2 diagnostics',
|
||||
async ({ page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`length = .750
|
||||
width = 0.500
|
||||
height = 0.500
|
||||
dia = 4
|
||||
|
||||
fn squareHole = (l, w) => {
|
||||
squareHoleSketch = startSketchOn('XY')
|
||||
|> startProfileAt([-width / 2, -length / 2], %)
|
||||
|> lineTo([width / 2, -length / 2], %)
|
||||
|> lineTo([width / 2, length / 2], %)
|
||||
|> lineTo([-width / 2, length / 2], %)
|
||||
|> close(%)
|
||||
return squareHoleSketch
|
||||
}
|
||||
`
|
||||
)
|
||||
})
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
fn squareHole = (l, w) => {
|
||||
squareHoleSketch = startSketchOn('XY')
|
||||
|> startProfileAt([-width / 2, -length / 2], %)
|
||||
|> lineTo([width / 2, -length / 2], %)
|
||||
|> lineTo([width / 2, length / 2], %)
|
||||
|> lineTo([-width / 2, length / 2], %)
|
||||
|> close(%)
|
||||
return squareHoleSketch
|
||||
}
|
||||
`
|
||||
)
|
||||
})
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
await u.closeDebugPanel()
|
||||
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
await u.closeDebugPanel()
|
||||
// check no error to begin with
|
||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||
|
||||
// check no error to begin with
|
||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||
// Click on the bottom of the code editor to add a new line
|
||||
await u.codeLocator.click()
|
||||
await page.keyboard.press('ArrowDown')
|
||||
await page.keyboard.press('ArrowDown')
|
||||
await page.keyboard.press('ArrowDown')
|
||||
await page.keyboard.press('ArrowDown')
|
||||
await page.keyboard.press('ArrowDown')
|
||||
await page.keyboard.press('ArrowDown')
|
||||
await page.keyboard.press('ArrowDown')
|
||||
await page.keyboard.press('ArrowDown')
|
||||
await page.keyboard.press('ArrowDown')
|
||||
await page.keyboard.press('ArrowDown')
|
||||
await page.keyboard.press('ArrowDown')
|
||||
await page.keyboard.press('ArrowDown')
|
||||
await page.keyboard.press('ArrowDown')
|
||||
await page.keyboard.press('Enter')
|
||||
await page.keyboard.type(`extrusion = startSketchOn('XY')
|
||||
|> circle({ center: [0, 0], radius: dia/2 }, %)
|
||||
|> hole(squareHole(length, width, height), %)
|
||||
|> extrude(height, %)`)
|
||||
|
||||
// Click on the bottom of the code editor to add a new line
|
||||
await u.codeLocator.click()
|
||||
await page.keyboard.press('ArrowDown')
|
||||
await page.keyboard.press('ArrowDown')
|
||||
await page.keyboard.press('ArrowDown')
|
||||
await page.keyboard.press('ArrowDown')
|
||||
await page.keyboard.press('ArrowDown')
|
||||
await page.keyboard.press('ArrowDown')
|
||||
await page.keyboard.press('ArrowDown')
|
||||
await page.keyboard.press('ArrowDown')
|
||||
await page.keyboard.press('ArrowDown')
|
||||
await page.keyboard.press('ArrowDown')
|
||||
await page.keyboard.press('ArrowDown')
|
||||
await page.keyboard.press('ArrowDown')
|
||||
await page.keyboard.press('ArrowDown')
|
||||
await page.keyboard.press('Enter')
|
||||
await page.keyboard.type(`extrusion = startSketchOn('XY')
|
||||
|> circle({ center = [0, 0], radius = dia/2 }, %)
|
||||
|> hole(squareHole(length, width, height), %)
|
||||
|> extrude(height, %)`)
|
||||
// error in gutter
|
||||
await expect(page.locator('.cm-lint-marker-error').first()).toBeVisible()
|
||||
await page.hover('.cm-lint-marker-error:first-child')
|
||||
await expect(
|
||||
page.getByText('Expected 2 arguments, got 3').first()
|
||||
).toBeVisible()
|
||||
|
||||
// error in gutter
|
||||
await expect(page.locator('.cm-lint-marker-error').first()).toBeVisible()
|
||||
await page.hover('.cm-lint-marker-error:first-child')
|
||||
await expect(
|
||||
page.getByText('Expected 2 arguments, got 3').first()
|
||||
).toBeVisible()
|
||||
|
||||
// Make sure there are two diagnostics
|
||||
await expect(page.locator('.cm-lint-marker-error')).toHaveCount(2)
|
||||
})
|
||||
// Make sure there are two diagnostics
|
||||
await expect(page.locator('.cm-lint-marker-error')).toHaveCount(2)
|
||||
}
|
||||
)
|
||||
test('if your kcl gets an error from the engine it is inlined', async ({
|
||||
context,
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
await context.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`box = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([0, 10], %)
|
||||
|> line([10, 0], %)
|
||||
|> line([0, -10], %, $revolveAxis)
|
||||
|> close(%)
|
||||
|> extrude(10, %)
|
||||
|
||||
sketch001 = startSketchOn(box, revolveAxis)
|
||||
|> startProfileAt([5, 10], %)
|
||||
|> line([0, -10], %)
|
||||
|> line([2, 0], %)
|
||||
|> line([0, -10], %)
|
||||
|> close(%)
|
||||
|> revolve({
|
||||
axis = revolveAxis,
|
||||
angle = 90
|
||||
}, %)
|
||||
`
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([0, 10], %)
|
||||
|> line([10, 0], %)
|
||||
|> line([0, -10], %, $revolveAxis)
|
||||
|> close(%)
|
||||
|> extrude(10, %)
|
||||
|
||||
sketch001 = startSketchOn(box, revolveAxis)
|
||||
|> startProfileAt([5, 10], %)
|
||||
|> line([0, -10], %)
|
||||
|> line([2, 0], %)
|
||||
|> line([0, -10], %)
|
||||
|> close(%)
|
||||
|> revolve({
|
||||
axis: revolveAxis,
|
||||
angle: 90
|
||||
}, %)
|
||||
`
|
||||
)
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await page.goto('/')
|
||||
await u.waitForPageLoad()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
|
||||
|
||||
@ -630,12 +740,15 @@ test.describe('Editor tests', () => {
|
||||
await expect(page.getByText(searchText)).toBeVisible()
|
||||
})
|
||||
test.describe('Autocomplete works', () => {
|
||||
test('with enter/click to accept the completion', async ({ page }) => {
|
||||
test('with enter/click to accept the completion', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// tests clicking on an option, selection the first option
|
||||
// and arrowing down to an option
|
||||
@ -697,19 +810,19 @@ test.describe('Editor tests', () => {
|
||||
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([3.14, 12], %)
|
||||
|> xLine(5, %) // lin`)
|
||||
|> startProfileAt([3.14, 12], %)
|
||||
|> xLine(5, %) // lin`)
|
||||
|
||||
// expect there to be no KCL errors
|
||||
await expect(page.locator('.cm-lint-marker-error')).toHaveCount(0)
|
||||
})
|
||||
|
||||
test('with tab to accept the completion', async ({ page }) => {
|
||||
test('with tab to accept the completion', async ({ page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// this test might be brittle as we add and remove functions
|
||||
// but should also be easy to update.
|
||||
@ -771,26 +884,30 @@ test.describe('Editor tests', () => {
|
||||
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([3.14, 12], %)
|
||||
|> xLine(5, %) // lin`)
|
||||
|> startProfileAt([3.14, 12], %)
|
||||
|> xLine(5, %) // lin`)
|
||||
})
|
||||
})
|
||||
test('Can undo a click and point extrude with ctrl+z', async ({ page }) => {
|
||||
test('Can undo a click and point extrude with ctrl+z', async ({
|
||||
page,
|
||||
context,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
await context.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([4.61, -14.01], %)
|
||||
|> line([12.73, -0.09], %)
|
||||
|> tangentialArcTo([24.95, -5.38], %)
|
||||
|> close(%)`
|
||||
|> startProfileAt([4.61, -14.01], %)
|
||||
|> line([12.73, -0.09], %)
|
||||
|> tangentialArcTo([24.95, -5.38], %)
|
||||
|> close(%)`
|
||||
)
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Start Sketch' })
|
||||
).not.toBeDisabled()
|
||||
@ -843,29 +960,32 @@ test.describe('Editor tests', () => {
|
||||
await page.waitForTimeout(100)
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([4.61, -14.01], %)
|
||||
|> line([12.73, -0.09], %)
|
||||
|> tangentialArcTo([24.95, -5.38], %)
|
||||
|> close(%)`)
|
||||
|> startProfileAt([4.61, -14.01], %)
|
||||
|> line([12.73, -0.09], %)
|
||||
|> tangentialArcTo([24.95, -5.38], %)
|
||||
|> close(%)`)
|
||||
})
|
||||
|
||||
test('Can undo a sketch modification with ctrl+z', async ({ page }) => {
|
||||
test('Can undo a sketch modification with ctrl+z', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([4.61, -10.01], %)
|
||||
|> line([12.73, -0.09], %)
|
||||
|> tangentialArcTo([24.95, -0.38], %)
|
||||
|> close(%)
|
||||
|> extrude(5, %)`
|
||||
|> startProfileAt([4.61, -10.01], %)
|
||||
|> line([12.73, -0.09], %)
|
||||
|> tangentialArcTo([24.95, -0.38], %)
|
||||
|> close(%)
|
||||
|> extrude(5, %)`
|
||||
)
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Start Sketch' })
|
||||
).not.toBeDisabled()
|
||||
@ -892,7 +1012,7 @@ test.describe('Editor tests', () => {
|
||||
})
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
const startPX = [665, 397]
|
||||
const startPX = [1200 / 2, 500 / 2]
|
||||
|
||||
const dragPX = 40
|
||||
|
||||
@ -906,9 +1026,9 @@ test.describe('Editor tests', () => {
|
||||
|
||||
await expect(page.getByTestId('segment-overlay')).toHaveCount(2)
|
||||
|
||||
// drag startProfieAt handle
|
||||
// drag startProfileAt handle
|
||||
await page.dragAndDrop('#stream', '#stream', {
|
||||
sourcePosition: { x: startPX[0], y: startPX[1] },
|
||||
sourcePosition: { x: startPX[0] + 68, y: startPX[1] + 147 },
|
||||
targetPosition: { x: startPX[0] + dragPX, y: startPX[1] + dragPX },
|
||||
})
|
||||
await page.waitForTimeout(100)
|
||||
@ -946,12 +1066,12 @@ test.describe('Editor tests', () => {
|
||||
// expect the code to have changed
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([7.12, -12.68], %)
|
||||
|> line([15.39, -2.78], %)
|
||||
|> tangentialArcTo([27.6, -3.05], %)
|
||||
|> close(%)
|
||||
|> extrude(5, %)
|
||||
`)
|
||||
|> startProfileAt([2.71, -2.71], %)
|
||||
|> line([15.4, -2.78], %)
|
||||
|> tangentialArcTo([27.6, -3.05], %)
|
||||
|> close(%)
|
||||
|> extrude(5, %)
|
||||
`)
|
||||
|
||||
// Hit undo
|
||||
await page.keyboard.down('Control')
|
||||
@ -960,11 +1080,11 @@ test.describe('Editor tests', () => {
|
||||
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([7.12, -12.68], %)
|
||||
|> line([15.39, -2.78], %)
|
||||
|> tangentialArcTo([24.95, -0.38], %)
|
||||
|> close(%)
|
||||
|> extrude(5, %)`)
|
||||
|> startProfileAt([2.71, -2.71], %)
|
||||
|> line([15.4, -2.78], %)
|
||||
|> tangentialArcTo([24.95, -0.38], %)
|
||||
|> close(%)
|
||||
|> extrude(5, %)`)
|
||||
|
||||
// Hit undo again.
|
||||
await page.keyboard.down('Control')
|
||||
@ -973,12 +1093,12 @@ test.describe('Editor tests', () => {
|
||||
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([7.12, -12.68], %)
|
||||
|> line([12.73, -0.09], %)
|
||||
|> tangentialArcTo([24.95, -0.38], %)
|
||||
|> close(%)
|
||||
|> extrude(5, %)
|
||||
`)
|
||||
|> startProfileAt([2.71, -2.71], %)
|
||||
|> line([12.73, -0.09], %)
|
||||
|> tangentialArcTo([24.95, -0.38], %)
|
||||
|> close(%)
|
||||
|> extrude(5, %)
|
||||
`)
|
||||
|
||||
// Hit undo again.
|
||||
await page.keyboard.down('Control')
|
||||
@ -988,31 +1108,29 @@ test.describe('Editor tests', () => {
|
||||
await page.waitForTimeout(100)
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([4.61, -10.01], %)
|
||||
|> line([12.73, -0.09], %)
|
||||
|> tangentialArcTo([24.95, -0.38], %)
|
||||
|> close(%)
|
||||
|> extrude(5, %)`)
|
||||
|> startProfileAt([4.61, -10.01], %)
|
||||
|> line([12.73, -0.09], %)
|
||||
|> tangentialArcTo([24.95, -0.38], %)
|
||||
|> close(%)
|
||||
|> extrude(5, %)`)
|
||||
})
|
||||
|
||||
test.fixme(
|
||||
`Can use the import stdlib function on a local OBJ file`,
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
const bracketDir = join(dir, 'cube')
|
||||
await fsp.mkdir(bracketDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('cube.obj'),
|
||||
join(bracketDir, 'cube.obj')
|
||||
)
|
||||
await fsp.writeFile(join(bracketDir, 'main.kcl'), '')
|
||||
},
|
||||
async ({ page, context }, testInfo) => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
const bracketDir = join(dir, 'cube')
|
||||
await fsp.mkdir(bracketDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('cube.obj'),
|
||||
join(bracketDir, 'cube.obj')
|
||||
)
|
||||
await fsp.writeFile(join(bracketDir, 'main.kcl'), '')
|
||||
})
|
||||
|
||||
const viewportSize = { width: 1200, height: 500 }
|
||||
await page.setViewportSize(viewportSize)
|
||||
await page.setBodyDimensions(viewportSize)
|
||||
|
||||
// Locators and constants
|
||||
const u = await getUtils(page)
|
||||
@ -1070,8 +1188,6 @@ test.describe('Editor tests', () => {
|
||||
})
|
||||
.toBeGreaterThan(15)
|
||||
})
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
@ -29,7 +29,7 @@ export class EditorFixture {
|
||||
reConstruct = (page: Page) => {
|
||||
this.page = page
|
||||
|
||||
this.codeContent = page.locator('.cm-content')
|
||||
this.codeContent = page.locator('.cm-content[data-language="kcl"]')
|
||||
this.diagnosticsTooltip = page.locator('.cm-tooltip-lint')
|
||||
this.diagnosticsGutterIcon = page.locator('.cm-lint-marker-error')
|
||||
this.activeLine = this.page.locator('.cm-activeLine')
|
||||
@ -54,13 +54,13 @@ export class EditorFixture {
|
||||
}
|
||||
}
|
||||
if (!shouldNormalise) {
|
||||
const expectStart = expect(this.codeContent)
|
||||
const expectStart = expect.poll(() => this.codeContent.textContent())
|
||||
if (not) {
|
||||
const result = await expectStart.not.toContainText(code, { timeout })
|
||||
const result = await expectStart.not.toContain(code)
|
||||
await resetPane()
|
||||
return result
|
||||
}
|
||||
const result = await expectStart.toContainText(code, { timeout })
|
||||
const result = await expectStart.toContain(code)
|
||||
await resetPane()
|
||||
return result
|
||||
}
|
||||
@ -147,4 +147,28 @@ export class EditorFixture {
|
||||
openPane() {
|
||||
return openPane(this.page, this.paneButtonTestId)
|
||||
}
|
||||
scrollToText(text: string, placeCursor?: boolean) {
|
||||
return this.page.evaluate(
|
||||
(args: { text: string; placeCursor?: boolean }) => {
|
||||
// error TS2339: Property 'docView' does not exist on type 'EditorView'.
|
||||
// Except it does so :shrug:
|
||||
// @ts-ignore
|
||||
let index = window.editorManager._editorView?.docView.view.state.doc
|
||||
.toString()
|
||||
.indexOf(args.text)
|
||||
window.editorManager._editorView?.focus()
|
||||
window.editorManager._editorView?.dispatch({
|
||||
selection: window.EditorSelection.create([
|
||||
window.EditorSelection.cursor(index),
|
||||
]),
|
||||
effects: [
|
||||
window.EditorView.scrollIntoView(
|
||||
window.EditorSelection.range(index, index + 1)
|
||||
),
|
||||
],
|
||||
})
|
||||
},
|
||||
{ text, placeCursor }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
import type {
|
||||
BrowserContext,
|
||||
ElectronApplication,
|
||||
Page,
|
||||
TestInfo,
|
||||
Page,
|
||||
} from '@playwright/test'
|
||||
import { test as base } from '@playwright/test'
|
||||
import { getUtils, setup, setupElectron, tearDown } from '../test-utils'
|
||||
|
||||
import { getUtils, setup, setupElectron } from '../test-utils'
|
||||
import fsp from 'fs/promises'
|
||||
import { join } from 'path'
|
||||
import { CmdBarFixture } from './cmdBarFixture'
|
||||
@ -20,11 +20,13 @@ export class AuthenticatedApp {
|
||||
public readonly page: Page
|
||||
public readonly context: BrowserContext
|
||||
public readonly testInfo: TestInfo
|
||||
public readonly viewPortSize = { width: 1000, height: 500 }
|
||||
public readonly viewPortSize = { width: 1200, height: 500 }
|
||||
public electronApp: undefined | ElectronApplication
|
||||
public dir: string = ''
|
||||
|
||||
constructor(context: BrowserContext, page: Page, testInfo: TestInfo) {
|
||||
this.page = page
|
||||
this.context = context
|
||||
this.page = page
|
||||
this.testInfo = testInfo
|
||||
}
|
||||
|
||||
@ -49,9 +51,7 @@ export class AuthenticatedApp {
|
||||
}
|
||||
}
|
||||
|
||||
interface Fixtures {
|
||||
app: AuthenticatedApp
|
||||
tronApp: AuthenticatedTronApp
|
||||
export interface Fixtures {
|
||||
cmdBar: CmdBarFixture
|
||||
editor: EditorFixture
|
||||
toolbar: ToolbarFixture
|
||||
@ -61,9 +61,11 @@ interface Fixtures {
|
||||
export class AuthenticatedTronApp {
|
||||
public readonly _page: Page
|
||||
public page: Page
|
||||
public readonly context: BrowserContext
|
||||
public context: BrowserContext
|
||||
public readonly testInfo: TestInfo
|
||||
public electronApp?: ElectronApplication
|
||||
public electronApp: ElectronApplication | undefined
|
||||
public readonly viewPortSize = { width: 1200, height: 500 }
|
||||
public dir: string = ''
|
||||
|
||||
constructor(context: BrowserContext, page: Page, testInfo: TestInfo) {
|
||||
this._page = page
|
||||
@ -79,15 +81,22 @@ export class AuthenticatedTronApp {
|
||||
appSettings?: Partial<SaveSettingsPayload>
|
||||
} = { fixtures: {} }
|
||||
) {
|
||||
const { electronApp, page } = await setupElectron({
|
||||
const { electronApp, page, context, dir } = await setupElectron({
|
||||
testInfo: this.testInfo,
|
||||
folderSetupFn: arg.folderSetupFn,
|
||||
cleanProjectDir: arg.cleanProjectDir,
|
||||
appSettings: arg.appSettings,
|
||||
})
|
||||
this.page = page
|
||||
this.context = context
|
||||
this.electronApp = electronApp
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
this.dir = dir
|
||||
|
||||
// Easier to access throughout utils
|
||||
this.page.dir = dir
|
||||
|
||||
// Setup localStorage, addCookies, reload
|
||||
await setup(this.context, this.page, this.testInfo)
|
||||
|
||||
for (const key of unsafeTypedKeys(arg.fixtures)) {
|
||||
const fixture = arg.fixtures[key]
|
||||
@ -110,32 +119,20 @@ export class AuthenticatedTronApp {
|
||||
})
|
||||
}
|
||||
|
||||
export const test = base.extend<Fixtures>({
|
||||
app: async ({ page, context }, use, testInfo) => {
|
||||
await use(new AuthenticatedApp(context, page, testInfo))
|
||||
},
|
||||
tronApp: async ({ page, context }, use, testInfo) => {
|
||||
await use(new AuthenticatedTronApp(context, page, testInfo))
|
||||
},
|
||||
cmdBar: async ({ page }, use) => {
|
||||
export const fixtures = {
|
||||
cmdBar: async ({ page }: { page: Page }, use: any) => {
|
||||
await use(new CmdBarFixture(page))
|
||||
},
|
||||
editor: async ({ page }, use) => {
|
||||
editor: async ({ page }: { page: Page }, use: any) => {
|
||||
await use(new EditorFixture(page))
|
||||
},
|
||||
toolbar: async ({ page }, use) => {
|
||||
toolbar: async ({ page }: { page: Page }, use: any) => {
|
||||
await use(new ToolbarFixture(page))
|
||||
},
|
||||
scene: async ({ page }, use) => {
|
||||
scene: async ({ page }: { page: Page }, use: any) => {
|
||||
await use(new SceneFixture(page))
|
||||
},
|
||||
homePage: async ({ page }, use) => {
|
||||
homePage: async ({ page }: { page: Page }, use: any) => {
|
||||
await use(new HomePageFixture(page))
|
||||
},
|
||||
})
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
|
||||
export { expect } from '@playwright/test'
|
||||
}
|
||||
|
@ -14,10 +14,14 @@ interface HomePageState {
|
||||
export class HomePageFixture {
|
||||
public page: Page
|
||||
|
||||
projectSection!: Locator
|
||||
projectCard!: Locator
|
||||
projectCardTitle!: Locator
|
||||
projectCardFile!: Locator
|
||||
projectCardFolder!: Locator
|
||||
projectButtonNew!: Locator
|
||||
projectButtonContinue!: Locator
|
||||
projectTextName!: Locator
|
||||
sortByDateBtn!: Locator
|
||||
sortByNameBtn!: Locator
|
||||
|
||||
@ -28,11 +32,19 @@ export class HomePageFixture {
|
||||
reConstruct = (page: Page) => {
|
||||
this.page = page
|
||||
|
||||
this.projectSection = this.page.getByTestId('home-section')
|
||||
|
||||
this.projectCard = this.page.getByTestId('project-link')
|
||||
this.projectCardTitle = this.page.getByTestId('project-title')
|
||||
this.projectCardFile = this.page.getByTestId('project-file-count')
|
||||
this.projectCardFolder = this.page.getByTestId('project-folder-count')
|
||||
|
||||
this.projectButtonNew = this.page.getByTestId('home-new-file')
|
||||
this.projectTextName = this.page.getByTestId('cmd-bar-arg-value')
|
||||
this.projectButtonContinue = this.page.getByRole('button', {
|
||||
name: 'Continue',
|
||||
})
|
||||
|
||||
this.sortByDateBtn = this.page.getByTestId('home-sort-by-modified')
|
||||
this.sortByNameBtn = this.page.getByTestId('home-sort-by-name')
|
||||
}
|
||||
@ -91,10 +103,25 @@ export class HomePageFixture {
|
||||
.toEqual(expectedState)
|
||||
}
|
||||
|
||||
createAndGoToProject = async (projectTitle: string) => {
|
||||
await expect(this.projectSection).not.toHaveText('Loading your Projects...')
|
||||
await this.projectButtonNew.click()
|
||||
await this.projectTextName.click()
|
||||
await this.projectTextName.fill(projectTitle)
|
||||
await this.projectButtonContinue.click()
|
||||
}
|
||||
|
||||
openProject = async (projectTitle: string) => {
|
||||
const projectCard = this.projectCard.locator(
|
||||
this.page.getByText(projectTitle)
|
||||
)
|
||||
await projectCard.click()
|
||||
}
|
||||
|
||||
goToModelingScene = async (name: string = 'testDefault') => {
|
||||
// On web this is a no-op. There is no project view.
|
||||
if (process.env.PLATFORM === 'web') return
|
||||
|
||||
await this.createAndGoToProject(name)
|
||||
}
|
||||
}
|
||||
|
@ -53,8 +53,9 @@ export class SceneFixture {
|
||||
|
||||
expectState = async (expected: SceneSerialised) => {
|
||||
return expect
|
||||
.poll(() => this._serialiseScene(), {
|
||||
message: `Expected scene state to match`,
|
||||
.poll(async () => await this._serialiseScene(), {
|
||||
intervals: [1_000, 2_000, 10_000],
|
||||
timeout: 60000,
|
||||
})
|
||||
.toEqual(expected)
|
||||
}
|
||||
@ -187,7 +188,10 @@ export class SceneFixture {
|
||||
type: 'default_camera_get_settings',
|
||||
},
|
||||
})
|
||||
await this.waitForExecutionDone()
|
||||
await this.page
|
||||
.locator(`[data-receive-command-type="default_camera_get_settings"]`)
|
||||
.first()
|
||||
.waitFor()
|
||||
const position = await Promise.all([
|
||||
this.page.getByTestId('cam-x-position').inputValue().then(Number),
|
||||
this.page.getByTestId('cam-y-position').inputValue().then(Number),
|
||||
@ -214,23 +218,7 @@ export class SceneFixture {
|
||||
coords: { x: number; y: number },
|
||||
diff: number
|
||||
) => {
|
||||
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 }
|
||||
)
|
||||
})
|
||||
await expectPixelColor(this.page, colour, coords, diff)
|
||||
}
|
||||
|
||||
get gizmo() {
|
||||
@ -238,6 +226,7 @@ export class SceneFixture {
|
||||
}
|
||||
|
||||
async clickGizmoMenuItem(name: string) {
|
||||
await this.gizmo.hover()
|
||||
await this.gizmo.click({ button: 'right' })
|
||||
const buttonToTest = this.page.getByRole('button', {
|
||||
name: name,
|
||||
@ -246,3 +235,28 @@ export class SceneFixture {
|
||||
await buttonToTest.click()
|
||||
}
|
||||
}
|
||||
|
||||
export async function expectPixelColor(
|
||||
page: Page,
|
||||
colour: [number, number, number],
|
||||
coords: { x: number; y: number },
|
||||
diff: number
|
||||
) {
|
||||
let finalValue = colour
|
||||
await expect
|
||||
.poll(async () => {
|
||||
const pixel = (await getPixelRGBs(page)(coords, 1))[0]
|
||||
if (!pixel) return null
|
||||
finalValue = pixel
|
||||
return pixel.every(
|
||||
(channel, index) => Math.abs(channel - colour[index]) < diff
|
||||
)
|
||||
})
|
||||
.toBeTruthy()
|
||||
.catch((cause) => {
|
||||
throw new Error(
|
||||
`ExpectPixelColor: expecting ${colour} got ${finalValue}`,
|
||||
{ cause }
|
||||
)
|
||||
})
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import type { Page, Locator } from '@playwright/test'
|
||||
import { expect } from './fixtureSetup'
|
||||
import { expect } from '../zoo-test'
|
||||
import { doAndWaitForImageDiff } from '../test-utils'
|
||||
|
||||
export class ToolbarFixture {
|
||||
|
@ -1,29 +1,22 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { setupElectron, tearDown, executorInputPath } from './test-utils'
|
||||
import { test, expect } from './zoo-test'
|
||||
import { executorInputPath } from './test-utils'
|
||||
import { join } from 'path'
|
||||
import fsp from 'fs/promises'
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
|
||||
test(
|
||||
'When machine-api server not found butt is disabled and shows the reason',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
const bracketDir = join(dir, 'bracket')
|
||||
await fsp.mkdir(bracketDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||
join(bracketDir, 'main.kcl')
|
||||
)
|
||||
},
|
||||
async ({ context, page }, testInfo) => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
const bracketDir = join(dir, 'bracket')
|
||||
await fsp.mkdir(bracketDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||
join(bracketDir, 'main.kcl')
|
||||
)
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await expect(page.getByText('bracket')).toBeVisible()
|
||||
|
||||
@ -47,28 +40,23 @@ test(
|
||||
// that the machine-api server is not found
|
||||
await makeButton.hover()
|
||||
await expect(page.getByText(notFoundText).first()).toBeVisible()
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'When machine-api server not found home screen & project status shows the reason',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
const bracketDir = join(dir, 'bracket')
|
||||
await fsp.mkdir(bracketDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||
join(bracketDir, 'main.kcl')
|
||||
)
|
||||
},
|
||||
async ({ context, page }, testInfo) => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
const bracketDir = join(dir, 'bracket')
|
||||
await fsp.mkdir(bracketDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||
join(bracketDir, 'main.kcl')
|
||||
)
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
const notFoundText = 'Machine API server was not discovered'
|
||||
|
||||
@ -91,7 +79,5 @@ test(
|
||||
|
||||
await networkMachineToggle.hover()
|
||||
await expect(page.getByText(notFoundText).nth(1)).toBeVisible()
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
12
e2e/playwright/null.spec.ts
Normal file
@ -0,0 +1,12 @@
|
||||
// These tests are meant to simply test starting and stopping the electron
|
||||
// application, check it can make it to the project pane, and nothing more.
|
||||
// It also tests our test wrappers are working.
|
||||
// Additionally this serves as a nice minimal example.
|
||||
|
||||
import { test, expect } from './zoo-test'
|
||||
|
||||
test.describe('Open the application', () => {
|
||||
test('see the project view', async ({ page, context }) => {
|
||||
await expect(page.getByTestId('home-section')).toBeVisible()
|
||||
})
|
||||
})
|
@ -1,79 +1,78 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { test, expect } from './zoo-test'
|
||||
import { join } from 'path'
|
||||
import fsp from 'fs/promises'
|
||||
import {
|
||||
getUtils,
|
||||
setup,
|
||||
setupElectron,
|
||||
tearDown,
|
||||
executorInputPath,
|
||||
createProject,
|
||||
} from './test-utils'
|
||||
import { getUtils, executorInputPath, createProject } from './test-utils'
|
||||
import { bracket } from 'lib/exampleKcl'
|
||||
import { onboardingPaths } from 'routes/Onboarding/paths'
|
||||
import {
|
||||
TEST_SETTINGS_KEY,
|
||||
TEST_SETTINGS_ONBOARDING_START,
|
||||
TEST_SETTINGS_ONBOARDING_EXPORT,
|
||||
TEST_SETTINGS_ONBOARDING_PARAMETRIC_MODELING,
|
||||
TEST_SETTINGS_ONBOARDING_USER_MENU,
|
||||
} from './storageStates'
|
||||
import * as TOML from '@iarna/toml'
|
||||
import { expectPixelColor } from './fixtures/sceneFixture'
|
||||
|
||||
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||
if (testInfo.tags.includes('@electron')) {
|
||||
return
|
||||
}
|
||||
await setup(context, page)
|
||||
})
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
// Because onboarding relies on an app setting we need to set it as incompletel
|
||||
// for all these tests.
|
||||
|
||||
test.describe('Onboarding tests', () => {
|
||||
test('Onboarding code is shown in the editor', async ({ page }) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
// Override beforeEach test setup
|
||||
await page.addInitScript(
|
||||
async ({ settingsKey }) => {
|
||||
// Give no initial code, so that the onboarding start is shown immediately
|
||||
localStorage.removeItem('persistCode')
|
||||
localStorage.removeItem(settingsKey)
|
||||
test(
|
||||
'Onboarding code is shown in the editor',
|
||||
{
|
||||
appSettings: {
|
||||
app: {
|
||||
onboardingStatus: 'incomplete',
|
||||
},
|
||||
},
|
||||
{ settingsKey: TEST_SETTINGS_KEY }
|
||||
)
|
||||
cleanProjectDir: true,
|
||||
},
|
||||
async ({ context, page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
// Test that the onboarding pane loaded
|
||||
await expect(
|
||||
page.getByText('Welcome to Modeling App! This')
|
||||
).toBeVisible()
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
// Test that the onboarding pane loaded
|
||||
await expect(
|
||||
page.getByText('Welcome to Modeling App! This')
|
||||
).toBeVisible()
|
||||
|
||||
// Test that the onboarding pane loaded
|
||||
await expect(page.getByText('Welcome to Modeling App! This')).toBeVisible()
|
||||
// *and* that the code is shown in the editor
|
||||
await expect(page.locator('.cm-content')).toContainText(
|
||||
'// Shelf Bracket'
|
||||
)
|
||||
|
||||
// *and* that the code is shown in the editor
|
||||
await expect(page.locator('.cm-content')).toContainText('// Shelf Bracket')
|
||||
})
|
||||
// Make sure the model loaded
|
||||
const XYPlanePoint = { x: 774, y: 116 } as const
|
||||
const modelColor: [number, number, number] = [45, 45, 45]
|
||||
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y)
|
||||
expect(await u.getGreatestPixDiff(XYPlanePoint, modelColor)).toBeLessThan(
|
||||
8
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'Desktop: fresh onboarding executes and loads',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName: _ }, testInfo) => {
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
appSettings: {
|
||||
app: {
|
||||
onboardingStatus: 'incomplete',
|
||||
},
|
||||
{
|
||||
tag: '@electron',
|
||||
appSettings: {
|
||||
app: {
|
||||
onboardingStatus: 'incomplete',
|
||||
},
|
||||
cleanProjectDir: true,
|
||||
})
|
||||
|
||||
},
|
||||
cleanProjectDir: true,
|
||||
},
|
||||
async ({ page, homePage }, testInfo) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
const viewportSize = { width: 1200, height: 500 }
|
||||
await page.setViewportSize(viewportSize)
|
||||
await page.setBodyDimensions(viewportSize)
|
||||
|
||||
await test.step(`Create a project and open to the onboarding`, async () => {
|
||||
await createProject({ name: 'project-link', page })
|
||||
@ -92,321 +91,370 @@ test.describe('Onboarding tests', () => {
|
||||
await expect(page.locator('.cm-content')).toContainText(
|
||||
'// Shelf Bracket'
|
||||
)
|
||||
})
|
||||
|
||||
await electronApp.close()
|
||||
// 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)
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
test('Code resets after confirmation', async ({ page }) => {
|
||||
const initialCode = `sketch001 = startSketchOn('XZ')`
|
||||
|
||||
// Load the page up with some code so we see the confirmation warning
|
||||
// when we go to replay onboarding
|
||||
await page.addInitScript((code) => {
|
||||
localStorage.setItem('persistCode', code)
|
||||
}, initialCode)
|
||||
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
|
||||
// Replay the onboarding
|
||||
await page.getByRole('link', { name: 'Settings' }).last().click()
|
||||
const replayButton = page.getByRole('button', { name: 'Replay onboarding' })
|
||||
await expect(replayButton).toBeVisible()
|
||||
await replayButton.click()
|
||||
|
||||
// Ensure we see the warning, and that the code has not yet updated
|
||||
await expect(
|
||||
page.getByText('Replaying onboarding resets your code')
|
||||
).toBeVisible()
|
||||
await expect(page.locator('.cm-content')).toHaveText(initialCode)
|
||||
|
||||
const nextButton = page.getByTestId('onboarding-next')
|
||||
await expect(nextButton).toBeVisible()
|
||||
await nextButton.click()
|
||||
|
||||
// Ensure we see the introduction and that the code has been reset
|
||||
await expect(page.getByText('Welcome to Modeling App!')).toBeVisible()
|
||||
await expect(page.locator('.cm-content')).toContainText('// Shelf Bracket')
|
||||
|
||||
// Ensure we persisted the code to local storage.
|
||||
// Playwright's addInitScript method unfortunately will reset
|
||||
// this code if we try reloading the page as a test,
|
||||
// so this is our best way to test persistence afaik.
|
||||
expect(
|
||||
await page.evaluate(() => {
|
||||
return localStorage.getItem('persistCode')
|
||||
})
|
||||
).toContain('// Shelf Bracket')
|
||||
})
|
||||
|
||||
test('Click through each onboarding step', async ({ page }) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
// Override beforeEach test setup
|
||||
await page.addInitScript(
|
||||
async ({ settingsKey, settings }) => {
|
||||
// Give no initial code, so that the onboarding start is shown immediately
|
||||
localStorage.setItem('persistCode', '')
|
||||
localStorage.setItem(settingsKey, settings)
|
||||
test(
|
||||
'Code resets after confirmation',
|
||||
{
|
||||
appSettings: {
|
||||
app: {
|
||||
onboardingStatus: 'incomplete',
|
||||
},
|
||||
},
|
||||
{
|
||||
settingsKey: TEST_SETTINGS_KEY,
|
||||
settings: TOML.stringify({ settings: TEST_SETTINGS_ONBOARDING_START }),
|
||||
}
|
||||
)
|
||||
cleanProjectDir: true,
|
||||
},
|
||||
async ({ context, page, homePage }) => {
|
||||
const initialCode = `sketch001 = startSketchOn('XZ')`
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 1080 })
|
||||
// Load the page up with some code so we see the confirmation warning
|
||||
// when we go to replay onboarding
|
||||
await context.addInitScript((code) => {
|
||||
localStorage.setItem('persistCode', code)
|
||||
}, initialCode)
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// Test that the onboarding pane loaded
|
||||
await expect(page.getByText('Welcome to Modeling App! This')).toBeVisible()
|
||||
// Replay the onboarding
|
||||
await page.getByRole('link', { name: 'Settings' }).last().click()
|
||||
const replayButton = page.getByRole('button', {
|
||||
name: 'Replay onboarding',
|
||||
})
|
||||
await expect(replayButton).toBeVisible()
|
||||
await replayButton.click()
|
||||
|
||||
const nextButton = page.getByTestId('onboarding-next')
|
||||
// Ensure we see the warning, and that the code has not yet updated
|
||||
await expect(page.getByText('Would you like to create')).toBeVisible()
|
||||
await expect(page.locator('.cm-content')).toHaveText(initialCode)
|
||||
|
||||
while ((await nextButton.innerText()) !== 'Finish') {
|
||||
await expect(nextButton).toBeVisible()
|
||||
const nextButton = page.getByTestId('onboarding-next')
|
||||
await nextButton.hover()
|
||||
await nextButton.click()
|
||||
|
||||
// Ensure we see the introduction and that the code has been reset
|
||||
await expect(page.getByText('Welcome to Modeling App!')).toBeVisible()
|
||||
await expect(page.locator('.cm-content')).toContainText(
|
||||
'// Shelf Bracket'
|
||||
)
|
||||
|
||||
// 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
|
||||
// saved to the file system, which we have other tests for.
|
||||
}
|
||||
)
|
||||
|
||||
// Finish the onboarding
|
||||
await expect(nextButton).toBeVisible()
|
||||
await nextButton.click()
|
||||
|
||||
// Test that the onboarding pane is gone
|
||||
await expect(page.getByTestId('onboarding-content')).not.toBeVisible()
|
||||
await expect(page.url()).not.toContain('onboarding')
|
||||
})
|
||||
|
||||
test('Onboarding redirects and code updating', async ({ page }) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
// Override beforeEach test setup
|
||||
await page.addInitScript(
|
||||
async ({ settingsKey, settings }) => {
|
||||
// Give some initial code, so we can test that it's cleared
|
||||
localStorage.setItem('persistCode', 'sigmaAllow = 15000')
|
||||
localStorage.setItem(settingsKey, settings)
|
||||
test(
|
||||
'Click through each onboarding step',
|
||||
{
|
||||
appSettings: {
|
||||
app: {
|
||||
onboardingStatus: 'incomplete',
|
||||
},
|
||||
},
|
||||
{
|
||||
settingsKey: TEST_SETTINGS_KEY,
|
||||
settings: TOML.stringify({ settings: TEST_SETTINGS_ONBOARDING_EXPORT }),
|
||||
},
|
||||
async ({ context, page, homePage }) => {
|
||||
// Override beforeEach test setup
|
||||
await context.addInitScript(
|
||||
async ({ settingsKey, settings }) => {
|
||||
// Give no initial code, so that the onboarding start is shown immediately
|
||||
localStorage.setItem('persistCode', '')
|
||||
localStorage.setItem(settingsKey, settings)
|
||||
},
|
||||
{
|
||||
settingsKey: TEST_SETTINGS_KEY,
|
||||
settings: TOML.stringify({
|
||||
settings: TEST_SETTINGS_ONBOARDING_START,
|
||||
}),
|
||||
}
|
||||
)
|
||||
|
||||
await page.setBodyDimensions({ width: 1200, height: 1080 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// Test that the onboarding pane loaded
|
||||
await expect(
|
||||
page.getByText('Welcome to Modeling App! This')
|
||||
).toBeVisible()
|
||||
|
||||
const nextButton = page.getByTestId('onboarding-next')
|
||||
|
||||
while ((await nextButton.innerText()) !== 'Finish') {
|
||||
await nextButton.hover()
|
||||
await nextButton.click()
|
||||
}
|
||||
)
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
// Finish the onboarding
|
||||
await nextButton.hover()
|
||||
await nextButton.click()
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
|
||||
// Test that the redirect happened
|
||||
await expect(page.url().split(':3000').slice(-1)[0]).toBe(
|
||||
`/file/%2Fbrowser%2Fmain.kcl/onboarding/export`
|
||||
)
|
||||
|
||||
// Test that you come back to this page when you refresh
|
||||
await page.reload()
|
||||
await expect(page.url().split(':3000').slice(-1)[0]).toBe(
|
||||
`/file/%2Fbrowser%2Fmain.kcl/onboarding/export`
|
||||
)
|
||||
|
||||
// Test that the onboarding pane loaded
|
||||
const title = page.locator('[data-testid="onboarding-content"]')
|
||||
await expect(title).toBeAttached()
|
||||
|
||||
// Test that the code changes when you advance to the next step
|
||||
await page.locator('[data-testid="onboarding-next"]').click()
|
||||
await expect(page.locator('.cm-content')).toHaveText('')
|
||||
|
||||
// Test that the code is not empty when you click on the next step
|
||||
await page.locator('[data-testid="onboarding-next"]').click()
|
||||
await expect(page.locator('.cm-content')).toHaveText(/.+/)
|
||||
})
|
||||
|
||||
test('Onboarding code gets reset to demo on Interactive Numbers step', async ({
|
||||
page,
|
||||
}) => {
|
||||
test.skip(
|
||||
process.platform === 'darwin',
|
||||
"Skip on macOS, because Playwright isn't behaving the same as the actual browser"
|
||||
)
|
||||
const u = await getUtils(page)
|
||||
const badCode = `// This is bad code we shouldn't see`
|
||||
// Override beforeEach test setup
|
||||
await page.addInitScript(
|
||||
async ({ settingsKey, settings, badCode }) => {
|
||||
localStorage.setItem('persistCode', badCode)
|
||||
localStorage.setItem(settingsKey, settings)
|
||||
},
|
||||
{
|
||||
settingsKey: TEST_SETTINGS_KEY,
|
||||
settings: TOML.stringify({
|
||||
settings: TEST_SETTINGS_ONBOARDING_PARAMETRIC_MODELING,
|
||||
}),
|
||||
badCode,
|
||||
}
|
||||
)
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 1080 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
|
||||
await page.waitForURL('**' + onboardingPaths.PARAMETRIC_MODELING, {
|
||||
waitUntil: 'domcontentloaded',
|
||||
})
|
||||
|
||||
const bracketNoNewLines = bracket.replace(/\n/g, '')
|
||||
|
||||
// Check the code got reset on load
|
||||
await expect(page.locator('#code-pane')).toBeVisible()
|
||||
await expect(u.codeLocator).toHaveText(bracketNoNewLines, {
|
||||
timeout: 10_000,
|
||||
})
|
||||
|
||||
// Mess with the code again
|
||||
await u.codeLocator.selectText()
|
||||
await u.codeLocator.fill(badCode)
|
||||
await expect(u.codeLocator).toHaveText(badCode)
|
||||
|
||||
// Click to the next step
|
||||
await page.locator('[data-testid="onboarding-next"]').click()
|
||||
await page.waitForURL('**' + onboardingPaths.INTERACTIVE_NUMBERS, {
|
||||
waitUntil: 'domcontentloaded',
|
||||
})
|
||||
|
||||
// Check that the code has been reset
|
||||
await expect(u.codeLocator).toHaveText(bracketNoNewLines)
|
||||
})
|
||||
|
||||
test('Avatar text updates depending on image load success', async ({
|
||||
page,
|
||||
}) => {
|
||||
// Override beforeEach test setup
|
||||
await page.addInitScript(
|
||||
async ({ settingsKey, settings }) => {
|
||||
localStorage.setItem(settingsKey, settings)
|
||||
},
|
||||
{
|
||||
settingsKey: TEST_SETTINGS_KEY,
|
||||
settings: TOML.stringify({
|
||||
settings: TEST_SETTINGS_ONBOARDING_USER_MENU,
|
||||
}),
|
||||
}
|
||||
)
|
||||
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
|
||||
await page.waitForURL('**/file/**', { waitUntil: 'domcontentloaded' })
|
||||
|
||||
// Test that the text in this step is correct
|
||||
const avatarLocator = await page
|
||||
.getByTestId('user-sidebar-toggle')
|
||||
.locator('img')
|
||||
const onboardingOverlayLocator = await page
|
||||
.getByTestId('onboarding-content')
|
||||
.locator('div')
|
||||
.nth(1)
|
||||
|
||||
// Expect the avatar to be visible and for the text to reference it
|
||||
await expect(avatarLocator).toBeVisible()
|
||||
await expect(onboardingOverlayLocator).toBeVisible()
|
||||
await expect(onboardingOverlayLocator).toContainText('your avatar')
|
||||
|
||||
// This is to force the avatar to 404.
|
||||
// For our test image (only triggers locally. on CI, it's Kurt's /
|
||||
// gravatar image )
|
||||
await page.route('/cat.jpg', async (route) => {
|
||||
await route.fulfill({
|
||||
status: 404,
|
||||
contentType: 'text/plain',
|
||||
body: 'Not Found!',
|
||||
})
|
||||
})
|
||||
|
||||
// 404 the CI avatar image
|
||||
await page.route('https://lh3.googleusercontent.com/**', async (route) => {
|
||||
await route.fulfill({
|
||||
status: 404,
|
||||
contentType: 'text/plain',
|
||||
body: 'Not Found!',
|
||||
})
|
||||
})
|
||||
|
||||
await page.reload({ waitUntil: 'domcontentloaded' })
|
||||
|
||||
// Now expect the text to be different
|
||||
await expect(avatarLocator).not.toBeVisible()
|
||||
await expect(onboardingOverlayLocator).toBeVisible()
|
||||
await expect(onboardingOverlayLocator).toContainText('the menu button')
|
||||
})
|
||||
|
||||
test("Avatar text doesn't mention avatar when no avatar", async ({
|
||||
page,
|
||||
}) => {
|
||||
// Override beforeEach test setup
|
||||
await page.addInitScript(
|
||||
async ({ settingsKey, settings }) => {
|
||||
localStorage.setItem(settingsKey, settings)
|
||||
localStorage.setItem('FORCE_NO_IMAGE', 'FORCE_NO_IMAGE')
|
||||
},
|
||||
{
|
||||
settingsKey: TEST_SETTINGS_KEY,
|
||||
settings: TOML.stringify({
|
||||
settings: TEST_SETTINGS_ONBOARDING_USER_MENU,
|
||||
}),
|
||||
}
|
||||
)
|
||||
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
|
||||
await page.waitForURL('**/file/**', { waitUntil: 'domcontentloaded' })
|
||||
|
||||
// Test that the text in this step is correct
|
||||
const sidebar = page.getByTestId('user-sidebar-toggle')
|
||||
const avatar = sidebar.locator('img')
|
||||
const onboardingOverlayLocator = page
|
||||
.getByTestId('onboarding-content')
|
||||
.locator('div')
|
||||
.nth(1)
|
||||
|
||||
// Expect the avatar to be visible and for the text to reference it
|
||||
await expect(avatar).not.toBeVisible()
|
||||
await expect(onboardingOverlayLocator).toBeVisible()
|
||||
await expect(onboardingOverlayLocator).toContainText('the menu button')
|
||||
|
||||
// Test we mention what else is in this menu for https://github.com/KittyCAD/modeling-app/issues/2939
|
||||
// which doesn't deserver its own full test spun up
|
||||
const userMenuFeatures = [
|
||||
'manage your account',
|
||||
'report a bug',
|
||||
'request a feature',
|
||||
'sign out',
|
||||
]
|
||||
for (const feature of userMenuFeatures) {
|
||||
await expect(onboardingOverlayLocator).toContainText(feature)
|
||||
// Test that the onboarding pane is gone
|
||||
await expect(page.getByTestId('onboarding-content')).not.toBeVisible()
|
||||
await expect.poll(() => page.url()).not.toContain('/onboarding')
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
test(
|
||||
'Onboarding redirects and code updating',
|
||||
{
|
||||
appSettings: {
|
||||
app: {
|
||||
onboardingStatus: '/export',
|
||||
},
|
||||
},
|
||||
cleanProjectDir: true,
|
||||
},
|
||||
async ({ context, page, homePage }) => {
|
||||
const originalCode = 'sigmaAllow = 15000'
|
||||
|
||||
// Override beforeEach test setup
|
||||
await context.addInitScript(
|
||||
async ({ settingsKey, settings }) => {
|
||||
// Give some initial code, so we can test that it's cleared
|
||||
localStorage.setItem('persistCode', originalCode)
|
||||
localStorage.setItem(settingsKey, settings)
|
||||
},
|
||||
{
|
||||
settingsKey: TEST_SETTINGS_KEY,
|
||||
settings: TOML.stringify({
|
||||
settings: TEST_SETTINGS_ONBOARDING_EXPORT,
|
||||
}),
|
||||
}
|
||||
)
|
||||
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// Test that the redirect happened
|
||||
await expect.poll(() => page.url()).toContain('/onboarding/export')
|
||||
|
||||
// Test that you come back to this page when you refresh
|
||||
await page.reload()
|
||||
await expect.poll(() => page.url()).toContain('/onboarding/export')
|
||||
|
||||
// Test that the code changes when you advance to the next step
|
||||
await page.getByTestId('onboarding-next').hover()
|
||||
await page.getByTestId('onboarding-next').click()
|
||||
|
||||
// Test that the onboarding pane loaded
|
||||
const title = page.locator('[data-testid="onboarding-content"]')
|
||||
await expect(title).toBeAttached()
|
||||
|
||||
await expect(page.locator('.cm-content')).not.toHaveText(originalCode)
|
||||
|
||||
// Test that the code is not empty when you click on the next step
|
||||
await page.locator('[data-testid="onboarding-next"]').hover()
|
||||
await page.locator('[data-testid="onboarding-next"]').click()
|
||||
await expect(page.locator('.cm-content')).toHaveText(/.+/)
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'Onboarding code gets reset to demo on Interactive Numbers step',
|
||||
{
|
||||
appSettings: {
|
||||
app: {
|
||||
onboardingStatus: '/parametric-modeling',
|
||||
},
|
||||
},
|
||||
cleanProjectDir: true,
|
||||
},
|
||||
|
||||
async ({ context, page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
const badCode = `// This is bad code we shouldn't see`
|
||||
|
||||
await page.setBodyDimensions({ width: 1200, height: 1080 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await expect
|
||||
.poll(() => page.url())
|
||||
.toContain(onboardingPaths.PARAMETRIC_MODELING)
|
||||
|
||||
const bracketNoNewLines = bracket.replace(/\n/g, '')
|
||||
|
||||
// Check the code got reset on load
|
||||
await expect(page.locator('#code-pane')).toBeVisible()
|
||||
await expect(u.codeLocator).toHaveText(bracketNoNewLines, {
|
||||
timeout: 10_000,
|
||||
})
|
||||
|
||||
// Mess with the code again
|
||||
await u.codeLocator.selectText()
|
||||
await u.codeLocator.fill(badCode)
|
||||
await expect(u.codeLocator).toHaveText(badCode)
|
||||
|
||||
// Click to the next step
|
||||
await page.locator('[data-testid="onboarding-next"]').hover()
|
||||
await page.locator('[data-testid="onboarding-next"]').click()
|
||||
await page.waitForURL('**' + onboardingPaths.INTERACTIVE_NUMBERS, {
|
||||
waitUntil: 'domcontentloaded',
|
||||
})
|
||||
|
||||
// Check that the code has been reset
|
||||
await expect(u.codeLocator).toHaveText(bracketNoNewLines)
|
||||
}
|
||||
)
|
||||
|
||||
// (lee) The two avatar tests are weird because even on main, we don't have
|
||||
// anything to do with the avatar inside the onboarding test. Due to the
|
||||
// low impact of an avatar not showing I'm changing this to fixme.
|
||||
test.fixme(
|
||||
'Avatar text updates depending on image load success',
|
||||
{
|
||||
appSettings: {
|
||||
app: {
|
||||
onboardingStatus: 'incomplete',
|
||||
},
|
||||
},
|
||||
cleanProjectDir: true,
|
||||
},
|
||||
async ({ context, page, homePage }) => {
|
||||
// Override beforeEach test setup
|
||||
await context.addInitScript(
|
||||
async ({ settingsKey, settings }) => {
|
||||
localStorage.setItem(settingsKey, settings)
|
||||
},
|
||||
{
|
||||
settingsKey: TEST_SETTINGS_KEY,
|
||||
settings: TOML.stringify({
|
||||
settings: TEST_SETTINGS_ONBOARDING_USER_MENU,
|
||||
}),
|
||||
}
|
||||
)
|
||||
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// Test that the text in this step is correct
|
||||
const avatarLocator = await page
|
||||
.getByTestId('user-sidebar-toggle')
|
||||
.locator('img')
|
||||
const onboardingOverlayLocator = await page
|
||||
.getByTestId('onboarding-content')
|
||||
.locator('div')
|
||||
.nth(1)
|
||||
|
||||
// Expect the avatar to be visible and for the text to reference it
|
||||
await expect(avatarLocator).toBeVisible()
|
||||
await expect(onboardingOverlayLocator).toBeVisible()
|
||||
await expect(onboardingOverlayLocator).toContainText('your avatar')
|
||||
|
||||
// This is to force the avatar to 404.
|
||||
// For our test image (only triggers locally. on CI, it's Kurt's /
|
||||
// gravatar image )
|
||||
await page.route('/cat.jpg', async (route) => {
|
||||
await route.fulfill({
|
||||
status: 404,
|
||||
contentType: 'text/plain',
|
||||
body: 'Not Found!',
|
||||
})
|
||||
})
|
||||
|
||||
// 404 the CI avatar image
|
||||
await page.route(
|
||||
'https://lh3.googleusercontent.com/**',
|
||||
async (route) => {
|
||||
await route.fulfill({
|
||||
status: 404,
|
||||
contentType: 'text/plain',
|
||||
body: 'Not Found!',
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
await page.reload({ waitUntil: 'domcontentloaded' })
|
||||
|
||||
// Now expect the text to be different
|
||||
await expect(avatarLocator).not.toBeVisible()
|
||||
await expect(onboardingOverlayLocator).toBeVisible()
|
||||
await expect(onboardingOverlayLocator).toContainText('the menu button')
|
||||
}
|
||||
)
|
||||
|
||||
test.fixme(
|
||||
"Avatar text doesn't mention avatar when no avatar",
|
||||
{
|
||||
appSettings: {
|
||||
app: {
|
||||
onboardingStatus: 'incomplete',
|
||||
},
|
||||
},
|
||||
cleanProjectDir: true,
|
||||
},
|
||||
async ({ context, page, homePage }) => {
|
||||
// Override beforeEach test setup
|
||||
await context.addInitScript(
|
||||
async ({ settingsKey, settings }) => {
|
||||
localStorage.setItem(settingsKey, settings)
|
||||
localStorage.setItem('FORCE_NO_IMAGE', 'FORCE_NO_IMAGE')
|
||||
},
|
||||
{
|
||||
settingsKey: TEST_SETTINGS_KEY,
|
||||
settings: TOML.stringify({
|
||||
settings: TEST_SETTINGS_ONBOARDING_USER_MENU,
|
||||
}),
|
||||
}
|
||||
)
|
||||
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// Test that the text in this step is correct
|
||||
const sidebar = page.getByTestId('user-sidebar-toggle')
|
||||
const avatar = sidebar.locator('img')
|
||||
const onboardingOverlayLocator = page
|
||||
.getByTestId('onboarding-content')
|
||||
.locator('div')
|
||||
.nth(1)
|
||||
|
||||
// Expect the avatar to be visible and for the text to reference it
|
||||
await expect(avatar).not.toBeVisible()
|
||||
await expect(onboardingOverlayLocator).toBeVisible()
|
||||
await expect(onboardingOverlayLocator).toContainText('the menu button')
|
||||
|
||||
// Test we mention what else is in this menu for https://github.com/KittyCAD/modeling-app/issues/2939
|
||||
// which doesn't deserver its own full test spun up
|
||||
const userMenuFeatures = [
|
||||
'manage your account',
|
||||
'report a bug',
|
||||
'request a feature',
|
||||
'sign out',
|
||||
]
|
||||
for (const feature of userMenuFeatures) {
|
||||
await expect(onboardingOverlayLocator).toContainText(feature)
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
test(
|
||||
test.fixme(
|
||||
'Restarting onboarding on desktop takes one attempt',
|
||||
{ tag: '@electron' },
|
||||
async ({ browser: _ }, testInfo) => {
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
const routerTemplateDir = join(dir, 'router-template-slate')
|
||||
await fsp.mkdir(routerTemplateDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('router-template-slate.kcl'),
|
||||
join(routerTemplateDir, 'main.kcl')
|
||||
)
|
||||
{
|
||||
appSettings: {
|
||||
app: {
|
||||
onboardingStatus: 'dismissed',
|
||||
},
|
||||
},
|
||||
cleanProjectDir: true,
|
||||
},
|
||||
async ({ context, page, homePage }, testInfo) => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
const routerTemplateDir = join(dir, 'router-template-slate')
|
||||
await fsp.mkdir(routerTemplateDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('router-template-slate.kcl'),
|
||||
join(routerTemplateDir, 'main.kcl')
|
||||
)
|
||||
})
|
||||
|
||||
// Our constants
|
||||
@ -418,9 +466,8 @@ test(
|
||||
const restartOnboardingButton = page.getByRole('button', {
|
||||
name: 'Reset onboarding',
|
||||
})
|
||||
const restartConfirmationButton = page.getByRole('button', {
|
||||
name: 'Make a new project',
|
||||
})
|
||||
const nextButton = page.getByTestId('onboarding-next')
|
||||
|
||||
const tutorialProjectIndicator = page
|
||||
.getByTestId('project-sidebar-toggle')
|
||||
.filter({ hasText: 'Tutorial Project 00' })
|
||||
@ -439,7 +486,7 @@ test(
|
||||
})
|
||||
|
||||
await test.step('Navigate into project', async () => {
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
page.on('console', console.log)
|
||||
|
||||
@ -455,14 +502,22 @@ test(
|
||||
await helpMenuButton.click()
|
||||
await restartOnboardingButton.click()
|
||||
|
||||
await expect(restartConfirmationButton).toBeVisible()
|
||||
await restartConfirmationButton.click()
|
||||
await nextButton.hover()
|
||||
await nextButton.click()
|
||||
})
|
||||
|
||||
await test.step('Confirm that the onboarding has restarted', async () => {
|
||||
await expect(tutorialProjectIndicator).toBeVisible()
|
||||
await expect(tutorialModalText).toBeVisible()
|
||||
// Make sure the model loaded
|
||||
const XYPlanePoint = { x: 988, y: 523 } as const
|
||||
const modelColor: [number, number, number] = [76, 76, 76]
|
||||
|
||||
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y)
|
||||
await expectPixelColor(page, modelColor, XYPlanePoint, 8)
|
||||
await tutorialDismissButton.click()
|
||||
// Make sure model still there.
|
||||
await expectPixelColor(page, modelColor, XYPlanePoint, 8)
|
||||
})
|
||||
|
||||
await test.step('Clear code and restart onboarding from settings', async () => {
|
||||
@ -480,11 +535,9 @@ test(
|
||||
|
||||
await restartOnboardingSettingsButton.click()
|
||||
// Since the code is empty, we should not see the confirmation dialog
|
||||
await expect(restartConfirmationButton).not.toBeVisible()
|
||||
await expect(nextButton).not.toBeVisible()
|
||||
await expect(tutorialProjectIndicator).toBeVisible()
|
||||
await expect(tutorialModalText).toBeVisible()
|
||||
})
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
@ -1,25 +1,51 @@
|
||||
import { test, expect, AuthenticatedApp } from './fixtures/fixtureSetup'
|
||||
import { test, expect, Page } from './zoo-test'
|
||||
import { EditorFixture } from './fixtures/editorFixture'
|
||||
import { SceneFixture } from './fixtures/sceneFixture'
|
||||
import { ToolbarFixture } from './fixtures/toolbarFixture'
|
||||
import fs from 'node:fs/promises'
|
||||
import path from 'node:path'
|
||||
import { getUtils } from './test-utils'
|
||||
|
||||
// test file is for testing point an click code gen functionality that's not sketch mode related
|
||||
|
||||
test(
|
||||
'verify extruding circle works',
|
||||
{ tag: ['@skipWin'] },
|
||||
async ({ app, cmdBar, editor, toolbar, scene }) => {
|
||||
test.skip(
|
||||
process.platform === 'win32',
|
||||
'Fails on windows in CI, can not be replicated locally on windows.'
|
||||
)
|
||||
const file = await app.getInputFile('test-circle-extrude.kcl')
|
||||
await app.initialise(file)
|
||||
const [clickCircle, moveToCircle] = scene.makeMouseHelpers(582, 217)
|
||||
test('verify extruding circle works', async ({
|
||||
context,
|
||||
homePage,
|
||||
cmdBar,
|
||||
editor,
|
||||
toolbar,
|
||||
scene,
|
||||
}) => {
|
||||
// TODO: fix this test on windows after the electron migration
|
||||
test.skip(process.platform === 'win32', 'Skip on windows')
|
||||
const file = await fs.readFile(
|
||||
path.resolve(
|
||||
__dirname,
|
||||
'../../',
|
||||
'./src/wasm-lib/tests/executor/inputs/test-circle-extrude.kcl'
|
||||
),
|
||||
'utf-8'
|
||||
)
|
||||
await context.addInitScript((file) => {
|
||||
localStorage.setItem('persistCode', file)
|
||||
}, file)
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await test.step('because there is sweepable geometry, verify extrude is enable when nothing is selected', async () => {
|
||||
await scene.clickNoWhere()
|
||||
await expect(toolbar.extrudeButton).toBeEnabled()
|
||||
const [clickCircle, moveToCircle] = scene.makeMouseHelpers(582, 217)
|
||||
|
||||
await test.step('because there is sweepable geometry, verify extrude is enable when nothing is selected', async () => {
|
||||
await scene.clickNoWhere()
|
||||
await expect(toolbar.extrudeButton).toBeEnabled()
|
||||
})
|
||||
|
||||
await test.step('check code model connection works and that button is still enable once circle is selected ', async () => {
|
||||
await moveToCircle()
|
||||
const circleSnippet =
|
||||
'circle({ center = [318.33, 168.1], radius = 182.8 }, %)'
|
||||
await editor.expectState({
|
||||
activeLines: ["constsketch002=startSketchOn('XZ')"],
|
||||
highlightedCode: circleSnippet,
|
||||
diagnostics: [],
|
||||
})
|
||||
|
||||
await test.step('check code model connection works and that button is still enable once circle is selected ', async () => {
|
||||
@ -27,7 +53,7 @@ test(
|
||||
const circleSnippet =
|
||||
'circle({ center = [318.33, 168.1], radius = 182.8 }, %)'
|
||||
await editor.expectState({
|
||||
activeLines: [],
|
||||
activeLines: ["constsketch002=startSketchOn('XZ')"],
|
||||
highlightedCode: circleSnippet,
|
||||
diagnostics: [],
|
||||
})
|
||||
@ -40,39 +66,42 @@ test(
|
||||
})
|
||||
await expect(toolbar.extrudeButton).toBeEnabled()
|
||||
})
|
||||
await expect(toolbar.extrudeButton).toBeEnabled()
|
||||
})
|
||||
|
||||
await test.step('do extrude flow and check extrude code is added to editor', async () => {
|
||||
await toolbar.extrudeButton.click()
|
||||
await test.step('do extrude flow and check extrude code is added to editor', async () => {
|
||||
await toolbar.extrudeButton.click()
|
||||
|
||||
await cmdBar.expectState({
|
||||
stage: 'arguments',
|
||||
currentArgKey: 'distance',
|
||||
currentArgValue: '5',
|
||||
headerArguments: { Selection: '1 face', Distance: '' },
|
||||
highlightedHeaderArg: 'distance',
|
||||
commandName: 'Extrude',
|
||||
})
|
||||
await cmdBar.progressCmdBar()
|
||||
|
||||
const expectString = 'extrude001 = extrude(5, sketch001)'
|
||||
await editor.expectEditor.not.toContain(expectString)
|
||||
|
||||
await cmdBar.expectState({
|
||||
stage: 'review',
|
||||
headerArguments: { Selection: '1 face', Distance: '5' },
|
||||
commandName: 'Extrude',
|
||||
})
|
||||
await cmdBar.progressCmdBar()
|
||||
|
||||
await editor.expectEditor.toContain(expectString)
|
||||
await cmdBar.expectState({
|
||||
stage: 'arguments',
|
||||
currentArgKey: 'distance',
|
||||
currentArgValue: '5',
|
||||
headerArguments: { Selection: '1 face', Distance: '' },
|
||||
highlightedHeaderArg: 'distance',
|
||||
commandName: 'Extrude',
|
||||
})
|
||||
}
|
||||
)
|
||||
await cmdBar.progressCmdBar()
|
||||
|
||||
const expectString = 'extrude001 = extrude(5, sketch001)'
|
||||
await editor.expectEditor.not.toContain(expectString)
|
||||
|
||||
await cmdBar.expectState({
|
||||
stage: 'review',
|
||||
headerArguments: { Selection: '1 face', Distance: '5' },
|
||||
commandName: 'Extrude',
|
||||
})
|
||||
await cmdBar.progressCmdBar()
|
||||
|
||||
await editor.expectEditor.toContain(expectString)
|
||||
})
|
||||
})
|
||||
|
||||
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 =
|
||||
(
|
||||
app: AuthenticatedApp,
|
||||
page: Page,
|
||||
editor: EditorFixture,
|
||||
toolbar: ToolbarFixture,
|
||||
scene: SceneFixture
|
||||
@ -124,7 +153,7 @@ test.describe('verify sketch on chamfer works', () => {
|
||||
await toolbar.startSketchPlaneSelection()
|
||||
await clickChamfer()
|
||||
// timeout wait for engine animation is unavoidable
|
||||
await app.page.waitForTimeout(600)
|
||||
await page.waitForTimeout(1000)
|
||||
await editor.expectEditor.toContain(afterChamferSelectSnippet)
|
||||
})
|
||||
await test.step('make sure a basic sketch can be added', async () => {
|
||||
@ -135,7 +164,9 @@ test.describe('verify sketch on chamfer works', () => {
|
||||
pixelDiff: 50,
|
||||
})
|
||||
await rectangle2ndClick()
|
||||
await editor.expectEditor.toContain(afterRectangle2ndClickSnippet)
|
||||
await editor.expectEditor.toContain(afterRectangle2ndClickSnippet, {
|
||||
shouldNormalise: true,
|
||||
})
|
||||
})
|
||||
|
||||
await test.step('Clean up so that `_sketchOnAChamfer` util can be called again', async () => {
|
||||
@ -150,24 +181,35 @@ test.describe('verify sketch on chamfer works', () => {
|
||||
})
|
||||
})
|
||||
}
|
||||
test(
|
||||
'works on all edge selections and can break up multi edges in a chamfer array',
|
||||
{ tag: ['@skipWin'] },
|
||||
async ({ app, editor, toolbar, scene }) => {
|
||||
test.skip(
|
||||
process.platform === 'win32',
|
||||
'Fails on windows in CI, can not be replicated locally on windows.'
|
||||
)
|
||||
const file = await app.getInputFile('e2e-can-sketch-on-chamfer.kcl')
|
||||
await app.initialise(file)
|
||||
test('works on all edge selections and can break up multi edges in a chamfer array', async ({
|
||||
context,
|
||||
page,
|
||||
homePage,
|
||||
editor,
|
||||
toolbar,
|
||||
scene,
|
||||
}) => {
|
||||
const file = await fs.readFile(
|
||||
path.resolve(
|
||||
__dirname,
|
||||
'../../',
|
||||
'./src/wasm-lib/tests/executor/inputs/e2e-can-sketch-on-chamfer.kcl'
|
||||
),
|
||||
'utf-8'
|
||||
)
|
||||
await context.addInitScript((file) => {
|
||||
localStorage.setItem('persistCode', file)
|
||||
}, file)
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
const sketchOnAChamfer = _sketchOnAChamfer(app, editor, toolbar, scene)
|
||||
const sketchOnAChamfer = _sketchOnAChamfer(page, editor, toolbar, scene)
|
||||
|
||||
await sketchOnAChamfer({
|
||||
clickCoords: { x: 570, y: 220 },
|
||||
cameraPos: { x: 16020, y: -2000, z: 10500 },
|
||||
cameraTarget: { x: -150, y: -4500, z: -80 },
|
||||
beforeChamferSnippet: `angledLine([segAng(rectangleSegmentA001)-90,217.26],%,$seg01)
|
||||
await sketchOnAChamfer({
|
||||
clickCoords: { x: 570, y: 220 },
|
||||
cameraPos: { x: 16020, y: -2000, z: 10500 },
|
||||
cameraTarget: { x: -150, y: -4500, z: -80 },
|
||||
beforeChamferSnippet: `angledLine([segAng(rectangleSegmentA001)-90,217.26],%,$seg01)
|
||||
chamfer({length = 30,tags = [
|
||||
seg01,
|
||||
getNextAdjacentEdge(yo),
|
||||
@ -175,10 +217,9 @@ test.describe('verify sketch on chamfer works', () => {
|
||||
getOppositeEdge(seg01)
|
||||
]}, %)`,
|
||||
|
||||
afterChamferSelectSnippet:
|
||||
'sketch002 = startSketchOn(extrude001, seg03)',
|
||||
afterRectangle1stClickSnippet: 'startProfileAt([205.96, 254.59], %)',
|
||||
afterRectangle2ndClickSnippet: `angledLine([0, 11.39], %, $rectangleSegmentA002)
|
||||
afterChamferSelectSnippet: 'sketch002 = startSketchOn(extrude001, seg03)',
|
||||
afterRectangle1stClickSnippet: 'startProfileAt([205.96, 254.59], %)',
|
||||
afterRectangle2ndClickSnippet: `angledLine([0, 11.39], %, $rectangleSegmentA002)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA002) - 90,
|
||||
105.26
|
||||
@ -189,13 +230,13 @@ test.describe('verify sketch on chamfer works', () => {
|
||||
], %, $rectangleSegmentC001)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)`,
|
||||
})
|
||||
})
|
||||
|
||||
await sketchOnAChamfer({
|
||||
clickCoords: { x: 690, y: 250 },
|
||||
cameraPos: { x: 16020, y: -2000, z: 10500 },
|
||||
cameraTarget: { x: -150, y: -4500, z: -80 },
|
||||
beforeChamferSnippet: `angledLine([
|
||||
await sketchOnAChamfer({
|
||||
clickCoords: { x: 690, y: 250 },
|
||||
cameraPos: { x: 16020, y: -2000, z: 10500 },
|
||||
cameraTarget: { x: -150, y: -4500, z: -80 },
|
||||
beforeChamferSnippet: `angledLine([
|
||||
segAng(rectangleSegmentA001) - 90,
|
||||
217.26
|
||||
], %, $seg01)chamfer({
|
||||
@ -207,10 +248,9 @@ test.describe('verify sketch on chamfer works', () => {
|
||||
]
|
||||
}, %)`,
|
||||
|
||||
afterChamferSelectSnippet:
|
||||
'sketch003 = startSketchOn(extrude001, seg04)',
|
||||
afterRectangle1stClickSnippet: 'startProfileAt([-209.64, 255.28], %)',
|
||||
afterRectangle2ndClickSnippet: `angledLine([0, 11.56], %, $rectangleSegmentA003)
|
||||
afterChamferSelectSnippet: 'sketch003 = startSketchOn(extrude001, seg04)',
|
||||
afterRectangle1stClickSnippet: 'startProfileAt([-209.64, 255.28], %)',
|
||||
afterRectangle2ndClickSnippet: `angledLine([0, 11.56], %, $rectangleSegmentA003)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA003) - 90,
|
||||
106.84
|
||||
@ -221,22 +261,21 @@ test.describe('verify sketch on chamfer works', () => {
|
||||
], %, $rectangleSegmentC002)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)`,
|
||||
})
|
||||
await sketchOnAChamfer({
|
||||
clickCoords: { x: 677, y: 87 },
|
||||
cameraPos: { x: -6200, y: 1500, z: 6200 },
|
||||
cameraTarget: { x: 8300, y: 1100, z: 4800 },
|
||||
beforeChamferSnippet: `angledLine([0, 268.43], %, $rectangleSegmentA001)chamfer({
|
||||
})
|
||||
await sketchOnAChamfer({
|
||||
clickCoords: { x: 677, y: 87 },
|
||||
cameraPos: { x: -6200, y: 1500, z: 6200 },
|
||||
cameraTarget: { x: 8300, y: 1100, z: 4800 },
|
||||
beforeChamferSnippet: `angledLine([0, 268.43], %, $rectangleSegmentA001)chamfer({
|
||||
length = 30,
|
||||
tags = [
|
||||
getNextAdjacentEdge(yo),
|
||||
getNextAdjacentEdge(seg02)
|
||||
]
|
||||
}, %)`,
|
||||
afterChamferSelectSnippet:
|
||||
'sketch003 = startSketchOn(extrude001, seg04)',
|
||||
afterRectangle1stClickSnippet: 'startProfileAt([-209.64, 255.28], %)',
|
||||
afterRectangle2ndClickSnippet: `angledLine([0, 11.56], %, $rectangleSegmentA003)
|
||||
afterChamferSelectSnippet: 'sketch003 = startSketchOn(extrude001, seg04)',
|
||||
afterRectangle1stClickSnippet: 'startProfileAt([75.8, 317.2], %)',
|
||||
afterRectangle2ndClickSnippet: `angledLine([0, 11.56], %, $rectangleSegmentA003)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA003) - 90,
|
||||
106.84
|
||||
@ -247,20 +286,19 @@ test.describe('verify sketch on chamfer works', () => {
|
||||
], %, $rectangleSegmentC002)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)`,
|
||||
})
|
||||
/// last one
|
||||
await sketchOnAChamfer({
|
||||
clickCoords: { x: 620, y: 300 },
|
||||
cameraPos: { x: -1100, y: -7700, z: 1600 },
|
||||
cameraTarget: { x: 1450, y: 670, z: 4000 },
|
||||
beforeChamferSnippet: `chamfer({
|
||||
})
|
||||
/// last one
|
||||
await sketchOnAChamfer({
|
||||
clickCoords: { x: 620, y: 300 },
|
||||
cameraPos: { x: -1100, y: -7700, z: 1600 },
|
||||
cameraTarget: { x: 1450, y: 670, z: 4000 },
|
||||
beforeChamferSnippet: `chamfer({
|
||||
length = 30,
|
||||
tags = [getNextAdjacentEdge(yo)]
|
||||
}, %)`,
|
||||
afterChamferSelectSnippet:
|
||||
'sketch005 = startSketchOn(extrude001, seg06)',
|
||||
afterRectangle1stClickSnippet: 'startProfileAt([-23.43, 19.69], %)',
|
||||
afterRectangle2ndClickSnippet: `angledLine([0, 9.1], %, $rectangleSegmentA005)
|
||||
afterChamferSelectSnippet: 'sketch005 = startSketchOn(extrude001, seg06)',
|
||||
afterRectangle1stClickSnippet: 'startProfileAt([-23.43, 19.69], %)',
|
||||
afterRectangle2ndClickSnippet: `angledLine([0, 9.1], %, $rectangleSegmentA005)
|
||||
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA005) - 90,
|
||||
@ -272,11 +310,11 @@ test.describe('verify sketch on chamfer works', () => {
|
||||
], %, $rectangleSegmentC004)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)`,
|
||||
})
|
||||
})
|
||||
|
||||
await test.step('verify at the end of the test that final code is what is expected', async () => {
|
||||
await editor.expectEditor.toContain(
|
||||
`sketch001 = startSketchOn('XZ')
|
||||
await test.step('verify at the end of the test that final code is what is expected', async () => {
|
||||
await editor.expectEditor.toContain(
|
||||
`sketch001 = startSketchOn('XZ')
|
||||
|
||||
|> startProfileAt([75.8, 317.2], %) // [$startCapTag, $EndCapTag]
|
||||
|> angledLine([0, 268.43], %, $rectangleSegmentA001)
|
||||
@ -305,7 +343,7 @@ test.describe('verify sketch on chamfer works', () => {
|
||||
tags = [getNextAdjacentEdge(yo)]
|
||||
}, %, $seg06)
|
||||
sketch005 = startSketchOn(extrude001, seg06)
|
||||
|> startProfileAt([-23.43, 19.69], %)
|
||||
|> startProfileAt([-23.43,19.69], %)
|
||||
|> angledLine([0, 9.1], %, $rectangleSegmentA005)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA005) - 90,
|
||||
@ -318,7 +356,7 @@ test.describe('verify sketch on chamfer works', () => {
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
sketch004 = startSketchOn(extrude001, seg05)
|
||||
|> startProfileAt([82.57, 322.96], %)
|
||||
|> startProfileAt([82.57,322.96], %)
|
||||
|> angledLine([0, 11.16], %, $rectangleSegmentA004)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA004) - 90,
|
||||
@ -331,7 +369,7 @@ test.describe('verify sketch on chamfer works', () => {
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
sketch003 = startSketchOn(extrude001, seg04)
|
||||
|> startProfileAt([-209.64, 255.28], %)
|
||||
|> startProfileAt([-209.64,255.28], %)
|
||||
|> angledLine([0, 11.56], %, $rectangleSegmentA003)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA003) - 90,
|
||||
@ -344,7 +382,7 @@ test.describe('verify sketch on chamfer works', () => {
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
sketch002 = startSketchOn(extrude001, seg03)
|
||||
|> startProfileAt([205.96, 254.59], %)
|
||||
|> startProfileAt([205.96,254.59], %)
|
||||
|> angledLine([0, 11.39], %, $rectangleSegmentA002)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA002) - 90,
|
||||
@ -357,43 +395,50 @@ test.describe('verify sketch on chamfer works', () => {
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
`,
|
||||
{ shouldNormalise: true }
|
||||
)
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'Works on chamfers that are non in a pipeExpression can break up multi edges in a chamfer array',
|
||||
{ tag: ['@skipWin'] },
|
||||
async ({ app, editor, toolbar, scene }) => {
|
||||
test.skip(
|
||||
process.platform === 'win32',
|
||||
'Fails on windows in CI, can not be replicated locally on windows.'
|
||||
{ shouldNormalise: true }
|
||||
)
|
||||
const file = await app.getInputFile(
|
||||
'e2e-can-sketch-on-chamfer-no-pipeExpr.kcl'
|
||||
)
|
||||
await app.initialise(file)
|
||||
})
|
||||
})
|
||||
|
||||
const sketchOnAChamfer = _sketchOnAChamfer(app, editor, toolbar, scene)
|
||||
test('Works on chamfers that are non in a pipeExpression can break up multi edges in a chamfer array', async ({
|
||||
context,
|
||||
page,
|
||||
homePage,
|
||||
editor,
|
||||
toolbar,
|
||||
scene,
|
||||
}) => {
|
||||
const file = await fs.readFile(
|
||||
path.resolve(
|
||||
__dirname,
|
||||
'../../',
|
||||
'./src/wasm-lib/tests/executor/inputs/e2e-can-sketch-on-chamfer-no-pipeExpr.kcl'
|
||||
),
|
||||
'utf-8'
|
||||
)
|
||||
await context.addInitScript((file) => {
|
||||
localStorage.setItem('persistCode', file)
|
||||
}, file)
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await sketchOnAChamfer({
|
||||
clickCoords: { x: 570, y: 220 },
|
||||
cameraPos: { x: 16020, y: -2000, z: 10500 },
|
||||
cameraTarget: { x: -150, y: -4500, z: -80 },
|
||||
beforeChamferSnippet: `angledLine([segAng(rectangleSegmentA001)-90,217.26],%,$seg01)
|
||||
const sketchOnAChamfer = _sketchOnAChamfer(page, editor, toolbar, scene)
|
||||
|
||||
await sketchOnAChamfer({
|
||||
clickCoords: { x: 570, y: 220 },
|
||||
cameraPos: { x: 16020, y: -2000, z: 10500 },
|
||||
cameraTarget: { x: -150, y: -4500, z: -80 },
|
||||
beforeChamferSnippet: `angledLine([segAng(rectangleSegmentA001)-90,217.26],%,$seg01)
|
||||
chamfer({length=30,tags=[
|
||||
seg01,
|
||||
getNextAdjacentEdge(yo),
|
||||
getNextAdjacentEdge(seg02),
|
||||
getOppositeEdge(seg01)
|
||||
]}, extrude001)`,
|
||||
beforeChamferSnippetEnd: '}, extrude001)',
|
||||
afterChamferSelectSnippet:
|
||||
'sketch002 = startSketchOn(extrude001, seg03)',
|
||||
afterRectangle1stClickSnippet: 'startProfileAt([205.96, 254.59], %)',
|
||||
afterRectangle2ndClickSnippet: `angledLine([0, 11.39], %, $rectangleSegmentA002)
|
||||
beforeChamferSnippetEnd: '}, extrude001)',
|
||||
afterChamferSelectSnippet: 'sketch002 = startSketchOn(extrude001, seg03)',
|
||||
afterRectangle1stClickSnippet: 'startProfileAt([205.96, 254.59], %)',
|
||||
afterRectangle2ndClickSnippet: `angledLine([0, 11.39], %, $rectangleSegmentA002)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA002) - 90,
|
||||
105.26
|
||||
@ -404,9 +449,9 @@ test.describe('verify sketch on chamfer works', () => {
|
||||
], %, $rectangleSegmentC001)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)`,
|
||||
})
|
||||
await editor.expectEditor.toContain(
|
||||
`sketch001 = startSketchOn('XZ')
|
||||
})
|
||||
await editor.expectEditor.toContain(
|
||||
`sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([75.8, 317.2], %)
|
||||
|> angledLine([0, 268.43], %, $rectangleSegmentA001)
|
||||
|> angledLine([
|
||||
@ -446,50 +491,56 @@ sketch002 = startSketchOn(extrude001, seg03)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
`,
|
||||
{ shouldNormalise: true }
|
||||
)
|
||||
}
|
||||
)
|
||||
{ shouldNormalise: true }
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
test(`Verify axis, origin, and horizontal snapping`, async ({
|
||||
app,
|
||||
page,
|
||||
homePage,
|
||||
editor,
|
||||
toolbar,
|
||||
scene,
|
||||
}) => {
|
||||
const viewPortSize = { width: 1200, height: 500 }
|
||||
|
||||
await page.setBodyDimensions(viewPortSize)
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// Constants and locators
|
||||
// These are mappings from screenspace to KCL coordinates,
|
||||
// until we merge in our coordinate system helpers
|
||||
const xzPlane = [
|
||||
app.viewPortSize.width * 0.65,
|
||||
app.viewPortSize.height * 0.3,
|
||||
viewPortSize.width * 0.65,
|
||||
viewPortSize.height * 0.3,
|
||||
] as const
|
||||
const originSloppy = {
|
||||
screen: [
|
||||
app.viewPortSize.width / 2 + 3, // 3px off the center of the screen
|
||||
app.viewPortSize.height / 2,
|
||||
viewPortSize.width / 2 + 3, // 3px off the center of the screen
|
||||
viewPortSize.height / 2,
|
||||
],
|
||||
kcl: [0, 0],
|
||||
} as const
|
||||
const xAxisSloppy = {
|
||||
screen: [
|
||||
app.viewPortSize.width * 0.75,
|
||||
app.viewPortSize.height / 2 - 3, // 3px off the X-axis
|
||||
viewPortSize.width * 0.75,
|
||||
viewPortSize.height / 2 - 3, // 3px off the X-axis
|
||||
],
|
||||
kcl: [16.95, 0],
|
||||
kcl: [20.34, 0],
|
||||
} as const
|
||||
const offYAxis = {
|
||||
screen: [
|
||||
app.viewPortSize.width * 0.6, // Well off the Y-axis, out of snapping range
|
||||
app.viewPortSize.height * 0.3,
|
||||
viewPortSize.width * 0.6, // Well off the Y-axis, out of snapping range
|
||||
viewPortSize.height * 0.3,
|
||||
],
|
||||
kcl: [6.78, 6.78],
|
||||
kcl: [8.14, 6.78],
|
||||
} as const
|
||||
const yAxisSloppy = {
|
||||
screen: [
|
||||
app.viewPortSize.width / 2 + 5, // 5px off the Y-axis
|
||||
app.viewPortSize.height * 0.3,
|
||||
viewPortSize.width / 2 + 5, // 5px off the Y-axis
|
||||
viewPortSize.height * 0.3,
|
||||
],
|
||||
kcl: [0, 6.78],
|
||||
} as const
|
||||
@ -510,15 +561,13 @@ test(`Verify axis, origin, and horizontal snapping`, async ({
|
||||
afterSegmentDraggedOnYAxis: `startProfileAt([${yAxisSloppy.kcl[0]}, ${yAxisSloppy.kcl[1]}], %)`,
|
||||
}
|
||||
|
||||
await app.initialise()
|
||||
|
||||
await test.step(`Start a sketch on the XZ plane`, async () => {
|
||||
await editor.closePane()
|
||||
await toolbar.startSketchPlaneSelection()
|
||||
await moveToXzPlane()
|
||||
await clickOnXzPlane()
|
||||
// timeout wait for engine animation is unavoidable
|
||||
await app.page.waitForTimeout(600)
|
||||
await page.waitForTimeout(600)
|
||||
await editor.expectEditor.toContain(expectedCodeSnippets.sketchOnXzPlane)
|
||||
})
|
||||
await test.step(`Place a point a few pixels off the middle, verify it still snaps to 0,0`, async () => {
|
||||
@ -553,11 +602,15 @@ test(`Verify axis, origin, and horizontal snapping`, async ({
|
||||
})
|
||||
|
||||
test(`Verify user can double-click to edit a sketch`, async ({
|
||||
app,
|
||||
context,
|
||||
page,
|
||||
homePage,
|
||||
editor,
|
||||
toolbar,
|
||||
scene,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
const initialCode = `closedSketch = startSketchOn('XZ')
|
||||
|> circle({ center = [8, 5], radius = 2 }, %)
|
||||
openSketch = startSketchOn('XY')
|
||||
@ -566,15 +619,24 @@ openSketch = startSketchOn('XY')
|
||||
|> xLine(5, %)
|
||||
|> tangentialArcTo([10, 0], %)
|
||||
`
|
||||
await app.initialise(initialCode)
|
||||
const viewPortSize = { width: 1000, height: 500 }
|
||||
await page.setBodyDimensions(viewPortSize)
|
||||
|
||||
await context.addInitScript((code) => {
|
||||
localStorage.setItem('persistCode', code)
|
||||
}, initialCode)
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
const pointInsideCircle = {
|
||||
x: app.viewPortSize.width * 0.63,
|
||||
y: app.viewPortSize.height * 0.5,
|
||||
x: viewPortSize.width * 0.63,
|
||||
y: viewPortSize.height * 0.5,
|
||||
}
|
||||
const pointOnPathAfterSketching = {
|
||||
x: app.viewPortSize.width * 0.58,
|
||||
y: app.viewPortSize.height * 0.5,
|
||||
x: viewPortSize.width * 0.65,
|
||||
y: viewPortSize.height * 0.5,
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [_clickOpenPath, moveToOpenPath, dblClickOpenPath] =
|
||||
@ -607,41 +669,59 @@ openSketch = startSketchOn('XY')
|
||||
diagnostics: [],
|
||||
})
|
||||
})
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
await exitSketch()
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
// Drag the sketch line out of the axis view which blocks the click
|
||||
await page.dragAndDrop('#stream', '#stream', {
|
||||
sourcePosition: {
|
||||
x: viewPortSize.width * 0.7,
|
||||
y: viewPortSize.height * 0.5,
|
||||
},
|
||||
targetPosition: {
|
||||
x: viewPortSize.width * 0.7,
|
||||
y: viewPortSize.height * 0.4,
|
||||
},
|
||||
})
|
||||
|
||||
await page.waitForTimeout(500)
|
||||
|
||||
await test.step(`Double-click on the open sketch`, async () => {
|
||||
await moveToOpenPath()
|
||||
await scene.expectPixelColor([250, 250, 250], pointOnPathAfterSketching, 15)
|
||||
// There is a full execution after exiting sketch that clears the scene.
|
||||
await app.page.waitForTimeout(500)
|
||||
await page.waitForTimeout(500)
|
||||
await dblClickOpenPath()
|
||||
await expect(toolbar.startSketchBtn).not.toBeVisible()
|
||||
await expect(toolbar.exitSketchBtn).toBeVisible()
|
||||
// Wait for enter sketch mode to complete
|
||||
await app.page.waitForTimeout(500)
|
||||
await page.waitForTimeout(500)
|
||||
await editor.expectState({
|
||||
activeLines: [`|>xLine(5,%)`],
|
||||
highlightedCode: 'xLine(5,%)',
|
||||
activeLines: [`|>tangentialArcTo([10,0],%)`],
|
||||
highlightedCode: 'tangentialArcTo([10,0],%)',
|
||||
diagnostics: [],
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test(`Offset plane point-and-click`, async ({
|
||||
app,
|
||||
context,
|
||||
page,
|
||||
homePage,
|
||||
scene,
|
||||
editor,
|
||||
toolbar,
|
||||
cmdBar,
|
||||
}) => {
|
||||
await app.initialise()
|
||||
|
||||
// One dumb hardcoded screen pixel value
|
||||
const testPoint = { x: 700, y: 150 }
|
||||
const [clickOnXzPlane] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
||||
const expectedOutput = `plane001 = offsetPlane('XZ', 5)`
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await test.step(`Look for the blue of the XZ plane`, async () => {
|
||||
await scene.expectPixelColor([50, 51, 96], testPoint, 15)
|
||||
})
|
||||
@ -684,8 +764,9 @@ const loftPointAndClickCases = [
|
||||
]
|
||||
loftPointAndClickCases.forEach(({ shouldPreselect }) => {
|
||||
test(`Loft point-and-click (preselected sketches: ${shouldPreselect})`, async ({
|
||||
app,
|
||||
context,
|
||||
page,
|
||||
homePage,
|
||||
scene,
|
||||
editor,
|
||||
toolbar,
|
||||
@ -697,7 +778,11 @@ loftPointAndClickCases.forEach(({ shouldPreselect }) => {
|
||||
sketch002 = startSketchOn(plane001)
|
||||
|> circle({ center = [0, 0], radius = 20 }, %)
|
||||
`
|
||||
await app.initialise(initialCode)
|
||||
await context.addInitScript((initialCode) => {
|
||||
localStorage.setItem('persistCode', initialCode)
|
||||
}, initialCode)
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// One dumb hardcoded screen pixel value
|
||||
const testPoint = { x: 575, y: 200 }
|
||||
@ -716,7 +801,7 @@ loftPointAndClickCases.forEach(({ shouldPreselect }) => {
|
||||
await clickOnSketch1()
|
||||
await page.keyboard.down('Shift')
|
||||
await clickOnSketch2()
|
||||
await app.page.waitForTimeout(500)
|
||||
await page.waitForTimeout(500)
|
||||
await page.keyboard.up('Shift')
|
||||
}
|
||||
|
||||
@ -775,17 +860,25 @@ const shellPointAndClickCapCases = [
|
||||
]
|
||||
shellPointAndClickCapCases.forEach(({ shouldPreselect }) => {
|
||||
test(`Shell point-and-click cap (preselected sketches: ${shouldPreselect})`, async ({
|
||||
app,
|
||||
context,
|
||||
page,
|
||||
homePage,
|
||||
scene,
|
||||
editor,
|
||||
toolbar,
|
||||
cmdBar,
|
||||
}) => {
|
||||
// TODO: fix this test on windows after the electron migration
|
||||
test.skip(process.platform === 'win32', 'Skip on windows')
|
||||
const initialCode = `sketch001 = startSketchOn('XZ')
|
||||
|> circle({ center = [0, 0], radius = 30 }, %)
|
||||
extrude001 = extrude(30, sketch001)
|
||||
`
|
||||
await app.initialise(initialCode)
|
||||
await context.addInitScript((initialCode) => {
|
||||
localStorage.setItem('persistCode', initialCode)
|
||||
}, initialCode)
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// One dumb hardcoded screen pixel value
|
||||
const testPoint = { x: 575, y: 200 }
|
||||
@ -812,7 +905,7 @@ shellPointAndClickCapCases.forEach(({ shouldPreselect }) => {
|
||||
commandName: 'Shell',
|
||||
})
|
||||
await clickOnCap()
|
||||
await app.page.waitForTimeout(500)
|
||||
await page.waitForTimeout(500)
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.expectState({
|
||||
@ -828,7 +921,7 @@ shellPointAndClickCapCases.forEach(({ shouldPreselect }) => {
|
||||
} else {
|
||||
await test.step(`Preselect the cap`, async () => {
|
||||
await clickOnCap()
|
||||
await app.page.waitForTimeout(500)
|
||||
await page.waitForTimeout(500)
|
||||
})
|
||||
|
||||
await test.step(`Go through the command bar flow with a preselected face (cap)`, async () => {
|
||||
@ -860,8 +953,9 @@ shellPointAndClickCapCases.forEach(({ shouldPreselect }) => {
|
||||
})
|
||||
|
||||
test('Shell point-and-click wall', async ({
|
||||
app,
|
||||
context,
|
||||
page,
|
||||
homePage,
|
||||
scene,
|
||||
editor,
|
||||
toolbar,
|
||||
@ -876,7 +970,11 @@ test('Shell point-and-click wall', async ({
|
||||
|> close(%)
|
||||
extrude001 = extrude(40, sketch001)
|
||||
`
|
||||
await app.initialise(initialCode)
|
||||
await context.addInitScript((initialCode) => {
|
||||
localStorage.setItem('persistCode', initialCode)
|
||||
}, initialCode)
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// One dumb hardcoded screen pixel value
|
||||
const testPoint = { x: 580, y: 180 }
|
||||
@ -907,7 +1005,7 @@ extrude001 = extrude(40, sketch001)
|
||||
await clickOnCap()
|
||||
await page.keyboard.down('Shift')
|
||||
await clickOnWall()
|
||||
await app.page.waitForTimeout(500)
|
||||
await page.waitForTimeout(500)
|
||||
await page.keyboard.up('Shift')
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.progressCmdBar()
|
||||
|
@ -1,46 +1,37 @@
|
||||
import { test, expect, Page } from '@playwright/test'
|
||||
import { join } from 'path'
|
||||
import { test, expect, Page } from './zoo-test'
|
||||
import path from 'path'
|
||||
import * as fsp from 'fs/promises'
|
||||
import {
|
||||
getUtils,
|
||||
setup,
|
||||
setupElectron,
|
||||
tearDown,
|
||||
executorInputPath,
|
||||
} from './test-utils'
|
||||
import { getUtils, executorInputPath } from './test-utils'
|
||||
import { TEST_CODE_TRIGGER_ENGINE_EXPORT_ERROR } from './storageStates'
|
||||
import { bracket } from 'lib/exampleKcl'
|
||||
|
||||
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||
await setup(context, page, testInfo)
|
||||
})
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
|
||||
test.describe('Regression tests', () => {
|
||||
// bugs we found that don't fit neatly into other categories
|
||||
test('bad model has inline error #3251', async ({ page }) => {
|
||||
test('bad model has inline error #3251', async ({
|
||||
context,
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
// because the model has `line([0,0]..` it is valid code, but the model is invalid
|
||||
// regression test for https://github.com/KittyCAD/modeling-app/issues/3251
|
||||
// Since the bad model also found as issue with the artifact graph, which in tern blocked the editor diognostics
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
await context.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`sketch2 = startSketchOn("XY")
|
||||
sketch001 = startSketchAt([-0, -0])
|
||||
|> line([0, 0], %)
|
||||
|> line([-4.84, -5.29], %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)`
|
||||
sketch001 = startSketchAt([-0, -0])
|
||||
|> line([0, 0], %)
|
||||
|> line([-4.84, -5.29], %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)`
|
||||
)
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
// error in guter
|
||||
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
|
||||
@ -56,6 +47,7 @@ sketch001 = startSketchAt([-0, -0])
|
||||
})
|
||||
test('user should not have to press down twice in cmdbar', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
// because the model has `line([0,0]..` it is valid code, but the model is invalid
|
||||
// regression test for https://github.com/KittyCAD/modeling-app/issues/3251
|
||||
@ -64,26 +56,38 @@ sketch001 = startSketchAt([-0, -0])
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`sketch2 = startSketchOn("XY")
|
||||
sketch001 = startSketchAt([-0, -0])
|
||||
|> line([0, 0], %)
|
||||
|> line([-4.84, -5.29], %)
|
||||
`sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([82.33, 238.21], %)
|
||||
|> angledLine([0, 288.63], %, $rectangleSegmentA001)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001) - 90,
|
||||
197.97
|
||||
], %, $rectangleSegmentB001)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001),
|
||||
-segLen(rectangleSegmentA001)
|
||||
], %, $rectangleSegmentC001)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)`
|
||||
|> close(%)
|
||||
extrude001 = extrude(50, sketch001)
|
||||
`
|
||||
)
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await page.goto('/')
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await test.step('Check arrow down works', async () => {
|
||||
await page.getByTestId('command-bar-open-button').hover()
|
||||
await page.getByTestId('command-bar-open-button').click()
|
||||
|
||||
await page
|
||||
.getByRole('option', { name: 'floppy disk arrow Export' })
|
||||
.click()
|
||||
const floppy = page.getByRole('option', {
|
||||
name: 'floppy disk arrow Export',
|
||||
})
|
||||
|
||||
await floppy.click()
|
||||
|
||||
// press arrow down key twice
|
||||
await page.keyboard.press('ArrowDown')
|
||||
@ -115,21 +119,22 @@ sketch001 = startSketchAt([-0, -0])
|
||||
)
|
||||
})
|
||||
})
|
||||
test('executes on load', async ({ page }) => {
|
||||
test('executes on load', async ({ page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`sketch001 = startSketchOn('-XZ')
|
||||
|> startProfileAt([-6.95, 4.98], %)
|
||||
|> line([25.1, 0.41], %)
|
||||
|> line([0.73, -14.93], %)
|
||||
|> line([-23.44, 0.52], %)`
|
||||
|> startProfileAt([-6.95, 4.98], %)
|
||||
|> line([25.1, 0.41], %)
|
||||
|> line([0.73, -14.93], %)
|
||||
|> line([-23.44, 0.52], %)`
|
||||
)
|
||||
})
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
// expand variables section
|
||||
const variablesTabButton = page.getByTestId('variables-pane-button')
|
||||
@ -148,14 +153,15 @@ sketch001 = startSketchAt([-0, -0])
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
test('re-executes', async ({ page }) => {
|
||||
test('re-executes', async ({ page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem('persistCode', `myVar = 5`)
|
||||
})
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
const variablesTabButton = page.getByTestId('variables-pane-button')
|
||||
await variablesTabButton.click()
|
||||
@ -174,32 +180,33 @@ sketch001 = startSketchAt([-0, -0])
|
||||
page.locator('.pretty-json-container >> text=myVar:67')
|
||||
).toBeVisible()
|
||||
})
|
||||
test('ProgramMemory can be serialised', async ({ page }) => {
|
||||
test('ProgramMemory can be serialised', async ({ page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`part = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([0, 1], %)
|
||||
|> line([1, 0], %)
|
||||
|> line([0, -1], %)
|
||||
|> close(%)
|
||||
|> extrude(1, %)
|
||||
|> patternLinear3d({
|
||||
axis: [1, 0, 1],
|
||||
repetitions: 3,
|
||||
distance: 6
|
||||
}, %)`
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([0, 1], %)
|
||||
|> line([1, 0], %)
|
||||
|> line([0, -1], %)
|
||||
|> close(%)
|
||||
|> extrude(1, %)
|
||||
|> patternLinear3d({
|
||||
axis: [1, 0, 1],
|
||||
repetitions: 3,
|
||||
distance: 6
|
||||
}, %)`
|
||||
)
|
||||
})
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
const messages: string[] = []
|
||||
|
||||
// Listen for all console events and push the message text to an array
|
||||
page.on('console', (message) => messages.push(message.text()))
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
// wait for execution done
|
||||
await u.openDebugPanel()
|
||||
@ -212,19 +219,26 @@ sketch001 = startSketchAt([-0, -0])
|
||||
})
|
||||
})
|
||||
})
|
||||
test('ensure the Zoo logo is not a link in browser app', async ({ page }) => {
|
||||
|
||||
// Not relevant to us anymore, or at least for the time being.
|
||||
test.skip('ensure the Zoo logo is not a link in browser app', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
const zooLogo = page.locator('[data-testid="app-logo"]')
|
||||
// Make sure it's not a link
|
||||
await expect(zooLogo).not.toHaveAttribute('href')
|
||||
})
|
||||
|
||||
test(
|
||||
'Position _ Is Out Of Range... regression test',
|
||||
{ tag: ['@skipWin'] },
|
||||
async ({ page }) => {
|
||||
async ({ context, page, homePage }) => {
|
||||
// SKip on windows, its being weird.
|
||||
test.skip(
|
||||
process.platform === 'win32',
|
||||
@ -233,25 +247,26 @@ sketch001 = startSketchAt([-0, -0])
|
||||
|
||||
const u = await getUtils(page)
|
||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.addInitScript(async () => {
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await context.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`exampleSketch = startSketchOn("XZ")
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> angledLine({ angle: 50, length: 45 }, %)
|
||||
|> yLineTo(0, %)
|
||||
|> close(%)
|
||||
|>
|
||||
|
||||
example = extrude(5, exampleSketch)
|
||||
shell({ faces: ['end'], thickness: 0.25 }, exampleSketch)`
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> angledLine({ angle: 50, length: 45 }, %)
|
||||
|> yLineTo(0, %)
|
||||
|> close(%)
|
||||
|>
|
||||
|
||||
example = extrude(5, exampleSketch)
|
||||
shell({ faces: ['end'], thickness: 0.25 }, exampleSketch)`
|
||||
)
|
||||
})
|
||||
|
||||
await expect(async () => {
|
||||
await page.goto('/')
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
// error in guter
|
||||
await expect(page.locator('.cm-lint-marker-error')).toBeVisible({
|
||||
timeout: 1_000,
|
||||
@ -293,12 +308,12 @@ sketch001 = startSketchAt([-0, -0])
|
||||
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toContainText(`exampleSketch = startSketchOn("XZ")
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> angledLine({ angle: 50, length: 45 }, %)
|
||||
|> yLineTo(0, %)
|
||||
|> close(%)
|
||||
|
||||
thing: "blah"`)
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> angledLine({ angle: 50, length: 45 }, %)
|
||||
|> yLineTo(0, %)
|
||||
|> close(%)
|
||||
|
||||
thing: "blah"`)
|
||||
|
||||
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
|
||||
}
|
||||
@ -306,6 +321,7 @@ sketch001 = startSketchAt([-0, -0])
|
||||
|
||||
test('when engine fails export we handle the failure and alert the user', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(
|
||||
@ -316,9 +332,10 @@ sketch001 = startSketchAt([-0, -0])
|
||||
{ code: TEST_CODE_TRIGGER_ENGINE_EXPORT_ERROR }
|
||||
)
|
||||
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
// wait for execution done
|
||||
await u.openDebugPanel()
|
||||
@ -374,7 +391,6 @@ sketch001 = startSketchAt([-0, -0])
|
||||
|
||||
// wait for execution done
|
||||
await u.openDebugPanel()
|
||||
await u.clearCommandLogs()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
await u.closeDebugPanel()
|
||||
|
||||
@ -408,7 +424,7 @@ sketch001 = startSketchAt([-0, -0])
|
||||
test(
|
||||
'ensure you can not export while an export is already going',
|
||||
{ tag: ['@skipLinux', '@skipWin'] },
|
||||
async ({ page }) => {
|
||||
async ({ page, homePage }) => {
|
||||
// This is being weird on ubuntu and windows.
|
||||
test.skip(
|
||||
// eslint-disable-next-line jest/valid-title
|
||||
@ -428,9 +444,10 @@ sketch001 = startSketchAt([-0, -0])
|
||||
}
|
||||
)
|
||||
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
// wait for execution done
|
||||
await u.openDebugPanel()
|
||||
@ -500,20 +517,17 @@ sketch001 = startSketchAt([-0, -0])
|
||||
test(
|
||||
`Network health indicator only appears in modeling view`,
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName: _ }, testInfo) => {
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
const bracketDir = join(dir, 'bracket')
|
||||
await fsp.mkdir(bracketDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||
join(bracketDir, 'main.kcl')
|
||||
)
|
||||
},
|
||||
async ({ context, page }, testInfo) => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
const bracketDir = path.join(dir, 'bracket')
|
||||
await fsp.mkdir(bracketDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||
path.join(bracketDir, 'main.kcl')
|
||||
)
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
const u = await getUtils(page)
|
||||
|
||||
// Locators
|
||||
@ -539,18 +553,19 @@ sketch001 = startSketchAt([-0, -0])
|
||||
await u.waitForPageLoad()
|
||||
await expect(networkHealthIndicator).toContainText('Connected')
|
||||
})
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
||||
test(`View gizmo stays visible even when zoomed out all the way`, 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)
|
||||
|
||||
// Constants and locators
|
||||
const planeColor: [number, number, number] = [161, 220, 155]
|
||||
const planeColor: [number, number, number] = [170, 220, 170]
|
||||
const bgColor: [number, number, number] = [27, 27, 27]
|
||||
const middlePixelIsColor = async (color: [number, number, number]) => {
|
||||
return u.getGreatestPixDiff({ x: 600, y: 250 }, color)
|
||||
@ -561,8 +576,9 @@ sketch001 = startSketchAt([-0, -0])
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem('persistCode', '')
|
||||
})
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
await u.closeKclCodePanel()
|
||||
})
|
||||
|
||||
@ -572,7 +588,7 @@ sketch001 = startSketchAt([-0, -0])
|
||||
timeout: 5000,
|
||||
message: 'Plane color is visible',
|
||||
})
|
||||
.toBeLessThan(15)
|
||||
.toBeLessThanOrEqual(15)
|
||||
|
||||
let maxZoomOuts = 10
|
||||
let middlePixelIsBackgroundColor =
|
||||
@ -590,7 +606,7 @@ sketch001 = startSketchAt([-0, -0])
|
||||
}
|
||||
|
||||
expect(middlePixelIsBackgroundColor, {
|
||||
message: 'We no longer the default planes',
|
||||
message: 'We should not see the default planes',
|
||||
}).toBeTruthy()
|
||||
})
|
||||
|
||||
|
@ -47,7 +47,11 @@ test.beforeEach(async ({ page }) => {
|
||||
|
||||
test.setTimeout(60_000)
|
||||
|
||||
test(
|
||||
// We test this end to end already - getting this to work on web just to take
|
||||
// a snapshot of it feels weird. I'd rather our regular tests fail.
|
||||
// The primary failure is doExport now relies on the filesystem. We can follow
|
||||
// up with another PR if we want this back.
|
||||
test.skip(
|
||||
'exports of each format should work',
|
||||
{ tag: ['@snapshot', '@skipWin', '@skipMacos'] },
|
||||
async ({ page, context }) => {
|
||||
@ -1164,3 +1168,109 @@ test.fixme('theme persists', async ({ page, context }) => {
|
||||
maxDiffPixels: 100,
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('code color goober', { tag: '@snapshot' }, () => {
|
||||
test('code color goober', async ({ page, context }) => {
|
||||
const u = await getUtils(page)
|
||||
await context.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`// Create a pipe using a sweep.
|
||||
|
||||
// Create a path for the sweep.
|
||||
sweepPath = startSketchOn('XZ')
|
||||
|> startProfileAt([0.05, 0.05], %)
|
||||
|> line([0, 7], %)
|
||||
|> tangentialArc({ offset = 90, radius = 5 }, %)
|
||||
|> line([-3, 0], %)
|
||||
|> tangentialArc({ offset = -90, radius = 5 }, %)
|
||||
|> line([0, 7], %)
|
||||
|
||||
sweepSketch = startSketchOn('XY')
|
||||
|> startProfileAt([2, 0], %)
|
||||
|> arc({
|
||||
angleEnd = 360,
|
||||
angleStart = 0,
|
||||
radius = 2
|
||||
}, %)
|
||||
|> sweep({
|
||||
path = sweepPath,
|
||||
}, %)
|
||||
|> appearance({
|
||||
color = "#bb00ff",
|
||||
metalness = 90,
|
||||
roughness = 90
|
||||
}, %)
|
||||
`
|
||||
)
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 1000 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
await u.clearAndCloseDebugPanel()
|
||||
|
||||
await expect(page, 'expect small color widget').toHaveScreenshot({
|
||||
maxDiffPixels: 100,
|
||||
})
|
||||
})
|
||||
|
||||
test('code color goober opening window', async ({ page, context }) => {
|
||||
const u = await getUtils(page)
|
||||
await context.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`// Create a pipe using a sweep.
|
||||
|
||||
// Create a path for the sweep.
|
||||
sweepPath = startSketchOn('XZ')
|
||||
|> startProfileAt([0.05, 0.05], %)
|
||||
|> line([0, 7], %)
|
||||
|> tangentialArc({ offset = 90, radius = 5 }, %)
|
||||
|> line([-3, 0], %)
|
||||
|> tangentialArc({ offset = -90, radius = 5 }, %)
|
||||
|> line([0, 7], %)
|
||||
|
||||
sweepSketch = startSketchOn('XY')
|
||||
|> startProfileAt([2, 0], %)
|
||||
|> arc({
|
||||
angleEnd = 360,
|
||||
angleStart = 0,
|
||||
radius = 2
|
||||
}, %)
|
||||
|> sweep({
|
||||
path = sweepPath,
|
||||
}, %)
|
||||
|> appearance({
|
||||
color = "#bb00ff",
|
||||
metalness = 90,
|
||||
roughness = 90
|
||||
}, %)
|
||||
`
|
||||
)
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 1000 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
await u.clearAndCloseDebugPanel()
|
||||
|
||||
await expect(page.locator('.cm-css-color-picker-wrapper')).toBeVisible()
|
||||
|
||||
// Click the color widget
|
||||
await page.locator('.cm-css-color-picker-wrapper input').click()
|
||||
|
||||
await expect(
|
||||
page,
|
||||
'expect small color widget to have window open'
|
||||
).toHaveScreenshot({
|
||||
maxDiffPixels: 100,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 59 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 51 KiB |
After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 74 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 66 KiB |
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
After Width: | Height: | Size: 148 KiB |
After Width: | Height: | Size: 144 KiB |
After Width: | Height: | Size: 130 KiB |
After Width: | Height: | Size: 132 KiB |
After Width: | Height: | Size: 128 KiB |
After Width: | Height: | Size: 111 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 38 KiB |
@ -14,7 +14,7 @@ export const TEST_SETTINGS = {
|
||||
},
|
||||
modeling: {
|
||||
defaultUnit: 'in',
|
||||
mouseControls: 'KittyCAD',
|
||||
mouseControls: 'Zoo',
|
||||
cameraProjection: 'perspective',
|
||||
showDebugPanel: true,
|
||||
},
|
||||
@ -109,242 +109,21 @@ keychain = startSketchOn("XY")
|
||||
|> close(%)
|
||||
|> extrude(thickness, %)
|
||||
|
||||
// generated from /home/paultag/Downloads/zma-logomark.svg
|
||||
fn svg = (surface, origin, depth) => {
|
||||
let a0 = surface |> startProfileAt([origin[0] + 45.430427, origin[1] + -14.627736], %)
|
||||
|> bezierCurve({
|
||||
control1: [ 0, 0.764157 ],
|
||||
control2: [ 0, 1.528314 ],
|
||||
to: [ 0, 2.292469 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ -3.03202, 0 ],
|
||||
control2: [ -6.064039, 0 ],
|
||||
to: [ -9.09606, 0 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 0, -1.077657 ],
|
||||
control2: [ 0, -2.155312 ],
|
||||
to: [ 0, -3.232969 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 2.741805, 0 ],
|
||||
control2: [ 5.483613, 0 ],
|
||||
to: [ 8.225417, 0 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ -2.740682, -2.961815 ],
|
||||
control2: [ -5.490342, -5.925794 ],
|
||||
to: [ -8.225417, -8.886255 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 0, -0.723995 ],
|
||||
control2: [ 0, -1.447988 ],
|
||||
to: [ 0, -2.171981 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 0.712124, 0.05061 ],
|
||||
control2: [ 1.511636, -0.09877 ],
|
||||
to: [ 2.172096, 0.07005 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 0.68573, 0.740811 ],
|
||||
control2: [ 1.371459, 1.481622 ],
|
||||
to: [ 2.057187, 2.222436 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 0, -0.76416 ],
|
||||
control2: [ 0, -1.52832 ],
|
||||
to: [ 0, -2.29248 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 3.032013, 0 ],
|
||||
control2: [ 6.064026, 0 ],
|
||||
to: [ 9.096038, 0 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 0, 1.077657 ],
|
||||
control2: [ 0, 2.155314 ],
|
||||
to: [ 0, 3.232973 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ -2.741312, 0 ],
|
||||
control2: [ -5.482623, 0 ],
|
||||
to: [ -8.223936, 0 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 2.741313, 2.961108 ],
|
||||
control2: [ 5.482624, 5.922216 ],
|
||||
to: [ 8.223936, 8.883325 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 0, 0.724968 ],
|
||||
control2: [ 0, 1.449938 ],
|
||||
to: [ 0, 2.174907 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ -0.712656, -0.05145 ],
|
||||
control2: [ -1.512554, 0.09643 ],
|
||||
to: [ -2.173592, -0.07298 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ -0.685222, -0.739834 ],
|
||||
control2: [ -1.370445, -1.479669 ],
|
||||
to: [ -2.055669, -2.219505 ]
|
||||
}, %)
|
||||
|> close(%)
|
||||
|> extrude(depth, %)
|
||||
keychain1 = startSketchOn("XY")
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> lineTo([width, 0], %)
|
||||
|> lineTo([width, height], %)
|
||||
|> lineTo([0, height], %)
|
||||
|> close(%)
|
||||
|> extrude(thickness, %)
|
||||
|
||||
let a1 = surface |> startProfileAt([origin[0] + 57.920488, origin[1] + -15.244943], %)
|
||||
|> bezierCurve({
|
||||
control1: [ -2.78904, 0.106635 ],
|
||||
control2: [ -5.052548, -2.969529 ],
|
||||
to: [ -4.055141, -5.598369 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 0.841523, -0.918736 ],
|
||||
control2: [ 0.439412, -1.541892 ],
|
||||
to: [ -0.368488, -2.214378 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ -0.418245, -0.448461 ],
|
||||
control2: [ -0.836489, -0.896922 ],
|
||||
to: [ -1.254732, -1.345384 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ -2.76806, 2.995359 ],
|
||||
control2: [ -2.32667, 8.18409 ],
|
||||
to: [ 0.897655, 10.678932 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 2.562822, 2.186098 ],
|
||||
control2: [ 6.605111, 2.28043 ],
|
||||
to: [ 9.271202, 0.226476 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ -0.743744, -0.797465 ],
|
||||
control2: [ -1.487487, -1.594932 ],
|
||||
to: [ -2.231232, -2.392397 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ -0.672938, 0.421422 ],
|
||||
control2: [ -1.465362, 0.646946 ],
|
||||
to: [ -2.259264, 0.64512 ]
|
||||
}, %)
|
||||
|> close(%)
|
||||
|> extrude(depth, %)
|
||||
|
||||
let a2 = surface |> startProfileAt([origin[0] + 62.19406300000001, origin[1] + -19.500698999999997], %)
|
||||
|> bezierCurve({
|
||||
control1: [ 0.302938, 1.281141 ],
|
||||
control2: [ -1.53575, 2.434288 ],
|
||||
to: [ -0.10908, 3.279477 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 0.504637, 0.54145 ],
|
||||
control2: [ 1.009273, 1.082899 ],
|
||||
to: [ 1.513909, 1.624348 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 2.767778, -2.995425 ],
|
||||
control2: [ 2.327135, -8.184384 ],
|
||||
to: [ -0.897661, -10.679047 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ -2.562947, -2.186022 ],
|
||||
control2: [ -6.604089, -2.279606 ],
|
||||
to: [ -9.271196, -0.227813 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 0.744231, 0.797952 ],
|
||||
control2: [ 1.488461, 1.595904 ],
|
||||
to: [ 2.232692, 2.393856 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 2.302377, -1.564629 ],
|
||||
control2: [ 5.793126, -0.15358 ],
|
||||
to: [ 6.396577, 2.547372 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 0.08981, 0.346302 ],
|
||||
control2: [ 0.134865, 0.704078 ],
|
||||
to: [ 0.13476, 1.061807 ]
|
||||
}, %)
|
||||
|> close(%)
|
||||
|> extrude(depth, %)
|
||||
|
||||
let a3 = surface |> startProfileAt([origin[0] + 74.124866, origin[1] + -15.244943], %)
|
||||
|> bezierCurve({
|
||||
control1: [ -2.78904, 0.106635 ],
|
||||
control2: [ -5.052549, -2.969529 ],
|
||||
to: [ -4.055142, -5.598369 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 0.841527, -0.918738 ],
|
||||
control2: [ 0.43941, -1.541892 ],
|
||||
to: [ -0.368497, -2.214367 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ -0.418254, -0.448466 ],
|
||||
control2: [ -0.836507, -0.896931 ],
|
||||
to: [ -1.254761, -1.345395 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ -2.768019, 2.995371 ],
|
||||
control2: [ -2.326624, 8.184088 ],
|
||||
to: [ 0.897678, 10.678932 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 2.56289, 2.186191 ],
|
||||
control2: [ 6.60516, 2.280307 ],
|
||||
to: [ 9.271371, 0.226476 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ -0.743808, -0.797465 ],
|
||||
control2: [ -1.487616, -1.594932 ],
|
||||
to: [ -2.231424, -2.392397 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ -0.672916, 0.421433 ],
|
||||
control2: [ -1.465344, 0.646926 ],
|
||||
to: [ -2.259225, 0.64512 ]
|
||||
}, %)
|
||||
|> close(%)
|
||||
|> extrude(depth, %)
|
||||
|
||||
let a4 = surface |> startProfileAt([origin[0] + 77.57333899999998, origin[1] + -16.989262999999998], %)
|
||||
|> bezierCurve({
|
||||
control1: [ 0.743298, 0.797463 ],
|
||||
control2: [ 1.486592, 1.594926 ],
|
||||
to: [ 2.229888, 2.392389 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 2.767827, -2.995393 ],
|
||||
control2: [ 2.327103, -8.184396 ],
|
||||
to: [ -0.897672, -10.679047 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ -2.562939, -2.186037 ],
|
||||
control2: [ -6.604077, -2.279589 ],
|
||||
to: [ -9.271185, -0.227813 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 0.744243, 0.797952 ],
|
||||
control2: [ 1.488486, 1.595904 ],
|
||||
to: [ 2.232729, 2.393856 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 2.302394, -1.564623 ],
|
||||
control2: [ 5.793201, -0.153598 ],
|
||||
to: [ 6.396692, 2.547372 ]
|
||||
}, %)
|
||||
|> bezierCurve({
|
||||
control1: [ 0.32074, 1.215468 ],
|
||||
control2: [ 0.06159, 2.564765 ],
|
||||
to: [ -0.690452, 3.573243 ]
|
||||
}, %)
|
||||
|> close(%)
|
||||
|> extrude(depth, %)
|
||||
keychain2 = startSketchOn("XY")
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> lineTo([width, 0], %)
|
||||
|> lineTo([width, height], %)
|
||||
|> lineTo([0, height], %)
|
||||
|> close(%)
|
||||
|> extrude(thickness, %)
|
||||
|
||||
box = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
@ -354,7 +133,7 @@ box = startSketchOn('XY')
|
||||
|> close(%)
|
||||
|> extrude(10, %)
|
||||
|
||||
sketch001 = startSketchOn(box, revolveAxis)
|
||||
sketch001 = startSketchOn(box, revolveAxis)
|
||||
|> startProfileAt([5, 10], %)
|
||||
|> line([0, -10], %)
|
||||
|> line([2, 0], %)
|
||||
@ -364,18 +143,12 @@ box = startSketchOn('XY')
|
||||
axis: revolveAxis,
|
||||
angle: 90
|
||||
}, %)
|
||||
return 0
|
||||
}
|
||||
|
||||
sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([0.0, 0.0], %)
|
||||
|> xLine(0.0, %)
|
||||
|> close(%)
|
||||
|
||||
|
||||
svg(startSketchOn(keychain, 'end'), [-33, 32], -thickness)
|
||||
|
||||
startSketchOn(keychain, 'end')
|
||||
|> circle({ center: [
|
||||
width / 2,
|
||||
height - (keychainHoleSize + 1.5)
|
||||
], radius: keychainHoleSize }, %)
|
||||
|> extrude(-thickness, %)`
|
||||
`
|
||||
|
||||
export const TEST_CODE_TRIGGER_ENGINE_EXPORT_ERROR = `thing = 1`
|
||||
|
@ -1,29 +1,16 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { test, expect } from './zoo-test'
|
||||
|
||||
import { commonPoints, getUtils, setup, tearDown } from './test-utils'
|
||||
|
||||
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||
await setup(context, page, testInfo)
|
||||
})
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
import { commonPoints, getUtils } from './test-utils'
|
||||
|
||||
test.describe('Test network and connection issues', () => {
|
||||
test('simulate network down and network little widget', async ({
|
||||
page,
|
||||
browserName,
|
||||
homePage,
|
||||
}) => {
|
||||
// TODO: Don't skip Mac for these. After `window.tearDown` is working in Safari, these should work on webkit
|
||||
test.skip(
|
||||
browserName === 'webkit',
|
||||
'Skip on Safari until `window.tearDown` is working there'
|
||||
)
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
const networkToggle = page.getByTestId('network-toggle')
|
||||
|
||||
@ -62,7 +49,7 @@ test.describe('Test network and connection issues', () => {
|
||||
})
|
||||
|
||||
// Expect the network to be down
|
||||
await expect(networkToggle).toContainText('Offline')
|
||||
await expect(networkToggle).toContainText('Problem')
|
||||
|
||||
// Click the network widget
|
||||
await networkWidget.click()
|
||||
@ -93,26 +80,19 @@ test.describe('Test network and connection issues', () => {
|
||||
|
||||
test('Engine disconnect & reconnect in sketch mode', async ({
|
||||
page,
|
||||
browserName,
|
||||
homePage,
|
||||
}) => {
|
||||
// TODO: Don't skip Mac for these. After `window.tearDown` is working in Safari, these should work on webkit
|
||||
test.skip(
|
||||
browserName === 'webkit',
|
||||
'Skip on Safari until `window.tearDown` is working there'
|
||||
)
|
||||
const networkToggle = page.getByTestId('network-toggle')
|
||||
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await u.openDebugPanel()
|
||||
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Start Sketch' })
|
||||
).not.toBeDisabled({ timeout: 15000 })
|
||||
|
||||
// click on "Start Sketch" button
|
||||
await u.clearCommandLogs()
|
||||
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||
@ -132,7 +112,7 @@ test.describe('Test network and connection issues', () => {
|
||||
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt(${commonPoints.startAt}, %)`)
|
||||
|> startProfileAt(${commonPoints.startAt}, %)`)
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
||||
@ -140,8 +120,8 @@ test.describe('Test network and connection issues', () => {
|
||||
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt(${commonPoints.startAt}, %)
|
||||
|> xLine(${commonPoints.num1}, %)`)
|
||||
|> startProfileAt(${commonPoints.startAt}, %)
|
||||
|> xLine(${commonPoints.num1}, %)`)
|
||||
|
||||
// Expect the network to be up
|
||||
await expect(networkToggle).toContainText('Connected')
|
||||
@ -156,7 +136,7 @@ test.describe('Test network and connection issues', () => {
|
||||
})
|
||||
|
||||
// Expect the network to be down
|
||||
await expect(networkToggle).toContainText('Offline')
|
||||
await expect(networkToggle).toContainText('Problem')
|
||||
|
||||
// Ensure we are not in sketch mode
|
||||
await expect(
|
||||
|
@ -1,22 +1,21 @@
|
||||
import {
|
||||
expect,
|
||||
Page,
|
||||
Download,
|
||||
BrowserContext,
|
||||
TestInfo,
|
||||
_electron as electron,
|
||||
ElectronApplication,
|
||||
Locator,
|
||||
test,
|
||||
} from '@playwright/test'
|
||||
import { test, Page } from './zoo-test'
|
||||
import { EngineCommand } from 'lang/std/artifactGraph'
|
||||
import fsp from 'fs/promises'
|
||||
import fsSync from 'fs'
|
||||
import { join } from 'path'
|
||||
import path from 'path'
|
||||
import pixelMatch from 'pixelmatch'
|
||||
import { PNG } from 'pngjs'
|
||||
import { Protocol } from 'playwright-core/types/protocol'
|
||||
import type { Models } from '@kittycad/lib'
|
||||
import { APP_NAME, COOKIE_NAME } from 'lib/constants'
|
||||
import { COOKIE_NAME } from 'lib/constants'
|
||||
import { secrets } from './secrets'
|
||||
import {
|
||||
TEST_SETTINGS_KEY,
|
||||
@ -30,6 +29,10 @@ import { isErrorWhitelisted } from './lib/console-error-whitelist'
|
||||
import { isArray } from 'lib/utils'
|
||||
import { reportRejection } from 'lib/trap'
|
||||
|
||||
const toNormalizedCode = (text: string) => {
|
||||
return text.replace(/\s+/g, '')
|
||||
}
|
||||
|
||||
type TestColor = [number, number, number]
|
||||
export const TEST_COLORS = {
|
||||
WHITE: [249, 249, 249] as TestColor,
|
||||
@ -98,11 +101,16 @@ async function removeCurrentCode(page: Page) {
|
||||
}
|
||||
|
||||
export async function sendCustomCmd(page: Page, cmd: EngineCommand) {
|
||||
await page.getByTestId('custom-cmd-input').fill(JSON.stringify(cmd))
|
||||
const json = JSON.stringify(cmd)
|
||||
await page.getByTestId('custom-cmd-input').fill(json)
|
||||
await expect(page.getByTestId('custom-cmd-input')).toHaveValue(json)
|
||||
await page.getByTestId('custom-cmd-send-button').scrollIntoViewIfNeeded()
|
||||
await page.getByTestId('custom-cmd-send-button').click()
|
||||
}
|
||||
|
||||
async function clearCommandLogs(page: Page) {
|
||||
await page.getByTestId('custom-cmd-input').fill('')
|
||||
await page.getByTestId('clear-commands').scrollIntoViewIfNeeded()
|
||||
await page.getByTestId('clear-commands').click()
|
||||
}
|
||||
|
||||
@ -150,6 +158,19 @@ export async function closePane(page: Page, testId: string) {
|
||||
|
||||
async function openKclCodePanel(page: Page) {
|
||||
await openPane(page, 'code-pane-button')
|
||||
|
||||
// Code Mirror lazy loads text! Wowza! Let's force-load the text for tests.
|
||||
await page.evaluate(() => {
|
||||
// editorManager is available on the window object.
|
||||
//@ts-ignore this is in an entirely different context that tsc can't see.
|
||||
editorManager._editorView.dispatch({
|
||||
selection: {
|
||||
//@ts-ignore this is in an entirely different context that tsc can't see.
|
||||
anchor: editorManager._editorView.docView.length,
|
||||
},
|
||||
scrollIntoView: true,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async function closeKclCodePanel(page: Page) {
|
||||
@ -165,6 +186,9 @@ async function closeKclCodePanel(page: Page) {
|
||||
|
||||
async function openDebugPanel(page: Page) {
|
||||
await openPane(page, 'debug-pane-button')
|
||||
|
||||
// The debug pane needs time to load everything.
|
||||
await page.waitForTimeout(3000)
|
||||
}
|
||||
|
||||
export async function closeDebugPanel(page: Page) {
|
||||
@ -412,6 +436,10 @@ export async function getUtils(page: Page, test_?: typeof test) {
|
||||
.boundingBox({ timeout: 5_000 })
|
||||
.then((box) => ({ ...box, x: box?.x || 0, y: box?.y || 0 })),
|
||||
codeLocator: page.locator('.cm-content'),
|
||||
crushKclCodeIntoOneLineAndThenMaybeSome: async () => {
|
||||
const code = await page.locator('.cm-content').innerText()
|
||||
return code.replaceAll(' ', '').replaceAll('\n', '')
|
||||
},
|
||||
normalisedEditorCode: async () => {
|
||||
const code = await page.locator('.cm-content').innerText()
|
||||
return normaliseKclNumbers(code)
|
||||
@ -482,13 +510,18 @@ export async function getUtils(page: Page, test_?: typeof test) {
|
||||
)
|
||||
},
|
||||
|
||||
toNormalizedCode: (text: string) => {
|
||||
return text.replace(/\s+/g, '')
|
||||
toNormalizedCode(text: string) {
|
||||
return toNormalizedCode(text)
|
||||
},
|
||||
|
||||
editorTextMatches: async (code: string) => {
|
||||
async editorTextMatches(code: string) {
|
||||
const editor = page.locator(editorSelector)
|
||||
return expect(editor).toHaveText(code, { useInnerText: true })
|
||||
return expect
|
||||
.poll(async () => {
|
||||
const text = await editor.textContent()
|
||||
return toNormalizedCode(text ?? '')
|
||||
})
|
||||
.toContain(toNormalizedCode(code))
|
||||
},
|
||||
|
||||
pasteCodeInEditor: async (code: string) => {
|
||||
@ -514,7 +547,7 @@ export async function getUtils(page: Page, test_?: typeof test) {
|
||||
page.getByRole('button', { name: 'Start Sketch' })
|
||||
).not.toBeDisabled()
|
||||
await page.getByTestId('create-file-button').click()
|
||||
await page.getByTestId('file-rename-field').fill(name)
|
||||
await page.getByTestId('tree-input-field').fill(name)
|
||||
await page.keyboard.press('Enter')
|
||||
})
|
||||
},
|
||||
@ -674,6 +707,34 @@ export const makeTemplate: (
|
||||
}
|
||||
}
|
||||
|
||||
const PLAYWRIGHT_DOWNLOAD_DIR = 'downloads-during-playwright'
|
||||
|
||||
export const getPlaywrightDownloadDir = (page: Page) => {
|
||||
return path.resolve(page.dir, PLAYWRIGHT_DOWNLOAD_DIR)
|
||||
}
|
||||
|
||||
const moveDownloadedFileTo = async (page: Page, toLocation: string) => {
|
||||
await fsp.mkdir(path.dirname(toLocation), { recursive: true })
|
||||
|
||||
const downloadDir = getPlaywrightDownloadDir(page)
|
||||
|
||||
// Expect there to be at least one file
|
||||
await expect
|
||||
.poll(async () => {
|
||||
const files = await fsp.readdir(downloadDir)
|
||||
return files.length
|
||||
})
|
||||
.toBeGreaterThan(0)
|
||||
|
||||
// Go through the downloads dir and move files to new location
|
||||
const files = await fsp.readdir(downloadDir)
|
||||
|
||||
// Assumption: only ever one file here.
|
||||
for (let file of files) {
|
||||
await fsp.rename(path.resolve(downloadDir, file), toLocation)
|
||||
}
|
||||
}
|
||||
|
||||
export interface Paths {
|
||||
modelPath: string
|
||||
imagePath: string
|
||||
@ -686,7 +747,8 @@ export const doExport = async (
|
||||
exportFrom: 'dropdown' | 'sidebarButton' | 'commandBar' = 'dropdown'
|
||||
): Promise<Paths> => {
|
||||
if (exportFrom === 'dropdown') {
|
||||
await page.getByRole('button', { name: APP_NAME }).click()
|
||||
await page.getByTestId('project-sidebar-toggle').click()
|
||||
|
||||
const exportMenuButton = page.getByRole('button', {
|
||||
name: 'Export current part',
|
||||
})
|
||||
@ -727,25 +789,12 @@ export const doExport = async (
|
||||
}
|
||||
await expect(page.getByText('Confirm Export')).toBeVisible()
|
||||
|
||||
const getPromiseAndResolve = () => {
|
||||
let resolve: any = () => {}
|
||||
const promise = new Promise<Download>((r) => {
|
||||
resolve = r
|
||||
})
|
||||
return [promise, resolve]
|
||||
}
|
||||
|
||||
const [downloadPromise1, downloadResolve1] = getPromiseAndResolve()
|
||||
let downloadCnt = 0
|
||||
|
||||
if (exportFrom === 'dropdown')
|
||||
page.on('download', async (download) => {
|
||||
if (downloadCnt === 0) {
|
||||
downloadResolve1(download)
|
||||
}
|
||||
downloadCnt++
|
||||
})
|
||||
await page.getByRole('button', { name: 'Submit command' }).click()
|
||||
|
||||
// This usually happens immediately after. If we're too slow we don't
|
||||
// catch it.
|
||||
await expect(page.getByText('Exported successfully')).toBeVisible()
|
||||
|
||||
if (exportFrom === 'sidebarButton' || exportFrom === 'commandBar') {
|
||||
return {
|
||||
modelPath: '',
|
||||
@ -755,15 +804,12 @@ export const doExport = async (
|
||||
}
|
||||
|
||||
// Handle download
|
||||
const download = await downloadPromise1
|
||||
const downloadLocationer = (extra = '', isImage = false) =>
|
||||
`./e2e/playwright/export-snapshots/${output.type}-${
|
||||
'storage' in output ? output.storage : ''
|
||||
}${extra}.${isImage ? 'png' : output.type}`
|
||||
const downloadLocation = downloadLocationer()
|
||||
|
||||
await download.saveAs(downloadLocation)
|
||||
|
||||
if (output.type === 'step') {
|
||||
// stable timestamps for step files
|
||||
const fileContents = await fsp.readFile(downloadLocation, 'utf-8')
|
||||
@ -772,6 +818,12 @@ export const doExport = async (
|
||||
'1970-01-01T00:00:00.0+00:00'
|
||||
)
|
||||
await fsp.writeFile(downloadLocation, newFileContents)
|
||||
} else {
|
||||
// By default all files are downloaded to the same place in playwright
|
||||
// (declared in src/lib/exportSave)
|
||||
// To remain consistent with our old web tests, we want to move some downloads
|
||||
// (images) to another directory.
|
||||
await moveDownloadedFileTo(page, downloadLocation)
|
||||
}
|
||||
|
||||
return {
|
||||
@ -798,6 +850,8 @@ export async function tearDown(page: Page, testInfo: TestInfo) {
|
||||
// It seems it's best to give the browser about 3s to close things
|
||||
// It's not super reliable but we have no real other choice for now
|
||||
await page.waitForTimeout(3000)
|
||||
|
||||
await testInfo.tronApp?.close()
|
||||
}
|
||||
|
||||
// settingsOverrides may need to be augmented to take more generic items,
|
||||
@ -808,12 +862,24 @@ export async function setup(
|
||||
testInfo?: TestInfo
|
||||
) {
|
||||
await context.addInitScript(
|
||||
async ({ token, settingsKey, settings, IS_PLAYWRIGHT_KEY }) => {
|
||||
async ({
|
||||
token,
|
||||
settingsKey,
|
||||
settings,
|
||||
IS_PLAYWRIGHT_KEY,
|
||||
PLAYWRIGHT_TEST_DIR,
|
||||
PERSIST_MODELING_CONTEXT,
|
||||
}) => {
|
||||
localStorage.clear()
|
||||
localStorage.setItem('TOKEN_PERSIST_KEY', token)
|
||||
localStorage.setItem('persistCode', ``)
|
||||
localStorage.setItem(
|
||||
PERSIST_MODELING_CONTEXT,
|
||||
JSON.stringify({ openPanes: ['code'] })
|
||||
)
|
||||
localStorage.setItem(settingsKey, settings)
|
||||
localStorage.setItem(IS_PLAYWRIGHT_KEY, 'true')
|
||||
localStorage.setItem('PLAYWRIGHT_TEST_DIR', PLAYWRIGHT_TEST_DIR)
|
||||
},
|
||||
{
|
||||
token: secrets.token,
|
||||
@ -830,6 +896,8 @@ export async function setup(
|
||||
} as Partial<SaveSettingsPayload>,
|
||||
}),
|
||||
IS_PLAYWRIGHT_KEY,
|
||||
PLAYWRIGHT_TEST_DIR: TEST_SETTINGS.app.projectDirectory,
|
||||
PERSIST_MODELING_CONTEXT,
|
||||
}
|
||||
)
|
||||
|
||||
@ -848,12 +916,15 @@ export async function setup(
|
||||
await page.emulateMedia({ reducedMotion: 'reduce' })
|
||||
|
||||
// Trigger a navigation, since loading file:// doesn't.
|
||||
await page.reload()
|
||||
// await page.reload()
|
||||
}
|
||||
|
||||
let electronApp: ElectronApplication | undefined = undefined
|
||||
let context: BrowserContext | undefined = undefined
|
||||
let page: Page | undefined = undefined
|
||||
|
||||
export async function setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn,
|
||||
cleanProjectDir = true,
|
||||
appSettings,
|
||||
}: {
|
||||
@ -861,7 +932,12 @@ export async function setupElectron({
|
||||
folderSetupFn?: (projectDirName: string) => Promise<void>
|
||||
cleanProjectDir?: boolean
|
||||
appSettings?: Partial<SaveSettingsPayload>
|
||||
}) {
|
||||
}): Promise<{
|
||||
electronApp: ElectronApplication
|
||||
context: BrowserContext
|
||||
page: Page
|
||||
dir: string
|
||||
}> {
|
||||
// create or otherwise clear the folder
|
||||
const projectDirName = testInfo.outputPath('electron-test-projects-dir')
|
||||
try {
|
||||
@ -876,7 +952,7 @@ export async function setupElectron({
|
||||
await fsp.mkdir(projectDirName)
|
||||
}
|
||||
|
||||
const electronApp = await electron.launch({
|
||||
const options = {
|
||||
args: ['.', '--no-sandbox'],
|
||||
env: {
|
||||
...process.env,
|
||||
@ -886,14 +962,22 @@ export async function setupElectron({
|
||||
...(process.env.ELECTRON_OVERRIDE_DIST_PATH
|
||||
? { executablePath: process.env.ELECTRON_OVERRIDE_DIST_PATH + 'electron' }
|
||||
: {}),
|
||||
})
|
||||
const context = electronApp.context()
|
||||
const page = await electronApp.firstWindow()
|
||||
context.on('console', console.log)
|
||||
page.on('console', console.log)
|
||||
}
|
||||
|
||||
// Do this once and then reuse window on subsequent calls.
|
||||
if (!electronApp) {
|
||||
electronApp = await electron.launch(options)
|
||||
}
|
||||
|
||||
if (!context || !page) {
|
||||
context = electronApp.context()
|
||||
page = await electronApp.firstWindow()
|
||||
context.on('console', console.log)
|
||||
page.on('console', console.log)
|
||||
}
|
||||
|
||||
if (cleanProjectDir) {
|
||||
const tempSettingsFilePath = join(projectDirName, SETTINGS_FILE_NAME)
|
||||
const tempSettingsFilePath = path.join(projectDirName, SETTINGS_FILE_NAME)
|
||||
const settingsOverrides = TOML.stringify(
|
||||
appSettings
|
||||
? {
|
||||
@ -920,11 +1004,7 @@ export async function setupElectron({
|
||||
await fsp.writeFile(tempSettingsFilePath, settingsOverrides)
|
||||
}
|
||||
|
||||
await folderSetupFn?.(projectDirName)
|
||||
|
||||
await setup(context, page)
|
||||
|
||||
return { electronApp, page, dir: projectDirName }
|
||||
return { electronApp, page, context, dir: projectDirName }
|
||||
}
|
||||
|
||||
function failOnConsoleErrors(page: Page, testInfo?: TestInfo) {
|
||||
@ -1010,7 +1090,7 @@ export async function createProject({
|
||||
}
|
||||
|
||||
export function executorInputPath(fileName: string): string {
|
||||
return join('src', 'wasm-lib', 'tests', 'executor', 'inputs', fileName)
|
||||
return path.join('src', 'wasm-lib', 'tests', 'executor', 'inputs', fileName)
|
||||
}
|
||||
|
||||
export async function doAndWaitForImageDiff(
|
||||
@ -1101,3 +1181,12 @@ export function getPixelRGBs(page: Page) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export async function pollEditorLinesSelectedLength(page: Page, lines: number) {
|
||||
return expect
|
||||
.poll(async () => {
|
||||
const lines = await page.locator('.cm-activeLine').all()
|
||||
return lines.length
|
||||
})
|
||||
.toBe(lines)
|
||||
}
|
||||
|
@ -1,23 +1,20 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { test, expect } from './zoo-test'
|
||||
import { EngineCommand } from 'lang/std/artifactGraph'
|
||||
import { uuidv4 } from 'lib/utils'
|
||||
import { getUtils, setup, tearDown } from './test-utils'
|
||||
|
||||
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||
await setup(context, page, testInfo)
|
||||
})
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
import { getUtils } from './test-utils'
|
||||
|
||||
test.describe('Testing Camera Movement', () => {
|
||||
test('Can move camera reliably', async ({ page, context }) => {
|
||||
test.skip(process.platform === 'darwin', 'Can move camera reliably')
|
||||
test('Can move camera reliably', async ({ page, context, homePage }) => {
|
||||
// TODO: fix this test on windows too after the electron migration
|
||||
const winOrMac =
|
||||
process.platform === 'win32' || process.platform === 'darwin'
|
||||
// eslint-disable-next-line
|
||||
test.skip(winOrMac, 'Skip on windows')
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
await u.openAndClearDebugPanel()
|
||||
await u.closeKclCodePanel()
|
||||
|
||||
@ -181,170 +178,177 @@ test.describe('Testing Camera Movement', () => {
|
||||
}, [0, -85, -85])
|
||||
})
|
||||
|
||||
test('Zoom should be consistent when exiting or entering sketches', async ({
|
||||
page,
|
||||
}) => {
|
||||
// start new sketch pan and zoom before exiting, when exiting the sketch should stay in the same place
|
||||
// than zoom and pan outside of sketch mode and enter again and it should not change from where it is
|
||||
// than again for sketching
|
||||
// TODO: fix after electron migration is merged
|
||||
test.fixme(
|
||||
'Zoom should be consistent when exiting or entering sketches',
|
||||
async ({ page, homePage }) => {
|
||||
// start new sketch pan and zoom before exiting, when exiting the sketch should stay in the same place
|
||||
// than zoom and pan outside of sketch mode and enter again and it should not change from where it is
|
||||
// than again for sketching
|
||||
|
||||
test.skip(process.platform !== 'darwin', 'Zoom should be consistent')
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
const u = await getUtils(page)
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await u.openDebugPanel()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
await u.openDebugPanel()
|
||||
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Start Sketch' })
|
||||
).not.toBeDisabled()
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Start Sketch' })
|
||||
).toBeVisible()
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Start Sketch' })
|
||||
).not.toBeDisabled()
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Start Sketch' })
|
||||
).toBeVisible()
|
||||
|
||||
// click on "Start Sketch" button
|
||||
await u.clearCommandLogs()
|
||||
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
// select a plane
|
||||
await page.mouse.click(700, 325)
|
||||
|
||||
let code = `sketch001 = startSketchOn('XY')`
|
||||
await expect(u.codeLocator).toHaveText(code)
|
||||
await u.closeDebugPanel()
|
||||
|
||||
await page.waitForTimeout(500) // TODO detect animation ending, or disable animation
|
||||
|
||||
// move the camera slightly
|
||||
await page.keyboard.down('Shift')
|
||||
await page.mouse.move(700, 300)
|
||||
await page.mouse.down({ button: 'right' })
|
||||
await page.mouse.move(800, 200)
|
||||
await page.mouse.up({ button: 'right' })
|
||||
await page.keyboard.up('Shift')
|
||||
|
||||
let y = 350,
|
||||
x = 948
|
||||
|
||||
await u.canvasLocator.click({ position: { x: 783, y } })
|
||||
code += `\n |> startProfileAt([8.12, -12.98], %)`
|
||||
// await expect(u.codeLocator).toHaveText(code)
|
||||
await u.canvasLocator.click({ position: { x, y } })
|
||||
code += `\n |> line([11.18, 0], %)`
|
||||
// await expect(u.codeLocator).toHaveText(code)
|
||||
await u.canvasLocator.click({ position: { x, y: 275 } })
|
||||
code += `\n |> line([0, 6.99], %)`
|
||||
// await expect(u.codeLocator).toHaveText(code)
|
||||
|
||||
// click the line button
|
||||
await page.getByRole('button', { name: 'line Line', exact: true }).click()
|
||||
|
||||
const hoverOverNothing = async () => {
|
||||
// await u.canvasLocator.hover({position: {x: 700, y: 325}})
|
||||
await page.mouse.move(700, 325)
|
||||
// click on "Start Sketch" button
|
||||
await u.clearCommandLogs()
|
||||
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||
await page.waitForTimeout(100)
|
||||
await expect(page.getByTestId('hover-highlight')).not.toBeVisible({
|
||||
|
||||
// select a plane
|
||||
await page.mouse.click(700, 325)
|
||||
|
||||
let code = `sketch001 = startSketchOn('XY')`
|
||||
await expect(u.codeLocator).toHaveText(code)
|
||||
await u.closeDebugPanel()
|
||||
|
||||
await page.waitForTimeout(500) // TODO detect animation ending, or disable animation
|
||||
|
||||
// move the camera slightly
|
||||
await page.keyboard.down('Shift')
|
||||
await page.mouse.move(700, 300)
|
||||
await page.mouse.down({ button: 'right' })
|
||||
await page.mouse.move(800, 200)
|
||||
await page.mouse.up({ button: 'right' })
|
||||
await page.keyboard.up('Shift')
|
||||
|
||||
let y = 350,
|
||||
x = 948
|
||||
|
||||
await u.canvasLocator.click({ position: { x: 783, y } })
|
||||
code += `\n |> startProfileAt([8.12, -12.98], %)`
|
||||
// await expect(u.codeLocator).toHaveText(code)
|
||||
await u.canvasLocator.click({ position: { x, y } })
|
||||
code += `\n |> line([11.18, 0], %)`
|
||||
// await expect(u.codeLocator).toHaveText(code)
|
||||
await u.canvasLocator.click({ position: { x, y: 275 } })
|
||||
code += `\n |> line([0, 6.99], %)`
|
||||
// await expect(u.codeLocator).toHaveText(code)
|
||||
|
||||
// click the line button
|
||||
await page.getByRole('button', { name: 'line Line', exact: true }).click()
|
||||
|
||||
const hoverOverNothing = async () => {
|
||||
// await u.canvasLocator.hover({position: {x: 700, y: 325}})
|
||||
await page.mouse.move(700, 325)
|
||||
await page.waitForTimeout(100)
|
||||
await expect(page.getByTestId('hover-highlight')).not.toBeVisible({
|
||||
timeout: 10_000,
|
||||
})
|
||||
}
|
||||
|
||||
await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
|
||||
|
||||
await page.waitForTimeout(200)
|
||||
// hover over horizontal line
|
||||
await u.canvasLocator.hover({ position: { x: 800, y } })
|
||||
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
|
||||
timeout: 10_000,
|
||||
})
|
||||
await page.waitForTimeout(200)
|
||||
|
||||
await hoverOverNothing()
|
||||
await page.waitForTimeout(200)
|
||||
// hover over vertical line
|
||||
await u.canvasLocator.hover({ position: { x, y: 325 } })
|
||||
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
|
||||
timeout: 10_000,
|
||||
})
|
||||
|
||||
await hoverOverNothing()
|
||||
|
||||
// click exit sketch
|
||||
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
||||
await page.waitForTimeout(400)
|
||||
|
||||
await hoverOverNothing()
|
||||
await page.waitForTimeout(200)
|
||||
// hover over horizontal line
|
||||
await page.mouse.move(858, y, { steps: 5 })
|
||||
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
|
||||
timeout: 10_000,
|
||||
})
|
||||
|
||||
await hoverOverNothing()
|
||||
|
||||
// hover over vertical line
|
||||
await page.mouse.move(x, 325)
|
||||
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
|
||||
timeout: 10_000,
|
||||
})
|
||||
|
||||
await hoverOverNothing()
|
||||
|
||||
// hover over vertical line
|
||||
await page.mouse.move(857, y)
|
||||
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
|
||||
timeout: 10_000,
|
||||
})
|
||||
// now click it
|
||||
await page.mouse.click(857, y)
|
||||
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Edit Sketch' })
|
||||
).toBeVisible()
|
||||
await hoverOverNothing()
|
||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||
|
||||
await page.waitForTimeout(400)
|
||||
|
||||
x = 975
|
||||
y = 468
|
||||
|
||||
await page.waitForTimeout(100)
|
||||
await page.mouse.move(x, 419, { steps: 5 })
|
||||
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
|
||||
timeout: 10_000,
|
||||
})
|
||||
|
||||
await hoverOverNothing()
|
||||
|
||||
await page.mouse.move(855, y)
|
||||
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
|
||||
timeout: 10_000,
|
||||
})
|
||||
|
||||
await hoverOverNothing()
|
||||
|
||||
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
||||
await page.waitForTimeout(200)
|
||||
|
||||
await hoverOverNothing()
|
||||
await page.waitForTimeout(200)
|
||||
|
||||
await page.mouse.move(x, 419)
|
||||
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
|
||||
timeout: 10_000,
|
||||
})
|
||||
|
||||
await hoverOverNothing()
|
||||
|
||||
await page.mouse.move(855, y)
|
||||
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
|
||||
timeout: 10_000,
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
|
||||
|
||||
await page.waitForTimeout(200)
|
||||
// hover over horizontal line
|
||||
await u.canvasLocator.hover({ position: { x: 800, y } })
|
||||
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
|
||||
timeout: 10_000,
|
||||
})
|
||||
await page.waitForTimeout(200)
|
||||
|
||||
await hoverOverNothing()
|
||||
await page.waitForTimeout(200)
|
||||
// hover over vertical line
|
||||
await u.canvasLocator.hover({ position: { x, y: 325 } })
|
||||
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
|
||||
timeout: 10_000,
|
||||
})
|
||||
|
||||
await hoverOverNothing()
|
||||
|
||||
// click exit sketch
|
||||
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
||||
await page.waitForTimeout(400)
|
||||
|
||||
await hoverOverNothing()
|
||||
await page.waitForTimeout(200)
|
||||
// hover over horizontal line
|
||||
await page.mouse.move(858, y, { steps: 5 })
|
||||
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
|
||||
timeout: 10_000,
|
||||
})
|
||||
|
||||
await hoverOverNothing()
|
||||
|
||||
// hover over vertical line
|
||||
await page.mouse.move(x, 325)
|
||||
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
|
||||
timeout: 10_000,
|
||||
})
|
||||
|
||||
await hoverOverNothing()
|
||||
|
||||
// hover over vertical line
|
||||
await page.mouse.move(857, y)
|
||||
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
|
||||
timeout: 10_000,
|
||||
})
|
||||
// now click it
|
||||
await page.mouse.click(857, y)
|
||||
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Edit Sketch' })
|
||||
).toBeVisible()
|
||||
await hoverOverNothing()
|
||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||
|
||||
await page.waitForTimeout(400)
|
||||
|
||||
x = 975
|
||||
y = 468
|
||||
|
||||
await page.waitForTimeout(100)
|
||||
await page.mouse.move(x, 419, { steps: 5 })
|
||||
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
|
||||
timeout: 10_000,
|
||||
})
|
||||
|
||||
await hoverOverNothing()
|
||||
|
||||
await page.mouse.move(855, y)
|
||||
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
|
||||
timeout: 10_000,
|
||||
})
|
||||
|
||||
await hoverOverNothing()
|
||||
|
||||
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
||||
await page.waitForTimeout(200)
|
||||
|
||||
await hoverOverNothing()
|
||||
await page.waitForTimeout(200)
|
||||
|
||||
await page.mouse.move(x, 419)
|
||||
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
|
||||
timeout: 10_000,
|
||||
})
|
||||
|
||||
await hoverOverNothing()
|
||||
|
||||
await page.mouse.move(855, y)
|
||||
await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
|
||||
timeout: 10_000,
|
||||
})
|
||||
})
|
||||
|
||||
test(`Zoom by scroll should not fire while orbiting`, async ({ page }) => {
|
||||
test(`Zoom by scroll should not fire while orbiting`, async ({
|
||||
homePage,
|
||||
page,
|
||||
}) => {
|
||||
// TODO: fix this test on windows after the electron migration
|
||||
test.skip(process.platform === 'win32', 'Skip on windows')
|
||||
/**
|
||||
* Currently we only allow zooming by scroll when no other camera movement is happening,
|
||||
* set within cameraMouseDragGuards in cameraControls.ts,
|
||||
@ -383,7 +387,8 @@ test.describe('Testing Camera Movement', () => {
|
||||
const expectedOrbitCamZPosition = 64.0
|
||||
|
||||
await test.step(`Test setup`, async () => {
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
await u.closeKclCodePanel()
|
||||
// This test requires the mouse controls to be set to Solidworks
|
||||
await u.openDebugPanel()
|
||||
@ -479,4 +484,31 @@ test.describe('Testing Camera Movement', () => {
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
test('Right-click opens context menu when not dragged', async ({
|
||||
homePage,
|
||||
page,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await test.step(`The menu should not show if we drag the mouse`, async () => {
|
||||
await page.mouse.move(900, 200)
|
||||
await page.mouse.down({ button: 'right' })
|
||||
await page.mouse.move(900, 300)
|
||||
await page.mouse.up({ button: 'right' })
|
||||
|
||||
await expect(page.getByTestId('view-controls-menu')).not.toBeVisible()
|
||||
})
|
||||
|
||||
await test.step(`The menu should show if we don't drag the mouse`, async () => {
|
||||
await page.mouse.move(900, 200)
|
||||
await page.mouse.down({ button: 'right' })
|
||||
await page.mouse.up({ button: 'right' })
|
||||
|
||||
await expect(page.getByTestId('view-controls-menu')).toBeVisible()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -1,71 +1,63 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
|
||||
import { getUtils, setup, tearDown, TEST_COLORS } from './test-utils'
|
||||
import { test, expect } from './zoo-test'
|
||||
import * as fsp from 'fs/promises'
|
||||
import {
|
||||
getUtils,
|
||||
TEST_COLORS,
|
||||
pollEditorLinesSelectedLength,
|
||||
executorInputPath,
|
||||
} from './test-utils'
|
||||
import { XOR } from 'lib/utils'
|
||||
|
||||
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||
await setup(context, page, testInfo)
|
||||
})
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
import path from 'node:path'
|
||||
|
||||
test.describe('Testing constraints', () => {
|
||||
test('Can constrain line length', async ({ page }) => {
|
||||
test('Can constrain line length', async ({ page, homePage }) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, -10], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([0, 20], %)
|
||||
|> xLine(-20, %)
|
||||
`
|
||||
|> startProfileAt([-10, -10], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([0, 20], %)
|
||||
|> xLine(-20, %)
|
||||
`
|
||||
)
|
||||
})
|
||||
|
||||
const u = await getUtils(page)
|
||||
// constants and locators
|
||||
const lengthValue = {
|
||||
old: '20',
|
||||
new: '25',
|
||||
}
|
||||
const cmdBarKclInput = page
|
||||
.getByTestId('cmd-bar-arg-value')
|
||||
.getByRole('textbox')
|
||||
const cmdBarSubmitButton = page.getByRole('button', {
|
||||
name: 'arrow right Continue',
|
||||
})
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
await u.closeDebugPanel()
|
||||
|
||||
// Click the line of code for line.
|
||||
// TODO remove this and reinstate `await topHorzSegmentClick()`
|
||||
await page.getByText(`line([0, ${lengthValue.old}], %)`).click()
|
||||
await page.getByText(`line([0, 20], %)`).click() // TODO remove this and reinstate // await topHorzSegmentClick()
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
// enter sketch again
|
||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||
await page.waitForTimeout(500) // wait for animation
|
||||
|
||||
// Wait for overlays to populate
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
const startXPx = 500
|
||||
|
||||
await page.getByText(`line([0, 20], %)`).click()
|
||||
await page.waitForTimeout(100)
|
||||
await page.getByTestId('constraint-length').click()
|
||||
await page.getByTestId('cmd-bar-arg-value').getByRole('textbox').fill('20')
|
||||
await page
|
||||
.getByRole('button', { name: 'dimension Length', exact: true })
|
||||
.getByRole('button', {
|
||||
name: 'arrow right Continue',
|
||||
})
|
||||
.click()
|
||||
await expect(cmdBarKclInput).toHaveText('20')
|
||||
await cmdBarKclInput.fill(lengthValue.new)
|
||||
await expect(
|
||||
page.getByText(`Can't calculate`),
|
||||
`Something went wrong with the KCL expression evaluation`
|
||||
).not.toBeVisible()
|
||||
await cmdBarSubmitButton.click()
|
||||
|
||||
await expect(page.locator('.cm-content')).toHaveText(
|
||||
`length001 = ${lengthValue.new}sketch001 = startSketchOn('XY') |> startProfileAt([-10, -10], %) |> line([20, 0], %) |> angledLine([90, length001], %) |> xLine(-20, %)`
|
||||
`length001 = 20sketch001 = startSketchOn('XY') |> startProfileAt([-10, -10], %) |> line([20, 0], %) |> angledLine([90, length001], %) |> xLine(-20, %)`
|
||||
)
|
||||
|
||||
// Make sure we didn't pop out of sketch mode.
|
||||
@ -73,41 +65,48 @@ test.describe('Testing constraints', () => {
|
||||
page.getByRole('button', { name: 'Exit Sketch' })
|
||||
).toBeVisible()
|
||||
|
||||
await page.waitForTimeout(500) // wait for animation
|
||||
await page.waitForTimeout(2500) // wait for animation
|
||||
|
||||
// Exit sketch
|
||||
await page.keyboard.press('Escape')
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Exit Sketch' })
|
||||
).not.toBeVisible()
|
||||
await page.mouse.move(startXPx + PUR * 15, 250 - PUR * 10)
|
||||
await expect
|
||||
.poll(async () => {
|
||||
await page.keyboard.press('Escape', { delay: 500 })
|
||||
return page.getByRole('button', { name: 'Exit Sketch' }).isVisible()
|
||||
})
|
||||
.toBe(false)
|
||||
})
|
||||
test(`Remove constraints`, async ({ page }) => {
|
||||
test(`Remove constraints`, async ({ page, homePage }) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`yo = 79
|
||||
part001 = startSketchOn('XZ')
|
||||
|> startProfileAt([-7.54, -26.74], %)
|
||||
|> line([74.36, 130.4], %, $seg01)
|
||||
|> line([78.92, -120.11], %)
|
||||
|> angledLine([segAng(seg01), yo], %)
|
||||
|> line([41.19, 58.97 + 5], %)
|
||||
part002 = startSketchOn('XZ')
|
||||
|> startProfileAt([299.05, 120], %)
|
||||
|> xLine(-385.34, %, $seg_what)
|
||||
|> yLine(-170.06, %)
|
||||
|> xLine(segLen(seg_what), %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)`
|
||||
part001 = startSketchOn('XZ')
|
||||
|> startProfileAt([-7.54, -26.74], %)
|
||||
|> line([74.36, 130.4], %, $seg01)
|
||||
|> line([78.92, -120.11], %)
|
||||
|> angledLine([segAng(seg01), yo], %)
|
||||
|> line([41.19, 58.97 + 5], %)
|
||||
part002 = startSketchOn('XZ')
|
||||
|> startProfileAt([299.05, 120], %)
|
||||
|> xLine(-385.34, %, $seg_what)
|
||||
|> yLine(-170.06, %)
|
||||
|> xLine(segLen(seg_what), %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)`
|
||||
)
|
||||
})
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await page.getByText('line([74.36, 130.4], %, $seg01)').click()
|
||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||
|
||||
// Wait for overlays to populate
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
const line3 = await u.getSegmentBodyCoords(`[data-overlay-index="${2}"]`)
|
||||
|
||||
await page.mouse.click(line3.x, line3.y)
|
||||
@ -120,14 +119,16 @@ part002 = startSketchOn('XZ')
|
||||
await page.getByRole('button', { name: 'remove constraints' }).click()
|
||||
|
||||
await page.getByText('line([39.13, 68.63], %)').click()
|
||||
await pollEditorLinesSelectedLength(page, 1)
|
||||
const activeLinesContent = await page.locator('.cm-activeLine').all()
|
||||
await expect(activeLinesContent).toHaveLength(1)
|
||||
await expect(activeLinesContent[0]).toHaveText('|> line([39.13, 68.63], %)')
|
||||
|
||||
// checking the count of the overlays is a good proxy check that the client sketch scene is in a good state
|
||||
await expect(page.getByTestId('segment-overlay')).toHaveCount(4)
|
||||
})
|
||||
test.describe('Test perpendicular distance constraint', () => {
|
||||
// TODO: fix this test on windows after the electron migration
|
||||
test.skip(process.platform === 'win32', 'Skip on windows')
|
||||
const cases = [
|
||||
{
|
||||
testName: 'Add variable',
|
||||
@ -139,43 +140,75 @@ part002 = startSketchOn('XZ')
|
||||
},
|
||||
] as const
|
||||
for (const { testName, offset } of cases) {
|
||||
test(`${testName}`, async ({ page }) => {
|
||||
test(`${testName}`, async ({ page, homePage }) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`yo = 5
|
||||
part001 = startSketchOn('XZ')
|
||||
|> startProfileAt([-7.54, -26.74], %)
|
||||
|> line([74.36, 130.4], %, $seg01)
|
||||
|> line([78.92, -120.11], %)
|
||||
|> angledLine([segAng(seg01), 78.33], %)
|
||||
|> line([51.19, 48.97], %)
|
||||
part002 = startSketchOn('XZ')
|
||||
|> startProfileAt([299.05, 231.45], %)
|
||||
|> xLine(-425.34, %, $seg_what)
|
||||
|> yLine(-264.06, %)
|
||||
|> xLine(segLen(seg_what), %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)`
|
||||
part001 = startSketchOn('XZ')
|
||||
|> startProfileAt([-7.54, -26.74], %)
|
||||
|> line([74.36, 130.4], %, $seg01)
|
||||
|> line([78.92, -120.11], %)
|
||||
|> angledLine([segAng(seg01), 78.33], %)
|
||||
|> line([51.19, 48.97], %)
|
||||
part002 = startSketchOn('XZ')
|
||||
|> startProfileAt([299.05, 231.45], %)
|
||||
|> xLine(-425.34, %, $seg_what)
|
||||
|> yLine(-264.06, %)
|
||||
|> xLine(segLen(seg_what), %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)`
|
||||
)
|
||||
|
||||
const isChecked = await createNewVariableCheckbox.isChecked()
|
||||
const addVariable = testName === 'Add variable'
|
||||
XOR(isChecked, addVariable) && // XOR because no need to click the checkbox if the state is already correct
|
||||
(await createNewVariableCheckbox.click())
|
||||
|
||||
await page
|
||||
.getByRole('button', { name: 'Add constraining value' })
|
||||
.click()
|
||||
|
||||
// Wait for the codemod to take effect
|
||||
await expect(page.locator('.cm-content')).toContainText(`angle: -57,`)
|
||||
await expect(page.locator('.cm-content')).toContainText(
|
||||
`offset: ${offset},`
|
||||
)
|
||||
|
||||
await pollEditorLinesSelectedLength(page, 2)
|
||||
const activeLinesContent = await page.locator('.cm-activeLine').all()
|
||||
await expect(activeLinesContent[0]).toHaveText(
|
||||
`|> line([74.36, 130.4], %, $seg01)`
|
||||
)
|
||||
await expect(activeLinesContent[1]).toHaveText(`}, %)`)
|
||||
|
||||
// checking the count of the overlays is a good proxy check that the client sketch scene is in a good state
|
||||
await expect(page.getByTestId('segment-overlay')).toHaveCount(4)
|
||||
})
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await page.getByText('line([74.36, 130.4], %, $seg01)').click()
|
||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||
|
||||
// Give time for overlays to populate
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
const [line1, line3] = await Promise.all([
|
||||
u.getSegmentBodyCoords(`[data-overlay-index="${0}"]`),
|
||||
u.getSegmentBodyCoords(`[data-overlay-index="${2}"]`),
|
||||
])
|
||||
|
||||
await page.mouse.click(line1.x, line1.y)
|
||||
await page.keyboard.down('Shift')
|
||||
await page.mouse.click(line3.x, line3.y)
|
||||
await page.waitForTimeout(100) // this wait is needed for webkit - not sure why
|
||||
await page.keyboard.up('Shift')
|
||||
await page.keyboard.down('Shift')
|
||||
await page.waitForTimeout(100)
|
||||
await page.mouse.click(line3.x, line3.y)
|
||||
await page.waitForTimeout(100)
|
||||
await page.keyboard.up('Shift')
|
||||
await page.waitForTimeout(100)
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'Length: open menu',
|
||||
@ -203,6 +236,7 @@ part002 = startSketchOn('XZ')
|
||||
`offset = ${offset},`
|
||||
)
|
||||
|
||||
await pollEditorLinesSelectedLength(page, 2)
|
||||
const activeLinesContent = await page.locator('.cm-activeLine').all()
|
||||
await expect(activeLinesContent[0]).toHaveText(
|
||||
`|> line([74.36, 130.4], %, $seg01)`
|
||||
@ -215,6 +249,8 @@ part002 = startSketchOn('XZ')
|
||||
}
|
||||
})
|
||||
test.describe('Test distance between constraint', () => {
|
||||
// TODO: fix this test on windows after the electron migration
|
||||
test.skip(process.platform === 'win32', 'Skip on windows')
|
||||
const cases = [
|
||||
{
|
||||
testName: 'Add variable',
|
||||
@ -238,33 +274,37 @@ part002 = startSketchOn('XZ')
|
||||
},
|
||||
] as const
|
||||
for (const { testName, value, constraint } of cases) {
|
||||
test(`${constraint} - ${testName}`, async ({ page }) => {
|
||||
test(`${constraint} - ${testName}`, async ({ page, homePage }) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`yo = 5
|
||||
part001 = startSketchOn('XZ')
|
||||
|> startProfileAt([-7.54, -26.74], %)
|
||||
|> line([74.36, 130.4], %)
|
||||
|> line([78.92, -120.11], %)
|
||||
|> line([9.16, 77.79], %)
|
||||
|> line([51.19, 48.97], %)
|
||||
part002 = startSketchOn('XZ')
|
||||
|> startProfileAt([299.05, 231.45], %)
|
||||
|> xLine(-425.34, %, $seg_what)
|
||||
|> yLine(-264.06, %)
|
||||
|> xLine(segLen(seg_what), %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)`
|
||||
part001 = startSketchOn('XZ')
|
||||
|> startProfileAt([-7.54, -26.74], %)
|
||||
|> line([74.36, 130.4], %)
|
||||
|> line([78.92, -120.11], %)
|
||||
|> line([9.16, 77.79], %)
|
||||
|> line([51.19, 48.97], %)
|
||||
part002 = startSketchOn('XZ')
|
||||
|> startProfileAt([299.05, 231.45], %)
|
||||
|> xLine(-425.34, %, $seg_what)
|
||||
|> yLine(-264.06, %)
|
||||
|> xLine(segLen(seg_what), %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)`
|
||||
)
|
||||
})
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await page.getByText('line([74.36, 130.4], %)').click()
|
||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||
|
||||
// Wait for overlays to populate
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
const [line1, line3] = await Promise.all([
|
||||
u.getSegmentBodyCoords(`[data-overlay-index="${0}"]`),
|
||||
u.getSegmentBodyCoords(`[data-overlay-index="${2}"]`),
|
||||
@ -344,33 +384,37 @@ part002 = startSketchOn('XZ')
|
||||
},
|
||||
] as const
|
||||
for (const { testName, addVariable, value, constraint } of cases) {
|
||||
test(`${constraint} - ${testName}`, async ({ page }) => {
|
||||
test(`${constraint} - ${testName}`, async ({ page, homePage }) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`yo = 5
|
||||
part001 = startSketchOn('XZ')
|
||||
|> startProfileAt([-7.54, -26.74], %)
|
||||
|> line([74.36, 130.4], %)
|
||||
|> line([78.92, -120.11], %)
|
||||
|> line([9.16, 77.79], %)
|
||||
|> line([51.19, 48.97], %)
|
||||
part002 = startSketchOn('XZ')
|
||||
|> startProfileAt([299.05, 231.45], %)
|
||||
|> xLine(-425.34, %, $seg_what)
|
||||
|> yLine(-264.06, %)
|
||||
|> xLine(segLen(seg_what), %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)`
|
||||
part001 = startSketchOn('XZ')
|
||||
|> startProfileAt([-7.54, -26.74], %)
|
||||
|> line([74.36, 130.4], %)
|
||||
|> line([78.92, -120.11], %)
|
||||
|> line([9.16, 77.79], %)
|
||||
|> line([51.19, 48.97], %)
|
||||
part002 = startSketchOn('XZ')
|
||||
|> startProfileAt([299.05, 231.45], %)
|
||||
|> xLine(-425.34, %, $seg_what)
|
||||
|> yLine(-264.06, %)
|
||||
|> xLine(segLen(seg_what), %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)`
|
||||
)
|
||||
})
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await page.getByText('line([74.36, 130.4], %)').click()
|
||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||
|
||||
// Wait for overlays to populate
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
const [line3] = await Promise.all([
|
||||
u.getSegmentBodyCoords(`[data-overlay-index="${2}"]`),
|
||||
])
|
||||
@ -381,9 +425,11 @@ part002 = startSketchOn('XZ')
|
||||
await page.mouse.click(900, 250)
|
||||
}
|
||||
await page.keyboard.down('Shift')
|
||||
await page.waitForTimeout(100)
|
||||
await page.mouse.click(line3.x, line3.y)
|
||||
await page.waitForTimeout(100) // this wait is needed for webkit - not sure why
|
||||
await page.waitForTimeout(100)
|
||||
await page.keyboard.up('Shift')
|
||||
await page.waitForTimeout(100)
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'Length: open menu',
|
||||
@ -424,6 +470,8 @@ part002 = startSketchOn('XZ')
|
||||
}
|
||||
})
|
||||
test.describe('Test Angle constraint double segment selection', () => {
|
||||
// TODO: fix this test on windows after the electron migration
|
||||
test.skip(process.platform === 'win32', 'Skip on windows')
|
||||
const cases = [
|
||||
{
|
||||
testName: 'Add variable',
|
||||
@ -451,33 +499,37 @@ part002 = startSketchOn('XZ')
|
||||
},
|
||||
] as const
|
||||
for (const { testName, addVariable, value, axisSelect } of cases) {
|
||||
test(`${testName}`, async ({ page }) => {
|
||||
test(`${testName}`, async ({ page, homePage }) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`yo = 5
|
||||
part001 = startSketchOn('XZ')
|
||||
|> startProfileAt([-7.54, -26.74], %)
|
||||
|> line([74.36, 130.4], %)
|
||||
|> line([78.92, -120.11], %)
|
||||
|> line([9.16, 77.79], %)
|
||||
|> line([51.19, 48.97], %)
|
||||
part002 = startSketchOn('XZ')
|
||||
|> startProfileAt([299.05, 231.45], %)
|
||||
|> xLine(-425.34, %, $seg_what)
|
||||
|> yLine(-264.06, %)
|
||||
|> xLine(segLen(seg_what), %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)`
|
||||
part001 = startSketchOn('XZ')
|
||||
|> startProfileAt([-7.54, -26.74], %)
|
||||
|> line([74.36, 130.4], %)
|
||||
|> line([78.92, -120.11], %)
|
||||
|> line([9.16, 77.79], %)
|
||||
|> line([51.19, 48.97], %)
|
||||
part002 = startSketchOn('XZ')
|
||||
|> startProfileAt([299.05, 231.45], %)
|
||||
|> xLine(-425.34, %, $seg_what)
|
||||
|> yLine(-264.06, %)
|
||||
|> xLine(segLen(seg_what), %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)`
|
||||
)
|
||||
})
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await page.getByText('line([74.36, 130.4], %)').click()
|
||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||
|
||||
// Wait for overlays to populate
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
const [line1, line3] = await Promise.all([
|
||||
u.getSegmentBodyCoords(`[data-overlay-index="${0}"]`),
|
||||
u.getSegmentBodyCoords(`[data-overlay-index="${2}"]`),
|
||||
@ -549,33 +601,37 @@ part002 = startSketchOn('XZ')
|
||||
},
|
||||
] as const
|
||||
for (const { testName, addVariable, value, constraint } of cases) {
|
||||
test(`${testName}`, async ({ page }) => {
|
||||
test(`${testName}`, async ({ page, homePage }) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`yo = 5
|
||||
part001 = startSketchOn('XZ')
|
||||
|> startProfileAt([-7.54, -26.74], %)
|
||||
|> line([74.36, 130.4], %)
|
||||
|> line([78.92, -120.11], %)
|
||||
|> line([9.16, 77.79], %)
|
||||
|> line([51.19, 48.97], %)
|
||||
part002 = startSketchOn('XZ')
|
||||
|> startProfileAt([299.05, 231.45], %)
|
||||
|> xLine(-425.34, %, $seg_what)
|
||||
|> yLine(-264.06, %)
|
||||
|> xLine(segLen(seg_what), %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)`
|
||||
part001 = startSketchOn('XZ')
|
||||
|> startProfileAt([-7.54, -26.74], %)
|
||||
|> line([74.36, 130.4], %)
|
||||
|> line([78.92, -120.11], %)
|
||||
|> line([9.16, 77.79], %)
|
||||
|> line([51.19, 48.97], %)
|
||||
part002 = startSketchOn('XZ')
|
||||
|> startProfileAt([299.05, 231.45], %)
|
||||
|> xLine(-425.34, %, $seg_what)
|
||||
|> yLine(-264.06, %)
|
||||
|> xLine(segLen(seg_what), %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)`
|
||||
)
|
||||
})
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await page.getByText('line([74.36, 130.4], %)').click()
|
||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||
|
||||
// Wait for overlays to populate
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
const line3 = await u.getSegmentBodyCoords(
|
||||
`[data-overlay-index="${2}"]`
|
||||
)
|
||||
@ -606,6 +662,8 @@ part002 = startSketchOn('XZ')
|
||||
}
|
||||
})
|
||||
test.describe('Test Length constraint single selection', () => {
|
||||
// TODO: fix this test on windows after the electron migration
|
||||
test.skip(process.platform === 'win32', 'Skip on windows')
|
||||
const cases = [
|
||||
{
|
||||
testName: 'Length - Add variable',
|
||||
@ -621,7 +679,7 @@ part002 = startSketchOn('XZ')
|
||||
},
|
||||
] as const
|
||||
for (const { testName, addVariable, value, constraint } of cases) {
|
||||
test(`${testName}`, async ({ page }) => {
|
||||
test(`${testName}`, async ({ context, homePage, page }) => {
|
||||
// constants and locators
|
||||
const cmdBarKclInput = page
|
||||
.getByTestId('cmd-bar-arg-value')
|
||||
@ -651,9 +709,9 @@ part002 = startSketchOn('XZ')
|
||||
)
|
||||
})
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await page.getByText('line([74.36, 130.4], %)').click()
|
||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||
@ -709,33 +767,37 @@ part002 = startSketchOn('XZ')
|
||||
},
|
||||
] as const
|
||||
for (const { codeAfter, constraintName } of cases) {
|
||||
test(`${constraintName}`, async ({ page }) => {
|
||||
test(`${constraintName}`, async ({ page, homePage }) => {
|
||||
await page.addInitScript(async (customCode) => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`yo = 5
|
||||
part001 = startSketchOn('XZ')
|
||||
|> startProfileAt([-7.54, -26.74], %)
|
||||
|> line([74.36, 130.4], %)
|
||||
|> line([78.92, -120.11], %)
|
||||
|> line([9.16, 77.79], %)
|
||||
|> line([51.19, 48.97], %)
|
||||
part002 = startSketchOn('XZ')
|
||||
|> startProfileAt([299.05, 231.45], %)
|
||||
|> xLine(-425.34, %, $seg_what)
|
||||
|> yLine(-264.06, %)
|
||||
|> xLine(segLen(seg_what), %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)`
|
||||
part001 = startSketchOn('XZ')
|
||||
|> startProfileAt([-7.54, -26.74], %)
|
||||
|> line([74.36, 130.4], %)
|
||||
|> line([78.92, -120.11], %)
|
||||
|> line([9.16, 77.79], %)
|
||||
|> line([51.19, 48.97], %)
|
||||
part002 = startSketchOn('XZ')
|
||||
|> startProfileAt([299.05, 231.45], %)
|
||||
|> xLine(-425.34, %, $seg_what)
|
||||
|> yLine(-264.06, %)
|
||||
|> xLine(segLen(seg_what), %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)`
|
||||
)
|
||||
})
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await page.getByText('line([74.36, 130.4], %)').click()
|
||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||
|
||||
// Wait for overlays to populate
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
const line1 = await u.getSegmentBodyCoords(
|
||||
`[data-overlay-index="${0}"]`
|
||||
)
|
||||
@ -754,8 +816,8 @@ part002 = startSketchOn('XZ')
|
||||
await page.keyboard.up('Shift')
|
||||
|
||||
// check actives lines
|
||||
await pollEditorLinesSelectedLength(page, codeAfter.length)
|
||||
const activeLinesContent = await page.locator('.cm-activeLine').all()
|
||||
await expect(activeLinesContent).toHaveLength(codeAfter.length)
|
||||
|
||||
const constraintMenuButton = page.getByRole('button', {
|
||||
name: 'Length: open menu',
|
||||
@ -787,6 +849,8 @@ part002 = startSketchOn('XZ')
|
||||
}
|
||||
})
|
||||
test.describe('Two segment - no modal constraints', () => {
|
||||
// TODO: fix this test on windows after the electron migration
|
||||
test.skip(process.platform === 'win32', 'Skip on windows')
|
||||
const cases = [
|
||||
{
|
||||
codeAfter: `|> angledLine([83, segLen(seg01)], %)`,
|
||||
@ -806,32 +870,36 @@ part002 = startSketchOn('XZ')
|
||||
},
|
||||
] as const
|
||||
for (const { codeAfter, constraintName } of cases) {
|
||||
test(`${constraintName}`, async ({ page }) => {
|
||||
test(`${constraintName}`, async ({ page, homePage }) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`yo = 5
|
||||
part001 = startSketchOn('XZ')
|
||||
|> startProfileAt([-7.54, -26.74], %)
|
||||
|> line([74.36, 130.4], %)
|
||||
|> line([78.92, -120.11], %)
|
||||
|> line([9.16, 77.79], %)
|
||||
part002 = startSketchOn('XZ')
|
||||
|> startProfileAt([299.05, 231.45], %)
|
||||
|> xLine(-425.34, %, $seg_what)
|
||||
|> yLine(-264.06, %)
|
||||
|> xLine(segLen(seg_what), %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)`
|
||||
part001 = startSketchOn('XZ')
|
||||
|> startProfileAt([-7.54, -26.74], %)
|
||||
|> line([74.36, 130.4], %)
|
||||
|> line([78.92, -120.11], %)
|
||||
|> line([9.16, 77.79], %)
|
||||
part002 = startSketchOn('XZ')
|
||||
|> startProfileAt([299.05, 231.45], %)
|
||||
|> xLine(-425.34, %, $seg_what)
|
||||
|> yLine(-264.06, %)
|
||||
|> xLine(segLen(seg_what), %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)`
|
||||
)
|
||||
})
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await page.getByText('line([74.36, 130.4], %)').click()
|
||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||
|
||||
// Wait for overlays to populate
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
const line1 = await u.getBoundingBox(`[data-overlay-index="${0}"]`)
|
||||
const line3 = await u.getBoundingBox(`[data-overlay-index="${2}"]`)
|
||||
|
||||
@ -858,8 +926,8 @@ part002 = startSketchOn('XZ')
|
||||
// check there are still 2 cursors (they should stay on the same lines as before constraint was applied)
|
||||
await expect(page.locator('.cm-cursor')).toHaveCount(2)
|
||||
// check actives lines
|
||||
await pollEditorLinesSelectedLength(page, 2)
|
||||
const activeLinesContent = await page.locator('.cm-activeLine').all()
|
||||
await expect(activeLinesContent).toHaveLength(2)
|
||||
|
||||
// check both cursors are where they should be after constraint is applied
|
||||
await expect(activeLinesContent[0]).toHaveText(
|
||||
@ -883,40 +951,47 @@ part002 = startSketchOn('XZ')
|
||||
},
|
||||
] as const
|
||||
for (const { codeAfter, constraintName, axisClick } of cases) {
|
||||
test(`${constraintName}`, async ({ page }) => {
|
||||
test(`${constraintName}`, async ({ page, homePage }) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`yo = 5
|
||||
part001 = startSketchOn('XZ')
|
||||
|> startProfileAt([-7.54, -26.74], %)
|
||||
|> line([74.36, 130.4], %)
|
||||
|> line([78.92, -120.11], %)
|
||||
|> line([9.16, 77.79], %)
|
||||
part002 = startSketchOn('XZ')
|
||||
|> startProfileAt([299.05, 231.45], %)
|
||||
|> xLine(-425.34, %, $seg_what)
|
||||
|> yLine(-264.06, %)
|
||||
|> xLine(segLen(seg_what), %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)`
|
||||
part001 = startSketchOn('XZ')
|
||||
|> startProfileAt([-7.54, -26.74], %)
|
||||
|> line([74.36, 130.4], %)
|
||||
|> line([78.92, -120.11], %)
|
||||
|> line([9.16, 77.79], %)
|
||||
part002 = startSketchOn('XZ')
|
||||
|> startProfileAt([299.05, 231.45], %)
|
||||
|> xLine(-425.34, %, $seg_what)
|
||||
|> yLine(-264.06, %)
|
||||
|> xLine(segLen(seg_what), %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)`
|
||||
)
|
||||
})
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await page.getByText('line([74.36, 130.4], %)').click()
|
||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||
|
||||
// Wait for overlays to populate
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
const line3 = await u.getBoundingBox(`[data-overlay-index="${2}"]`)
|
||||
|
||||
// select segment and axis by holding down shift
|
||||
await page.mouse.click(line3.x - 3, line3.y + 20)
|
||||
await page.waitForTimeout(100)
|
||||
await page.keyboard.down('Shift')
|
||||
await page.waitForTimeout(100)
|
||||
await page.mouse.click(axisClick.x, axisClick.y)
|
||||
await page.waitForTimeout(100)
|
||||
await page.keyboard.up('Shift')
|
||||
await page.waitForTimeout(100)
|
||||
const constraintMenuButton = page.getByRole('button', {
|
||||
name: 'Length: open menu',
|
||||
})
|
||||
@ -936,95 +1011,162 @@ part002 = startSketchOn('XZ')
|
||||
}
|
||||
})
|
||||
|
||||
test('Horizontally constrained line remains selected after applying constraint', async ({
|
||||
page,
|
||||
}) => {
|
||||
test.setTimeout(70_000)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-1.05, -1.07], %)
|
||||
|> line([3.79, 2.68], %, $seg01)
|
||||
|> line([3.13, -2.4], %)`
|
||||
test.fixme(
|
||||
'Horizontally constrained line remains selected after applying constraint',
|
||||
async ({ page, homePage }) => {
|
||||
test.setTimeout(70_000)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-1.05, -1.07], %)
|
||||
|> line([3.79, 2.68], %, $seg01)
|
||||
|> line([3.13, -2.4], %)`
|
||||
)
|
||||
})
|
||||
const u = await getUtils(page)
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await page.getByText('line([3.79, 2.68], %, $seg01)').click()
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Edit Sketch' })
|
||||
).toBeEnabled({ timeout: 10_000 })
|
||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||
|
||||
// Wait for overlays to populate
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
await page.waitForTimeout(100)
|
||||
const lineBefore = await u.getSegmentBodyCoords(
|
||||
`[data-overlay-index="1"]`,
|
||||
0
|
||||
)
|
||||
})
|
||||
expect(
|
||||
await u.getGreatestPixDiff(lineBefore, TEST_COLORS.WHITE)
|
||||
).toBeLessThan(3)
|
||||
await page.mouse.move(lineBefore.x, lineBefore.y)
|
||||
await page.waitForTimeout(50)
|
||||
await page.mouse.click(lineBefore.x, lineBefore.y)
|
||||
expect(
|
||||
await u.getGreatestPixDiff(lineBefore, TEST_COLORS.BLUE)
|
||||
).toBeLessThan(3)
|
||||
|
||||
// constants and locators
|
||||
const cmdBarKclInput = page
|
||||
.getByTestId('cmd-bar-arg-value')
|
||||
.getByRole('textbox')
|
||||
const cmdBarSubmitButton = page.getByRole('button', {
|
||||
name: 'arrow right Continue',
|
||||
})
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'Length: open menu',
|
||||
})
|
||||
.click()
|
||||
await page.waitForTimeout(500)
|
||||
await page
|
||||
.getByRole('button', { name: 'Horizontal', exact: true })
|
||||
.click()
|
||||
await page.waitForTimeout(500)
|
||||
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await pollEditorLinesSelectedLength(page, 1)
|
||||
let activeLinesContent = await page.locator('.cm-activeLine').all()
|
||||
await expect(activeLinesContent[0]).toHaveText(`|> xLine(3.13, %)`)
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
// Wait for code editor to settle.
|
||||
await page.waitForTimeout(2000)
|
||||
|
||||
await page.getByText('line([3.79, 2.68], %, $seg01)').click()
|
||||
await expect(page.getByRole('button', { name: 'Edit Sketch' })).toBeEnabled(
|
||||
{ timeout: 10_000 }
|
||||
)
|
||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||
// If the overlay-angle is updated the THREE.js scene is in a good state
|
||||
await expect(
|
||||
await page.locator('[data-overlay-index="1"]')
|
||||
).toHaveAttribute('data-overlay-angle', '0')
|
||||
|
||||
await page.waitForTimeout(100)
|
||||
const lineBefore = await u.getSegmentBodyCoords(
|
||||
`[data-overlay-index="1"]`,
|
||||
0
|
||||
)
|
||||
expect(
|
||||
await u.getGreatestPixDiff(lineBefore, TEST_COLORS.WHITE)
|
||||
).toBeLessThan(3)
|
||||
await page.mouse.move(lineBefore.x, lineBefore.y)
|
||||
await page.waitForTimeout(50)
|
||||
await page.mouse.click(lineBefore.x, lineBefore.y)
|
||||
expect(
|
||||
await u.getGreatestPixDiff(lineBefore, TEST_COLORS.BLUE)
|
||||
).toBeLessThan(3)
|
||||
const lineAfter = await u.getSegmentBodyCoords(
|
||||
`[data-overlay-index="1"]`,
|
||||
0
|
||||
)
|
||||
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'Length: open menu',
|
||||
})
|
||||
.click()
|
||||
await page.getByRole('button', { name: 'Horizontal', exact: true }).click()
|
||||
const linebb = await u.getBoundingBox('[data-overlay-index="1"]')
|
||||
await page.mouse.move(linebb.x, linebb.y, { steps: 25 })
|
||||
await page.mouse.click(linebb.x, linebb.y)
|
||||
|
||||
let activeLinesContent = await page.locator('.cm-activeLine').all()
|
||||
await expect(activeLinesContent[0]).toHaveText(`|> xLine(3.13, %)`)
|
||||
await expect
|
||||
.poll(
|
||||
async () => await u.getGreatestPixDiff(lineAfter, TEST_COLORS.BLUE)
|
||||
)
|
||||
.toBeLessThan(3)
|
||||
|
||||
// If the overlay-angle is updated the THREE.js scene is in a good state
|
||||
await expect(
|
||||
await page.locator('[data-overlay-index="1"]')
|
||||
).toHaveAttribute('data-overlay-angle', '0')
|
||||
await page.waitForTimeout(500)
|
||||
|
||||
const lineAfter = await u.getSegmentBodyCoords(
|
||||
`[data-overlay-index="1"]`,
|
||||
0
|
||||
)
|
||||
expect(
|
||||
await u.getGreatestPixDiff(lineAfter, TEST_COLORS.BLUE)
|
||||
).toBeLessThan(3)
|
||||
// await expect(page.getByRole('button', { name: 'length', exact: true })).toBeVisible()
|
||||
await page.waitForTimeout(200)
|
||||
// await page.getByRole('button', { name: 'length', exact: true }).click()
|
||||
await page.getByTestId('constraint-length').click()
|
||||
|
||||
await page.waitForTimeout(300)
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'Length: open menu',
|
||||
})
|
||||
.click()
|
||||
// await expect(page.getByRole('button', { name: 'length', exact: true })).toBeVisible()
|
||||
await page.waitForTimeout(200)
|
||||
// await page.getByRole('button', { name: 'length', exact: true }).click()
|
||||
await page.getByTestId('dropdown-constraint-length').click()
|
||||
await page
|
||||
.getByTestId('cmd-bar-arg-value')
|
||||
.getByRole('textbox')
|
||||
.fill('10')
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'arrow right Continue',
|
||||
})
|
||||
.click()
|
||||
|
||||
await cmdBarKclInput.fill('10')
|
||||
await cmdBarSubmitButton.click()
|
||||
await pollEditorLinesSelectedLength(page, 1)
|
||||
activeLinesContent = await page.locator('.cm-activeLine').all()
|
||||
await expect(activeLinesContent[0]).toHaveText(`|> xLine(length001, %)`)
|
||||
|
||||
activeLinesContent = await page.locator('.cm-activeLine').all()
|
||||
await expect(activeLinesContent[0]).toHaveText(`|> xLine(length001, %)`)
|
||||
|
||||
// checking the count of the overlays is a good proxy check that the client sketch scene is in a good state
|
||||
await expect(page.getByTestId('segment-overlay')).toHaveCount(2)
|
||||
})
|
||||
// checking the count of the overlays is a good proxy check that the client sketch scene is in a good state
|
||||
await expect(page.getByTestId('segment-overlay')).toHaveCount(2)
|
||||
}
|
||||
)
|
||||
})
|
||||
test.describe('Electron constraint tests', () => {
|
||||
test(
|
||||
'Able to double click label to set constraint',
|
||||
{ tag: '@electron' },
|
||||
async ({ page, context, homePage, scene, editor, toolbar }) => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
const bracketDir = path.join(dir, 'test-sample')
|
||||
await fsp.mkdir(bracketDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('angled_line.kcl'),
|
||||
path.join(bracketDir, 'main.kcl')
|
||||
)
|
||||
})
|
||||
const [clickHandler] = scene.makeMouseHelpers(600, 300)
|
||||
|
||||
await test.step('setup test', async () => {
|
||||
await homePage.expectState({
|
||||
projectCards: [
|
||||
{
|
||||
title: 'test-sample',
|
||||
fileCount: 1,
|
||||
},
|
||||
],
|
||||
sortBy: 'last-modified-desc',
|
||||
})
|
||||
await homePage.openProject('test-sample')
|
||||
await scene.waitForExecutionDone()
|
||||
})
|
||||
|
||||
await test.step('Double click to constrain', async () => {
|
||||
await clickHandler()
|
||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||
const child = page
|
||||
.locator('.segment-length-label-text')
|
||||
.first()
|
||||
.locator('xpath=..')
|
||||
await child.dblclick()
|
||||
const cmdBarSubmitButton = page.getByRole('button', {
|
||||
name: 'arrow right Continue',
|
||||
})
|
||||
await cmdBarSubmitButton.click()
|
||||
await expect(page.locator('.cm-content')).toContainText(
|
||||
'length001 = 15.3'
|
||||
)
|
||||
await expect(page.locator('.cm-content')).toContainText(
|
||||
'|> angledLine([9, length001], %)'
|
||||
)
|
||||
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
||||
})
|
||||
}
|
||||
)
|
||||
})
|
||||
|
@ -1,18 +1,11 @@
|
||||
import { _test, _expect } from './playwright-deprecated'
|
||||
import { test } from './fixtures/fixtureSetup'
|
||||
import { getUtils, setup, tearDown } from './test-utils'
|
||||
import { test, expect } from './zoo-test'
|
||||
import { getUtils } from './test-utils'
|
||||
import { uuidv4 } from 'lib/utils'
|
||||
import { TEST_CODE_GIZMO } from './storageStates'
|
||||
|
||||
_test.beforeEach(async ({ context, page }, testInfo) => {
|
||||
await setup(context, page, testInfo)
|
||||
})
|
||||
|
||||
_test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
|
||||
_test.describe('Testing Gizmo', () => {
|
||||
test.describe('Testing Gizmo', () => {
|
||||
// TODO: fix this test on windows after the electron migration
|
||||
test.skip(process.platform === 'win32', 'Skip on windows')
|
||||
const cases = [
|
||||
{
|
||||
testDescription: 'top view',
|
||||
@ -57,14 +50,17 @@ _test.describe('Testing Gizmo', () => {
|
||||
expectedCameraTarget,
|
||||
testDescription,
|
||||
} of cases) {
|
||||
_test(`check ${testDescription}`, async ({ page, browserName }) => {
|
||||
test(`check ${testDescription}`, async ({ page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript((TEST_CODE_GIZMO) => {
|
||||
localStorage.setItem('persistCode', TEST_CODE_GIZMO)
|
||||
}, TEST_CODE_GIZMO)
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await page.waitForTimeout(100)
|
||||
// wait for execution done
|
||||
await u.openDebugPanel()
|
||||
@ -117,30 +113,30 @@ _test.describe('Testing Gizmo', () => {
|
||||
|
||||
await Promise.all([
|
||||
// position
|
||||
_expect(page.getByTestId('cam-x-position')).toHaveValue(
|
||||
expect(page.getByTestId('cam-x-position')).toHaveValue(
|
||||
expectedCameraPosition.x.toString()
|
||||
),
|
||||
_expect(page.getByTestId('cam-y-position')).toHaveValue(
|
||||
expect(page.getByTestId('cam-y-position')).toHaveValue(
|
||||
expectedCameraPosition.y.toString()
|
||||
),
|
||||
_expect(page.getByTestId('cam-z-position')).toHaveValue(
|
||||
expect(page.getByTestId('cam-z-position')).toHaveValue(
|
||||
expectedCameraPosition.z.toString()
|
||||
),
|
||||
// target
|
||||
_expect(page.getByTestId('cam-x-target')).toHaveValue(
|
||||
expect(page.getByTestId('cam-x-target')).toHaveValue(
|
||||
expectedCameraTarget.x.toString()
|
||||
),
|
||||
_expect(page.getByTestId('cam-y-target')).toHaveValue(
|
||||
expect(page.getByTestId('cam-y-target')).toHaveValue(
|
||||
expectedCameraTarget.y.toString()
|
||||
),
|
||||
_expect(page.getByTestId('cam-z-target')).toHaveValue(
|
||||
expect(page.getByTestId('cam-z-target')).toHaveValue(
|
||||
expectedCameraTarget.z.toString()
|
||||
),
|
||||
])
|
||||
})
|
||||
}
|
||||
|
||||
_test('Context menu and popover menu', async ({ page }) => {
|
||||
test('Context menu and popover menu', async ({ page, homePage }) => {
|
||||
const testCase = {
|
||||
testDescription: 'Right view',
|
||||
expectedCameraPosition: { x: 5660.02, y: -152, z: 26 },
|
||||
@ -152,9 +148,9 @@ _test.describe('Testing Gizmo', () => {
|
||||
await page.addInitScript((TEST_CODE_GIZMO) => {
|
||||
localStorage.setItem('persistCode', TEST_CODE_GIZMO)
|
||||
}, TEST_CODE_GIZMO)
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await page.waitForTimeout(100)
|
||||
// wait for execution done
|
||||
await u.openDebugPanel()
|
||||
@ -196,7 +192,7 @@ _test.describe('Testing Gizmo', () => {
|
||||
const buttonToTest = page.getByRole('button', {
|
||||
name: testCase.testDescription,
|
||||
})
|
||||
await _expect(buttonToTest).toBeVisible()
|
||||
await expect(buttonToTest).toBeVisible()
|
||||
await buttonToTest.click()
|
||||
|
||||
// Now assert we've moved to the correct view
|
||||
@ -215,23 +211,23 @@ _test.describe('Testing Gizmo', () => {
|
||||
|
||||
await Promise.all([
|
||||
// position
|
||||
_expect(page.getByTestId('cam-x-position')).toHaveValue(
|
||||
expect(page.getByTestId('cam-x-position')).toHaveValue(
|
||||
testCase.expectedCameraPosition.x.toString()
|
||||
),
|
||||
_expect(page.getByTestId('cam-y-position')).toHaveValue(
|
||||
expect(page.getByTestId('cam-y-position')).toHaveValue(
|
||||
testCase.expectedCameraPosition.y.toString()
|
||||
),
|
||||
_expect(page.getByTestId('cam-z-position')).toHaveValue(
|
||||
expect(page.getByTestId('cam-z-position')).toHaveValue(
|
||||
testCase.expectedCameraPosition.z.toString()
|
||||
),
|
||||
// target
|
||||
_expect(page.getByTestId('cam-x-target')).toHaveValue(
|
||||
expect(page.getByTestId('cam-x-target')).toHaveValue(
|
||||
testCase.expectedCameraTarget.x.toString()
|
||||
),
|
||||
_expect(page.getByTestId('cam-y-target')).toHaveValue(
|
||||
expect(page.getByTestId('cam-y-target')).toHaveValue(
|
||||
testCase.expectedCameraTarget.y.toString()
|
||||
),
|
||||
_expect(page.getByTestId('cam-z-target')).toHaveValue(
|
||||
expect(page.getByTestId('cam-z-target')).toHaveValue(
|
||||
testCase.expectedCameraTarget.z.toString()
|
||||
),
|
||||
])
|
||||
@ -242,32 +238,59 @@ _test.describe('Testing Gizmo', () => {
|
||||
const gizmoPopoverButton = page.getByRole('button', {
|
||||
name: 'view settings',
|
||||
})
|
||||
await _expect(gizmoPopoverButton).toBeVisible()
|
||||
await expect(gizmoPopoverButton).toBeVisible()
|
||||
await gizmoPopoverButton.click()
|
||||
await _expect(buttonToTest).toBeVisible()
|
||||
await expect(buttonToTest).toBeVisible()
|
||||
})
|
||||
})
|
||||
|
||||
test.describe(`Testing gizmo, fixture-based`, () => {
|
||||
test('Center on selection from menu', async ({
|
||||
app,
|
||||
context,
|
||||
page,
|
||||
homePage,
|
||||
cmdBar,
|
||||
editor,
|
||||
toolbar,
|
||||
scene,
|
||||
}) => {
|
||||
test.skip(
|
||||
process.platform === 'win32',
|
||||
'Fails on windows in CI, can not be replicated locally on windows.'
|
||||
)
|
||||
await context.addInitScript(() => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`
|
||||
const sketch002 = startSketchOn('XZ')
|
||||
|> startProfileAt([-108.83, -57.48], %)
|
||||
|> angledLine([0, 105.13], %, $rectangleSegmentA001)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001) - 90,
|
||||
77.9
|
||||
], %)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001),
|
||||
-segLen(rectangleSegmentA001)
|
||||
], %)
|
||||
|> close(%)
|
||||
const sketch001 = startSketchOn('XZ')
|
||||
|> circle({
|
||||
center: [818.33, 168.1],
|
||||
radius: 182.8
|
||||
}, %)
|
||||
|> extrude(50, %)
|
||||
`
|
||||
)
|
||||
})
|
||||
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
const u = await getUtils(page)
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await test.step(`Setup`, async () => {
|
||||
const file = await app.getInputFile('test-circle-extrude.kcl')
|
||||
await app.initialise(file)
|
||||
await scene.expectState({
|
||||
camera: {
|
||||
position: [4982.21, -23865.37, 13810.64],
|
||||
target: [4982.21, 0, 2737.1],
|
||||
position: [11912.6, -39586.98, 21391.21],
|
||||
target: [11912.6, -635, 3317.49],
|
||||
},
|
||||
})
|
||||
})
|
||||
@ -275,7 +298,7 @@ test.describe(`Testing gizmo, fixture-based`, () => {
|
||||
|
||||
await test.step(`Select an edge of this circle`, async () => {
|
||||
const circleSnippet =
|
||||
'circle({ center = [318.33, 168.1], radius = 182.8 }, %)'
|
||||
'circle({ center: [818.33, 168.1], radius: 182.8 }, %)'
|
||||
await moveToCircle()
|
||||
await clickCircle()
|
||||
await editor.expectState({
|
||||
@ -292,8 +315,8 @@ test.describe(`Testing gizmo, fixture-based`, () => {
|
||||
await test.step(`Verify the camera moved`, async () => {
|
||||
await scene.expectState({
|
||||
camera: {
|
||||
position: [0, -23865.37, 11073.53],
|
||||
target: [0, 0, 0],
|
||||
position: [20785.58, -40221.98, 22343.46],
|
||||
target: [20785.58, -1270, 4269.74],
|
||||
},
|
||||
})
|
||||
})
|
||||
|
@ -1,18 +1,8 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { getUtils, setup, tearDown } from './test-utils'
|
||||
import { TEST_SETTINGS, TEST_SETTINGS_KEY } from './storageStates'
|
||||
import * as TOML from '@iarna/toml'
|
||||
|
||||
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||
await setup(context, page, testInfo)
|
||||
})
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
import { test, expect } from './zoo-test'
|
||||
import { getUtils } from './test-utils'
|
||||
|
||||
test.describe('Test toggling perspective', () => {
|
||||
test('via command palette and toggle', async ({ page }) => {
|
||||
test.fixme('via command palette and toggle', async ({ page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
// Locators and constants
|
||||
@ -20,7 +10,7 @@ test.describe('Test toggling perspective', () => {
|
||||
const screenHeight = 500
|
||||
const checkedScreenLocation = {
|
||||
x: screenWidth * 0.71,
|
||||
y: screenHeight * 0.4,
|
||||
y: screenHeight * 0.2,
|
||||
}
|
||||
const backgroundColor: [number, number, number] = [29, 29, 29]
|
||||
const xzPlaneColor: [number, number, number] = [82, 55, 96]
|
||||
@ -40,8 +30,8 @@ test.describe('Test toggling perspective', () => {
|
||||
})
|
||||
|
||||
await test.step('Setup', async () => {
|
||||
await page.setViewportSize({ width: screenWidth, height: screenHeight })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: screenWidth, height: screenHeight })
|
||||
await homePage.goToModelingScene()
|
||||
await u.closeKclCodePanel()
|
||||
await expect
|
||||
.poll(async () => locationToHaveColor(backgroundColor), {
|
||||
@ -52,11 +42,17 @@ test.describe('Test toggling perspective', () => {
|
||||
await expect(projectionToggle).toHaveAttribute('aria-checked', 'true')
|
||||
})
|
||||
|
||||
// Extremely wild note: flicking between ortho and persp actually changes
|
||||
// the orientation of the axis/camera. How can you see this? Well toggle it,
|
||||
// then refresh. You'll see it doesn't match what we left.
|
||||
await test.step('Switch to ortho via command palette', async () => {
|
||||
await commandPaletteButton.click()
|
||||
await page.waitForTimeout(1000)
|
||||
await commandOption.click()
|
||||
await page.waitForTimeout(1000)
|
||||
await orthoOption.click()
|
||||
await expect(commandToast).toBeVisible()
|
||||
await expect(commandToast).not.toBeVisible()
|
||||
await expect
|
||||
.poll(async () => locationToHaveColor(xzPlaneColor), {
|
||||
timeout: 5000,
|
||||
@ -67,27 +63,9 @@ test.describe('Test toggling perspective', () => {
|
||||
})
|
||||
|
||||
await test.step(`Refresh the page and ensure the stream is loaded in ortho`, async () => {
|
||||
// In playwright web, the settings set while testing are not persisted because
|
||||
// the `addInitScript` within `setup` is re-run on page reload
|
||||
await page.addInitScript(
|
||||
({ settingsKey, settings }) => {
|
||||
localStorage.setItem(settingsKey, settings)
|
||||
},
|
||||
{
|
||||
settingsKey: TEST_SETTINGS_KEY,
|
||||
settings: TOML.stringify({
|
||||
settings: {
|
||||
...TEST_SETTINGS,
|
||||
modeling: {
|
||||
...TEST_SETTINGS.modeling,
|
||||
cameraProjection: 'orthographic',
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
)
|
||||
await page.reload()
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.waitForTimeout(1000)
|
||||
await u.closeKclCodePanel()
|
||||
await expect
|
||||
.poll(async () => locationToHaveColor(xzPlaneColor), {
|
||||
timeout: 5000,
|
||||
|
@ -1,35 +1,30 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { getUtils, setup, setupElectron, tearDown } from './test-utils'
|
||||
import { test, expect } from './zoo-test'
|
||||
import { getUtils } from './test-utils'
|
||||
import { bracket } from 'lib/exampleKcl'
|
||||
import * as fsp from 'fs/promises'
|
||||
import { join } from 'path'
|
||||
import { FILE_EXT } from 'lib/constants'
|
||||
import { UnitLength_type } from '@kittycad/lib/dist/types/src/models'
|
||||
|
||||
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||
await setup(context, page, testInfo)
|
||||
})
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
|
||||
test.describe('Testing in-app sample loading', () => {
|
||||
/**
|
||||
* Note this test implicitly depends on the KCL sample "car-wheel.kcl",
|
||||
* its title, and its units settings. https://github.com/KittyCAD/kcl-samples/blob/main/car-wheel/car-wheel.kcl
|
||||
*/
|
||||
test('Web: should overwrite current code, cannot create new file', async ({
|
||||
editor,
|
||||
context,
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
await test.step(`Test setup`, async () => {
|
||||
await page.addInitScript((code) => {
|
||||
await context.addInitScript((code) => {
|
||||
window.localStorage.setItem('persistCode', code)
|
||||
}, bracket)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
})
|
||||
|
||||
// Locators and constants
|
||||
@ -54,13 +49,13 @@ test.describe('Testing in-app sample loading', () => {
|
||||
})
|
||||
const warningText = page.getByText('Overwrite current file and units?')
|
||||
const confirmButton = page.getByRole('button', { name: 'Submit command' })
|
||||
const codeLocator = page.locator('.cm-content')
|
||||
const unitsToast = (unit: UnitLength_type) =>
|
||||
page.getByText(`Set default unit to "${unit}" for this project`)
|
||||
|
||||
await test.step(`Precondition: check the initial code`, async () => {
|
||||
await u.openKclCodePanel()
|
||||
await expect(codeLocator).toContainText(bracket.split('\n')[0])
|
||||
await editor.scrollToText(bracket.split('\n')[0])
|
||||
await editor.expectEditor.toContain(bracket.split('\n')[0])
|
||||
})
|
||||
|
||||
await test.step(`Load a KCL sample with the command palette`, async () => {
|
||||
@ -73,7 +68,7 @@ test.describe('Testing in-app sample loading', () => {
|
||||
await expect(warningText).toBeVisible()
|
||||
await confirmButton.click()
|
||||
|
||||
await expect(codeLocator).toContainText('// ' + newSample.title)
|
||||
await editor.expectEditor.toContain('// ' + newSample.title)
|
||||
await expect(unitsToast('in')).toBeVisible()
|
||||
})
|
||||
})
|
||||
@ -86,16 +81,13 @@ test.describe('Testing in-app sample loading', () => {
|
||||
test(
|
||||
'Desktop: should create new file by default, optionally overwrite',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName: _ }, testInfo) => {
|
||||
const { electronApp, page, dir } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
const bracketDir = join(dir, 'bracket')
|
||||
await fsp.mkdir(bracketDir, { recursive: true })
|
||||
await fsp.writeFile(join(bracketDir, 'main.kcl'), bracket, {
|
||||
encoding: 'utf-8',
|
||||
})
|
||||
},
|
||||
async ({ editor, context, page }, testInfo) => {
|
||||
const { dir } = await context.folderSetupFn(async (dir) => {
|
||||
const bracketDir = join(dir, 'bracket')
|
||||
await fsp.mkdir(bracketDir, { recursive: true })
|
||||
await fsp.writeFile(join(bracketDir, 'main.kcl'), bracket, {
|
||||
encoding: 'utf-8',
|
||||
})
|
||||
})
|
||||
const u = await getUtils(page)
|
||||
|
||||
@ -134,19 +126,19 @@ test.describe('Testing in-app sample loading', () => {
|
||||
page.getByRole('listitem').filter({
|
||||
has: page.getByRole('button', { name }),
|
||||
})
|
||||
const codeLocator = page.locator('.cm-content')
|
||||
const unitsToast = (unit: UnitLength_type) =>
|
||||
page.getByText(`Set default unit to "${unit}" for this project`)
|
||||
|
||||
await test.step(`Test setup`, async () => {
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await projectCard.click()
|
||||
await u.waitForPageLoad()
|
||||
})
|
||||
|
||||
await test.step(`Precondition: check the initial code`, async () => {
|
||||
await u.openKclCodePanel()
|
||||
await expect(codeLocator).toContainText(bracket.split('\n')[0])
|
||||
await editor.scrollToText(bracket.split('\n')[0])
|
||||
await editor.expectEditor.toContain(bracket.split('\n')[0])
|
||||
await u.openFilePanel()
|
||||
|
||||
await expect(projectMenuButton).toContainText('main.kcl')
|
||||
@ -163,7 +155,7 @@ test.describe('Testing in-app sample loading', () => {
|
||||
})
|
||||
|
||||
await test.step(`Ensure we made and opened a new file`, async () => {
|
||||
await expect(codeLocator).toContainText('// ' + sampleOne.title)
|
||||
await editor.expectEditor.toContain('// ' + sampleOne.title)
|
||||
await expect(newlyCreatedFile(sampleOne.file)).toBeVisible()
|
||||
await expect(projectMenuButton).toContainText(sampleOne.file)
|
||||
await expect(unitsToast('in')).toBeVisible()
|
||||
@ -182,7 +174,7 @@ test.describe('Testing in-app sample loading', () => {
|
||||
})
|
||||
|
||||
await test.step(`Ensure we overwrote the current file without navigating`, async () => {
|
||||
await expect(codeLocator).toContainText('// ' + sampleTwo.title)
|
||||
await editor.expectEditor.toContain('// ' + sampleTwo.title)
|
||||
await test.step(`Check actual file contents`, async () => {
|
||||
await expect
|
||||
.poll(async () => {
|
||||
@ -198,8 +190,6 @@ test.describe('Testing in-app sample loading', () => {
|
||||
await expect(projectMenuButton).toContainText(sampleOne.file)
|
||||
await expect(unitsToast('mm')).toBeVisible()
|
||||
})
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
@ -1,24 +1,16 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { test, expect } from './zoo-test'
|
||||
|
||||
import { commonPoints, getUtils, setup, tearDown } from './test-utils'
|
||||
import { commonPoints, getUtils } from './test-utils'
|
||||
import { Coords2d } from 'lang/std/sketch'
|
||||
import { KCL_DEFAULT_LENGTH } from 'lib/constants'
|
||||
import { uuidv4 } from 'lib/utils'
|
||||
|
||||
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||
await setup(context, page, testInfo)
|
||||
})
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
|
||||
test.describe('Testing selections', () => {
|
||||
test.setTimeout(90_000)
|
||||
test(
|
||||
'Selections work on fresh and edited sketch',
|
||||
{ tag: ['@skipWin'] },
|
||||
async ({ page }) => {
|
||||
async ({ page, homePage }) => {
|
||||
// Skip on windows its being weird.
|
||||
test.skip(process.platform === 'win32', 'Skip on windows')
|
||||
|
||||
@ -27,9 +19,9 @@ test.describe('Testing selections', () => {
|
||||
// source ranges are wrong, hovers won't work
|
||||
const u = await getUtils(page)
|
||||
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.openDebugPanel()
|
||||
|
||||
const yAxisClick = () =>
|
||||
@ -79,31 +71,31 @@ test.describe('Testing selections', () => {
|
||||
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt(${commonPoints.startAt}, %)`)
|
||||
|> startProfileAt(${commonPoints.startAt}, %)`)
|
||||
|
||||
await page.waitForTimeout(100)
|
||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
||||
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt(${commonPoints.startAt}, %)
|
||||
|> xLine(${commonPoints.num1}, %)`)
|
||||
|> startProfileAt(${commonPoints.startAt}, %)
|
||||
|> xLine(${commonPoints.num1}, %)`)
|
||||
|
||||
await page.waitForTimeout(100)
|
||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt(${commonPoints.startAt}, %)
|
||||
|> xLine(${commonPoints.num1}, %)
|
||||
|> yLine(${commonPoints.num1 + 0.01}, %)`)
|
||||
|> startProfileAt(${commonPoints.startAt}, %)
|
||||
|> xLine(${commonPoints.num1}, %)
|
||||
|> yLine(${commonPoints.num1 + 0.01}, %)`)
|
||||
await page.waitForTimeout(100)
|
||||
await page.mouse.click(startXPx, 500 - PUR * 20)
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt(${commonPoints.startAt}, %)
|
||||
|> xLine(${commonPoints.num1}, %)
|
||||
|> yLine(${commonPoints.num1 + 0.01}, %)
|
||||
|> xLine(${commonPoints.num2 * -1}, %)`)
|
||||
|> startProfileAt(${commonPoints.startAt}, %)
|
||||
|> xLine(${commonPoints.num1}, %)
|
||||
|> yLine(${commonPoints.num1 + 0.01}, %)
|
||||
|> xLine(${commonPoints.num2 * -1}, %)`)
|
||||
|
||||
// deselect line tool
|
||||
await page.getByRole('button', { name: 'line Line', exact: true }).click()
|
||||
@ -264,78 +256,78 @@ test.describe('Testing selections', () => {
|
||||
}
|
||||
)
|
||||
|
||||
test('Solids should be select and deletable', async ({ page }) => {
|
||||
test('Solids should be select and deletable', async ({ page, homePage }) => {
|
||||
test.setTimeout(90_000)
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([-79.26, 95.04], %)
|
||||
|> line([112.54, 127.64], %, $seg02)
|
||||
|> line([170.36, -121.61], %, $seg01)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(50, sketch001)
|
||||
sketch005 = startSketchOn(extrude001, 'END')
|
||||
|> startProfileAt([23.24, 136.52], %)
|
||||
|> line([-8.44, 36.61], %)
|
||||
|> line([49.4, 2.05], %)
|
||||
|> line([29.69, -46.95], %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
sketch003 = startSketchOn(extrude001, seg01)
|
||||
|> startProfileAt([21.23, 17.81], %)
|
||||
|> line([51.97, 21.32], %)
|
||||
|> line([4.07, -22.75], %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
sketch002 = startSketchOn(extrude001, seg02)
|
||||
|> startProfileAt([-100.54, 16.99], %)
|
||||
|> line([0, 20.03], %)
|
||||
|> line([62.61, 0], %, $seg03)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude002 = extrude(50, sketch002)
|
||||
sketch004 = startSketchOn(extrude002, seg03)
|
||||
|> startProfileAt([57.07, 134.77], %)
|
||||
|> line([-4.72, 22.84], %)
|
||||
|> line([28.8, 6.71], %)
|
||||
|> line([9.19, -25.33], %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude003 = extrude(20, sketch004)
|
||||
pipeLength = 40
|
||||
pipeSmallDia = 10
|
||||
pipeLargeDia = 20
|
||||
thickness = 0.5
|
||||
part009 = startSketchOn('XY')
|
||||
|> startProfileAt([pipeLargeDia - (thickness / 2), 38], %)
|
||||
|> line([thickness, 0], %)
|
||||
|> line([0, -1], %)
|
||||
|> angledLineToX({
|
||||
angle = 60,
|
||||
to = pipeSmallDia + thickness
|
||||
}, %)
|
||||
|> line([0, -pipeLength], %)
|
||||
|> angledLineToX({
|
||||
angle = -60,
|
||||
to = pipeLargeDia + thickness
|
||||
}, %)
|
||||
|> line([0, -1], %)
|
||||
|> line([-thickness, 0], %)
|
||||
|> line([0, 1], %)
|
||||
|> angledLineToX({ angle = 120, to = pipeSmallDia }, %)
|
||||
|> line([0, pipeLength], %)
|
||||
|> angledLineToX({ angle = 60, to = pipeLargeDia }, %)
|
||||
|> close(%)
|
||||
rev = revolve({ axis = 'y' }, part009)
|
||||
`
|
||||
|> startProfileAt([-79.26, 95.04], %)
|
||||
|> line([112.54, 127.64], %, $seg02)
|
||||
|> line([170.36, -121.61], %, $seg01)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(50, sketch001)
|
||||
sketch005 = startSketchOn(extrude001, 'END')
|
||||
|> startProfileAt([23.24, 136.52], %)
|
||||
|> line([-8.44, 36.61], %)
|
||||
|> line([49.4, 2.05], %)
|
||||
|> line([29.69, -46.95], %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
sketch003 = startSketchOn(extrude001, seg01)
|
||||
|> startProfileAt([21.23, 17.81], %)
|
||||
|> line([51.97, 21.32], %)
|
||||
|> line([4.07, -22.75], %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
sketch002 = startSketchOn(extrude001, seg02)
|
||||
|> startProfileAt([-100.54, 16.99], %)
|
||||
|> line([0, 20.03], %)
|
||||
|> line([62.61, 0], %, $seg03)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude002 = extrude(50, sketch002)
|
||||
sketch004 = startSketchOn(extrude002, seg03)
|
||||
|> startProfileAt([57.07, 134.77], %)
|
||||
|> line([-4.72, 22.84], %)
|
||||
|> line([28.8, 6.71], %)
|
||||
|> line([9.19, -25.33], %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude003 = extrude(20, sketch004)
|
||||
pipeLength = 40
|
||||
pipeSmallDia = 10
|
||||
pipeLargeDia = 20
|
||||
thickness = 0.5
|
||||
part009 = startSketchOn('XY')
|
||||
|> startProfileAt([pipeLargeDia - (thickness / 2), 38], %)
|
||||
|> line([thickness, 0], %)
|
||||
|> line([0, -1], %)
|
||||
|> angledLineToX({
|
||||
angle = 60,
|
||||
to = pipeSmallDia + thickness
|
||||
}, %)
|
||||
|> line([0, -pipeLength], %)
|
||||
|> angledLineToX({
|
||||
angle = -60,
|
||||
to = pipeLargeDia + thickness
|
||||
}, %)
|
||||
|> line([0, -1], %)
|
||||
|> line([-thickness, 0], %)
|
||||
|> line([0, 1], %)
|
||||
|> angledLineToX({ angle = 120, to = pipeSmallDia }, %)
|
||||
|> line([0, pipeLength], %)
|
||||
|> angledLineToX({ angle = 60, to = pipeLargeDia }, %)
|
||||
|> close(%)
|
||||
rev = revolve({ axis: 'y' }, part009)
|
||||
`
|
||||
)
|
||||
}, KCL_DEFAULT_LENGTH)
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.goto('/')
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
@ -395,29 +387,29 @@ rev = revolve({ axis = 'y' }, part009)
|
||||
`extrude001 = extrude(50, sketch001)`
|
||||
)
|
||||
await expect(u.codeLocator).toContainText(`sketch005 = startSketchOn({
|
||||
plane = {
|
||||
origin = { x = 0, y = -50, z = 0 },
|
||||
x_axis = { x = 1, y = 0, z = 0 },
|
||||
y_axis = { x = 0, y = 0, z = 1 },
|
||||
z_axis = { x = 0, y = -1, z = 0 }
|
||||
}
|
||||
})`)
|
||||
plane = {
|
||||
origin = { x = 0, y = -50, z = 0 },
|
||||
x_axis = { x = 1, y = 0, z = 0 },
|
||||
y_axis = { x = 0, y = 0, z = 1 },
|
||||
z_axis = { x = 0, y = -1, z = 0 }
|
||||
}
|
||||
})`)
|
||||
await expect(u.codeLocator).toContainText(`sketch003 = startSketchOn({
|
||||
plane = {
|
||||
origin = { x = 116.53, y = 0, z = 163.25 },
|
||||
x_axis = { x = -0.81, y = 0, z = 0.58 },
|
||||
y_axis = { x = 0, y = -1, z = 0 },
|
||||
z_axis = { x = 0.58, y = 0, z = 0.81 }
|
||||
}
|
||||
})`)
|
||||
plane = {
|
||||
origin = { x = 116.53, y = 0, z = 163.25 },
|
||||
x_axis = { x = -0.81, y = 0, z = 0.58 },
|
||||
y_axis = { x = 0, y = -1, z = 0 },
|
||||
z_axis = { x = 0.58, y = 0, z = 0.81 }
|
||||
}
|
||||
})`)
|
||||
await expect(u.codeLocator).toContainText(`sketch002 = startSketchOn({
|
||||
plane = {
|
||||
origin = { x = -91.74, y = 0, z = 80.89 },
|
||||
x_axis = { x = -0.66, y = 0, z = -0.75 },
|
||||
y_axis = { x = 0, y = -1, z = 0 },
|
||||
z_axis = { x = -0.75, y = 0, z = 0.66 }
|
||||
}
|
||||
})`)
|
||||
plane = {
|
||||
origin = { x = -91.74, y = 0, z = 80.89 },
|
||||
x_axis = { x = -0.66, y = 0, z = -0.75 },
|
||||
y_axis = { x = 0, y = -1, z = 0 },
|
||||
z_axis = { x = -0.75, y = 0, z = 0.66 }
|
||||
}
|
||||
})`)
|
||||
|
||||
// DELETE SOLID 2D
|
||||
await page.mouse.click(solid2d.x, solid2d.y)
|
||||
@ -433,31 +425,32 @@ rev = revolve({ axis = 'y' }, part009)
|
||||
})
|
||||
test("Deleting solid that the AST mod can't handle results in a toast message", async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([-79.26, 95.04], %)
|
||||
|> line([112.54, 127.64], %, $seg02)
|
||||
|> line([170.36, -121.61], %, $seg01)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(50, sketch001)
|
||||
launderExtrudeThroughVar = extrude001
|
||||
sketch002 = startSketchOn(launderExtrudeThroughVar, seg02)
|
||||
|> startProfileAt([-100.54, 16.99], %)
|
||||
|> line([0, 20.03], %)
|
||||
|> line([62.61, 0], %, $seg03)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
`
|
||||
|> startProfileAt([-79.26, 95.04], %)
|
||||
|> line([112.54, 127.64], %, $seg02)
|
||||
|> line([170.36, -121.61], %, $seg01)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(50, sketch001)
|
||||
launderExtrudeThroughVar = extrude001
|
||||
sketch002 = startSketchOn(launderExtrudeThroughVar, seg02)
|
||||
|> startProfileAt([-100.54, 16.99], %)
|
||||
|> line([0, 20.03], %)
|
||||
|> line([62.61, 0], %, $seg03)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
`
|
||||
)
|
||||
}, KCL_DEFAULT_LENGTH)
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.goto('/')
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]', 10_000)
|
||||
@ -497,37 +490,40 @@ sketch002 = startSketchOn(launderExtrudeThroughVar, seg02)
|
||||
})
|
||||
test('Hovering over 3d features highlights code, clicking puts the cursor in the right place and sends selection id to engine', 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)
|
||||
await page.addInitScript(async (KCL_DEFAULT_LENGTH) => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`part001 = startSketchOn('XZ')
|
||||
|> startProfileAt([20, 0], %)
|
||||
|> line([7.13, 4 + 0], %)
|
||||
|> angledLine({ angle = 3 + 0, length = 3.14 + 0 }, %)
|
||||
|> lineTo([20.14 + 0, -0.14 + 0], %)
|
||||
|> xLineTo(29 + 0, %)
|
||||
|> yLine(-3.14 + 0, %, $a)
|
||||
|> xLine(1.63, %)
|
||||
|> angledLineOfXLength({ angle = 3 + 0, length = 3.14 }, %)
|
||||
|> angledLineOfYLength({ angle = 30, length = 3 + 0 }, %)
|
||||
|> angledLineToX({ angle = 22.14 + 0, to = 12 }, %)
|
||||
|> angledLineToY({ angle = 30, to = 11.14 }, %)
|
||||
|> angledLineThatIntersects({
|
||||
angle = 3.14,
|
||||
intersectTag = a,
|
||||
offset = 0
|
||||
}, %)
|
||||
|> tangentialArcTo([13.14 + 0, 13.14], %)
|
||||
|> close(%)
|
||||
|> extrude(5 + 7, %)
|
||||
`
|
||||
|> startProfileAt([20, 0], %)
|
||||
|> line([7.13, 4 + 0], %)
|
||||
|> angledLine({ angle = 3 + 0, length = 3.14 + 0 }, %)
|
||||
|> lineTo([20.14 + 0, -0.14 + 0], %)
|
||||
|> xLineTo(29 + 0, %)
|
||||
|> yLine(-3.14 + 0, %, $a)
|
||||
|> xLine(1.63, %)
|
||||
|> angledLineOfXLength({ angle = 3 + 0, length = 3.14 }, %)
|
||||
|> angledLineOfYLength({ angle = 30, length = 3 + 0 }, %)
|
||||
|> angledLineToX({ angle = 22.14 + 0, to = 12 }, %)
|
||||
|> angledLineToY({ angle = 30, to = 11.14 }, %)
|
||||
|> angledLineThatIntersects({
|
||||
angle = 3.14,
|
||||
intersectTag = a,
|
||||
offset = 0
|
||||
}, %)
|
||||
|> tangentialArcTo([13.14 + 0, 13.14], %)
|
||||
|> close(%)
|
||||
|> extrude(5 + 7, %)
|
||||
`
|
||||
)
|
||||
}, KCL_DEFAULT_LENGTH)
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// wait for execution done
|
||||
await u.openDebugPanel()
|
||||
@ -727,29 +723,29 @@ sketch002 = startSketchOn(launderExtrudeThroughVar, seg02)
|
||||
|
||||
await u.removeCurrentCode()
|
||||
await u.codeLocator.fill(`sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([75.8, 317.2], %) // [$startCapTag, $EndCapTag]
|
||||
|> angledLine([0, 268.43], %, $rectangleSegmentA001)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001) - 90,
|
||||
217.26
|
||||
], %, $seg01)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001),
|
||||
-segLen(rectangleSegmentA001)
|
||||
], %, $yo)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %, $seg02)
|
||||
|> close(%)
|
||||
extrude001 = extrude(100, sketch001)
|
||||
|> chamfer({
|
||||
length = 30,
|
||||
tags = [
|
||||
seg01,
|
||||
getNextAdjacentEdge(yo),
|
||||
getNextAdjacentEdge(seg02),
|
||||
getOppositeEdge(seg01)
|
||||
]
|
||||
}, %)
|
||||
`)
|
||||
|> startProfileAt([75.8, 317.2], %) // [$startCapTag, $EndCapTag]
|
||||
|> angledLine([0, 268.43], %, $rectangleSegmentA001)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001) - 90,
|
||||
217.26
|
||||
], %, $seg01)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001),
|
||||
-segLen(rectangleSegmentA001)
|
||||
], %, $yo)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %, $seg02)
|
||||
|> close(%)
|
||||
extrude001 = extrude(100, sketch001)
|
||||
|> chamfer({
|
||||
length = 30,
|
||||
tags = [
|
||||
seg01,
|
||||
getNextAdjacentEdge(yo),
|
||||
getNextAdjacentEdge(seg02),
|
||||
getOppositeEdge(seg01)
|
||||
]
|
||||
}, %)
|
||||
`)
|
||||
await expect(
|
||||
page.getByTestId('model-state-indicator-execution-done')
|
||||
).toBeVisible()
|
||||
@ -837,39 +833,45 @@ extrude001 = extrude(100, sketch001)
|
||||
})
|
||||
test("Extrude button should be disabled if there's no extrudable geometry when nothing is selected", async ({
|
||||
page,
|
||||
editor,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([3.29, 7.86], %)
|
||||
|> line([2.48, 2.44], %)
|
||||
|> line([2.66, 1.17], %)
|
||||
|> line([3.75, 0.46], %)
|
||||
|> line([4.99, -0.46], %, $seg01)
|
||||
|> line([3.3, -2.12], %)
|
||||
|> line([2.16, -3.33], %)
|
||||
|> line([0.85, -3.08], %)
|
||||
|> line([-0.18, -3.36], %)
|
||||
|> line([-3.86, -2.73], %)
|
||||
|> line([-17.67, 0.85], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(10, sketch001)
|
||||
`
|
||||
|> startProfileAt([3.29, 7.86], %)
|
||||
|> line([2.48, 2.44], %)
|
||||
|> line([2.66, 1.17], %)
|
||||
|> line([3.75, 0.46], %)
|
||||
|> line([4.99, -0.46], %, $seg01)
|
||||
|> line([3.3, -2.12], %)
|
||||
|> line([2.16, -3.33], %)
|
||||
|> line([0.85, -3.08], %)
|
||||
|> line([-0.18, -3.36], %)
|
||||
|> line([-3.86, -2.73], %)
|
||||
|> line([-17.67, 0.85], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(10, sketch001)
|
||||
`
|
||||
)
|
||||
})
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
// wait for execution done
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
await u.closeDebugPanel()
|
||||
|
||||
const selectUnExtrudable = () =>
|
||||
page.getByText(`line([4.99, -0.46], %, $seg01)`).click()
|
||||
const selectUnExtrudable = async () => {
|
||||
await editor.scrollToText(`line([4.99, -0.46], %, $seg01)`)
|
||||
await page.getByText(`line([4.99, -0.46], %, $seg01)`).click()
|
||||
}
|
||||
const clickEmpty = () => page.mouse.click(700, 460)
|
||||
await selectUnExtrudable()
|
||||
// expect extrude button to be disabled
|
||||
@ -879,17 +881,18 @@ extrude001 = extrude(10, sketch001)
|
||||
|
||||
// expect active line to contain nothing
|
||||
await expect(page.locator('.cm-activeLine')).toHaveText('')
|
||||
|
||||
// and extrude to still be disabled
|
||||
await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled()
|
||||
|
||||
const codeToAdd = `${await u.codeLocator.allInnerTexts()}
|
||||
sketch002 = startSketchOn(extrude001, $seg01)
|
||||
|> startProfileAt([-12.94, 6.6], %)
|
||||
|> line([2.45, -0.2], %)
|
||||
|> line([-2, -1.25], %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
`
|
||||
sketch002 = startSketchOn(extrude001, $seg01)
|
||||
|> startProfileAt([-12.94, 6.6], %)
|
||||
|> line([2.45, -0.2], %)
|
||||
|> line([-2, -1.25], %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
`
|
||||
await u.codeLocator.fill(codeToAdd)
|
||||
|
||||
await selectUnExtrudable()
|
||||
@ -904,23 +907,23 @@ sketch002 = startSketchOn(extrude001, $seg01)
|
||||
).not.toBeDisabled()
|
||||
})
|
||||
|
||||
test('Fillet button states test', async ({ page }) => {
|
||||
test('Fillet button states test', async ({ page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([-5, -5], %)
|
||||
|> line([0, 10], %)
|
||||
|> line([10, 0], %)
|
||||
|> line([0, -10], %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)`
|
||||
|> startProfileAt([-5, -5], %)
|
||||
|> line([0, 10], %)
|
||||
|> line([10, 0], %)
|
||||
|> line([0, -10], %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)`
|
||||
)
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
await u.closeDebugPanel()
|
||||
@ -937,7 +940,7 @@ sketch002 = startSketchOn(extrude001, $seg01)
|
||||
|
||||
// test fillet button with the body in the scene
|
||||
const codeToAdd = `${await u.codeLocator.allInnerTexts()}
|
||||
extrude001 = extrude(10, sketch001)`
|
||||
extrude001 = extrude(10, sketch001)`
|
||||
await u.codeLocator.clear()
|
||||
await u.codeLocator.fill(codeToAdd)
|
||||
await selectSegment()
|
||||
@ -958,6 +961,7 @@ extrude001 = extrude(10, sketch001)`
|
||||
|
||||
test('Testing selections (and hovers) work on sketches when NOT in sketch mode', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const cases = [
|
||||
{
|
||||
@ -978,21 +982,21 @@ extrude001 = extrude(10, sketch001)`
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`yo = 79
|
||||
part001 = startSketchOn('XZ')
|
||||
|> startProfileAt([-7.54, -26.74], %)
|
||||
|> ${cases[0].expectedCode}
|
||||
|> line([-3.19, -138.43], %)
|
||||
|> ${cases[1].expectedCode}
|
||||
|> line([41.19, 28.97 + 5], %)
|
||||
|> ${cases[2].expectedCode}`
|
||||
part001 = startSketchOn('XZ')
|
||||
|> startProfileAt([-7.54, -26.74], %)
|
||||
|> ${cases[0].expectedCode}
|
||||
|> line([-3.19, -138.43], %)
|
||||
|> ${cases[1].expectedCode}
|
||||
|> line([41.19, 28.97 + 5], %)
|
||||
|> ${cases[2].expectedCode}`
|
||||
)
|
||||
},
|
||||
{ cases }
|
||||
)
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.openAndClearDebugPanel()
|
||||
|
||||
await u.sendCustomCmd({
|
||||
@ -1025,24 +1029,25 @@ part001 = startSketchOn('XZ')
|
||||
})
|
||||
test("Hovering and selection of extruded faces works, and is not overridden shortly after user's click", async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([-79.26, 95.04], %)
|
||||
|> line([112.54, 127.64], %)
|
||||
|> line([170.36, -121.61], %, $seg01)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(50, sketch001)
|
||||
`
|
||||
|> startProfileAt([-79.26, 95.04], %)
|
||||
|> line([112.54, 127.64], %)
|
||||
|> line([170.36, -121.61], %, $seg01)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(50, sketch001)
|
||||
`
|
||||
)
|
||||
})
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.openAndClearDebugPanel()
|
||||
|
||||
await u.sendCustomCmd({
|
||||
@ -1125,6 +1130,7 @@ extrude001 = extrude(50, sketch001)
|
||||
})
|
||||
test("Various pipe expressions should and shouldn't allow edit and or extrude", async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
const selectionsSnippets = {
|
||||
@ -1143,46 +1149,46 @@ extrude001 = extrude(50, sketch001)
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`part001 = startSketchOn('XZ')
|
||||
${extrudeAndEditBlocked}
|
||||
|> line([25.96, 2.93], %)
|
||||
|> line([5.25, -5.72], %)
|
||||
|> line([-2.01, -10.35], %)
|
||||
|> line([-27.65, -2.78], %)
|
||||
|> close(%)
|
||||
|> extrude(5, %)
|
||||
sketch002 = startSketchOn('XZ')
|
||||
${extrudeAndEditAllowed}
|
||||
|> line([10.32, 6.47], %)
|
||||
|> line([9.71, -6.16], %)
|
||||
|> line([-3.08, -9.86], %)
|
||||
|> line([-12.02, -1.54], %)
|
||||
|> close(%)
|
||||
sketch003 = startSketchOn('XZ')
|
||||
${editOnly}
|
||||
|> line([27.55, -1.65], %)
|
||||
|> line([4.95, -8], %)
|
||||
|> line([-20.38, -10.12], %)
|
||||
|> line([-15.79, 17.08], %)
|
||||
|
||||
fn yohey = (pos) => {
|
||||
sketch004 = startSketchOn('XZ')
|
||||
${extrudeAndEditBlockedInFunction}
|
||||
|> line([27.55, -1.65], %)
|
||||
|> line([4.95, -10.53], %)
|
||||
|> line([-20.38, -8], %)
|
||||
|> line([-15.79, 17.08], %)
|
||||
return ''
|
||||
}
|
||||
|
||||
yohey([15.79, -34.6])
|
||||
`
|
||||
${extrudeAndEditBlocked}
|
||||
|> line([25.96, 2.93], %)
|
||||
|> line([5.25, -5.72], %)
|
||||
|> line([-2.01, -10.35], %)
|
||||
|> line([-27.65, -2.78], %)
|
||||
|> close(%)
|
||||
|> extrude(5, %)
|
||||
sketch002 = startSketchOn('XZ')
|
||||
${extrudeAndEditAllowed}
|
||||
|> line([10.32, 6.47], %)
|
||||
|> line([9.71, -6.16], %)
|
||||
|> line([-3.08, -9.86], %)
|
||||
|> line([-12.02, -1.54], %)
|
||||
|> close(%)
|
||||
sketch003 = startSketchOn('XZ')
|
||||
${editOnly}
|
||||
|> line([27.55, -1.65], %)
|
||||
|> line([4.95, -8], %)
|
||||
|> line([-20.38, -10.12], %)
|
||||
|> line([-15.79, 17.08], %)
|
||||
|
||||
fn yohey = (pos) => {
|
||||
sketch004 = startSketchOn('XZ')
|
||||
${extrudeAndEditBlockedInFunction}
|
||||
|> line([27.55, -1.65], %)
|
||||
|> line([4.95, -10.53], %)
|
||||
|> line([-20.38, -8], %)
|
||||
|> line([-15.79, 17.08], %)
|
||||
return ''
|
||||
}
|
||||
|
||||
yohey([15.79, -34.6])
|
||||
`
|
||||
)
|
||||
},
|
||||
selectionsSnippets
|
||||
)
|
||||
await page.setViewportSize({ width: 1200, height: 1000 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 1000 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// wait for execution done
|
||||
await u.openDebugPanel()
|
||||
@ -1222,6 +1228,7 @@ extrude001 = extrude(50, sketch001)
|
||||
|
||||
test('Deselecting line tool should mean nothing happens on click', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
/**
|
||||
* If the line tool is clicked when the state is 'No Points' it will exit Sketch mode.
|
||||
@ -1230,9 +1237,9 @@ extrude001 = extrude(50, sketch001)
|
||||
* To continue to test this workflow, we now enter sketch mode and place a single point before exiting the line tool.
|
||||
*/
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.openDebugPanel()
|
||||
|
||||
await expect(
|
||||
|
@ -1,29 +1,16 @@
|
||||
import { test, expect, Page } from '@playwright/test'
|
||||
import {
|
||||
getUtils,
|
||||
setup,
|
||||
tearDown,
|
||||
setupElectron,
|
||||
createProject,
|
||||
} from './test-utils'
|
||||
import { test, expect, Page } from './zoo-test'
|
||||
import { getUtils, createProject } from './test-utils'
|
||||
import { join } from 'path'
|
||||
import fs from 'fs'
|
||||
|
||||
test.beforeEach(async ({ context, page }) => {
|
||||
await setup(context, page)
|
||||
})
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
|
||||
test.describe('Text-to-CAD tests', () => {
|
||||
test('basic lego happy case', async ({ page }) => {
|
||||
test('basic lego happy case', async ({ page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
await test.step('Set up', async () => {
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
})
|
||||
|
||||
await sendPromptFromCommandBar(page, 'a 2x4 lego')
|
||||
@ -43,25 +30,17 @@ test.describe('Text-to-CAD tests', () => {
|
||||
const successToastMessage = page.getByText(`Text-to-CAD successful`)
|
||||
await expect(successToastMessage).toBeVisible({ timeout: 15000 })
|
||||
|
||||
await expect(page.getByText('Copied')).not.toBeVisible()
|
||||
|
||||
// Hit copy to clipboard.
|
||||
// Hit accept.
|
||||
const copyToClipboardButton = page.getByRole('button', {
|
||||
name: 'Copy to clipboard',
|
||||
name: 'Accept',
|
||||
})
|
||||
await expect(copyToClipboardButton).toBeVisible()
|
||||
|
||||
await copyToClipboardButton.click()
|
||||
|
||||
// Expect the code to be copied.
|
||||
await expect(page.getByText('Copied')).toBeVisible()
|
||||
|
||||
// Click in the code editor.
|
||||
await page.locator('.cm-content').click()
|
||||
|
||||
// Paste the code.
|
||||
await page.keyboard.press('ControlOrMeta+KeyV')
|
||||
|
||||
// Expect the code to be pasted.
|
||||
await expect(page.locator('.cm-content')).toContainText(`const`)
|
||||
|
||||
@ -70,29 +49,18 @@ test.describe('Text-to-CAD tests', () => {
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
await u.closeDebugPanel()
|
||||
|
||||
// Find the toast close button.
|
||||
const closeButton = page
|
||||
.getByRole('status')
|
||||
.locator('div')
|
||||
.filter({ hasText: 'Text-to-CAD successfulPrompt' })
|
||||
.first()
|
||||
.getByRole('button', { name: 'Close' })
|
||||
await expect(closeButton).toBeVisible()
|
||||
await closeButton.click()
|
||||
|
||||
// The toast should disappear.
|
||||
await expect(successToastMessage).not.toBeVisible()
|
||||
})
|
||||
|
||||
test('success model, then ignore success toast, user can create new prompt from command bar', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await sendPromptFromCommandBar(page, 'a 2x6 lego')
|
||||
|
||||
@ -111,10 +79,6 @@ test.describe('Text-to-CAD tests', () => {
|
||||
const successToastMessage = page.getByText(`Text-to-CAD successful`)
|
||||
await expect(successToastMessage).toBeVisible({ timeout: 15000 })
|
||||
|
||||
await expect(page.getByText('Copied')).not.toBeVisible()
|
||||
|
||||
await expect(successToastMessage).toBeVisible()
|
||||
|
||||
// Can send a new prompt from the command bar.
|
||||
await sendPromptFromCommandBar(page, 'a 2x4 lego')
|
||||
|
||||
@ -133,12 +97,14 @@ test.describe('Text-to-CAD tests', () => {
|
||||
|
||||
test('you can reject text-to-cad output and it does nothing', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await sendPromptFromCommandBar(page, 'a 2x4 lego')
|
||||
|
||||
@ -170,12 +136,16 @@ test.describe('Text-to-CAD tests', () => {
|
||||
await expect(page.locator('.cm-content')).toContainText(``)
|
||||
})
|
||||
|
||||
test('sending a bad prompt fails, can dismiss', async ({ page }) => {
|
||||
test('sending a bad prompt fails, can dismiss', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
const commandBarButton = page.getByRole('button', { name: 'Commands' })
|
||||
await expect(commandBarButton).toBeVisible()
|
||||
@ -236,12 +206,14 @@ test.describe('Text-to-CAD tests', () => {
|
||||
|
||||
test('sending a bad prompt fails, can start over from toast', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
const commandBarButton = page.getByRole('button', { name: 'Commands' })
|
||||
await expect(commandBarButton).toBeVisible()
|
||||
@ -324,12 +296,14 @@ test.describe('Text-to-CAD tests', () => {
|
||||
|
||||
test('sending a bad prompt fails, can ignore toast, can start over from command bar', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
const commandBarButton = page.getByRole('button', { name: 'Commands' })
|
||||
await expect(commandBarButton).toBeVisible()
|
||||
@ -391,19 +365,21 @@ test.describe('Text-to-CAD tests', () => {
|
||||
|
||||
await expect(successToastMessage).toBeVisible({ timeout: 15000 })
|
||||
|
||||
await expect(page.getByText('Copied')).not.toBeVisible()
|
||||
|
||||
// old failure toast should stick around.
|
||||
await expect(failureToastMessage).toBeVisible()
|
||||
await expect(page.getByText(`Text-to-CAD failed`)).toBeVisible()
|
||||
})
|
||||
|
||||
test('ensure you can shift+enter in the prompt box', async ({ page }) => {
|
||||
test('ensure you can shift+enter in the prompt box', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
const promptWithNewline = `a 2x4\nlego`
|
||||
|
||||
@ -456,7 +432,7 @@ test.describe('Text-to-CAD tests', () => {
|
||||
test(
|
||||
'can do many at once and get many prompts back, and interact with many',
|
||||
{ tag: ['@skipWin'] },
|
||||
async ({ page }) => {
|
||||
async ({ page, homePage }) => {
|
||||
// Let this test run longer since we've seen it timeout.
|
||||
test.setTimeout(180_000)
|
||||
// skip on windows
|
||||
@ -467,9 +443,10 @@ test.describe('Text-to-CAD tests', () => {
|
||||
|
||||
const u = await getUtils(page)
|
||||
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await sendPromptFromCommandBar(page, 'a 2x4 lego')
|
||||
|
||||
@ -495,8 +472,6 @@ test.describe('Text-to-CAD tests', () => {
|
||||
// We should have three success toasts.
|
||||
await expect(successToastMessage).toHaveCount(3, { timeout: 25_000 })
|
||||
|
||||
await expect(page.getByText('Copied')).not.toBeVisible()
|
||||
|
||||
await expect(page.getByText(`a 2x4 lego`)).toBeVisible()
|
||||
await expect(page.getByText(`a 2x8 lego`)).toBeVisible()
|
||||
await expect(page.getByText(`a 2x10 lego`)).toBeVisible()
|
||||
@ -514,31 +489,15 @@ test.describe('Text-to-CAD tests', () => {
|
||||
|
||||
// Ensure you can copy the code for one of the models remaining.
|
||||
const copyToClipboardButton = page.getByRole('button', {
|
||||
name: 'Copy to clipboard',
|
||||
name: 'Accept',
|
||||
})
|
||||
await expect(copyToClipboardButton.first()).toBeVisible()
|
||||
// Click the button.
|
||||
await copyToClipboardButton.first().click()
|
||||
|
||||
// Expect the code to be copied.
|
||||
await expect(page.getByText('Copied')).toBeVisible()
|
||||
|
||||
// Click in the code editor.
|
||||
await page.locator('.cm-content').click({ position: { x: 10, y: 10 } })
|
||||
|
||||
// Paste the code.
|
||||
await page.keyboard.down('ControlOrMeta')
|
||||
await page.keyboard.press('KeyV')
|
||||
await page.keyboard.up('ControlOrMeta')
|
||||
|
||||
// Expect the code to be pasted.
|
||||
await expect(page.locator('.cm-content')).toContainText(`2x8`)
|
||||
|
||||
// Find the toast close button.
|
||||
const closeButton = page.locator('[data-negative-button="close"]').first()
|
||||
await expect(closeButton).toBeVisible()
|
||||
await closeButton.click()
|
||||
|
||||
// Ensure the final toast remains.
|
||||
await expect(page.getByText(`a 2x10 lego`)).not.toBeVisible()
|
||||
await expect(page.getByText(`Prompt: "a 2x8 lego`)).not.toBeVisible()
|
||||
@ -549,40 +508,21 @@ test.describe('Text-to-CAD tests', () => {
|
||||
// Click the button.
|
||||
await copyToClipboardButton.click()
|
||||
|
||||
// Expect the code to be copied.
|
||||
await expect(page.getByText('Copied')).toBeVisible()
|
||||
|
||||
// Click in the code editor.
|
||||
await page.locator('.cm-content').click({ position: { x: 10, y: 10 } })
|
||||
|
||||
// Paste the code.
|
||||
await page.keyboard.down('ControlOrMeta')
|
||||
await page.keyboard.press('KeyA')
|
||||
await page.keyboard.up('ControlOrMeta')
|
||||
await page.keyboard.press('Backspace')
|
||||
await page.keyboard.down('ControlOrMeta')
|
||||
await page.keyboard.press('KeyV')
|
||||
await page.keyboard.up('ControlOrMeta')
|
||||
|
||||
// Expect the code to be pasted.
|
||||
await expect(page.locator('.cm-content')).toContainText(`2x4`)
|
||||
|
||||
// Expect the toast to disappear.
|
||||
// Find the toast close button.
|
||||
await expect(closeButton).toBeVisible()
|
||||
await closeButton.click()
|
||||
await expect(successToastMessage).not.toBeVisible()
|
||||
}
|
||||
)
|
||||
|
||||
test('can do many at once with errors, clicking dismiss error does not dismiss all', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await sendPromptFromCommandBar(page, 'a 2x4 lego')
|
||||
|
||||
@ -631,57 +571,37 @@ test.describe('Text-to-CAD tests', () => {
|
||||
|
||||
// Ensure you can copy the code for one of the models remaining.
|
||||
const copyToClipboardButton = page.getByRole('button', {
|
||||
name: 'Copy to clipboard',
|
||||
name: 'Accept',
|
||||
})
|
||||
await expect(copyToClipboardButton.first()).toBeVisible()
|
||||
// Click the button.
|
||||
await copyToClipboardButton.first().click()
|
||||
|
||||
// Expect the code to be copied.
|
||||
await expect(page.getByText('Copied')).toBeVisible()
|
||||
|
||||
// Click in the code editor.
|
||||
await page.locator('.cm-content').click({ position: { x: 10, y: 10 } })
|
||||
|
||||
// Paste the code.
|
||||
await page.keyboard.down('ControlOrMeta')
|
||||
await page.keyboard.press('KeyV')
|
||||
await page.keyboard.up('ControlOrMeta')
|
||||
|
||||
// Expect the code to be pasted.
|
||||
await expect(page.locator('.cm-content')).toContainText(`2x4`)
|
||||
|
||||
// Find the toast close button.
|
||||
const closeButton = page
|
||||
.getByRole('status')
|
||||
.locator('div')
|
||||
.filter({ hasText: 'Text-to-CAD successfulPrompt' })
|
||||
.first()
|
||||
.getByRole('button', { name: 'Close' })
|
||||
await expect(closeButton).toBeVisible()
|
||||
await closeButton.click()
|
||||
|
||||
// Expect the toast to disappear.
|
||||
await expect(page.getByText('Copied')).not.toBeVisible()
|
||||
await expect(successToastMessage).not.toBeVisible()
|
||||
})
|
||||
})
|
||||
|
||||
async function sendPromptFromCommandBar(page: Page, promptStr: string) {
|
||||
await page.waitForTimeout(1000)
|
||||
await test.step(`Send prompt from command bar: ${promptStr}`, async () => {
|
||||
const commandBarButton = page.getByRole('button', { name: 'Commands' })
|
||||
await expect(commandBarButton).toBeVisible()
|
||||
// Click the command bar button
|
||||
await commandBarButton.hover()
|
||||
await commandBarButton.click()
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
// Wait for the command bar to appear
|
||||
const cmdSearchBar = page.getByPlaceholder('Search commands')
|
||||
await expect(cmdSearchBar).toBeVisible()
|
||||
|
||||
const textToCadCommand = page.getByText('Use the Zoo Text-to-CAD API ')
|
||||
const textToCadCommand = page.getByText('Use the Zoo Text-to-CAD API')
|
||||
await expect(textToCadCommand.first()).toBeVisible()
|
||||
// Click the Text-to-CAD command
|
||||
await textToCadCommand.first().scrollIntoViewIfNeeded()
|
||||
await textToCadCommand.first().click()
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
// Enter the prompt.
|
||||
const prompt = page.getByText('Prompt')
|
||||
@ -697,12 +617,13 @@ async function sendPromptFromCommandBar(page: Page, promptStr: string) {
|
||||
test(
|
||||
'Text-to-CAD functionality',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
async ({ context, page }, testInfo) => {
|
||||
const projectName = 'project-000'
|
||||
const prompt = 'lego 2x4'
|
||||
const textToCadFileName = 'lego-2x4.kcl'
|
||||
|
||||
const { electronApp, page, dir } = await setupElectron({ testInfo })
|
||||
const { dir } = await context.folderSetupFn(async () => {})
|
||||
|
||||
const fileExists = () =>
|
||||
fs.existsSync(join(dir, projectName, textToCadFileName))
|
||||
|
||||
@ -711,7 +632,7 @@ test(
|
||||
test
|
||||
)
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
// Locators
|
||||
const projectMenuButton = page
|
||||
@ -761,7 +682,5 @@ test(
|
||||
// Confirm we've navigated back to the main.kcl file after deletion
|
||||
await expect(projectMenuButton).toContainText('main.kcl')
|
||||
})
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
@ -1,19 +1,10 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { test, expect } from './zoo-test'
|
||||
|
||||
import { doExport, getUtils, makeTemplate, setup, tearDown } from './test-utils'
|
||||
import { doExport, getUtils, makeTemplate } from './test-utils'
|
||||
|
||||
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||
await setup(context, page, testInfo)
|
||||
})
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
|
||||
test('Units menu', async ({ page }) => {
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
test.fixme('Units menu', async ({ page, homePage }) => {
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
const unitsMenuButton = page.getByRole('button', {
|
||||
name: 'Current Units',
|
||||
@ -41,7 +32,7 @@ test('Units menu', async ({ page }) => {
|
||||
await expect(unitsMenuButton).toContainText('mm')
|
||||
})
|
||||
|
||||
test('Successful export shows a success toast', async ({ page }) => {
|
||||
test('Successful export shows a success toast', async ({ page, homePage }) => {
|
||||
// FYI this test doesn't work with only engine running locally
|
||||
// And you will need to have the KittyCAD CLI installed
|
||||
const u = await getUtils(page)
|
||||
@ -57,41 +48,41 @@ totalHeightHalf = 2
|
||||
armThick = 0.5
|
||||
totalLen = 9.5
|
||||
part001 = startSketchOn('-XZ')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> yLine(baseHeight, %)
|
||||
|> xLine(baseLen, %)
|
||||
|> angledLineToY({
|
||||
angle: topAng,
|
||||
to: totalHeightHalf,
|
||||
}, %, $seg04)
|
||||
|> xLineTo(totalLen, %, $seg03)
|
||||
|> yLine(-armThick, %, $seg01)
|
||||
|> angledLineThatIntersects({
|
||||
angle: HALF_TURN,
|
||||
offset: -armThick,
|
||||
intersectTag: seg04
|
||||
}, %)
|
||||
|> angledLineToY([segAng(seg04) + 180, ZERO], %)
|
||||
|> angledLineToY({
|
||||
angle: -bottomAng,
|
||||
to: -totalHeightHalf - armThick,
|
||||
}, %, $seg02)
|
||||
|> xLineTo(segEndX(seg03) + 0, %)
|
||||
|> yLine(-segLen(seg01), %)
|
||||
|> angledLineThatIntersects({
|
||||
angle: HALF_TURN,
|
||||
offset: -armThick,
|
||||
intersectTag: seg02
|
||||
}, %)
|
||||
|> angledLineToY([segAng(seg02) + 180, -baseHeight], %)
|
||||
|> xLineTo(ZERO, %)
|
||||
|> close(%)
|
||||
|> extrude(4, %)`
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> yLine(baseHeight, %)
|
||||
|> xLine(baseLen, %)
|
||||
|> angledLineToY({
|
||||
angle = topAng,
|
||||
to = totalHeightHalf,
|
||||
}, %, $seg04)
|
||||
|> xLineTo(totalLen, %, $seg03)
|
||||
|> yLine(-armThick, %, $seg01)
|
||||
|> angledLineThatIntersects({
|
||||
angle = HALF_TURN,
|
||||
offset = -armThick,
|
||||
intersectTag = seg04
|
||||
}, %)
|
||||
|> angledLineToY([segAng(seg04) + 180, ZERO], %)
|
||||
|> angledLineToY({
|
||||
angle = -bottomAng,
|
||||
to = -totalHeightHalf - armThick,
|
||||
}, %, $seg02)
|
||||
|> xLineTo(segEndX(seg03) + 0, %)
|
||||
|> yLine(-segLen(seg01), %)
|
||||
|> angledLineThatIntersects({
|
||||
angle = HALF_TURN,
|
||||
offset = -armThick,
|
||||
intersectTag = seg02
|
||||
}, %)
|
||||
|> angledLineToY([segAng(seg02) + 180, -baseHeight], %)
|
||||
|> xLineTo(ZERO, %)
|
||||
|> close(%)
|
||||
|> extrude(4, %)`
|
||||
)
|
||||
})
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
await u.waitForCmdReceive('extrude')
|
||||
@ -106,25 +97,14 @@ part001 = startSketchOn('-XZ')
|
||||
},
|
||||
page
|
||||
)
|
||||
|
||||
// This is the main thing we're testing,
|
||||
// We test the export functionality across all
|
||||
// file types in snapshot-tests.spec.ts
|
||||
await expect(page.getByText('Exported successfully')).toBeVisible()
|
||||
})
|
||||
|
||||
test('Paste should not work unless an input is focused', async ({
|
||||
page,
|
||||
browserName,
|
||||
homePage,
|
||||
}) => {
|
||||
// To run this test locally, uncomment Firefox in playwright.config.ts
|
||||
test.skip(
|
||||
browserName !== 'firefox',
|
||||
"This bug is really Firefox-only, which we don't run in CI."
|
||||
)
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
await page
|
||||
.getByRole('button', { name: 'Start Sketch' })
|
||||
.waitFor({ state: 'visible' })
|
||||
@ -164,12 +144,12 @@ test('Paste should not work unless an input is focused', async ({
|
||||
|
||||
test('Keyboard shortcuts can be viewed through the help menu', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await page.waitForURL('**/file/**', { waitUntil: 'domcontentloaded' })
|
||||
await page.waitForURL('file:///**', { waitUntil: 'domcontentloaded' })
|
||||
await page
|
||||
.getByRole('button', { name: 'Start Sketch' })
|
||||
.waitFor({ state: 'visible' })
|
||||
@ -181,7 +161,7 @@ test('Keyboard shortcuts can be viewed through the help menu', async ({
|
||||
await page.getByRole('button', { name: 'Keyboard Shortcuts' }).click()
|
||||
|
||||
// Verify the URL and that you can see a list of shortcuts
|
||||
await expect(page.url()).toContain('?tab=keybindings')
|
||||
await expect.poll(() => page.url()).toContain('?tab=keybindings')
|
||||
await expect(
|
||||
page.getByRole('heading', { name: 'Enter Sketch Mode' })
|
||||
).toBeAttached()
|
||||
@ -189,12 +169,13 @@ test('Keyboard shortcuts can be viewed through the help menu', async ({
|
||||
|
||||
test('First escape in tool pops you out of tool, second exits sketch mode', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
// Wait for the app to be ready for use
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
await u.closeDebugPanel()
|
||||
@ -258,7 +239,7 @@ test('First escape in tool pops you out of tool, second exits sketch mode', asyn
|
||||
|
||||
test.fixme(
|
||||
'Basic default modeling and sketch hotkeys work',
|
||||
async ({ page }) => {
|
||||
async ({ page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
// This test can run long if it takes a little too long to load
|
||||
@ -285,8 +266,8 @@ test.fixme(
|
||||
})
|
||||
)
|
||||
})
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
await u.closeDebugPanel()
|
||||
@ -437,10 +418,11 @@ test.fixme(
|
||||
}
|
||||
)
|
||||
|
||||
test('Delete key does not navigate back', async ({ page }) => {
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.goto('/')
|
||||
await page.waitForURL('**/file/**', { waitUntil: 'domcontentloaded' })
|
||||
test('Delete key does not navigate back', async ({ page, homePage }) => {
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await page.waitForURL('file:///**', { waitUntil: 'domcontentloaded' })
|
||||
|
||||
const settingsButton = page.getByRole('link', {
|
||||
name: 'Settings',
|
||||
@ -449,45 +431,45 @@ test('Delete key does not navigate back', async ({ page }) => {
|
||||
const settingsCloseButton = page.getByTestId('settings-close-button')
|
||||
|
||||
await settingsButton.click()
|
||||
await expect(page.url()).toContain('/settings')
|
||||
await expect.poll(() => page.url()).toContain('/settings')
|
||||
|
||||
// Make sure that delete doesn't go back from settings
|
||||
await page.keyboard.press('Delete')
|
||||
await expect(page.url()).toContain('/settings')
|
||||
await expect.poll(() => page.url()).toContain('/settings')
|
||||
|
||||
// Now close the settings and try delete again,
|
||||
// make sure it doesn't go back to settings
|
||||
await settingsCloseButton.click()
|
||||
await page.keyboard.press('Delete')
|
||||
await expect(page.url()).not.toContain('/settings')
|
||||
await expect.poll(() => page.url()).not.toContain('/settings')
|
||||
})
|
||||
|
||||
test('Sketch on face', async ({ page }) => {
|
||||
test('Sketch on face', async ({ page, homePage }) => {
|
||||
test.setTimeout(90_000)
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([3.29, 7.86], %)
|
||||
|> line([2.48, 2.44], %)
|
||||
|> line([2.66, 1.17], %)
|
||||
|> line([3.75, 0.46], %)
|
||||
|> line([4.99, -0.46], %)
|
||||
|> line([3.3, -2.12], %)
|
||||
|> line([2.16, -3.33], %)
|
||||
|> line([0.85, -3.08], %)
|
||||
|> line([-0.18, -3.36], %)
|
||||
|> line([-3.86, -2.73], %)
|
||||
|> line([-17.67, 0.85], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(5 + 7, sketch001)`
|
||||
|> startProfileAt([3.29, 7.86], %)
|
||||
|> line([2.48, 2.44], %)
|
||||
|> line([2.66, 1.17], %)
|
||||
|> line([3.75, 0.46], %)
|
||||
|> line([4.99, -0.46], %)
|
||||
|> line([3.3, -2.12], %)
|
||||
|> line([2.16, -3.33], %)
|
||||
|> line([0.85, -3.08], %)
|
||||
|> line([-0.18, -3.36], %)
|
||||
|> line([-3.86, -2.73], %)
|
||||
|> line([-17.67, 0.85], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(5 + 7, sketch001)`
|
||||
)
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// wait for execution done
|
||||
await u.openDebugPanel()
|
||||
@ -541,7 +523,8 @@ test('Sketch on face', async ({ page }) => {
|
||||
|> line([2.45, -0.2], %)
|
||||
|> line([-2.6, -1.25], %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)`)
|
||||
|> close(%)
|
||||
`)
|
||||
)
|
||||
|
||||
await u.openAndClearDebugPanel()
|
||||
@ -556,7 +539,7 @@ test('Sketch on face', async ({ page }) => {
|
||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||
await page.waitForTimeout(400)
|
||||
await page.waitForTimeout(150)
|
||||
await page.setViewportSize({ width: 1200, height: 1200 })
|
||||
await page.setBodyDimensions({ width: 1200, height: 1200 })
|
||||
await u.openAndClearDebugPanel()
|
||||
await u.updateCamPosition([452, -152, 1166])
|
||||
await u.closeDebugPanel()
|
||||
@ -574,11 +557,11 @@ test('Sketch on face', async ({ page }) => {
|
||||
previousCodeContent = await page.locator('.cm-content').innerText()
|
||||
|
||||
const result = makeTemplate`sketch002 = startSketchOn(extrude001, seg01)
|
||||
|> startProfileAt([-12.83, 6.7], %)
|
||||
|> line([${[2.28, 2.35]}, -${0.07}], %)
|
||||
|> line([-3.05, -1.47], %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)`
|
||||
|> startProfileAt([-12.83, 6.7], %)
|
||||
|> line([${[2.28, 2.35]}, -${0.07}], %)
|
||||
|> line([-3.05, -1.47], %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)`
|
||||
|
||||
await expect(page.locator('.cm-content')).toHaveText(result.regExp)
|
||||
|
||||
@ -602,6 +585,6 @@ test('Sketch on face', async ({ page }) => {
|
||||
await page.getByRole('button', { name: 'checkmark Submit command' }).click()
|
||||
|
||||
const result2 = result.genNext`
|
||||
const sketch002 = extrude(${[5, 5]} + 7, sketch002)`
|
||||
const sketch002 = extrude(${[5, 5]} + 7, sketch002)`
|
||||
await expect(page.locator('.cm-content')).toHaveText(result2.regExp)
|
||||
})
|
||||
|
334
e2e/playwright/zoo-test.ts
Normal file
@ -0,0 +1,334 @@
|
||||
import {
|
||||
test as playwrightTestFn,
|
||||
TestInfo as TestInfoPlaywright,
|
||||
BrowserContext as BrowserContextPlaywright,
|
||||
Page as PagePlaywright,
|
||||
TestDetails as TestDetailsPlaywright,
|
||||
PlaywrightTestArgs,
|
||||
PlaywrightTestOptions,
|
||||
PlaywrightWorkerArgs,
|
||||
PlaywrightWorkerOptions,
|
||||
ElectronApplication,
|
||||
} from '@playwright/test'
|
||||
|
||||
import {
|
||||
fixtures,
|
||||
Fixtures,
|
||||
AuthenticatedTronApp,
|
||||
AuthenticatedApp,
|
||||
} from './fixtures/fixtureSetup'
|
||||
|
||||
import { SaveSettingsPayload } from 'lib/settings/settingsTypes'
|
||||
export { expect } from '@playwright/test'
|
||||
|
||||
declare module '@playwright/test' {
|
||||
interface TestInfo {
|
||||
tronApp?: AuthenticatedTronApp
|
||||
}
|
||||
interface BrowserContext {
|
||||
folderSetupFn: (
|
||||
cb: (dir: string) => Promise<void>
|
||||
) => Promise<{ dir: string }>
|
||||
}
|
||||
interface Page {
|
||||
dir: string
|
||||
TEST_SETTINGS_FILE_KEY?: string
|
||||
setBodyDimensions: (dims: {
|
||||
width: number
|
||||
height: number
|
||||
}) => Promise<void>
|
||||
}
|
||||
}
|
||||
|
||||
export type TestInfo = TestInfoPlaywright
|
||||
export type BrowserContext = BrowserContextPlaywright
|
||||
export type Page = PagePlaywright
|
||||
export type TestDetails = TestDetailsPlaywright & {
|
||||
cleanProjectDir?: boolean
|
||||
appSettings?: Partial<SaveSettingsPayload>
|
||||
}
|
||||
|
||||
// Our custom decorated Zoo test object. Makes it easier to add fixtures, and
|
||||
// switch between web and electron if needed.
|
||||
const pwTestFnWithFixtures = playwrightTestFn.extend<Fixtures>(fixtures)
|
||||
|
||||
// In JavaScript you cannot replace a function's body only (despite functions
|
||||
// are themselves objects, which you'd expect a body property or something...)
|
||||
// So we must redefine the function and then re-attach properties.
|
||||
type PWFunction = (
|
||||
args: PlaywrightTestArgs &
|
||||
Fixtures &
|
||||
PlaywrightWorkerArgs &
|
||||
PlaywrightTestOptions &
|
||||
PlaywrightWorkerOptions & {
|
||||
electronApp?: ElectronApplication
|
||||
},
|
||||
testInfo: TestInfo
|
||||
) => void | Promise<void>
|
||||
|
||||
let firstUrl = ''
|
||||
|
||||
// The below error is due to the extreme type spaghetti going on. playwright/
|
||||
// types/test.d.ts does not export 2 functions (below is one of them) but tsc
|
||||
// is trying to use a interface name it can't see.
|
||||
// e2e/playwright/zoo-test.ts:64:14 - error TS4023: Exported variable 'test' has
|
||||
// or is using name 'TestFunction' from external module
|
||||
// "/home/lee/Code/Zoo/modeling-app/dirty2/node_modules/playwright/types/test"
|
||||
// but cannot be named.
|
||||
export const test = (
|
||||
desc: string,
|
||||
objOrFn: PWFunction | TestDetails,
|
||||
fnMaybe?: PWFunction
|
||||
) => {
|
||||
const hasTestConf = typeof objOrFn === 'object'
|
||||
const fn = hasTestConf ? fnMaybe : objOrFn
|
||||
|
||||
return pwTestFnWithFixtures(
|
||||
desc,
|
||||
hasTestConf ? objOrFn : {},
|
||||
async (
|
||||
{
|
||||
page,
|
||||
context,
|
||||
cmdBar,
|
||||
editor,
|
||||
toolbar,
|
||||
scene,
|
||||
homePage,
|
||||
request,
|
||||
playwright,
|
||||
browser,
|
||||
acceptDownloads,
|
||||
bypassCSP,
|
||||
colorScheme,
|
||||
clientCertificates,
|
||||
deviceScaleFactor,
|
||||
extraHTTPHeaders,
|
||||
geolocation,
|
||||
hasTouch,
|
||||
httpCredentials,
|
||||
ignoreHTTPSErrors,
|
||||
isMobile,
|
||||
javaScriptEnabled,
|
||||
locale,
|
||||
offline,
|
||||
permissions,
|
||||
proxy,
|
||||
storageState,
|
||||
timezoneId,
|
||||
userAgent,
|
||||
viewport,
|
||||
baseURL,
|
||||
contextOptions,
|
||||
actionTimeout,
|
||||
navigationTimeout,
|
||||
serviceWorkers,
|
||||
testIdAttribute,
|
||||
browserName,
|
||||
defaultBrowserType,
|
||||
headless,
|
||||
channel,
|
||||
launchOptions,
|
||||
connectOptions,
|
||||
screenshot,
|
||||
trace,
|
||||
video,
|
||||
},
|
||||
testInfo
|
||||
) => {
|
||||
// To switch to web, use PLATFORM=web environment variable.
|
||||
// Only use this for debugging, since the playwright tracer is busted
|
||||
// for electron.
|
||||
|
||||
let tronApp
|
||||
|
||||
if (process.env.PLATFORM === 'web') {
|
||||
tronApp = new AuthenticatedApp(context, page, testInfo)
|
||||
} else {
|
||||
tronApp = new AuthenticatedTronApp(context, page, testInfo)
|
||||
}
|
||||
|
||||
const fixtures: Fixtures = { cmdBar, editor, toolbar, scene, homePage }
|
||||
if (tronApp instanceof AuthenticatedTronApp) {
|
||||
const options = {
|
||||
fixtures,
|
||||
}
|
||||
if (hasTestConf) {
|
||||
Object.assign(options, {
|
||||
appSettings: objOrFn?.appSettings,
|
||||
cleanProjectDir: objOrFn?.cleanProjectDir,
|
||||
})
|
||||
}
|
||||
await tronApp.initialise(options)
|
||||
} else {
|
||||
await tronApp.initialise('')
|
||||
}
|
||||
|
||||
// We need to patch this because addInitScript will bind too late in our
|
||||
// electron tests, never running. We need to call reload() after each call
|
||||
// to guarantee it runs.
|
||||
const oldContextAddInitScript = tronApp.context.addInitScript
|
||||
tronApp.context.addInitScript = async function (a, b) {
|
||||
// @ts-ignore pretty sure way out of tsc's type checking capabilities.
|
||||
// This code works perfectly fine.
|
||||
await oldContextAddInitScript.apply(this, [a, b])
|
||||
await tronApp.page.reload()
|
||||
}
|
||||
|
||||
// No idea why we mix and match page and context's addInitScript but we do
|
||||
const oldPageAddInitScript = tronApp.page.addInitScript
|
||||
tronApp.page.addInitScript = async function (a: any, b: any) {
|
||||
// @ts-ignore pretty sure way out of tsc's type checking capabilities.
|
||||
// This code works perfectly fine.
|
||||
await oldPageAddInitScript.apply(this, [a, b])
|
||||
await tronApp.page.reload()
|
||||
}
|
||||
|
||||
// Create a consistent way to resize the page across electron and web.
|
||||
// (lee) I had to do everything in the book to make electron change its
|
||||
// damn window size. I succeeded in making it consistently and reliably
|
||||
// do it after a whole afternoon.
|
||||
tronApp.page.setBodyDimensions = async function (dims: {
|
||||
width: number
|
||||
height: number
|
||||
}) {
|
||||
await tronApp.page.setViewportSize(dims)
|
||||
|
||||
if (!(tronApp instanceof AuthenticatedTronApp)) {
|
||||
return
|
||||
}
|
||||
|
||||
await tronApp.electronApp?.evaluateHandle(async ({ app }, dims) => {
|
||||
// @ts-ignore sorry jon but see comment in main.ts why this is ignored
|
||||
await app.resizeWindow(dims.width, dims.height)
|
||||
}, dims)
|
||||
|
||||
return tronApp.page.evaluate(
|
||||
async (dims: { width: number; height: number }) => {
|
||||
await window.electron.resizeWindow(dims.width, dims.height)
|
||||
window.document.body.style.width = dims.width + 'px'
|
||||
window.document.body.style.height = dims.height + 'px'
|
||||
window.document.documentElement.style.width = dims.width + 'px'
|
||||
window.document.documentElement.style.height = dims.height + 'px'
|
||||
},
|
||||
dims
|
||||
)
|
||||
}
|
||||
|
||||
await tronApp.page.setBodyDimensions(tronApp.viewPortSize)
|
||||
|
||||
// We need to expose this in order for some tests that require folder
|
||||
// creation. Before they used to do this by their own electronSetup({...})
|
||||
// calls.
|
||||
if (tronApp instanceof AuthenticatedTronApp) {
|
||||
tronApp.context.folderSetupFn = async function (fn) {
|
||||
return fn(tronApp.dir)
|
||||
.then(() => tronApp.page.reload())
|
||||
.then(() => ({
|
||||
dir: tronApp.dir,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
if (!firstUrl) {
|
||||
await tronApp.page.getByText('Your Projects').count()
|
||||
firstUrl = tronApp.page.url()
|
||||
}
|
||||
|
||||
// Due to the app controlling its own window context we need to inject new
|
||||
// options and context here.
|
||||
// NOTE TO LEE: Seems to destroy page context when calling an electron loadURL.
|
||||
// await tronApp.electronApp.evaluate(({ app }) => {
|
||||
// return app.reuseWindowForTest();
|
||||
// });
|
||||
|
||||
await tronApp.electronApp?.evaluate(({ app }, projectDirName) => {
|
||||
// @ts-ignore can't declaration merge see main.ts
|
||||
app.testProperty['TEST_SETTINGS_FILE_KEY'] = projectDirName
|
||||
}, tronApp.dir)
|
||||
|
||||
// Always start at the root view
|
||||
await tronApp.page.goto(firstUrl)
|
||||
|
||||
// Force a hard reload, destroying the stream and other state
|
||||
await tronApp.page.reload()
|
||||
|
||||
// tsc aint smart enough to know this'll never be undefined
|
||||
// but I dont blame it, the logic to know is complex
|
||||
if (fn) {
|
||||
await fn(
|
||||
{
|
||||
context: tronApp.context,
|
||||
page: tronApp.page,
|
||||
electronApp:
|
||||
tronApp instanceof AuthenticatedTronApp
|
||||
? tronApp.electronApp
|
||||
: undefined,
|
||||
...fixtures,
|
||||
request,
|
||||
playwright,
|
||||
browser,
|
||||
acceptDownloads,
|
||||
bypassCSP,
|
||||
colorScheme,
|
||||
clientCertificates,
|
||||
deviceScaleFactor,
|
||||
extraHTTPHeaders,
|
||||
geolocation,
|
||||
hasTouch,
|
||||
httpCredentials,
|
||||
ignoreHTTPSErrors,
|
||||
isMobile,
|
||||
javaScriptEnabled,
|
||||
locale,
|
||||
offline,
|
||||
permissions,
|
||||
proxy,
|
||||
storageState,
|
||||
timezoneId,
|
||||
userAgent,
|
||||
viewport,
|
||||
baseURL,
|
||||
contextOptions,
|
||||
actionTimeout,
|
||||
navigationTimeout,
|
||||
serviceWorkers,
|
||||
testIdAttribute,
|
||||
browserName,
|
||||
defaultBrowserType,
|
||||
headless,
|
||||
channel,
|
||||
launchOptions,
|
||||
connectOptions,
|
||||
screenshot,
|
||||
trace,
|
||||
video,
|
||||
},
|
||||
testInfo
|
||||
)
|
||||
}
|
||||
|
||||
testInfo.tronApp =
|
||||
tronApp instanceof AuthenticatedTronApp ? tronApp : undefined
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
type ZooTest = typeof test
|
||||
|
||||
test.describe = pwTestFnWithFixtures.describe
|
||||
test.beforeEach = pwTestFnWithFixtures.beforeEach
|
||||
test.afterEach = pwTestFnWithFixtures.afterEach
|
||||
test.step = pwTestFnWithFixtures.step
|
||||
test.skip = pwTestFnWithFixtures.skip
|
||||
test.setTimeout = pwTestFnWithFixtures.setTimeout
|
||||
test.fixme = pwTestFnWithFixtures.fixme as unknown as ZooTest
|
||||
test.only = pwTestFnWithFixtures.only
|
||||
test.fail = pwTestFnWithFixtures.fail
|
||||
test.slow = pwTestFnWithFixtures.slow
|
||||
test.beforeAll = pwTestFnWithFixtures.beforeAll
|
||||
test.afterAll = pwTestFnWithFixtures.afterAll
|
||||
test.use = pwTestFnWithFixtures.use
|
||||
test.expect = pwTestFnWithFixtures.expect
|
||||
test.extend = pwTestFnWithFixtures.extend
|
||||
test.info = pwTestFnWithFixtures.info
|
2
interface.d.ts
vendored
@ -7,6 +7,7 @@ import { MachinesListing } from 'components/MachineManagerProvider'
|
||||
type EnvFn = (value?: string) => string
|
||||
|
||||
export interface IElectronAPI {
|
||||
resizeWindow: (width: number, height: number) => Promise<void>
|
||||
open: typeof dialog.showOpenDialog
|
||||
save: typeof dialog.showSaveDialog
|
||||
openExternal: typeof shell.openExternal
|
||||
@ -79,6 +80,7 @@ export interface IElectronAPI {
|
||||
onUpdateError: (callback: (value: { error: Error }) => void) => Electron
|
||||
appRestart: () => void
|
||||
getArgvParsed: () => any
|
||||
getAppTestProperty: (propertyName: string) => any
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
24
package.json
@ -103,24 +103,22 @@
|
||||
"generate:machine-api": "npx openapi-typescript ./openapi/machine-api.json -o src/lib/machine-api.d.ts",
|
||||
"tron:start": "electron-forge start",
|
||||
"tron:package": "electron-forge package",
|
||||
"tron:test": "NODE_ENV=development yarn playwright test --config=playwright.electron.config.ts --grep=@electron",
|
||||
"chrome:test": "PLATFORM=web NODE_ENV=development yarn playwright test --config=playwright.config.ts --project='Google Chrome' --grep-invert='@snapshot'",
|
||||
"tron:test": "NODE_ENV=development yarn playwright test --config=playwright.electron.config.ts --grep-invert='@snapshot'",
|
||||
"tronb:vite": "vite build -c vite.main.config.ts && vite build -c vite.preload.config.ts && vite build -c vite.renderer.config.ts",
|
||||
"tronb:package": "electron-builder --config electron-builder.yml",
|
||||
"test-setup": "yarn install && yarn build:wasm",
|
||||
"test": "vitest --mode development",
|
||||
"test:unit": "vitest run --mode development --exclude **/kclSamples.test.ts",
|
||||
"test:unit:kcl-samples": "vitest run --mode development ./src/lang/kclSamples.test.ts",
|
||||
"test:playwright:browser:chrome": "playwright test --project='Google Chrome' --config=playwright.ci.config.ts --grep-invert='@snapshot|@electron'",
|
||||
"test:playwright:browser:chrome:windows": "playwright test --project=\"Google Chrome\" --config=playwright.ci.config.ts --grep-invert=\"@snapshot|@electron|@skipWin\"",
|
||||
"test:playwright:browser:chrome:ubuntu": "playwright test --project='Google Chrome' --config=playwright.ci.config.ts --grep-invert='@snapshot|@electron|@skipLinux'",
|
||||
"test:playwright:electron": "playwright test --config=playwright.electron.config.ts --grep=@electron",
|
||||
"test:playwright:electron:windows": "playwright test --config=playwright.electron.config.ts --grep=@electron --grep-invert=@skipWin",
|
||||
"test:playwright:electron:macos": "playwright test --config=playwright.electron.config.ts --grep=@electron --grep-invert=@skipMacos",
|
||||
"test:playwright:electron:ubuntu": "playwright test --config=playwright.electron.config.ts --grep=@electron --grep-invert=@skipLinux",
|
||||
"test:playwright:electron:local": "yarn tron:package && NODE_ENV=development playwright test --config=playwright.electron.config.ts --grep=@electron",
|
||||
"test:playwright:electron:windows:local": "yarn tron:package && NODE_ENV=development playwright test --config=playwright.electron.config.ts --grep=@electron --grep-invert=@skipWin",
|
||||
"test:playwright:electron:macos:local": "yarn tron:package && NODE_ENV=development playwright test --config=playwright.electron.config.ts --grep=@electron --grep-invert=@skipMacos",
|
||||
"test:playwright:electron:ubuntu:local": "yarn tron:package && NODE_ENV=development playwright test --config=playwright.electron.config.ts --grep=@electron --grep-invert=@skipLinux",
|
||||
"test:playwright:electron": "playwright test --config=playwright.electron.config.ts --grep-invert='@snapshot'",
|
||||
"test:playwright:electron:windows": "playwright test --config=playwright.electron.config.ts --grep-invert=\"@skipWin|@snapshot\"",
|
||||
"test:playwright:electron:macos": "playwright test --config=playwright.electron.config.ts --grep-invert='@skipMacos|@snapshot'",
|
||||
"test:playwright:electron:ubuntu": "playwright test --config=playwright.electron.config.ts --grep-invert='@skipLinux|@snapshot'",
|
||||
"test:playwright:electron:local": "yarn tron:package && NODE_ENV=development playwright test --config=playwright.electron.config.ts --grep-invert='@snapshot'",
|
||||
"test:playwright:electron:windows:local": "yarn tron:package && NODE_ENV=development playwright test --config=playwright.electron.config.ts --grep-invert=\"@skipWin|@snapshot\"",
|
||||
"test:playwright:electron:macos:local": "yarn tron:package && NODE_ENV=development playwright test --config=playwright.electron.config.ts --grep-invert='@skipMacos|@snapshot'",
|
||||
"test:playwright:electron:ubuntu:local": "yarn tron:package && NODE_ENV=development playwright test --config=playwright.electron.config.ts --grep-invert='@skipLinux|@snapshot'",
|
||||
"test:unit:local": "yarn simpleserver:bg && yarn test:unit; kill-port 3000",
|
||||
"test:unit:kcl-samples:local": "yarn simpleserver:bg && yarn test:unit:kcl-samples; kill-port 3000"
|
||||
},
|
||||
@ -152,7 +150,7 @@
|
||||
"@iarna/toml": "^2.2.5",
|
||||
"@lezer/generator": "^1.7.1",
|
||||
"@nabla/vite-plugin-eslint": "^2.0.5",
|
||||
"@playwright/test": "^1.46.1",
|
||||
"@playwright/test": "^1.49.0",
|
||||
"@testing-library/jest-dom": "^5.14.1",
|
||||
"@testing-library/react": "^15.0.2",
|
||||
"@types/d3-force": "^3.0.10",
|
||||
|
@ -1,58 +0,0 @@
|
||||
import { defineConfig, devices } from '@playwright/test'
|
||||
|
||||
/**
|
||||
* See https://playwright.dev/docs/test-configuration.
|
||||
*/
|
||||
export default defineConfig({
|
||||
timeout: 120_000, // override the default 30s timeout
|
||||
testDir: './e2e/playwright',
|
||||
/* Run tests in files in parallel */
|
||||
fullyParallel: true,
|
||||
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||
forbidOnly: true,
|
||||
/* Do not retry */
|
||||
retries: 0,
|
||||
/* Different amount of parallelism on CI and local. */
|
||||
workers: 1,
|
||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||
reporter: [
|
||||
['dot'],
|
||||
['list'],
|
||||
['json', { outputFile: './test-results/report.json' }],
|
||||
['html'],
|
||||
],
|
||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||
use: {
|
||||
/* Base URL to use in actions like `await page.goto('/')`. */
|
||||
baseURL: 'http://localhost:3000',
|
||||
|
||||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||
trace: 'retain-on-failure',
|
||||
actionTimeout: 15_000,
|
||||
screenshot: 'only-on-failure',
|
||||
},
|
||||
|
||||
/* Configure projects for major browsers */
|
||||
projects: [
|
||||
{
|
||||
name: 'Google Chrome',
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
channel: 'chrome',
|
||||
contextOptions: {
|
||||
/* Chromium is the only one with these permission types */
|
||||
permissions: ['clipboard-write', 'clipboard-read'],
|
||||
},
|
||||
}, // or 'chrome-beta'
|
||||
},
|
||||
{
|
||||
name: 'webkit',
|
||||
use: { ...devices['Desktop Safari'] },
|
||||
},
|
||||
],
|
||||
|
||||
webServer: {
|
||||
command: 'yarn start',
|
||||
reuseExistingServer: false,
|
||||
},
|
||||
})
|
@ -13,7 +13,7 @@ export default defineConfig({
|
||||
/* Do not retry */
|
||||
retries: 0,
|
||||
/* Different amount of parallelism on CI and local. */
|
||||
workers: 1,
|
||||
workers: 8,
|
||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||
reporter: [
|
||||
['dot'],
|
||||
|
@ -28,6 +28,7 @@ data = {
|
||||
"content":
|
||||
f'''
|
||||
**{release_version}** is now available! Check out the latest features and improvements here: <https://zoo.dev/modeling-app/download>
|
||||
|
||||
{modified_release_body}
|
||||
''',
|
||||
"username": "Modeling App Release Updates",
|
||||
|
@ -119,6 +119,11 @@
|
||||
"title": "Pipe and Flange Assembly",
|
||||
"description": "A crucial component in various piping systems, designed to facilitate the connection, disconnection, and access to piping for inspection, cleaning, and modifications. This assembly combines pipes (long cylindrical conduits) with flanges (plate-like fittings) to create a secure yet detachable joint."
|
||||
},
|
||||
{
|
||||
"file": "pipe-with-bend.kcl",
|
||||
"title": "Pipe with bend",
|
||||
"description": "A tubular section or hollow cylinder, usually but not necessarily of circular cross-section, used mainly to convey substances that can flow."
|
||||
},
|
||||
{
|
||||
"file": "poopy-shoe.kcl",
|
||||
"title": "Poopy Shoe",
|
||||
|