Compare commits
1 Commits
move-tests
...
achalmers/
Author | SHA1 | Date | |
---|---|---|---|
078ffa02b0 |
59
.github/ci-cd-scripts/playwright-browser-chrome.sh
vendored
Executable file
@ -0,0 +1,59 @@
|
|||||||
|
# bash strict mode
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
if [[ ! -f "test-results/.last-run.json" ]]; then
|
||||||
|
# if no last run artifact, than run plawright normally
|
||||||
|
echo "run playwright normally"
|
||||||
|
if [[ "$3" == ubuntu-latest* ]]; then
|
||||||
|
yarn test:playwright:browser:chrome:ubuntu -- --shard=$1/$2 || true
|
||||||
|
elif [[ "$3" == windows-latest* ]]; then
|
||||||
|
yarn test:playwright:browser:chrome:windows -- --shard=$1/$2 || true
|
||||||
|
else
|
||||||
|
echo "Do not run playwright. Unable to detect os runtime."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# # send to axiom
|
||||||
|
node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
|
||||||
|
fi
|
||||||
|
|
||||||
|
retry=1
|
||||||
|
max_retrys=4
|
||||||
|
|
||||||
|
# retry failed tests, doing our own retries because using inbuilt playwright retries causes connection issues
|
||||||
|
while [[ $retry -le $max_retrys ]]; do
|
||||||
|
if [[ -f "test-results/.last-run.json" ]]; then
|
||||||
|
failed_tests=$(jq '.failedTests | length' test-results/.last-run.json)
|
||||||
|
if [[ $failed_tests -gt 0 ]]; then
|
||||||
|
echo "retried=true" >>$GITHUB_OUTPUT
|
||||||
|
echo "run playwright with last failed tests and retry $retry"
|
||||||
|
if [[ "$3" == ubuntu-latest* ]]; then
|
||||||
|
yarn test:playwright:browser:chrome:ubuntu -- --last-failed || true
|
||||||
|
elif [[ "$3" == windows-latest* ]]; then
|
||||||
|
yarn test:playwright:browser:chrome:windows -- --last-failed || true
|
||||||
|
else
|
||||||
|
echo "Do not run playwright. Unable to detect os runtime."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# send to axiom
|
||||||
|
node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
|
||||||
|
retry=$((retry + 1))
|
||||||
|
else
|
||||||
|
echo "retried=false" >>$GITHUB_OUTPUT
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "retried=false" >>$GITHUB_OUTPUT
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "retried=false" >>$GITHUB_OUTPUT
|
||||||
|
|
||||||
|
if [[ -f "test-results/.last-run.json" ]]; then
|
||||||
|
failed_tests=$(jq '.failedTests | length' test-results/.last-run.json)
|
||||||
|
if [[ $failed_tests -gt 0 ]]; then
|
||||||
|
# if it still fails after 3 retrys, then fail the job
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
exit 0
|
20
.github/ci-cd-scripts/playwright-electron.sh
vendored
@ -1,17 +1,15 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# bash strict mode
|
# bash strict mode
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
if [[ ! -f "test-results/.last-run.json" ]]; then
|
if [[ ! -f "test-results/.last-run.json" ]]; then
|
||||||
# if no last run artifact, than run plawright normally
|
# if no last run artifact, than run plawright normally
|
||||||
echo "run playwright normally"
|
echo "run playwright normally"
|
||||||
if [[ "$3" == ubuntu-latest* ]]; then
|
if [[ "$1" == ubuntu-latest* ]]; then
|
||||||
xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn test:playwright:electron:ubuntu -- --shard=$1/$2 || true
|
xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn test:playwright:electron:ubuntu || true
|
||||||
elif [[ "$3" == windows-latest* ]]; then
|
elif [[ "$1" == windows-latest* ]]; then
|
||||||
yarn test:playwright:electron:windows -- --shard=$1/$2 || true
|
yarn test:playwright:electron:windows || true
|
||||||
elif [[ "$3" == macos-14* ]]; then
|
elif [[ "$1" == macos-14* ]]; then
|
||||||
yarn test:playwright:electron:macos -- --shard=$1/$2 || true
|
yarn test:playwright:electron:macos || true
|
||||||
else
|
else
|
||||||
echo "Do not run playwright. Unable to detect os runtime."
|
echo "Do not run playwright. Unable to detect os runtime."
|
||||||
exit 1
|
exit 1
|
||||||
@ -30,11 +28,11 @@ while [[ $retry -le $max_retrys ]]; do
|
|||||||
if [[ $failed_tests -gt 0 ]]; then
|
if [[ $failed_tests -gt 0 ]]; then
|
||||||
echo "retried=true" >>$GITHUB_OUTPUT
|
echo "retried=true" >>$GITHUB_OUTPUT
|
||||||
echo "run playwright with last failed tests and retry $retry"
|
echo "run playwright with last failed tests and retry $retry"
|
||||||
if [[ "$3" == ubuntu-latest* ]]; then
|
if [[ "$1" == ubuntu-latest* ]]; then
|
||||||
xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn test:playwright:electron:ubuntu -- --last-failed || true
|
xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn test:playwright:electron:ubuntu -- --last-failed || true
|
||||||
elif [[ "$3" == windows-latest* ]]; then
|
elif [[ "$1" == windows-latest* ]]; then
|
||||||
yarn test:playwright:electron:windows -- --last-failed || true
|
yarn test:playwright:electron:windows -- --last-failed || true
|
||||||
elif [[ "$3" == macos-14* ]]; then
|
elif [[ "$1" == macos-14* ]]; then
|
||||||
yarn test:playwright:electron:macos -- --last-failed || true
|
yarn test:playwright:electron:macos -- --last-failed || true
|
||||||
else
|
else
|
||||||
echo "Do not run playwright. Unable to detect os runtime."
|
echo "Do not run playwright. Unable to detect os runtime."
|
||||||
|
35
.github/workflows/build-apps.yml
vendored
@ -165,6 +165,7 @@ jobs:
|
|||||||
- name: Build the app (release)
|
- name: Build the app (release)
|
||||||
if: ${{ env.IS_RELEASE == 'true' || env.IS_NIGHTLY == 'true' }}
|
if: ${{ env.IS_RELEASE == 'true' || env.IS_NIGHTLY == 'true' }}
|
||||||
env:
|
env:
|
||||||
|
PUBLISH_FOR_PULL_REQUEST: true
|
||||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||||
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
||||||
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
||||||
@ -172,6 +173,7 @@ jobs:
|
|||||||
CSC_LINK: ${{ secrets.APPLE_CERTIFICATE }}
|
CSC_LINK: ${{ secrets.APPLE_CERTIFICATE }}
|
||||||
CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
||||||
CSC_KEYCHAIN: ${{ secrets.APPLE_SIGNING_IDENTITY }}
|
CSC_KEYCHAIN: ${{ secrets.APPLE_SIGNING_IDENTITY }}
|
||||||
|
CSC_FOR_PULL_REQUEST: true
|
||||||
WINDOWS_CERTIFICATE_THUMBPRINT: ${{ secrets.WINDOWS_CERTIFICATE_THUMBPRINT }}
|
WINDOWS_CERTIFICATE_THUMBPRINT: ${{ secrets.WINDOWS_CERTIFICATE_THUMBPRINT }}
|
||||||
run: yarn electron-builder --config --publish always
|
run: yarn electron-builder --config --publish always
|
||||||
|
|
||||||
@ -227,6 +229,7 @@ jobs:
|
|||||||
CSC_LINK: ${{ secrets.APPLE_CERTIFICATE }}
|
CSC_LINK: ${{ secrets.APPLE_CERTIFICATE }}
|
||||||
CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
||||||
CSC_KEYCHAIN: ${{ secrets.APPLE_SIGNING_IDENTITY }}
|
CSC_KEYCHAIN: ${{ secrets.APPLE_SIGNING_IDENTITY }}
|
||||||
|
CSC_FOR_PULL_REQUEST: true
|
||||||
WINDOWS_CERTIFICATE_THUMBPRINT: ${{ secrets.WINDOWS_CERTIFICATE_THUMBPRINT }}
|
WINDOWS_CERTIFICATE_THUMBPRINT: ${{ secrets.WINDOWS_CERTIFICATE_THUMBPRINT }}
|
||||||
run: yarn electron-builder --config --publish always
|
run: yarn electron-builder --config --publish always
|
||||||
|
|
||||||
@ -359,17 +362,6 @@ jobs:
|
|||||||
- name: List artifacts
|
- name: List artifacts
|
||||||
run: "ls -R out"
|
run: "ls -R out"
|
||||||
|
|
||||||
- name: Set more complete nightly release notes
|
|
||||||
if: ${{ env.IS_NIGHTLY == 'true' }}
|
|
||||||
run: |
|
|
||||||
# Note: preferred going this way instead of a full clone in the checkout step,
|
|
||||||
# see https://github.com/actions/checkout/issues/1471
|
|
||||||
git fetch --prune --unshallow --tags
|
|
||||||
export TAG="nightly-${VERSION}"
|
|
||||||
export PREVIOUS_TAG=$(git describe --tags --match="nightly-v[0-9]*" --abbrev=0)
|
|
||||||
export NOTES=$(./scripts/get-nightly-changelog.sh)
|
|
||||||
yarn files:set-notes
|
|
||||||
|
|
||||||
- name: Authenticate to Google Cloud
|
- name: Authenticate to Google Cloud
|
||||||
if: ${{ env.IS_NIGHTLY == 'true' }}
|
if: ${{ env.IS_NIGHTLY == 'true' }}
|
||||||
uses: 'google-github-actions/auth@v2.1.7'
|
uses: 'google-github-actions/auth@v2.1.7'
|
||||||
@ -391,17 +383,12 @@ jobs:
|
|||||||
parent: false
|
parent: false
|
||||||
destination: 'dl.kittycad.io/releases/modeling-app/nightly'
|
destination: 'dl.kittycad.io/releases/modeling-app/nightly'
|
||||||
|
|
||||||
- name: Invalidate bucket cache on latest*.yml and last_download.json files
|
- name: Create draft release
|
||||||
if: ${{ env.IS_NIGHTLY == 'true' }}
|
uses: softprops/action-gh-release@v2
|
||||||
run: yarn files:invalidate-bucket:nightly
|
if: ${{ env.IS_RELEASE == 'true' }}
|
||||||
|
|
||||||
- name: Tag nightly commit
|
|
||||||
if: ${{ env.IS_NIGHTLY == 'true' }}
|
|
||||||
uses: actions/github-script@v7
|
|
||||||
with:
|
with:
|
||||||
script: |
|
name: ${{ env.VERSION }}
|
||||||
const { VERSION } = process.env
|
tag_name: ${{ env.VERSION }}
|
||||||
const { owner, repo } = context.repo
|
draft: true
|
||||||
const { sha } = context
|
generate_release_notes: true
|
||||||
const ref = `refs/tags/nightly-${VERSION}`
|
files: 'out/Zoo*'
|
||||||
github.rest.git.createRef({ owner, repo, sha, ref })
|
|
||||||
|
156
.github/workflows/e2e-tests.yml
vendored
@ -33,13 +33,13 @@ jobs:
|
|||||||
rust:
|
rust:
|
||||||
- 'src/wasm-lib/**'
|
- 'src/wasm-lib/**'
|
||||||
|
|
||||||
electron:
|
browser:
|
||||||
timeout-minutes: ${{ matrix.os == 'macos-14' && 60 || 50 }}
|
timeout-minutes: ${{ matrix.os == 'macos-14' && 60 || 50 }}
|
||||||
name: playwright:electron:${{ matrix.os }} ${{ matrix.shardIndex }} ${{ matrix.shardTotal }}
|
name: playwright:browser:${{ matrix.os }} ${{ matrix.shardIndex }} ${{ matrix.shardTotal }}
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest-8-cores, windows-latest-8-cores, macos-14-large]
|
os: [ubuntu-latest-8-cores, windows-latest-8-cores]
|
||||||
shardIndex: [1, 2, 3, 4]
|
shardIndex: [1, 2, 3, 4]
|
||||||
shardTotal: [4]
|
shardTotal: [4]
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
@ -68,7 +68,7 @@ jobs:
|
|||||||
- name: Download Wasm Cache
|
- name: Download Wasm Cache
|
||||||
id: download-wasm
|
id: download-wasm
|
||||||
if: needs.check-rust-changes.outputs.rust-changed == 'false'
|
if: needs.check-rust-changes.outputs.rust-changed == 'false'
|
||||||
uses: dawidd6/action-download-artifact@v7
|
uses: dawidd6/action-download-artifact@v6
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
github_token: ${{secrets.GITHUB_TOKEN}}
|
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||||
@ -123,13 +123,13 @@ jobs:
|
|||||||
if: steps.download-wasm.outcome == 'failure'
|
if: steps.download-wasm.outcome == 'failure'
|
||||||
shell: bash
|
shell: bash
|
||||||
run: yarn build:wasm
|
run: yarn build:wasm
|
||||||
- name: build electron
|
- name: build web
|
||||||
|
run: yarn build:local
|
||||||
shell: bash
|
shell: bash
|
||||||
run: yarn tron:package
|
|
||||||
- name: Run ubuntu/chrome snapshots
|
- name: Run ubuntu/chrome snapshots
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
PLATFORM=web yarn playwright test --config=playwright.config.ts --retries="3" --update-snapshots --grep=@snapshot --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
|
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
|
||||||
NODE_ENV: development
|
NODE_ENV: development
|
||||||
@ -186,12 +186,12 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: test-results-${{ matrix.os }}-${{ matrix.shardIndex }}-${{ github.sha }}
|
name: test-results-${{ matrix.os }}-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||||
path: test-results/
|
path: test-results/
|
||||||
- name: Run playwright/electron flow (with retries)
|
- name: Run playwright/chrome flow (with retries)
|
||||||
id: retry
|
id: retry
|
||||||
if: ${{ !cancelled() && (success() || failure()) }}
|
if: ${{ !cancelled() && (success() || failure()) }}
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
.github/ci-cd-scripts/playwright-electron.sh ${{matrix.shardIndex}} ${{matrix.shardTotal}} ${{matrix.os}}
|
.github/ci-cd-scripts/playwright-browser-chrome.sh ${{matrix.shardIndex}} ${{matrix.shardTotal}} ${{matrix.os}}
|
||||||
env:
|
env:
|
||||||
CI: true
|
CI: true
|
||||||
FAIL_ON_CONSOLE_ERRORS: true
|
FAIL_ON_CONSOLE_ERRORS: true
|
||||||
@ -199,6 +199,11 @@ jobs:
|
|||||||
VITE_KC_DEV_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
VITE_KC_DEV_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||||
VITE_KC_SKIP_AUTH: true
|
VITE_KC_SKIP_AUTH: true
|
||||||
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||||
|
- name: send to axiom
|
||||||
|
if: always()
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
node playwrightProcess.mjs | tee /tmp/github-actions.log
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
@ -216,3 +221,136 @@ jobs:
|
|||||||
retention-days: 30
|
retention-days: 30
|
||||||
overwrite: true
|
overwrite: true
|
||||||
|
|
||||||
|
|
||||||
|
electron:
|
||||||
|
name: playwright:electron:${{matrix.os}}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest-8-cores, windows-latest-8-cores, macos-14-large]
|
||||||
|
timeout-minutes: 60
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
needs: check-rust-changes
|
||||||
|
steps:
|
||||||
|
- name: Tune GitHub-hosted runner network
|
||||||
|
uses: smorimoto/tune-github-hosted-runner-network@v1
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version-file: '.nvmrc'
|
||||||
|
cache: 'yarn'
|
||||||
|
- uses: KittyCAD/action-install-cli@main
|
||||||
|
- name: Install dependencies
|
||||||
|
shell: bash
|
||||||
|
run: yarn
|
||||||
|
- name: Cache Playwright Browsers
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cache/ms-playwright/
|
||||||
|
key: ${{ runner.os }}-playwright-${{ hashFiles('yarn.lock') }}
|
||||||
|
- name: Install Playwright Browsers
|
||||||
|
shell: bash
|
||||||
|
run: yarn playwright install chromium --with-deps
|
||||||
|
- name: Download Wasm Cache
|
||||||
|
id: download-wasm
|
||||||
|
if: needs.check-rust-changes.outputs.rust-changed == 'false'
|
||||||
|
uses: dawidd6/action-download-artifact@v6
|
||||||
|
continue-on-error: true
|
||||||
|
with:
|
||||||
|
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||||
|
name: wasm-bundle
|
||||||
|
workflow: build-and-store-wasm.yml
|
||||||
|
branch: main
|
||||||
|
path: src/wasm-lib/pkg
|
||||||
|
- name: copy wasm blob
|
||||||
|
if: needs.check-rust-changes.outputs.rust-changed == 'false'
|
||||||
|
shell: bash
|
||||||
|
run: cp src/wasm-lib/pkg/wasm_lib_bg.wasm public
|
||||||
|
continue-on-error: true
|
||||||
|
- name: Setup Rust
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
- name: Cache Wasm (because rust diff)
|
||||||
|
if: needs.check-rust-changes.outputs.rust-changed == 'true'
|
||||||
|
uses: Swatinem/rust-cache@v2
|
||||||
|
with:
|
||||||
|
workspaces: './src/wasm-lib'
|
||||||
|
- name: OR Cache Wasm (because wasm cache failed)
|
||||||
|
if: steps.download-wasm.outcome == 'failure'
|
||||||
|
uses: Swatinem/rust-cache@v2
|
||||||
|
with:
|
||||||
|
workspaces: './src/wasm-lib'
|
||||||
|
- name: install good sed
|
||||||
|
if: ${{ startsWith(matrix.os, 'macos') }}
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
brew install gnu-sed
|
||||||
|
echo "/opt/homebrew/opt/gnu-sed/libexec/gnubin" >> $GITHUB_PATH
|
||||||
|
- name: Install vector
|
||||||
|
if: ${{ startsWith(matrix.os, 'ubuntu') }}
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
curl --proto '=https' --tlsv1.2 -sSfL https://sh.vector.dev > /tmp/vector.sh
|
||||||
|
chmod +x /tmp/vector.sh
|
||||||
|
/tmp/vector.sh -y -no-modify-path
|
||||||
|
mkdir -p /tmp/vector
|
||||||
|
cp .github/workflows/vector.toml /tmp/vector.toml
|
||||||
|
sed -i "s#GITHUB_WORKFLOW#${GITHUB_WORKFLOW}#g" /tmp/vector.toml
|
||||||
|
sed -i "s#GITHUB_REPOSITORY#${GITHUB_REPOSITORY}#g" /tmp/vector.toml
|
||||||
|
sed -i "s#GITHUB_SHA#${GITHUB_SHA}#g" /tmp/vector.toml
|
||||||
|
sed -i "s#GITHUB_REF_NAME#${GITHUB_REF_NAME}#g" /tmp/vector.toml
|
||||||
|
sed -i "s#GH_ACTIONS_AXIOM_TOKEN#${{secrets.GH_ACTIONS_AXIOM_TOKEN}}#g" /tmp/vector.toml
|
||||||
|
cat /tmp/vector.toml
|
||||||
|
${HOME}/.vector/bin/vector --config /tmp/vector.toml &
|
||||||
|
- name: Build Wasm (because rust diff)
|
||||||
|
if: needs.check-rust-changes.outputs.rust-changed == 'true'
|
||||||
|
shell: bash
|
||||||
|
run: yarn build:wasm
|
||||||
|
- name: OR Build Wasm (because wasm cache failed)
|
||||||
|
if: steps.download-wasm.outcome == 'failure'
|
||||||
|
shell: bash
|
||||||
|
run: yarn build:wasm
|
||||||
|
- name: build electron
|
||||||
|
shell: bash
|
||||||
|
run: yarn tron:package
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
if: ${{ !cancelled() && (success() || failure()) }}
|
||||||
|
continue-on-error: true
|
||||||
|
with:
|
||||||
|
name: test-results-electron-${{ matrix.os }}-${{ github.sha }}
|
||||||
|
path: test-results/
|
||||||
|
- name: Run electron tests (with retries)
|
||||||
|
id: retry
|
||||||
|
if: ${{ !cancelled() && (success() || failure()) }}
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
.github/ci-cd-scripts/playwright-electron.sh ${{ matrix.os }}
|
||||||
|
env:
|
||||||
|
CI: true
|
||||||
|
FAIL_ON_CONSOLE_ERRORS: true
|
||||||
|
NODE_ENV: development
|
||||||
|
VITE_KC_DEV_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||||
|
VITE_KC_SKIP_AUTH: true
|
||||||
|
IS_UBUNTU: ${{ startsWith(matrix.os, 'ubuntu') && 'true' || 'false' }}
|
||||||
|
#DEBUG: 'pw:browser*'
|
||||||
|
- name: send to axiom
|
||||||
|
if: ${{ !cancelled() && (success() || failure()) && !startsWith(matrix.os, 'windows') }}
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
node playwrightProcess.mjs | tee /tmp/github-actions.log
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
if: ${{ !cancelled() && (success() || failure()) }}
|
||||||
|
with:
|
||||||
|
name: test-results-electron-${{ matrix.os }}-${{ github.sha }}
|
||||||
|
path: test-results/
|
||||||
|
include-hidden-files: true
|
||||||
|
retention-days: 30
|
||||||
|
overwrite: true
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
if: ${{ !cancelled() && (success() || failure()) }}
|
||||||
|
with:
|
||||||
|
name: playwright-report-electron-${{ matrix.os }}-${{ github.sha }}
|
||||||
|
path: playwright-report/
|
||||||
|
include-hidden-files: true
|
||||||
|
retention-days: 30
|
||||||
|
overwrite: true
|
||||||
|
12
.github/workflows/publish-apps-release.yml
vendored
@ -126,13 +126,11 @@ jobs:
|
|||||||
destination: 'dl.kittycad.io/releases/modeling-app'
|
destination: 'dl.kittycad.io/releases/modeling-app'
|
||||||
|
|
||||||
- name: Invalidate bucket cache on latest*.yml and last_download.json files
|
- name: Invalidate bucket cache on latest*.yml and last_download.json files
|
||||||
run: yarn files:invalidate-bucket
|
run: |
|
||||||
|
gcloud compute url-maps invalidate-cdn-cache dl-url-map --path="/releases/modeling-app/last_download.json" --async
|
||||||
- name: Upload release files to Github
|
gcloud compute url-maps invalidate-cdn-cache dl-url-map --path="/releases/modeling-app/latest-linux-arm64.yml" --async
|
||||||
if: ${{ github.event_name == 'release' }}
|
gcloud compute url-maps invalidate-cdn-cache dl-url-map --path="/releases/modeling-app/latest-mac.yml" --async
|
||||||
uses: softprops/action-gh-release@v2
|
gcloud compute url-maps invalidate-cdn-cache dl-url-map --path="/releases/modeling-app/latest.yml" --async
|
||||||
with:
|
|
||||||
files: 'out/Zoo*'
|
|
||||||
|
|
||||||
|
|
||||||
announce_release:
|
announce_release:
|
||||||
|
1
.gitignore
vendored
@ -61,7 +61,6 @@ Mac_App_Distribution.provisionprofile
|
|||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
src/wasm-lib/pkg
|
src/wasm-lib/pkg
|
||||||
|
|
||||||
.eslintcache
|
|
||||||
venv
|
venv
|
||||||
.vite/
|
.vite/
|
||||||
|
|
||||||
|
43
INSTALL.md
@ -1,43 +0,0 @@
|
|||||||
# Setting Up Zoo Modeling App
|
|
||||||
|
|
||||||
Compared to other CAD software, getting Zoo Modeling App up and running is quick and straightforward across platforms. It's about 100MB to download and is quick to install.
|
|
||||||
|
|
||||||
## Windows
|
|
||||||
|
|
||||||
1. Download the [Zoo Modeling App installer](https://zoo.dev/modeling-app/download) for Windows and for your processor type.
|
|
||||||
|
|
||||||
2. Once downloaded, run the installer `Zoo Modeling App-{version}-{arch}-win.exe` which should take a few seconds.
|
|
||||||
|
|
||||||
3. The installation happens at `C:\Program Files\Zoo Modeling App`. A shortcut in the start menu is also created so you can run the app easily by clicking on it.
|
|
||||||
|
|
||||||
## macOS
|
|
||||||
|
|
||||||
1. Download the [Zoo Modeling App installer](https://zoo.dev/modeling-app/download) for macOS and for your processor type.
|
|
||||||
|
|
||||||
2. Once downloaded, open the disk image `Zoo Modeling App-{version}-{arch}-mac.dmg` and drag the applications to your `Applications` directory.
|
|
||||||
|
|
||||||
3. You can then open your `Applications` directory and double-click on `Zoo Modeling App` to open.
|
|
||||||
|
|
||||||
|
|
||||||
## Linux
|
|
||||||
|
|
||||||
1. Download the [Zoo Modeling App installer](https://zoo.dev/modeling-app/download) for Linux and for your processor type.
|
|
||||||
|
|
||||||
2. Install the dependencies needed to run the [AppImage format](https://appimage.org/).
|
|
||||||
- On Ubuntu, install the FUSE library with these commands in a terminal.
|
|
||||||
```bash
|
|
||||||
sudo apt update
|
|
||||||
sudo apt install libfuse2
|
|
||||||
```
|
|
||||||
- Optionally, follow [these steps](https://github.com/probonopd/go-appimage/blob/master/src/appimaged/README.md#initial-setup) to install `appimaged`. It is a daemon that makes interacting with AppImage files more seamless.
|
|
||||||
- Once installed, copy the downloaded `Zoo Modeling App-{version}-{arch}-linux.AppImage` to the directory of your choice, for instance `~/Applications`.
|
|
||||||
|
|
||||||
- `appimaged` should automatically find it and make it executable. If not, run:
|
|
||||||
```bash
|
|
||||||
chmod a+x ~/Applications/Zoo\ Modeling\ App-{version}-{arch}-linux.AppImage
|
|
||||||
```
|
|
||||||
|
|
||||||
3. You can double-click on the AppImage to run it, or in a terminal with this command:
|
|
||||||
```bash
|
|
||||||
~/Applications/Zoo\ Modeling\ App-{version}-{arch}-linux.AppImage
|
|
||||||
```
|
|
39
README.md
@ -99,7 +99,7 @@ yarn tron:start
|
|||||||
|
|
||||||
This will start the application and hot-reload on changes.
|
This will start the application and hot-reload on changes.
|
||||||
|
|
||||||
Devtools can be opened with the usual Cmd-Opt-I (Mac) or Ctrl-Shift-I (Linux and Windows).
|
Devtools can be opened with the usual Cmd/Ctrl-Shift-I.
|
||||||
|
|
||||||
To build, run `yarn tron:package`.
|
To build, run `yarn tron:package`.
|
||||||
|
|
||||||
@ -136,7 +136,7 @@ https://github.com/KittyCAD/modeling-app/issues/new
|
|||||||
|
|
||||||
#### 2. Push a new tag
|
#### 2. Push a new tag
|
||||||
|
|
||||||
Create a new tag and push it to the repo. The `semantic-release.sh` script will automatically bump the minor part, which we use the most. For instance going from `v0.27.0` to `v0.28.0`.
|
Create a new tag and push it to the repo (eg. `v0.28.0` for `$VERSION`)
|
||||||
|
|
||||||
```
|
```
|
||||||
VERSION=$(./scripts/semantic-release.sh)
|
VERSION=$(./scripts/semantic-release.sh)
|
||||||
@ -146,14 +146,16 @@ git push origin --tags
|
|||||||
|
|
||||||
This will trigger the `build-apps` workflow, set the version, build & sign the apps, and generate release files as well as updater-test artifacts.
|
This will trigger the `build-apps` workflow, set the version, build & sign the apps, and generate release files as well as updater-test artifacts.
|
||||||
|
|
||||||
The workflow should be listed right away [in this list](https://github.com/KittyCAD/modeling-app/actions/workflows/build-apps.yml?query=event%3Apush)).
|
Once the workflow succeeds, a draft release will be created at https://github.com/KittyCAD/modeling-app/releases.
|
||||||
|
|
||||||
#### 3. Manually test artifacts
|
#### 3. Manually test artifacts from the Cut Release PR
|
||||||
|
|
||||||
##### Release builds
|
##### Release builds
|
||||||
|
|
||||||
The release builds can be found under the `out-{arch}-{platform}` zip files, at the very bottom of the `build-apps` summary page for the workflow (triggered by the tag in 2.).
|
The release builds can be found under the `out-{arch}-{platform}` zip files, at the very bottom of the `build-apps` summary page for the workflow (triggered by the tag in 2.).
|
||||||
|
|
||||||
|
Alternatively, the draft release will also include these builds.
|
||||||
|
|
||||||
Manually test against this [list](https://github.com/KittyCAD/modeling-app/issues/3588) across Windows, MacOS, Linux and posting results as comments in the issue.
|
Manually test against this [list](https://github.com/KittyCAD/modeling-app/issues/3588) across Windows, MacOS, Linux and posting results as comments in the issue.
|
||||||
|
|
||||||
##### Updater-test builds
|
##### Updater-test builds
|
||||||
@ -176,11 +178,9 @@ If the prompt doesn't show up, start the app in command line to grab the electro
|
|||||||
|
|
||||||
#### 4. Publish the release
|
#### 4. Publish the release
|
||||||
|
|
||||||
Head over to https://github.com/KittyCAD/modeling-app/releases/new, pick the newly created tag and type it in the _Release title_ field as well.
|
Head over to https://github.com/KittyCAD/modeling-app/releases, paste in the changelog discussed in the issue, and publish the draft release created by the `build-apps` workflow from step 2.
|
||||||
|
|
||||||
Hit _Generate release notes_ as a starting point to discuss the changelog in the issue. Once done, make sure _Set as the latest release_ is checked, and hit _Publish release_.
|
A new Action kicks in at https://github.com/KittyCAD/modeling-app/actions, which can be found under `release` event filter. On success, the files will be uploaded to the public bucket and the announcement on Discord will be sent.
|
||||||
|
|
||||||
A new `publish-apps-release` will kick in and you should be able to find it [here](https://github.com/KittyCAD/modeling-app/actions?query=event%3Arelease). On success, the files will be uploaded to the public bucket as well as to the GitHub release, and the announcement on Discord will be sent.
|
|
||||||
|
|
||||||
#### 5. Close the issue
|
#### 5. Close the issue
|
||||||
|
|
||||||
@ -388,6 +388,23 @@ yarn test:unit:local
|
|||||||
|
|
||||||
#### E2E Tests
|
#### E2E Tests
|
||||||
|
|
||||||
|
**Playwright Browser**
|
||||||
|
|
||||||
|
These E2E tests run in a browser (without electron).
|
||||||
|
There are tests that are skipped if they are ran in a windows OS or Linux OS. We can use playwright tags to implement test skipping.
|
||||||
|
|
||||||
|
Breaking down the command `yarn test:playwright:browser:chrome:windows`
|
||||||
|
- The application is `playwright`
|
||||||
|
- The runtime is a `browser`
|
||||||
|
- The specific `browser` is `chrome`
|
||||||
|
- The test should run in a `windows` environment. It will skip tests that are broken or flaky in the windows OS.
|
||||||
|
|
||||||
|
```
|
||||||
|
yarn test:playwright:browser:chrome
|
||||||
|
yarn test:playwright:browser:chrome:windows
|
||||||
|
yarn test:playwright:browser:chrome:ubuntu
|
||||||
|
```
|
||||||
|
|
||||||
**Playwright Electron**
|
**Playwright Electron**
|
||||||
|
|
||||||
These E2E tests run in electron. There are tests that are skipped if they are ran in a windows, linux, or macos environment. We can use playwright tags to implement test skipping.
|
These E2E tests run in electron. There are tests that are skipped if they are ran in a windows, linux, or macos environment. We can use playwright tags to implement test skipping.
|
||||||
@ -433,9 +450,3 @@ PS: for the debug panel, the following JSON is useful for snapping the camera
|
|||||||
## KCL
|
## KCL
|
||||||
|
|
||||||
For how to contribute to KCL, [see our KCL README](https://github.com/KittyCAD/modeling-app/tree/main/src/wasm-lib/kcl).
|
For how to contribute to KCL, [see our KCL README](https://github.com/KittyCAD/modeling-app/tree/main/src/wasm-lib/kcl).
|
||||||
|
|
||||||
### Logging
|
|
||||||
|
|
||||||
To display logging (to the terminal or console) set `ZOO_LOG=1`. This will log some warnings and simple performance metrics. To view these in test runs, use `-- --nocapture`.
|
|
||||||
|
|
||||||
To enable memory metrics, build with `--features dhat-heap`.
|
|
||||||
|
Before Width: | Height: | Size: 119 KiB After Width: | Height: | Size: 176 KiB |
Before Width: | Height: | Size: 259 KiB After Width: | Height: | Size: 157 KiB |
BIN
assets/icon.ico
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 183 KiB |
BIN
assets/icon.png
Before Width: | Height: | Size: 113 KiB After Width: | Height: | Size: 113 KiB |
@ -22,5 +22,3 @@ once fixed in engine will just start working here with no language changes.
|
|||||||
|
|
||||||
- **Chamfers**: Chamfers cannot intersect, you will get an error. Only simple
|
- **Chamfers**: Chamfers cannot intersect, you will get an error. Only simple
|
||||||
chamfer cases work currently.
|
chamfer cases work currently.
|
||||||
|
|
||||||
- **Appearance**: Changing the appearance on a loft does not work.
|
|
||||||
|
@ -58,7 +58,7 @@ mountingPlate = extrude(thickness, mountingPlateSketch)
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
// Sketch on the face of a chamfer.
|
// Sketch on the face of a chamfer.
|
||||||
fn cube(pos, scale) {
|
fn cube = (pos, scale) => {
|
||||||
sg = startSketchOn('XY')
|
sg = startSketchOn('XY')
|
||||||
|> startProfileAt(pos, %)
|
|> startProfileAt(pos, %)
|
||||||
|> line([0, scale], %)
|
|> line([0, scale], %)
|
||||||
|
@ -19,7 +19,6 @@ layout: manual
|
|||||||
* [`angledLineThatIntersects`](kcl/angledLineThatIntersects)
|
* [`angledLineThatIntersects`](kcl/angledLineThatIntersects)
|
||||||
* [`angledLineToX`](kcl/angledLineToX)
|
* [`angledLineToX`](kcl/angledLineToX)
|
||||||
* [`angledLineToY`](kcl/angledLineToY)
|
* [`angledLineToY`](kcl/angledLineToY)
|
||||||
* [`appearance`](kcl/appearance)
|
|
||||||
* [`arc`](kcl/arc)
|
* [`arc`](kcl/arc)
|
||||||
* [`arcTo`](kcl/arcTo)
|
* [`arcTo`](kcl/arcTo)
|
||||||
* [`asin`](kcl/asin)
|
* [`asin`](kcl/asin)
|
||||||
|
@ -37,7 +37,7 @@ assertEqual(n, 3, 0.0001, "5/2 = 2.5, rounded up makes 3")
|
|||||||
startSketchOn('XZ')
|
startSketchOn('XZ')
|
||||||
|> circle({ center = [0, 0], radius = 2 }, %)
|
|> circle({ center = [0, 0], radius = 2 }, %)
|
||||||
|> extrude(5, %)
|
|> extrude(5, %)
|
||||||
|> patternTransform(n, fn(id) {
|
|> patternTransform(n, (id) => {
|
||||||
return { translate = [4 * id, 0, 0] }
|
return { translate = [4 * id, 0, 0] }
|
||||||
}, %)
|
}, %)
|
||||||
```
|
```
|
||||||
|
@ -29,7 +29,7 @@ map(array: [KclValue], map_fn: FunctionParam) -> [KclValue]
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
r = 10 // radius
|
r = 10 // radius
|
||||||
fn drawCircle(id) {
|
fn drawCircle = (id) => {
|
||||||
return startSketchOn("XY")
|
return startSketchOn("XY")
|
||||||
|> circle({ center = [id * 2 * r, 0], radius = r }, %)
|
|> circle({ center = [id * 2 * r, 0], radius = r }, %)
|
||||||
}
|
}
|
||||||
@ -45,7 +45,7 @@ circles = map([1..3], drawCircle)
|
|||||||
```js
|
```js
|
||||||
r = 10 // radius
|
r = 10 // radius
|
||||||
// Call `map`, using an anonymous function instead of a named one.
|
// Call `map`, using an anonymous function instead of a named one.
|
||||||
circles = map([1..3], fn(id) {
|
circles = map([1..3], (id) => {
|
||||||
return startSketchOn("XY")
|
return startSketchOn("XY")
|
||||||
|> circle({ center = [id * 2 * r, 0], radius = r }, %)
|
|> circle({ center = [id * 2 * r, 0], radius = r }, %)
|
||||||
})
|
})
|
||||||
|
@ -12,7 +12,7 @@ to other modules.
|
|||||||
|
|
||||||
```
|
```
|
||||||
// util.kcl
|
// util.kcl
|
||||||
export fn increment(x) {
|
export fn increment = (x) => {
|
||||||
return x + 1
|
return x + 1
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -37,11 +37,11 @@ Multiple functions can be exported in a file.
|
|||||||
|
|
||||||
```
|
```
|
||||||
// util.kcl
|
// util.kcl
|
||||||
export fn increment(x) {
|
export fn increment = (x) => {
|
||||||
return x + 1
|
return x + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
export fn decrement(x) {
|
export fn decrement = (x) => {
|
||||||
return x - 1
|
return x - 1
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -30,7 +30,7 @@ patternTransform2d(total_instances: u32, transform_function: FunctionParam, soli
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
// Each instance will be shifted along the X axis.
|
// Each instance will be shifted along the X axis.
|
||||||
fn transform(id) {
|
fn transform = (id) => {
|
||||||
return { translate = [4 * id, 0] }
|
return { translate = [4 * id, 0] }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,14 +30,14 @@ reduce(array: [KclValue], start: KclValue, reduce_fn: FunctionParam) -> KclValue
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
// This function adds two numbers.
|
// This function adds two numbers.
|
||||||
fn add(a, b) {
|
fn add = (a, b) => {
|
||||||
return a + b
|
return a + b
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function adds an array of numbers.
|
// This function adds an array of numbers.
|
||||||
// It uses the `reduce` function, to call the `add` function on every
|
// It uses the `reduce` function, to call the `add` function on every
|
||||||
// element of the `arr` parameter. The starting value is 0.
|
// element of the `arr` parameter. The starting value is 0.
|
||||||
fn sum(arr) {
|
fn sum = (arr) => {
|
||||||
return reduce(arr, 0, add)
|
return reduce(arr, 0, add)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,7 +61,7 @@ assertEqual(sum([1, 2, 3]), 6, 0.00001, "1 + 2 + 3 summed is 6")
|
|||||||
// an anonymous `add` function as its parameter, instead of declaring a
|
// an anonymous `add` function as its parameter, instead of declaring a
|
||||||
// named function outside.
|
// named function outside.
|
||||||
arr = [1, 2, 3]
|
arr = [1, 2, 3]
|
||||||
sum = reduce(arr, 0, fn(i, result_so_far) {
|
sum = reduce(arr, 0, (i, result_so_far) => {
|
||||||
return i + result_so_far
|
return i + result_so_far
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -74,7 +74,7 @@ assertEqual(sum, 6, 0.00001, "1 + 2 + 3 summed is 6")
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
// Declare a function that sketches a decagon.
|
// Declare a function that sketches a decagon.
|
||||||
fn decagon(radius) {
|
fn decagon = (radius) => {
|
||||||
// Each side of the decagon is turned this many degrees from the previous angle.
|
// Each side of the decagon is turned this many degrees from the previous angle.
|
||||||
stepAngle = 1 / 10 * tau()
|
stepAngle = 1 / 10 * tau()
|
||||||
|
|
||||||
@ -84,7 +84,7 @@ fn decagon(radius) {
|
|||||||
// Use a `reduce` to draw the remaining decagon sides.
|
// Use a `reduce` to draw the remaining decagon sides.
|
||||||
// For each number in the array 1..10, run the given function,
|
// For each number in the array 1..10, run the given function,
|
||||||
// which takes a partially-sketched decagon and adds one more edge to it.
|
// which takes a partially-sketched decagon and adds one more edge to it.
|
||||||
fullDecagon = reduce([1..10], startOfDecagonSketch, fn(i, partialDecagon) {
|
fullDecagon = reduce([1..10], startOfDecagonSketch, (i, partialDecagon) => {
|
||||||
// Draw one edge of the decagon.
|
// Draw one edge of the decagon.
|
||||||
x = cos(stepAngle * i) * radius
|
x = cos(stepAngle * i) * radius
|
||||||
y = sin(stepAngle * i) * radius
|
y = sin(stepAngle * i) * radius
|
||||||
|
@ -36,7 +36,7 @@ cube = startSketchAt([0, 0])
|
|||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(5, %)
|
|> extrude(5, %)
|
||||||
|
|
||||||
fn cylinder(radius, tag) {
|
fn cylinder = (radius, tag) => {
|
||||||
return startSketchAt([0, 0])
|
return startSketchAt([0, 0])
|
||||||
|> circle({
|
|> circle({
|
||||||
radius = radius,
|
radius = radius,
|
||||||
|
@ -36,7 +36,7 @@ cube = startSketchAt([0, 0])
|
|||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(5, %)
|
|> extrude(5, %)
|
||||||
|
|
||||||
fn cylinder(radius, tag) {
|
fn cylinder = (radius, tag) => {
|
||||||
return startSketchAt([0, 0])
|
return startSketchAt([0, 0])
|
||||||
|> circle({
|
|> circle({
|
||||||
radius = radius,
|
radius = radius,
|
||||||
|
23901
docs/kcl/std.json
@ -41,7 +41,7 @@ If you want to get a value from an array you can use the index like so:
|
|||||||
An object is defined with `{}` braces. Here is an example object:
|
An object is defined with `{}` braces. Here is an example object:
|
||||||
|
|
||||||
```
|
```
|
||||||
myObj = { a = 0, b = "thing" }
|
myObj = {a: 0, b: "thing"}
|
||||||
```
|
```
|
||||||
|
|
||||||
We support two different ways of getting properties from objects, you can call
|
We support two different ways of getting properties from objects, you can call
|
||||||
@ -54,7 +54,7 @@ We also have support for defining your own functions. Functions can take in any
|
|||||||
type of argument. Below is an example of the syntax:
|
type of argument. Below is an example of the syntax:
|
||||||
|
|
||||||
```
|
```
|
||||||
fn myFn(x) {
|
fn myFn = (x) => {
|
||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -90,12 +90,12 @@ startSketchOn('XZ')
|
|||||||
|> startProfileAt(origin, %)
|
|> startProfileAt(origin, %)
|
||||||
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|
||||||
|> angledLine([
|
|> angledLine([
|
||||||
segAng(rectangleSegmentA001) - 90,
|
segAng(rectangleSegmentA001, %) - 90,
|
||||||
196.99
|
196.99
|
||||||
], %, $rectangleSegmentB001)
|
], %, $rectangleSegmentB001)
|
||||||
|> angledLine([
|
|> angledLine([
|
||||||
segAng(rectangleSegmentA001),
|
segAng(rectangleSegmentA001, %),
|
||||||
-segLen(rectangleSegmentA001)
|
-segLen(rectangleSegmentA001, %)
|
||||||
], %, $rectangleSegmentC001)
|
], %, $rectangleSegmentC001)
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
@ -118,17 +118,17 @@ use the tag `rectangleSegmentA001` in any function or expression in the file.
|
|||||||
However if the code was written like this:
|
However if the code was written like this:
|
||||||
|
|
||||||
```
|
```
|
||||||
fn rect(origin) {
|
fn rect = (origin) => {
|
||||||
return startSketchOn('XZ')
|
return startSketchOn('XZ')
|
||||||
|> startProfileAt(origin, %)
|
|> startProfileAt(origin, %)
|
||||||
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|
||||||
|> angledLine([
|
|> angledLine([
|
||||||
segAng(rectangleSegmentA001) - 90,
|
segAng(rectangleSegmentA001, %) - 90,
|
||||||
196.99
|
196.99
|
||||||
], %, $rectangleSegmentB001)
|
], %, $rectangleSegmentB001)
|
||||||
|> angledLine([
|
|> angledLine([
|
||||||
segAng(rectangleSegmentA001),
|
segAng(rectangleSegmentA001, %),
|
||||||
-segLen(rectangleSegmentA001)
|
-segLen(rectangleSegmentA001, %)
|
||||||
], %, $rectangleSegmentC001)
|
], %, $rectangleSegmentC001)
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
@ -146,17 +146,17 @@ Tags are accessible through the sketch group they are declared in.
|
|||||||
For example the following code works.
|
For example the following code works.
|
||||||
|
|
||||||
```
|
```
|
||||||
fn rect(origin) {
|
fn rect = (origin) => {
|
||||||
return startSketchOn('XZ')
|
return startSketchOn('XZ')
|
||||||
|> startProfileAt(origin, %)
|
|> startProfileAt(origin, %)
|
||||||
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|
||||||
|> angledLine([
|
|> angledLine([
|
||||||
segAng(rectangleSegmentA001) - 90,
|
segAng(rectangleSegmentA001, %) - 90,
|
||||||
196.99
|
196.99
|
||||||
], %, $rectangleSegmentB001)
|
], %, $rectangleSegmentB001)
|
||||||
|> angledLine([
|
|> angledLine([
|
||||||
segAng(rectangleSegmentA001),
|
segAng(rectangleSegmentA001, %),
|
||||||
-segLen(rectangleSegmentA001)
|
-segLen(rectangleSegmentA001, %)
|
||||||
], %, $rectangleSegmentC001)
|
], %, $rectangleSegmentC001)
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
@ -167,10 +167,7 @@ myRect = rect([20, 0])
|
|||||||
|
|
||||||
myRect
|
myRect
|
||||||
|> extrude(10, %)
|
|> extrude(10, %)
|
||||||
|> fillet({
|
|> fillet({radius: 0.5, tags: [myRect.tags.rectangleSegmentA001]}, %)
|
||||||
radius = 0.5,
|
|
||||||
tags = [myRect.tags.rectangleSegmentA001]
|
|
||||||
}, %)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
See how we use the tag `rectangleSegmentA001` in the `fillet` function outside
|
See how we use the tag `rectangleSegmentA001` in the `fillet` function outside
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
---
|
|
||||||
title: "AppearanceData"
|
|
||||||
excerpt: "Data for appearance."
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
Data for appearance.
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `color` |`string`| Color of the new material, a hex string like "#ff0000". | No |
|
|
||||||
| `metalness` |`number` (**maximum:** 100.0)| Metalness of the new material, a percentage like 95.7. | No |
|
|
||||||
| `roughness` |`number` (**maximum:** 100.0)| Roughness of the new material, a percentage like 95.7. | No |
|
|
||||||
|
|
||||||
|
|
161
docs/kcl/types/BinaryOperator.md
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
---
|
||||||
|
title: "BinaryOperator"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**This schema accepts exactly one of the following:**
|
||||||
|
|
||||||
|
Add two numbers.
|
||||||
|
|
||||||
|
**enum:** `+`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Subtract two numbers.
|
||||||
|
|
||||||
|
**enum:** `-`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Multiply two numbers.
|
||||||
|
|
||||||
|
**enum:** `*`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Divide two numbers.
|
||||||
|
|
||||||
|
**enum:** `/`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Modulo two numbers.
|
||||||
|
|
||||||
|
**enum:** `%`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Raise a number to a power.
|
||||||
|
|
||||||
|
**enum:** `^`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Are two numbers equal?
|
||||||
|
|
||||||
|
**enum:** `==`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Are two numbers not equal?
|
||||||
|
|
||||||
|
**enum:** `!=`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Is left greater than right
|
||||||
|
|
||||||
|
**enum:** `>`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Is left greater than or equal to right
|
||||||
|
|
||||||
|
**enum:** `>=`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Is left less than right
|
||||||
|
|
||||||
|
**enum:** `<`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Is left less than or equal to right
|
||||||
|
|
||||||
|
**enum:** `<=`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
161
docs/kcl/types/BinaryPart.md
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
---
|
||||||
|
title: "BinaryPart"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**This schema accepts exactly one of the following:**
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `Literal`| | No |
|
||||||
|
| `value` |[`LiteralValue`](/docs/kcl/types/LiteralValue)| | No |
|
||||||
|
| `raw` |`string`| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: [`Identifier`](/docs/kcl/types/Identifier)| | No |
|
||||||
|
| `name` |`string`| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `BinaryExpression`| | No |
|
||||||
|
| `operator` |[`BinaryOperator`](/docs/kcl/types/BinaryOperator)| | No |
|
||||||
|
| `left` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| | No |
|
||||||
|
| `right` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `CallExpression`| | No |
|
||||||
|
| `callee` |[`Identifier`](/docs/kcl/types/Identifier)| | No |
|
||||||
|
| `arguments` |`[` [`Expr`](/docs/kcl/types/Expr) `]`| | No |
|
||||||
|
| `optional` |`boolean`| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `UnaryExpression`| | No |
|
||||||
|
| `operator` |[`UnaryOperator`](/docs/kcl/types/UnaryOperator)| | No |
|
||||||
|
| `argument` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `MemberExpression`| | No |
|
||||||
|
| `object` |[`MemberObject`](/docs/kcl/types/MemberObject)| | No |
|
||||||
|
| `property` |[`LiteralIdentifier`](/docs/kcl/types/LiteralIdentifier)| | No |
|
||||||
|
| `computed` |`boolean`| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `IfExpression`| | No |
|
||||||
|
| `cond` |[`Expr`](/docs/kcl/types/Expr)| | No |
|
||||||
|
| `then_val` |[`Program`](/docs/kcl/types/Program)| | No |
|
||||||
|
| `else_ifs` |`[` [`ElseIf`](/docs/kcl/types/ElseIf) `]`| | No |
|
||||||
|
| `final_else` |[`Program`](/docs/kcl/types/Program)| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
97
docs/kcl/types/BodyItem.md
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
---
|
||||||
|
title: "BodyItem"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**This schema accepts exactly one of the following:**
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `ImportStatement`| | No |
|
||||||
|
| `items` |`[` [`ImportItem`](/docs/kcl/types/ImportItem) `]`| | No |
|
||||||
|
| `path` |`string`| | No |
|
||||||
|
| `raw_path` |`string`| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `ExpressionStatement`| | No |
|
||||||
|
| `expression` |[`Expr`](/docs/kcl/types/Expr)| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `VariableDeclaration`| | No |
|
||||||
|
| `declarations` |`[` [`VariableDeclarator`](/docs/kcl/types/VariableDeclarator) `]`| | No |
|
||||||
|
| `visibility` |[`ItemVisibility`](/docs/kcl/types/ItemVisibility)| | No |
|
||||||
|
| `kind` |[`VariableKind`](/docs/kcl/types/VariableKind)| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `ReturnStatement`| | No |
|
||||||
|
| `argument` |[`Expr`](/docs/kcl/types/Expr)| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
41
docs/kcl/types/CommentStyle.md
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
title: "CommentStyle"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**This schema accepts exactly one of the following:**
|
||||||
|
|
||||||
|
Like // foo
|
||||||
|
|
||||||
|
**enum:** `line`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Like /* foo */
|
||||||
|
|
||||||
|
**enum:** `block`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
24
docs/kcl/types/ElseIf.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
title: "ElseIf"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `cond` |[`Expr`](/docs/kcl/types/Expr)| | No |
|
||||||
|
| `then_val` |[`Program`](/docs/kcl/types/Program)| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
16
docs/kcl/types/EnvironmentRef.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
title: "EnvironmentRef"
|
||||||
|
excerpt: "An index pointing to an environment."
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
An index pointing to an environment.
|
||||||
|
|
||||||
|
**Type:** `integer` (`uint`)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
318
docs/kcl/types/Expr.md
Normal file
@ -0,0 +1,318 @@
|
|||||||
|
---
|
||||||
|
title: "Expr"
|
||||||
|
excerpt: "An expression can be evaluated to yield a single KCL value."
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
An expression can be evaluated to yield a single KCL value.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**This schema accepts exactly one of the following:**
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `Literal`| | No |
|
||||||
|
| `value` |[`LiteralValue`](/docs/kcl/types/LiteralValue)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
|
| `raw` |`string`| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: [`Identifier`](/docs/kcl/types/Identifier)| | No |
|
||||||
|
| `name` |`string`| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: [`TagDeclarator`](/docs/kcl/types#tag-declaration)| | No |
|
||||||
|
| `value` |`string`| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `BinaryExpression`| | No |
|
||||||
|
| `operator` |[`BinaryOperator`](/docs/kcl/types/BinaryOperator)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
|
| `left` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
|
| `right` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: [`FunctionExpression`](/docs/kcl/types/FunctionExpression)| | No |
|
||||||
|
| `params` |`[` [`Parameter`](/docs/kcl/types/Parameter) `]`| | No |
|
||||||
|
| `body` |[`Program`](/docs/kcl/types/Program)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `CallExpression`| | No |
|
||||||
|
| `callee` |[`Identifier`](/docs/kcl/types/Identifier)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
|
| `arguments` |`[` [`Expr`](/docs/kcl/types/Expr) `]`| | No |
|
||||||
|
| `optional` |`boolean`| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `PipeExpression`| | No |
|
||||||
|
| `body` |`[` [`Expr`](/docs/kcl/types/Expr) `]`| | No |
|
||||||
|
| `nonCodeMeta` |[`NonCodeMeta`](/docs/kcl/types/NonCodeMeta)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `PipeSubstitution`| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `ArrayExpression`| | No |
|
||||||
|
| `elements` |`[` [`Expr`](/docs/kcl/types/Expr) `]`| | No |
|
||||||
|
| `nonCodeMeta` |[`NonCodeMeta`](/docs/kcl/types/NonCodeMeta)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `ArrayRangeExpression`| | No |
|
||||||
|
| `startElement` |[`Expr`](/docs/kcl/types/Expr)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
|
| `endElement` |[`Expr`](/docs/kcl/types/Expr)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
|
| `endInclusive` |`boolean`| Is the `end_element` included in the range? | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `ObjectExpression`| | No |
|
||||||
|
| `properties` |`[` [`ObjectProperty`](/docs/kcl/types/ObjectProperty) `]`| | No |
|
||||||
|
| `nonCodeMeta` |[`NonCodeMeta`](/docs/kcl/types/NonCodeMeta)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `MemberExpression`| | No |
|
||||||
|
| `object` |[`MemberObject`](/docs/kcl/types/MemberObject)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
|
| `property` |[`LiteralIdentifier`](/docs/kcl/types/LiteralIdentifier)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
|
| `computed` |`boolean`| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `UnaryExpression`| | No |
|
||||||
|
| `operator` |[`UnaryOperator`](/docs/kcl/types/UnaryOperator)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
|
| `argument` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `IfExpression`| | No |
|
||||||
|
| `cond` |[`Expr`](/docs/kcl/types/Expr)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
|
| `then_val` |[`Program`](/docs/kcl/types/Program)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
|
| `else_ifs` |`[` [`ElseIf`](/docs/kcl/types/ElseIf) `]`| | No |
|
||||||
|
| `final_else` |[`Program`](/docs/kcl/types/Program)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
KCL value for an optional parameter which was not given an argument. (remember, parameters are in the function declaration, arguments are in the function call/application).
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `None`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
24
docs/kcl/types/FunctionExpression.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
title: "FunctionExpression"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `params` |`[` [`Parameter`](/docs/kcl/types/Parameter) `]`| | No |
|
||||||
|
| `body` |[`Program`](/docs/kcl/types/Program)| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
23
docs/kcl/types/Identifier.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
title: "Identifier"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `name` |`string`| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
24
docs/kcl/types/ImportItem.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
title: "ImportItem"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `name` |[`Identifier`](/docs/kcl/types/Identifier)| Name of the item to import. | No |
|
||||||
|
| `alias` |[`Identifier`](/docs/kcl/types/Identifier)| Rename the item using an identifier after "as". | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
16
docs/kcl/types/ItemVisibility.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
title: "ItemVisibility"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**enum:** `default`, `export`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -317,6 +317,7 @@ Data for an imported geometry.
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: `Function`| | No |
|
| `type` |enum: `Function`| | No |
|
||||||
|
| `expression` |[`FunctionExpression`](/docs/kcl/types/FunctionExpression)| Any KCL value. | No |
|
||||||
| `memory` |[`ProgramMemory`](/docs/kcl/types/ProgramMemory)| Any KCL value. | No |
|
| `memory` |[`ProgramMemory`](/docs/kcl/types/ProgramMemory)| Any KCL value. | No |
|
||||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||||
|
|
||||||
|
56
docs/kcl/types/LiteralIdentifier.md
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
---
|
||||||
|
title: "LiteralIdentifier"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**This schema accepts exactly one of the following:**
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: [`Identifier`](/docs/kcl/types/Identifier)| | No |
|
||||||
|
| `name` |`string`| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `Literal`| | No |
|
||||||
|
| `value` |[`LiteralValue`](/docs/kcl/types/LiteralValue)| | No |
|
||||||
|
| `raw` |`string`| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
47
docs/kcl/types/LiteralValue.md
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
---
|
||||||
|
title: "LiteralValue"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**This schema accepts any of the following:**
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `number` (`double`)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `string`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `boolean`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
57
docs/kcl/types/MemberObject.md
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
---
|
||||||
|
title: "MemberObject"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**This schema accepts exactly one of the following:**
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `MemberExpression`| | No |
|
||||||
|
| `object` |[`MemberObject`](/docs/kcl/types/MemberObject)| | No |
|
||||||
|
| `property` |[`LiteralIdentifier`](/docs/kcl/types/LiteralIdentifier)| | No |
|
||||||
|
| `computed` |`boolean`| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: [`Identifier`](/docs/kcl/types/Identifier)| | No |
|
||||||
|
| `name` |`string`| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
22
docs/kcl/types/NonCodeMeta.md
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
title: "NonCodeMeta"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `nonCodeNodes` |`object`| | No |
|
||||||
|
| `startNodes` |`[` [`NonCodeNode`](/docs/kcl/types/NonCodeNode) `]`| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
|
||||||
|
|
23
docs/kcl/types/NonCodeNode.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
title: "NonCodeNode"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `value` |[`NonCodeValue`](/docs/kcl/types/NonCodeValue)| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
86
docs/kcl/types/NonCodeValue.md
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
---
|
||||||
|
title: "NonCodeValue"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**This schema accepts exactly one of the following:**
|
||||||
|
|
||||||
|
An inline comment. Here are examples: `1 + 1 // This is an inline comment`. `1 + 1 /* Here's another */`.
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `inlineComment`| | No |
|
||||||
|
| `value` |`string`| | No |
|
||||||
|
| `style` |[`CommentStyle`](/docs/kcl/types/CommentStyle)| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
A block comment. An example of this is the following: ```python,no_run /* This is a block comment */ 1 + 1 ``` Now this is important. The block comment is attached to the next line. This is always the case. Also the block comment doesn't have a new line above it. If it did it would be a `NewLineBlockComment`.
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `blockComment`| | No |
|
||||||
|
| `value` |`string`| | No |
|
||||||
|
| `style` |[`CommentStyle`](/docs/kcl/types/CommentStyle)| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
A block comment that has a new line above it. The user explicitly added a new line above the block comment.
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `newLineBlockComment`| | No |
|
||||||
|
| `value` |`string`| | No |
|
||||||
|
| `style` |[`CommentStyle`](/docs/kcl/types/CommentStyle)| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `newLine`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
24
docs/kcl/types/ObjectProperty.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
title: "ObjectProperty"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `key` |[`Identifier`](/docs/kcl/types/Identifier)| | No |
|
||||||
|
| `value` |[`Expr`](/docs/kcl/types/Expr)| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
23
docs/kcl/types/Parameter.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
title: "Parameter"
|
||||||
|
excerpt: "Parameter of a KCL function."
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
Parameter of a KCL function.
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `identifier` |[`Identifier`](/docs/kcl/types/Identifier)| The parameter's label or name. | No |
|
||||||
|
| `optional` |`boolean`| Is the parameter optional? | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
|
||||||
|
|
26
docs/kcl/types/Program.md
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
title: "Program"
|
||||||
|
excerpt: "A KCL program top level, or function body."
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
A KCL program top level, or function body.
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `body` |`[` [`BodyItem`](/docs/kcl/types/BodyItem) `]`| | No |
|
||||||
|
| `nonCodeMeta` |[`NonCodeMeta`](/docs/kcl/types/NonCodeMeta)| A KCL program top level, or function body. | No |
|
||||||
|
| `shebang` |[`Shebang`](/docs/kcl/types/Shebang)| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
23
docs/kcl/types/Shebang.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
title: "Shebang"
|
||||||
|
excerpt: "A shebang. This is a special type of comment that is at the top of the file. It looks like this: ```python,no_run #!/usr/bin/env python ```"
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
A shebang. This is a special type of comment that is at the top of the file. It looks like this: ```python,no_run #!/usr/bin/env python ```
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `content` |`string`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
15
docs/kcl/types/Uint.md
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
title: "Uint"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `integer` (`uint32`)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
41
docs/kcl/types/UnaryOperator.md
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
title: "UnaryOperator"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**This schema accepts exactly one of the following:**
|
||||||
|
|
||||||
|
Negate a number.
|
||||||
|
|
||||||
|
**enum:** `-`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Negate a boolean.
|
||||||
|
|
||||||
|
**enum:** `!`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
24
docs/kcl/types/VariableDeclarator.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
title: "VariableDeclarator"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `id` |[`Identifier`](/docs/kcl/types/Identifier)| The identifier of the variable. | No |
|
||||||
|
| `init` |[`Expr`](/docs/kcl/types/Expr)| The value of the variable. | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
41
docs/kcl/types/VariableKind.md
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
title: "VariableKind"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**This schema accepts exactly one of the following:**
|
||||||
|
|
||||||
|
Declare a named constant.
|
||||||
|
|
||||||
|
**enum:** `const`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Declare a function.
|
||||||
|
|
||||||
|
**enum:** `fn`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,11 +1,22 @@
|
|||||||
import { test, expect } from './zoo-test'
|
import { test, expect } from '@playwright/test'
|
||||||
|
|
||||||
|
import { setupElectron, tearDown } from './test-utils'
|
||||||
|
|
||||||
|
test.afterEach(async ({ page }, testInfo) => {
|
||||||
|
await tearDown(page, testInfo)
|
||||||
|
})
|
||||||
|
|
||||||
test.describe('Electron app header tests', () => {
|
test.describe('Electron app header tests', () => {
|
||||||
test(
|
test(
|
||||||
'Open Command Palette button has correct shortcut',
|
'Open Command Palette button has correct shortcut',
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ page }, testInfo) => {
|
async ({ browserName }, testInfo) => {
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
const { electronApp, page } = await setupElectron({
|
||||||
|
testInfo,
|
||||||
|
folderSetupFn: async () => {},
|
||||||
|
})
|
||||||
|
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
// No space before the shortcut since it checks textContent.
|
// No space before the shortcut since it checks textContent.
|
||||||
let text
|
let text
|
||||||
@ -23,14 +34,21 @@ test.describe('Electron app header tests', () => {
|
|||||||
const commandsButton = page.getByRole('button', { name: 'Commands' })
|
const commandsButton = page.getByRole('button', { name: 'Commands' })
|
||||||
await expect(commandsButton).toBeVisible()
|
await expect(commandsButton).toBeVisible()
|
||||||
await expect(commandsButton).toHaveText(text)
|
await expect(commandsButton).toHaveText(text)
|
||||||
|
|
||||||
|
await electronApp.close()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'User settings has correct shortcut',
|
'User settings has correct shortcut',
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ page }, testInfo) => {
|
async ({ browserName }, testInfo) => {
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
const { electronApp, page } = await setupElectron({
|
||||||
|
testInfo,
|
||||||
|
folderSetupFn: async () => {},
|
||||||
|
})
|
||||||
|
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
// Open the user sidebar menu.
|
// Open the user sidebar menu.
|
||||||
await page.getByTestId('user-sidebar-toggle').click()
|
await page.getByTestId('user-sidebar-toggle').click()
|
||||||
@ -41,6 +59,8 @@ test.describe('Electron app header tests', () => {
|
|||||||
const userSettingsButton = page.getByTestId('user-settings')
|
const userSettingsButton = page.getByTestId('user-settings')
|
||||||
await expect(userSettingsButton).toBeVisible()
|
await expect(userSettingsButton).toBeVisible()
|
||||||
await expect(userSettingsButton).toHaveText(text)
|
await expect(userSettingsButton).toHaveText(text)
|
||||||
|
|
||||||
|
await electronApp.close()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -1,26 +1,29 @@
|
|||||||
import { test, expect, Page } from './zoo-test'
|
import { test, expect, Page } from '@playwright/test'
|
||||||
import {
|
import {
|
||||||
getUtils,
|
getUtils,
|
||||||
TEST_COLORS,
|
TEST_COLORS,
|
||||||
|
setup,
|
||||||
|
tearDown,
|
||||||
commonPoints,
|
commonPoints,
|
||||||
PERSIST_MODELING_CONTEXT,
|
PERSIST_MODELING_CONTEXT,
|
||||||
} from './test-utils'
|
} from './test-utils'
|
||||||
import { HomePageFixture } from './fixtures/homePageFixture'
|
|
||||||
|
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||||
|
await setup(context, page, testInfo)
|
||||||
|
})
|
||||||
|
|
||||||
|
test.afterEach(async ({ page }, testInfo) => {
|
||||||
|
await tearDown(page, testInfo)
|
||||||
|
})
|
||||||
|
|
||||||
test.setTimeout(120000)
|
test.setTimeout(120000)
|
||||||
|
|
||||||
async function doBasicSketch(
|
async function doBasicSketch(page: Page, openPanes: string[]) {
|
||||||
page: Page,
|
|
||||||
homePage: HomePageFixture,
|
|
||||||
openPanes: string[]
|
|
||||||
) {
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
await u.waitForPageLoad()
|
|
||||||
await page.waitForTimeout()
|
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
|
|
||||||
// If we have the code pane open, we should see the code.
|
// If we have the code pane open, we should see the code.
|
||||||
@ -145,11 +148,13 @@ async function doBasicSketch(
|
|||||||
}
|
}
|
||||||
|
|
||||||
test.describe('Basic sketch', () => {
|
test.describe('Basic sketch', () => {
|
||||||
test.fixme('code pane open at start', async ({ page, homePage }) => {
|
test('code pane open at start', { tag: ['@skipWin'] }, async ({ page }) => {
|
||||||
await doBasicSketch(page, homePage, ['code'])
|
// Skip on windows it is being weird.
|
||||||
|
test.skip(process.platform === 'win32', 'Skip on windows')
|
||||||
|
await doBasicSketch(page, ['code'])
|
||||||
})
|
})
|
||||||
|
|
||||||
test.fixme('code pane closed at start', async ({ page, homePage }) => {
|
test('code pane closed at start', async ({ page }) => {
|
||||||
// Load the app with the code panes
|
// Load the app with the code panes
|
||||||
await page.addInitScript(async (persistModelingContext) => {
|
await page.addInitScript(async (persistModelingContext) => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
@ -157,6 +162,6 @@ test.describe('Basic sketch', () => {
|
|||||||
JSON.stringify({ openPanes: [] })
|
JSON.stringify({ openPanes: [] })
|
||||||
)
|
)
|
||||||
}, PERSIST_MODELING_CONTEXT)
|
}, PERSIST_MODELING_CONTEXT)
|
||||||
await doBasicSketch(page, homePage, [])
|
await doBasicSketch(page, [])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -1,21 +1,27 @@
|
|||||||
import { test, expect, Page } from './zoo-test'
|
import { test, expect } from '@playwright/test'
|
||||||
import { HomePageFixture } from './fixtures/homePageFixture'
|
import { getUtils, setup, tearDown } from './test-utils'
|
||||||
import { getUtils } from './test-utils'
|
|
||||||
import { EngineCommand } from 'lang/std/artifactGraph'
|
import { EngineCommand } from 'lang/std/artifactGraph'
|
||||||
import { uuidv4 } from 'lib/utils'
|
import { uuidv4 } from 'lib/utils'
|
||||||
|
|
||||||
|
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||||
|
await setup(context, page, testInfo)
|
||||||
|
})
|
||||||
|
|
||||||
|
test.afterEach(async ({ page }, testInfo) => {
|
||||||
|
await tearDown(page, testInfo)
|
||||||
|
})
|
||||||
|
|
||||||
test.describe('Can create sketches on all planes and their back sides', () => {
|
test.describe('Can create sketches on all planes and their back sides', () => {
|
||||||
const sketchOnPlaneAndBackSideTest = async (
|
const sketchOnPlaneAndBackSideTest = async (
|
||||||
page: Page,
|
page: any,
|
||||||
homePage: HomePageFixture,
|
|
||||||
plane: string,
|
plane: string,
|
||||||
clickCoords: { x: number; y: number }
|
clickCoords: { x: number; y: number }
|
||||||
) => {
|
) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
|
|
||||||
const coord =
|
const coord =
|
||||||
@ -77,39 +83,32 @@ test.describe('Can create sketches on all planes and their back sides', () => {
|
|||||||
await u.clearCommandLogs()
|
await u.clearCommandLogs()
|
||||||
await u.removeCurrentCode()
|
await u.removeCurrentCode()
|
||||||
}
|
}
|
||||||
test('XY', async ({ page, homePage }) => {
|
test('XY', async ({ page }) => {
|
||||||
await sketchOnPlaneAndBackSideTest(
|
await sketchOnPlaneAndBackSideTest(
|
||||||
page,
|
page,
|
||||||
homePage,
|
|
||||||
'XY',
|
'XY',
|
||||||
{ x: 600, y: 388 } // red plane
|
{ 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.
|
// { 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, homePage }) => {
|
test('YZ', async ({ page }) => {
|
||||||
await sketchOnPlaneAndBackSideTest(page, homePage, 'YZ', { x: 700, y: 250 }) // green plane
|
await sketchOnPlaneAndBackSideTest(page, 'YZ', { x: 700, y: 250 }) // green plane
|
||||||
})
|
})
|
||||||
|
|
||||||
test('XZ', async ({ page, homePage }) => {
|
test('XZ', async ({ page }) => {
|
||||||
await sketchOnPlaneAndBackSideTest(page, homePage, '-XZ', { x: 700, y: 80 }) // blue plane
|
await sketchOnPlaneAndBackSideTest(page, '-XZ', { x: 700, y: 80 }) // blue plane
|
||||||
})
|
})
|
||||||
|
|
||||||
test('-XY', async ({ page, homePage }) => {
|
test('-XY', async ({ page }) => {
|
||||||
await sketchOnPlaneAndBackSideTest(page, homePage, '-XY', {
|
await sketchOnPlaneAndBackSideTest(page, '-XY', { x: 600, y: 118 }) // back of red plane
|
||||||
x: 600,
|
|
||||||
y: 118,
|
|
||||||
}) // back of red plane
|
|
||||||
})
|
})
|
||||||
|
|
||||||
test('-YZ', async ({ page, homePage }) => {
|
test('-YZ', async ({ page }) => {
|
||||||
await sketchOnPlaneAndBackSideTest(page, homePage, '-YZ', {
|
await sketchOnPlaneAndBackSideTest(page, '-YZ', { x: 700, y: 219 }) // back of green plane
|
||||||
x: 700,
|
|
||||||
y: 219,
|
|
||||||
}) // back of green plan
|
|
||||||
})
|
})
|
||||||
|
|
||||||
test('-XZ', async ({ page, homePage }) => {
|
test('-XZ', async ({ page }) => {
|
||||||
await sketchOnPlaneAndBackSideTest(page, homePage, 'XZ', { x: 700, y: 427 }) // back of blue plane
|
await sketchOnPlaneAndBackSideTest(page, 'XZ', { x: 700, y: 427 }) // back of blue plane
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -1,15 +1,28 @@
|
|||||||
import { test, expect } from './zoo-test'
|
import { test, expect } from '@playwright/test'
|
||||||
|
|
||||||
import { getUtils, executorInputPath } from './test-utils'
|
import {
|
||||||
|
getUtils,
|
||||||
|
setup,
|
||||||
|
setupElectron,
|
||||||
|
tearDown,
|
||||||
|
executorInputPath,
|
||||||
|
} from './test-utils'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import { bracket } from 'lib/exampleKcl'
|
import { bracket } from 'lib/exampleKcl'
|
||||||
import { TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW } from './storageStates'
|
import { TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW } from './storageStates'
|
||||||
import fsp from 'fs/promises'
|
import fsp from 'fs/promises'
|
||||||
|
|
||||||
|
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||||
|
await setup(context, page, testInfo)
|
||||||
|
})
|
||||||
|
|
||||||
|
test.afterEach(async ({ page }, testInfo) => {
|
||||||
|
await tearDown(page, testInfo)
|
||||||
|
})
|
||||||
|
|
||||||
test.describe('Code pane and errors', () => {
|
test.describe('Code pane and errors', () => {
|
||||||
test('Typing KCL errors induces a badge on the code pane button', async ({
|
test('Typing KCL errors induces a badge on the code pane button', async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
|
||||||
}) => {
|
}) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
|
|
||||||
@ -18,18 +31,18 @@ test.describe('Code pane and errors', () => {
|
|||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'persistCode',
|
'persistCode',
|
||||||
`// Extruded Triangle
|
`// Extruded Triangle
|
||||||
sketch001 = startSketchOn('XZ')
|
sketch001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([0, 0], %)
|
|> startProfileAt([0, 0], %)
|
||||||
|> line([10, 0], %)
|
|> line([10, 0], %)
|
||||||
|> line([-5, 10], %)
|
|> line([-5, 10], %)
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
extrude001 = extrude(5, sketch001)`
|
extrude001 = extrude(5, sketch001)`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
// wait for execution done
|
// wait for execution done
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
@ -49,11 +62,11 @@ test.describe('Code pane and errors', () => {
|
|||||||
await expect(codePaneButtonHolder).toContainText('notification')
|
await expect(codePaneButtonHolder).toContainText('notification')
|
||||||
})
|
})
|
||||||
|
|
||||||
test.skip('Opening and closing the code pane will consistently show error diagnostics', async ({
|
test('Opening and closing the code pane will consistently show error diagnostics', async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
|
||||||
editor,
|
|
||||||
}) => {
|
}) => {
|
||||||
|
await page.goto('http://localhost:3000')
|
||||||
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
|
|
||||||
// Load the app with the working starter code
|
// Load the app with the working starter code
|
||||||
@ -61,8 +74,8 @@ test.describe('Code pane and errors', () => {
|
|||||||
localStorage.setItem('persistCode', code)
|
localStorage.setItem('persistCode', code)
|
||||||
}, bracket)
|
}, bracket)
|
||||||
|
|
||||||
await page.setBodyDimensions({ width: 1200, height: 900 })
|
await page.setViewportSize({ width: 1200, height: 900 })
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
// wait for execution done
|
// wait for execution done
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
@ -78,9 +91,8 @@ test.describe('Code pane and errors', () => {
|
|||||||
await expect(codePaneButtonHolder).not.toContainText('notification')
|
await expect(codePaneButtonHolder).not.toContainText('notification')
|
||||||
|
|
||||||
// Delete a character to break the KCL
|
// Delete a character to break the KCL
|
||||||
await editor.openPane()
|
await u.openKclCodePanel()
|
||||||
await editor.scrollToText('thickness, bracketLeg1Sketch)')
|
await page.getByText('thickness, bracketLeg1Sketch)').click()
|
||||||
await page.getByText('extrude(thickness, bracketLeg1Sketch)').click()
|
|
||||||
await page.keyboard.press('Backspace')
|
await page.keyboard.press('Backspace')
|
||||||
|
|
||||||
// Ensure that a badge appears on the button
|
// Ensure that a badge appears on the button
|
||||||
@ -104,10 +116,7 @@ test.describe('Code pane and errors', () => {
|
|||||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
|
|
||||||
// Open the code pane
|
// Open the code pane
|
||||||
await editor.openPane()
|
await u.openKclCodePanel()
|
||||||
|
|
||||||
// Go to our problematic code again (missing closing paren!)
|
|
||||||
await editor.scrollToText('extrude(thickness, bracketLeg1Sketch')
|
|
||||||
|
|
||||||
// Ensure that a badge appears on the button
|
// Ensure that a badge appears on the button
|
||||||
await expect(codePaneButtonHolder).toContainText('notification')
|
await expect(codePaneButtonHolder).toContainText('notification')
|
||||||
@ -120,16 +129,18 @@ test.describe('Code pane and errors', () => {
|
|||||||
await expect(page.locator('.cm-tooltip').first()).toBeVisible()
|
await expect(page.locator('.cm-tooltip').first()).toBeVisible()
|
||||||
})
|
})
|
||||||
|
|
||||||
test.fixme(
|
test('When error is not in view you can click the badge to scroll to it', async ({
|
||||||
'When error is not in view you can click the badge to scroll to it',
|
page,
|
||||||
async ({ page, homePage, context }) => {
|
}) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
|
||||||
// Load the app with the working starter code
|
// Load the app with the working starter code
|
||||||
await context.addInitScript((code) => {
|
await page.addInitScript((code) => {
|
||||||
localStorage.setItem('persistCode', code)
|
localStorage.setItem('persistCode', code)
|
||||||
}, TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW)
|
}, TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW)
|
||||||
|
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
await page.waitForTimeout(1000)
|
await page.waitForTimeout(1000)
|
||||||
|
|
||||||
@ -153,25 +164,24 @@ test.describe('Code pane and errors', () => {
|
|||||||
await expect(
|
await expect(
|
||||||
page
|
page
|
||||||
.getByText(
|
.getByText(
|
||||||
'Modeling command failed: [ApiError { error_code: InternalEngine, message: "Solid3D revolve failed: sketch profile must lie entirely on one side of the revolution axis" }]'
|
'sketch profile must lie entirely on one side of the revolution axis'
|
||||||
)
|
)
|
||||||
.first()
|
.first()
|
||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
}
|
})
|
||||||
)
|
|
||||||
|
|
||||||
test('When error is not in view WITH LINTS you can click the badge to scroll to it', async ({
|
test('When error is not in view WITH LINTS you can click the badge to scroll to it', async ({
|
||||||
context,
|
|
||||||
page,
|
page,
|
||||||
homePage,
|
|
||||||
}) => {
|
}) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
|
||||||
// Load the app with the working starter code
|
// Load the app with the working starter code
|
||||||
await context.addInitScript((code) => {
|
await page.addInitScript((code) => {
|
||||||
localStorage.setItem('persistCode', code)
|
localStorage.setItem('persistCode', code)
|
||||||
}, TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW)
|
}, TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW)
|
||||||
|
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
await page.waitForTimeout(1000)
|
await page.waitForTimeout(1000)
|
||||||
|
|
||||||
@ -231,9 +241,11 @@ test.describe('Code pane and errors', () => {
|
|||||||
test(
|
test(
|
||||||
'Opening multiple panes persists when switching projects',
|
'Opening multiple panes persists when switching projects',
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ context, page }, testInfo) => {
|
async ({ browserName }, testInfo) => {
|
||||||
// Setup multiple projects.
|
// Setup multiple projects.
|
||||||
await context.folderSetupFn(async (dir) => {
|
const { electronApp, page } = await setupElectron({
|
||||||
|
testInfo,
|
||||||
|
folderSetupFn: async (dir) => {
|
||||||
const routerTemplateDir = join(dir, 'router-template-slate')
|
const routerTemplateDir = join(dir, 'router-template-slate')
|
||||||
const bracketDir = join(dir, 'bracket')
|
const bracketDir = join(dir, 'bracket')
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
@ -250,10 +262,11 @@ test(
|
|||||||
join(bracketDir, 'main.kcl')
|
join(bracketDir, 'main.kcl')
|
||||||
),
|
),
|
||||||
])
|
])
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await test.step('Opening the bracket project should load', async () => {
|
await test.step('Opening the bracket project should load', async () => {
|
||||||
await expect(page.getByText('bracket')).toBeVisible()
|
await expect(page.getByText('bracket')).toBeVisible()
|
||||||
@ -296,21 +309,30 @@ test(
|
|||||||
await expect(page.locator('#variables-pane')).toBeVisible()
|
await expect(page.locator('#variables-pane')).toBeVisible()
|
||||||
await expect(page.locator('#logs-pane')).toBeVisible()
|
await expect(page.locator('#logs-pane')).toBeVisible()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
await electronApp.close()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'external change of file contents are reflected in editor',
|
'external change of file contents are reflected in editor',
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ context, page }, testInfo) => {
|
async ({ browserName }, testInfo) => {
|
||||||
const PROJECT_DIR_NAME = 'lee-was-here'
|
const PROJECT_DIR_NAME = 'lee-was-here'
|
||||||
const { dir: projectsDir } = await context.folderSetupFn(async (dir) => {
|
const {
|
||||||
|
electronApp,
|
||||||
|
page,
|
||||||
|
dir: projectsDir,
|
||||||
|
} = await setupElectron({
|
||||||
|
testInfo,
|
||||||
|
folderSetupFn: async (dir) => {
|
||||||
const aProjectDir = join(dir, PROJECT_DIR_NAME)
|
const aProjectDir = join(dir, PROJECT_DIR_NAME)
|
||||||
await fsp.mkdir(aProjectDir, { recursive: true })
|
await fsp.mkdir(aProjectDir, { recursive: true })
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await test.step('Open the project', async () => {
|
await test.step('Open the project', async () => {
|
||||||
await expect(page.getByText(PROJECT_DIR_NAME)).toBeVisible()
|
await expect(page.getByText(PROJECT_DIR_NAME)).toBeVisible()
|
||||||
@ -329,5 +351,7 @@ test(
|
|||||||
)
|
)
|
||||||
await u.editorTextMatches(content)
|
await u.editorTextMatches(content)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
await electronApp.close()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -1,12 +1,19 @@
|
|||||||
import { test, expect } from './zoo-test'
|
import { test, expect } from '@playwright/test'
|
||||||
|
|
||||||
import { getUtils } from './test-utils'
|
import { getUtils, setup, tearDown } from './test-utils'
|
||||||
import { KCL_DEFAULT_LENGTH } from 'lib/constants'
|
import { KCL_DEFAULT_LENGTH } from 'lib/constants'
|
||||||
|
|
||||||
|
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||||
|
await setup(context, page, testInfo)
|
||||||
|
})
|
||||||
|
|
||||||
|
test.afterEach(async ({ page }, testInfo) => {
|
||||||
|
await tearDown(page, testInfo)
|
||||||
|
})
|
||||||
|
|
||||||
test.describe('Command bar tests', () => {
|
test.describe('Command bar tests', () => {
|
||||||
test('Extrude from command bar selects extrude line after', async ({
|
test('Extrude from command bar selects extrude line after', async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
|
||||||
}) => {
|
}) => {
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
@ -22,9 +29,9 @@ test.describe('Command bar tests', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
@ -45,7 +52,7 @@ test.describe('Command bar tests', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Fillet from command bar', async ({ page, homePage }) => {
|
test('Fillet from command bar', async ({ page }) => {
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'persistCode',
|
'persistCode',
|
||||||
@ -56,13 +63,13 @@ test.describe('Command bar tests', () => {
|
|||||||
|> line([0, -10], %)
|
|> line([0, -10], %)
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
extrude001 = extrude(-10, sketch001)`
|
extrude001 = extrude(-10, sketch001)`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
@ -80,16 +87,16 @@ test.describe('Command bar tests', () => {
|
|||||||
await page.keyboard.press('Enter') // submit
|
await page.keyboard.press('Enter') // submit
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
await expect(page.locator('.cm-activeLine')).toContainText(
|
await expect(page.locator('.cm-activeLine')).toContainText(
|
||||||
`fillet({ radius: ${KCL_DEFAULT_LENGTH}, tags: [seg01] }, %)`
|
`fillet({ radius = ${KCL_DEFAULT_LENGTH}, tags = [seg01] }, %)`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Command bar can change a setting, and switch back and forth between arguments', async ({
|
test('Command bar can change a setting, and switch back and forth between arguments', async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
|
||||||
}) => {
|
}) => {
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
const u = await getUtils(page)
|
||||||
await homePage.goToModelingScene()
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
const commandBarButton = page.getByRole('button', { name: 'Commands' })
|
const commandBarButton = page.getByRole('button', { name: 'Commands' })
|
||||||
const cmdSearchBar = page.getByPlaceholder('Search commands')
|
const cmdSearchBar = page.getByPlaceholder('Search commands')
|
||||||
@ -146,7 +153,7 @@ test.describe('Command bar tests', () => {
|
|||||||
// Check that the visibility changed
|
// Check that the visibility changed
|
||||||
await expect(paneSelector).not.toBeVisible()
|
await expect(paneSelector).not.toBeVisible()
|
||||||
|
|
||||||
commandOptionInput = page.locator('[id="option-input"]')
|
commandOptionInput = page.getByPlaceholder('off')
|
||||||
|
|
||||||
// Test case for https://github.com/KittyCAD/modeling-app/issues/2882
|
// Test case for https://github.com/KittyCAD/modeling-app/issues/2882
|
||||||
await commandBarButton.click()
|
await commandBarButton.click()
|
||||||
@ -167,10 +174,10 @@ test.describe('Command bar tests', () => {
|
|||||||
|
|
||||||
test('Command bar keybinding works from code editor and can change a setting', async ({
|
test('Command bar keybinding works from code editor and can change a setting', async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
|
||||||
}) => {
|
}) => {
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
const u = await getUtils(page)
|
||||||
await homePage.goToModelingScene()
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('button', { name: 'Start Sketch' })
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
@ -214,7 +221,7 @@ test.describe('Command bar tests', () => {
|
|||||||
await expect(page.locator('body')).not.toHaveClass(`body-bg dark`)
|
await expect(page.locator('body')).not.toHaveClass(`body-bg dark`)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Can extrude from the command bar', async ({ page, homePage }) => {
|
test('Can extrude from the command bar', async ({ page }) => {
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'persistCode',
|
'persistCode',
|
||||||
@ -230,9 +237,9 @@ test.describe('Command bar tests', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
// Make sure the stream is up
|
// Make sure the stream is up
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
@ -286,19 +293,26 @@ test.describe('Command bar tests', () => {
|
|||||||
await continueButton.click()
|
await continueButton.click()
|
||||||
await submitButton.click()
|
await submitButton.click()
|
||||||
|
|
||||||
|
// Check that the code was updated
|
||||||
await u.waitForCmdReceive('extrude')
|
await u.waitForCmdReceive('extrude')
|
||||||
|
// Unfortunately this indentation seems to matter for the test
|
||||||
await expect(page.locator('.cm-content')).toContainText(
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
'extrude001 = extrude(distance001, sketch001)'
|
`distance = sqrt(20)
|
||||||
|
distance001 = ${KCL_DEFAULT_LENGTH}
|
||||||
|
sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([-6.95, 10.98], %)
|
||||||
|
|> line([25.1, 0.41], %)
|
||||||
|
|> line([0.73, -20.93], %)
|
||||||
|
|> line([-23.44, 0.52], %)
|
||||||
|
|> close(%)
|
||||||
|
extrude001 = extrude(distance001, sketch001)`.replace(/(\r\n|\n|\r)/gm, '') // remove newlines
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Can switch between sketch tools via command bar', async ({
|
test('Can switch between sketch tools via command bar', async ({ page }) => {
|
||||||
page,
|
const u = await getUtils(page)
|
||||||
homePage,
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
}) => {
|
await u.waitForAuthSkipAppStart()
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
|
||||||
await homePage.goToModelingScene()
|
|
||||||
|
|
||||||
const sketchButton = page.getByRole('button', { name: 'Start Sketch' })
|
const sketchButton = page.getByRole('button', { name: 'Start Sketch' })
|
||||||
const cmdBarButton = page.getByRole('button', { name: 'Commands' })
|
const cmdBarButton = page.getByRole('button', { name: 'Commands' })
|
||||||
|
@ -1,16 +1,23 @@
|
|||||||
import { test, expect } from './zoo-test'
|
import { test, expect } from '@playwright/test'
|
||||||
import { getUtils } from './test-utils'
|
import { getUtils, setup, tearDown } from './test-utils'
|
||||||
|
|
||||||
|
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||||
|
await setup(context, page, testInfo)
|
||||||
|
})
|
||||||
|
|
||||||
|
test.afterEach(async ({ page }, testInfo) => {
|
||||||
|
await tearDown(page, testInfo)
|
||||||
|
})
|
||||||
test.describe('Copilot ghost text', () => {
|
test.describe('Copilot ghost text', () => {
|
||||||
// eslint-disable-next-line jest/valid-title
|
// eslint-disable-next-line jest/valid-title
|
||||||
test.skip(true, 'Needs to get covered again')
|
test.skip(true, 'Needs to get covered again')
|
||||||
|
|
||||||
test('completes code in empty file', async ({ page, homePage }) => {
|
test('completes code in empty file', async ({ page }) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
await u.codeLocator.click()
|
await u.codeLocator.click()
|
||||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
@ -45,13 +52,12 @@ test.describe('Copilot ghost text', () => {
|
|||||||
|
|
||||||
test.skip('copilot disabled in sketch mode no select plane', async ({
|
test.skip('copilot disabled in sketch mode no select plane', async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
|
||||||
}) => {
|
}) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
await u.codeLocator.click()
|
await u.codeLocator.click()
|
||||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
@ -95,13 +101,12 @@ test.describe('Copilot ghost text', () => {
|
|||||||
|
|
||||||
test('copilot disabled in sketch mode after selecting plane', async ({
|
test('copilot disabled in sketch mode after selecting plane', async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
|
||||||
}) => {
|
}) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
await u.codeLocator.click()
|
await u.codeLocator.click()
|
||||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
@ -179,12 +184,12 @@ test.describe('Copilot ghost text', () => {
|
|||||||
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
await expect(page.locator('.cm-ghostText')).not.toBeVisible()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('ArrowUp in code rejects the suggestion', async ({ page, homePage }) => {
|
test('ArrowUp in code rejects the suggestion', async ({ page }) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
await u.codeLocator.click()
|
await u.codeLocator.click()
|
||||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
@ -207,15 +212,12 @@ test.describe('Copilot ghost text', () => {
|
|||||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('ArrowDown in code rejects the suggestion', async ({
|
test('ArrowDown in code rejects the suggestion', async ({ page }) => {
|
||||||
page,
|
|
||||||
homePage,
|
|
||||||
}) => {
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
await u.codeLocator.click()
|
await u.codeLocator.click()
|
||||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
@ -238,15 +240,12 @@ test.describe('Copilot ghost text', () => {
|
|||||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('ArrowLeft in code rejects the suggestion', async ({
|
test('ArrowLeft in code rejects the suggestion', async ({ page }) => {
|
||||||
page,
|
|
||||||
homePage,
|
|
||||||
}) => {
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
await u.codeLocator.click()
|
await u.codeLocator.click()
|
||||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
@ -269,15 +268,12 @@ test.describe('Copilot ghost text', () => {
|
|||||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('ArrowRight in code rejects the suggestion', async ({
|
test('ArrowRight in code rejects the suggestion', async ({ page }) => {
|
||||||
page,
|
|
||||||
homePage,
|
|
||||||
}) => {
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
await u.codeLocator.click()
|
await u.codeLocator.click()
|
||||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
@ -300,12 +296,12 @@ test.describe('Copilot ghost text', () => {
|
|||||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Enter in code scoots it down', async ({ page, homePage }) => {
|
test('Enter in code scoots it down', async ({ page }) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
await u.codeLocator.click()
|
await u.codeLocator.click()
|
||||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
@ -330,15 +326,12 @@ test.describe('Copilot ghost text', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Ctrl+shift+z in code rejects the suggestion', async ({
|
test('Ctrl+shift+z in code rejects the suggestion', async ({ page }) => {
|
||||||
page,
|
|
||||||
homePage,
|
|
||||||
}) => {
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
await u.codeLocator.click()
|
await u.codeLocator.click()
|
||||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
@ -367,13 +360,12 @@ test.describe('Copilot ghost text', () => {
|
|||||||
|
|
||||||
test('Ctrl+z in code rejects the suggestion and undos the last code', async ({
|
test('Ctrl+z in code rejects the suggestion and undos the last code', async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
|
||||||
}) => {
|
}) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
await page.waitForTimeout(800)
|
await page.waitForTimeout(800)
|
||||||
await u.codeLocator.click()
|
await u.codeLocator.click()
|
||||||
@ -428,17 +420,15 @@ test.describe('Copilot ghost text', () => {
|
|||||||
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
||||||
|
|
||||||
// TODO when we make codemirror a widget, we can test this.
|
// TODO when we make codemirror a widget, we can test this.
|
||||||
//await expect(page.locator('.cm-content')).toHaveText(``) })
|
//await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
})
|
||||||
|
|
||||||
test('delete in code rejects the suggestion', async ({
|
test('delete in code rejects the suggestion', async ({ page }) => {
|
||||||
page,
|
|
||||||
homePage,
|
|
||||||
}) => {
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
await u.codeLocator.click()
|
await u.codeLocator.click()
|
||||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
@ -463,15 +453,12 @@ test.describe('Copilot ghost text', () => {
|
|||||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('backspace in code rejects the suggestion', async ({
|
test('backspace in code rejects the suggestion', async ({ page }) => {
|
||||||
page,
|
|
||||||
homePage,
|
|
||||||
}) => {
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
await u.codeLocator.click()
|
await u.codeLocator.click()
|
||||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
@ -496,15 +483,12 @@ test.describe('Copilot ghost text', () => {
|
|||||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('focus outside code pane rejects the suggestion', async ({
|
test('focus outside code pane rejects the suggestion', async ({ page }) => {
|
||||||
page,
|
|
||||||
homePage,
|
|
||||||
}) => {
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
await u.codeLocator.click()
|
await u.codeLocator.click()
|
||||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
@ -530,5 +514,4 @@ test.describe('Copilot ghost text', () => {
|
|||||||
|
|
||||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
})
|
})
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
@ -1,6 +1,14 @@
|
|||||||
import { test, expect } from './zoo-test'
|
import { test, expect } from '@playwright/test'
|
||||||
|
|
||||||
import { getUtils } from './test-utils'
|
import { getUtils, setup, tearDown } from './test-utils'
|
||||||
|
|
||||||
|
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||||
|
await setup(context, page, testInfo)
|
||||||
|
})
|
||||||
|
|
||||||
|
test.afterEach(async ({ page }, testInfo) => {
|
||||||
|
await tearDown(page, testInfo)
|
||||||
|
})
|
||||||
|
|
||||||
function countNewlines(input: string): number {
|
function countNewlines(input: string): number {
|
||||||
let count = 0
|
let count = 0
|
||||||
@ -16,14 +24,13 @@ test.describe('Debug pane', () => {
|
|||||||
test('Artifact IDs in the artifact graph are stable across code edits', async ({
|
test('Artifact IDs in the artifact graph are stable across code edits', async ({
|
||||||
page,
|
page,
|
||||||
context,
|
context,
|
||||||
homePage,
|
|
||||||
}) => {
|
}) => {
|
||||||
const code = `sketch001 = startSketchOn('XZ')
|
const code = `sketch001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([0, 0], %)
|
|> startProfileAt([0, 0], %)
|
||||||
|> line([1, 1], %)
|
|> line([1, 1], %)
|
||||||
`
|
`
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
const tree = page.getByTestId('debug-feature-tree')
|
const tree = page.getByTestId('debug-feature-tree')
|
||||||
const segment = tree.locator('li', {
|
const segment = tree.locator('li', {
|
||||||
@ -32,7 +39,7 @@ test.describe('Debug pane', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
await test.step('Test setup', async () => {
|
await test.step('Test setup', async () => {
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
await u.openKclCodePanel()
|
await u.openKclCodePanel()
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
// Set the code in the code editor.
|
// Set the code in the code editor.
|
||||||
|
@ -1,31 +1,39 @@
|
|||||||
import { test, expect } from './zoo-test'
|
import { test, expect } from '@playwright/test'
|
||||||
import path from 'path'
|
import { join } from 'path'
|
||||||
import {
|
import {
|
||||||
getUtils,
|
getUtils,
|
||||||
|
setupElectron,
|
||||||
|
tearDown,
|
||||||
executorInputPath,
|
executorInputPath,
|
||||||
getPlaywrightDownloadDir,
|
|
||||||
} from './test-utils'
|
} from './test-utils'
|
||||||
import fsp from 'fs/promises'
|
import fsp from 'fs/promises'
|
||||||
|
|
||||||
|
test.afterEach(async ({ page }, testInfo) => {
|
||||||
|
await tearDown(page, testInfo)
|
||||||
|
})
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'export works on the first try',
|
'export works on the first try',
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ page, context }, testInfo) => {
|
async ({ browserName }, testInfo) => {
|
||||||
await context.folderSetupFn(async (dir) => {
|
const { electronApp, page } = await setupElectron({
|
||||||
const bracketDir = path.join(dir, 'bracket')
|
testInfo,
|
||||||
|
folderSetupFn: async (dir) => {
|
||||||
|
const bracketDir = join(dir, 'bracket')
|
||||||
await Promise.all([fsp.mkdir(bracketDir, { recursive: true })])
|
await Promise.all([fsp.mkdir(bracketDir, { recursive: true })])
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
fsp.copyFile(
|
fsp.copyFile(
|
||||||
executorInputPath('router-template-slate.kcl'),
|
executorInputPath('router-template-slate.kcl'),
|
||||||
path.join(bracketDir, 'other.kcl')
|
join(bracketDir, 'other.kcl')
|
||||||
),
|
),
|
||||||
fsp.copyFile(
|
fsp.copyFile(
|
||||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||||
path.join(bracketDir, 'main.kcl')
|
join(bracketDir, 'main.kcl')
|
||||||
),
|
),
|
||||||
])
|
])
|
||||||
|
},
|
||||||
})
|
})
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
page.on('console', console.log)
|
page.on('console', console.log)
|
||||||
|
|
||||||
@ -85,16 +93,12 @@ test(
|
|||||||
await expect(successToastMessage).toBeVisible()
|
await expect(successToastMessage).toBeVisible()
|
||||||
await expect(exportingToastMessage).not.toBeVisible()
|
await expect(exportingToastMessage).not.toBeVisible()
|
||||||
|
|
||||||
const firstFileFullPath = path.resolve(
|
|
||||||
getPlaywrightDownloadDir(page),
|
|
||||||
exportFileName
|
|
||||||
)
|
|
||||||
await test.step('Check the export size', async () => {
|
await test.step('Check the export size', async () => {
|
||||||
await expect
|
await expect
|
||||||
.poll(
|
.poll(
|
||||||
async () => {
|
async () => {
|
||||||
try {
|
try {
|
||||||
const outputGltf = await fsp.readFile(firstFileFullPath)
|
const outputGltf = await fsp.readFile(exportFileName)
|
||||||
return outputGltf.byteLength
|
return outputGltf.byteLength
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return 0
|
return 0
|
||||||
@ -103,6 +107,9 @@ test(
|
|||||||
{ timeout: 15_000 }
|
{ timeout: 15_000 }
|
||||||
)
|
)
|
||||||
.toBeGreaterThan(300_000)
|
.toBeGreaterThan(300_000)
|
||||||
|
|
||||||
|
// clean up exported file
|
||||||
|
await fsp.rm(exportFileName)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -163,16 +170,12 @@ test(
|
|||||||
expect(exportingToastMessage).not.toBeVisible(),
|
expect(exportingToastMessage).not.toBeVisible(),
|
||||||
]))
|
]))
|
||||||
|
|
||||||
const secondFileFullPath = path.resolve(
|
|
||||||
getPlaywrightDownloadDir(page),
|
|
||||||
exportFileName
|
|
||||||
)
|
|
||||||
await test.step('Check the export size', async () => {
|
await test.step('Check the export size', async () => {
|
||||||
await expect
|
await expect
|
||||||
.poll(
|
.poll(
|
||||||
async () => {
|
async () => {
|
||||||
try {
|
try {
|
||||||
const outputGltf = await fsp.readFile(secondFileFullPath)
|
const outputGltf = await fsp.readFile(exportFileName)
|
||||||
return outputGltf.byteLength
|
return outputGltf.byteLength
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return 0
|
return 0
|
||||||
@ -181,7 +184,13 @@ test(
|
|||||||
{ timeout: 15_000 }
|
{ timeout: 15_000 }
|
||||||
)
|
)
|
||||||
.toBeGreaterThan(100_000)
|
.toBeGreaterThan(100_000)
|
||||||
|
|
||||||
|
// clean up exported file
|
||||||
|
await fsp.rm(exportFileName)
|
||||||
})
|
})
|
||||||
|
await electronApp.close()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
await electronApp.close()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { test, expect } from './zoo-test'
|
import { test, expect } from '@playwright/test'
|
||||||
import fsp from 'fs/promises'
|
import fsp from 'fs/promises'
|
||||||
import { uuidv4 } from 'lib/utils'
|
import { uuidv4 } from 'lib/utils'
|
||||||
import {
|
import {
|
||||||
@ -6,16 +6,26 @@ import {
|
|||||||
darkModePlaneColorXZ,
|
darkModePlaneColorXZ,
|
||||||
executorInputPath,
|
executorInputPath,
|
||||||
getUtils,
|
getUtils,
|
||||||
|
setup,
|
||||||
|
setupElectron,
|
||||||
|
tearDown,
|
||||||
} from './test-utils'
|
} from './test-utils'
|
||||||
|
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
|
|
||||||
test.describe('Editor tests', () => {
|
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||||
test('can comment out code with ctrl+/', async ({ page, homePage }) => {
|
await setup(context, page, testInfo)
|
||||||
const u = await getUtils(page)
|
})
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
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()
|
||||||
|
|
||||||
// check no error to begin with
|
// check no error to begin with
|
||||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
@ -56,12 +66,11 @@ test.describe('Editor tests', () => {
|
|||||||
|
|
||||||
test('if you click the format button it formats your code', async ({
|
test('if you click the format button it formats your code', async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
|
||||||
}) => {
|
}) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
// check no error to begin with
|
// check no error to begin with
|
||||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
@ -87,12 +96,11 @@ test.describe('Editor tests', () => {
|
|||||||
|
|
||||||
test('if you click the format button it formats your code and executes so lints are still there', async ({
|
test('if you click the format button it formats your code and executes so lints are still there', async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
|
||||||
}) => {
|
}) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
// check no error to begin with
|
// check no error to begin with
|
||||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
@ -143,7 +151,9 @@ test.describe('Editor tests', () => {
|
|||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('fold gutters work', async ({ page, homePage }) => {
|
test('fold gutters work', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
|
||||||
const fullCode = `sketch001 = startSketchOn('XY')
|
const fullCode = `sketch001 = startSketchOn('XY')
|
||||||
|> startProfileAt([-10, -10], %)
|
|> startProfileAt([-10, -10], %)
|
||||||
|> line([20, 0], %)
|
|> line([20, 0], %)
|
||||||
@ -161,9 +171,9 @@ test.describe('Editor tests', () => {
|
|||||||
|> close(%)`
|
|> close(%)`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
// TODO: Jess needs to fix this but you have to mod the code to get them to show
|
// TODO: Jess needs to fix this but you have to mod the code to get them to show
|
||||||
// up, its an annoying codemirror thing.
|
// up, its an annoying codemirror thing.
|
||||||
@ -214,10 +224,7 @@ test.describe('Editor tests', () => {
|
|||||||
await expect(foldGutterFoldLine).not.toBeVisible()
|
await expect(foldGutterFoldLine).not.toBeVisible()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('hover over functions shows function description', async ({
|
test('hover over functions shows function description', async ({ page }) => {
|
||||||
page,
|
|
||||||
homePage,
|
|
||||||
}) => {
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
@ -230,9 +237,9 @@ test.describe('Editor tests', () => {
|
|||||||
|> close(%)`
|
|> close(%)`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
// check no error to begin with
|
// check no error to begin with
|
||||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
@ -261,7 +268,6 @@ test.describe('Editor tests', () => {
|
|||||||
|
|
||||||
test('if you use the format keyboard binding it formats your code', async ({
|
test('if you use the format keyboard binding it formats your code', async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
|
||||||
}) => {
|
}) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
@ -276,9 +282,9 @@ test.describe('Editor tests', () => {
|
|||||||
)
|
)
|
||||||
localStorage.setItem('disableAxis', 'true')
|
localStorage.setItem('disableAxis', 'true')
|
||||||
})
|
})
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
// check no error to begin with
|
// check no error to begin with
|
||||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
@ -304,7 +310,6 @@ test.describe('Editor tests', () => {
|
|||||||
|
|
||||||
test('if you use the format keyboard binding it formats your code and executes so lints are shown', async ({
|
test('if you use the format keyboard binding it formats your code and executes so lints are shown', async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
|
||||||
}) => {
|
}) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
@ -319,9 +324,9 @@ test.describe('Editor tests', () => {
|
|||||||
)
|
)
|
||||||
localStorage.setItem('disableAxis', 'true')
|
localStorage.setItem('disableAxis', 'true')
|
||||||
})
|
})
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
@ -364,14 +369,11 @@ test.describe('Editor tests', () => {
|
|||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('if you write kcl with lint errors you get lints', async ({
|
test('if you write kcl with lint errors you get lints', async ({ page }) => {
|
||||||
page,
|
|
||||||
homePage,
|
|
||||||
}) => {
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
// check no error to begin with
|
// check no error to begin with
|
||||||
await expect(page.locator('.cm-lint-marker-info')).not.toBeVisible()
|
await expect(page.locator('.cm-lint-marker-info')).not.toBeVisible()
|
||||||
@ -407,10 +409,7 @@ test.describe('Editor tests', () => {
|
|||||||
await expect(page.locator('.cm-lint-marker-info')).not.toBeVisible()
|
await expect(page.locator('.cm-lint-marker-info')).not.toBeVisible()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('if you fixup kcl errors you clear lints', async ({
|
test('if you fixup kcl errors you clear lints', async ({ page }) => {
|
||||||
page,
|
|
||||||
homePage,
|
|
||||||
}) => {
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
@ -424,9 +423,9 @@ test.describe('Editor tests', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
// check no error to begin with
|
// check no error to begin with
|
||||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
@ -448,14 +447,11 @@ test.describe('Editor tests', () => {
|
|||||||
).not.toBeVisible()
|
).not.toBeVisible()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('if you write invalid kcl you get inlined errors', async ({
|
test('if you write invalid kcl you get inlined errors', async ({ page }) => {
|
||||||
page,
|
|
||||||
homePage,
|
|
||||||
}) => {
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
// check no error to begin with
|
// check no error to begin with
|
||||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
@ -522,9 +518,7 @@ test.describe('Editor tests', () => {
|
|||||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
})
|
})
|
||||||
|
|
||||||
test.fixme(
|
test('error with 2 source ranges gets 2 diagnostics', async ({ page }) => {
|
||||||
'error with 2 source ranges gets 2 diagnostics',
|
|
||||||
async ({ page, homePage }) => {
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
@ -546,11 +540,9 @@ test.describe('Editor tests', () => {
|
|||||||
`
|
`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
await u.waitForPageLoad()
|
|
||||||
await page.waitForTimeout(1000)
|
|
||||||
|
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
@ -576,7 +568,7 @@ test.describe('Editor tests', () => {
|
|||||||
await page.keyboard.press('ArrowDown')
|
await page.keyboard.press('ArrowDown')
|
||||||
await page.keyboard.press('Enter')
|
await page.keyboard.press('Enter')
|
||||||
await page.keyboard.type(`extrusion = startSketchOn('XY')
|
await page.keyboard.type(`extrusion = startSketchOn('XY')
|
||||||
|> circle({ center: [0, 0], radius: dia/2 }, %)
|
|> circle({ center = [0, 0], radius = dia/2 }, %)
|
||||||
|> hole(squareHole(length, width, height), %)
|
|> hole(squareHole(length, width, height), %)
|
||||||
|> extrude(height, %)`)
|
|> extrude(height, %)`)
|
||||||
|
|
||||||
@ -589,14 +581,12 @@ test.describe('Editor tests', () => {
|
|||||||
|
|
||||||
// Make sure there are two diagnostics
|
// Make sure there are two diagnostics
|
||||||
await expect(page.locator('.cm-lint-marker-error')).toHaveCount(2)
|
await expect(page.locator('.cm-lint-marker-error')).toHaveCount(2)
|
||||||
}
|
})
|
||||||
)
|
|
||||||
test('if your kcl gets an error from the engine it is inlined', async ({
|
test('if your kcl gets an error from the engine it is inlined', async ({
|
||||||
context,
|
|
||||||
page,
|
page,
|
||||||
homePage,
|
|
||||||
}) => {
|
}) => {
|
||||||
await context.addInitScript(async () => {
|
const u = await getUtils(page)
|
||||||
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'persistCode',
|
'persistCode',
|
||||||
`box = startSketchOn('XY')
|
`box = startSketchOn('XY')
|
||||||
@ -614,16 +604,17 @@ test.describe('Editor tests', () => {
|
|||||||
|> line([0, -10], %)
|
|> line([0, -10], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> revolve({
|
|> revolve({
|
||||||
axis: revolveAxis,
|
axis = revolveAxis,
|
||||||
angle: 90
|
angle = 90
|
||||||
}, %)
|
}, %)
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await page.goto('/')
|
||||||
|
await u.waitForPageLoad()
|
||||||
|
|
||||||
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
|
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
|
||||||
|
|
||||||
@ -634,15 +625,12 @@ test.describe('Editor tests', () => {
|
|||||||
await expect(page.getByText(searchText)).toBeVisible()
|
await expect(page.getByText(searchText)).toBeVisible()
|
||||||
})
|
})
|
||||||
test.describe('Autocomplete works', () => {
|
test.describe('Autocomplete works', () => {
|
||||||
test('with enter/click to accept the completion', async ({
|
test('with enter/click to accept the completion', async ({ page }) => {
|
||||||
page,
|
|
||||||
homePage,
|
|
||||||
}) => {
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
// tests clicking on an option, selection the first option
|
// tests clicking on an option, selection the first option
|
||||||
// and arrowing down to an option
|
// and arrowing down to an option
|
||||||
@ -711,12 +699,12 @@ test.describe('Editor tests', () => {
|
|||||||
await expect(page.locator('.cm-lint-marker-error')).toHaveCount(0)
|
await expect(page.locator('.cm-lint-marker-error')).toHaveCount(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('with tab to accept the completion', async ({ page, homePage }) => {
|
test('with tab to accept the completion', async ({ page }) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
// this test might be brittle as we add and remove functions
|
// this test might be brittle as we add and remove functions
|
||||||
// but should also be easy to update.
|
// but should also be easy to update.
|
||||||
@ -782,13 +770,9 @@ test.describe('Editor tests', () => {
|
|||||||
|> xLine(5, %) // lin`)
|
|> xLine(5, %) // lin`)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
test('Can undo a click and point extrude with ctrl+z', async ({
|
test('Can undo a click and point extrude with ctrl+z', async ({ page }) => {
|
||||||
page,
|
|
||||||
context,
|
|
||||||
homePage,
|
|
||||||
}) => {
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await context.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'persistCode',
|
'persistCode',
|
||||||
`sketch001 = startSketchOn('XZ')
|
`sketch001 = startSketchOn('XZ')
|
||||||
@ -799,9 +783,9 @@ test.describe('Editor tests', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('button', { name: 'Start Sketch' })
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
).not.toBeDisabled()
|
).not.toBeDisabled()
|
||||||
@ -860,10 +844,7 @@ test.describe('Editor tests', () => {
|
|||||||
|> close(%)`)
|
|> close(%)`)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Can undo a sketch modification with ctrl+z', async ({
|
test('Can undo a sketch modification with ctrl+z', async ({ page }) => {
|
||||||
page,
|
|
||||||
homePage,
|
|
||||||
}) => {
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
@ -877,9 +858,9 @@ test.describe('Editor tests', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('button', { name: 'Start Sketch' })
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
).not.toBeDisabled()
|
).not.toBeDisabled()
|
||||||
@ -906,7 +887,7 @@ test.describe('Editor tests', () => {
|
|||||||
})
|
})
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
const startPX = [1200 / 2, 500 / 2]
|
const startPX = [665, 397]
|
||||||
|
|
||||||
const dragPX = 40
|
const dragPX = 40
|
||||||
|
|
||||||
@ -920,9 +901,9 @@ test.describe('Editor tests', () => {
|
|||||||
|
|
||||||
await expect(page.getByTestId('segment-overlay')).toHaveCount(2)
|
await expect(page.getByTestId('segment-overlay')).toHaveCount(2)
|
||||||
|
|
||||||
// drag startProfileAt handle
|
// drag startProfieAt handle
|
||||||
await page.dragAndDrop('#stream', '#stream', {
|
await page.dragAndDrop('#stream', '#stream', {
|
||||||
sourcePosition: { x: startPX[0] + 68, y: startPX[1] + 147 },
|
sourcePosition: { x: startPX[0], y: startPX[1] },
|
||||||
targetPosition: { x: startPX[0] + dragPX, y: startPX[1] + dragPX },
|
targetPosition: { x: startPX[0] + dragPX, y: startPX[1] + dragPX },
|
||||||
})
|
})
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
@ -960,12 +941,12 @@ test.describe('Editor tests', () => {
|
|||||||
// expect the code to have changed
|
// expect the code to have changed
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toHaveText(`sketch001 = startSketchOn('XZ')
|
.toHaveText(`sketch001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([2.71, -2.71], %)
|
|> startProfileAt([7.12, -12.68], %)
|
||||||
|> line([15.4, -2.78], %)
|
|> line([15.39, -2.78], %)
|
||||||
|> tangentialArcTo([27.6, -3.05], %)
|
|> tangentialArcTo([27.6, -3.05], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(5, %)
|
|> extrude(5, %)
|
||||||
`)
|
`)
|
||||||
|
|
||||||
// Hit undo
|
// Hit undo
|
||||||
await page.keyboard.down('Control')
|
await page.keyboard.down('Control')
|
||||||
@ -974,8 +955,8 @@ test.describe('Editor tests', () => {
|
|||||||
|
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toHaveText(`sketch001 = startSketchOn('XZ')
|
.toHaveText(`sketch001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([2.71, -2.71], %)
|
|> startProfileAt([7.12, -12.68], %)
|
||||||
|> line([15.4, -2.78], %)
|
|> line([15.39, -2.78], %)
|
||||||
|> tangentialArcTo([24.95, -0.38], %)
|
|> tangentialArcTo([24.95, -0.38], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(5, %)`)
|
|> extrude(5, %)`)
|
||||||
@ -987,12 +968,12 @@ test.describe('Editor tests', () => {
|
|||||||
|
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toHaveText(`sketch001 = startSketchOn('XZ')
|
.toHaveText(`sketch001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([2.71, -2.71], %)
|
|> startProfileAt([7.12, -12.68], %)
|
||||||
|> line([12.73, -0.09], %)
|
|> line([12.73, -0.09], %)
|
||||||
|> tangentialArcTo([24.95, -0.38], %)
|
|> tangentialArcTo([24.95, -0.38], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(5, %)
|
|> extrude(5, %)
|
||||||
`)
|
`)
|
||||||
|
|
||||||
// Hit undo again.
|
// Hit undo again.
|
||||||
await page.keyboard.down('Control')
|
await page.keyboard.down('Control')
|
||||||
@ -1012,8 +993,10 @@ test.describe('Editor tests', () => {
|
|||||||
test.fixme(
|
test.fixme(
|
||||||
`Can use the import stdlib function on a local OBJ file`,
|
`Can use the import stdlib function on a local OBJ file`,
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ page, context }, testInfo) => {
|
async ({ browserName }, testInfo) => {
|
||||||
await context.folderSetupFn(async (dir) => {
|
const { electronApp, page } = await setupElectron({
|
||||||
|
testInfo,
|
||||||
|
folderSetupFn: async (dir) => {
|
||||||
const bracketDir = join(dir, 'cube')
|
const bracketDir = join(dir, 'cube')
|
||||||
await fsp.mkdir(bracketDir, { recursive: true })
|
await fsp.mkdir(bracketDir, { recursive: true })
|
||||||
await fsp.copyFile(
|
await fsp.copyFile(
|
||||||
@ -1021,10 +1004,10 @@ test.describe('Editor tests', () => {
|
|||||||
join(bracketDir, 'cube.obj')
|
join(bracketDir, 'cube.obj')
|
||||||
)
|
)
|
||||||
await fsp.writeFile(join(bracketDir, 'main.kcl'), '')
|
await fsp.writeFile(join(bracketDir, 'main.kcl'), '')
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const viewportSize = { width: 1200, height: 500 }
|
const viewportSize = { width: 1200, height: 500 }
|
||||||
await page.setBodyDimensions(viewportSize)
|
await page.setViewportSize(viewportSize)
|
||||||
|
|
||||||
// Locators and constants
|
// Locators and constants
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
@ -1082,6 +1065,8 @@ test.describe('Editor tests', () => {
|
|||||||
})
|
})
|
||||||
.toBeGreaterThan(15)
|
.toBeGreaterThan(15)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
await electronApp.close()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -29,7 +29,7 @@ export class EditorFixture {
|
|||||||
reConstruct = (page: Page) => {
|
reConstruct = (page: Page) => {
|
||||||
this.page = page
|
this.page = page
|
||||||
|
|
||||||
this.codeContent = page.locator('.cm-content[data-language="kcl"]')
|
this.codeContent = page.locator('.cm-content')
|
||||||
this.diagnosticsTooltip = page.locator('.cm-tooltip-lint')
|
this.diagnosticsTooltip = page.locator('.cm-tooltip-lint')
|
||||||
this.diagnosticsGutterIcon = page.locator('.cm-lint-marker-error')
|
this.diagnosticsGutterIcon = page.locator('.cm-lint-marker-error')
|
||||||
this.activeLine = this.page.locator('.cm-activeLine')
|
this.activeLine = this.page.locator('.cm-activeLine')
|
||||||
@ -54,13 +54,13 @@ export class EditorFixture {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!shouldNormalise) {
|
if (!shouldNormalise) {
|
||||||
const expectStart = expect.poll(() => this.codeContent.textContent())
|
const expectStart = expect(this.codeContent)
|
||||||
if (not) {
|
if (not) {
|
||||||
const result = await expectStart.not.toContain(code)
|
const result = await expectStart.not.toContainText(code, { timeout })
|
||||||
await resetPane()
|
await resetPane()
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
const result = await expectStart.toContain(code)
|
const result = await expectStart.toContainText(code, { timeout })
|
||||||
await resetPane()
|
await resetPane()
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@ -147,20 +147,4 @@ export class EditorFixture {
|
|||||||
openPane() {
|
openPane() {
|
||||||
return openPane(this.page, this.paneButtonTestId)
|
return openPane(this.page, this.paneButtonTestId)
|
||||||
}
|
}
|
||||||
scrollToText(text: string) {
|
|
||||||
return this.page.evaluate((scrollToText: string) => {
|
|
||||||
// editorManager is available on the window object.
|
|
||||||
// @ts-ignore
|
|
||||||
let index = editorManager._editorView.docView.view.state.doc
|
|
||||||
.toString()
|
|
||||||
.indexOf(scrollToText)
|
|
||||||
// @ts-ignore
|
|
||||||
editorManager._editorView.dispatch({
|
|
||||||
selection: {
|
|
||||||
anchor: index,
|
|
||||||
},
|
|
||||||
scrollIntoView: true,
|
|
||||||
})
|
|
||||||
}, text)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import type {
|
import type {
|
||||||
BrowserContext,
|
BrowserContext,
|
||||||
ElectronApplication,
|
ElectronApplication,
|
||||||
TestInfo,
|
|
||||||
Page,
|
Page,
|
||||||
|
TestInfo,
|
||||||
} from '@playwright/test'
|
} from '@playwright/test'
|
||||||
|
import { test as base } from '@playwright/test'
|
||||||
import { getUtils, setup, setupElectron } from '../test-utils'
|
import { getUtils, setup, setupElectron, tearDown } from '../test-utils'
|
||||||
import fsp from 'fs/promises'
|
import fsp from 'fs/promises'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import { CmdBarFixture } from './cmdBarFixture'
|
import { CmdBarFixture } from './cmdBarFixture'
|
||||||
@ -20,11 +20,11 @@ export class AuthenticatedApp {
|
|||||||
public readonly page: Page
|
public readonly page: Page
|
||||||
public readonly context: BrowserContext
|
public readonly context: BrowserContext
|
||||||
public readonly testInfo: TestInfo
|
public readonly testInfo: TestInfo
|
||||||
public readonly viewPortSize = { width: 1200, height: 500 }
|
public readonly viewPortSize = { width: 1000, height: 500 }
|
||||||
|
|
||||||
constructor(context: BrowserContext, page: Page, testInfo: TestInfo) {
|
constructor(context: BrowserContext, page: Page, testInfo: TestInfo) {
|
||||||
this.context = context
|
|
||||||
this.page = page
|
this.page = page
|
||||||
|
this.context = context
|
||||||
this.testInfo = testInfo
|
this.testInfo = testInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,7 +49,9 @@ export class AuthenticatedApp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Fixtures {
|
interface Fixtures {
|
||||||
|
app: AuthenticatedApp
|
||||||
|
tronApp: AuthenticatedTronApp
|
||||||
cmdBar: CmdBarFixture
|
cmdBar: CmdBarFixture
|
||||||
editor: EditorFixture
|
editor: EditorFixture
|
||||||
toolbar: ToolbarFixture
|
toolbar: ToolbarFixture
|
||||||
@ -59,11 +61,9 @@ export interface Fixtures {
|
|||||||
export class AuthenticatedTronApp {
|
export class AuthenticatedTronApp {
|
||||||
public readonly _page: Page
|
public readonly _page: Page
|
||||||
public page: Page
|
public page: Page
|
||||||
public context: BrowserContext
|
public readonly context: BrowserContext
|
||||||
public readonly testInfo: TestInfo
|
public readonly testInfo: TestInfo
|
||||||
public electronApp?: ElectronApplication
|
public electronApp?: ElectronApplication
|
||||||
public readonly viewPortSize = { width: 1200, height: 500 }
|
|
||||||
public dir: string = ''
|
|
||||||
|
|
||||||
constructor(context: BrowserContext, page: Page, testInfo: TestInfo) {
|
constructor(context: BrowserContext, page: Page, testInfo: TestInfo) {
|
||||||
this._page = page
|
this._page = page
|
||||||
@ -79,22 +79,15 @@ export class AuthenticatedTronApp {
|
|||||||
appSettings?: Partial<SaveSettingsPayload>
|
appSettings?: Partial<SaveSettingsPayload>
|
||||||
} = { fixtures: {} }
|
} = { fixtures: {} }
|
||||||
) {
|
) {
|
||||||
const { electronApp, page, context, dir, options } = await setupElectron({
|
const { electronApp, page } = await setupElectron({
|
||||||
testInfo: this.testInfo,
|
testInfo: this.testInfo,
|
||||||
folderSetupFn: arg.folderSetupFn,
|
folderSetupFn: arg.folderSetupFn,
|
||||||
cleanProjectDir: arg.cleanProjectDir,
|
cleanProjectDir: arg.cleanProjectDir,
|
||||||
appSettings: arg.appSettings,
|
appSettings: arg.appSettings,
|
||||||
})
|
})
|
||||||
this.page = page
|
this.page = page
|
||||||
this.context = context
|
|
||||||
this.electronApp = electronApp
|
this.electronApp = electronApp
|
||||||
this.dir = dir
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
// Easier to access throughout utils
|
|
||||||
this.page.dir = dir
|
|
||||||
|
|
||||||
// Setup localStorage, addCookies, reload
|
|
||||||
await setup(this.context, this.page, this.testInfo)
|
|
||||||
|
|
||||||
for (const key of unsafeTypedKeys(arg.fixtures)) {
|
for (const key of unsafeTypedKeys(arg.fixtures)) {
|
||||||
const fixture = arg.fixtures[key]
|
const fixture = arg.fixtures[key]
|
||||||
@ -117,20 +110,32 @@ export class AuthenticatedTronApp {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const fixtures = {
|
export const test = base.extend<Fixtures>({
|
||||||
cmdBar: async ({ page }: { page: Page }, use: any) => {
|
app: async ({ page, context }, use, testInfo) => {
|
||||||
|
await use(new AuthenticatedApp(context, page, testInfo))
|
||||||
|
},
|
||||||
|
tronApp: async ({ page, context }, use, testInfo) => {
|
||||||
|
await use(new AuthenticatedTronApp(context, page, testInfo))
|
||||||
|
},
|
||||||
|
cmdBar: async ({ page }, use) => {
|
||||||
await use(new CmdBarFixture(page))
|
await use(new CmdBarFixture(page))
|
||||||
},
|
},
|
||||||
editor: async ({ page }: { page: Page }, use: any) => {
|
editor: async ({ page }, use) => {
|
||||||
await use(new EditorFixture(page))
|
await use(new EditorFixture(page))
|
||||||
},
|
},
|
||||||
toolbar: async ({ page }: { page: Page }, use: any) => {
|
toolbar: async ({ page }, use) => {
|
||||||
await use(new ToolbarFixture(page))
|
await use(new ToolbarFixture(page))
|
||||||
},
|
},
|
||||||
scene: async ({ page }: { page: Page }, use: any) => {
|
scene: async ({ page }, use) => {
|
||||||
await use(new SceneFixture(page))
|
await use(new SceneFixture(page))
|
||||||
},
|
},
|
||||||
homePage: async ({ page }: { page: Page }, use: any) => {
|
homePage: async ({ page }, use) => {
|
||||||
await use(new HomePageFixture(page))
|
await use(new HomePageFixture(page))
|
||||||
},
|
},
|
||||||
}
|
})
|
||||||
|
|
||||||
|
test.afterEach(async ({ page }, testInfo) => {
|
||||||
|
await tearDown(page, testInfo)
|
||||||
|
})
|
||||||
|
|
||||||
|
export { expect } from '@playwright/test'
|
||||||
|
@ -4,6 +4,7 @@ import { expect } from '@playwright/test'
|
|||||||
interface ProjectCardState {
|
interface ProjectCardState {
|
||||||
title: string
|
title: string
|
||||||
fileCount: number
|
fileCount: number
|
||||||
|
folderCount: number
|
||||||
}
|
}
|
||||||
|
|
||||||
interface HomePageState {
|
interface HomePageState {
|
||||||
@ -14,14 +15,10 @@ interface HomePageState {
|
|||||||
export class HomePageFixture {
|
export class HomePageFixture {
|
||||||
public page: Page
|
public page: Page
|
||||||
|
|
||||||
projectSection!: Locator
|
|
||||||
projectCard!: Locator
|
projectCard!: Locator
|
||||||
projectCardTitle!: Locator
|
projectCardTitle!: Locator
|
||||||
projectCardFile!: Locator
|
projectCardFile!: Locator
|
||||||
projectCardFolder!: Locator
|
projectCardFolder!: Locator
|
||||||
projectButtonNew!: Locator
|
|
||||||
projectButtonContinue!: Locator
|
|
||||||
projectTextName!: Locator
|
|
||||||
sortByDateBtn!: Locator
|
sortByDateBtn!: Locator
|
||||||
sortByNameBtn!: Locator
|
sortByNameBtn!: Locator
|
||||||
|
|
||||||
@ -32,19 +29,11 @@ export class HomePageFixture {
|
|||||||
reConstruct = (page: Page) => {
|
reConstruct = (page: Page) => {
|
||||||
this.page = page
|
this.page = page
|
||||||
|
|
||||||
this.projectSection = this.page.getByTestId('home-section')
|
|
||||||
|
|
||||||
this.projectCard = this.page.getByTestId('project-link')
|
this.projectCard = this.page.getByTestId('project-link')
|
||||||
this.projectCardTitle = this.page.getByTestId('project-title')
|
this.projectCardTitle = this.page.getByTestId('project-title')
|
||||||
this.projectCardFile = this.page.getByTestId('project-file-count')
|
this.projectCardFile = this.page.getByTestId('project-file-count')
|
||||||
this.projectCardFolder = this.page.getByTestId('project-folder-count')
|
this.projectCardFolder = this.page.getByTestId('project-folder-count')
|
||||||
|
|
||||||
this.projectButtonNew = this.page.getByTestId('home-new-file')
|
|
||||||
this.projectTextName = this.page.getByTestId('cmd-bar-arg-value')
|
|
||||||
this.projectButtonContinue = this.page.getByRole('button', {
|
|
||||||
name: 'Continue',
|
|
||||||
})
|
|
||||||
|
|
||||||
this.sortByDateBtn = this.page.getByTestId('home-sort-by-modified')
|
this.sortByDateBtn = this.page.getByTestId('home-sort-by-modified')
|
||||||
this.sortByNameBtn = this.page.getByTestId('home-sort-by-name')
|
this.sortByNameBtn = this.page.getByTestId('home-sort-by-name')
|
||||||
}
|
}
|
||||||
@ -72,13 +61,15 @@ export class HomePageFixture {
|
|||||||
const projectCards = await this.projectCard.all()
|
const projectCards = await this.projectCard.all()
|
||||||
const projectCardStates: Array<ProjectCardState> = []
|
const projectCardStates: Array<ProjectCardState> = []
|
||||||
for (const projectCard of projectCards) {
|
for (const projectCard of projectCards) {
|
||||||
const [title, fileCount] = await Promise.all([
|
const [title, fileCount, folderCount] = await Promise.all([
|
||||||
(await projectCard.locator(this.projectCardTitle).textContent()) || '',
|
(await projectCard.locator(this.projectCardTitle).textContent()) || '',
|
||||||
Number(await projectCard.locator(this.projectCardFile).textContent()),
|
Number(await projectCard.locator(this.projectCardFile).textContent()),
|
||||||
|
Number(await projectCard.locator(this.projectCardFolder).textContent()),
|
||||||
])
|
])
|
||||||
projectCardStates.push({
|
projectCardStates.push({
|
||||||
title: title,
|
title: title,
|
||||||
fileCount,
|
fileCount,
|
||||||
|
folderCount,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return projectCardStates
|
return projectCardStates
|
||||||
@ -103,25 +94,10 @@ export class HomePageFixture {
|
|||||||
.toEqual(expectedState)
|
.toEqual(expectedState)
|
||||||
}
|
}
|
||||||
|
|
||||||
createAndGoToProject = async (projectTitle: string) => {
|
|
||||||
await expect(this.projectSection).not.toHaveText('Loading your Projects...')
|
|
||||||
await this.projectButtonNew.click()
|
|
||||||
await this.projectTextName.click()
|
|
||||||
await this.projectTextName.fill(projectTitle)
|
|
||||||
await this.projectButtonContinue.click()
|
|
||||||
}
|
|
||||||
|
|
||||||
openProject = async (projectTitle: string) => {
|
openProject = async (projectTitle: string) => {
|
||||||
const projectCard = this.projectCard.locator(
|
const projectCard = this.projectCard.locator(
|
||||||
this.page.getByText(projectTitle)
|
this.page.getByText(projectTitle)
|
||||||
)
|
)
|
||||||
await projectCard.click()
|
await projectCard.click()
|
||||||
}
|
}
|
||||||
|
|
||||||
goToModelingScene = async (name: string = 'testDefault') => {
|
|
||||||
// On web this is a no-op. There is no project view.
|
|
||||||
if (process.env.PLATFORM === 'web') return
|
|
||||||
|
|
||||||
await this.createAndGoToProject(name)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,6 @@ type SceneSerialised = {
|
|||||||
|
|
||||||
type ClickHandler = (clickParams?: mouseParams) => Promise<void | boolean>
|
type ClickHandler = (clickParams?: mouseParams) => Promise<void | boolean>
|
||||||
type MoveHandler = (moveParams?: mouseParams) => Promise<void | boolean>
|
type MoveHandler = (moveParams?: mouseParams) => Promise<void | boolean>
|
||||||
type DblClickHandler = (clickParams?: mouseParams) => Promise<void | boolean>
|
|
||||||
type DragToHandler = (dragParams: mouseDragToParams) => Promise<void | boolean>
|
type DragToHandler = (dragParams: mouseDragToParams) => Promise<void | boolean>
|
||||||
type DragFromHandler = (
|
type DragFromHandler = (
|
||||||
dragParams: mouseDragFromParams
|
dragParams: mouseDragFromParams
|
||||||
@ -53,9 +52,8 @@ export class SceneFixture {
|
|||||||
|
|
||||||
expectState = async (expected: SceneSerialised) => {
|
expectState = async (expected: SceneSerialised) => {
|
||||||
return expect
|
return expect
|
||||||
.poll(async () => await this._serialiseScene(), {
|
.poll(() => this._serialiseScene(), {
|
||||||
intervals: [1_000, 2_000, 10_000],
|
message: `Expected scene state to match`,
|
||||||
timeout: 60000,
|
|
||||||
})
|
})
|
||||||
.toEqual(expected)
|
.toEqual(expected)
|
||||||
}
|
}
|
||||||
@ -70,7 +68,7 @@ export class SceneFixture {
|
|||||||
x: number,
|
x: number,
|
||||||
y: number,
|
y: number,
|
||||||
{ steps }: { steps: number } = { steps: 20 }
|
{ steps }: { steps: number } = { steps: 20 }
|
||||||
): [ClickHandler, MoveHandler, DblClickHandler] =>
|
): [ClickHandler, MoveHandler] =>
|
||||||
[
|
[
|
||||||
(clickParams?: mouseParams) => {
|
(clickParams?: mouseParams) => {
|
||||||
if (clickParams?.pixelDiff) {
|
if (clickParams?.pixelDiff) {
|
||||||
@ -92,16 +90,6 @@ export class SceneFixture {
|
|||||||
}
|
}
|
||||||
return this.page.mouse.move(x, y, { steps })
|
return this.page.mouse.move(x, y, { steps })
|
||||||
},
|
},
|
||||||
(clickParams?: mouseParams) => {
|
|
||||||
if (clickParams?.pixelDiff) {
|
|
||||||
return doAndWaitForImageDiff(
|
|
||||||
this.page,
|
|
||||||
() => this.page.mouse.dblclick(x, y),
|
|
||||||
clickParams.pixelDiff
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return this.page.mouse.dblclick(x, y)
|
|
||||||
},
|
|
||||||
] as const
|
] as const
|
||||||
makeDragHelpers = (
|
makeDragHelpers = (
|
||||||
x: number,
|
x: number,
|
||||||
@ -188,10 +176,7 @@ export class SceneFixture {
|
|||||||
type: 'default_camera_get_settings',
|
type: 'default_camera_get_settings',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
await this.page
|
await this.waitForExecutionDone()
|
||||||
.locator(`[data-receive-command-type="default_camera_get_settings"]`)
|
|
||||||
.first()
|
|
||||||
.waitFor()
|
|
||||||
const position = await Promise.all([
|
const position = await Promise.all([
|
||||||
this.page.getByTestId('cam-x-position').inputValue().then(Number),
|
this.page.getByTestId('cam-x-position').inputValue().then(Number),
|
||||||
this.page.getByTestId('cam-y-position').inputValue().then(Number),
|
this.page.getByTestId('cam-y-position').inputValue().then(Number),
|
||||||
@ -242,7 +227,6 @@ export class SceneFixture {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async clickGizmoMenuItem(name: string) {
|
async clickGizmoMenuItem(name: string) {
|
||||||
await this.gizmo.hover()
|
|
||||||
await this.gizmo.click({ button: 'right' })
|
await this.gizmo.click({ button: 'right' })
|
||||||
const buttonToTest = this.page.getByRole('button', {
|
const buttonToTest = this.page.getByRole('button', {
|
||||||
name: name,
|
name: name,
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
import type { Page, Locator } from '@playwright/test'
|
import type { Page, Locator } from '@playwright/test'
|
||||||
import { expect } from '../zoo-test'
|
import { expect } from './fixtureSetup'
|
||||||
import { doAndWaitForImageDiff } from '../test-utils'
|
import { doAndWaitForImageDiff } from '../test-utils'
|
||||||
|
|
||||||
export class ToolbarFixture {
|
export class ToolbarFixture {
|
||||||
public page: Page
|
public page: Page
|
||||||
|
|
||||||
extrudeButton!: Locator
|
extrudeButton!: Locator
|
||||||
loftButton!: Locator
|
|
||||||
shellButton!: Locator
|
|
||||||
offsetPlaneButton!: Locator
|
offsetPlaneButton!: Locator
|
||||||
startSketchBtn!: Locator
|
startSketchBtn!: Locator
|
||||||
lineBtn!: Locator
|
lineBtn!: Locator
|
||||||
@ -28,8 +26,6 @@ export class ToolbarFixture {
|
|||||||
reConstruct = (page: Page) => {
|
reConstruct = (page: Page) => {
|
||||||
this.page = page
|
this.page = page
|
||||||
this.extrudeButton = page.getByTestId('extrude')
|
this.extrudeButton = page.getByTestId('extrude')
|
||||||
this.loftButton = page.getByTestId('loft')
|
|
||||||
this.shellButton = page.getByTestId('shell')
|
|
||||||
this.offsetPlaneButton = page.getByTestId('plane-offset')
|
this.offsetPlaneButton = page.getByTestId('plane-offset')
|
||||||
this.startSketchBtn = page.getByTestId('sketch')
|
this.startSketchBtn = page.getByTestId('sketch')
|
||||||
this.lineBtn = page.getByTestId('line')
|
this.lineBtn = page.getByTestId('line')
|
||||||
|
@ -1,22 +1,29 @@
|
|||||||
import { test, expect } from './zoo-test'
|
import { test, expect } from '@playwright/test'
|
||||||
import { executorInputPath } from './test-utils'
|
import { setupElectron, tearDown, executorInputPath } from './test-utils'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import fsp from 'fs/promises'
|
import fsp from 'fs/promises'
|
||||||
|
|
||||||
|
test.afterEach(async ({ page }, testInfo) => {
|
||||||
|
await tearDown(page, testInfo)
|
||||||
|
})
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'When machine-api server not found butt is disabled and shows the reason',
|
'When machine-api server not found butt is disabled and shows the reason',
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ context, page }, testInfo) => {
|
async ({ browserName }, testInfo) => {
|
||||||
await context.folderSetupFn(async (dir) => {
|
const { electronApp, page } = await setupElectron({
|
||||||
|
testInfo,
|
||||||
|
folderSetupFn: async (dir) => {
|
||||||
const bracketDir = join(dir, 'bracket')
|
const bracketDir = join(dir, 'bracket')
|
||||||
await fsp.mkdir(bracketDir, { recursive: true })
|
await fsp.mkdir(bracketDir, { recursive: true })
|
||||||
await fsp.copyFile(
|
await fsp.copyFile(
|
||||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||||
join(bracketDir, 'main.kcl')
|
join(bracketDir, 'main.kcl')
|
||||||
)
|
)
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await expect(page.getByText('bracket')).toBeVisible()
|
await expect(page.getByText('bracket')).toBeVisible()
|
||||||
|
|
||||||
@ -40,23 +47,28 @@ test(
|
|||||||
// that the machine-api server is not found
|
// that the machine-api server is not found
|
||||||
await makeButton.hover()
|
await makeButton.hover()
|
||||||
await expect(page.getByText(notFoundText).first()).toBeVisible()
|
await expect(page.getByText(notFoundText).first()).toBeVisible()
|
||||||
|
|
||||||
|
await electronApp.close()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'When machine-api server not found home screen & project status shows the reason',
|
'When machine-api server not found home screen & project status shows the reason',
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ context, page }, testInfo) => {
|
async ({ browserName }, testInfo) => {
|
||||||
await context.folderSetupFn(async (dir) => {
|
const { electronApp, page } = await setupElectron({
|
||||||
|
testInfo,
|
||||||
|
folderSetupFn: async (dir) => {
|
||||||
const bracketDir = join(dir, 'bracket')
|
const bracketDir = join(dir, 'bracket')
|
||||||
await fsp.mkdir(bracketDir, { recursive: true })
|
await fsp.mkdir(bracketDir, { recursive: true })
|
||||||
await fsp.copyFile(
|
await fsp.copyFile(
|
||||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||||
join(bracketDir, 'main.kcl')
|
join(bracketDir, 'main.kcl')
|
||||||
)
|
)
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
const notFoundText = 'Machine API server was not discovered'
|
const notFoundText = 'Machine API server was not discovered'
|
||||||
|
|
||||||
@ -79,5 +91,7 @@ test(
|
|||||||
|
|
||||||
await networkMachineToggle.hover()
|
await networkMachineToggle.hover()
|
||||||
await expect(page.getByText(notFoundText).nth(1)).toBeVisible()
|
await expect(page.getByText(notFoundText).nth(1)).toBeVisible()
|
||||||
|
|
||||||
|
await electronApp.close()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
// These tests are meant to simply test starting and stopping the electron
|
|
||||||
// application, check it can make it to the project pane, and nothing more.
|
|
||||||
// It also tests our test wrappers are working.
|
|
||||||
// Additionally this serves as a nice minimal example.
|
|
||||||
|
|
||||||
import { test, expect } from './zoo-test'
|
|
||||||
|
|
||||||
test.describe('Open the application', () => {
|
|
||||||
test('see the project view', async ({ page, context }) => {
|
|
||||||
await expect(page.getByTestId('home-section')).toBeVisible()
|
|
||||||
})
|
|
||||||
})
|
|
@ -1,63 +1,79 @@
|
|||||||
import { test, expect } from './zoo-test'
|
import { test, expect } from '@playwright/test'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import fsp from 'fs/promises'
|
import fsp from 'fs/promises'
|
||||||
import { getUtils, executorInputPath, createProject } from './test-utils'
|
import {
|
||||||
|
getUtils,
|
||||||
|
setup,
|
||||||
|
setupElectron,
|
||||||
|
tearDown,
|
||||||
|
executorInputPath,
|
||||||
|
createProject,
|
||||||
|
} from './test-utils'
|
||||||
import { bracket } from 'lib/exampleKcl'
|
import { bracket } from 'lib/exampleKcl'
|
||||||
import { onboardingPaths } from 'routes/Onboarding/paths'
|
import { onboardingPaths } from 'routes/Onboarding/paths'
|
||||||
import {
|
import {
|
||||||
TEST_SETTINGS_KEY,
|
TEST_SETTINGS_KEY,
|
||||||
TEST_SETTINGS_ONBOARDING_START,
|
TEST_SETTINGS_ONBOARDING_START,
|
||||||
TEST_SETTINGS_ONBOARDING_EXPORT,
|
TEST_SETTINGS_ONBOARDING_EXPORT,
|
||||||
|
TEST_SETTINGS_ONBOARDING_PARAMETRIC_MODELING,
|
||||||
TEST_SETTINGS_ONBOARDING_USER_MENU,
|
TEST_SETTINGS_ONBOARDING_USER_MENU,
|
||||||
} from './storageStates'
|
} from './storageStates'
|
||||||
import * as TOML from '@iarna/toml'
|
import * as TOML from '@iarna/toml'
|
||||||
|
|
||||||
// Because onboarding relies on an app setting we need to set it as incompletel
|
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||||
// for all these tests.
|
if (testInfo.tags.includes('@electron')) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
await setup(context, page)
|
||||||
|
})
|
||||||
|
|
||||||
|
test.afterEach(async ({ page }, testInfo) => {
|
||||||
|
await tearDown(page, testInfo)
|
||||||
|
})
|
||||||
|
|
||||||
test.describe('Onboarding tests', () => {
|
test.describe('Onboarding tests', () => {
|
||||||
test(
|
test('Onboarding code is shown in the editor', async ({ page }) => {
|
||||||
'Onboarding code is shown in the editor',
|
const u = await getUtils(page)
|
||||||
{
|
|
||||||
appSettings: {
|
// Override beforeEach test setup
|
||||||
app: {
|
await page.addInitScript(
|
||||||
onboardingStatus: 'incomplete',
|
async ({ settingsKey }) => {
|
||||||
|
// Give no initial code, so that the onboarding start is shown immediately
|
||||||
|
localStorage.removeItem('persistCode')
|
||||||
|
localStorage.removeItem(settingsKey)
|
||||||
},
|
},
|
||||||
},
|
{ settingsKey: TEST_SETTINGS_KEY }
|
||||||
cleanProjectDir: true,
|
)
|
||||||
},
|
|
||||||
async ({ context, page, homePage }) => {
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
// Test that the onboarding pane loaded
|
// Test that the onboarding pane loaded
|
||||||
await expect(
|
await expect(page.getByText('Welcome to Modeling App! This')).toBeVisible()
|
||||||
page.getByText('Welcome to Modeling App! This')
|
|
||||||
).toBeVisible()
|
|
||||||
|
|
||||||
// *and* that the code is shown in the editor
|
// *and* that the code is shown in the editor
|
||||||
await expect(page.locator('.cm-content')).toContainText(
|
await expect(page.locator('.cm-content')).toContainText('// Shelf Bracket')
|
||||||
'// Shelf Bracket'
|
})
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'Desktop: fresh onboarding executes and loads',
|
'Desktop: fresh onboarding executes and loads',
|
||||||
{
|
{ tag: '@electron' },
|
||||||
tag: '@electron',
|
async ({ browserName: _ }, testInfo) => {
|
||||||
|
const { electronApp, page } = await setupElectron({
|
||||||
|
testInfo,
|
||||||
appSettings: {
|
appSettings: {
|
||||||
app: {
|
app: {
|
||||||
onboardingStatus: 'incomplete',
|
onboardingStatus: 'incomplete',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
cleanProjectDir: true,
|
cleanProjectDir: true,
|
||||||
},
|
})
|
||||||
async ({ page, homePage }, testInfo) => {
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
|
|
||||||
const viewportSize = { width: 1200, height: 500 }
|
const viewportSize = { width: 1200, height: 500 }
|
||||||
await page.setBodyDimensions(viewportSize)
|
await page.setViewportSize(viewportSize)
|
||||||
|
|
||||||
await test.step(`Create a project and open to the onboarding`, async () => {
|
await test.step(`Create a project and open to the onboarding`, async () => {
|
||||||
await createProject({ name: 'project-link', page })
|
await createProject({ name: 'project-link', page })
|
||||||
@ -77,71 +93,60 @@ test.describe('Onboarding tests', () => {
|
|||||||
'// Shelf Bracket'
|
'// Shelf Bracket'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
await electronApp.close()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
test(
|
test('Code resets after confirmation', async ({ page }) => {
|
||||||
'Code resets after confirmation',
|
|
||||||
{
|
|
||||||
appSettings: {
|
|
||||||
app: {
|
|
||||||
onboardingStatus: 'incomplete',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
cleanProjectDir: true,
|
|
||||||
},
|
|
||||||
async ({ context, page, homePage }) => {
|
|
||||||
const initialCode = `sketch001 = startSketchOn('XZ')`
|
const initialCode = `sketch001 = startSketchOn('XZ')`
|
||||||
|
|
||||||
// Load the page up with some code so we see the confirmation warning
|
// Load the page up with some code so we see the confirmation warning
|
||||||
// when we go to replay onboarding
|
// when we go to replay onboarding
|
||||||
await context.addInitScript((code) => {
|
await page.addInitScript((code) => {
|
||||||
localStorage.setItem('persistCode', code)
|
localStorage.setItem('persistCode', code)
|
||||||
}, initialCode)
|
}, initialCode)
|
||||||
|
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
const u = await getUtils(page)
|
||||||
await homePage.goToModelingScene()
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
// Replay the onboarding
|
// Replay the onboarding
|
||||||
await page.getByRole('link', { name: 'Settings' }).last().click()
|
await page.getByRole('link', { name: 'Settings' }).last().click()
|
||||||
const replayButton = page.getByRole('button', {
|
const replayButton = page.getByRole('button', { name: 'Replay onboarding' })
|
||||||
name: 'Replay onboarding',
|
|
||||||
})
|
|
||||||
await expect(replayButton).toBeVisible()
|
await expect(replayButton).toBeVisible()
|
||||||
await replayButton.click()
|
await replayButton.click()
|
||||||
|
|
||||||
// Ensure we see the warning, and that the code has not yet updated
|
// Ensure we see the warning, and that the code has not yet updated
|
||||||
await expect(page.getByText('Would you like to create')).toBeVisible()
|
await expect(
|
||||||
|
page.getByText('Replaying onboarding resets your code')
|
||||||
|
).toBeVisible()
|
||||||
await expect(page.locator('.cm-content')).toHaveText(initialCode)
|
await expect(page.locator('.cm-content')).toHaveText(initialCode)
|
||||||
|
|
||||||
const nextButton = page.getByTestId('onboarding-next')
|
const nextButton = page.getByTestId('onboarding-next')
|
||||||
await nextButton.hover()
|
await expect(nextButton).toBeVisible()
|
||||||
await nextButton.click()
|
await nextButton.click()
|
||||||
|
|
||||||
// Ensure we see the introduction and that the code has been reset
|
// Ensure we see the introduction and that the code has been reset
|
||||||
await expect(page.getByText('Welcome to Modeling App!')).toBeVisible()
|
await expect(page.getByText('Welcome to Modeling App!')).toBeVisible()
|
||||||
await expect(page.locator('.cm-content')).toContainText(
|
await expect(page.locator('.cm-content')).toContainText('// Shelf Bracket')
|
||||||
'// Shelf Bracket'
|
|
||||||
)
|
|
||||||
|
|
||||||
// There used to be old code here that checked if we stored the reset
|
// Ensure we persisted the code to local storage.
|
||||||
// code into localStorage but that isnt the case on desktop. It gets
|
// Playwright's addInitScript method unfortunately will reset
|
||||||
// saved to the file system, which we have other tests for.
|
// this code if we try reloading the page as a test,
|
||||||
}
|
// so this is our best way to test persistence afaik.
|
||||||
)
|
expect(
|
||||||
|
await page.evaluate(() => {
|
||||||
|
return localStorage.getItem('persistCode')
|
||||||
|
})
|
||||||
|
).toContain('// Shelf Bracket')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Click through each onboarding step', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
|
||||||
test(
|
|
||||||
'Click through each onboarding step',
|
|
||||||
{
|
|
||||||
appSettings: {
|
|
||||||
app: {
|
|
||||||
onboardingStatus: 'incomplete',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
async ({ context, page, homePage }) => {
|
|
||||||
// Override beforeEach test setup
|
// Override beforeEach test setup
|
||||||
await context.addInitScript(
|
await page.addInitScript(
|
||||||
async ({ settingsKey, settings }) => {
|
async ({ settingsKey, settings }) => {
|
||||||
// Give no initial code, so that the onboarding start is shown immediately
|
// Give no initial code, so that the onboarding start is shown immediately
|
||||||
localStorage.setItem('persistCode', '')
|
localStorage.setItem('persistCode', '')
|
||||||
@ -149,113 +154,107 @@ test.describe('Onboarding tests', () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
settingsKey: TEST_SETTINGS_KEY,
|
settingsKey: TEST_SETTINGS_KEY,
|
||||||
settings: TOML.stringify({
|
settings: TOML.stringify({ settings: TEST_SETTINGS_ONBOARDING_START }),
|
||||||
settings: TEST_SETTINGS_ONBOARDING_START,
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
await page.setBodyDimensions({ width: 1200, height: 1080 })
|
await page.setViewportSize({ width: 1200, height: 1080 })
|
||||||
await homePage.goToModelingScene()
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
// Test that the onboarding pane loaded
|
// Test that the onboarding pane loaded
|
||||||
await expect(
|
await expect(page.getByText('Welcome to Modeling App! This')).toBeVisible()
|
||||||
page.getByText('Welcome to Modeling App! This')
|
|
||||||
).toBeVisible()
|
|
||||||
|
|
||||||
const nextButton = page.getByTestId('onboarding-next')
|
const nextButton = page.getByTestId('onboarding-next')
|
||||||
|
|
||||||
while ((await nextButton.innerText()) !== 'Finish') {
|
while ((await nextButton.innerText()) !== 'Finish') {
|
||||||
await nextButton.hover()
|
await expect(nextButton).toBeVisible()
|
||||||
await nextButton.click()
|
await nextButton.click()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finish the onboarding
|
// Finish the onboarding
|
||||||
await nextButton.hover()
|
await expect(nextButton).toBeVisible()
|
||||||
await nextButton.click()
|
await nextButton.click()
|
||||||
|
|
||||||
// Test that the onboarding pane is gone
|
// Test that the onboarding pane is gone
|
||||||
await expect(page.getByTestId('onboarding-content')).not.toBeVisible()
|
await expect(page.getByTestId('onboarding-content')).not.toBeVisible()
|
||||||
await expect.poll(() => page.url()).not.toContain('/onboarding')
|
await expect(page.url()).not.toContain('onboarding')
|
||||||
}
|
})
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
test('Onboarding redirects and code updating', async ({ page }) => {
|
||||||
'Onboarding redirects and code updating',
|
const u = await getUtils(page)
|
||||||
{
|
|
||||||
appSettings: {
|
|
||||||
app: {
|
|
||||||
onboardingStatus: '/export',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
cleanProjectDir: true,
|
|
||||||
},
|
|
||||||
async ({ context, page, homePage }) => {
|
|
||||||
const originalCode = 'sigmaAllow = 15000'
|
|
||||||
|
|
||||||
// Override beforeEach test setup
|
// Override beforeEach test setup
|
||||||
await context.addInitScript(
|
await page.addInitScript(
|
||||||
async ({ settingsKey, settings }) => {
|
async ({ settingsKey, settings }) => {
|
||||||
// Give some initial code, so we can test that it's cleared
|
// Give some initial code, so we can test that it's cleared
|
||||||
localStorage.setItem('persistCode', originalCode)
|
localStorage.setItem('persistCode', 'sigmaAllow = 15000')
|
||||||
localStorage.setItem(settingsKey, settings)
|
localStorage.setItem(settingsKey, settings)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
settingsKey: TEST_SETTINGS_KEY,
|
settingsKey: TEST_SETTINGS_KEY,
|
||||||
settings: TOML.stringify({
|
settings: TOML.stringify({ settings: TEST_SETTINGS_ONBOARDING_EXPORT }),
|
||||||
settings: TEST_SETTINGS_ONBOARDING_EXPORT,
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
await homePage.goToModelingScene()
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
// Test that the redirect happened
|
// Test that the redirect happened
|
||||||
await expect.poll(() => page.url()).toContain('/onboarding/export')
|
await expect(page.url().split(':3000').slice(-1)[0]).toBe(
|
||||||
|
`/file/%2Fbrowser%2Fmain.kcl/onboarding/export`
|
||||||
|
)
|
||||||
|
|
||||||
// Test that you come back to this page when you refresh
|
// Test that you come back to this page when you refresh
|
||||||
await page.reload()
|
await page.reload()
|
||||||
await expect.poll(() => page.url()).toContain('/onboarding/export')
|
await expect(page.url().split(':3000').slice(-1)[0]).toBe(
|
||||||
|
`/file/%2Fbrowser%2Fmain.kcl/onboarding/export`
|
||||||
// Test that the code changes when you advance to the next step
|
)
|
||||||
await page.getByTestId('onboarding-next').hover()
|
|
||||||
await page.getByTestId('onboarding-next').click()
|
|
||||||
|
|
||||||
// Test that the onboarding pane loaded
|
// Test that the onboarding pane loaded
|
||||||
const title = page.locator('[data-testid="onboarding-content"]')
|
const title = page.locator('[data-testid="onboarding-content"]')
|
||||||
await expect(title).toBeAttached()
|
await expect(title).toBeAttached()
|
||||||
|
|
||||||
await expect(page.locator('.cm-content')).not.toHaveText(originalCode)
|
// Test that the code changes when you advance to the next step
|
||||||
|
await page.locator('[data-testid="onboarding-next"]').click()
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText('')
|
||||||
|
|
||||||
// Test that the code is not empty when you click on the next step
|
// Test that the code is not empty when you click on the next step
|
||||||
await page.locator('[data-testid="onboarding-next"]').hover()
|
|
||||||
await page.locator('[data-testid="onboarding-next"]').click()
|
await page.locator('[data-testid="onboarding-next"]').click()
|
||||||
await expect(page.locator('.cm-content')).toHaveText(/.+/)
|
await expect(page.locator('.cm-content')).toHaveText(/.+/)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Onboarding code gets reset to demo on Interactive Numbers step', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
test.skip(
|
||||||
|
process.platform === 'darwin',
|
||||||
|
"Skip on macOS, because Playwright isn't behaving the same as the actual browser"
|
||||||
|
)
|
||||||
|
const u = await getUtils(page)
|
||||||
|
const badCode = `// This is bad code we shouldn't see`
|
||||||
|
// Override beforeEach test setup
|
||||||
|
await page.addInitScript(
|
||||||
|
async ({ settingsKey, settings, badCode }) => {
|
||||||
|
localStorage.setItem('persistCode', badCode)
|
||||||
|
localStorage.setItem(settingsKey, settings)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
settingsKey: TEST_SETTINGS_KEY,
|
||||||
|
settings: TOML.stringify({
|
||||||
|
settings: TEST_SETTINGS_ONBOARDING_PARAMETRIC_MODELING,
|
||||||
|
}),
|
||||||
|
badCode,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
test(
|
await page.setViewportSize({ width: 1200, height: 1080 })
|
||||||
'Onboarding code gets reset to demo on Interactive Numbers step',
|
await u.waitForAuthSkipAppStart()
|
||||||
{
|
|
||||||
appSettings: {
|
|
||||||
app: {
|
|
||||||
onboardingStatus: '/parametric-modeling',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
cleanProjectDir: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
async ({ context, page, homePage }) => {
|
await page.waitForURL('**' + onboardingPaths.PARAMETRIC_MODELING, {
|
||||||
const u = await getUtils(page)
|
waitUntil: 'domcontentloaded',
|
||||||
const badCode = `// This is bad code we shouldn't see`
|
})
|
||||||
|
|
||||||
await page.setBodyDimensions({ width: 1200, height: 1080 })
|
|
||||||
await homePage.goToModelingScene()
|
|
||||||
|
|
||||||
await expect
|
|
||||||
.poll(() => page.url())
|
|
||||||
.toContain(onboardingPaths.PARAMETRIC_MODELING)
|
|
||||||
|
|
||||||
const bracketNoNewLines = bracket.replace(/\n/g, '')
|
const bracketNoNewLines = bracket.replace(/\n/g, '')
|
||||||
|
|
||||||
@ -271,7 +270,6 @@ test.describe('Onboarding tests', () => {
|
|||||||
await expect(u.codeLocator).toHaveText(badCode)
|
await expect(u.codeLocator).toHaveText(badCode)
|
||||||
|
|
||||||
// Click to the next step
|
// Click to the next step
|
||||||
await page.locator('[data-testid="onboarding-next"]').hover()
|
|
||||||
await page.locator('[data-testid="onboarding-next"]').click()
|
await page.locator('[data-testid="onboarding-next"]').click()
|
||||||
await page.waitForURL('**' + onboardingPaths.INTERACTIVE_NUMBERS, {
|
await page.waitForURL('**' + onboardingPaths.INTERACTIVE_NUMBERS, {
|
||||||
waitUntil: 'domcontentloaded',
|
waitUntil: 'domcontentloaded',
|
||||||
@ -279,25 +277,13 @@ test.describe('Onboarding tests', () => {
|
|||||||
|
|
||||||
// Check that the code has been reset
|
// Check that the code has been reset
|
||||||
await expect(u.codeLocator).toHaveText(bracketNoNewLines)
|
await expect(u.codeLocator).toHaveText(bracketNoNewLines)
|
||||||
}
|
})
|
||||||
)
|
|
||||||
|
|
||||||
// (lee) The two avatar tests are weird because even on main, we don't have
|
test('Avatar text updates depending on image load success', async ({
|
||||||
// anything to do with the avatar inside the onboarding test. Due to the
|
page,
|
||||||
// low impact of an avatar not showing I'm changing this to fixme.
|
}) => {
|
||||||
test.fixme(
|
|
||||||
'Avatar text updates depending on image load success',
|
|
||||||
{
|
|
||||||
appSettings: {
|
|
||||||
app: {
|
|
||||||
onboardingStatus: 'incomplete',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
cleanProjectDir: true,
|
|
||||||
},
|
|
||||||
async ({ context, page, homePage }) => {
|
|
||||||
// Override beforeEach test setup
|
// Override beforeEach test setup
|
||||||
await context.addInitScript(
|
await page.addInitScript(
|
||||||
async ({ settingsKey, settings }) => {
|
async ({ settingsKey, settings }) => {
|
||||||
localStorage.setItem(settingsKey, settings)
|
localStorage.setItem(settingsKey, settings)
|
||||||
},
|
},
|
||||||
@ -309,8 +295,11 @@ test.describe('Onboarding tests', () => {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
const u = await getUtils(page)
|
||||||
await homePage.goToModelingScene()
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await page.waitForURL('**/file/**', { waitUntil: 'domcontentloaded' })
|
||||||
|
|
||||||
// Test that the text in this step is correct
|
// Test that the text in this step is correct
|
||||||
const avatarLocator = await page
|
const avatarLocator = await page
|
||||||
@ -338,16 +327,13 @@ test.describe('Onboarding tests', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// 404 the CI avatar image
|
// 404 the CI avatar image
|
||||||
await page.route(
|
await page.route('https://lh3.googleusercontent.com/**', async (route) => {
|
||||||
'https://lh3.googleusercontent.com/**',
|
|
||||||
async (route) => {
|
|
||||||
await route.fulfill({
|
await route.fulfill({
|
||||||
status: 404,
|
status: 404,
|
||||||
contentType: 'text/plain',
|
contentType: 'text/plain',
|
||||||
body: 'Not Found!',
|
body: 'Not Found!',
|
||||||
})
|
})
|
||||||
}
|
})
|
||||||
)
|
|
||||||
|
|
||||||
await page.reload({ waitUntil: 'domcontentloaded' })
|
await page.reload({ waitUntil: 'domcontentloaded' })
|
||||||
|
|
||||||
@ -355,22 +341,13 @@ test.describe('Onboarding tests', () => {
|
|||||||
await expect(avatarLocator).not.toBeVisible()
|
await expect(avatarLocator).not.toBeVisible()
|
||||||
await expect(onboardingOverlayLocator).toBeVisible()
|
await expect(onboardingOverlayLocator).toBeVisible()
|
||||||
await expect(onboardingOverlayLocator).toContainText('the menu button')
|
await expect(onboardingOverlayLocator).toContainText('the menu button')
|
||||||
}
|
})
|
||||||
)
|
|
||||||
|
|
||||||
test.fixme(
|
test("Avatar text doesn't mention avatar when no avatar", async ({
|
||||||
"Avatar text doesn't mention avatar when no avatar",
|
page,
|
||||||
{
|
}) => {
|
||||||
appSettings: {
|
|
||||||
app: {
|
|
||||||
onboardingStatus: 'incomplete',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
cleanProjectDir: true,
|
|
||||||
},
|
|
||||||
async ({ context, page, homePage }) => {
|
|
||||||
// Override beforeEach test setup
|
// Override beforeEach test setup
|
||||||
await context.addInitScript(
|
await page.addInitScript(
|
||||||
async ({ settingsKey, settings }) => {
|
async ({ settingsKey, settings }) => {
|
||||||
localStorage.setItem(settingsKey, settings)
|
localStorage.setItem(settingsKey, settings)
|
||||||
localStorage.setItem('FORCE_NO_IMAGE', 'FORCE_NO_IMAGE')
|
localStorage.setItem('FORCE_NO_IMAGE', 'FORCE_NO_IMAGE')
|
||||||
@ -383,8 +360,11 @@ test.describe('Onboarding tests', () => {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
const u = await getUtils(page)
|
||||||
await homePage.goToModelingScene()
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await page.waitForURL('**/file/**', { waitUntil: 'domcontentloaded' })
|
||||||
|
|
||||||
// Test that the text in this step is correct
|
// Test that the text in this step is correct
|
||||||
const sidebar = page.getByTestId('user-sidebar-toggle')
|
const sidebar = page.getByTestId('user-sidebar-toggle')
|
||||||
@ -410,28 +390,23 @@ test.describe('Onboarding tests', () => {
|
|||||||
for (const feature of userMenuFeatures) {
|
for (const feature of userMenuFeatures) {
|
||||||
await expect(onboardingOverlayLocator).toContainText(feature)
|
await expect(onboardingOverlayLocator).toContainText(feature)
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'Restarting onboarding on desktop takes one attempt',
|
'Restarting onboarding on desktop takes one attempt',
|
||||||
{
|
{ tag: '@electron' },
|
||||||
appSettings: {
|
async ({ browser: _ }, testInfo) => {
|
||||||
app: {
|
const { electronApp, page } = await setupElectron({
|
||||||
onboardingStatus: 'dismissed',
|
testInfo,
|
||||||
},
|
folderSetupFn: async (dir) => {
|
||||||
},
|
|
||||||
cleanProjectDir: true,
|
|
||||||
},
|
|
||||||
async ({ context, page, homePage }, testInfo) => {
|
|
||||||
await context.folderSetupFn(async (dir) => {
|
|
||||||
const routerTemplateDir = join(dir, 'router-template-slate')
|
const routerTemplateDir = join(dir, 'router-template-slate')
|
||||||
await fsp.mkdir(routerTemplateDir, { recursive: true })
|
await fsp.mkdir(routerTemplateDir, { recursive: true })
|
||||||
await fsp.copyFile(
|
await fsp.copyFile(
|
||||||
executorInputPath('router-template-slate.kcl'),
|
executorInputPath('router-template-slate.kcl'),
|
||||||
join(routerTemplateDir, 'main.kcl')
|
join(routerTemplateDir, 'main.kcl')
|
||||||
)
|
)
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
// Our constants
|
// Our constants
|
||||||
@ -443,8 +418,9 @@ test(
|
|||||||
const restartOnboardingButton = page.getByRole('button', {
|
const restartOnboardingButton = page.getByRole('button', {
|
||||||
name: 'Reset onboarding',
|
name: 'Reset onboarding',
|
||||||
})
|
})
|
||||||
const nextButton = page.getByTestId('onboarding-next')
|
const restartConfirmationButton = page.getByRole('button', {
|
||||||
|
name: 'Make a new project',
|
||||||
|
})
|
||||||
const tutorialProjectIndicator = page
|
const tutorialProjectIndicator = page
|
||||||
.getByTestId('project-sidebar-toggle')
|
.getByTestId('project-sidebar-toggle')
|
||||||
.filter({ hasText: 'Tutorial Project 00' })
|
.filter({ hasText: 'Tutorial Project 00' })
|
||||||
@ -463,7 +439,7 @@ test(
|
|||||||
})
|
})
|
||||||
|
|
||||||
await test.step('Navigate into project', async () => {
|
await test.step('Navigate into project', async () => {
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
page.on('console', console.log)
|
page.on('console', console.log)
|
||||||
|
|
||||||
@ -479,8 +455,8 @@ test(
|
|||||||
await helpMenuButton.click()
|
await helpMenuButton.click()
|
||||||
await restartOnboardingButton.click()
|
await restartOnboardingButton.click()
|
||||||
|
|
||||||
await nextButton.hover()
|
await expect(restartConfirmationButton).toBeVisible()
|
||||||
await nextButton.click()
|
await restartConfirmationButton.click()
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step('Confirm that the onboarding has restarted', async () => {
|
await test.step('Confirm that the onboarding has restarted', async () => {
|
||||||
@ -504,9 +480,11 @@ test(
|
|||||||
|
|
||||||
await restartOnboardingSettingsButton.click()
|
await restartOnboardingSettingsButton.click()
|
||||||
// Since the code is empty, we should not see the confirmation dialog
|
// Since the code is empty, we should not see the confirmation dialog
|
||||||
await expect(nextButton).not.toBeVisible()
|
await expect(restartConfirmationButton).not.toBeVisible()
|
||||||
await expect(tutorialProjectIndicator).toBeVisible()
|
await expect(tutorialProjectIndicator).toBeVisible()
|
||||||
await expect(tutorialModalText).toBeVisible()
|
await expect(tutorialModalText).toBeVisible()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
await electronApp.close()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -1,34 +1,20 @@
|
|||||||
import { test, expect, Page } from './zoo-test'
|
import { test, expect, AuthenticatedApp } from './fixtures/fixtureSetup'
|
||||||
import { EditorFixture } from './fixtures/editorFixture'
|
import { EditorFixture } from './fixtures/editorFixture'
|
||||||
import { SceneFixture } from './fixtures/sceneFixture'
|
import { SceneFixture } from './fixtures/sceneFixture'
|
||||||
import { ToolbarFixture } from './fixtures/toolbarFixture'
|
import { ToolbarFixture } from './fixtures/toolbarFixture'
|
||||||
import fs from 'node:fs/promises'
|
|
||||||
import path from 'node:path'
|
|
||||||
import { getUtils } from './test-utils'
|
|
||||||
|
|
||||||
// test file is for testing point an click code gen functionality that's not sketch mode related
|
// test file is for testing point an click code gen functionality that's not sketch mode related
|
||||||
|
|
||||||
test('verify extruding circle works', async ({
|
test(
|
||||||
context,
|
'verify extruding circle works',
|
||||||
homePage,
|
{ tag: ['@skipWin'] },
|
||||||
cmdBar,
|
async ({ app, cmdBar, editor, toolbar, scene }) => {
|
||||||
editor,
|
test.skip(
|
||||||
toolbar,
|
process.platform === 'win32',
|
||||||
scene,
|
'Fails on windows in CI, can not be replicated locally on windows.'
|
||||||
}) => {
|
|
||||||
const file = await fs.readFile(
|
|
||||||
path.resolve(
|
|
||||||
__dirname,
|
|
||||||
'../../',
|
|
||||||
'./src/wasm-lib/tests/executor/inputs/test-circle-extrude.kcl'
|
|
||||||
),
|
|
||||||
'utf-8'
|
|
||||||
)
|
)
|
||||||
await context.addInitScript((file) => {
|
const file = await app.getInputFile('test-circle-extrude.kcl')
|
||||||
localStorage.setItem('persistCode', file)
|
await app.initialise(file)
|
||||||
}, file)
|
|
||||||
await homePage.goToModelingScene()
|
|
||||||
|
|
||||||
const [clickCircle, moveToCircle] = scene.makeMouseHelpers(582, 217)
|
const [clickCircle, moveToCircle] = scene.makeMouseHelpers(582, 217)
|
||||||
|
|
||||||
await test.step('because there is sweepable geometry, verify extrude is enable when nothing is selected', async () => {
|
await test.step('because there is sweepable geometry, verify extrude is enable when nothing is selected', async () => {
|
||||||
@ -41,17 +27,7 @@ test('verify extruding circle works', async ({
|
|||||||
const circleSnippet =
|
const circleSnippet =
|
||||||
'circle({ center = [318.33, 168.1], radius = 182.8 }, %)'
|
'circle({ center = [318.33, 168.1], radius = 182.8 }, %)'
|
||||||
await editor.expectState({
|
await editor.expectState({
|
||||||
activeLines: ["constsketch002=startSketchOn('XZ')"],
|
activeLines: [],
|
||||||
highlightedCode: circleSnippet,
|
|
||||||
diagnostics: [],
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step('check code model connection works and that button is still enable once circle is selected ', async () => {
|
|
||||||
await moveToCircle()
|
|
||||||
const circleSnippet =
|
|
||||||
'circle({ center = [318.33, 168.1], radius = 182.8 }, %)'
|
|
||||||
await editor.expectState({
|
|
||||||
activeLines: ["constsketch002=startSketchOn('XZ')"],
|
|
||||||
highlightedCode: circleSnippet,
|
highlightedCode: circleSnippet,
|
||||||
diagnostics: [],
|
diagnostics: [],
|
||||||
})
|
})
|
||||||
@ -64,8 +40,6 @@ test('verify extruding circle works', async ({
|
|||||||
})
|
})
|
||||||
await expect(toolbar.extrudeButton).toBeEnabled()
|
await expect(toolbar.extrudeButton).toBeEnabled()
|
||||||
})
|
})
|
||||||
await expect(toolbar.extrudeButton).toBeEnabled()
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step('do extrude flow and check extrude code is added to editor', async () => {
|
await test.step('do extrude flow and check extrude code is added to editor', async () => {
|
||||||
await toolbar.extrudeButton.click()
|
await toolbar.extrudeButton.click()
|
||||||
@ -92,12 +66,13 @@ test('verify extruding circle works', async ({
|
|||||||
|
|
||||||
await editor.expectEditor.toContain(expectString)
|
await editor.expectEditor.toContain(expectString)
|
||||||
})
|
})
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
test.describe('verify sketch on chamfer works', () => {
|
test.describe('verify sketch on chamfer works', () => {
|
||||||
const _sketchOnAChamfer =
|
const _sketchOnAChamfer =
|
||||||
(
|
(
|
||||||
page: Page,
|
app: AuthenticatedApp,
|
||||||
editor: EditorFixture,
|
editor: EditorFixture,
|
||||||
toolbar: ToolbarFixture,
|
toolbar: ToolbarFixture,
|
||||||
scene: SceneFixture
|
scene: SceneFixture
|
||||||
@ -149,7 +124,7 @@ test.describe('verify sketch on chamfer works', () => {
|
|||||||
await toolbar.startSketchPlaneSelection()
|
await toolbar.startSketchPlaneSelection()
|
||||||
await clickChamfer()
|
await clickChamfer()
|
||||||
// timeout wait for engine animation is unavoidable
|
// timeout wait for engine animation is unavoidable
|
||||||
await page.waitForTimeout(1000)
|
await app.page.waitForTimeout(600)
|
||||||
await editor.expectEditor.toContain(afterChamferSelectSnippet)
|
await editor.expectEditor.toContain(afterChamferSelectSnippet)
|
||||||
})
|
})
|
||||||
await test.step('make sure a basic sketch can be added', async () => {
|
await test.step('make sure a basic sketch can be added', async () => {
|
||||||
@ -160,9 +135,7 @@ test.describe('verify sketch on chamfer works', () => {
|
|||||||
pixelDiff: 50,
|
pixelDiff: 50,
|
||||||
})
|
})
|
||||||
await rectangle2ndClick()
|
await rectangle2ndClick()
|
||||||
await editor.expectEditor.toContain(afterRectangle2ndClickSnippet, {
|
await editor.expectEditor.toContain(afterRectangle2ndClickSnippet)
|
||||||
shouldNormalise: true,
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step('Clean up so that `_sketchOnAChamfer` util can be called again', async () => {
|
await test.step('Clean up so that `_sketchOnAChamfer` util can be called again', async () => {
|
||||||
@ -177,29 +150,18 @@ test.describe('verify sketch on chamfer works', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
test('works on all edge selections and can break up multi edges in a chamfer array', async ({
|
test(
|
||||||
context,
|
'works on all edge selections and can break up multi edges in a chamfer array',
|
||||||
page,
|
{ tag: ['@skipWin'] },
|
||||||
homePage,
|
async ({ app, editor, toolbar, scene }) => {
|
||||||
editor,
|
test.skip(
|
||||||
toolbar,
|
process.platform === 'win32',
|
||||||
scene,
|
'Fails on windows in CI, can not be replicated locally on windows.'
|
||||||
}) => {
|
|
||||||
const file = await fs.readFile(
|
|
||||||
path.resolve(
|
|
||||||
__dirname,
|
|
||||||
'../../',
|
|
||||||
'./src/wasm-lib/tests/executor/inputs/e2e-can-sketch-on-chamfer.kcl'
|
|
||||||
),
|
|
||||||
'utf-8'
|
|
||||||
)
|
)
|
||||||
await context.addInitScript((file) => {
|
const file = await app.getInputFile('e2e-can-sketch-on-chamfer.kcl')
|
||||||
localStorage.setItem('persistCode', file)
|
await app.initialise(file)
|
||||||
}, file)
|
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
|
||||||
await homePage.goToModelingScene()
|
|
||||||
|
|
||||||
const sketchOnAChamfer = _sketchOnAChamfer(page, editor, toolbar, scene)
|
const sketchOnAChamfer = _sketchOnAChamfer(app, editor, toolbar, scene)
|
||||||
|
|
||||||
await sketchOnAChamfer({
|
await sketchOnAChamfer({
|
||||||
clickCoords: { x: 570, y: 220 },
|
clickCoords: { x: 570, y: 220 },
|
||||||
@ -213,7 +175,8 @@ test.describe('verify sketch on chamfer works', () => {
|
|||||||
getOppositeEdge(seg01)
|
getOppositeEdge(seg01)
|
||||||
]}, %)`,
|
]}, %)`,
|
||||||
|
|
||||||
afterChamferSelectSnippet: 'sketch002 = startSketchOn(extrude001, seg03)',
|
afterChamferSelectSnippet:
|
||||||
|
'sketch002 = startSketchOn(extrude001, seg03)',
|
||||||
afterRectangle1stClickSnippet: 'startProfileAt([205.96, 254.59], %)',
|
afterRectangle1stClickSnippet: 'startProfileAt([205.96, 254.59], %)',
|
||||||
afterRectangle2ndClickSnippet: `angledLine([0, 11.39], %, $rectangleSegmentA002)
|
afterRectangle2ndClickSnippet: `angledLine([0, 11.39], %, $rectangleSegmentA002)
|
||||||
|> angledLine([
|
|> angledLine([
|
||||||
@ -244,7 +207,8 @@ test.describe('verify sketch on chamfer works', () => {
|
|||||||
]
|
]
|
||||||
}, %)`,
|
}, %)`,
|
||||||
|
|
||||||
afterChamferSelectSnippet: 'sketch003 = startSketchOn(extrude001, seg04)',
|
afterChamferSelectSnippet:
|
||||||
|
'sketch003 = startSketchOn(extrude001, seg04)',
|
||||||
afterRectangle1stClickSnippet: 'startProfileAt([-209.64, 255.28], %)',
|
afterRectangle1stClickSnippet: 'startProfileAt([-209.64, 255.28], %)',
|
||||||
afterRectangle2ndClickSnippet: `angledLine([0, 11.56], %, $rectangleSegmentA003)
|
afterRectangle2ndClickSnippet: `angledLine([0, 11.56], %, $rectangleSegmentA003)
|
||||||
|> angledLine([
|
|> angledLine([
|
||||||
@ -269,8 +233,9 @@ test.describe('verify sketch on chamfer works', () => {
|
|||||||
getNextAdjacentEdge(seg02)
|
getNextAdjacentEdge(seg02)
|
||||||
]
|
]
|
||||||
}, %)`,
|
}, %)`,
|
||||||
afterChamferSelectSnippet: 'sketch003 = startSketchOn(extrude001, seg04)',
|
afterChamferSelectSnippet:
|
||||||
afterRectangle1stClickSnippet: 'startProfileAt([75.8, 317.2], %)',
|
'sketch003 = startSketchOn(extrude001, seg04)',
|
||||||
|
afterRectangle1stClickSnippet: 'startProfileAt([-209.64, 255.28], %)',
|
||||||
afterRectangle2ndClickSnippet: `angledLine([0, 11.56], %, $rectangleSegmentA003)
|
afterRectangle2ndClickSnippet: `angledLine([0, 11.56], %, $rectangleSegmentA003)
|
||||||
|> angledLine([
|
|> angledLine([
|
||||||
segAng(rectangleSegmentA003) - 90,
|
segAng(rectangleSegmentA003) - 90,
|
||||||
@ -292,7 +257,8 @@ test.describe('verify sketch on chamfer works', () => {
|
|||||||
length = 30,
|
length = 30,
|
||||||
tags = [getNextAdjacentEdge(yo)]
|
tags = [getNextAdjacentEdge(yo)]
|
||||||
}, %)`,
|
}, %)`,
|
||||||
afterChamferSelectSnippet: 'sketch005 = startSketchOn(extrude001, seg06)',
|
afterChamferSelectSnippet:
|
||||||
|
'sketch005 = startSketchOn(extrude001, seg06)',
|
||||||
afterRectangle1stClickSnippet: 'startProfileAt([-23.43, 19.69], %)',
|
afterRectangle1stClickSnippet: 'startProfileAt([-23.43, 19.69], %)',
|
||||||
afterRectangle2ndClickSnippet: `angledLine([0, 9.1], %, $rectangleSegmentA005)
|
afterRectangle2ndClickSnippet: `angledLine([0, 9.1], %, $rectangleSegmentA005)
|
||||||
|
|
||||||
@ -339,7 +305,7 @@ test.describe('verify sketch on chamfer works', () => {
|
|||||||
tags = [getNextAdjacentEdge(yo)]
|
tags = [getNextAdjacentEdge(yo)]
|
||||||
}, %, $seg06)
|
}, %, $seg06)
|
||||||
sketch005 = startSketchOn(extrude001, seg06)
|
sketch005 = startSketchOn(extrude001, seg06)
|
||||||
|> startProfileAt([-23.43,19.69], %)
|
|> startProfileAt([-23.43, 19.69], %)
|
||||||
|> angledLine([0, 9.1], %, $rectangleSegmentA005)
|
|> angledLine([0, 9.1], %, $rectangleSegmentA005)
|
||||||
|> angledLine([
|
|> angledLine([
|
||||||
segAng(rectangleSegmentA005) - 90,
|
segAng(rectangleSegmentA005) - 90,
|
||||||
@ -352,7 +318,7 @@ test.describe('verify sketch on chamfer works', () => {
|
|||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
sketch004 = startSketchOn(extrude001, seg05)
|
sketch004 = startSketchOn(extrude001, seg05)
|
||||||
|> startProfileAt([82.57,322.96], %)
|
|> startProfileAt([82.57, 322.96], %)
|
||||||
|> angledLine([0, 11.16], %, $rectangleSegmentA004)
|
|> angledLine([0, 11.16], %, $rectangleSegmentA004)
|
||||||
|> angledLine([
|
|> angledLine([
|
||||||
segAng(rectangleSegmentA004) - 90,
|
segAng(rectangleSegmentA004) - 90,
|
||||||
@ -365,7 +331,7 @@ test.describe('verify sketch on chamfer works', () => {
|
|||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
sketch003 = startSketchOn(extrude001, seg04)
|
sketch003 = startSketchOn(extrude001, seg04)
|
||||||
|> startProfileAt([-209.64,255.28], %)
|
|> startProfileAt([-209.64, 255.28], %)
|
||||||
|> angledLine([0, 11.56], %, $rectangleSegmentA003)
|
|> angledLine([0, 11.56], %, $rectangleSegmentA003)
|
||||||
|> angledLine([
|
|> angledLine([
|
||||||
segAng(rectangleSegmentA003) - 90,
|
segAng(rectangleSegmentA003) - 90,
|
||||||
@ -378,7 +344,7 @@ test.describe('verify sketch on chamfer works', () => {
|
|||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
sketch002 = startSketchOn(extrude001, seg03)
|
sketch002 = startSketchOn(extrude001, seg03)
|
||||||
|> startProfileAt([205.96,254.59], %)
|
|> startProfileAt([205.96, 254.59], %)
|
||||||
|> angledLine([0, 11.39], %, $rectangleSegmentA002)
|
|> angledLine([0, 11.39], %, $rectangleSegmentA002)
|
||||||
|> angledLine([
|
|> angledLine([
|
||||||
segAng(rectangleSegmentA002) - 90,
|
segAng(rectangleSegmentA002) - 90,
|
||||||
@ -394,31 +360,23 @@ test.describe('verify sketch on chamfer works', () => {
|
|||||||
{ shouldNormalise: true }
|
{ shouldNormalise: true }
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
}
|
||||||
|
|
||||||
test('Works on chamfers that are non in a pipeExpression can break up multi edges in a chamfer array', async ({
|
|
||||||
context,
|
|
||||||
page,
|
|
||||||
homePage,
|
|
||||||
editor,
|
|
||||||
toolbar,
|
|
||||||
scene,
|
|
||||||
}) => {
|
|
||||||
const file = await fs.readFile(
|
|
||||||
path.resolve(
|
|
||||||
__dirname,
|
|
||||||
'../../',
|
|
||||||
'./src/wasm-lib/tests/executor/inputs/e2e-can-sketch-on-chamfer-no-pipeExpr.kcl'
|
|
||||||
),
|
|
||||||
'utf-8'
|
|
||||||
)
|
)
|
||||||
await context.addInitScript((file) => {
|
|
||||||
localStorage.setItem('persistCode', file)
|
|
||||||
}, file)
|
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
|
||||||
await homePage.goToModelingScene()
|
|
||||||
|
|
||||||
const sketchOnAChamfer = _sketchOnAChamfer(page, editor, toolbar, scene)
|
test(
|
||||||
|
'Works on chamfers that are non in a pipeExpression can break up multi edges in a chamfer array',
|
||||||
|
{ tag: ['@skipWin'] },
|
||||||
|
async ({ app, editor, toolbar, scene }) => {
|
||||||
|
test.skip(
|
||||||
|
process.platform === 'win32',
|
||||||
|
'Fails on windows in CI, can not be replicated locally on windows.'
|
||||||
|
)
|
||||||
|
const file = await app.getInputFile(
|
||||||
|
'e2e-can-sketch-on-chamfer-no-pipeExpr.kcl'
|
||||||
|
)
|
||||||
|
await app.initialise(file)
|
||||||
|
|
||||||
|
const sketchOnAChamfer = _sketchOnAChamfer(app, editor, toolbar, scene)
|
||||||
|
|
||||||
await sketchOnAChamfer({
|
await sketchOnAChamfer({
|
||||||
clickCoords: { x: 570, y: 220 },
|
clickCoords: { x: 570, y: 220 },
|
||||||
@ -432,7 +390,8 @@ test.describe('verify sketch on chamfer works', () => {
|
|||||||
getOppositeEdge(seg01)
|
getOppositeEdge(seg01)
|
||||||
]}, extrude001)`,
|
]}, extrude001)`,
|
||||||
beforeChamferSnippetEnd: '}, extrude001)',
|
beforeChamferSnippetEnd: '}, extrude001)',
|
||||||
afterChamferSelectSnippet: 'sketch002 = startSketchOn(extrude001, seg03)',
|
afterChamferSelectSnippet:
|
||||||
|
'sketch002 = startSketchOn(extrude001, seg03)',
|
||||||
afterRectangle1stClickSnippet: 'startProfileAt([205.96, 254.59], %)',
|
afterRectangle1stClickSnippet: 'startProfileAt([205.96, 254.59], %)',
|
||||||
afterRectangle2ndClickSnippet: `angledLine([0, 11.39], %, $rectangleSegmentA002)
|
afterRectangle2ndClickSnippet: `angledLine([0, 11.39], %, $rectangleSegmentA002)
|
||||||
|> angledLine([
|
|> angledLine([
|
||||||
@ -489,54 +448,48 @@ sketch002 = startSketchOn(extrude001, seg03)
|
|||||||
`,
|
`,
|
||||||
{ shouldNormalise: true }
|
{ shouldNormalise: true }
|
||||||
)
|
)
|
||||||
})
|
}
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test(`Verify axis, origin, and horizontal snapping`, async ({
|
test(`Verify axis, origin, and horizontal snapping`, async ({
|
||||||
page,
|
app,
|
||||||
homePage,
|
|
||||||
editor,
|
editor,
|
||||||
toolbar,
|
toolbar,
|
||||||
scene,
|
scene,
|
||||||
}) => {
|
}) => {
|
||||||
const viewPortSize = { width: 1200, height: 500 }
|
|
||||||
|
|
||||||
await page.setBodyDimensions(viewPortSize)
|
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
|
||||||
|
|
||||||
// Constants and locators
|
// Constants and locators
|
||||||
// These are mappings from screenspace to KCL coordinates,
|
// These are mappings from screenspace to KCL coordinates,
|
||||||
// until we merge in our coordinate system helpers
|
// until we merge in our coordinate system helpers
|
||||||
const xzPlane = [
|
const xzPlane = [
|
||||||
viewPortSize.width * 0.65,
|
app.viewPortSize.width * 0.65,
|
||||||
viewPortSize.height * 0.3,
|
app.viewPortSize.height * 0.3,
|
||||||
] as const
|
] as const
|
||||||
const originSloppy = {
|
const originSloppy = {
|
||||||
screen: [
|
screen: [
|
||||||
viewPortSize.width / 2 + 3, // 3px off the center of the screen
|
app.viewPortSize.width / 2 + 3, // 3px off the center of the screen
|
||||||
viewPortSize.height / 2,
|
app.viewPortSize.height / 2,
|
||||||
],
|
],
|
||||||
kcl: [0, 0],
|
kcl: [0, 0],
|
||||||
} as const
|
} as const
|
||||||
const xAxisSloppy = {
|
const xAxisSloppy = {
|
||||||
screen: [
|
screen: [
|
||||||
viewPortSize.width * 0.75,
|
app.viewPortSize.width * 0.75,
|
||||||
viewPortSize.height / 2 - 3, // 3px off the X-axis
|
app.viewPortSize.height / 2 - 3, // 3px off the X-axis
|
||||||
],
|
],
|
||||||
kcl: [20.34, 0],
|
kcl: [16.95, 0],
|
||||||
} as const
|
} as const
|
||||||
const offYAxis = {
|
const offYAxis = {
|
||||||
screen: [
|
screen: [
|
||||||
viewPortSize.width * 0.6, // Well off the Y-axis, out of snapping range
|
app.viewPortSize.width * 0.6, // Well off the Y-axis, out of snapping range
|
||||||
viewPortSize.height * 0.3,
|
app.viewPortSize.height * 0.3,
|
||||||
],
|
],
|
||||||
kcl: [8.14, 6.78],
|
kcl: [6.78, 6.78],
|
||||||
} as const
|
} as const
|
||||||
const yAxisSloppy = {
|
const yAxisSloppy = {
|
||||||
screen: [
|
screen: [
|
||||||
viewPortSize.width / 2 + 5, // 5px off the Y-axis
|
app.viewPortSize.width / 2 + 5, // 5px off the Y-axis
|
||||||
viewPortSize.height * 0.3,
|
app.viewPortSize.height * 0.3,
|
||||||
],
|
],
|
||||||
kcl: [0, 6.78],
|
kcl: [0, 6.78],
|
||||||
} as const
|
} as const
|
||||||
@ -557,13 +510,15 @@ test(`Verify axis, origin, and horizontal snapping`, async ({
|
|||||||
afterSegmentDraggedOnYAxis: `startProfileAt([${yAxisSloppy.kcl[0]}, ${yAxisSloppy.kcl[1]}], %)`,
|
afterSegmentDraggedOnYAxis: `startProfileAt([${yAxisSloppy.kcl[0]}, ${yAxisSloppy.kcl[1]}], %)`,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await app.initialise()
|
||||||
|
|
||||||
await test.step(`Start a sketch on the XZ plane`, async () => {
|
await test.step(`Start a sketch on the XZ plane`, async () => {
|
||||||
await editor.closePane()
|
await editor.closePane()
|
||||||
await toolbar.startSketchPlaneSelection()
|
await toolbar.startSketchPlaneSelection()
|
||||||
await moveToXzPlane()
|
await moveToXzPlane()
|
||||||
await clickOnXzPlane()
|
await clickOnXzPlane()
|
||||||
// timeout wait for engine animation is unavoidable
|
// timeout wait for engine animation is unavoidable
|
||||||
await page.waitForTimeout(600)
|
await app.page.waitForTimeout(600)
|
||||||
await editor.expectEditor.toContain(expectedCodeSnippets.sketchOnXzPlane)
|
await editor.expectEditor.toContain(expectedCodeSnippets.sketchOnXzPlane)
|
||||||
})
|
})
|
||||||
await test.step(`Place a point a few pixels off the middle, verify it still snaps to 0,0`, async () => {
|
await test.step(`Place a point a few pixels off the middle, verify it still snaps to 0,0`, async () => {
|
||||||
@ -597,127 +552,20 @@ test(`Verify axis, origin, and horizontal snapping`, async ({
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test(`Verify user can double-click to edit a sketch`, async ({
|
|
||||||
context,
|
|
||||||
page,
|
|
||||||
homePage,
|
|
||||||
editor,
|
|
||||||
toolbar,
|
|
||||||
scene,
|
|
||||||
}) => {
|
|
||||||
const u = await getUtils(page)
|
|
||||||
|
|
||||||
const initialCode = `closedSketch = startSketchOn('XZ')
|
|
||||||
|> circle({ center = [8, 5], radius = 2 }, %)
|
|
||||||
openSketch = startSketchOn('XY')
|
|
||||||
|> startProfileAt([-5, 0], %)
|
|
||||||
|> lineTo([0, 5], %)
|
|
||||||
|> xLine(5, %)
|
|
||||||
|> tangentialArcTo([10, 0], %)
|
|
||||||
`
|
|
||||||
const viewPortSize = { width: 1000, height: 500 }
|
|
||||||
await page.setBodyDimensions(viewPortSize)
|
|
||||||
|
|
||||||
await context.addInitScript((code) => {
|
|
||||||
localStorage.setItem('persistCode', code)
|
|
||||||
}, initialCode)
|
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
|
||||||
await u.waitForPageLoad()
|
|
||||||
await page.waitForTimeout(1000)
|
|
||||||
|
|
||||||
const pointInsideCircle = {
|
|
||||||
x: viewPortSize.width * 0.63,
|
|
||||||
y: viewPortSize.height * 0.5,
|
|
||||||
}
|
|
||||||
const pointOnPathAfterSketching = {
|
|
||||||
x: viewPortSize.width * 0.65,
|
|
||||||
y: viewPortSize.height * 0.5,
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
const [_clickOpenPath, moveToOpenPath, dblClickOpenPath] =
|
|
||||||
scene.makeMouseHelpers(
|
|
||||||
pointOnPathAfterSketching.x,
|
|
||||||
pointOnPathAfterSketching.y
|
|
||||||
)
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
const [_clickCircle, moveToCircle, dblClickCircle] = scene.makeMouseHelpers(
|
|
||||||
pointInsideCircle.x,
|
|
||||||
pointInsideCircle.y
|
|
||||||
)
|
|
||||||
|
|
||||||
const exitSketch = async () => {
|
|
||||||
await test.step(`Exit sketch mode`, async () => {
|
|
||||||
await toolbar.exitSketchBtn.click()
|
|
||||||
await expect(toolbar.exitSketchBtn).not.toBeVisible()
|
|
||||||
await expect(toolbar.startSketchBtn).toBeEnabled()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
await test.step(`Double-click on the closed sketch`, async () => {
|
|
||||||
await moveToCircle()
|
|
||||||
await dblClickCircle()
|
|
||||||
await expect(toolbar.startSketchBtn).not.toBeVisible()
|
|
||||||
await expect(toolbar.exitSketchBtn).toBeVisible()
|
|
||||||
await editor.expectState({
|
|
||||||
activeLines: [`|>circle({center=[8,5],radius=2},%)`],
|
|
||||||
highlightedCode: 'circle({center=[8,5],radius=2},%)',
|
|
||||||
diagnostics: [],
|
|
||||||
})
|
|
||||||
})
|
|
||||||
await page.waitForTimeout(1000)
|
|
||||||
|
|
||||||
await exitSketch()
|
|
||||||
await page.waitForTimeout(1000)
|
|
||||||
|
|
||||||
// Drag the sketch line out of the axis view which blocks the click
|
|
||||||
await page.dragAndDrop('#stream', '#stream', {
|
|
||||||
sourcePosition: {
|
|
||||||
x: viewPortSize.width * 0.7,
|
|
||||||
y: viewPortSize.height * 0.5,
|
|
||||||
},
|
|
||||||
targetPosition: {
|
|
||||||
x: viewPortSize.width * 0.7,
|
|
||||||
y: viewPortSize.height * 0.4,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
await page.waitForTimeout(500)
|
|
||||||
|
|
||||||
await test.step(`Double-click on the open sketch`, async () => {
|
|
||||||
await moveToOpenPath()
|
|
||||||
await scene.expectPixelColor([250, 250, 250], pointOnPathAfterSketching, 15)
|
|
||||||
// There is a full execution after exiting sketch that clears the scene.
|
|
||||||
await page.waitForTimeout(500)
|
|
||||||
await dblClickOpenPath()
|
|
||||||
await expect(toolbar.startSketchBtn).not.toBeVisible()
|
|
||||||
await expect(toolbar.exitSketchBtn).toBeVisible()
|
|
||||||
// Wait for enter sketch mode to complete
|
|
||||||
await page.waitForTimeout(500)
|
|
||||||
await editor.expectState({
|
|
||||||
activeLines: [`|>tangentialArcTo([10,0],%)`],
|
|
||||||
highlightedCode: 'tangentialArcTo([10,0],%)',
|
|
||||||
diagnostics: [],
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
test(`Offset plane point-and-click`, async ({
|
test(`Offset plane point-and-click`, async ({
|
||||||
context,
|
app,
|
||||||
page,
|
|
||||||
homePage,
|
|
||||||
scene,
|
scene,
|
||||||
editor,
|
editor,
|
||||||
toolbar,
|
toolbar,
|
||||||
cmdBar,
|
cmdBar,
|
||||||
}) => {
|
}) => {
|
||||||
|
await app.initialise()
|
||||||
|
|
||||||
// One dumb hardcoded screen pixel value
|
// One dumb hardcoded screen pixel value
|
||||||
const testPoint = { x: 700, y: 150 }
|
const testPoint = { x: 700, y: 150 }
|
||||||
const [clickOnXzPlane] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
const [clickOnXzPlane] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
||||||
const expectedOutput = `plane001 = offsetPlane('XZ', 5)`
|
const expectedOutput = `plane001 = offsetPlane('XZ', 5)`
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
|
||||||
|
|
||||||
await test.step(`Look for the blue of the XZ plane`, async () => {
|
await test.step(`Look for the blue of the XZ plane`, async () => {
|
||||||
await scene.expectPixelColor([50, 51, 96], testPoint, 15)
|
await scene.expectPixelColor([50, 51, 96], testPoint, 15)
|
||||||
})
|
})
|
||||||
@ -753,270 +601,3 @@ test(`Offset plane point-and-click`, async ({
|
|||||||
await scene.expectPixelColor([74, 74, 74], testPoint, 15)
|
await scene.expectPixelColor([74, 74, 74], testPoint, 15)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const loftPointAndClickCases = [
|
|
||||||
{ shouldPreselect: true },
|
|
||||||
{ shouldPreselect: false },
|
|
||||||
]
|
|
||||||
loftPointAndClickCases.forEach(({ shouldPreselect }) => {
|
|
||||||
test(`Loft point-and-click (preselected sketches: ${shouldPreselect})`, async ({
|
|
||||||
app,
|
|
||||||
page,
|
|
||||||
scene,
|
|
||||||
editor,
|
|
||||||
toolbar,
|
|
||||||
cmdBar,
|
|
||||||
}) => {
|
|
||||||
const initialCode = `sketch001 = startSketchOn('XZ')
|
|
||||||
|> circle({ center = [0, 0], radius = 30 }, %)
|
|
||||||
plane001 = offsetPlane('XZ', 50)
|
|
||||||
sketch002 = startSketchOn(plane001)
|
|
||||||
|> circle({ center = [0, 0], radius = 20 }, %)
|
|
||||||
`
|
|
||||||
await app.initialise(initialCode)
|
|
||||||
|
|
||||||
// One dumb hardcoded screen pixel value
|
|
||||||
const testPoint = { x: 575, y: 200 }
|
|
||||||
const [clickOnSketch1] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
|
||||||
const [clickOnSketch2] = scene.makeMouseHelpers(
|
|
||||||
testPoint.x,
|
|
||||||
testPoint.y + 80
|
|
||||||
)
|
|
||||||
const loftDeclaration = 'loft001 = loft([sketch001, sketch002])'
|
|
||||||
|
|
||||||
await test.step(`Look for the white of the sketch001 shape`, async () => {
|
|
||||||
await scene.expectPixelColor([254, 254, 254], testPoint, 15)
|
|
||||||
})
|
|
||||||
|
|
||||||
async function selectSketches() {
|
|
||||||
await clickOnSketch1()
|
|
||||||
await page.keyboard.down('Shift')
|
|
||||||
await clickOnSketch2()
|
|
||||||
await app.page.waitForTimeout(500)
|
|
||||||
await page.keyboard.up('Shift')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!shouldPreselect) {
|
|
||||||
await test.step(`Go through the command bar flow without preselected sketches`, async () => {
|
|
||||||
await toolbar.loftButton.click()
|
|
||||||
await cmdBar.expectState({
|
|
||||||
stage: 'arguments',
|
|
||||||
currentArgKey: 'selection',
|
|
||||||
currentArgValue: '',
|
|
||||||
headerArguments: { Selection: '' },
|
|
||||||
highlightedHeaderArg: 'selection',
|
|
||||||
commandName: 'Loft',
|
|
||||||
})
|
|
||||||
await selectSketches()
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
await cmdBar.expectState({
|
|
||||||
stage: 'review',
|
|
||||||
headerArguments: { Selection: '2 faces' },
|
|
||||||
commandName: 'Loft',
|
|
||||||
})
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
await test.step(`Preselect the two sketches`, async () => {
|
|
||||||
await selectSketches()
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step(`Go through the command bar flow with preselected sketches`, async () => {
|
|
||||||
await toolbar.loftButton.click()
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
await cmdBar.expectState({
|
|
||||||
stage: 'review',
|
|
||||||
headerArguments: { Selection: '2 faces' },
|
|
||||||
commandName: 'Loft',
|
|
||||||
})
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
|
|
||||||
await editor.expectEditor.toContain(loftDeclaration)
|
|
||||||
await editor.expectState({
|
|
||||||
diagnostics: [],
|
|
||||||
activeLines: [loftDeclaration],
|
|
||||||
highlightedCode: '',
|
|
||||||
})
|
|
||||||
await scene.expectPixelColor([89, 89, 89], testPoint, 15)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
const shellPointAndClickCapCases = [
|
|
||||||
{ shouldPreselect: true },
|
|
||||||
{ shouldPreselect: false },
|
|
||||||
]
|
|
||||||
shellPointAndClickCapCases.forEach(({ shouldPreselect }) => {
|
|
||||||
test(`Shell point-and-click cap (preselected sketches: ${shouldPreselect})`, async ({
|
|
||||||
context,
|
|
||||||
page,
|
|
||||||
homePage,
|
|
||||||
scene,
|
|
||||||
editor,
|
|
||||||
toolbar,
|
|
||||||
cmdBar,
|
|
||||||
}) => {
|
|
||||||
const initialCode = `sketch001 = startSketchOn('XZ')
|
|
||||||
|> circle({ center = [0, 0], radius = 30 }, %)
|
|
||||||
extrude001 = extrude(30, sketch001)
|
|
||||||
`
|
|
||||||
await context.addInitScript((initialCode) => {
|
|
||||||
localStorage.setItem('persistCode', initialCode)
|
|
||||||
}, initialCode)
|
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
|
||||||
await homePage.goToModelingScene()
|
|
||||||
|
|
||||||
// One dumb hardcoded screen pixel value
|
|
||||||
const testPoint = { x: 575, y: 200 }
|
|
||||||
const [clickOnCap] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
|
||||||
const shellDeclaration =
|
|
||||||
"shell001 = shell({ faces = ['end'], thickness = 5 }, extrude001)"
|
|
||||||
|
|
||||||
await test.step(`Look for the grey of the shape`, async () => {
|
|
||||||
await scene.expectPixelColor([127, 127, 127], testPoint, 15)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!shouldPreselect) {
|
|
||||||
await test.step(`Go through the command bar flow without preselected faces`, async () => {
|
|
||||||
await toolbar.shellButton.click()
|
|
||||||
await cmdBar.expectState({
|
|
||||||
stage: 'arguments',
|
|
||||||
currentArgKey: 'selection',
|
|
||||||
currentArgValue: '',
|
|
||||||
headerArguments: {
|
|
||||||
Selection: '',
|
|
||||||
Thickness: '',
|
|
||||||
},
|
|
||||||
highlightedHeaderArg: 'selection',
|
|
||||||
commandName: 'Shell',
|
|
||||||
})
|
|
||||||
await clickOnCap()
|
|
||||||
await page.waitForTimeout(500)
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
await cmdBar.expectState({
|
|
||||||
stage: 'review',
|
|
||||||
headerArguments: {
|
|
||||||
Selection: '1 cap',
|
|
||||||
Thickness: '5',
|
|
||||||
},
|
|
||||||
commandName: 'Shell',
|
|
||||||
})
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
await test.step(`Preselect the cap`, async () => {
|
|
||||||
await clickOnCap()
|
|
||||||
await page.waitForTimeout(500)
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step(`Go through the command bar flow with a preselected face (cap)`, async () => {
|
|
||||||
await toolbar.shellButton.click()
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
await cmdBar.expectState({
|
|
||||||
stage: 'review',
|
|
||||||
headerArguments: {
|
|
||||||
Selection: '1 cap',
|
|
||||||
Thickness: '5',
|
|
||||||
},
|
|
||||||
commandName: 'Shell',
|
|
||||||
})
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
|
|
||||||
await editor.expectEditor.toContain(shellDeclaration)
|
|
||||||
await editor.expectState({
|
|
||||||
diagnostics: [],
|
|
||||||
activeLines: [shellDeclaration],
|
|
||||||
highlightedCode: '',
|
|
||||||
})
|
|
||||||
await scene.expectPixelColor([146, 146, 146], testPoint, 15)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
test('Shell point-and-click wall', async ({
|
|
||||||
context,
|
|
||||||
page,
|
|
||||||
homePage,
|
|
||||||
scene,
|
|
||||||
editor,
|
|
||||||
toolbar,
|
|
||||||
cmdBar,
|
|
||||||
}) => {
|
|
||||||
const initialCode = `sketch001 = startSketchOn('XY')
|
|
||||||
|> startProfileAt([-20, 20], %)
|
|
||||||
|> xLine(40, %)
|
|
||||||
|> yLine(-60, %)
|
|
||||||
|> xLine(-40, %)
|
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|
||||||
|> close(%)
|
|
||||||
extrude001 = extrude(40, sketch001)
|
|
||||||
`
|
|
||||||
await context.addInitScript((initialCode) => {
|
|
||||||
localStorage.setItem('persistCode', initialCode)
|
|
||||||
}, initialCode)
|
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
|
||||||
await homePage.goToModelingScene()
|
|
||||||
|
|
||||||
// One dumb hardcoded screen pixel value
|
|
||||||
const testPoint = { x: 580, y: 180 }
|
|
||||||
const [clickOnCap] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
|
||||||
const [clickOnWall] = scene.makeMouseHelpers(testPoint.x, testPoint.y + 70)
|
|
||||||
const mutatedCode = 'xLine(-40, %, $seg01)'
|
|
||||||
const shellDeclaration =
|
|
||||||
"shell001 = shell({ faces = ['end', seg01], thickness = 5}, extrude001)"
|
|
||||||
const formattedOutLastLine = '}, extrude001)'
|
|
||||||
|
|
||||||
await test.step(`Look for the grey of the shape`, async () => {
|
|
||||||
await scene.expectPixelColor([99, 99, 99], testPoint, 15)
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step(`Go through the command bar flow, selecting a wall and keeping default thickness`, async () => {
|
|
||||||
await toolbar.shellButton.click()
|
|
||||||
await cmdBar.expectState({
|
|
||||||
stage: 'arguments',
|
|
||||||
currentArgKey: 'selection',
|
|
||||||
currentArgValue: '',
|
|
||||||
headerArguments: {
|
|
||||||
Selection: '',
|
|
||||||
Thickness: '',
|
|
||||||
},
|
|
||||||
highlightedHeaderArg: 'selection',
|
|
||||||
commandName: 'Shell',
|
|
||||||
})
|
|
||||||
await clickOnCap()
|
|
||||||
await page.keyboard.down('Shift')
|
|
||||||
await clickOnWall()
|
|
||||||
await page.waitForTimeout(500)
|
|
||||||
await page.keyboard.up('Shift')
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
await cmdBar.expectState({
|
|
||||||
stage: 'review',
|
|
||||||
headerArguments: {
|
|
||||||
Selection: '1 cap, 1 face',
|
|
||||||
Thickness: '5',
|
|
||||||
},
|
|
||||||
commandName: 'Shell',
|
|
||||||
})
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
|
|
||||||
await editor.expectEditor.toContain(mutatedCode)
|
|
||||||
await editor.expectEditor.toContain(shellDeclaration)
|
|
||||||
await editor.expectState({
|
|
||||||
diagnostics: [],
|
|
||||||
activeLines: [formattedOutLastLine],
|
|
||||||
highlightedCode: '',
|
|
||||||
})
|
|
||||||
await scene.expectPixelColor([49, 49, 49], testPoint, 15)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
@ -1,26 +1,36 @@
|
|||||||
import { test, expect, Page } from './zoo-test'
|
import { test, expect, Page } from '@playwright/test'
|
||||||
import path from 'path'
|
import { join } from 'path'
|
||||||
import * as fsp from 'fs/promises'
|
import * as fsp from 'fs/promises'
|
||||||
import { getUtils, executorInputPath } from './test-utils'
|
import {
|
||||||
|
getUtils,
|
||||||
|
setup,
|
||||||
|
setupElectron,
|
||||||
|
tearDown,
|
||||||
|
executorInputPath,
|
||||||
|
} from './test-utils'
|
||||||
import { TEST_CODE_TRIGGER_ENGINE_EXPORT_ERROR } from './storageStates'
|
import { TEST_CODE_TRIGGER_ENGINE_EXPORT_ERROR } from './storageStates'
|
||||||
import { bracket } from 'lib/exampleKcl'
|
import { bracket } from 'lib/exampleKcl'
|
||||||
|
|
||||||
|
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||||
|
await setup(context, page, testInfo)
|
||||||
|
})
|
||||||
|
|
||||||
|
test.afterEach(async ({ page }, testInfo) => {
|
||||||
|
await tearDown(page, testInfo)
|
||||||
|
})
|
||||||
|
|
||||||
test.describe('Regression tests', () => {
|
test.describe('Regression tests', () => {
|
||||||
// bugs we found that don't fit neatly into other categories
|
// bugs we found that don't fit neatly into other categories
|
||||||
test('bad model has inline error #3251', async ({
|
test('bad model has inline error #3251', async ({ page }) => {
|
||||||
context,
|
|
||||||
page,
|
|
||||||
homePage,
|
|
||||||
}) => {
|
|
||||||
// because the model has `line([0,0]..` it is valid code, but the model is invalid
|
// because the model has `line([0,0]..` it is valid code, but the model is invalid
|
||||||
// regression test for https://github.com/KittyCAD/modeling-app/issues/3251
|
// regression test for https://github.com/KittyCAD/modeling-app/issues/3251
|
||||||
// Since the bad model also found as issue with the artifact graph, which in tern blocked the editor diognostics
|
// Since the bad model also found as issue with the artifact graph, which in tern blocked the editor diognostics
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await context.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'persistCode',
|
'persistCode',
|
||||||
`sketch2 = startSketchOn("XY")
|
`sketch2 = startSketchOn("XY")
|
||||||
sketch001 = startSketchAt([-0, -0])
|
sketch001 = startSketchAt([-0, -0])
|
||||||
|> line([0, 0], %)
|
|> line([0, 0], %)
|
||||||
|> line([-4.84, -5.29], %)
|
|> line([-4.84, -5.29], %)
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
@ -28,10 +38,9 @@ test.describe('Regression tests', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
await u.waitForPageLoad()
|
|
||||||
|
|
||||||
// error in guter
|
// error in guter
|
||||||
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
|
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
|
||||||
@ -47,7 +56,6 @@ test.describe('Regression tests', () => {
|
|||||||
})
|
})
|
||||||
test('user should not have to press down twice in cmdbar', async ({
|
test('user should not have to press down twice in cmdbar', async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
|
||||||
}) => {
|
}) => {
|
||||||
// because the model has `line([0,0]..` it is valid code, but the model is invalid
|
// because the model has `line([0,0]..` it is valid code, but the model is invalid
|
||||||
// regression test for https://github.com/KittyCAD/modeling-app/issues/3251
|
// regression test for https://github.com/KittyCAD/modeling-app/issues/3251
|
||||||
@ -56,38 +64,26 @@ test.describe('Regression tests', () => {
|
|||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'persistCode',
|
'persistCode',
|
||||||
`sketch001 = startSketchOn('XY')
|
`sketch2 = startSketchOn("XY")
|
||||||
|> startProfileAt([82.33, 238.21], %)
|
sketch001 = startSketchAt([-0, -0])
|
||||||
|> angledLine([0, 288.63], %, $rectangleSegmentA001)
|
|> line([0, 0], %)
|
||||||
|> angledLine([
|
|> line([-4.84, -5.29], %)
|
||||||
segAng(rectangleSegmentA001) - 90,
|
|
||||||
197.97
|
|
||||||
], %, $rectangleSegmentB001)
|
|
||||||
|> angledLine([
|
|
||||||
segAng(rectangleSegmentA001),
|
|
||||||
-segLen(rectangleSegmentA001)
|
|
||||||
], %, $rectangleSegmentC001)
|
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)`
|
||||||
extrude001 = extrude(50, sketch001)
|
|
||||||
`
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await page.goto('/')
|
||||||
await u.waitForPageLoad()
|
await u.waitForPageLoad()
|
||||||
|
|
||||||
await test.step('Check arrow down works', async () => {
|
await test.step('Check arrow down works', async () => {
|
||||||
await page.getByTestId('command-bar-open-button').hover()
|
|
||||||
await page.getByTestId('command-bar-open-button').click()
|
await page.getByTestId('command-bar-open-button').click()
|
||||||
|
|
||||||
const floppy = page.getByRole('option', {
|
await page
|
||||||
name: 'floppy disk arrow Export',
|
.getByRole('option', { name: 'floppy disk arrow Export' })
|
||||||
})
|
.click()
|
||||||
|
|
||||||
await floppy.click()
|
|
||||||
|
|
||||||
// press arrow down key twice
|
// press arrow down key twice
|
||||||
await page.keyboard.press('ArrowDown')
|
await page.keyboard.press('ArrowDown')
|
||||||
@ -119,7 +115,7 @@ extrude001 = extrude(50, sketch001)
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
test('executes on load', async ({ page, homePage }) => {
|
test('executes on load', async ({ page }) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
@ -131,10 +127,9 @@ extrude001 = extrude(50, sketch001)
|
|||||||
|> line([-23.44, 0.52], %)`
|
|> line([-23.44, 0.52], %)`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
await u.waitForPageLoad()
|
|
||||||
|
|
||||||
// expand variables section
|
// expand variables section
|
||||||
const variablesTabButton = page.getByTestId('variables-pane-button')
|
const variablesTabButton = page.getByTestId('variables-pane-button')
|
||||||
@ -153,15 +148,14 @@ extrude001 = extrude(50, sketch001)
|
|||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('re-executes', async ({ page, homePage }) => {
|
test('re-executes', async ({ page }) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem('persistCode', `myVar = 5`)
|
localStorage.setItem('persistCode', `myVar = 5`)
|
||||||
})
|
})
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
await u.waitForPageLoad()
|
|
||||||
|
|
||||||
const variablesTabButton = page.getByTestId('variables-pane-button')
|
const variablesTabButton = page.getByTestId('variables-pane-button')
|
||||||
await variablesTabButton.click()
|
await variablesTabButton.click()
|
||||||
@ -180,7 +174,7 @@ extrude001 = extrude(50, sketch001)
|
|||||||
page.locator('.pretty-json-container >> text=myVar:67')
|
page.locator('.pretty-json-container >> text=myVar:67')
|
||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
})
|
})
|
||||||
test('ProgramMemory can be serialised', async ({ page, homePage }) => {
|
test('ProgramMemory can be serialised', async ({ page }) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
@ -199,14 +193,13 @@ extrude001 = extrude(50, sketch001)
|
|||||||
}, %)`
|
}, %)`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
const messages: string[] = []
|
const messages: string[] = []
|
||||||
|
|
||||||
// Listen for all console events and push the message text to an array
|
// Listen for all console events and push the message text to an array
|
||||||
page.on('console', (message) => messages.push(message.text()))
|
page.on('console', (message) => messages.push(message.text()))
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
await u.waitForPageLoad()
|
|
||||||
|
|
||||||
// wait for execution done
|
// wait for execution done
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
@ -219,26 +212,19 @@ extrude001 = extrude(50, sketch001)
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
test('ensure the Zoo logo is not a link in browser app', async ({ page }) => {
|
||||||
// Not relevant to us anymore, or at least for the time being.
|
|
||||||
test.skip('ensure the Zoo logo is not a link in browser app', async ({
|
|
||||||
page,
|
|
||||||
homePage,
|
|
||||||
}) => {
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
await u.waitForPageLoad()
|
|
||||||
|
|
||||||
const zooLogo = page.locator('[data-testid="app-logo"]')
|
const zooLogo = page.locator('[data-testid="app-logo"]')
|
||||||
// Make sure it's not a link
|
// Make sure it's not a link
|
||||||
await expect(zooLogo).not.toHaveAttribute('href')
|
await expect(zooLogo).not.toHaveAttribute('href')
|
||||||
})
|
})
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'Position _ Is Out Of Range... regression test',
|
'Position _ Is Out Of Range... regression test',
|
||||||
{ tag: ['@skipWin'] },
|
{ tag: ['@skipWin'] },
|
||||||
async ({ context, page, homePage }) => {
|
async ({ page }) => {
|
||||||
// SKip on windows, its being weird.
|
// SKip on windows, its being weird.
|
||||||
test.skip(
|
test.skip(
|
||||||
process.platform === 'win32',
|
process.platform === 'win32',
|
||||||
@ -247,8 +233,8 @@ extrude001 = extrude(50, sketch001)
|
|||||||
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
await context.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'persistCode',
|
'persistCode',
|
||||||
`exampleSketch = startSketchOn("XZ")
|
`exampleSketch = startSketchOn("XZ")
|
||||||
@ -264,9 +250,8 @@ extrude001 = extrude(50, sketch001)
|
|||||||
})
|
})
|
||||||
|
|
||||||
await expect(async () => {
|
await expect(async () => {
|
||||||
await homePage.goToModelingScene()
|
await page.goto('/')
|
||||||
await u.waitForPageLoad()
|
await u.waitForPageLoad()
|
||||||
|
|
||||||
// error in guter
|
// error in guter
|
||||||
await expect(page.locator('.cm-lint-marker-error')).toBeVisible({
|
await expect(page.locator('.cm-lint-marker-error')).toBeVisible({
|
||||||
timeout: 1_000,
|
timeout: 1_000,
|
||||||
@ -321,7 +306,6 @@ extrude001 = extrude(50, sketch001)
|
|||||||
|
|
||||||
test('when engine fails export we handle the failure and alert the user', async ({
|
test('when engine fails export we handle the failure and alert the user', async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
|
||||||
}) => {
|
}) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.addInitScript(
|
await page.addInitScript(
|
||||||
@ -332,10 +316,9 @@ extrude001 = extrude(50, sketch001)
|
|||||||
{ code: TEST_CODE_TRIGGER_ENGINE_EXPORT_ERROR }
|
{ code: TEST_CODE_TRIGGER_ENGINE_EXPORT_ERROR }
|
||||||
)
|
)
|
||||||
|
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
await u.waitForPageLoad()
|
|
||||||
|
|
||||||
// wait for execution done
|
// wait for execution done
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
@ -391,6 +374,7 @@ extrude001 = extrude(50, sketch001)
|
|||||||
|
|
||||||
// wait for execution done
|
// wait for execution done
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
|
await u.clearCommandLogs()
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
@ -424,7 +408,7 @@ extrude001 = extrude(50, sketch001)
|
|||||||
test(
|
test(
|
||||||
'ensure you can not export while an export is already going',
|
'ensure you can not export while an export is already going',
|
||||||
{ tag: ['@skipLinux', '@skipWin'] },
|
{ tag: ['@skipLinux', '@skipWin'] },
|
||||||
async ({ page, homePage }) => {
|
async ({ page }) => {
|
||||||
// This is being weird on ubuntu and windows.
|
// This is being weird on ubuntu and windows.
|
||||||
test.skip(
|
test.skip(
|
||||||
// eslint-disable-next-line jest/valid-title
|
// eslint-disable-next-line jest/valid-title
|
||||||
@ -444,10 +428,9 @@ extrude001 = extrude(50, sketch001)
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
await u.waitForPageLoad()
|
|
||||||
|
|
||||||
// wait for execution done
|
// wait for execution done
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
@ -517,17 +500,20 @@ extrude001 = extrude(50, sketch001)
|
|||||||
test(
|
test(
|
||||||
`Network health indicator only appears in modeling view`,
|
`Network health indicator only appears in modeling view`,
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ context, page }, testInfo) => {
|
async ({ browserName: _ }, testInfo) => {
|
||||||
await context.folderSetupFn(async (dir) => {
|
const { electronApp, page } = await setupElectron({
|
||||||
const bracketDir = path.join(dir, 'bracket')
|
testInfo,
|
||||||
|
folderSetupFn: async (dir) => {
|
||||||
|
const bracketDir = join(dir, 'bracket')
|
||||||
await fsp.mkdir(bracketDir, { recursive: true })
|
await fsp.mkdir(bracketDir, { recursive: true })
|
||||||
await fsp.copyFile(
|
await fsp.copyFile(
|
||||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||||
path.join(bracketDir, 'main.kcl')
|
join(bracketDir, 'main.kcl')
|
||||||
)
|
)
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
|
|
||||||
// Locators
|
// Locators
|
||||||
@ -553,12 +539,13 @@ extrude001 = extrude(50, sketch001)
|
|||||||
await u.waitForPageLoad()
|
await u.waitForPageLoad()
|
||||||
await expect(networkHealthIndicator).toContainText('Connected')
|
await expect(networkHealthIndicator).toContainText('Connected')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
await electronApp.close()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
test(`View gizmo stays visible even when zoomed out all the way`, async ({
|
test(`View gizmo stays visible even when zoomed out all the way`, async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
|
||||||
}) => {
|
}) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
|
|
||||||
@ -574,9 +561,8 @@ extrude001 = extrude(50, sketch001)
|
|||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem('persistCode', '')
|
localStorage.setItem('persistCode', '')
|
||||||
})
|
})
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
await u.waitForPageLoad()
|
|
||||||
await u.closeKclCodePanel()
|
await u.closeKclCodePanel()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -7,8 +7,6 @@ try {
|
|||||||
.split('\n')
|
.split('\n')
|
||||||
.filter((line) => line && line.length > 1)
|
.filter((line) => line && line.length > 1)
|
||||||
.forEach((line) => {
|
.forEach((line) => {
|
||||||
// Allow line comments.
|
|
||||||
if (line.trimStart().startsWith('#')) return
|
|
||||||
const [key, value] = line.split('=')
|
const [key, value] = line.split('=')
|
||||||
// prefer env vars over secrets file
|
// prefer env vars over secrets file
|
||||||
secrets[key] = process.env[key] || (value as any).replaceAll('"', '')
|
secrets[key] = process.env[key] || (value as any).replaceAll('"', '')
|
||||||
|
@ -1,20 +1,27 @@
|
|||||||
import { test, expect, Page } from './zoo-test'
|
import { test, expect, Page } from '@playwright/test'
|
||||||
import fs from 'node:fs/promises'
|
import { test as test2, expect as expect2 } from './fixtures/fixtureSetup'
|
||||||
import path from 'node:path'
|
|
||||||
import { HomePageFixture } from './fixtures/homePageFixture'
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getMovementUtils,
|
getMovementUtils,
|
||||||
getUtils,
|
getUtils,
|
||||||
PERSIST_MODELING_CONTEXT,
|
PERSIST_MODELING_CONTEXT,
|
||||||
|
setup,
|
||||||
|
tearDown,
|
||||||
} from './test-utils'
|
} from './test-utils'
|
||||||
import { uuidv4, roundOff } from 'lib/utils'
|
import { uuidv4, roundOff } from 'lib/utils'
|
||||||
|
|
||||||
|
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||||
|
await setup(context, page, testInfo)
|
||||||
|
})
|
||||||
|
|
||||||
|
test.afterEach(async ({ page }, testInfo) => {
|
||||||
|
await tearDown(page, testInfo)
|
||||||
|
})
|
||||||
|
|
||||||
test.describe('Sketch tests', () => {
|
test.describe('Sketch tests', () => {
|
||||||
test('multi-sketch file shows multiple Edit Sketch buttons', async ({
|
test('multi-sketch file shows multiple Edit Sketch buttons', async ({
|
||||||
page,
|
page,
|
||||||
context,
|
context,
|
||||||
homePage,
|
|
||||||
}) => {
|
}) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
const selectionsSnippets = {
|
const selectionsSnippets = {
|
||||||
@ -72,9 +79,9 @@ test.describe('Sketch tests', () => {
|
|||||||
},
|
},
|
||||||
selectionsSnippets
|
selectionsSnippets
|
||||||
)
|
)
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
// wait for execution done
|
// wait for execution done
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
@ -101,7 +108,6 @@ test.describe('Sketch tests', () => {
|
|||||||
})
|
})
|
||||||
test('Can delete most of a sketch and the line tool will still work', async ({
|
test('Can delete most of a sketch and the line tool will still work', async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
|
||||||
}) => {
|
}) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
@ -114,9 +120,12 @@ test.describe('Sketch tests', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
await expect(async () => {
|
await expect(async () => {
|
||||||
|
await page.mouse.click(700, 200)
|
||||||
await page.getByText('tangentialArcTo([24.95, -5.38], %)').click()
|
await page.getByText('tangentialArcTo([24.95, -5.38], %)').click()
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('button', { name: 'Edit Sketch' })
|
page.getByRole('button', { name: 'Edit Sketch' })
|
||||||
@ -133,7 +142,8 @@ test.describe('Sketch tests', () => {
|
|||||||
await page.keyboard.press('Home')
|
await page.keyboard.press('Home')
|
||||||
await page.keyboard.up('Shift')
|
await page.keyboard.up('Shift')
|
||||||
await page.keyboard.press('Backspace')
|
await page.keyboard.press('Backspace')
|
||||||
await u.openDebugPanel()
|
await u.openAndClearDebugPanel()
|
||||||
|
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]', 10_000)
|
await u.expectCmdLog('[data-message-type="execution-done"]', 10_000)
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
@ -141,31 +151,26 @@ test.describe('Sketch tests', () => {
|
|||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
await expect(async () => {
|
await expect(async () => {
|
||||||
await page.mouse.move(700, 200, { steps: 25 })
|
|
||||||
await page.mouse.click(700, 200)
|
await page.mouse.click(700, 200)
|
||||||
|
|
||||||
await expect
|
await expect.poll(u.normalisedEditorCode, { timeout: 1000 })
|
||||||
.poll(u.crushKclCodeIntoOneLineAndThenMaybeSome, { timeout: 1000 })
|
.toBe(`sketch001 = startSketchOn('XZ')
|
||||||
.toBe(
|
|> startProfileAt([12.34, -12.34], %)
|
||||||
`sketch001 = startSketchOn('XZ')
|
|> yLine(12.34, %)
|
||||||
|> startProfileAt([4.61,-14.01], %)
|
|
||||||
|> yLine(15.95, %)
|
`)
|
||||||
`
|
|
||||||
.replaceAll(' ', '')
|
|
||||||
.replaceAll('\n', '')
|
|
||||||
)
|
|
||||||
}).toPass({ timeout: 40_000, intervals: [1_000] })
|
}).toPass({ timeout: 40_000, intervals: [1_000] })
|
||||||
})
|
})
|
||||||
|
test('Can exit selection of face', async ({ page }) => {
|
||||||
test('Can exit selection of face', async ({ page, homePage }) => {
|
|
||||||
// Load the app with the code panes
|
// Load the app with the code panes
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem('persistCode', ``)
|
localStorage.setItem('persistCode', ``)
|
||||||
})
|
})
|
||||||
|
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
const u = await getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||||
await expect(
|
await expect(
|
||||||
@ -182,7 +187,6 @@ test.describe('Sketch tests', () => {
|
|||||||
test.describe('Can edit segments by dragging their handles', () => {
|
test.describe('Can edit segments by dragging their handles', () => {
|
||||||
const doEditSegmentsByDraggingHandle = async (
|
const doEditSegmentsByDraggingHandle = async (
|
||||||
page: Page,
|
page: Page,
|
||||||
homePage: HomePageFixture,
|
|
||||||
openPanes: string[]
|
openPanes: string[]
|
||||||
) => {
|
) => {
|
||||||
// Load the app with the code panes
|
// Load the app with the code panes
|
||||||
@ -198,8 +202,9 @@ test.describe('Sketch tests', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await homePage.goToModelingScene()
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('button', { name: 'Start Sketch' })
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
).not.toBeDisabled()
|
).not.toBeDisabled()
|
||||||
@ -313,7 +318,7 @@ test.describe('Sketch tests', () => {
|
|||||||
|> line([1.97, 2.06], %)
|
|> line([1.97, 2.06], %)
|
||||||
|> close(%)`)
|
|> close(%)`)
|
||||||
}
|
}
|
||||||
test('code pane open at start-handles', async ({ page, homePage }) => {
|
test('code pane open at start-handles', async ({ page }) => {
|
||||||
// Load the app with the code panes
|
// Load the app with the code panes
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
@ -326,10 +331,10 @@ test.describe('Sketch tests', () => {
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
await doEditSegmentsByDraggingHandle(page, homePage, ['code'])
|
await doEditSegmentsByDraggingHandle(page, ['code'])
|
||||||
})
|
})
|
||||||
|
|
||||||
test('code pane closed at start-handles', async ({ page, homePage }) => {
|
test('code pane closed at start-handles', async ({ page }) => {
|
||||||
// Load the app with the code panes
|
// Load the app with the code panes
|
||||||
await page.addInitScript(async (persistModelingContext) => {
|
await page.addInitScript(async (persistModelingContext) => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
@ -337,14 +342,12 @@ test.describe('Sketch tests', () => {
|
|||||||
JSON.stringify({ openPanes: [] })
|
JSON.stringify({ openPanes: [] })
|
||||||
)
|
)
|
||||||
}, PERSIST_MODELING_CONTEXT)
|
}, PERSIST_MODELING_CONTEXT)
|
||||||
await doEditSegmentsByDraggingHandle(page, homePage, [])
|
await doEditSegmentsByDraggingHandle(page, [])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Can edit a circle center and radius by dragging its handles', async ({
|
test('Can edit a circle center and radius by dragging its handles', async ({
|
||||||
page,
|
page,
|
||||||
editor,
|
|
||||||
homePage,
|
|
||||||
}) => {
|
}) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
@ -355,8 +358,9 @@ test.describe('Sketch tests', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('button', { name: 'Start Sketch' })
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
).not.toBeDisabled()
|
).not.toBeDisabled()
|
||||||
@ -395,7 +399,6 @@ test.describe('Sketch tests', () => {
|
|||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||||
await page.waitForTimeout(400)
|
await page.waitForTimeout(400)
|
||||||
|
|
||||||
let prevContent = await page.locator('.cm-content').innerText()
|
let prevContent = await page.locator('.cm-content').innerText()
|
||||||
|
|
||||||
await expect(page.getByTestId('segment-overlay')).toHaveCount(1)
|
await expect(page.getByTestId('segment-overlay')).toHaveCount(1)
|
||||||
@ -406,9 +409,7 @@ test.describe('Sketch tests', () => {
|
|||||||
targetPosition: { x: startPX[0] + dragPX, y: startPX[1] - dragPX },
|
targetPosition: { x: startPX[0] + dragPX, y: startPX[1] - dragPX },
|
||||||
})
|
})
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
||||||
await editor.expectEditor.not.toContain(prevContent)
|
|
||||||
|
|
||||||
prevContent = await page.locator('.cm-content').innerText()
|
prevContent = await page.locator('.cm-content').innerText()
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -421,20 +422,18 @@ test.describe('Sketch tests', () => {
|
|||||||
sourcePosition: { x: lineEnd.x - 5, y: lineEnd.y },
|
sourcePosition: { x: lineEnd.x - 5, y: lineEnd.y },
|
||||||
targetPosition: { x: lineEnd.x + dragPX * 2, y: lineEnd.y + dragPX },
|
targetPosition: { x: lineEnd.x + dragPX * 2, y: lineEnd.y + dragPX },
|
||||||
})
|
})
|
||||||
await editor.expectEditor.not.toContain(prevContent)
|
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
||||||
prevContent = await page.locator('.cm-content').innerText()
|
prevContent = await page.locator('.cm-content').innerText()
|
||||||
})
|
})
|
||||||
|
|
||||||
// expect the code to have changed
|
// expect the code to have changed
|
||||||
await editor.expectEditor.toContain(
|
await expect(page.locator('.cm-content'))
|
||||||
`sketch001 = startSketchOn('XZ')
|
.toHaveText(`sketch001 = startSketchOn('XZ')
|
||||||
|> circle({ center = [7.26, -2.37], radius = 11.44 }, %)`,
|
|> circle({ center = [7.26, -2.37], radius = 11.44 }, %)
|
||||||
{ shouldNormalise: true }
|
`)
|
||||||
)
|
|
||||||
})
|
})
|
||||||
test('Can edit a sketch that has been extruded in the same pipe', async ({
|
test('Can edit a sketch that has been extruded in the same pipe', async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
|
||||||
}) => {
|
}) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
@ -449,8 +448,9 @@ test.describe('Sketch tests', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('button', { name: 'Start Sketch' })
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
).not.toBeDisabled()
|
).not.toBeDisabled()
|
||||||
@ -504,11 +504,11 @@ test.describe('Sketch tests', () => {
|
|||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
const lineEnd = await u.getBoundingBox('[data-overlay-index="0"]')
|
const lineEnd = await u.getBoundingBox('[data-overlay-index="0"]')
|
||||||
await page.dragAndDrop('#stream', '#stream', {
|
|
||||||
sourcePosition: { x: lineEnd.x - 15, y: lineEnd.y },
|
|
||||||
targetPosition: { x: lineEnd.x, y: lineEnd.y + 15 },
|
|
||||||
})
|
|
||||||
await page.waitForTimeout(100)
|
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)
|
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
||||||
prevContent = await page.locator('.cm-content').innerText()
|
prevContent = await page.locator('.cm-content').innerText()
|
||||||
|
|
||||||
@ -517,8 +517,8 @@ test.describe('Sketch tests', () => {
|
|||||||
await page.dragAndDrop('#stream', '#stream', {
|
await page.dragAndDrop('#stream', '#stream', {
|
||||||
sourcePosition: { x: tangentEnd.x + 10, y: tangentEnd.y - 5 },
|
sourcePosition: { x: tangentEnd.x + 10, y: tangentEnd.y - 5 },
|
||||||
targetPosition: {
|
targetPosition: {
|
||||||
x: tangentEnd.x,
|
x: tangentEnd.x + dragPX,
|
||||||
y: tangentEnd.y - 15,
|
y: tangentEnd.y + dragPX,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
@ -528,16 +528,15 @@ test.describe('Sketch tests', () => {
|
|||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toHaveText(`sketch001 = startSketchOn('XZ')
|
.toHaveText(`sketch001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([7.12, -12.68], %)
|
|> startProfileAt([7.12, -12.68], %)
|
||||||
|> line([12.68, -1.09], %)
|
|> line([15.39, -2.78], %)
|
||||||
|> tangentialArcTo([24.89, 0.68], %)
|
|> tangentialArcTo([27.6, -3.05], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(5, %)
|
|> extrude(5, %)
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Can edit a sketch that has been revolved in the same pipe', async ({
|
test('Can edit a sketch that has been revolved in the same pipe', async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
|
||||||
}) => {
|
}) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
@ -552,8 +551,9 @@ test.describe('Sketch tests', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('button', { name: 'Start Sketch' })
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
).not.toBeDisabled()
|
).not.toBeDisabled()
|
||||||
@ -636,13 +636,12 @@ test.describe('Sketch tests', () => {
|
|||||||
|> close(%)
|
|> close(%)
|
||||||
|> revolve({ axis = "X" }, %)`)
|
|> revolve({ axis = "X" }, %)`)
|
||||||
})
|
})
|
||||||
test('Can add multiple sketches', async ({ page, homePage }) => {
|
test('Can add multiple sketches', async ({ page }) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
|
|
||||||
const viewportSize = { width: 1200, height: 500 }
|
const viewportSize = { width: 1200, height: 500 }
|
||||||
await page.setBodyDimensions(viewportSize)
|
await page.setViewportSize(viewportSize)
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
|
|
||||||
const center = { x: viewportSize.width / 2, y: viewportSize.height / 2 }
|
const center = { x: viewportSize.width / 2, y: viewportSize.height / 2 }
|
||||||
@ -737,8 +736,9 @@ test.describe('Sketch tests', () => {
|
|||||||
scale = 1
|
scale = 1
|
||||||
) => {
|
) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
|
|
||||||
const code = `sketch001 = startSketchOn('-XZ')
|
const code = `sketch001 = startSketchOn('-XZ')
|
||||||
@ -820,19 +820,16 @@ test.describe('Sketch tests', () => {
|
|||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
await u.removeCurrentCode()
|
await u.removeCurrentCode()
|
||||||
}
|
}
|
||||||
test('[0, 100, 100]', async ({ page, homePage }) => {
|
test('[0, 100, 100]', async ({ page }) => {
|
||||||
await homePage.goToModelingScene()
|
|
||||||
await doSnapAtDifferentScales(page, [0, 100, 100], 0.01)
|
await doSnapAtDifferentScales(page, [0, 100, 100], 0.01)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('[0, 10000, 10000]', async ({ page, homePage }) => {
|
test('[0, 10000, 10000]', async ({ page }) => {
|
||||||
await homePage.goToModelingScene()
|
|
||||||
await doSnapAtDifferentScales(page, [0, 10000, 10000])
|
await doSnapAtDifferentScales(page, [0, 10000, 10000])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
test('exiting a close extrude, has the extrude button enabled ready to go', async ({
|
test('exiting a close extrude, has the extrude button enabled ready to go', async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
|
||||||
}) => {
|
}) => {
|
||||||
// this was a regression https://github.com/KittyCAD/modeling-app/issues/2832
|
// this was a regression https://github.com/KittyCAD/modeling-app/issues/2832
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
@ -844,15 +841,15 @@ test.describe('Sketch tests', () => {
|
|||||||
|> line([1.02, -1.32], %, $seg01)
|
|> line([1.02, -1.32], %, $seg01)
|
||||||
|> line([-1.01, -0.77], %)
|
|> line([-1.01, -0.77], %)
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
// wait for execution done
|
// wait for execution done
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
@ -888,10 +885,7 @@ test.describe('Sketch tests', () => {
|
|||||||
timeout: 10_000,
|
timeout: 10_000,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
test("Existing sketch with bad code delete user's code", async ({
|
test("Existing sketch with bad code delete user's code", async ({ page }) => {
|
||||||
page,
|
|
||||||
homePage,
|
|
||||||
}) => {
|
|
||||||
// this was a regression https://github.com/KittyCAD/modeling-app/issues/2832
|
// this was a regression https://github.com/KittyCAD/modeling-app/issues/2832
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
@ -903,15 +897,15 @@ test.describe('Sketch tests', () => {
|
|||||||
|> line([-1.01, -0.77], %)
|
|> line([-1.01, -0.77], %)
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
extrude001 = extrude(5, sketch001)
|
extrude001 = extrude(5, sketch001)
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
@ -943,16 +937,20 @@ test.describe('Sketch tests', () => {
|
|||||||
|> line([-1.01, -0.77], %)
|
|> line([-1.01, -0.77], %)
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
extrude001 = extrude(5, sketch001)
|
extrude001 = extrude(5, sketch001)
|
||||||
sketch002 = startSketchOn(extrude001, 'END')
|
sketch002 = startSketchOn(extrude001, 'END')
|
||||||
|>
|
|>
|
||||||
`.replace(/\s/g, '')
|
`.replace(/\s/g, '')
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
test('empty-scene default-planes act as expected', async ({
|
test('empty-scene default-planes act as expected', async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
browserName,
|
||||||
}) => {
|
}) => {
|
||||||
|
test.skip(
|
||||||
|
browserName === 'webkit',
|
||||||
|
'Skip on Safari until `window.tearDown` is working there'
|
||||||
|
)
|
||||||
/**
|
/**
|
||||||
* Tests the following things
|
* Tests the following things
|
||||||
* 1) The the planes are there on load because the scene is empty
|
* 1) The the planes are there on load because the scene is empty
|
||||||
@ -965,7 +963,9 @@ test.describe('Sketch tests', () => {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await homePage.goToModelingScene()
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
@ -992,7 +992,7 @@ test.describe('Sketch tests', () => {
|
|||||||
|> line([20, 0], %)
|
|> line([20, 0], %)
|
||||||
|> line([0, 20], %)
|
|> line([0, 20], %)
|
||||||
|> xLine(-20, %)
|
|> xLine(-20, %)
|
||||||
`)
|
`)
|
||||||
|
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
|
||||||
@ -1013,7 +1013,7 @@ test.describe('Sketch tests', () => {
|
|||||||
|
|
||||||
// click start Sketch
|
// click start Sketch
|
||||||
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||||
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y, { steps: 50 })
|
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y, { steps: 5 })
|
||||||
const hoveredColor: [number, number, number] = [93, 93, 127]
|
const hoveredColor: [number, number, number] = [93, 93, 127]
|
||||||
// now that we're expecting the user to select a plan, it does respond to hover
|
// now that we're expecting the user to select a plan, it does respond to hover
|
||||||
await expect
|
await expect
|
||||||
@ -1029,7 +1029,7 @@ test.describe('Sketch tests', () => {
|
|||||||
await expect(u.codeLocator).toHaveText(`sketch001 = startSketchOn('XZ')
|
await expect(u.codeLocator).toHaveText(`sketch001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([11.8, 9.09], %)
|
|> startProfileAt([11.8, 9.09], %)
|
||||||
|> line([3.39, -3.39], %)
|
|> line([3.39, -3.39], %)
|
||||||
`)
|
`)
|
||||||
|
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
@ -1037,9 +1037,11 @@ test.describe('Sketch tests', () => {
|
|||||||
`sketch001 = startSketchOn('XZ')
|
`sketch001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([11.8, 9.09], %)
|
|> startProfileAt([11.8, 9.09], %)
|
||||||
|> line([3.39, -3.39], %)
|
|> line([3.39, -3.39], %)
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
await page.reload()
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
@ -1051,9 +1053,16 @@ test.describe('Sketch tests', () => {
|
|||||||
).toBeLessThan(3)
|
).toBeLessThan(3)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Can attempt to sketch on revolved face', async ({ page, homePage }) => {
|
test('Can attempt to sketch on revolved face', async ({
|
||||||
|
page,
|
||||||
|
browserName,
|
||||||
|
}) => {
|
||||||
|
test.skip(
|
||||||
|
browserName === 'webkit',
|
||||||
|
'Skip on Safari until `window.tearDown` is working there'
|
||||||
|
)
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
@ -1078,7 +1087,7 @@ test.describe('Sketch tests', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
@ -1110,10 +1119,9 @@ test.describe('Sketch tests', () => {
|
|||||||
|
|
||||||
test('Can sketch on face when user defined function was used in the sketch', async ({
|
test('Can sketch on face when user defined function was used in the sketch', async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
|
||||||
}) => {
|
}) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
// Checking for a regression that performs a sketch when a user defined function
|
// Checking for a regression that performs a sketch when a user defined function
|
||||||
// is declared at the top of the file and used in the sketch that is being drawn on.
|
// is declared at the top of the file and used in the sketch that is being drawn on.
|
||||||
@ -1124,16 +1132,16 @@ test.describe('Sketch tests', () => {
|
|||||||
'persistCode',
|
'persistCode',
|
||||||
`fn in2mm = (inches) => {
|
`fn in2mm = (inches) => {
|
||||||
return inches * 25.4
|
return inches * 25.4
|
||||||
}
|
}
|
||||||
|
|
||||||
const railTop = in2mm(.748)
|
const railTop = in2mm(.748)
|
||||||
const railSide = in2mm(.024)
|
const railSide = in2mm(.024)
|
||||||
const railBaseWidth = in2mm(.612)
|
const railBaseWidth = in2mm(.612)
|
||||||
const railWideWidth = in2mm(.835)
|
const railWideWidth = in2mm(.835)
|
||||||
const railBaseLength = in2mm(.200)
|
const railBaseLength = in2mm(.200)
|
||||||
const railClampable = in2mm(.200)
|
const railClampable = in2mm(.200)
|
||||||
|
|
||||||
const rail = startSketchOn('XZ')
|
const rail = startSketchOn('XZ')
|
||||||
|> startProfileAt([
|
|> startProfileAt([
|
||||||
-railTop / 2,
|
-railTop / 2,
|
||||||
railClampable + railBaseLength
|
railClampable + railBaseLength
|
||||||
@ -1167,7 +1175,7 @@ test.describe('Sketch tests', () => {
|
|||||||
|
|
||||||
const center = { x: 600, y: 250 }
|
const center = { x: 600, y: 250 }
|
||||||
const rectangleSize = 20
|
const rectangleSize = 20
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
// Start a sketch
|
// Start a sketch
|
||||||
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||||
@ -1206,33 +1214,27 @@ test.describe('Sketch tests', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Sketch mode should be toleratant to syntax errors', () => {
|
test2.describe('Sketch mode should be toleratant to syntax errors', () => {
|
||||||
test(
|
test2(
|
||||||
'adding a syntax error, recovers after fixing',
|
'adding a syntax error, recovers after fixing',
|
||||||
{ tag: ['@skipWin'] },
|
{ tag: ['@skipWin'] },
|
||||||
async ({ page, homePage, context, scene, editor, toolbar }) => {
|
async ({ app, scene, editor, toolbar }) => {
|
||||||
const file = await fs.readFile(
|
test.skip(
|
||||||
path.resolve(
|
process.platform === 'win32',
|
||||||
__dirname,
|
'a codemirror error appears in this test only on windows, that causes the test to fail only because of our "no new error" logic, but it can not be replicated locally'
|
||||||
'../../',
|
|
||||||
'./src/wasm-lib/tests/executor/inputs/e2e-can-sketch-on-chamfer.kcl'
|
|
||||||
),
|
|
||||||
'utf-8'
|
|
||||||
)
|
)
|
||||||
await context.addInitScript((file) => {
|
const file = await app.getInputFile('e2e-can-sketch-on-chamfer.kcl')
|
||||||
localStorage.setItem('persistCode', file)
|
await app.initialise(file)
|
||||||
}, file)
|
|
||||||
await homePage.goToModelingScene()
|
|
||||||
|
|
||||||
const [objClick] = scene.makeMouseHelpers(600, 250)
|
const [objClick] = scene.makeMouseHelpers(600, 250)
|
||||||
const arrowHeadLocation = { x: 706, y: 129 } as const
|
const arrowHeadLocation = { x: 604, y: 129 } as const
|
||||||
const arrowHeadWhite: [number, number, number] = [255, 255, 255]
|
const arrowHeadWhite: [number, number, number] = [255, 255, 255]
|
||||||
const backgroundGray: [number, number, number] = [28, 28, 28]
|
const backgroundGray: [number, number, number] = [28, 28, 28]
|
||||||
const verifyArrowHeadColor = async (c: [number, number, number]) =>
|
const verifyArrowHeadColor = async (c: [number, number, number]) =>
|
||||||
scene.expectPixelColor(c, arrowHeadLocation, 15)
|
scene.expectPixelColor(c, arrowHeadLocation, 15)
|
||||||
|
|
||||||
await test.step('check chamfer selection changes cursor positon', async () => {
|
await test.step('check chamfer selection changes cursor positon', async () => {
|
||||||
await expect(async () => {
|
await expect2(async () => {
|
||||||
// sometimes initial click doesn't register
|
// sometimes initial click doesn't register
|
||||||
await objClick()
|
await objClick()
|
||||||
await editor.expectActiveLinesToBe([
|
await editor.expectActiveLinesToBe([
|
||||||
@ -1268,36 +1270,24 @@ test.describe('Sketch mode should be toleratant to syntax errors', () => {
|
|||||||
// this checks sketch segments have been drawn
|
// this checks sketch segments have been drawn
|
||||||
await verifyArrowHeadColor(arrowHeadWhite)
|
await verifyArrowHeadColor(arrowHeadWhite)
|
||||||
})
|
})
|
||||||
await page.waitForTimeout(100)
|
await app.page.waitForTimeout(100)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe(`Sketching with offset planes`, () => {
|
test2.describe(`Sketching with offset planes`, () => {
|
||||||
test(`Can select an offset plane to sketch on`, async ({
|
test2(
|
||||||
context,
|
`Can select an offset plane to sketch on`,
|
||||||
page,
|
async ({ app, scene, toolbar, editor }) => {
|
||||||
scene,
|
|
||||||
toolbar,
|
|
||||||
editor,
|
|
||||||
homePage,
|
|
||||||
}) => {
|
|
||||||
// We seed the scene with a single offset plane
|
// We seed the scene with a single offset plane
|
||||||
await context.addInitScript(() => {
|
await app.initialise(`offsetPlane001 = offsetPlane("XY", 10)`)
|
||||||
localStorage.setItem(
|
|
||||||
'persistCode',
|
|
||||||
`offsetPlane001 = offsetPlane("XY", 10)`
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
|
||||||
|
|
||||||
const [planeClick, planeHover] = scene.makeMouseHelpers(650, 200)
|
const [planeClick, planeHover] = scene.makeMouseHelpers(650, 200)
|
||||||
|
|
||||||
await test.step(`Start sketching on the offset plane`, async () => {
|
await test2.step(`Start sketching on the offset plane`, async () => {
|
||||||
await toolbar.startSketchPlaneSelection()
|
await toolbar.startSketchPlaneSelection()
|
||||||
|
|
||||||
await test.step(`Hovering should highlight code`, async () => {
|
await test2.step(`Hovering should highlight code`, async () => {
|
||||||
await planeHover()
|
await planeHover()
|
||||||
await editor.expectState({
|
await editor.expectState({
|
||||||
activeLines: [`offsetPlane001=offsetPlane("XY",10)`],
|
activeLines: [`offsetPlane001=offsetPlane("XY",10)`],
|
||||||
@ -1306,18 +1296,22 @@ test.describe(`Sketching with offset planes`, () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step(`Clicking should select the plane and enter sketch mode`, async () => {
|
await test2.step(
|
||||||
|
`Clicking should select the plane and enter sketch mode`,
|
||||||
|
async () => {
|
||||||
await planeClick()
|
await planeClick()
|
||||||
// Have to wait for engine-side animation to finish
|
// Have to wait for engine-side animation to finish
|
||||||
await page.waitForTimeout(600)
|
await app.page.waitForTimeout(600)
|
||||||
await expect(toolbar.lineBtn).toBeEnabled()
|
await expect2(toolbar.lineBtn).toBeEnabled()
|
||||||
await editor.expectEditor.toContain('startSketchOn(offsetPlane001)')
|
await editor.expectEditor.toContain('startSketchOn(offsetPlane001)')
|
||||||
await editor.expectState({
|
await editor.expectState({
|
||||||
activeLines: [`offsetPlane001=offsetPlane("XY",10)`],
|
activeLines: [`offsetPlane001=offsetPlane("XY",10)`],
|
||||||
diagnostics: [],
|
diagnostics: [],
|
||||||
highlightedCode: '',
|
highlightedCode: '',
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
}
|
||||||
})
|
)
|
||||||
})
|
})
|
||||||
|
@ -47,11 +47,7 @@ test.beforeEach(async ({ page }) => {
|
|||||||
|
|
||||||
test.setTimeout(60_000)
|
test.setTimeout(60_000)
|
||||||
|
|
||||||
// We test this end to end already - getting this to work on web just to take
|
test(
|
||||||
// a snapshot of it feels weird. I'd rather our regular tests fail.
|
|
||||||
// The primary failure is doExport now relies on the filesystem. We can follow
|
|
||||||
// up with another PR if we want this back.
|
|
||||||
test.skip(
|
|
||||||
'exports of each format should work',
|
'exports of each format should work',
|
||||||
{ tag: ['@snapshot', '@skipWin', '@skipMacos'] },
|
{ tag: ['@snapshot', '@skipWin', '@skipMacos'] },
|
||||||
async ({ page, context }) => {
|
async ({ page, context }) => {
|
||||||
@ -954,75 +950,7 @@ test(
|
|||||||
|
|
||||||
test.describe('Grid visibility', { tag: '@snapshot' }, () => {
|
test.describe('Grid visibility', { tag: '@snapshot' }, () => {
|
||||||
// FIXME: Skip on macos its being weird.
|
// FIXME: Skip on macos its being weird.
|
||||||
// test.skip(process.platform === 'darwin', 'Skip on macos')
|
test.skip(process.platform === 'darwin', 'Skip on macos')
|
||||||
|
|
||||||
test('Grid turned off to on via command bar', async ({ page }) => {
|
|
||||||
const u = await getUtils(page)
|
|
||||||
const stream = page.getByTestId('stream')
|
|
||||||
const mask = [
|
|
||||||
page.locator('#app-header'),
|
|
||||||
page.locator('#sidebar-top-ribbon'),
|
|
||||||
page.locator('#sidebar-bottom-ribbon'),
|
|
||||||
]
|
|
||||||
|
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
|
||||||
await page.goto('/')
|
|
||||||
await u.waitForAuthSkipAppStart()
|
|
||||||
|
|
||||||
await u.openDebugPanel()
|
|
||||||
// wait for execution done
|
|
||||||
await expect(
|
|
||||||
page.locator('[data-message-type="execution-done"]')
|
|
||||||
).toHaveCount(1)
|
|
||||||
await u.closeDebugPanel()
|
|
||||||
await u.closeKclCodePanel()
|
|
||||||
// TODO: Find a way to truly know that the objects have finished
|
|
||||||
// rendering, because an execution-done message is not sufficient.
|
|
||||||
await page.waitForTimeout(1000)
|
|
||||||
|
|
||||||
// Open the command bar.
|
|
||||||
await page
|
|
||||||
.getByRole('button', { name: 'Commands', exact: false })
|
|
||||||
.or(page.getByRole('button', { name: '⌘K' }))
|
|
||||||
.click()
|
|
||||||
const commandName = 'show scale grid'
|
|
||||||
const commandOption = page.getByRole('option', {
|
|
||||||
name: commandName,
|
|
||||||
exact: false,
|
|
||||||
})
|
|
||||||
const cmdSearchBar = page.getByPlaceholder('Search commands')
|
|
||||||
// This selector changes after we set the setting
|
|
||||||
await cmdSearchBar.fill(commandName)
|
|
||||||
await expect(commandOption).toBeVisible()
|
|
||||||
await commandOption.click()
|
|
||||||
|
|
||||||
const toggleInput = page.getByPlaceholder('Off')
|
|
||||||
await expect(toggleInput).toBeVisible()
|
|
||||||
await expect(toggleInput).toBeFocused()
|
|
||||||
|
|
||||||
// Select On
|
|
||||||
await page.keyboard.press('ArrowDown')
|
|
||||||
await expect(page.getByRole('option', { name: 'Off' })).toHaveAttribute(
|
|
||||||
'data-headlessui-state',
|
|
||||||
'active selected'
|
|
||||||
)
|
|
||||||
await page.keyboard.press('ArrowUp')
|
|
||||||
await expect(page.getByRole('option', { name: 'On' })).toHaveAttribute(
|
|
||||||
'data-headlessui-state',
|
|
||||||
'active'
|
|
||||||
)
|
|
||||||
await page.keyboard.press('Enter')
|
|
||||||
|
|
||||||
// Check the toast appeared
|
|
||||||
await expect(
|
|
||||||
page.getByText(`Set show scale grid to "true" as a user default`)
|
|
||||||
).toBeVisible()
|
|
||||||
|
|
||||||
await expect(stream).toHaveScreenshot({
|
|
||||||
maxDiffPixels: 100,
|
|
||||||
mask,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
test('Grid turned off', async ({ page }) => {
|
test('Grid turned off', async ({ page }) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
@ -109,21 +109,242 @@ keychain = startSketchOn("XY")
|
|||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(thickness, %)
|
|> extrude(thickness, %)
|
||||||
|
|
||||||
keychain1 = startSketchOn("XY")
|
// generated from /home/paultag/Downloads/zma-logomark.svg
|
||||||
|> startProfileAt([0, 0], %)
|
fn svg = (surface, origin, depth) => {
|
||||||
|> lineTo([width, 0], %)
|
let a0 = surface |> startProfileAt([origin[0] + 45.430427, origin[1] + -14.627736], %)
|
||||||
|> lineTo([width, height], %)
|
|> bezierCurve({
|
||||||
|> lineTo([0, height], %)
|
control1: [ 0, 0.764157 ],
|
||||||
|
control2: [ 0, 1.528314 ],
|
||||||
|
to: [ 0, 2.292469 ]
|
||||||
|
}, %)
|
||||||
|
|> bezierCurve({
|
||||||
|
control1: [ -3.03202, 0 ],
|
||||||
|
control2: [ -6.064039, 0 ],
|
||||||
|
to: [ -9.09606, 0 ]
|
||||||
|
}, %)
|
||||||
|
|> bezierCurve({
|
||||||
|
control1: [ 0, -1.077657 ],
|
||||||
|
control2: [ 0, -2.155312 ],
|
||||||
|
to: [ 0, -3.232969 ]
|
||||||
|
}, %)
|
||||||
|
|> bezierCurve({
|
||||||
|
control1: [ 2.741805, 0 ],
|
||||||
|
control2: [ 5.483613, 0 ],
|
||||||
|
to: [ 8.225417, 0 ]
|
||||||
|
}, %)
|
||||||
|
|> bezierCurve({
|
||||||
|
control1: [ -2.740682, -2.961815 ],
|
||||||
|
control2: [ -5.490342, -5.925794 ],
|
||||||
|
to: [ -8.225417, -8.886255 ]
|
||||||
|
}, %)
|
||||||
|
|> bezierCurve({
|
||||||
|
control1: [ 0, -0.723995 ],
|
||||||
|
control2: [ 0, -1.447988 ],
|
||||||
|
to: [ 0, -2.171981 ]
|
||||||
|
}, %)
|
||||||
|
|> bezierCurve({
|
||||||
|
control1: [ 0.712124, 0.05061 ],
|
||||||
|
control2: [ 1.511636, -0.09877 ],
|
||||||
|
to: [ 2.172096, 0.07005 ]
|
||||||
|
}, %)
|
||||||
|
|> bezierCurve({
|
||||||
|
control1: [ 0.68573, 0.740811 ],
|
||||||
|
control2: [ 1.371459, 1.481622 ],
|
||||||
|
to: [ 2.057187, 2.222436 ]
|
||||||
|
}, %)
|
||||||
|
|> bezierCurve({
|
||||||
|
control1: [ 0, -0.76416 ],
|
||||||
|
control2: [ 0, -1.52832 ],
|
||||||
|
to: [ 0, -2.29248 ]
|
||||||
|
}, %)
|
||||||
|
|> bezierCurve({
|
||||||
|
control1: [ 3.032013, 0 ],
|
||||||
|
control2: [ 6.064026, 0 ],
|
||||||
|
to: [ 9.096038, 0 ]
|
||||||
|
}, %)
|
||||||
|
|> bezierCurve({
|
||||||
|
control1: [ 0, 1.077657 ],
|
||||||
|
control2: [ 0, 2.155314 ],
|
||||||
|
to: [ 0, 3.232973 ]
|
||||||
|
}, %)
|
||||||
|
|> bezierCurve({
|
||||||
|
control1: [ -2.741312, 0 ],
|
||||||
|
control2: [ -5.482623, 0 ],
|
||||||
|
to: [ -8.223936, 0 ]
|
||||||
|
}, %)
|
||||||
|
|> bezierCurve({
|
||||||
|
control1: [ 2.741313, 2.961108 ],
|
||||||
|
control2: [ 5.482624, 5.922216 ],
|
||||||
|
to: [ 8.223936, 8.883325 ]
|
||||||
|
}, %)
|
||||||
|
|> bezierCurve({
|
||||||
|
control1: [ 0, 0.724968 ],
|
||||||
|
control2: [ 0, 1.449938 ],
|
||||||
|
to: [ 0, 2.174907 ]
|
||||||
|
}, %)
|
||||||
|
|> bezierCurve({
|
||||||
|
control1: [ -0.712656, -0.05145 ],
|
||||||
|
control2: [ -1.512554, 0.09643 ],
|
||||||
|
to: [ -2.173592, -0.07298 ]
|
||||||
|
}, %)
|
||||||
|
|> bezierCurve({
|
||||||
|
control1: [ -0.685222, -0.739834 ],
|
||||||
|
control2: [ -1.370445, -1.479669 ],
|
||||||
|
to: [ -2.055669, -2.219505 ]
|
||||||
|
}, %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(thickness, %)
|
|> extrude(depth, %)
|
||||||
|
|
||||||
keychain2 = startSketchOn("XY")
|
let a1 = surface |> startProfileAt([origin[0] + 57.920488, origin[1] + -15.244943], %)
|
||||||
|> startProfileAt([0, 0], %)
|
|> bezierCurve({
|
||||||
|> lineTo([width, 0], %)
|
control1: [ -2.78904, 0.106635 ],
|
||||||
|> lineTo([width, height], %)
|
control2: [ -5.052548, -2.969529 ],
|
||||||
|> lineTo([0, height], %)
|
to: [ -4.055141, -5.598369 ]
|
||||||
|
}, %)
|
||||||
|
|> bezierCurve({
|
||||||
|
control1: [ 0.841523, -0.918736 ],
|
||||||
|
control2: [ 0.439412, -1.541892 ],
|
||||||
|
to: [ -0.368488, -2.214378 ]
|
||||||
|
}, %)
|
||||||
|
|> bezierCurve({
|
||||||
|
control1: [ -0.418245, -0.448461 ],
|
||||||
|
control2: [ -0.836489, -0.896922 ],
|
||||||
|
to: [ -1.254732, -1.345384 ]
|
||||||
|
}, %)
|
||||||
|
|> bezierCurve({
|
||||||
|
control1: [ -2.76806, 2.995359 ],
|
||||||
|
control2: [ -2.32667, 8.18409 ],
|
||||||
|
to: [ 0.897655, 10.678932 ]
|
||||||
|
}, %)
|
||||||
|
|> bezierCurve({
|
||||||
|
control1: [ 2.562822, 2.186098 ],
|
||||||
|
control2: [ 6.605111, 2.28043 ],
|
||||||
|
to: [ 9.271202, 0.226476 ]
|
||||||
|
}, %)
|
||||||
|
|> bezierCurve({
|
||||||
|
control1: [ -0.743744, -0.797465 ],
|
||||||
|
control2: [ -1.487487, -1.594932 ],
|
||||||
|
to: [ -2.231232, -2.392397 ]
|
||||||
|
}, %)
|
||||||
|
|> bezierCurve({
|
||||||
|
control1: [ -0.672938, 0.421422 ],
|
||||||
|
control2: [ -1.465362, 0.646946 ],
|
||||||
|
to: [ -2.259264, 0.64512 ]
|
||||||
|
}, %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(thickness, %)
|
|> extrude(depth, %)
|
||||||
|
|
||||||
|
let a2 = surface |> startProfileAt([origin[0] + 62.19406300000001, origin[1] + -19.500698999999997], %)
|
||||||
|
|> bezierCurve({
|
||||||
|
control1: [ 0.302938, 1.281141 ],
|
||||||
|
control2: [ -1.53575, 2.434288 ],
|
||||||
|
to: [ -0.10908, 3.279477 ]
|
||||||
|
}, %)
|
||||||
|
|> bezierCurve({
|
||||||
|
control1: [ 0.504637, 0.54145 ],
|
||||||
|
control2: [ 1.009273, 1.082899 ],
|
||||||
|
to: [ 1.513909, 1.624348 ]
|
||||||
|
}, %)
|
||||||
|
|> bezierCurve({
|
||||||
|
control1: [ 2.767778, -2.995425 ],
|
||||||
|
control2: [ 2.327135, -8.184384 ],
|
||||||
|
to: [ -0.897661, -10.679047 ]
|
||||||
|
}, %)
|
||||||
|
|> bezierCurve({
|
||||||
|
control1: [ -2.562947, -2.186022 ],
|
||||||
|
control2: [ -6.604089, -2.279606 ],
|
||||||
|
to: [ -9.271196, -0.227813 ]
|
||||||
|
}, %)
|
||||||
|
|> bezierCurve({
|
||||||
|
control1: [ 0.744231, 0.797952 ],
|
||||||
|
control2: [ 1.488461, 1.595904 ],
|
||||||
|
to: [ 2.232692, 2.393856 ]
|
||||||
|
}, %)
|
||||||
|
|> bezierCurve({
|
||||||
|
control1: [ 2.302377, -1.564629 ],
|
||||||
|
control2: [ 5.793126, -0.15358 ],
|
||||||
|
to: [ 6.396577, 2.547372 ]
|
||||||
|
}, %)
|
||||||
|
|> bezierCurve({
|
||||||
|
control1: [ 0.08981, 0.346302 ],
|
||||||
|
control2: [ 0.134865, 0.704078 ],
|
||||||
|
to: [ 0.13476, 1.061807 ]
|
||||||
|
}, %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(depth, %)
|
||||||
|
|
||||||
|
let a3 = surface |> startProfileAt([origin[0] + 74.124866, origin[1] + -15.244943], %)
|
||||||
|
|> bezierCurve({
|
||||||
|
control1: [ -2.78904, 0.106635 ],
|
||||||
|
control2: [ -5.052549, -2.969529 ],
|
||||||
|
to: [ -4.055142, -5.598369 ]
|
||||||
|
}, %)
|
||||||
|
|> bezierCurve({
|
||||||
|
control1: [ 0.841527, -0.918738 ],
|
||||||
|
control2: [ 0.43941, -1.541892 ],
|
||||||
|
to: [ -0.368497, -2.214367 ]
|
||||||
|
}, %)
|
||||||
|
|> bezierCurve({
|
||||||
|
control1: [ -0.418254, -0.448466 ],
|
||||||
|
control2: [ -0.836507, -0.896931 ],
|
||||||
|
to: [ -1.254761, -1.345395 ]
|
||||||
|
}, %)
|
||||||
|
|> bezierCurve({
|
||||||
|
control1: [ -2.768019, 2.995371 ],
|
||||||
|
control2: [ -2.326624, 8.184088 ],
|
||||||
|
to: [ 0.897678, 10.678932 ]
|
||||||
|
}, %)
|
||||||
|
|> bezierCurve({
|
||||||
|
control1: [ 2.56289, 2.186191 ],
|
||||||
|
control2: [ 6.60516, 2.280307 ],
|
||||||
|
to: [ 9.271371, 0.226476 ]
|
||||||
|
}, %)
|
||||||
|
|> bezierCurve({
|
||||||
|
control1: [ -0.743808, -0.797465 ],
|
||||||
|
control2: [ -1.487616, -1.594932 ],
|
||||||
|
to: [ -2.231424, -2.392397 ]
|
||||||
|
}, %)
|
||||||
|
|> bezierCurve({
|
||||||
|
control1: [ -0.672916, 0.421433 ],
|
||||||
|
control2: [ -1.465344, 0.646926 ],
|
||||||
|
to: [ -2.259225, 0.64512 ]
|
||||||
|
}, %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(depth, %)
|
||||||
|
|
||||||
|
let a4 = surface |> startProfileAt([origin[0] + 77.57333899999998, origin[1] + -16.989262999999998], %)
|
||||||
|
|> bezierCurve({
|
||||||
|
control1: [ 0.743298, 0.797463 ],
|
||||||
|
control2: [ 1.486592, 1.594926 ],
|
||||||
|
to: [ 2.229888, 2.392389 ]
|
||||||
|
}, %)
|
||||||
|
|> bezierCurve({
|
||||||
|
control1: [ 2.767827, -2.995393 ],
|
||||||
|
control2: [ 2.327103, -8.184396 ],
|
||||||
|
to: [ -0.897672, -10.679047 ]
|
||||||
|
}, %)
|
||||||
|
|> bezierCurve({
|
||||||
|
control1: [ -2.562939, -2.186037 ],
|
||||||
|
control2: [ -6.604077, -2.279589 ],
|
||||||
|
to: [ -9.271185, -0.227813 ]
|
||||||
|
}, %)
|
||||||
|
|> bezierCurve({
|
||||||
|
control1: [ 0.744243, 0.797952 ],
|
||||||
|
control2: [ 1.488486, 1.595904 ],
|
||||||
|
to: [ 2.232729, 2.393856 ]
|
||||||
|
}, %)
|
||||||
|
|> bezierCurve({
|
||||||
|
control1: [ 2.302394, -1.564623 ],
|
||||||
|
control2: [ 5.793201, -0.153598 ],
|
||||||
|
to: [ 6.396692, 2.547372 ]
|
||||||
|
}, %)
|
||||||
|
|> bezierCurve({
|
||||||
|
control1: [ 0.32074, 1.215468 ],
|
||||||
|
control2: [ 0.06159, 2.564765 ],
|
||||||
|
to: [ -0.690452, 3.573243 ]
|
||||||
|
}, %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(depth, %)
|
||||||
|
|
||||||
box = startSketchOn('XY')
|
box = startSketchOn('XY')
|
||||||
|> startProfileAt([0, 0], %)
|
|> startProfileAt([0, 0], %)
|
||||||
@ -133,7 +354,7 @@ box = startSketchOn('XY')
|
|||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(10, %)
|
|> extrude(10, %)
|
||||||
|
|
||||||
sketch001 = startSketchOn(box, revolveAxis)
|
sketch001 = startSketchOn(box, revolveAxis)
|
||||||
|> startProfileAt([5, 10], %)
|
|> startProfileAt([5, 10], %)
|
||||||
|> line([0, -10], %)
|
|> line([0, -10], %)
|
||||||
|> line([2, 0], %)
|
|> line([2, 0], %)
|
||||||
@ -143,12 +364,18 @@ sketch001 = startSketchOn(box, revolveAxis)
|
|||||||
axis: revolveAxis,
|
axis: revolveAxis,
|
||||||
angle: 90
|
angle: 90
|
||||||
}, %)
|
}, %)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
sketch001 = startSketchOn('XZ')
|
|
||||||
|> startProfileAt([0.0, 0.0], %)
|
|
||||||
|> xLine(0.0, %)
|
|
||||||
|> close(%)
|
|
||||||
|
|
||||||
`
|
|
||||||
|
svg(startSketchOn(keychain, 'end'), [-33, 32], -thickness)
|
||||||
|
|
||||||
|
startSketchOn(keychain, 'end')
|
||||||
|
|> circle({ center: [
|
||||||
|
width / 2,
|
||||||
|
height - (keychainHoleSize + 1.5)
|
||||||
|
], radius: keychainHoleSize }, %)
|
||||||
|
|> extrude(-thickness, %)`
|
||||||
|
|
||||||
export const TEST_CODE_TRIGGER_ENGINE_EXPORT_ERROR = `thing = 1`
|
export const TEST_CODE_TRIGGER_ENGINE_EXPORT_ERROR = `thing = 1`
|
||||||
|
@ -1,16 +1,29 @@
|
|||||||
import { test, expect } from './zoo-test'
|
import { test, expect } from '@playwright/test'
|
||||||
|
|
||||||
import { commonPoints, getUtils } from './test-utils'
|
import { commonPoints, getUtils, setup, tearDown } from './test-utils'
|
||||||
|
|
||||||
|
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||||
|
await setup(context, page, testInfo)
|
||||||
|
})
|
||||||
|
|
||||||
|
test.afterEach(async ({ page }, testInfo) => {
|
||||||
|
await tearDown(page, testInfo)
|
||||||
|
})
|
||||||
|
|
||||||
test.describe('Test network and connection issues', () => {
|
test.describe('Test network and connection issues', () => {
|
||||||
test('simulate network down and network little widget', async ({
|
test('simulate network down and network little widget', async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
browserName,
|
||||||
}) => {
|
}) => {
|
||||||
|
// TODO: Don't skip Mac for these. After `window.tearDown` is working in Safari, these should work on webkit
|
||||||
|
test.skip(
|
||||||
|
browserName === 'webkit',
|
||||||
|
'Skip on Safari until `window.tearDown` is working there'
|
||||||
|
)
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
const networkToggle = page.getByTestId('network-toggle')
|
const networkToggle = page.getByTestId('network-toggle')
|
||||||
|
|
||||||
@ -49,7 +62,7 @@ test.describe('Test network and connection issues', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Expect the network to be down
|
// Expect the network to be down
|
||||||
await expect(networkToggle).toContainText('Problem')
|
await expect(networkToggle).toContainText('Offline')
|
||||||
|
|
||||||
// Click the network widget
|
// Click the network widget
|
||||||
await networkWidget.click()
|
await networkWidget.click()
|
||||||
@ -80,19 +93,26 @@ test.describe('Test network and connection issues', () => {
|
|||||||
|
|
||||||
test('Engine disconnect & reconnect in sketch mode', async ({
|
test('Engine disconnect & reconnect in sketch mode', async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
browserName,
|
||||||
}) => {
|
}) => {
|
||||||
// TODO: Don't skip Mac for these. After `window.tearDown` is working in Safari, these should work on webkit
|
// TODO: Don't skip Mac for these. After `window.tearDown` is working in Safari, these should work on webkit
|
||||||
|
test.skip(
|
||||||
|
browserName === 'webkit',
|
||||||
|
'Skip on Safari until `window.tearDown` is working there'
|
||||||
|
)
|
||||||
const networkToggle = page.getByTestId('network-toggle')
|
const networkToggle = page.getByTestId('network-toggle')
|
||||||
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
await u.waitForPageLoad()
|
|
||||||
|
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
|
).not.toBeDisabled({ timeout: 15000 })
|
||||||
|
|
||||||
// click on "Start Sketch" button
|
// click on "Start Sketch" button
|
||||||
await u.clearCommandLogs()
|
await u.clearCommandLogs()
|
||||||
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||||
@ -136,7 +156,7 @@ test.describe('Test network and connection issues', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Expect the network to be down
|
// Expect the network to be down
|
||||||
await expect(networkToggle).toContainText('Problem')
|
await expect(networkToggle).toContainText('Offline')
|
||||||
|
|
||||||
// Ensure we are not in sketch mode
|
// Ensure we are not in sketch mode
|
||||||
await expect(
|
await expect(
|
||||||
|
@ -1,20 +1,22 @@
|
|||||||
import {
|
import {
|
||||||
expect,
|
expect,
|
||||||
|
Page,
|
||||||
|
Download,
|
||||||
BrowserContext,
|
BrowserContext,
|
||||||
TestInfo,
|
TestInfo,
|
||||||
_electron as electron,
|
_electron as electron,
|
||||||
Locator,
|
Locator,
|
||||||
|
test,
|
||||||
} from '@playwright/test'
|
} from '@playwright/test'
|
||||||
import { test, Page } from './zoo-test'
|
|
||||||
import { EngineCommand } from 'lang/std/artifactGraph'
|
import { EngineCommand } from 'lang/std/artifactGraph'
|
||||||
import fsp from 'fs/promises'
|
import fsp from 'fs/promises'
|
||||||
import fsSync from 'fs'
|
import fsSync from 'fs'
|
||||||
import path from 'path'
|
import { join } from 'path'
|
||||||
import pixelMatch from 'pixelmatch'
|
import pixelMatch from 'pixelmatch'
|
||||||
import { PNG } from 'pngjs'
|
import { PNG } from 'pngjs'
|
||||||
import { Protocol } from 'playwright-core/types/protocol'
|
import { Protocol } from 'playwright-core/types/protocol'
|
||||||
import type { Models } from '@kittycad/lib'
|
import type { Models } from '@kittycad/lib'
|
||||||
import { COOKIE_NAME } from 'lib/constants'
|
import { APP_NAME, COOKIE_NAME } from 'lib/constants'
|
||||||
import { secrets } from './secrets'
|
import { secrets } from './secrets'
|
||||||
import {
|
import {
|
||||||
TEST_SETTINGS_KEY,
|
TEST_SETTINGS_KEY,
|
||||||
@ -28,134 +30,6 @@ import { isErrorWhitelisted } from './lib/console-error-whitelist'
|
|||||||
import { isArray } from 'lib/utils'
|
import { isArray } from 'lib/utils'
|
||||||
import { reportRejection } from 'lib/trap'
|
import { reportRejection } from 'lib/trap'
|
||||||
|
|
||||||
// The below is copied from playwright-core because it exports none of them :(
|
|
||||||
import { Env, BrowserContextOptions } from 'playwright-core'
|
|
||||||
import type * as channels from '@protocol/channels'
|
|
||||||
|
|
||||||
// Copied from playwright-core
|
|
||||||
function envObjectToArray(env: Env): { name: string; value: string }[] {
|
|
||||||
const result: { name: string; value: string }[] = []
|
|
||||||
for (const name in env) {
|
|
||||||
if (!Object.is(env[name], undefined))
|
|
||||||
result.push({ name, value: String(env[name]) })
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copied from playwright-core
|
|
||||||
export async function toClientCertificatesProtocol(
|
|
||||||
certs?: BrowserContextOptions['clientCertificates']
|
|
||||||
): Promise<channels.PlaywrightNewRequestParams['clientCertificates']> {
|
|
||||||
if (!certs) return undefined
|
|
||||||
|
|
||||||
const bufferizeContent = async (
|
|
||||||
value?: Buffer,
|
|
||||||
path?: string
|
|
||||||
): Promise<Buffer | undefined> => {
|
|
||||||
if (value) return value
|
|
||||||
if (path) return await fs.promises.readFile(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
return await Promise.all(
|
|
||||||
certs.map(async (cert) => ({
|
|
||||||
origin: cert.origin,
|
|
||||||
cert: await bufferizeContent(cert.cert, cert.certPath),
|
|
||||||
key: await bufferizeContent(cert.key, cert.keyPath),
|
|
||||||
pfx: await bufferizeContent(cert.pfx, cert.pfxPath),
|
|
||||||
passphrase: cert.passphrase,
|
|
||||||
}))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copied from playwright-core
|
|
||||||
function toAcceptDownloadsProtocol(acceptDownloads?: boolean) {
|
|
||||||
if (acceptDownloads === undefined) return undefined
|
|
||||||
if (acceptDownloads) return 'accept'
|
|
||||||
return 'deny'
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copied from playwright-core
|
|
||||||
function prepareRecordHarOptions(
|
|
||||||
options: BrowserContextOptions['recordHar']
|
|
||||||
): channels.RecordHarOptions | undefined {
|
|
||||||
if (!options) return
|
|
||||||
return {
|
|
||||||
path: options.path,
|
|
||||||
content: options.content || (options.omitContent ? 'omit' : undefined),
|
|
||||||
urlGlob: isString(options.urlFilter) ? options.urlFilter : undefined,
|
|
||||||
urlRegexSource: isRegExp(options.urlFilter)
|
|
||||||
? options.urlFilter.source
|
|
||||||
: undefined,
|
|
||||||
urlRegexFlags: isRegExp(options.urlFilter)
|
|
||||||
? options.urlFilter.flags
|
|
||||||
: undefined,
|
|
||||||
mode: options.mode,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copied from playwright-core
|
|
||||||
async function prepareStorageState(
|
|
||||||
options: BrowserContextOptions
|
|
||||||
): Promise<channels.BrowserNewContextParams['storageState']> {
|
|
||||||
if (typeof options.storageState !== 'string') return options.storageState
|
|
||||||
try {
|
|
||||||
return JSON.parse(await fs.promises.readFile(options.storageState, 'utf8'))
|
|
||||||
} catch (e) {
|
|
||||||
rewriteErrorMessage(
|
|
||||||
e,
|
|
||||||
`Error reading storage state from ${options.storageState}:\n` + e.message
|
|
||||||
)
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copied from playwright-core
|
|
||||||
async function prepareBrowserContextParams(
|
|
||||||
options: BrowserContextOptions
|
|
||||||
): Promise<channels.BrowserNewContextParams> {
|
|
||||||
if (options.videoSize && !options.videosPath)
|
|
||||||
throw new Error(`"videoSize" option requires "videosPath" to be specified`)
|
|
||||||
if (options.extraHTTPHeaders)
|
|
||||||
network.validateHeaders(options.extraHTTPHeaders)
|
|
||||||
const contextParams: channels.BrowserNewContextParams = {
|
|
||||||
...options,
|
|
||||||
viewport: options.viewport === null ? undefined : options.viewport,
|
|
||||||
noDefaultViewport: options.viewport === null,
|
|
||||||
extraHTTPHeaders: options.extraHTTPHeaders
|
|
||||||
? headersObjectToArray(options.extraHTTPHeaders)
|
|
||||||
: undefined,
|
|
||||||
storageState: await prepareStorageState(options),
|
|
||||||
serviceWorkers: options.serviceWorkers,
|
|
||||||
recordHar: prepareRecordHarOptions(options.recordHar),
|
|
||||||
colorScheme:
|
|
||||||
options.colorScheme === null ? 'no-override' : options.colorScheme,
|
|
||||||
reducedMotion:
|
|
||||||
options.reducedMotion === null ? 'no-override' : options.reducedMotion,
|
|
||||||
forcedColors:
|
|
||||||
options.forcedColors === null ? 'no-override' : options.forcedColors,
|
|
||||||
acceptDownloads: toAcceptDownloadsProtocol(options.acceptDownloads),
|
|
||||||
clientCertificates: await toClientCertificatesProtocol(
|
|
||||||
options.clientCertificates
|
|
||||||
),
|
|
||||||
}
|
|
||||||
if (!contextParams.recordVideo && options.videosPath) {
|
|
||||||
contextParams.recordVideo = {
|
|
||||||
dir: options.videosPath,
|
|
||||||
size: options.videoSize,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (contextParams.recordVideo && contextParams.recordVideo.dir)
|
|
||||||
contextParams.recordVideo.dir = path.resolve(
|
|
||||||
process.cwd(),
|
|
||||||
contextParams.recordVideo.dir
|
|
||||||
)
|
|
||||||
return contextParams
|
|
||||||
}
|
|
||||||
|
|
||||||
const toNormalizedCode = (text: string) => {
|
|
||||||
return text.replace(/\s+/g, '')
|
|
||||||
}
|
|
||||||
|
|
||||||
type TestColor = [number, number, number]
|
type TestColor = [number, number, number]
|
||||||
export const TEST_COLORS = {
|
export const TEST_COLORS = {
|
||||||
WHITE: [249, 249, 249] as TestColor,
|
WHITE: [249, 249, 249] as TestColor,
|
||||||
@ -224,16 +98,11 @@ async function removeCurrentCode(page: Page) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function sendCustomCmd(page: Page, cmd: EngineCommand) {
|
export async function sendCustomCmd(page: Page, cmd: EngineCommand) {
|
||||||
const json = JSON.stringify(cmd)
|
await page.getByTestId('custom-cmd-input').fill(JSON.stringify(cmd))
|
||||||
await page.getByTestId('custom-cmd-input').fill(json)
|
|
||||||
await expect(page.getByTestId('custom-cmd-input')).toHaveValue(json)
|
|
||||||
await page.getByTestId('custom-cmd-send-button').scrollIntoViewIfNeeded()
|
|
||||||
await page.getByTestId('custom-cmd-send-button').click()
|
await page.getByTestId('custom-cmd-send-button').click()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function clearCommandLogs(page: Page) {
|
async function clearCommandLogs(page: Page) {
|
||||||
await page.getByTestId('custom-cmd-input').fill('')
|
|
||||||
await page.getByTestId('clear-commands').scrollIntoViewIfNeeded()
|
|
||||||
await page.getByTestId('clear-commands').click()
|
await page.getByTestId('clear-commands').click()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,19 +150,6 @@ export async function closePane(page: Page, testId: string) {
|
|||||||
|
|
||||||
async function openKclCodePanel(page: Page) {
|
async function openKclCodePanel(page: Page) {
|
||||||
await openPane(page, 'code-pane-button')
|
await openPane(page, 'code-pane-button')
|
||||||
|
|
||||||
// Code Mirror lazy loads text! Wowza! Let's force-load the text for tests.
|
|
||||||
await page.evaluate(() => {
|
|
||||||
// editorManager is available on the window object.
|
|
||||||
//@ts-ignore this is in an entirely different context that tsc can't see.
|
|
||||||
editorManager._editorView.dispatch({
|
|
||||||
selection: {
|
|
||||||
//@ts-ignore this is in an entirely different context that tsc can't see.
|
|
||||||
anchor: editorManager._editorView.docView.length,
|
|
||||||
},
|
|
||||||
scrollIntoView: true,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function closeKclCodePanel(page: Page) {
|
async function closeKclCodePanel(page: Page) {
|
||||||
@ -309,9 +165,6 @@ async function closeKclCodePanel(page: Page) {
|
|||||||
|
|
||||||
async function openDebugPanel(page: Page) {
|
async function openDebugPanel(page: Page) {
|
||||||
await openPane(page, 'debug-pane-button')
|
await openPane(page, 'debug-pane-button')
|
||||||
|
|
||||||
// The debug pane needs time to load everything.
|
|
||||||
await page.waitForTimeout(3000)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function closeDebugPanel(page: Page) {
|
export async function closeDebugPanel(page: Page) {
|
||||||
@ -559,10 +412,6 @@ export async function getUtils(page: Page, test_?: typeof test) {
|
|||||||
.boundingBox({ timeout: 5_000 })
|
.boundingBox({ timeout: 5_000 })
|
||||||
.then((box) => ({ ...box, x: box?.x || 0, y: box?.y || 0 })),
|
.then((box) => ({ ...box, x: box?.x || 0, y: box?.y || 0 })),
|
||||||
codeLocator: page.locator('.cm-content'),
|
codeLocator: page.locator('.cm-content'),
|
||||||
crushKclCodeIntoOneLineAndThenMaybeSome: async () => {
|
|
||||||
const code = await page.locator('.cm-content').innerText()
|
|
||||||
return code.replaceAll(' ', '').replaceAll('\n', '')
|
|
||||||
},
|
|
||||||
normalisedEditorCode: async () => {
|
normalisedEditorCode: async () => {
|
||||||
const code = await page.locator('.cm-content').innerText()
|
const code = await page.locator('.cm-content').innerText()
|
||||||
return normaliseKclNumbers(code)
|
return normaliseKclNumbers(code)
|
||||||
@ -633,18 +482,13 @@ export async function getUtils(page: Page, test_?: typeof test) {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
toNormalizedCode(text: string) {
|
toNormalizedCode: (text: string) => {
|
||||||
return toNormalizedCode(text)
|
return text.replace(/\s+/g, '')
|
||||||
},
|
},
|
||||||
|
|
||||||
async editorTextMatches(code: string) {
|
editorTextMatches: async (code: string) => {
|
||||||
const editor = page.locator(editorSelector)
|
const editor = page.locator(editorSelector)
|
||||||
return expect
|
return expect(editor).toHaveText(code, { useInnerText: true })
|
||||||
.poll(async () => {
|
|
||||||
const text = await editor.textContent()
|
|
||||||
return toNormalizedCode(text ?? '')
|
|
||||||
})
|
|
||||||
.toContain(toNormalizedCode(code))
|
|
||||||
},
|
},
|
||||||
|
|
||||||
pasteCodeInEditor: async (code: string) => {
|
pasteCodeInEditor: async (code: string) => {
|
||||||
@ -670,7 +514,7 @@ export async function getUtils(page: Page, test_?: typeof test) {
|
|||||||
page.getByRole('button', { name: 'Start Sketch' })
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
).not.toBeDisabled()
|
).not.toBeDisabled()
|
||||||
await page.getByTestId('create-file-button').click()
|
await page.getByTestId('create-file-button').click()
|
||||||
await page.getByTestId('tree-input-field').fill(name)
|
await page.getByTestId('file-rename-field').fill(name)
|
||||||
await page.keyboard.press('Enter')
|
await page.keyboard.press('Enter')
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@ -830,34 +674,6 @@ export const makeTemplate: (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const PLAYWRIGHT_DOWNLOAD_DIR = 'downloads-during-playwright'
|
|
||||||
|
|
||||||
export const getPlaywrightDownloadDir = (page: Page) => {
|
|
||||||
return path.resolve(page.dir, PLAYWRIGHT_DOWNLOAD_DIR)
|
|
||||||
}
|
|
||||||
|
|
||||||
const moveDownloadedFileTo = async (page: Page, toLocation: string) => {
|
|
||||||
await fsp.mkdir(path.dirname(toLocation), { recursive: true })
|
|
||||||
|
|
||||||
const downloadDir = getPlaywrightDownloadDir(page)
|
|
||||||
|
|
||||||
// Expect there to be at least one file
|
|
||||||
await expect
|
|
||||||
.poll(async () => {
|
|
||||||
const files = await fsp.readdir(downloadDir)
|
|
||||||
return files.length
|
|
||||||
})
|
|
||||||
.toBeGreaterThan(0)
|
|
||||||
|
|
||||||
// Go through the downloads dir and move files to new location
|
|
||||||
const files = await fsp.readdir(downloadDir)
|
|
||||||
|
|
||||||
// Assumption: only ever one file here.
|
|
||||||
for (let file of files) {
|
|
||||||
await fsp.rename(path.resolve(downloadDir, file), toLocation)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Paths {
|
export interface Paths {
|
||||||
modelPath: string
|
modelPath: string
|
||||||
imagePath: string
|
imagePath: string
|
||||||
@ -870,8 +686,7 @@ export const doExport = async (
|
|||||||
exportFrom: 'dropdown' | 'sidebarButton' | 'commandBar' = 'dropdown'
|
exportFrom: 'dropdown' | 'sidebarButton' | 'commandBar' = 'dropdown'
|
||||||
): Promise<Paths> => {
|
): Promise<Paths> => {
|
||||||
if (exportFrom === 'dropdown') {
|
if (exportFrom === 'dropdown') {
|
||||||
await page.getByTestId('project-sidebar-toggle').click()
|
await page.getByRole('button', { name: APP_NAME }).click()
|
||||||
|
|
||||||
const exportMenuButton = page.getByRole('button', {
|
const exportMenuButton = page.getByRole('button', {
|
||||||
name: 'Export current part',
|
name: 'Export current part',
|
||||||
})
|
})
|
||||||
@ -912,12 +727,25 @@ export const doExport = async (
|
|||||||
}
|
}
|
||||||
await expect(page.getByText('Confirm Export')).toBeVisible()
|
await expect(page.getByText('Confirm Export')).toBeVisible()
|
||||||
|
|
||||||
|
const getPromiseAndResolve = () => {
|
||||||
|
let resolve: any = () => {}
|
||||||
|
const promise = new Promise<Download>((r) => {
|
||||||
|
resolve = r
|
||||||
|
})
|
||||||
|
return [promise, resolve]
|
||||||
|
}
|
||||||
|
|
||||||
|
const [downloadPromise1, downloadResolve1] = getPromiseAndResolve()
|
||||||
|
let downloadCnt = 0
|
||||||
|
|
||||||
|
if (exportFrom === 'dropdown')
|
||||||
|
page.on('download', async (download) => {
|
||||||
|
if (downloadCnt === 0) {
|
||||||
|
downloadResolve1(download)
|
||||||
|
}
|
||||||
|
downloadCnt++
|
||||||
|
})
|
||||||
await page.getByRole('button', { name: 'Submit command' }).click()
|
await page.getByRole('button', { name: 'Submit command' }).click()
|
||||||
|
|
||||||
// This usually happens immediately after. If we're too slow we don't
|
|
||||||
// catch it.
|
|
||||||
await expect(page.getByText('Exported successfully')).toBeVisible()
|
|
||||||
|
|
||||||
if (exportFrom === 'sidebarButton' || exportFrom === 'commandBar') {
|
if (exportFrom === 'sidebarButton' || exportFrom === 'commandBar') {
|
||||||
return {
|
return {
|
||||||
modelPath: '',
|
modelPath: '',
|
||||||
@ -927,12 +755,15 @@ export const doExport = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle download
|
// Handle download
|
||||||
|
const download = await downloadPromise1
|
||||||
const downloadLocationer = (extra = '', isImage = false) =>
|
const downloadLocationer = (extra = '', isImage = false) =>
|
||||||
`./e2e/playwright/export-snapshots/${output.type}-${
|
`./e2e/playwright/export-snapshots/${output.type}-${
|
||||||
'storage' in output ? output.storage : ''
|
'storage' in output ? output.storage : ''
|
||||||
}${extra}.${isImage ? 'png' : output.type}`
|
}${extra}.${isImage ? 'png' : output.type}`
|
||||||
const downloadLocation = downloadLocationer()
|
const downloadLocation = downloadLocationer()
|
||||||
|
|
||||||
|
await download.saveAs(downloadLocation)
|
||||||
|
|
||||||
if (output.type === 'step') {
|
if (output.type === 'step') {
|
||||||
// stable timestamps for step files
|
// stable timestamps for step files
|
||||||
const fileContents = await fsp.readFile(downloadLocation, 'utf-8')
|
const fileContents = await fsp.readFile(downloadLocation, 'utf-8')
|
||||||
@ -941,12 +772,6 @@ export const doExport = async (
|
|||||||
'1970-01-01T00:00:00.0+00:00'
|
'1970-01-01T00:00:00.0+00:00'
|
||||||
)
|
)
|
||||||
await fsp.writeFile(downloadLocation, newFileContents)
|
await fsp.writeFile(downloadLocation, newFileContents)
|
||||||
} else {
|
|
||||||
// By default all files are downloaded to the same place in playwright
|
|
||||||
// (declared in src/lib/exportSave)
|
|
||||||
// To remain consistent with our old web tests, we want to move some downloads
|
|
||||||
// (images) to another directory.
|
|
||||||
await moveDownloadedFileTo(page, downloadLocation)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -973,8 +798,6 @@ export async function tearDown(page: Page, testInfo: TestInfo) {
|
|||||||
// It seems it's best to give the browser about 3s to close things
|
// It seems it's best to give the browser about 3s to close things
|
||||||
// It's not super reliable but we have no real other choice for now
|
// It's not super reliable but we have no real other choice for now
|
||||||
await page.waitForTimeout(3000)
|
await page.waitForTimeout(3000)
|
||||||
|
|
||||||
await testInfo.tronApp?.close()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// settingsOverrides may need to be augmented to take more generic items,
|
// settingsOverrides may need to be augmented to take more generic items,
|
||||||
@ -985,24 +808,12 @@ export async function setup(
|
|||||||
testInfo?: TestInfo
|
testInfo?: TestInfo
|
||||||
) {
|
) {
|
||||||
await context.addInitScript(
|
await context.addInitScript(
|
||||||
async ({
|
async ({ token, settingsKey, settings, IS_PLAYWRIGHT_KEY }) => {
|
||||||
token,
|
|
||||||
settingsKey,
|
|
||||||
settings,
|
|
||||||
IS_PLAYWRIGHT_KEY,
|
|
||||||
PLAYWRIGHT_TEST_DIR,
|
|
||||||
PERSIST_MODELING_CONTEXT,
|
|
||||||
}) => {
|
|
||||||
localStorage.clear()
|
localStorage.clear()
|
||||||
localStorage.setItem('TOKEN_PERSIST_KEY', token)
|
localStorage.setItem('TOKEN_PERSIST_KEY', token)
|
||||||
localStorage.setItem('persistCode', ``)
|
localStorage.setItem('persistCode', ``)
|
||||||
localStorage.setItem(
|
|
||||||
PERSIST_MODELING_CONTEXT,
|
|
||||||
JSON.stringify({ openPanes: ['code'] })
|
|
||||||
)
|
|
||||||
localStorage.setItem(settingsKey, settings)
|
localStorage.setItem(settingsKey, settings)
|
||||||
localStorage.setItem(IS_PLAYWRIGHT_KEY, 'true')
|
localStorage.setItem(IS_PLAYWRIGHT_KEY, 'true')
|
||||||
localStorage.setItem('PLAYWRIGHT_TEST_DIR', PLAYWRIGHT_TEST_DIR)
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
token: secrets.token,
|
token: secrets.token,
|
||||||
@ -1019,8 +830,6 @@ export async function setup(
|
|||||||
} as Partial<SaveSettingsPayload>,
|
} as Partial<SaveSettingsPayload>,
|
||||||
}),
|
}),
|
||||||
IS_PLAYWRIGHT_KEY,
|
IS_PLAYWRIGHT_KEY,
|
||||||
PLAYWRIGHT_TEST_DIR: TEST_SETTINGS.app.projectDirectory,
|
|
||||||
PERSIST_MODELING_CONTEXT,
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1039,15 +848,12 @@ export async function setup(
|
|||||||
await page.emulateMedia({ reducedMotion: 'reduce' })
|
await page.emulateMedia({ reducedMotion: 'reduce' })
|
||||||
|
|
||||||
// Trigger a navigation, since loading file:// doesn't.
|
// Trigger a navigation, since loading file:// doesn't.
|
||||||
// await page.reload()
|
await page.reload()
|
||||||
}
|
}
|
||||||
|
|
||||||
let electronApp = undefined
|
|
||||||
let context = undefined
|
|
||||||
let page = undefined
|
|
||||||
|
|
||||||
export async function setupElectron({
|
export async function setupElectron({
|
||||||
testInfo,
|
testInfo,
|
||||||
|
folderSetupFn,
|
||||||
cleanProjectDir = true,
|
cleanProjectDir = true,
|
||||||
appSettings,
|
appSettings,
|
||||||
}: {
|
}: {
|
||||||
@ -1070,7 +876,7 @@ export async function setupElectron({
|
|||||||
await fsp.mkdir(projectDirName)
|
await fsp.mkdir(projectDirName)
|
||||||
}
|
}
|
||||||
|
|
||||||
const options = {
|
const electronApp = await electron.launch({
|
||||||
args: ['.', '--no-sandbox'],
|
args: ['.', '--no-sandbox'],
|
||||||
env: {
|
env: {
|
||||||
...process.env,
|
...process.env,
|
||||||
@ -1080,22 +886,14 @@ export async function setupElectron({
|
|||||||
...(process.env.ELECTRON_OVERRIDE_DIST_PATH
|
...(process.env.ELECTRON_OVERRIDE_DIST_PATH
|
||||||
? { executablePath: process.env.ELECTRON_OVERRIDE_DIST_PATH + 'electron' }
|
? { executablePath: process.env.ELECTRON_OVERRIDE_DIST_PATH + 'electron' }
|
||||||
: {}),
|
: {}),
|
||||||
}
|
})
|
||||||
|
const context = electronApp.context()
|
||||||
// Do this once and then reuse window on subsequent calls.
|
const page = await electronApp.firstWindow()
|
||||||
if (!electronApp) {
|
|
||||||
electronApp = await electron.launch(options)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!context || !page) {
|
|
||||||
context = electronApp.context()
|
|
||||||
page = await electronApp.firstWindow()
|
|
||||||
context.on('console', console.log)
|
context.on('console', console.log)
|
||||||
page.on('console', console.log)
|
page.on('console', console.log)
|
||||||
}
|
|
||||||
|
|
||||||
if (cleanProjectDir) {
|
if (cleanProjectDir) {
|
||||||
const tempSettingsFilePath = path.join(projectDirName, SETTINGS_FILE_NAME)
|
const tempSettingsFilePath = join(projectDirName, SETTINGS_FILE_NAME)
|
||||||
const settingsOverrides = TOML.stringify(
|
const settingsOverrides = TOML.stringify(
|
||||||
appSettings
|
appSettings
|
||||||
? {
|
? {
|
||||||
@ -1122,7 +920,11 @@ export async function setupElectron({
|
|||||||
await fsp.writeFile(tempSettingsFilePath, settingsOverrides)
|
await fsp.writeFile(tempSettingsFilePath, settingsOverrides)
|
||||||
}
|
}
|
||||||
|
|
||||||
return { electronApp, page, context, dir: projectDirName, options }
|
await folderSetupFn?.(projectDirName)
|
||||||
|
|
||||||
|
await setup(context, page)
|
||||||
|
|
||||||
|
return { electronApp, page, dir: projectDirName }
|
||||||
}
|
}
|
||||||
|
|
||||||
function failOnConsoleErrors(page: Page, testInfo?: TestInfo) {
|
function failOnConsoleErrors(page: Page, testInfo?: TestInfo) {
|
||||||
@ -1208,7 +1010,7 @@ export async function createProject({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function executorInputPath(fileName: string): string {
|
export function executorInputPath(fileName: string): string {
|
||||||
return path.join('src', 'wasm-lib', 'tests', 'executor', 'inputs', fileName)
|
return join('src', 'wasm-lib', 'tests', 'executor', 'inputs', fileName)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function doAndWaitForImageDiff(
|
export async function doAndWaitForImageDiff(
|
||||||
@ -1299,12 +1101,3 @@ export function getPixelRGBs(page: Page) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function pollEditorLinesSelectedLength(page: Page, lines: number) {
|
|
||||||
return expect
|
|
||||||
.poll(async () => {
|
|
||||||
const lines = await page.locator('.cm-activeLine').all()
|
|
||||||
return lines.length
|
|
||||||
})
|
|
||||||
.toBe(lines)
|
|
||||||
}
|
|
||||||
|
@ -1,14 +1,23 @@
|
|||||||
import { test, expect } from './zoo-test'
|
import { test, expect } from '@playwright/test'
|
||||||
import { EngineCommand } from 'lang/std/artifactGraph'
|
import { EngineCommand } from 'lang/std/artifactGraph'
|
||||||
import { uuidv4 } from 'lib/utils'
|
import { uuidv4 } from 'lib/utils'
|
||||||
import { getUtils } from './test-utils'
|
import { getUtils, setup, tearDown } from './test-utils'
|
||||||
|
|
||||||
|
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||||
|
await setup(context, page, testInfo)
|
||||||
|
})
|
||||||
|
|
||||||
|
test.afterEach(async ({ page }, testInfo) => {
|
||||||
|
await tearDown(page, testInfo)
|
||||||
|
})
|
||||||
|
|
||||||
test.describe('Testing Camera Movement', () => {
|
test.describe('Testing Camera Movement', () => {
|
||||||
test('Can move camera reliably', async ({ page, context, homePage }) => {
|
test('Can move camera reliably', async ({ page, context }) => {
|
||||||
|
test.skip(process.platform === 'darwin', 'Can move camera reliably')
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
await u.openAndClearDebugPanel()
|
await u.openAndClearDebugPanel()
|
||||||
await u.closeKclCodePanel()
|
await u.closeKclCodePanel()
|
||||||
|
|
||||||
@ -174,7 +183,6 @@ test.describe('Testing Camera Movement', () => {
|
|||||||
|
|
||||||
test('Zoom should be consistent when exiting or entering sketches', async ({
|
test('Zoom should be consistent when exiting or entering sketches', async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
|
||||||
}) => {
|
}) => {
|
||||||
// start new sketch pan and zoom before exiting, when exiting the sketch should stay in the same place
|
// start new sketch pan and zoom before exiting, when exiting the sketch should stay in the same place
|
||||||
// than zoom and pan outside of sketch mode and enter again and it should not change from where it is
|
// than zoom and pan outside of sketch mode and enter again and it should not change from where it is
|
||||||
@ -182,9 +190,9 @@ test.describe('Testing Camera Movement', () => {
|
|||||||
|
|
||||||
test.skip(process.platform !== 'darwin', 'Zoom should be consistent')
|
test.skip(process.platform !== 'darwin', 'Zoom should be consistent')
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
@ -336,10 +344,7 @@ test.describe('Testing Camera Movement', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test(`Zoom by scroll should not fire while orbiting`, async ({
|
test(`Zoom by scroll should not fire while orbiting`, async ({ page }) => {
|
||||||
page,
|
|
||||||
homePage,
|
|
||||||
}) => {
|
|
||||||
/**
|
/**
|
||||||
* Currently we only allow zooming by scroll when no other camera movement is happening,
|
* Currently we only allow zooming by scroll when no other camera movement is happening,
|
||||||
* set within cameraMouseDragGuards in cameraControls.ts,
|
* set within cameraMouseDragGuards in cameraControls.ts,
|
||||||
@ -378,7 +383,7 @@ test.describe('Testing Camera Movement', () => {
|
|||||||
const expectedOrbitCamZPosition = 64.0
|
const expectedOrbitCamZPosition = 64.0
|
||||||
|
|
||||||
await test.step(`Test setup`, async () => {
|
await test.step(`Test setup`, async () => {
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
await u.closeKclCodePanel()
|
await u.closeKclCodePanel()
|
||||||
// This test requires the mouse controls to be set to Solidworks
|
// This test requires the mouse controls to be set to Solidworks
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
|
@ -1,14 +1,18 @@
|
|||||||
import { test, expect } from './zoo-test'
|
import { test, expect } from '@playwright/test'
|
||||||
|
|
||||||
import {
|
import { getUtils, setup, tearDown, TEST_COLORS } from './test-utils'
|
||||||
getUtils,
|
|
||||||
TEST_COLORS,
|
|
||||||
pollEditorLinesSelectedLength,
|
|
||||||
} from './test-utils'
|
|
||||||
import { XOR } from 'lib/utils'
|
import { XOR } from 'lib/utils'
|
||||||
|
|
||||||
|
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||||
|
await setup(context, page, testInfo)
|
||||||
|
})
|
||||||
|
|
||||||
|
test.afterEach(async ({ page }, testInfo) => {
|
||||||
|
await tearDown(page, testInfo)
|
||||||
|
})
|
||||||
|
|
||||||
test.describe('Testing constraints', () => {
|
test.describe('Testing constraints', () => {
|
||||||
test('Can constrain line length', async ({ page, homePage }) => {
|
test('Can constrain line length', async ({ page }) => {
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'persistCode',
|
'persistCode',
|
||||||
@ -23,10 +27,9 @@ test.describe('Testing constraints', () => {
|
|||||||
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
await u.waitForPageLoad()
|
|
||||||
|
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
@ -38,9 +41,7 @@ test.describe('Testing constraints', () => {
|
|||||||
|
|
||||||
// enter sketch again
|
// enter sketch again
|
||||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||||
|
await page.waitForTimeout(500) // wait for animation
|
||||||
// Wait for overlays to populate
|
|
||||||
await page.waitForTimeout(1000)
|
|
||||||
|
|
||||||
const startXPx = 500
|
const startXPx = 500
|
||||||
await page.mouse.move(startXPx + PUR * 15, 250 - PUR * 10)
|
await page.mouse.move(startXPx + PUR * 15, 250 - PUR * 10)
|
||||||
@ -62,29 +63,27 @@ test.describe('Testing constraints', () => {
|
|||||||
page.getByRole('button', { name: 'Exit Sketch' })
|
page.getByRole('button', { name: 'Exit Sketch' })
|
||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
|
|
||||||
await page.waitForTimeout(2500) // wait for animation
|
await page.waitForTimeout(500) // wait for animation
|
||||||
|
|
||||||
// Exit sketch
|
// Exit sketch
|
||||||
await page.mouse.move(startXPx + PUR * 15, 250 - PUR * 10)
|
await page.mouse.move(startXPx + PUR * 15, 250 - PUR * 10)
|
||||||
await expect
|
await page.keyboard.press('Escape')
|
||||||
.poll(async () => {
|
await expect(
|
||||||
await page.keyboard.press('Escape', { delay: 500 })
|
page.getByRole('button', { name: 'Exit Sketch' })
|
||||||
return page.getByRole('button', { name: 'Exit Sketch' }).isVisible()
|
).not.toBeVisible()
|
||||||
})
|
})
|
||||||
.toBe(true)
|
test(`Remove constraints`, async ({ page }) => {
|
||||||
})
|
|
||||||
test(`Remove constraints`, async ({ page, homePage }) => {
|
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'persistCode',
|
'persistCode',
|
||||||
`yo = 79
|
`yo = 79
|
||||||
part001 = startSketchOn('XZ')
|
part001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([-7.54, -26.74], %)
|
|> startProfileAt([-7.54, -26.74], %)
|
||||||
|> line([74.36, 130.4], %, $seg01)
|
|> line([74.36, 130.4], %, $seg01)
|
||||||
|> line([78.92, -120.11], %)
|
|> line([78.92, -120.11], %)
|
||||||
|> angledLine([segAng(seg01), yo], %)
|
|> angledLine([segAng(seg01), yo], %)
|
||||||
|> line([41.19, 58.97 + 5], %)
|
|> line([41.19, 58.97 + 5], %)
|
||||||
part002 = startSketchOn('XZ')
|
part002 = startSketchOn('XZ')
|
||||||
|> startProfileAt([299.05, 120], %)
|
|> startProfileAt([299.05, 120], %)
|
||||||
|> xLine(-385.34, %, $seg_what)
|
|> xLine(-385.34, %, $seg_what)
|
||||||
|> yLine(-170.06, %)
|
|> yLine(-170.06, %)
|
||||||
@ -93,17 +92,13 @@ test.describe('Testing constraints', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
await u.waitForPageLoad()
|
|
||||||
|
|
||||||
await page.getByText('line([74.36, 130.4], %, $seg01)').click()
|
await page.getByText('line([74.36, 130.4], %, $seg01)').click()
|
||||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||||
|
|
||||||
// Wait for overlays to populate
|
|
||||||
await page.waitForTimeout(1000)
|
|
||||||
|
|
||||||
const line3 = await u.getSegmentBodyCoords(`[data-overlay-index="${2}"]`)
|
const line3 = await u.getSegmentBodyCoords(`[data-overlay-index="${2}"]`)
|
||||||
|
|
||||||
await page.mouse.click(line3.x, line3.y)
|
await page.mouse.click(line3.x, line3.y)
|
||||||
@ -116,8 +111,8 @@ test.describe('Testing constraints', () => {
|
|||||||
await page.getByRole('button', { name: 'remove constraints' }).click()
|
await page.getByRole('button', { name: 'remove constraints' }).click()
|
||||||
|
|
||||||
await page.getByText('line([39.13, 68.63], %)').click()
|
await page.getByText('line([39.13, 68.63], %)').click()
|
||||||
await pollEditorLinesSelectedLength(page, 1)
|
|
||||||
const activeLinesContent = await page.locator('.cm-activeLine').all()
|
const activeLinesContent = await page.locator('.cm-activeLine').all()
|
||||||
|
await expect(activeLinesContent).toHaveLength(1)
|
||||||
await expect(activeLinesContent[0]).toHaveText('|> line([39.13, 68.63], %)')
|
await expect(activeLinesContent[0]).toHaveText('|> line([39.13, 68.63], %)')
|
||||||
|
|
||||||
// checking the count of the overlays is a good proxy check that the client sketch scene is in a good state
|
// checking the count of the overlays is a good proxy check that the client sketch scene is in a good state
|
||||||
@ -135,75 +130,43 @@ test.describe('Testing constraints', () => {
|
|||||||
},
|
},
|
||||||
] as const
|
] as const
|
||||||
for (const { testName, offset } of cases) {
|
for (const { testName, offset } of cases) {
|
||||||
test(`${testName}`, async ({ page, homePage }) => {
|
test(`${testName}`, async ({ page }) => {
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'persistCode',
|
'persistCode',
|
||||||
`yo = 5
|
`yo = 5
|
||||||
part001 = startSketchOn('XZ')
|
part001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([-7.54, -26.74], %)
|
|> startProfileAt([-7.54, -26.74], %)
|
||||||
|> line([74.36, 130.4], %, $seg01)
|
|> line([74.36, 130.4], %, $seg01)
|
||||||
|> line([78.92, -120.11], %)
|
|> line([78.92, -120.11], %)
|
||||||
|> angledLine([segAng(seg01), 78.33], %)
|
|> angledLine([segAng(seg01), 78.33], %)
|
||||||
|> line([51.19, 48.97], %)
|
|> line([51.19, 48.97], %)
|
||||||
part002 = startSketchOn('XZ')
|
part002 = startSketchOn('XZ')
|
||||||
|> startProfileAt([299.05, 231.45], %)
|
|> startProfileAt([299.05, 231.45], %)
|
||||||
|> xLine(-425.34, %, $seg_what)
|
|> xLine(-425.34, %, $seg_what)
|
||||||
|> yLine(-264.06, %)
|
|> yLine(-264.06, %)
|
||||||
|> xLine(segLen(seg_what), %)
|
|> xLine(segLen(seg_what), %)
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)`
|
|> lineTo([profileStartX(%), profileStartY(%)], %)`
|
||||||
)
|
)
|
||||||
|
|
||||||
const isChecked = await createNewVariableCheckbox.isChecked()
|
|
||||||
const addVariable = testName === 'Add variable'
|
|
||||||
XOR(isChecked, addVariable) && // XOR because no need to click the checkbox if the state is already correct
|
|
||||||
(await createNewVariableCheckbox.click())
|
|
||||||
|
|
||||||
await page
|
|
||||||
.getByRole('button', { name: 'Add constraining value' })
|
|
||||||
.click()
|
|
||||||
|
|
||||||
// Wait for the codemod to take effect
|
|
||||||
await expect(page.locator('.cm-content')).toContainText(`angle: -57,`)
|
|
||||||
await expect(page.locator('.cm-content')).toContainText(
|
|
||||||
`offset: ${offset},`
|
|
||||||
)
|
|
||||||
|
|
||||||
await pollEditorLinesSelectedLength(page, 2)
|
|
||||||
const activeLinesContent = await page.locator('.cm-activeLine').all()
|
|
||||||
await expect(activeLinesContent[0]).toHaveText(
|
|
||||||
`|> line([74.36, 130.4], %, $seg01)`
|
|
||||||
)
|
|
||||||
await expect(activeLinesContent[1]).toHaveText(`}, %)`)
|
|
||||||
|
|
||||||
// checking the count of the overlays is a good proxy check that the client sketch scene is in a good state
|
|
||||||
await expect(page.getByTestId('segment-overlay')).toHaveCount(4)
|
|
||||||
})
|
})
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
await u.waitForPageLoad()
|
|
||||||
|
|
||||||
await page.getByText('line([74.36, 130.4], %, $seg01)').click()
|
await page.getByText('line([74.36, 130.4], %, $seg01)').click()
|
||||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||||
|
|
||||||
// Give time for overlays to populate
|
|
||||||
await page.waitForTimeout(1000)
|
|
||||||
|
|
||||||
const [line1, line3] = await Promise.all([
|
const [line1, line3] = await Promise.all([
|
||||||
u.getSegmentBodyCoords(`[data-overlay-index="${0}"]`),
|
u.getSegmentBodyCoords(`[data-overlay-index="${0}"]`),
|
||||||
u.getSegmentBodyCoords(`[data-overlay-index="${2}"]`),
|
u.getSegmentBodyCoords(`[data-overlay-index="${2}"]`),
|
||||||
])
|
])
|
||||||
|
|
||||||
await page.mouse.click(line1.x, line1.y)
|
await page.mouse.click(line1.x, line1.y)
|
||||||
await page.keyboard.up('Shift')
|
|
||||||
await page.keyboard.down('Shift')
|
await page.keyboard.down('Shift')
|
||||||
await page.waitForTimeout(100)
|
|
||||||
await page.mouse.click(line3.x, line3.y)
|
await page.mouse.click(line3.x, line3.y)
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100) // this wait is needed for webkit - not sure why
|
||||||
await page.keyboard.up('Shift')
|
await page.keyboard.up('Shift')
|
||||||
await page.waitForTimeout(100)
|
|
||||||
await page
|
await page
|
||||||
.getByRole('button', {
|
.getByRole('button', {
|
||||||
name: 'Length: open menu',
|
name: 'Length: open menu',
|
||||||
@ -231,7 +194,6 @@ test.describe('Testing constraints', () => {
|
|||||||
`offset = ${offset},`
|
`offset = ${offset},`
|
||||||
)
|
)
|
||||||
|
|
||||||
await pollEditorLinesSelectedLength(page, 2)
|
|
||||||
const activeLinesContent = await page.locator('.cm-activeLine').all()
|
const activeLinesContent = await page.locator('.cm-activeLine').all()
|
||||||
await expect(activeLinesContent[0]).toHaveText(
|
await expect(activeLinesContent[0]).toHaveText(
|
||||||
`|> line([74.36, 130.4], %, $seg01)`
|
`|> line([74.36, 130.4], %, $seg01)`
|
||||||
@ -267,18 +229,18 @@ test.describe('Testing constraints', () => {
|
|||||||
},
|
},
|
||||||
] as const
|
] as const
|
||||||
for (const { testName, value, constraint } of cases) {
|
for (const { testName, value, constraint } of cases) {
|
||||||
test(`${constraint} - ${testName}`, async ({ page, homePage }) => {
|
test(`${constraint} - ${testName}`, async ({ page }) => {
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'persistCode',
|
'persistCode',
|
||||||
`yo = 5
|
`yo = 5
|
||||||
part001 = startSketchOn('XZ')
|
part001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([-7.54, -26.74], %)
|
|> startProfileAt([-7.54, -26.74], %)
|
||||||
|> line([74.36, 130.4], %)
|
|> line([74.36, 130.4], %)
|
||||||
|> line([78.92, -120.11], %)
|
|> line([78.92, -120.11], %)
|
||||||
|> line([9.16, 77.79], %)
|
|> line([9.16, 77.79], %)
|
||||||
|> line([51.19, 48.97], %)
|
|> line([51.19, 48.97], %)
|
||||||
part002 = startSketchOn('XZ')
|
part002 = startSketchOn('XZ')
|
||||||
|> startProfileAt([299.05, 231.45], %)
|
|> startProfileAt([299.05, 231.45], %)
|
||||||
|> xLine(-425.34, %, $seg_what)
|
|> xLine(-425.34, %, $seg_what)
|
||||||
|> yLine(-264.06, %)
|
|> yLine(-264.06, %)
|
||||||
@ -287,17 +249,13 @@ test.describe('Testing constraints', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
await u.waitForPageLoad()
|
|
||||||
|
|
||||||
await page.getByText('line([74.36, 130.4], %)').click()
|
await page.getByText('line([74.36, 130.4], %)').click()
|
||||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||||
|
|
||||||
// Wait for overlays to populate
|
|
||||||
await page.waitForTimeout(1000)
|
|
||||||
|
|
||||||
const [line1, line3] = await Promise.all([
|
const [line1, line3] = await Promise.all([
|
||||||
u.getSegmentBodyCoords(`[data-overlay-index="${0}"]`),
|
u.getSegmentBodyCoords(`[data-overlay-index="${0}"]`),
|
||||||
u.getSegmentBodyCoords(`[data-overlay-index="${2}"]`),
|
u.getSegmentBodyCoords(`[data-overlay-index="${2}"]`),
|
||||||
@ -377,18 +335,18 @@ test.describe('Testing constraints', () => {
|
|||||||
},
|
},
|
||||||
] as const
|
] as const
|
||||||
for (const { testName, addVariable, value, constraint } of cases) {
|
for (const { testName, addVariable, value, constraint } of cases) {
|
||||||
test(`${constraint} - ${testName}`, async ({ page, homePage }) => {
|
test(`${constraint} - ${testName}`, async ({ page }) => {
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'persistCode',
|
'persistCode',
|
||||||
`yo = 5
|
`yo = 5
|
||||||
part001 = startSketchOn('XZ')
|
part001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([-7.54, -26.74], %)
|
|> startProfileAt([-7.54, -26.74], %)
|
||||||
|> line([74.36, 130.4], %)
|
|> line([74.36, 130.4], %)
|
||||||
|> line([78.92, -120.11], %)
|
|> line([78.92, -120.11], %)
|
||||||
|> line([9.16, 77.79], %)
|
|> line([9.16, 77.79], %)
|
||||||
|> line([51.19, 48.97], %)
|
|> line([51.19, 48.97], %)
|
||||||
part002 = startSketchOn('XZ')
|
part002 = startSketchOn('XZ')
|
||||||
|> startProfileAt([299.05, 231.45], %)
|
|> startProfileAt([299.05, 231.45], %)
|
||||||
|> xLine(-425.34, %, $seg_what)
|
|> xLine(-425.34, %, $seg_what)
|
||||||
|> yLine(-264.06, %)
|
|> yLine(-264.06, %)
|
||||||
@ -397,17 +355,13 @@ test.describe('Testing constraints', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
await u.waitForPageLoad()
|
|
||||||
|
|
||||||
await page.getByText('line([74.36, 130.4], %)').click()
|
await page.getByText('line([74.36, 130.4], %)').click()
|
||||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||||
|
|
||||||
// Wait for overlays to populate
|
|
||||||
await page.waitForTimeout(1000)
|
|
||||||
|
|
||||||
const [line3] = await Promise.all([
|
const [line3] = await Promise.all([
|
||||||
u.getSegmentBodyCoords(`[data-overlay-index="${2}"]`),
|
u.getSegmentBodyCoords(`[data-overlay-index="${2}"]`),
|
||||||
])
|
])
|
||||||
@ -418,11 +372,9 @@ test.describe('Testing constraints', () => {
|
|||||||
await page.mouse.click(900, 250)
|
await page.mouse.click(900, 250)
|
||||||
}
|
}
|
||||||
await page.keyboard.down('Shift')
|
await page.keyboard.down('Shift')
|
||||||
await page.waitForTimeout(100)
|
|
||||||
await page.mouse.click(line3.x, line3.y)
|
await page.mouse.click(line3.x, line3.y)
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100) // this wait is needed for webkit - not sure why
|
||||||
await page.keyboard.up('Shift')
|
await page.keyboard.up('Shift')
|
||||||
await page.waitForTimeout(100)
|
|
||||||
await page
|
await page
|
||||||
.getByRole('button', {
|
.getByRole('button', {
|
||||||
name: 'Length: open menu',
|
name: 'Length: open menu',
|
||||||
@ -490,18 +442,18 @@ test.describe('Testing constraints', () => {
|
|||||||
},
|
},
|
||||||
] as const
|
] as const
|
||||||
for (const { testName, addVariable, value, axisSelect } of cases) {
|
for (const { testName, addVariable, value, axisSelect } of cases) {
|
||||||
test(`${testName}`, async ({ page, homePage }) => {
|
test(`${testName}`, async ({ page }) => {
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'persistCode',
|
'persistCode',
|
||||||
`yo = 5
|
`yo = 5
|
||||||
part001 = startSketchOn('XZ')
|
part001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([-7.54, -26.74], %)
|
|> startProfileAt([-7.54, -26.74], %)
|
||||||
|> line([74.36, 130.4], %)
|
|> line([74.36, 130.4], %)
|
||||||
|> line([78.92, -120.11], %)
|
|> line([78.92, -120.11], %)
|
||||||
|> line([9.16, 77.79], %)
|
|> line([9.16, 77.79], %)
|
||||||
|> line([51.19, 48.97], %)
|
|> line([51.19, 48.97], %)
|
||||||
part002 = startSketchOn('XZ')
|
part002 = startSketchOn('XZ')
|
||||||
|> startProfileAt([299.05, 231.45], %)
|
|> startProfileAt([299.05, 231.45], %)
|
||||||
|> xLine(-425.34, %, $seg_what)
|
|> xLine(-425.34, %, $seg_what)
|
||||||
|> yLine(-264.06, %)
|
|> yLine(-264.06, %)
|
||||||
@ -510,17 +462,13 @@ test.describe('Testing constraints', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
await u.waitForPageLoad()
|
|
||||||
|
|
||||||
await page.getByText('line([74.36, 130.4], %)').click()
|
await page.getByText('line([74.36, 130.4], %)').click()
|
||||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||||
|
|
||||||
// Wait for overlays to populate
|
|
||||||
await page.waitForTimeout(1000)
|
|
||||||
|
|
||||||
const [line1, line3] = await Promise.all([
|
const [line1, line3] = await Promise.all([
|
||||||
u.getSegmentBodyCoords(`[data-overlay-index="${0}"]`),
|
u.getSegmentBodyCoords(`[data-overlay-index="${0}"]`),
|
||||||
u.getSegmentBodyCoords(`[data-overlay-index="${2}"]`),
|
u.getSegmentBodyCoords(`[data-overlay-index="${2}"]`),
|
||||||
@ -576,7 +524,7 @@ test.describe('Testing constraints', () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
test.describe('Test Angle constraint single selection', () => {
|
test.describe('Test Angle/Length constraint single selection', () => {
|
||||||
const cases = [
|
const cases = [
|
||||||
{
|
{
|
||||||
testName: 'Angle - Add variable',
|
testName: 'Angle - Add variable',
|
||||||
@ -590,70 +538,6 @@ test.describe('Testing constraints', () => {
|
|||||||
constraint: 'angle',
|
constraint: 'angle',
|
||||||
value: '83, 78.33',
|
value: '83, 78.33',
|
||||||
},
|
},
|
||||||
] as const
|
|
||||||
for (const { testName, addVariable, value, constraint } of cases) {
|
|
||||||
test(`${testName}`, async ({ page, homePage }) => {
|
|
||||||
await page.addInitScript(async () => {
|
|
||||||
localStorage.setItem(
|
|
||||||
'persistCode',
|
|
||||||
`yo = 5
|
|
||||||
part001 = startSketchOn('XZ')
|
|
||||||
|> startProfileAt([-7.54, -26.74], %)
|
|
||||||
|> line([74.36, 130.4], %)
|
|
||||||
|> line([78.92, -120.11], %)
|
|
||||||
|> line([9.16, 77.79], %)
|
|
||||||
|> line([51.19, 48.97], %)
|
|
||||||
part002 = startSketchOn('XZ')
|
|
||||||
|> startProfileAt([299.05, 231.45], %)
|
|
||||||
|> xLine(-425.34, %, $seg_what)
|
|
||||||
|> yLine(-264.06, %)
|
|
||||||
|> xLine(segLen(seg_what), %)
|
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)`
|
|
||||||
)
|
|
||||||
})
|
|
||||||
const u = await getUtils(page)
|
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
|
||||||
await u.waitForPageLoad()
|
|
||||||
|
|
||||||
await page.getByText('line([74.36, 130.4], %)').click()
|
|
||||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
|
||||||
|
|
||||||
// Wait for overlays to populate
|
|
||||||
await page.waitForTimeout(1000)
|
|
||||||
|
|
||||||
const line3 = await u.getSegmentBodyCoords(
|
|
||||||
`[data-overlay-index="${2}"]`
|
|
||||||
)
|
|
||||||
|
|
||||||
await page.mouse.click(line3.x, line3.y)
|
|
||||||
await page
|
|
||||||
.getByRole('button', {
|
|
||||||
name: 'Length: open menu',
|
|
||||||
})
|
|
||||||
.click()
|
|
||||||
await page.getByTestId('dropdown-constraint-' + constraint).click()
|
|
||||||
|
|
||||||
if (!addVariable) {
|
|
||||||
await page.getByTestId('create-new-variable-checkbox').click()
|
|
||||||
}
|
|
||||||
await page
|
|
||||||
.getByRole('button', { name: 'Add constraining value' })
|
|
||||||
.click()
|
|
||||||
|
|
||||||
const changedCode = `|> angledLine([${value}], %)`
|
|
||||||
await expect(page.locator('.cm-content')).toContainText(changedCode)
|
|
||||||
// checking active assures the cursor is where it should be
|
|
||||||
await expect(page.locator('.cm-activeLine')).toHaveText(changedCode)
|
|
||||||
|
|
||||||
// checking the count of the overlays is a good proxy check that the client sketch scene is in a good state
|
|
||||||
await expect(page.getByTestId('segment-overlay')).toHaveCount(4)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
test.describe('Test Length constraint single selection', () => {
|
|
||||||
const cases = [
|
|
||||||
{
|
{
|
||||||
testName: 'Length - Add variable',
|
testName: 'Length - Add variable',
|
||||||
addVariable: true,
|
addVariable: true,
|
||||||
@ -668,17 +552,7 @@ test.describe('Testing constraints', () => {
|
|||||||
},
|
},
|
||||||
] as const
|
] as const
|
||||||
for (const { testName, addVariable, value, constraint } of cases) {
|
for (const { testName, addVariable, value, constraint } of cases) {
|
||||||
test(`${testName}`, async ({ context, homePage, page }) => {
|
test(`${testName}`, async ({ page }) => {
|
||||||
// constants and locators
|
|
||||||
const cmdBarKclInput = page
|
|
||||||
.getByTestId('cmd-bar-arg-value')
|
|
||||||
.getByRole('textbox')
|
|
||||||
const cmdBarKclVariableNameInput =
|
|
||||||
page.getByPlaceholder('Variable name')
|
|
||||||
const cmdBarSubmitButton = page.getByRole('button', {
|
|
||||||
name: 'arrow right Continue',
|
|
||||||
})
|
|
||||||
|
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'persistCode',
|
'persistCode',
|
||||||
@ -698,9 +572,9 @@ part002 = startSketchOn('XZ')
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
await page.getByText('line([74.36, 130.4], %)').click()
|
await page.getByText('line([74.36, 130.4], %)').click()
|
||||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||||
@ -718,13 +592,11 @@ part002 = startSketchOn('XZ')
|
|||||||
await page.getByTestId('dropdown-constraint-' + constraint).click()
|
await page.getByTestId('dropdown-constraint-' + constraint).click()
|
||||||
|
|
||||||
if (!addVariable) {
|
if (!addVariable) {
|
||||||
await test.step(`Clear the variable input`, async () => {
|
await page.getByTestId('create-new-variable-checkbox').click()
|
||||||
await cmdBarKclVariableNameInput.clear()
|
|
||||||
await cmdBarKclVariableNameInput.press('Backspace')
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
await expect(cmdBarKclInput).toHaveText('78.33')
|
await page
|
||||||
await cmdBarSubmitButton.click()
|
.getByRole('button', { name: 'Add constraining value' })
|
||||||
|
.click()
|
||||||
|
|
||||||
const changedCode = `|> angledLine([${value}], %)`
|
const changedCode = `|> angledLine([${value}], %)`
|
||||||
await expect(page.locator('.cm-content')).toContainText(changedCode)
|
await expect(page.locator('.cm-content')).toContainText(changedCode)
|
||||||
@ -756,18 +628,18 @@ part002 = startSketchOn('XZ')
|
|||||||
},
|
},
|
||||||
] as const
|
] as const
|
||||||
for (const { codeAfter, constraintName } of cases) {
|
for (const { codeAfter, constraintName } of cases) {
|
||||||
test(`${constraintName}`, async ({ page, homePage }) => {
|
test(`${constraintName}`, async ({ page }) => {
|
||||||
await page.addInitScript(async (customCode) => {
|
await page.addInitScript(async (customCode) => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'persistCode',
|
'persistCode',
|
||||||
`yo = 5
|
`yo = 5
|
||||||
part001 = startSketchOn('XZ')
|
part001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([-7.54, -26.74], %)
|
|> startProfileAt([-7.54, -26.74], %)
|
||||||
|> line([74.36, 130.4], %)
|
|> line([74.36, 130.4], %)
|
||||||
|> line([78.92, -120.11], %)
|
|> line([78.92, -120.11], %)
|
||||||
|> line([9.16, 77.79], %)
|
|> line([9.16, 77.79], %)
|
||||||
|> line([51.19, 48.97], %)
|
|> line([51.19, 48.97], %)
|
||||||
part002 = startSketchOn('XZ')
|
part002 = startSketchOn('XZ')
|
||||||
|> startProfileAt([299.05, 231.45], %)
|
|> startProfileAt([299.05, 231.45], %)
|
||||||
|> xLine(-425.34, %, $seg_what)
|
|> xLine(-425.34, %, $seg_what)
|
||||||
|> yLine(-264.06, %)
|
|> yLine(-264.06, %)
|
||||||
@ -776,17 +648,13 @@ part002 = startSketchOn('XZ')
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
await u.waitForPageLoad()
|
|
||||||
|
|
||||||
await page.getByText('line([74.36, 130.4], %)').click()
|
await page.getByText('line([74.36, 130.4], %)').click()
|
||||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||||
|
|
||||||
// Wait for overlays to populate
|
|
||||||
await page.waitForTimeout(1000)
|
|
||||||
|
|
||||||
const line1 = await u.getSegmentBodyCoords(
|
const line1 = await u.getSegmentBodyCoords(
|
||||||
`[data-overlay-index="${0}"]`
|
`[data-overlay-index="${0}"]`
|
||||||
)
|
)
|
||||||
@ -805,8 +673,8 @@ part002 = startSketchOn('XZ')
|
|||||||
await page.keyboard.up('Shift')
|
await page.keyboard.up('Shift')
|
||||||
|
|
||||||
// check actives lines
|
// check actives lines
|
||||||
await pollEditorLinesSelectedLength(page, codeAfter.length)
|
|
||||||
const activeLinesContent = await page.locator('.cm-activeLine').all()
|
const activeLinesContent = await page.locator('.cm-activeLine').all()
|
||||||
|
await expect(activeLinesContent).toHaveLength(codeAfter.length)
|
||||||
|
|
||||||
const constraintMenuButton = page.getByRole('button', {
|
const constraintMenuButton = page.getByRole('button', {
|
||||||
name: 'Length: open menu',
|
name: 'Length: open menu',
|
||||||
@ -857,17 +725,17 @@ part002 = startSketchOn('XZ')
|
|||||||
},
|
},
|
||||||
] as const
|
] as const
|
||||||
for (const { codeAfter, constraintName } of cases) {
|
for (const { codeAfter, constraintName } of cases) {
|
||||||
test(`${constraintName}`, async ({ page, homePage }) => {
|
test(`${constraintName}`, async ({ page }) => {
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'persistCode',
|
'persistCode',
|
||||||
`yo = 5
|
`yo = 5
|
||||||
part001 = startSketchOn('XZ')
|
part001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([-7.54, -26.74], %)
|
|> startProfileAt([-7.54, -26.74], %)
|
||||||
|> line([74.36, 130.4], %)
|
|> line([74.36, 130.4], %)
|
||||||
|> line([78.92, -120.11], %)
|
|> line([78.92, -120.11], %)
|
||||||
|> line([9.16, 77.79], %)
|
|> line([9.16, 77.79], %)
|
||||||
part002 = startSketchOn('XZ')
|
part002 = startSketchOn('XZ')
|
||||||
|> startProfileAt([299.05, 231.45], %)
|
|> startProfileAt([299.05, 231.45], %)
|
||||||
|> xLine(-425.34, %, $seg_what)
|
|> xLine(-425.34, %, $seg_what)
|
||||||
|> yLine(-264.06, %)
|
|> yLine(-264.06, %)
|
||||||
@ -876,17 +744,13 @@ part002 = startSketchOn('XZ')
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
await u.waitForPageLoad()
|
|
||||||
|
|
||||||
await page.getByText('line([74.36, 130.4], %)').click()
|
await page.getByText('line([74.36, 130.4], %)').click()
|
||||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||||
|
|
||||||
// Wait for overlays to populate
|
|
||||||
await page.waitForTimeout(1000)
|
|
||||||
|
|
||||||
const line1 = await u.getBoundingBox(`[data-overlay-index="${0}"]`)
|
const line1 = await u.getBoundingBox(`[data-overlay-index="${0}"]`)
|
||||||
const line3 = await u.getBoundingBox(`[data-overlay-index="${2}"]`)
|
const line3 = await u.getBoundingBox(`[data-overlay-index="${2}"]`)
|
||||||
|
|
||||||
@ -913,8 +777,8 @@ part002 = startSketchOn('XZ')
|
|||||||
// check there are still 2 cursors (they should stay on the same lines as before constraint was applied)
|
// check there are still 2 cursors (they should stay on the same lines as before constraint was applied)
|
||||||
await expect(page.locator('.cm-cursor')).toHaveCount(2)
|
await expect(page.locator('.cm-cursor')).toHaveCount(2)
|
||||||
// check actives lines
|
// check actives lines
|
||||||
await pollEditorLinesSelectedLength(page, 2)
|
|
||||||
const activeLinesContent = await page.locator('.cm-activeLine').all()
|
const activeLinesContent = await page.locator('.cm-activeLine').all()
|
||||||
|
await expect(activeLinesContent).toHaveLength(2)
|
||||||
|
|
||||||
// check both cursors are where they should be after constraint is applied
|
// check both cursors are where they should be after constraint is applied
|
||||||
await expect(activeLinesContent[0]).toHaveText(
|
await expect(activeLinesContent[0]).toHaveText(
|
||||||
@ -938,17 +802,17 @@ part002 = startSketchOn('XZ')
|
|||||||
},
|
},
|
||||||
] as const
|
] as const
|
||||||
for (const { codeAfter, constraintName, axisClick } of cases) {
|
for (const { codeAfter, constraintName, axisClick } of cases) {
|
||||||
test(`${constraintName}`, async ({ page, homePage }) => {
|
test(`${constraintName}`, async ({ page }) => {
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'persistCode',
|
'persistCode',
|
||||||
`yo = 5
|
`yo = 5
|
||||||
part001 = startSketchOn('XZ')
|
part001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([-7.54, -26.74], %)
|
|> startProfileAt([-7.54, -26.74], %)
|
||||||
|> line([74.36, 130.4], %)
|
|> line([74.36, 130.4], %)
|
||||||
|> line([78.92, -120.11], %)
|
|> line([78.92, -120.11], %)
|
||||||
|> line([9.16, 77.79], %)
|
|> line([9.16, 77.79], %)
|
||||||
part002 = startSketchOn('XZ')
|
part002 = startSketchOn('XZ')
|
||||||
|> startProfileAt([299.05, 231.45], %)
|
|> startProfileAt([299.05, 231.45], %)
|
||||||
|> xLine(-425.34, %, $seg_what)
|
|> xLine(-425.34, %, $seg_what)
|
||||||
|> yLine(-264.06, %)
|
|> yLine(-264.06, %)
|
||||||
@ -957,28 +821,21 @@ part002 = startSketchOn('XZ')
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
await u.waitForPageLoad()
|
|
||||||
|
|
||||||
await page.getByText('line([74.36, 130.4], %)').click()
|
await page.getByText('line([74.36, 130.4], %)').click()
|
||||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||||
|
|
||||||
// Wait for overlays to populate
|
|
||||||
await page.waitForTimeout(1000)
|
|
||||||
|
|
||||||
const line3 = await u.getBoundingBox(`[data-overlay-index="${2}"]`)
|
const line3 = await u.getBoundingBox(`[data-overlay-index="${2}"]`)
|
||||||
|
|
||||||
// select segment and axis by holding down shift
|
// select segment and axis by holding down shift
|
||||||
await page.mouse.click(line3.x - 3, line3.y + 20)
|
await page.mouse.click(line3.x - 3, line3.y + 20)
|
||||||
await page.waitForTimeout(100)
|
|
||||||
await page.keyboard.down('Shift')
|
await page.keyboard.down('Shift')
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
await page.mouse.click(axisClick.x, axisClick.y)
|
await page.mouse.click(axisClick.x, axisClick.y)
|
||||||
await page.waitForTimeout(100)
|
|
||||||
await page.keyboard.up('Shift')
|
await page.keyboard.up('Shift')
|
||||||
await page.waitForTimeout(100)
|
|
||||||
const constraintMenuButton = page.getByRole('button', {
|
const constraintMenuButton = page.getByRole('button', {
|
||||||
name: 'Length: open menu',
|
name: 'Length: open menu',
|
||||||
})
|
})
|
||||||
@ -1000,7 +857,6 @@ part002 = startSketchOn('XZ')
|
|||||||
|
|
||||||
test('Horizontally constrained line remains selected after applying constraint', async ({
|
test('Horizontally constrained line remains selected after applying constraint', async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
|
||||||
}) => {
|
}) => {
|
||||||
test.setTimeout(70_000)
|
test.setTimeout(70_000)
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
@ -1013,10 +869,9 @@ part002 = startSketchOn('XZ')
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
await u.waitForPageLoad()
|
|
||||||
|
|
||||||
await page.getByText('line([3.79, 2.68], %, $seg01)').click()
|
await page.getByText('line([3.79, 2.68], %, $seg01)').click()
|
||||||
await expect(page.getByRole('button', { name: 'Edit Sketch' })).toBeEnabled(
|
await expect(page.getByRole('button', { name: 'Edit Sketch' })).toBeEnabled(
|
||||||
@ -1024,9 +879,6 @@ part002 = startSketchOn('XZ')
|
|||||||
)
|
)
|
||||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||||
|
|
||||||
// Wait for overlays to populate
|
|
||||||
await page.waitForTimeout(1000)
|
|
||||||
|
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
const lineBefore = await u.getSegmentBodyCoords(
|
const lineBefore = await u.getSegmentBodyCoords(
|
||||||
`[data-overlay-index="1"]`,
|
`[data-overlay-index="1"]`,
|
||||||
@ -1047,17 +899,11 @@ part002 = startSketchOn('XZ')
|
|||||||
name: 'Length: open menu',
|
name: 'Length: open menu',
|
||||||
})
|
})
|
||||||
.click()
|
.click()
|
||||||
await page.waitForTimeout(500)
|
|
||||||
await page.getByRole('button', { name: 'Horizontal', exact: true }).click()
|
await page.getByRole('button', { name: 'Horizontal', exact: true }).click()
|
||||||
await page.waitForTimeout(500)
|
|
||||||
|
|
||||||
await pollEditorLinesSelectedLength(page, 1)
|
|
||||||
let activeLinesContent = await page.locator('.cm-activeLine').all()
|
let activeLinesContent = await page.locator('.cm-activeLine').all()
|
||||||
await expect(activeLinesContent[0]).toHaveText(`|> xLine(3.13, %)`)
|
await expect(activeLinesContent[0]).toHaveText(`|> xLine(3.13, %)`)
|
||||||
|
|
||||||
// Wait for code editor to settle.
|
|
||||||
await page.waitForTimeout(2000)
|
|
||||||
|
|
||||||
// If the overlay-angle is updated the THREE.js scene is in a good state
|
// If the overlay-angle is updated the THREE.js scene is in a good state
|
||||||
await expect(
|
await expect(
|
||||||
await page.locator('[data-overlay-index="1"]')
|
await page.locator('[data-overlay-index="1"]')
|
||||||
@ -1067,17 +913,11 @@ part002 = startSketchOn('XZ')
|
|||||||
`[data-overlay-index="1"]`,
|
`[data-overlay-index="1"]`,
|
||||||
0
|
0
|
||||||
)
|
)
|
||||||
|
expect(
|
||||||
|
await u.getGreatestPixDiff(lineAfter, TEST_COLORS.BLUE)
|
||||||
|
).toBeLessThan(3)
|
||||||
|
|
||||||
const linebb = await u.getBoundingBox('[data-overlay-index="1"]')
|
await page.waitForTimeout(300)
|
||||||
await page.mouse.move(linebb.x, linebb.y, { steps: 25 })
|
|
||||||
await page.mouse.click(linebb.x, linebb.y)
|
|
||||||
|
|
||||||
await expect
|
|
||||||
.poll(async () => await u.getGreatestPixDiff(lineAfter, TEST_COLORS.BLUE))
|
|
||||||
.toBeLessThan(3)
|
|
||||||
|
|
||||||
await page.waitForTimeout(500)
|
|
||||||
|
|
||||||
await page
|
await page
|
||||||
.getByRole('button', {
|
.getByRole('button', {
|
||||||
name: 'Length: open menu',
|
name: 'Length: open menu',
|
||||||
@ -1091,7 +931,6 @@ part002 = startSketchOn('XZ')
|
|||||||
await page.getByLabel('length Value').fill('10')
|
await page.getByLabel('length Value').fill('10')
|
||||||
await page.getByRole('button', { name: 'Add constraining value' }).click()
|
await page.getByRole('button', { name: 'Add constraining value' }).click()
|
||||||
|
|
||||||
await pollEditorLinesSelectedLength(page, 1)
|
|
||||||
activeLinesContent = await page.locator('.cm-activeLine').all()
|
activeLinesContent = await page.locator('.cm-activeLine').all()
|
||||||
await expect(activeLinesContent[0]).toHaveText(`|> xLine(length001, %)`)
|
await expect(activeLinesContent[0]).toHaveText(`|> xLine(length001, %)`)
|
||||||
|
|
||||||
|
@ -1,9 +1,18 @@
|
|||||||
import { test, expect } from './zoo-test'
|
import { _test, _expect } from './playwright-deprecated'
|
||||||
import { getUtils } from './test-utils'
|
import { test } from './fixtures/fixtureSetup'
|
||||||
|
import { getUtils, setup, tearDown } from './test-utils'
|
||||||
import { uuidv4 } from 'lib/utils'
|
import { uuidv4 } from 'lib/utils'
|
||||||
import { TEST_CODE_GIZMO } from './storageStates'
|
import { TEST_CODE_GIZMO } from './storageStates'
|
||||||
|
|
||||||
test.describe('Testing Gizmo', () => {
|
_test.beforeEach(async ({ context, page }, testInfo) => {
|
||||||
|
await setup(context, page, testInfo)
|
||||||
|
})
|
||||||
|
|
||||||
|
_test.afterEach(async ({ page }, testInfo) => {
|
||||||
|
await tearDown(page, testInfo)
|
||||||
|
})
|
||||||
|
|
||||||
|
_test.describe('Testing Gizmo', () => {
|
||||||
const cases = [
|
const cases = [
|
||||||
{
|
{
|
||||||
testDescription: 'top view',
|
testDescription: 'top view',
|
||||||
@ -48,17 +57,14 @@ test.describe('Testing Gizmo', () => {
|
|||||||
expectedCameraTarget,
|
expectedCameraTarget,
|
||||||
testDescription,
|
testDescription,
|
||||||
} of cases) {
|
} of cases) {
|
||||||
test(`check ${testDescription}`, async ({ page, homePage }) => {
|
_test(`check ${testDescription}`, async ({ page, browserName }) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.addInitScript((TEST_CODE_GIZMO) => {
|
await page.addInitScript((TEST_CODE_GIZMO) => {
|
||||||
localStorage.setItem('persistCode', TEST_CODE_GIZMO)
|
localStorage.setItem('persistCode', TEST_CODE_GIZMO)
|
||||||
}, TEST_CODE_GIZMO)
|
}, TEST_CODE_GIZMO)
|
||||||
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
|
||||||
await u.waitForPageLoad()
|
|
||||||
|
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
// wait for execution done
|
// wait for execution done
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
@ -111,30 +117,30 @@ test.describe('Testing Gizmo', () => {
|
|||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
// position
|
// position
|
||||||
expect(page.getByTestId('cam-x-position')).toHaveValue(
|
_expect(page.getByTestId('cam-x-position')).toHaveValue(
|
||||||
expectedCameraPosition.x.toString()
|
expectedCameraPosition.x.toString()
|
||||||
),
|
),
|
||||||
expect(page.getByTestId('cam-y-position')).toHaveValue(
|
_expect(page.getByTestId('cam-y-position')).toHaveValue(
|
||||||
expectedCameraPosition.y.toString()
|
expectedCameraPosition.y.toString()
|
||||||
),
|
),
|
||||||
expect(page.getByTestId('cam-z-position')).toHaveValue(
|
_expect(page.getByTestId('cam-z-position')).toHaveValue(
|
||||||
expectedCameraPosition.z.toString()
|
expectedCameraPosition.z.toString()
|
||||||
),
|
),
|
||||||
// target
|
// target
|
||||||
expect(page.getByTestId('cam-x-target')).toHaveValue(
|
_expect(page.getByTestId('cam-x-target')).toHaveValue(
|
||||||
expectedCameraTarget.x.toString()
|
expectedCameraTarget.x.toString()
|
||||||
),
|
),
|
||||||
expect(page.getByTestId('cam-y-target')).toHaveValue(
|
_expect(page.getByTestId('cam-y-target')).toHaveValue(
|
||||||
expectedCameraTarget.y.toString()
|
expectedCameraTarget.y.toString()
|
||||||
),
|
),
|
||||||
expect(page.getByTestId('cam-z-target')).toHaveValue(
|
_expect(page.getByTestId('cam-z-target')).toHaveValue(
|
||||||
expectedCameraTarget.z.toString()
|
expectedCameraTarget.z.toString()
|
||||||
),
|
),
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
test('Context menu and popover menu', async ({ page, homePage }) => {
|
_test('Context menu and popover menu', async ({ page }) => {
|
||||||
const testCase = {
|
const testCase = {
|
||||||
testDescription: 'Right view',
|
testDescription: 'Right view',
|
||||||
expectedCameraPosition: { x: 5660.02, y: -152, z: 26 },
|
expectedCameraPosition: { x: 5660.02, y: -152, z: 26 },
|
||||||
@ -146,9 +152,9 @@ test.describe('Testing Gizmo', () => {
|
|||||||
await page.addInitScript((TEST_CODE_GIZMO) => {
|
await page.addInitScript((TEST_CODE_GIZMO) => {
|
||||||
localStorage.setItem('persistCode', TEST_CODE_GIZMO)
|
localStorage.setItem('persistCode', TEST_CODE_GIZMO)
|
||||||
}, TEST_CODE_GIZMO)
|
}, TEST_CODE_GIZMO)
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await u.waitForAuthSkipAppStart()
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
// wait for execution done
|
// wait for execution done
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
@ -190,7 +196,7 @@ test.describe('Testing Gizmo', () => {
|
|||||||
const buttonToTest = page.getByRole('button', {
|
const buttonToTest = page.getByRole('button', {
|
||||||
name: testCase.testDescription,
|
name: testCase.testDescription,
|
||||||
})
|
})
|
||||||
await expect(buttonToTest).toBeVisible()
|
await _expect(buttonToTest).toBeVisible()
|
||||||
await buttonToTest.click()
|
await buttonToTest.click()
|
||||||
|
|
||||||
// Now assert we've moved to the correct view
|
// Now assert we've moved to the correct view
|
||||||
@ -209,23 +215,23 @@ test.describe('Testing Gizmo', () => {
|
|||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
// position
|
// position
|
||||||
expect(page.getByTestId('cam-x-position')).toHaveValue(
|
_expect(page.getByTestId('cam-x-position')).toHaveValue(
|
||||||
testCase.expectedCameraPosition.x.toString()
|
testCase.expectedCameraPosition.x.toString()
|
||||||
),
|
),
|
||||||
expect(page.getByTestId('cam-y-position')).toHaveValue(
|
_expect(page.getByTestId('cam-y-position')).toHaveValue(
|
||||||
testCase.expectedCameraPosition.y.toString()
|
testCase.expectedCameraPosition.y.toString()
|
||||||
),
|
),
|
||||||
expect(page.getByTestId('cam-z-position')).toHaveValue(
|
_expect(page.getByTestId('cam-z-position')).toHaveValue(
|
||||||
testCase.expectedCameraPosition.z.toString()
|
testCase.expectedCameraPosition.z.toString()
|
||||||
),
|
),
|
||||||
// target
|
// target
|
||||||
expect(page.getByTestId('cam-x-target')).toHaveValue(
|
_expect(page.getByTestId('cam-x-target')).toHaveValue(
|
||||||
testCase.expectedCameraTarget.x.toString()
|
testCase.expectedCameraTarget.x.toString()
|
||||||
),
|
),
|
||||||
expect(page.getByTestId('cam-y-target')).toHaveValue(
|
_expect(page.getByTestId('cam-y-target')).toHaveValue(
|
||||||
testCase.expectedCameraTarget.y.toString()
|
testCase.expectedCameraTarget.y.toString()
|
||||||
),
|
),
|
||||||
expect(page.getByTestId('cam-z-target')).toHaveValue(
|
_expect(page.getByTestId('cam-z-target')).toHaveValue(
|
||||||
testCase.expectedCameraTarget.z.toString()
|
testCase.expectedCameraTarget.z.toString()
|
||||||
),
|
),
|
||||||
])
|
])
|
||||||
@ -236,59 +242,32 @@ test.describe('Testing Gizmo', () => {
|
|||||||
const gizmoPopoverButton = page.getByRole('button', {
|
const gizmoPopoverButton = page.getByRole('button', {
|
||||||
name: 'view settings',
|
name: 'view settings',
|
||||||
})
|
})
|
||||||
await expect(gizmoPopoverButton).toBeVisible()
|
await _expect(gizmoPopoverButton).toBeVisible()
|
||||||
await gizmoPopoverButton.click()
|
await gizmoPopoverButton.click()
|
||||||
await expect(buttonToTest).toBeVisible()
|
await _expect(buttonToTest).toBeVisible()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe(`Testing gizmo, fixture-based`, () => {
|
test.describe(`Testing gizmo, fixture-based`, () => {
|
||||||
test('Center on selection from menu', async ({
|
test('Center on selection from menu', async ({
|
||||||
context,
|
app,
|
||||||
page,
|
|
||||||
homePage,
|
|
||||||
cmdBar,
|
cmdBar,
|
||||||
editor,
|
editor,
|
||||||
toolbar,
|
toolbar,
|
||||||
scene,
|
scene,
|
||||||
}) => {
|
}) => {
|
||||||
await context.addInitScript(() => {
|
test.skip(
|
||||||
localStorage.setItem(
|
process.platform === 'win32',
|
||||||
'persistCode',
|
'Fails on windows in CI, can not be replicated locally on windows.'
|
||||||
`
|
|
||||||
const sketch002 = startSketchOn('XZ')
|
|
||||||
|> startProfileAt([-108.83, -57.48], %)
|
|
||||||
|> angledLine([0, 105.13], %, $rectangleSegmentA001)
|
|
||||||
|> angledLine([
|
|
||||||
segAng(rectangleSegmentA001) - 90,
|
|
||||||
77.9
|
|
||||||
], %)
|
|
||||||
|> angledLine([
|
|
||||||
segAng(rectangleSegmentA001),
|
|
||||||
-segLen(rectangleSegmentA001)
|
|
||||||
], %)
|
|
||||||
|> close(%)
|
|
||||||
const sketch001 = startSketchOn('XZ')
|
|
||||||
|> circle({
|
|
||||||
center: [818.33, 168.1],
|
|
||||||
radius: 182.8
|
|
||||||
}, %)
|
|
||||||
|> extrude(50, %)
|
|
||||||
`
|
|
||||||
)
|
)
|
||||||
})
|
|
||||||
|
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
|
||||||
const u = await getUtils(page)
|
|
||||||
await u.waitForPageLoad()
|
|
||||||
|
|
||||||
await test.step(`Setup`, async () => {
|
await test.step(`Setup`, async () => {
|
||||||
|
const file = await app.getInputFile('test-circle-extrude.kcl')
|
||||||
|
await app.initialise(file)
|
||||||
await scene.expectState({
|
await scene.expectState({
|
||||||
camera: {
|
camera: {
|
||||||
position: [11912.6, -39586.98, 21391.21],
|
position: [4982.21, -23865.37, 13810.64],
|
||||||
target: [11912.6, -635, 3317.49],
|
target: [4982.21, 0, 2737.1],
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -296,7 +275,7 @@ test.describe(`Testing gizmo, fixture-based`, () => {
|
|||||||
|
|
||||||
await test.step(`Select an edge of this circle`, async () => {
|
await test.step(`Select an edge of this circle`, async () => {
|
||||||
const circleSnippet =
|
const circleSnippet =
|
||||||
'circle({ center: [818.33, 168.1], radius: 182.8 }, %)'
|
'circle({ center = [318.33, 168.1], radius = 182.8 }, %)'
|
||||||
await moveToCircle()
|
await moveToCircle()
|
||||||
await clickCircle()
|
await clickCircle()
|
||||||
await editor.expectState({
|
await editor.expectState({
|
||||||
@ -313,8 +292,8 @@ test.describe(`Testing gizmo, fixture-based`, () => {
|
|||||||
await test.step(`Verify the camera moved`, async () => {
|
await test.step(`Verify the camera moved`, async () => {
|
||||||
await scene.expectState({
|
await scene.expectState({
|
||||||
camera: {
|
camera: {
|
||||||
position: [20785.58, -40221.98, 22343.46],
|
position: [0, -23865.37, 11073.53],
|
||||||
target: [20785.58, -1270, 4269.74],
|
target: [0, 0, 0],
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|