Compare commits
134 Commits
kcl-54
...
franknoiro
Author | SHA1 | Date | |
---|---|---|---|
7ca51be5bc | |||
0bb5797377 | |||
c3097aa334 | |||
2cd0fcc9a7 | |||
5b653fb558 | |||
6a5b23f848 | |||
a480783ee8 | |||
bc0f5b5787 | |||
962eb0e376 | |||
ababe24b97 | |||
1bf7a2abd4 | |||
0ea4f94e8f | |||
6cc6130b6a | |||
8294903cd0 | |||
7b7379c3dd | |||
9acdf1e42f | |||
5dd95d27de | |||
1016b4c879 | |||
df6f571294 | |||
be05dd7ba1 | |||
bc22d888ee | |||
e6ae89ebf9 | |||
e7b23e1638 | |||
ce7a967f5f | |||
38446b5b2a | |||
48e1a8ed02 | |||
a059166b9c | |||
086a2b851d | |||
8e4c5fb24d | |||
6993893600 | |||
bfdf8babed | |||
4d31fb890d | |||
ef451fd8f7 | |||
656eb0abec | |||
a7896329f7 | |||
b9e11ac201 | |||
1aa27bd91c | |||
118ec28b04 | |||
449b43792b | |||
f1e95156ea | |||
45e5b25cda | |||
bdec611cf3 | |||
fa612d5f28 | |||
d270447777 | |||
1a59fc4f99 | |||
d38dcb9ba2 | |||
c7b348390e | |||
bf6c91932c | |||
716ed3acb3 | |||
65e08bea92 | |||
74859e99e7 | |||
aad583be2e | |||
3e4505e2e3 | |||
1fe1cfb397 | |||
2f721c84ed | |||
9112ff8c0a | |||
96e12b5aba | |||
bec3ba71cd | |||
c6766d2a73 | |||
befac6ad87 | |||
60bc38559b | |||
2a56155587 | |||
87c3673a71 | |||
21889162ff | |||
f1cccc22cd | |||
10c1f3a849 | |||
d168ef94e9 | |||
29f8b05f56 | |||
af482c30bd | |||
4e36e398ee | |||
b5f81b9384 | |||
879b471aed | |||
964d81dc0e | |||
443f7cd5fd | |||
2fc8cb5376 | |||
ee20a09e7e | |||
f4e801351c | |||
7329753211 | |||
7d46e7e271 | |||
0583eb07d5 | |||
73694563cf | |||
bb4ed59191 | |||
566143757f | |||
822f2ffc73 | |||
eb3ceba497 | |||
2c6d0621c9 | |||
d8e84cb5e3 | |||
0b1e79871f | |||
954fddf578 | |||
f94f748339 | |||
e2b7b22ca9 | |||
efc8c82d8b | |||
eac5abba79 | |||
1956c14b8a | |||
017fac7041 | |||
0bdc50c78f | |||
57d78b6094 | |||
db5ce7ba85 | |||
51c16d0048 | |||
6532b23f1c | |||
49304b9ecd | |||
358b34de4c | |||
9973e5fde3 | |||
36875e05fd | |||
42123383bb | |||
ef4c606ed1 | |||
1ebb73b935 | |||
b57d31c0e7 | |||
678ebbc310 | |||
cc2efd316c | |||
d1f811f91d | |||
7ca3afff9f | |||
71b9e40bd9 | |||
4f35197a96 | |||
40b0cf5fd3 | |||
355e6acf0d | |||
4ff38e7f44 | |||
1dcd3b84b7 | |||
2957216bd3 | |||
11160f0b40 | |||
4b2c745db5 | |||
bb983021b1 | |||
d27b8871bc | |||
1753047d87 | |||
fa16fcedff | |||
0677474097 | |||
0c4826cdd5 | |||
b6fe660b84 | |||
c53fa421ad | |||
736533a482 | |||
a15565682d | |||
58861cd24a | |||
c0cdcb1b98 | |||
41f45afb3c |
@ -1,3 +1,3 @@
|
|||||||
[codespell]
|
[codespell]
|
||||||
ignore-words-list: crate,everytime,inout,co-ordinate,ot,nwo,atleast,ue,afterall,ser
|
ignore-words-list: crate,everytime,inout,co-ordinate,ot,nwo,atleast,ue,afterall,ser,fromM,FromM
|
||||||
skip: **/target,node_modules,build,dist,./out,**/Cargo.lock,./docs/kcl/*.md,.yarn.lock,**/yarn.lock,./openapi/*.json,./packages/codemirror-lang-kcl/test/all.test.ts,./public/kcl-samples,./rust/kcl-lib/tests/kcl_samples,tsconfig.tsbuildinfo
|
skip: **/target,node_modules,build,dist,./out,**/Cargo.lock,./docs/kcl/*.md,.yarn.lock,**/yarn.lock,./openapi/*.json,./packages/codemirror-lang-kcl/test/all.test.ts,./public/kcl-samples,./rust/kcl-lib/tests/kcl_samples,tsconfig.tsbuildinfo
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
NODE_ENV=development
|
NODE_ENV=development
|
||||||
DEV=true
|
DEV=true
|
||||||
|
|
||||||
VITE_KC_API_WS_MODELING_URL=wss://api.dev.zoo.dev/ws/modeling/commands
|
VITE_KC_API_WS_MODELING_URL=wss://api.dev.zoo.dev/ws/modeling/commands
|
||||||
VITE_KC_API_BASE_URL=https://api.dev.zoo.dev
|
VITE_KC_API_BASE_URL=https://api.dev.zoo.dev
|
||||||
VITE_KC_SITE_BASE_URL=https://dev.zoo.dev
|
VITE_KC_SITE_BASE_URL=https://dev.zoo.dev
|
||||||
@ -8,3 +9,5 @@ VITE_KC_SKIP_AUTH=false
|
|||||||
VITE_KC_CONNECTION_TIMEOUT_MS=5000
|
VITE_KC_CONNECTION_TIMEOUT_MS=5000
|
||||||
# ONLY add your token in .env.development.local if you want to skip auth, otherwise this token takes precedence!
|
# ONLY add your token in .env.development.local if you want to skip auth, otherwise this token takes precedence!
|
||||||
#VITE_KC_DEV_TOKEN="your token from dev.zoo.dev should go in .env.development.local"
|
#VITE_KC_DEV_TOKEN="your token from dev.zoo.dev should go in .env.development.local"
|
||||||
|
|
||||||
|
FAIL_ON_CONSOLE_ERRORS=true
|
||||||
|
10
.envrc
10
.envrc
@ -1,3 +1,13 @@
|
|||||||
|
# Load optional shared environment variables
|
||||||
source_up_if_exists
|
source_up_if_exists
|
||||||
|
|
||||||
|
# Load default development environment variables
|
||||||
|
dotenv .env.development
|
||||||
|
|
||||||
|
# Load optional environment variables overrides
|
||||||
|
dotenv_if_exists .env.development.local
|
||||||
|
|
||||||
|
# Load optional testing environment variables
|
||||||
|
dotenv_if_exists e2e/playwright/playwright-secrets.env
|
||||||
|
|
||||||
use flake .
|
use flake .
|
||||||
|
78
.eslintrc
78
.eslintrc
@ -1,78 +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": {
|
|
||||||
"@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
Normal file
127
.eslintrc.json
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
{
|
||||||
|
"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
4
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -1,5 +1,5 @@
|
|||||||
name: Bug Report
|
name: Bug Report
|
||||||
description: File a bug report for the Zoo Modeling App
|
description: File a bug report for the Zoo Design Studio
|
||||||
title: "[BUG]: "
|
title: "[BUG]: "
|
||||||
labels: ["bug"]
|
labels: ["bug"]
|
||||||
assignees: []
|
assignees: []
|
||||||
@ -70,7 +70,7 @@ body:
|
|||||||
id: version
|
id: version
|
||||||
attributes:
|
attributes:
|
||||||
label: Version
|
label: Version
|
||||||
description: "The version of the Zoo Modeling App you're using."
|
description: "The version of the Zoo Design Studio you're using."
|
||||||
placeholder: "example: v0.15.0. You can find this in the settings."
|
placeholder: "example: v0.15.0. You can find this in the settings."
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
24
.github/ci-cd-scripts/start-vector-macos.sh
vendored
Executable file
24
.github/ci-cd-scripts/start-vector-macos.sh
vendored
Executable file
@ -0,0 +1,24 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Install vector
|
||||||
|
brew tap vectordotdev/brew && brew install vector
|
||||||
|
|
||||||
|
# Configure vector
|
||||||
|
mkdir -p /tmp/vector
|
||||||
|
cp .github/workflows/vector.toml /tmp/vector.toml
|
||||||
|
sed -i '' "s#OS_NAME#${OS_NAME}#g" /tmp/vector.toml
|
||||||
|
sed -i '' "s#GITHUB_WORKFLOW#${GITHUB_WORKFLOW}#g" /tmp/vector.toml
|
||||||
|
sed -i '' "s#GITHUB_REPOSITORY#${GITHUB_REPOSITORY}#g" /tmp/vector.toml
|
||||||
|
sed -i '' "s#GITHUB_SHA#${GITHUB_SHA}#g" /tmp/vector.toml
|
||||||
|
sed -i '' "s#GITHUB_REF_NAME#${GITHUB_REF_NAME}#g" /tmp/vector.toml
|
||||||
|
sed -i '' "s#GH_ACTIONS_AXIOM_TOKEN#${GH_ACTIONS_AXIOM_TOKEN}#g" /tmp/vector.toml
|
||||||
|
|
||||||
|
# Display settings
|
||||||
|
echo
|
||||||
|
echo 'Vector config:'
|
||||||
|
cat /tmp/vector.toml
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Start in the background
|
||||||
|
$(brew --prefix)/opt/vector/bin/vector --config /tmp/vector.toml &
|
24
.github/ci-cd-scripts/start-vector-ubuntu.sh
vendored
Executable file
24
.github/ci-cd-scripts/start-vector-ubuntu.sh
vendored
Executable file
@ -0,0 +1,24 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Install vector
|
||||||
|
curl --proto '=https' --tlsv1.2 -sSfL https://sh.vector.dev | bash -s -- -y
|
||||||
|
|
||||||
|
# Configure vector
|
||||||
|
mkdir -p /tmp/vector
|
||||||
|
cp .github/workflows/vector.toml /tmp/vector.toml
|
||||||
|
sed -i "s#OS_NAME#${OS_NAME}#g" /tmp/vector.toml
|
||||||
|
sed -i "s#GITHUB_WORKFLOW#${GITHUB_WORKFLOW}#g" /tmp/vector.toml
|
||||||
|
sed -i "s#GITHUB_REPOSITORY#${GITHUB_REPOSITORY}#g" /tmp/vector.toml
|
||||||
|
sed -i "s#GITHUB_SHA#${GITHUB_SHA}#g" /tmp/vector.toml
|
||||||
|
sed -i "s#GITHUB_REF_NAME#${GITHUB_REF_NAME}#g" /tmp/vector.toml
|
||||||
|
sed -i "s#GH_ACTIONS_AXIOM_TOKEN#${GH_ACTIONS_AXIOM_TOKEN}#g" /tmp/vector.toml
|
||||||
|
|
||||||
|
# Display settings
|
||||||
|
echo
|
||||||
|
echo 'Vector config:'
|
||||||
|
cat /tmp/vector.toml
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Start in background
|
||||||
|
${HOME}/.vector/bin/vector --config /tmp/vector.toml &
|
2
.github/workflows/build-and-store-wasm.yml
vendored
2
.github/workflows/build-and-store-wasm.yml
vendored
@ -24,7 +24,7 @@ jobs:
|
|||||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||||
with:
|
with:
|
||||||
cache: false # Configured below.
|
cache: false # Configured below.
|
||||||
- uses: taiki-e/install-action@37bdc826eaedac215f638a96472df572feab0f9b
|
- uses: taiki-e/install-action@d4635f2de61c8b8104d59cd4aede2060638378cc
|
||||||
with:
|
with:
|
||||||
tool: wasm-pack
|
tool: wasm-pack
|
||||||
- name: Rust Cache
|
- name: Rust Cache
|
||||||
|
4
.github/workflows/build-apps.yml
vendored
4
.github/workflows/build-apps.yml
vendored
@ -77,7 +77,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
cache: false # Configured below.
|
cache: false # Configured below.
|
||||||
|
|
||||||
- uses: taiki-e/install-action@37bdc826eaedac215f638a96472df572feab0f9b
|
- uses: taiki-e/install-action@d4635f2de61c8b8104d59cd4aede2060638378cc
|
||||||
if: ${{ steps.wasm.outputs.should-build-wasm == 'true' }}
|
if: ${{ steps.wasm.outputs.should-build-wasm == 'true' }}
|
||||||
with:
|
with:
|
||||||
tool: wasm-pack
|
tool: wasm-pack
|
||||||
@ -241,7 +241,7 @@ jobs:
|
|||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: out-arm64-${{ matrix.platform }}
|
name: out-arm64-${{ matrix.platform }}
|
||||||
# first two will pick both Zoo Modeling App-$VERSION-arm64-win.exe and Zoo Modeling App-$VERSION-win.exe
|
# first two will pick both Zoo Design Studio-$VERSION-arm64-win.exe and Zoo Design Studio-$VERSION-win.exe
|
||||||
path: |
|
path: |
|
||||||
out/*-${{ env.VERSION_NO_V }}-win.*
|
out/*-${{ env.VERSION_NO_V }}-win.*
|
||||||
out/*-${{ env.VERSION_NO_V }}-arm64-win.*
|
out/*-${{ env.VERSION_NO_V }}-arm64-win.*
|
||||||
|
19
.github/workflows/cargo-test.yml
vendored
19
.github/workflows/cargo-test.yml
vendored
@ -34,20 +34,11 @@ jobs:
|
|||||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||||
with:
|
with:
|
||||||
cache: false # Configured below.
|
cache: false # Configured below.
|
||||||
- name: Install vector
|
- name: Start Vector
|
||||||
run: |
|
run: .github/ci-cd-scripts/start-vector-ubuntu.sh
|
||||||
curl --proto '=https' --tlsv1.2 -sSfL https://sh.vector.dev > /tmp/vector.sh
|
env:
|
||||||
chmod +x /tmp/vector.sh
|
GH_ACTIONS_AXIOM_TOKEN: ${{ secrets.GH_ACTIONS_AXIOM_TOKEN }}
|
||||||
/tmp/vector.sh -y -no-modify-path
|
OS_NAME: ${{ env.OS_NAME }}
|
||||||
mkdir -p /tmp/vector
|
|
||||||
cp .github/workflows/vector.toml /tmp/vector.toml
|
|
||||||
sed -i "s#GITHUB_WORKFLOW#${GITHUB_WORKFLOW}#g" /tmp/vector.toml
|
|
||||||
sed -i "s#GITHUB_REPOSITORY#${GITHUB_REPOSITORY}#g" /tmp/vector.toml
|
|
||||||
sed -i "s#GITHUB_SHA#${GITHUB_SHA}#g" /tmp/vector.toml
|
|
||||||
sed -i "s#GITHUB_REF_NAME#${GITHUB_REF_NAME}#g" /tmp/vector.toml
|
|
||||||
sed -i "s#GH_ACTIONS_AXIOM_TOKEN#${{secrets.GH_ACTIONS_AXIOM_TOKEN}}#g" /tmp/vector.toml
|
|
||||||
cat /tmp/vector.toml
|
|
||||||
${HOME}/.vector/bin/vector --config /tmp/vector.toml &
|
|
||||||
- uses: taiki-e/install-action@cargo-llvm-cov
|
- uses: taiki-e/install-action@cargo-llvm-cov
|
||||||
- uses: taiki-e/install-action@nextest
|
- uses: taiki-e/install-action@nextest
|
||||||
- name: Install just
|
- name: Install just
|
||||||
|
43
.github/workflows/e2e-tests.yml
vendored
43
.github/workflows/e2e-tests.yml
vendored
@ -140,7 +140,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
cache: false # Configured below.
|
cache: false # Configured below.
|
||||||
|
|
||||||
- uses: taiki-e/install-action@37bdc826eaedac215f638a96472df572feab0f9b
|
- uses: taiki-e/install-action@d4635f2de61c8b8104d59cd4aede2060638378cc
|
||||||
if: ${{ needs.conditions.outputs.should-run == 'true' && steps.wasm.outputs.should-build-wasm == 'true' }}
|
if: ${{ needs.conditions.outputs.should-run == 'true' && steps.wasm.outputs.should-build-wasm == 'true' }}
|
||||||
with:
|
with:
|
||||||
tool: wasm-pack
|
tool: wasm-pack
|
||||||
@ -229,10 +229,6 @@ jobs:
|
|||||||
timeout_minutes: 30
|
timeout_minutes: 30
|
||||||
max_attempts: 3
|
max_attempts: 3
|
||||||
env:
|
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 }}
|
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||||
snapshottoken: ${{ secrets.KITTYCAD_API_TOKEN }}
|
snapshottoken: ${{ secrets.KITTYCAD_API_TOKEN }}
|
||||||
|
|
||||||
@ -257,7 +253,8 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Commit changes, if any
|
- name: Commit changes, if any
|
||||||
if: ${{ needs.conditions.outputs.should-run == 'true' && steps.git-check.outputs.modified == 'true' }}
|
# TODO: find a more reliable way to detect visual changes
|
||||||
|
if: ${{ false && needs.conditions.outputs.should-run == 'true' && steps.git-check.outputs.modified == 'true' }}
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
git add e2e/playwright/snapshot-tests.spec.ts-snapshots e2e/playwright/snapshots
|
git add e2e/playwright/snapshot-tests.spec.ts-snapshots e2e/playwright/snapshots
|
||||||
@ -284,7 +281,7 @@ jobs:
|
|||||||
os:
|
os:
|
||||||
- "runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64"
|
- "runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64"
|
||||||
- namespace-profile-macos-8-cores
|
- namespace-profile-macos-8-cores
|
||||||
- windows-latest
|
- windows-latest-8-cores
|
||||||
shardIndex: [1, 2, 3, 4]
|
shardIndex: [1, 2, 3, 4]
|
||||||
shardTotal: [4]
|
shardTotal: [4]
|
||||||
# Disable macos and windows tests on hourly e2e tests since we only care
|
# Disable macos and windows tests on hourly e2e tests since we only care
|
||||||
@ -295,7 +292,7 @@ jobs:
|
|||||||
exclude:
|
exclude:
|
||||||
- os: namespace-profile-macos-8-cores
|
- os: namespace-profile-macos-8-cores
|
||||||
isScheduled: true
|
isScheduled: true
|
||||||
- os: windows-latest
|
- os: windows-latest-8-cores
|
||||||
isScheduled: true
|
isScheduled: true
|
||||||
# TODO: add ref here for main and latest release tag
|
# TODO: add ref here for main and latest release tag
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
@ -342,22 +339,12 @@ jobs:
|
|||||||
if: needs.conditions.outputs.should-run == 'true'
|
if: needs.conditions.outputs.should-run == 'true'
|
||||||
run: yarn tronb:vite:dev
|
run: yarn tronb:vite:dev
|
||||||
|
|
||||||
- name: Install vector
|
- name: Start Vector
|
||||||
if: contains(matrix.os, 'ubuntu')
|
if: ${{ needs.conditions.outputs.should-run == 'true' && !contains(matrix.os, 'windows') }}
|
||||||
shell: bash
|
run: .github/ci-cd-scripts/start-vector-${{ env.OS_NAME }}.sh
|
||||||
run: |
|
env:
|
||||||
curl --proto '=https' --tlsv1.2 -sSfL https://sh.vector.dev > /tmp/vector.sh
|
GH_ACTIONS_AXIOM_TOKEN: ${{ secrets.GH_ACTIONS_AXIOM_TOKEN }}
|
||||||
chmod +x /tmp/vector.sh
|
OS_NAME: ${{ env.OS_NAME }}
|
||||||
/tmp/vector.sh -y -no-modify-path
|
|
||||||
mkdir -p /tmp/vector
|
|
||||||
cp .github/workflows/vector.toml /tmp/vector.toml
|
|
||||||
sed -i "s#GITHUB_WORKFLOW#${GITHUB_WORKFLOW}#g" /tmp/vector.toml
|
|
||||||
sed -i "s#GITHUB_REPOSITORY#${GITHUB_REPOSITORY}#g" /tmp/vector.toml
|
|
||||||
sed -i "s#GITHUB_SHA#${GITHUB_SHA}#g" /tmp/vector.toml
|
|
||||||
sed -i "s#GITHUB_REF_NAME#${GITHUB_REF_NAME}#g" /tmp/vector.toml
|
|
||||||
sed -i "s#GH_ACTIONS_AXIOM_TOKEN#${{secrets.GH_ACTIONS_AXIOM_TOKEN}}#g" /tmp/vector.toml
|
|
||||||
cat /tmp/vector.toml
|
|
||||||
${HOME}/.vector/bin/vector --config /tmp/vector.toml &
|
|
||||||
|
|
||||||
- uses: actions/download-artifact@v4
|
- uses: actions/download-artifact@v4
|
||||||
if: ${{ needs.conditions.outputs.should-run == 'true' && !cancelled() && (success() || failure()) }}
|
if: ${{ needs.conditions.outputs.should-run == 'true' && !cancelled() && (success() || failure()) }}
|
||||||
@ -373,14 +360,10 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
shell: bash
|
shell: bash
|
||||||
command: .github/ci-cd-scripts/playwright-electron.sh ${{matrix.shardIndex}} ${{matrix.shardTotal}} ${{ env.OS_NAME }}
|
command: .github/ci-cd-scripts/playwright-electron.sh ${{matrix.shardIndex}} ${{matrix.shardTotal}} ${{ env.OS_NAME }}
|
||||||
timeout_minutes: 45
|
timeout_minutes: 30
|
||||||
max_attempts: 15
|
max_attempts: 9
|
||||||
env:
|
env:
|
||||||
CI: true
|
|
||||||
FAIL_ON_CONSOLE_ERRORS: 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 }}
|
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
|
180
.github/workflows/static-analysis.yml
vendored
180
.github/workflows/static-analysis.yml
vendored
@ -28,43 +28,57 @@ jobs:
|
|||||||
- run: yarn fmt-check
|
- run: yarn fmt-check
|
||||||
|
|
||||||
yarn-build-wasm:
|
yarn-build-wasm:
|
||||||
runs-on: ubuntu-22.04
|
# Build the wasm blob once on the fastest runner.
|
||||||
|
runs-on: runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version-file: '.nvmrc'
|
node-version-file: '.nvmrc'
|
||||||
cache: 'yarn'
|
cache: 'yarn'
|
||||||
- run: yarn install
|
|
||||||
- uses: taiki-e/install-action@37bdc826eaedac215f638a96472df572feab0f9b
|
- 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
|
||||||
|
with:
|
||||||
|
cache: false # Configured below.
|
||||||
|
|
||||||
|
- uses: taiki-e/install-action@d4635f2de61c8b8104d59cd4aede2060638378cc
|
||||||
with:
|
with:
|
||||||
tool: wasm-pack
|
tool: wasm-pack
|
||||||
- run: yarn build:wasm
|
|
||||||
|
|
||||||
yarn-tsc:
|
- name: Rust Cache
|
||||||
runs-on: ubuntu-22.04
|
uses: Swatinem/rust-cache@v2
|
||||||
|
|
||||||
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:
|
with:
|
||||||
workspaces: './rust'
|
workspaces: './rust'
|
||||||
|
|
||||||
- uses: taiki-e/install-action@37bdc826eaedac215f638a96472df572feab0f9b
|
- name: Build Wasm
|
||||||
with:
|
shell: bash
|
||||||
tool: wasm-pack
|
run: yarn build:wasm
|
||||||
- run: yarn build:wasm
|
|
||||||
- run: yarn tsc
|
|
||||||
|
|
||||||
yarn-lint:
|
- uses: actions/upload-artifact@v4
|
||||||
runs-on: ubuntu-22.04
|
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:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
@ -73,9 +87,85 @@ jobs:
|
|||||||
node-version-file: '.nvmrc'
|
node-version-file: '.nvmrc'
|
||||||
cache: 'yarn'
|
cache: 'yarn'
|
||||||
- run: yarn install
|
- run: yarn install
|
||||||
- run: yarn --cwd ./rust/kcl-language-server --modules-folder node_modules 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 tsc
|
||||||
|
|
||||||
|
yarn-lint:
|
||||||
|
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 lint
|
- 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:
|
python-codespell:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
@ -91,6 +181,7 @@ jobs:
|
|||||||
|
|
||||||
yarn-unit-test-kcl-samples:
|
yarn-unit-test-kcl-samples:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
needs: yarn-build-wasm
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
@ -100,10 +191,25 @@ jobs:
|
|||||||
cache: 'yarn'
|
cache: 'yarn'
|
||||||
|
|
||||||
- run: yarn install
|
- run: yarn install
|
||||||
- uses: taiki-e/install-action@37bdc826eaedac215f638a96472df572feab0f9b
|
- uses: taiki-e/install-action@d4635f2de61c8b8104d59cd4aede2060638378cc
|
||||||
with:
|
with:
|
||||||
tool: wasm-pack
|
tool: wasm-pack
|
||||||
- run: yarn build:wasm
|
|
||||||
|
- 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 simpleserver:bg
|
- run: yarn simpleserver:bg
|
||||||
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
|
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
|
||||||
@ -120,6 +226,7 @@ jobs:
|
|||||||
|
|
||||||
yarn-unit-test:
|
yarn-unit-test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
needs: yarn-build-wasm
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
@ -129,10 +236,25 @@ jobs:
|
|||||||
cache: 'yarn'
|
cache: 'yarn'
|
||||||
|
|
||||||
- run: yarn install
|
- run: yarn install
|
||||||
- uses: taiki-e/install-action@37bdc826eaedac215f638a96472df572feab0f9b
|
- uses: taiki-e/install-action@d4635f2de61c8b8104d59cd4aede2060638378cc
|
||||||
with:
|
with:
|
||||||
tool: wasm-pack
|
tool: wasm-pack
|
||||||
- run: yarn build:wasm
|
|
||||||
|
- 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 simpleserver:bg
|
- run: yarn simpleserver:bg
|
||||||
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
|
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
|
||||||
|
14
.github/workflows/update-e2e-branch.yml
vendored
14
.github/workflows/update-e2e-branch.yml
vendored
@ -28,12 +28,18 @@ jobs:
|
|||||||
|
|
||||||
- name: Sync with main
|
- name: Sync with main
|
||||||
run: |
|
run: |
|
||||||
# checkout our branch
|
# Create the branch
|
||||||
git checkout all-e2e || git checkout -b all-e2e
|
git checkout all-e2e || git checkout -b all-e2e
|
||||||
# fetch origin
|
|
||||||
|
# Reset to main
|
||||||
git fetch origin
|
git fetch origin
|
||||||
# reset to main
|
|
||||||
git reset --hard origin/main
|
git reset --hard origin/main
|
||||||
# force push it
|
|
||||||
|
# Get a new SHA to prevent overwriting the commit status on main
|
||||||
|
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
git config --local user.name "github-actions[bot]"
|
||||||
|
git commit --allow-empty --message="[all-e2e] $(git log --max-count=1 --pretty=%B)"
|
||||||
|
|
||||||
|
# Overwrite the branch
|
||||||
git remote set-url origin https://x-access-token:${{ steps.app-token.outputs.token }}@github.com/${{ github.repository }}.git
|
git remote set-url origin https://x-access-token:${{ steps.app-token.outputs.token }}@github.com/${{ github.repository }}.git
|
||||||
git push --force origin all-e2e
|
git push --force origin all-e2e
|
||||||
|
1
.github/workflows/vector.toml
vendored
1
.github/workflows/vector.toml
vendored
@ -8,6 +8,7 @@ include = ["/tmp/github-actions.log"]
|
|||||||
type = "remap"
|
type = "remap"
|
||||||
inputs = [ "github-actions-file" ]
|
inputs = [ "github-actions-file" ]
|
||||||
source = '''
|
source = '''
|
||||||
|
.platform = "OS_NAME"
|
||||||
.action = "GITHUB_WORKFLOW"
|
.action = "GITHUB_WORKFLOW"
|
||||||
.repo = "GITHUB_REPOSITORY"
|
.repo = "GITHUB_REPOSITORY"
|
||||||
.sha = "GITHUB_SHA"
|
.sha = "GITHUB_SHA"
|
||||||
|
20
INSTALL.md
20
INSTALL.md
@ -1,27 +1,27 @@
|
|||||||
# Setting Up Zoo Modeling App
|
# Setting Up Zoo Design Studio
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
## Windows
|
## Windows
|
||||||
|
|
||||||
1. Download the [Zoo Modeling App installer](https://zoo.dev/modeling-app/download) for Windows and for your processor type.
|
1. Download the [Zoo Design Studio installer](https://zoo.dev/modeling-app/download) for Windows and for your processor type.
|
||||||
|
|
||||||
2. Once downloaded, run the installer `Zoo Modeling App-{version}-{arch}-win.exe` which should take a few seconds.
|
2. Once downloaded, run the installer `Zoo Design Studio-{version}-{arch}-win.exe` which should take a few seconds.
|
||||||
|
|
||||||
3. The installation happens at `C:\Program Files\Zoo Modeling App`. A shortcut in the start menu is also created so you can run the app easily by clicking on it.
|
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.
|
||||||
|
|
||||||
## macOS
|
## macOS
|
||||||
|
|
||||||
1. Download the [Zoo Modeling App installer](https://zoo.dev/modeling-app/download) for macOS and for your processor type.
|
1. Download the [Zoo Design Studio installer](https://zoo.dev/modeling-app/download) for macOS and for your processor type.
|
||||||
|
|
||||||
2. Once downloaded, open the disk image `Zoo Modeling App-{version}-{arch}-mac.dmg` and drag the applications to your `Applications` directory.
|
2. Once downloaded, open the disk image `Zoo Design Studio-{version}-{arch}-mac.dmg` and drag the applications to your `Applications` directory.
|
||||||
|
|
||||||
3. You can then open your `Applications` directory and double-click on `Zoo Modeling App` to open.
|
3. You can then open your `Applications` directory and double-click on `Zoo Design Studio` to open.
|
||||||
|
|
||||||
|
|
||||||
## Linux
|
## Linux
|
||||||
|
|
||||||
1. Download the [Zoo Modeling App installer](https://zoo.dev/modeling-app/download) for Linux and for your processor type.
|
1. Download the [Zoo Design Studio 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/).
|
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.
|
- On Ubuntu, install the FUSE library with these commands in a terminal.
|
||||||
@ -30,7 +30,7 @@ Compared to other CAD software, getting Zoo Modeling App up and running is quick
|
|||||||
sudo apt install libfuse2
|
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.
|
- Optionally, follow [these steps](https://github.com/probonopd/go-appimage/blob/master/src/appimaged/README.md#initial-setup) to install `appimaged`. It is a daemon that makes interacting with AppImage files more seamless.
|
||||||
- Once installed, copy the downloaded `Zoo Modeling App-{version}-{arch}-linux.AppImage` to the directory of your choice, for instance `~/Applications`.
|
- Once installed, copy the downloaded `Zoo Design Studio-{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:
|
- `appimaged` should automatically find it and make it executable. If not, run:
|
||||||
```bash
|
```bash
|
||||||
|
68
Makefile
68
Makefile
@ -4,18 +4,38 @@ all: install build check
|
|||||||
###############################################################################
|
###############################################################################
|
||||||
# INSTALL
|
# INSTALL
|
||||||
|
|
||||||
WASM_PACK ?= ~/.cargo/bin/wasm-pack
|
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
|
||||||
|
|
||||||
.PHONY: install
|
.PHONY: install
|
||||||
install: node_modules/.yarn-integrity $(WASM_PACK) ## Install dependencies
|
install: node_modules/.yarn-integrity $(CARGO) $(WASM_PACK) ## Install dependencies
|
||||||
|
|
||||||
node_modules/.yarn-integrity: package.json yarn.lock
|
node_modules/.yarn-integrity: package.json yarn.lock
|
||||||
yarn install
|
yarn install
|
||||||
|
ifeq ($(OS),Windows_NT)
|
||||||
|
@ type nul > $@
|
||||||
|
else
|
||||||
@ touch $@
|
@ touch $@
|
||||||
|
endif
|
||||||
|
|
||||||
|
$(CARGO):
|
||||||
|
ifeq ($(OS),Windows_NT)
|
||||||
|
yarn install:rust:windows
|
||||||
|
else
|
||||||
|
yarn install:rust
|
||||||
|
endif
|
||||||
|
|
||||||
$(WASM_PACK):
|
$(WASM_PACK):
|
||||||
yarn install:rust
|
ifeq ($(OS),Windows_NT)
|
||||||
|
yarn install:wasm-pack:cargo
|
||||||
|
else
|
||||||
yarn install:wasm-pack:sh
|
yarn install:wasm-pack:sh
|
||||||
|
endif
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# BUILD
|
# BUILD
|
||||||
@ -31,13 +51,17 @@ VITE_SOURCES := $(wildcard vite.*) $(wildcard vite/**/*.tsx)
|
|||||||
build: build-web build-desktop
|
build: build-web build-desktop
|
||||||
|
|
||||||
.PHONY: build-web
|
.PHONY: build-web
|
||||||
build-web: public/kcl_wasm_lib_bg.wasm build/index.html
|
build-web: install public/kcl_wasm_lib_bg.wasm build/index.html
|
||||||
|
|
||||||
.PHONY: build-desktop
|
.PHONY: build-desktop
|
||||||
build-desktop: public/kcl_wasm_lib_bg.wasm .vite/build/main.js
|
build-desktop: install public/kcl_wasm_lib_bg.wasm .vite/build/main.js
|
||||||
|
|
||||||
public/kcl_wasm_lib_bg.wasm: $(CARGO_SOURCES)$(RUST_SOURCES)
|
public/kcl_wasm_lib_bg.wasm: $(CARGO_SOURCES) $(RUST_SOURCES)
|
||||||
yarn build:wasm
|
ifeq ($(OS),Windows_NT)
|
||||||
|
yarn build:wasm:dev:windows
|
||||||
|
else
|
||||||
|
yarn build:wasm:dev
|
||||||
|
endif
|
||||||
|
|
||||||
build/index.html: $(REACT_SOURCES) $(TYPESCRIPT_SOURCES) $(VITE_SOURCES)
|
build/index.html: $(REACT_SOURCES) $(TYPESCRIPT_SOURCES) $(VITE_SOURCES)
|
||||||
yarn build:local
|
yarn build:local
|
||||||
@ -63,8 +87,10 @@ lint: install ## Lint the code
|
|||||||
###############################################################################
|
###############################################################################
|
||||||
# RUN
|
# RUN
|
||||||
|
|
||||||
|
TARGET ?= desktop
|
||||||
|
|
||||||
.PHONY: run
|
.PHONY: run
|
||||||
run: run-web
|
run: run-$(TARGET)
|
||||||
|
|
||||||
.PHONY: run-web
|
.PHONY: run-web
|
||||||
run-web: install build-web ## Start the web app
|
run-web: install build-web ## Start the web app
|
||||||
@ -77,9 +103,9 @@ run-desktop: install build-desktop ## Start the desktop app
|
|||||||
###############################################################################
|
###############################################################################
|
||||||
# TEST
|
# TEST
|
||||||
|
|
||||||
E2E_WORKERS ?= 1
|
E2E_GREP ?=
|
||||||
|
E2E_WORKERS ?=
|
||||||
E2E_FAILURES ?= 1
|
E2E_FAILURES ?= 1
|
||||||
E2E_GREP ?= ""
|
|
||||||
|
|
||||||
.PHONY: test
|
.PHONY: test
|
||||||
test: test-unit test-e2e
|
test: test-unit test-e2e
|
||||||
@ -90,31 +116,47 @@ test-unit: install ## Run the unit tests
|
|||||||
yarn test:unit
|
yarn test:unit
|
||||||
|
|
||||||
.PHONY: test-e2e
|
.PHONY: test-e2e
|
||||||
test-e2e: test-e2e-desktop
|
test-e2e: test-e2e-$(TARGET)
|
||||||
|
|
||||||
.PHONY: test-e2e-web
|
.PHONY: test-e2e-web
|
||||||
test-e2e-web: install build-web ## Run the web e2e tests
|
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 )
|
@ curl -fs localhost:3000 >/dev/null || ( echo "Error: localhost:3000 not available, 'make run-web' first" && exit 1 )
|
||||||
yarn chrome:test --headed --workers=$(E2E_WORKERS) --max-failures=$(E2E_FAILURES) --grep=$(E2E_GREP)
|
ifdef E2E_GREP
|
||||||
|
yarn chrome:test --headed --grep="$(E2E_GREP)" --max-failures=$(E2E_FAILURES)
|
||||||
|
else
|
||||||
|
yarn chrome:test --headed --workers='100%'
|
||||||
|
endif
|
||||||
|
|
||||||
.PHONY: test-e2e-desktop
|
.PHONY: test-e2e-desktop
|
||||||
test-e2e-desktop: install build-desktop ## Run the desktop e2e tests
|
test-e2e-desktop: install build-desktop ## Run the desktop e2e tests
|
||||||
yarn test:playwright:electron --workers=$(E2E_WORKERS) --max-failures=$(E2E_FAILURES) --grep=$(E2E_GREP)
|
ifdef E2E_GREP
|
||||||
|
yarn test:playwright:electron --grep="$(E2E_GREP)" --max-failures=$(E2E_FAILURES)
|
||||||
|
else
|
||||||
|
yarn test:playwright:electron --workers='100%'
|
||||||
|
endif
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# CLEAN
|
# CLEAN
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean: ## Delete all artifacts
|
clean: ## Delete all artifacts
|
||||||
|
ifeq ($(OS),Windows_NT)
|
||||||
|
git clean --force -d -X
|
||||||
|
else
|
||||||
rm -rf .vite/ build/
|
rm -rf .vite/ build/
|
||||||
rm -rf trace.zip playwright-report/ test-results/
|
rm -rf trace.zip playwright-report/ test-results/
|
||||||
rm -rf public/kcl_wasm_lib_bg.wasm
|
rm -rf public/kcl_wasm_lib_bg.wasm
|
||||||
rm -rf rust/*/bindings/ rust/*/pkg/ rust/target/
|
rm -rf rust/*/bindings/ rust/*/pkg/ rust/target/
|
||||||
rm -rf node_modules/ rust/*/node_modules/
|
rm -rf node_modules/ rust/*/node_modules/
|
||||||
|
endif
|
||||||
|
|
||||||
.PHONY: help
|
.PHONY: help
|
||||||
help: install
|
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}'
|
@ grep -E '^[^[:space:]]+:.*## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
|
||||||
|
endif
|
||||||
|
|
||||||
.DEFAULT_GOAL := help
|
.DEFAULT_GOAL := help
|
||||||
|
|
||||||
|
16
README.md
16
README.md
@ -1,17 +1,17 @@
|
|||||||

