Compare commits
3 Commits
jess/chang
...
nrc-std-ax
Author | SHA1 | Date | |
---|---|---|---|
87dfda28a9 | |||
42f44e11f5 | |||
16ad7ff77a |
79
.eslintrc
Normal file
@ -0,0 +1,79 @@
|
||||
{
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"project": "./tsconfig.json"
|
||||
},
|
||||
"plugins": [
|
||||
"react-perf",
|
||||
"css-modules",
|
||||
"jest",
|
||||
"jsx-a11y",
|
||||
"react",
|
||||
"react-hooks",
|
||||
"suggest-no-throw",
|
||||
"testing-library",
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"extends": [
|
||||
"plugin:css-modules/recommended",
|
||||
"plugin:jsx-a11y/recommended",
|
||||
"plugin:react-hooks/recommended"
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/no-empty-object-type": "error",
|
||||
"@typescript-eslint/no-floating-promises": "error",
|
||||
"@typescript-eslint/no-misused-promises": "error",
|
||||
"@typescript-eslint/no-unused-vars": ["error", {
|
||||
"varsIgnorePattern": "^_",
|
||||
"argsIgnorePattern": "^_",
|
||||
"ignoreRestSiblings": true,
|
||||
"vars": "all",
|
||||
"args": "none"
|
||||
}],
|
||||
"jsx-a11y/click-events-have-key-events": "off",
|
||||
"jsx-a11y/no-autofocus": "off",
|
||||
"jsx-a11y/no-noninteractive-element-interactions": "off",
|
||||
"no-restricted-globals": [
|
||||
"error",
|
||||
{
|
||||
"name": "isNaN",
|
||||
"message": "Use Number.isNaN() instead."
|
||||
},
|
||||
],
|
||||
"no-restricted-syntax": [
|
||||
"error",
|
||||
{
|
||||
"selector": "CallExpression[callee.object.name='Array'][callee.property.name='isArray']",
|
||||
"message": "Use isArray() in lib/utils.ts instead of Array.isArray()."
|
||||
}
|
||||
],
|
||||
"semi": [
|
||||
"error",
|
||||
"never"
|
||||
],
|
||||
"react-hooks/exhaustive-deps": "off",
|
||||
"suggest-no-throw/suggest-no-throw": "warn",
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["e2e/**/*.ts"], // Update the pattern based on your file structure
|
||||
"extends": [
|
||||
"plugin:testing-library/react"
|
||||
],
|
||||
"rules": {
|
||||
"suggest-no-throw/suggest-no-throw": "off",
|
||||
"testing-library/prefer-screen-queries": "off",
|
||||
"jest/valid-expect": "off"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["src/**/*.test.ts"],
|
||||
"extends": [
|
||||
"plugin:testing-library/react"
|
||||
],
|
||||
"rules": {
|
||||
"suggest-no-throw/suggest-no-throw": "off",
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
127
.eslintrc.json
@ -1,127 +0,0 @@
|
||||
{
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"project": "./tsconfig.json"
|
||||
},
|
||||
"plugins": [
|
||||
"react-perf",
|
||||
"css-modules",
|
||||
"jest",
|
||||
"jsx-a11y",
|
||||
"react",
|
||||
"react-hooks",
|
||||
"suggest-no-throw",
|
||||
"testing-library",
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"extends": [
|
||||
"plugin:css-modules/recommended",
|
||||
"plugin:jsx-a11y/recommended",
|
||||
"plugin:react-hooks/recommended"
|
||||
],
|
||||
"rules": {
|
||||
"no-array-constructor": "off", // This is wrong; use the @typescript-eslint one instead.
|
||||
"@typescript-eslint/no-array-constructor": "error",
|
||||
"@typescript-eslint/no-array-delete": "error",
|
||||
"@typescript-eslint/no-duplicate-enum-values": "error",
|
||||
"@typescript-eslint/no-duplicate-type-constituents": "error",
|
||||
"@typescript-eslint/no-empty-object-type": "error",
|
||||
"@typescript-eslint/no-extra-non-null-assertion": "error",
|
||||
"@typescript-eslint/no-floating-promises": "error",
|
||||
"@typescript-eslint/no-for-in-array": "error",
|
||||
"no-implied-eval": "off", // This is wrong; use the @typescript-eslint one instead.
|
||||
"@typescript-eslint/no-implied-eval": "error",
|
||||
"@typescript-eslint/no-misused-new": "error",
|
||||
"@typescript-eslint/no-misused-promises": "error",
|
||||
"@typescript-eslint/no-namespace": "error",
|
||||
"@typescript-eslint/no-non-null-asserted-optional-chain": "error",
|
||||
"@typescript-eslint/no-redundant-type-constituents": "error",
|
||||
"@typescript-eslint/no-this-alias": "warn",
|
||||
"@typescript-eslint/no-unnecessary-type-assertion": "error",
|
||||
"@typescript-eslint/no-unnecessary-type-constraint": "error",
|
||||
"no-unused-vars": "off", // This is wrong; use the @typescript-eslint one instead.
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
"varsIgnorePattern": "^_",
|
||||
"argsIgnorePattern": "^_",
|
||||
"ignoreRestSiblings": true,
|
||||
"vars": "all",
|
||||
"args": "none"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-unsafe-unary-minus": "error",
|
||||
"@typescript-eslint/no-wrapper-object-types": "error",
|
||||
"no-throw-literal": "off", // Use @typescript-eslint/only-throw-error instead.
|
||||
"@typescript-eslint/only-throw-error": "error",
|
||||
"@typescript-eslint/prefer-as-const": "warn",
|
||||
"@typescript-eslint/prefer-namespace-keyword": "error",
|
||||
"@typescript-eslint/consistent-type-imports": "error",
|
||||
"@typescript-eslint/restrict-plus-operands": "error",
|
||||
"jsx-a11y/click-events-have-key-events": "off",
|
||||
"jsx-a11y/no-autofocus": "off",
|
||||
"jsx-a11y/no-noninteractive-element-interactions": "off",
|
||||
"no-restricted-globals": [
|
||||
"error",
|
||||
{
|
||||
"name": "isNaN",
|
||||
"message": "Use Number.isNaN() instead."
|
||||
}
|
||||
],
|
||||
"no-restricted-syntax": [
|
||||
"error",
|
||||
{
|
||||
"selector": "CallExpression[callee.object.name='Array'][callee.property.name='isArray']",
|
||||
"message": "Use isArray() in lib/utils.ts instead of Array.isArray()."
|
||||
},
|
||||
{
|
||||
"selector": "CallExpression[callee.object.name='TOML'][callee.property.name='stringify']",
|
||||
"message": "Do not use TOML.stringify directly. Use the wrappers in test-utils instead like settingsToToml."
|
||||
},
|
||||
{
|
||||
"selector": "CallExpression[callee.object.name='TOML'][callee.property.name='parse']",
|
||||
"message": "Do not use TOML.parse directly. Use the wrappers in test-utils instead like tomlToSettings."
|
||||
}
|
||||
],
|
||||
"no-restricted-imports": [
|
||||
"error",
|
||||
{
|
||||
"patterns": [
|
||||
// Restrict all relative imports except for .css files.
|
||||
{
|
||||
"group": ["./*", "../*", "!./*.css", "!../*.css"],
|
||||
"message": "Use absolute imports instead."
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"semi": ["error", "never"],
|
||||
"react-hooks/exhaustive-deps": "off",
|
||||
"suggest-no-throw/suggest-no-throw": "error"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["e2e/**/*.ts"], // Update the pattern based on your file structure
|
||||
"extends": ["plugin:testing-library/react"],
|
||||
"rules": {
|
||||
"suggest-no-throw/suggest-no-throw": "off",
|
||||
"testing-library/prefer-screen-queries": "off",
|
||||
"jest/valid-expect": "off"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["src/**/*.test.ts"],
|
||||
"extends": ["plugin:testing-library/react"],
|
||||
"rules": {
|
||||
"suggest-no-throw/suggest-no-throw": "off"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["packages/**/*.ts", "rust/**/*.ts"],
|
||||
"extends": [],
|
||||
"rules": {
|
||||
"no-restricted-imports": "off"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
4
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -1,5 +1,5 @@
|
||||
name: Bug Report
|
||||
description: File a bug report for the Zoo Design Studio
|
||||
description: File a bug report for the Zoo Modeling App
|
||||
title: "[BUG]: "
|
||||
labels: ["bug"]
|
||||
assignees: []
|
||||
@ -70,7 +70,7 @@ body:
|
||||
id: version
|
||||
attributes:
|
||||
label: Version
|
||||
description: "The version of the Zoo Design Studio you're using."
|
||||
description: "The version of the Zoo Modeling App you're using."
|
||||
placeholder: "example: v0.15.0. You can find this in the settings."
|
||||
validations:
|
||||
required: true
|
||||
|
2
.github/workflows/build-apps.yml
vendored
@ -241,7 +241,7 @@ jobs:
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: out-arm64-${{ matrix.platform }}
|
||||
# first two will pick both Zoo Design Studio-$VERSION-arm64-win.exe and Zoo Design Studio-$VERSION-win.exe
|
||||
# first two will pick both Zoo Modeling App-$VERSION-arm64-win.exe and Zoo Modeling App-$VERSION-win.exe
|
||||
path: |
|
||||
out/*-${{ env.VERSION_NO_V }}-win.*
|
||||
out/*-${{ env.VERSION_NO_V }}-arm64-win.*
|
||||
|
16
.github/workflows/e2e-tests.yml
vendored
@ -229,6 +229,10 @@ jobs:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
env:
|
||||
CI: true
|
||||
NODE_ENV: development
|
||||
VITE_KC_DEV_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
VITE_KC_SKIP_AUTH: true
|
||||
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
snapshottoken: ${{ secrets.KITTYCAD_API_TOKEN }}
|
||||
|
||||
@ -281,7 +285,7 @@ jobs:
|
||||
os:
|
||||
- "runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64"
|
||||
- namespace-profile-macos-8-cores
|
||||
- windows-latest-8-cores
|
||||
- windows-latest
|
||||
shardIndex: [1, 2, 3, 4]
|
||||
shardTotal: [4]
|
||||
# Disable macos and windows tests on hourly e2e tests since we only care
|
||||
@ -292,7 +296,7 @@ jobs:
|
||||
exclude:
|
||||
- os: namespace-profile-macos-8-cores
|
||||
isScheduled: true
|
||||
- os: windows-latest-8-cores
|
||||
- os: windows-latest
|
||||
isScheduled: true
|
||||
# TODO: add ref here for main and latest release tag
|
||||
runs-on: ${{ matrix.os }}
|
||||
@ -370,10 +374,14 @@ jobs:
|
||||
with:
|
||||
shell: bash
|
||||
command: .github/ci-cd-scripts/playwright-electron.sh ${{matrix.shardIndex}} ${{matrix.shardTotal}} ${{ env.OS_NAME }}
|
||||
timeout_minutes: 30
|
||||
max_attempts: 9
|
||||
timeout_minutes: 45
|
||||
max_attempts: 15
|
||||
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
|
||||
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
|
174
.github/workflows/static-analysis.yml
vendored
@ -28,87 +28,43 @@ jobs:
|
||||
- run: yarn fmt-check
|
||||
|
||||
yarn-build-wasm:
|
||||
# Build the wasm blob once on the fastest runner.
|
||||
runs-on: runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install
|
||||
|
||||
- name: Use correct Rust toolchain
|
||||
shell: bash
|
||||
run: |
|
||||
[ -e rust-toolchain.toml ] || cp rust/rust-toolchain.toml ./
|
||||
|
||||
- name: Install rust
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
- run: yarn install
|
||||
- uses: taiki-e/install-action@37bdc826eaedac215f638a96472df572feab0f9b
|
||||
with:
|
||||
cache: false # Configured below.
|
||||
tool: wasm-pack
|
||||
- run: yarn build:wasm
|
||||
|
||||
yarn-tsc:
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
- run: yarn install
|
||||
- run: yarn --cwd ./rust/kcl-language-server --modules-folder node_modules install
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: './rust'
|
||||
|
||||
- uses: taiki-e/install-action@37bdc826eaedac215f638a96472df572feab0f9b
|
||||
with:
|
||||
tool: wasm-pack
|
||||
|
||||
- name: Rust Cache
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: './rust'
|
||||
|
||||
- name: Build Wasm
|
||||
shell: bash
|
||||
run: yarn build:wasm
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: prepared-wasm
|
||||
path: |
|
||||
rust/kcl-wasm-lib/pkg/kcl_wasm_lib*
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: prepared-ts-rs-bindings
|
||||
path: |
|
||||
rust/kcl-lib/bindings/*
|
||||
|
||||
yarn-tsc:
|
||||
runs-on: ubuntu-latest
|
||||
needs: yarn-build-wasm
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
- run: yarn install
|
||||
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
|
||||
- name: Copy prepared wasm
|
||||
run: |
|
||||
ls -R prepared-wasm
|
||||
cp prepared-wasm/kcl_wasm_lib_bg.wasm public
|
||||
mkdir rust/kcl-wasm-lib/pkg
|
||||
cp prepared-wasm/kcl_wasm_lib* rust/kcl-wasm-lib/pkg
|
||||
|
||||
- name: Copy prepared ts-rs bindings
|
||||
run: |
|
||||
ls -R prepared-ts-rs-bindings
|
||||
mkdir rust/kcl-lib/bindings
|
||||
cp -r prepared-ts-rs-bindings/* rust/kcl-lib/bindings/
|
||||
|
||||
- run: yarn build:wasm
|
||||
- run: yarn tsc
|
||||
|
||||
yarn-lint:
|
||||
runs-on: ubuntu-latest
|
||||
needs: yarn-build-wasm
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@ -117,55 +73,9 @@ jobs:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
- run: yarn install
|
||||
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
|
||||
- name: Copy prepared wasm
|
||||
run: |
|
||||
ls -R prepared-wasm
|
||||
cp prepared-wasm/kcl_wasm_lib_bg.wasm public
|
||||
mkdir rust/kcl-wasm-lib/pkg
|
||||
cp prepared-wasm/kcl_wasm_lib* rust/kcl-wasm-lib/pkg
|
||||
|
||||
- name: Copy prepared ts-rs bindings
|
||||
run: |
|
||||
ls -R prepared-ts-rs-bindings
|
||||
mkdir rust/kcl-lib/bindings
|
||||
cp -r prepared-ts-rs-bindings/* rust/kcl-lib/bindings/
|
||||
|
||||
- run: yarn --cwd ./rust/kcl-language-server --modules-folder node_modules install
|
||||
- run: yarn lint
|
||||
|
||||
yarn-circular-dependencies:
|
||||
runs-on: ubuntu-latest
|
||||
needs: yarn-build-wasm
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
- run: yarn install
|
||||
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
|
||||
- name: Copy prepared wasm
|
||||
run: |
|
||||
ls -R prepared-wasm
|
||||
cp prepared-wasm/kcl_wasm_lib_bg.wasm public
|
||||
mkdir rust/kcl-wasm-lib/pkg
|
||||
cp prepared-wasm/kcl_wasm_lib* rust/kcl-wasm-lib/pkg
|
||||
|
||||
- name: Copy prepared ts-rs bindings
|
||||
run: |
|
||||
ls -R prepared-ts-rs-bindings
|
||||
mkdir rust/kcl-lib/bindings
|
||||
cp -r prepared-ts-rs-bindings/* rust/kcl-lib/bindings/
|
||||
|
||||
- run: yarn circular-deps:diff
|
||||
|
||||
python-codespell:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
@ -181,7 +91,6 @@ jobs:
|
||||
|
||||
yarn-unit-test-kcl-samples:
|
||||
runs-on: ubuntu-latest
|
||||
needs: yarn-build-wasm
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
@ -194,22 +103,7 @@ jobs:
|
||||
- uses: taiki-e/install-action@37bdc826eaedac215f638a96472df572feab0f9b
|
||||
with:
|
||||
tool: wasm-pack
|
||||
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
|
||||
- name: Copy prepared wasm
|
||||
run: |
|
||||
ls -R prepared-wasm
|
||||
cp prepared-wasm/kcl_wasm_lib_bg.wasm public
|
||||
mkdir rust/kcl-wasm-lib/pkg
|
||||
cp prepared-wasm/kcl_wasm_lib* rust/kcl-wasm-lib/pkg
|
||||
|
||||
- name: Copy prepared ts-rs bindings
|
||||
run: |
|
||||
ls -R prepared-ts-rs-bindings
|
||||
mkdir rust/kcl-lib/bindings
|
||||
cp -r prepared-ts-rs-bindings/* rust/kcl-lib/bindings/
|
||||
- run: yarn build:wasm
|
||||
|
||||
- run: yarn simpleserver:bg
|
||||
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
|
||||
@ -226,7 +120,6 @@ jobs:
|
||||
|
||||
yarn-unit-test:
|
||||
runs-on: ubuntu-latest
|
||||
needs: yarn-build-wasm
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
@ -239,22 +132,7 @@ jobs:
|
||||
- uses: taiki-e/install-action@37bdc826eaedac215f638a96472df572feab0f9b
|
||||
with:
|
||||
tool: wasm-pack
|
||||
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
|
||||
- name: Copy prepared wasm
|
||||
run: |
|
||||
ls -R prepared-wasm
|
||||
cp prepared-wasm/kcl_wasm_lib_bg.wasm public
|
||||
mkdir rust/kcl-wasm-lib/pkg
|
||||
cp prepared-wasm/kcl_wasm_lib* rust/kcl-wasm-lib/pkg
|
||||
|
||||
- name: Copy prepared ts-rs bindings
|
||||
run: |
|
||||
ls -R prepared-ts-rs-bindings
|
||||
mkdir rust/kcl-lib/bindings
|
||||
cp -r prepared-ts-rs-bindings/* rust/kcl-lib/bindings/
|
||||
- run: yarn build:wasm
|
||||
|
||||
- run: yarn simpleserver:bg
|
||||
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
|
||||
|
20
INSTALL.md
@ -1,27 +1,27 @@
|
||||
# Setting Up Zoo Design Studio
|
||||
# Setting Up Zoo Modeling App
|
||||
|
||||
Compared to other CAD software, getting Zoo Design Studio up and running is quick and straightforward across platforms. It's about 100MB to download and is quick to install.
|
||||
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 Design Studio installer](https://zoo.dev/modeling-app/download) for Windows and for your processor type.
|
||||
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 Design Studio-{version}-{arch}-win.exe` which should take a few seconds.
|
||||
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 Design Studio`. A shortcut in the start menu is also created so you can run the app easily by clicking on it.
|
||||
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 Design Studio installer](https://zoo.dev/modeling-app/download) for macOS and for your processor type.
|
||||
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 Design Studio-{version}-{arch}-mac.dmg` and drag the applications to your `Applications` directory.
|
||||
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 Design Studio` to open.
|
||||
3. You can then open your `Applications` directory and double-click on `Zoo Modeling App` to open.
|
||||
|
||||
|
||||
## Linux
|
||||
|
||||
1. Download the [Zoo Design Studio installer](https://zoo.dev/modeling-app/download) for Linux and for your processor type.
|
||||
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.
|
||||
@ -30,7 +30,7 @@ Compared to other CAD software, getting Zoo Design Studio up and running is quic
|
||||
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 Design Studio-{version}-{arch}-linux.AppImage` to the directory of your choice, for instance `~/Applications`.
|
||||
- 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
|
||||
|
68
Makefile
@ -4,38 +4,18 @@ all: install build check
|
||||
###############################################################################
|
||||
# INSTALL
|
||||
|
||||
ifeq ($(OS),Windows_NT)
|
||||
CARGO ?= ~/.cargo/bin/cargo.exe
|
||||
WASM_PACK ?= ~/.cargo/bin/wasm-pack.exe
|
||||
else
|
||||
CARGO ?= ~/.cargo/bin/cargo
|
||||
WASM_PACK ?= ~/.cargo/bin/wasm-pack
|
||||
endif
|
||||
WASM_PACK ?= ~/.cargo/bin/wasm-pack
|
||||
|
||||
.PHONY: install
|
||||
install: node_modules/.yarn-integrity $(CARGO) $(WASM_PACK) ## Install dependencies
|
||||
install: node_modules/.yarn-integrity $(WASM_PACK) ## Install dependencies
|
||||
|
||||
node_modules/.yarn-integrity: package.json yarn.lock
|
||||
yarn install
|
||||
ifeq ($(OS),Windows_NT)
|
||||
@ type nul > $@
|
||||
else
|
||||
@ touch $@
|
||||
endif
|
||||
|
||||
$(CARGO):
|
||||
ifeq ($(OS),Windows_NT)
|
||||
yarn install:rust:windows
|
||||
else
|
||||
yarn install:rust
|
||||
endif
|
||||
|
||||
$(WASM_PACK):
|
||||
ifeq ($(OS),Windows_NT)
|
||||
yarn install:wasm-pack:cargo
|
||||
else
|
||||
yarn install:rust
|
||||
yarn install:wasm-pack:sh
|
||||
endif
|
||||
|
||||
###############################################################################
|
||||
# BUILD
|
||||
@ -51,17 +31,13 @@ VITE_SOURCES := $(wildcard vite.*) $(wildcard vite/**/*.tsx)
|
||||
build: build-web build-desktop
|
||||
|
||||
.PHONY: build-web
|
||||
build-web: install public/kcl_wasm_lib_bg.wasm build/index.html
|
||||
build-web: public/kcl_wasm_lib_bg.wasm build/index.html
|
||||
|
||||
.PHONY: build-desktop
|
||||
build-desktop: install public/kcl_wasm_lib_bg.wasm .vite/build/main.js
|
||||
build-desktop: public/kcl_wasm_lib_bg.wasm .vite/build/main.js
|
||||
|
||||
public/kcl_wasm_lib_bg.wasm: $(CARGO_SOURCES) $(RUST_SOURCES)
|
||||
ifeq ($(OS),Windows_NT)
|
||||
yarn build:wasm:dev:windows
|
||||
else
|
||||
yarn build:wasm:dev
|
||||
endif
|
||||
public/kcl_wasm_lib_bg.wasm: $(CARGO_SOURCES)$(RUST_SOURCES)
|
||||
yarn build:wasm
|
||||
|
||||
build/index.html: $(REACT_SOURCES) $(TYPESCRIPT_SOURCES) $(VITE_SOURCES)
|
||||
yarn build:local
|
||||
@ -87,10 +63,8 @@ lint: install ## Lint the code
|
||||
###############################################################################
|
||||
# RUN
|
||||
|
||||
TARGET ?= desktop
|
||||
|
||||
.PHONY: run
|
||||
run: run-$(TARGET)
|
||||
run: run-web
|
||||
|
||||
.PHONY: run-web
|
||||
run-web: install build-web ## Start the web app
|
||||
@ -103,9 +77,9 @@ run-desktop: install build-desktop ## Start the desktop app
|
||||
###############################################################################
|
||||
# TEST
|
||||
|
||||
E2E_GREP ?=
|
||||
E2E_WORKERS ?=
|
||||
E2E_WORKERS ?= 1
|
||||
E2E_FAILURES ?= 1
|
||||
E2E_GREP ?= ""
|
||||
|
||||
.PHONY: test
|
||||
test: test-unit test-e2e
|
||||
@ -116,47 +90,31 @@ test-unit: install ## Run the unit tests
|
||||
yarn test:unit
|
||||
|
||||
.PHONY: test-e2e
|
||||
test-e2e: test-e2e-$(TARGET)
|
||||
test-e2e: test-e2e-desktop
|
||||
|
||||
.PHONY: test-e2e-web
|
||||
test-e2e-web: install build-web ## Run the web e2e tests
|
||||
@ curl -fs localhost:3000 >/dev/null || ( echo "Error: localhost:3000 not available, 'make run-web' first" && exit 1 )
|
||||
ifdef E2E_GREP
|
||||
yarn chrome:test --headed --grep="$(E2E_GREP)" --max-failures=$(E2E_FAILURES)
|
||||
else
|
||||
yarn chrome:test --headed --workers='100%'
|
||||
endif
|
||||
yarn chrome:test --headed --workers=$(E2E_WORKERS) --max-failures=$(E2E_FAILURES) --grep=$(E2E_GREP)
|
||||
|
||||
.PHONY: test-e2e-desktop
|
||||
test-e2e-desktop: install build-desktop ## Run the desktop e2e tests
|
||||
ifdef E2E_GREP
|
||||
yarn test:playwright:electron --grep="$(E2E_GREP)" --max-failures=$(E2E_FAILURES)
|
||||
else
|
||||
yarn test:playwright:electron --workers='100%'
|
||||
endif
|
||||
yarn test:playwright:electron --workers=$(E2E_WORKERS) --max-failures=$(E2E_FAILURES) --grep=$(E2E_GREP)
|
||||
|
||||
###############################################################################
|
||||
# CLEAN
|
||||
|
||||
.PHONY: clean
|
||||
clean: ## Delete all artifacts
|
||||
ifeq ($(OS),Windows_NT)
|
||||
git clean --force -d -X
|
||||
else
|
||||
rm -rf .vite/ build/
|
||||
rm -rf trace.zip playwright-report/ test-results/
|
||||
rm -rf public/kcl_wasm_lib_bg.wasm
|
||||
rm -rf rust/*/bindings/ rust/*/pkg/ rust/target/
|
||||
rm -rf node_modules/ rust/*/node_modules/
|
||||
endif
|
||||
|
||||
.PHONY: help
|
||||
help: install
|
||||
ifeq ($(OS),Windows_NT)
|
||||
@ powershell -Command "Get-Content $(MAKEFILE_LIST) | Select-String -Pattern '^[^\s]+:.*##\s.*$$' | ForEach-Object { $$line = $$_.Line -split ':.*?##\s+'; Write-Host -NoNewline $$line[0].PadRight(30) -ForegroundColor Cyan; Write-Host $$line[1] }"
|
||||
else
|
||||
@ grep -E '^[^[:space:]]+:.*## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
|
||||
endif
|
||||
|
||||
.DEFAULT_GOAL := help
|
||||
|
||||
|
16
README.md
@ -1,17 +1,17 @@
|
||||

|
||||

|
||||
|
||||
## Zoo Design Studio
|
||||
## Zoo Modeling App
|
||||
|
||||
download at [zoo.dev/modeling-app/download](https://zoo.dev/modeling-app/download)
|
||||
|
||||
A CAD application from the future, brought to you by the [Zoo team](https://zoo.dev).
|
||||
|
||||
Design Studio is our take on what a modern modelling experience can be. It is applying several lessons learned in the decades since most major CAD tools came into existence:
|
||||
Modeling App is our take on what a modern modelling experience can be. It is applying several lessons learned in the decades since most major CAD tools came into existence:
|
||||
|
||||
- All artifacts—including parts and assemblies—should be represented as human-readable code. At the end of the day, your CAD project should be "plain text"
|
||||
- This makes version control—which is a solved problem in software engineering—trivial for CAD
|
||||
- All GUI (or point-and-click) interactions should be actions performed on this code representation under the hood
|
||||
- This unlocks a hybrid approach to modeling. Whether you point-and-click as you always have or you write your own KCL code, you are performing the same action in Design Studio
|
||||
- This unlocks a hybrid approach to modeling. Whether you point-and-click as you always have or you write your own KCL code, you are performing the same action in Modeling App
|
||||
- Everything graphics _has_ to be built for the GPU
|
||||
- Most CAD applications have had to retrofit support for GPUs, but our geometry engine is made for GPUs (primarily Nvidia's Vulkan), getting the order of magnitude rendering performance boost with it
|
||||
- Make the resource-intensive pieces of an application auto-scaling
|
||||
@ -19,9 +19,9 @@ Design Studio is our take on what a modern modelling experience can be. It is ap
|
||||
|
||||
We are excited about what a small team of people could build in a short time with our API. We welcome you to try our API, build your own applications, or contribute to ours!
|
||||
|
||||
Design Studio is a _hybrid_ user interface for CAD modeling. You can point-and-click to design parts (and soon assemblies), but everything you make is really just [`kcl` code](https://github.com/KittyCAD/kcl-experiments) under the hood. All of your CAD models can be checked into source control such as GitHub and responsibly versioned, rolled back, and more.
|
||||
Modeling App is a _hybrid_ user interface for CAD modeling. You can point-and-click to design parts (and soon assemblies), but everything you make is really just [`kcl` code](https://github.com/KittyCAD/kcl-experiments) under the hood. All of your CAD models can be checked into source control such as GitHub and responsibly versioned, rolled back, and more.
|
||||
|
||||
The 3D view in Design Studio is just a video stream from our hosted geometry engine. The app sends new modeling commands to the engine via WebSockets, which returns back video frames of the view within the engine.
|
||||
The 3D view in Modeling App is just a video stream from our hosted geometry engine. The app sends new modeling commands to the engine via WebSockets, which returns back video frames of the view within the engine.
|
||||
|
||||
## Tools
|
||||
|
||||
@ -198,13 +198,13 @@ If the prompt doesn't show up, start the app in command line to grab the electro
|
||||
|
||||
```
|
||||
# Windows (PowerShell)
|
||||
& 'C:\Program Files\Zoo Design Studio\Zoo Design Studio.exe'
|
||||
& 'C:\Program Files\Zoo Modeling App\Zoo Modeling App.exe'
|
||||
|
||||
# macOS
|
||||
/Applications/Zoo\ Modeling\ App.app/Contents/MacOS/Zoo\ Modeling\ App
|
||||
|
||||
# Linux
|
||||
./Zoo Design Studio-{version}-{arch}-linux.AppImage
|
||||
./Zoo Modeling App-{version}-{arch}-linux.AppImage
|
||||
```
|
||||
|
||||
#### 4. Publish the release
|
||||
|
@ -12,7 +12,7 @@ Import a CAD file.
|
||||
|
||||
For formats lacking unit data (such as STL, OBJ, or PLY files), the default unit of measurement is millimeters. Alternatively you may specify the unit by passing your desired measurement unit in the options parameter. When importing a GLTF file, the bin file will be imported as well. Import paths are relative to the current project directory.
|
||||
|
||||
Note: The import command currently only works when using the native Design Studio.
|
||||
Note: The import command currently only works when using the native Modeling App.
|
||||
|
||||
```js
|
||||
import(
|
||||
|
@ -1,6 +1,6 @@
|
||||
---
|
||||
title: "KCL Standard Library"
|
||||
excerpt: "Documentation for the KCL standard library for the Zoo Design Studio."
|
||||
excerpt: "Documentation for the KCL standard library for the Zoo Modeling App."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
---
|
||||
title: "KCL Known Issues"
|
||||
excerpt: "Known issues with the KCL standard library for the Zoo Design Studio."
|
||||
excerpt: "Known issues with the KCL standard library for the Zoo Modeling App."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
---
|
||||
title: "KCL Modules"
|
||||
excerpt: "Documentation of modules for the KCL language for the Zoo Design Studio."
|
||||
excerpt: "Documentation of modules for the KCL language for the Zoo Modeling App."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
@ -95,7 +95,7 @@ import "tests/inputs/cube.obj"
|
||||
When importing a GLTF file, the bin file will be imported as well.
|
||||
|
||||
Import paths are relative to the current project directory. Imports currently only work when
|
||||
using the native Design Studio, not in the browser.
|
||||
using the native Modeling App, not in the browser.
|
||||
|
||||
### Supported values
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
---
|
||||
title: "KCL Settings"
|
||||
excerpt: "Documentation of settings for the KCL language and Zoo Design Studio."
|
||||
excerpt: "Documentation of settings for the KCL language and Zoo Modeling App."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
# KCL Settings
|
||||
|
||||
There are three levels of settings available in the KittyCAD Design Studiolication:
|
||||
There are three levels of settings available in the KittyCAD modeling application:
|
||||
|
||||
1. [User Settings](/docs/kcl/settings/user): Global settings that apply to all projects, stored in `user.toml`
|
||||
2. [Project Settings](/docs/kcl/settings/project): Settings specific to a project, stored in `project.toml`
|
||||
@ -14,7 +14,7 @@ There are three levels of settings available in the KittyCAD Design Studiolicati
|
||||
|
||||
## Configuration Files
|
||||
|
||||
The KittyCAD Design Studio uses TOML files for configuration:
|
||||
The KittyCAD modeling app uses TOML files for configuration:
|
||||
|
||||
* **User Settings**: `user.toml` - See [complete documentation](/docs/kcl/settings/user)
|
||||
* **Project Settings**: `project.toml` - See [complete documentation](/docs/kcl/settings/project)
|
||||
|
@ -35,7 +35,7 @@ base_unit = "in"
|
||||
|
||||
#### app
|
||||
|
||||
The settings for the Design Studio.
|
||||
The settings for the modeling app.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
@ -37,7 +37,7 @@ text_wrapping = false
|
||||
|
||||
#### app
|
||||
|
||||
The settings for the Design Studio.
|
||||
The settings for the modeling app.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
@ -17,7 +17,7 @@ circle(@sketch_or_surface: Sketch | Plane | Face, center: Point2d, radius: numbe
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `sketch_or_surface` | [`Sketch`](/docs/kcl/types/Sketch) OR [`Plane`](/docs/kcl/types/Plane) OR [`Face`](/docs/kcl/types/Face) | Sketch to extend, or plane or surface to sketch on. | Yes |
|
||||
| `sketch_or_surface` | [`Sketch`](/docs/kcl/types/Sketch) `|` [`Plane`](/docs/kcl/types/Face) `|` [`Plane`](/docs/kcl/types/Face) | Sketch to extend, or plane or surface to sketch on. | Yes |
|
||||
| `center` | [`Point2d`](/docs/kcl/types/Point2d) | The center of the circle. | Yes |
|
||||
| `radius` | [`number`](/docs/kcl/types/number) | The radius of the circle. | Yes |
|
||||
| [`tag`](/docs/kcl/types/tag) | [`tag`](/docs/kcl/types/tag) | Create a new tag which refers to this circle. | No |
|
||||
|
6847
docs/kcl/std.json
@ -1,6 +1,6 @@
|
||||
---
|
||||
title: "KCL Types"
|
||||
excerpt: "Documentation of types for the KCL standard library for the Zoo Design Studio."
|
||||
excerpt: "Documentation of types for the KCL standard library for the Zoo Modeling App."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
---
|
||||
title: "std::Axis2d"
|
||||
excerpt: "An infinite line in 2d space."
|
||||
excerpt: "An infinte line in 2d space."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
An infinite line in 2d space.
|
||||
An infinte line in 2d space.
|
||||
|
||||
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
---
|
||||
title: "std::Axis3d"
|
||||
excerpt: "An infinite line in 3d space."
|
||||
excerpt: "An infinte line in 3d space."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
An infinite line in 3d space.
|
||||
An infinte line in 3d space.
|
||||
|
||||
|
||||
|
||||
|
@ -28,7 +28,7 @@ An extrude plane.
|
||||
| `faceId` |[`string`](/docs/kcl/types/string)| The face id for the extrude plane. | No |
|
||||
| [`tag`](/docs/kcl/types/tag) |[`TagDeclarator`](/docs/kcl/types#tag-declaration)| The tag. | No |
|
||||
| `id` |[`string`](/docs/kcl/types/string)| The id of the geometry. | No |
|
||||
| `sourceRange` |`[integer, integer, integer]`| The source range. | No |
|
||||
| `sourceRange` |[`SourceRange`](/docs/kcl/types/SourceRange)| The source range. | No |
|
||||
|
||||
|
||||
----
|
||||
@ -48,7 +48,7 @@ An extruded arc.
|
||||
| `faceId` |[`string`](/docs/kcl/types/string)| The face id for the extrude plane. | No |
|
||||
| [`tag`](/docs/kcl/types/tag) |[`TagDeclarator`](/docs/kcl/types#tag-declaration)| The tag. | No |
|
||||
| `id` |[`string`](/docs/kcl/types/string)| The id of the geometry. | No |
|
||||
| `sourceRange` |`[integer, integer, integer]`| The source range. | No |
|
||||
| `sourceRange` |[`SourceRange`](/docs/kcl/types/SourceRange)| The source range. | No |
|
||||
|
||||
|
||||
----
|
||||
@ -68,7 +68,7 @@ Geometry metadata.
|
||||
| `faceId` |[`string`](/docs/kcl/types/string)| The id for the chamfer surface. | No |
|
||||
| [`tag`](/docs/kcl/types/tag) |[`TagDeclarator`](/docs/kcl/types#tag-declaration)| The tag. | No |
|
||||
| `id` |[`string`](/docs/kcl/types/string)| The id of the geometry. | No |
|
||||
| `sourceRange` |`[integer, integer, integer]`| The source range. | No |
|
||||
| `sourceRange` |[`SourceRange`](/docs/kcl/types/SourceRange)| The source range. | No |
|
||||
|
||||
|
||||
----
|
||||
@ -88,7 +88,7 @@ Geometry metadata.
|
||||
| `faceId` |[`string`](/docs/kcl/types/string)| The id for the fillet surface. | No |
|
||||
| [`tag`](/docs/kcl/types/tag) |[`TagDeclarator`](/docs/kcl/types#tag-declaration)| The tag. | No |
|
||||
| `id` |[`string`](/docs/kcl/types/string)| The id of the geometry. | No |
|
||||
| `sourceRange` |`[integer, integer, integer]`| The source range. | No |
|
||||
| `sourceRange` |[`SourceRange`](/docs/kcl/types/SourceRange)| The source range. | No |
|
||||
|
||||
|
||||
----
|
||||
|
@ -17,6 +17,6 @@ Geometry metadata.
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `id` |[`string`](/docs/kcl/types/string)| The id of the geometry. | No |
|
||||
| `sourceRange` |`[integer, integer, integer]`| The source range. | No |
|
||||
| `sourceRange` |[`SourceRange`](/docs/kcl/types/SourceRange)| The source range. | No |
|
||||
|
||||
|
||||
|
@ -17,7 +17,7 @@ A helix.
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `value` |[`string`](/docs/kcl/types/string)| The id of the helix. | No |
|
||||
| `artifactId` |[`string`](/docs/kcl/types/string)| The artifact ID. | No |
|
||||
| `artifactId` |[`ArtifactId`](/docs/kcl/types/ArtifactId)| The artifact ID. | No |
|
||||
| `revolutions` |[`number`](/docs/kcl/types/number)| Number of revolutions. | No |
|
||||
| `angleStart` |[`number`](/docs/kcl/types/number)| Start angle (in degrees). | No |
|
||||
| `ccw` |`boolean`| Is the helix rotation counter clockwise? | No |
|
||||
|
@ -285,7 +285,7 @@ Data for an imported geometry.
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `Module`| | No |
|
||||
| `value` |`integer`| Identifier of a source file. Uses a u32 to keep the size small. | No |
|
||||
| `value` |[`ModuleId`](/docs/kcl/types/ModuleId)| Identifier of a source file. Uses a u32 to keep the size small. | No |
|
||||
|
||||
|
||||
----
|
||||
|
@ -17,6 +17,6 @@ Data for polar coordinates.
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `angle` |[`number`](/docs/kcl/types/number)| The angle of the line (in degrees). | No |
|
||||
| `length` |[`number`](/docs/kcl/types/number)| The length of the line. | No |
|
||||
| `length` |[`TyF64`](/docs/kcl/types/TyF64)| The length of the line. | No |
|
||||
|
||||
|
||||
|
@ -25,7 +25,7 @@ A sketch type.
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `plane`| | No |
|
||||
| `id` |[`string`](/docs/kcl/types/string)| The id of the plane. | No |
|
||||
| `artifactId` |[`string`](/docs/kcl/types/string)| The artifact ID. | No |
|
||||
| `artifactId` |[`ArtifactId`](/docs/kcl/types/ArtifactId)| The artifact ID. | No |
|
||||
| `value` |[`PlaneType`](/docs/kcl/types/PlaneType)| Type for a plane. | No |
|
||||
| `origin` |[`Point3d`](/docs/kcl/types/Point3d)| Origin of the plane. | No |
|
||||
| `xAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the plane's X axis be? | No |
|
||||
@ -49,7 +49,7 @@ A face.
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `face`| | No |
|
||||
| `id` |[`string`](/docs/kcl/types/string)| The id of the face. | No |
|
||||
| `artifactId` |[`string`](/docs/kcl/types/string)| The artifact ID. | No |
|
||||
| `artifactId` |[`ArtifactId`](/docs/kcl/types/ArtifactId)| The artifact ID. | No |
|
||||
| `value` |[`string`](/docs/kcl/types/string)| The tag of the face. | No |
|
||||
| `xAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the face's X axis be? | No |
|
||||
| `yAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the face's Y axis be? | No |
|
||||
|
15
docs/kcl/types/SourceRange.md
Normal file
@ -0,0 +1,15 @@
|
||||
---
|
||||
title: "SourceRange"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
**Type:** `integer` (`uint`)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -5,11 +5,17 @@ layout: manual
|
||||
---
|
||||
|
||||
|
||||
**Type:** [`number`](/docs/kcl/types/number) (`double`)
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `n` |[`number`](/docs/kcl/types/number)| | No |
|
||||
| `ty` |[`NumericType`](/docs/kcl/types/NumericType)| | No |
|
||||
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
import { test, expect } from './zoo-test'
|
||||
|
||||
test.describe('Electron app header tests', () => {
|
||||
test(
|
||||
|
@ -1,14 +1,13 @@
|
||||
import type { Page } from '@playwright/test'
|
||||
|
||||
import type { HomePageFixture } from '@e2e/playwright/fixtures/homePageFixture'
|
||||
import { Page } from '@playwright/test'
|
||||
import { test, expect } from './zoo-test'
|
||||
import {
|
||||
PERSIST_MODELING_CONTEXT,
|
||||
getUtils,
|
||||
TEST_COLORS,
|
||||
commonPoints,
|
||||
getUtils,
|
||||
PERSIST_MODELING_CONTEXT,
|
||||
orRunWhenFullSuiteEnabled,
|
||||
} from '@e2e/playwright/test-utils'
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
} from './test-utils'
|
||||
import { HomePageFixture } from './fixtures/homePageFixture'
|
||||
|
||||
test.setTimeout(120000)
|
||||
|
||||
@ -86,7 +85,7 @@ async function doBasicSketch(
|
||||
await page.mouse.click(startXPx, 500 - PUR * 20)
|
||||
if (openPanes.includes('code')) {
|
||||
await expect(u.codeLocator)
|
||||
.toHaveText(`@settings(defaultLengthUnit = in)sketch001 = startSketchOn(XZ)profile001 = startProfileAt(${
|
||||
.toHaveText(`sketch001 = startSketchOn(XZ)profile001 = startProfileAt(${
|
||||
commonPoints.startAt
|
||||
}, sketch001)
|
||||
|> xLine(length = ${commonPoints.num1})
|
||||
@ -120,7 +119,10 @@ async function doBasicSketch(
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
if (openPanes.includes('code')) {
|
||||
expect(await u.getGreatestPixDiff(line1, TEST_COLORS.BLUE)).toBeLessThan(3)
|
||||
await expect(
|
||||
await u.getGreatestPixDiff(line1, TEST_COLORS.BLUE)
|
||||
).toBeLessThan(3)
|
||||
await expect(await u.getGreatestPixDiff(line1, [0, 0, 255])).toBeLessThan(3)
|
||||
}
|
||||
|
||||
// hold down shift
|
||||
@ -143,7 +145,7 @@ async function doBasicSketch(
|
||||
// Open the code pane.
|
||||
await u.openKclCodePanel()
|
||||
await expect(u.codeLocator)
|
||||
.toHaveText(`@settings(defaultLengthUnit = in)sketch001 = startSketchOn(XZ)profile001 = startProfileAt(${
|
||||
.toHaveText(`sketch001 = startSketchOn(XZ)profile001 = startProfileAt(${
|
||||
commonPoints.startAt
|
||||
}, sketch001)
|
||||
|> xLine(length = ${commonPoints.num1}, tag = $seg01)
|
||||
|
@ -1,118 +0,0 @@
|
||||
import fs from 'node:fs/promises'
|
||||
import path from 'node:path'
|
||||
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
|
||||
test.describe('Point and click for boolean workflows', () => {
|
||||
// Boolean operations to test
|
||||
const booleanOperations = [
|
||||
{
|
||||
name: 'union',
|
||||
code: 'union([extrude001, extrude006])',
|
||||
},
|
||||
{
|
||||
name: 'subtract',
|
||||
code: 'subtract([extrude001], tools = [extrude006])',
|
||||
},
|
||||
{
|
||||
name: 'intersect',
|
||||
code: 'intersect([extrude001, extrude006])',
|
||||
},
|
||||
] as const
|
||||
for (let i = 0; i < booleanOperations.length; i++) {
|
||||
const operation = booleanOperations[i]
|
||||
const operationName = operation.name
|
||||
const commandName = `Boolean ${
|
||||
operationName.charAt(0).toUpperCase() + operationName.slice(1)
|
||||
}`
|
||||
test(`Create boolean operation -- ${operationName}`, async ({
|
||||
context,
|
||||
homePage,
|
||||
cmdBar,
|
||||
editor,
|
||||
toolbar,
|
||||
scene,
|
||||
page,
|
||||
}) => {
|
||||
const file = await fs.readFile(
|
||||
path.resolve(
|
||||
__dirname,
|
||||
'../../',
|
||||
'./rust/kcl-lib/e2e/executor/inputs/boolean-setup-with'
|
||||
),
|
||||
'utf-8'
|
||||
)
|
||||
await context.addInitScript((file) => {
|
||||
localStorage.setItem('persistCode', file)
|
||||
}, file)
|
||||
await homePage.goToModelingScene()
|
||||
await scene.waitForExecutionDone()
|
||||
|
||||
await scene.settled(cmdBar)
|
||||
|
||||
// Test coordinates for selection - these might need adjustment based on actual scene layout
|
||||
const cylinderPoint = { x: 592, y: 174 }
|
||||
const secondObjectPoint = { x: 683, y: 273 }
|
||||
|
||||
// Create mouse helpers for selecting objects
|
||||
const [clickFirstObject] = scene.makeMouseHelpers(
|
||||
cylinderPoint.x,
|
||||
cylinderPoint.y,
|
||||
{ steps: 10 }
|
||||
)
|
||||
const [clickSecondObject] = scene.makeMouseHelpers(
|
||||
secondObjectPoint.x,
|
||||
secondObjectPoint.y,
|
||||
{ steps: 10 }
|
||||
)
|
||||
|
||||
await test.step(`Test ${operationName} operation`, async () => {
|
||||
// Click the boolean operation button in the toolbar
|
||||
await toolbar.selectBoolean(operationName)
|
||||
|
||||
// Verify command bar is showing the right command
|
||||
await expect(cmdBar.page.getByTestId('command-name')).toContainText(
|
||||
commandName
|
||||
)
|
||||
|
||||
// Select first object in the scene, expect there to be a pixel diff from the selection color change
|
||||
await clickFirstObject({ pixelDiff: 50 })
|
||||
|
||||
// For subtract, we need to proceed to the next step before selecting the second object
|
||||
if (operationName !== 'subtract') {
|
||||
// should down shift key to select multiple objects
|
||||
await page.keyboard.down('Shift')
|
||||
}
|
||||
|
||||
// Select second object
|
||||
await clickSecondObject({ pixelDiff: 50 })
|
||||
|
||||
// Confirm the operation in the command bar
|
||||
await cmdBar.progressCmdBar()
|
||||
|
||||
if (operationName === 'union' || operationName === 'intersect') {
|
||||
await cmdBar.expectState({
|
||||
stage: 'review',
|
||||
headerArguments: {
|
||||
Solids: '2 paths',
|
||||
},
|
||||
commandName,
|
||||
})
|
||||
} else if (operationName === 'subtract') {
|
||||
await cmdBar.expectState({
|
||||
stage: 'review',
|
||||
headerArguments: {
|
||||
Tool: '1 path',
|
||||
Target: '1 path',
|
||||
},
|
||||
commandName,
|
||||
})
|
||||
}
|
||||
|
||||
await cmdBar.submit()
|
||||
|
||||
await editor.expectEditor.toContain(operation.code)
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
@ -1,11 +1,10 @@
|
||||
import type { Page } from '@playwright/test'
|
||||
import type { EngineCommand } from '@src/lang/std/artifactGraph'
|
||||
import { uuidv4 } from '@src/lib/utils'
|
||||
|
||||
import type { HomePageFixture } from '@e2e/playwright/fixtures/homePageFixture'
|
||||
import type { SceneFixture } from '@e2e/playwright/fixtures/sceneFixture'
|
||||
import { getUtils } from '@e2e/playwright/test-utils'
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
import { Page } from '@playwright/test'
|
||||
import { test, expect } from './zoo-test'
|
||||
import { HomePageFixture } from './fixtures/homePageFixture'
|
||||
import { getUtils } from './test-utils'
|
||||
import { EngineCommand } from 'lang/std/artifactGraph'
|
||||
import { uuidv4 } from 'lib/utils'
|
||||
import { SceneFixture } from './fixtures/sceneFixture'
|
||||
|
||||
test.describe(
|
||||
'Can create sketches on all planes and their back sides',
|
||||
@ -47,7 +46,7 @@ test.describe(
|
||||
},
|
||||
}
|
||||
|
||||
const code = `@settings(defaultLengthUnit = in)sketch001 = startSketchOn(${plane})profile001 = startProfileAt([0.91, -1.22], sketch001)`
|
||||
const code = `sketch001 = startSketchOn(${plane})profile001 = startProfileAt([0.91, -1.22], sketch001)`
|
||||
|
||||
await u.openDebugPanel()
|
||||
|
||||
|
@ -1,14 +1,13 @@
|
||||
import { bracket } from '@src/lib/exampleKcl'
|
||||
import fsp from 'fs/promises'
|
||||
import { join } from 'path'
|
||||
|
||||
import { TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW } from '@e2e/playwright/storageStates'
|
||||
import { test, expect } from './zoo-test'
|
||||
import {
|
||||
executorInputPath,
|
||||
getUtils,
|
||||
orRunWhenFullSuiteEnabled,
|
||||
} from '@e2e/playwright/test-utils'
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
getUtils,
|
||||
executorInputPath,
|
||||
} from './test-utils'
|
||||
import { join } from 'path'
|
||||
import { bracket } from 'lib/exampleKcl'
|
||||
import { TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW } from './storageStates'
|
||||
import fsp from 'fs/promises'
|
||||
|
||||
test.describe('Code pane and errors', { tag: ['@skipWin'] }, () => {
|
||||
test('Typing KCL errors induces a badge on the code pane button', async ({
|
||||
@ -252,11 +251,11 @@ test(
|
||||
])
|
||||
await Promise.all([
|
||||
fsp.copyFile(
|
||||
executorInputPath('cylinder-inches.kcl'),
|
||||
executorInputPath('router-template-slate.kcl'),
|
||||
join(routerTemplateDir, 'main.kcl')
|
||||
),
|
||||
fsp.copyFile(
|
||||
executorInputPath('e2e-can-sketch-on-chamfer.kcl'),
|
||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||
join(bracketDir, 'main.kcl')
|
||||
),
|
||||
])
|
||||
|
@ -1,13 +1,12 @@
|
||||
import { KCL_DEFAULT_LENGTH } from '@src/lib/constants'
|
||||
import { test, expect } from './zoo-test'
|
||||
import * as fsp from 'fs/promises'
|
||||
import path, { join } from 'path'
|
||||
|
||||
import {
|
||||
executorInputPath,
|
||||
getUtils,
|
||||
orRunWhenFullSuiteEnabled,
|
||||
} from '@e2e/playwright/test-utils'
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
} from './test-utils'
|
||||
import { KCL_DEFAULT_LENGTH } from 'lib/constants'
|
||||
import path, { join } from 'path'
|
||||
|
||||
test.describe('Command bar tests', { tag: ['@skipWin'] }, () => {
|
||||
test('Extrude from command bar selects extrude line after', async ({
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { getUtils } from '@e2e/playwright/test-utils'
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
import { test, expect } from './zoo-test'
|
||||
import { getUtils } from './test-utils'
|
||||
|
||||
test.describe('Copilot ghost text', () => {
|
||||
// eslint-disable-next-line jest/valid-title
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { getUtils } from '@e2e/playwright/test-utils'
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
import { test, expect } from './zoo-test'
|
||||
|
||||
import { getUtils } from './test-utils'
|
||||
|
||||
function countNewlines(input: string): number {
|
||||
let count = 0
|
||||
|
@ -1,12 +1,11 @@
|
||||
import fsp from 'fs/promises'
|
||||
import { test, expect } from './zoo-test'
|
||||
import path from 'path'
|
||||
|
||||
import {
|
||||
getUtils,
|
||||
executorInputPath,
|
||||
getPlaywrightDownloadDir,
|
||||
getUtils,
|
||||
} from '@e2e/playwright/test-utils'
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
} from './test-utils'
|
||||
import fsp from 'fs/promises'
|
||||
|
||||
test(
|
||||
'export works on the first try',
|
||||
@ -21,55 +20,68 @@ test(
|
||||
await Promise.all([fsp.mkdir(bracketDir, { recursive: true })])
|
||||
await Promise.all([
|
||||
fsp.copyFile(
|
||||
executorInputPath('cylinder-inches.kcl'),
|
||||
executorInputPath('router-template-slate.kcl'),
|
||||
path.join(bracketDir, 'other.kcl')
|
||||
),
|
||||
fsp.copyFile(
|
||||
executorInputPath('e2e-can-sketch-on-chamfer.kcl'),
|
||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||
path.join(bracketDir, 'main.kcl')
|
||||
),
|
||||
])
|
||||
})
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await test.step('on open of project', async () => {
|
||||
// Open the project
|
||||
const projectName = page.getByText(`bracket`)
|
||||
await expect(projectName).toBeVisible()
|
||||
await projectName.click()
|
||||
await scene.waitForExecutionDone()
|
||||
await page.waitForTimeout(1_000) // wait for panel buttons to be available
|
||||
page.on('console', console.log)
|
||||
|
||||
// Expect zero errors in gutter
|
||||
await test.step('on open of project', async () => {
|
||||
await expect(page.getByText(`bracket`)).toBeVisible()
|
||||
|
||||
// open the project
|
||||
await page.getByText(`bracket`).click()
|
||||
|
||||
// expect zero errors in guter
|
||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||
|
||||
// Click the export button
|
||||
// export the model
|
||||
const exportButton = page.getByTestId('export-pane-button')
|
||||
await expect(exportButton).toBeVisible()
|
||||
await exportButton.click()
|
||||
await page.waitForTimeout(1_000) // wait for export options to be available
|
||||
|
||||
// Select the first format option
|
||||
// Wait for the model to finish loading
|
||||
const modelStateIndicator = page.getByTestId(
|
||||
'model-state-indicator-execution-done'
|
||||
)
|
||||
await expect(modelStateIndicator).toBeVisible({ timeout: 60000 })
|
||||
|
||||
const gltfOption = page.getByText('glTF')
|
||||
const exportFileName = `main.gltf` // source file is named `main.kcl`
|
||||
const submitButton = page.getByText('Confirm Export')
|
||||
const exportingToastMessage = page.getByText(`Exporting...`)
|
||||
const errorToastMessage = page.getByText(`Error while exporting`)
|
||||
const engineErrorToastMessage = page.getByText(`Nothing to export`)
|
||||
const alreadyExportingToastMessage = page.getByText(`Already exporting`)
|
||||
// The open file's name is `main.kcl`, so the export file name should be `main.gltf`
|
||||
const exportFileName = `main.gltf`
|
||||
|
||||
// Click the export button
|
||||
await exportButton.click()
|
||||
|
||||
await expect(gltfOption).toBeVisible()
|
||||
await expect(page.getByText('STL')).toBeVisible()
|
||||
|
||||
await page.keyboard.press('Enter')
|
||||
|
||||
// Click the checkbox
|
||||
const submitButton = page.getByText('Confirm Export')
|
||||
await expect(submitButton).toBeVisible()
|
||||
|
||||
await page.waitForTimeout(500)
|
||||
|
||||
await page.keyboard.press('Enter')
|
||||
|
||||
// Find the toast.
|
||||
// Look out for the toast message
|
||||
const exportingToastMessage = page.getByText(`Exporting...`)
|
||||
const alreadyExportingToastMessage = page.getByText(`Already exporting`)
|
||||
await expect(exportingToastMessage).toBeVisible()
|
||||
await expect(alreadyExportingToastMessage).not.toBeVisible()
|
||||
|
||||
// Expect it to succeed
|
||||
const errorToastMessage = page.getByText(`Error while exporting`)
|
||||
const engineErrorToastMessage = page.getByText(`Nothing to export`)
|
||||
// Expect it to succeed.
|
||||
await expect(errorToastMessage).not.toBeVisible()
|
||||
await expect(engineErrorToastMessage).not.toBeVisible()
|
||||
|
||||
@ -77,7 +89,6 @@ test(
|
||||
await expect(successToastMessage).toBeVisible()
|
||||
await expect(exportingToastMessage).not.toBeVisible()
|
||||
|
||||
// Check for the exported file
|
||||
const firstFileFullPath = path.resolve(
|
||||
getPlaywrightDownloadDir(tronApp.projectDirName),
|
||||
exportFileName
|
||||
@ -96,7 +107,7 @@ test(
|
||||
},
|
||||
{ timeout: 15_000 }
|
||||
)
|
||||
.toBeGreaterThan(30_000)
|
||||
.toBeGreaterThan(300_000)
|
||||
})
|
||||
})
|
||||
|
||||
@ -104,53 +115,60 @@ test(
|
||||
const u = await getUtils(page)
|
||||
await u.openFilePanel()
|
||||
|
||||
// Click on the other file
|
||||
const otherKclButton = page.getByRole('button', { name: 'other.kcl' })
|
||||
|
||||
// Click the file
|
||||
await otherKclButton.click()
|
||||
|
||||
// Close the file pane
|
||||
await u.closeFilePanel()
|
||||
await scene.waitForExecutionDone()
|
||||
await page.waitForTimeout(1_000) // wait for panel buttons to be available
|
||||
|
||||
// Expect zero errors in gutter
|
||||
// FIXME: await scene.waitForExecutionDone() does not work. The modeling indicator stays in -receive-reliable and not execution done
|
||||
await page.waitForTimeout(10000)
|
||||
|
||||
// expect zero errors in guter
|
||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||
|
||||
// Click the export button
|
||||
// export the model
|
||||
const exportButton = page.getByTestId('export-pane-button')
|
||||
await expect(exportButton).toBeVisible()
|
||||
await exportButton.click()
|
||||
await page.waitForTimeout(1_000) // wait for export options to be available
|
||||
|
||||
// Select the first format option
|
||||
const gltfOption = page.getByText('glTF')
|
||||
const exportFileName = `other.gltf` // source file is named `other.kcl`
|
||||
const submitButton = page.getByText('Confirm Export')
|
||||
const exportingToastMessage = page.getByText(`Exporting...`)
|
||||
const errorToastMessage = page.getByText(`Error while exporting`)
|
||||
const engineErrorToastMessage = page.getByText(`Nothing to export`)
|
||||
const alreadyExportingToastMessage = page.getByText(`Already exporting`)
|
||||
// The open file's name is `other.kcl`, so the export file name should be `other.gltf`
|
||||
const exportFileName = `other.gltf`
|
||||
|
||||
// Click the export button
|
||||
await exportButton.click()
|
||||
|
||||
await expect(gltfOption).toBeVisible()
|
||||
await expect(page.getByText('STL')).toBeVisible()
|
||||
|
||||
await page.keyboard.press('Enter')
|
||||
|
||||
// Click the checkbox
|
||||
const submitButton = page.getByText('Confirm Export')
|
||||
await expect(submitButton).toBeVisible()
|
||||
await page.waitForTimeout(500)
|
||||
|
||||
await page.keyboard.press('Enter')
|
||||
|
||||
// Find the toast.
|
||||
// Look out for the toast message
|
||||
const exportingToastMessage = page.getByText(`Exporting...`)
|
||||
const alreadyExportingToastMessage = page.getByText(`Already exporting`)
|
||||
await expect(exportingToastMessage).toBeVisible()
|
||||
await expect(alreadyExportingToastMessage).not.toBeVisible()
|
||||
|
||||
// Expect it to succeed
|
||||
const errorToastMessage = page.getByText(`Error while exporting`)
|
||||
const engineErrorToastMessage = page.getByText(`Nothing to export`)
|
||||
await expect(errorToastMessage).not.toBeVisible()
|
||||
await expect(engineErrorToastMessage).not.toBeVisible()
|
||||
|
||||
const successToastMessage = page.getByText(`Exported successfully`)
|
||||
await expect(successToastMessage).toBeVisible()
|
||||
await expect(exportingToastMessage).not.toBeVisible()
|
||||
await test.step('Check the success toast message shows and nothing else', async () =>
|
||||
Promise.all([
|
||||
expect(alreadyExportingToastMessage).not.toBeVisible(),
|
||||
expect(errorToastMessage).not.toBeVisible(),
|
||||
expect(engineErrorToastMessage).not.toBeVisible(),
|
||||
expect(successToastMessage).toBeVisible(),
|
||||
expect(exportingToastMessage).not.toBeVisible(),
|
||||
]))
|
||||
|
||||
// Check for the exported file=
|
||||
const secondFileFullPath = path.resolve(
|
||||
getPlaywrightDownloadDir(tronApp.projectDirName),
|
||||
exportFileName
|
||||
@ -169,7 +187,7 @@ test(
|
||||
},
|
||||
{ timeout: 15_000 }
|
||||
)
|
||||
.toBeGreaterThan(50_000)
|
||||
.toBeGreaterThan(70_000)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
import { uuidv4 } from '@src/lib/utils'
|
||||
import { test, expect } from './zoo-test'
|
||||
import fsp from 'fs/promises'
|
||||
import { join } from 'path'
|
||||
|
||||
import { uuidv4 } from 'lib/utils'
|
||||
import {
|
||||
TEST_COLORS,
|
||||
executorInputPath,
|
||||
getUtils,
|
||||
orRunWhenFullSuiteEnabled,
|
||||
} from '@e2e/playwright/test-utils'
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
TEST_COLORS,
|
||||
} from './test-utils'
|
||||
|
||||
import { join } from 'path'
|
||||
|
||||
test.describe('Editor tests', { tag: ['@skipWin'] }, () => {
|
||||
test('can comment out code with ctrl+/', async ({ page, homePage }) => {
|
||||
@ -32,30 +32,26 @@ test.describe('Editor tests', { tag: ['@skipWin'] }, () => {
|
||||
await page.keyboard.press('/')
|
||||
await page.keyboard.up('ControlOrMeta')
|
||||
|
||||
await expect(page.locator('.cm-content')).toHaveText(
|
||||
`@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XY)
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn(XY)
|
||||
|> startProfileAt([-10, -10], %)
|
||||
|> line(end = [20, 0])
|
||||
|> line(end = [0, 20])
|
||||
|> line(end = [-20, 0])
|
||||
// |> close()`.replaceAll('\n', '')
|
||||
)
|
||||
// |> close()`)
|
||||
|
||||
// uncomment the code
|
||||
await page.keyboard.down('ControlOrMeta')
|
||||
await page.keyboard.press('/')
|
||||
await page.keyboard.up('ControlOrMeta')
|
||||
|
||||
await expect(page.locator('.cm-content')).toHaveText(
|
||||
`@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XY)
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn(XY)
|
||||
|> startProfileAt([-10, -10], %)
|
||||
|> line(end = [20, 0])
|
||||
|> line(end = [0, 20])
|
||||
|> line(end = [-20, 0])
|
||||
|> close()`.replaceAll('\n', '')
|
||||
)
|
||||
|> close()`)
|
||||
})
|
||||
|
||||
test('ensure we use the cache, and do not re-execute', async ({
|
||||
@ -182,15 +178,13 @@ sketch001 = startSketchOn(XY)
|
||||
await page.locator('#code-pane button:first-child').click()
|
||||
await page.locator('button:has-text("Format code")').click()
|
||||
|
||||
await expect(page.locator('.cm-content')).toHaveText(
|
||||
`@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XY)
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn(XY)
|
||||
|> startProfileAt([-10, -10], %)
|
||||
|> line(end = [20, 0])
|
||||
|> line(end = [0, 20])
|
||||
|> line(end = [-20, 0])
|
||||
|> close()`.replaceAll('\n', '')
|
||||
)
|
||||
|> close()`)
|
||||
})
|
||||
|
||||
test('if you click the format button it formats your code and executes so lints are still there', async ({
|
||||
@ -233,15 +227,13 @@ sketch001 = startSketchOn(XY)
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
await u.closeDebugPanel()
|
||||
|
||||
await expect(page.locator('.cm-content')).toHaveText(
|
||||
`@settings(defaultLengthUnit = in)
|
||||
sketch_001 = startSketchOn(XY)
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch_001 = startSketchOn(XY)
|
||||
|> startProfileAt([-10, -10], %)
|
||||
|> line(end = [20, 0])
|
||||
|> line(end = [0, 20])
|
||||
|> line(end = [-20, 0])
|
||||
|> close()`.replaceAll('\n', '')
|
||||
)
|
||||
|> close()`)
|
||||
|
||||
// error in guter
|
||||
await expect(page.locator('.cm-lint-marker-info').first()).toBeVisible()
|
||||
@ -479,7 +471,6 @@ sketch_001 = startSketchOn(XY)
|
||||
test('if you write kcl with lint errors you get lints', async ({
|
||||
page,
|
||||
homePage,
|
||||
scene,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
@ -499,7 +490,10 @@ sketch_001 = startSketchOn(XY)
|
||||
await page.keyboard.press('ArrowLeft')
|
||||
await page.keyboard.press('ArrowRight')
|
||||
|
||||
await scene.waitForExecutionDone()
|
||||
// FIXME: lsp errors do not propagate to the frontend until engine is connected and code is executed
|
||||
// This timeout is to wait for engine connection. LSP and code execution errors should be handled differently
|
||||
// LSP can emit errors as fast as it waits and show them in the editor
|
||||
await page.waitForTimeout(10000)
|
||||
|
||||
// error in guter
|
||||
await expect(page.locator('.cm-lint-marker-info').first()).toBeVisible()
|
||||
@ -821,12 +815,10 @@ sketch_001 = startSketchOn(XY)
|
||||
// there shouldn't be any auto complete options for 'lin' in the comment
|
||||
await expect(page.locator('.cm-completionLabel')).not.toBeVisible()
|
||||
|
||||
await expect(page.locator('.cm-content')).toHaveText(
|
||||
`@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XZ)
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn(XZ)
|
||||
|> startProfileAt([3.14, 12], %)
|
||||
|> xLine(%, length = 5) // lin`.replaceAll('\n', '')
|
||||
)
|
||||
|> xLine(%, length = 5) // lin`)
|
||||
|
||||
// expect there to be no KCL errors
|
||||
await expect(page.locator('.cm-lint-marker-error')).toHaveCount(0)
|
||||
@ -896,12 +888,10 @@ sketch001 = startSketchOn(XZ)
|
||||
// there shouldn't be any auto complete options for 'lin' in the comment
|
||||
await expect(page.locator('.cm-completionLabel')).not.toBeVisible()
|
||||
|
||||
await expect(page.locator('.cm-content')).toHaveText(
|
||||
`@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XZ)
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn(XZ)
|
||||
|> startProfileAt([3.14, 12], %)
|
||||
|> xLine(%, length = 5) // lin`.replaceAll('\n', '')
|
||||
)
|
||||
|> xLine(%, length = 5) // lin`)
|
||||
})
|
||||
})
|
||||
test('Can undo a click and point extrude with ctrl+z', async ({
|
||||
@ -985,13 +975,12 @@ sketch001 = startSketchOn(XZ)
|
||||
test(
|
||||
'Can undo a sketch modification with ctrl+z',
|
||||
{ tag: ['@skipWin'] },
|
||||
async ({ page, homePage, editor }) => {
|
||||
async ({ page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`@settings(defaultLengthUnit=in)
|
||||
sketch001 = startSketchOn(XZ)
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
|> startProfileAt([4.61, -10.01], %)
|
||||
|> line(end = [12.73, -0.09])
|
||||
|> tangentialArcTo([24.95, -0.38], %)
|
||||
@ -1081,45 +1070,41 @@ sketch001 = startSketchOn(XZ)
|
||||
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
||||
|
||||
// expect the code to have changed
|
||||
await editor.expectEditor.toContain(
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn(XZ)
|
||||
|> startProfileAt([2.71, -2.71], %)
|
||||
|> line(end = [15.4, -2.78])
|
||||
|> tangentialArcTo([27.6, -3.05], %)
|
||||
|> close()
|
||||
|> extrude(length = 5)`,
|
||||
{ shouldNormalise: true }
|
||||
)
|
||||
|> extrude(length = 5)
|
||||
`)
|
||||
|
||||
// Hit undo
|
||||
await page.keyboard.down('Control')
|
||||
await page.keyboard.press('KeyZ')
|
||||
await page.keyboard.up('Control')
|
||||
|
||||
await editor.expectEditor.toContain(
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn(XZ)
|
||||
|> startProfileAt([2.71, -2.71], %)
|
||||
|> line(end = [15.4, -2.78])
|
||||
|> tangentialArcTo([24.95, -0.38], %)
|
||||
|> close()
|
||||
|> extrude(length = 5)`,
|
||||
{ shouldNormalise: true }
|
||||
)
|
||||
|> extrude(length = 5)`)
|
||||
|
||||
// Hit undo again.
|
||||
await page.keyboard.down('Control')
|
||||
await page.keyboard.press('KeyZ')
|
||||
await page.keyboard.up('Control')
|
||||
|
||||
await editor.expectEditor.toContain(
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn(XZ)
|
||||
|> startProfileAt([2.71, -2.71], %)
|
||||
|> line(end = [12.73, -0.09])
|
||||
|> tangentialArcTo([24.95, -0.38], %)
|
||||
|> close()
|
||||
|> extrude(length = 5)`,
|
||||
{ shouldNormalise: true }
|
||||
)
|
||||
|> extrude(length = 5)
|
||||
`)
|
||||
|
||||
// Hit undo again.
|
||||
await page.keyboard.down('Control')
|
||||
@ -1127,15 +1112,13 @@ sketch001 = startSketchOn(XZ)
|
||||
await page.keyboard.up('Control')
|
||||
|
||||
await page.waitForTimeout(100)
|
||||
await editor.expectEditor.toContain(
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
|> startProfileAt([4.61, -10.01], %)
|
||||
|> line(end = [12.73, -0.09])
|
||||
|> tangentialArcTo([24.95, -0.38], %)
|
||||
|> close()
|
||||
|> extrude(length = 5)`,
|
||||
{ shouldNormalise: true }
|
||||
)
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn(XZ)
|
||||
|> startProfileAt([4.61, -10.01], %)
|
||||
|> line(end = [12.73, -0.09])
|
||||
|> tangentialArcTo([24.95, -0.38], %)
|
||||
|> close()
|
||||
|> extrude(length = 5)`)
|
||||
}
|
||||
)
|
||||
|
||||
@ -1223,130 +1206,4 @@ sketch001 = startSketchOn(XZ)
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
test('Rectangle tool panning with middle click', async ({
|
||||
page,
|
||||
homePage,
|
||||
toolbar,
|
||||
scene,
|
||||
cmdBar,
|
||||
editor,
|
||||
}) => {
|
||||
await page.setBodyDimensions({ width: 1200, height: 900 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// wait until scene is ready to be interacted with
|
||||
await scene.connectionEstablished()
|
||||
await scene.settled(cmdBar)
|
||||
|
||||
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||
|
||||
// select an axis plane
|
||||
await page.mouse.click(700, 200)
|
||||
|
||||
// Needed as we don't yet have a way to get a signal from the engine that the camera has animated to the sketch plane
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
const middleMousePan = async (
|
||||
startX: number,
|
||||
startY: number,
|
||||
endX: number,
|
||||
endY: number
|
||||
) => {
|
||||
const initialCode = await editor.getCurrentCode()
|
||||
|
||||
await page.mouse.click(startX, startY, { button: 'middle' })
|
||||
await page.mouse.move(endX, endY, {
|
||||
steps: 10,
|
||||
})
|
||||
|
||||
// We expect the code to be the same, middle mouse click should not modify the code, only do panning
|
||||
await editor.expectEditor.toBe(initialCode)
|
||||
}
|
||||
|
||||
await test.step(`Verify corner rectangle panning`, async () => {
|
||||
await page.getByTestId('corner-rectangle').click()
|
||||
await middleMousePan(800, 500, 900, 600)
|
||||
})
|
||||
|
||||
await test.step(`Verify center rectangle panning`, async () => {
|
||||
await toolbar.selectCenterRectangle()
|
||||
await middleMousePan(800, 200, 900, 300)
|
||||
})
|
||||
})
|
||||
|
||||
test('Can select lines on the main axis', async ({
|
||||
page,
|
||||
homePage,
|
||||
toolbar,
|
||||
}) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
profile001 = startProfileAt([100.00, 100.0], sketch001)
|
||||
|> yLine(length = -100.0)
|
||||
|> xLine(length = 200.0)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()`
|
||||
)
|
||||
})
|
||||
|
||||
const width = 1200
|
||||
const height = 800
|
||||
const viewportSize = { width, height }
|
||||
await page.setBodyDimensions(viewportSize)
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
const u = await getUtils(page)
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await toolbar.editSketch(0)
|
||||
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
// Click on the bottom segment that lies on the x axis
|
||||
await page.mouse.click(width * 0.85, height / 2)
|
||||
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
// Verify segment is selected (you can check for visual indicators or state)
|
||||
const element = page.locator('[data-overlay-index="1"]')
|
||||
await expect(element).toHaveAttribute('data-overlay-visible', 'true')
|
||||
})
|
||||
|
||||
test(`Only show axis planes when there are no errors`, async ({
|
||||
page,
|
||||
homePage,
|
||||
scene,
|
||||
cmdBar,
|
||||
}) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
profile001 = circle(sketch001, center = [-100.0, -100.0], radius = 50.0)
|
||||
|
||||
sketch002 = startSketchOn(XZ)
|
||||
profile002 = circle(sketch002, center = [-100.0, 100.0], radius = 50.0)
|
||||
extrude001 = extrude(profile002, length = 0)` // length = 0 is causing the error
|
||||
)
|
||||
})
|
||||
|
||||
const viewportSize = { width: 1200, height: 800 }
|
||||
await page.setBodyDimensions(viewportSize)
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await scene.connectionEstablished()
|
||||
await scene.settled(cmdBar)
|
||||
|
||||
await scene.expectPixelColor(
|
||||
TEST_COLORS.DARK_MODE_BKGD,
|
||||
// This is a position where the blue part of the axis plane is visible if its rendered
|
||||
{ x: viewportSize.width * 0.75, y: viewportSize.height * 0.2 },
|
||||
15
|
||||
)
|
||||
})
|
||||
})
|
||||
|
@ -1,8 +1,7 @@
|
||||
import { test, expect } from './zoo-test'
|
||||
import * as fsp from 'fs/promises'
|
||||
import { join } from 'path'
|
||||
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
|
||||
const FEATURE_TREE_EXAMPLE_CODE = `export fn timesFive(x) {
|
||||
return 5 * x
|
||||
}
|
||||
|
@ -1,16 +1,15 @@
|
||||
import { FILE_EXT } from '@src/lib/constants'
|
||||
import * as fs from 'fs'
|
||||
import { test, expect } from './zoo-test'
|
||||
import * as fsp from 'fs/promises'
|
||||
import { join } from 'path'
|
||||
|
||||
import * as fs from 'fs'
|
||||
import {
|
||||
createProject,
|
||||
executorInputPath,
|
||||
getUtils,
|
||||
orRunWhenFullSuiteEnabled,
|
||||
runningOnWindows,
|
||||
} from '@e2e/playwright/test-utils'
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
} from './test-utils'
|
||||
import { join } from 'path'
|
||||
import { FILE_EXT } from 'lib/constants'
|
||||
|
||||
test.describe('integrations tests', () => {
|
||||
test(
|
||||
|
@ -1,5 +1,5 @@
|
||||
import type { Locator, Page, Request, Route, TestInfo } from '@playwright/test'
|
||||
import { expect } from '@playwright/test'
|
||||
import type { Page, Locator, Route, Request } from '@playwright/test'
|
||||
import { expect, TestInfo } from '@playwright/test'
|
||||
import * as fs from 'fs'
|
||||
import * as path from 'path'
|
||||
|
||||
|
@ -1,12 +1,11 @@
|
||||
import type { Locator, Page } from '@playwright/test'
|
||||
import type { Page, Locator } from '@playwright/test'
|
||||
import { expect } from '@playwright/test'
|
||||
|
||||
import {
|
||||
checkIfPaneIsOpen,
|
||||
closePane,
|
||||
checkIfPaneIsOpen,
|
||||
openPane,
|
||||
sansWhitespace,
|
||||
} from '@e2e/playwright/test-utils'
|
||||
} from '../test-utils'
|
||||
|
||||
interface EditorState {
|
||||
activeLines: Array<string>
|
||||
@ -82,13 +81,6 @@ export class EditorFixture {
|
||||
expectEditor = {
|
||||
toContain: this._expectEditorToContain(),
|
||||
not: { toContain: this._expectEditorToContain(true) },
|
||||
toBe: async (code: string) => {
|
||||
const currentCode = await this.getCurrentCode()
|
||||
return expect(currentCode).toBe(code)
|
||||
},
|
||||
}
|
||||
getCurrentCode = async () => {
|
||||
return await this.codeContent.innerText()
|
||||
}
|
||||
snapshot = async (options?: { timeout?: number; name?: string }) => {
|
||||
const wasPaneOpen = await this.checkIfPaneIsOpen()
|
||||
|
@ -1,28 +1,28 @@
|
||||
/* eslint-disable react-hooks/rules-of-hooks */
|
||||
|
||||
import type {
|
||||
BrowserContext,
|
||||
ElectronApplication,
|
||||
Page,
|
||||
TestInfo,
|
||||
Page,
|
||||
} from '@playwright/test'
|
||||
|
||||
import { _electron as electron } from '@playwright/test'
|
||||
|
||||
import { SETTINGS_FILE_NAME } from '@src/lib/constants'
|
||||
import type { DeepPartial } from '@src/lib/types'
|
||||
import * as TOML from '@iarna/toml'
|
||||
import { TEST_SETTINGS } from '../storageStates'
|
||||
import { SETTINGS_FILE_NAME } from 'lib/constants'
|
||||
import { getUtils, setup } from '../test-utils'
|
||||
import fsp from 'fs/promises'
|
||||
import fs from 'node:fs'
|
||||
import path from 'path'
|
||||
|
||||
import type { Settings } from '@rust/kcl-lib/bindings/Settings'
|
||||
|
||||
import { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture'
|
||||
import { EditorFixture } from '@e2e/playwright/fixtures/editorFixture'
|
||||
import { HomePageFixture } from '@e2e/playwright/fixtures/homePageFixture'
|
||||
import { SceneFixture } from '@e2e/playwright/fixtures/sceneFixture'
|
||||
import { ToolbarFixture } from '@e2e/playwright/fixtures/toolbarFixture'
|
||||
|
||||
import { TEST_SETTINGS } from '@e2e/playwright/storageStates'
|
||||
import { getUtils, settingsToToml, setup } from '@e2e/playwright/test-utils'
|
||||
import { CmdBarFixture } from './cmdBarFixture'
|
||||
import { EditorFixture } from './editorFixture'
|
||||
import { ToolbarFixture } from './toolbarFixture'
|
||||
import { SceneFixture } from './sceneFixture'
|
||||
import { HomePageFixture } from './homePageFixture'
|
||||
import { DeepPartial } from 'lib/types'
|
||||
import { Settings } from '@rust/kcl-lib/bindings/Settings'
|
||||
|
||||
export class AuthenticatedApp {
|
||||
public readonly page: Page
|
||||
@ -124,7 +124,6 @@ export class ElectronZoo {
|
||||
|
||||
// We need to expose this in order for some tests that require folder
|
||||
// creation and some code below.
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
const that = this
|
||||
|
||||
const options = {
|
||||
@ -177,25 +176,6 @@ export class ElectronZoo {
|
||||
|
||||
this.context = this.electron.context()
|
||||
await this.context.tracing.start({ screenshots: true, snapshots: true })
|
||||
|
||||
// We need to patch this because addInitScript will bind too late in our
|
||||
// electron tests, never running. We need to call reload() after each call
|
||||
// to guarantee it runs.
|
||||
const oldContextAddInitScript = this.context.addInitScript
|
||||
this.context.addInitScript = async function (a, b) {
|
||||
// @ts-ignore pretty sure way out of tsc's type checking capabilities.
|
||||
// This code works perfectly fine.
|
||||
await oldContextAddInitScript.apply(this, [a, b])
|
||||
await that.page.reload()
|
||||
}
|
||||
|
||||
const oldPageAddInitScript = this.page.addInitScript
|
||||
this.page.addInitScript = async function (a: any, b: any) {
|
||||
// @ts-ignore pretty sure way out of tsc's type checking capabilities.
|
||||
// This code works perfectly fine.
|
||||
await oldPageAddInitScript.apply(this, [a, b])
|
||||
await that.page.reload()
|
||||
}
|
||||
}
|
||||
|
||||
await this.context.tracing.startChunk()
|
||||
@ -238,6 +218,26 @@ export class ElectronZoo {
|
||||
}))
|
||||
}
|
||||
|
||||
// We need to patch this because addInitScript will bind too late in our
|
||||
// electron tests, never running. We need to call reload() after each call
|
||||
// to guarantee it runs.
|
||||
const oldContextAddInitScript = this.context.addInitScript
|
||||
this.context.addInitScript = async function (a, b) {
|
||||
// @ts-ignore pretty sure way out of tsc's type checking capabilities.
|
||||
// This code works perfectly fine.
|
||||
await oldContextAddInitScript.apply(this, [a, b])
|
||||
await that.page.reload()
|
||||
}
|
||||
|
||||
// No idea why we mix and match page and context's addInitScript but we do
|
||||
const oldPageAddInitScript = this.page.addInitScript
|
||||
this.page.addInitScript = async function (a: any, b: any) {
|
||||
// @ts-ignore pretty sure way out of tsc's type checking capabilities.
|
||||
// This code works perfectly fine.
|
||||
await oldPageAddInitScript.apply(this, [a, b])
|
||||
await that.page.reload()
|
||||
}
|
||||
|
||||
if (!this.firstUrl) {
|
||||
await this.page.getByText('Your Projects').count()
|
||||
this.firstUrl = this.page.url()
|
||||
@ -286,30 +286,26 @@ export class ElectronZoo {
|
||||
let settingsOverridesToml = ''
|
||||
|
||||
if (appSettings) {
|
||||
settingsOverridesToml = settingsToToml({
|
||||
settingsOverridesToml = TOML.stringify({
|
||||
// @ts-expect-error
|
||||
settings: {
|
||||
...TEST_SETTINGS,
|
||||
...appSettings,
|
||||
app: {
|
||||
...TEST_SETTINGS.app,
|
||||
project_directory: this.projectDirName,
|
||||
...appSettings.app,
|
||||
},
|
||||
project: {
|
||||
...TEST_SETTINGS.project,
|
||||
directory: this.projectDirName,
|
||||
},
|
||||
},
|
||||
})
|
||||
} else {
|
||||
settingsOverridesToml = settingsToToml({
|
||||
settingsOverridesToml = TOML.stringify({
|
||||
// @ts-expect-error
|
||||
settings: {
|
||||
...TEST_SETTINGS,
|
||||
app: {
|
||||
...TEST_SETTINGS.app,
|
||||
},
|
||||
project: {
|
||||
...TEST_SETTINGS.project,
|
||||
directory: this.projectDirName,
|
||||
project_directory: this.projectDirName,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -1,4 +1,4 @@
|
||||
import type { Locator, Page } from '@playwright/test'
|
||||
import type { Page, Locator } from '@playwright/test'
|
||||
import { expect } from '@playwright/test'
|
||||
|
||||
interface ProjectCardState {
|
||||
@ -96,7 +96,7 @@ export class HomePageFixture {
|
||||
await expect(this.projectSection).not.toHaveText('Loading your Projects...')
|
||||
}
|
||||
|
||||
createAndGoToProject = async (projectTitle = 'untitled') => {
|
||||
createAndGoToProject = async (projectTitle = 'project-$nnn') => {
|
||||
await this.projectsLoaded()
|
||||
await this.projectButtonNew.click()
|
||||
await this.projectTextName.click()
|
||||
|
@ -1,17 +1,15 @@
|
||||
import type { Locator, Page } from '@playwright/test'
|
||||
import { isArray, uuidv4 } from '@src/lib/utils'
|
||||
|
||||
import type { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture'
|
||||
|
||||
import type { Page, Locator } from '@playwright/test'
|
||||
import { expect } from '../zoo-test'
|
||||
import { isArray, uuidv4 } from 'lib/utils'
|
||||
import { CmdBarFixture } from './cmdBarFixture'
|
||||
import {
|
||||
closeDebugPanel,
|
||||
doAndWaitForImageDiff,
|
||||
getPixelRGBs,
|
||||
getUtils,
|
||||
openAndClearDebugPanel,
|
||||
sendCustomCmd,
|
||||
} from '@e2e/playwright/test-utils'
|
||||
import { expect } from '@e2e/playwright/zoo-test'
|
||||
getUtils,
|
||||
} from '../test-utils'
|
||||
|
||||
type MouseParams = {
|
||||
pixelDiff?: number
|
||||
@ -312,9 +310,7 @@ export async function expectPixelColor(
|
||||
.toBeTruthy()
|
||||
.catch((cause) => {
|
||||
throw new Error(
|
||||
`ExpectPixelColor: point ${JSON.stringify(
|
||||
coords
|
||||
)} was expecting ${colour} but got ${finalValue}`,
|
||||
`ExpectPixelColor: expecting ${colour} got ${finalValue}`,
|
||||
{ cause }
|
||||
)
|
||||
})
|
||||
|
@ -1,18 +1,14 @@
|
||||
import { type Locator, type Page, test } from '@playwright/test'
|
||||
import type { SidebarType } from '@src/components/ModelingSidebar/ModelingPanes'
|
||||
import { SIDEBAR_BUTTON_SUFFIX } from '@src/lib/constants'
|
||||
import type { ToolbarModeName } from '@src/lib/toolbar'
|
||||
|
||||
import { type Page, type Locator, test } from '@playwright/test'
|
||||
import { expect } from '../zoo-test'
|
||||
import {
|
||||
checkIfPaneIsOpen,
|
||||
closePane,
|
||||
doAndWaitForImageDiff,
|
||||
openPane,
|
||||
} from '@e2e/playwright/test-utils'
|
||||
import { expect } from '@e2e/playwright/zoo-test'
|
||||
import { type baseUnitLabels } from '@src/lib/settings/settingsTypes'
|
||||
|
||||
type LengthUnitLabel = (typeof baseUnitLabels)[keyof typeof baseUnitLabels]
|
||||
} from '../test-utils'
|
||||
import { SidebarType } from 'components/ModelingSidebar/ModelingPanes'
|
||||
import { SIDEBAR_BUTTON_SUFFIX } from 'lib/constants'
|
||||
import { ToolbarModeName } from 'lib/toolbar'
|
||||
|
||||
export class ToolbarFixture {
|
||||
public page: Page
|
||||
@ -185,14 +181,6 @@ export class ToolbarFixture {
|
||||
).toBeVisible()
|
||||
await this.page.getByTestId('dropdown-center-rectangle').click()
|
||||
}
|
||||
selectBoolean = async (operation: 'union' | 'subtract' | 'intersect') => {
|
||||
await this.page
|
||||
.getByRole('button', { name: 'caret down Union: open menu' })
|
||||
.click()
|
||||
const operationTestId = `dropdown-boolean-${operation}`
|
||||
await expect(this.page.getByTestId(operationTestId)).toBeVisible()
|
||||
await this.page.getByTestId(operationTestId).click()
|
||||
}
|
||||
|
||||
selectCircleThreePoint = async () => {
|
||||
await this.page
|
||||
@ -239,12 +227,6 @@ export class ToolbarFixture {
|
||||
async checkIfFeatureTreePaneIsOpen() {
|
||||
return this.checkIfPaneIsOpen(this.featureTreeId)
|
||||
}
|
||||
async selectUnit(unit: LengthUnitLabel) {
|
||||
await this.page.getByTestId('units-menu').click()
|
||||
const optionLocator = this.page.getByRole('button', { name: unit })
|
||||
await expect(optionLocator).toBeVisible()
|
||||
await optionLocator.click()
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific operation button from the Feature Tree pane.
|
||||
|
@ -1,120 +0,0 @@
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
import * as fsp from 'fs/promises'
|
||||
import path from 'path'
|
||||
|
||||
test.describe('Import UI tests', () => {
|
||||
test('shows toast when trying to sketch on imported face', async ({
|
||||
context,
|
||||
page,
|
||||
homePage,
|
||||
toolbar,
|
||||
scene,
|
||||
editor,
|
||||
}) => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
const projectDir = path.join(dir, 'import-test')
|
||||
await fsp.mkdir(projectDir, { recursive: true })
|
||||
|
||||
// Create the imported file
|
||||
await fsp.writeFile(
|
||||
path.join(projectDir, 'toBeImported.kcl'),
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
profile001 = startProfileAt([281.54, 305.81], sketch001)
|
||||
|> angledLine([0, 123.43], %, $rectangleSegmentA001)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001) - 90,
|
||||
85.99
|
||||
], %)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001),
|
||||
-segLen(rectangleSegmentA001)
|
||||
], %)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
extrude(profile001, length = 100)`
|
||||
)
|
||||
|
||||
// Create the main file that imports
|
||||
await fsp.writeFile(
|
||||
path.join(projectDir, 'main.kcl'),
|
||||
`import "toBeImported.kcl" as importedCube
|
||||
|
||||
importedCube
|
||||
|
||||
sketch001 = startSketchOn(XZ)
|
||||
profile001 = startProfileAt([-134.53, -56.17], sketch001)
|
||||
|> angledLine([0, 79.05], %, $rectangleSegmentA001)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001) - 90,
|
||||
76.28
|
||||
], %)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001),
|
||||
-segLen(rectangleSegmentA001)
|
||||
], %, $seg01)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg02)
|
||||
|> close()
|
||||
extrude001 = extrude(profile001, length = 100)
|
||||
sketch003 = startSketchOn(extrude001, seg02)
|
||||
sketch002 = startSketchOn(extrude001, seg01)`
|
||||
)
|
||||
})
|
||||
|
||||
await homePage.openProject('import-test')
|
||||
await scene.waitForExecutionDone()
|
||||
|
||||
await scene.moveCameraTo(
|
||||
{
|
||||
x: -114,
|
||||
y: -897,
|
||||
z: 475,
|
||||
},
|
||||
{
|
||||
x: -114,
|
||||
y: -51,
|
||||
z: 83,
|
||||
}
|
||||
)
|
||||
const [_, hoverOverNonImport] = scene.makeMouseHelpers(611, 364)
|
||||
const [importedFaceClick, hoverOverImported] = scene.makeMouseHelpers(
|
||||
940,
|
||||
150
|
||||
)
|
||||
|
||||
await test.step('check code highlight works for code define in the file being edited', async () => {
|
||||
await hoverOverNonImport()
|
||||
await editor.expectState({
|
||||
highlightedCode: 'startProfileAt([-134.53,-56.17],sketch001)',
|
||||
diagnostics: [],
|
||||
activeLines: ['import"toBeImported.kcl"asimportedCube'],
|
||||
})
|
||||
})
|
||||
|
||||
await test.step('check code does nothing when geometry is defined in an import', async () => {
|
||||
await hoverOverImported()
|
||||
await editor.expectState({
|
||||
highlightedCode: '',
|
||||
diagnostics: [],
|
||||
activeLines: ['import"toBeImported.kcl"asimportedCube'],
|
||||
})
|
||||
})
|
||||
|
||||
await test.step('check the user is warned when sketching on a imported face', async () => {
|
||||
// Start sketch mode
|
||||
await toolbar.startSketchPlaneSelection()
|
||||
|
||||
// Click on a face from the imported model
|
||||
// await new Promise(() => {})
|
||||
await importedFaceClick()
|
||||
|
||||
// Verify toast appears with correct content
|
||||
await expect(page.getByText('This face is from an import')).toBeVisible()
|
||||
await expect(
|
||||
page.locator('.font-mono').getByText('toBeImported.kcl')
|
||||
).toBeVisible()
|
||||
await expect(
|
||||
page.getByText('Please select this from the files pane to edit')
|
||||
).toBeVisible()
|
||||
})
|
||||
})
|
||||
})
|
@ -258,6 +258,14 @@ export const isErrorWhitelisted = (exception: Error) => {
|
||||
foundInSpec: 'e2e/playwright/testing-settings.spec.ts',
|
||||
},
|
||||
// TODO: fix this error in the code
|
||||
{
|
||||
name: 'TypeError',
|
||||
message: "Cannot read properties of undefined (reading 'length')",
|
||||
stack: '',
|
||||
project: 'Google Chrome',
|
||||
foundInSpec: '', // many tests are impacted by this error
|
||||
},
|
||||
// TODO: fix this error in the code
|
||||
{
|
||||
name: 'ReferenceError',
|
||||
message: '_testUtils is not defined',
|
||||
@ -265,6 +273,7 @@ export const isErrorWhitelisted = (exception: Error) => {
|
||||
project: 'Google Chrome',
|
||||
foundInSpec: 'e2e/playwright/snapshot-tests.spec.ts',
|
||||
},
|
||||
// TODO: fix this error in the code
|
||||
{
|
||||
name: 'TypeError',
|
||||
message: 'Failed to fetch',
|
||||
@ -272,13 +281,21 @@ export const isErrorWhitelisted = (exception: Error) => {
|
||||
project: 'Google Chrome',
|
||||
foundInSpec: 'e2e/playwright/snapshot-tests.spec.ts',
|
||||
},
|
||||
// TODO: fix this error in the code
|
||||
{
|
||||
name: 'Error',
|
||||
message: 'The "path" argument must be of type string. Received undefined',
|
||||
stack:
|
||||
'Error: The "path" argument must be of type string. Received undefined',
|
||||
name: 'ReferenceError',
|
||||
message: 'originalCode is not defined',
|
||||
stack: '',
|
||||
project: 'Google Chrome',
|
||||
foundInSpec: '', // many tests are impacted by this error
|
||||
foundInSpec: 'e2e/playwright/onboarding-tests.spec.ts',
|
||||
},
|
||||
// TODO: fix this error in the code
|
||||
{
|
||||
name: 'ReferenceError',
|
||||
message: 'createNewVariableCheckbox is not defined',
|
||||
stack: '',
|
||||
project: 'Google Chrome',
|
||||
foundInSpec: 'e2e/playwright/testing-constraints.spec.ts',
|
||||
},
|
||||
]
|
||||
|
||||
|
@ -1,7 +0,0 @@
|
||||
export const throwError = (message: string): never => {
|
||||
throw new Error(message)
|
||||
}
|
||||
|
||||
export const throwTronAppMissing = () => {
|
||||
throwError('tronApp is missing')
|
||||
}
|
@ -1,8 +1,7 @@
|
||||
import fsp from 'fs/promises'
|
||||
import { test, expect } from './zoo-test'
|
||||
import { executorInputPath } from './test-utils'
|
||||
import { join } from 'path'
|
||||
|
||||
import { executorInputPath } from '@e2e/playwright/test-utils'
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
import fsp from 'fs/promises'
|
||||
|
||||
test(
|
||||
'When machine-api server not found butt is disabled and shows the reason',
|
||||
@ -12,7 +11,7 @@ test(
|
||||
const bracketDir = join(dir, 'bracket')
|
||||
await fsp.mkdir(bracketDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('cylinder-inches.kcl'),
|
||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||
join(bracketDir, 'main.kcl')
|
||||
)
|
||||
})
|
||||
@ -52,7 +51,7 @@ test(
|
||||
const bracketDir = join(dir, 'bracket')
|
||||
await fsp.mkdir(bracketDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('cylinder-inches.kcl'),
|
||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||
join(bracketDir, 'main.kcl')
|
||||
)
|
||||
})
|
||||
|
@ -1,15 +1,13 @@
|
||||
import { PROJECT_SETTINGS_FILE_NAME } from '@src/lib/constants'
|
||||
import { test, expect } from './zoo-test'
|
||||
import { PROJECT_SETTINGS_FILE_NAME } from 'lib/constants'
|
||||
import * as fsp from 'fs/promises'
|
||||
import { join } from 'path'
|
||||
|
||||
import type { NamedView } from '@rust/kcl-lib/bindings/NamedView'
|
||||
|
||||
import {
|
||||
createProject,
|
||||
perProjectsettingsToToml,
|
||||
tomlToPerProjectSettings,
|
||||
} from '@e2e/playwright/test-utils'
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
perProjectsettingsToToml,
|
||||
} from './test-utils'
|
||||
import { NamedView } from '@rust/kcl-lib/bindings/NamedView'
|
||||
|
||||
// Helper function to determine if the file path on disk exists
|
||||
// Specifically this is used to check if project.toml exists on disk
|
||||
|
@ -2,7 +2,8 @@
|
||||
// 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 { expect, test } from '@e2e/playwright/zoo-test'
|
||||
|
||||
import { test, expect } from './zoo-test'
|
||||
|
||||
test.describe('Open the application', () => {
|
||||
test('see the project view', async ({ page, context }) => {
|
||||
|
@ -1,23 +1,22 @@
|
||||
import { bracket } from '@src/lib/exampleKcl'
|
||||
import { onboardingPaths } from '@src/routes/Onboarding/paths'
|
||||
import fsp from 'fs/promises'
|
||||
import { test, expect } from './zoo-test'
|
||||
import { join } from 'path'
|
||||
|
||||
import { expectPixelColor } from '@e2e/playwright/fixtures/sceneFixture'
|
||||
import fsp from 'fs/promises'
|
||||
import {
|
||||
getUtils,
|
||||
executorInputPath,
|
||||
createProject,
|
||||
settingsToToml,
|
||||
orRunWhenFullSuiteEnabled,
|
||||
} from './test-utils'
|
||||
import { bracket } from 'lib/exampleKcl'
|
||||
import { onboardingPaths } from 'routes/Onboarding/paths'
|
||||
import {
|
||||
TEST_SETTINGS_KEY,
|
||||
TEST_SETTINGS_ONBOARDING_EXPORT,
|
||||
TEST_SETTINGS_ONBOARDING_START,
|
||||
TEST_SETTINGS_ONBOARDING_EXPORT,
|
||||
TEST_SETTINGS_ONBOARDING_USER_MENU,
|
||||
} from '@e2e/playwright/storageStates'
|
||||
import {
|
||||
createProject,
|
||||
executorInputPath,
|
||||
getUtils,
|
||||
orRunWhenFullSuiteEnabled,
|
||||
settingsToToml,
|
||||
} from '@e2e/playwright/test-utils'
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
} from './storageStates'
|
||||
import { expectPixelColor } from './fixtures/sceneFixture'
|
||||
|
||||
// Because our default test settings have the onboardingStatus set to 'dismissed',
|
||||
// we must set it to empty for the tests where we want to see the onboarding immediately.
|
||||
@ -42,10 +41,10 @@ test.describe('Onboarding tests', () => {
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// Test that the onboarding pane loaded
|
||||
await expect(page.getByText('Welcome to Design Studio! This')).toBeVisible()
|
||||
await expect(page.getByText('Welcome to Modeling App! This')).toBeVisible()
|
||||
|
||||
// Test that the onboarding pane loaded
|
||||
await expect(page.getByText('Welcome to Design Studio! This')).toBeVisible()
|
||||
await expect(page.getByText('Welcome to Modeling App! This')).toBeVisible()
|
||||
|
||||
// *and* that the code is shown in the editor
|
||||
await expect(page.locator('.cm-content')).toContainText('// Shelf Bracket')
|
||||
@ -86,7 +85,7 @@ test.describe('Onboarding tests', () => {
|
||||
await test.step(`Ensure we see the onboarding stuff`, async () => {
|
||||
// Test that the onboarding pane loaded
|
||||
await expect(
|
||||
page.getByText('Welcome to Design Studio! This')
|
||||
page.getByText('Welcome to Modeling App! This')
|
||||
).toBeVisible()
|
||||
|
||||
// *and* that the code is shown in the editor
|
||||
@ -147,7 +146,7 @@ test.describe('Onboarding tests', () => {
|
||||
await nextButton.click()
|
||||
|
||||
// Ensure we see the introduction and that the code has been reset
|
||||
await expect(page.getByText('Welcome to Design Studio!')).toBeVisible()
|
||||
await expect(page.getByText('Welcome to Modeling App!')).toBeVisible()
|
||||
await expect(page.locator('.cm-content')).toContainText('// Shelf Bracket')
|
||||
|
||||
// There used to be old code here that checked if we stored the reset
|
||||
@ -188,7 +187,7 @@ test.describe('Onboarding tests', () => {
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// Test that the onboarding pane loaded
|
||||
await expect(page.getByText('Welcome to Design Studio! This')).toBeVisible()
|
||||
await expect(page.getByText('Welcome to Modeling App! This')).toBeVisible()
|
||||
|
||||
const nextButton = page.getByTestId('onboarding-next')
|
||||
const prevButton = page.getByTestId('onboarding-prev')
|
||||
@ -231,9 +230,9 @@ test.describe('Onboarding tests', () => {
|
||||
|
||||
// Override beforeEach test setup
|
||||
await context.addInitScript(
|
||||
async ({ settingsKey, settings, code }) => {
|
||||
async ({ settingsKey, settings }) => {
|
||||
// Give some initial code, so we can test that it's cleared
|
||||
localStorage.setItem('persistCode', code)
|
||||
localStorage.setItem('persistCode', originalCode)
|
||||
localStorage.setItem(settingsKey, settings)
|
||||
},
|
||||
{
|
||||
@ -241,7 +240,6 @@ test.describe('Onboarding tests', () => {
|
||||
settings: settingsToToml({
|
||||
settings: TEST_SETTINGS_ONBOARDING_EXPORT,
|
||||
}),
|
||||
code: originalCode,
|
||||
}
|
||||
)
|
||||
|
||||
@ -494,7 +492,7 @@ test('Restarting onboarding on desktop takes one attempt', async ({
|
||||
const tutorialProjectIndicator = page
|
||||
.getByTestId('project-sidebar-toggle')
|
||||
.filter({ hasText: 'Tutorial Project 00' })
|
||||
const tutorialModalText = page.getByText('Welcome to Design Studio!')
|
||||
const tutorialModalText = page.getByText('Welcome to Modeling App!')
|
||||
const tutorialDismissButton = page.getByRole('button', { name: 'Dismiss' })
|
||||
const userMenuButton = page.getByTestId('user-sidebar-toggle')
|
||||
const userMenuSettingsButton = page.getByRole('button', {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { expect, test } from '@playwright/test'
|
||||
import { test, expect } from '@playwright/test'
|
||||
|
||||
/** @deprecated, import from ./fixtureSetup.ts instead */
|
||||
export const _test = test
|
||||
|
@ -1,12 +1,12 @@
|
||||
import type { Locator, Page } from '@playwright/test'
|
||||
import { Page } from '@playwright/test'
|
||||
import { test, expect } from './zoo-test'
|
||||
import { EditorFixture } from './fixtures/editorFixture'
|
||||
import { SceneFixture } from './fixtures/sceneFixture'
|
||||
import { ToolbarFixture } from './fixtures/toolbarFixture'
|
||||
import fs from 'node:fs/promises'
|
||||
import path from 'node:path'
|
||||
|
||||
import type { EditorFixture } from '@e2e/playwright/fixtures/editorFixture'
|
||||
import type { SceneFixture } from '@e2e/playwright/fixtures/sceneFixture'
|
||||
import type { ToolbarFixture } from '@e2e/playwright/fixtures/toolbarFixture'
|
||||
import { getUtils, orRunWhenFullSuiteEnabled } from '@e2e/playwright/test-utils'
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
import { getUtils, orRunWhenFullSuiteEnabled } from './test-utils'
|
||||
import { Locator } from '@playwright/test'
|
||||
|
||||
// test file is for testing point an click code gen functionality that's not sketch mode related
|
||||
|
||||
@ -137,7 +137,7 @@ test.describe('Point-and-click tests', () => {
|
||||
|
||||
await scene.moveCameraTo(cameraPos, cameraTarget)
|
||||
|
||||
await test.step('check chamfer selection changes cursor position', async () => {
|
||||
await test.step('check chamfer selection changes cursor positon', async () => {
|
||||
await expect(async () => {
|
||||
// sometimes initial click doesn't register
|
||||
await clickChamfer()
|
||||
@ -173,7 +173,7 @@ test.describe('Point-and-click tests', () => {
|
||||
})
|
||||
await test.step('Check there is no errors after code created in previous steps executes', async () => {
|
||||
await editor.expectState({
|
||||
activeLines: ['@settings(defaultLengthUnit = in)'],
|
||||
activeLines: ['sketch001 = startSketchOn(XZ)'],
|
||||
highlightedCode: '',
|
||||
diagnostics: [],
|
||||
})
|
||||
@ -299,8 +299,7 @@ test.describe('Point-and-click tests', () => {
|
||||
|
||||
await test.step('verify at the end of the test that final code is what is expected', async () => {
|
||||
await editor.expectEditor.toContain(
|
||||
`@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XZ)
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
|> startProfileAt([75.8, 317.2], %) // [$startCapTag, $EndCapTag]
|
||||
|> angledLine([0, 268.43], %, $rectangleSegmentA001)
|
||||
|> angledLine([
|
||||
@ -370,7 +369,7 @@ profile001 = startProfileAt([205.96, 254.59], sketch002)
|
||||
})
|
||||
})
|
||||
|
||||
test('Works on chamfers that are not in a pipeExpression can break up multi edges in a chamfer array', async ({
|
||||
test('Works on chamfers that are non in a pipeExpression can break up multi edges in a chamfer array', async ({
|
||||
context,
|
||||
page,
|
||||
homePage,
|
||||
@ -419,8 +418,7 @@ profile001 = startProfileAt([205.96, 254.59], sketch002)
|
||||
|>close()`,
|
||||
})
|
||||
await editor.expectEditor.toContain(
|
||||
`@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XZ)
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
|> startProfileAt([75.8, 317.2], %)
|
||||
|> angledLine([0, 268.43], %, $rectangleSegmentA001)
|
||||
|> angledLine([
|
||||
@ -1084,8 +1082,8 @@ openSketch = startSketchOn(XY)
|
||||
}) => {
|
||||
// One dumb hardcoded screen pixel value
|
||||
const testPoint = { x: 620, y: 257 }
|
||||
const expectedOutput = `helix001 = helix( axis = X, radius = 5, length = 5, revolutions = 1, angleStart = 360, ccw = false,)`
|
||||
const expectedLine = `axis=X,`
|
||||
const expectedOutput = `helix001 = helix( axis = 'X', radius = 5, length = 5, revolutions = 1, angleStart = 360, ccw = false,)`
|
||||
const expectedLine = `axis='X',`
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
@ -1099,6 +1097,7 @@ openSketch = startSketchOn(XY)
|
||||
Mode: '',
|
||||
AngleStart: '',
|
||||
Revolutions: '',
|
||||
Length: '',
|
||||
Radius: '',
|
||||
CounterClockWise: '',
|
||||
},
|
||||
@ -1195,14 +1194,14 @@ openSketch = startSketchOn(XY)
|
||||
{
|
||||
selectionType: 'segment',
|
||||
testPoint: { x: 513, y: 221 },
|
||||
expectedOutput: `helix001 = helix( axis = seg01, radius = 1, revolutions = 20, angleStart = 0, ccw = false,)`,
|
||||
expectedEditedOutput: `helix001 = helix( axis = seg01, radius = 5, revolutions = 20, angleStart = 0, ccw = false,)`,
|
||||
expectedOutput: `helix001 = helix( axis = seg01, radius = 1, length = 100, revolutions = 20, angleStart = 0, ccw = false,)`,
|
||||
expectedEditedOutput: `helix001 = helix( axis = seg01, radius = 1, length = 50, revolutions = 20, angleStart = 0, ccw = false,)`,
|
||||
},
|
||||
{
|
||||
selectionType: 'sweepEdge',
|
||||
testPoint: { x: 564, y: 364 },
|
||||
expectedOutput: `helix001 = helix( axis = getOppositeEdge(seg01), radius = 1, revolutions = 20, angleStart = 0, ccw = false,)`,
|
||||
expectedEditedOutput: `helix001 = helix( axis = getOppositeEdge(seg01), radius = 5, revolutions = 20, angleStart = 0, ccw = false,)`,
|
||||
expectedOutput: `helix001 = helix( axis = getOppositeEdge(seg01), radius = 1, length = 100, revolutions = 20, angleStart = 0, ccw = false,)`,
|
||||
expectedEditedOutput: `helix001 = helix( axis = getOppositeEdge(seg01), radius = 1, length = 50, revolutions = 20, angleStart = 0, ccw = false,)`,
|
||||
},
|
||||
]
|
||||
helixCases.map(
|
||||
@ -1217,7 +1216,7 @@ openSketch = startSketchOn(XY)
|
||||
cmdBar,
|
||||
}) => {
|
||||
page.on('console', console.log)
|
||||
const initialCode = `sketch001 = startSketchOn(XZ)
|
||||
const initialCode = `sketch001 = startSketchOn('XZ')
|
||||
profile001 = startProfileAt([0, 0], sketch001)
|
||||
|> yLine(length = 100)
|
||||
|> line(endAbsolute = [100, 0])
|
||||
@ -1245,6 +1244,7 @@ openSketch = startSketchOn(XY)
|
||||
AngleStart: '',
|
||||
Mode: '',
|
||||
CounterClockWise: '',
|
||||
Length: '',
|
||||
Radius: '',
|
||||
Revolutions: '',
|
||||
},
|
||||
@ -1261,6 +1261,8 @@ openSketch = startSketchOn(XY)
|
||||
await cmdBar.progressCmdBar()
|
||||
await page.keyboard.insertText('1')
|
||||
await cmdBar.progressCmdBar()
|
||||
await page.keyboard.insertText('100')
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.expectState({
|
||||
stage: 'review',
|
||||
headerArguments: {
|
||||
@ -1269,6 +1271,7 @@ openSketch = startSketchOn(XY)
|
||||
AngleStart: '0',
|
||||
Revolutions: '20',
|
||||
Radius: '1',
|
||||
Length: '100',
|
||||
CounterClockWise: '',
|
||||
},
|
||||
commandName: 'Helix',
|
||||
@ -1289,8 +1292,8 @@ openSketch = startSketchOn(XY)
|
||||
0
|
||||
)
|
||||
await operationButton.dblclick()
|
||||
const initialInput = '1'
|
||||
const newInput = '5'
|
||||
const initialInput = '100'
|
||||
const newInput = '50'
|
||||
await cmdBar.expectState({
|
||||
commandName: 'Helix',
|
||||
stage: 'arguments',
|
||||
@ -1299,14 +1302,13 @@ openSketch = startSketchOn(XY)
|
||||
headerArguments: {
|
||||
AngleStart: '0',
|
||||
Revolutions: '20',
|
||||
Radius: initialInput,
|
||||
Radius: '1',
|
||||
Length: initialInput,
|
||||
CounterClockWise: '',
|
||||
},
|
||||
highlightedHeaderArg: 'CounterClockWise',
|
||||
})
|
||||
await page
|
||||
.getByRole('button', { name: 'radius', exact: false })
|
||||
.click()
|
||||
await page.keyboard.press('Shift+Backspace')
|
||||
await expect(cmdBar.currentArgumentInput).toBeVisible()
|
||||
await cmdBar.currentArgumentInput
|
||||
.locator('.cm-content')
|
||||
@ -1317,7 +1319,8 @@ openSketch = startSketchOn(XY)
|
||||
headerArguments: {
|
||||
AngleStart: '0',
|
||||
Revolutions: '20',
|
||||
Radius: newInput,
|
||||
Radius: '1',
|
||||
Length: newInput,
|
||||
CounterClockWise: '',
|
||||
},
|
||||
commandName: 'Helix',
|
||||
@ -1388,6 +1391,7 @@ extrude001 = extrude(profile001, length = 100)
|
||||
Mode: '',
|
||||
AngleStart: '',
|
||||
Revolutions: '',
|
||||
Length: '',
|
||||
Radius: '',
|
||||
CounterClockWise: '',
|
||||
},
|
||||
@ -1641,10 +1645,9 @@ loft001 = loft([sketch001, sketch002])
|
||||
{
|
||||
targetType: 'circle',
|
||||
testPoint: { x: 700, y: 250 },
|
||||
initialCode: `@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(YZ)
|
||||
initialCode: `sketch001 = startSketchOn('YZ')
|
||||
profile001 = circle(sketch001, center = [0, 0], radius = 500)
|
||||
sketch002 = startSketchOn(XZ)
|
||||
sketch002 = startSketchOn('XZ')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> xLine(length = -500)
|
||||
|> tangentialArcTo([-2000, 500], %)`,
|
||||
@ -1652,8 +1655,7 @@ sketch002 = startSketchOn(XZ)
|
||||
{
|
||||
targetType: 'rectangle',
|
||||
testPoint: { x: 710, y: 255 },
|
||||
initialCode: `@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(YZ)
|
||||
initialCode: `sketch001 = startSketchOn('YZ')
|
||||
profile001 = startProfileAt([-400, -400], sketch001)
|
||||
|> angledLine([0, 800], %, $rectangleSegmentA001)
|
||||
|> angledLine([
|
||||
@ -1666,7 +1668,7 @@ profile001 = startProfileAt([-400, -400], sketch001)
|
||||
], %)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
sketch002 = startSketchOn(XZ)
|
||||
sketch002 = startSketchOn('XZ')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> xLine(length = -500)
|
||||
|> tangentialArcTo([-2000, 500], %)`,
|
||||
@ -1810,8 +1812,7 @@ sketch002 = startSketchOn(XZ)
|
||||
toolbar,
|
||||
cmdBar,
|
||||
}) => {
|
||||
const initialCode = `@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(YZ)
|
||||
const initialCode = `sketch001 = startSketchOn(YZ)
|
||||
|> circle(
|
||||
center = [0, 0],
|
||||
radius = 500
|
||||
@ -2480,8 +2481,7 @@ extrude001 = extrude(profile001, length = 5)
|
||||
cmdBar,
|
||||
}) => {
|
||||
// Code samples
|
||||
const initialCode = `@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XY)
|
||||
const initialCode = `sketch001 = startSketchOn(XY)
|
||||
|> startProfileAt([-12, -6], %)
|
||||
|> line(end = [0, 12])
|
||||
|> line(end = [24, 0])
|
||||
@ -2773,8 +2773,7 @@ extrude001 = extrude(sketch001, length = -12)
|
||||
toolbar,
|
||||
}) => {
|
||||
// Code samples
|
||||
const initialCode = `@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XY)
|
||||
const initialCode = `sketch001 = startSketchOn(XY)
|
||||
|> startProfileAt([-12, -6], %)
|
||||
|> line(end = [0, 12])
|
||||
|> line(end = [24, 0], tag = $seg02)
|
||||
@ -2928,8 +2927,7 @@ chamfer04 = chamfer(extrude001, length = 5, tags = [getOppositeEdge(seg02)])
|
||||
toolbar,
|
||||
cmdBar,
|
||||
}) => {
|
||||
const initialCode = `@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XZ)
|
||||
const initialCode = `sketch001 = startSketchOn(XZ)
|
||||
|> circle(center = [0, 0], radius = 30)
|
||||
extrude001 = extrude(sketch001, length = 30)
|
||||
`
|
||||
@ -3064,8 +3062,7 @@ extrude001 = extrude(sketch001, length = 30)
|
||||
toolbar,
|
||||
cmdBar,
|
||||
}) => {
|
||||
const initialCode = `@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XY)
|
||||
const initialCode = `sketch001 = startSketchOn(XY)
|
||||
|> startProfileAt([-20, 20], %)
|
||||
|> xLine(length = 40)
|
||||
|> yLine(length = -60)
|
||||
@ -3183,8 +3180,7 @@ extrude001 = extrude(sketch001, length = 40)
|
||||
})
|
||||
|
||||
const shellSketchOnFacesCases = [
|
||||
`@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XZ)
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
|> circle(center = [0, 0], radius = 100)
|
||||
|> extrude(length = 100)
|
||||
|
||||
@ -3192,8 +3188,7 @@ sketch002 = startSketchOn(sketch001, 'END')
|
||||
|> circle(center = [0, 0], radius = 50)
|
||||
|> extrude(length = 50)
|
||||
`,
|
||||
`@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XZ)
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
|> circle(center = [0, 0], radius = 100)
|
||||
extrude001 = extrude(sketch001, length = 100)
|
||||
|
||||
@ -3476,39 +3471,6 @@ segAng(rectangleSegmentA002),
|
||||
|
||||
const newCodeToFind = `revolve001 = revolve(sketch002, angle = 360, axis = X)`
|
||||
expect(editor.expectEditor.toContain(newCodeToFind)).toBeTruthy()
|
||||
|
||||
// Edit flow
|
||||
const newAngle = '90'
|
||||
await toolbar.openPane('feature-tree')
|
||||
const operationButton = await toolbar.getFeatureTreeOperation(
|
||||
'Revolve',
|
||||
0
|
||||
)
|
||||
await operationButton.dblclick({ button: 'left' })
|
||||
await cmdBar.expectState({
|
||||
commandName: 'Revolve',
|
||||
currentArgKey: 'angle',
|
||||
currentArgValue: '360',
|
||||
headerArguments: {
|
||||
Angle: '360',
|
||||
},
|
||||
highlightedHeaderArg: 'angle',
|
||||
stage: 'arguments',
|
||||
})
|
||||
await page.keyboard.insertText(newAngle)
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.expectState({
|
||||
stage: 'review',
|
||||
headerArguments: {
|
||||
Angle: newAngle,
|
||||
},
|
||||
commandName: 'Revolve',
|
||||
})
|
||||
await cmdBar.progressCmdBar()
|
||||
await toolbar.closePane('feature-tree')
|
||||
await editor.expectEditor.toContain(
|
||||
newCodeToFind.replace('angle = 360', 'angle = ' + newAngle)
|
||||
)
|
||||
})
|
||||
test('revolve surface around edge from an extruded solid2d', async ({
|
||||
context,
|
||||
@ -3519,22 +3481,26 @@ segAng(rectangleSegmentA002),
|
||||
toolbar,
|
||||
cmdBar,
|
||||
}) => {
|
||||
const initialCode = `sketch001 = startSketchOn(XZ)
|
||||
|> startProfileAt([-102.57, 101.72], %)
|
||||
|> angledLine([0, 202.6], %, $rectangleSegmentA001)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001) - 90,
|
||||
202.6
|
||||
], %, $rectangleSegmentB001)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001),
|
||||
-segLen(rectangleSegmentA001)
|
||||
], %, $rectangleSegmentC001)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
const initialCode = `
|
||||
sketch001 = startSketchOn(XZ)
|
||||
|> startProfileAt([-102.57, 101.72], %)
|
||||
|> angledLine([0, 202.6], %, $rectangleSegmentA001)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001) - 90,
|
||||
202.6
|
||||
], %, $rectangleSegmentB001)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001),
|
||||
-segLen(rectangleSegmentA001)
|
||||
], %, $rectangleSegmentC001)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
extrude001 = extrude(sketch001, length = 50)
|
||||
sketch002 = startSketchOn(extrude001, rectangleSegmentA001)
|
||||
|> circle(center = [-11.34, 10.0], radius = 8.69)
|
||||
|> circle(
|
||||
center = [-11.34, 10.0],
|
||||
radius = 8.69
|
||||
)
|
||||
`
|
||||
await context.addInitScript((initialCode) => {
|
||||
localStorage.setItem('persistCode', initialCode)
|
||||
@ -3552,49 +3518,9 @@ sketch002 = startSketchOn(extrude001, rectangleSegmentA001)
|
||||
const lineCodeToSelection = `|> angledLine([0, 202.6], %, $rectangleSegmentA001)`
|
||||
await page.getByText(lineCodeToSelection).click()
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.progressCmdBar()
|
||||
|
||||
const newCodeToFind = `revolve001 = revolve(sketch002, angle = 360, axis = rectangleSegmentA001)`
|
||||
await editor.expectEditor.toContain(newCodeToFind)
|
||||
|
||||
// Edit flow
|
||||
const newAngle = '180'
|
||||
await toolbar.openPane('feature-tree')
|
||||
const operationButton = await toolbar.getFeatureTreeOperation(
|
||||
'Revolve',
|
||||
0
|
||||
)
|
||||
await operationButton.dblclick({ button: 'left' })
|
||||
await cmdBar.expectState({
|
||||
commandName: 'Revolve',
|
||||
currentArgKey: 'angle',
|
||||
currentArgValue: '360',
|
||||
headerArguments: {
|
||||
Angle: '360',
|
||||
},
|
||||
highlightedHeaderArg: 'angle',
|
||||
stage: 'arguments',
|
||||
})
|
||||
await page.keyboard.insertText(newAngle)
|
||||
await page.getByRole('button', { name: 'Create new variable' }).click()
|
||||
await expect(page.getByPlaceholder('Variable name')).toHaveValue(
|
||||
'angle001'
|
||||
)
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.expectState({
|
||||
stage: 'review',
|
||||
headerArguments: {
|
||||
Angle: newAngle,
|
||||
},
|
||||
commandName: 'Revolve',
|
||||
})
|
||||
await cmdBar.progressCmdBar()
|
||||
await toolbar.closePane('feature-tree')
|
||||
await editor.expectEditor.toContain('angle001 = ' + newAngle)
|
||||
await editor.expectEditor.toContain(
|
||||
newCodeToFind.replace('angle = 360', 'angle = angle001')
|
||||
)
|
||||
const newCodeToFind = `revolve001 = revolve(sketch002, angle = 360, axis = getOppositeEdge(rectangleSegmentA001)) `
|
||||
expect(editor.expectEditor.toContain(newCodeToFind)).toBeTruthy()
|
||||
})
|
||||
test('revolve sketch circle around line segment from startProfileAt sketch', async ({
|
||||
context,
|
||||
@ -3605,22 +3531,26 @@ sketch002 = startSketchOn(extrude001, rectangleSegmentA001)
|
||||
toolbar,
|
||||
cmdBar,
|
||||
}) => {
|
||||
const initialCode = `sketch002 = startSketchOn(XY)
|
||||
|> startProfileAt([-2.02, 1.79], %)
|
||||
|> xLine(length = 2.6)
|
||||
sketch001 = startSketchOn(-XY)
|
||||
|> startProfileAt([-0.48, 1.25], %)
|
||||
|> angledLine([0, 2.38], %, $rectangleSegmentA001)
|
||||
|> angledLine([segAng(rectangleSegmentA001) - 90, 2.4], %, $rectangleSegmentB001)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001),
|
||||
-segLen(rectangleSegmentA001)
|
||||
], %, $rectangleSegmentC001)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
extrude001 = extrude(sketch001, length = 5)
|
||||
sketch003 = startSketchOn(extrude001, 'START')
|
||||
|> circle(center = [-0.69, 0.56], radius = 0.28)
|
||||
const initialCode = `
|
||||
sketch002 = startSketchOn(XY)
|
||||
|> startProfileAt([-2.02, 1.79], %)
|
||||
|> xLine(length = 2.6)
|
||||
sketch001 = startSketchOn('-XY')
|
||||
|> startProfileAt([-0.48, 1.25], %)
|
||||
|> angledLine([0, 2.38], %, $rectangleSegmentA001)
|
||||
|> angledLine([segAng(rectangleSegmentA001) - 90, 2.4], %, $rectangleSegmentB001)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001),
|
||||
-segLen(rectangleSegmentA001)
|
||||
], %, $rectangleSegmentC001)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
extrude001 = extrude(sketch001, length = 5)
|
||||
sketch003 = startSketchOn(extrude001, 'START')
|
||||
|> circle(
|
||||
center = [-0.69, 0.56],
|
||||
radius = 0.28
|
||||
)
|
||||
`
|
||||
|
||||
await context.addInitScript((initialCode) => {
|
||||
@ -3639,44 +3569,9 @@ sketch003 = startSketchOn(extrude001, 'START')
|
||||
const lineCodeToSelection = `|> xLine(length = 2.6)`
|
||||
await page.getByText(lineCodeToSelection).click()
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.progressCmdBar()
|
||||
|
||||
const newCodeToFind = `revolve001 = revolve(sketch003, angle = 360, axis = seg01)`
|
||||
expect(editor.expectEditor.toContain(newCodeToFind)).toBeTruthy()
|
||||
|
||||
// Edit flow
|
||||
const newAngle = '270'
|
||||
await toolbar.openPane('feature-tree')
|
||||
const operationButton = await toolbar.getFeatureTreeOperation(
|
||||
'Revolve',
|
||||
0
|
||||
)
|
||||
await operationButton.dblclick({ button: 'left' })
|
||||
await cmdBar.expectState({
|
||||
commandName: 'Revolve',
|
||||
currentArgKey: 'angle',
|
||||
currentArgValue: '360',
|
||||
headerArguments: {
|
||||
Angle: '360',
|
||||
},
|
||||
highlightedHeaderArg: 'angle',
|
||||
stage: 'arguments',
|
||||
})
|
||||
await page.keyboard.insertText(newAngle)
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.expectState({
|
||||
stage: 'review',
|
||||
headerArguments: {
|
||||
Angle: newAngle,
|
||||
},
|
||||
commandName: 'Revolve',
|
||||
})
|
||||
await cmdBar.progressCmdBar()
|
||||
await toolbar.closePane('feature-tree')
|
||||
await editor.expectEditor.toContain(
|
||||
newCodeToFind.replace('angle = 360', 'angle = ' + newAngle)
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@ -3689,8 +3584,7 @@ sketch003 = startSketchOn(extrude001, 'START')
|
||||
toolbar,
|
||||
cmdBar,
|
||||
}) => {
|
||||
const initialCode = `@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XZ)
|
||||
const initialCode = `sketch001 = startSketchOn(XZ)
|
||||
profile001 = circle(
|
||||
sketch001,
|
||||
center = [0, 0],
|
||||
|
@ -1,20 +1,19 @@
|
||||
import { DEFAULT_PROJECT_KCL_FILE } from '@src/lib/constants'
|
||||
import fs from 'fs'
|
||||
import fsp from 'fs/promises'
|
||||
import path from 'path'
|
||||
|
||||
import type { Paths } from '@e2e/playwright/test-utils'
|
||||
import { test, expect } from './zoo-test'
|
||||
import {
|
||||
createProject,
|
||||
doExport,
|
||||
executorInputPath,
|
||||
getPlaywrightDownloadDir,
|
||||
getUtils,
|
||||
isOutOfViewInScrollContainer,
|
||||
Paths,
|
||||
createProject,
|
||||
getPlaywrightDownloadDir,
|
||||
orRunWhenFullSuiteEnabled,
|
||||
runningOnWindows,
|
||||
} from '@e2e/playwright/test-utils'
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
} from './test-utils'
|
||||
import fsp from 'fs/promises'
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { DEFAULT_PROJECT_KCL_FILE } from 'lib/constants'
|
||||
|
||||
test(
|
||||
'projects reload if a new one is created, deleted, or renamed externally',
|
||||
@ -88,7 +87,7 @@ test(
|
||||
const bracketDir = path.join(dir, 'bracket')
|
||||
await fsp.mkdir(bracketDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('cylinder-inches.kcl'),
|
||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||
path.join(bracketDir, 'main.kcl')
|
||||
)
|
||||
})
|
||||
@ -125,7 +124,7 @@ test(
|
||||
const bracketDir = path.join(dir, 'bracket')
|
||||
await fsp.mkdir(bracketDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('cylinder-inches.kcl'),
|
||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||
path.join(bracketDir, 'main.kcl')
|
||||
)
|
||||
const errorDir = path.join(dir, 'broken-code')
|
||||
@ -163,7 +162,7 @@ test(
|
||||
// gray at this pixel means the stream has loaded in the most
|
||||
// user way we can verify it (pixel color)
|
||||
await expect
|
||||
.poll(() => u.getGreatestPixDiff(pointOnModel, [110, 110, 110]), {
|
||||
.poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
|
||||
timeout: 10_000,
|
||||
})
|
||||
.toBeLessThan(20)
|
||||
@ -214,7 +213,7 @@ test(
|
||||
const bracketDir = path.join(dir, 'bracket')
|
||||
await fsp.mkdir(bracketDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('cylinder-inches.kcl'),
|
||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||
path.join(bracketDir, 'main.kcl')
|
||||
)
|
||||
const emptyDir = path.join(dir, 'empty')
|
||||
@ -249,7 +248,7 @@ test(
|
||||
// gray at this pixel means the stream has loaded in the most
|
||||
// user way we can verify it (pixel color)
|
||||
await expect
|
||||
.poll(() => u.getGreatestPixDiff(pointOnModel, [125, 125, 125]), {
|
||||
.poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
|
||||
timeout: 10_000,
|
||||
})
|
||||
.toBeLessThan(15)
|
||||
@ -291,7 +290,7 @@ test(
|
||||
const bracketDir = path.join(dir, 'bracket')
|
||||
await fsp.mkdir(bracketDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('cylinder-inches.kcl'),
|
||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||
path.join(bracketDir, 'main.kcl')
|
||||
)
|
||||
|
||||
@ -320,7 +319,7 @@ test(
|
||||
// gray at this pixel means the stream has loaded in the most
|
||||
// user way we can verify it (pixel color)
|
||||
await expect
|
||||
.poll(() => u.getGreatestPixDiff(pointOnModel, [125, 125, 125]), {
|
||||
.poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
|
||||
timeout: 10_000,
|
||||
})
|
||||
.toBeLessThan(15)
|
||||
@ -360,7 +359,7 @@ test(
|
||||
const bracketDir = path.join(dir, 'bracket')
|
||||
await fsp.mkdir(bracketDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('cylinder-inches.kcl'),
|
||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||
path.join(bracketDir, 'main.kcl')
|
||||
)
|
||||
await fsp.copyFile(
|
||||
@ -394,7 +393,7 @@ test(
|
||||
// gray at this pixel means the stream has loaded in the most
|
||||
// user way we can verify it (pixel color)
|
||||
await expect
|
||||
.poll(() => u.getGreatestPixDiff(pointOnModel, [125, 125, 125]), {
|
||||
.poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
|
||||
timeout: 10_000,
|
||||
})
|
||||
.toBeLessThan(15)
|
||||
@ -444,6 +443,7 @@ test(
|
||||
await page.getByText('broken-code').click()
|
||||
|
||||
// Gotcha: You can not use scene.waitForExecutionDone() since the KCL code is going to fail
|
||||
await expect(page.getByTestId('loading')).toBeAttached()
|
||||
await expect(page.getByTestId('loading')).not.toBeAttached({
|
||||
timeout: 20_000,
|
||||
})
|
||||
@ -481,7 +481,7 @@ test.describe('Can export from electron app', () => {
|
||||
const bracketDir = path.join(dir, 'bracket')
|
||||
await fsp.mkdir(bracketDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('cylinder-inches.kcl'),
|
||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||
path.join(bracketDir, 'main.kcl')
|
||||
)
|
||||
})
|
||||
@ -513,7 +513,7 @@ test.describe('Can export from electron app', () => {
|
||||
// gray at this pixel means the stream has loaded in the most
|
||||
// user way we can verify it (pixel color)
|
||||
await expect
|
||||
.poll(() => u.getGreatestPixDiff(pointOnModel, [125, 125, 125]), {
|
||||
.poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
|
||||
timeout: 10_000,
|
||||
})
|
||||
.toBeLessThan(15)
|
||||
@ -554,7 +554,7 @@ test.describe('Can export from electron app', () => {
|
||||
},
|
||||
{ timeout: 15_000 }
|
||||
)
|
||||
.toBeGreaterThan(50_000)
|
||||
.toBeGreaterThan(300_000)
|
||||
|
||||
// clean up exported file
|
||||
await fsp.rm(filepath)
|
||||
@ -828,7 +828,7 @@ test.describe(`Project management commands`, () => {
|
||||
const commandButton = page.getByRole('button', { name: 'Commands' })
|
||||
const commandOption = page.getByRole('option', { name: 'rename project' })
|
||||
const projectNameOption = page.getByRole('option', { name: projectName })
|
||||
const projectRenamedName = `untitled`
|
||||
const projectRenamedName = `project-000`
|
||||
// const projectMenuButton = page.getByTestId('project-sidebar-toggle')
|
||||
const commandContinueButton = page.getByRole('button', {
|
||||
name: 'Continue',
|
||||
@ -941,7 +941,7 @@ test.describe(`Project management commands`, () => {
|
||||
const commandButton = page.getByRole('button', { name: 'Commands' })
|
||||
const commandOption = page.getByRole('option', { name: 'rename project' })
|
||||
const projectNameOption = page.getByRole('option', { name: projectName })
|
||||
const projectRenamedName = `untitled`
|
||||
const projectRenamedName = `project-000`
|
||||
const commandContinueButton = page.getByRole('button', {
|
||||
name: 'Continue',
|
||||
})
|
||||
@ -1139,7 +1139,7 @@ test(`Create a few projects using the default project name`, async ({
|
||||
await test.step(`Create project ${i}`, async () => {
|
||||
await homePage.expectState({
|
||||
projectCards: Array.from({ length: i }, (_, i) => ({
|
||||
title: i === 0 ? 'untitled' : `untitled-${i}`,
|
||||
title: `project-${i.toString().padStart(3, '0')}`,
|
||||
fileCount: 1,
|
||||
})).toReversed(),
|
||||
sortBy: 'last-modified-desc',
|
||||
@ -1323,9 +1323,9 @@ test(
|
||||
})
|
||||
|
||||
await test.step('Check we can still create a project', async () => {
|
||||
await createProject({ name: 'new-project', page, returnHome: true })
|
||||
await createProject({ name: 'project-000', page, returnHome: true })
|
||||
await expect(
|
||||
page.getByTestId('project-link').filter({ hasText: 'new-project' })
|
||||
page.getByTestId('project-link').filter({ hasText: 'project-000' })
|
||||
).toBeVisible()
|
||||
})
|
||||
}
|
||||
@ -1507,12 +1507,7 @@ test(
|
||||
|
||||
await u.waitForPageLoad()
|
||||
|
||||
// The file should be prepopulated with the user's unit settings.
|
||||
await expect(page.locator('.cm-content')).toHaveText(
|
||||
'@settings(defaultLengthUnit = in)'
|
||||
)
|
||||
|
||||
await page.locator('.cm-content').fill(`sketch001 = startSketchOn('XZ')
|
||||
await page.locator('.cm-content').fill(`sketch001 = startSketchOn(XZ)
|
||||
|> startProfileAt([-87.4, 282.92], %)
|
||||
|> line(end = [324.07, 27.199], tag = $seg01)
|
||||
|> line(end = [118.328, -291.754])
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
|
||||
import { test, expect } from './zoo-test'
|
||||
/* eslint-disable jest/no-conditional-expect */
|
||||
|
||||
/**
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { orRunWhenFullSuiteEnabled } from '@e2e/playwright/test-utils'
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
import { test, expect } from './zoo-test'
|
||||
import { orRunWhenFullSuiteEnabled } from './test-utils'
|
||||
|
||||
/* eslint-disable jest/no-conditional-expect */
|
||||
|
||||
|
@ -1,18 +1,17 @@
|
||||
import type { Page } from '@playwright/test'
|
||||
import { bracket } from '@src/lib/exampleKcl'
|
||||
import { reportRejection } from '@src/lib/trap'
|
||||
import * as fsp from 'fs/promises'
|
||||
import { Page } from '@playwright/test'
|
||||
import { test, expect } from './zoo-test'
|
||||
import path from 'path'
|
||||
|
||||
import { TEST_CODE_TRIGGER_ENGINE_EXPORT_ERROR } from '@e2e/playwright/storageStates'
|
||||
import type { TestColor } from '@e2e/playwright/test-utils'
|
||||
import * as fsp from 'fs/promises'
|
||||
import {
|
||||
TEST_COLORS,
|
||||
executorInputPath,
|
||||
getUtils,
|
||||
executorInputPath,
|
||||
TEST_COLORS,
|
||||
TestColor,
|
||||
orRunWhenFullSuiteEnabled,
|
||||
} from '@e2e/playwright/test-utils'
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
} from './test-utils'
|
||||
import { TEST_CODE_TRIGGER_ENGINE_EXPORT_ERROR } from './storageStates'
|
||||
import { bracket } from 'lib/exampleKcl'
|
||||
import { reportRejection } from 'lib/trap'
|
||||
|
||||
test.describe('Regression tests', { tag: ['@skipWin'] }, () => {
|
||||
// bugs we found that don't fit neatly into other categories
|
||||
@ -332,7 +331,7 @@ extrude001 = extrude(sketch001, length = 50)
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`@settings(defaultLengthUnit = mm)
|
||||
sketch002 = startSketchOn(XY)
|
||||
sketch002 = startSketchOn('XY')
|
||||
profile002 = startProfileAt([72.24, -52.05], sketch002)
|
||||
|> angledLine([0, 181.26], %, $rectangleSegmentA001)
|
||||
|> angledLine([
|
||||
@ -583,7 +582,7 @@ extrude002 = extrude(profile002, length = 150)
|
||||
const bracketDir = path.join(dir, 'bracket')
|
||||
await fsp.mkdir(bracketDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('cylinder-inches.kcl'),
|
||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||
path.join(bracketDir, 'main.kcl')
|
||||
)
|
||||
})
|
||||
@ -620,7 +619,6 @@ extrude002 = extrude(profile002, length = 150)
|
||||
test(`View gizmo stays visible even when zoomed out all the way`, async ({
|
||||
page,
|
||||
homePage,
|
||||
scene,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
@ -634,7 +632,7 @@ extrude002 = extrude(profile002, length = 150)
|
||||
|
||||
await test.step(`Load an empty file`, async () => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem('persistCode', '@settings(defaultLengthUnit = in)')
|
||||
localStorage.setItem('persistCode', '')
|
||||
})
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
@ -648,31 +646,22 @@ extrude002 = extrude(profile002, length = 150)
|
||||
timeout: 5000,
|
||||
message: 'Plane color is visible',
|
||||
})
|
||||
.toBeLessThanOrEqual(20)
|
||||
await expect(scene.startEditSketchBtn).toBeEnabled()
|
||||
.toBeLessThanOrEqual(15)
|
||||
|
||||
let maxZoomOuts = 10
|
||||
let middlePixelIsBackgroundColor =
|
||||
(await middlePixelIsColor(bgColor)) < 10
|
||||
|
||||
console.time('pressing control')
|
||||
await page.keyboard.down('Control')
|
||||
|
||||
while (!middlePixelIsBackgroundColor && maxZoomOuts > 0) {
|
||||
await page.waitForTimeout(100)
|
||||
await page.mouse.move(650, 460)
|
||||
console.time('moved to start point')
|
||||
await page.keyboard.down('Control')
|
||||
await page.mouse.move(600, 460)
|
||||
await page.mouse.down({ button: 'right' })
|
||||
console.time('moused down')
|
||||
await page.mouse.move(650, 50, { steps: 20 })
|
||||
console.time('moved to end point')
|
||||
await page.waitForTimeout(100)
|
||||
await page.mouse.move(600, 50, { steps: 20 })
|
||||
await page.mouse.up({ button: 'right' })
|
||||
console.time('moused up')
|
||||
await page.keyboard.up('Control')
|
||||
await page.waitForTimeout(100)
|
||||
maxZoomOuts--
|
||||
middlePixelIsBackgroundColor = (await middlePixelIsColor(bgColor)) < 15
|
||||
middlePixelIsBackgroundColor = (await middlePixelIsColor(bgColor)) < 10
|
||||
}
|
||||
await page.keyboard.up('Control')
|
||||
|
||||
expect(middlePixelIsBackgroundColor, {
|
||||
message: 'We should not see the default planes',
|
||||
@ -689,12 +678,13 @@ extrude002 = extrude(profile002, length = 150)
|
||||
homePage,
|
||||
scene,
|
||||
toolbar,
|
||||
viewport,
|
||||
}) => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
const legoDir = path.join(dir, 'lego')
|
||||
await fsp.mkdir(legoDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('e2e-can-sketch-on-chamfer.kcl'),
|
||||
executorInputPath('lego.kcl'),
|
||||
path.join(legoDir, 'main.kcl')
|
||||
)
|
||||
})
|
||||
@ -707,8 +697,11 @@ extrude002 = extrude(profile002, length = 150)
|
||||
await scene.loadingIndicator.waitFor({ state: 'detached' })
|
||||
})
|
||||
await test.step(`The part should start loading quickly, not waiting until execution is complete`, async () => {
|
||||
// TODO: use the viewport size to pick the center point, but the `viewport` fixture's values were wrong.
|
||||
await scene.expectPixelColor([116, 116, 116], { x: 500, y: 250 }, 15)
|
||||
await scene.expectPixelColor(
|
||||
[143, 143, 143],
|
||||
{ x: (viewport?.width ?? 1200) / 2, y: (viewport?.height ?? 500) / 2 },
|
||||
15
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@ -798,74 +791,6 @@ plane002 = offsetPlane(XZ, offset = -2 * x)`
|
||||
await page.getByTestId('custom-cmd-send-button').click()
|
||||
}
|
||||
)
|
||||
|
||||
test('scale other than default works with sketch mode', async ({
|
||||
page,
|
||||
homePage,
|
||||
toolbar,
|
||||
editor,
|
||||
scene,
|
||||
}) => {
|
||||
await test.step('Load the washer code', async () => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`@settings(defaultLengthUnit = in)
|
||||
|
||||
innerDiameter = 0.203
|
||||
outerDiameter = 0.438
|
||||
thicknessMax = 0.038
|
||||
thicknessMin = 0.024
|
||||
washerSketch = startSketchOn(XY)
|
||||
|> circle(center = [0, 0], radius = outerDiameter / 2)
|
||||
|
||||
washer = extrude(washerSketch, length = thicknessMax)`
|
||||
)
|
||||
})
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
})
|
||||
const [circleCenterClick] = scene.makeMouseHelpers(650, 300)
|
||||
const [circleRadiusClick] = scene.makeMouseHelpers(800, 320)
|
||||
const [washerFaceClick] = scene.makeMouseHelpers(657, 286)
|
||||
|
||||
await page.waitForTimeout(100)
|
||||
await test.step('Start sketching on the washer face', async () => {
|
||||
await toolbar.startSketchPlaneSelection()
|
||||
await washerFaceClick()
|
||||
await page.waitForTimeout(600) // engine animation
|
||||
await toolbar.expectToolbarMode.toBe('sketching')
|
||||
})
|
||||
|
||||
await test.step('Draw a circle and verify code', async () => {
|
||||
// select circle tool
|
||||
await expect
|
||||
.poll(async () => {
|
||||
await toolbar.circleBtn.click()
|
||||
return toolbar.circleBtn.getAttribute('aria-pressed')
|
||||
})
|
||||
.toBe('true')
|
||||
await page.waitForTimeout(100)
|
||||
await circleCenterClick()
|
||||
// this number will be different if the scale is not set correctly for inches
|
||||
await editor.expectEditor.toContain(
|
||||
'circle(sketch001, center = [0.06, -0.06]'
|
||||
)
|
||||
await circleRadiusClick()
|
||||
|
||||
await editor.expectEditor.toContain(
|
||||
'circle(sketch001, center = [0.06, -0.06], radius = 0.18'
|
||||
)
|
||||
})
|
||||
|
||||
await test.step('Exit sketch mode', async () => {
|
||||
await toolbar.exitSketch()
|
||||
await toolbar.expectToolbarMode.toBe('modeling')
|
||||
|
||||
await toolbar.selectUnit('Yards')
|
||||
await editor.expectEditor.toContain('@settings(defaultLengthUnit = yd)')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
async function clickExportButton(page: Page) {
|
||||
|
@ -1,20 +1,20 @@
|
||||
import type { Page } from '@playwright/test'
|
||||
import { roundOff, uuidv4 } from '@src/lib/utils'
|
||||
import { Page } from '@playwright/test'
|
||||
import { test, expect } from './zoo-test'
|
||||
import fs from 'node:fs/promises'
|
||||
import path from 'node:path'
|
||||
import { HomePageFixture } from './fixtures/homePageFixture'
|
||||
|
||||
import type { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture'
|
||||
import type { HomePageFixture } from '@e2e/playwright/fixtures/homePageFixture'
|
||||
import type { SceneFixture } from '@e2e/playwright/fixtures/sceneFixture'
|
||||
import type { ToolbarFixture } from '@e2e/playwright/fixtures/toolbarFixture'
|
||||
import {
|
||||
PERSIST_MODELING_CONTEXT,
|
||||
TEST_COLORS,
|
||||
getMovementUtils,
|
||||
getUtils,
|
||||
PERSIST_MODELING_CONTEXT,
|
||||
TEST_COLORS,
|
||||
orRunWhenFullSuiteEnabled,
|
||||
} from '@e2e/playwright/test-utils'
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
} from './test-utils'
|
||||
import { uuidv4, roundOff } from 'lib/utils'
|
||||
import { SceneFixture } from './fixtures/sceneFixture'
|
||||
import { ToolbarFixture } from './fixtures/toolbarFixture'
|
||||
import { CmdBarFixture } from './fixtures/cmdBarFixture'
|
||||
|
||||
test.describe('Sketch tests', { tag: ['@skipWin'] }, () => {
|
||||
test('multi-sketch file shows multiple Edit Sketch buttons', async ({
|
||||
@ -113,8 +113,7 @@ test.describe('Sketch tests', { tag: ['@skipWin'] }, () => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XZ)
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
|> startProfileAt([2.61, -4.01], %)
|
||||
|> xLine(length = 8.73)
|
||||
|> tangentialArcTo([8.33, -1.31], %)`
|
||||
@ -160,10 +159,7 @@ sketch001 = startSketchOn(XZ)
|
||||
await page.mouse.click(700, 200)
|
||||
|
||||
await expect.poll(u.normalisedEditorCode, { timeout: 1000 })
|
||||
.toBe(`@settings(defaultLengthUnit = in)
|
||||
|
||||
|
||||
sketch002 = startSketchOn(XZ)
|
||||
.toBe(`sketch002 = startSketchOn(XZ)
|
||||
sketch001 = startProfileAt([12.34, -12.34], sketch002)
|
||||
|> yLine(length = 12.34)
|
||||
|
||||
@ -479,8 +475,7 @@ sketch001 = startProfileAt([12.34, -12.34], sketch002)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`@settings(defaultLengthUnit=in)
|
||||
sketch001 = startSketchOn(XZ)
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
|> circle(center = [4.61, -5.01], radius = 8)`
|
||||
)
|
||||
})
|
||||
@ -565,14 +560,12 @@ sketch001 = startSketchOn(XZ)
|
||||
test('Can edit a sketch that has been extruded in the same pipe', async ({
|
||||
page,
|
||||
homePage,
|
||||
editor,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`@settings(defaultLengthUnit=in)
|
||||
sketch001 = startSketchOn(XZ)
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
|> startProfileAt([4.61, -10.01], %)
|
||||
|> line(end = [12.73, -0.09])
|
||||
|> tangentialArcTo([24.95, -0.38], %)
|
||||
@ -657,29 +650,26 @@ sketch001 = startSketchOn(XZ)
|
||||
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
||||
|
||||
// expect the code to have changed
|
||||
await editor.expectEditor.toContain(
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn(XZ)
|
||||
|> startProfileAt([7.12, -12.68], %)
|
||||
|> line(end = [12.68, -1.09])
|
||||
|> tangentialArcTo([24.89, 0.68], %)
|
||||
|> close()
|
||||
|> extrude(length = 5)`,
|
||||
{ shouldNormalise: true }
|
||||
)
|
||||
|> extrude(length = 5)
|
||||
`)
|
||||
})
|
||||
|
||||
test('Can edit a sketch that has been revolved in the same pipe', async ({
|
||||
page,
|
||||
homePage,
|
||||
scene,
|
||||
editor,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`@settings(defaultLengthUnit=in)
|
||||
sketch001 = startSketchOn(XZ)
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
|> startProfileAt([4.61, -14.01], %)
|
||||
|> line(end = [12.73, -0.09])
|
||||
|> tangentialArcTo([24.95, -5.38], %)
|
||||
@ -764,16 +754,14 @@ sketch001 = startSketchOn(XZ)
|
||||
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
||||
|
||||
// expect the code to have changed
|
||||
await editor.expectEditor.toContain(
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn(XZ)
|
||||
|> startProfileAt([6.44, -12.07], %)
|
||||
|> line(end = [14.72, 1.97])
|
||||
|> tangentialArcTo([24.95, -5.38], %)
|
||||
|> line(end = [1.97, 2.06])
|
||||
|> close()
|
||||
|> revolve(axis = X)`,
|
||||
{ shouldNormalise: true }
|
||||
)
|
||||
|> revolve(axis = X)`)
|
||||
})
|
||||
test('Can add multiple sketches', async ({ page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
@ -801,8 +789,7 @@ sketch001 = startSketchOn(XZ)
|
||||
200
|
||||
)
|
||||
|
||||
let codeStr =
|
||||
'@settings(defaultLengthUnit = in)sketch001 = startSketchOn(XY)'
|
||||
let codeStr = 'sketch001 = startSketchOn(XY)'
|
||||
|
||||
await page.mouse.click(center.x, viewportSize.height * 0.55)
|
||||
await expect(u.codeLocator).toHaveText(codeStr)
|
||||
@ -881,8 +868,7 @@ sketch001 = startSketchOn(XZ)
|
||||
|
||||
await u.openDebugPanel()
|
||||
|
||||
const code = `@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(-XZ)
|
||||
const code = `sketch001 = startSketchOn(-XZ)
|
||||
profile001 = startProfileAt([${roundOff(scale * 69.6)}, ${roundOff(
|
||||
scale * 34.8
|
||||
)}], sketch001)
|
||||
@ -912,7 +898,7 @@ profile001 = startProfileAt([${roundOff(scale * 69.6)}, ${roundOff(
|
||||
await page.mouse.move(700, 200, { steps: 10 })
|
||||
await page.mouse.click(700, 200, { delay: 200 })
|
||||
await expect(page.locator('.cm-content')).toHaveText(
|
||||
`@settings(defaultLengthUnit = in)sketch001 = startSketchOn(-XZ)`
|
||||
`sketch001 = startSketchOn(-XZ)`
|
||||
)
|
||||
|
||||
let prevContent = await page.locator('.cm-content').innerText()
|
||||
@ -1440,8 +1426,7 @@ test.describe(`Sketching with offset planes`, () => {
|
||||
await context.addInitScript(() => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`@settings(defaultLengthUnit = in)
|
||||
offsetPlane001 = offsetPlane(XY, offset = 10)`
|
||||
`offsetPlane001 = offsetPlane(XY, offset = 10)`
|
||||
)
|
||||
})
|
||||
|
||||
@ -1455,7 +1440,7 @@ offsetPlane001 = offsetPlane(XY, offset = 10)`
|
||||
await test.step(`Hovering should highlight code`, async () => {
|
||||
await planeHover()
|
||||
await editor.expectState({
|
||||
activeLines: [`@settings(defaultLengthUnit = in)`],
|
||||
activeLines: [`offsetPlane001=offsetPlane(XY,offset=10)`],
|
||||
diagnostics: [],
|
||||
highlightedCode: 'offsetPlane(XY, offset = 10)',
|
||||
})
|
||||
@ -1468,7 +1453,7 @@ offsetPlane001 = offsetPlane(XY, offset = 10)`
|
||||
await expect(toolbar.lineBtn).toBeEnabled()
|
||||
await editor.expectEditor.toContain('startSketchOn(offsetPlane001)')
|
||||
await editor.expectState({
|
||||
activeLines: [`@settings(defaultLengthUnit = in)`],
|
||||
activeLines: [`offsetPlane001=offsetPlane(XY,offset=10)`],
|
||||
diagnostics: [],
|
||||
highlightedCode: '',
|
||||
})
|
||||
@ -1619,8 +1604,7 @@ profile002 = startProfileAt([117.2, 56.08], sketch001)
|
||||
await context.addInitScript(() => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XZ)
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
profile002 = startProfileAt([40.68, 87.67], sketch001)
|
||||
|> xLine(length = 239.17)
|
||||
profile003 = startProfileAt([206.63, -56.73], sketch001)
|
||||
@ -2188,8 +2172,7 @@ profile003 = startProfileAt([206.63, -56.73], sketch001)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XZ)
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
profile001 = startProfileAt([6.24, 4.54], sketch001)
|
||||
|> line(end = [-0.41, 6.99])
|
||||
|> line(end = [8.61, 0.74])
|
||||
@ -2334,8 +2317,7 @@ profile004 = circleThreePoint(sketch001, p1 = [13.44, -6.8], p2 = [13.39, -2.07]
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XZ)
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
profile001 = startProfileAt([6.24, 4.54], sketch001)
|
||||
|> line(end = [-0.41, 6.99])
|
||||
|> line(end = [8.61, 0.74])
|
||||
@ -2440,8 +2422,7 @@ profile003 = circle(sketch001, center = [6.92, -4.2], radius = 3.16)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XZ)
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
profile001 = startProfileAt([-63.43, 193.08], sketch001)
|
||||
|> line(end = [168.52, 149.87])
|
||||
|> line(end = [190.29, -39.18])
|
||||
@ -2505,11 +2486,7 @@ extrude001 = extrude(profile003, length = 5)
|
||||
page,
|
||||
}) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`@settings(defaultLengthUnit = in)
|
||||
myVar = 5`
|
||||
)
|
||||
localStorage.setItem('persistCode', `myVar = 5`)
|
||||
})
|
||||
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
@ -2556,8 +2533,7 @@ extrude001 = extrude(profile003, length = 5)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XZ)
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
profile001 = startProfileAt([85.19, 338.59], sketch001)
|
||||
|> line(end = [213.3, -94.52])
|
||||
|> line(end = [-230.09, -55.34])
|
||||
@ -2599,8 +2575,7 @@ profile002 = startProfileAt([85.81, 52.55], sketch002)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`@settings(defaultLengthUnit = in)
|
||||
thePart = startSketchOn(XZ)
|
||||
`thePart = startSketchOn(XZ)
|
||||
|> startProfileAt([7.53, 10.51], %)
|
||||
|> line(end = [12.54, 1.83])
|
||||
|> line(end = [6.65, -6.91])
|
||||
@ -2661,8 +2636,7 @@ extrude001 = extrude(thePart, length = 75)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XZ)
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
profile001 = startProfileAt([6.71, -3.66], sketch001)
|
||||
|> line(end = [2.65, 9.02], tag = $seg02)
|
||||
|> line(end = [3.73, -9.36], tag = $seg01)
|
||||
@ -2835,8 +2809,7 @@ extrude003 = extrude(profile011, length = 2.5)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XZ)
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
profile001 = startProfileAt([34, 42.66], sketch001)
|
||||
|> line(end = [102.65, 151.99])
|
||||
|> line(end = [76, -138.66])
|
||||
|
@ -1,22 +1,21 @@
|
||||
import type { Models } from '@kittycad/lib'
|
||||
import { KCL_DEFAULT_LENGTH } from '@src/lib/constants'
|
||||
import { spawn } from 'child_process'
|
||||
import fsp from 'fs/promises'
|
||||
import JSZip from 'jszip'
|
||||
import path from 'path'
|
||||
|
||||
import type { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture'
|
||||
import type { SceneFixture } from '@e2e/playwright/fixtures/sceneFixture'
|
||||
import { secrets } from '@e2e/playwright/secrets'
|
||||
import { TEST_SETTINGS, TEST_SETTINGS_KEY } from '@e2e/playwright/storageStates'
|
||||
import type { Paths } from '@e2e/playwright/test-utils'
|
||||
import { test, expect } from './zoo-test'
|
||||
import { secrets } from './secrets'
|
||||
import {
|
||||
Paths,
|
||||
doExport,
|
||||
getUtils,
|
||||
orRunWhenFullSuiteEnabled,
|
||||
settingsToToml,
|
||||
} from '@e2e/playwright/test-utils'
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
orRunWhenFullSuiteEnabled,
|
||||
} from './test-utils'
|
||||
import { Models } from '@kittycad/lib'
|
||||
import fsp from 'fs/promises'
|
||||
import { spawn } from 'child_process'
|
||||
import { KCL_DEFAULT_LENGTH } from 'lib/constants'
|
||||
import JSZip from 'jszip'
|
||||
import path from 'path'
|
||||
import { TEST_SETTINGS, TEST_SETTINGS_KEY } from './storageStates'
|
||||
import { SceneFixture } from './fixtures/sceneFixture'
|
||||
import { CmdBarFixture } from './fixtures/cmdBarFixture'
|
||||
|
||||
test.beforeEach(async ({ page, context }) => {
|
||||
// Make the user avatar image always 404
|
||||
@ -346,12 +345,10 @@ const extrudeDefaultPlane = async (
|
||||
app: {
|
||||
onboarding_status: 'dismissed',
|
||||
show_debug_panel: true,
|
||||
appearance: {
|
||||
theme: 'dark',
|
||||
},
|
||||
theme: 'dark',
|
||||
},
|
||||
project: {
|
||||
default_project_name: 'untitled',
|
||||
default_project_name: 'project-$nnn',
|
||||
},
|
||||
text_editor: {
|
||||
text_wrapping: true,
|
||||
@ -455,7 +452,7 @@ test(
|
||||
await page.waitForTimeout(700) // TODO detect animation ending, or disable animation
|
||||
|
||||
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||
code += `profile001 = startProfileAt([182.59, -246.32], sketch001)`
|
||||
code += `profile001 = startProfileAt([7.19, -9.7], sketch001)`
|
||||
await expect(page.locator('.cm-content')).toHaveText(code)
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
@ -473,7 +470,7 @@ test(
|
||||
await page.waitForTimeout(500)
|
||||
|
||||
code += `
|
||||
|> xLine(length = 184.3)`
|
||||
|> xLine(length = 7.25)`
|
||||
await expect(page.locator('.cm-content')).toHaveText(code)
|
||||
|
||||
await page
|
||||
@ -631,7 +628,7 @@ test(
|
||||
mask: [page.getByTestId('model-state-indicator')],
|
||||
})
|
||||
await expect(page.locator('.cm-content')).toHaveText(
|
||||
`sketch001 = startSketchOn(XZ)profile001 = circle(sketch001, center = [366.89, -62.01], radius = 1)`
|
||||
`sketch001 = startSketchOn(XZ)profile001 = circle(sketch001, center = [14.44, -2.44], radius = 1)`
|
||||
)
|
||||
}
|
||||
)
|
||||
@ -668,7 +665,7 @@ test.describe(
|
||||
|
||||
const startXPx = 600
|
||||
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||
code += `profile001 = startProfileAt([182.59, -246.32], sketch001)`
|
||||
code += `profile001 = startProfileAt([7.19, -9.7], sketch001)`
|
||||
await expect(u.codeLocator).toHaveText(code)
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
@ -676,7 +673,7 @@ test.describe(
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
code += `
|
||||
|> xLine(length = 184.3)`
|
||||
|> xLine(length = 7.25)`
|
||||
await expect(u.codeLocator).toHaveText(code)
|
||||
|
||||
await page
|
||||
@ -691,7 +688,7 @@ test.describe(
|
||||
await page.mouse.click(startXPx + PUR * 30, 500 - PUR * 20)
|
||||
|
||||
code += `
|
||||
|> tangentialArcTo([551.2, -62.01], %)`
|
||||
|> tangentialArcTo([21.7, -2.44], %)`
|
||||
await expect(u.codeLocator).toHaveText(code)
|
||||
|
||||
// click tangential arc tool again to unequip it
|
||||
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 55 KiB |
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 68 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 75 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 65 KiB |
Before Width: | Height: | Size: 140 KiB After Width: | Height: | Size: 143 KiB |
Before Width: | Height: | Size: 124 KiB After Width: | Height: | Size: 131 KiB |
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 68 KiB |
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 76 KiB |
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 69 KiB |
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 72 KiB |
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 72 KiB |
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 68 KiB |
@ -1,5 +1,5 @@
|
||||
{
|
||||
"original_source_code": "sketch001 = startSketchOn('XZ')\nprofile001 = startProfileAt([57.81, 250.51], sketch001)\n |> line(end = [121.13, 56.63], tag = $seg02)\n |> line(end = [83.37, -34.61], tag = $seg01)\n |> line(end = [19.66, -116.4])\n |> line(end = [-221.8, -41.69])\n |> line(endAbsolute = [profileStartX(%), profileStartY(%)])\n |> close()\nextrude001 = extrude(profile001, length = 200)\nsketch002 = startSketchOn('XZ')\n |> startProfileAt([-73.64, -42.89], %)\n |> xLine(length = 173.71)\n |> line(end = [-22.12, -94.4])\n |> xLine(length = -156.98)\n |> line(endAbsolute = [profileStartX(%), profileStartY(%)])\n |> close()\nextrude002 = extrude(sketch002, length = 50)\nsketch003 = startSketchOn(XY)\n |> startProfileAt([52.92, 157.81], %)\n |> angledLine([0, 176.4], %, $rectangleSegmentA001)\n |> angledLine([\n segAng(rectangleSegmentA001) - 90,\n 53.4\n ], %, $rectangleSegmentB001)\n |> angledLine([\n segAng(rectangleSegmentA001),\n -segLen(rectangleSegmentA001)\n ], %, $rectangleSegmentC001)\n |> line(endAbsolute = [profileStartX(%), profileStartY(%)])\n |> close()\nextrude003 = extrude(sketch003, length = 20)\n",
|
||||
"original_source_code": "sketch001 = startSketchOn('XZ')\nprofile001 = startProfileAt([57.81, 250.51], sketch001)\n |> line(end = [121.13, 56.63], tag = $seg02)\n |> line(end = [83.37, -34.61], tag = $seg01)\n |> line(end = [19.66, -116.4])\n |> line(end = [-221.8, -41.69])\n |> line(endAbsolute = [profileStartX(%), profileStartY(%)])\n |> close()\nextrude001 = extrude(profile001, length = 200)\nsketch002 = startSketchOn('XZ')\n |> startProfileAt([-73.64, -42.89], %)\n |> xLine(length = 173.71)\n |> line(end = [-22.12, -94.4])\n |> xLine(length = -156.98)\n |> line(endAbsolute = [profileStartX(%), profileStartY(%)])\n |> close()\nextrude002 = extrude(sketch002, length = 50)\nsketch003 = startSketchOn('XY')\n |> startProfileAt([52.92, 157.81], %)\n |> angledLine([0, 176.4], %, $rectangleSegmentA001)\n |> angledLine([\n segAng(rectangleSegmentA001) - 90,\n 53.4\n ], %, $rectangleSegmentB001)\n |> angledLine([\n segAng(rectangleSegmentA001),\n -segLen(rectangleSegmentA001)\n ], %, $rectangleSegmentC001)\n |> line(endAbsolute = [profileStartX(%), profileStartY(%)])\n |> close()\nextrude003 = extrude(sketch003, length = 20)\n",
|
||||
"prompt": "make this neon green please, use #39FF14",
|
||||
"source_ranges": [
|
||||
{
|
||||
@ -30,4 +30,4 @@
|
||||
}
|
||||
],
|
||||
"kcl_version": "0.2.48"
|
||||
}
|
||||
}
|
@ -29,5 +29,5 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"kcl_version": "0.2.57"
|
||||
"kcl_version": "0.2.54"
|
||||
}
|
@ -1,19 +1,17 @@
|
||||
import type { SaveSettingsPayload } from '@src/lib/settings/settingsTypes'
|
||||
import { Themes } from '@src/lib/theme'
|
||||
import type { DeepPartial } from '@src/lib/types'
|
||||
import { onboardingPaths } from '@src/routes/Onboarding/paths'
|
||||
|
||||
import type { Settings } from '@rust/kcl-lib/bindings/Settings'
|
||||
import { Settings } from '@rust/kcl-lib/bindings/Settings'
|
||||
import { SaveSettingsPayload } from 'lib/settings/settingsTypes'
|
||||
import { Themes } from 'lib/theme'
|
||||
import { DeepPartial } from 'lib/types'
|
||||
import { onboardingPaths } from 'routes/Onboarding/paths'
|
||||
|
||||
export const IS_PLAYWRIGHT_KEY = 'playwright'
|
||||
|
||||
export const TEST_SETTINGS_KEY = '/settings.toml'
|
||||
export const TEST_SETTINGS: DeepPartial<Settings> = {
|
||||
app: {
|
||||
appearance: {
|
||||
theme: Themes.Dark,
|
||||
},
|
||||
theme: Themes.Dark,
|
||||
onboarding_status: 'dismissed',
|
||||
project_directory: '',
|
||||
show_debug_panel: true,
|
||||
},
|
||||
modeling: {
|
||||
@ -23,8 +21,7 @@ export const TEST_SETTINGS: DeepPartial<Settings> = {
|
||||
camera_projection: 'perspective',
|
||||
},
|
||||
project: {
|
||||
default_project_name: 'untitled',
|
||||
directory: '',
|
||||
default_project_name: 'project-$nnn',
|
||||
},
|
||||
text_editor: {
|
||||
text_wrapping: true,
|
||||
@ -57,7 +54,7 @@ export const TEST_SETTINGS_ONBOARDING_START: DeepPartial<Settings> = {
|
||||
|
||||
export const TEST_SETTINGS_DEFAULT_THEME: DeepPartial<Settings> = {
|
||||
...TEST_SETTINGS,
|
||||
app: { ...TEST_SETTINGS.app, appearance: { theme: Themes.System } },
|
||||
app: { ...TEST_SETTINGS.app, theme: Themes.System },
|
||||
}
|
||||
|
||||
export const TEST_SETTINGS_CORRUPTED = {
|
||||
@ -80,8 +77,7 @@ export const TEST_SETTINGS_CORRUPTED = {
|
||||
},
|
||||
} satisfies Partial<SaveSettingsPayload>
|
||||
|
||||
export const TEST_CODE_GIZMO = `@settings(defaultLengthUnit = in)
|
||||
part001 = startSketchOn(XZ)
|
||||
export const TEST_CODE_GIZMO = `part001 = startSketchOn(XZ)
|
||||
|> startProfileAt([20, 0], %)
|
||||
|> line(end = [7.13, 4 + 0])
|
||||
|> angledLine({ angle: 3 + 0, length: 3.14 + 0 }, %)
|
||||
|
@ -1,12 +1,7 @@
|
||||
import type { EngineCommand } from '@src/lang/std/artifactGraph'
|
||||
import { uuidv4 } from '@src/lib/utils'
|
||||
|
||||
import {
|
||||
commonPoints,
|
||||
getUtils,
|
||||
orRunWhenFullSuiteEnabled,
|
||||
} from '@e2e/playwright/test-utils'
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
import { test, expect } from './zoo-test'
|
||||
import { commonPoints, getUtils, orRunWhenFullSuiteEnabled } from './test-utils'
|
||||
import { EngineCommand } from 'lang/std/artifactGraph'
|
||||
import { uuidv4 } from 'lib/utils'
|
||||
|
||||
test.describe('Test network and connection issues', () => {
|
||||
test(
|
||||
|
@ -1,9 +1,9 @@
|
||||
import {
|
||||
orRunWhenFullSuiteEnabled,
|
||||
runningOnLinux,
|
||||
runningOnMac,
|
||||
runningOnWindows,
|
||||
} from '@e2e/playwright/test-utils'
|
||||
orRunWhenFullSuiteEnabled,
|
||||
} from './test-utils'
|
||||
|
||||
describe('platform detection utilities', () => {
|
||||
const originalPlatform = process.platform
|
||||
|
@ -1,29 +1,32 @@
|
||||
import * as TOML from '@iarna/toml'
|
||||
import type { Models } from '@kittycad/lib'
|
||||
import type { BrowserContext, Locator, Page, TestInfo } from '@playwright/test'
|
||||
import { expect } from '@playwright/test'
|
||||
import type { EngineCommand } from '@src/lang/std/artifactGraph'
|
||||
import type { Configuration } from '@src/lang/wasm'
|
||||
import { COOKIE_NAME } from '@src/lib/constants'
|
||||
import { reportRejection } from '@src/lib/trap'
|
||||
import type { DeepPartial } from '@src/lib/types'
|
||||
import { isArray } from '@src/lib/utils'
|
||||
import {
|
||||
expect,
|
||||
BrowserContext,
|
||||
TestInfo,
|
||||
Locator,
|
||||
Page,
|
||||
} from '@playwright/test'
|
||||
import { test } from './zoo-test'
|
||||
import { EngineCommand } from 'lang/std/artifactGraph'
|
||||
import fsp from 'fs/promises'
|
||||
import path from 'path'
|
||||
import pixelMatch from 'pixelmatch'
|
||||
import type { Protocol } from 'playwright-core/types/protocol'
|
||||
import { PNG } from 'pngjs'
|
||||
|
||||
import type { ProjectConfiguration } from '@rust/kcl-lib/bindings/ProjectConfiguration'
|
||||
|
||||
import { isErrorWhitelisted } from '@e2e/playwright/lib/console-error-whitelist'
|
||||
import { secrets } from '@e2e/playwright/secrets'
|
||||
import { Protocol } from 'playwright-core/types/protocol'
|
||||
import type { Models } from '@kittycad/lib'
|
||||
import { COOKIE_NAME } from 'lib/constants'
|
||||
import { secrets } from './secrets'
|
||||
import {
|
||||
IS_PLAYWRIGHT_KEY,
|
||||
TEST_SETTINGS,
|
||||
TEST_SETTINGS_KEY,
|
||||
} from '@e2e/playwright/storageStates'
|
||||
import { test } from '@e2e/playwright/zoo-test'
|
||||
TEST_SETTINGS,
|
||||
IS_PLAYWRIGHT_KEY,
|
||||
} from './storageStates'
|
||||
import * as TOML from '@iarna/toml'
|
||||
import { isErrorWhitelisted } from './lib/console-error-whitelist'
|
||||
import { isArray } from 'lib/utils'
|
||||
import { reportRejection } from 'lib/trap'
|
||||
import { DeepPartial } from 'lib/types'
|
||||
import { Configuration } from 'lang/wasm'
|
||||
import { ProjectConfiguration } from '@rust/kcl-lib/bindings/ProjectConfiguration'
|
||||
|
||||
const toNormalizedCode = (text: string) => {
|
||||
return text.replace(/\s+/g, '')
|
||||
@ -680,8 +683,8 @@ const _makeTemplate = (
|
||||
isArray(currentOptions)
|
||||
? currentOptions[i]
|
||||
: typeof currentOptions === 'number'
|
||||
? currentOptions
|
||||
: ''
|
||||
? currentOptions
|
||||
: ''
|
||||
)
|
||||
)
|
||||
})
|
||||
@ -900,21 +903,15 @@ export async function setup(
|
||||
settings: {
|
||||
...TEST_SETTINGS,
|
||||
app: {
|
||||
appearance: {
|
||||
...TEST_SETTINGS.app?.appearance,
|
||||
theme: 'dark',
|
||||
},
|
||||
...TEST_SETTINGS.project,
|
||||
project_directory: TEST_SETTINGS.app?.project_directory,
|
||||
onboarding_status: 'dismissed',
|
||||
},
|
||||
project: {
|
||||
...TEST_SETTINGS.project,
|
||||
directory: TEST_SETTINGS.project?.directory,
|
||||
theme: 'dark',
|
||||
},
|
||||
},
|
||||
}),
|
||||
IS_PLAYWRIGHT_KEY,
|
||||
PLAYWRIGHT_TEST_DIR: TEST_SETTINGS.project?.directory || '',
|
||||
PLAYWRIGHT_TEST_DIR: TEST_SETTINGS.app?.project_directory || '',
|
||||
PERSIST_MODELING_CONTEXT,
|
||||
}
|
||||
)
|
||||
@ -1115,25 +1112,21 @@ export async function pollEditorLinesSelectedLength(page: Page, lines: number) {
|
||||
}
|
||||
|
||||
export function settingsToToml(settings: DeepPartial<Configuration>) {
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
return TOML.stringify(settings as any)
|
||||
}
|
||||
|
||||
export function tomlToSettings(toml: string): DeepPartial<Configuration> {
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
return TOML.parse(toml)
|
||||
}
|
||||
|
||||
export function tomlToPerProjectSettings(
|
||||
toml: string
|
||||
): DeepPartial<ProjectConfiguration> {
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
return TOML.parse(toml)
|
||||
}
|
||||
|
||||
export function perProjectsettingsToToml(
|
||||
settings: DeepPartial<ProjectConfiguration>
|
||||
) {
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
return TOML.stringify(settings as any)
|
||||
}
|
||||
|