Compare commits
83 Commits
jtran/sequ
...
revert-580
Author | SHA1 | Date | |
---|---|---|---|
bfc4540a33 | |||
3d1072b67a | |||
e9c5f8e1d0 | |||
22f188dc64 | |||
f2c5661710 | |||
ecb2359bc5 | |||
6e5058bbdc | |||
988a068d6d | |||
0688ce7fe9 | |||
f9e09893e7 | |||
dd1534a61d | |||
e17c6e272c | |||
cb0470a31d | |||
ff6186f4f0 | |||
4dd669bd46 | |||
09131722e3 | |||
87ab8fe78d | |||
bc928a34ef | |||
dca78acdf2 | |||
40f4450995 | |||
178d943423 | |||
e78788482e | |||
e50e9a00d4 | |||
dc82b4c8ea | |||
a8b0e1a771 | |||
75a975b1e1 | |||
3f02bb2065 | |||
9c986d3aa8 | |||
4741d9592b | |||
e9806b83d7 | |||
0229105158 | |||
9e37e13b6b | |||
58e0c0e916 | |||
dd99c27d56 | |||
3cff26b987 | |||
78ac5b0a11 | |||
24d0b14668 | |||
6fb32eeff2 | |||
ec64daa01f | |||
e8886bb358 | |||
05a6313d97 | |||
80f78e1c61 | |||
c441a3ab1c | |||
e894242768 | |||
d8dff03746 | |||
60aee7ddba | |||
6c09da24a4 | |||
b61cd3123f | |||
865bf8ae7a | |||
f8e53c6577 | |||
f31c2c6f81 | |||
e5c05e1980 | |||
6d0da100e5 | |||
b8a0ad7144 | |||
724e65ac97 | |||
b5028f7aa8 | |||
df6b4f4c37 | |||
41eb64925b | |||
fc076173ff | |||
98822869f7 | |||
df0510c199 | |||
fda65bcbd7 | |||
c37e564b59 | |||
d6ad4b6e66 | |||
546b4ea3b8 | |||
310932dc5a | |||
51459bb413 | |||
e00dae11ba | |||
831c7f764a | |||
ec4ad268f7 | |||
be640ea0bd | |||
f8ceab2233 | |||
aea82e004a | |||
bc3a0e3896 | |||
be69039d40 | |||
2c6d69497c | |||
18db3783af | |||
b536040feb | |||
dd45cd4ef9 | |||
25cc5581be | |||
f7c192b64b | |||
faae169154 | |||
c74b9ba940 |
377
.github/dependabot.yml
vendored
377
.github/dependabot.yml
vendored
@ -1,52 +1,331 @@
|
||||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
# DO NOT EDIT THIS FILE. This dependabot file was generated
|
||||
# by https://github.com/KittyCAD/ciso Changes to this file should be addressed in
|
||||
# the ciso repository.
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: 'npm' # See documentation for possible values
|
||||
directories:
|
||||
- '/'
|
||||
- '/packages/codemirror-lang-kcl/'
|
||||
- '/packages/codemirror-lsp-client/'
|
||||
- '/rust/kcl-language-server/'
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: monday
|
||||
reviewers:
|
||||
- franknoirot
|
||||
- irev-dev
|
||||
- package-ecosystem: 'github-actions' # See documentation for possible values
|
||||
directory: '/' # Location of package manifests
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: monday
|
||||
reviewers:
|
||||
- adamchalmers
|
||||
- jessfraz
|
||||
- package-ecosystem: 'cargo' # See documentation for possible values
|
||||
directory: '/rust/' # Location of package manifests
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: monday
|
||||
reviewers:
|
||||
- adamchalmers
|
||||
- jessfraz
|
||||
groups:
|
||||
serde-dependencies:
|
||||
patterns:
|
||||
- "serde*"
|
||||
wasm-bindgen-deps:
|
||||
patterns:
|
||||
- "wasm-bindgen*"
|
||||
- package-ecosystem: "pip"
|
||||
directories:
|
||||
- "/public/kcl-samples/"
|
||||
- "/rust/kcl-python-bindings/"
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: monday
|
||||
reviewers:
|
||||
- adamchalmers
|
||||
- jessfraz
|
||||
- package-ecosystem: github-actions
|
||||
directory: /
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: monday
|
||||
time: '03:00'
|
||||
timezone: America/Los_Angeles
|
||||
open-pull-requests-limit: 5
|
||||
groups:
|
||||
security:
|
||||
applies-to: security-updates
|
||||
update-types:
|
||||
- major
|
||||
- minor
|
||||
- patch
|
||||
patch:
|
||||
applies-to: version-updates
|
||||
update-types:
|
||||
- patch
|
||||
major:
|
||||
applies-to: version-updates
|
||||
update-types:
|
||||
- major
|
||||
minor:
|
||||
applies-to: version-updates
|
||||
update-types:
|
||||
- minor
|
||||
- patch
|
||||
- package-ecosystem: cargo
|
||||
directory: /rust
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: monday
|
||||
time: '03:00'
|
||||
timezone: America/Los_Angeles
|
||||
open-pull-requests-limit: 5
|
||||
reviewers:
|
||||
- adamchalmers
|
||||
- franknoirot
|
||||
- irev-dev
|
||||
- jessfraz
|
||||
groups:
|
||||
security:
|
||||
applies-to: security-updates
|
||||
update-types:
|
||||
- major
|
||||
- minor
|
||||
- patch
|
||||
patch:
|
||||
applies-to: version-updates
|
||||
update-types:
|
||||
- patch
|
||||
major:
|
||||
applies-to: version-updates
|
||||
update-types:
|
||||
- major
|
||||
minor:
|
||||
applies-to: version-updates
|
||||
update-types:
|
||||
- minor
|
||||
- patch
|
||||
- package-ecosystem: npm
|
||||
directory: /
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: monday
|
||||
time: '03:00'
|
||||
timezone: America/Los_Angeles
|
||||
open-pull-requests-limit: 5
|
||||
reviewers:
|
||||
- adamchalmers
|
||||
- franknoirot
|
||||
- irev-dev
|
||||
- jessfraz
|
||||
groups:
|
||||
security:
|
||||
applies-to: security-updates
|
||||
update-types:
|
||||
- major
|
||||
- minor
|
||||
- patch
|
||||
patch:
|
||||
applies-to: version-updates
|
||||
update-types:
|
||||
- patch
|
||||
major:
|
||||
applies-to: version-updates
|
||||
update-types:
|
||||
- major
|
||||
minor:
|
||||
applies-to: version-updates
|
||||
update-types:
|
||||
- minor
|
||||
- patch
|
||||
- package-ecosystem: npm
|
||||
directory: /rust/kcl-language-server
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: monday
|
||||
time: '03:00'
|
||||
timezone: America/Los_Angeles
|
||||
open-pull-requests-limit: 5
|
||||
reviewers:
|
||||
- adamchalmers
|
||||
- franknoirot
|
||||
- irev-dev
|
||||
- jessfraz
|
||||
groups:
|
||||
security:
|
||||
applies-to: security-updates
|
||||
update-types:
|
||||
- major
|
||||
- minor
|
||||
- patch
|
||||
patch:
|
||||
applies-to: version-updates
|
||||
update-types:
|
||||
- patch
|
||||
major:
|
||||
applies-to: version-updates
|
||||
update-types:
|
||||
- major
|
||||
minor:
|
||||
applies-to: version-updates
|
||||
update-types:
|
||||
- minor
|
||||
- patch
|
||||
- package-ecosystem: npm
|
||||
directory: /packages/codemirror-lang-kcl
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: monday
|
||||
time: '03:00'
|
||||
timezone: America/Los_Angeles
|
||||
open-pull-requests-limit: 5
|
||||
reviewers:
|
||||
- adamchalmers
|
||||
- franknoirot
|
||||
- irev-dev
|
||||
- jessfraz
|
||||
groups:
|
||||
security:
|
||||
applies-to: security-updates
|
||||
update-types:
|
||||
- major
|
||||
- minor
|
||||
- patch
|
||||
patch:
|
||||
applies-to: version-updates
|
||||
update-types:
|
||||
- patch
|
||||
major:
|
||||
applies-to: version-updates
|
||||
update-types:
|
||||
- major
|
||||
minor:
|
||||
applies-to: version-updates
|
||||
update-types:
|
||||
- minor
|
||||
- patch
|
||||
- package-ecosystem: npm
|
||||
directory: /packages/codemirror-lsp-client
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: monday
|
||||
time: '03:00'
|
||||
timezone: America/Los_Angeles
|
||||
open-pull-requests-limit: 5
|
||||
reviewers:
|
||||
- adamchalmers
|
||||
- franknoirot
|
||||
- irev-dev
|
||||
- jessfraz
|
||||
groups:
|
||||
security:
|
||||
applies-to: security-updates
|
||||
update-types:
|
||||
- major
|
||||
- minor
|
||||
- patch
|
||||
patch:
|
||||
applies-to: version-updates
|
||||
update-types:
|
||||
- patch
|
||||
major:
|
||||
applies-to: version-updates
|
||||
update-types:
|
||||
- major
|
||||
minor:
|
||||
applies-to: version-updates
|
||||
update-types:
|
||||
- minor
|
||||
- patch
|
||||
- package-ecosystem: npm
|
||||
directory: /.github/actions/github-release
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: monday
|
||||
time: '03:00'
|
||||
timezone: America/Los_Angeles
|
||||
open-pull-requests-limit: 5
|
||||
reviewers:
|
||||
- adamchalmers
|
||||
- franknoirot
|
||||
- irev-dev
|
||||
- jessfraz
|
||||
groups:
|
||||
security:
|
||||
applies-to: security-updates
|
||||
update-types:
|
||||
- major
|
||||
- minor
|
||||
- patch
|
||||
patch:
|
||||
applies-to: version-updates
|
||||
update-types:
|
||||
- patch
|
||||
major:
|
||||
applies-to: version-updates
|
||||
update-types:
|
||||
- major
|
||||
minor:
|
||||
applies-to: version-updates
|
||||
update-types:
|
||||
- minor
|
||||
- patch
|
||||
- package-ecosystem: pip
|
||||
directory: /public/kcl-samples
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: monday
|
||||
time: '03:00'
|
||||
timezone: America/Los_Angeles
|
||||
open-pull-requests-limit: 5
|
||||
reviewers:
|
||||
- adamchalmers
|
||||
- franknoirot
|
||||
- irev-dev
|
||||
- jessfraz
|
||||
groups:
|
||||
security:
|
||||
applies-to: security-updates
|
||||
update-types:
|
||||
- major
|
||||
- minor
|
||||
- patch
|
||||
patch:
|
||||
applies-to: version-updates
|
||||
update-types:
|
||||
- patch
|
||||
major:
|
||||
applies-to: version-updates
|
||||
update-types:
|
||||
- major
|
||||
minor:
|
||||
applies-to: version-updates
|
||||
update-types:
|
||||
- minor
|
||||
- patch
|
||||
- package-ecosystem: pip
|
||||
directory: /rust/kcl-python-bindings
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: monday
|
||||
time: '03:00'
|
||||
timezone: America/Los_Angeles
|
||||
open-pull-requests-limit: 5
|
||||
reviewers:
|
||||
- adamchalmers
|
||||
- franknoirot
|
||||
- irev-dev
|
||||
- jessfraz
|
||||
groups:
|
||||
security:
|
||||
applies-to: security-updates
|
||||
update-types:
|
||||
- major
|
||||
- minor
|
||||
- patch
|
||||
patch:
|
||||
applies-to: version-updates
|
||||
update-types:
|
||||
- patch
|
||||
major:
|
||||
applies-to: version-updates
|
||||
update-types:
|
||||
- major
|
||||
minor:
|
||||
applies-to: version-updates
|
||||
update-types:
|
||||
- minor
|
||||
- patch
|
||||
- package-ecosystem: docker
|
||||
directory: /.github/actions/github-release
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: monday
|
||||
time: '03:00'
|
||||
timezone: America/Los_Angeles
|
||||
open-pull-requests-limit: 5
|
||||
reviewers:
|
||||
- adamchalmers
|
||||
- franknoirot
|
||||
- irev-dev
|
||||
- jessfraz
|
||||
groups:
|
||||
security:
|
||||
applies-to: security-updates
|
||||
update-types:
|
||||
- major
|
||||
- minor
|
||||
- patch
|
||||
patch:
|
||||
applies-to: version-updates
|
||||
update-types:
|
||||
- patch
|
||||
major:
|
||||
applies-to: version-updates
|
||||
update-types:
|
||||
- major
|
||||
minor:
|
||||
applies-to: version-updates
|
||||
update-types:
|
||||
- minor
|
||||
- patch
|
||||
|
16
.github/workflows/build-and-store-wasm.yml
vendored
16
.github/workflows/build-and-store-wasm.yml
vendored
@ -16,15 +16,21 @@ jobs:
|
||||
cache: 'yarn'
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
- name: Setup Rust
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
- name: Cache wasm
|
||||
uses: Swatinem/rust-cache@v2
|
||||
- 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:
|
||||
workspaces: './rust'
|
||||
cache: false # Configured below.
|
||||
- uses: taiki-e/install-action@955a6ff1416eae278c9f833008a9beb4b7f9afe3
|
||||
with:
|
||||
tool: wasm-pack
|
||||
- name: Rust Cache
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: rust
|
||||
- name: build wasm
|
||||
run: yarn build:wasm
|
||||
|
||||
|
56
.github/workflows/build-apps.yml
vendored
56
.github/workflows/build-apps.yml
vendored
@ -33,19 +33,63 @@ jobs:
|
||||
|
||||
- run: yarn install
|
||||
|
||||
- name: Setup Rust
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- id: filter
|
||||
name: Check for Rust changes
|
||||
uses: dorny/paths-filter@v3
|
||||
with:
|
||||
workspaces: './rust'
|
||||
filters: |
|
||||
rust:
|
||||
- 'rust/**'
|
||||
|
||||
- name: Download Wasm Cache
|
||||
id: download-wasm
|
||||
if: ${{ github.event_name == 'pull_request' && steps.filter.outputs.rust == 'false' }}
|
||||
uses: dawidd6/action-download-artifact@v7
|
||||
continue-on-error: true
|
||||
with:
|
||||
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||
name: wasm-bundle
|
||||
workflow: build-and-store-wasm.yml
|
||||
branch: main
|
||||
path: rust/kcl-wasm-lib/pkg
|
||||
|
||||
- name: Build WASM condition
|
||||
id: wasm
|
||||
run: |
|
||||
set -euox pipefail
|
||||
# Build wasm if this is a push to main or tag, there are Rust changes, or
|
||||
# downloading from the wasm cache failed.
|
||||
if [[ ${{github.event_name}} == 'push' || ${{steps.filter.outputs.rust}} == 'true' || ${{steps.download-wasm.outcome}} == 'failure' ]]; then
|
||||
echo "should-build-wasm=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "should-build-wasm=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Use correct Rust toolchain
|
||||
if: ${{ steps.wasm.outputs.should-build-wasm == 'true' }}
|
||||
shell: bash
|
||||
run: |
|
||||
[ -e rust-toolchain.toml ] || cp rust/rust-toolchain.toml ./
|
||||
|
||||
- name: Install rust
|
||||
if: ${{ steps.wasm.outputs.should-build-wasm == 'true' }}
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
with:
|
||||
cache: false # Configured below.
|
||||
|
||||
# TODO: see if we can fetch from main instead if no diff at rust
|
||||
- uses: taiki-e/install-action@955a6ff1416eae278c9f833008a9beb4b7f9afe3
|
||||
if: ${{ steps.wasm.outputs.should-build-wasm == 'true' }}
|
||||
with:
|
||||
tool: wasm-pack
|
||||
|
||||
- name: Rust Cache
|
||||
if: ${{ steps.wasm.outputs.should-build-wasm == 'true' }}
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: rust
|
||||
|
||||
- name: Run build:wasm
|
||||
if: ${{ steps.wasm.outputs.should-build-wasm == 'true' }}
|
||||
run: "yarn build:wasm"
|
||||
|
||||
- name: Set nightly version, product name, release notes, and icons
|
||||
|
62
.github/workflows/cargo-bench.yml
vendored
Normal file
62
.github/workflows/cargo-bench.yml
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- '**.rs'
|
||||
- '**/Cargo.toml'
|
||||
- '**/Cargo.lock'
|
||||
- '**/rust-toolchain.toml'
|
||||
- .github/workflows/cargo-bench.yml
|
||||
pull_request:
|
||||
paths:
|
||||
- '**.rs'
|
||||
- '**/Cargo.toml'
|
||||
- '**/Cargo.lock'
|
||||
- '**/rust-toolchain.toml'
|
||||
- .github/workflows/cargo-bench.yml
|
||||
workflow_dispatch:
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
name: cargo bench
|
||||
jobs:
|
||||
cargo-bench:
|
||||
name: cargo bench
|
||||
runs-on:
|
||||
- runs-on=${{ github.run_id }}
|
||||
- runner=32cpu-linux-x64
|
||||
- extras=s3-cache
|
||||
steps:
|
||||
- uses: runs-on/action@v1
|
||||
- uses: actions/checkout@v4
|
||||
- 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: rust
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
cargo install cargo-criterion
|
||||
cargo install cargo-codspeed
|
||||
cd rust/kcl-lib
|
||||
cargo add --dev codspeed-criterion-compat --rename criterion
|
||||
- name: Build the benchmark target(s)
|
||||
run: |
|
||||
cd rust
|
||||
cargo codspeed build --measurement-mode walltime
|
||||
- name: Run the benchmarks
|
||||
uses: CodSpeedHQ/action@v3
|
||||
with:
|
||||
working-directory: rust
|
||||
run: cargo codspeed run
|
||||
token: ${{ secrets.CODSPEED_TOKEN }}
|
||||
mode: walltime
|
||||
env:
|
||||
KITTYCAD_API_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN }}
|
2
.github/workflows/cargo-check.yml
vendored
2
.github/workflows/cargo-check.yml
vendored
@ -22,7 +22,7 @@ jobs:
|
||||
- name: Use correct Rust toolchain
|
||||
shell: bash
|
||||
run: |
|
||||
cp --update=none rust/rust-toolchain.toml ./ || true
|
||||
[ -e rust-toolchain.toml ] || cp rust/rust-toolchain.toml ./
|
||||
- name: Install rust
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
with:
|
||||
|
2
.github/workflows/cargo-clippy.yml
vendored
2
.github/workflows/cargo-clippy.yml
vendored
@ -29,7 +29,7 @@ jobs:
|
||||
- name: Use correct Rust toolchain
|
||||
shell: bash
|
||||
run: |
|
||||
cp --update=none rust/rust-toolchain.toml ./ || true
|
||||
[ -e rust-toolchain.toml ] || cp rust/rust-toolchain.toml ./
|
||||
- name: Install rust
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
with:
|
||||
|
2
.github/workflows/cargo-fmt.yml
vendored
2
.github/workflows/cargo-fmt.yml
vendored
@ -31,7 +31,7 @@ jobs:
|
||||
- name: Use correct Rust toolchain
|
||||
shell: bash
|
||||
run: |
|
||||
cp --update=none rust/rust-toolchain.toml ./ || true
|
||||
[ -e rust-toolchain.toml ] || cp rust/rust-toolchain.toml ./
|
||||
- name: Install rust
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
with:
|
||||
|
2
.github/workflows/cargo-test.yml
vendored
2
.github/workflows/cargo-test.yml
vendored
@ -29,7 +29,7 @@ jobs:
|
||||
- name: Use correct Rust toolchain
|
||||
shell: bash
|
||||
run: |
|
||||
cp --update=none rust/rust-toolchain.toml ./ || true
|
||||
[ -e rust-toolchain.toml ] || cp rust/rust-toolchain.toml ./
|
||||
- name: Install rust
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
with:
|
||||
|
528
.github/workflows/e2e-tests.yml
vendored
528
.github/workflows/e2e-tests.yml
vendored
@ -18,16 +18,19 @@ permissions:
|
||||
|
||||
jobs:
|
||||
|
||||
path-changes:
|
||||
conditions:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
significant: ${{ steps.path-changes.outputs.significant }}
|
||||
should-run: ${{ steps.should-run.outputs.should-run }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Fetch the base branch
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
run: git fetch origin ${{ github.base_ref }} --depth=1
|
||||
|
||||
- name: Check for path changes
|
||||
id: path-changes
|
||||
shell: bash
|
||||
@ -47,218 +50,341 @@ jobs:
|
||||
else
|
||||
echo "significant=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
- name: Display path changes
|
||||
|
||||
- name: Should run
|
||||
id: should-run
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# We should run when this is a scheduled run or if there are
|
||||
# significant changes in the diff.
|
||||
if [[ ${{ github.event_name }} == 'schedule' || ${{ steps.path-changes.outputs.significant }} == 'true' ]]; then
|
||||
echo "should-run=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "should-run=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Display conditions
|
||||
shell: bash
|
||||
run: |
|
||||
# For debugging purposes.
|
||||
set -euo pipefail
|
||||
echo "significant: ${{ steps.path-changes.outputs.significant }}"
|
||||
echo "should-run: ${{ steps.should-run.outputs.should-run }}"
|
||||
|
||||
|
||||
prepare-wasm:
|
||||
# seperate job on Ubuntu to build or fetch the wasm blob once on the fastest runner
|
||||
runs-on: runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64
|
||||
needs: conditions
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
if: needs.conditions.outputs.should-run == 'true'
|
||||
|
||||
- id: filter
|
||||
if: needs.conditions.outputs.should-run == 'true'
|
||||
name: Check for Rust changes
|
||||
uses: dorny/paths-filter@v3
|
||||
with:
|
||||
filters: |
|
||||
rust:
|
||||
- 'rust/**'
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
if: needs.conditions.outputs.should-run == 'true'
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install dependencies
|
||||
if: needs.conditions.outputs.should-run == 'true'
|
||||
run: yarn
|
||||
|
||||
- name: Download Wasm Cache
|
||||
id: download-wasm
|
||||
if: ${{ needs.conditions.outputs.should-run == 'true' && github.event_name != 'schedule' && steps.filter.outputs.rust == 'false' }}
|
||||
uses: dawidd6/action-download-artifact@v7
|
||||
continue-on-error: true
|
||||
with:
|
||||
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||
name: wasm-bundle
|
||||
workflow: build-and-store-wasm.yml
|
||||
branch: main
|
||||
path: rust/kcl-wasm-lib/pkg
|
||||
|
||||
- name: Build WASM condition
|
||||
id: wasm
|
||||
if: needs.conditions.outputs.should-run == 'true'
|
||||
run: |
|
||||
set -euox pipefail
|
||||
# Build wasm if this is a scheduled run, there are Rust changes, or
|
||||
# downloading from the wasm cache failed.
|
||||
if [[ ${{github.event_name}} == 'schedule' || ${{steps.filter.outputs.rust}} == 'true' || ${{steps.download-wasm.outcome}} == 'failure' ]]; then
|
||||
echo "should-build-wasm=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "should-build-wasm=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Use correct Rust toolchain
|
||||
if: ${{ needs.conditions.outputs.should-run == 'true' && steps.wasm.outputs.should-build-wasm == 'true' }}
|
||||
shell: bash
|
||||
run: |
|
||||
[ -e rust-toolchain.toml ] || cp rust/rust-toolchain.toml ./
|
||||
|
||||
- name: Install rust
|
||||
if: ${{ needs.conditions.outputs.should-run == 'true' && steps.wasm.outputs.should-build-wasm == 'true' }}
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
with:
|
||||
cache: false # Configured below.
|
||||
|
||||
- uses: taiki-e/install-action@955a6ff1416eae278c9f833008a9beb4b7f9afe3
|
||||
if: ${{ needs.conditions.outputs.should-run == 'true' && steps.wasm.outputs.should-build-wasm == 'true' }}
|
||||
with:
|
||||
tool: wasm-pack
|
||||
|
||||
- name: Rust Cache
|
||||
if: ${{ needs.conditions.outputs.should-run == 'true' && steps.wasm.outputs.should-build-wasm == 'true' }}
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: './rust'
|
||||
|
||||
- name: Build Wasm
|
||||
if: ${{ needs.conditions.outputs.should-run == 'true' && steps.wasm.outputs.should-build-wasm == 'true' }}
|
||||
shell: bash
|
||||
run: yarn build:wasm
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: needs.conditions.outputs.should-run == 'true'
|
||||
with:
|
||||
name: prepared-wasm
|
||||
path: |
|
||||
rust/kcl-wasm-lib/pkg/kcl_wasm_lib*
|
||||
|
||||
|
||||
snapshots:
|
||||
name: playwright:snapshots:ubuntu
|
||||
runs-on: runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64
|
||||
needs: [conditions, prepare-wasm]
|
||||
steps:
|
||||
- uses: actions/create-github-app-token@v1
|
||||
if: needs.conditions.outputs.should-run == 'true'
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ secrets.MODELING_APP_GH_APP_ID }}
|
||||
private-key: ${{ secrets.MODELING_APP_GH_APP_PRIVATE_KEY }}
|
||||
owner: ${{ github.repository_owner }}
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
if: needs.conditions.outputs.should-run == 'true'
|
||||
with:
|
||||
token: ${{ steps.app-token.outputs.token }}
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
if: needs.conditions.outputs.should-run == 'true'
|
||||
name: prepared-wasm
|
||||
|
||||
- name: Copy prepared wasm
|
||||
if: needs.conditions.outputs.should-run == 'true'
|
||||
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
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
if: needs.conditions.outputs.should-run == 'true'
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install dependencies
|
||||
id: deps-install
|
||||
if: needs.conditions.outputs.should-run == 'true'
|
||||
run: yarn
|
||||
|
||||
- name: Cache Playwright Browsers
|
||||
if: needs.conditions.outputs.should-run == 'true'
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/ms-playwright/
|
||||
key: ${{ runner.os }}-playwright-${{ hashFiles('yarn.lock') }}
|
||||
|
||||
- name: Install Playwright Browsers
|
||||
if: needs.conditions.outputs.should-run == 'true'
|
||||
run: yarn playwright install --with-deps
|
||||
|
||||
- name: build web
|
||||
if: needs.conditions.outputs.should-run == 'true'
|
||||
run: yarn tronb:vite:dev
|
||||
|
||||
- name: Run ubuntu/chrome snapshots
|
||||
if: needs.conditions.outputs.should-run == 'true'
|
||||
uses: nick-fields/retry@v3.0.2
|
||||
with:
|
||||
shell: bash
|
||||
command: yarn test:snapshots
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
env:
|
||||
CI: true
|
||||
NODE_ENV: development
|
||||
VITE_KC_DEV_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
VITE_KC_SKIP_AUTH: true
|
||||
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
snapshottoken: ${{ secrets.KITTYCAD_API_TOKEN }}
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: ${{ needs.conditions.outputs.should-run == 'true' && !cancelled() && (success() || failure()) }}
|
||||
with:
|
||||
name: playwright-report-ubuntu-snapshot-${{ github.sha }}
|
||||
path: playwright-report/
|
||||
include-hidden-files: true
|
||||
retention-days: 30
|
||||
overwrite: true
|
||||
|
||||
- name: check for changes
|
||||
if: ${{ needs.conditions.outputs.should-run == 'true' && github.ref != 'refs/heads/main' }}
|
||||
shell: bash
|
||||
id: git-check
|
||||
run: |
|
||||
git add e2e/playwright/snapshot-tests.spec.ts-snapshots e2e/playwright/snapshots
|
||||
if git status | grep -q "Changes to be committed"
|
||||
then echo "modified=true" >> $GITHUB_OUTPUT
|
||||
else echo "modified=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Commit changes, if any
|
||||
if: ${{ needs.conditions.outputs.should-run == 'true' && steps.git-check.outputs.modified == 'true' }}
|
||||
shell: bash
|
||||
run: |
|
||||
git add e2e/playwright/snapshot-tests.spec.ts-snapshots e2e/playwright/snapshots
|
||||
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git config --local user.name "github-actions[bot]"
|
||||
git remote set-url origin https://${{ github.actor }}:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git
|
||||
git fetch origin
|
||||
echo ${{ github.head_ref }}
|
||||
git checkout ${{ github.head_ref }}
|
||||
git commit -m "A snapshot a day keeps the bugs away! 📷🐛" || true
|
||||
git push
|
||||
git push origin ${{ github.head_ref }}
|
||||
|
||||
electron:
|
||||
needs: [conditions, prepare-wasm]
|
||||
timeout-minutes: 60
|
||||
name: playwright:electron:${{ matrix.os }} ${{ matrix.shardIndex }} ${{ matrix.shardTotal }}
|
||||
env:
|
||||
OS_NAME: ${{ contains(matrix.os, 'ubuntu') && 'ubuntu' || (contains(matrix.os, 'windows') && 'windows' || 'macos') }}
|
||||
name: playwright:electron:${{ contains(matrix.os, 'ubuntu') && 'ubuntu' || (contains(matrix.os, 'windows') && 'windows' || 'macos') }}:${{ matrix.shardIndex }}:${{ matrix.shardTotal }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# TODO: enable self-hosted-windows-8-cores once available
|
||||
os: [namespace-profile-ubuntu-8-cores, namespace-profile-macos-8-cores, windows-16-cores]
|
||||
# TODO: enable namespace-profile-windows-latest once available
|
||||
os:
|
||||
- "runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64"
|
||||
- namespace-profile-macos-8-cores
|
||||
- windows-latest
|
||||
shardIndex: [1, 2, 3, 4]
|
||||
shardTotal: [4]
|
||||
# Disable macos and windows tests on hourly e2e tests since we only care
|
||||
# about server side changes.
|
||||
# Technique from https://github.com/joaomcteixeira/python-project-skeleton/pull/31/files
|
||||
isScheduled:
|
||||
- ${{ github.event_name == 'schedule' }}
|
||||
exclude:
|
||||
- os: namespace-profile-macos-8-cores
|
||||
isScheduled: true
|
||||
- os: windows-latest
|
||||
isScheduled: true
|
||||
# TODO: add ref here for main and latest release tag
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: path-changes
|
||||
if: ${{ needs.path-changes.outputs.significant == 'true' }}
|
||||
steps:
|
||||
- uses: actions/create-github-app-token@v1
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ secrets.MODELING_APP_GH_APP_ID }}
|
||||
private-key: ${{ secrets.MODELING_APP_GH_APP_PRIVATE_KEY }}
|
||||
owner: ${{ github.repository_owner }}
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ steps.app-token.outputs.token }}
|
||||
- id: filter
|
||||
name: Check for Rust changes
|
||||
uses: dorny/paths-filter@v3
|
||||
with:
|
||||
filters: |
|
||||
rust:
|
||||
- 'rust/**'
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
- name: Install dependencies
|
||||
id: deps-install
|
||||
shell: bash
|
||||
run: yarn
|
||||
- name: Cache Playwright Browsers
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/ms-playwright/
|
||||
key: ${{ runner.os }}-playwright-${{ hashFiles('yarn.lock') }}
|
||||
- name: Install Playwright Browsers
|
||||
shell: bash
|
||||
run: yarn playwright install --with-deps
|
||||
- name: Download Wasm Cache
|
||||
id: download-wasm
|
||||
if: github.event_name != 'schedule' && steps.filter.outputs.rust == 'false'
|
||||
uses: dawidd6/action-download-artifact@v7
|
||||
continue-on-error: true
|
||||
with:
|
||||
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||
name: wasm-bundle
|
||||
workflow: build-and-store-wasm.yml
|
||||
branch: main
|
||||
path: rust/kcl-wasm-lib/pkg
|
||||
- name: copy wasm blob
|
||||
if: github.event_name != 'schedule' && steps.filter.outputs.rust == 'false'
|
||||
shell: bash
|
||||
run: cp rust/kcl-wasm-lib/pkg/kcl_wasm_lib_bg.wasm public
|
||||
continue-on-error: true
|
||||
- name: Setup Rust
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
- uses: taiki-e/install-action@955a6ff1416eae278c9f833008a9beb4b7f9afe3
|
||||
with:
|
||||
tool: wasm-pack
|
||||
- name: Cache Wasm (because rust diff)
|
||||
if: github.event_name == 'schedule' || steps.filter.outputs.rust == 'true'
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: './rust'
|
||||
- name: OR Cache Wasm (because wasm cache failed)
|
||||
if: steps.download-wasm.outcome == 'failure'
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: './rust'
|
||||
- name: install good sed
|
||||
if: ${{ startsWith(matrix.os, 'macos') }}
|
||||
shell: bash
|
||||
run: |
|
||||
brew install gnu-sed
|
||||
echo "/opt/homebrew/opt/gnu-sed/libexec/gnubin" >> $GITHUB_PATH
|
||||
- name: Install vector
|
||||
shell: bash
|
||||
# TODO: figure out what to do with this, it's failing
|
||||
if: false
|
||||
run: |
|
||||
curl --proto '=https' --tlsv1.2 -sSfL https://sh.vector.dev > /tmp/vector.sh
|
||||
chmod +x /tmp/vector.sh
|
||||
/tmp/vector.sh -y -no-modify-path
|
||||
mkdir -p /tmp/vector
|
||||
cp .github/workflows/vector.toml /tmp/vector.toml
|
||||
sed -i "s#GITHUB_WORKFLOW#${GITHUB_WORKFLOW}#g" /tmp/vector.toml
|
||||
sed -i "s#GITHUB_REPOSITORY#${GITHUB_REPOSITORY}#g" /tmp/vector.toml
|
||||
sed -i "s#GITHUB_SHA#${GITHUB_SHA}#g" /tmp/vector.toml
|
||||
sed -i "s#GITHUB_REF_NAME#${GITHUB_REF_NAME}#g" /tmp/vector.toml
|
||||
sed -i "s#GH_ACTIONS_AXIOM_TOKEN#${{secrets.GH_ACTIONS_AXIOM_TOKEN}}#g" /tmp/vector.toml
|
||||
cat /tmp/vector.toml
|
||||
${HOME}/.vector/bin/vector --config /tmp/vector.toml &
|
||||
- name: Build Wasm (because rust diff)
|
||||
if: github.event_name == 'schedule' || steps.filter.outputs.rust == 'true'
|
||||
shell: bash
|
||||
run: yarn build:wasm
|
||||
- name: OR Build Wasm (because wasm cache failed)
|
||||
if: steps.download-wasm.outcome == 'failure'
|
||||
shell: bash
|
||||
run: yarn build:wasm
|
||||
- name: build web
|
||||
shell: bash
|
||||
run: yarn tronb:vite:dev
|
||||
- name: Run ubuntu/chrome snapshots
|
||||
if: ${{ matrix.os == 'namespace-profile-ubuntu-8-cores' && matrix.shardIndex == 1 }}
|
||||
shell: bash
|
||||
# TODO: break this in its own job, for now it's not slowing down the overall execution as ubuntu is the quickest,
|
||||
# but we could do better. This forces a large 1/1 shard of all 20 snapshot tests that runs in about 3 minutes.
|
||||
run: |
|
||||
yarn test:snapshots
|
||||
env:
|
||||
CI: true
|
||||
NODE_ENV: development
|
||||
VITE_KC_DEV_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
VITE_KC_SKIP_AUTH: true
|
||||
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
snapshottoken: ${{ secrets.KITTYCAD_API_TOKEN }}
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: ${{ !cancelled() && (success() || failure()) }}
|
||||
with:
|
||||
name: playwright-report-snapshots-${{ matrix.os }}-snapshot-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||
path: playwright-report/
|
||||
include-hidden-files: true
|
||||
retention-days: 30
|
||||
overwrite: true
|
||||
- name: Clean up test-results
|
||||
if: ${{ !cancelled() && (success() || failure()) }}
|
||||
continue-on-error: true
|
||||
run: rm -r test-results
|
||||
- name: check for changes
|
||||
if: ${{ matrix.os == 'namespace-profile-ubuntu-8-cores' && matrix.shardIndex == 1 && github.ref != 'refs/heads/main' }}
|
||||
shell: bash
|
||||
id: git-check
|
||||
run: |
|
||||
git add e2e/playwright/snapshot-tests.spec.ts-snapshots e2e/playwright/snapshots
|
||||
if git status | grep -q "Changes to be committed"
|
||||
then echo "modified=true" >> $GITHUB_OUTPUT
|
||||
else echo "modified=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
- name: Commit changes, if any
|
||||
if: steps.git-check.outputs.modified == 'true'
|
||||
shell: bash
|
||||
run: |
|
||||
git add e2e/playwright/snapshot-tests.spec.ts-snapshots e2e/playwright/snapshots
|
||||
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git config --local user.name "github-actions[bot]"
|
||||
git remote set-url origin https://${{ github.actor }}:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git
|
||||
git fetch origin
|
||||
echo ${{ github.head_ref }}
|
||||
git checkout ${{ github.head_ref }}
|
||||
git commit -m "A snapshot a day keeps the bugs away! 📷🐛 (OS: ${{matrix.os}})" || true
|
||||
git push
|
||||
git push origin ${{ github.head_ref }}
|
||||
# only upload artifacts if there's actually changes
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: steps.git-check.outputs.modified == 'true'
|
||||
with:
|
||||
name: playwright-report-${{ matrix.os }}-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||
path: playwright-report/
|
||||
include-hidden-files: true
|
||||
retention-days: 30
|
||||
- uses: actions/download-artifact@v4
|
||||
if: ${{ !cancelled() && (success() || failure()) }}
|
||||
continue-on-error: true
|
||||
with:
|
||||
name: test-results-${{ matrix.os }}-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||
path: test-results/
|
||||
- name: Run playwright/electron flow (with retries)
|
||||
id: retry
|
||||
if: ${{ !cancelled() && steps.deps-install.outcome == 'success' }}
|
||||
uses: nick-fields/retry@v3.0.2
|
||||
with:
|
||||
shell: bash
|
||||
command: .github/ci-cd-scripts/playwright-electron.sh ${{matrix.shardIndex}} ${{matrix.shardTotal}} ${{matrix.os}}
|
||||
timeout_minutes: 30
|
||||
max_attempts: 25
|
||||
env:
|
||||
CI: true
|
||||
FAIL_ON_CONSOLE_ERRORS: true
|
||||
NODE_ENV: development
|
||||
VITE_KC_DEV_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
VITE_KC_SKIP_AUTH: true
|
||||
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: test-results-${{ matrix.os }}-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||
path: test-results/
|
||||
include-hidden-files: true
|
||||
retention-days: 30
|
||||
overwrite: true
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-report-${{ matrix.os }}-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||
path: playwright-report/
|
||||
include-hidden-files: true
|
||||
retention-days: 30
|
||||
overwrite: true
|
||||
- uses: actions/checkout@v4
|
||||
if: needs.conditions.outputs.should-run == 'true'
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
if: needs.conditions.outputs.should-run == 'true'
|
||||
name: prepared-wasm
|
||||
|
||||
- name: Copy prepared wasm
|
||||
if: needs.conditions.outputs.should-run == 'true'
|
||||
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
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
if: needs.conditions.outputs.should-run == 'true'
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install dependencies
|
||||
id: deps-install
|
||||
if: needs.conditions.outputs.should-run == 'true'
|
||||
run: yarn
|
||||
|
||||
- name: Cache Playwright Browsers
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/ms-playwright/
|
||||
key: ${{ runner.os }}-playwright-${{ hashFiles('yarn.lock') }}
|
||||
|
||||
- name: Install Playwright Browsers
|
||||
run: yarn playwright install --with-deps
|
||||
|
||||
- name: Build web
|
||||
if: needs.conditions.outputs.should-run == 'true'
|
||||
run: yarn tronb:vite:dev
|
||||
|
||||
- name: Install good sed
|
||||
if: startsWith(matrix.os, 'macos')
|
||||
shell: bash
|
||||
run: |
|
||||
brew install gnu-sed
|
||||
echo "/opt/homebrew/opt/gnu-sed/libexec/gnubin" >> $GITHUB_PATH
|
||||
|
||||
# TODO: Add back axiom logs
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
if: ${{ needs.conditions.outputs.should-run == 'true' && !cancelled() && (success() || failure()) }}
|
||||
continue-on-error: true
|
||||
with:
|
||||
name: test-results-${{ env.OS_NAME }}-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||
path: test-results/
|
||||
|
||||
- name: Run playwright/electron flow (with retries)
|
||||
id: retry
|
||||
if: ${{ needs.conditions.outputs.should-run == 'true' && !cancelled() && steps.deps-install.outcome == 'success' }}
|
||||
uses: nick-fields/retry@v3.0.2
|
||||
with:
|
||||
shell: bash
|
||||
command: .github/ci-cd-scripts/playwright-electron.sh ${{matrix.shardIndex}} ${{matrix.shardTotal}} ${{ env.OS_NAME }}
|
||||
timeout_minutes: 45
|
||||
max_attempts: 15
|
||||
env:
|
||||
CI: true
|
||||
FAIL_ON_CONSOLE_ERRORS: true
|
||||
NODE_ENV: development
|
||||
VITE_KC_DEV_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
VITE_KC_SKIP_AUTH: true
|
||||
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: ${{ needs.conditions.outputs.should-run == 'true' && always() }}
|
||||
with:
|
||||
name: test-results-${{ env.OS_NAME }}-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||
path: test-results/
|
||||
include-hidden-files: true
|
||||
retention-days: 30
|
||||
overwrite: true
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: ${{ needs.conditions.outputs.should-run == 'true' && always() }}
|
||||
with:
|
||||
name: playwright-report-${{ env.OS_NAME }}-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||
path: playwright-report/
|
||||
include-hidden-files: true
|
||||
retention-days: 30
|
||||
overwrite: true
|
||||
|
13
.github/workflows/generate-website-docs.yml
vendored
13
.github/workflows/generate-website-docs.yml
vendored
@ -5,6 +5,7 @@ on:
|
||||
paths:
|
||||
- .github/workflows/generate-website-docs.yml
|
||||
- 'docs/**'
|
||||
- 'public/kcl-samples/**'
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/workflows/generate-website-docs.yml
|
||||
@ -39,9 +40,21 @@ jobs:
|
||||
# cleanup old
|
||||
rm -rf documentation/content/pages/docs/kcl/*.md
|
||||
rm -rf documentation/content/pages/docs/kcl/types
|
||||
rm -rf documentation/content/pages/docs/kcl/settings
|
||||
rm -rf documentation/content/pages/docs/kcl/consts
|
||||
# move new
|
||||
mv -f docs/kcl/*.md documentation/content/pages/docs/kcl/
|
||||
mv -f docs/kcl/types documentation/content/pages/docs/kcl/
|
||||
mv -f docs/kcl/settings documentation/content/pages/docs/kcl/
|
||||
mv -f docs/kcl/consts documentation/content/pages/docs/kcl/
|
||||
- name: move kcl-samples
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p documentation/content/pages/docs/kcl-samples
|
||||
# cleanup old
|
||||
rm -rf documentation/content/pages/docs/kcl-samples/*
|
||||
# move new
|
||||
mv -f public/kcl-samples/* documentation/content/pages/docs/kcl-samples/
|
||||
- name: commit the changes in the docs repo
|
||||
shell: bash
|
||||
run: |
|
||||
|
5
.github/workflows/kcl-python-bindings.yml
vendored
5
.github/workflows/kcl-python-bindings.yml
vendored
@ -159,10 +159,13 @@ jobs:
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
needs: [linux-x86_64, windows, macos, sdist]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: rust/kcl-python-bindings
|
||||
- name: Install the latest version of uv
|
||||
uses: astral-sh/setup-uv@v5
|
||||
- name: Install codespell
|
||||
- name: do uv things
|
||||
run: |
|
||||
cd rust/kcl-python-bindings
|
||||
uv venv .venv
|
||||
|
52
.github/workflows/kcl-samples-manifest.yml
vendored
52
.github/workflows/kcl-samples-manifest.yml
vendored
@ -1,52 +0,0 @@
|
||||
name: KCL Samples Manifest
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
workflow_dispatch: # Allows manual triggering from the Actions tab
|
||||
|
||||
jobs:
|
||||
generate-manifest:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
# Checkout the repository
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Set up Node.js
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
|
||||
# Run the script to generate the manifest.json
|
||||
- name: Run manifest generation script
|
||||
working-directory: public/kcl-samples
|
||||
run: node generate-manifest.js
|
||||
|
||||
# Check if the manifest.json has changed
|
||||
- name: Check for changes
|
||||
id: check-for-changes
|
||||
working-directory: public/kcl-samples
|
||||
run: |
|
||||
git diff --exit-code ./manifest.json || echo "changed=changes detected" >> "$GITHUB_OUTPUT"
|
||||
|
||||
# Stage and commit the changes if any were made
|
||||
- name: Commit and push changes
|
||||
if: ${{ success() && (steps.check-for-changes.outputs.changed == 'changes detected') }}
|
||||
working-directory: public/kcl-samples
|
||||
run: |
|
||||
git add ./manifest.json
|
||||
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git config --local user.name "github-actions[bot]"
|
||||
git remote set-url origin https://${{ github.actor }}:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git
|
||||
git fetch origin
|
||||
echo ${{ github.head_ref }}
|
||||
git checkout ${{ github.head_ref }}
|
||||
git commit -m "Update manifest.json"
|
||||
git push origin ${{ github.head_ref }}
|
64
.github/workflows/pr-kcl-samples-repo.yml
vendored
64
.github/workflows/pr-kcl-samples-repo.yml
vendored
@ -1,64 +0,0 @@
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'public/kcl-samples/**'
|
||||
# pull_request:
|
||||
# paths:
|
||||
# - .github/workflows/pr-kcl-samples-repo.yml
|
||||
workflow_dispatch:
|
||||
name: Create PR in kcl-samples repo
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
jobs:
|
||||
pr-kcl-samples-repo:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/create-github-app-token@v1
|
||||
id: app-token
|
||||
with:
|
||||
# required
|
||||
app-id: ${{ secrets.GH_ORG_APP_ID }}
|
||||
private-key: ${{ secrets.GH_ORG_APP_PRIVATE_KEY }}
|
||||
owner: ${{ github.repository_owner }}
|
||||
- uses: actions/checkout@v4
|
||||
# Checkout the other repo since we want to update the files there.
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'KittyCAD/kcl-samples'
|
||||
ref: next
|
||||
path: kcl-samples-repo
|
||||
token: ${{ steps.app-token.outputs.token }}
|
||||
- name: Move files to repo work tree
|
||||
shell: bash
|
||||
run: |
|
||||
rsync -av --delete \
|
||||
--exclude='.git' \
|
||||
--exclude='.github' \
|
||||
--exclude='screenshots' \
|
||||
--exclude='step' \
|
||||
public/kcl-samples/ kcl-samples-repo/
|
||||
- name: Commit the changes in the repo
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
cd kcl-samples-repo
|
||||
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git config --local user.name "github-actions[bot]"
|
||||
export NEW_BRANCH="update-from-modeling-app"
|
||||
git switch -c "$NEW_BRANCH"
|
||||
git add --all .
|
||||
git commit -m "Updates from modeling-app repo" || exit 0
|
||||
git push -f origin "$NEW_BRANCH"
|
||||
gh pr create --title "Update from modeling-app" \
|
||||
--body "Updating files from the modeling-app repo" \
|
||||
--head "$NEW_BRANCH" \
|
||||
--reviewer jtran \
|
||||
--base next || true
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
|
5
.gitignore
vendored
5
.gitignore
vendored
@ -53,15 +53,14 @@ e2e/playwright/export-snapshots/*
|
||||
|
||||
/public/kcl-samples.zip
|
||||
/public/kcl-samples/.github
|
||||
/public/kcl-samples/screenshots
|
||||
/public/kcl-samples/step
|
||||
/public/kcl-samples/screenshots/main.kcl
|
||||
/public/kcl-samples/step/main.kcl
|
||||
/test-results/
|
||||
/playwright-report/
|
||||
/blob-report/
|
||||
/playwright/.cache/
|
||||
/src/lang/std/artifactMapCache
|
||||
|
||||
|
||||
## generated files
|
||||
src/**/*.typegen.ts
|
||||
|
||||
|
@ -40,7 +40,7 @@ sketch001 = startSketchOn('XZ')
|
||||
angle = angleToMatchLengthY(seg01, 15, %),
|
||||
length = 5
|
||||
}, %)
|
||||
|> yLineTo(0, %)
|
||||
|> yLine(endAbsolute = 0)
|
||||
|> close()
|
||||
|
||||
extrusion = extrude(sketch001, length = 5)
|
||||
|
@ -35,10 +35,10 @@ angledLine(
|
||||
```js
|
||||
exampleSketch = startSketchOn(XZ)
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> yLineTo(15, %)
|
||||
|> yLine(endAbsolute = 15)
|
||||
|> angledLine({ angle = 30, length = 15 }, %)
|
||||
|> line(end = [8, -10])
|
||||
|> yLineTo(0, %)
|
||||
|> yLine(endAbsolute = 0)
|
||||
|> close()
|
||||
|
||||
example = extrude(exampleSketch, length = 10)
|
||||
|
File diff suppressed because one or more lines are too long
@ -37,7 +37,7 @@ sketch001 = startSketchOn('XZ')
|
||||
angle = toDegrees(asin(0.5)),
|
||||
length = 20
|
||||
}, %)
|
||||
|> yLineTo(0, %)
|
||||
|> yLine(endAbsolute = 0)
|
||||
|> close()
|
||||
|
||||
extrude001 = extrude(sketch001, length = 5)
|
||||
|
@ -37,7 +37,7 @@ sketch001 = startSketchOn('XZ')
|
||||
angle = toDegrees(atan(1.25)),
|
||||
length = 20
|
||||
}, %)
|
||||
|> yLineTo(0, %)
|
||||
|> yLine(endAbsolute = 0)
|
||||
|> close()
|
||||
|
||||
extrude001 = extrude(sketch001, length = 5)
|
||||
|
@ -41,7 +41,7 @@ sketch001 = startSketchOn('XZ')
|
||||
angle = toDegrees(atan2(1.25, 2)),
|
||||
length = 20
|
||||
}, %)
|
||||
|> yLineTo(0, %)
|
||||
|> yLine(endAbsolute = 0)
|
||||
|> close()
|
||||
|
||||
extrude001 = extrude(sketch001, length = 5)
|
||||
|
@ -35,7 +35,7 @@ sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line(endAbsolute = [12, 10])
|
||||
|> line(end = [ceil(7.02986), 0])
|
||||
|> yLineTo(0, %)
|
||||
|> yLine(endAbsolute = 0)
|
||||
|> close()
|
||||
|
||||
extrude001 = extrude(sketch001, length = 5)
|
||||
|
File diff suppressed because one or more lines are too long
25
docs/kcl/consts.md
Normal file
25
docs/kcl/consts.md
Normal file
@ -0,0 +1,25 @@
|
||||
---
|
||||
title: "KCL Constants"
|
||||
excerpt: "Documentation for the KCL constants."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
|
||||
### `std`
|
||||
|
||||
- [`HALF_TURN`](/docs/kcl/consts/std-HALF_TURN)
|
||||
- [`QUARTER_TURN`](/docs/kcl/consts/std-QUARTER_TURN)
|
||||
- [`THREE_QUARTER_TURN`](/docs/kcl/consts/std-THREE_QUARTER_TURN)
|
||||
- [`XY`](/docs/kcl/consts/std-XY)
|
||||
- [`XZ`](/docs/kcl/consts/std-XZ)
|
||||
- [`YZ`](/docs/kcl/consts/std-YZ)
|
||||
- [`ZERO`](/docs/kcl/consts/std-ZERO)
|
||||
|
||||
### `std::math`
|
||||
|
||||
- [`E`](/docs/kcl/consts/std-math-E)
|
||||
- [`PI`](/docs/kcl/consts/std-math-PI)
|
||||
- [`TAU`](/docs/kcl/consts/std-math-TAU)
|
||||
|
@ -21,7 +21,7 @@ exampleSketch = startSketchOn("XZ")
|
||||
angle = 30,
|
||||
length = 2 * E ^ 2,
|
||||
}, %)
|
||||
|> yLineTo(0, %)
|
||||
|> yLine(endAbsolute = 0)
|
||||
|> close()
|
||||
|
||||
example = extrude(exampleSketch, length = 10)
|
@ -21,7 +21,7 @@ exampleSketch = startSketchOn("XZ")
|
||||
angle = 50,
|
||||
length = 10 * TAU,
|
||||
}, %)
|
||||
|> yLineTo(0, %)
|
||||
|> yLine(endAbsolute = 0)
|
||||
|> close()
|
||||
|
||||
example = extrude(exampleSketch, length = 5)
|
@ -31,7 +31,7 @@ e(): number
|
||||
exampleSketch = startSketchOn("XZ")
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> angledLine({ angle = 30, length = 2 * e() ^ 2 }, %)
|
||||
|> yLineTo(0, %)
|
||||
|> yLine(endAbsolute = 0)
|
||||
|> close()
|
||||
|
||||
example = extrude(exampleSketch, length = 10)
|
||||
|
@ -6,13 +6,13 @@ layout: manual
|
||||
|
||||
Extend a 2-dimensional sketch through a third dimension in order to create new 3-dimensional volume, or if extruded into an existing volume, cut into an existing solid.
|
||||
|
||||
|
||||
You can provide more than one sketch to extrude, and they will all be extruded in the same direction.
|
||||
|
||||
```js
|
||||
extrude(
|
||||
sketchSet: SketchSet,
|
||||
sketches: [Sketch],
|
||||
length: number,
|
||||
): SolidSet
|
||||
): [Solid]
|
||||
```
|
||||
|
||||
|
||||
@ -20,12 +20,12 @@ extrude(
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `sketchSet` | [`SketchSet`](/docs/kcl/types/SketchSet) | Which sketches should be extruded | Yes |
|
||||
| `sketches` | [`[Sketch]`](/docs/kcl/types/Sketch) | Which sketch or sketches should be extruded | Yes |
|
||||
| `length` | [`number`](/docs/kcl/types/number) | How far to extrude the given sketches | Yes |
|
||||
|
||||
### Returns
|
||||
|
||||
[`SolidSet`](/docs/kcl/types/SolidSet) - A solid or a group of solids.
|
||||
[`[Solid]`](/docs/kcl/types/Solid)
|
||||
|
||||
|
||||
### Examples
|
||||
|
@ -35,7 +35,7 @@ sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line(endAbsolute = [12, 10])
|
||||
|> line(end = [floor(7.02986), 0])
|
||||
|> yLineTo(0, %)
|
||||
|> yLine(endAbsolute = 0)
|
||||
|> close()
|
||||
|
||||
extrude001 = extrude(sketch001, length = 5)
|
||||
|
File diff suppressed because one or more lines are too long
@ -10,7 +10,7 @@ Use a 2-dimensional sketch to cut a hole in another 2-dimensional sketch.
|
||||
|
||||
```js
|
||||
hole(
|
||||
holeSketch: SketchSet,
|
||||
holeSketch: [Sketch],
|
||||
sketch: Sketch,
|
||||
): Sketch
|
||||
```
|
||||
@ -20,7 +20,7 @@ hole(
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `holeSketch` | [`SketchSet`](/docs/kcl/types/SketchSet) | A sketch or a group of sketches. | Yes |
|
||||
| `holeSketch` | [`[Sketch]`](/docs/kcl/types/Sketch) | | Yes |
|
||||
| `sketch` | [`Sketch`](/docs/kcl/types/Sketch) | | Yes |
|
||||
|
||||
### Returns
|
||||
|
@ -12,25 +12,26 @@ layout: manual
|
||||
* [`Modules`](kcl/modules)
|
||||
* [`Settings`](kcl/settings)
|
||||
* [`Known Issues`](kcl/known-issues)
|
||||
* [`Constants`](kcl/consts)
|
||||
|
||||
### Standard library
|
||||
|
||||
* **Primitive types**
|
||||
* [`bool`](kcl/bool)
|
||||
* [`number`](kcl/number)
|
||||
* [`string`](kcl/string)
|
||||
* [`tag`](kcl/tag)
|
||||
* [`bool`](kcl/types/bool)
|
||||
* [`number`](kcl/types/number)
|
||||
* [`string`](kcl/types/string)
|
||||
* [`tag`](kcl/types/tag)
|
||||
* **std**
|
||||
* [`HALF_TURN`](kcl/const_std-HALF_TURN)
|
||||
* [`Plane`](kcl/Plane)
|
||||
* [`QUARTER_TURN`](kcl/const_std-QUARTER_TURN)
|
||||
* [`Sketch`](kcl/Sketch)
|
||||
* [`Solid`](kcl/Solid)
|
||||
* [`THREE_QUARTER_TURN`](kcl/const_std-THREE_QUARTER_TURN)
|
||||
* [`XY`](kcl/const_std-XY)
|
||||
* [`XZ`](kcl/const_std-XZ)
|
||||
* [`YZ`](kcl/const_std-YZ)
|
||||
* [`ZERO`](kcl/const_std-ZERO)
|
||||
* [`HALF_TURN`](kcl/consts/std-HALF_TURN)
|
||||
* [`Plane`](kcl/types/Plane)
|
||||
* [`QUARTER_TURN`](kcl/consts/std-QUARTER_TURN)
|
||||
* [`Sketch`](kcl/types/Sketch)
|
||||
* [`Solid`](kcl/types/Solid)
|
||||
* [`THREE_QUARTER_TURN`](kcl/consts/std-THREE_QUARTER_TURN)
|
||||
* [`XY`](kcl/consts/std-XY)
|
||||
* [`XZ`](kcl/consts/std-XZ)
|
||||
* [`YZ`](kcl/consts/std-YZ)
|
||||
* [`ZERO`](kcl/consts/std-ZERO)
|
||||
* [`abs`](kcl/abs)
|
||||
* [`acos`](kcl/acos)
|
||||
* [`angleToMatchLengthX`](kcl/angleToMatchLengthX)
|
||||
@ -131,14 +132,12 @@ layout: manual
|
||||
* [`toRadians`](kcl/toRadians)
|
||||
* [`translate`](kcl/translate)
|
||||
* [`xLine`](kcl/xLine)
|
||||
* [`xLineTo`](kcl/xLineTo)
|
||||
* [`yLine`](kcl/yLine)
|
||||
* [`yLineTo`](kcl/yLineTo)
|
||||
* [`yd`](kcl/yd)
|
||||
* **std::math**
|
||||
* [`E`](kcl/const_std-math-E)
|
||||
* [`PI`](kcl/const_std-math-PI)
|
||||
* [`TAU`](kcl/const_std-math-TAU)
|
||||
* [`E`](kcl/consts/std-math-E)
|
||||
* [`PI`](kcl/consts/std-math-PI)
|
||||
* [`TAU`](kcl/consts/std-math-TAU)
|
||||
* [`cos`](kcl/std-math-cos)
|
||||
* [`sin`](kcl/std-math-sin)
|
||||
* [`tan`](kcl/std-math-tan)
|
||||
|
@ -13,9 +13,7 @@ once fixed in engine will just start working here with no language changes.
|
||||
If you see a red line around your model, it means this is happening.
|
||||
|
||||
- **Import**: Right now you can import a file, even if that file has brep data
|
||||
you cannot edit it, after v1, the engine will account for this. You also cannot
|
||||
currently move or transform the imported objects at all, once we have assemblies
|
||||
this will work.
|
||||
you cannot edit it, after v1, the engine will account for this.
|
||||
|
||||
- **Fillets**: Fillets cannot intersect, you will get an error. Only simple fillet
|
||||
cases work currently.
|
||||
|
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
@ -10,7 +10,7 @@ Repeat a 2-dimensional sketch some number of times along a partial or complete c
|
||||
|
||||
```js
|
||||
patternCircular2d(
|
||||
sketchSet: SketchSet,
|
||||
sketchSet: [Sketch],
|
||||
instances: integer,
|
||||
center: [number],
|
||||
arcDegrees: number,
|
||||
@ -24,7 +24,7 @@ patternCircular2d(
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `sketchSet` | [`SketchSet`](/docs/kcl/types/SketchSet) | Which sketch(es) to pattern | Yes |
|
||||
| `sketchSet` | [`[Sketch]`](/docs/kcl/types/Sketch) | Which sketch(es) to pattern | Yes |
|
||||
| `instances` | `integer` | The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect. | Yes |
|
||||
| `center` | [`[number]`](/docs/kcl/types/number) | The center about which to make the pattern. This is a 2D vector. | Yes |
|
||||
| `arcDegrees` | [`number`](/docs/kcl/types/number) | The arc angle (in degrees) to place the repetitions. Must be greater than 0. | Yes |
|
||||
|
@ -10,7 +10,7 @@ Repeat a 3-dimensional solid some number of times along a partial or complete ci
|
||||
|
||||
```js
|
||||
patternCircular3d(
|
||||
solidSet: SolidSet,
|
||||
solids: [Solid],
|
||||
instances: integer,
|
||||
axis: [number],
|
||||
center: [number],
|
||||
@ -25,7 +25,7 @@ patternCircular3d(
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `solidSet` | [`SolidSet`](/docs/kcl/types/SolidSet) | Which solid(s) to pattern | Yes |
|
||||
| `solids` | [`[Solid]`](/docs/kcl/types/Solid) | Which solid(s) to pattern | Yes |
|
||||
| `instances` | `integer` | The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect. | Yes |
|
||||
| `axis` | [`[number]`](/docs/kcl/types/number) | The axis around which to make the pattern. This is a 3D vector | Yes |
|
||||
| `center` | [`[number]`](/docs/kcl/types/number) | The center about which to make the pattern. This is a 3D vector. | Yes |
|
||||
|
@ -10,7 +10,7 @@ Repeat a 2-dimensional sketch along some dimension, with a dynamic amount of dis
|
||||
|
||||
```js
|
||||
patternLinear2d(
|
||||
sketchSet: SketchSet,
|
||||
sketches: [Sketch],
|
||||
instances: integer,
|
||||
distance: number,
|
||||
axis: [number],
|
||||
@ -23,7 +23,7 @@ patternLinear2d(
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `sketchSet` | [`SketchSet`](/docs/kcl/types/SketchSet) | The sketch(es) to duplicate | Yes |
|
||||
| `sketches` | [`[Sketch]`](/docs/kcl/types/Sketch) | The sketch(es) to duplicate | Yes |
|
||||
| `instances` | `integer` | The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect. | Yes |
|
||||
| `distance` | [`number`](/docs/kcl/types/number) | Distance between each repetition. Also known as 'spacing'. | Yes |
|
||||
| `axis` | [`[number]`](/docs/kcl/types/number) | The axis of the pattern. A 2D vector. | Yes |
|
||||
|
@ -10,7 +10,7 @@ Repeat a 3-dimensional solid along a linear path, with a dynamic amount of dista
|
||||
|
||||
```js
|
||||
patternLinear3d(
|
||||
solidSet: SolidSet,
|
||||
solids: [Solid],
|
||||
instances: integer,
|
||||
distance: number,
|
||||
axis: [number],
|
||||
@ -23,7 +23,7 @@ patternLinear3d(
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `solidSet` | [`SolidSet`](/docs/kcl/types/SolidSet) | The solid(s) to duplicate | Yes |
|
||||
| `solids` | [`[Solid]`](/docs/kcl/types/Solid) | The solid(s) to duplicate | Yes |
|
||||
| `instances` | `integer` | The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect. | Yes |
|
||||
| `distance` | [`number`](/docs/kcl/types/number) | Distance between each repetition. Also known as 'spacing'. | Yes |
|
||||
| `axis` | [`[number]`](/docs/kcl/types/number) | The axis of the pattern. A 2D vector. | Yes |
|
||||
|
@ -36,7 +36,7 @@ The transform function returns a transform object. All properties of the object
|
||||
|
||||
```js
|
||||
patternTransform(
|
||||
solidSet: SolidSet,
|
||||
solids: [Solid],
|
||||
instances: integer,
|
||||
transform: FunctionSource,
|
||||
useOriginal?: bool,
|
||||
@ -48,7 +48,7 @@ patternTransform(
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `solidSet` | [`SolidSet`](/docs/kcl/types/SolidSet) | The solid(s) to duplicate | Yes |
|
||||
| `solids` | [`[Solid]`](/docs/kcl/types/Solid) | The solid(s) to duplicate | Yes |
|
||||
| `instances` | `integer` | The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect. | Yes |
|
||||
| `transform` | `FunctionSource` | How each replica should be transformed. The transform function takes a single parameter: an integer representing which number replication the transform is for. E.g. the first replica to be transformed will be passed the argument `1`. This simplifies your math: the transform function can rely on id `0` being the original instance passed into the `patternTransform`. See the examples. | Yes |
|
||||
| `useOriginal` | [`bool`](/docs/kcl/types/bool) | If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid. Defaults to false. | No |
|
||||
|
File diff suppressed because one or more lines are too long
@ -38,7 +38,7 @@ pow(
|
||||
exampleSketch = startSketchOn("XZ")
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> angledLine({ angle = 50, length = pow(5, 2) }, %)
|
||||
|> yLineTo(0, %)
|
||||
|> yLine(endAbsolute = 0)
|
||||
|> close()
|
||||
|
||||
example = extrude(exampleSketch, length = 5)
|
||||
|
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
@ -35,7 +35,7 @@ sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line(endAbsolute = [12, 10])
|
||||
|> line(end = [round(7.02986), 0])
|
||||
|> yLineTo(0, %)
|
||||
|> yLine(endAbsolute = 0)
|
||||
|> close()
|
||||
|
||||
extrude001 = extrude(sketch001, length = 5)
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,16 +1,31 @@
|
||||
---
|
||||
title: "KCL settings"
|
||||
title: "KCL Settings"
|
||||
excerpt: "Documentation of settings for the KCL language and Zoo Modeling App."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
# Per-file settings
|
||||
# KCL Settings
|
||||
|
||||
There are three levels of settings available in the KittyCAD modeling application:
|
||||
|
||||
1. [User Settings](/docs/kcl/settings/user): Global settings that apply to all projects, stored in `user.toml`
|
||||
2. [Project Settings](/docs/kcl/settings/project): Settings specific to a project, stored in `project.toml`
|
||||
3. Per-file Settings: Settings that apply to a single KCL file, specified using the `@settings` attribute
|
||||
|
||||
## Configuration Files
|
||||
|
||||
The KittyCAD modeling app uses TOML files for configuration:
|
||||
|
||||
* **User Settings**: `user.toml` - See [complete documentation](/docs/kcl/settings/user)
|
||||
* **Project Settings**: `project.toml` - See [complete documentation](/docs/kcl/settings/project)
|
||||
|
||||
## Per-file settings
|
||||
|
||||
Settings which affect a single file are configured using the settings attribute.
|
||||
This must be at the top of the KCL file (comments before the attribute are permitted).
|
||||
E.g.,
|
||||
For example:
|
||||
|
||||
```
|
||||
```js
|
||||
// The settings attribute.
|
||||
@settings(defaultLengthUnit = in)
|
||||
|
||||
|
208
docs/kcl/settings/project.md
Normal file
208
docs/kcl/settings/project.md
Normal file
@ -0,0 +1,208 @@
|
||||
---
|
||||
title: "Project Settings"
|
||||
excerpt: "Project specific settings for the app. These live in `project.toml` in the base of the project directory. Updating the settings for the project in the app will update this file automatically. Do not edit this file manually, as it may be overwritten by the app. Manual edits can cause corruption of the settings file."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
# Project Settings
|
||||
|
||||
Project specific settings for the app. These live in `project.toml` in the base of the project directory. Updating the settings for the project in the app will update this file automatically. Do not edit this file manually, as it may be overwritten by the app. Manual edits can cause corruption of the settings file.
|
||||
|
||||
## Project Configuration Structure
|
||||
|
||||
```toml
|
||||
[settings.app]
|
||||
# Set the appearance of the application
|
||||
name = "My Awesome Project"
|
||||
|
||||
[settings.app.appearance]
|
||||
# Use dark mode theme
|
||||
theme = "dark"
|
||||
# Set the app color to blue (240.0 = blue, 0.0 = red, 120.0 = green)
|
||||
color = 240.0
|
||||
|
||||
[settings.modeling]
|
||||
# Use inches as the default measurement unit
|
||||
base_unit = "in"
|
||||
|
||||
```
|
||||
|
||||
## Available Settings
|
||||
|
||||
### settings
|
||||
|
||||
|
||||
|
||||
#### app
|
||||
|
||||
The settings for the modeling app.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
This setting has the following nested options:
|
||||
|
||||
##### appearance
|
||||
|
||||
The settings for the appearance of the app.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
This setting has further nested options. See the schema for full details.
|
||||
##### onboarding_status
|
||||
|
||||
The onboarding status of the app.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
##### theme_color
|
||||
|
||||
The hue of the primary theme color for the app.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
##### enable_ssao
|
||||
|
||||
Whether or not Screen Space Ambient Occlusion (SSAO) is enabled.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
##### dismiss_web_banner
|
||||
|
||||
Permanently dismiss the banner warning to download the desktop app. This setting only applies to the web app. And is temporary until we have Linux support.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
##### stream_idle_mode
|
||||
|
||||
When the user is idle, and this is true, the stream will be torn down.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
##### allow_orbit_in_sketch_mode
|
||||
|
||||
When the user is idle, and this is true, the stream will be torn down.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
##### show_debug_panel
|
||||
|
||||
Whether to show the debug panel, which lets you see various states of the app to aid in development.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
##### named_views
|
||||
|
||||
Settings that affect the behavior of the command bar.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
|
||||
#### modeling
|
||||
|
||||
Settings that affect the behavior while modeling.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
This setting has the following nested options:
|
||||
|
||||
##### base_unit
|
||||
|
||||
The default unit to use in modeling dimensions.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
##### highlight_edges
|
||||
|
||||
Highlight edges of 3D objects?
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
##### show_debug_panel
|
||||
|
||||
Whether to show the debug panel, which lets you see various states of the app to aid in development. Remove this when we remove backwards compatibility with the old settings file.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
##### enable_ssao
|
||||
|
||||
Whether or not Screen Space Ambient Occlusion (SSAO) is enabled.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
|
||||
#### text_editor
|
||||
|
||||
Settings that affect the behavior of the KCL text editor.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
This setting has the following nested options:
|
||||
|
||||
##### text_wrapping
|
||||
|
||||
Whether to wrap text in the editor or overflow with scroll.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
##### blinking_cursor
|
||||
|
||||
Whether to make the cursor blink in the editor.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
|
||||
#### command_bar
|
||||
|
||||
Settings that affect the behavior of the command bar.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
This setting has the following nested options:
|
||||
|
||||
##### include_settings
|
||||
|
||||
Whether to include settings in the command bar.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
|
||||
|
||||
|
||||
## Complete Example
|
||||
|
||||
```toml
|
||||
[settings.app]
|
||||
# Set the appearance of the application
|
||||
name = "My Awesome Project"
|
||||
|
||||
[settings.app.appearance]
|
||||
# Use dark mode theme
|
||||
theme = "dark"
|
||||
# Set the app color to blue (240.0 = blue, 0.0 = red, 120.0 = green)
|
||||
color = 240.0
|
||||
|
||||
[settings.modeling]
|
||||
# Use inches as the default measurement unit
|
||||
base_unit = "in"
|
||||
|
||||
```
|
272
docs/kcl/settings/user.md
Normal file
272
docs/kcl/settings/user.md
Normal file
@ -0,0 +1,272 @@
|
||||
---
|
||||
title: "User Settings"
|
||||
excerpt: "User specific settings for the app. These live in `user.toml` in the app's configuration directory. Updating the settings in the app will update this file automatically. Do not edit this file manually, as it may be overwritten by the app. Manual edits can cause corruption of the settings file."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
# User Settings
|
||||
|
||||
User specific settings for the app. These live in `user.toml` in the app's configuration directory. Updating the settings in the app will update this file automatically. Do not edit this file manually, as it may be overwritten by the app. Manual edits can cause corruption of the settings file.
|
||||
|
||||
## User Configuration Structure
|
||||
|
||||
```toml
|
||||
[settings.app]
|
||||
# Set the appearance of the application
|
||||
[settings.app.appearance]
|
||||
# Use dark mode theme
|
||||
theme = "dark"
|
||||
# Set the app color to blue (240.0 = blue, 0.0 = red, 120.0 = green)
|
||||
color = 240.0
|
||||
|
||||
[settings.modeling]
|
||||
# Use millimeters as the default measurement unit
|
||||
base_unit = "mm"
|
||||
|
||||
[settings.text_editor]
|
||||
# Disable text wrapping in the editor
|
||||
text_wrapping = false
|
||||
|
||||
```
|
||||
|
||||
## Available Settings
|
||||
|
||||
### settings
|
||||
|
||||
|
||||
|
||||
#### app
|
||||
|
||||
The settings for the modeling app.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
This setting has the following nested options:
|
||||
|
||||
##### appearance
|
||||
|
||||
The settings for the appearance of the app.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
This setting has further nested options. See the schema for full details.
|
||||
##### onboarding_status
|
||||
|
||||
The onboarding status of the app.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
##### project_directory
|
||||
|
||||
Backwards compatible project directory setting.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
##### theme
|
||||
|
||||
Backwards compatible theme setting.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
##### theme_color
|
||||
|
||||
The hue of the primary theme color for the app.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
##### enable_ssao
|
||||
|
||||
Whether or not Screen Space Ambient Occlusion (SSAO) is enabled.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
##### dismiss_web_banner
|
||||
|
||||
Permanently dismiss the banner warning to download the desktop app. This setting only applies to the web app. And is temporary until we have Linux support.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
##### stream_idle_mode
|
||||
|
||||
When the user is idle, and this is true, the stream will be torn down.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
##### allow_orbit_in_sketch_mode
|
||||
|
||||
When the user is idle, and this is true, the stream will be torn down.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
##### show_debug_panel
|
||||
|
||||
Whether to show the debug panel, which lets you see various states of the app to aid in development.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
|
||||
#### modeling
|
||||
|
||||
Settings that affect the behavior while modeling.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
This setting has the following nested options:
|
||||
|
||||
##### base_unit
|
||||
|
||||
The default unit to use in modeling dimensions.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
##### camera_projection
|
||||
|
||||
The projection mode the camera should use while modeling.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
##### camera_orbit
|
||||
|
||||
The methodology the camera should use to orbit around the model.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
##### mouse_controls
|
||||
|
||||
The controls for how to navigate the 3D view.
|
||||
|
||||
**Possible values:** `zoo`, `onshape`, `trackpad_friendly`, `solidworks`, `nx`, `creo`, `autocad`
|
||||
|
||||
**Default:** None
|
||||
|
||||
##### highlight_edges
|
||||
|
||||
Highlight edges of 3D objects?
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
##### show_debug_panel
|
||||
|
||||
Whether to show the debug panel, which lets you see various states of the app to aid in development. Remove this when we remove backwards compatibility with the old settings file.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
##### enable_ssao
|
||||
|
||||
Whether or not Screen Space Ambient Occlusion (SSAO) is enabled.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
##### show_scale_grid
|
||||
|
||||
Whether or not to show a scale grid in the 3D modeling view
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
|
||||
#### text_editor
|
||||
|
||||
Settings that affect the behavior of the KCL text editor.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
This setting has the following nested options:
|
||||
|
||||
##### text_wrapping
|
||||
|
||||
Whether to wrap text in the editor or overflow with scroll.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
##### blinking_cursor
|
||||
|
||||
Whether to make the cursor blink in the editor.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
|
||||
#### project
|
||||
|
||||
Settings that affect the behavior of project management.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
This setting has the following nested options:
|
||||
|
||||
##### directory
|
||||
|
||||
The directory to save and load projects from.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
##### default_project_name
|
||||
|
||||
The default project name to use when creating a new project.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
|
||||
#### command_bar
|
||||
|
||||
Settings that affect the behavior of the command bar.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
This setting has the following nested options:
|
||||
|
||||
##### include_settings
|
||||
|
||||
Whether to include settings in the command bar.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
|
||||
|
||||
|
||||
## Complete Example
|
||||
|
||||
```toml
|
||||
[settings.app]
|
||||
# Set the appearance of the application
|
||||
[settings.app.appearance]
|
||||
# Use dark mode theme
|
||||
theme = "dark"
|
||||
# Set the app color to blue (240.0 = blue, 0.0 = red, 120.0 = green)
|
||||
color = 240.0
|
||||
|
||||
[settings.modeling]
|
||||
# Use millimeters as the default measurement unit
|
||||
base_unit = "mm"
|
||||
|
||||
[settings.text_editor]
|
||||
# Disable text wrapping in the editor
|
||||
text_wrapping = false
|
||||
|
||||
```
|
@ -10,10 +10,10 @@ Remove volume from a 3-dimensional shape such that a wall of the provided thickn
|
||||
|
||||
```js
|
||||
shell(
|
||||
solidSet: SolidSet,
|
||||
solids: [Solid],
|
||||
thickness: number,
|
||||
faces: [FaceTag],
|
||||
): SolidSet
|
||||
): [Solid]
|
||||
```
|
||||
|
||||
|
||||
@ -21,13 +21,13 @@ shell(
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `solidSet` | [`SolidSet`](/docs/kcl/types/SolidSet) | Which solid (or solids) to shell out | Yes |
|
||||
| `solids` | [`[Solid]`](/docs/kcl/types/Solid) | Which solid (or solids) to shell out | Yes |
|
||||
| `thickness` | [`number`](/docs/kcl/types/number) | The thickness of the shell | Yes |
|
||||
| `faces` | [`[FaceTag]`](/docs/kcl/types/FaceTag) | The faces you want removed | Yes |
|
||||
|
||||
### Returns
|
||||
|
||||
[`SolidSet`](/docs/kcl/types/SolidSet) - A solid or a group of solids.
|
||||
[`[Solid]`](/docs/kcl/types/Solid)
|
||||
|
||||
|
||||
### Examples
|
||||
|
@ -34,7 +34,7 @@ sqrt(num: number): number
|
||||
exampleSketch = startSketchOn("XZ")
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> angledLine({ angle = 50, length = sqrt(2500) }, %)
|
||||
|> yLineTo(0, %)
|
||||
|> yLine(endAbsolute = 0)
|
||||
|> close()
|
||||
|
||||
example = extrude(exampleSketch, length = 5)
|
||||
|
@ -137,9 +137,9 @@ a1 = startSketchOn({
|
||||
})
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line(end = [100.0, 0])
|
||||
|> yLine(-100.0, %)
|
||||
|> xLine(-100.0, %)
|
||||
|> yLine(100.0, %)
|
||||
|> yLine(length = -100.0)
|
||||
|> xLine(length = -100.0)
|
||||
|> yLine(length = 100.0)
|
||||
|> close()
|
||||
|> extrude(length = 3.14)
|
||||
```
|
||||
|
@ -33,7 +33,7 @@ exampleSketch = startSketchOn("XZ")
|
||||
angle = 30,
|
||||
length = 3 / cos(toRadians(30)),
|
||||
}, %)
|
||||
|> yLineTo(0, %)
|
||||
|> yLine(endAbsolute = 0)
|
||||
|> close()
|
||||
|
||||
example = extrude(exampleSketch, length = 5)
|
||||
|
@ -33,7 +33,7 @@ exampleSketch = startSketchOn("XZ")
|
||||
angle = 50,
|
||||
length = 15 / sin(toDegrees(135)),
|
||||
}, %)
|
||||
|> yLineTo(0, %)
|
||||
|> yLine(endAbsolute = 0)
|
||||
|> close()
|
||||
|
||||
example = extrude(exampleSketch, length = 5)
|
||||
|
@ -33,7 +33,7 @@ exampleSketch = startSketchOn("XZ")
|
||||
angle = 50,
|
||||
length = 50 * tan(1/2),
|
||||
}, %)
|
||||
|> yLineTo(0, %)
|
||||
|> yLine(endAbsolute = 0)
|
||||
|> close()
|
||||
|
||||
example = extrude(exampleSketch, length = 5)
|
||||
|
152799
docs/kcl/std.json
152799
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
@ -31,7 +31,7 @@ tau(): number
|
||||
exampleSketch = startSketchOn("XZ")
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> angledLine({ angle = 50, length = 10 * tau() }, %)
|
||||
|> yLineTo(0, %)
|
||||
|> yLine(endAbsolute = 0)
|
||||
|> close()
|
||||
|
||||
example = extrude(exampleSketch, length = 5)
|
||||
|
@ -37,7 +37,7 @@ exampleSketch = startSketchOn("XZ")
|
||||
angle = 50,
|
||||
length = 70 * cos(toDegrees(pi() / 4))
|
||||
}, %)
|
||||
|> yLineTo(0, %)
|
||||
|> yLine(endAbsolute = 0)
|
||||
|> close()
|
||||
|
||||
example = extrude(exampleSketch, length = 5)
|
||||
|
@ -37,7 +37,7 @@ exampleSketch = startSketchOn("XZ")
|
||||
angle = 50,
|
||||
length = 70 * cos(toRadians(45))
|
||||
}, %)
|
||||
|> yLineTo(0, %)
|
||||
|> yLine(endAbsolute = 0)
|
||||
|> close()
|
||||
|
||||
example = extrude(exampleSketch, length = 5)
|
||||
|
File diff suppressed because one or more lines are too long
@ -24,6 +24,5 @@ A face.
|
||||
| `zAxis` |[`Point3d`](/docs/kcl/types/Point3d)| The z-axis (normal). | No |
|
||||
| `solid` |[`Solid`](/docs/kcl/types/Solid)| The solid the face is on. | No |
|
||||
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A unit of length. | No |
|
||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||
|
||||
|
||||
|
@ -22,6 +22,5 @@ A helix.
|
||||
| `angleStart` |[`number`](/docs/kcl/types/number)| Start angle (in degrees). | No |
|
||||
| `ccw` |`boolean`| Is the helix rotation counter clockwise? | No |
|
||||
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A unit of length. | No |
|
||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||
|
||||
|
||||
|
@ -22,6 +22,5 @@ A helix.
|
||||
| `angleStart` |[`number`](/docs/kcl/types/number)| Start angle (in degrees). | No |
|
||||
| `ccw` |`boolean`| Is the helix rotation counter clockwise? | No |
|
||||
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A unit of length. | No |
|
||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||
|
||||
|
||||
|
@ -18,6 +18,5 @@ Data for an imported geometry.
|
||||
|----------|------|-------------|----------|
|
||||
| `id` |[`string`](/docs/kcl/types/string)| The ID of the imported geometry. | No |
|
||||
| `value` |`[` [`string`](/docs/kcl/types/string) `]`| The original file paths. | No |
|
||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||
|
||||
|
||||
|
@ -25,7 +25,6 @@ Any KCL value.
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `Uuid`| | No |
|
||||
| `value` |[`string`](/docs/kcl/types/string)| | No |
|
||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||
|
||||
|
||||
----
|
||||
@ -42,7 +41,6 @@ Any KCL value.
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `Bool`| | No |
|
||||
| `value` |`boolean`| | No |
|
||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||
|
||||
|
||||
----
|
||||
@ -60,7 +58,6 @@ Any KCL value.
|
||||
| `type` |enum: `Number`| | No |
|
||||
| `value` |[`number`](/docs/kcl/types/number)| | No |
|
||||
| `ty` |[`NumericType`](/docs/kcl/types/NumericType)| | No |
|
||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||
|
||||
|
||||
----
|
||||
@ -77,7 +74,6 @@ Any KCL value.
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `String`| | No |
|
||||
| `value` |[`string`](/docs/kcl/types/string)| | No |
|
||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||
|
||||
|
||||
----
|
||||
@ -94,7 +90,22 @@ Any KCL value.
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `MixedArray`| | No |
|
||||
| `value` |`[` [`KclValue`](/docs/kcl/types/KclValue) `]`| | No |
|
||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `HomArray`| | No |
|
||||
| `value` |`[` [`KclValue`](/docs/kcl/types/KclValue) `]`| | No |
|
||||
|
||||
|
||||
----
|
||||
@ -111,7 +122,6 @@ Any KCL value.
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `Object`| | No |
|
||||
| `value` |`object`| | No |
|
||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||
|
||||
|
||||
----
|
||||
@ -128,8 +138,6 @@ Any KCL value.
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: [`TagIdentifier`](/docs/kcl/types#tag-identifier)| | No |
|
||||
| `value` |[`string`](/docs/kcl/types/string)| | No |
|
||||
| `info` |[`TagEngineInfo`](/docs/kcl/types/TagEngineInfo)| | No |
|
||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||
|
||||
|
||||
----
|
||||
@ -207,22 +215,6 @@ Any KCL value.
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `Sketches`| | No |
|
||||
| `value` |`[` [`Sketch`](/docs/kcl/types/Sketch) `]`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
@ -239,22 +231,6 @@ Any KCL value.
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `Solids`| | No |
|
||||
| `value` |`[` [`Solid`](/docs/kcl/types/Solid) `]`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
@ -279,7 +255,6 @@ Data for an imported geometry.
|
||||
| `type` |enum: [`ImportedGeometry`](/docs/kcl/types/ImportedGeometry)| | No |
|
||||
| `id` |[`string`](/docs/kcl/types/string)| The ID of the imported geometry. | No |
|
||||
| `value` |`[` [`string`](/docs/kcl/types/string) `]`| The original file paths. | No |
|
||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||
|
||||
|
||||
----
|
||||
@ -295,7 +270,6 @@ Data for an imported geometry.
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `Function`| | No |
|
||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||
|
||||
|
||||
----
|
||||
@ -312,7 +286,6 @@ Data for an imported geometry.
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `Module`| | No |
|
||||
| `value` |[`ModuleId`](/docs/kcl/types/ModuleId)| Identifier of a source file. Uses a u32 to keep the size small. | No |
|
||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||
|
||||
|
||||
----
|
||||
@ -328,7 +301,6 @@ Data for an imported geometry.
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `Type`| | No |
|
||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||
|
||||
|
||||
----
|
||||
@ -345,24 +317,6 @@ Data for an imported geometry.
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: [`KclNone`](/docs/kcl/types/KclNone)| | No |
|
||||
| `value` |[`KclNone`](/docs/kcl/types/KclNone)| KCL value for an optional parameter which was not given an argument. (remember, parameters are in the function declaration, arguments are in the function call/application). | No |
|
||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `Tombstone`| | No |
|
||||
| `value` |`null`| | No |
|
||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
@ -126,6 +126,30 @@ A base path.
|
||||
| `__geoMeta` |[`GeoMeta`](/docs/kcl/types/GeoMeta)| Metadata. | No |
|
||||
|
||||
|
||||
----
|
||||
A base path.
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `ArcThreePoint`| | No |
|
||||
| `p1` |`[number, number]`| Point 1 of the arc (base on the end of previous segment) | No |
|
||||
| `p2` |`[number, number]`| Point 2 of the arc (interior kwarg) | No |
|
||||
| `p3` |`[number, number]`| Point 3 of the arc (end kwarg) | No |
|
||||
| `from` |`[number, number]`| The from point. | No |
|
||||
| `to` |`[number, number]`| The to point. | No |
|
||||
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A unit of length. | No |
|
||||
| [`tag`](/docs/kcl/types/tag) |[`TagDeclarator`](/docs/kcl/types#tag-declaration)| The tag of the path. | No |
|
||||
| `__geoMeta` |[`GeoMeta`](/docs/kcl/types/GeoMeta)| Metadata. | No |
|
||||
|
||||
|
||||
----
|
||||
A path that is horizontal.
|
||||
|
||||
|
@ -1,57 +0,0 @@
|
||||
---
|
||||
title: "SketchSet"
|
||||
excerpt: "A sketch or a group of sketches."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
A sketch or a group of sketches.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
**This schema accepts exactly one of the following:**
|
||||
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `sketch`| | No |
|
||||
| `id` |[`string`](/docs/kcl/types/string)| The id of the sketch (this will change when the engine's reference to it changes). | No |
|
||||
| `paths` |`[` [`Path`](/docs/kcl/types/Path) `]`| The paths in the sketch. | No |
|
||||
| `on` |[`SketchSurface`](/docs/kcl/types/SketchSurface)| What the sketch is on (can be a plane or a face). | No |
|
||||
| `start` |[`BasePath`](/docs/kcl/types/BasePath)| The starting path. | No |
|
||||
| `tags` |`object`| Tag identifiers that have been declared in this sketch. | No |
|
||||
| `artifactId` |[`ArtifactId`](/docs/kcl/types/ArtifactId)| The original id of the sketch. This stays the same even if the sketch is is sketched on face etc. | No |
|
||||
| `originalId` |[`string`](/docs/kcl/types/string)| | No |
|
||||
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A unit of length. | No |
|
||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| Metadata. | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `[object, array]`
|
||||
|
||||
`[` [`Sketch`](/docs/kcl/types/Sketch) `]`
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `sketches`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
@ -32,7 +32,6 @@ A sketch type.
|
||||
| `yAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the plane's Y axis be? | No |
|
||||
| `zAxis` |[`Point3d`](/docs/kcl/types/Point3d)| The z-axis (normal). | No |
|
||||
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A unit of length. | No |
|
||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||
|
||||
|
||||
----
|
||||
@ -57,7 +56,6 @@ A face.
|
||||
| `zAxis` |[`Point3d`](/docs/kcl/types/Point3d)| The z-axis (normal). | No |
|
||||
| `solid` |[`Solid`](/docs/kcl/types/Solid)| The solid the face is on. | No |
|
||||
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A unit of length. | No |
|
||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
51
docs/kcl/types/SolidOrImportedGeometry.md
Normal file
51
docs/kcl/types/SolidOrImportedGeometry.md
Normal file
@ -0,0 +1,51 @@
|
||||
---
|
||||
title: "SolidOrImportedGeometry"
|
||||
excerpt: "Data for a solid or an imported geometry."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
Data for a solid or an imported geometry.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
**This schema accepts exactly one of the following:**
|
||||
|
||||
Data for an imported geometry.
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `importedGeometry`| | No |
|
||||
| `id` |[`string`](/docs/kcl/types/string)| The ID of the imported geometry. | No |
|
||||
| `value` |`[` [`string`](/docs/kcl/types/string) `]`| The original file paths. | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `[object, array]`
|
||||
|
||||
`[` [`Solid`](/docs/kcl/types/Solid) `]`
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `solidSet`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
@ -1,58 +0,0 @@
|
||||
---
|
||||
title: "SolidSet"
|
||||
excerpt: "A solid or a group of solids."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
A solid or a group of solids.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
**This schema accepts exactly one of the following:**
|
||||
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `solid`| | No |
|
||||
| `id` |[`string`](/docs/kcl/types/string)| The id of the solid. | No |
|
||||
| `artifactId` |[`ArtifactId`](/docs/kcl/types/ArtifactId)| The artifact ID of the solid. Unlike `id`, this doesn't change. | No |
|
||||
| `value` |`[` [`ExtrudeSurface`](/docs/kcl/types/ExtrudeSurface) `]`| The extrude surfaces. | No |
|
||||
| `sketch` |[`Sketch`](/docs/kcl/types/Sketch)| The sketch. | No |
|
||||
| `height` |[`number`](/docs/kcl/types/number)| The height of the solid. | No |
|
||||
| `startCapId` |[`string`](/docs/kcl/types/string)| The id of the extrusion start cap | No |
|
||||
| `endCapId` |[`string`](/docs/kcl/types/string)| The id of the extrusion end cap | No |
|
||||
| `edgeCuts` |`[` [`EdgeCut`](/docs/kcl/types/EdgeCut) `]`| Chamfers or fillets on this solid. | No |
|
||||
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A unit of length. | No |
|
||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| Metadata. | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `[object, array]`
|
||||
|
||||
`[` [`Solid`](/docs/kcl/types/Solid) `]`
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `solids`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
@ -10,8 +10,9 @@ Draw a line relative to the current origin to a specified distance away from the
|
||||
|
||||
```js
|
||||
xLine(
|
||||
length: number,
|
||||
sketch: Sketch,
|
||||
length?: number,
|
||||
endAbsolute?: number,
|
||||
tag?: TagDeclarator,
|
||||
): Sketch
|
||||
```
|
||||
@ -21,9 +22,10 @@ xLine(
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `length` | [`number`](/docs/kcl/types/number) | | Yes |
|
||||
| `sketch` | [`Sketch`](/docs/kcl/types/Sketch) | | Yes |
|
||||
| [`tag`](/docs/kcl/types/tag) | [`TagDeclarator`](/docs/kcl/types#tag-declaration) | | No |
|
||||
| `sketch` | [`Sketch`](/docs/kcl/types/Sketch) | Which sketch should this path be added to? | Yes |
|
||||
| `length` | [`number`](/docs/kcl/types/number) | How far away along the X axis should this line go? Incompatible with `endAbsolute`. | No |
|
||||
| `endAbsolute` | [`number`](/docs/kcl/types/number) | Which absolute X value should this line go to? Incompatible with `length`. | No |
|
||||
| [`tag`](/docs/kcl/types/tag) | [`TagDeclarator`](/docs/kcl/types#tag-declaration) | Create a new tag which refers to this line | No |
|
||||
|
||||
### Returns
|
||||
|
||||
@ -35,12 +37,12 @@ xLine(
|
||||
```js
|
||||
exampleSketch = startSketchOn(XZ)
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> xLine(15, %)
|
||||
|> xLine(length = 15)
|
||||
|> angledLine({ angle = 80, length = 15 }, %)
|
||||
|> line(end = [8, -10])
|
||||
|> xLine(10, %)
|
||||
|> xLine(length = 10)
|
||||
|> angledLine({ angle = 120, length = 30 }, %)
|
||||
|> xLine(-15, %)
|
||||
|> xLine(length = -15)
|
||||
|> close()
|
||||
|
||||
example = extrude(exampleSketch, length = 10)
|
||||
|
File diff suppressed because one or more lines are too long
@ -10,8 +10,9 @@ Draw a line relative to the current origin to a specified distance away from the
|
||||
|
||||
```js
|
||||
yLine(
|
||||
length: number,
|
||||
sketch: Sketch,
|
||||
length?: number,
|
||||
endAbsolute?: number,
|
||||
tag?: TagDeclarator,
|
||||
): Sketch
|
||||
```
|
||||
@ -21,9 +22,10 @@ yLine(
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `length` | [`number`](/docs/kcl/types/number) | | Yes |
|
||||
| `sketch` | [`Sketch`](/docs/kcl/types/Sketch) | | Yes |
|
||||
| [`tag`](/docs/kcl/types/tag) | [`TagDeclarator`](/docs/kcl/types#tag-declaration) | | No |
|
||||
| `sketch` | [`Sketch`](/docs/kcl/types/Sketch) | Which sketch should this path be added to? | Yes |
|
||||
| `length` | [`number`](/docs/kcl/types/number) | How far away along the Y axis should this line go? Incompatible with `endAbsolute`. | No |
|
||||
| `endAbsolute` | [`number`](/docs/kcl/types/number) | Which absolute Y value should this line go to? Incompatible with `length`. | No |
|
||||
| [`tag`](/docs/kcl/types/tag) | [`TagDeclarator`](/docs/kcl/types#tag-declaration) | Create a new tag which refers to this line | No |
|
||||
|
||||
### Returns
|
||||
|
||||
@ -35,10 +37,10 @@ yLine(
|
||||
```js
|
||||
exampleSketch = startSketchOn(XZ)
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> yLine(15, %)
|
||||
|> yLine(length = 15)
|
||||
|> angledLine({ angle = 30, length = 15 }, %)
|
||||
|> line(end = [8, -10])
|
||||
|> yLine(-5, %)
|
||||
|> yLine(length = -5)
|
||||
|> close()
|
||||
|
||||
example = extrude(exampleSketch, length = 10)
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,4 +1,5 @@
|
||||
import { test, expect, Page } from './zoo-test'
|
||||
import { Page } from '@playwright/test'
|
||||
import { test, expect } from './zoo-test'
|
||||
import {
|
||||
getUtils,
|
||||
TEST_COLORS,
|
||||
@ -65,7 +66,7 @@ async function doBasicSketch(
|
||||
if (openPanes.includes('code')) {
|
||||
await expect(u.codeLocator)
|
||||
.toHaveText(`sketch001 = startSketchOn('XZ')profile001 = startProfileAt(${commonPoints.startAt}, sketch001)
|
||||
|> xLine(${commonPoints.num1}, %)`)
|
||||
|> xLine(length = ${commonPoints.num1})`)
|
||||
}
|
||||
await page.waitForTimeout(500)
|
||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
|
||||
@ -74,8 +75,8 @@ async function doBasicSketch(
|
||||
.toHaveText(`sketch001 = startSketchOn('XZ')profile001 = startProfileAt(${
|
||||
commonPoints.startAt
|
||||
}, sketch001)
|
||||
|> xLine(${commonPoints.num1}, %)
|
||||
|> yLine(${commonPoints.num1 + 0.01}, %)`)
|
||||
|> xLine(length = ${commonPoints.num1})
|
||||
|> yLine(length = ${commonPoints.num1 + 0.01})`)
|
||||
} else {
|
||||
await page.waitForTimeout(500)
|
||||
}
|
||||
@ -86,9 +87,9 @@ async function doBasicSketch(
|
||||
.toHaveText(`sketch001 = startSketchOn('XZ')profile001 = startProfileAt(${
|
||||
commonPoints.startAt
|
||||
}, sketch001)
|
||||
|> xLine(${commonPoints.num1}, %)
|
||||
|> yLine(${commonPoints.num1 + 0.01}, %)
|
||||
|> xLine(${commonPoints.num2 * -1}, %)`)
|
||||
|> xLine(length = ${commonPoints.num1})
|
||||
|> yLine(length = ${commonPoints.num1 + 0.01})
|
||||
|> xLine(length = ${commonPoints.num2 * -1})`)
|
||||
}
|
||||
|
||||
// deselect line tool
|
||||
@ -146,9 +147,9 @@ async function doBasicSketch(
|
||||
.toHaveText(`sketch001 = startSketchOn('XZ')profile001 = startProfileAt(${
|
||||
commonPoints.startAt
|
||||
}, sketch001)
|
||||
|> xLine(${commonPoints.num1}, %, $seg01)
|
||||
|> yLine(${commonPoints.num1 + 0.01}, %)
|
||||
|> xLine(-segLen(seg01), %)`)
|
||||
|> xLine(length = ${commonPoints.num1}, tag = $seg01)
|
||||
|> yLine(length = ${commonPoints.num1 + 0.01})
|
||||
|> xLine(length = -segLen(seg01))`)
|
||||
}
|
||||
|
||||
test.describe('Basic sketch', { tag: ['@skipWin'] }, () => {
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { test, expect, Page } from './zoo-test'
|
||||
import { Page } from '@playwright/test'
|
||||
import { test, expect } from './zoo-test'
|
||||
import { HomePageFixture } from './fixtures/homePageFixture'
|
||||
import { getUtils } from './test-utils'
|
||||
import { EngineCommand } from 'lang/std/artifactGraph'
|
||||
|
@ -16,7 +16,7 @@ test.describe('Command bar tests', { tag: ['@skipWin'] }, () => {
|
||||
|> startProfileAt([-10, -10], %)
|
||||
|> line(end = [20, 0])
|
||||
|> line(end = [0, 20])
|
||||
|> xLine(-20, %)
|
||||
|> xLine(length = -20)
|
||||
|> close()
|
||||
`
|
||||
)
|
||||
|
@ -10,7 +10,11 @@ import fsp from 'fs/promises'
|
||||
test(
|
||||
'export works on the first try',
|
||||
{ tag: ['@electron', '@skipLocalEngine'] },
|
||||
async ({ page, context, scene }, testInfo) => {
|
||||
async ({ page, context, scene, tronApp }, testInfo) => {
|
||||
if (!tronApp) {
|
||||
fail()
|
||||
}
|
||||
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
const bracketDir = path.join(dir, 'bracket')
|
||||
await Promise.all([fsp.mkdir(bracketDir, { recursive: true })])
|
||||
@ -86,7 +90,7 @@ test(
|
||||
await expect(exportingToastMessage).not.toBeVisible()
|
||||
|
||||
const firstFileFullPath = path.resolve(
|
||||
getPlaywrightDownloadDir(page),
|
||||
getPlaywrightDownloadDir(tronApp.projectDirName),
|
||||
exportFileName
|
||||
)
|
||||
await test.step('Check the export size', async () => {
|
||||
@ -165,7 +169,7 @@ test(
|
||||
]))
|
||||
|
||||
const secondFileFullPath = path.resolve(
|
||||
getPlaywrightDownloadDir(page),
|
||||
getPlaywrightDownloadDir(tronApp.projectDirName),
|
||||
exportFileName
|
||||
)
|
||||
await test.step('Check the export size', async () => {
|
||||
@ -181,7 +185,7 @@ test(
|
||||
},
|
||||
{ timeout: 15_000 }
|
||||
)
|
||||
.toBeGreaterThan(100_000)
|
||||
.toBeGreaterThan(70_000)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -800,10 +800,10 @@ test.describe('Editor tests', { tag: ['@skipWin'] }, () => {
|
||||
await page.keyboard.press('ArrowDown')
|
||||
await page.keyboard.press('Enter')
|
||||
// finish line with comment
|
||||
await page.keyboard.type('5')
|
||||
await page.waitForTimeout(100)
|
||||
await page.keyboard.press('Tab')
|
||||
await page.waitForTimeout(100)
|
||||
await page.keyboard.type('5')
|
||||
await page.waitForTimeout(100)
|
||||
await page.keyboard.press('Tab')
|
||||
|
||||
await page.keyboard.type(' // ')
|
||||
@ -817,7 +817,7 @@ test.describe('Editor tests', { tag: ['@skipWin'] }, () => {
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([3.14, 12], %)
|
||||
|> xLine(5, %) // lin`)
|
||||
|> xLine(%, length = 5) // lin`)
|
||||
|
||||
// expect there to be no KCL errors
|
||||
await expect(page.locator('.cm-lint-marker-error')).toHaveCount(0)
|
||||
@ -873,10 +873,10 @@ test.describe('Editor tests', { tag: ['@skipWin'] }, () => {
|
||||
await page.keyboard.press('ArrowDown')
|
||||
await page.keyboard.press('Tab')
|
||||
// finish line with comment
|
||||
await page.keyboard.type('5')
|
||||
await page.waitForTimeout(100)
|
||||
await page.keyboard.press('Tab')
|
||||
await page.waitForTimeout(100)
|
||||
await page.keyboard.type('5')
|
||||
await page.waitForTimeout(100)
|
||||
await page.keyboard.press('Tab')
|
||||
|
||||
await page.keyboard.type(' // ')
|
||||
@ -890,7 +890,7 @@ test.describe('Editor tests', { tag: ['@skipWin'] }, () => {
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([3.14, 12], %)
|
||||
|> xLine(5, %) // lin`)
|
||||
|> xLine(%, length = 5) // lin`)
|
||||
})
|
||||
})
|
||||
test('Can undo a click and point extrude with ctrl+z', async ({
|
||||
|
@ -8,7 +8,7 @@ const FEATURE_TREE_EXAMPLE_CODE = `export fn timesFive(x) {
|
||||
export fn triangle() {
|
||||
return startSketchOn('XZ')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> xLine(10, %)
|
||||
|> xLine(length = 10)
|
||||
|> line(end = [-10, -5])
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
@ -28,7 +28,7 @@ plane001 = offsetPlane('XY', offset = 10)
|
||||
sketch002 = startSketchOn(plane001)
|
||||
|> startProfileAt([-20, 0], %)
|
||||
|> line(end = [5, -15])
|
||||
|> xLine(-10, %)
|
||||
|> xLine(length = -10)
|
||||
|> line(endAbsolute = [-40, 0])
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
@ -274,7 +274,6 @@ test.describe('Feature Tree pane', () => {
|
||||
currentArgKey: 'distance',
|
||||
currentArgValue: initialInput,
|
||||
headerArguments: {
|
||||
Selection: '1 face',
|
||||
Distance: initialInput,
|
||||
},
|
||||
highlightedHeaderArg: 'distance',
|
||||
@ -291,7 +290,6 @@ test.describe('Feature Tree pane', () => {
|
||||
await cmdBar.expectState({
|
||||
stage: 'review',
|
||||
headerArguments: {
|
||||
Selection: '1 face',
|
||||
// The calculated value is shown in the argument summary
|
||||
Distance: initialInput,
|
||||
},
|
||||
@ -402,7 +400,7 @@ test.describe('Feature Tree pane', () => {
|
||||
sketch001 = startSketchOn(plane001)
|
||||
profile001 = circle(sketch001, center = [0, 20], radius = 12)
|
||||
profile002 = startProfileAt([0, 7.25], sketch001)
|
||||
|> xLine(13.3, %)
|
||||
|> xLine(length = 13.3)
|
||||
profile003 = startProfileAt([0, -4.93], sketch001)
|
||||
|> line(endAbsolute = [-5.56, 0])`
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
|
@ -158,11 +158,14 @@ test.describe('when using the file tree to', () => {
|
||||
await createNewFile('lee')
|
||||
|
||||
await test.step('Postcondition: there are 5 new lee-*.kcl files', async () => {
|
||||
await expect(
|
||||
page
|
||||
.locator('[data-testid="file-pane-scroll-container"] button')
|
||||
.filter({ hasText: /lee[-]?[0-5]?/ })
|
||||
).toHaveCount(5)
|
||||
await expect
|
||||
.poll(() =>
|
||||
page
|
||||
.locator('[data-testid="file-pane-scroll-container"] button')
|
||||
.filter({ hasText: /lee[-]?[0-5]?/ })
|
||||
.count()
|
||||
)
|
||||
.toEqual(5)
|
||||
})
|
||||
}
|
||||
)
|
||||
|
@ -27,28 +27,19 @@ type CmdBarSerialised =
|
||||
|
||||
export class CmdBarFixture {
|
||||
public page: Page
|
||||
|
||||
get cmdBarOpenBtn() {
|
||||
return this.page.getByTestId('command-bar-open-button')
|
||||
}
|
||||
|
||||
get cmdBarElement() {
|
||||
return this.page.getByTestId('command-bar')
|
||||
}
|
||||
public cmdBarOpenBtn!: Locator
|
||||
public cmdBarElement!: Locator
|
||||
|
||||
constructor(page: Page) {
|
||||
this.page = page
|
||||
this.cmdBarOpenBtn = this.page.getByTestId('command-bar-open-button')
|
||||
this.cmdBarElement = this.page.getByTestId('command-bar')
|
||||
}
|
||||
|
||||
get currentArgumentInput() {
|
||||
return this.page.getByTestId('cmd-bar-arg-value')
|
||||
}
|
||||
|
||||
// Put all selectors here because this method is re-run on fixture creation.
|
||||
reConstruct = (page: Page) => {
|
||||
this.page = page
|
||||
}
|
||||
|
||||
private _serialiseCmdBar = async (): Promise<CmdBarSerialised> => {
|
||||
if (!(await this.page.getByTestId('command-bar-wrapper').isVisible())) {
|
||||
return { stage: 'commandBarClosed' }
|
||||
|
@ -24,11 +24,6 @@ export class EditorFixture {
|
||||
|
||||
constructor(page: Page) {
|
||||
this.page = page
|
||||
this.reConstruct(page)
|
||||
}
|
||||
reConstruct = (page: Page) => {
|
||||
this.page = page
|
||||
|
||||
this.codeContent = page.locator('.cm-content[data-language="kcl"]')
|
||||
this.diagnosticsTooltip = page.locator('.cm-tooltip-lint')
|
||||
this.diagnosticsGutterIcon = page.locator('.cm-lint-marker-error')
|
||||
@ -87,6 +82,30 @@ export class EditorFixture {
|
||||
toContain: this._expectEditorToContain(),
|
||||
not: { toContain: this._expectEditorToContain(true) },
|
||||
}
|
||||
snapshot = async (options?: { timeout?: number; name?: string }) => {
|
||||
const wasPaneOpen = await this.checkIfPaneIsOpen()
|
||||
if (!wasPaneOpen) {
|
||||
await this.openPane()
|
||||
}
|
||||
|
||||
try {
|
||||
// Use expect.poll to implement retry logic
|
||||
await expect
|
||||
.poll(
|
||||
async () => {
|
||||
const code = await this.codeContent.textContent()
|
||||
return code || ''
|
||||
},
|
||||
{ timeout: options?.timeout || 5000 }
|
||||
)
|
||||
.toMatchSnapshot(options?.name || 'editor-content')
|
||||
} finally {
|
||||
// Reset pane state if needed
|
||||
if (!wasPaneOpen) {
|
||||
await this.closePane()
|
||||
}
|
||||
}
|
||||
}
|
||||
private _serialiseDiagnostics = async (): Promise<Array<string>> => {
|
||||
const diagnostics = await this.diagnosticsGutterIcon.all()
|
||||
const diagnosticsContent: string[] = []
|
||||
|
@ -1,13 +1,31 @@
|
||||
/* eslint-disable react-hooks/rules-of-hooks */
|
||||
|
||||
import type {
|
||||
BrowserContext,
|
||||
ElectronApplication,
|
||||
Fixtures as PlaywrightFixtures,
|
||||
TestInfo,
|
||||
Page,
|
||||
} from '@playwright/test'
|
||||
|
||||
import { getUtils, setup, setupElectron } from '../test-utils'
|
||||
import {
|
||||
_electron as electron,
|
||||
PlaywrightTestArgs,
|
||||
PlaywrightWorkerArgs,
|
||||
} from '@playwright/test'
|
||||
|
||||
import * as TOML from '@iarna/toml'
|
||||
import {
|
||||
TEST_SETTINGS_KEY,
|
||||
TEST_SETTINGS_CORRUPTED,
|
||||
TEST_SETTINGS,
|
||||
TEST_SETTINGS_DEFAULT_THEME,
|
||||
} from '../storageStates'
|
||||
import { SETTINGS_FILE_NAME, PROJECT_SETTINGS_FILE_NAME } from 'lib/constants'
|
||||
import { getUtils, setup } from '../test-utils'
|
||||
import fsp from 'fs/promises'
|
||||
import { join } from 'path'
|
||||
import fs from 'node:fs'
|
||||
import path from 'path'
|
||||
import { CmdBarFixture } from './cmdBarFixture'
|
||||
import { EditorFixture } from './editorFixture'
|
||||
import { ToolbarFixture } from './toolbarFixture'
|
||||
@ -23,7 +41,7 @@ export class AuthenticatedApp {
|
||||
public readonly testInfo: TestInfo
|
||||
public readonly viewPortSize = { width: 1200, height: 500 }
|
||||
public electronApp: undefined | ElectronApplication
|
||||
public dir: string = ''
|
||||
public projectDirName: string = ''
|
||||
|
||||
constructor(context: BrowserContext, page: Page, testInfo: TestInfo) {
|
||||
this.context = context
|
||||
@ -46,7 +64,7 @@ export class AuthenticatedApp {
|
||||
}
|
||||
getInputFile = (fileName: string) => {
|
||||
return fsp.readFile(
|
||||
join('rust', 'kcl-lib', 'e2e', 'executor', 'inputs', fileName),
|
||||
path.join('rust', 'kcl-lib', 'e2e', 'executor', 'inputs', fileName),
|
||||
'utf-8'
|
||||
)
|
||||
}
|
||||
@ -59,101 +77,326 @@ export interface Fixtures {
|
||||
scene: SceneFixture
|
||||
homePage: HomePageFixture
|
||||
}
|
||||
export class AuthenticatedTronApp {
|
||||
public originalPage: Page
|
||||
public page: Page
|
||||
public browserContext: BrowserContext
|
||||
public context: BrowserContext
|
||||
public readonly testInfo: TestInfo
|
||||
public electronApp: ElectronApplication | undefined
|
||||
public readonly viewPortSize = { width: 1200, height: 500 }
|
||||
public dir: string = ''
|
||||
|
||||
constructor(
|
||||
browserContext: BrowserContext,
|
||||
originalPage: Page,
|
||||
testInfo: TestInfo
|
||||
) {
|
||||
this.page = originalPage
|
||||
this.originalPage = originalPage
|
||||
this.browserContext = browserContext
|
||||
// Will be overwritten in the initializer
|
||||
this.context = browserContext
|
||||
this.testInfo = testInfo
|
||||
}
|
||||
async initialise(
|
||||
arg: {
|
||||
fixtures: Partial<Fixtures>
|
||||
folderSetupFn?: (projectDirName: string) => Promise<void>
|
||||
cleanProjectDir?: boolean
|
||||
appSettings?: DeepPartial<Settings>
|
||||
} = { fixtures: {} }
|
||||
) {
|
||||
const { electronApp, page, context, dir } = await setupElectron({
|
||||
testInfo: this.testInfo,
|
||||
folderSetupFn: arg.folderSetupFn,
|
||||
cleanProjectDir: arg.cleanProjectDir,
|
||||
appSettings: arg.appSettings,
|
||||
viewport: this.viewPortSize,
|
||||
export class ElectronZoo {
|
||||
public available: boolean = true
|
||||
public electron!: ElectronApplication
|
||||
public firstUrl = ''
|
||||
public viewPortSize = { width: 1200, height: 500 }
|
||||
public projectDirName = ''
|
||||
|
||||
public page!: Page
|
||||
public context!: BrowserContext
|
||||
|
||||
constructor() {}
|
||||
|
||||
// Help remote end by signaling we're done with the connection.
|
||||
// If it takes longer than 10s to stop, just resolve.
|
||||
async makeAvailableAgain() {
|
||||
await this.page.evaluate(async () => {
|
||||
return new Promise((resolve) => {
|
||||
if (!window.engineCommandManager.engineConnection?.state?.type) {
|
||||
return resolve(undefined)
|
||||
}
|
||||
|
||||
window.engineCommandManager.tearDown()
|
||||
|
||||
// Keep polling (per js event tick) until state is Disconnected.
|
||||
const timeA = Date.now()
|
||||
const checkDisconnected = () => {
|
||||
// It's possible we never even created an engineConnection
|
||||
// e.g. never left Projects view.
|
||||
if (
|
||||
window.engineCommandManager?.engineConnection?.state.type ===
|
||||
'disconnected'
|
||||
) {
|
||||
return resolve(undefined)
|
||||
}
|
||||
|
||||
if (Date.now() - timeA > 10000) {
|
||||
return resolve(undefined)
|
||||
}
|
||||
|
||||
setTimeout(checkDisconnected, 0)
|
||||
}
|
||||
checkDisconnected()
|
||||
})
|
||||
})
|
||||
this.page = page
|
||||
|
||||
// These assignments "fix" some brokenness in the Playwright Workbench when
|
||||
// running against electron applications.
|
||||
// The timeline is still broken but failure screenshots work again.
|
||||
this.context = context
|
||||
// TODO: try to get this to work again for screenshots, but it messed with test ends when enabled
|
||||
// Object.assign(this.browserContext, this.context)
|
||||
await this.context.tracing.stopChunk({ path: 'trace.zip' })
|
||||
|
||||
this.electronApp = electronApp
|
||||
this.dir = dir
|
||||
// Only after cleanup we're ready.
|
||||
this.available = true
|
||||
}
|
||||
|
||||
// Easier to access throughout utils
|
||||
this.page.dir = dir
|
||||
async createInstanceIfMissing(testInfo: TestInfo) {
|
||||
// Create or otherwise clear the folder.
|
||||
this.projectDirName = testInfo.outputPath('electron-test-projects-dir')
|
||||
|
||||
// Setup localStorage, addCookies, reload
|
||||
await setup(this.context, this.page, this.testInfo)
|
||||
// We need to expose this in order for some tests that require folder
|
||||
// creation and some code below.
|
||||
const that = this
|
||||
|
||||
for (const key of unsafeTypedKeys(arg.fixtures)) {
|
||||
const fixture = arg.fixtures[key]
|
||||
if (
|
||||
!fixture ||
|
||||
fixture instanceof AuthenticatedApp ||
|
||||
fixture instanceof AuthenticatedTronApp
|
||||
)
|
||||
continue
|
||||
fixture.reConstruct(page)
|
||||
const options = {
|
||||
timeout: 120000,
|
||||
args: ['.', '--no-sandbox'],
|
||||
env: {
|
||||
...process.env,
|
||||
TEST_SETTINGS_FILE_KEY: this.projectDirName,
|
||||
IS_PLAYWRIGHT: 'true',
|
||||
},
|
||||
...(process.env.ELECTRON_OVERRIDE_DIST_PATH
|
||||
? {
|
||||
executablePath:
|
||||
process.env.ELECTRON_OVERRIDE_DIST_PATH + 'electron',
|
||||
}
|
||||
: {}),
|
||||
...(process.env.PLAYWRIGHT_RECORD_VIDEO
|
||||
? {
|
||||
recordVideo: {
|
||||
dir: testInfo.snapshotPath(),
|
||||
size: this.viewPortSize,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
}
|
||||
|
||||
// Do this once and then reuse window on subsequent calls.
|
||||
if (!this.electron) {
|
||||
this.electron = await electron.launch(options)
|
||||
|
||||
// Mac takes quite a long time to create the first window in CI.
|
||||
// Turns out we can't trust firstWindow() either. So loop.
|
||||
let timeoutId: ReturnType<typeof setTimeout>
|
||||
const tryToGetWindowPage = () =>
|
||||
new Promise((resolve) => {
|
||||
const fn = () => {
|
||||
this.page = this.electron.windows()[0]
|
||||
timeoutId = setTimeout(() => {
|
||||
if (this.page) {
|
||||
clearTimeout(timeoutId)
|
||||
return resolve(undefined)
|
||||
}
|
||||
fn()
|
||||
}, 0)
|
||||
}
|
||||
fn()
|
||||
})
|
||||
|
||||
await tryToGetWindowPage()
|
||||
|
||||
this.context = this.electron.context()
|
||||
await this.context.tracing.start({ screenshots: true, snapshots: true })
|
||||
}
|
||||
|
||||
await this.context.tracing.startChunk()
|
||||
|
||||
await setup(this.context, this.page, testInfo)
|
||||
|
||||
await this.cleanProjectDir()
|
||||
|
||||
// Create a consistent way to resize the page across electron and web.
|
||||
// (lee) I had to do everything in the book to make electron change its
|
||||
// damn window size. I succeeded in making it consistently and reliably
|
||||
// do it after a whole afternoon.
|
||||
this.page.setBodyDimensions = async function (dims: {
|
||||
width: number
|
||||
height: number
|
||||
}) {
|
||||
await this.setViewportSize(dims)
|
||||
|
||||
await that.electron?.evaluateHandle(async ({ app }, dims) => {
|
||||
// @ts-ignore sorry jon but see comment in main.ts why this is ignored
|
||||
await app.resizeWindow(dims.width, dims.height)
|
||||
}, dims)
|
||||
|
||||
return this.evaluate(async (dims: { width: number; height: number }) => {
|
||||
await window.electron.resizeWindow(dims.width, dims.height)
|
||||
window.document.body.style.width = dims.width + 'px'
|
||||
window.document.body.style.height = dims.height + 'px'
|
||||
window.document.documentElement.style.width = dims.width + 'px'
|
||||
window.document.documentElement.style.height = dims.height + 'px'
|
||||
}, dims)
|
||||
}
|
||||
|
||||
await this.page.setBodyDimensions(this.viewPortSize)
|
||||
|
||||
this.context.folderSetupFn = async function (fn) {
|
||||
return fn(that.projectDirName)
|
||||
.then(() => that.page.reload())
|
||||
.then(() => ({
|
||||
dir: that.projectDirName,
|
||||
}))
|
||||
}
|
||||
|
||||
// We need to patch this because addInitScript will bind too late in our
|
||||
// electron tests, never running. We need to call reload() after each call
|
||||
// to guarantee it runs.
|
||||
const oldContextAddInitScript = this.context.addInitScript
|
||||
this.context.addInitScript = async function (a, b) {
|
||||
// @ts-ignore pretty sure way out of tsc's type checking capabilities.
|
||||
// This code works perfectly fine.
|
||||
await oldContextAddInitScript.apply(this, [a, b])
|
||||
await that.page.reload()
|
||||
}
|
||||
|
||||
// No idea why we mix and match page and context's addInitScript but we do
|
||||
const oldPageAddInitScript = this.page.addInitScript
|
||||
this.page.addInitScript = async function (a: any, b: any) {
|
||||
// @ts-ignore pretty sure way out of tsc's type checking capabilities.
|
||||
// This code works perfectly fine.
|
||||
await oldPageAddInitScript.apply(this, [a, b])
|
||||
await that.page.reload()
|
||||
}
|
||||
|
||||
if (!this.firstUrl) {
|
||||
await this.page.getByText('Your Projects').count()
|
||||
this.firstUrl = this.page.url()
|
||||
}
|
||||
|
||||
// Due to the app controlling its own window context we need to inject new
|
||||
// options and context here.
|
||||
// NOTE TO LEE: Seems to destroy page context when calling an electron loadURL.
|
||||
// await tronApp.electronApp.evaluate(({ app }) => {
|
||||
// return app.reuseWindowForTest();
|
||||
// });
|
||||
|
||||
await 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
|
||||
await this.page.goto(this.firstUrl)
|
||||
|
||||
// Force a hard reload, destroying the stream and other state
|
||||
await this.page.reload()
|
||||
}
|
||||
|
||||
close = async () => {
|
||||
await this.electronApp?.close?.()
|
||||
async cleanProjectDir(appSettings?: DeepPartial<Settings>) {
|
||||
try {
|
||||
if (fs.existsSync(this.projectDirName)) {
|
||||
await fsp.rm(this.projectDirName, { recursive: true })
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
|
||||
try {
|
||||
await fsp.mkdir(this.projectDirName)
|
||||
} catch (e) {
|
||||
// Not a problem if it already exists.
|
||||
}
|
||||
|
||||
const tempSettingsFilePath = path.join(
|
||||
this.projectDirName,
|
||||
SETTINGS_FILE_NAME
|
||||
)
|
||||
|
||||
let settingsOverridesToml = ''
|
||||
|
||||
if (appSettings) {
|
||||
settingsOverridesToml = TOML.stringify({
|
||||
// @ts-expect-error
|
||||
settings: {
|
||||
...TEST_SETTINGS,
|
||||
...appSettings,
|
||||
app: {
|
||||
...TEST_SETTINGS.app,
|
||||
project_directory: this.projectDirName,
|
||||
...appSettings.app,
|
||||
},
|
||||
},
|
||||
})
|
||||
} else {
|
||||
settingsOverridesToml = TOML.stringify({
|
||||
// @ts-expect-error
|
||||
settings: {
|
||||
...TEST_SETTINGS,
|
||||
app: {
|
||||
...TEST_SETTINGS.app,
|
||||
project_directory: this.projectDirName,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
await fsp.writeFile(tempSettingsFilePath, settingsOverridesToml)
|
||||
}
|
||||
debugPause = () =>
|
||||
new Promise(() => {
|
||||
console.log('UN-RESOLVING PROMISE')
|
||||
})
|
||||
}
|
||||
|
||||
export const fixtures = {
|
||||
cmdBar: async ({ page }: { page: Page }, use: any) => {
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
// If yee encounter this, please try to type it.
|
||||
type FnUse = any
|
||||
|
||||
const fixturesForElectron = {
|
||||
page: async (
|
||||
{ tronApp }: { tronApp: ElectronZoo },
|
||||
use: FnUse,
|
||||
testInfo: TestInfo
|
||||
) => {
|
||||
await use(tronApp.page)
|
||||
},
|
||||
context: async (
|
||||
{ tronApp }: { tronApp: ElectronZoo },
|
||||
use: FnUse,
|
||||
testInfo: TestInfo
|
||||
) => {
|
||||
await use(tronApp.context)
|
||||
},
|
||||
}
|
||||
|
||||
const fixturesForWeb = {
|
||||
page: async (
|
||||
{ page, context }: { page: Page; context: BrowserContext },
|
||||
use: FnUse,
|
||||
testInfo: TestInfo
|
||||
) => {
|
||||
page.setBodyDimensions = page.setViewportSize
|
||||
|
||||
// We do the same thing in ElectronZoo. addInitScript simply doesn't fire
|
||||
// at the correct time, so we reload the page and it fires appropriately.
|
||||
const oldPageAddInitScript = page.addInitScript
|
||||
page.addInitScript = async function (...args) {
|
||||
// @ts-expect-error
|
||||
await oldPageAddInitScript.apply(this, args)
|
||||
await page.reload()
|
||||
}
|
||||
|
||||
const oldContextAddInitScript = context.addInitScript
|
||||
context.addInitScript = async function (...args) {
|
||||
// @ts-expect-error
|
||||
await oldContextAddInitScript.apply(this, args)
|
||||
await page.reload()
|
||||
}
|
||||
|
||||
const webApp = new AuthenticatedApp(context, page, testInfo)
|
||||
await webApp.initialise()
|
||||
|
||||
await use(page)
|
||||
},
|
||||
}
|
||||
|
||||
const fixturesBasedOnProcessEnvPlatform = {
|
||||
cmdBar: async ({ page }: { page: Page }, use: FnUse) => {
|
||||
await use(new CmdBarFixture(page))
|
||||
},
|
||||
editor: async ({ page }: { page: Page }, use: any) => {
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
editor: async ({ page }: { page: Page }, use: FnUse) => {
|
||||
await use(new EditorFixture(page))
|
||||
},
|
||||
toolbar: async ({ page }: { page: Page }, use: any) => {
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
toolbar: async ({ page }: { page: Page }, use: FnUse) => {
|
||||
await use(new ToolbarFixture(page))
|
||||
},
|
||||
scene: async ({ page }: { page: Page }, use: any) => {
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
scene: async ({ page }: { page: Page }, use: FnUse) => {
|
||||
await use(new SceneFixture(page))
|
||||
},
|
||||
homePage: async ({ page }: { page: Page }, use: any) => {
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
homePage: async ({ page }: { page: Page }, use: FnUse) => {
|
||||
await use(new HomePageFixture(page))
|
||||
},
|
||||
}
|
||||
|
||||
if (process.env.PLATFORM === 'web') {
|
||||
Object.assign(fixturesBasedOnProcessEnvPlatform, fixturesForWeb)
|
||||
} else {
|
||||
Object.assign(fixturesBasedOnProcessEnvPlatform, fixturesForElectron)
|
||||
}
|
||||
|
||||
export { fixturesBasedOnProcessEnvPlatform }
|
||||
|
@ -27,10 +27,6 @@ export class HomePageFixture {
|
||||
|
||||
constructor(page: Page) {
|
||||
this.page = page
|
||||
this.reConstruct(page)
|
||||
}
|
||||
reConstruct = (page: Page) => {
|
||||
this.page = page
|
||||
|
||||
this.projectSection = this.page.getByTestId('home-section')
|
||||
|
||||
@ -96,8 +92,12 @@ export class HomePageFixture {
|
||||
}
|
||||
}
|
||||
|
||||
createAndGoToProject = async (projectTitle = 'project-$nnn') => {
|
||||
projectsLoaded = async () => {
|
||||
await expect(this.projectSection).not.toHaveText('Loading your Projects...')
|
||||
}
|
||||
|
||||
createAndGoToProject = async (projectTitle = 'project-$nnn') => {
|
||||
await this.projectsLoaded()
|
||||
await this.projectButtonNew.click()
|
||||
await this.projectTextName.click()
|
||||
await this.projectTextName.fill(projectTitle)
|
||||
|
@ -53,7 +53,12 @@ export class SceneFixture {
|
||||
|
||||
constructor(page: Page) {
|
||||
this.page = page
|
||||
this.reConstruct(page)
|
||||
this.streamWrapper = page.getByTestId('stream')
|
||||
this.networkToggleConnected = page.getByTestId('network-toggle-ok')
|
||||
this.loadingIndicator = this.streamWrapper.getByTestId('loading')
|
||||
this.startEditSketchBtn = page
|
||||
.getByRole('button', { name: 'Start Sketch' })
|
||||
.or(page.getByRole('button', { name: 'Edit Sketch' }))
|
||||
}
|
||||
private _serialiseScene = async (): Promise<SceneSerialised> => {
|
||||
const camera = await this.getCameraInfo()
|
||||
@ -72,17 +77,6 @@ export class SceneFixture {
|
||||
.toEqual(expected)
|
||||
}
|
||||
|
||||
reConstruct = (page: Page) => {
|
||||
this.page = page
|
||||
|
||||
this.streamWrapper = page.getByTestId('stream')
|
||||
this.networkToggleConnected = page.getByTestId('network-toggle-ok')
|
||||
this.loadingIndicator = this.streamWrapper.getByTestId('loading')
|
||||
this.startEditSketchBtn = page
|
||||
.getByRole('button', { name: 'Start Sketch' })
|
||||
.or(page.getByRole('button', { name: 'Edit Sketch' }))
|
||||
}
|
||||
|
||||
makeMouseHelpers = (
|
||||
x: number,
|
||||
y: number,
|
||||
@ -253,7 +247,7 @@ export class SceneFixture {
|
||||
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
await u.clearAndCloseDebugPanel()
|
||||
await u.closeDebugPanel()
|
||||
|
||||
await this.waitForExecutionDone()
|
||||
await expect(this.startEditSketchBtn).not.toBeDisabled()
|
||||
|
@ -8,6 +8,7 @@ import {
|
||||
} from '../test-utils'
|
||||
import { SidebarType } from 'components/ModelingSidebar/ModelingPanes'
|
||||
import { SIDEBAR_BUTTON_SUFFIX } from 'lib/constants'
|
||||
import { ToolbarModeName } from 'lib/toolbar'
|
||||
|
||||
export class ToolbarFixture {
|
||||
public page: Page
|
||||
@ -28,7 +29,6 @@ export class ToolbarFixture {
|
||||
rectangleBtn!: Locator
|
||||
lengthConstraintBtn!: Locator
|
||||
exitSketchBtn!: Locator
|
||||
editSketchBtn!: Locator
|
||||
fileTreeBtn!: Locator
|
||||
createFileBtn!: Locator
|
||||
fileCreateToast!: Locator
|
||||
@ -38,13 +38,12 @@ export class ToolbarFixture {
|
||||
featureTreeId = 'feature-tree' as const
|
||||
/** The pane element for the Feature Tree */
|
||||
featureTreePane!: Locator
|
||||
gizmo!: Locator
|
||||
gizmoDisabled!: Locator
|
||||
|
||||
constructor(page: Page) {
|
||||
this.page = page
|
||||
this.reConstruct(page)
|
||||
}
|
||||
reConstruct = (page: Page) => {
|
||||
this.page = page
|
||||
|
||||
this.extrudeButton = page.getByTestId('extrude')
|
||||
this.loftButton = page.getByTestId('loft')
|
||||
this.sweepButton = page.getByTestId('sweep')
|
||||
@ -61,7 +60,6 @@ export class ToolbarFixture {
|
||||
this.rectangleBtn = page.getByTestId('corner-rectangle')
|
||||
this.lengthConstraintBtn = page.getByTestId('constraint-length')
|
||||
this.exitSketchBtn = page.getByTestId('sketch-exit')
|
||||
this.editSketchBtn = page.locator('[name="Edit Sketch"]')
|
||||
this.fileTreeBtn = page.locator('[id="files-button-holder"]')
|
||||
this.createFileBtn = page.getByTestId('create-file-button')
|
||||
this.treeInputField = page.getByTestId('tree-input-field')
|
||||
@ -69,6 +67,17 @@ export class ToolbarFixture {
|
||||
this.filePane = page.locator('#files-pane')
|
||||
this.featureTreePane = page.locator('#feature-tree-pane')
|
||||
this.fileCreateToast = page.getByText('Successfully created')
|
||||
|
||||
// Note to test writers: having two locators like this is preferable to one
|
||||
// which changes another el property because it means our test "signal" is
|
||||
// completely decoupled from the elements themselves. It means the same
|
||||
// element or two different elements can represent these states.
|
||||
this.gizmo = page.getByTestId('gizmo')
|
||||
this.gizmoDisabled = page.getByTestId('gizmo-disabled')
|
||||
}
|
||||
|
||||
get editSketchBtn() {
|
||||
return this.page.locator('[name="Edit Sketch"]')
|
||||
}
|
||||
|
||||
get logoLink() {
|
||||
@ -84,6 +93,18 @@ export class ToolbarFixture {
|
||||
startSketchPlaneSelection = async () =>
|
||||
doAndWaitForImageDiff(this.page, () => this.startSketchBtn.click(), 500)
|
||||
|
||||
waitUntilSketchingReady = async () => {
|
||||
await expect(this.gizmoDisabled).toBeVisible()
|
||||
}
|
||||
|
||||
startSketchThenCallbackThenWaitUntilReady = async (
|
||||
cb: () => Promise<void>
|
||||
) => {
|
||||
await this.startSketchBtn.click()
|
||||
await cb()
|
||||
await this.waitUntilSketchingReady()
|
||||
}
|
||||
|
||||
exitSketch = async () => {
|
||||
await this.exitSketchBtn.click()
|
||||
await expect(
|
||||
@ -100,6 +121,15 @@ export class ToolbarFixture {
|
||||
// this is for the engine animation, as it takes 500ms to complete
|
||||
await this.page.waitForTimeout(600)
|
||||
}
|
||||
private _getMode = () =>
|
||||
this.page.locator('[data-current-mode]').getAttribute('data-current-mode')
|
||||
expectToolbarMode = {
|
||||
toBe: (mode: ToolbarModeName) => expect.poll(this._getMode).toEqual(mode),
|
||||
not: {
|
||||
toBe: (mode: ToolbarModeName) =>
|
||||
expect.poll(this._getMode).not.toEqual(mode),
|
||||
},
|
||||
}
|
||||
|
||||
private _serialiseFileTree = async () => {
|
||||
return this.page
|
||||
@ -156,6 +186,22 @@ export class ToolbarFixture {
|
||||
).toBeVisible()
|
||||
await this.page.getByTestId('dropdown-circle-three-points').click()
|
||||
}
|
||||
selectArc = async () => {
|
||||
await this.page
|
||||
.getByRole('button', { name: 'caret down Tangential Arc:' })
|
||||
.click()
|
||||
await expect(this.page.getByTestId('dropdown-arc')).toBeVisible()
|
||||
await this.page.getByTestId('dropdown-arc').click()
|
||||
}
|
||||
selectThreePointArc = async () => {
|
||||
await this.page
|
||||
.getByRole('button', { name: 'caret down Tangential Arc:' })
|
||||
.click()
|
||||
await expect(
|
||||
this.page.getByTestId('dropdown-three-point-arc')
|
||||
).toBeVisible()
|
||||
await this.page.getByTestId('dropdown-three-point-arc').click()
|
||||
}
|
||||
|
||||
async closePane(paneId: SidebarType) {
|
||||
return closePane(this.page, paneId + SIDEBAR_BUTTON_SUFFIX)
|
||||
|
@ -21,58 +21,54 @@ import { expectPixelColor } from './fixtures/sceneFixture'
|
||||
// we must set it to empty for the tests where we want to see the onboarding immediately.
|
||||
|
||||
test.describe('Onboarding tests', () => {
|
||||
test(
|
||||
'Onboarding code is shown in the editor',
|
||||
{
|
||||
appSettings: {
|
||||
app: {
|
||||
onboarding_status: '',
|
||||
},
|
||||
},
|
||||
cleanProjectDir: true,
|
||||
},
|
||||
async ({ page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// Test that the onboarding pane loaded
|
||||
await expect(
|
||||
page.getByText('Welcome to Modeling App! This')
|
||||
).toBeVisible()
|
||||
|
||||
// Test that the onboarding pane loaded
|
||||
await expect(
|
||||
page.getByText('Welcome to Modeling App! This')
|
||||
).toBeVisible()
|
||||
|
||||
// *and* that the code is shown in the editor
|
||||
await expect(page.locator('.cm-content')).toContainText(
|
||||
'// Shelf Bracket'
|
||||
)
|
||||
|
||||
// Make sure the model loaded
|
||||
const XYPlanePoint = { x: 774, y: 116 } as const
|
||||
const modelColor: [number, number, number] = [45, 45, 45]
|
||||
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y)
|
||||
expect(await u.getGreatestPixDiff(XYPlanePoint, modelColor)).toBeLessThan(
|
||||
8
|
||||
)
|
||||
test('Onboarding code is shown in the editor', async ({
|
||||
page,
|
||||
homePage,
|
||||
tronApp,
|
||||
}) => {
|
||||
if (!tronApp) {
|
||||
fail()
|
||||
}
|
||||
)
|
||||
await tronApp.cleanProjectDir({
|
||||
app: {
|
||||
onboarding_status: '',
|
||||
},
|
||||
})
|
||||
|
||||
const u = await getUtils(page)
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// Test that the onboarding pane loaded
|
||||
await expect(page.getByText('Welcome to Modeling App! This')).toBeVisible()
|
||||
|
||||
// Test that the onboarding pane loaded
|
||||
await expect(page.getByText('Welcome to Modeling App! This')).toBeVisible()
|
||||
|
||||
// *and* that the code is shown in the editor
|
||||
await expect(page.locator('.cm-content')).toContainText('// Shelf Bracket')
|
||||
|
||||
// Make sure the model loaded
|
||||
const XYPlanePoint = { x: 774, y: 116 } as const
|
||||
const modelColor: [number, number, number] = [45, 45, 45]
|
||||
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y)
|
||||
expect(await u.getGreatestPixDiff(XYPlanePoint, modelColor)).toBeLessThan(8)
|
||||
})
|
||||
|
||||
test(
|
||||
'Desktop: fresh onboarding executes and loads',
|
||||
{
|
||||
tag: '@electron',
|
||||
appSettings: {
|
||||
},
|
||||
async ({ page, tronApp }) => {
|
||||
if (!tronApp) {
|
||||
fail()
|
||||
}
|
||||
await tronApp.cleanProjectDir({
|
||||
app: {
|
||||
onboarding_status: '',
|
||||
},
|
||||
},
|
||||
cleanProjectDir: true,
|
||||
},
|
||||
async ({ page }) => {
|
||||
})
|
||||
const u = await getUtils(page)
|
||||
|
||||
const viewportSize = { width: 1200, height: 500 }
|
||||
@ -107,223 +103,235 @@ test.describe('Onboarding tests', () => {
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'Code resets after confirmation',
|
||||
{
|
||||
cleanProjectDir: true,
|
||||
},
|
||||
async ({ context, page, homePage }) => {
|
||||
const initialCode = `sketch001 = startSketchOn('XZ')`
|
||||
test('Code resets after confirmation', async ({
|
||||
context,
|
||||
page,
|
||||
homePage,
|
||||
tronApp,
|
||||
scene,
|
||||
cmdBar,
|
||||
}) => {
|
||||
if (!tronApp) {
|
||||
fail()
|
||||
}
|
||||
await tronApp.cleanProjectDir()
|
||||
|
||||
// Load the page up with some code so we see the confirmation warning
|
||||
// when we go to replay onboarding
|
||||
await context.addInitScript((code) => {
|
||||
localStorage.setItem('persistCode', code)
|
||||
}, initialCode)
|
||||
const initialCode = `sketch001 = startSketchOn('XZ')`
|
||||
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
// Load the page up with some code so we see the confirmation warning
|
||||
// when we go to replay onboarding
|
||||
await page.addInitScript((code) => {
|
||||
localStorage.setItem('persistCode', code)
|
||||
}, initialCode)
|
||||
|
||||
// Replay the onboarding
|
||||
await page.getByRole('link', { name: 'Settings' }).last().click()
|
||||
const replayButton = page.getByRole('button', {
|
||||
name: 'Replay onboarding',
|
||||
})
|
||||
await expect(replayButton).toBeVisible()
|
||||
await replayButton.click()
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
await scene.connectionEstablished()
|
||||
|
||||
// Ensure we see the warning, and that the code has not yet updated
|
||||
await expect(page.getByText('Would you like to create')).toBeVisible()
|
||||
await expect(page.locator('.cm-content')).toHaveText(initialCode)
|
||||
// Replay the onboarding
|
||||
await page.getByRole('link', { name: 'Settings' }).last().click()
|
||||
const replayButton = page.getByRole('button', {
|
||||
name: 'Replay onboarding',
|
||||
})
|
||||
await expect(replayButton).toBeVisible()
|
||||
await replayButton.click()
|
||||
|
||||
const nextButton = page.getByTestId('onboarding-next')
|
||||
// Ensure we see the warning, and that the code has not yet updated
|
||||
await expect(page.getByText('Would you like to create')).toBeVisible()
|
||||
await expect(page.locator('.cm-content')).toHaveText(initialCode)
|
||||
|
||||
const nextButton = page.getByTestId('onboarding-next')
|
||||
await nextButton.hover()
|
||||
await nextButton.click()
|
||||
|
||||
// Ensure we see the introduction and that the code has been reset
|
||||
await expect(page.getByText('Welcome to Modeling App!')).toBeVisible()
|
||||
await expect(page.locator('.cm-content')).toContainText('// Shelf Bracket')
|
||||
|
||||
// There used to be old code here that checked if we stored the reset
|
||||
// code into localStorage but that isn't the case on desktop. It gets
|
||||
// saved to the file system, which we have other tests for.
|
||||
})
|
||||
|
||||
test('Click through each onboarding step and back', async ({
|
||||
context,
|
||||
page,
|
||||
homePage,
|
||||
tronApp,
|
||||
}) => {
|
||||
if (!tronApp) {
|
||||
fail()
|
||||
}
|
||||
await tronApp.cleanProjectDir({
|
||||
app: {
|
||||
onboarding_status: '',
|
||||
},
|
||||
})
|
||||
// Override beforeEach test setup
|
||||
await context.addInitScript(
|
||||
async ({ settingsKey, settings }) => {
|
||||
// Give no initial code, so that the onboarding start is shown immediately
|
||||
localStorage.setItem('persistCode', '')
|
||||
localStorage.setItem(settingsKey, settings)
|
||||
},
|
||||
{
|
||||
settingsKey: TEST_SETTINGS_KEY,
|
||||
settings: settingsToToml({
|
||||
settings: TEST_SETTINGS_ONBOARDING_START,
|
||||
}),
|
||||
}
|
||||
)
|
||||
|
||||
await page.setBodyDimensions({ width: 1200, height: 1080 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// Test that the onboarding pane loaded
|
||||
await expect(page.getByText('Welcome to Modeling App! This')).toBeVisible()
|
||||
|
||||
const nextButton = page.getByTestId('onboarding-next')
|
||||
const prevButton = page.getByTestId('onboarding-prev')
|
||||
|
||||
while ((await nextButton.innerText()) !== 'Finish') {
|
||||
await nextButton.hover()
|
||||
await nextButton.click()
|
||||
|
||||
// Ensure we see the introduction and that the code has been reset
|
||||
await expect(page.getByText('Welcome to Modeling App!')).toBeVisible()
|
||||
await expect(page.locator('.cm-content')).toContainText(
|
||||
'// Shelf Bracket'
|
||||
)
|
||||
|
||||
// There used to be old code here that checked if we stored the reset
|
||||
// code into localStorage but that isn't the case on desktop. It gets
|
||||
// saved to the file system, which we have other tests for.
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'Click through each onboarding step and back',
|
||||
{
|
||||
appSettings: {
|
||||
app: {
|
||||
onboarding_status: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
async ({ context, page, homePage }) => {
|
||||
// Override beforeEach test setup
|
||||
await context.addInitScript(
|
||||
async ({ settingsKey, settings }) => {
|
||||
// Give no initial code, so that the onboarding start is shown immediately
|
||||
localStorage.setItem('persistCode', '')
|
||||
localStorage.setItem(settingsKey, settings)
|
||||
},
|
||||
{
|
||||
settingsKey: TEST_SETTINGS_KEY,
|
||||
settings: settingsToToml({
|
||||
settings: TEST_SETTINGS_ONBOARDING_START,
|
||||
}),
|
||||
}
|
||||
)
|
||||
|
||||
await page.setBodyDimensions({ width: 1200, height: 1080 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// Test that the onboarding pane loaded
|
||||
await expect(
|
||||
page.getByText('Welcome to Modeling App! This')
|
||||
).toBeVisible()
|
||||
|
||||
const nextButton = page.getByTestId('onboarding-next')
|
||||
const prevButton = page.getByTestId('onboarding-prev')
|
||||
|
||||
while ((await nextButton.innerText()) !== 'Finish') {
|
||||
await nextButton.hover()
|
||||
await nextButton.click()
|
||||
}
|
||||
|
||||
while ((await prevButton.innerText()) !== 'Dismiss') {
|
||||
await prevButton.hover()
|
||||
await prevButton.click()
|
||||
}
|
||||
|
||||
// Dismiss the onboarding
|
||||
while ((await prevButton.innerText()) !== 'Dismiss') {
|
||||
await prevButton.hover()
|
||||
await prevButton.click()
|
||||
|
||||
// Test that the onboarding pane is gone
|
||||
await expect(page.getByTestId('onboarding-content')).not.toBeVisible()
|
||||
await expect.poll(() => page.url()).not.toContain('/onboarding')
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'Onboarding redirects and code updating',
|
||||
{
|
||||
appSettings: {
|
||||
app: {
|
||||
onboarding_status: '/export',
|
||||
},
|
||||
// Dismiss the onboarding
|
||||
await prevButton.hover()
|
||||
await prevButton.click()
|
||||
|
||||
// Test that the onboarding pane is gone
|
||||
await expect(page.getByTestId('onboarding-content')).not.toBeVisible()
|
||||
await expect.poll(() => page.url()).not.toContain('/onboarding')
|
||||
})
|
||||
|
||||
test('Onboarding redirects and code updating', async ({
|
||||
context,
|
||||
page,
|
||||
homePage,
|
||||
tronApp,
|
||||
}) => {
|
||||
if (!tronApp) {
|
||||
fail()
|
||||
}
|
||||
await tronApp.cleanProjectDir({
|
||||
app: {
|
||||
onboarding_status: '/export',
|
||||
},
|
||||
cleanProjectDir: true,
|
||||
},
|
||||
async ({ context, page, homePage }) => {
|
||||
const originalCode = 'sigmaAllow = 15000'
|
||||
})
|
||||
|
||||
// Override beforeEach test setup
|
||||
await context.addInitScript(
|
||||
async ({ settingsKey, settings }) => {
|
||||
// Give some initial code, so we can test that it's cleared
|
||||
localStorage.setItem('persistCode', originalCode)
|
||||
localStorage.setItem(settingsKey, settings)
|
||||
},
|
||||
{
|
||||
settingsKey: TEST_SETTINGS_KEY,
|
||||
settings: settingsToToml({
|
||||
settings: TEST_SETTINGS_ONBOARDING_EXPORT,
|
||||
}),
|
||||
}
|
||||
)
|
||||
const originalCode = 'sigmaAllow = 15000'
|
||||
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// Test that the redirect happened
|
||||
await expect.poll(() => page.url()).toContain('/onboarding/export')
|
||||
|
||||
// Test that you come back to this page when you refresh
|
||||
await page.reload()
|
||||
await expect.poll(() => page.url()).toContain('/onboarding/export')
|
||||
|
||||
// Test that the code changes when you advance to the next step
|
||||
await page.getByTestId('onboarding-next').hover()
|
||||
await page.getByTestId('onboarding-next').click()
|
||||
|
||||
// Test that the onboarding pane loaded
|
||||
const title = page.locator('[data-testid="onboarding-content"]')
|
||||
await expect(title).toBeAttached()
|
||||
|
||||
await expect(page.locator('.cm-content')).not.toHaveText(originalCode)
|
||||
|
||||
// Test that the code is not empty when you click on the next step
|
||||
await page.locator('[data-testid="onboarding-next"]').hover()
|
||||
await page.locator('[data-testid="onboarding-next"]').click()
|
||||
await expect(page.locator('.cm-content')).toHaveText(/.+/)
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'Onboarding code gets reset to demo on Interactive Numbers step',
|
||||
{
|
||||
appSettings: {
|
||||
app: {
|
||||
onboarding_status: '/parametric-modeling',
|
||||
},
|
||||
// Override beforeEach test setup
|
||||
await context.addInitScript(
|
||||
async ({ settingsKey, settings }) => {
|
||||
// Give some initial code, so we can test that it's cleared
|
||||
localStorage.setItem('persistCode', originalCode)
|
||||
localStorage.setItem(settingsKey, settings)
|
||||
},
|
||||
cleanProjectDir: true,
|
||||
},
|
||||
{
|
||||
settingsKey: TEST_SETTINGS_KEY,
|
||||
settings: settingsToToml({
|
||||
settings: TEST_SETTINGS_ONBOARDING_EXPORT,
|
||||
}),
|
||||
}
|
||||
)
|
||||
|
||||
async ({ page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
const badCode = `// This is bad code we shouldn't see`
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await page.setBodyDimensions({ width: 1200, height: 1080 })
|
||||
await homePage.goToModelingScene()
|
||||
// Test that the redirect happened
|
||||
await expect.poll(() => page.url()).toContain('/onboarding/export')
|
||||
|
||||
await expect
|
||||
.poll(() => page.url())
|
||||
.toContain(onboardingPaths.PARAMETRIC_MODELING)
|
||||
// Test that you come back to this page when you refresh
|
||||
await page.reload()
|
||||
await expect.poll(() => page.url()).toContain('/onboarding/export')
|
||||
|
||||
const bracketNoNewLines = bracket.replace(/\n/g, '')
|
||||
// Test that the code changes when you advance to the next step
|
||||
await page.getByTestId('onboarding-next').hover()
|
||||
await page.getByTestId('onboarding-next').click()
|
||||
|
||||
// Check the code got reset on load
|
||||
await expect(page.locator('#code-pane')).toBeVisible()
|
||||
await expect(u.codeLocator).toHaveText(bracketNoNewLines, {
|
||||
timeout: 10_000,
|
||||
})
|
||||
// Test that the onboarding pane loaded
|
||||
const title = page.locator('[data-testid="onboarding-content"]')
|
||||
await expect(title).toBeAttached()
|
||||
|
||||
// Mess with the code again
|
||||
await u.codeLocator.selectText()
|
||||
await u.codeLocator.fill(badCode)
|
||||
await expect(u.codeLocator).toHaveText(badCode)
|
||||
await expect(page.locator('.cm-content')).not.toHaveText(originalCode)
|
||||
|
||||
// Click to the next step
|
||||
await page.locator('[data-testid="onboarding-next"]').hover()
|
||||
await page.locator('[data-testid="onboarding-next"]').click()
|
||||
await page.waitForURL('**' + onboardingPaths.INTERACTIVE_NUMBERS, {
|
||||
waitUntil: 'domcontentloaded',
|
||||
})
|
||||
// Test that the code is not empty when you click on the next step
|
||||
await page.locator('[data-testid="onboarding-next"]').hover()
|
||||
await page.locator('[data-testid="onboarding-next"]').click()
|
||||
await expect(page.locator('.cm-content')).toHaveText(/.+/)
|
||||
})
|
||||
|
||||
// Check that the code has been reset
|
||||
await expect(u.codeLocator).toHaveText(bracketNoNewLines)
|
||||
test('Onboarding code gets reset to demo on Interactive Numbers step', async ({
|
||||
page,
|
||||
homePage,
|
||||
tronApp,
|
||||
}) => {
|
||||
if (!tronApp) {
|
||||
fail()
|
||||
}
|
||||
)
|
||||
await tronApp.cleanProjectDir({
|
||||
app: {
|
||||
onboarding_status: '/parametric-modeling',
|
||||
},
|
||||
})
|
||||
|
||||
const u = await getUtils(page)
|
||||
const badCode = `// This is bad code we shouldn't see`
|
||||
|
||||
await page.setBodyDimensions({ width: 1200, height: 1080 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await expect
|
||||
.poll(() => page.url())
|
||||
.toContain(onboardingPaths.PARAMETRIC_MODELING)
|
||||
|
||||
const bracketNoNewLines = bracket.replace(/\n/g, '')
|
||||
|
||||
// Check the code got reset on load
|
||||
await expect(page.locator('#code-pane')).toBeVisible()
|
||||
await expect(u.codeLocator).toHaveText(bracketNoNewLines, {
|
||||
timeout: 10_000,
|
||||
})
|
||||
|
||||
// Mess with the code again
|
||||
await u.codeLocator.selectText()
|
||||
await u.codeLocator.fill(badCode)
|
||||
await expect(u.codeLocator).toHaveText(badCode)
|
||||
|
||||
// Click to the next step
|
||||
await page.locator('[data-testid="onboarding-next"]').hover()
|
||||
await page.locator('[data-testid="onboarding-next"]').click()
|
||||
await page.waitForURL('**' + onboardingPaths.INTERACTIVE_NUMBERS, {
|
||||
waitUntil: 'domcontentloaded',
|
||||
})
|
||||
|
||||
// Check that the code has been reset
|
||||
await expect(u.codeLocator).toHaveText(bracketNoNewLines)
|
||||
})
|
||||
|
||||
// (lee) The two avatar tests are weird because even on main, we don't have
|
||||
// anything to do with the avatar inside the onboarding test. Due to the
|
||||
// low impact of an avatar not showing I'm changing this to fixme.
|
||||
test.fixme(
|
||||
'Avatar text updates depending on image load success',
|
||||
{
|
||||
appSettings: {
|
||||
async ({ context, page, homePage, tronApp }) => {
|
||||
if (!tronApp) {
|
||||
fail()
|
||||
}
|
||||
|
||||
await tronApp.cleanProjectDir({
|
||||
app: {
|
||||
onboarding_status: '',
|
||||
},
|
||||
},
|
||||
cleanProjectDir: true,
|
||||
},
|
||||
async ({ context, page, homePage }) => {
|
||||
})
|
||||
|
||||
// Override beforeEach test setup
|
||||
await context.addInitScript(
|
||||
async ({ settingsKey, settings }) => {
|
||||
@ -388,15 +396,16 @@ test.describe('Onboarding tests', () => {
|
||||
|
||||
test.fixme(
|
||||
"Avatar text doesn't mention avatar when no avatar",
|
||||
{
|
||||
appSettings: {
|
||||
async ({ context, page, homePage, tronApp }) => {
|
||||
if (!tronApp) {
|
||||
fail()
|
||||
}
|
||||
|
||||
await tronApp.cleanProjectDir({
|
||||
app: {
|
||||
onboarding_status: '',
|
||||
},
|
||||
},
|
||||
cleanProjectDir: true,
|
||||
},
|
||||
async ({ context, page, homePage }) => {
|
||||
})
|
||||
// Override beforeEach test setup
|
||||
await context.addInitScript(
|
||||
async ({ settingsKey, settings }) => {
|
||||
@ -444,15 +453,17 @@ test.describe('Onboarding tests', () => {
|
||||
|
||||
test.fixme(
|
||||
'Restarting onboarding on desktop takes one attempt',
|
||||
{
|
||||
appSettings: {
|
||||
async ({ context, page, tronApp }) => {
|
||||
if (!tronApp) {
|
||||
fail()
|
||||
}
|
||||
|
||||
await tronApp.cleanProjectDir({
|
||||
app: {
|
||||
onboarding_status: 'dismissed',
|
||||
},
|
||||
},
|
||||
cleanProjectDir: true,
|
||||
},
|
||||
async ({ context, page }) => {
|
||||
})
|
||||
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
const routerTemplateDir = join(dir, 'router-template-slate')
|
||||
await fsp.mkdir(routerTemplateDir, { recursive: true })
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user