Compare commits
139 Commits
jtran/disa
...
v0.24.12
Author | SHA1 | Date | |
---|---|---|---|
d916c79874 | |||
4fd5e26abe | |||
8f9bef922f | |||
545e610bbc | |||
55a3e2a4ed | |||
591f17b182 | |||
a7a88bd762 | |||
0916f990cb | |||
75ae4b4a4a | |||
4a490d5900 | |||
4d9cdc6b40 | |||
0d3880233c | |||
8a029605bd | |||
f26adee360 | |||
0f2a01b6c8 | |||
e099c95c5f | |||
f23bc673aa | |||
b60c1e874d | |||
5857684ebc | |||
e8fc6bc037 | |||
5bdd090119 | |||
669cab8737 | |||
f1ea60d6ab | |||
3faec650b1 | |||
b2b62ec163 | |||
5b798c2aa3 | |||
a23bd1f034 | |||
4d00dddfd8 | |||
f055acb6a6 | |||
bf9d88e9a5 | |||
712a3790e8 | |||
f1ab8602a2 | |||
a1f72b1d5a | |||
f9048b8882 | |||
4b0f83d3ac | |||
62c27e0809 | |||
6a09bbc0ef | |||
997f60e1e5 | |||
6f1d7138c0 | |||
3dabab2c74 | |||
13986fcfd7 | |||
d1e21d673e | |||
ca3a88b4df | |||
fb57df2cad | |||
57a91cdb26 | |||
33079b4151 | |||
a75157573b | |||
e29345fbf6 | |||
35c7183809 | |||
b9fe3ed9e0 | |||
5a25b60485 | |||
4b9f71c994 | |||
e86a5622c8 | |||
a503d1ce50 | |||
11a94cc99e | |||
e295f82495 | |||
0b9cf2dc21 | |||
eb58507e93 | |||
ca28a5f549 | |||
6f4bbdb79e | |||
6773dbe7ff | |||
acfc2b47fa | |||
9dc7ff9797 | |||
308a0fb06e | |||
214ae6f512 | |||
8d54fec589 | |||
4bfbecd3e7 | |||
dff3848a00 | |||
f875efab1b | |||
3f082c8222 | |||
e1c45bdb33 | |||
5cb5dbd689 | |||
0a8881bc69 | |||
be9438160e | |||
77b565f781 | |||
c843dfad95 | |||
a3ff0a45eb | |||
4617ad0fed | |||
5fa51a2f92 | |||
4218777afb | |||
8b1b462e29 | |||
2bc99ba39b | |||
ffe0da6dcd | |||
d27afb8c74 | |||
1c778bf373 | |||
5df8a943a9 | |||
ab729dbcdb | |||
84865eaed0 | |||
543e809739 | |||
61b669cf4e | |||
75f1aaa824 | |||
f4848d7dea | |||
a0167f6ba6 | |||
e5a26c42e6 | |||
9c87b124d9 | |||
21389c089d | |||
29f57be8c1 | |||
cd55f07619 | |||
baf7d3dd9d | |||
54a9a50969 | |||
2830c750fa | |||
d3160cd85a | |||
fd1b4c3a32 | |||
b0a41c31ac | |||
5825ba575c | |||
e5bec2140e | |||
7bf6bc3048 | |||
22f9df73ed | |||
834472e0a6 | |||
bcdf6e314f | |||
55e9845ade | |||
d61cf882c1 | |||
874d19cbfd | |||
9dcc955760 | |||
9b594efe53 | |||
7b9f40c4cb | |||
81b79da90f | |||
2ad5a880fa | |||
b57a9ba54c | |||
b32f5c1d4e | |||
b6d4cc7a4e | |||
43a34b191e | |||
19a93e8deb | |||
b8c623e1ec | |||
4006c28479 | |||
8c932fdb8d | |||
a74c715c01 | |||
1ac39d95f2 | |||
41b1ec94fa | |||
525c803888 | |||
2ee1c78aad | |||
dc21034b86 | |||
1684786659 | |||
12505b4398 | |||
115f2fdea2 | |||
0df28abc4b | |||
1e07ea4986 | |||
f34c23d203 | |||
5295f0ae7d |
@ -1,3 +1,3 @@
|
|||||||
[codespell]
|
[codespell]
|
||||||
ignore-words-list: crate,everytime,inout,co-ordinate,ot,nwo,absolutey,atleast,ue
|
ignore-words-list: crate,everytime,inout,co-ordinate,ot,nwo,absolutey,atleast,ue,afterall
|
||||||
skip: **/target,node_modules,build,**/Cargo.lock,./docs/kcl/*.md,./src-tauri/gen/schemas
|
skip: **/target,node_modules,build,**/Cargo.lock,./docs/kcl/*.md,./src-tauri/gen/schemas
|
||||||
|
48
.github/workflows/ci.yml
vendored
@ -20,6 +20,11 @@ concurrency:
|
|||||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
actions: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check-format:
|
check-format:
|
||||||
runs-on: 'ubuntu-latest'
|
runs-on: 'ubuntu-latest'
|
||||||
@ -84,8 +89,43 @@ jobs:
|
|||||||
- run: yarn build:wasm
|
- run: yarn build:wasm
|
||||||
|
|
||||||
- run: yarn simpleserver:ci
|
- run: yarn simpleserver:ci
|
||||||
|
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
|
||||||
|
|
||||||
|
- name: Install Chromium Browser
|
||||||
|
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
|
||||||
|
run: yarn playwright install chromium --with-deps
|
||||||
|
|
||||||
|
- name: run unit tests
|
||||||
|
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
|
||||||
|
run: yarn test:nowatch
|
||||||
|
env:
|
||||||
|
VITE_KC_DEV_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||||
|
|
||||||
|
- name: check for changes
|
||||||
|
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
|
||||||
|
id: git-check
|
||||||
|
run: |
|
||||||
|
git add src/lang/std/artifactMapGraphs
|
||||||
|
if git status src/lang/std/artifactMapGraphs | grep -q "Changes to be committed"
|
||||||
|
then echo "modified=true" >> $GITHUB_OUTPUT
|
||||||
|
else echo "modified=false" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
- name: Commit changes, if any
|
||||||
|
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' && steps.git-check.outputs.modified == 'true' }}
|
||||||
|
run: |
|
||||||
|
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
git config --local user.name "github-actions[bot]"
|
||||||
|
git remote set-url origin https://${{ github.actor }}:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git
|
||||||
|
git fetch origin
|
||||||
|
echo ${{ github.head_ref }}
|
||||||
|
git checkout ${{ github.head_ref }}
|
||||||
|
# TODO when webkit works on ubuntu remove the os part of the commit message
|
||||||
|
git commit -am "Look at this (photo)Graph *in the voice of Nickelback*" || true
|
||||||
|
git push
|
||||||
|
git push origin ${{ github.head_ref }}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
- run: yarn test:nowatch
|
|
||||||
|
|
||||||
|
|
||||||
prepare-json-files:
|
prepare-json-files:
|
||||||
@ -495,7 +535,7 @@ jobs:
|
|||||||
project_id: kittycadapi
|
project_id: kittycadapi
|
||||||
|
|
||||||
- name: Upload release files to public bucket
|
- name: Upload release files to public bucket
|
||||||
uses: google-github-actions/upload-cloud-storage@v2.1.0
|
uses: google-github-actions/upload-cloud-storage@v2.1.1
|
||||||
with:
|
with:
|
||||||
path: artifact
|
path: artifact
|
||||||
glob: '*/Zoo*'
|
glob: '*/Zoo*'
|
||||||
@ -503,13 +543,13 @@ jobs:
|
|||||||
destination: ${{ env.BUCKET_DIR }}/${{ env.VERSION }}
|
destination: ${{ env.BUCKET_DIR }}/${{ env.VERSION }}
|
||||||
|
|
||||||
- name: Upload update endpoint to public bucket
|
- name: Upload update endpoint to public bucket
|
||||||
uses: google-github-actions/upload-cloud-storage@v2.1.0
|
uses: google-github-actions/upload-cloud-storage@v2.1.1
|
||||||
with:
|
with:
|
||||||
path: last_update.json
|
path: last_update.json
|
||||||
destination: ${{ env.BUCKET_DIR }}
|
destination: ${{ env.BUCKET_DIR }}
|
||||||
|
|
||||||
- name: Upload download endpoint to public bucket
|
- name: Upload download endpoint to public bucket
|
||||||
uses: google-github-actions/upload-cloud-storage@v2.1.0
|
uses: google-github-actions/upload-cloud-storage@v2.1.1
|
||||||
with:
|
with:
|
||||||
path: last_download.json
|
path: last_download.json
|
||||||
destination: ${{ env.BUCKET_DIR }}
|
destination: ${{ env.BUCKET_DIR }}
|
||||||
|
49
.github/workflows/generate-machine-api-types.yml
vendored
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
name: generate machine-api types
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- 'openapi/machine-api.json'
|
||||||
|
- '.github/workflows/generate-machine-api-types.yml'
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
jobs:
|
||||||
|
generate:
|
||||||
|
runs-on: 'ubuntu-latest'
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version-file: '.nvmrc'
|
||||||
|
cache: 'yarn'
|
||||||
|
- run: yarn install
|
||||||
|
- run: yarn generate:machine-api
|
||||||
|
- run: yarn fmt
|
||||||
|
- name: check for changes
|
||||||
|
id: git-check
|
||||||
|
run: |
|
||||||
|
git add .
|
||||||
|
if git status | grep -q "Changes to be committed"
|
||||||
|
then echo "modified=true" >> $GITHUB_OUTPUT
|
||||||
|
else echo "modified=false" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
- name: Commit changes, if any
|
||||||
|
if: steps.git-check.outputs.modified == 'true'
|
||||||
|
run: |
|
||||||
|
git add .
|
||||||
|
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
git config --local user.name "github-actions[bot]"
|
||||||
|
git remote set-url origin https://${{ github.actor }}:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git
|
||||||
|
git fetch origin
|
||||||
|
echo ${{ github.head_ref }}
|
||||||
|
git checkout ${{ github.head_ref }}
|
||||||
|
git commit -am "New machine-api types" || true
|
||||||
|
git push
|
||||||
|
git push origin ${{ github.head_ref }}
|
||||||
|
|
69
.github/workflows/playwright.yml
vendored
@ -13,7 +13,7 @@ permissions:
|
|||||||
contents: write
|
contents: write
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
actions: read
|
actions: read
|
||||||
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
@ -34,8 +34,13 @@ jobs:
|
|||||||
- 'src/wasm-lib/**'
|
- 'src/wasm-lib/**'
|
||||||
|
|
||||||
playwright-ubuntu:
|
playwright-ubuntu:
|
||||||
timeout-minutes: 60
|
timeout-minutes: 30
|
||||||
runs-on: ubuntu-latest-8-cores
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
shardIndex: [1, 2, 3, 4]
|
||||||
|
shardTotal: [4]
|
||||||
needs: check-rust-changes
|
needs: check-rust-changes
|
||||||
steps:
|
steps:
|
||||||
- name: Tune GitHub-hosted runner network
|
- name: Tune GitHub-hosted runner network
|
||||||
@ -106,13 +111,19 @@ jobs:
|
|||||||
- name: build web
|
- name: build web
|
||||||
run: yarn build:local
|
run: yarn build:local
|
||||||
- name: Run ubuntu/chrome snapshots
|
- name: Run ubuntu/chrome snapshots
|
||||||
continue-on-error: true
|
|
||||||
run: |
|
run: |
|
||||||
yarn playwright test --project="Google Chrome" --update-snapshots e2e/playwright/snapshot-tests.spec.ts
|
yarn playwright test --project="Google Chrome" --config=playwright.ci.config.ts --retries="3" --update-snapshots --grep=@snapshot --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
|
||||||
env:
|
env:
|
||||||
CI: true
|
CI: true
|
||||||
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||||
snapshottoken: ${{ secrets.KITTYCAD_API_TOKEN }}
|
snapshottoken: ${{ secrets.KITTYCAD_API_TOKEN }}
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
name: playwright-report-ubuntu-snapshot-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||||
|
path: playwright-report/
|
||||||
|
retention-days: 30
|
||||||
|
overwrite: true
|
||||||
- name: Clean up test-results
|
- name: Clean up test-results
|
||||||
if: always()
|
if: always()
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
@ -143,7 +154,7 @@ jobs:
|
|||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
if: steps.git-check.outputs.modified == 'true'
|
if: steps.git-check.outputs.modified == 'true'
|
||||||
with:
|
with:
|
||||||
name: playwright-report-ubuntu-${{ github.sha }}
|
name: playwright-report-ubuntu-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||||
path: playwright-report/
|
path: playwright-report/
|
||||||
retention-days: 30
|
retention-days: 30
|
||||||
# if have previous run results, use them
|
# if have previous run results, use them
|
||||||
@ -151,7 +162,7 @@ jobs:
|
|||||||
if: always()
|
if: always()
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
name: test-results-ubuntu-${{ github.sha }}
|
name: test-results-ubuntu-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||||
path: test-results/
|
path: test-results/
|
||||||
- name: Run ubuntu/chrome flow (with retries)
|
- name: Run ubuntu/chrome flow (with retries)
|
||||||
id: retry
|
id: retry
|
||||||
@ -160,14 +171,14 @@ jobs:
|
|||||||
if [[ ! -f "test-results/.last-run.json" ]]; then
|
if [[ ! -f "test-results/.last-run.json" ]]; then
|
||||||
# if no last run artifact, than run plawright normally
|
# if no last run artifact, than run plawright normally
|
||||||
echo "run playwright normally"
|
echo "run playwright normally"
|
||||||
yarn playwright test --project="Google Chrome" e2e/playwright/flow-tests.spec.ts || true
|
yarn playwright test --project="Google Chrome" --config=playwright.ci.config.ts --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} --grep-invert=@snapshot || true
|
||||||
# # send to axiom
|
# # send to axiom
|
||||||
node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
|
node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
retry=1
|
retry=1
|
||||||
max_retrys=4
|
max_retrys=4
|
||||||
|
|
||||||
# retry failed tests, doing our own retries because using inbuilt playwright retries causes connection issues
|
# retry failed tests, doing our own retries because using inbuilt playwright retries causes connection issues
|
||||||
while [[ $retry -le $max_retrys ]]; do
|
while [[ $retry -le $max_retrys ]]; do
|
||||||
if [[ -f "test-results/.last-run.json" ]]; then
|
if [[ -f "test-results/.last-run.json" ]]; then
|
||||||
@ -175,7 +186,7 @@ jobs:
|
|||||||
if [[ $failed_tests -gt 0 ]]; then
|
if [[ $failed_tests -gt 0 ]]; then
|
||||||
echo "retried=true" >>$GITHUB_OUTPUT
|
echo "retried=true" >>$GITHUB_OUTPUT
|
||||||
echo "run playwright with last failed tests and retry $retry"
|
echo "run playwright with last failed tests and retry $retry"
|
||||||
yarn playwright test --project="Google Chrome" --last-failed e2e/playwright/flow-tests.spec.ts || true
|
yarn playwright test --project="Google Chrome" --config=playwright.ci.config.ts --last-failed --grep-invert=@snapshot || true
|
||||||
# send to axiom
|
# send to axiom
|
||||||
node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
|
node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
|
||||||
retry=$((retry + 1))
|
retry=$((retry + 1))
|
||||||
@ -188,9 +199,9 @@ jobs:
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "retried=false" >>$GITHUB_OUTPUT
|
echo "retried=false" >>$GITHUB_OUTPUT
|
||||||
|
|
||||||
if [[ -f "test-results/.last-run.json" ]]; then
|
if [[ -f "test-results/.last-run.json" ]]; then
|
||||||
failed_tests=$(jq '.failedTests | length' test-results/.last-run.json)
|
failed_tests=$(jq '.failedTests | length' test-results/.last-run.json)
|
||||||
if [[ $failed_tests -gt 0 ]]; then
|
if [[ $failed_tests -gt 0 ]]; then
|
||||||
@ -210,21 +221,26 @@ jobs:
|
|||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
name: test-results-ubuntu-${{ github.sha }}
|
name: test-results-ubuntu-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||||
path: test-results/
|
path: test-results/
|
||||||
retention-days: 30
|
retention-days: 30
|
||||||
overwrite: true
|
overwrite: true
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
name: playwright-report-ubuntu-${{ github.sha }}
|
name: playwright-report-ubuntu-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||||
path: playwright-report/
|
path: playwright-report/
|
||||||
retention-days: 30
|
retention-days: 30
|
||||||
overwrite: true
|
overwrite: true
|
||||||
|
|
||||||
playwright-macos:
|
playwright-macos:
|
||||||
timeout-minutes: 60
|
timeout-minutes: 30
|
||||||
runs-on: macos-14-large
|
runs-on: macos-14
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
shardIndex: [1, 2, 3, 4]
|
||||||
|
shardTotal: [4]
|
||||||
needs: check-rust-changes
|
needs: check-rust-changes
|
||||||
steps:
|
steps:
|
||||||
- name: Tune GitHub-hosted runner network
|
- name: Tune GitHub-hosted runner network
|
||||||
@ -300,7 +316,7 @@ jobs:
|
|||||||
if: ${{ always() }}
|
if: ${{ always() }}
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
name: test-results-macos-${{ github.sha }}
|
name: test-results-macos-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||||
path: test-results/
|
path: test-results/
|
||||||
- name: Run macos/safari flow (with retries)
|
- name: Run macos/safari flow (with retries)
|
||||||
id: retry
|
id: retry
|
||||||
@ -309,14 +325,14 @@ jobs:
|
|||||||
if [[ ! -f "test-results/.last-run.json" ]]; then
|
if [[ ! -f "test-results/.last-run.json" ]]; then
|
||||||
# if no last run artifact, than run plawright normally
|
# if no last run artifact, than run plawright normally
|
||||||
echo "run playwright normally"
|
echo "run playwright normally"
|
||||||
yarn playwright test --project="webkit" e2e/playwright/flow-tests.spec.ts || true
|
yarn playwright test --project="webkit" --config=playwright.ci.config.ts --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} --grep-invert=@snapshot || true
|
||||||
# # send to axiom
|
# # send to axiom
|
||||||
node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
|
node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
retry=1
|
retry=1
|
||||||
max_retrys=4
|
max_retrys=4
|
||||||
|
|
||||||
# retry failed tests, doing our own retries because using inbuilt playwright retries causes connection issues
|
# retry failed tests, doing our own retries because using inbuilt playwright retries causes connection issues
|
||||||
while [[ $retry -le $max_retrys ]]; do
|
while [[ $retry -le $max_retrys ]]; do
|
||||||
if [[ -f "test-results/.last-run.json" ]]; then
|
if [[ -f "test-results/.last-run.json" ]]; then
|
||||||
@ -324,7 +340,7 @@ jobs:
|
|||||||
if [[ $failed_tests -gt 0 ]]; then
|
if [[ $failed_tests -gt 0 ]]; then
|
||||||
echo "retried=true" >>$GITHUB_OUTPUT
|
echo "retried=true" >>$GITHUB_OUTPUT
|
||||||
echo "run playwright with last failed tests and retry $retry"
|
echo "run playwright with last failed tests and retry $retry"
|
||||||
yarn playwright test --project="webkit" --last-failed e2e/playwright/flow-tests.spec.ts || true
|
yarn playwright test --project="webkit" --config=playwright.ci.config.ts --last-failed --grep-invert=@snapshot || true
|
||||||
# send to axiom
|
# send to axiom
|
||||||
node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
|
node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
|
||||||
retry=$((retry + 1))
|
retry=$((retry + 1))
|
||||||
@ -337,9 +353,9 @@ jobs:
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "retried=false" >>$GITHUB_OUTPUT
|
echo "retried=false" >>$GITHUB_OUTPUT
|
||||||
|
|
||||||
if [[ -f "test-results/.last-run.json" ]]; then
|
if [[ -f "test-results/.last-run.json" ]]; then
|
||||||
failed_tests=$(jq '.failedTests | length' test-results/.last-run.json)
|
failed_tests=$(jq '.failedTests | length' test-results/.last-run.json)
|
||||||
if [[ $failed_tests -gt 0 ]]; then
|
if [[ $failed_tests -gt 0 ]]; then
|
||||||
@ -354,15 +370,14 @@ jobs:
|
|||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
if: ${{ always() }}
|
if: ${{ always() }}
|
||||||
with:
|
with:
|
||||||
name: test-results-macos-${{ github.sha }}
|
name: test-results-macos-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||||
path: test-results/
|
path: test-results/
|
||||||
retention-days: 30
|
retention-days: 30
|
||||||
overwrite: true
|
overwrite: true
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
if: ${{ always() }}
|
if: ${{ always() }}
|
||||||
with:
|
with:
|
||||||
name: playwright-report-macos-${{ github.sha }}
|
name: playwright-report-macos-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||||
path: playwright-report/
|
path: playwright-report/
|
||||||
retention-days: 30
|
retention-days: 30
|
||||||
overwrite: true
|
overwrite: true
|
||||||
|
|
||||||
|
2
.gitignore
vendored
@ -39,6 +39,7 @@ src/wasm-lib/grackle/test_json_output
|
|||||||
e2e/playwright/playwright-secrets.env
|
e2e/playwright/playwright-secrets.env
|
||||||
e2e/playwright/temp1.png
|
e2e/playwright/temp1.png
|
||||||
e2e/playwright/temp2.png
|
e2e/playwright/temp2.png
|
||||||
|
e2e/playwright/temp3.png
|
||||||
# exports from snapshot-tests.spec.ts "exports of each format should work"
|
# exports from snapshot-tests.spec.ts "exports of each format should work"
|
||||||
e2e/playwright/export-snapshots/*
|
e2e/playwright/export-snapshots/*
|
||||||
!e2e/playwright/export-snapshots/*.png
|
!e2e/playwright/export-snapshots/*.png
|
||||||
@ -48,6 +49,7 @@ e2e/playwright/export-snapshots/*
|
|||||||
/playwright-report/
|
/playwright-report/
|
||||||
/blob-report/
|
/blob-report/
|
||||||
/playwright/.cache/
|
/playwright/.cache/
|
||||||
|
/src/lang/std/artifactMapCache
|
||||||
|
|
||||||
|
|
||||||
## generated files
|
## generated files
|
||||||
|
12
README.md
@ -110,6 +110,18 @@ Note that these became separate apps on Macos, so make sure you open the right o
|
|||||||
|
|
||||||
<img width="1232" alt="image (1)" src="https://user-images.githubusercontent.com/29681384/211947073-e76b4933-bef5-4636-bc4d-e930ac8e290f.png">
|
<img width="1232" alt="image (1)" src="https://user-images.githubusercontent.com/29681384/211947073-e76b4933-bef5-4636-bc4d-e930ac8e290f.png">
|
||||||
|
|
||||||
|
## Checking out commits / Bisecting
|
||||||
|
|
||||||
|
Which commands from setup are one off vs need to be run every time?
|
||||||
|
|
||||||
|
The following will need to be run when checking out a new commit and guarantees the build is not stale:
|
||||||
|
```bash
|
||||||
|
yarn install
|
||||||
|
yarn wasm-prep
|
||||||
|
yarn build:wasm-dev # or yarn build:wasm for slower but more production-like build
|
||||||
|
yarn start # or yarn build:local && yarn serve for slower but more production-like build
|
||||||
|
```
|
||||||
|
|
||||||
## Before submitting a PR
|
## Before submitting a PR
|
||||||
|
|
||||||
Before you submit a contribution PR to this repo, please ensure that:
|
Before you submit a contribution PR to this repo, please ensure that:
|
||||||
|
@ -25,5 +25,5 @@ once fixed in engine will just start working here with no language changes.
|
|||||||
|
|
||||||
Sketching on the chamfered face does not currently work.
|
Sketching on the chamfered face does not currently work.
|
||||||
|
|
||||||
- **Shell**: Shell is only working for `end` faces, not for `side` or `start`
|
- **Shell**: Shell sometimes does not work when arcs or fillets are involved.
|
||||||
faces. We are tracking the engine side bug on this.
|
We are tracking the engine side bug on this.
|
||||||
|
37
docs/kcl/assertEqual.md
Normal file
@ -21,6 +21,7 @@ layout: manual
|
|||||||
* [`arc`](kcl/arc)
|
* [`arc`](kcl/arc)
|
||||||
* [`asin`](kcl/asin)
|
* [`asin`](kcl/asin)
|
||||||
* [`assert`](kcl/assert)
|
* [`assert`](kcl/assert)
|
||||||
|
* [`assertEqual`](kcl/assertEqual)
|
||||||
* [`assertGreaterThan`](kcl/assertGreaterThan)
|
* [`assertGreaterThan`](kcl/assertGreaterThan)
|
||||||
* [`assertGreaterThanOrEq`](kcl/assertGreaterThanOrEq)
|
* [`assertGreaterThanOrEq`](kcl/assertGreaterThanOrEq)
|
||||||
* [`assertLessThan`](kcl/assertLessThan)
|
* [`assertLessThan`](kcl/assertLessThan)
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: "legAngX"
|
title: "legAngX"
|
||||||
excerpt: "Returns the angle of the given leg for x."
|
excerpt: "Compute the angle of the given leg for x."
|
||||||
layout: manual
|
layout: manual
|
||||||
---
|
---
|
||||||
|
|
||||||
Returns the angle of the given leg for x.
|
Compute the angle of the given leg for x.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: "legAngY"
|
title: "legAngY"
|
||||||
excerpt: "Returns the angle of the given leg for y."
|
excerpt: "Compute the angle of the given leg for y."
|
||||||
layout: manual
|
layout: manual
|
||||||
---
|
---
|
||||||
|
|
||||||
Returns the angle of the given leg for y.
|
Compute the angle of the given leg for y.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: "legLen"
|
title: "legLen"
|
||||||
excerpt: "Returns the length of the given leg."
|
excerpt: "Compute the length of the given leg."
|
||||||
layout: manual
|
layout: manual
|
||||||
---
|
---
|
||||||
|
|
||||||
Returns the length of the given leg.
|
Compute the length of the given leg.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
3734
docs/kcl/std.json
153
e2e/playwright/basic-sketch.spec.ts
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
import { test, expect, Page } from '@playwright/test'
|
||||||
|
import {
|
||||||
|
getUtils,
|
||||||
|
TEST_COLORS,
|
||||||
|
setup,
|
||||||
|
tearDown,
|
||||||
|
commonPoints,
|
||||||
|
PERSIST_MODELING_CONTEXT,
|
||||||
|
} from './test-utils'
|
||||||
|
|
||||||
|
test.beforeEach(async ({ context, page }) => {
|
||||||
|
await setup(context, page)
|
||||||
|
})
|
||||||
|
|
||||||
|
test.afterEach(async ({ page }, testInfo) => {
|
||||||
|
await tearDown(page, testInfo)
|
||||||
|
})
|
||||||
|
|
||||||
|
test.setTimeout(120000)
|
||||||
|
|
||||||
|
async function doBasicSketch(page: Page, openPanes: string[]) {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
await u.openDebugPanel()
|
||||||
|
|
||||||
|
// If we have the code pane open, we should see the code.
|
||||||
|
if (openPanes.includes('code')) {
|
||||||
|
await expect(u.codeLocator).toHaveText(``)
|
||||||
|
} else {
|
||||||
|
// Ensure we don't see the code.
|
||||||
|
await expect(u.codeLocator).not.toBeVisible()
|
||||||
|
}
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
|
).not.toBeDisabled()
|
||||||
|
await expect(page.getByRole('button', { name: 'Start Sketch' })).toBeVisible()
|
||||||
|
|
||||||
|
// click on "Start Sketch" button
|
||||||
|
await u.clearCommandLogs()
|
||||||
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
|
// select a plane
|
||||||
|
await page.mouse.click(700, 200)
|
||||||
|
|
||||||
|
if (openPanes.includes('code')) {
|
||||||
|
await expect(u.codeLocator).toHaveText(
|
||||||
|
`const sketch001 = startSketchOn('XZ')`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
await page.waitForTimeout(1000) // TODO detect animation ending, or disable animation
|
||||||
|
|
||||||
|
const startXPx = 600
|
||||||
|
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||||
|
if (openPanes.includes('code')) {
|
||||||
|
await expect(u.codeLocator)
|
||||||
|
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt(${commonPoints.startAt}, %)`)
|
||||||
|
}
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
|
||||||
|
if (openPanes.includes('code')) {
|
||||||
|
await expect(u.codeLocator)
|
||||||
|
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt(${commonPoints.startAt}, %)
|
||||||
|
|> line([${commonPoints.num1}, 0], %)`)
|
||||||
|
}
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
|
||||||
|
if (openPanes.includes('code')) {
|
||||||
|
await expect(u.codeLocator)
|
||||||
|
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt(${commonPoints.startAt}, %)
|
||||||
|
|> line([${commonPoints.num1}, 0], %)
|
||||||
|
|> line([0, ${commonPoints.num1 + 0.01}], %)`)
|
||||||
|
} else {
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
}
|
||||||
|
await page.mouse.click(startXPx, 500 - PUR * 20)
|
||||||
|
if (openPanes.includes('code')) {
|
||||||
|
await expect(u.codeLocator)
|
||||||
|
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt(${commonPoints.startAt}, %)
|
||||||
|
|> line([${commonPoints.num1}, 0], %)
|
||||||
|
|> line([0, ${commonPoints.num1 + 0.01}], %)
|
||||||
|
|> line([-${commonPoints.num2}, 0], %)`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// deselect line tool
|
||||||
|
await page.getByRole('button', { name: 'Line', exact: true }).click()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
|
||||||
|
const line1 = await u.getSegmentBodyCoords(`[data-overlay-index="${0}"]`, 0)
|
||||||
|
if (openPanes.includes('code')) {
|
||||||
|
expect(await u.getGreatestPixDiff(line1, TEST_COLORS.WHITE)).toBeLessThan(3)
|
||||||
|
await expect(
|
||||||
|
await u.getGreatestPixDiff(line1, [249, 249, 249])
|
||||||
|
).toBeLessThan(3)
|
||||||
|
}
|
||||||
|
// click between first two clicks to get center of the line
|
||||||
|
await page.mouse.click(startXPx + PUR * 15, 500 - PUR * 10)
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
if (openPanes.includes('code')) {
|
||||||
|
expect(await u.getGreatestPixDiff(line1, TEST_COLORS.BLUE)).toBeLessThan(3)
|
||||||
|
await expect(await u.getGreatestPixDiff(line1, [0, 0, 255])).toBeLessThan(3)
|
||||||
|
}
|
||||||
|
|
||||||
|
// hold down shift
|
||||||
|
await page.keyboard.down('Shift')
|
||||||
|
// click between the latest two clicks to get center of the line
|
||||||
|
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 20)
|
||||||
|
|
||||||
|
// selected two lines therefore there should be two cursors
|
||||||
|
if (openPanes.includes('code')) {
|
||||||
|
await expect(page.locator('.cm-cursor')).toHaveCount(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
await page.getByRole('button', { name: 'Length: open menu' }).click()
|
||||||
|
await page.getByRole('button', { name: 'Equal Length' }).click()
|
||||||
|
|
||||||
|
// Open the code pane.
|
||||||
|
await u.openKclCodePanel()
|
||||||
|
await expect(u.codeLocator).toHaveText(`const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt(${commonPoints.startAt}, %)
|
||||||
|
|> line([${commonPoints.num1}, 0], %, $seg01)
|
||||||
|
|> line([0, ${commonPoints.num1 + 0.01}], %)
|
||||||
|
|> angledLine([180, segLen(seg01)], %)`)
|
||||||
|
}
|
||||||
|
|
||||||
|
test.describe('Basic sketch', () => {
|
||||||
|
test('code pane open at start', async ({ page }) => {
|
||||||
|
await doBasicSketch(page, ['code'])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('code pane closed at start', async ({ page }) => {
|
||||||
|
// Load the app with the code panes
|
||||||
|
await page.addInitScript(async (persistModelingContext) => {
|
||||||
|
localStorage.setItem(
|
||||||
|
persistModelingContext,
|
||||||
|
JSON.stringify({ openPanes: [] })
|
||||||
|
)
|
||||||
|
}, PERSIST_MODELING_CONTEXT)
|
||||||
|
await doBasicSketch(page, [])
|
||||||
|
})
|
||||||
|
})
|
@ -0,0 +1,111 @@
|
|||||||
|
import { test, expect } from '@playwright/test'
|
||||||
|
import { getUtils, setup, tearDown } from './test-utils'
|
||||||
|
import { EngineCommand } from 'lang/std/artifactGraph'
|
||||||
|
import { uuidv4 } from 'lib/utils'
|
||||||
|
|
||||||
|
test.beforeEach(async ({ context, page }) => {
|
||||||
|
await setup(context, page)
|
||||||
|
})
|
||||||
|
|
||||||
|
test.afterEach(async ({ page }, testInfo) => {
|
||||||
|
await tearDown(page, testInfo)
|
||||||
|
})
|
||||||
|
|
||||||
|
test.describe('Can create sketches on all planes and their back sides', () => {
|
||||||
|
const sketchOnPlaneAndBackSideTest = async (
|
||||||
|
page: any,
|
||||||
|
plane: string,
|
||||||
|
clickCoords: { x: number; y: number }
|
||||||
|
) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
await u.openDebugPanel()
|
||||||
|
|
||||||
|
const coord =
|
||||||
|
plane === '-XY' || plane === '-YZ' || plane === 'XZ' ? -100 : 100
|
||||||
|
const camCommand: EngineCommand = {
|
||||||
|
type: 'modeling_cmd_req',
|
||||||
|
cmd_id: uuidv4(),
|
||||||
|
cmd: {
|
||||||
|
type: 'default_camera_look_at',
|
||||||
|
center: { x: 0, y: 0, z: 0 },
|
||||||
|
vantage: { x: coord, y: coord, z: coord },
|
||||||
|
up: { x: 0, y: 0, z: 1 },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const updateCamCommand: EngineCommand = {
|
||||||
|
type: 'modeling_cmd_req',
|
||||||
|
cmd_id: uuidv4(),
|
||||||
|
cmd: {
|
||||||
|
type: 'default_camera_get_settings',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const code = `const sketch001 = startSketchOn('${plane}')
|
||||||
|
|> startProfileAt([0.9, -1.22], %)`
|
||||||
|
|
||||||
|
await u.openDebugPanel()
|
||||||
|
|
||||||
|
await u.clearCommandLogs()
|
||||||
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||||
|
|
||||||
|
await u.sendCustomCmd(camCommand)
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await u.sendCustomCmd(updateCamCommand)
|
||||||
|
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
await page.mouse.click(clickCoords.x, clickCoords.y)
|
||||||
|
await page.waitForTimeout(300) // wait for animation
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Line', exact: true })
|
||||||
|
).toBeVisible()
|
||||||
|
|
||||||
|
// draw a line
|
||||||
|
const startXPx = 600
|
||||||
|
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(code)
|
||||||
|
|
||||||
|
await page.getByRole('button', { name: 'Line', exact: true }).click()
|
||||||
|
await u.openAndClearDebugPanel()
|
||||||
|
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
|
||||||
|
await u.clearCommandLogs()
|
||||||
|
await u.removeCurrentCode()
|
||||||
|
}
|
||||||
|
test('XY', async ({ page }) => {
|
||||||
|
await sketchOnPlaneAndBackSideTest(
|
||||||
|
page,
|
||||||
|
'XY',
|
||||||
|
{ x: 600, y: 388 } // red plane
|
||||||
|
// { x: 600, y: 400 }, // red plane // clicks grid helper and that causes problems, should fix so that these coords work too.
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('YZ', async ({ page }) => {
|
||||||
|
await sketchOnPlaneAndBackSideTest(page, 'YZ', { x: 700, y: 250 }) // green plane
|
||||||
|
})
|
||||||
|
|
||||||
|
test('XZ', async ({ page }) => {
|
||||||
|
await sketchOnPlaneAndBackSideTest(page, '-XZ', { x: 700, y: 80 }) // blue plane
|
||||||
|
})
|
||||||
|
|
||||||
|
test('-XY', async ({ page }) => {
|
||||||
|
await sketchOnPlaneAndBackSideTest(page, '-XY', { x: 600, y: 118 }) // back of red plane
|
||||||
|
})
|
||||||
|
|
||||||
|
test('-YZ', async ({ page }) => {
|
||||||
|
await sketchOnPlaneAndBackSideTest(page, '-YZ', { x: 700, y: 219 }) // back of green plane
|
||||||
|
})
|
||||||
|
|
||||||
|
test('-XZ', async ({ page }) => {
|
||||||
|
await sketchOnPlaneAndBackSideTest(page, 'XZ', { x: 700, y: 427 }) // back of blue plane
|
||||||
|
})
|
||||||
|
})
|
219
e2e/playwright/code-pane-and-errors.spec.ts
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
import { test, expect } from '@playwright/test'
|
||||||
|
|
||||||
|
import { getUtils, setup, tearDown } from './test-utils'
|
||||||
|
import { bracket } from 'lib/exampleKcl'
|
||||||
|
import { TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW } from './storageStates'
|
||||||
|
|
||||||
|
test.beforeEach(async ({ context, page }) => {
|
||||||
|
await setup(context, page)
|
||||||
|
})
|
||||||
|
|
||||||
|
test.afterEach(async ({ page }, testInfo) => {
|
||||||
|
await tearDown(page, testInfo)
|
||||||
|
})
|
||||||
|
|
||||||
|
test.describe('Code pane and errors', () => {
|
||||||
|
test('Typing KCL errors induces a badge on the code pane button', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
|
||||||
|
// Load the app with the working starter code
|
||||||
|
await page.addInitScript((code) => {
|
||||||
|
localStorage.setItem('persistCode', code)
|
||||||
|
}, bracket)
|
||||||
|
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
// wait for execution done
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
// Ensure no badge is present
|
||||||
|
const codePaneButtonHolder = page.locator('#code-button-holder')
|
||||||
|
await expect(codePaneButtonHolder).not.toContainText('notification')
|
||||||
|
|
||||||
|
// Delete a character to break the KCL
|
||||||
|
await u.openKclCodePanel()
|
||||||
|
await page.getByText('extrude(').click()
|
||||||
|
await page.keyboard.press('Backspace')
|
||||||
|
|
||||||
|
// Ensure that a badge appears on the button
|
||||||
|
await expect(codePaneButtonHolder).toContainText('notification')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Opening and closing the code pane will consistently show error diagnostics', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
|
||||||
|
// Load the app with the working starter code
|
||||||
|
await page.addInitScript((code) => {
|
||||||
|
localStorage.setItem('persistCode', code)
|
||||||
|
}, bracket)
|
||||||
|
|
||||||
|
await page.setViewportSize({ width: 1200, height: 900 })
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
// wait for execution done
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
// Ensure we have no errors in the gutter.
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
|
|
||||||
|
// Ensure no badge is present
|
||||||
|
const codePaneButton = page.getByRole('button', { name: 'KCL Code pane' })
|
||||||
|
const codePaneButtonHolder = page.locator('#code-button-holder')
|
||||||
|
await expect(codePaneButtonHolder).not.toContainText('notification')
|
||||||
|
|
||||||
|
// Delete a character to break the KCL
|
||||||
|
await u.openKclCodePanel()
|
||||||
|
await page.getByText('extrude(').click()
|
||||||
|
await page.keyboard.press('Backspace')
|
||||||
|
|
||||||
|
// Ensure that a badge appears on the button
|
||||||
|
await expect(codePaneButtonHolder).toContainText('notification')
|
||||||
|
|
||||||
|
// Ensure we have an error diagnostic.
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
|
||||||
|
|
||||||
|
// error text on hover
|
||||||
|
await page.hover('.cm-lint-marker-error')
|
||||||
|
await expect(page.getByText('Unexpected token').first()).toBeVisible()
|
||||||
|
|
||||||
|
// Close the code pane
|
||||||
|
await codePaneButton.click()
|
||||||
|
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
|
||||||
|
// Ensure that a badge appears on the button
|
||||||
|
await expect(codePaneButtonHolder).toContainText('notification')
|
||||||
|
// Ensure we have no errors in the gutter.
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
|
|
||||||
|
// Open the code pane
|
||||||
|
await u.openKclCodePanel()
|
||||||
|
|
||||||
|
// Ensure that a badge appears on the button
|
||||||
|
await expect(codePaneButtonHolder).toContainText('notification')
|
||||||
|
|
||||||
|
// Ensure we have an error diagnostic.
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
|
||||||
|
|
||||||
|
// error text on hover
|
||||||
|
await page.hover('.cm-lint-marker-error')
|
||||||
|
await expect(page.getByText('Unexpected token').first()).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('When error is not in view you can click the badge to scroll to it', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
|
||||||
|
// Load the app with the working starter code
|
||||||
|
await page.addInitScript((code) => {
|
||||||
|
localStorage.setItem('persistCode', code)
|
||||||
|
}, TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW)
|
||||||
|
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await page.waitForTimeout(1000)
|
||||||
|
|
||||||
|
// Ensure badge is present
|
||||||
|
const codePaneButtonHolder = page.locator('#code-button-holder')
|
||||||
|
await expect(codePaneButtonHolder).toContainText('notification')
|
||||||
|
|
||||||
|
// Ensure we have no errors in the gutter, since error out of view.
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
|
|
||||||
|
// Click the badge.
|
||||||
|
const badge = page.locator('#code-badge')
|
||||||
|
await expect(badge).toBeVisible()
|
||||||
|
await badge.click()
|
||||||
|
|
||||||
|
// Ensure we have an error diagnostic.
|
||||||
|
await expect(page.locator('.cm-lint-marker-error').first()).toBeVisible()
|
||||||
|
|
||||||
|
// Hover over the error to see the error message
|
||||||
|
await page.hover('.cm-lint-marker-error')
|
||||||
|
await expect(
|
||||||
|
page
|
||||||
|
.getByText(
|
||||||
|
'sketch profile must lie entirely on one side of the revolution axis'
|
||||||
|
)
|
||||||
|
.first()
|
||||||
|
).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('When error is not in view WITH LINTS you can click the badge to scroll to it', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
|
||||||
|
// Load the app with the working starter code
|
||||||
|
await page.addInitScript((code) => {
|
||||||
|
localStorage.setItem('persistCode', code)
|
||||||
|
}, TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW)
|
||||||
|
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await page.waitForTimeout(1000)
|
||||||
|
|
||||||
|
// Ensure badge is present
|
||||||
|
const codePaneButtonHolder = page.locator('#code-button-holder')
|
||||||
|
await expect(codePaneButtonHolder).toContainText('notification')
|
||||||
|
|
||||||
|
// Ensure we have no errors in the gutter, since error out of view.
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
|
|
||||||
|
// click in the editor to focus it
|
||||||
|
await page.locator('.cm-content').click()
|
||||||
|
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
|
||||||
|
// go to the start of the editor and enter more text which will trigger
|
||||||
|
// a lint error.
|
||||||
|
// GO to the start of the editor.
|
||||||
|
await page.keyboard.press('ArrowUp')
|
||||||
|
await page.keyboard.press('ArrowUp')
|
||||||
|
await page.keyboard.press('ArrowUp')
|
||||||
|
await page.keyboard.press('ArrowUp')
|
||||||
|
await page.keyboard.press('ArrowUp')
|
||||||
|
await page.keyboard.press('ArrowUp')
|
||||||
|
await page.keyboard.press('ArrowUp')
|
||||||
|
await page.keyboard.press('ArrowUp')
|
||||||
|
await page.keyboard.press('ArrowUp')
|
||||||
|
await page.keyboard.press('ArrowUp')
|
||||||
|
await page.keyboard.press('Home')
|
||||||
|
await page.keyboard.type('const foo_bar = 1')
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
|
// ensure we have a lint error
|
||||||
|
await expect(page.locator('.cm-lint-marker-info').first()).toBeVisible()
|
||||||
|
|
||||||
|
// Click the badge.
|
||||||
|
const badge = page.locator('#code-badge')
|
||||||
|
await expect(badge).toBeVisible()
|
||||||
|
await badge.click()
|
||||||
|
|
||||||
|
// Ensure we have an error diagnostic.
|
||||||
|
await expect(page.locator('.cm-lint-marker-error').first()).toBeVisible()
|
||||||
|
|
||||||
|
// Hover over the error to see the error message
|
||||||
|
await page.hover('.cm-lint-marker-error')
|
||||||
|
await expect(
|
||||||
|
page
|
||||||
|
.getByText(
|
||||||
|
'sketch profile must lie entirely on one side of the revolution axis'
|
||||||
|
)
|
||||||
|
.first()
|
||||||
|
).toBeVisible()
|
||||||
|
})
|
||||||
|
})
|
364
e2e/playwright/command-bar-tests.spec.ts
Normal file
@ -0,0 +1,364 @@
|
|||||||
|
import { test, expect } from '@playwright/test'
|
||||||
|
|
||||||
|
import { getUtils, setup, tearDown } from './test-utils'
|
||||||
|
import { KCL_DEFAULT_LENGTH } from 'lib/constants'
|
||||||
|
|
||||||
|
test.beforeEach(async ({ context, page }) => {
|
||||||
|
await setup(context, page)
|
||||||
|
})
|
||||||
|
|
||||||
|
test.afterEach(async ({ page }, testInfo) => {
|
||||||
|
await tearDown(page, testInfo)
|
||||||
|
})
|
||||||
|
|
||||||
|
test.describe('Command bar tests', () => {
|
||||||
|
test('Extrude from command bar selects extrude line after', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
await page.addInitScript(async () => {
|
||||||
|
localStorage.setItem(
|
||||||
|
'persistCode',
|
||||||
|
`const sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-10, -10], %)
|
||||||
|
|> line([20, 0], %)
|
||||||
|
|> line([0, 20], %)
|
||||||
|
|> xLine(-20, %)
|
||||||
|
|> close(%)
|
||||||
|
`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
// Click the line of code for xLine.
|
||||||
|
await page.getByText(`close(%)`).click() // TODO remove this and reinstate // await topHorzSegmentClick()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
|
await page.getByRole('button', { name: 'Extrude' }).click()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await expect(page.locator('.cm-activeLine')).toHaveText(
|
||||||
|
`const extrude001 = extrude(${KCL_DEFAULT_LENGTH}, sketch001)`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Fillet from command bar', async ({ page }) => {
|
||||||
|
await page.addInitScript(async () => {
|
||||||
|
localStorage.setItem(
|
||||||
|
'persistCode',
|
||||||
|
`const sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-5, -5], %)
|
||||||
|
|> line([0, 10], %)
|
||||||
|
|> line([10, 0], %)
|
||||||
|
|> line([0, -10], %)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
const extrude001 = extrude(-10, sketch001)`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
const selectSegment = () => page.getByText(`line([0, -10], %)`).click()
|
||||||
|
|
||||||
|
await selectSegment()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.getByRole('button', { name: 'Fillet' }).click()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await expect(page.locator('.cm-activeLine')).toContainText(
|
||||||
|
`fillet({ radius: ${KCL_DEFAULT_LENGTH}, tags: [seg01] }, %)`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Command bar can change a setting, and switch back and forth between arguments', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
const commandBarButton = page.getByRole('button', { name: 'Commands' })
|
||||||
|
const cmdSearchBar = page.getByPlaceholder('Search commands')
|
||||||
|
const commandName = 'debug panel'
|
||||||
|
const commandOption = page.getByRole('option', {
|
||||||
|
name: commandName,
|
||||||
|
exact: false,
|
||||||
|
})
|
||||||
|
const commandLevelArgButton = page.getByRole('button', { name: 'level' })
|
||||||
|
const commandThemeArgButton = page.getByRole('button', { name: 'value' })
|
||||||
|
const paneSelector = page.getByRole('button', { name: 'debug panel' })
|
||||||
|
// This selector changes after we set the setting
|
||||||
|
let commandOptionInput = page.getByPlaceholder('On')
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
|
).not.toBeDisabled()
|
||||||
|
|
||||||
|
// First try opening the command bar and closing it
|
||||||
|
await page
|
||||||
|
.getByRole('button', { name: 'Commands', exact: false })
|
||||||
|
.or(page.getByRole('button', { name: '⌘K' }))
|
||||||
|
.click()
|
||||||
|
|
||||||
|
await expect(cmdSearchBar).toBeVisible()
|
||||||
|
await page.keyboard.press('Escape')
|
||||||
|
await expect(cmdSearchBar).not.toBeVisible()
|
||||||
|
|
||||||
|
// Now try the same, but with the keyboard shortcut, check focus
|
||||||
|
await page.keyboard.press('Meta+K')
|
||||||
|
await expect(cmdSearchBar).toBeVisible()
|
||||||
|
await expect(cmdSearchBar).toBeFocused()
|
||||||
|
|
||||||
|
// Try typing in the command bar
|
||||||
|
await cmdSearchBar.fill(commandName)
|
||||||
|
await expect(commandOption).toBeVisible()
|
||||||
|
await commandOption.click()
|
||||||
|
const toggleInput = page.getByPlaceholder('On')
|
||||||
|
await expect(toggleInput).toBeVisible()
|
||||||
|
await expect(toggleInput).toBeFocused()
|
||||||
|
// Select On
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await expect(page.getByRole('option', { name: 'Off' })).toHaveAttribute(
|
||||||
|
'data-headlessui-state',
|
||||||
|
'active'
|
||||||
|
)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
|
// Check the toast appeared
|
||||||
|
await expect(
|
||||||
|
page.getByText(`Set show debug panel to "false" for this project`)
|
||||||
|
).toBeVisible()
|
||||||
|
// Check that the visibility changed
|
||||||
|
await expect(paneSelector).not.toBeVisible()
|
||||||
|
|
||||||
|
commandOptionInput = page.getByPlaceholder('off')
|
||||||
|
|
||||||
|
// Test case for https://github.com/KittyCAD/modeling-app/issues/2882
|
||||||
|
await commandBarButton.click()
|
||||||
|
await cmdSearchBar.focus()
|
||||||
|
await cmdSearchBar.fill(commandName)
|
||||||
|
await commandOption.click()
|
||||||
|
await expect(commandThemeArgButton).toBeDisabled()
|
||||||
|
await commandOptionInput.focus()
|
||||||
|
await commandOptionInput.fill('on')
|
||||||
|
await commandLevelArgButton.click()
|
||||||
|
await expect(commandLevelArgButton).toBeDisabled()
|
||||||
|
|
||||||
|
// Test case for https://github.com/KittyCAD/modeling-app/issues/2881
|
||||||
|
await commandThemeArgButton.click()
|
||||||
|
await expect(commandThemeArgButton).toBeDisabled()
|
||||||
|
await expect(commandLevelArgButton).toHaveText('level: project')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Command bar keybinding works from code editor and can change a setting', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
|
).not.toBeDisabled()
|
||||||
|
|
||||||
|
// Put the cursor in the code editor
|
||||||
|
await page.locator('.cm-content').click()
|
||||||
|
|
||||||
|
// Now try the same, but with the keyboard shortcut, check focus
|
||||||
|
await page.keyboard.press('Meta+K')
|
||||||
|
|
||||||
|
let cmdSearchBar = page.getByPlaceholder('Search commands')
|
||||||
|
await expect(cmdSearchBar).toBeVisible()
|
||||||
|
await expect(cmdSearchBar).toBeFocused()
|
||||||
|
|
||||||
|
// Try typing in the command bar
|
||||||
|
await cmdSearchBar.fill('theme')
|
||||||
|
const themeOption = page.getByRole('option', {
|
||||||
|
name: 'Settings · app · theme',
|
||||||
|
})
|
||||||
|
await expect(themeOption).toBeVisible()
|
||||||
|
await themeOption.click()
|
||||||
|
const themeInput = page.getByPlaceholder('dark')
|
||||||
|
await expect(themeInput).toBeVisible()
|
||||||
|
await expect(themeInput).toBeFocused()
|
||||||
|
// Select dark theme
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await expect(page.getByRole('option', { name: 'system' })).toHaveAttribute(
|
||||||
|
'data-headlessui-state',
|
||||||
|
'active'
|
||||||
|
)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
|
// Check the toast appeared
|
||||||
|
await expect(
|
||||||
|
page.getByText(`Set theme to "system" as a user default`)
|
||||||
|
).toBeVisible()
|
||||||
|
// Check that the theme changed
|
||||||
|
await expect(page.locator('body')).not.toHaveClass(`body-bg dark`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Can extrude from the command bar', async ({ page }) => {
|
||||||
|
await page.addInitScript(async () => {
|
||||||
|
localStorage.setItem(
|
||||||
|
'persistCode',
|
||||||
|
`const distance = sqrt(20)
|
||||||
|
const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([-6.95, 10.98], %)
|
||||||
|
|> line([25.1, 0.41], %)
|
||||||
|
|> line([0.73, -20.93], %)
|
||||||
|
|> line([-23.44, 0.52], %)
|
||||||
|
|> close(%)
|
||||||
|
`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
// Make sure the stream is up
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
|
).not.toBeDisabled()
|
||||||
|
await u.clearCommandLogs()
|
||||||
|
await page.getByRole('button', { name: 'Extrude' }).isEnabled()
|
||||||
|
|
||||||
|
let cmdSearchBar = page.getByPlaceholder('Search commands')
|
||||||
|
await page.keyboard.press('Meta+K')
|
||||||
|
await expect(cmdSearchBar).toBeVisible()
|
||||||
|
|
||||||
|
// Search for extrude command and choose it
|
||||||
|
await page.getByRole('option', { name: 'Extrude' }).click()
|
||||||
|
|
||||||
|
// Assert that we're on the selection step
|
||||||
|
await expect(page.getByRole('button', { name: 'selection' })).toBeDisabled()
|
||||||
|
// Select a face
|
||||||
|
await page.mouse.move(700, 200)
|
||||||
|
await page.mouse.click(700, 200)
|
||||||
|
|
||||||
|
// Assert that we're on the distance step
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'distance', exact: false })
|
||||||
|
).toBeDisabled()
|
||||||
|
|
||||||
|
// Assert that the an alternative variable name is chosen,
|
||||||
|
// since the default variable name is already in use (distance)
|
||||||
|
await page.getByRole('button', { name: 'Create new variable' }).click()
|
||||||
|
await expect(page.getByPlaceholder('Variable name')).toHaveValue(
|
||||||
|
'distance001'
|
||||||
|
)
|
||||||
|
|
||||||
|
const continueButton = page.getByRole('button', { name: 'Continue' })
|
||||||
|
const submitButton = page.getByRole('button', { name: 'Submit command' })
|
||||||
|
await continueButton.click()
|
||||||
|
|
||||||
|
// Review step and argument hotkeys
|
||||||
|
await expect(submitButton).toBeEnabled()
|
||||||
|
await expect(submitButton).toBeFocused()
|
||||||
|
await submitButton.press('Backspace')
|
||||||
|
|
||||||
|
// Assert we're back on the distance step
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'distance', exact: false })
|
||||||
|
).toBeDisabled()
|
||||||
|
|
||||||
|
await continueButton.click()
|
||||||
|
await submitButton.click()
|
||||||
|
|
||||||
|
// Check that the code was updated
|
||||||
|
await u.waitForCmdReceive('extrude')
|
||||||
|
// Unfortunately this indentation seems to matter for the test
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`const distance = sqrt(20)
|
||||||
|
const distance001 = ${KCL_DEFAULT_LENGTH}
|
||||||
|
const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([-6.95, 10.98], %)
|
||||||
|
|> line([25.1, 0.41], %)
|
||||||
|
|> line([0.73, -20.93], %)
|
||||||
|
|> line([-23.44, 0.52], %)
|
||||||
|
|> close(%)
|
||||||
|
const extrude001 = extrude(distance001, sketch001)`.replace(
|
||||||
|
/(\r\n|\n|\r)/gm,
|
||||||
|
''
|
||||||
|
) // remove newlines
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Can switch between sketch tools via command bar', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
const sketchButton = page.getByRole('button', { name: 'Start Sketch' })
|
||||||
|
const cmdBarButton = page.getByRole('button', { name: 'Commands' })
|
||||||
|
const rectangleToolCommand = page.getByRole('option', {
|
||||||
|
name: 'rectangle',
|
||||||
|
})
|
||||||
|
const rectangleToolButton = page.getByRole('button', {
|
||||||
|
name: 'Corner rectangle',
|
||||||
|
exact: true,
|
||||||
|
})
|
||||||
|
const lineToolCommand = page.getByRole('option', {
|
||||||
|
name: 'Line',
|
||||||
|
})
|
||||||
|
const lineToolButton = page.getByRole('button', {
|
||||||
|
name: 'Line',
|
||||||
|
exact: true,
|
||||||
|
})
|
||||||
|
const arcToolCommand = page.getByRole('option', { name: 'Tangential Arc' })
|
||||||
|
const arcToolButton = page.getByRole('button', {
|
||||||
|
name: 'Tangential Arc',
|
||||||
|
exact: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Start a sketch
|
||||||
|
await sketchButton.click()
|
||||||
|
await page.mouse.click(700, 200)
|
||||||
|
|
||||||
|
// Switch between sketch tools via the command bar
|
||||||
|
await expect(lineToolButton).toHaveAttribute('aria-pressed', 'true')
|
||||||
|
await cmdBarButton.click()
|
||||||
|
await rectangleToolCommand.click()
|
||||||
|
await expect(rectangleToolButton).toHaveAttribute('aria-pressed', 'true')
|
||||||
|
await cmdBarButton.click()
|
||||||
|
await lineToolCommand.click()
|
||||||
|
await expect(lineToolButton).toHaveAttribute('aria-pressed', 'true')
|
||||||
|
|
||||||
|
// Click in the scene a couple times to draw a line
|
||||||
|
// so tangential arc is valid
|
||||||
|
await page.mouse.click(700, 200)
|
||||||
|
await page.mouse.move(700, 300, { steps: 5 })
|
||||||
|
await page.mouse.click(700, 300)
|
||||||
|
|
||||||
|
// switch to tangential arc via command bar
|
||||||
|
await cmdBarButton.click()
|
||||||
|
await arcToolCommand.click()
|
||||||
|
await expect(arcToolButton).toHaveAttribute('aria-pressed', 'true')
|
||||||
|
})
|
||||||
|
})
|
519
e2e/playwright/copilot-ghost-test.spec.ts
Normal file
@ -0,0 +1,519 @@
|
|||||||
|
import { test, expect } from '@playwright/test'
|
||||||
|
import { getUtils, setup, tearDown } from './test-utils'
|
||||||
|
|
||||||
|
test.beforeEach(async ({ context, page }) => {
|
||||||
|
await setup(context, page)
|
||||||
|
})
|
||||||
|
|
||||||
|
test.afterEach(async ({ page }, testInfo) => {
|
||||||
|
await tearDown(page, testInfo)
|
||||||
|
})
|
||||||
|
test.describe('Copilot ghost text', () => {
|
||||||
|
test.skip(true, 'Needs to get covered again')
|
||||||
|
|
||||||
|
test('completes code in empty file', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toBeVisible()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
|
||||||
|
)
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => {`
|
||||||
|
)
|
||||||
|
|
||||||
|
// We should be able to hit Tab to accept the completion.
|
||||||
|
await page.keyboard.press('Tab')
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
|
||||||
|
)
|
||||||
|
|
||||||
|
// Hit enter a few times.
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %) `
|
||||||
|
)
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
test.skip('copilot disabled in sketch mode no select plane', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
|
||||||
|
// Click sketch mode.
|
||||||
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
|
||||||
|
// Exit sketch mode.
|
||||||
|
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
||||||
|
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
|
||||||
|
)
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => {`
|
||||||
|
)
|
||||||
|
|
||||||
|
// We should be able to hit Tab to accept the completion.
|
||||||
|
await page.keyboard.press('Tab')
|
||||||
|
await expect(page.locator('.cm-content')).toContainText(
|
||||||
|
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('copilot disabled in sketch mode after selecting plane', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
|
||||||
|
// Click sketch mode.
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
|
).not.toBeDisabled()
|
||||||
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||||
|
|
||||||
|
// select a plane
|
||||||
|
await page.mouse.click(700, 200)
|
||||||
|
await page.waitForTimeout(700) // wait for animation
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`const sketch001 = startSketchOn('XZ')`
|
||||||
|
)
|
||||||
|
|
||||||
|
// Escape to exit the tool.
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
await page.keyboard.press('Escape')
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`const sketch001 = startSketchOn('XZ')`
|
||||||
|
)
|
||||||
|
|
||||||
|
// Escape again to exit sketch mode.
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
await page.keyboard.press('Escape')
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`const sketch001 = startSketchOn('XZ')fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
|
||||||
|
)
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => {`
|
||||||
|
)
|
||||||
|
|
||||||
|
// We should be able to hit Tab to accept the completion.
|
||||||
|
await page.keyboard.press('Tab')
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`const sketch001 = startSketchOn('XZ')fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
|
||||||
|
)
|
||||||
|
|
||||||
|
// Hit enter a few times.
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`const sketch001 = startSketchOn('XZ')fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %) `
|
||||||
|
)
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('ArrowUp in code rejects the suggestion', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toBeVisible()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
|
||||||
|
)
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => {`
|
||||||
|
)
|
||||||
|
|
||||||
|
// Going elsewhere in the code should hide the ghost text.
|
||||||
|
await page.keyboard.press('ArrowUp')
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('ArrowDown in code rejects the suggestion', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toBeVisible()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
|
||||||
|
)
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => {`
|
||||||
|
)
|
||||||
|
|
||||||
|
// Going elsewhere in the code should hide the ghost text.
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('ArrowLeft in code rejects the suggestion', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toBeVisible()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
|
||||||
|
)
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => {`
|
||||||
|
)
|
||||||
|
|
||||||
|
// Going elsewhere in the code should hide the ghost text.
|
||||||
|
await page.keyboard.press('ArrowLeft')
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('ArrowRight in code rejects the suggestion', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toBeVisible()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
|
||||||
|
)
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => {`
|
||||||
|
)
|
||||||
|
|
||||||
|
// Going elsewhere in the code should hide the ghost text.
|
||||||
|
await page.keyboard.press('ArrowRight')
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Enter in code scoots it down', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toBeVisible()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
|
||||||
|
)
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => {`
|
||||||
|
)
|
||||||
|
|
||||||
|
// Going elsewhere in the code should hide the ghost text.
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toBeVisible()
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Ctrl+shift+z in code rejects the suggestion', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
const CtrlKey = process.platform === 'darwin' ? 'Meta' : 'Control'
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toBeVisible()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
|
||||||
|
)
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => {`
|
||||||
|
)
|
||||||
|
|
||||||
|
// Going elsewhere in the code should hide the ghost text.
|
||||||
|
await page.keyboard.down(CtrlKey)
|
||||||
|
await page.keyboard.down('Shift')
|
||||||
|
await page.keyboard.press('KeyZ')
|
||||||
|
await page.keyboard.up(CtrlKey)
|
||||||
|
await page.keyboard.up('Shift')
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Ctrl+z in code rejects the suggestion and undos the last code', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
const CtrlKey = process.platform === 'darwin' ? 'Meta' : 'Control'
|
||||||
|
|
||||||
|
await page.waitForTimeout(800)
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
|
||||||
|
await page.keyboard.type('{thing: "blah"}', { delay: 0 })
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(`{thing: "blah"}`)
|
||||||
|
|
||||||
|
// We wanna make sure the code saves.
|
||||||
|
await page.waitForTimeout(800)
|
||||||
|
|
||||||
|
// Ctrl+z
|
||||||
|
await page.keyboard.down(CtrlKey)
|
||||||
|
await page.keyboard.press('KeyZ')
|
||||||
|
await page.keyboard.up(CtrlKey)
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
|
||||||
|
// Ctrl+shift+z
|
||||||
|
await page.keyboard.down(CtrlKey)
|
||||||
|
await page.keyboard.down('Shift')
|
||||||
|
await page.keyboard.press('KeyZ')
|
||||||
|
await page.keyboard.up(CtrlKey)
|
||||||
|
await page.keyboard.up('Shift')
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(`{thing: "blah"}`)
|
||||||
|
|
||||||
|
// We wanna make sure the code saves.
|
||||||
|
await page.waitForTimeout(800)
|
||||||
|
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toBeVisible()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`{thing: "blah"}fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
|
||||||
|
)
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => {`
|
||||||
|
)
|
||||||
|
|
||||||
|
// Once for the enter.
|
||||||
|
await page.keyboard.down(CtrlKey)
|
||||||
|
await page.keyboard.press('KeyZ')
|
||||||
|
await page.keyboard.up(CtrlKey)
|
||||||
|
|
||||||
|
// Once for the text.
|
||||||
|
await page.keyboard.down(CtrlKey)
|
||||||
|
await page.keyboard.press('KeyZ')
|
||||||
|
await page.keyboard.up(CtrlKey)
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
||||||
|
|
||||||
|
// TODO when we make codemirror a widget, we can test this.
|
||||||
|
//await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('delete in code rejects the suggestion', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toBeVisible()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
|
||||||
|
)
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => {`
|
||||||
|
)
|
||||||
|
|
||||||
|
// Going elsewhere in the code should hide the ghost text.
|
||||||
|
await page.keyboard.press('Delete')
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('backspace in code rejects the suggestion', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toBeVisible()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
|
||||||
|
)
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => {`
|
||||||
|
)
|
||||||
|
|
||||||
|
// Going elsewhere in the code should hide the ghost text.
|
||||||
|
await page.keyboard.press('Backspace')
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('focus outside code pane rejects the suggestion', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toBeVisible()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => { const sg = startSketchOn('XY') |> startProfileAt(pos, %) |> line([0, scale], %) |> line([scale, 0], %) |> line([0, -scale], %) return sg}const part001 = cube([0,0], 20) |> close(%) |> extrude(20, %)`
|
||||||
|
)
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).toHaveText(
|
||||||
|
`fn cube = (pos, scale) => {`
|
||||||
|
)
|
||||||
|
|
||||||
|
// Going outside the editor should hide the ghost text.
|
||||||
|
await page.mouse.move(0, 0)
|
||||||
|
await page
|
||||||
|
.getByRole('button', { name: 'Start Sketch' })
|
||||||
|
.waitFor({ state: 'visible' })
|
||||||
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||||
|
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
})
|
||||||
|
})
|
867
e2e/playwright/editor-tests.spec.ts
Normal file
@ -0,0 +1,867 @@
|
|||||||
|
import { test, expect } from '@playwright/test'
|
||||||
|
import { uuidv4 } from 'lib/utils'
|
||||||
|
import { getUtils, setup, tearDown } from './test-utils'
|
||||||
|
|
||||||
|
test.beforeEach(async ({ context, page }) => {
|
||||||
|
await setup(context, page)
|
||||||
|
})
|
||||||
|
|
||||||
|
test.afterEach(async ({ page }, testInfo) => {
|
||||||
|
await tearDown(page, testInfo)
|
||||||
|
})
|
||||||
|
|
||||||
|
test.describe('Editor tests', () => {
|
||||||
|
test('can comment out code with ctrl+/', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
const CtrlKey = process.platform === 'darwin' ? 'Meta' : 'Control'
|
||||||
|
|
||||||
|
// check no error to begin with
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await page.keyboard.type(`const sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-10, -10], %)
|
||||||
|
|> line([20, 0], %)
|
||||||
|
|> line([0, 20], %)
|
||||||
|
|> line([-20, 0], %)
|
||||||
|
|> close(%)`)
|
||||||
|
|
||||||
|
await page.keyboard.down(CtrlKey)
|
||||||
|
await page.keyboard.press('/')
|
||||||
|
await page.keyboard.up(CtrlKey)
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content'))
|
||||||
|
.toHaveText(`const sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-10, -10], %)
|
||||||
|
|> line([20, 0], %)
|
||||||
|
|> line([0, 20], %)
|
||||||
|
|> line([-20, 0], %)
|
||||||
|
// |> close(%)`)
|
||||||
|
|
||||||
|
// uncomment the code
|
||||||
|
await page.keyboard.down(CtrlKey)
|
||||||
|
await page.keyboard.press('/')
|
||||||
|
await page.keyboard.up(CtrlKey)
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content'))
|
||||||
|
.toHaveText(`const sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-10, -10], %)
|
||||||
|
|> line([20, 0], %)
|
||||||
|
|> line([0, 20], %)
|
||||||
|
|> line([-20, 0], %)
|
||||||
|
|> close(%)`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('if you click the format button it formats your code', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
// check no error to begin with
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await page.keyboard.type(`const sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-10, -10], %)
|
||||||
|
|> line([20, 0], %)
|
||||||
|
|> line([0, 20], %)
|
||||||
|
|> line([-20, 0], %)
|
||||||
|
|> close(%)`)
|
||||||
|
await page.locator('#code-pane button:first-child').click()
|
||||||
|
await page.locator('button:has-text("Format code")').click()
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content'))
|
||||||
|
.toHaveText(`const sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-10, -10], %)
|
||||||
|
|> line([20, 0], %)
|
||||||
|
|> line([0, 20], %)
|
||||||
|
|> line([-20, 0], %)
|
||||||
|
|> close(%)`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('fold gutters work', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
|
||||||
|
const fullCode = `const sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-10, -10], %)
|
||||||
|
|> line([20, 0], %)
|
||||||
|
|> line([0, 20], %)
|
||||||
|
|> line([-20, 0], %)
|
||||||
|
|> close(%)`
|
||||||
|
await page.addInitScript(async () => {
|
||||||
|
localStorage.setItem(
|
||||||
|
'persistCode',
|
||||||
|
`const sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-10, -10], %)
|
||||||
|
|> line([20, 0], %)
|
||||||
|
|> line([0, 20], %)
|
||||||
|
|> line([-20, 0], %)
|
||||||
|
|> close(%)`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
// TODO: Jess needs to fix this but you have to mod the code to get them to show
|
||||||
|
// up, its an annoying codemirror thing.
|
||||||
|
await page.locator('.cm-content').click()
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
|
const foldGutterFoldLine = page.locator('[title="Fold line"]')
|
||||||
|
const foldGutterUnfoldLine = page.locator('[title="Unfold line"]')
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(fullCode)
|
||||||
|
|
||||||
|
// check no error to begin with
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
|
|
||||||
|
// Make sure we have a fold gutter
|
||||||
|
await expect(foldGutterFoldLine).toBeVisible()
|
||||||
|
await expect(foldGutterUnfoldLine).not.toBeVisible()
|
||||||
|
|
||||||
|
// Collapse the code
|
||||||
|
await foldGutterFoldLine.click()
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`const sketch001 = startSketchOn('XY')… `
|
||||||
|
)
|
||||||
|
await expect(page.locator('.cm-content')).not.toHaveText(fullCode)
|
||||||
|
await expect(foldGutterFoldLine).not.toBeVisible()
|
||||||
|
await expect(foldGutterUnfoldLine.nth(1)).toBeVisible()
|
||||||
|
|
||||||
|
// Expand the code
|
||||||
|
await foldGutterUnfoldLine.nth(1).click()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(fullCode)
|
||||||
|
|
||||||
|
// Delete all the code.
|
||||||
|
await page.locator('.cm-content').click()
|
||||||
|
// Select all
|
||||||
|
await page.keyboard.press('Control+A')
|
||||||
|
await page.keyboard.press('Backspace')
|
||||||
|
await page.keyboard.press('Meta+A')
|
||||||
|
await page.keyboard.press('Backspace')
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
await expect(page.locator('.cm-content')).not.toHaveText(fullCode)
|
||||||
|
|
||||||
|
await expect(foldGutterUnfoldLine).not.toBeVisible()
|
||||||
|
await expect(foldGutterFoldLine).not.toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('hover over functions shows function description', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.addInitScript(async () => {
|
||||||
|
localStorage.setItem(
|
||||||
|
'persistCode',
|
||||||
|
`const sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-10, -10], %)
|
||||||
|
|> line([20, 0], %)
|
||||||
|
|> line([0, 20], %)
|
||||||
|
|> line([-20, 0], %)
|
||||||
|
|> close(%)`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
// check no error to begin with
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
|
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
// focus the editor
|
||||||
|
await u.codeLocator.click()
|
||||||
|
|
||||||
|
// Hover over the startSketchOn function
|
||||||
|
await page.getByText('startSketchOn').hover()
|
||||||
|
await expect(page.locator('.hover-tooltip')).toBeVisible()
|
||||||
|
await expect(
|
||||||
|
page.getByText(
|
||||||
|
'Start a new 2-dimensional sketch on a specific plane or face'
|
||||||
|
)
|
||||||
|
).toBeVisible()
|
||||||
|
|
||||||
|
// Hover over the line function
|
||||||
|
await page.getByText('line').first().hover()
|
||||||
|
await expect(page.locator('.hover-tooltip')).toBeVisible()
|
||||||
|
await expect(page.getByText('Draw a line')).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('if you use the format keyboard binding it formats your code', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.addInitScript(async () => {
|
||||||
|
localStorage.setItem(
|
||||||
|
'persistCode',
|
||||||
|
`const sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-10, -10], %)
|
||||||
|
|> line([20, 0], %)
|
||||||
|
|> line([0, 20], %)
|
||||||
|
|> line([-20, 0], %)
|
||||||
|
|> close(%)`
|
||||||
|
)
|
||||||
|
localStorage.setItem('disableAxis', 'true')
|
||||||
|
})
|
||||||
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
// check no error to begin with
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
|
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
// focus the editor
|
||||||
|
await u.codeLocator.click()
|
||||||
|
|
||||||
|
// Hit alt+shift+f to format the code
|
||||||
|
await page.keyboard.press('Alt+Shift+KeyF')
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content'))
|
||||||
|
.toHaveText(`const sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-10, -10], %)
|
||||||
|
|> line([20, 0], %)
|
||||||
|
|> line([0, 20], %)
|
||||||
|
|> line([-20, 0], %)
|
||||||
|
|> close(%)`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('if you write kcl with lint errors you get lints', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
// check no error to begin with
|
||||||
|
await expect(page.locator('.cm-lint-marker-info')).not.toBeVisible()
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await page.keyboard.type('const my_snake_case_var = 5')
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.keyboard.type('const myCamelCaseVar = 5')
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
|
// press arrows to clear autocomplete
|
||||||
|
await page.keyboard.press('ArrowLeft')
|
||||||
|
await page.keyboard.press('ArrowRight')
|
||||||
|
|
||||||
|
// error in guter
|
||||||
|
await expect(page.locator('.cm-lint-marker-info').first()).toBeVisible()
|
||||||
|
|
||||||
|
// error text on hover
|
||||||
|
await page.hover('.cm-lint-marker-info')
|
||||||
|
await expect(
|
||||||
|
page.getByText('Identifiers must be lowerCamelCase').first()
|
||||||
|
).toBeVisible()
|
||||||
|
|
||||||
|
// select the line that's causing the error and delete it
|
||||||
|
await page.getByText('const my_snake_case_var = 5').click()
|
||||||
|
await page.keyboard.press('End')
|
||||||
|
await page.keyboard.down('Shift')
|
||||||
|
await page.keyboard.press('Home')
|
||||||
|
await page.keyboard.up('Shift')
|
||||||
|
await page.keyboard.press('Backspace')
|
||||||
|
|
||||||
|
// wait for .cm-lint-marker-info not to be visible
|
||||||
|
await expect(page.locator('.cm-lint-marker-info')).not.toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('if you fixup kcl errors you clear lints', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.addInitScript(async () => {
|
||||||
|
localStorage.setItem(
|
||||||
|
'persistCode',
|
||||||
|
`const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([3.29, 7.86], %)
|
||||||
|
|> line([2.48, 2.44], %)
|
||||||
|
|> line([2.66, 1.17], %)
|
||||||
|
|> close(%)
|
||||||
|
`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
// check no error to begin with
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
|
||||||
|
await page.getByText(' |> line([2.48, 2.44], %)').click()
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.locator('.cm-lint-marker-error').first()
|
||||||
|
).not.toBeVisible()
|
||||||
|
await page.keyboard.press('End')
|
||||||
|
await page.keyboard.press('Backspace')
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-lint-marker-error').first()).toBeVisible()
|
||||||
|
await page.keyboard.type(')')
|
||||||
|
await expect(
|
||||||
|
page.locator('.cm-lint-marker-error').first()
|
||||||
|
).not.toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('if you write invalid kcl you get inlined errors', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
// check no error to begin with
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
|
|
||||||
|
/* add the following code to the editor ($ error is not a valid line)
|
||||||
|
$ error
|
||||||
|
const topAng = 30
|
||||||
|
const bottomAng = 25
|
||||||
|
*/
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await page.keyboard.type('$ error')
|
||||||
|
|
||||||
|
// press arrows to clear autocomplete
|
||||||
|
await page.keyboard.press('ArrowLeft')
|
||||||
|
await page.keyboard.press('ArrowRight')
|
||||||
|
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.keyboard.type('const topAng = 30')
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.keyboard.type('const bottomAng = 25')
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
|
// error in guter
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
|
||||||
|
|
||||||
|
// error text on hover
|
||||||
|
await page.hover('.cm-lint-marker-error')
|
||||||
|
await expect(page.getByText('Unexpected token').first()).toBeVisible()
|
||||||
|
|
||||||
|
// select the line that's causing the error and delete it
|
||||||
|
await page.getByText('$ error').click()
|
||||||
|
await page.keyboard.press('End')
|
||||||
|
await page.keyboard.down('Shift')
|
||||||
|
await page.keyboard.press('Home')
|
||||||
|
await page.keyboard.up('Shift')
|
||||||
|
await page.keyboard.press('Backspace')
|
||||||
|
|
||||||
|
// wait for .cm-lint-marker-error not to be visible
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
|
|
||||||
|
// let's check we get an error when defining the same variable twice
|
||||||
|
await page.getByText('const bottomAng = 25').click()
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.keyboard.type("// Let's define the same thing twice")
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.keyboard.type('const topAng = 42')
|
||||||
|
await page.keyboard.press('ArrowLeft')
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
|
||||||
|
await expect(
|
||||||
|
page.locator('.cm-lint-marker.cm-lint-marker-error')
|
||||||
|
).toBeVisible()
|
||||||
|
|
||||||
|
await page.locator('.cm-lint-marker.cm-lint-marker-error').hover()
|
||||||
|
await expect(page.locator('.cm-diagnosticText').first()).toBeVisible()
|
||||||
|
await expect(
|
||||||
|
page.getByText('Cannot redefine `topAng`').first()
|
||||||
|
).toBeVisible()
|
||||||
|
|
||||||
|
const secondTopAng = page.getByText('topAng').first()
|
||||||
|
await secondTopAng?.dblclick()
|
||||||
|
await page.keyboard.type('otherAng')
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('error with 2 source ranges gets 2 diagnostics', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.addInitScript(async () => {
|
||||||
|
localStorage.setItem(
|
||||||
|
'persistCode',
|
||||||
|
`const length = .750
|
||||||
|
const width = 0.500
|
||||||
|
const height = 0.500
|
||||||
|
const dia = 4
|
||||||
|
|
||||||
|
fn squareHole = (l, w) => {
|
||||||
|
const squareHoleSketch = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-width / 2, -length / 2], %)
|
||||||
|
|> lineTo([width / 2, -length / 2], %)
|
||||||
|
|> lineTo([width / 2, length / 2], %)
|
||||||
|
|> lineTo([-width / 2, length / 2], %)
|
||||||
|
|> close(%)
|
||||||
|
return squareHoleSketch
|
||||||
|
}
|
||||||
|
`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
// check no error to begin with
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
|
|
||||||
|
// Click on the bottom of the code editor to add a new line
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.keyboard.type(`const extrusion = startSketchOn('XY')
|
||||||
|
|> circle([0, 0], dia/2, %)
|
||||||
|
|> hole(squareHole(length, width, height), %)
|
||||||
|
|> extrude(height, %)`)
|
||||||
|
|
||||||
|
// error in gutter
|
||||||
|
await expect(page.locator('.cm-lint-marker-error').first()).toBeVisible()
|
||||||
|
await page.hover('.cm-lint-marker-error:first-child')
|
||||||
|
await expect(
|
||||||
|
page.getByText('Expected 2 arguments, got 3').first()
|
||||||
|
).toBeVisible()
|
||||||
|
|
||||||
|
// Make sure there are two diagnostics
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).toHaveCount(2)
|
||||||
|
})
|
||||||
|
test('if your kcl gets an error from the engine it is inlined', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.addInitScript(async () => {
|
||||||
|
localStorage.setItem(
|
||||||
|
'persistCode',
|
||||||
|
`const box = startSketchOn('XY')
|
||||||
|
|> startProfileAt([0, 0], %)
|
||||||
|
|> line([0, 10], %)
|
||||||
|
|> line([10, 0], %)
|
||||||
|
|> line([0, -10], %, $revolveAxis)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(10, %)
|
||||||
|
|
||||||
|
const sketch001 = startSketchOn(box, revolveAxis)
|
||||||
|
|> startProfileAt([5, 10], %)
|
||||||
|
|> line([0, -10], %)
|
||||||
|
|> line([2, 0], %)
|
||||||
|
|> line([0, -10], %)
|
||||||
|
|> close(%)
|
||||||
|
|> revolve({
|
||||||
|
axis: revolveAxis,
|
||||||
|
angle: 90
|
||||||
|
}, %)
|
||||||
|
`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
|
await page.goto('/')
|
||||||
|
await u.waitForPageLoad()
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
|
||||||
|
|
||||||
|
// error text on hover
|
||||||
|
await page.hover('.cm-lint-marker-error')
|
||||||
|
const searchText =
|
||||||
|
'sketch profile must lie entirely on one side of the revolution axis'
|
||||||
|
await expect(page.getByText(searchText)).toBeVisible()
|
||||||
|
})
|
||||||
|
test.describe('Autocomplete works', () => {
|
||||||
|
test('with enter/click to accept the completion', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
// this test might be brittle as we add and remove functions
|
||||||
|
// but should also be easy to update.
|
||||||
|
// tests clicking on an option, selection the first option
|
||||||
|
// and arrowing down to an option
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await page.keyboard.type('const sketch001 = start')
|
||||||
|
|
||||||
|
// expect there to be six auto complete options
|
||||||
|
await expect(page.locator('.cm-completionLabel')).toHaveCount(8)
|
||||||
|
// this makes sure we can accept a completion with click
|
||||||
|
await page.getByText('startSketchOn').click()
|
||||||
|
await page.keyboard.type("'XZ'")
|
||||||
|
await page.keyboard.press('Tab')
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.keyboard.type(' |> startProfi')
|
||||||
|
// expect there be a single auto complete option that we can just hit enter on
|
||||||
|
await expect(page.locator('.cm-completionLabel')).toBeVisible()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.press('Enter') // accepting the auto complete, not a new line
|
||||||
|
|
||||||
|
await page.keyboard.press('Tab')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.type('12')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.press('Tab')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.press('Tab')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.press('Tab')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.type(' |> lin')
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-tooltip-autocomplete')).toBeVisible()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
// press arrow down twice then enter to accept xLine
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
// finish line with comment
|
||||||
|
await page.keyboard.type('5')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.press('Tab')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.press('Tab')
|
||||||
|
|
||||||
|
await page.keyboard.type(' // ')
|
||||||
|
// Since we need to parse the ast to know we are in a comment we gotta hang tight.
|
||||||
|
await page.waitForTimeout(700)
|
||||||
|
await page.keyboard.type('lin ')
|
||||||
|
await page.waitForTimeout(200)
|
||||||
|
// there shouldn't be any auto complete options for 'lin' in the comment
|
||||||
|
await expect(page.locator('.cm-completionLabel')).not.toBeVisible()
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content'))
|
||||||
|
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([3.14, 12], %)
|
||||||
|
|> xLine(5, %) // lin`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('with tab to accept the completion', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
// this test might be brittle as we add and remove functions
|
||||||
|
// but should also be easy to update.
|
||||||
|
// tests clicking on an option, selection the first option
|
||||||
|
// and arrowing down to an option
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await page.keyboard.type('const sketch001 = startSketchO')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
|
// Make sure just hitting tab will take the only one left
|
||||||
|
await expect(page.locator('.cm-completionLabel')).toHaveCount(1)
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('Tab')
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.type("'XZ'")
|
||||||
|
await page.keyboard.press('Tab')
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.keyboard.type(' |> startProfi')
|
||||||
|
// expect there be a single auto complete option that we can just hit enter on
|
||||||
|
await expect(page.locator('.cm-completionLabel')).toBeVisible()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.press('Tab') // accepting the auto complete, not a new line
|
||||||
|
|
||||||
|
await page.keyboard.press('Tab')
|
||||||
|
await page.keyboard.type('12')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.press('Tab')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.press('Tab')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.press('Tab')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.type(' |> lin')
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-tooltip-autocomplete')).toBeVisible()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
// press arrow down twice then tab to accept xLine
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await page.keyboard.press('Tab')
|
||||||
|
// finish line with comment
|
||||||
|
await page.keyboard.type('5')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.press('Tab')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.press('Tab')
|
||||||
|
|
||||||
|
await page.keyboard.type(' // ')
|
||||||
|
// Since we need to parse the ast to know we are in a comment we gotta hang tight.
|
||||||
|
await page.waitForTimeout(700)
|
||||||
|
await page.keyboard.type('lin ')
|
||||||
|
await page.waitForTimeout(200)
|
||||||
|
// there shouldn't be any auto complete options for 'lin' in the comment
|
||||||
|
await expect(page.locator('.cm-completionLabel')).not.toBeVisible()
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content'))
|
||||||
|
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([3.14, 12], %)
|
||||||
|
|> xLine(5, %) // lin`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
test('Can undo a click and point extrude with ctrl+z', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.addInitScript(async () => {
|
||||||
|
localStorage.setItem(
|
||||||
|
'persistCode',
|
||||||
|
`const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([4.61, -14.01], %)
|
||||||
|
|> line([12.73, -0.09], %)
|
||||||
|
|> tangentialArcTo([24.95, -5.38], %)
|
||||||
|
|> close(%)`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
|
).not.toBeDisabled()
|
||||||
|
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await u.openAndClearDebugPanel()
|
||||||
|
await u.sendCustomCmd({
|
||||||
|
type: 'modeling_cmd_req',
|
||||||
|
cmd_id: uuidv4(),
|
||||||
|
cmd: {
|
||||||
|
type: 'default_camera_look_at',
|
||||||
|
vantage: { x: 0, y: -1250, z: 580 },
|
||||||
|
center: { x: 0, y: 0, z: 0 },
|
||||||
|
up: { x: 0, y: 0, z: 1 },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await u.sendCustomCmd({
|
||||||
|
type: 'modeling_cmd_req',
|
||||||
|
cmd_id: uuidv4(),
|
||||||
|
cmd: {
|
||||||
|
type: 'default_camera_get_settings',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
|
const startPX = [665, 458]
|
||||||
|
|
||||||
|
const dragPX = 40
|
||||||
|
|
||||||
|
await page.getByText('startProfileAt([4.61, -14.01], %)').click()
|
||||||
|
await expect(page.getByRole('button', { name: 'Extrude' })).toBeVisible()
|
||||||
|
await page.getByRole('button', { name: 'Extrude' }).click()
|
||||||
|
|
||||||
|
await expect(page.getByTestId('command-bar')).toBeVisible()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await expect(page.getByText('Confirm Extrude')).toBeVisible()
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
|
// expect the code to have changed
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`const sketch001 = startSketchOn('XZ') |> startProfileAt([4.61, -14.01], %) |> line([12.73, -0.09], %) |> tangentialArcTo([24.95, -5.38], %) |> close(%)const extrude001 = extrude(5, sketch001)`
|
||||||
|
)
|
||||||
|
|
||||||
|
// Now hit undo
|
||||||
|
await page.keyboard.down('Control')
|
||||||
|
await page.keyboard.press('KeyZ')
|
||||||
|
await page.keyboard.up('Control')
|
||||||
|
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await expect(page.locator('.cm-content'))
|
||||||
|
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([4.61, -14.01], %)
|
||||||
|
|> line([12.73, -0.09], %)
|
||||||
|
|> tangentialArcTo([24.95, -5.38], %)
|
||||||
|
|> close(%)`)
|
||||||
|
})
|
||||||
|
|
||||||
|
// failing for the same reason as "Can edit a sketch that has been extruded in the same pipe"
|
||||||
|
// please fix together
|
||||||
|
test.fixme('Can undo a sketch modification with ctrl+z', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.addInitScript(async () => {
|
||||||
|
localStorage.setItem(
|
||||||
|
'persistCode',
|
||||||
|
`const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([4.61, -14.01], %)
|
||||||
|
|> line([12.73, -0.09], %)
|
||||||
|
|> tangentialArcTo([24.95, -5.38], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(5, %)`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
|
).not.toBeDisabled()
|
||||||
|
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await u.openAndClearDebugPanel()
|
||||||
|
await u.sendCustomCmd({
|
||||||
|
type: 'modeling_cmd_req',
|
||||||
|
cmd_id: uuidv4(),
|
||||||
|
cmd: {
|
||||||
|
type: 'default_camera_look_at',
|
||||||
|
vantage: { x: 0, y: -1250, z: 580 },
|
||||||
|
center: { x: 0, y: 0, z: 0 },
|
||||||
|
up: { x: 0, y: 0, z: 1 },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await u.sendCustomCmd({
|
||||||
|
type: 'modeling_cmd_req',
|
||||||
|
cmd_id: uuidv4(),
|
||||||
|
cmd: {
|
||||||
|
type: 'default_camera_get_settings',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
|
const startPX = [665, 458]
|
||||||
|
|
||||||
|
const dragPX = 40
|
||||||
|
|
||||||
|
await page.getByText('startProfileAt([4.61, -14.01], %)').click()
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Edit Sketch' })
|
||||||
|
).toBeVisible()
|
||||||
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||||
|
await page.waitForTimeout(400)
|
||||||
|
let prevContent = await page.locator('.cm-content').innerText()
|
||||||
|
|
||||||
|
await expect(page.getByTestId('segment-overlay')).toHaveCount(2)
|
||||||
|
|
||||||
|
// drag startProfieAt handle
|
||||||
|
await page.dragAndDrop('#stream', '#stream', {
|
||||||
|
sourcePosition: { x: startPX[0], y: startPX[1] },
|
||||||
|
targetPosition: { x: startPX[0] + dragPX, y: startPX[1] + dragPX },
|
||||||
|
})
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
||||||
|
prevContent = await page.locator('.cm-content').innerText()
|
||||||
|
|
||||||
|
// drag line handle
|
||||||
|
// we wait so it saves the code
|
||||||
|
await page.waitForTimeout(800)
|
||||||
|
|
||||||
|
const lineEnd = await u.getBoundingBox('[data-overlay-index="0"]')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.dragAndDrop('#stream', '#stream', {
|
||||||
|
sourcePosition: { x: lineEnd.x - 5, y: lineEnd.y },
|
||||||
|
targetPosition: { x: lineEnd.x + dragPX, y: lineEnd.y + dragPX },
|
||||||
|
})
|
||||||
|
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
||||||
|
prevContent = await page.locator('.cm-content').innerText()
|
||||||
|
|
||||||
|
// we wait so it saves the code
|
||||||
|
await page.waitForTimeout(800)
|
||||||
|
|
||||||
|
// drag tangentialArcTo handle
|
||||||
|
const tangentEnd = await u.getBoundingBox('[data-overlay-index="1"]')
|
||||||
|
await page.dragAndDrop('#stream', '#stream', {
|
||||||
|
sourcePosition: { x: tangentEnd.x, y: tangentEnd.y - 5 },
|
||||||
|
targetPosition: {
|
||||||
|
x: tangentEnd.x + dragPX,
|
||||||
|
y: tangentEnd.y + dragPX,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
||||||
|
|
||||||
|
// expect the code to have changed
|
||||||
|
await expect(page.locator('.cm-content'))
|
||||||
|
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([7.12, -16.82], %)
|
||||||
|
|> line([15.4, -2.74], %)
|
||||||
|
|> tangentialArcTo([24.95, -5.38], %)
|
||||||
|
|> line([2.65, -2.69], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(5, %)`)
|
||||||
|
|
||||||
|
// Hit undo
|
||||||
|
await page.keyboard.down('Control')
|
||||||
|
await page.keyboard.press('KeyZ')
|
||||||
|
await page.keyboard.up('Control')
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content'))
|
||||||
|
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([7.12, -16.82], %)
|
||||||
|
|> line([15.4, -2.74], %)
|
||||||
|
|> tangentialArcTo([24.95, -5.38], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(5, %)`)
|
||||||
|
|
||||||
|
// Hit undo again.
|
||||||
|
await page.keyboard.down('Control')
|
||||||
|
await page.keyboard.press('KeyZ')
|
||||||
|
await page.keyboard.up('Control')
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content'))
|
||||||
|
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([7.12, -16.82], %)
|
||||||
|
|> line([12.73, -0.09], %)
|
||||||
|
|> tangentialArcTo([24.95, -5.38], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(5, %)`)
|
||||||
|
|
||||||
|
// Hit undo again.
|
||||||
|
await page.keyboard.down('Control')
|
||||||
|
await page.keyboard.press('KeyZ')
|
||||||
|
await page.keyboard.up('Control')
|
||||||
|
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await expect(page.locator('.cm-content'))
|
||||||
|
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([4.61, -14.01], %)
|
||||||
|
|> line([12.73, -0.09], %)
|
||||||
|
|> tangentialArcTo([24.95, -5.38], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(5, %)`)
|
||||||
|
})
|
||||||
|
})
|
Before Width: | Height: | Size: 249 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 249 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 249 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 171 KiB After Width: | Height: | Size: 49 KiB |