Compare commits
90 Commits
nrc-doc-si
...
pierremtb/
Author | SHA1 | Date | |
---|---|---|---|
cd672d52f6 | |||
597f4a1d54 | |||
c814c90a9e | |||
64832f9046 | |||
172e01529c | |||
5a4a32c044 | |||
b955184191 | |||
d7914219da | |||
ead4c1286b | |||
34494f3bba | |||
a0fe33260e | |||
8955b5fcd3 | |||
df6256266c | |||
5708b8c64b | |||
5b8284e737 | |||
dd9b0ec5f0 | |||
c467568ee4 | |||
e5d082f441 | |||
d4d3e179b1 | |||
cb976ec31b | |||
cc9eb65456 | |||
a589f56e73 | |||
4f4c44e7c7 | |||
1b75020686 | |||
fba62dab98 | |||
4c1564e2b0 | |||
a85a534d6b | |||
6ddbb7a31d | |||
051bb0589e | |||
7f9851ae28 | |||
fbcbb341e2 | |||
4a080d1583 | |||
85c721fb49 | |||
27af2d08a3 | |||
fb8b975b5e | |||
62d8d45a58 | |||
ae3440df0a | |||
af658c909d | |||
7ec11d23c8 | |||
30000a1eac | |||
cb3b45747c | |||
fe66310f2d | |||
fefb6cfe87 | |||
0f8375cbb4 | |||
107adc77b3 | |||
4356885aa2 | |||
6a2027cd51 | |||
f49cf8281c | |||
7de27c648f | |||
344fb6f84d | |||
df808b3e58 | |||
e1ab6bbc48 | |||
0a1f35b89a | |||
78278d6889 | |||
6f1a539e83 | |||
0ad619e1d2 | |||
8d876a806e | |||
c7f0a6c2a0 | |||
e4941cb524 | |||
1b687a82a6 | |||
1bb882acf8 | |||
478bf34f2b | |||
dbc87292e4 | |||
bb3a74076f | |||
0cd6031aae | |||
1e1bdbd6e7 | |||
631b63b1b6 | |||
eabcf86436 | |||
7ce0ef770a | |||
599ab33e40 | |||
c584d942d4 | |||
35b8872678 | |||
2f245fe445 | |||
4b95980e9e | |||
53d6613d0d | |||
416d0b37a2 | |||
5f2a10ec7e | |||
24edb66b3c | |||
903ba33c46 | |||
c5bf6ad42d | |||
eeaa71142a | |||
0d1fc1b513 | |||
d510e58ebc | |||
23a01e86e6 | |||
9dd6e3e852 | |||
9eaacc2a51 | |||
de6e0f6b18 | |||
d02a9f59ae | |||
92f930dfc0 | |||
e651e0c2cf |
@ -3,17 +3,17 @@
|
||||
NODE_ENV=development
|
||||
DEV=true
|
||||
|
||||
# App
|
||||
VITE_KC_API_WS_MODELING_URL=wss://api.dev.zoo.dev/ws/modeling/commands
|
||||
VITE_KC_API_BASE_URL=https://api.dev.zoo.dev
|
||||
VITE_KITTYCAD_API_BASE_URL=https://api.dev.zoo.dev
|
||||
VITE_KC_SITE_BASE_URL=https://dev.zoo.dev
|
||||
VITE_KC_SITE_APP_URL=https://app.dev.zoo.dev
|
||||
VITE_KC_SKIP_AUTH=false
|
||||
VITE_KC_CONNECTION_TIMEOUT_MS=5000
|
||||
#VITE_KC_DEV_TOKEN="optional token to skip auth in the app"
|
||||
#token="required token for playwright. TODO: clean up env vars in #3973"
|
||||
#VITE_WASM_URL="optional override of Wasm URL if not on default port 3000"
|
||||
#VITE_KITTYCAD_API_TOKEN="required for testing, optional to skip auth in the app"
|
||||
FAIL_ON_CONSOLE_ERRORS=true
|
||||
|
||||
# KCL
|
||||
RUST_BACKTRACE=1
|
||||
PYO3_PYTHON=/usr/local/bin/python3
|
||||
#KITTYCAD_API_TOKEN="required token for engine testing"
|
||||
|
||||
FAIL_ON_CONSOLE_ERRORS=true
|
||||
#KITTYCAD_API_TOKEN=$VITE_KITTYCAD_API_TOKEN
|
||||
|
@ -1,7 +1,8 @@
|
||||
NODE_ENV=production
|
||||
|
||||
# App
|
||||
VITE_KC_API_WS_MODELING_URL=wss://api.zoo.dev/ws/modeling/commands
|
||||
VITE_KC_API_BASE_URL=https://api.zoo.dev
|
||||
VITE_KITTYCAD_API_BASE_URL=https://api.zoo.dev
|
||||
VITE_KC_SITE_BASE_URL=https://zoo.dev
|
||||
VITE_KC_SITE_APP_URL=https://app.zoo.dev
|
||||
VITE_KC_SKIP_AUTH=false
|
||||
VITE_KC_CONNECTION_TIMEOUT_MS=15000
|
||||
|
11
.github/ISSUE_TEMPLATE/release.md
vendored
@ -2,7 +2,7 @@
|
||||
name: Release
|
||||
about: Create a new release for the Zoo Design Studio
|
||||
title: "Cut release v1.?.?"
|
||||
labels: [release]
|
||||
labels: [meta/release]
|
||||
---
|
||||
|
||||
> Instructions: https://github.com/KittyCAD/modeling-app/blob/main/CONTRIBUTING.md#shipping-releases
|
||||
@ -19,7 +19,8 @@ Release builds URL: ???
|
||||
* [ ] Confirm the application opens (dismiss the updater)
|
||||
* [ ] Create a project with a basic Text-to-CAD prompt
|
||||
* [ ] Confirm the result is viewable in an engine stream
|
||||
* [ ] Open the application again and confirm the updater can downgrade
|
||||
* [ ] Use 'Check for updates' to bring back the updater toast
|
||||
* [ ] Confirm the app can update to the previous release
|
||||
|
||||
## macOS via ???
|
||||
|
||||
@ -27,7 +28,8 @@ Release builds URL: ???
|
||||
* [ ] Confirm the application opens (dismiss the updater)
|
||||
* [ ] Create a project with a basic Text-to-CAD prompt
|
||||
* [ ] Confirm the result is viewable in an engine stream
|
||||
* [ ] Open the application again and confirm the updater can downgrade
|
||||
* [ ] Use 'Check for updates' to bring back the updater toast
|
||||
* [ ] Confirm the app can update to the previous release
|
||||
|
||||
## Linux via ???
|
||||
|
||||
@ -35,4 +37,5 @@ Release builds URL: ???
|
||||
* [ ] Confirm the application opens (dismiss the updater)
|
||||
* [ ] Create a project with a basic Text-to-CAD prompt
|
||||
* [ ] Confirm the result is viewable in an engine stream
|
||||
* [ ] Open the application again and confirm the updater can downgrade
|
||||
* [ ] Use 'Check for updates' to bring back the updater toast
|
||||
* [ ] Confirm the app can update to the previous release
|
||||
|
4
.github/workflows/build-apps.yml
vendored
@ -43,7 +43,7 @@ jobs:
|
||||
- name: Download Wasm Cache
|
||||
id: download-wasm
|
||||
if: ${{ github.event_name == 'pull_request' && steps.filter.outputs.rust == 'false' }}
|
||||
uses: dawidd6/action-download-artifact@v7
|
||||
uses: dawidd6/action-download-artifact@v11
|
||||
continue-on-error: true
|
||||
with:
|
||||
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||
@ -362,7 +362,7 @@ jobs:
|
||||
|
||||
- name: Authenticate to Google Cloud
|
||||
if: ${{ env.IS_STAGING == 'true' }}
|
||||
uses: 'google-github-actions/auth@v2.1.8'
|
||||
uses: 'google-github-actions/auth@v2.1.10'
|
||||
with:
|
||||
credentials_json: '${{ secrets.GOOGLE_CLOUD_DL_SA }}'
|
||||
|
||||
|
12
.github/workflows/cargo-test.yml
vendored
@ -25,8 +25,8 @@ jobs:
|
||||
- runner=8cpu-linux-x64
|
||||
- extras=s3-cache
|
||||
steps:
|
||||
- uses: runs-on/action@v1
|
||||
- uses: actions/create-github-app-token@v1
|
||||
- uses: runs-on/action@v2
|
||||
- uses: actions/create-github-app-token@v2
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ secrets.MODELING_APP_GH_APP_ID }}
|
||||
@ -149,8 +149,8 @@ jobs:
|
||||
partitionIndex: [1, 2, 3, 4, 5, 6]
|
||||
partitionTotal: [6]
|
||||
steps:
|
||||
- uses: runs-on/action@v1
|
||||
- uses: actions/create-github-app-token@v1
|
||||
- uses: runs-on/action@v2
|
||||
- uses: actions/create-github-app-token@v2
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ secrets.MODELING_APP_GH_APP_ID }}
|
||||
@ -207,8 +207,8 @@ jobs:
|
||||
- runner=32cpu-linux-x64
|
||||
- extras=s3-cache
|
||||
steps:
|
||||
- uses: runs-on/action@v1
|
||||
- uses: actions/create-github-app-token@v1
|
||||
- uses: runs-on/action@v2
|
||||
- uses: actions/create-github-app-token@v2
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ secrets.MODELING_APP_GH_APP_ID }}
|
||||
|
14
.github/workflows/e2e-tests.yml
vendored
@ -46,7 +46,7 @@ jobs:
|
||||
- name: Download Wasm cache
|
||||
id: download-wasm
|
||||
if: ${{ github.event_name != 'schedule' && steps.filter.outputs.rust == 'false' }}
|
||||
uses: dawidd6/action-download-artifact@v7
|
||||
uses: dawidd6/action-download-artifact@v11
|
||||
continue-on-error: true
|
||||
with:
|
||||
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||
@ -110,7 +110,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
|
||||
- uses: actions/create-github-app-token@v1
|
||||
- uses: actions/create-github-app-token@v2
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ secrets.MODELING_APP_GH_APP_ID }}
|
||||
@ -157,7 +157,7 @@ jobs:
|
||||
timeout_minutes: 5
|
||||
max_attempts: 5
|
||||
env:
|
||||
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
VITE_KITTYCAD_API_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
TAB_API_URL: ${{ secrets.TAB_API_URL }}
|
||||
TAB_API_KEY: ${{ secrets.TAB_API_KEY }}
|
||||
CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
|
||||
@ -169,7 +169,7 @@ jobs:
|
||||
if: always()
|
||||
run: npm run test:snapshots -- --last-failed --update-snapshots
|
||||
env:
|
||||
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
VITE_KITTYCAD_API_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
TAB_API_URL: ${{ secrets.TAB_API_URL }}
|
||||
TAB_API_KEY: ${{ secrets.TAB_API_KEY }}
|
||||
CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
|
||||
@ -230,7 +230,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
|
||||
- uses: actions/create-github-app-token@v1
|
||||
- uses: actions/create-github-app-token@v2
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ secrets.MODELING_APP_GH_APP_ID }}
|
||||
@ -284,7 +284,7 @@ jobs:
|
||||
timeout_minutes: 5
|
||||
max_attempts: 5
|
||||
env:
|
||||
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
VITE_KITTYCAD_API_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
TAB_API_URL: ${{ secrets.TAB_API_URL }}
|
||||
TAB_API_KEY: ${{ secrets.TAB_API_KEY }}
|
||||
CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
|
||||
@ -410,7 +410,7 @@ jobs:
|
||||
max_attempts: 9
|
||||
env:
|
||||
FAIL_ON_CONSOLE_ERRORS: true
|
||||
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
VITE_KITTYCAD_API_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
TAB_API_URL: ${{ secrets.TAB_API_URL }}
|
||||
TAB_API_KEY: ${{ secrets.TAB_API_KEY }}
|
||||
CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
|
||||
|
2
.github/workflows/generate-website-docs.yml
vendored
@ -20,7 +20,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/create-github-app-token@v1
|
||||
- uses: actions/create-github-app-token@v2
|
||||
id: app-token
|
||||
with:
|
||||
# required
|
||||
|
2
.github/workflows/kcl-language-server.yml
vendored
@ -328,7 +328,7 @@ jobs:
|
||||
mkdir -p releases/language-server/${{ env.TAG }}
|
||||
cp -r build/* releases/language-server/${{ env.TAG }}
|
||||
- name: "Authenticate to Google Cloud"
|
||||
uses: "google-github-actions/auth@v2.1.8"
|
||||
uses: "google-github-actions/auth@v2.1.10"
|
||||
with:
|
||||
credentials_json: "${{ secrets.GOOGLE_CLOUD_DL_SA }}"
|
||||
- name: Set up Cloud SDK
|
||||
|
6
.github/workflows/kcl-python-bindings.yml
vendored
@ -113,7 +113,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v5
|
||||
uses: astral-sh/setup-uv@v6
|
||||
- uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
- uses: taiki-e/install-action@just
|
||||
- name: Run tests
|
||||
@ -130,7 +130,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install the latest version of uv
|
||||
uses: astral-sh/setup-uv@v5
|
||||
uses: astral-sh/setup-uv@v6
|
||||
- name: Install codespell
|
||||
run: |
|
||||
uv venv .venv
|
||||
@ -161,7 +161,7 @@ jobs:
|
||||
with:
|
||||
path: rust/kcl-python-bindings
|
||||
- name: Install the latest version of uv
|
||||
uses: astral-sh/setup-uv@v5
|
||||
uses: astral-sh/setup-uv@v6
|
||||
- name: do uv things
|
||||
run: |
|
||||
cd rust/kcl-python-bindings
|
||||
|
2
.github/workflows/publish-apps-release.yml
vendored
@ -108,7 +108,7 @@ jobs:
|
||||
run: npm run files:set-notes
|
||||
|
||||
- name: Authenticate to Google Cloud
|
||||
uses: 'google-github-actions/auth@v2.1.8'
|
||||
uses: 'google-github-actions/auth@v2.1.10'
|
||||
with:
|
||||
credentials_json: '${{ secrets.GOOGLE_CLOUD_DL_SA }}'
|
||||
|
||||
|
30
.github/workflows/static-analysis.yml
vendored
@ -120,6 +120,36 @@ jobs:
|
||||
|
||||
- run: npm run circular-deps:diff
|
||||
|
||||
npm-url-checker:
|
||||
runs-on: ubuntu-latest
|
||||
needs: npm-build-wasm
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'npm'
|
||||
- run: npm install
|
||||
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
|
||||
- name: Copy prepared wasm
|
||||
run: |
|
||||
ls -R prepared-wasm
|
||||
cp prepared-wasm/kcl_wasm_lib_bg.wasm public
|
||||
mkdir rust/kcl-wasm-lib/pkg
|
||||
cp prepared-wasm/kcl_wasm_lib* rust/kcl-wasm-lib/pkg
|
||||
|
||||
- name: Copy prepared ts-rs bindings
|
||||
run: |
|
||||
ls -R prepared-ts-rs-bindings
|
||||
mkdir rust/kcl-lib/bindings
|
||||
cp -r prepared-ts-rs-bindings/* rust/kcl-lib/bindings/
|
||||
|
||||
- run: npm run url-checker:diff
|
||||
|
||||
python-codespell:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
|
2
.github/workflows/unit-tests.yml
vendored
@ -62,7 +62,7 @@ jobs:
|
||||
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
|
||||
run: xvfb-run -a npm run test:unit
|
||||
env:
|
||||
VITE_KC_DEV_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
VITE_KITTYCAD_API_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
|
||||
- name: Check for changes
|
||||
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
|
||||
|
2
.gitignore
vendored
@ -87,4 +87,4 @@ venv
|
||||
|
||||
.vscode-test
|
||||
.biome/
|
||||
.million
|
||||
.million
|
@ -65,7 +65,7 @@ If you're not a Zoo employee you won't be able to access the dev environment, yo
|
||||
|
||||
### Development environment variables
|
||||
|
||||
The Copilot LSP plugin in the editor requires a Zoo API token to run. In production, we authenticate this with a token via cookie in the browser and device auth token in the desktop environment, but this token is inaccessible in the dev browser version because the cookie is considered "cross-site" (from `localhost` to `zoo.dev`). There is an optional environment variable called `VITE_KC_DEV_TOKEN` that you can populate with a dev token in a `.env.development.local` file to not check it into Git, which will use that token instead of other methods for the LSP service.
|
||||
The Copilot LSP plugin in the editor requires a Zoo API token to run. In production, we authenticate this with a token via cookie in the browser and device auth token in the desktop environment, but this token is inaccessible in the dev browser version because the cookie is considered "cross-site" (from `localhost` to `zoo.dev`). There is an optional environment variable called `VITE_KITTYCAD_API_TOKEN` that you can populate with a dev token in a `.env.development.local` file to not check it into Git, which will use that token instead of other methods for the LSP service.
|
||||
|
||||
### Developing in Chrome
|
||||
|
||||
@ -96,7 +96,7 @@ To package the app for your platform with electron-builder, run `npm run tronb:p
|
||||
|
||||
Prepare these system dependencies:
|
||||
|
||||
- Set $token from https://zoo.dev/account/api-tokens
|
||||
- Set `$VITE_KITTYCAD_API_TOKEN` from https://zoo.dev/account/api-tokens
|
||||
|
||||
#### Snapshot tests (Google Chrome on Ubuntu only)
|
||||
|
||||
@ -235,6 +235,47 @@ To display logging (to the terminal or console) set `ZOO_LOG=1`. This will log s
|
||||
|
||||
To enable memory metrics, build with `--features dhat-heap`.
|
||||
|
||||
## Running scripts
|
||||
|
||||
There are multiple scripts under the folder path `./scripts` which can be used in various settings.
|
||||
|
||||
### Pattern for a static file, npm run commands, and CI-CD checks
|
||||
|
||||
If you want to implement a static checker follow this pattern. Two static checkers we have are circular dependency checks in our typescript code and url checker to see if any hard coded URL is the typescript application 404s. We have a set of known files in `./scripts/known/*.txt` which is the baseline.
|
||||
|
||||
If you improve the baseline, run the overwrite command and commit the new smaller baseline. Try not to make the baseline bigger, the CI CD will complain.
|
||||
These baselines are to hold us to higher standards and help implement automated testing against the repository
|
||||
|
||||
#### Output result to stdout
|
||||
- `npm run circular-deps`
|
||||
- `npm run url-checker`
|
||||
|
||||
- create a `<name>.sh` file that will run the static checker then output the result to `stdout`
|
||||
|
||||
#### Overwrite result to known .txt file on disk
|
||||
|
||||
If the application needs to overwrite the known file on disk use this pattern. This known .txt file will be source controlled as the baseline
|
||||
|
||||
- `npm run circular-deps:overwrite`
|
||||
- `npm run url-checker:overwrite`
|
||||
|
||||
#### Diff baseline and current
|
||||
|
||||
These commands will write a /tmp/ file on disk and compare it to the known file in the repository. This command will also be used in the CI CD pipeline for automated checks
|
||||
|
||||
- create a `diff-<name>.sh` file that is the script to diff your tmp file to the baseline
|
||||
e.g. `diff-url-checker.sh`
|
||||
```bash
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
npm run url-checker > /tmp/urls.txt
|
||||
diff --ignore-blank-lines -w /tmp/urls.txt ./scripts/known/urls.txt
|
||||
```
|
||||
|
||||
- `npm run circular-deps:diff`
|
||||
- `npm run url-checker:diff`
|
||||
|
||||
## Proposing changes
|
||||
|
||||
Before you submit a contribution PR to this repo, please ensure that:
|
||||
@ -280,6 +321,9 @@ Assign someone to each section of the manual checklist generated by the issue te
|
||||
Follow the instructions [here](./rust/README.md) to publish new crates.
|
||||
This ensures that the KCL accepted by the app is also accepted by the CLI.
|
||||
|
||||
If there are documentation changes, merge the corresponding Dependabot PRs [here](https://github.com/KittyCAD/website/pulls/app%2Fdependabot) for the website.
|
||||
You can trigger Dependabot to check for updates [here](https://github.com/KittyCAD/website/network/updates/17261214/jobs).
|
||||
|
||||
#### 5. Publish the release
|
||||
|
||||
Head over to https://github.com/KittyCAD/modeling-app/releases/new, pick the newly created tag and type it in the **Release title** field as well.
|
||||
|
5
Makefile
@ -49,7 +49,7 @@ RUST_SOURCES := $(wildcard rust/**/*.rs)
|
||||
|
||||
REACT_SOURCES := $(wildcard src/*.tsx) $(wildcard src/**/*.tsx)
|
||||
TYPESCRIPT_SOURCES := tsconfig.* $(wildcard src/*.ts) $(wildcard src/**/*.ts)
|
||||
VITE_SOURCES := $(wildcard vite.*) $(wildcard vite/**/*.tsx)
|
||||
VITE_SOURCES := $(wildcard vite.*) $(wildcard vite/**/*.tsx) .env*
|
||||
|
||||
.PHONY: build
|
||||
build: install public/kcl_wasm_lib_bg.wasm public/kcl-samples/manifest.json .vite/build/main.js
|
||||
@ -62,7 +62,10 @@ else
|
||||
endif
|
||||
|
||||
public/kcl-samples/manifest.json: $(KCL_SOURCES)
|
||||
ifndef WINDOWS
|
||||
cd rust/kcl-lib && EXPECTORATE=overwrite cargo test generate_manifest
|
||||
@ touch $@
|
||||
endif
|
||||
|
||||
.vite/build/main.js: $(REACT_SOURCES) $(TYPESCRIPT_SOURCES) $(VITE_SOURCES)
|
||||
npm run tronb:vite:dev
|
||||
|
@ -83,6 +83,13 @@ Allow orbiting in sketch mode.
|
||||
Whether to show the debug panel, which lets you see various states of the app to aid in development.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
##### fixed_size_grid
|
||||
|
||||
If true, the grid cells will be fixed-size, where the width is your default length unit. If false, the grid will get larger as you zoom out, and smaller as you zoom in.
|
||||
|
||||
|
||||
**Default:** None
|
||||
|
||||
|
||||
|
@ -10,9 +10,11 @@ Extend the current sketch with a new involute circular curve.
|
||||
```kcl
|
||||
involuteCircular(
|
||||
@sketch: Sketch,
|
||||
startRadius: number(Length),
|
||||
endRadius: number(Length),
|
||||
angle: number(Angle),
|
||||
startRadius?: number(Length),
|
||||
endRadius?: number(Length),
|
||||
startDiameter?: number(Length),
|
||||
endDiameter?: number(Length),
|
||||
reverse?: bool,
|
||||
tag?: TagDecl,
|
||||
): Sketch
|
||||
@ -25,9 +27,11 @@ involuteCircular(
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `sketch` | [`Sketch`](/docs/kcl-std/types/std-types-Sketch) | Which sketch should this path be added to? | Yes |
|
||||
| `startRadius` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The involute is described between two circles, start_radius is the radius of the inner circle. | Yes |
|
||||
| `endRadius` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The involute is described between two circles, end_radius is the radius of the outer circle. | Yes |
|
||||
| `angle` | [`number(Angle)`](/docs/kcl-std/types/std-types-number) | The angle to rotate the involute by. A value of zero will produce a curve with a tangent along the x-axis at the start point of the curve. | Yes |
|
||||
| `startRadius` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The involute is described between two circles, startRadius is the radius of the inner circle. Either `startRadius` or `startDiameter` must be given (but not both). | No |
|
||||
| `endRadius` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The involute is described between two circles, endRadius is the radius of the outer circle. Either `endRadius` or `endDiameter` must be given (but not both). | No |
|
||||
| `startDiameter` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The involute is described between two circles, startDiameter describes the inner circle. Either `startRadius` or `startDiameter` must be given (but not both). | No |
|
||||
| `endDiameter` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The involute is described between two circles, endDiameter describes the outer circle. Either `endRadius` or `endDiameter` must be given (but not both). | No |
|
||||
| `reverse` | [`bool`](/docs/kcl-std/types/std-types-bool) | If reverse is true, the segment will start from the end of the involute, otherwise it will start from that start. | No |
|
||||
| `tag` | [`TagDecl`](/docs/kcl-std/types/std-types-TagDecl) | Create a new tag which refers to this line. | No |
|
||||
|
||||
|
48
docs/kcl-std/functions/std-sketch-planeOf.md
Normal file
55
docs/kcl-std/functions/std-sketch-rectangle.md
Normal file
@ -38,7 +38,7 @@ revolved around the same axis.
|
||||
| `sketches` | [`[Sketch; 1+]`](/docs/kcl-std/types/std-types-Sketch) | The sketch or set of sketches that should be revolved | Yes |
|
||||
| `axis` | [`Axis2d`](/docs/kcl-std/types/std-types-Axis2d) or [`Edge`](/docs/kcl-std/types/std-types-Edge) | Axis of revolution. | Yes |
|
||||
| `angle` | [`number(Angle)`](/docs/kcl-std/types/std-types-number) | Angle to revolve (in degrees). Default is 360. | No |
|
||||
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | Tolerance for the revolve operation. | No |
|
||||
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | Defines the smallest distance below which two entities are considered coincident, intersecting, coplanar, or similar. For most use cases, it should not be changed from its default value of 10^-7 millimeters. | No |
|
||||
| `symmetric` | [`bool`](/docs/kcl-std/types/std-types-bool) | If true, the extrusion will happen symmetrically around the sketch. Otherwise, the extrusion will happen on only one side of the sketch. | No |
|
||||
| `bidirectionalAngle` | [`number(Angle)`](/docs/kcl-std/types/std-types-number) | If specified, will also revolve in the opposite direction to 'angle' to the specified angle. If 'symmetric' is true, this value is ignored. | No |
|
||||
| `tagStart` | [`TagDecl`](/docs/kcl-std/types/std-types-TagDecl) | A named tag for the face at the start of the revolve, i.e. the original sketch. | No |
|
||||
|
@ -28,7 +28,7 @@ will smoothly blend the transition.
|
||||
| `solid` | [`Solid`](/docs/kcl-std/types/std-types-Solid) | The solid whose edges should be filletted | Yes |
|
||||
| `radius` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The radius of the fillet | Yes |
|
||||
| `tags` | [`[Edge; 1+]`](/docs/kcl-std/types/std-types-Edge) | The paths you want to fillet | Yes |
|
||||
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The tolerance for this fillet | No |
|
||||
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | Defines the smallest distance below which two entities are considered coincident, intersecting, coplanar, or similar. For most use cases, it should not be changed from its default value of 10^-7 millimeters. | No |
|
||||
| `tag` | [`TagDecl`](/docs/kcl-std/types/std-types-TagDecl) | Create a new tag which refers to this fillet | No |
|
||||
|
||||
### Returns
|
||||
|
@ -24,7 +24,7 @@ verifying fit, and analyzing overlapping geometries in assemblies.
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `solids` | `[Solid; 2+]` | The solids to intersect. | Yes |
|
||||
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The tolerance to use for the intersection operation. | No |
|
||||
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | Defines the smallest distance below which two entities are considered coincident, intersecting, coplanar, or similar. For most use cases, it should not be changed from its default value of 10^-7 millimeters. | No |
|
||||
|
||||
### Returns
|
||||
|
||||
|
@ -27,7 +27,7 @@ and complex multi-body part modeling.
|
||||
|----------|------|-------------|----------|
|
||||
| `solids` | [`[Solid; 1+]`](/docs/kcl-std/types/std-types-Solid) | The solids to use as the base to subtract from. | Yes |
|
||||
| `tools` | [`[Solid]`](/docs/kcl-std/types/std-types-Solid) | The solids to subtract. | Yes |
|
||||
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The tolerance to use for the subtraction operation. | No |
|
||||
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | Defines the smallest distance below which two entities are considered coincident, intersecting, coplanar, or similar. For most use cases, it should not be changed from its default value of 10^-7 millimeters. | No |
|
||||
|
||||
### Returns
|
||||
|
||||
|
@ -21,7 +21,7 @@ union(
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `solids` | `[Solid; 2+]` | The solids to union. | Yes |
|
||||
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The tolerance to use for the union operation. | No |
|
||||
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | Defines the smallest distance below which two entities are considered coincident, intersecting, coplanar, or similar. For most use cases, it should not be changed from its default value of 10^-7 millimeters. | No |
|
||||
|
||||
### Returns
|
||||
|
||||
|
@ -67,10 +67,12 @@ layout: manual
|
||||
* [`patternCircular2d`](/docs/kcl-std/functions/std-sketch-patternCircular2d)
|
||||
* [`patternLinear2d`](/docs/kcl-std/functions/std-sketch-patternLinear2d)
|
||||
* [`patternTransform2d`](/docs/kcl-std/functions/std-sketch-patternTransform2d)
|
||||
* [`planeOf`](/docs/kcl-std/functions/std-sketch-planeOf)
|
||||
* [`polygon`](/docs/kcl-std/functions/std-sketch-polygon)
|
||||
* [`profileStart`](/docs/kcl-std/functions/std-sketch-profileStart)
|
||||
* [`profileStartX`](/docs/kcl-std/functions/std-sketch-profileStartX)
|
||||
* [`profileStartY`](/docs/kcl-std/functions/std-sketch-profileStartY)
|
||||
* [`rectangle`](/docs/kcl-std/functions/std-sketch-rectangle)
|
||||
* [`revolve`](/docs/kcl-std/functions/std-sketch-revolve)
|
||||
* [`segAng`](/docs/kcl-std/functions/std-sketch-segAng)
|
||||
* [`segEnd`](/docs/kcl-std/functions/std-sketch-segEnd)
|
||||
|
@ -32,10 +32,12 @@ This module contains functions for creating and manipulating sketches, and makin
|
||||
* [`patternCircular2d`](/docs/kcl-std/functions/std-sketch-patternCircular2d)
|
||||
* [`patternLinear2d`](/docs/kcl-std/functions/std-sketch-patternLinear2d)
|
||||
* [`patternTransform2d`](/docs/kcl-std/functions/std-sketch-patternTransform2d)
|
||||
* [`planeOf`](/docs/kcl-std/functions/std-sketch-planeOf)
|
||||
* [`polygon`](/docs/kcl-std/functions/std-sketch-polygon)
|
||||
* [`profileStart`](/docs/kcl-std/functions/std-sketch-profileStart)
|
||||
* [`profileStartX`](/docs/kcl-std/functions/std-sketch-profileStartX)
|
||||
* [`profileStartY`](/docs/kcl-std/functions/std-sketch-profileStartY)
|
||||
* [`rectangle`](/docs/kcl-std/functions/std-sketch-rectangle)
|
||||
* [`revolve`](/docs/kcl-std/functions/std-sketch-revolve)
|
||||
* [`segAng`](/docs/kcl-std/functions/std-sketch-segAng)
|
||||
* [`segEnd`](/docs/kcl-std/functions/std-sketch-segEnd)
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
|
||||
// test file is for testing auth functionality
|
||||
test.describe('Authentication tests', () => {
|
||||
test(
|
||||
`The user can sign out and back in`,
|
||||
@ -13,22 +12,12 @@ test.describe('Authentication tests', () => {
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
await homePage.projectSection.waitFor()
|
||||
|
||||
// This is only needed as an override to test-utils' setup() for this test
|
||||
await page.addInitScript(() => {
|
||||
localStorage.setItem('TOKEN_PERSIST_KEY', '')
|
||||
})
|
||||
|
||||
await test.step('Click on sign out and expect sign in page', async () => {
|
||||
await toolbar.userSidebarButton.click()
|
||||
await toolbar.signOutButton.click()
|
||||
await expect(signInPage.signInButton).toBeVisible()
|
||||
})
|
||||
|
||||
await test.step("Refresh doesn't log the user back in", async () => {
|
||||
await page.reload()
|
||||
await expect(signInPage.signInButton).toBeVisible()
|
||||
})
|
||||
|
||||
await test.step('Click on sign in and cancel, click again and expect different code', async () => {
|
||||
await signInPage.signInButton.click()
|
||||
await expect(signInPage.userCode).toBeVisible()
|
||||
|
@ -12,7 +12,7 @@ test.describe('Point and click for boolean workflows', () => {
|
||||
},
|
||||
{
|
||||
name: 'subtract',
|
||||
code: 'subtract([extrude001], tools = [extrude006])',
|
||||
code: 'subtract(extrude001, tools = extrude006)',
|
||||
},
|
||||
{
|
||||
name: 'intersect',
|
||||
@ -81,6 +81,8 @@ test.describe('Point and click for boolean workflows', () => {
|
||||
if (operationName !== 'subtract') {
|
||||
// should down shift key to select multiple objects
|
||||
await page.keyboard.down('Shift')
|
||||
} else {
|
||||
await cmdBar.progressCmdBar()
|
||||
}
|
||||
|
||||
// Select second object
|
||||
@ -103,8 +105,8 @@ test.describe('Point and click for boolean workflows', () => {
|
||||
await cmdBar.expectState({
|
||||
stage: 'review',
|
||||
headerArguments: {
|
||||
Tool: '1 path',
|
||||
Target: '1 path',
|
||||
Solids: '1 path',
|
||||
Tools: '1 path',
|
||||
},
|
||||
commandName,
|
||||
})
|
||||
|
@ -5,6 +5,7 @@ import { uuidv4 } from '@src/lib/utils'
|
||||
import type { HomePageFixture } from '@e2e/playwright/fixtures/homePageFixture'
|
||||
import type { SceneFixture } from '@e2e/playwright/fixtures/sceneFixture'
|
||||
import type { ToolbarFixture } from '@e2e/playwright/fixtures/toolbarFixture'
|
||||
import type { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture'
|
||||
import { getUtils } from '@e2e/playwright/test-utils'
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
|
||||
@ -14,13 +15,18 @@ test.describe('Can create sketches on all planes and their back sides', () => {
|
||||
homePage: HomePageFixture,
|
||||
scene: SceneFixture,
|
||||
toolbar: ToolbarFixture,
|
||||
cmdBar: CmdBarFixture,
|
||||
plane: string,
|
||||
clickCoords: { x: number; y: number }
|
||||
) => {
|
||||
const u = await getUtils(page)
|
||||
// await page.addInitScript(() => {
|
||||
// localStorage.setItem('persistCode', '@settings(defaultLengthUnit = in)')
|
||||
// })
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
// await scene.settled(cmdBar)
|
||||
const XYPlanRed: [number, number, number] = [98, 50, 51]
|
||||
await scene.expectPixelColor(XYPlanRed, { x: 700, y: 300 }, 15)
|
||||
|
||||
@ -119,12 +125,166 @@ test.describe('Can create sketches on all planes and their back sides', () => {
|
||||
]
|
||||
|
||||
for (const config of planeConfigs) {
|
||||
test(config.plane, async ({ page, homePage, scene, toolbar }) => {
|
||||
test(config.plane, async ({ page, homePage, scene, toolbar, cmdBar }) => {
|
||||
await sketchOnPlaneAndBackSideTest(
|
||||
page,
|
||||
homePage,
|
||||
scene,
|
||||
toolbar,
|
||||
cmdBar,
|
||||
config.plane,
|
||||
config.coords
|
||||
)
|
||||
})
|
||||
}
|
||||
})
|
||||
test.describe('Can create sketches on offset planes and their back sides', () => {
|
||||
const sketchOnPlaneAndBackSideTest = async (
|
||||
page: Page,
|
||||
homePage: HomePageFixture,
|
||||
scene: SceneFixture,
|
||||
toolbar: ToolbarFixture,
|
||||
cmdbar: CmdBarFixture,
|
||||
plane: string,
|
||||
clickCoords: { x: number; y: number }
|
||||
) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(() => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`@settings(defaultLengthUnit = in)
|
||||
xyPlane = offsetPlane(XY, offset = 0.05)
|
||||
xzPlane = offsetPlane(XZ, offset = 0.05)
|
||||
yzPlane = offsetPlane(YZ, offset = 0.05)
|
||||
`
|
||||
)
|
||||
})
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
// await scene.settled(cmdbar)
|
||||
const XYPlanRed: [number, number, number] = [74, 74, 74]
|
||||
await scene.expectPixelColor(XYPlanRed, { x: 700, y: 300 }, 15)
|
||||
|
||||
await u.openDebugPanel()
|
||||
|
||||
const coord =
|
||||
plane === '-XY' || plane === '-YZ' || plane === 'XZ' ? -100 : 100
|
||||
const camCommand: EngineCommand = {
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'default_camera_look_at',
|
||||
center: { x: 0, y: 0, z: 0 },
|
||||
vantage: { x: coord, y: coord, z: coord },
|
||||
up: { x: 0, y: 0, z: 1 },
|
||||
},
|
||||
}
|
||||
const updateCamCommand: EngineCommand = {
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'default_camera_get_settings',
|
||||
},
|
||||
}
|
||||
|
||||
const prefix = plane.length === 3 ? '-' : ''
|
||||
const planeName = plane
|
||||
.slice(plane.length === 3 ? 1 : 0)
|
||||
.toLocaleLowerCase()
|
||||
|
||||
const codeLine1 = `sketch001 = startSketchOn(${prefix}${planeName}Plane)`
|
||||
const codeLine2 = `profile001 = startProfile(sketch001, at = [${0.91 + (plane[0] === '-' ? 0.01 : 0)}, -${1.21 + (plane[0] === '-' ? 0.01 : 0)}])`
|
||||
|
||||
await u.openDebugPanel()
|
||||
|
||||
await u.clearCommandLogs()
|
||||
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||
|
||||
await u.sendCustomCmd(camCommand)
|
||||
await page.waitForTimeout(100)
|
||||
await u.sendCustomCmd(updateCamCommand)
|
||||
|
||||
await u.closeDebugPanel()
|
||||
|
||||
await toolbar.openFeatureTreePane()
|
||||
await toolbar.getDefaultPlaneVisibilityButton('XY').click()
|
||||
await toolbar.getDefaultPlaneVisibilityButton('XZ').click()
|
||||
await toolbar.getDefaultPlaneVisibilityButton('YZ').click()
|
||||
await expect(
|
||||
toolbar
|
||||
.getDefaultPlaneVisibilityButton('YZ')
|
||||
.locator('[aria-label="eye crossed out"]')
|
||||
).toBeVisible()
|
||||
|
||||
await page.mouse.click(clickCoords.x, clickCoords.y)
|
||||
await page.waitForTimeout(600) // wait for animation
|
||||
|
||||
await toolbar.waitUntilSketchingReady()
|
||||
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'line Line', exact: true })
|
||||
).toBeVisible()
|
||||
|
||||
await u.closeDebugPanel()
|
||||
await page.mouse.click(707, 393)
|
||||
|
||||
await expect(page.locator('.cm-content')).toContainText(codeLine1)
|
||||
await expect(page.locator('.cm-content')).toContainText(codeLine2)
|
||||
|
||||
await page
|
||||
.getByRole('button', { name: 'line Line', exact: true })
|
||||
.first()
|
||||
.click()
|
||||
await u.openAndClearDebugPanel()
|
||||
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
|
||||
await u.clearCommandLogs()
|
||||
await u.removeCurrentCode()
|
||||
}
|
||||
|
||||
const planeConfigs = [
|
||||
{
|
||||
plane: 'XY',
|
||||
coords: { x: 600, y: 388 },
|
||||
description: 'red plane',
|
||||
},
|
||||
{
|
||||
plane: 'YZ',
|
||||
coords: { x: 700, y: 250 },
|
||||
description: 'green plane',
|
||||
},
|
||||
{
|
||||
plane: 'XZ',
|
||||
coords: { x: 684, y: 427 },
|
||||
description: 'blue plane',
|
||||
},
|
||||
{
|
||||
plane: '-XY',
|
||||
coords: { x: 600, y: 118 },
|
||||
description: 'back of red plane',
|
||||
},
|
||||
{
|
||||
plane: '-YZ',
|
||||
coords: { x: 700, y: 219 },
|
||||
description: 'back of green plane',
|
||||
},
|
||||
{
|
||||
plane: '-XZ',
|
||||
coords: { x: 700, y: 80 },
|
||||
description: 'back of blue plane',
|
||||
},
|
||||
]
|
||||
|
||||
for (const config of planeConfigs) {
|
||||
test(config.plane, async ({ page, homePage, scene, toolbar, cmdBar }) => {
|
||||
await sketchOnPlaneAndBackSideTest(
|
||||
page,
|
||||
homePage,
|
||||
scene,
|
||||
toolbar,
|
||||
cmdBar,
|
||||
config.plane,
|
||||
config.coords
|
||||
)
|
||||
|
@ -265,6 +265,8 @@ middle(0)
|
||||
})
|
||||
await expect(
|
||||
page.getByText(`assert failed: Expected 0 to be greater than 0 but it wasn't
|
||||
|
||||
Backtrace:
|
||||
assert()
|
||||
check()
|
||||
middle()`)
|
||||
|
@ -307,7 +307,7 @@ test.describe('Command bar tests', () => {
|
||||
)
|
||||
|
||||
const continueButton = page.getByRole('button', { name: 'Continue' })
|
||||
const submitButton = page.getByRole('button', { name: 'Submit command' })
|
||||
const submitButton = page.getByTestId('command-bar-submit')
|
||||
await continueButton.click()
|
||||
|
||||
// Review step and argument hotkeys
|
||||
@ -525,7 +525,9 @@ test.describe('Command bar tests', () => {
|
||||
const projectName = 'test'
|
||||
const beforeKclCode = `a = 5
|
||||
b = a * a
|
||||
c = 3 + a`
|
||||
c = 3 + a
|
||||
theta = 45deg
|
||||
`
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
const testProject = join(dir, projectName)
|
||||
await fsp.mkdir(testProject, { recursive: true })
|
||||
@ -615,9 +617,45 @@ c = 3 + a`
|
||||
stage: 'commandBarClosed',
|
||||
})
|
||||
})
|
||||
await test.step(`Edit a parameter with explicit units via command bar`, async () => {
|
||||
await cmdBar.cmdBarOpenBtn.click()
|
||||
await cmdBar.chooseCommand('edit parameter')
|
||||
await cmdBar
|
||||
.selectOption({
|
||||
name: 'theta',
|
||||
})
|
||||
.click()
|
||||
await cmdBar.expectState({
|
||||
stage: 'arguments',
|
||||
commandName: 'Edit parameter',
|
||||
currentArgKey: 'value',
|
||||
currentArgValue: '45deg',
|
||||
headerArguments: {
|
||||
Name: 'theta',
|
||||
Value: '',
|
||||
},
|
||||
highlightedHeaderArg: 'value',
|
||||
})
|
||||
await cmdBar.argumentInput
|
||||
.locator('[contenteditable]')
|
||||
.fill('45deg + 1deg')
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.expectState({
|
||||
stage: 'review',
|
||||
commandName: 'Edit parameter',
|
||||
headerArguments: {
|
||||
Name: 'theta',
|
||||
Value: '46deg',
|
||||
},
|
||||
})
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.expectState({
|
||||
stage: 'commandBarClosed',
|
||||
})
|
||||
})
|
||||
|
||||
await editor.expectEditor.toContain(
|
||||
`a = 5b = a * amyParameter001 = ${newValue}c = 3 + a`
|
||||
`a = 5b = a * amyParameter001 = ${newValue}c = 3 + atheta = 45deg + 1deg`
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -54,9 +54,7 @@ test(
|
||||
await page.keyboard.press('Enter')
|
||||
|
||||
// Click the checkbox
|
||||
const submitButton = page.getByText('Confirm Export')
|
||||
await expect(submitButton).toBeVisible()
|
||||
await page.keyboard.press('Enter')
|
||||
await cmdBar.submit()
|
||||
|
||||
// Expect it to succeed
|
||||
const errorToastMessage = page.getByText(`Error while exporting`)
|
||||
@ -119,9 +117,7 @@ test(
|
||||
await page.keyboard.press('Enter')
|
||||
|
||||
// Click the checkbox
|
||||
const submitButton = page.getByText('Confirm Export')
|
||||
await expect(submitButton).toBeVisible()
|
||||
await page.keyboard.press('Enter')
|
||||
await cmdBar.submit()
|
||||
|
||||
// Look out for the toast message
|
||||
const exportingToastMessage = page.getByText(`Exporting...`)
|
||||
|
@ -288,7 +288,7 @@ a1 = startSketchOn(offsetPlane(XY, offset = 10))
|
||||
// error text on hover
|
||||
await page.hover('.cm-lint-marker-info')
|
||||
await expect(
|
||||
page.getByText('Identifiers must be lowerCamelCase').first()
|
||||
page.getByText('Identifiers should be lowerCamelCase').first()
|
||||
).toBeVisible()
|
||||
|
||||
await page.locator('#code-pane button:first-child').click()
|
||||
@ -314,7 +314,7 @@ sketch_001 = startSketchOn(XY)
|
||||
// error text on hover
|
||||
await page.hover('.cm-lint-marker-info')
|
||||
await expect(
|
||||
page.getByText('Identifiers must be lowerCamelCase').first()
|
||||
page.getByText('Identifiers should be lowerCamelCase').first()
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
@ -511,7 +511,7 @@ sketch_001 = startSketchOn(XY)
|
||||
// error text on hover
|
||||
await page.hover('.cm-lint-marker-info')
|
||||
await expect(
|
||||
page.getByText('Identifiers must be lowerCamelCase').first()
|
||||
page.getByText('Identifiers should be lowerCamelCase').first()
|
||||
).toBeVisible()
|
||||
|
||||
// focus the editor
|
||||
@ -539,7 +539,7 @@ sketch_001 = startSketchOn(XY)
|
||||
// error text on hover
|
||||
await page.hover('.cm-lint-marker-info')
|
||||
await expect(
|
||||
page.getByText('Identifiers must be lowerCamelCase').first()
|
||||
page.getByText('Identifiers should be lowerCamelCase').first()
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
@ -681,7 +681,7 @@ a1 = startSketchOn(offsetPlane(XY, offset = 10))
|
||||
// error text on hover
|
||||
await page.hover('.cm-lint-marker-info')
|
||||
await expect(
|
||||
page.getByText('Identifiers must be lowerCamelCase').first()
|
||||
page.getByText('Identifiers should be lowerCamelCase').first()
|
||||
).toBeVisible()
|
||||
|
||||
// select the line that's causing the error and delete it
|
||||
@ -1617,4 +1617,33 @@ sketch001 = startSketchOn(XZ)
|
||||
// Verify error is still visible
|
||||
await expect(page.locator('.cm-lint-marker-error')).toHaveCount(1)
|
||||
})
|
||||
|
||||
test('Core dump hotkey', async ({ page, scene, cmdBar, homePage }) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
profile001 = circle(sketch001, center = [-100.0, -100.0], radius = 50.0)
|
||||
`
|
||||
)
|
||||
})
|
||||
|
||||
const viewportSize = { width: 1200, height: 800 }
|
||||
await page.setBodyDimensions(viewportSize)
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await scene.connectionEstablished()
|
||||
await scene.settled(cmdBar)
|
||||
|
||||
const modifier = process.platform === 'darwin' ? 'Meta' : 'Control'
|
||||
|
||||
await page.keyboard.press(`${modifier}+Shift+.`)
|
||||
|
||||
const toast1 = page.getByText('Starting core dump...')
|
||||
await expect(toast1).toBeVisible()
|
||||
|
||||
const toast2 = page.getByText('Core dump completed')
|
||||
await expect(toast2).toBeVisible()
|
||||
})
|
||||
})
|
||||
|
@ -229,11 +229,12 @@ test.describe('Feature Tree pane', () => {
|
||||
const initialCode = `sketch001 = startSketchOn(XZ)
|
||||
|> circle(center = [0, 0], radius = 5)
|
||||
renamedExtrude = extrude(sketch001, length = ${initialInput})`
|
||||
const newConstantName = 'length001'
|
||||
const expectedCode = `${newConstantName} = 23
|
||||
const newParameterName = 'length001'
|
||||
const expectedCode = `${newParameterName} = 23
|
||||
sketch001 = startSketchOn(XZ)
|
||||
|> circle(center = [0, 0], radius = 5)
|
||||
renamedExtrude = extrude(sketch001, length = ${newConstantName})`
|
||||
renamedExtrude = extrude(sketch001, length = ${newParameterName})`
|
||||
const editedParameterValue = '23 * 2'
|
||||
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
const testDir = join(dir, 'test-sample')
|
||||
@ -279,7 +280,7 @@ test.describe('Feature Tree pane', () => {
|
||||
})
|
||||
})
|
||||
|
||||
await test.step('Add a named constant for distance argument and submit', async () => {
|
||||
await test.step('Add a parameter for distance argument and submit', async () => {
|
||||
await expect(cmdBar.currentArgumentInput).toBeVisible()
|
||||
await cmdBar.variableCheckbox.click()
|
||||
await cmdBar.progressCmdBar()
|
||||
@ -296,13 +297,43 @@ test.describe('Feature Tree pane', () => {
|
||||
highlightedCode: '',
|
||||
diagnostics: [],
|
||||
activeLines: [
|
||||
`renamedExtrude = extrude(sketch001, length = ${newConstantName})`,
|
||||
`renamedExtrude = extrude(sketch001, length = ${newParameterName})`,
|
||||
],
|
||||
})
|
||||
await editor.expectEditor.toContain(expectedCode, {
|
||||
shouldNormalise: true,
|
||||
})
|
||||
})
|
||||
|
||||
await test.step('Edit the parameter via the feature tree', async () => {
|
||||
const parameter = await toolbar.getFeatureTreeOperation('Parameter', 0)
|
||||
await parameter.dblclick()
|
||||
await cmdBar.expectState({
|
||||
commandName: 'Edit parameter',
|
||||
currentArgKey: 'value',
|
||||
currentArgValue: '23',
|
||||
headerArguments: {
|
||||
Name: newParameterName,
|
||||
Value: '23',
|
||||
},
|
||||
stage: 'arguments',
|
||||
highlightedHeaderArg: 'value',
|
||||
})
|
||||
await cmdBar.argumentInput
|
||||
.locator('[contenteditable]')
|
||||
.fill(editedParameterValue)
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.expectState({
|
||||
stage: 'review',
|
||||
commandName: 'Edit parameter',
|
||||
headerArguments: {
|
||||
Name: newParameterName,
|
||||
Value: '46', // Shows calculated result
|
||||
},
|
||||
})
|
||||
await cmdBar.progressCmdBar()
|
||||
await editor.expectEditor.toContain(editedParameterValue)
|
||||
})
|
||||
})
|
||||
test(`User can edit an offset plane operation from the feature tree`, async ({
|
||||
context,
|
||||
|
@ -118,15 +118,11 @@ export class CmdBarFixture {
|
||||
return
|
||||
}
|
||||
|
||||
const arrowButton = this.page.getByRole('button', {
|
||||
name: 'arrow right Continue',
|
||||
})
|
||||
const arrowButton = this.page.getByTestId('command-bar-continue')
|
||||
if (await arrowButton.isVisible()) {
|
||||
await arrowButton.click()
|
||||
await this.continue()
|
||||
} else {
|
||||
await this.page
|
||||
.getByRole('button', { name: 'checkmark Submit command' })
|
||||
.click()
|
||||
await this.submit()
|
||||
}
|
||||
}
|
||||
|
||||
@ -191,6 +187,13 @@ export class CmdBarFixture {
|
||||
return this.page.getByRole('option', options)
|
||||
}
|
||||
|
||||
/**
|
||||
* Select an optional argument from the command bar during review
|
||||
*/
|
||||
clickOptionalArgument = async (argName: string) => {
|
||||
await this.page.getByTestId(`cmd-bar-add-optional-arg-${argName}`).click()
|
||||
}
|
||||
|
||||
/**
|
||||
* Clicks the Create new variable button for kcl input
|
||||
*/
|
||||
|
@ -183,14 +183,15 @@ export class EditorFixture {
|
||||
scrollToText(text: string, placeCursor?: boolean) {
|
||||
return this.page.evaluate(
|
||||
(args: { text: string; placeCursor?: boolean }) => {
|
||||
const editorView = window.editorManager.getEditorView()
|
||||
// error TS2339: Property 'docView' does not exist on type 'EditorView'.
|
||||
// Except it does so :shrug:
|
||||
// @ts-ignore
|
||||
let index = window.editorManager._editorView?.docView.view.state.doc
|
||||
const index = editorView?.docView.view.state.doc
|
||||
.toString()
|
||||
.indexOf(args.text)
|
||||
window.editorManager._editorView?.focus()
|
||||
window.editorManager._editorView?.dispatch({
|
||||
editorView?.focus()
|
||||
editorView?.dispatch({
|
||||
selection: window.EditorSelection.create([
|
||||
window.EditorSelection.cursor(index),
|
||||
]),
|
||||
|
@ -274,6 +274,13 @@ export class ToolbarFixture {
|
||||
.nth(operationIndex)
|
||||
}
|
||||
|
||||
getDefaultPlaneVisibilityButton(plane: 'XY' | 'XZ' | 'YZ' = 'XY') {
|
||||
const index = plane === 'XZ' ? 0 : plane === 'XY' ? 1 : 2
|
||||
return this.featureTreePane
|
||||
.getByTestId('feature-tree-visibility-toggle')
|
||||
.nth(index)
|
||||
}
|
||||
|
||||
/**
|
||||
* View source on a specific operation in the Feature Tree pane.
|
||||
* @param operationName The name of the operation type
|
||||
|
@ -5,7 +5,7 @@ import type {
|
||||
FullResult,
|
||||
} from '@playwright/test/reporter'
|
||||
|
||||
class MyAPIReporter implements Reporter {
|
||||
class APIReporter implements Reporter {
|
||||
private pendingRequests: Promise<void>[] = []
|
||||
private allResults: Record<string, any>[] = []
|
||||
private blockingResults: Record<string, any>[] = []
|
||||
@ -32,7 +32,7 @@ class MyAPIReporter implements Reporter {
|
||||
'X-API-Key': process.env.TAB_API_KEY || '',
|
||||
}),
|
||||
body: JSON.stringify({
|
||||
project: 'https://github.com/KittyCAD/modeling-app',
|
||||
project: `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}`,
|
||||
branch:
|
||||
process.env.GITHUB_HEAD_REF || process.env.GITHUB_REF_NAME || '',
|
||||
commit: process.env.CI_COMMIT_SHA || process.env.GITHUB_SHA || '',
|
||||
@ -60,7 +60,7 @@ class MyAPIReporter implements Reporter {
|
||||
|
||||
const payload = {
|
||||
// Required information
|
||||
project: 'https://github.com/KittyCAD/modeling-app',
|
||||
project: `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}`,
|
||||
suite: process.env.CI_SUITE || 'e2e',
|
||||
branch: process.env.GITHUB_HEAD_REF || process.env.GITHUB_REF_NAME || '',
|
||||
commit: process.env.CI_COMMIT_SHA || process.env.GITHUB_SHA || '',
|
||||
@ -124,4 +124,4 @@ class MyAPIReporter implements Reporter {
|
||||
}
|
||||
}
|
||||
|
||||
export default MyAPIReporter
|
||||
export default APIReporter
|
||||
|
@ -475,6 +475,7 @@ test.describe('Can export from electron app', () => {
|
||||
},
|
||||
tronApp.projectDirName,
|
||||
page,
|
||||
cmdBar,
|
||||
method
|
||||
)
|
||||
)
|
||||
@ -779,9 +780,6 @@ test.describe(`Project management commands`, () => {
|
||||
const commandContinueButton = page.getByRole('button', {
|
||||
name: 'Continue',
|
||||
})
|
||||
const commandSubmitButton = page.getByRole('button', {
|
||||
name: 'Submit command',
|
||||
})
|
||||
const toastMessage = page.getByText(`Successfully renamed`)
|
||||
|
||||
await test.step(`Setup`, async () => {
|
||||
@ -800,8 +798,7 @@ test.describe(`Project management commands`, () => {
|
||||
await expect(commandContinueButton).toBeVisible()
|
||||
await commandContinueButton.click()
|
||||
|
||||
await expect(commandSubmitButton).toBeVisible()
|
||||
await commandSubmitButton.click()
|
||||
await cmdBar.submit()
|
||||
|
||||
await expect(toastMessage).toBeVisible()
|
||||
})
|
||||
@ -837,9 +834,6 @@ test.describe(`Project management commands`, () => {
|
||||
})
|
||||
const projectNameOption = page.getByRole('option', { name: projectName })
|
||||
const commandWarning = page.getByText('Are you sure you want to delete?')
|
||||
const commandSubmitButton = page.getByRole('button', {
|
||||
name: 'Submit command',
|
||||
})
|
||||
const toastMessage = page.getByText(`Successfully deleted`)
|
||||
const noProjectsMessage = page.getByText('No projects found')
|
||||
|
||||
@ -859,8 +853,7 @@ test.describe(`Project management commands`, () => {
|
||||
await projectNameOption.click()
|
||||
|
||||
await expect(commandWarning).toBeVisible()
|
||||
await expect(commandSubmitButton).toBeVisible()
|
||||
await commandSubmitButton.click()
|
||||
await cmdBar.submit()
|
||||
|
||||
await expect(toastMessage).toBeVisible()
|
||||
})
|
||||
@ -894,9 +887,6 @@ test.describe(`Project management commands`, () => {
|
||||
const commandContinueButton = page.getByRole('button', {
|
||||
name: 'Continue',
|
||||
})
|
||||
const commandSubmitButton = page.getByRole('button', {
|
||||
name: 'Submit command',
|
||||
})
|
||||
const toastMessage = page.getByText(`Successfully renamed`)
|
||||
|
||||
await test.step(`Setup`, async () => {
|
||||
@ -914,8 +904,7 @@ test.describe(`Project management commands`, () => {
|
||||
await expect(commandContinueButton).toBeVisible()
|
||||
await commandContinueButton.click()
|
||||
|
||||
await expect(commandSubmitButton).toBeVisible()
|
||||
await commandSubmitButton.click()
|
||||
await cmdBar.submit()
|
||||
|
||||
await expect(toastMessage).toBeVisible()
|
||||
})
|
||||
@ -949,9 +938,6 @@ test.describe(`Project management commands`, () => {
|
||||
})
|
||||
const projectNameOption = page.getByRole('option', { name: projectName })
|
||||
const commandWarning = page.getByText('Are you sure you want to delete?')
|
||||
const commandSubmitButton = page.getByRole('button', {
|
||||
name: 'Submit command',
|
||||
})
|
||||
const toastMessage = page.getByText(`Successfully deleted`)
|
||||
const noProjectsMessage = page.getByText('No projects found')
|
||||
|
||||
@ -967,8 +953,7 @@ test.describe(`Project management commands`, () => {
|
||||
await projectNameOption.click()
|
||||
|
||||
await expect(commandWarning).toBeVisible()
|
||||
await expect(commandSubmitButton).toBeVisible()
|
||||
await commandSubmitButton.click()
|
||||
await cmdBar.submit()
|
||||
|
||||
await expect(toastMessage).toBeVisible()
|
||||
})
|
||||
|
@ -1,6 +1,7 @@
|
||||
import path from 'path'
|
||||
import { bracket } from '@e2e/playwright/fixtures/bracket'
|
||||
import type { Page } from '@playwright/test'
|
||||
import type { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture'
|
||||
import { reportRejection } from '@src/lib/trap'
|
||||
import * as fsp from 'fs/promises'
|
||||
|
||||
@ -421,10 +422,7 @@ extrude002 = extrude(profile002, length = 150)
|
||||
await page.keyboard.press('Enter')
|
||||
|
||||
// Click the checkbox
|
||||
const submitButton = page.getByText('Confirm Export')
|
||||
await expect(submitButton).toBeVisible()
|
||||
|
||||
await page.keyboard.press('Enter')
|
||||
await cmdBar.submit()
|
||||
|
||||
// Find the toast.
|
||||
// Look out for the toast message
|
||||
@ -461,8 +459,7 @@ extrude002 = extrude(profile002, length = 150)
|
||||
await page.keyboard.press('Enter')
|
||||
|
||||
// Click the checkbox
|
||||
await expect(submitButton).toBeVisible()
|
||||
await page.keyboard.press('Enter')
|
||||
await cmdBar.submit()
|
||||
|
||||
// Find the toast.
|
||||
// Look out for the toast message
|
||||
@ -482,6 +479,7 @@ extrude002 = extrude(profile002, length = 150)
|
||||
test('ensure you CAN export while an export is already going', async ({
|
||||
page,
|
||||
homePage,
|
||||
cmdBar,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await test.step('Set up the code and durations', async () => {
|
||||
@ -516,11 +514,11 @@ extrude002 = extrude(profile002, length = 150)
|
||||
const successToastMessage = page.getByText(`Exported successfully`)
|
||||
|
||||
await test.step('second export', async () => {
|
||||
await clickExportButton(page)
|
||||
await clickExportButton(page, cmdBar)
|
||||
|
||||
await expect(exportingToastMessage).toBeVisible()
|
||||
|
||||
await clickExportButton(page)
|
||||
await clickExportButton(page, cmdBar)
|
||||
|
||||
await test.step('The first export still succeeds', async () => {
|
||||
await Promise.all([
|
||||
@ -537,7 +535,7 @@ extrude002 = extrude(profile002, length = 150)
|
||||
|
||||
await test.step('Successful, unblocked export', async () => {
|
||||
// Try exporting again.
|
||||
await clickExportButton(page)
|
||||
await clickExportButton(page, cmdBar)
|
||||
|
||||
// Find the toast.
|
||||
// Look out for the toast message
|
||||
@ -880,7 +878,7 @@ s2 = startSketchOn(XY)
|
||||
})
|
||||
})
|
||||
|
||||
async function clickExportButton(page: Page) {
|
||||
async function clickExportButton(page: Page, cmdBar: CmdBarFixture) {
|
||||
await test.step('Running export flow', async () => {
|
||||
// export the model
|
||||
const exportButton = page.getByTestId('export-pane-button')
|
||||
@ -896,9 +894,6 @@ async function clickExportButton(page: Page) {
|
||||
await page.keyboard.press('Enter')
|
||||
|
||||
// Click the checkbox
|
||||
const submitButton = page.getByText('Confirm Export')
|
||||
await expect(submitButton).toBeVisible()
|
||||
|
||||
await page.keyboard.press('Enter')
|
||||
await cmdBar.submit()
|
||||
})
|
||||
}
|
||||
|
@ -1478,6 +1478,7 @@ sketch001 = startSketchOn(XZ)
|
||||
await page.mouse.move(1200, 139)
|
||||
await page.mouse.down()
|
||||
await page.mouse.move(870, 250)
|
||||
await page.mouse.up()
|
||||
|
||||
await page.waitForTimeout(200)
|
||||
|
||||
@ -1487,6 +1488,60 @@ sketch001 = startSketchOn(XZ)
|
||||
)
|
||||
})
|
||||
|
||||
test('Can undo with closed code pane', async ({
|
||||
page,
|
||||
homePage,
|
||||
editor,
|
||||
toolbar,
|
||||
scene,
|
||||
cmdBar,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
const viewportSize = { width: 1500, height: 750 }
|
||||
await page.setBodyDimensions(viewportSize)
|
||||
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`@settings(defaultLengthUnit=in)
|
||||
sketch001 = startSketchOn(XZ)
|
||||
|> startProfile(at = [-10, -10])
|
||||
|> line(end = [20.0, 10.0])
|
||||
|> tangentialArc(end = [5.49, 8.37])`
|
||||
)
|
||||
})
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
await toolbar.waitForFeatureTreeToBeBuilt()
|
||||
await scene.settled(cmdBar)
|
||||
|
||||
await (await toolbar.getFeatureTreeOperation('Sketch', 0)).dblclick()
|
||||
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
await page.mouse.move(1200, 139)
|
||||
await page.mouse.down()
|
||||
await page.mouse.move(870, 250)
|
||||
await page.mouse.up()
|
||||
|
||||
await editor.expectEditor.toContain(`tangentialArc(end=[-5.85,4.32])`, {
|
||||
shouldNormalise: true,
|
||||
})
|
||||
|
||||
await u.closeKclCodePanel()
|
||||
|
||||
// Undo the last change
|
||||
await page.keyboard.down('Control')
|
||||
await page.keyboard.press('KeyZ')
|
||||
await page.keyboard.up('Control')
|
||||
|
||||
await u.openKclCodePanel()
|
||||
await editor.expectEditor.toContain(`tangentialArc(end = [5.49, 8.37])`, {
|
||||
shouldNormalise: true,
|
||||
})
|
||||
})
|
||||
|
||||
test('Can delete a single segment line with keyboard', async ({
|
||||
page,
|
||||
scene,
|
||||
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 56 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 75 KiB |
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 61 KiB |
Before Width: | Height: | Size: 134 KiB After Width: | Height: | Size: 135 KiB |
Before Width: | Height: | Size: 117 KiB After Width: | Height: | Size: 118 KiB |
Before Width: | Height: | Size: 134 KiB After Width: | Height: | Size: 135 KiB |
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 68 KiB |
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 71 KiB |
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 67 KiB |
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 69 KiB |
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 69 KiB |
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 65 KiB |
@ -12,6 +12,7 @@ export const TEST_SETTINGS: DeepPartial<Settings> = {
|
||||
},
|
||||
onboarding_status: 'dismissed',
|
||||
show_debug_panel: true,
|
||||
fixed_size_grid: false,
|
||||
},
|
||||
modeling: {
|
||||
enable_ssao: false,
|
||||
|
@ -17,11 +17,12 @@ import dotenv from 'dotenv'
|
||||
|
||||
const NODE_ENV = process.env.NODE_ENV || 'development'
|
||||
dotenv.config({ path: [`.env.${NODE_ENV}.local`, `.env.${NODE_ENV}`] })
|
||||
export const token = process.env.token || ''
|
||||
export const token = process.env.VITE_KITTYCAD_API_TOKEN || ''
|
||||
|
||||
import type { ProjectConfiguration } from '@rust/kcl-lib/bindings/ProjectConfiguration'
|
||||
|
||||
import type { ElectronZoo } from '@e2e/playwright/fixtures/fixtureSetup'
|
||||
import type { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture'
|
||||
import { isErrorWhitelisted } from '@e2e/playwright/lib/console-error-whitelist'
|
||||
import { TEST_SETTINGS, TEST_SETTINGS_KEY } from '@e2e/playwright/storageStates'
|
||||
import { test } from '@e2e/playwright/zoo-test'
|
||||
@ -158,10 +159,10 @@ async function openKclCodePanel(page: Page) {
|
||||
await page.evaluate(() => {
|
||||
// editorManager is available on the window object.
|
||||
//@ts-ignore this is in an entirely different context that tsc can't see.
|
||||
editorManager._editorView.dispatch({
|
||||
editorManager.getEditorView().dispatch({
|
||||
selection: {
|
||||
//@ts-ignore this is in an entirely different context that tsc can't see.
|
||||
anchor: editorManager._editorView.docView.length,
|
||||
anchor: editorManager.getEditorView().docView.length,
|
||||
},
|
||||
scrollIntoView: true,
|
||||
})
|
||||
@ -737,6 +738,7 @@ export const doExport = async (
|
||||
output: Models['OutputFormat3d_type'],
|
||||
rootDir: string,
|
||||
page: Page,
|
||||
cmdBar: CmdBarFixture,
|
||||
exportFrom: 'dropdown' | 'sidebarButton' | 'commandBar' = 'dropdown'
|
||||
): Promise<Paths> => {
|
||||
if (exportFrom === 'dropdown') {
|
||||
@ -780,9 +782,7 @@ export const doExport = async (
|
||||
.click()
|
||||
await page.locator('#arg-form').waitFor({ state: 'detached' })
|
||||
}
|
||||
await expect(page.getByText('Confirm Export')).toBeVisible()
|
||||
|
||||
await page.getByRole('button', { name: 'Submit command' }).click()
|
||||
await cmdBar.submit()
|
||||
|
||||
await expect(page.getByText('Exported successfully')).toBeVisible()
|
||||
|
||||
@ -880,6 +880,10 @@ export async function setup(
|
||||
},
|
||||
...TEST_SETTINGS.project,
|
||||
onboarding_status: 'dismissed',
|
||||
// Tests were written before this setting existed.
|
||||
// It's true by default because it's a good user experience, but
|
||||
// these tests require it to be false.
|
||||
fixed_size_grid: false,
|
||||
},
|
||||
project: {
|
||||
...TEST_SETTINGS.project,
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
|
||||
test.describe('Testing constraints', () => {
|
||||
test('Can constrain line length', async ({ page, homePage }) => {
|
||||
test('Can constrain line length', async ({ page, homePage, cmdBar }) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
@ -50,11 +50,7 @@ test.describe('Testing constraints', () => {
|
||||
await page.waitForTimeout(100)
|
||||
await page.getByTestId('constraint-length').click()
|
||||
await page.getByTestId('cmd-bar-arg-value').getByRole('textbox').fill('20')
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'arrow right Continue',
|
||||
})
|
||||
.click()
|
||||
await cmdBar.continue()
|
||||
|
||||
await expect(page.locator('.cm-content')).toHaveText(
|
||||
`length001 = 20sketch001 = startSketchOn(XY) |> startProfile(at = [-10, -10]) |> line(end = [20, 0]) |> angledLine(angle = 90, length = length001) |> xLine(length = -20)`
|
||||
@ -681,9 +677,6 @@ test.describe('Testing constraints', () => {
|
||||
.getByRole('textbox')
|
||||
const cmdBarKclVariableNameInput =
|
||||
page.getByPlaceholder('Variable name')
|
||||
const cmdBarSubmitButton = page.getByRole('button', {
|
||||
name: 'arrow right Continue',
|
||||
})
|
||||
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
@ -736,7 +729,7 @@ part002 = startSketchOn(XZ)
|
||||
await page.waitForTimeout(500)
|
||||
const [ang, len] = value.split(', ')
|
||||
const changedCode = `|> angledLine(angle = ${ang}, length = ${len})`
|
||||
await cmdBarSubmitButton.click()
|
||||
await cmdBar.continue()
|
||||
await expect(page.locator('.cm-content')).toContainText(changedCode)
|
||||
|
||||
// checking active assures the cursor is where it should be
|
||||
@ -1101,11 +1094,7 @@ part002 = startSketchOn(XZ)
|
||||
await page.waitForTimeout(500)
|
||||
|
||||
await page.getByTestId('cmd-bar-arg-value').getByRole('textbox').fill('10')
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'arrow right Continue',
|
||||
})
|
||||
.click()
|
||||
await cmdBar.continue()
|
||||
|
||||
await pollEditorLinesSelectedLength(page, 1)
|
||||
activeLinesContent = await page.locator('.cm-activeLine').all()
|
||||
|
@ -21,7 +21,7 @@ test.describe('Testing loading external models', () => {
|
||||
// We have no more web tests
|
||||
test.fail(
|
||||
'Web: should overwrite current code, cannot create new file',
|
||||
async ({ editor, context, page, homePage }) => {
|
||||
async ({ editor, context, page, homePage, cmdBar }) => {
|
||||
const u = await getUtils(page)
|
||||
await test.step(`Test setup`, async () => {
|
||||
await context.addInitScript((code) => {
|
||||
@ -52,9 +52,6 @@ test.describe('Testing loading external models', () => {
|
||||
name,
|
||||
})
|
||||
const warningText = page.getByText('Overwrite current file with sample?')
|
||||
const confirmButton = page.getByRole('button', {
|
||||
name: 'Submit command',
|
||||
})
|
||||
|
||||
await test.step(`Precondition: check the initial code`, async () => {
|
||||
await u.openKclCodePanel()
|
||||
@ -70,7 +67,7 @@ test.describe('Testing loading external models', () => {
|
||||
await expect(commandMethodOption('Create new file')).not.toBeVisible()
|
||||
await commandMethodOption('Overwrite').click()
|
||||
await expect(warningText).toBeVisible()
|
||||
await confirmButton.click()
|
||||
await cmdBar.submit()
|
||||
|
||||
await editor.expectEditor.toContain('// ' + newSample.title)
|
||||
})
|
||||
|
@ -3,6 +3,7 @@ import type { LineInputsType } from '@src/lang/std/sketchcombos'
|
||||
import { uuidv4 } from '@src/lib/utils'
|
||||
|
||||
import type { EditorFixture } from '@e2e/playwright/fixtures/editorFixture'
|
||||
import type { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture'
|
||||
import { deg, getUtils, wiggleMove } from '@e2e/playwright/test-utils'
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
|
||||
@ -18,7 +19,7 @@ test.describe('Testing segment overlays', () => {
|
||||
* @param {number} options.steps - The number of steps to perform
|
||||
*/
|
||||
const _clickConstrained =
|
||||
(page: Page, editor: EditorFixture) =>
|
||||
(page: Page, editor: EditorFixture, cmdBar: CmdBarFixture) =>
|
||||
async ({
|
||||
hoverPos,
|
||||
constraintType,
|
||||
@ -93,11 +94,7 @@ test.describe('Testing segment overlays', () => {
|
||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||
).toBeFocused()
|
||||
await page.waitForTimeout(500)
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'arrow right Continue',
|
||||
})
|
||||
.click()
|
||||
await cmdBar.continue()
|
||||
await editor.expectEditor.toContain(expectFinal, {
|
||||
shouldNormalise: true,
|
||||
})
|
||||
@ -113,7 +110,7 @@ test.describe('Testing segment overlays', () => {
|
||||
* @param {number} options.steps - The number of steps to perform
|
||||
*/
|
||||
const _clickUnconstrained =
|
||||
(page: Page, editor: EditorFixture) =>
|
||||
(page: Page, editor: EditorFixture, cmdBar: CmdBarFixture) =>
|
||||
async ({
|
||||
hoverPos,
|
||||
constraintType,
|
||||
@ -163,11 +160,7 @@ test.describe('Testing segment overlays', () => {
|
||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||
).toBeFocused()
|
||||
await page.waitForTimeout(500)
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'arrow right Continue',
|
||||
})
|
||||
.click()
|
||||
await cmdBar.continue()
|
||||
await editor.expectEditor.toContain(expectAfterUnconstrained, {
|
||||
shouldNormalise: true,
|
||||
})
|
||||
@ -239,8 +232,8 @@ test.describe('Testing segment overlays', () => {
|
||||
|
||||
await expect(page.getByTestId('segment-overlay')).toHaveCount(14)
|
||||
|
||||
const clickUnconstrained = _clickUnconstrained(page, editor)
|
||||
const clickConstrained = _clickConstrained(page, editor)
|
||||
const clickUnconstrained = _clickUnconstrained(page, editor, cmdBar)
|
||||
const clickConstrained = _clickConstrained(page, editor, cmdBar)
|
||||
|
||||
await u.openAndClearDebugPanel()
|
||||
await u.sendCustomCmd({
|
||||
@ -664,7 +657,7 @@ profile002 = circle(sketch001, center = [345, 0], radius = 238.38)
|
||||
await expect(
|
||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||
).toBeFocused()
|
||||
await page.getByRole('button', { name: 'arrow right Continue' }).click()
|
||||
await cmdBar.continue()
|
||||
|
||||
// Verify the X constraint was added
|
||||
await editor.expectEditor.toContain('center = [xAbs001, 0]', {
|
||||
@ -682,7 +675,7 @@ profile002 = circle(sketch001, center = [345, 0], radius = 238.38)
|
||||
await expect(
|
||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||
).toBeFocused()
|
||||
await page.getByRole('button', { name: 'arrow right Continue' }).click()
|
||||
await cmdBar.continue()
|
||||
|
||||
// Verify the Y constraint was added
|
||||
await editor.expectEditor.toContain('center = [xAbs001, yAbs001]', {
|
||||
@ -700,7 +693,7 @@ profile002 = circle(sketch001, center = [345, 0], radius = 238.38)
|
||||
await expect(
|
||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||
).toBeFocused()
|
||||
await page.getByRole('button', { name: 'arrow right Continue' }).click()
|
||||
await cmdBar.continue()
|
||||
|
||||
// Verify all constraints were added
|
||||
await editor.expectEditor.toContain(
|
||||
@ -887,7 +880,7 @@ profile003 = startProfile(sketch001, at = [64.39, 35.16])
|
||||
await expect(
|
||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||
).toBeFocused()
|
||||
await page.getByRole('button', { name: 'arrow right Continue' }).click()
|
||||
await cmdBar.continue()
|
||||
|
||||
// Verify the constraint was added
|
||||
await editor.expectEditor.toContain(
|
||||
@ -910,7 +903,7 @@ profile003 = startProfile(sketch001, at = [64.39, 35.16])
|
||||
await expect(
|
||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||
).toBeFocused()
|
||||
await page.getByRole('button', { name: 'arrow right Continue' }).click()
|
||||
await cmdBar.continue()
|
||||
|
||||
// Verify both constraints were added
|
||||
await editor.expectEditor.toContain(
|
||||
@ -935,7 +928,7 @@ profile003 = startProfile(sketch001, at = [64.39, 35.16])
|
||||
await expect(
|
||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||
).toBeFocused()
|
||||
await page.getByRole('button', { name: 'arrow right Continue' }).click()
|
||||
await cmdBar.continue()
|
||||
|
||||
// Verify the constraint was added
|
||||
await editor.expectEditor.toContain('endAbsolute = [xAbs002, 84.07]', {
|
||||
@ -955,7 +948,7 @@ profile003 = startProfile(sketch001, at = [64.39, 35.16])
|
||||
await expect(
|
||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||
).toBeFocused()
|
||||
await page.getByRole('button', { name: 'arrow right Continue' }).click()
|
||||
await cmdBar.continue()
|
||||
|
||||
// Verify all constraints were added
|
||||
await editor.expectEditor.toContain(
|
||||
|
@ -32,7 +32,7 @@ test('Units menu', async ({ page, homePage }) => {
|
||||
test(
|
||||
'Successful export shows a success toast',
|
||||
{ tag: '@skipLocalEngine' },
|
||||
async ({ page, homePage, tronApp }) => {
|
||||
async ({ page, homePage, cmdBar, tronApp }) => {
|
||||
// FYI this test doesn't work with only engine running locally
|
||||
// And you will need to have the KittyCAD CLI installed
|
||||
const u = await getUtils(page)
|
||||
@ -94,7 +94,8 @@ part001 = startSketchOn(-XZ)
|
||||
presentation: 'pretty',
|
||||
},
|
||||
tronApp?.projectDirName,
|
||||
page
|
||||
page,
|
||||
cmdBar
|
||||
)
|
||||
}
|
||||
)
|
||||
@ -254,6 +255,7 @@ test('First escape in tool pops you out of tool, second exits sketch mode', asyn
|
||||
test('Basic default modeling and sketch hotkeys work', async ({
|
||||
page,
|
||||
homePage,
|
||||
cmdBar,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await test.step(`Set up test`, async () => {
|
||||
@ -397,11 +399,8 @@ test('Basic default modeling and sketch hotkeys work', async ({
|
||||
await expect(page.getByRole('button', { name: 'Continue' })).toBeVisible({
|
||||
timeout: 20_000,
|
||||
})
|
||||
await page.getByRole('button', { name: 'Continue' }).click()
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Submit command' })
|
||||
).toBeVisible()
|
||||
await page.getByRole('button', { name: 'Submit command' }).click()
|
||||
await cmdBar.continue()
|
||||
await cmdBar.submit()
|
||||
await expect(page.locator('.cm-content')).toContainText('extrude(')
|
||||
})
|
||||
|
||||
@ -575,8 +574,7 @@ profile001 = startProfile(sketch002, at = [-12.34, 12.34])
|
||||
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.progressCmdBar()
|
||||
await expect(page.getByText('Confirm Extrude')).toBeVisible()
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.submit()
|
||||
|
||||
const result2 = result.genNext`
|
||||
const sketch002 = extrude(sketch002, length = ${[5, 5]} + 7)`
|
||||
|
18
flake.lock
generated
@ -20,11 +20,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1745998881,
|
||||
"narHash": "sha256-vonyYAKJSlsX4n9GCsS0pHxR6yCrfqBIuGvANlkwG6U=",
|
||||
"lastModified": 1750865895,
|
||||
"narHash": "sha256-p2dWAQcLVzquy9LxYCZPwyUdugw78Qv3ChvnX755qHA=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "423d2df5b04b4ee7688c3d71396e872afa236a89",
|
||||
"rev": "61c0f513911459945e2cb8bf333dc849f1b976ff",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -36,11 +36,11 @@
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1745998881,
|
||||
"narHash": "sha256-vonyYAKJSlsX4n9GCsS0pHxR6yCrfqBIuGvANlkwG6U=",
|
||||
"lastModified": 1750865895,
|
||||
"narHash": "sha256-p2dWAQcLVzquy9LxYCZPwyUdugw78Qv3ChvnX755qHA=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "423d2df5b04b4ee7688c3d71396e872afa236a89",
|
||||
"rev": "61c0f513911459945e2cb8bf333dc849f1b976ff",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -78,11 +78,11 @@
|
||||
"nixpkgs": "nixpkgs_3"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1745980514,
|
||||
"narHash": "sha256-CITAeiuXGjDvT5iZBXr6vKVWQwsUQLJUMFO91bfJFC4=",
|
||||
"lastModified": 1750964660,
|
||||
"narHash": "sha256-YQ6EyFetjH1uy5JhdhRdPe6cuNXlYpMAQePFfZj4W7M=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "7fbdae44b0f40ea432e46fd152ad8be0f8f41ad6",
|
||||
"rev": "04f0fcfb1a50c63529805a798b4b5c21610ff390",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
7
interface.d.ts
vendored
@ -72,16 +72,13 @@ export interface IElectronAPI {
|
||||
}
|
||||
process: {
|
||||
env: {
|
||||
BASE_URL: string
|
||||
IS_PLAYWRIGHT: string
|
||||
VITE_KC_DEV_TOKEN: string
|
||||
VITE_KITTYCAD_API_TOKEN: string
|
||||
VITE_KC_API_WS_MODELING_URL: string
|
||||
VITE_KC_API_BASE_URL: string
|
||||
VITE_KITTYCAD_API_BASE_URL: string
|
||||
VITE_KC_SITE_BASE_URL: string
|
||||
VITE_KC_SITE_APP_URL: string
|
||||
VITE_KC_SKIP_AUTH: string
|
||||
VITE_KC_CONNECTION_TIMEOUT_MS: string
|
||||
VITE_KC_DEV_TOKEN: string
|
||||
NODE_ENV: string
|
||||
PROD: string
|
||||
DEV: string
|
||||
|
@ -125,18 +125,57 @@ test('Shows a loading spinner when uninitialized credit count', async () => {
|
||||
await expect(queryByTestId('spinner')).toBeVisible()
|
||||
})
|
||||
|
||||
test('Shows the total credits for Unknown subscription', async () => {
|
||||
const data = {
|
||||
balance: {
|
||||
monthlyApiCreditsRemaining: 10,
|
||||
stableApiCreditsRemaining: 25,
|
||||
},
|
||||
subscriptions: {
|
||||
monthlyPayAsYouGoApiCreditsTotal: 20,
|
||||
name: "unknown",
|
||||
}
|
||||
const unKnownTierData = {
|
||||
balance: {
|
||||
monthlyApiCreditsRemaining: 10,
|
||||
stableApiCreditsRemaining: 25,
|
||||
},
|
||||
subscriptions: {
|
||||
monthlyPayAsYouGoApiCreditsTotal: 20,
|
||||
name: "unknown",
|
||||
}
|
||||
}
|
||||
|
||||
const freeTierData = {
|
||||
balance: {
|
||||
monthlyApiCreditsRemaining: 10,
|
||||
stableApiCreditsRemaining: 0,
|
||||
},
|
||||
subscriptions: {
|
||||
monthlyPayAsYouGoApiCreditsTotal: 20,
|
||||
name: "free",
|
||||
}
|
||||
}
|
||||
|
||||
const proTierData = {
|
||||
// These are all ignored
|
||||
balance: {
|
||||
monthlyApiCreditsRemaining: 10,
|
||||
stableApiCreditsRemaining: 0,
|
||||
},
|
||||
subscriptions: {
|
||||
// This should be ignored because it's Pro tier.
|
||||
monthlyPayAsYouGoApiCreditsTotal: 20,
|
||||
name: "pro",
|
||||
}
|
||||
}
|
||||
|
||||
const enterpriseTierData = {
|
||||
// These are all ignored, user is part of an org.
|
||||
balance: {
|
||||
monthlyApiCreditsRemaining: 10,
|
||||
stableApiCreditsRemaining: 0,
|
||||
},
|
||||
subscriptions: {
|
||||
// This should be ignored because it's Pro tier.
|
||||
monthlyPayAsYouGoApiCreditsTotal: 20,
|
||||
// This should be ignored because the user is part of an Org.
|
||||
name: "free",
|
||||
}
|
||||
}
|
||||
|
||||
test('Shows the total credits for Unknown subscription', async () => {
|
||||
const data = unKnownTierData
|
||||
server.use(
|
||||
http.get('*/user/payment/balance', (req, res, ctx) => {
|
||||
return HttpResponse.json(createUserPaymentBalanceResponse(data.balance))
|
||||
@ -166,17 +205,7 @@ test('Shows the total credits for Unknown subscription', async () => {
|
||||
})
|
||||
|
||||
test('Progress bar reflects ratio left of Free subscription', async () => {
|
||||
const data = {
|
||||
balance: {
|
||||
monthlyApiCreditsRemaining: 10,
|
||||
stableApiCreditsRemaining: 0,
|
||||
},
|
||||
subscriptions: {
|
||||
monthlyPayAsYouGoApiCreditsTotal: 20,
|
||||
name: "free",
|
||||
}
|
||||
}
|
||||
|
||||
const data = freeTierData
|
||||
server.use(
|
||||
http.get('*/user/payment/balance', (req, res, ctx) => {
|
||||
return HttpResponse.json(createUserPaymentBalanceResponse(data.balance))
|
||||
@ -212,19 +241,7 @@ test('Progress bar reflects ratio left of Free subscription', async () => {
|
||||
})
|
||||
})
|
||||
test('Shows infinite credits for Pro subscription', async () => {
|
||||
const data = {
|
||||
// These are all ignored
|
||||
balance: {
|
||||
monthlyApiCreditsRemaining: 10,
|
||||
stableApiCreditsRemaining: 0,
|
||||
},
|
||||
subscriptions: {
|
||||
// This should be ignored because it's Pro tier.
|
||||
monthlyPayAsYouGoApiCreditsTotal: 20,
|
||||
name: "pro",
|
||||
}
|
||||
}
|
||||
|
||||
const data = proTierData
|
||||
server.use(
|
||||
http.get('*/user/payment/balance', (req, res, ctx) => {
|
||||
return HttpResponse.json(createUserPaymentBalanceResponse(data.balance))
|
||||
@ -255,19 +272,7 @@ test('Shows infinite credits for Pro subscription', async () => {
|
||||
await expect(queryByTestId('billing-remaining-progress-bar-inline')).toBe(null)
|
||||
})
|
||||
test('Shows infinite credits for Enterprise subscription', async () => {
|
||||
const data = {
|
||||
// These are all ignored, user is part of an org.
|
||||
balance: {
|
||||
monthlyApiCreditsRemaining: 10,
|
||||
stableApiCreditsRemaining: 0,
|
||||
},
|
||||
subscriptions: {
|
||||
// This should be ignored because it's Pro tier.
|
||||
monthlyPayAsYouGoApiCreditsTotal: 20,
|
||||
// This should be ignored because the user is part of an Org.
|
||||
name: "free",
|
||||
}
|
||||
}
|
||||
const data = enterpriseTierData
|
||||
|
||||
server.use(
|
||||
http.get('*/user/payment/balance', (req, res, ctx) => {
|
||||
@ -297,3 +302,58 @@ test('Shows infinite credits for Enterprise subscription', async () => {
|
||||
await expect(queryByTestId('infinity')).toBeVisible()
|
||||
await expect(queryByTestId('billing-remaining-progress-bar-inline')).toBe(null)
|
||||
})
|
||||
|
||||
test('Show upgrade button if credits are not infinite', async () => {
|
||||
const data = freeTierData
|
||||
server.use(
|
||||
http.get('*/user/payment/balance', (req, res, ctx) => {
|
||||
return HttpResponse.json(createUserPaymentBalanceResponse(data.balance))
|
||||
}),
|
||||
http.get('*/user/payment/subscriptions', (req, res, ctx) => {
|
||||
return HttpResponse.json(createUserPaymentSubscriptionsResponse(data.subscriptions))
|
||||
}),
|
||||
http.get('*/org', (req, res, ctx) => {
|
||||
return new HttpResponse(403)
|
||||
}),
|
||||
)
|
||||
|
||||
const billingActor = createActor(billingMachine, { input: BILLING_CONTEXT_DEFAULTS }).start()
|
||||
|
||||
const { queryByTestId } = render(<BillingDialog
|
||||
billingActor={billingActor}
|
||||
/>)
|
||||
|
||||
await act(() => {
|
||||
billingActor.send({ type: BillingTransition.Update, apiToken: "it doesn't matter wtf this is :)" })
|
||||
})
|
||||
|
||||
await expect(queryByTestId('billing-upgrade-button')).toBeVisible()
|
||||
})
|
||||
|
||||
test('Hide upgrade button if credits are infinite', async () => {
|
||||
const data = enterpriseTierData
|
||||
server.use(
|
||||
http.get('*/user/payment/balance', (req, res, ctx) => {
|
||||
return HttpResponse.json(createUserPaymentBalanceResponse(data.balance))
|
||||
}),
|
||||
http.get('*/user/payment/subscriptions', (req, res, ctx) => {
|
||||
return HttpResponse.json(createUserPaymentSubscriptionsResponse(data.subscriptions))
|
||||
}),
|
||||
// Ok finally the first use of an org lol
|
||||
http.get('*/org', (req, res, ctx) => {
|
||||
return HttpResponse.json(createOrgResponse())
|
||||
}),
|
||||
)
|
||||
|
||||
const billingActor = createActor(billingMachine, { input: BILLING_CONTEXT_DEFAULTS }).start()
|
||||
|
||||
const { queryByTestId } = render(<BillingDialog
|
||||
billingActor={billingActor}
|
||||
/>)
|
||||
|
||||
await act(() => {
|
||||
billingActor.send({ type: BillingTransition.Update, apiToken: "it doesn't matter wtf this is :)" })
|
||||
})
|
||||
|
||||
await expect(queryByTestId('billing-upgrade-button')).toBe(null)
|
||||
})
|
||||
|
1144
package-lock.json
generated
@ -110,8 +110,11 @@
|
||||
"remove-importmeta": "sed -i 's/import.meta.url/window.location.origin/g' \"./rust/kcl-wasm-lib/pkg/kcl_wasm_lib.js\"; sed -i '' 's/import.meta.url/window.location.origin/g' \"./rust/kcl-wasm-lib/pkg/kcl_wasm_lib.js\" || echo \"sed for both mac and linux\"",
|
||||
"lint-fix": "eslint --fix --ext .ts --ext .tsx src e2e packages/codemirror-lsp-client/src rust/kcl-language-server/client/src",
|
||||
"lint": "eslint --max-warnings 0 --ext .ts --ext .tsx src e2e packages/codemirror-lsp-client/src rust/kcl-language-server/client/src",
|
||||
"url-checker":"./scripts/url-checker.sh",
|
||||
"url-checker:overwrite":"npm run url-checker > scripts/known/urls.txt",
|
||||
"url-checker:diff":"./scripts/diff-url-checker.sh",
|
||||
"circular-deps": "dpdm --no-warning --no-tree -T --skip-dynamic-imports=circular src/index.tsx",
|
||||
"circular-deps:overwrite": "npm run circular-deps | sed '$d' | grep -v '^npm run' > known-circular.txt",
|
||||
"circular-deps:overwrite": "npm run circular-deps | sed '$d' | grep -v '^npm run' > scripts/known/circular.txt",
|
||||
"circular-deps:diff": "./scripts/diff-circular-deps.sh",
|
||||
"circular-deps:diff:nodejs": "npm run circular-deps:diff || node ./scripts/diff.js",
|
||||
"files:set-version": "echo \"$(jq --arg v \"$VERSION\" '.version=$v' package.json --indent 2)\" > package.json",
|
||||
@ -224,7 +227,7 @@
|
||||
"ts-node": "^10.0.0",
|
||||
"typescript": "^5.8.3",
|
||||
"typescript-eslint": "^8.30.1",
|
||||
"vite": "^5.4.18",
|
||||
"vite": "^5.4.19",
|
||||
"vite-plugin-package-version": "^1.1.0",
|
||||
"vite-plugin-top-level-await": "^1.5.0",
|
||||
"vite-tsconfig-paths": "^4.3.2",
|
||||
|
@ -111,7 +111,8 @@ commaSep1NoTrailingComma<term> { term ("," term)* }
|
||||
|
||||
PipeSubstitution { "%" }
|
||||
|
||||
identifier { (@asciiLetter | "_") (@asciiLetter | @digit | "_")* }
|
||||
// Includes non-whitespace unicode characters.
|
||||
identifier { $[a-zA-Z_\u{a1}-\u{167f}\u{1681}-\u{1fff}\u{200e}-\u{2027}\u{202a}-\u{202e}\u{2030}-\u{205e}\u{2061}-\u{2fff}\u{3001}-\u{fefe}\u{ff00}-\u{10ffff}] $[a-zA-Z0-9_\u{a1}-\u{167f}\u{1681}-\u{1fff}\u{200e}-\u{2027}\u{202a}-\u{202e}\u{2030}-\u{205e}\u{2061}-\u{2fff}\u{3001}-\u{fefe}\u{ff00}-\u{10ffff}]* }
|
||||
AnnotationName { "@" identifier? }
|
||||
PropertyName { identifier }
|
||||
TagDeclarator { "$" identifier }
|
||||
|
@ -29,7 +29,7 @@
|
||||
"vscode-uri": "^3.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.14.1",
|
||||
"@types/node": "^24.0.7",
|
||||
"ts-node": "^10.9.2"
|
||||
}
|
||||
}
|
||||
|
@ -42,15 +42,15 @@ fn helicalGear(nTeeth, module, pressureAngle, helixAngle, gearHeight) {
|
||||
helicalGearSketch = startSketchOn(offsetPlane(XY, offset = offsetHeight))
|
||||
|> startProfile(at = polar(angle = helixCalc, length = baseDiameter / 2))
|
||||
|> involuteCircular(
|
||||
startRadius = baseDiameter / 2,
|
||||
endRadius = tipDiameter / 2,
|
||||
startDiameter = baseDiameter,
|
||||
endDiameter = tipDiameter,
|
||||
angle = helixCalc,
|
||||
tag = $seg01,
|
||||
)
|
||||
|> line(endAbsolute = polar(angle = 160 / nTeeth + helixCalc, length = tipDiameter / 2))
|
||||
|> involuteCircular(
|
||||
startRadius = baseDiameter / 2,
|
||||
endRadius = tipDiameter / 2,
|
||||
startDiameter = baseDiameter,
|
||||
endDiameter = tipDiameter,
|
||||
angle = -(4 * atan(segEndY(seg01) / segEndX(seg01)) - (3 * helixCalc)),
|
||||
reverse = true,
|
||||
)
|
||||
|
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 105 KiB |
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 84 KiB |
Before Width: | Height: | Size: 126 KiB After Width: | Height: | Size: 126 KiB |
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 70 KiB |
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 65 KiB |
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 81 KiB |