|

|
||||||
|
|
||||||
## Zoo Modeling App
|
## Zoo Design Studio
|
||||||
|
|
||||||
download at [zoo.dev/modeling-app/download](https://zoo.dev/modeling-app/download)
|
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).
|
A CAD application from the future, brought to you by the [Zoo team](https://zoo.dev).
|
||||||
|
|
||||||
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:
|
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:
|
||||||
|
|
||||||
- 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"
|
- 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
|
- 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
|
- 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 Modeling App
|
- 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
|
||||||
- Everything graphics _has_ to be built for the GPU
|
- 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
|
- 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
|
- Make the resource-intensive pieces of an application auto-scaling
|
||||||
@ -19,9 +19,9 @@ Modeling App is our take on what a modern modelling experience can be. It is app
|
|||||||
|
|
||||||
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!
|
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!
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
## Tools
|
## Tools
|
||||||
|
|
||||||
@ -198,13 +198,13 @@ If the prompt doesn't show up, start the app in command line to grab the electro
|
|||||||
|
|
||||||
```
|
```
|
||||||
# Windows (PowerShell)
|
# Windows (PowerShell)
|
||||||
& 'C:\Program Files\Zoo Modeling App\Zoo Modeling App.exe'
|
& 'C:\Program Files\Zoo Design Studio\Zoo Design Studio.exe'
|
||||||
|
|
||||||
# macOS
|
# macOS
|
||||||
/Applications/Zoo\ Modeling\ App.app/Contents/MacOS/Zoo\ Modeling\ App
|
/Applications/Zoo\ Modeling\ App.app/Contents/MacOS/Zoo\ Modeling\ App
|
||||||
|
|
||||||
# Linux
|
# Linux
|
||||||
./Zoo Modeling App-{version}-{arch}-linux.AppImage
|
./Zoo Design Studio-{version}-{arch}-linux.AppImage
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 4. Publish the release
|
#### 4. Publish the release
|
||||||
|
@ -54,7 +54,7 @@ example = extrude(exampleSketch, length = 5)
|
|||||||
// Add color to a revolved solid.
|
// Add color to a revolved solid.
|
||||||
sketch001 = startSketchOn(XY)
|
sketch001 = startSketchOn(XY)
|
||||||
|> circle(center = [15, 0], radius = 5)
|
|> circle(center = [15, 0], radius = 5)
|
||||||
|> revolve(angle = 360, axis = 'y')
|
|> revolve(angle = 360, axis = Y)
|
||||||
|> appearance(color = '#ff0000', metalness = 90, roughness = 90)
|
|> appearance(color = '#ff0000', metalness = 90, roughness = 90)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -9,13 +9,12 @@ layout: manual
|
|||||||
|
|
||||||
### `std`
|
### `std`
|
||||||
|
|
||||||
- [`HALF_TURN`](/docs/kcl/consts/std-HALF_TURN)
|
- [`X`](/docs/kcl/consts/std-X)
|
||||||
- [`QUARTER_TURN`](/docs/kcl/consts/std-QUARTER_TURN)
|
|
||||||
- [`THREE_QUARTER_TURN`](/docs/kcl/consts/std-THREE_QUARTER_TURN)
|
|
||||||
- [`XY`](/docs/kcl/consts/std-XY)
|
- [`XY`](/docs/kcl/consts/std-XY)
|
||||||
- [`XZ`](/docs/kcl/consts/std-XZ)
|
- [`XZ`](/docs/kcl/consts/std-XZ)
|
||||||
|
- [`Y`](/docs/kcl/consts/std-Y)
|
||||||
- [`YZ`](/docs/kcl/consts/std-YZ)
|
- [`YZ`](/docs/kcl/consts/std-YZ)
|
||||||
- [`ZERO`](/docs/kcl/consts/std-ZERO)
|
- [`Z`](/docs/kcl/consts/std-Z)
|
||||||
|
|
||||||
### `std::math`
|
### `std::math`
|
||||||
|
|
||||||
@ -23,3 +22,10 @@ layout: manual
|
|||||||
- [`PI`](/docs/kcl/consts/std-math-PI)
|
- [`PI`](/docs/kcl/consts/std-math-PI)
|
||||||
- [`TAU`](/docs/kcl/consts/std-math-TAU)
|
- [`TAU`](/docs/kcl/consts/std-math-TAU)
|
||||||
|
|
||||||
|
### `std::turns`
|
||||||
|
|
||||||
|
- [`HALF_TURN`](/docs/kcl/consts/std-turns-HALF_TURN)
|
||||||
|
- [`QUARTER_TURN`](/docs/kcl/consts/std-turns-QUARTER_TURN)
|
||||||
|
- [`THREE_QUARTER_TURN`](/docs/kcl/consts/std-turns-THREE_QUARTER_TURN)
|
||||||
|
- [`ZERO`](/docs/kcl/consts/std-turns-ZERO)
|
||||||
|
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
---
|
|
||||||
title: "std::HALF_TURN"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
```js
|
|
||||||
std::HALF_TURN: number(deg) = 180deg
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
|||||||
---
|
|
||||||
title: "std::QUARTER_TURN"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
```js
|
|
||||||
std::QUARTER_TURN: number(deg) = 90deg
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
|||||||
---
|
|
||||||
title: "std::THREE_QUARTER_TURN"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
```js
|
|
||||||
std::THREE_QUARTER_TURN: number(deg) = 270deg
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
title: "std::ZERO"
|
title: "std::X"
|
||||||
excerpt: ""
|
excerpt: ""
|
||||||
layout: manual
|
layout: manual
|
||||||
---
|
---
|
||||||
@ -9,7 +9,7 @@ layout: manual
|
|||||||
|
|
||||||
|
|
||||||
```js
|
```js
|
||||||
std::ZERO: number = 0
|
std::X
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
15
docs/kcl/consts/std-Y.md
Normal file
15
docs/kcl/consts/std-Y.md
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
title: "std::Y"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```js
|
||||||
|
std::Y
|
||||||
|
```
|
||||||
|
|
||||||
|
|
15
docs/kcl/consts/std-Z.md
Normal file
15
docs/kcl/consts/std-Z.md
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
title: "std::Z"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```js
|
||||||
|
std::Z
|
||||||
|
```
|
||||||
|
|
||||||
|
|
@ -18,7 +18,7 @@ std::math::PI: number = 3.14159265358979323846264338327950288_
|
|||||||
circumference = 70
|
circumference = 70
|
||||||
|
|
||||||
exampleSketch = startSketchOn(XZ)
|
exampleSketch = startSketchOn(XZ)
|
||||||
|> circle(center = [0, 0], radius = circumference/ (2 * PI))
|
|> circle(center = [0, 0], radius = circumference / (2 * PI))
|
||||||
|
|
||||||
example = extrude(exampleSketch, length = 5)
|
example = extrude(exampleSketch, length = 5)
|
||||||
```
|
```
|
||||||
|
15
docs/kcl/consts/std-turns-HALF_TURN.md
Normal file
15
docs/kcl/consts/std-turns-HALF_TURN.md
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
title: "std::turns::HALF_TURN"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```js
|
||||||
|
std::turns::HALF_TURN: number(deg) = 180deg
|
||||||
|
```
|
||||||
|
|
||||||
|
|
15
docs/kcl/consts/std-turns-QUARTER_TURN.md
Normal file
15
docs/kcl/consts/std-turns-QUARTER_TURN.md
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
title: "std::turns::QUARTER_TURN"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```js
|
||||||
|
std::turns::QUARTER_TURN: number(deg) = 90deg
|
||||||
|
```
|
||||||
|
|
||||||
|
|
15
docs/kcl/consts/std-turns-THREE_QUARTER_TURN.md
Normal file
15
docs/kcl/consts/std-turns-THREE_QUARTER_TURN.md
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
title: "std::turns::THREE_QUARTER_TURN"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```js
|
||||||
|
std::turns::THREE_QUARTER_TURN: number(deg) = 270deg
|
||||||
|
```
|
||||||
|
|
||||||
|
|
15
docs/kcl/consts/std-turns-ZERO.md
Normal file
15
docs/kcl/consts/std-turns-ZERO.md
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
title: "std::turns::ZERO"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```js
|
||||||
|
std::turns::ZERO: number = 0
|
||||||
|
```
|
||||||
|
|
||||||
|
|
45
docs/kcl/fromCm.md
Normal file
45
docs/kcl/fromCm.md
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -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.
|
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 Modeling App.
|
Note: The import command currently only works when using the native Design Studio.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import(
|
import(
|
||||||
|
File diff suppressed because one or more lines are too long
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: "KCL Standard Library"
|
title: "KCL Standard Library"
|
||||||
excerpt: "Documentation for the KCL standard library for the Zoo Modeling App."
|
excerpt: "Documentation for the KCL standard library for the Zoo Design Studio."
|
||||||
layout: manual
|
layout: manual
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -22,20 +22,22 @@ layout: manual
|
|||||||
* [`string`](kcl/types/string)
|
* [`string`](kcl/types/string)
|
||||||
* [`tag`](kcl/types/tag)
|
* [`tag`](kcl/types/tag)
|
||||||
* **std**
|
* **std**
|
||||||
|
* [`Axis2d`](kcl/types/Axis2d)
|
||||||
|
* [`Axis3d`](kcl/types/Axis3d)
|
||||||
|
* [`Edge`](kcl/types/Edge)
|
||||||
* [`Face`](kcl/types/Face)
|
* [`Face`](kcl/types/Face)
|
||||||
* [`HALF_TURN`](kcl/consts/std-HALF_TURN)
|
|
||||||
* [`Helix`](kcl/types/Helix)
|
* [`Helix`](kcl/types/Helix)
|
||||||
* [`Plane`](kcl/types/Plane)
|
* [`Plane`](kcl/types/Plane)
|
||||||
* [`Point2d`](kcl/types/Point2d)
|
* [`Point2d`](kcl/types/Point2d)
|
||||||
* [`Point3d`](kcl/types/Point3d)
|
* [`Point3d`](kcl/types/Point3d)
|
||||||
* [`QUARTER_TURN`](kcl/consts/std-QUARTER_TURN)
|
|
||||||
* [`Sketch`](kcl/types/Sketch)
|
* [`Sketch`](kcl/types/Sketch)
|
||||||
* [`Solid`](kcl/types/Solid)
|
* [`Solid`](kcl/types/Solid)
|
||||||
* [`THREE_QUARTER_TURN`](kcl/consts/std-THREE_QUARTER_TURN)
|
* [`X`](kcl/consts/std-X)
|
||||||
* [`XY`](kcl/consts/std-XY)
|
* [`XY`](kcl/consts/std-XY)
|
||||||
* [`XZ`](kcl/consts/std-XZ)
|
* [`XZ`](kcl/consts/std-XZ)
|
||||||
|
* [`Y`](kcl/consts/std-Y)
|
||||||
* [`YZ`](kcl/consts/std-YZ)
|
* [`YZ`](kcl/consts/std-YZ)
|
||||||
* [`ZERO`](kcl/consts/std-ZERO)
|
* [`Z`](kcl/consts/std-Z)
|
||||||
* [`abs`](kcl/abs)
|
* [`abs`](kcl/abs)
|
||||||
* [`acos`](kcl/acos)
|
* [`acos`](kcl/acos)
|
||||||
* [`angleToMatchLengthX`](kcl/angleToMatchLengthX)
|
* [`angleToMatchLengthX`](kcl/angleToMatchLengthX)
|
||||||
@ -63,19 +65,22 @@ layout: manual
|
|||||||
* [`chamfer`](kcl/chamfer)
|
* [`chamfer`](kcl/chamfer)
|
||||||
* [`circleThreePoint`](kcl/circleThreePoint)
|
* [`circleThreePoint`](kcl/circleThreePoint)
|
||||||
* [`close`](kcl/close)
|
* [`close`](kcl/close)
|
||||||
* [`cm`](kcl/cm)
|
|
||||||
* [`extrude`](kcl/extrude)
|
* [`extrude`](kcl/extrude)
|
||||||
* [`fillet`](kcl/fillet)
|
* [`fillet`](kcl/fillet)
|
||||||
* [`floor`](kcl/floor)
|
* [`floor`](kcl/floor)
|
||||||
* [`ft`](kcl/ft)
|
* [`fromCm`](kcl/fromCm)
|
||||||
|
* [`fromFt`](kcl/fromFt)
|
||||||
|
* [`fromInches`](kcl/fromInches)
|
||||||
|
* [`fromM`](kcl/fromM)
|
||||||
|
* [`fromMm`](kcl/fromMm)
|
||||||
|
* [`fromYd`](kcl/fromYd)
|
||||||
* [`getCommonEdge`](kcl/getCommonEdge)
|
* [`getCommonEdge`](kcl/getCommonEdge)
|
||||||
* [`getNextAdjacentEdge`](kcl/getNextAdjacentEdge)
|
* [`getNextAdjacentEdge`](kcl/getNextAdjacentEdge)
|
||||||
* [`getOppositeEdge`](kcl/getOppositeEdge)
|
* [`getOppositeEdge`](kcl/getOppositeEdge)
|
||||||
* [`getPreviousAdjacentEdge`](kcl/getPreviousAdjacentEdge)
|
* [`getPreviousAdjacentEdge`](kcl/getPreviousAdjacentEdge)
|
||||||
* [`helix`](kcl/helix)
|
* [`helix`](kcl/std-helix)
|
||||||
* [`hole`](kcl/hole)
|
* [`hole`](kcl/hole)
|
||||||
* [`hollow`](kcl/hollow)
|
* [`hollow`](kcl/hollow)
|
||||||
* [`inch`](kcl/inch)
|
|
||||||
* [`lastSegX`](kcl/lastSegX)
|
* [`lastSegX`](kcl/lastSegX)
|
||||||
* [`lastSegY`](kcl/lastSegY)
|
* [`lastSegY`](kcl/lastSegY)
|
||||||
* [`legAngX`](kcl/legAngX)
|
* [`legAngX`](kcl/legAngX)
|
||||||
@ -87,12 +92,9 @@ layout: manual
|
|||||||
* [`log`](kcl/log)
|
* [`log`](kcl/log)
|
||||||
* [`log10`](kcl/log10)
|
* [`log10`](kcl/log10)
|
||||||
* [`log2`](kcl/log2)
|
* [`log2`](kcl/log2)
|
||||||
* [`m`](kcl/m)
|
|
||||||
* [`map`](kcl/map)
|
* [`map`](kcl/map)
|
||||||
* [`max`](kcl/max)
|
* [`max`](kcl/max)
|
||||||
* [`min`](kcl/min)
|
* [`min`](kcl/min)
|
||||||
* [`mirror2d`](kcl/mirror2d)
|
|
||||||
* [`mm`](kcl/mm)
|
|
||||||
* [`offsetPlane`](kcl/offsetPlane)
|
* [`offsetPlane`](kcl/offsetPlane)
|
||||||
* [`patternCircular2d`](kcl/patternCircular2d)
|
* [`patternCircular2d`](kcl/patternCircular2d)
|
||||||
* [`patternCircular3d`](kcl/patternCircular3d)
|
* [`patternCircular3d`](kcl/patternCircular3d)
|
||||||
@ -100,7 +102,6 @@ layout: manual
|
|||||||
* [`patternLinear3d`](kcl/patternLinear3d)
|
* [`patternLinear3d`](kcl/patternLinear3d)
|
||||||
* [`patternTransform`](kcl/patternTransform)
|
* [`patternTransform`](kcl/patternTransform)
|
||||||
* [`patternTransform2d`](kcl/patternTransform2d)
|
* [`patternTransform2d`](kcl/patternTransform2d)
|
||||||
* [`polar`](kcl/polar)
|
|
||||||
* [`polygon`](kcl/polygon)
|
* [`polygon`](kcl/polygon)
|
||||||
* [`pop`](kcl/pop)
|
* [`pop`](kcl/pop)
|
||||||
* [`pow`](kcl/pow)
|
* [`pow`](kcl/pow)
|
||||||
@ -110,7 +111,7 @@ layout: manual
|
|||||||
* [`push`](kcl/push)
|
* [`push`](kcl/push)
|
||||||
* [`reduce`](kcl/reduce)
|
* [`reduce`](kcl/reduce)
|
||||||
* [`rem`](kcl/rem)
|
* [`rem`](kcl/rem)
|
||||||
* [`revolve`](kcl/revolve)
|
* [`revolve`](kcl/std-revolve)
|
||||||
* [`rotate`](kcl/rotate)
|
* [`rotate`](kcl/rotate)
|
||||||
* [`round`](kcl/round)
|
* [`round`](kcl/round)
|
||||||
* [`scale`](kcl/scale)
|
* [`scale`](kcl/scale)
|
||||||
@ -136,13 +137,19 @@ layout: manual
|
|||||||
* [`translate`](kcl/translate)
|
* [`translate`](kcl/translate)
|
||||||
* [`xLine`](kcl/xLine)
|
* [`xLine`](kcl/xLine)
|
||||||
* [`yLine`](kcl/yLine)
|
* [`yLine`](kcl/yLine)
|
||||||
* [`yd`](kcl/yd)
|
|
||||||
* **std::math**
|
* **std::math**
|
||||||
* [`E`](kcl/consts/std-math-E)
|
* [`E`](kcl/consts/std-math-E)
|
||||||
* [`PI`](kcl/consts/std-math-PI)
|
* [`PI`](kcl/consts/std-math-PI)
|
||||||
* [`TAU`](kcl/consts/std-math-TAU)
|
* [`TAU`](kcl/consts/std-math-TAU)
|
||||||
* [`cos`](kcl/std-math-cos)
|
* [`cos`](kcl/std-math-cos)
|
||||||
|
* [`polar`](kcl/std-math-polar)
|
||||||
* [`sin`](kcl/std-math-sin)
|
* [`sin`](kcl/std-math-sin)
|
||||||
* [`tan`](kcl/std-math-tan)
|
* [`tan`](kcl/std-math-tan)
|
||||||
* **std::sketch**
|
* **std::sketch**
|
||||||
* [`circle`](kcl/std-sketch-circle)
|
* [`circle`](kcl/std-sketch-circle)
|
||||||
|
* [`mirror2d`](kcl/std-sketch-mirror2d)
|
||||||
|
* **std::turns**
|
||||||
|
* [`turns::HALF_TURN`](kcl/consts/std-turns-HALF_TURN)
|
||||||
|
* [`turns::QUARTER_TURN`](kcl/consts/std-turns-QUARTER_TURN)
|
||||||
|
* [`turns::THREE_QUARTER_TURN`](kcl/consts/std-turns-THREE_QUARTER_TURN)
|
||||||
|
* [`turns::ZERO`](kcl/consts/std-turns-ZERO)
|
||||||
|
File diff suppressed because one or more lines are too long
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: "KCL Known Issues"
|
title: "KCL Known Issues"
|
||||||
excerpt: "Known issues with the KCL standard library for the Zoo Modeling App."
|
excerpt: "Known issues with the KCL standard library for the Zoo Design Studio."
|
||||||
layout: manual
|
layout: manual
|
||||||
---
|
---
|
||||||
|
|
||||||
|
File diff suppressed because one or more lines are too long
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: "KCL Modules"
|
title: "KCL Modules"
|
||||||
excerpt: "Documentation of modules for the KCL language for the Zoo Modeling App."
|
excerpt: "Documentation of modules for the KCL language for the Zoo Design Studio."
|
||||||
layout: manual
|
layout: manual
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -95,7 +95,7 @@ import "tests/inputs/cube.obj"
|
|||||||
When importing a GLTF file, the bin file will be imported as well.
|
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
|
Import paths are relative to the current project directory. Imports currently only work when
|
||||||
using the native Modeling App, not in the browser.
|
using the native Design Studio, not in the browser.
|
||||||
|
|
||||||
### Supported values
|
### Supported values
|
||||||
|
|
||||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,12 +1,12 @@
|
|||||||
---
|
---
|
||||||
title: "KCL Settings"
|
title: "KCL Settings"
|
||||||
excerpt: "Documentation of settings for the KCL language and Zoo Modeling App."
|
excerpt: "Documentation of settings for the KCL language and Zoo Design Studio."
|
||||||
layout: manual
|
layout: manual
|
||||||
---
|
---
|
||||||
|
|
||||||
# KCL Settings
|
# KCL Settings
|
||||||
|
|
||||||
There are three levels of settings available in the KittyCAD modeling application:
|
There are three levels of settings available in Zoo Design Studio:
|
||||||
|
|
||||||
1. [User Settings](/docs/kcl/settings/user): Global settings that apply to all projects, stored in `user.toml`
|
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`
|
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 modeling applicatio
|
|||||||
|
|
||||||
## Configuration Files
|
## Configuration Files
|
||||||
|
|
||||||
The KittyCAD modeling app uses TOML files for configuration:
|
Zoo Design Studio uses TOML files for configuration:
|
||||||
|
|
||||||
* **User Settings**: `user.toml` - See [complete documentation](/docs/kcl/settings/user)
|
* **User Settings**: `user.toml` - See [complete documentation](/docs/kcl/settings/user)
|
||||||
* **Project Settings**: `project.toml` - See [complete documentation](/docs/kcl/settings/project)
|
* **Project Settings**: `project.toml` - See [complete documentation](/docs/kcl/settings/project)
|
||||||
|
@ -35,7 +35,7 @@ base_unit = "in"
|
|||||||
|
|
||||||
#### app
|
#### app
|
||||||
|
|
||||||
The settings for the modeling app.
|
The settings for the Design Studio.
|
||||||
|
|
||||||
|
|
||||||
**Default:** None
|
**Default:** None
|
||||||
|
@ -37,7 +37,7 @@ text_wrapping = false
|
|||||||
|
|
||||||
#### app
|
#### app
|
||||||
|
|
||||||
The settings for the modeling app.
|
The settings for the Design Studio.
|
||||||
|
|
||||||
|
|
||||||
**Default:** None
|
**Default:** None
|
||||||
@ -96,7 +96,7 @@ Permanently dismiss the banner warning to download the desktop app. This setting
|
|||||||
|
|
||||||
##### stream_idle_mode
|
##### stream_idle_mode
|
||||||
|
|
||||||
When the user is idle, and this is true, the stream will be torn down.
|
When the user is idle, teardown the stream after some time.
|
||||||
|
|
||||||
|
|
||||||
**Default:** None
|
**Default:** None
|
||||||
|
@ -146,7 +146,7 @@ exampleSketch = startSketchOn(XY)
|
|||||||
|> line(end = [-2, 0])
|
|> line(end = [-2, 0])
|
||||||
|> close()
|
|> close()
|
||||||
|
|
||||||
example = revolve(exampleSketch, axis = 'y', angle = 180)
|
example = revolve(exampleSketch, axis = Y, angle = 180)
|
||||||
|
|
||||||
exampleSketch002 = startSketchOn(example, 'end')
|
exampleSketch002 = startSketchOn(example, 'end')
|
||||||
|> startProfileAt([4.5, -5], %)
|
|> startProfileAt([4.5, -5], %)
|
||||||
@ -177,7 +177,7 @@ exampleSketch = startSketchOn(XY)
|
|||||||
|
|
||||||
example = revolve(
|
example = revolve(
|
||||||
exampleSketch,
|
exampleSketch,
|
||||||
axis = 'y',
|
axis = Y,
|
||||||
angle = 180,
|
angle = 180,
|
||||||
tagEnd = $end01,
|
tagEnd = $end01,
|
||||||
)
|
)
|
||||||
|
116
docs/kcl/std-helix.md
Normal file
116
docs/kcl/std-helix.md
Normal file
File diff suppressed because one or more lines are too long
44
docs/kcl/std-math-polar.md
Normal file
44
docs/kcl/std-math-polar.md
Normal file
File diff suppressed because one or more lines are too long
246
docs/kcl/std-revolve.md
Normal file
246
docs/kcl/std-revolve.md
Normal file
File diff suppressed because one or more lines are too long
@ -17,7 +17,7 @@ circle(@sketch_or_surface: Sketch | Plane | Face, center: Point2d, radius: numbe
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `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 |
|
| `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 |
|
||||||
| `center` | [`Point2d`](/docs/kcl/types/Point2d) | The center of the circle. | 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 |
|
| `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 |
|
| [`tag`](/docs/kcl/types/tag) | [`tag`](/docs/kcl/types/tag) | Create a new tag which refers to this circle. | No |
|
||||||
|
119
docs/kcl/std-sketch-mirror2d.md
Normal file
119
docs/kcl/std-sketch-mirror2d.md
Normal file
File diff suppressed because one or more lines are too long
24739
docs/kcl/std.json
24739
docs/kcl/std.json
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@ -74,7 +74,7 @@ helixPath = helix(
|
|||||||
revolutions = 4,
|
revolutions = 4,
|
||||||
length = 10,
|
length = 10,
|
||||||
radius = 5,
|
radius = 5,
|
||||||
axis = 'Z',
|
axis = Z,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Create a spring by sweeping around the helix path.
|
// Create a spring by sweeping around the helix path.
|
||||||
|
File diff suppressed because one or more lines are too long
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: "KCL Types"
|
title: "KCL Types"
|
||||||
excerpt: "Documentation of types for the KCL standard library for the Zoo Modeling App."
|
excerpt: "Documentation of types for the KCL standard library for the Zoo Design Studio."
|
||||||
layout: manual
|
layout: manual
|
||||||
---
|
---
|
||||||
|
|
||||||
|
12
docs/kcl/types/Axis2d.md
Normal file
12
docs/kcl/types/Axis2d.md
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
title: "std::Axis2d"
|
||||||
|
excerpt: "An infinite line in 2d space."
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
An infinite line in 2d space.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
12
docs/kcl/types/Axis3d.md
Normal file
12
docs/kcl/types/Axis3d.md
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
title: "std::Axis3d"
|
||||||
|
excerpt: "An infinite line in 3d space."
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
An infinite line in 3d space.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
12
docs/kcl/types/Edge.md
Normal file
12
docs/kcl/types/Edge.md
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
title: "std::Edge"
|
||||||
|
excerpt: "The edge of a solid."
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
The edge of a solid.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -28,7 +28,7 @@ An extrude plane.
|
|||||||
| `faceId` |[`string`](/docs/kcl/types/string)| The face id for the extrude plane. | No |
|
| `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 |
|
| [`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 |
|
| `id` |[`string`](/docs/kcl/types/string)| The id of the geometry. | No |
|
||||||
| `sourceRange` |[`SourceRange`](/docs/kcl/types/SourceRange)| The source range. | No |
|
| `sourceRange` |`[integer, integer, integer]`| 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 |
|
| `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 |
|
| [`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 |
|
| `id` |[`string`](/docs/kcl/types/string)| The id of the geometry. | No |
|
||||||
| `sourceRange` |[`SourceRange`](/docs/kcl/types/SourceRange)| The source range. | No |
|
| `sourceRange` |`[integer, integer, integer]`| The source range. | No |
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
@ -68,7 +68,7 @@ Geometry metadata.
|
|||||||
| `faceId` |[`string`](/docs/kcl/types/string)| The id for the chamfer surface. | No |
|
| `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 |
|
| [`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 |
|
| `id` |[`string`](/docs/kcl/types/string)| The id of the geometry. | No |
|
||||||
| `sourceRange` |[`SourceRange`](/docs/kcl/types/SourceRange)| The source range. | No |
|
| `sourceRange` |`[integer, integer, integer]`| The source range. | No |
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
@ -88,7 +88,7 @@ Geometry metadata.
|
|||||||
| `faceId` |[`string`](/docs/kcl/types/string)| The id for the fillet surface. | No |
|
| `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 |
|
| [`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 |
|
| `id` |[`string`](/docs/kcl/types/string)| The id of the geometry. | No |
|
||||||
| `sourceRange` |[`SourceRange`](/docs/kcl/types/SourceRange)| The source range. | No |
|
| `sourceRange` |`[integer, integer, integer]`| The source range. | No |
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
|
@ -17,6 +17,6 @@ Geometry metadata.
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `id` |[`string`](/docs/kcl/types/string)| The id of the geometry. | No |
|
| `id` |[`string`](/docs/kcl/types/string)| The id of the geometry. | No |
|
||||||
| `sourceRange` |[`SourceRange`](/docs/kcl/types/SourceRange)| The source range. | No |
|
| `sourceRange` |`[integer, integer, integer]`| The source range. | No |
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ A helix.
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `value` |[`string`](/docs/kcl/types/string)| The id of the helix. | No |
|
| `value` |[`string`](/docs/kcl/types/string)| The id of the helix. | No |
|
||||||
| `artifactId` |[`ArtifactId`](/docs/kcl/types/ArtifactId)| The artifact ID. | No |
|
| `artifactId` |[`string`](/docs/kcl/types/string)| The artifact ID. | No |
|
||||||
| `revolutions` |[`number`](/docs/kcl/types/number)| Number of revolutions. | No |
|
| `revolutions` |[`number`](/docs/kcl/types/number)| Number of revolutions. | No |
|
||||||
| `angleStart` |[`number`](/docs/kcl/types/number)| Start angle (in degrees). | No |
|
| `angleStart` |[`number`](/docs/kcl/types/number)| Start angle (in degrees). | No |
|
||||||
| `ccw` |`boolean`| Is the helix rotation counter clockwise? | No |
|
| `ccw` |`boolean`| Is the helix rotation counter clockwise? | No |
|
||||||
|
@ -285,7 +285,7 @@ Data for an imported geometry.
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: `Module`| | No |
|
| `type` |enum: `Module`| | No |
|
||||||
| `value` |[`ModuleId`](/docs/kcl/types/ModuleId)| Identifier of a source file. Uses a u32 to keep the size small. | No |
|
| `value` |`integer`| Identifier of a source file. Uses a u32 to keep the size small. | No |
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
---
|
|
||||||
title: "PolarCoordsData"
|
|
||||||
excerpt: "Data for polar coordinates."
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
Data for polar coordinates.
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `angle` |[`number`](/docs/kcl/types/number)| The angle of the line (in degrees). | No |
|
|
||||||
| `length` |[`TyF64`](/docs/kcl/types/TyF64)| The length of the line. | No |
|
|
||||||
|
|
||||||
|
|
@ -25,7 +25,7 @@ A sketch type.
|
|||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: `plane`| | No |
|
| `type` |enum: `plane`| | No |
|
||||||
| `id` |[`string`](/docs/kcl/types/string)| The id of the plane. | No |
|
| `id` |[`string`](/docs/kcl/types/string)| The id of the plane. | No |
|
||||||
| `artifactId` |[`ArtifactId`](/docs/kcl/types/ArtifactId)| The artifact ID. | No |
|
| `artifactId` |[`string`](/docs/kcl/types/string)| The artifact ID. | No |
|
||||||
| `value` |[`PlaneType`](/docs/kcl/types/PlaneType)| Type for a plane. | No |
|
| `value` |[`PlaneType`](/docs/kcl/types/PlaneType)| Type for a plane. | No |
|
||||||
| `origin` |[`Point3d`](/docs/kcl/types/Point3d)| Origin of the 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 |
|
| `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 |
|
| `type` |enum: `face`| | No |
|
||||||
| `id` |[`string`](/docs/kcl/types/string)| The id of the face. | No |
|
| `id` |[`string`](/docs/kcl/types/string)| The id of the face. | No |
|
||||||
| `artifactId` |[`ArtifactId`](/docs/kcl/types/ArtifactId)| The artifact ID. | No |
|
| `artifactId` |[`string`](/docs/kcl/types/string)| The artifact ID. | No |
|
||||||
| `value` |[`string`](/docs/kcl/types/string)| The tag of the face. | 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 |
|
| `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 |
|
| `yAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the face's Y axis be? | No |
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
---
|
|
||||||
title: "SourceRange"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
**Type:** `integer` (`uint`)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -5,17 +5,11 @@ layout: manual
|
|||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
**Type:** `object`
|
**Type:** [`number`](/docs/kcl/types/number) (`double`)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `n` |[`number`](/docs/kcl/types/number)| | No |
|
|
||||||
| `ty` |[`NumericType`](/docs/kcl/types/NumericType)| | No |
|
|
||||||
|
|
||||||
|
|
||||||
|
File diff suppressed because one or more lines are too long
@ -1,4 +1,4 @@
|
|||||||
import { test, expect } from './zoo-test'
|
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||||
|
|
||||||
test.describe('Electron app header tests', () => {
|
test.describe('Electron app header tests', () => {
|
||||||
test(
|
test(
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
import { Page } from '@playwright/test'
|
import type { Page } from '@playwright/test'
|
||||||
import { test, expect } from './zoo-test'
|
|
||||||
|
import type { HomePageFixture } from '@e2e/playwright/fixtures/homePageFixture'
|
||||||
import {
|
import {
|
||||||
getUtils,
|
PERSIST_MODELING_CONTEXT,
|
||||||
TEST_COLORS,
|
TEST_COLORS,
|
||||||
commonPoints,
|
commonPoints,
|
||||||
PERSIST_MODELING_CONTEXT,
|
getUtils,
|
||||||
orRunWhenFullSuiteEnabled,
|
orRunWhenFullSuiteEnabled,
|
||||||
} from './test-utils'
|
} from '@e2e/playwright/test-utils'
|
||||||
import { HomePageFixture } from './fixtures/homePageFixture'
|
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||||
|
|
||||||
test.setTimeout(120000)
|
test.setTimeout(120000)
|
||||||
|
|
||||||
@ -85,7 +86,7 @@ async function doBasicSketch(
|
|||||||
await page.mouse.click(startXPx, 500 - PUR * 20)
|
await page.mouse.click(startXPx, 500 - PUR * 20)
|
||||||
if (openPanes.includes('code')) {
|
if (openPanes.includes('code')) {
|
||||||
await expect(u.codeLocator)
|
await expect(u.codeLocator)
|
||||||
.toHaveText(`sketch001 = startSketchOn(XZ)profile001 = startProfileAt(${
|
.toHaveText(`@settings(defaultLengthUnit = in)sketch001 = startSketchOn(XZ)profile001 = startProfileAt(${
|
||||||
commonPoints.startAt
|
commonPoints.startAt
|
||||||
}, sketch001)
|
}, sketch001)
|
||||||
|> xLine(length = ${commonPoints.num1})
|
|> xLine(length = ${commonPoints.num1})
|
||||||
@ -119,10 +120,7 @@ async function doBasicSketch(
|
|||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
if (openPanes.includes('code')) {
|
if (openPanes.includes('code')) {
|
||||||
await expect(
|
expect(await u.getGreatestPixDiff(line1, TEST_COLORS.BLUE)).toBeLessThan(3)
|
||||||
await u.getGreatestPixDiff(line1, TEST_COLORS.BLUE)
|
|
||||||
).toBeLessThan(3)
|
|
||||||
await expect(await u.getGreatestPixDiff(line1, [0, 0, 255])).toBeLessThan(3)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// hold down shift
|
// hold down shift
|
||||||
@ -145,7 +143,7 @@ async function doBasicSketch(
|
|||||||
// Open the code pane.
|
// Open the code pane.
|
||||||
await u.openKclCodePanel()
|
await u.openKclCodePanel()
|
||||||
await expect(u.codeLocator)
|
await expect(u.codeLocator)
|
||||||
.toHaveText(`sketch001 = startSketchOn(XZ)profile001 = startProfileAt(${
|
.toHaveText(`@settings(defaultLengthUnit = in)sketch001 = startSketchOn(XZ)profile001 = startProfileAt(${
|
||||||
commonPoints.startAt
|
commonPoints.startAt
|
||||||
}, sketch001)
|
}, sketch001)
|
||||||
|> xLine(length = ${commonPoints.num1}, tag = $seg01)
|
|> xLine(length = ${commonPoints.num1}, tag = $seg01)
|
||||||
|
119
e2e/playwright/boolean.spec.ts
Normal file
119
e2e/playwright/boolean.spec.ts
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
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-sketch-on-faces.kcl'
|
||||||
|
),
|
||||||
|
'utf-8'
|
||||||
|
)
|
||||||
|
await context.addInitScript((file) => {
|
||||||
|
localStorage.setItem('persistCode', file)
|
||||||
|
}, file)
|
||||||
|
await homePage.goToModelingScene()
|
||||||
|
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 })
|
||||||
|
await page.waitForTimeout(1000)
|
||||||
|
|
||||||
|
// 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 })
|
||||||
|
|
||||||
|
await page.waitForTimeout(1000)
|
||||||
|
|
||||||
|
// 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,10 +1,12 @@
|
|||||||
import { Page } from '@playwright/test'
|
import type { Page } from '@playwright/test'
|
||||||
import { test, expect } from './zoo-test'
|
import type { EngineCommand } from '@src/lang/std/artifactGraph'
|
||||||
import { HomePageFixture } from './fixtures/homePageFixture'
|
import { uuidv4 } from '@src/lib/utils'
|
||||||
import { getUtils } from './test-utils'
|
|
||||||
import { EngineCommand } from 'lang/std/artifactGraph'
|
import type { HomePageFixture } from '@e2e/playwright/fixtures/homePageFixture'
|
||||||
import { uuidv4 } from 'lib/utils'
|
import type { SceneFixture } from '@e2e/playwright/fixtures/sceneFixture'
|
||||||
import { SceneFixture } from './fixtures/sceneFixture'
|
import type { ToolbarFixture } from '@e2e/playwright/fixtures/toolbarFixture'
|
||||||
|
import { getUtils } from '@e2e/playwright/test-utils'
|
||||||
|
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||||
|
|
||||||
test.describe(
|
test.describe(
|
||||||
'Can create sketches on all planes and their back sides',
|
'Can create sketches on all planes and their back sides',
|
||||||
@ -14,6 +16,7 @@ test.describe(
|
|||||||
page: Page,
|
page: Page,
|
||||||
homePage: HomePageFixture,
|
homePage: HomePageFixture,
|
||||||
scene: SceneFixture,
|
scene: SceneFixture,
|
||||||
|
toolbar: ToolbarFixture,
|
||||||
plane: string,
|
plane: string,
|
||||||
clickCoords: { x: number; y: number }
|
clickCoords: { x: number; y: number }
|
||||||
) => {
|
) => {
|
||||||
@ -46,7 +49,7 @@ test.describe(
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const code = `sketch001 = startSketchOn(${plane})profile001 = startProfileAt([0.91, -1.22], sketch001)`
|
const code = `@settings(defaultLengthUnit = in)sketch001 = startSketchOn(${plane})profile001 = startProfileAt([0.91, -1.22], sketch001)`
|
||||||
|
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
|
|
||||||
@ -58,9 +61,12 @@ test.describe(
|
|||||||
await u.sendCustomCmd(updateCamCommand)
|
await u.sendCustomCmd(updateCamCommand)
|
||||||
|
|
||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
await page.mouse.click(clickCoords.x, clickCoords.y)
|
await page.mouse.click(clickCoords.x, clickCoords.y)
|
||||||
await page.waitForTimeout(600) // wait for animation
|
await page.waitForTimeout(600) // wait for animation
|
||||||
|
|
||||||
|
await toolbar.waitUntilSketchingReady()
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('button', { name: 'line Line', exact: true })
|
page.getByRole('button', { name: 'line Line', exact: true })
|
||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
@ -116,11 +122,12 @@ test.describe(
|
|||||||
]
|
]
|
||||||
|
|
||||||
for (const config of planeConfigs) {
|
for (const config of planeConfigs) {
|
||||||
test(config.plane, async ({ page, homePage, scene }) => {
|
test(config.plane, async ({ page, homePage, scene, toolbar }) => {
|
||||||
await sketchOnPlaneAndBackSideTest(
|
await sketchOnPlaneAndBackSideTest(
|
||||||
page,
|
page,
|
||||||
homePage,
|
homePage,
|
||||||
scene,
|
scene,
|
||||||
|
toolbar,
|
||||||
config.plane,
|
config.plane,
|
||||||
config.coords
|
config.coords
|
||||||
)
|
)
|
||||||
|
@ -1,19 +1,21 @@
|
|||||||
import { test, expect } from './zoo-test'
|
import { bracket } from '@src/lib/exampleKcl'
|
||||||
import {
|
|
||||||
orRunWhenFullSuiteEnabled,
|
|
||||||
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'
|
import fsp from 'fs/promises'
|
||||||
|
import { join } from 'path'
|
||||||
|
|
||||||
|
import { TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW } from '@e2e/playwright/storageStates'
|
||||||
|
import {
|
||||||
|
executorInputPath,
|
||||||
|
getUtils,
|
||||||
|
orRunWhenFullSuiteEnabled,
|
||||||
|
} from '@e2e/playwright/test-utils'
|
||||||
|
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||||
|
|
||||||
test.describe('Code pane and errors', { tag: ['@skipWin'] }, () => {
|
test.describe('Code pane and errors', { tag: ['@skipWin'] }, () => {
|
||||||
test('Typing KCL errors induces a badge on the code pane button', async ({
|
test('Typing KCL errors induces a badge on the code pane button', async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
homePage,
|
||||||
scene,
|
scene,
|
||||||
|
cmdBar,
|
||||||
}) => {
|
}) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
|
|
||||||
@ -21,20 +23,21 @@ test.describe('Code pane and errors', { tag: ['@skipWin'] }, () => {
|
|||||||
await page.addInitScript(() => {
|
await page.addInitScript(() => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'persistCode',
|
'persistCode',
|
||||||
`// Extruded Triangle
|
`@settings(defaultLengthUnit = in)
|
||||||
sketch001 = startSketchOn(XZ)
|
// Extruded Triangle
|
||||||
|> startProfileAt([0, 0], %)
|
sketch001 = startSketchOn(XZ)
|
||||||
|> line(end = [10, 0])
|
|> startProfileAt([0, 0], %)
|
||||||
|> line(end = [-5, 10])
|
|> line(end = [10, 0])
|
||||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|> line(end = [-5, 10])
|
||||||
|> close()
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||||
extrude001 = extrude(sketch001, length = 5)`
|
|> close()
|
||||||
|
extrude001 = extrude(sketch001, length = 5)`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
await scene.waitForExecutionDone()
|
await scene.settled(cmdBar)
|
||||||
|
|
||||||
// Ensure no badge is present
|
// Ensure no badge is present
|
||||||
const codePaneButtonHolder = page.locator('#code-button-holder')
|
const codePaneButtonHolder = page.locator('#code-button-holder')
|
||||||
@ -169,6 +172,8 @@ test.describe('Code pane and errors', { tag: ['@skipWin'] }, () => {
|
|||||||
context,
|
context,
|
||||||
page,
|
page,
|
||||||
homePage,
|
homePage,
|
||||||
|
scene,
|
||||||
|
cmdBar,
|
||||||
}) => {
|
}) => {
|
||||||
// Load the app with the working starter code
|
// Load the app with the working starter code
|
||||||
await context.addInitScript((code) => {
|
await context.addInitScript((code) => {
|
||||||
@ -178,9 +183,7 @@ test.describe('Code pane and errors', { tag: ['@skipWin'] }, () => {
|
|||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
|
|
||||||
// FIXME: await scene.waitForExecutionDone() does not work. It still fails.
|
await scene.settled(cmdBar)
|
||||||
// I needed to increase this timeout to get this to pass.
|
|
||||||
await page.waitForTimeout(10000)
|
|
||||||
|
|
||||||
// Ensure badge is present
|
// Ensure badge is present
|
||||||
const codePaneButtonHolder = page.locator('#code-button-holder')
|
const codePaneButtonHolder = page.locator('#code-button-holder')
|
||||||
@ -250,11 +253,11 @@ test(
|
|||||||
])
|
])
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
fsp.copyFile(
|
fsp.copyFile(
|
||||||
executorInputPath('router-template-slate.kcl'),
|
executorInputPath('cylinder-inches.kcl'),
|
||||||
join(routerTemplateDir, 'main.kcl')
|
join(routerTemplateDir, 'main.kcl')
|
||||||
),
|
),
|
||||||
fsp.copyFile(
|
fsp.copyFile(
|
||||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
executorInputPath('e2e-can-sketch-on-chamfer.kcl'),
|
||||||
join(bracketDir, 'main.kcl')
|
join(bracketDir, 'main.kcl')
|
||||||
),
|
),
|
||||||
])
|
])
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import { test, expect } from './zoo-test'
|
import { KCL_DEFAULT_LENGTH } from '@src/lib/constants'
|
||||||
import * as fsp from 'fs/promises'
|
import * as fsp from 'fs/promises'
|
||||||
|
import path, { join } from 'path'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
executorInputPath,
|
executorInputPath,
|
||||||
getUtils,
|
getUtils,
|
||||||
orRunWhenFullSuiteEnabled,
|
orRunWhenFullSuiteEnabled,
|
||||||
} from './test-utils'
|
} from '@e2e/playwright/test-utils'
|
||||||
import { KCL_DEFAULT_LENGTH } from 'lib/constants'
|
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||||
import path, { join } from 'path'
|
|
||||||
|
|
||||||
test.describe('Command bar tests', { tag: ['@skipWin'] }, () => {
|
test.describe('Command bar tests', { tag: ['@skipWin'] }, () => {
|
||||||
test('Extrude from command bar selects extrude line after', async ({
|
test('Extrude from command bar selects extrude line after', async ({
|
||||||
@ -316,9 +317,13 @@ test.describe('Command bar tests', { tag: ['@skipWin'] }, () => {
|
|||||||
test('Can switch between sketch tools via command bar', async ({
|
test('Can switch between sketch tools via command bar', async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
homePage,
|
||||||
|
scene,
|
||||||
|
cmdBar,
|
||||||
|
toolbar,
|
||||||
}) => {
|
}) => {
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
|
await scene.settled(cmdBar)
|
||||||
|
|
||||||
const sketchButton = page.getByRole('button', { name: 'Start Sketch' })
|
const sketchButton = page.getByRole('button', { name: 'Start Sketch' })
|
||||||
const cmdBarButton = page.getByRole('button', { name: 'Commands' })
|
const cmdBarButton = page.getByRole('button', { name: 'Commands' })
|
||||||
@ -342,7 +347,9 @@ test.describe('Command bar tests', { tag: ['@skipWin'] }, () => {
|
|||||||
|
|
||||||
// Start a sketch
|
// Start a sketch
|
||||||
await sketchButton.click()
|
await sketchButton.click()
|
||||||
|
|
||||||
await page.mouse.click(700, 200)
|
await page.mouse.click(700, 200)
|
||||||
|
await toolbar.waitUntilSketchingReady()
|
||||||
|
|
||||||
// Switch between sketch tools via the command bar
|
// Switch between sketch tools via the command bar
|
||||||
await expect(lineToolButton).toHaveAttribute('aria-pressed', 'true')
|
await expect(lineToolButton).toHaveAttribute('aria-pressed', 'true')
|
||||||
@ -513,7 +520,8 @@ c = 3 + a`
|
|||||||
await homePage.openProject(projectName)
|
await homePage.openProject(projectName)
|
||||||
// TODO: you probably shouldn't need an engine connection to add a parameter,
|
// TODO: you probably shouldn't need an engine connection to add a parameter,
|
||||||
// but you do because all modeling commands have that requirement
|
// but you do because all modeling commands have that requirement
|
||||||
await scene.settled(cmdBar)
|
// Don't use scene.settled here
|
||||||
|
await expect(scene.startEditSketchBtn).toBeEnabled({ timeout: 15_000 })
|
||||||
|
|
||||||
await test.step(`Create a parameter via command bar`, async () => {
|
await test.step(`Create a parameter via command bar`, async () => {
|
||||||
await cmdBar.cmdBarOpenBtn.click()
|
await cmdBar.cmdBarOpenBtn.click()
|
||||||
@ -542,7 +550,12 @@ c = 3 + a`
|
|||||||
)
|
)
|
||||||
|
|
||||||
const newValue = `2 * b + a`
|
const newValue = `2 * b + a`
|
||||||
|
|
||||||
await test.step(`Edit the parameter via command bar`, async () => {
|
await test.step(`Edit the parameter via command bar`, async () => {
|
||||||
|
// TODO: make the command palette command registration more static, and the enabled state more dynamic
|
||||||
|
// so that we can just open the command palette and know all commands will be there.
|
||||||
|
await expect(scene.startEditSketchBtn).toBeEnabled()
|
||||||
|
|
||||||
await cmdBar.cmdBarOpenBtn.click()
|
await cmdBar.cmdBarOpenBtn.click()
|
||||||
await cmdBar.chooseCommand('edit parameter')
|
await cmdBar.chooseCommand('edit parameter')
|
||||||
await cmdBar.expectState({
|
await cmdBar.expectState({
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { test, expect } from './zoo-test'
|
import { getUtils } from '@e2e/playwright/test-utils'
|
||||||
import { getUtils } from './test-utils'
|
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||||
|
|
||||||
test.describe('Copilot ghost text', () => {
|
test.describe('Copilot ghost text', () => {
|
||||||
// eslint-disable-next-line jest/valid-title
|
// eslint-disable-next-line jest/valid-title
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { test, expect } from './zoo-test'
|
import { getUtils } from '@e2e/playwright/test-utils'
|
||||||
|
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||||
import { getUtils } from './test-utils'
|
|
||||||
|
|
||||||
function countNewlines(input: string): number {
|
function countNewlines(input: string): number {
|
||||||
let count = 0
|
let count = 0
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
import { test, expect } from './zoo-test'
|
import fsp from 'fs/promises'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getUtils,
|
|
||||||
executorInputPath,
|
executorInputPath,
|
||||||
getPlaywrightDownloadDir,
|
getPlaywrightDownloadDir,
|
||||||
} from './test-utils'
|
getUtils,
|
||||||
import fsp from 'fs/promises'
|
} from '@e2e/playwright/test-utils'
|
||||||
|
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'export works on the first try',
|
'export works on the first try',
|
||||||
{ tag: ['@electron', '@skipLocalEngine'] },
|
{ tag: ['@electron', '@skipLocalEngine'] },
|
||||||
async ({ page, context, scene, tronApp }, testInfo) => {
|
async ({ page, context, scene, tronApp, cmdBar }, testInfo) => {
|
||||||
if (!tronApp) {
|
if (!tronApp) {
|
||||||
fail()
|
fail()
|
||||||
}
|
}
|
||||||
@ -20,68 +21,52 @@ test(
|
|||||||
await Promise.all([fsp.mkdir(bracketDir, { recursive: true })])
|
await Promise.all([fsp.mkdir(bracketDir, { recursive: true })])
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
fsp.copyFile(
|
fsp.copyFile(
|
||||||
executorInputPath('router-template-slate.kcl'),
|
executorInputPath('cylinder-inches.kcl'),
|
||||||
path.join(bracketDir, 'other.kcl')
|
path.join(bracketDir, 'other.kcl')
|
||||||
),
|
),
|
||||||
fsp.copyFile(
|
fsp.copyFile(
|
||||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
executorInputPath('e2e-can-sketch-on-chamfer.kcl'),
|
||||||
path.join(bracketDir, 'main.kcl')
|
path.join(bracketDir, 'main.kcl')
|
||||||
),
|
),
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
|
|
||||||
page.on('console', console.log)
|
|
||||||
|
|
||||||
await test.step('on open of project', async () => {
|
await test.step('on open of project', async () => {
|
||||||
await expect(page.getByText(`bracket`)).toBeVisible()
|
// Open the project
|
||||||
|
const projectName = page.getByText(`bracket`)
|
||||||
|
await expect(projectName).toBeVisible()
|
||||||
|
await projectName.click()
|
||||||
|
await scene.settled(cmdBar)
|
||||||
|
|
||||||
// open the project
|
// Expect zero errors in gutter
|
||||||
await page.getByText(`bracket`).click()
|
|
||||||
|
|
||||||
// expect zero errors in guter
|
|
||||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
|
|
||||||
// export the model
|
// Click the export button
|
||||||
const exportButton = page.getByTestId('export-pane-button')
|
const exportButton = page.getByTestId('export-pane-button')
|
||||||
await expect(exportButton).toBeVisible()
|
await expect(exportButton).toBeVisible()
|
||||||
|
|
||||||
// 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 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 exportButton.click()
|
||||||
|
|
||||||
|
// Select the first format option
|
||||||
|
const gltfOption = cmdBar.selectOption({ name: 'glTF' })
|
||||||
|
const exportFileName = `main.gltf` // source file is named `main.kcl`
|
||||||
await expect(gltfOption).toBeVisible()
|
await expect(gltfOption).toBeVisible()
|
||||||
await expect(page.getByText('STL')).toBeVisible()
|
|
||||||
|
|
||||||
await page.keyboard.press('Enter')
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
// Click the checkbox
|
// Click the checkbox
|
||||||
|
const submitButton = page.getByText('Confirm Export')
|
||||||
await expect(submitButton).toBeVisible()
|
await expect(submitButton).toBeVisible()
|
||||||
|
|
||||||
await page.waitForTimeout(500)
|
|
||||||
|
|
||||||
await page.keyboard.press('Enter')
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
// Find the toast.
|
|
||||||
// Look out for the toast message
|
// Look out for the toast message
|
||||||
|
const exportingToastMessage = page.getByText(`Exporting...`)
|
||||||
|
const alreadyExportingToastMessage = page.getByText(`Already exporting`)
|
||||||
await expect(exportingToastMessage).toBeVisible()
|
await expect(exportingToastMessage).toBeVisible()
|
||||||
await expect(alreadyExportingToastMessage).not.toBeVisible()
|
await expect(alreadyExportingToastMessage).not.toBeVisible()
|
||||||
|
|
||||||
// Expect it to succeed.
|
// 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(errorToastMessage).not.toBeVisible()
|
||||||
await expect(engineErrorToastMessage).not.toBeVisible()
|
await expect(engineErrorToastMessage).not.toBeVisible()
|
||||||
|
|
||||||
@ -89,6 +74,7 @@ test(
|
|||||||
await expect(successToastMessage).toBeVisible()
|
await expect(successToastMessage).toBeVisible()
|
||||||
await expect(exportingToastMessage).not.toBeVisible()
|
await expect(exportingToastMessage).not.toBeVisible()
|
||||||
|
|
||||||
|
// Check for the exported file
|
||||||
const firstFileFullPath = path.resolve(
|
const firstFileFullPath = path.resolve(
|
||||||
getPlaywrightDownloadDir(tronApp.projectDirName),
|
getPlaywrightDownloadDir(tronApp.projectDirName),
|
||||||
exportFileName
|
exportFileName
|
||||||
@ -107,7 +93,7 @@ test(
|
|||||||
},
|
},
|
||||||
{ timeout: 15_000 }
|
{ timeout: 15_000 }
|
||||||
)
|
)
|
||||||
.toBeGreaterThan(300_000)
|
.toBeGreaterThan(30_000)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -115,60 +101,50 @@ test(
|
|||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await u.openFilePanel()
|
await u.openFilePanel()
|
||||||
|
|
||||||
|
// Click on the other file
|
||||||
const otherKclButton = page.getByRole('button', { name: 'other.kcl' })
|
const otherKclButton = page.getByRole('button', { name: 'other.kcl' })
|
||||||
|
|
||||||
// Click the file
|
|
||||||
await otherKclButton.click()
|
await otherKclButton.click()
|
||||||
|
|
||||||
// Close the file pane
|
// Close the file pane
|
||||||
await u.closeFilePanel()
|
await u.closeFilePanel()
|
||||||
|
await scene.settled(cmdBar)
|
||||||
|
|
||||||
// FIXME: await scene.waitForExecutionDone() does not work. The modeling indicator stays in -receive-reliable and not execution done
|
// Expect zero errors in gutter
|
||||||
await page.waitForTimeout(10000)
|
|
||||||
|
|
||||||
// expect zero errors in guter
|
|
||||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
|
|
||||||
// export the model
|
// Click the export button
|
||||||
const exportButton = page.getByTestId('export-pane-button')
|
const exportButton = page.getByTestId('export-pane-button')
|
||||||
await expect(exportButton).toBeVisible()
|
await expect(exportButton).toBeVisible()
|
||||||
|
|
||||||
const gltfOption = page.getByText('glTF')
|
|
||||||
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 exportButton.click()
|
||||||
|
|
||||||
|
// Select the first format option
|
||||||
|
const gltfOption = cmdBar.selectOption({ name: 'glTF' })
|
||||||
|
const exportFileName = `other.gltf` // source file is named `other.kcl`
|
||||||
await expect(gltfOption).toBeVisible()
|
await expect(gltfOption).toBeVisible()
|
||||||
await expect(page.getByText('STL')).toBeVisible()
|
|
||||||
|
|
||||||
await page.keyboard.press('Enter')
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
// Click the checkbox
|
// Click the checkbox
|
||||||
|
const submitButton = page.getByText('Confirm Export')
|
||||||
await expect(submitButton).toBeVisible()
|
await expect(submitButton).toBeVisible()
|
||||||
|
|
||||||
await page.keyboard.press('Enter')
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
// Find the toast.
|
|
||||||
// Look out for the toast message
|
// Look out for the toast message
|
||||||
|
const exportingToastMessage = page.getByText(`Exporting...`)
|
||||||
|
const alreadyExportingToastMessage = page.getByText(`Already exporting`)
|
||||||
await expect(exportingToastMessage).toBeVisible()
|
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`)
|
const successToastMessage = page.getByText(`Exported successfully`)
|
||||||
await test.step('Check the success toast message shows and nothing else', async () =>
|
await expect(successToastMessage).toBeVisible()
|
||||||
Promise.all([
|
await expect(exportingToastMessage).not.toBeVisible()
|
||||||
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(
|
const secondFileFullPath = path.resolve(
|
||||||
getPlaywrightDownloadDir(tronApp.projectDirName),
|
getPlaywrightDownloadDir(tronApp.projectDirName),
|
||||||
exportFileName
|
exportFileName
|
||||||
@ -187,7 +163,7 @@ test(
|
|||||||
},
|
},
|
||||||
{ timeout: 15_000 }
|
{ timeout: 15_000 }
|
||||||
)
|
)
|
||||||
.toBeGreaterThan(70_000)
|
.toBeGreaterThan(50_000)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import { test, expect } from './zoo-test'
|
import { uuidv4 } from '@src/lib/utils'
|
||||||
import fsp from 'fs/promises'
|
import fsp from 'fs/promises'
|
||||||
import { uuidv4 } from 'lib/utils'
|
import { join } from 'path'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
TEST_COLORS,
|
||||||
executorInputPath,
|
executorInputPath,
|
||||||
getUtils,
|
getUtils,
|
||||||
orRunWhenFullSuiteEnabled,
|
orRunWhenFullSuiteEnabled,
|
||||||
TEST_COLORS,
|
} from '@e2e/playwright/test-utils'
|
||||||
} from './test-utils'
|
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||||
|
|
||||||
import { join } from 'path'
|
|
||||||
|
|
||||||
test.describe('Editor tests', { tag: ['@skipWin'] }, () => {
|
test.describe('Editor tests', { tag: ['@skipWin'] }, () => {
|
||||||
test('can comment out code with ctrl+/', async ({ page, homePage }) => {
|
test('can comment out code with ctrl+/', async ({ page, homePage }) => {
|
||||||
@ -32,26 +32,30 @@ test.describe('Editor tests', { tag: ['@skipWin'] }, () => {
|
|||||||
await page.keyboard.press('/')
|
await page.keyboard.press('/')
|
||||||
await page.keyboard.up('ControlOrMeta')
|
await page.keyboard.up('ControlOrMeta')
|
||||||
|
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
.toHaveText(`sketch001 = startSketchOn(XY)
|
`@settings(defaultLengthUnit = in)
|
||||||
|
sketch001 = startSketchOn(XY)
|
||||||
|> startProfileAt([-10, -10], %)
|
|> startProfileAt([-10, -10], %)
|
||||||
|> line(end = [20, 0])
|
|> line(end = [20, 0])
|
||||||
|> line(end = [0, 20])
|
|> line(end = [0, 20])
|
||||||
|> line(end = [-20, 0])
|
|> line(end = [-20, 0])
|
||||||
// |> close()`)
|
// |> close()`.replaceAll('\n', '')
|
||||||
|
)
|
||||||
|
|
||||||
// uncomment the code
|
// uncomment the code
|
||||||
await page.keyboard.down('ControlOrMeta')
|
await page.keyboard.down('ControlOrMeta')
|
||||||
await page.keyboard.press('/')
|
await page.keyboard.press('/')
|
||||||
await page.keyboard.up('ControlOrMeta')
|
await page.keyboard.up('ControlOrMeta')
|
||||||
|
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
.toHaveText(`sketch001 = startSketchOn(XY)
|
`@settings(defaultLengthUnit = in)
|
||||||
|
sketch001 = startSketchOn(XY)
|
||||||
|> startProfileAt([-10, -10], %)
|
|> startProfileAt([-10, -10], %)
|
||||||
|> line(end = [20, 0])
|
|> line(end = [20, 0])
|
||||||
|> line(end = [0, 20])
|
|> line(end = [0, 20])
|
||||||
|> line(end = [-20, 0])
|
|> line(end = [-20, 0])
|
||||||
|> close()`)
|
|> close()`.replaceAll('\n', '')
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('ensure we use the cache, and do not re-execute', async ({
|
test('ensure we use the cache, and do not re-execute', async ({
|
||||||
@ -74,12 +78,14 @@ test.describe('Editor tests', { tag: ['@skipWin'] }, () => {
|
|||||||
|
|
||||||
// Ensure we execute the first time.
|
// Ensure we execute the first time.
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
await expect(
|
await expect
|
||||||
page.locator('[data-receive-command-type="scene_clear_all"]')
|
.poll(() =>
|
||||||
).toHaveCount(1)
|
page.locator('[data-receive-command-type="scene_clear_all"]').count()
|
||||||
await expect(
|
)
|
||||||
page.locator('[data-message-type="execution-done"]')
|
.toBe(1)
|
||||||
).toHaveCount(2)
|
await expect
|
||||||
|
.poll(() => page.locator('[data-message-type="execution-done"]').count())
|
||||||
|
.toBe(2)
|
||||||
|
|
||||||
// Add whitespace to the end of the code.
|
// Add whitespace to the end of the code.
|
||||||
await u.codeLocator.click()
|
await u.codeLocator.click()
|
||||||
@ -106,12 +112,14 @@ test.describe('Editor tests', { tag: ['@skipWin'] }, () => {
|
|||||||
test('ensure we use the cache, and do not clear on append', async ({
|
test('ensure we use the cache, and do not clear on append', async ({
|
||||||
homePage,
|
homePage,
|
||||||
page,
|
page,
|
||||||
|
scene,
|
||||||
|
cmdBar,
|
||||||
}) => {
|
}) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
await u.waitForPageLoad()
|
await scene.settled(cmdBar)
|
||||||
|
|
||||||
await u.codeLocator.click()
|
await u.codeLocator.click()
|
||||||
await page.keyboard.type(`sketch001 = startSketchOn(XY)
|
await page.keyboard.type(`sketch001 = startSketchOn(XY)
|
||||||
@ -178,13 +186,15 @@ test.describe('Editor tests', { tag: ['@skipWin'] }, () => {
|
|||||||
await page.locator('#code-pane button:first-child').click()
|
await page.locator('#code-pane button:first-child').click()
|
||||||
await page.locator('button:has-text("Format code")').click()
|
await page.locator('button:has-text("Format code")').click()
|
||||||
|
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
.toHaveText(`sketch001 = startSketchOn(XY)
|
`@settings(defaultLengthUnit = in)
|
||||||
|
sketch001 = startSketchOn(XY)
|
||||||
|> startProfileAt([-10, -10], %)
|
|> startProfileAt([-10, -10], %)
|
||||||
|> line(end = [20, 0])
|
|> line(end = [20, 0])
|
||||||
|> line(end = [0, 20])
|
|> line(end = [0, 20])
|
||||||
|> line(end = [-20, 0])
|
|> line(end = [-20, 0])
|
||||||
|> close()`)
|
|> close()`.replaceAll('\n', '')
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('if you click the format button it formats your code and executes so lints are still there', async ({
|
test('if you click the format button it formats your code and executes so lints are still there', async ({
|
||||||
@ -227,13 +237,15 @@ test.describe('Editor tests', { tag: ['@skipWin'] }, () => {
|
|||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
.toHaveText(`sketch_001 = startSketchOn(XY)
|
`@settings(defaultLengthUnit = in)
|
||||||
|
sketch_001 = startSketchOn(XY)
|
||||||
|> startProfileAt([-10, -10], %)
|
|> startProfileAt([-10, -10], %)
|
||||||
|> line(end = [20, 0])
|
|> line(end = [20, 0])
|
||||||
|> line(end = [0, 20])
|
|> line(end = [0, 20])
|
||||||
|> line(end = [-20, 0])
|
|> line(end = [-20, 0])
|
||||||
|> close()`)
|
|> close()`.replaceAll('\n', '')
|
||||||
|
)
|
||||||
|
|
||||||
// error in guter
|
// error in guter
|
||||||
await expect(page.locator('.cm-lint-marker-info').first()).toBeVisible()
|
await expect(page.locator('.cm-lint-marker-info').first()).toBeVisible()
|
||||||
@ -471,6 +483,7 @@ test.describe('Editor tests', { tag: ['@skipWin'] }, () => {
|
|||||||
test('if you write kcl with lint errors you get lints', async ({
|
test('if you write kcl with lint errors you get lints', async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
homePage,
|
||||||
|
scene,
|
||||||
}) => {
|
}) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||||
@ -490,10 +503,7 @@ test.describe('Editor tests', { tag: ['@skipWin'] }, () => {
|
|||||||
await page.keyboard.press('ArrowLeft')
|
await page.keyboard.press('ArrowLeft')
|
||||||
await page.keyboard.press('ArrowRight')
|
await page.keyboard.press('ArrowRight')
|
||||||
|
|
||||||
// FIXME: lsp errors do not propagate to the frontend until engine is connected and code is executed
|
await scene.connectionEstablished()
|
||||||
// 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
|
// error in guter
|
||||||
await expect(page.locator('.cm-lint-marker-info').first()).toBeVisible()
|
await expect(page.locator('.cm-lint-marker-info').first()).toBeVisible()
|
||||||
@ -815,10 +825,12 @@ test.describe('Editor tests', { tag: ['@skipWin'] }, () => {
|
|||||||
// there shouldn't be any auto complete options for 'lin' in the comment
|
// 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-completionLabel')).not.toBeVisible()
|
||||||
|
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
.toHaveText(`sketch001 = startSketchOn(XZ)
|
`@settings(defaultLengthUnit = in)
|
||||||
|
sketch001 = startSketchOn(XZ)
|
||||||
|> startProfileAt([3.14, 12], %)
|
|> startProfileAt([3.14, 12], %)
|
||||||
|> xLine(%, length = 5) // lin`)
|
|> xLine(%, length = 5) // lin`.replaceAll('\n', '')
|
||||||
|
)
|
||||||
|
|
||||||
// expect there to be no KCL errors
|
// expect there to be no KCL errors
|
||||||
await expect(page.locator('.cm-lint-marker-error')).toHaveCount(0)
|
await expect(page.locator('.cm-lint-marker-error')).toHaveCount(0)
|
||||||
@ -888,10 +900,12 @@ test.describe('Editor tests', { tag: ['@skipWin'] }, () => {
|
|||||||
// there shouldn't be any auto complete options for 'lin' in the comment
|
// 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-completionLabel')).not.toBeVisible()
|
||||||
|
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
.toHaveText(`sketch001 = startSketchOn(XZ)
|
`@settings(defaultLengthUnit = in)
|
||||||
|
sketch001 = startSketchOn(XZ)
|
||||||
|> startProfileAt([3.14, 12], %)
|
|> startProfileAt([3.14, 12], %)
|
||||||
|> xLine(%, length = 5) // lin`)
|
|> xLine(%, length = 5) // lin`.replaceAll('\n', '')
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
test('Can undo a click and point extrude with ctrl+z', async ({
|
test('Can undo a click and point extrude with ctrl+z', async ({
|
||||||
@ -975,12 +989,13 @@ test.describe('Editor tests', { tag: ['@skipWin'] }, () => {
|
|||||||
test(
|
test(
|
||||||
'Can undo a sketch modification with ctrl+z',
|
'Can undo a sketch modification with ctrl+z',
|
||||||
{ tag: ['@skipWin'] },
|
{ tag: ['@skipWin'] },
|
||||||
async ({ page, homePage }) => {
|
async ({ page, homePage, editor }) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'persistCode',
|
'persistCode',
|
||||||
`sketch001 = startSketchOn(XZ)
|
`@settings(defaultLengthUnit=in)
|
||||||
|
sketch001 = startSketchOn(XZ)
|
||||||
|> startProfileAt([4.61, -10.01], %)
|
|> startProfileAt([4.61, -10.01], %)
|
||||||
|> line(end = [12.73, -0.09])
|
|> line(end = [12.73, -0.09])
|
||||||
|> tangentialArcTo([24.95, -0.38], %)
|
|> tangentialArcTo([24.95, -0.38], %)
|
||||||
@ -1070,41 +1085,45 @@ test.describe('Editor tests', { tag: ['@skipWin'] }, () => {
|
|||||||
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
||||||
|
|
||||||
// expect the code to have changed
|
// expect the code to have changed
|
||||||
await expect(page.locator('.cm-content'))
|
await editor.expectEditor.toContain(
|
||||||
.toHaveText(`sketch001 = startSketchOn(XZ)
|
`sketch001 = startSketchOn(XZ)
|
||||||
|> startProfileAt([2.71, -2.71], %)
|
|> startProfileAt([2.71, -2.71], %)
|
||||||
|> line(end = [15.4, -2.78])
|
|> line(end = [15.4, -2.78])
|
||||||
|> tangentialArcTo([27.6, -3.05], %)
|
|> tangentialArcTo([27.6, -3.05], %)
|
||||||
|> close()
|
|> close()
|
||||||
|> extrude(length = 5)
|
|> extrude(length = 5)`,
|
||||||
`)
|
{ shouldNormalise: true }
|
||||||
|
)
|
||||||
|
|
||||||
// Hit undo
|
// Hit undo
|
||||||
await page.keyboard.down('Control')
|
await page.keyboard.down('Control')
|
||||||
await page.keyboard.press('KeyZ')
|
await page.keyboard.press('KeyZ')
|
||||||
await page.keyboard.up('Control')
|
await page.keyboard.up('Control')
|
||||||
|
|
||||||
await expect(page.locator('.cm-content'))
|
await editor.expectEditor.toContain(
|
||||||
.toHaveText(`sketch001 = startSketchOn(XZ)
|
`sketch001 = startSketchOn(XZ)
|
||||||
|> startProfileAt([2.71, -2.71], %)
|
|> startProfileAt([2.71, -2.71], %)
|
||||||
|> line(end = [15.4, -2.78])
|
|> line(end = [15.4, -2.78])
|
||||||
|> tangentialArcTo([24.95, -0.38], %)
|
|> tangentialArcTo([24.95, -0.38], %)
|
||||||
|> close()
|
|> close()
|
||||||
|> extrude(length = 5)`)
|
|> extrude(length = 5)`,
|
||||||
|
{ shouldNormalise: true }
|
||||||
|
)
|
||||||
|
|
||||||
// Hit undo again.
|
// Hit undo again.
|
||||||
await page.keyboard.down('Control')
|
await page.keyboard.down('Control')
|
||||||
await page.keyboard.press('KeyZ')
|
await page.keyboard.press('KeyZ')
|
||||||
await page.keyboard.up('Control')
|
await page.keyboard.up('Control')
|
||||||
|
|
||||||
await expect(page.locator('.cm-content'))
|
await editor.expectEditor.toContain(
|
||||||
.toHaveText(`sketch001 = startSketchOn(XZ)
|
`sketch001 = startSketchOn(XZ)
|
||||||
|> startProfileAt([2.71, -2.71], %)
|
|> startProfileAt([2.71, -2.71], %)
|
||||||
|> line(end = [12.73, -0.09])
|
|> line(end = [12.73, -0.09])
|
||||||
|> tangentialArcTo([24.95, -0.38], %)
|
|> tangentialArcTo([24.95, -0.38], %)
|
||||||
|> close()
|
|> close()
|
||||||
|> extrude(length = 5)
|
|> extrude(length = 5)`,
|
||||||
`)
|
{ shouldNormalise: true }
|
||||||
|
)
|
||||||
|
|
||||||
// Hit undo again.
|
// Hit undo again.
|
||||||
await page.keyboard.down('Control')
|
await page.keyboard.down('Control')
|
||||||
@ -1112,13 +1131,15 @@ test.describe('Editor tests', { tag: ['@skipWin'] }, () => {
|
|||||||
await page.keyboard.up('Control')
|
await page.keyboard.up('Control')
|
||||||
|
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
await expect(page.locator('.cm-content'))
|
await editor.expectEditor.toContain(
|
||||||
.toHaveText(`sketch001 = startSketchOn(XZ)
|
`sketch001 = startSketchOn(XZ)
|
||||||
|> startProfileAt([4.61, -10.01], %)
|
|> startProfileAt([4.61, -10.01], %)
|
||||||
|> line(end = [12.73, -0.09])
|
|> line(end = [12.73, -0.09])
|
||||||
|> tangentialArcTo([24.95, -0.38], %)
|
|> tangentialArcTo([24.95, -0.38], %)
|
||||||
|> close()
|
|> close()
|
||||||
|> extrude(length = 5)`)
|
|> extrude(length = 5)`,
|
||||||
|
{ shouldNormalise: true }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1206,4 +1227,130 @@ test.describe('Editor tests', { tag: ['@skipWin'] }, () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
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,7 +1,8 @@
|
|||||||
import { test, expect } from './zoo-test'
|
|
||||||
import * as fsp from 'fs/promises'
|
import * as fsp from 'fs/promises'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
|
|
||||||
|
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||||
|
|
||||||
const FEATURE_TREE_EXAMPLE_CODE = `export fn timesFive(x) {
|
const FEATURE_TREE_EXAMPLE_CODE = `export fn timesFive(x) {
|
||||||
return 5 * x
|
return 5 * x
|
||||||
}
|
}
|
||||||
@ -21,7 +22,7 @@ sketch001 = startSketchOn(XZ)
|
|||||||
|> angledLine([-45, length001], %)
|
|> angledLine([-45, length001], %)
|
||||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||||
|> close()
|
|> close()
|
||||||
revolve001 = revolve(sketch001, axis = "X")
|
revolve001 = revolve(sketch001, axis = X)
|
||||||
triangle()
|
triangle()
|
||||||
|> extrude(length = 30)
|
|> extrude(length = 30)
|
||||||
plane001 = offsetPlane(XY, offset = 10)
|
plane001 = offsetPlane(XY, offset = 10)
|
||||||
@ -63,7 +64,7 @@ test.describe('Feature Tree pane', () => {
|
|||||||
test(
|
test(
|
||||||
'User can go to definition and go to function definition',
|
'User can go to definition and go to function definition',
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ context, homePage, scene, editor, toolbar }) => {
|
async ({ context, homePage, scene, editor, toolbar, cmdBar, page }) => {
|
||||||
await context.folderSetupFn(async (dir) => {
|
await context.folderSetupFn(async (dir) => {
|
||||||
const bracketDir = join(dir, 'test-sample')
|
const bracketDir = join(dir, 'test-sample')
|
||||||
await fsp.mkdir(bracketDir, { recursive: true })
|
await fsp.mkdir(bracketDir, { recursive: true })
|
||||||
@ -85,9 +86,13 @@ test.describe('Feature Tree pane', () => {
|
|||||||
sortBy: 'last-modified-desc',
|
sortBy: 'last-modified-desc',
|
||||||
})
|
})
|
||||||
await homePage.openProject('test-sample')
|
await homePage.openProject('test-sample')
|
||||||
await scene.waitForExecutionDone()
|
await scene.connectionEstablished()
|
||||||
await editor.closePane()
|
await scene.settled(cmdBar)
|
||||||
|
|
||||||
await toolbar.openFeatureTreePane()
|
await toolbar.openFeatureTreePane()
|
||||||
|
await expect
|
||||||
|
.poll(() => page.getByText('Feature tree').count())
|
||||||
|
.toBeGreaterThan(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
async function testViewSource({
|
async function testViewSource({
|
||||||
@ -126,7 +131,7 @@ test.describe('Feature Tree pane', () => {
|
|||||||
await testViewSource({
|
await testViewSource({
|
||||||
operationName: 'Revolve',
|
operationName: 'Revolve',
|
||||||
operationIndex: 0,
|
operationIndex: 0,
|
||||||
expectedActiveLine: 'revolve001 = revolve(sketch001, axis = "X")',
|
expectedActiveLine: 'revolve001 = revolve(sketch001, axis = X)',
|
||||||
})
|
})
|
||||||
await testViewSource({
|
await testViewSource({
|
||||||
operationName: 'Triangle',
|
operationName: 'Triangle',
|
||||||
@ -253,7 +258,7 @@ test.describe('Feature Tree pane', () => {
|
|||||||
sortBy: 'last-modified-desc',
|
sortBy: 'last-modified-desc',
|
||||||
})
|
})
|
||||||
await homePage.openProject('test-sample')
|
await homePage.openProject('test-sample')
|
||||||
await scene.waitForExecutionDone()
|
await scene.settled(cmdBar)
|
||||||
await toolbar.openFeatureTreePane()
|
await toolbar.openFeatureTreePane()
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -338,7 +343,7 @@ test.describe('Feature Tree pane', () => {
|
|||||||
sortBy: 'last-modified-desc',
|
sortBy: 'last-modified-desc',
|
||||||
})
|
})
|
||||||
await homePage.openProject('test-sample')
|
await homePage.openProject('test-sample')
|
||||||
await scene.waitForExecutionDone()
|
await scene.settled(cmdBar)
|
||||||
await toolbar.openFeatureTreePane()
|
await toolbar.openFeatureTreePane()
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -413,8 +418,7 @@ profile003 = startProfileAt([0, -4.93], sketch001)
|
|||||||
const planeColor: [number, number, number] = [74, 74, 74]
|
const planeColor: [number, number, number] = [74, 74, 74]
|
||||||
|
|
||||||
await homePage.openProject('test-sample')
|
await homePage.openProject('test-sample')
|
||||||
// FIXME: @lf94 has a better way to verify execution completion, in a PR rn
|
await scene.settled(cmdBar)
|
||||||
await scene.waitForExecutionDone()
|
|
||||||
|
|
||||||
await test.step(`Verify we see the sketch`, async () => {
|
await test.step(`Verify we see the sketch`, async () => {
|
||||||
await scene.expectPixelColor(sketchColor, testPoint, 10)
|
await scene.expectPixelColor(sketchColor, testPoint, 10)
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
import { test, expect } from './zoo-test'
|
import { FILE_EXT } from '@src/lib/constants'
|
||||||
import * as fsp from 'fs/promises'
|
|
||||||
import * as fs from 'fs'
|
import * as fs from 'fs'
|
||||||
|
import * as fsp from 'fs/promises'
|
||||||
|
import { join } from 'path'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
createProject,
|
createProject,
|
||||||
executorInputPath,
|
executorInputPath,
|
||||||
getUtils,
|
getUtils,
|
||||||
orRunWhenFullSuiteEnabled,
|
orRunWhenFullSuiteEnabled,
|
||||||
runningOnWindows,
|
runningOnWindows,
|
||||||
} from './test-utils'
|
} from '@e2e/playwright/test-utils'
|
||||||
import { join } from 'path'
|
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||||
import { FILE_EXT } from 'lib/constants'
|
|
||||||
|
|
||||||
test.describe('integrations tests', () => {
|
test.describe('integrations tests', () => {
|
||||||
test(
|
test(
|
||||||
@ -46,6 +47,7 @@ test.describe('integrations tests', () => {
|
|||||||
await scene.connectionEstablished()
|
await scene.connectionEstablished()
|
||||||
await scene.settled(cmdBar)
|
await scene.settled(cmdBar)
|
||||||
await clickObj()
|
await clickObj()
|
||||||
|
await page.waitForTimeout(1000)
|
||||||
await scene.moveNoWhere()
|
await scene.moveNoWhere()
|
||||||
await editor.expectState({
|
await editor.expectState({
|
||||||
activeLines: [
|
activeLines: [
|
||||||
@ -71,11 +73,11 @@ test.describe('integrations tests', () => {
|
|||||||
})
|
})
|
||||||
await test.step('setup for next assertion', async () => {
|
await test.step('setup for next assertion', async () => {
|
||||||
await toolbar.openFile('main.kcl')
|
await toolbar.openFile('main.kcl')
|
||||||
|
await page.waitForTimeout(1000)
|
||||||
await scene.settled(cmdBar)
|
|
||||||
|
|
||||||
await clickObj()
|
await clickObj()
|
||||||
|
await page.waitForTimeout(1000)
|
||||||
await scene.moveNoWhere()
|
await scene.moveNoWhere()
|
||||||
|
await page.waitForTimeout(1000)
|
||||||
await editor.expectState({
|
await editor.expectState({
|
||||||
activeLines: [
|
activeLines: [
|
||||||
'|>startProfileAt([75.8,317.2],%)//[$startCapTag,$EndCapTag]',
|
'|>startProfileAt([75.8,317.2],%)//[$startCapTag,$EndCapTag]',
|
||||||
@ -88,7 +90,7 @@ test.describe('integrations tests', () => {
|
|||||||
await toolbar.expectFileTreeState(['main.kcl', fileName])
|
await toolbar.expectFileTreeState(['main.kcl', fileName])
|
||||||
})
|
})
|
||||||
await test.step('check sketch mode is exited when opening a different file', async () => {
|
await test.step('check sketch mode is exited when opening a different file', async () => {
|
||||||
await toolbar.openFile(fileName, { wait: false })
|
await toolbar.openFile(fileName)
|
||||||
|
|
||||||
// check we're out of sketch mode
|
// check we're out of sketch mode
|
||||||
await expect(toolbar.exitSketchBtn).not.toBeVisible()
|
await expect(toolbar.exitSketchBtn).not.toBeVisible()
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import type { Page, Locator, Route, Request } from '@playwright/test'
|
import type { Locator, Page, Request, Route, TestInfo } from '@playwright/test'
|
||||||
import { expect, TestInfo } from '@playwright/test'
|
import { expect } from '@playwright/test'
|
||||||
import * as fs from 'fs'
|
import * as fs from 'fs'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
|
|
||||||
@ -112,22 +112,16 @@ export class CmdBarFixture {
|
|||||||
* and assumes we are past the `pickCommand` step.
|
* and assumes we are past the `pickCommand` step.
|
||||||
*/
|
*/
|
||||||
progressCmdBar = async (shouldFuzzProgressMethod = true) => {
|
progressCmdBar = async (shouldFuzzProgressMethod = true) => {
|
||||||
// FIXME: Progressing the command bar is a race condition. We have an async useEffect that reports the final state via useCalculateKclExpression. If this does not run quickly enough, it will not "fail" the continue because you can press continue if the state is not ready. E2E tests do not know this.
|
await this.page.waitForTimeout(2000)
|
||||||
// Wait 1250ms to assume the await executeAst of the KCL input field is finished
|
const arrowButton = this.page.getByRole('button', {
|
||||||
await this.page.waitForTimeout(1250)
|
name: 'arrow right Continue',
|
||||||
if (shouldFuzzProgressMethod || Math.random() > 0.5) {
|
})
|
||||||
const arrowButton = this.page.getByRole('button', {
|
if (await arrowButton.isVisible()) {
|
||||||
name: 'arrow right Continue',
|
await arrowButton.click()
|
||||||
})
|
|
||||||
if (await arrowButton.isVisible()) {
|
|
||||||
await arrowButton.click()
|
|
||||||
} else {
|
|
||||||
await this.page
|
|
||||||
.getByRole('button', { name: 'checkmark Submit command' })
|
|
||||||
.click()
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
await this.page.keyboard.press('Enter')
|
await this.page
|
||||||
|
.getByRole('button', { name: 'checkmark Submit command' })
|
||||||
|
.click()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import type { Page, Locator } from '@playwright/test'
|
import type { Locator, Page } from '@playwright/test'
|
||||||
import { expect } from '@playwright/test'
|
import { expect } from '@playwright/test'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
closePane,
|
|
||||||
checkIfPaneIsOpen,
|
checkIfPaneIsOpen,
|
||||||
|
closePane,
|
||||||
openPane,
|
openPane,
|
||||||
sansWhitespace,
|
sansWhitespace,
|
||||||
} from '../test-utils'
|
} from '@e2e/playwright/test-utils'
|
||||||
|
|
||||||
interface EditorState {
|
interface EditorState {
|
||||||
activeLines: Array<string>
|
activeLines: Array<string>
|
||||||
@ -81,6 +82,13 @@ export class EditorFixture {
|
|||||||
expectEditor = {
|
expectEditor = {
|
||||||
toContain: this._expectEditorToContain(),
|
toContain: this._expectEditorToContain(),
|
||||||
not: { toContain: this._expectEditorToContain(true) },
|
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 }) => {
|
snapshot = async (options?: { timeout?: number; name?: string }) => {
|
||||||
const wasPaneOpen = await this.checkIfPaneIsOpen()
|
const wasPaneOpen = await this.checkIfPaneIsOpen()
|
||||||
|
@ -1,28 +1,28 @@
|
|||||||
/* eslint-disable react-hooks/rules-of-hooks */
|
/* eslint-disable react-hooks/rules-of-hooks */
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
BrowserContext,
|
BrowserContext,
|
||||||
ElectronApplication,
|
ElectronApplication,
|
||||||
TestInfo,
|
|
||||||
Page,
|
Page,
|
||||||
|
TestInfo,
|
||||||
} from '@playwright/test'
|
} from '@playwright/test'
|
||||||
|
|
||||||
import { _electron as electron } from '@playwright/test'
|
import { _electron as electron } from '@playwright/test'
|
||||||
|
|
||||||
import * as TOML from '@iarna/toml'
|
import { SETTINGS_FILE_NAME } from '@src/lib/constants'
|
||||||
import { TEST_SETTINGS } from '../storageStates'
|
import type { DeepPartial } from '@src/lib/types'
|
||||||
import { SETTINGS_FILE_NAME } from 'lib/constants'
|
|
||||||
import { getUtils, setup } from '../test-utils'
|
|
||||||
import fsp from 'fs/promises'
|
import fsp from 'fs/promises'
|
||||||
import fs from 'node:fs'
|
import fs from 'node:fs'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import { CmdBarFixture } from './cmdBarFixture'
|
|
||||||
import { EditorFixture } from './editorFixture'
|
import type { Settings } from '@rust/kcl-lib/bindings/Settings'
|
||||||
import { ToolbarFixture } from './toolbarFixture'
|
|
||||||
import { SceneFixture } from './sceneFixture'
|
import { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture'
|
||||||
import { HomePageFixture } from './homePageFixture'
|
import { EditorFixture } from '@e2e/playwright/fixtures/editorFixture'
|
||||||
import { DeepPartial } from 'lib/types'
|
import { HomePageFixture } from '@e2e/playwright/fixtures/homePageFixture'
|
||||||
import { Settings } from '@rust/kcl-lib/bindings/Settings'
|
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'
|
||||||
|
|
||||||
export class AuthenticatedApp {
|
export class AuthenticatedApp {
|
||||||
public readonly page: Page
|
public readonly page: Page
|
||||||
@ -39,7 +39,8 @@ export class AuthenticatedApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async initialise(code = '') {
|
async initialise(code = '') {
|
||||||
await setup(this.context, this.page, this.testInfo)
|
const testDir = this.testInfo.outputPath('electron-test-projects-dir')
|
||||||
|
await setup(this.context, this.page, testDir, this.testInfo)
|
||||||
const u = await getUtils(this.page)
|
const u = await getUtils(this.page)
|
||||||
|
|
||||||
await this.page.addInitScript(async (code) => {
|
await this.page.addInitScript(async (code) => {
|
||||||
@ -102,11 +103,11 @@ export class ElectronZoo {
|
|||||||
return resolve(undefined)
|
return resolve(undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Date.now() - timeA > 10000) {
|
if (Date.now() - timeA > 3000) {
|
||||||
return resolve(undefined)
|
return resolve(undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(checkDisconnected, 0)
|
setTimeout(checkDisconnected, 1)
|
||||||
}
|
}
|
||||||
checkDisconnected()
|
checkDisconnected()
|
||||||
})
|
})
|
||||||
@ -124,14 +125,13 @@ export class ElectronZoo {
|
|||||||
|
|
||||||
// We need to expose this in order for some tests that require folder
|
// We need to expose this in order for some tests that require folder
|
||||||
// creation and some code below.
|
// creation and some code below.
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||||
const that = this
|
const that = this
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
timeout: 120000,
|
|
||||||
args: ['.', '--no-sandbox'],
|
args: ['.', '--no-sandbox'],
|
||||||
env: {
|
env: {
|
||||||
...process.env,
|
...process.env,
|
||||||
TEST_SETTINGS_FILE_KEY: this.projectDirName,
|
|
||||||
IS_PLAYWRIGHT: 'true',
|
IS_PLAYWRIGHT: 'true',
|
||||||
},
|
},
|
||||||
...(process.env.ELECTRON_OVERRIDE_DIST_PATH
|
...(process.env.ELECTRON_OVERRIDE_DIST_PATH
|
||||||
@ -176,11 +176,37 @@ export class ElectronZoo {
|
|||||||
|
|
||||||
this.context = this.electron.context()
|
this.context = this.electron.context()
|
||||||
await this.context.tracing.start({ screenshots: true, snapshots: true })
|
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()
|
await this.context.tracing.startChunk()
|
||||||
|
|
||||||
await setup(this.context, this.page, testInfo)
|
// THIS IS ABSOLUTELY NECESSARY TO CHANGE THE PROJECT DIRECTORY BETWEEN
|
||||||
|
// TESTS BECAUSE OF THE ELECTRON INSTANCE REUSE.
|
||||||
|
await this.electron?.evaluate(({ app }, projectDirName) => {
|
||||||
|
// @ts-ignore can't declaration merge see main.ts
|
||||||
|
app.testProperty['TEST_SETTINGS_FILE_KEY'] = projectDirName
|
||||||
|
}, this.projectDirName)
|
||||||
|
|
||||||
|
await setup(this.context, this.page, this.projectDirName, testInfo)
|
||||||
|
|
||||||
await this.cleanProjectDir()
|
await this.cleanProjectDir()
|
||||||
|
|
||||||
@ -218,26 +244,6 @@ 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) {
|
if (!this.firstUrl) {
|
||||||
await this.page.getByText('Your Projects').count()
|
await this.page.getByText('Your Projects').count()
|
||||||
this.firstUrl = this.page.url()
|
this.firstUrl = this.page.url()
|
||||||
@ -250,11 +256,6 @@ export class ElectronZoo {
|
|||||||
// return app.reuseWindowForTest();
|
// return app.reuseWindowForTest();
|
||||||
// });
|
// });
|
||||||
|
|
||||||
await this.electron?.evaluate(({ app }, projectDirName) => {
|
|
||||||
// @ts-ignore can't declaration merge see main.ts
|
|
||||||
app.testProperty['TEST_SETTINGS_FILE_KEY'] = projectDirName
|
|
||||||
}, this.projectDirName)
|
|
||||||
|
|
||||||
// Always start at the root view
|
// Always start at the root view
|
||||||
await this.page.goto(this.firstUrl)
|
await this.page.goto(this.firstUrl)
|
||||||
|
|
||||||
@ -278,34 +279,39 @@ export class ElectronZoo {
|
|||||||
// Not a problem if it already exists.
|
// Not a problem if it already exists.
|
||||||
}
|
}
|
||||||
|
|
||||||
const tempSettingsFilePath = path.join(
|
const tempSettingsFilePath = path.resolve(
|
||||||
this.projectDirName,
|
this.projectDirName,
|
||||||
|
'..',
|
||||||
SETTINGS_FILE_NAME
|
SETTINGS_FILE_NAME
|
||||||
)
|
)
|
||||||
|
|
||||||
let settingsOverridesToml = ''
|
let settingsOverridesToml = ''
|
||||||
|
|
||||||
if (appSettings) {
|
if (appSettings) {
|
||||||
settingsOverridesToml = TOML.stringify({
|
settingsOverridesToml = settingsToToml({
|
||||||
// @ts-expect-error
|
|
||||||
settings: {
|
settings: {
|
||||||
...TEST_SETTINGS,
|
...TEST_SETTINGS,
|
||||||
...appSettings,
|
...appSettings,
|
||||||
app: {
|
app: {
|
||||||
...TEST_SETTINGS.app,
|
...TEST_SETTINGS.app,
|
||||||
project_directory: this.projectDirName,
|
|
||||||
...appSettings.app,
|
...appSettings.app,
|
||||||
},
|
},
|
||||||
|
project: {
|
||||||
|
...TEST_SETTINGS.project,
|
||||||
|
directory: this.projectDirName,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
settingsOverridesToml = TOML.stringify({
|
settingsOverridesToml = settingsToToml({
|
||||||
// @ts-expect-error
|
|
||||||
settings: {
|
settings: {
|
||||||
...TEST_SETTINGS,
|
...TEST_SETTINGS,
|
||||||
app: {
|
app: {
|
||||||
...TEST_SETTINGS.app,
|
...TEST_SETTINGS.app,
|
||||||
project_directory: this.projectDirName,
|
},
|
||||||
|
project: {
|
||||||
|
...TEST_SETTINGS.project,
|
||||||
|
directory: this.projectDirName,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type { Page, Locator } from '@playwright/test'
|
import type { Locator, Page } from '@playwright/test'
|
||||||
import { expect } from '@playwright/test'
|
import { expect } from '@playwright/test'
|
||||||
|
|
||||||
interface ProjectCardState {
|
interface ProjectCardState {
|
||||||
@ -96,7 +96,7 @@ export class HomePageFixture {
|
|||||||
await expect(this.projectSection).not.toHaveText('Loading your Projects...')
|
await expect(this.projectSection).not.toHaveText('Loading your Projects...')
|
||||||
}
|
}
|
||||||
|
|
||||||
createAndGoToProject = async (projectTitle = 'project-$nnn') => {
|
createAndGoToProject = async (projectTitle = 'untitled') => {
|
||||||
await this.projectsLoaded()
|
await this.projectsLoaded()
|
||||||
await this.projectButtonNew.click()
|
await this.projectButtonNew.click()
|
||||||
await this.projectTextName.click()
|
await this.projectTextName.click()
|
||||||
|
@ -1,15 +1,17 @@
|
|||||||
import type { Page, Locator } from '@playwright/test'
|
import type { Locator, Page } from '@playwright/test'
|
||||||
import { expect } from '../zoo-test'
|
import { isArray, uuidv4 } from '@src/lib/utils'
|
||||||
import { isArray, uuidv4 } from 'lib/utils'
|
|
||||||
import { CmdBarFixture } from './cmdBarFixture'
|
import type { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
closeDebugPanel,
|
closeDebugPanel,
|
||||||
doAndWaitForImageDiff,
|
doAndWaitForImageDiff,
|
||||||
getPixelRGBs,
|
getPixelRGBs,
|
||||||
|
getUtils,
|
||||||
openAndClearDebugPanel,
|
openAndClearDebugPanel,
|
||||||
sendCustomCmd,
|
sendCustomCmd,
|
||||||
getUtils,
|
} from '@e2e/playwright/test-utils'
|
||||||
} from '../test-utils'
|
import { expect } from '@e2e/playwright/zoo-test'
|
||||||
|
|
||||||
type MouseParams = {
|
type MouseParams = {
|
||||||
pixelDiff?: number
|
pixelDiff?: number
|
||||||
@ -41,21 +43,13 @@ type DragFromHandler = (
|
|||||||
export class SceneFixture {
|
export class SceneFixture {
|
||||||
public page: Page
|
public page: Page
|
||||||
public streamWrapper!: Locator
|
public streamWrapper!: Locator
|
||||||
public loadingIndicator!: Locator
|
|
||||||
public networkToggleConnected!: Locator
|
public networkToggleConnected!: Locator
|
||||||
public startEditSketchBtn!: Locator
|
public startEditSketchBtn!: Locator
|
||||||
|
|
||||||
get exeIndicator() {
|
|
||||||
return this.page
|
|
||||||
.getByTestId('model-state-indicator-execution-done')
|
|
||||||
.or(this.page.getByTestId('model-state-indicator-receive-reliable'))
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(page: Page) {
|
constructor(page: Page) {
|
||||||
this.page = page
|
this.page = page
|
||||||
this.streamWrapper = page.getByTestId('stream')
|
this.streamWrapper = page.getByTestId('stream')
|
||||||
this.networkToggleConnected = page.getByTestId('network-toggle-ok')
|
this.networkToggleConnected = page.getByTestId('network-toggle-ok')
|
||||||
this.loadingIndicator = this.streamWrapper.getByTestId('loading')
|
|
||||||
this.startEditSketchBtn = page
|
this.startEditSketchBtn = page
|
||||||
.getByRole('button', { name: 'Start Sketch' })
|
.getByRole('button', { name: 'Start Sketch' })
|
||||||
.or(page.getByRole('button', { name: 'Edit Sketch' }))
|
.or(page.getByRole('button', { name: 'Edit Sketch' }))
|
||||||
@ -229,10 +223,6 @@ export class SceneFixture {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
waitForExecutionDone = async () => {
|
|
||||||
await expect(this.exeIndicator).toBeVisible({ timeout: 30000 })
|
|
||||||
}
|
|
||||||
|
|
||||||
connectionEstablished = async () => {
|
connectionEstablished = async () => {
|
||||||
const timeout = 30000
|
const timeout = 30000
|
||||||
await expect(this.networkToggleConnected).toBeVisible({ timeout })
|
await expect(this.networkToggleConnected).toBeVisible({ timeout })
|
||||||
@ -241,6 +231,9 @@ export class SceneFixture {
|
|||||||
settled = async (cmdBar: CmdBarFixture) => {
|
settled = async (cmdBar: CmdBarFixture) => {
|
||||||
const u = await getUtils(this.page)
|
const u = await getUtils(this.page)
|
||||||
|
|
||||||
|
await expect(this.startEditSketchBtn).not.toBeDisabled()
|
||||||
|
await expect(this.startEditSketchBtn).toBeVisible()
|
||||||
|
|
||||||
await cmdBar.openCmdBar()
|
await cmdBar.openCmdBar()
|
||||||
await cmdBar.chooseCommand('Settings · app · show debug panel')
|
await cmdBar.chooseCommand('Settings · app · show debug panel')
|
||||||
await cmdBar.selectOption({ name: 'on' }).click()
|
await cmdBar.selectOption({ name: 'on' }).click()
|
||||||
@ -248,10 +241,6 @@ export class SceneFixture {
|
|||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
await this.waitForExecutionDone()
|
|
||||||
await expect(this.startEditSketchBtn).not.toBeDisabled()
|
|
||||||
await expect(this.startEditSketchBtn).toBeVisible()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
expectPixelColor = async (
|
expectPixelColor = async (
|
||||||
@ -310,7 +299,9 @@ export async function expectPixelColor(
|
|||||||
.toBeTruthy()
|
.toBeTruthy()
|
||||||
.catch((cause) => {
|
.catch((cause) => {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`ExpectPixelColor: expecting ${colour} got ${finalValue}`,
|
`ExpectPixelColor: point ${JSON.stringify(
|
||||||
|
coords
|
||||||
|
)} was expecting ${colour} but got ${finalValue}`,
|
||||||
{ cause }
|
{ cause }
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -1,14 +1,18 @@
|
|||||||
import { type Page, type Locator, test } from '@playwright/test'
|
import { type Locator, type Page, test } from '@playwright/test'
|
||||||
import { expect } from '../zoo-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 {
|
import {
|
||||||
checkIfPaneIsOpen,
|
checkIfPaneIsOpen,
|
||||||
closePane,
|
closePane,
|
||||||
doAndWaitForImageDiff,
|
doAndWaitForImageDiff,
|
||||||
openPane,
|
openPane,
|
||||||
} from '../test-utils'
|
} from '@e2e/playwright/test-utils'
|
||||||
import { SidebarType } from 'components/ModelingSidebar/ModelingPanes'
|
import { expect } from '@e2e/playwright/zoo-test'
|
||||||
import { SIDEBAR_BUTTON_SUFFIX } from 'lib/constants'
|
import { type baseUnitLabels } from '@src/lib/settings/settingsTypes'
|
||||||
import { ToolbarModeName } from 'lib/toolbar'
|
|
||||||
|
type LengthUnitLabel = (typeof baseUnitLabels)[keyof typeof baseUnitLabels]
|
||||||
|
|
||||||
export class ToolbarFixture {
|
export class ToolbarFixture {
|
||||||
public page: Page
|
public page: Page
|
||||||
@ -40,6 +44,7 @@ export class ToolbarFixture {
|
|||||||
featureTreePane!: Locator
|
featureTreePane!: Locator
|
||||||
gizmo!: Locator
|
gizmo!: Locator
|
||||||
gizmoDisabled!: Locator
|
gizmoDisabled!: Locator
|
||||||
|
insertButton!: Locator
|
||||||
|
|
||||||
constructor(page: Page) {
|
constructor(page: Page) {
|
||||||
this.page = page
|
this.page = page
|
||||||
@ -74,18 +79,14 @@ export class ToolbarFixture {
|
|||||||
// element or two different elements can represent these states.
|
// element or two different elements can represent these states.
|
||||||
this.gizmo = page.getByTestId('gizmo')
|
this.gizmo = page.getByTestId('gizmo')
|
||||||
this.gizmoDisabled = page.getByTestId('gizmo-disabled')
|
this.gizmoDisabled = page.getByTestId('gizmo-disabled')
|
||||||
|
|
||||||
|
this.insertButton = page.getByTestId('insert-pane-button')
|
||||||
}
|
}
|
||||||
|
|
||||||
get logoLink() {
|
get logoLink() {
|
||||||
return this.page.getByTestId('app-logo')
|
return this.page.getByTestId('app-logo')
|
||||||
}
|
}
|
||||||
|
|
||||||
get exeIndicator() {
|
|
||||||
return this.page
|
|
||||||
.getByTestId('model-state-indicator-receive-reliable')
|
|
||||||
.or(this.page.getByTestId('model-state-indicator-execution-done'))
|
|
||||||
}
|
|
||||||
|
|
||||||
startSketchPlaneSelection = async () =>
|
startSketchPlaneSelection = async () =>
|
||||||
doAndWaitForImageDiff(this.page, () => this.startSketchBtn.click(), 500)
|
doAndWaitForImageDiff(this.page, () => this.startSketchBtn.click(), 500)
|
||||||
|
|
||||||
@ -161,16 +162,10 @@ export class ToolbarFixture {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Opens file by it's name and waits for execution to finish
|
* Opens file by it's name
|
||||||
*/
|
*/
|
||||||
openFile = async (
|
openFile = async (fileName: string) => {
|
||||||
fileName: string,
|
|
||||||
{ wait }: { wait?: boolean } = { wait: true }
|
|
||||||
) => {
|
|
||||||
await this.filePane.getByText(fileName).click()
|
await this.filePane.getByText(fileName).click()
|
||||||
if (wait) {
|
|
||||||
await expect(this.exeIndicator).toBeVisible({ timeout: 15_000 })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
selectCenterRectangle = async () => {
|
selectCenterRectangle = async () => {
|
||||||
await this.page
|
await this.page
|
||||||
@ -181,6 +176,14 @@ export class ToolbarFixture {
|
|||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
await this.page.getByTestId('dropdown-center-rectangle').click()
|
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 () => {
|
selectCircleThreePoint = async () => {
|
||||||
await this.page
|
await this.page
|
||||||
@ -227,6 +230,12 @@ export class ToolbarFixture {
|
|||||||
async checkIfFeatureTreePaneIsOpen() {
|
async checkIfFeatureTreePaneIsOpen() {
|
||||||
return this.checkIfPaneIsOpen(this.featureTreeId)
|
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.
|
* Get a specific operation button from the Feature Tree pane.
|
||||||
|
121
e2e/playwright/import-ui.spec.ts
Normal file
121
e2e/playwright/import-ui.spec.ts
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
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,
|
||||||
|
cmdBar,
|
||||||
|
}) => {
|
||||||
|
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.settled(cmdBar)
|
||||||
|
|
||||||
|
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()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
@ -257,6 +257,29 @@ export const isErrorWhitelisted = (exception: Error) => {
|
|||||||
project: 'Google Chrome',
|
project: 'Google Chrome',
|
||||||
foundInSpec: 'e2e/playwright/testing-settings.spec.ts',
|
foundInSpec: 'e2e/playwright/testing-settings.spec.ts',
|
||||||
},
|
},
|
||||||
|
// TODO: fix this error in the code
|
||||||
|
{
|
||||||
|
name: 'ReferenceError',
|
||||||
|
message: '_testUtils is not defined',
|
||||||
|
stack: '',
|
||||||
|
project: 'Google Chrome',
|
||||||
|
foundInSpec: 'e2e/playwright/snapshot-tests.spec.ts',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'TypeError',
|
||||||
|
message: 'Failed to fetch',
|
||||||
|
stack: '',
|
||||||
|
project: 'Google Chrome',
|
||||||
|
foundInSpec: 'e2e/playwright/snapshot-tests.spec.ts',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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',
|
||||||
|
project: 'Google Chrome',
|
||||||
|
foundInSpec: '', // many tests are impacted by this error
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
const cleanString = (str: string) => str.replace(/[`"]/g, '')
|
const cleanString = (str: string) => str.replace(/[`"]/g, '')
|
||||||
|
7
e2e/playwright/lib/electron-helpers.ts
Normal file
7
e2e/playwright/lib/electron-helpers.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export const throwError = (message: string): never => {
|
||||||
|
throw new Error(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const throwTronAppMissing = () => {
|
||||||
|
throwError('tronApp is missing')
|
||||||
|
}
|
@ -1,17 +1,18 @@
|
|||||||
import { test, expect } from './zoo-test'
|
|
||||||
import { executorInputPath } from './test-utils'
|
|
||||||
import { join } from 'path'
|
|
||||||
import fsp from 'fs/promises'
|
import fsp from 'fs/promises'
|
||||||
|
import { join } from 'path'
|
||||||
|
|
||||||
|
import { executorInputPath } from '@e2e/playwright/test-utils'
|
||||||
|
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'When machine-api server not found butt is disabled and shows the reason',
|
'When machine-api server not found butt is disabled and shows the reason',
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ context, page }, testInfo) => {
|
async ({ context, page, scene, cmdBar }, testInfo) => {
|
||||||
await context.folderSetupFn(async (dir) => {
|
await context.folderSetupFn(async (dir) => {
|
||||||
const bracketDir = join(dir, 'bracket')
|
const bracketDir = join(dir, 'bracket')
|
||||||
await fsp.mkdir(bracketDir, { recursive: true })
|
await fsp.mkdir(bracketDir, { recursive: true })
|
||||||
await fsp.copyFile(
|
await fsp.copyFile(
|
||||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
executorInputPath('cylinder-inches.kcl'),
|
||||||
join(bracketDir, 'main.kcl')
|
join(bracketDir, 'main.kcl')
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -22,10 +23,7 @@ test(
|
|||||||
|
|
||||||
await page.getByText('bracket').click()
|
await page.getByText('bracket').click()
|
||||||
|
|
||||||
await expect(page.getByTestId('loading')).toBeAttached()
|
await scene.settled(cmdBar)
|
||||||
await expect(page.getByTestId('loading')).not.toBeAttached({
|
|
||||||
timeout: 20_000,
|
|
||||||
})
|
|
||||||
|
|
||||||
const notFoundText = 'Machine API server was not discovered'
|
const notFoundText = 'Machine API server was not discovered'
|
||||||
await expect(page.getByText(notFoundText).first()).not.toBeVisible()
|
await expect(page.getByText(notFoundText).first()).not.toBeVisible()
|
||||||
@ -46,12 +44,12 @@ test(
|
|||||||
test(
|
test(
|
||||||
'When machine-api server not found home screen & project status shows the reason',
|
'When machine-api server not found home screen & project status shows the reason',
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ context, page }, testInfo) => {
|
async ({ context, page, scene, cmdBar }, testInfo) => {
|
||||||
await context.folderSetupFn(async (dir) => {
|
await context.folderSetupFn(async (dir) => {
|
||||||
const bracketDir = join(dir, 'bracket')
|
const bracketDir = join(dir, 'bracket')
|
||||||
await fsp.mkdir(bracketDir, { recursive: true })
|
await fsp.mkdir(bracketDir, { recursive: true })
|
||||||
await fsp.copyFile(
|
await fsp.copyFile(
|
||||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
executorInputPath('cylinder-inches.kcl'),
|
||||||
join(bracketDir, 'main.kcl')
|
join(bracketDir, 'main.kcl')
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -70,10 +68,7 @@ test(
|
|||||||
|
|
||||||
await page.getByText('bracket').click()
|
await page.getByText('bracket').click()
|
||||||
|
|
||||||
await expect(page.getByTestId('loading')).toBeAttached()
|
await scene.settled(cmdBar)
|
||||||
await expect(page.getByTestId('loading')).not.toBeAttached({
|
|
||||||
timeout: 20_000,
|
|
||||||
})
|
|
||||||
|
|
||||||
await expect(page.getByText(notFoundText).nth(1)).not.toBeVisible()
|
await expect(page.getByText(notFoundText).nth(1)).not.toBeVisible()
|
||||||
|
|
||||||
|
305
e2e/playwright/named-views.spec.ts
Normal file
305
e2e/playwright/named-views.spec.ts
Normal file
@ -0,0 +1,305 @@
|
|||||||
|
import { PROJECT_SETTINGS_FILE_NAME } from '@src/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'
|
||||||
|
|
||||||
|
// Helper function to determine if the file path on disk exists
|
||||||
|
// Specifically this is used to check if project.toml exists on disk
|
||||||
|
const fileExists = async (path: string) => {
|
||||||
|
return !!(await fsp
|
||||||
|
.stat(path)
|
||||||
|
.then((_) => true)
|
||||||
|
.catch((_) => false))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Here are a few uuids.
|
||||||
|
// When created named views rust will auto generate uuids and they will
|
||||||
|
// never match the snapshots. Overwrite them in memory to these
|
||||||
|
// values to have them match the snapshots.
|
||||||
|
const uuid1: string = '0656fb1a-9640-473e-b334-591dc70c0138'
|
||||||
|
const uuid2: string = 'c810cf04-c6cc-4a4a-8b11-17bf445dcab7'
|
||||||
|
const uuid3: string = 'cfecbfee-48a6-4561-b96d-ffbe5678bb7d'
|
||||||
|
|
||||||
|
// Look up the named view by name and then rewrite it with the same uuid each time
|
||||||
|
const nameToUuid: Map<string, string> = new Map()
|
||||||
|
nameToUuid.set('uuid1', uuid1)
|
||||||
|
nameToUuid.set('uuid2', uuid2)
|
||||||
|
nameToUuid.set('uuid3', uuid3)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given the project.toml string, overwrite the named views to be the constant uuid
|
||||||
|
* values to match the snapshots. The uuids are randomly generated
|
||||||
|
*/
|
||||||
|
function tomlStringOverWriteNamedViewUuids(toml: string): string {
|
||||||
|
const settings = tomlToPerProjectSettings(toml)
|
||||||
|
const namedViews = settings.settings?.app?.named_views
|
||||||
|
if (namedViews) {
|
||||||
|
const entries = Object.entries(namedViews)
|
||||||
|
const remappedNamedViews: { [key: string]: NamedView } = {}
|
||||||
|
entries.forEach(([_, value]) => {
|
||||||
|
if (value) {
|
||||||
|
// {name:'uuid1'} -> uuid1 lookup
|
||||||
|
const staticUuid = nameToUuid.get(value.name)
|
||||||
|
if (staticUuid) {
|
||||||
|
remappedNamedViews[staticUuid] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (settings && settings.settings && settings.settings.app) {
|
||||||
|
settings.settings.app.named_views = remappedNamedViews
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return perProjectsettingsToToml(settings)
|
||||||
|
}
|
||||||
|
|
||||||
|
test.describe('Named view tests', () => {
|
||||||
|
test('Verify project.toml is not created', async ({ page }, testInfo) => {
|
||||||
|
// Create project and load it
|
||||||
|
const projectName = 'named-views'
|
||||||
|
await createProject({ name: projectName, page })
|
||||||
|
|
||||||
|
// Generate file paths for project.toml
|
||||||
|
const projectDirName = testInfo.outputPath('electron-test-projects-dir')
|
||||||
|
const tempProjectSettingsFilePath = join(
|
||||||
|
projectDirName,
|
||||||
|
projectName,
|
||||||
|
PROJECT_SETTINGS_FILE_NAME
|
||||||
|
)
|
||||||
|
|
||||||
|
// project.toml should not exist on initial project creation
|
||||||
|
let exists = await fileExists(tempProjectSettingsFilePath)
|
||||||
|
expect(exists).toBe(false)
|
||||||
|
})
|
||||||
|
test('Verify named view gets created', async ({
|
||||||
|
cmdBar,
|
||||||
|
scene,
|
||||||
|
page,
|
||||||
|
}, testInfo) => {
|
||||||
|
const projectName = 'named-views'
|
||||||
|
const myNamedView = 'uuid1'
|
||||||
|
|
||||||
|
// Create and load project
|
||||||
|
await createProject({ name: projectName, page })
|
||||||
|
await scene.settled(cmdBar)
|
||||||
|
|
||||||
|
// Create named view
|
||||||
|
const projectDirName = testInfo.outputPath('electron-test-projects-dir')
|
||||||
|
await cmdBar.openCmdBar()
|
||||||
|
await cmdBar.chooseCommand('create named view')
|
||||||
|
await cmdBar.argumentInput.fill(myNamedView)
|
||||||
|
await cmdBar.progressCmdBar(false)
|
||||||
|
|
||||||
|
// Generate paths for the project.toml
|
||||||
|
const tempProjectSettingsFilePath = join(
|
||||||
|
projectDirName,
|
||||||
|
projectName,
|
||||||
|
PROJECT_SETTINGS_FILE_NAME
|
||||||
|
)
|
||||||
|
|
||||||
|
// Expect project.toml to be generated on disk since a named view was created
|
||||||
|
await expect(async () => {
|
||||||
|
let exists = await fileExists(tempProjectSettingsFilePath)
|
||||||
|
expect(exists).toBe(true)
|
||||||
|
}).toPass()
|
||||||
|
|
||||||
|
await expect(async () => {
|
||||||
|
// Read project.toml into memory
|
||||||
|
let tomlString = await fsp.readFile(tempProjectSettingsFilePath, 'utf-8')
|
||||||
|
|
||||||
|
// Rewrite the uuids in the named views to match snapshot otherwise they will be randomly generated from rust and break
|
||||||
|
tomlString = tomlStringOverWriteNamedViewUuids(tomlString)
|
||||||
|
|
||||||
|
// Write the entire tomlString to a snapshot.
|
||||||
|
// There are many key/value pairs to check this is a safer match.
|
||||||
|
expect(tomlString).toMatchSnapshot('verify-named-view-gets-created')
|
||||||
|
}).toPass()
|
||||||
|
})
|
||||||
|
test('Verify named view gets deleted', async ({
|
||||||
|
cmdBar,
|
||||||
|
scene,
|
||||||
|
page,
|
||||||
|
}, testInfo) => {
|
||||||
|
const projectName = 'named-views'
|
||||||
|
const myNamedView1 = 'uuid1'
|
||||||
|
const myNamedView2 = 'uuid2'
|
||||||
|
|
||||||
|
// Create project and go into the project
|
||||||
|
await createProject({ name: projectName, page })
|
||||||
|
await scene.settled(cmdBar)
|
||||||
|
|
||||||
|
// Create a new named view
|
||||||
|
await cmdBar.openCmdBar()
|
||||||
|
await cmdBar.chooseCommand('create named view')
|
||||||
|
await cmdBar.argumentInput.fill(myNamedView1)
|
||||||
|
await cmdBar.progressCmdBar(false)
|
||||||
|
|
||||||
|
// Generate file paths for project.toml
|
||||||
|
const projectDirName = testInfo.outputPath('electron-test-projects-dir')
|
||||||
|
const tempProjectSettingsFilePath = join(
|
||||||
|
projectDirName,
|
||||||
|
projectName,
|
||||||
|
PROJECT_SETTINGS_FILE_NAME
|
||||||
|
)
|
||||||
|
|
||||||
|
// Except the project.toml to be written to disk since a named view was created
|
||||||
|
await expect(async () => {
|
||||||
|
let exists = await fileExists(tempProjectSettingsFilePath)
|
||||||
|
expect(exists).toBe(true)
|
||||||
|
}).toPass()
|
||||||
|
|
||||||
|
await expect(async () => {
|
||||||
|
// Read project.toml into memory
|
||||||
|
let tomlString = await fsp.readFile(tempProjectSettingsFilePath, 'utf-8')
|
||||||
|
// Rewrite the uuids in the named views to match snapshot otherwise they will be randomly generated from rust and break
|
||||||
|
tomlString = tomlStringOverWriteNamedViewUuids(tomlString)
|
||||||
|
|
||||||
|
// Write the entire tomlString to a snapshot.
|
||||||
|
// There are many key/value pairs to check this is a safer match.
|
||||||
|
expect(tomlString).toMatchSnapshot('verify-named-view-gets-created')
|
||||||
|
}).toPass()
|
||||||
|
|
||||||
|
// Delete a named view
|
||||||
|
await cmdBar.openCmdBar()
|
||||||
|
await cmdBar.chooseCommand('delete named view')
|
||||||
|
cmdBar.selectOption({ name: myNamedView2 })
|
||||||
|
await cmdBar.progressCmdBar(false)
|
||||||
|
|
||||||
|
await expect(async () => {
|
||||||
|
// Read project.toml into memory again since we deleted a named view
|
||||||
|
let tomlString = await fsp.readFile(tempProjectSettingsFilePath, 'utf-8')
|
||||||
|
// Rewrite the uuids in the named views to match snapshot otherwise they will be randomly generated from rust and break
|
||||||
|
tomlString = tomlStringOverWriteNamedViewUuids(tomlString)
|
||||||
|
|
||||||
|
// // Write the entire tomlString to a snapshot.
|
||||||
|
// // There are many key/value pairs to check this is a safer match.
|
||||||
|
expect(tomlString).toMatchSnapshot('verify-named-view-gets-deleted')
|
||||||
|
}).toPass()
|
||||||
|
})
|
||||||
|
test('Verify named view gets loaded', async ({
|
||||||
|
cmdBar,
|
||||||
|
scene,
|
||||||
|
page,
|
||||||
|
}, testInfo) => {
|
||||||
|
const projectName = 'named-views'
|
||||||
|
const myNamedView = 'uuid1'
|
||||||
|
|
||||||
|
// Create project and go into the project
|
||||||
|
await createProject({ name: projectName, page })
|
||||||
|
await scene.settled(cmdBar)
|
||||||
|
|
||||||
|
// Create a new named view
|
||||||
|
await cmdBar.openCmdBar()
|
||||||
|
await cmdBar.chooseCommand('create named view')
|
||||||
|
await cmdBar.argumentInput.fill(myNamedView)
|
||||||
|
await cmdBar.progressCmdBar(false)
|
||||||
|
|
||||||
|
// Generate file paths for project.toml
|
||||||
|
const projectDirName = testInfo.outputPath('electron-test-projects-dir')
|
||||||
|
const tempProjectSettingsFilePath = join(
|
||||||
|
projectDirName,
|
||||||
|
projectName,
|
||||||
|
PROJECT_SETTINGS_FILE_NAME
|
||||||
|
)
|
||||||
|
|
||||||
|
// Except the project.toml to be written to disk since a named view was created
|
||||||
|
await expect(async () => {
|
||||||
|
let exists = await fileExists(tempProjectSettingsFilePath)
|
||||||
|
expect(exists).toBe(true)
|
||||||
|
}).toPass()
|
||||||
|
|
||||||
|
await expect(async () => {
|
||||||
|
// Read project.toml into memory
|
||||||
|
let tomlString = await fsp.readFile(tempProjectSettingsFilePath, 'utf-8')
|
||||||
|
// Rewrite the uuids in the named views to match snapshot otherwise they will be randomly generated from rust and break
|
||||||
|
tomlString = tomlStringOverWriteNamedViewUuids(tomlString)
|
||||||
|
|
||||||
|
// Write the entire tomlString to a snapshot.
|
||||||
|
// There are many key/value pairs to check this is a safer match.
|
||||||
|
expect(tomlString).toMatchSnapshot('verify-named-view-gets-created')
|
||||||
|
}).toPass()
|
||||||
|
|
||||||
|
// Create a load a named view
|
||||||
|
await cmdBar.openCmdBar()
|
||||||
|
await cmdBar.chooseCommand('load named view')
|
||||||
|
await cmdBar.argumentInput.fill(myNamedView)
|
||||||
|
await cmdBar.progressCmdBar(false)
|
||||||
|
|
||||||
|
// Check the toast appeared
|
||||||
|
await expect(
|
||||||
|
page.getByText(`Named view ${myNamedView} loaded.`)
|
||||||
|
).toBeVisible()
|
||||||
|
})
|
||||||
|
test('Verify two named views get created', async ({
|
||||||
|
cmdBar,
|
||||||
|
scene,
|
||||||
|
page,
|
||||||
|
}, testInfo) => {
|
||||||
|
const projectName = 'named-views'
|
||||||
|
const myNamedView1 = 'uuid1'
|
||||||
|
const myNamedView2 = 'uuid2'
|
||||||
|
|
||||||
|
// Create and load project
|
||||||
|
await createProject({ name: projectName, page })
|
||||||
|
await scene.settled(cmdBar)
|
||||||
|
|
||||||
|
// Create named view
|
||||||
|
const projectDirName = testInfo.outputPath('electron-test-projects-dir')
|
||||||
|
await cmdBar.openCmdBar()
|
||||||
|
await cmdBar.chooseCommand('create named view')
|
||||||
|
await cmdBar.argumentInput.fill(myNamedView1)
|
||||||
|
await cmdBar.progressCmdBar(false)
|
||||||
|
|
||||||
|
await page.waitForTimeout(1000)
|
||||||
|
|
||||||
|
const orbitMouseStart = { x: 800, y: 130 }
|
||||||
|
const orbitMouseEnd = { x: 0, y: 130 }
|
||||||
|
await page.mouse.move(orbitMouseStart.x, orbitMouseStart.y)
|
||||||
|
await page.mouse.down({ button: 'middle' })
|
||||||
|
await page.mouse.move(orbitMouseEnd.x, orbitMouseEnd.y, {
|
||||||
|
steps: 3,
|
||||||
|
})
|
||||||
|
await page.mouse.up({ button: 'middle' })
|
||||||
|
|
||||||
|
await page.waitForTimeout(1000)
|
||||||
|
|
||||||
|
await cmdBar.openCmdBar()
|
||||||
|
await cmdBar.chooseCommand('create named view')
|
||||||
|
await cmdBar.argumentInput.fill(myNamedView2)
|
||||||
|
await cmdBar.progressCmdBar(false)
|
||||||
|
|
||||||
|
// Wait a moment for the project.toml to get written to disk with the new view point
|
||||||
|
await page.waitForTimeout(1000)
|
||||||
|
|
||||||
|
// Generate paths for the project.toml
|
||||||
|
const tempProjectSettingsFilePath = join(
|
||||||
|
projectDirName,
|
||||||
|
projectName,
|
||||||
|
PROJECT_SETTINGS_FILE_NAME
|
||||||
|
)
|
||||||
|
|
||||||
|
// Expect project.toml to be generated on disk since a named view was created
|
||||||
|
await expect(async () => {
|
||||||
|
let exists = await fileExists(tempProjectSettingsFilePath)
|
||||||
|
expect(exists).toBe(true)
|
||||||
|
}).toPass()
|
||||||
|
|
||||||
|
await expect(async () => {
|
||||||
|
// Read project.toml into memory
|
||||||
|
let tomlString = await fsp.readFile(tempProjectSettingsFilePath, 'utf-8')
|
||||||
|
// Rewrite the uuids in the named views to match snapshot otherwise they will be randomly generated from rust and break
|
||||||
|
tomlString = tomlStringOverWriteNamedViewUuids(tomlString)
|
||||||
|
|
||||||
|
// Write the entire tomlString to a snapshot.
|
||||||
|
// There are many key/value pairs to check this is a safer match.
|
||||||
|
expect(tomlString).toMatchSnapshot('verify-two-named-view-gets-created')
|
||||||
|
}).toPass()
|
||||||
|
})
|
||||||
|
})
|
@ -0,0 +1,16 @@
|
|||||||
|
[settings]
|
||||||
|
modeling = { }
|
||||||
|
text_editor = { }
|
||||||
|
command_bar = { }
|
||||||
|
|
||||||
|
[settings.app.named_views.0656fb1a-9640-473e-b334-591dc70c0138]
|
||||||
|
name = "uuid1"
|
||||||
|
eye_offset = 1_378.0059
|
||||||
|
fov_y = 45
|
||||||
|
is_ortho = false
|
||||||
|
ortho_scale_enabled = true
|
||||||
|
ortho_scale_factor = 1.6
|
||||||
|
pivot_position = [ 0, 0, 0 ]
|
||||||
|
pivot_rotation = [ 0.5380994, 0.0, 0.0, 0.8428814 ]
|
||||||
|
world_coord_system = "right_handed_up_z"
|
||||||
|
version = 1
|
@ -0,0 +1,16 @@
|
|||||||
|
[settings]
|
||||||
|
modeling = { }
|
||||||
|
text_editor = { }
|
||||||
|
command_bar = { }
|
||||||
|
|
||||||
|
[settings.app.named_views.0656fb1a-9640-473e-b334-591dc70c0138]
|
||||||
|
name = "uuid1"
|
||||||
|
eye_offset = 1_378.0059
|
||||||
|
fov_y = 45
|
||||||
|
is_ortho = false
|
||||||
|
ortho_scale_enabled = true
|
||||||
|
ortho_scale_factor = 1.6
|
||||||
|
pivot_position = [ 0, 0, 0 ]
|
||||||
|
pivot_rotation = [ 0.5380994, 0.0, 0.0, 0.8428814 ]
|
||||||
|
world_coord_system = "right_handed_up_z"
|
||||||
|
version = 1
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user