Compare commits

..

1 Commits

Author SHA1 Message Date
a21c2c50b3 start of url scheme
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-04-25 14:38:03 -07:00
521 changed files with 16220 additions and 35234 deletions

View File

@ -1,3 +1,3 @@
[codespell] [codespell]
ignore-words-list: crate,everytime,inout,co-ordinate,ot,nwo,absolutey,atleast,ue ignore-words-list: crate,everytime,inout,co-ordinate,ot,nwo,absolutey,atleast
skip: **/target,node_modules,build,**/Cargo.lock,./docs/kcl/*.md,./src-tauri/gen/schemas skip: **/target,node_modules,build,**/Cargo.lock,./docs/kcl/*.md,./src-tauri/gen/schemas

View File

@ -3,4 +3,3 @@ VITE_KC_API_BASE_URL=https://api.dev.zoo.dev
VITE_KC_SITE_BASE_URL=https://dev.zoo.dev VITE_KC_SITE_BASE_URL=https://dev.zoo.dev
VITE_KC_SKIP_AUTH=false VITE_KC_SKIP_AUTH=false
VITE_KC_CONNECTION_TIMEOUT_MS=5000 VITE_KC_CONNECTION_TIMEOUT_MS=5000
VITE_KC_DEV_TOKEN="your token from dev.zoo.dev should go in .env.development.local"

View File

@ -27,7 +27,7 @@ jobs:
# Upload the WASM bundle as an artifact # Upload the WASM bundle as an artifact
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v2
with: with:
name: wasm-bundle name: wasm-bundle
path: src/wasm-lib/pkg path: src/wasm-lib/pkg

View File

@ -54,8 +54,3 @@ jobs:
run: | run: |
cd "${{ matrix.dir }}" cd "${{ matrix.dir }}"
cargo clippy --all --tests --benches -- -D warnings cargo clippy --all --tests --benches -- -D warnings
# If this fails, run "cargo check" to update Cargo.lock,
# then add Cargo.lock to the PR.
- name: Check Cargo.lock doesn't need updating
run: |
cargo check --locked || echo "Pls run cargo check and commit the changed Cargo.lock"

View File

@ -13,7 +13,7 @@ on:
# Will checkout the last commit from the default branch (main as of 2023-10-04) # Will checkout the last commit from the default branch (main as of 2023-10-04)
env: env:
BUILD_RELEASE: ${{ github.event_name == 'release' || github.event_name == 'schedule' || github.event_name == 'pull_request' && (contains(github.event.pull_request.title, 'Cut release v')) }} BUILD_RELEASE: ${{ github.event_name == 'release' || github.event_name == 'schedule' || github.event_name == 'pull_request' && contains(github.event.pull_request.title, 'Cut release v') }}
concurrency: concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
@ -130,9 +130,7 @@ jobs:
matrix: matrix:
os: [macos-14, ubuntu-latest, windows-latest] os: [macos-14, ubuntu-latest, windows-latest]
env: env:
# Specific Apple Universal target for macos
TAURI_ARGS_MACOS: ${{ matrix.os == 'macos-14' && '--target universal-apple-darwin' || '' }} TAURI_ARGS_MACOS: ${{ matrix.os == 'macos-14' && '--target universal-apple-darwin' || '' }}
# Only build executable on linux (no appimage or deb)
TAURI_ARGS_UBUNTU: ${{ matrix.os == 'ubuntu-latest' && '--bundles' || '' }} TAURI_ARGS_UBUNTU: ${{ matrix.os == 'ubuntu-latest' && '--bundles' || '' }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -147,14 +145,6 @@ jobs:
cp artifact/src-tauri/tauri.conf.json src-tauri/tauri.conf.json cp artifact/src-tauri/tauri.conf.json src-tauri/tauri.conf.json
cp artifact/src-tauri/tauri.release.conf.json src-tauri/tauri.release.conf.json cp artifact/src-tauri/tauri.release.conf.json src-tauri/tauri.release.conf.json
- name: Update WebView2 on Windows
if: matrix.os == 'windows-latest'
# Workaround needed to build the tauri windows app with matching edge version.
# From https://github.com/actions/runner-images/issues/9538
run: |
Invoke-WebRequest -Uri 'https://go.microsoft.com/fwlink/p/?LinkId=2124703' -OutFile 'setup.exe'
Start-Process -FilePath setup.exe -Verb RunAs -Wait
- name: Install ubuntu system dependencies - name: Install ubuntu system dependencies
if: matrix.os == 'ubuntu-latest' if: matrix.os == 'ubuntu-latest'
run: | run: |
@ -180,7 +170,9 @@ jobs:
- name: Setup Rust - name: Setup Rust
uses: dtolnay/rust-toolchain@stable uses: dtolnay/rust-toolchain@stable
# TODO: re-enable for Windows builds, see https://github.com/tauri-apps/tauri/issues/9045
- name: Setup Rust cache - name: Setup Rust cache
if: matrix.os != 'windows-latest'
uses: swatinem/rust-cache@v2 uses: swatinem/rust-cache@v2
with: with:
workspaces: './src-tauri -> target' workspaces: './src-tauri -> target'
@ -245,96 +237,6 @@ jobs:
includeDebug: true includeDebug: true
args: "${{ env.TAURI_ARGS_MACOS }} ${{ env.TAURI_ARGS_UBUNTU }}" args: "${{ env.TAURI_ARGS_MACOS }} ${{ env.TAURI_ARGS_UBUNTU }}"
- name: Build for Mac TestFlight (nightly)
if: ${{ github.event_name == 'schedule' && matrix.os == 'macos-14' }}
shell: bash
run: |
unset APPLE_SIGNING_IDENTITY
unset APPLE_CERTIFICATE
sign_app="3rd Party Mac Developer Application: KittyCAD Inc (${APPLE_TEAM_ID})"
sign_install="3rd Party Mac Developer Installer: KittyCAD Inc (${APPLE_TEAM_ID})"
profile="src-tauri/entitlements/Mac_App_Distribution.provisionprofile"
mkdir -p src-tauri/entitlements
echo -n "${APPLE_STORE_PROVISIONING_PROFILE}" | base64 --decode -o "${profile}"
echo -n "${APPLE_STORE_DISTRIBUTION_CERT}" | base64 --decode -o "dist.cer"
echo -n "${APPLE_STORE_INSTALLER_CERT}" | base64 --decode -o "installer.cer"
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
KEYCHAIN_PASSWORD="password"
# create temporary keychain
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
# import certificate to keychain
security import "dist.cer" -P "$APPLE_STORE_P12_PASSWORD" -k $KEYCHAIN_PATH -f pkcs12 -t cert -A
security import "installer.cer" -P "$APPLE_STORE_P12_PASSWORD" -k $KEYCHAIN_PATH -f pkcs12 -t cert -A
security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security list-keychain -d user -s $KEYCHAIN_PATH
target="universal-apple-darwin"
# Turn off the default target
# We don't want to install the updater for the apple store build
sed -i.bu "s/default =/# default =/" src-tauri/Cargo.toml
rm src-tauri/Cargo.toml.bu
git diff src-tauri/Cargo.toml
yarn tauri build --target "${target}" --verbose --config src-tauri/tauri.app-store.conf.json
app_path="src-tauri/target/${target}/release/bundle/macos/Zoo Modeling App.app"
build_name="src-tauri/target/${target}/release/bundle/macos/Zoo Modeling App.pkg"
cp_dir="src-tauri/target/${target}/release/bundle/macos/Zoo Modeling App.app/Contents/embedded.provisionprofile"
entitlements="src-tauri/entitlements/app-store.entitlements"
cp "${profile}" "${cp_dir}"
codesign --deep --force -s "${sign_app}" --entitlements "${entitlements}" "${app_path}"
productbuild --component "${app_path}" /Applications/ --sign "${sign_install}" "${build_name}"
# Undo the changes to the Cargo.toml
git checkout src-tauri/Cargo.toml
env:
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_STORE_PROVISIONING_PROFILE: ${{ secrets.APPLE_STORE_PROVISIONING_PROFILE }}
APPLE_STORE_DISTRIBUTION_CERT: ${{ secrets.APPLE_STORE_DISTRIBUTION_CERT }}
APPLE_STORE_INSTALLER_CERT: ${{ secrets.APPLE_STORE_INSTALLER_CERT }}
APPLE_STORE_P12_PASSWORD: ${{ secrets.APPLE_STORE_P12_PASSWORD }}
- name: 'Upload to Mac TestFlight (nightly)'
uses: apple-actions/upload-testflight-build@v1
if: ${{ github.event_name == 'schedule' && matrix.os == 'macos-14' }}
with:
app-path: 'src-tauri/target/universal-apple-darwin/release/bundle/macos/Zoo Modeling App.pkg'
issuer-id: ${{ secrets.APPLE_STORE_ISSUER_ID }}
api-key-id: ${{ secrets.APPLE_STORE_API_KEY_ID }}
api-private-key: ${{ secrets.APPLE_STORE_API_PRIVATE_KEY }}
app-type: osx
- name: Clean up after Mac TestFlight (nightly)
if: ${{ github.event_name == 'schedule' && matrix.os == 'macos-14' }}
shell: bash
run: |
git status
# remove our target builds because we want to make sure the later build
# includes the updater, and that anything we changed with the target
# does not persist
rm -rf src-tauri/target
# Lets get rid of the info.plist for the normal mac builds since its
# being sketchy.
rm src-tauri/Info.plist
# We do this after the apple store because the apple store build is
# specific and we want to overwrite it with the this new build after and
# not upload the apple store build to the public bucket
- name: Build the app (release) and sign - name: Build the app (release) and sign
uses: tauri-apps/tauri-action@v0 uses: tauri-apps/tauri-action@v0
if: ${{ env.BUILD_RELEASE == 'true' }} if: ${{ env.BUILD_RELEASE == 'true' }}
@ -359,10 +261,11 @@ jobs:
with: with:
path: "${{ env.PREFIX }}/${{ env.MODE }}/bundle/*/*" path: "${{ env.PREFIX }}/${{ env.MODE }}/bundle/*/*"
# TODO: re-enable linux e2e tests when possible
- name: Run e2e tests (linux only) - name: Run e2e tests (linux only)
if: ${{ matrix.os == 'ubuntu-latest' && github.event_name != 'release' && github.event_name != 'schedule' }} if: false
run: | run: |
cargo install tauri-driver --force cargo install tauri-driver
source .env.${{ env.BUILD_RELEASE == 'true' && 'production' || 'development' }} source .env.${{ env.BUILD_RELEASE == 'true' && 'production' || 'development' }}
export VITE_KC_API_BASE_URL export VITE_KC_API_BASE_URL
xvfb-run yarn test:e2e:tauri xvfb-run yarn test:e2e:tauri
@ -370,17 +273,6 @@ jobs:
E2E_APPLICATION: "./src-tauri/target/${{ env.BUILD_RELEASE == 'true' && 'release' || 'debug' }}/zoo-modeling-app" E2E_APPLICATION: "./src-tauri/target/${{ env.BUILD_RELEASE == 'true' && 'release' || 'debug' }}/zoo-modeling-app"
KITTYCAD_API_TOKEN: ${{ env.BUILD_RELEASE == 'true' && secrets.KITTYCAD_API_TOKEN || secrets.KITTYCAD_API_TOKEN_DEV }} KITTYCAD_API_TOKEN: ${{ env.BUILD_RELEASE == 'true' && secrets.KITTYCAD_API_TOKEN || secrets.KITTYCAD_API_TOKEN_DEV }}
- name: Run e2e tests (windows only)
if: ${{ matrix.os == 'windows-latest' && github.event_name != 'release' && github.event_name != 'schedule' }}
run: |
cargo install tauri-driver --force
yarn wdio run wdio.conf.ts
env:
E2E_APPLICATION: ".\\src-tauri\\target\\${{ env.BUILD_RELEASE == 'true' && 'release' || 'debug' }}\\Zoo Modeling App.exe"
KITTYCAD_API_TOKEN: ${{ env.BUILD_RELEASE == 'true' && secrets.KITTYCAD_API_TOKEN || secrets.KITTYCAD_API_TOKEN_DEV }}
VITE_KC_API_BASE_URL: ${{ env.BUILD_RELEASE == 'true' && 'https://api.zoo.dev' || 'https://api.dev.zoo.dev' }}
E2E_TAURI_ENABLED: true
TS_NODE_COMPILER_OPTIONS: '{"module": "commonjs"}'
publish-apps-release: publish-apps-release:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -457,7 +349,7 @@ jobs:
cat last_download.json cat last_download.json
- name: Authenticate to Google Cloud - name: Authenticate to Google Cloud
uses: 'google-github-actions/auth@v2.1.3' uses: 'google-github-actions/auth@v2.1.2'
with: with:
credentials_json: '${{ secrets.GOOGLE_CLOUD_DL_SA }}' credentials_json: '${{ secrets.GOOGLE_CLOUD_DL_SA }}'

View File

@ -1,37 +0,0 @@
name: Create Release
on:
push:
branches:
- main
jobs:
create-release:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: read
if: contains(github.event.head_commit.message, 'Cut release v')
steps:
- uses: actions/github-script@v7
name: Read Cut release PR info and create release
with:
script: |
const { owner, repo } = context.repo
const pulls = await github.rest.repos.listPullRequestsAssociatedWithCommit({
owner,
repo,
commit_sha: context.sha,
})
const { title, body } = pulls.data[0]
const version = title.split('Cut release ')[1]
const result = await github.rest.repos.createRelease({
owner,
repo,
body,
tag_name: version,
name: version,
draft: true,
})
console.log(result)

View File

@ -27,7 +27,7 @@ jobs:
- id: filter - id: filter
name: Check for Rust changes name: Check for Rust changes
uses: dorny/paths-filter@v3 uses: dorny/paths-filter@v2
with: with:
filters: | filters: |
rust: rust:
@ -46,45 +46,40 @@ jobs:
- uses: KittyCAD/action-install-cli@main - uses: KittyCAD/action-install-cli@main
- name: Install dependencies - name: Install dependencies
run: yarn 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 - name: Install Playwright Browsers
run: yarn playwright install --with-deps run: yarn playwright install --with-deps
- name: Download Wasm Cache - name: download wasm
id: download-wasm id: download-wasm
if: needs.check-rust-changes.outputs.rust-changed == 'false' if: needs.check-rust-changes.outputs.rust-changed == 'false'
uses: dawidd6/action-download-artifact@v5 uses: actions/download-artifact@v2
continue-on-error: true continue-on-error: true
with: with:
github_token: ${{secrets.GITHUB_TOKEN}}
name: wasm-bundle name: wasm-bundle
workflow: build-and-store-wasm.yml
branch: main
path: src/wasm-lib/pkg path: src/wasm-lib/pkg
- name: copy wasm blob - name: copy wasm blob
if: needs.check-rust-changes.outputs.rust-changed == 'false' if: needs.check-rust-changes.outputs.rust-changed == 'false'
run: cp src/wasm-lib/pkg/wasm_lib_bg.wasm public run: cp src/wasm-lib/pkg/wasm_lib_bg.wasm public
continue-on-error: true continue-on-error: true
- name: Setup Rust - name: Setup Rust
if: needs.check-rust-changes.outputs.rust-changed == 'true'
uses: dtolnay/rust-toolchain@stable uses: dtolnay/rust-toolchain@stable
- name: Cache Wasm (because rust diff) - name: Setup Rust
if: steps.download-wasm.outcome == 'failure'
uses: dtolnay/rust-toolchain@stable
- name: Cache wasm
if: needs.check-rust-changes.outputs.rust-changed == 'true' if: needs.check-rust-changes.outputs.rust-changed == 'true'
uses: Swatinem/rust-cache@v2 uses: Swatinem/rust-cache@v2
with: with:
workspaces: './src/wasm-lib' workspaces: './src/wasm-lib'
- name: OR Cache Wasm (because wasm cache failed) - name: Cache wasm
if: steps.download-wasm.outcome == 'failure' if: steps.download-wasm.outcome == 'failure'
uses: Swatinem/rust-cache@v2 uses: Swatinem/rust-cache@v2
with: with:
workspaces: './src/wasm-lib' workspaces: './src/wasm-lib'
- name: Build Wasm (because rust diff) - name: build wasm
if: needs.check-rust-changes.outputs.rust-changed == 'true' if: needs.check-rust-changes.outputs.rust-changed == 'true'
run: yarn build:wasm run: yarn build:wasm
- name: OR Build Wasm (because wasm cache failed) - name: build wasm
if: steps.download-wasm.outcome == 'failure' if: steps.download-wasm.outcome == 'failure'
run: yarn build:wasm run: yarn build:wasm
- name: build web - name: build web
@ -121,7 +116,7 @@ jobs:
git fetch origin git fetch origin
echo ${{ github.head_ref }} echo ${{ github.head_ref }}
git checkout ${{ github.head_ref }} git checkout ${{ github.head_ref }}
# TODO when webkit works on ubuntu remove the os part of the commit message # TODO when safari works on ubuntu remove the os part of the commit message
git commit -am "A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)" || true git commit -am "A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)" || true
git push git push
git push origin ${{ github.head_ref }} git push origin ${{ github.head_ref }}
@ -136,6 +131,11 @@ jobs:
name: playwright-report name: playwright-report
path: playwright-report/ path: playwright-report/
retention-days: 30 retention-days: 30
- uses: actions/upload-artifact@v4
if: github.ref == 'refs/heads/main'
with:
name: wasm-bundle
path: src/wasm-lib/pkg
playwright-macos: playwright-macos:
timeout-minutes: 60 timeout-minutes: 60
@ -149,53 +149,46 @@ jobs:
cache: 'yarn' cache: 'yarn'
- name: Install dependencies - name: Install dependencies
run: yarn run: yarn
- name: Cache Playwright Browsers
uses: actions/cache@v4
with:
path: |
~/.cache/ms-playwright
key: ${{ runner.os }}-playwright-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-playwright-
- name: Install Playwright Browsers - name: Install Playwright Browsers
run: yarn playwright install --with-deps run: yarn playwright install --with-deps
- name: Download Wasm Cache - name: download wasm
id: download-wasm id: download-wasm
if: needs.check-rust-changes.outputs.rust-changed == 'false' if: needs.check-rust-changes.outputs.rust-changed == 'false'
uses: dawidd6/action-download-artifact@v5 uses: actions/download-artifact@v4
continue-on-error: true
with: with:
github_token: ${{secrets.GITHUB_TOKEN}}
name: wasm-bundle name: wasm-bundle
workflow: build-and-store-wasm.yml
branch: main
path: src/wasm-lib/pkg path: src/wasm-lib/pkg
continue-on-error: true
- name: copy wasm blob - name: copy wasm blob
if: needs.check-rust-changes.outputs.rust-changed == 'false' if: needs.check-rust-changes.outputs.rust-changed == 'false'
run: cp src/wasm-lib/pkg/wasm_lib_bg.wasm public run: cp src/wasm-lib/pkg/wasm_lib_bg.wasm public
continue-on-error: true continue-on-error: true
- name: Setup Rust - name: Setup Rust
if: needs.check-rust-changes.outputs.rust-changed == 'true'
uses: dtolnay/rust-toolchain@stable uses: dtolnay/rust-toolchain@stable
- name: Cache Wasm (because rust diff) - name: Setup Rust
if: steps.download-wasm.outcome == 'failure'
uses: dtolnay/rust-toolchain@stable
- name: Cache wasm
if: needs.check-rust-changes.outputs.rust-changed == 'true' if: needs.check-rust-changes.outputs.rust-changed == 'true'
uses: Swatinem/rust-cache@v2 uses: Swatinem/rust-cache@v2
with: with:
workspaces: './src/wasm-lib' workspaces: './src/wasm-lib'
- name: OR Cache Wasm (because wasm cache failed) - name: Cache wasm
if: steps.download-wasm.outcome == 'failure' if: steps.download-wasm.outcome == 'failure'
uses: Swatinem/rust-cache@v2 uses: Swatinem/rust-cache@v2
with: with:
workspaces: './src/wasm-lib' workspaces: './src/wasm-lib'
- name: Build Wasm (because rust diff) - name: build wasm
if: needs.check-rust-changes.outputs.rust-changed == 'true' if: needs.check-rust-changes.outputs.rust-changed == 'true'
run: yarn build:wasm run: yarn build:wasm
- name: OR Build Wasm (because wasm cache failed) - name: build wasm
if: steps.download-wasm.outcome == 'failure' if: steps.download-wasm.outcome == 'failure'
run: yarn build:wasm run: yarn build:wasm
- name: build web - name: build web
run: yarn build:local run: yarn build:local
- name: Run macos/safari flow - name: Run macos/safari flow
# webkit doesn't work on Ubuntu because of the same reason tauri doesn't (webRTC issues) # safari doesn't work on Ubuntu because of the same reason tauri doesn't (webRTC issues)
# TODO remove this and the matrix and run all tests on ubuntu when this is fixed # TODO remove this and the matrix and run all tests on ubuntu when this is fixed
run: yarn playwright test --project="webkit" e2e/playwright/flow-tests.spec.ts run: yarn playwright test --project="webkit" e2e/playwright/flow-tests.spec.ts
env: env:
@ -207,3 +200,8 @@ jobs:
name: playwright-report name: playwright-report
path: playwright-report/ path: playwright-report/
retention-days: 30 retention-days: 30
- uses: actions/upload-artifact@v4
if: github.ref == 'refs/heads/main'
with:
name: wasm-bundle
path: src/wasm-lib/pkg

1
.gitignore vendored
View File

@ -54,4 +54,3 @@ src/**/*.typegen.ts
src-tauri/gen src-tauri/gen
src/wasm-lib/grackle/stdlib_cube_partial.json src/wasm-lib/grackle/stdlib_cube_partial.json
Mac_App_Distribution.provisionprofile

View File

@ -1,14 +0,0 @@
.PHONY: dev
WASM_LIB_FILES := $(wildcard src/wasm-lib/**/*.rs)
dev: node_modules public/wasm_lib_bg.wasm
yarn start
public/wasm_lib_bg.wasm: $(WASM_LIB_FILES)
yarn build:wasm-dev
node_modules: package.json
package.json:
yarn install

View File

@ -60,12 +60,6 @@ followed by:
yarn build:wasm-dev yarn build:wasm-dev
``` ```
or if you have the gh cli installed
```
./get-latest-wasm-bundle.sh # this will download the latest main wasm bundle
```
That will build the WASM binary and put in the `public` dir (though gitignored) That will build the WASM binary and put in the `public` dir (though gitignored)
finally, to run the web app only, run: finally, to run the web app only, run:
@ -74,13 +68,7 @@ finally, to run the web app only, run:
yarn start yarn start
``` ```
If you're not an KittyCAD employee you won't be able to access the dev environment, you should copy everything from `.env.production` to `.env.development` to make it point to production instead, then when you navigate to `localhost:3000` the easiest way to sign in is to paste `localStorage.setItem('TOKEN_PERSIST_KEY', "your-token-from-https://zoo.dev/account/api-tokens")` replacing the with a real token from https://zoo.dev/account/api-tokens ofcourse, then navigate to localhost:3000 again. Note that navigating to localhost:3000/signin removes your token so you will need to set the token again. ## Developing in Chrome
### 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 `dev.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.
### Developing in Chrome
Chrome is in the process of rolling out a new default which Chrome is in the process of rolling out a new default which
[blocks Third-Party Cookies](https://developer.chrome.com/en/docs/privacy-sandbox/third-party-cookie-phase-out/). [blocks Third-Party Cookies](https://developer.chrome.com/en/docs/privacy-sandbox/third-party-cookie-phase-out/).
@ -102,7 +90,6 @@ yarn test
Which will run our suite of [Vitest unit](https://vitest.dev/) and [React Testing Library E2E](https://testing-library.com/docs/react-testing-library/intro/) tests, in interactive mode by default. Which will run our suite of [Vitest unit](https://vitest.dev/) and [React Testing Library E2E](https://testing-library.com/docs/react-testing-library/intro/) tests, in interactive mode by default.
For running the rust (not tauri rust though) only, you can For running the rust (not tauri rust though) only, you can
```bash ```bash
cd src/wasm-lib cd src/wasm-lib
cargo test cargo test
@ -165,7 +152,6 @@ console.log(
- `) - `)
) )
``` ```
grab the md list and delete any that are older than the last bump grab the md list and delete any that are older than the last bump
2. Merge the PR 2. Merge the PR
@ -195,49 +181,39 @@ $ cargo +nightly fuzz run parser
For more information on fuzzing you can check out For more information on fuzzing you can check out
[this guide](https://rust-fuzz.github.io/book/cargo-fuzz.html). [this guide](https://rust-fuzz.github.io/book/cargo-fuzz.html).
### Playwright ### Playwright
For a portable way to run Playwright you'll need Docker. First time running plawright locally, you'll need to add the secrets file
After that, open a terminal and run:
```bash ```bash
docker run --network host --rm --init -it playwright/chrome:playwright-1.43.1 touch ./e2e/playwright/playwright-secrets.env
``` printf 'token="your-token"\nsnapshottoken="your-snapshot-token"' > ./e2e/playwright/playwright-secrets.env
and in another terminal, run:
```bash
PW_TEST_CONNECT_WS_ENDPOINT=ws://127.0.0.1:4444/ yarn playwright test --project="Google Chrome" <test suite>
```
An example of a `<test suite>` is: `e2e/playwright/flow-tests.spec.ts`
YOU WILL NEED A PLAYWRIGHT-SECRETS.ENV FILE:
```bash
# ./e2e/playwright/playwright-secrets.env
token=<your-token>
snapshottoken=<your-snapshot-token>
``` ```
then replace "your-token" with a dev token from dev.zoo.dev/account/api-tokens then replace "your-token" with a dev token from dev.zoo.dev/account/api-tokens
then:
run playwright
```
yarn playwright test
```
run a specific test suite
```
yarn playwright test src/e2e-tests/example.spec.ts
```
run a specific test change the test from `test('...` to `test.only('...` run a specific test change the test from `test('...` to `test.only('...`
(note if you commit this, the tests will instantly fail without running any of the tests) (note if you commit this, the tests will instantly fail without running any of the tests)
run headed run headed
``` ```
yarn playwright test --headed yarn playwright test --headed
``` ```
run with step through debugger run with step through debugger
``` ```
PWDEBUG=1 yarn playwright test PWDEBUG=1 yarn playwright test
``` ```
However, if you want a debugger I recommend using VSCode and the `playwright` extension, as the above command is a cruder debugger that steps into every function call which is annoying. However, if you want a debugger I recommend using VSCode and the `playwright` extension, as the above command is a cruder debugger that steps into every function call which is annoying.
With the extension you can set a breakpoint after `waitForDefaultPlanesVisibilityChange` in order to skip app loading, then the vscode debugger's "step over" is much better for being able to stay at the right level of abstraction as you debug the code. With the extension you can set a breakpoint after `waitForDefaultPlanesVisibilityChange` in order to skip app loading, then the vscode debugger's "step over" is much better for being able to stay at the right level of abstraction as you debug the code.
@ -282,6 +258,7 @@ Where `./store` should look like this
</details> </details>
However because much of our tests involve clicking in the stream at specific locations, it's code-gen looks `await page.locator('video').click();` when really we need to use a pixel coord, so I think it's of limited use. However because much of our tests involve clicking in the stream at specific locations, it's code-gen looks `await page.locator('video').click();` when really we need to use a pixel coord, so I think it's of limited use.
#### Some notes on CI #### Some notes on CI
@ -312,26 +289,3 @@ PS: for the debug panel, the following JSON is useful for snapping the camera
``` ```
</details> </details>
### Tauri e2e tests
#### Windows (local only until the CI edge version mismatch is fixed)
```
yarn install
yarn build:wasm
cp src/wasm-lib/pkg/wasm_lib_bg.wasm public
yarn vite build --mode development
yarn tauri build --debug -b
$env:KITTYCAD_API_TOKEN="<YOUR_KITTYCAD_API_TOKEN>"
$env:VITE_KC_API_BASE_URL="https://api.dev.zoo.dev"
$env:E2E_TAURI_ENABLED="true"
$env:TS_NODE_COMPILER_OPTIONS='{"module": "commonjs"}'
$env:E2E_APPLICATION=".\src-tauri\target\debug\Zoo Modeling App.exe"
Stop-Process -Name msedgedriver
yarn wdio run wdio.conf.ts
```
## KCL
For how to contribute to KCL, [see our KCL README](https://github.com/KittyCAD/modeling-app/tree/main/src/wasm-lib/kcl).

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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

View File

@ -23,7 +23,6 @@ layout: manual
* [`atan`](kcl/atan) * [`atan`](kcl/atan)
* [`bezierCurve`](kcl/bezierCurve) * [`bezierCurve`](kcl/bezierCurve)
* [`ceil`](kcl/ceil) * [`ceil`](kcl/ceil)
* [`chamfer`](kcl/chamfer)
* [`circle`](kcl/circle) * [`circle`](kcl/circle)
* [`close`](kcl/close) * [`close`](kcl/close)
* [`cos`](kcl/cos) * [`cos`](kcl/cos)
@ -32,6 +31,7 @@ layout: manual
* [`fillet`](kcl/fillet) * [`fillet`](kcl/fillet)
* [`floor`](kcl/floor) * [`floor`](kcl/floor)
* [`getEdge`](kcl/getEdge) * [`getEdge`](kcl/getEdge)
* [`getExtrudeWallTransform`](kcl/getExtrudeWallTransform)
* [`getNextAdjacentEdge`](kcl/getNextAdjacentEdge) * [`getNextAdjacentEdge`](kcl/getNextAdjacentEdge)
* [`getOppositeEdge`](kcl/getOppositeEdge) * [`getOppositeEdge`](kcl/getOppositeEdge)
* [`getPreviousAdjacentEdge`](kcl/getPreviousAdjacentEdge) * [`getPreviousAdjacentEdge`](kcl/getPreviousAdjacentEdge)
@ -57,15 +57,11 @@ layout: manual
* [`patternLinear3d`](kcl/patternLinear3d) * [`patternLinear3d`](kcl/patternLinear3d)
* [`pi`](kcl/pi) * [`pi`](kcl/pi)
* [`pow`](kcl/pow) * [`pow`](kcl/pow)
* [`profileStart`](kcl/profileStart)
* [`profileStartX`](kcl/profileStartX)
* [`profileStartY`](kcl/profileStartY)
* [`revolve`](kcl/revolve) * [`revolve`](kcl/revolve)
* [`segAng`](kcl/segAng) * [`segAng`](kcl/segAng)
* [`segEndX`](kcl/segEndX) * [`segEndX`](kcl/segEndX)
* [`segEndY`](kcl/segEndY) * [`segEndY`](kcl/segEndY)
* [`segLen`](kcl/segLen) * [`segLen`](kcl/segLen)
* [`shell`](kcl/shell)
* [`sin`](kcl/sin) * [`sin`](kcl/sin)
* [`sqrt`](kcl/sqrt) * [`sqrt`](kcl/sqrt)
* [`startProfileAt`](kcl/startProfileAt) * [`startProfileAt`](kcl/startProfileAt)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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 it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 249 KiB

After

Width:  |  Height:  |  Size: 224 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 249 KiB

After

Width:  |  Height:  |  Size: 224 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 249 KiB

After

Width:  |  Height:  |  Size: 224 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 171 KiB

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 171 KiB

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 171 KiB

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 171 KiB

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 249 KiB

After

Width:  |  Height:  |  Size: 224 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 171 KiB

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 171 KiB

After

Width:  |  Height:  |  Size: 165 KiB

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,10 @@
import { test, expect } from '@playwright/test' import { test, expect, Download } from '@playwright/test'
import { secrets } from './secrets' import { secrets } from './secrets'
import { Paths, doExport, getUtils } from './test-utils' import { getUtils } from './test-utils'
import { Models } from '@kittycad/lib' import { Models } from '@kittycad/lib'
import fsp from 'fs/promises' import fsp from 'fs/promises'
import { spawn } from 'child_process' import { spawn } from 'child_process'
import { KCL_DEFAULT_LENGTH } from 'lib/constants' import { APP_NAME } from 'lib/constants'
import JSZip from 'jszip' import JSZip from 'jszip'
import path from 'path' import path from 'path'
import { TEST_SETTINGS, TEST_SETTINGS_KEY } from './storageStates' import { TEST_SETTINGS, TEST_SETTINGS_KEY } from './storageStates'
@ -20,7 +20,6 @@ test.beforeEach(async ({ page }) => {
localStorage.setItem('TOKEN_PERSIST_KEY', token) localStorage.setItem('TOKEN_PERSIST_KEY', token)
localStorage.setItem('persistCode', ``) localStorage.setItem('persistCode', ``)
localStorage.setItem(settingsKey, settings) localStorage.setItem(settingsKey, settings)
localStorage.setItem('playwright', 'true')
}, },
{ {
token: secrets.token, token: secrets.token,
@ -45,7 +44,7 @@ test.setTimeout(60_000)
test('exports of each format should work', async ({ page, context }) => { test('exports of each format should work', async ({ page, context }) => {
// FYI this test doesn't work with only engine running locally // FYI this test doesn't work with only engine running locally
// And you will need to have the KittyCAD CLI installed // And you will need to have the KittyCAD CLI installed
const u = await getUtils(page) const u = getUtils(page)
await context.addInitScript(async () => { await context.addInitScript(async () => {
;(window as any).playwrightSkipFilePicker = true ;(window as any).playwrightSkipFilePicker = true
localStorage.setItem( localStorage.setItem(
@ -99,6 +98,78 @@ const part001 = startSketchOn('-XZ')
await page.waitForTimeout(1000) await page.waitForTimeout(1000)
await u.clearAndCloseDebugPanel() await u.clearAndCloseDebugPanel()
interface Paths {
modelPath: string
imagePath: string
outputType: string
}
const doExport = async (
output: Models['OutputFormat_type']
): Promise<Paths> => {
await page.getByRole('button', { name: APP_NAME }).click()
await expect(
page.getByRole('button', { name: 'Export Part' })
).toBeVisible()
await page.getByRole('button', { name: 'Export Part' }).click()
await expect(page.getByTestId('command-bar')).toBeVisible()
// Go through export via command bar
await page.getByRole('option', { name: output.type, exact: false }).click()
await page.locator('#arg-form').waitFor({ state: 'detached' })
if ('storage' in output) {
await page.getByTestId('arg-name-storage').waitFor({ timeout: 1000 })
await page.getByRole('button', { name: 'storage', exact: false }).click()
await page
.getByRole('option', { name: output.storage, exact: false })
.click()
await page.locator('#arg-form').waitFor({ state: 'detached' })
}
await expect(page.getByText('Confirm Export')).toBeVisible()
const getPromiseAndResolve = () => {
let resolve: any = () => {}
const promise = new Promise<Download>((r) => {
resolve = r
})
return [promise, resolve]
}
const [downloadPromise1, downloadResolve1] = getPromiseAndResolve()
let downloadCnt = 0
page.on('download', async (download) => {
if (downloadCnt === 0) {
downloadResolve1(download)
}
downloadCnt++
})
await page.getByRole('button', { name: 'Submit command' }).click()
// Handle download
const download = await downloadPromise1
const downloadLocationer = (extra = '', isImage = false) =>
`./e2e/playwright/export-snapshots/${output.type}-${
'storage' in output ? output.storage : ''
}${extra}.${isImage ? 'png' : output.type}`
const downloadLocation = downloadLocationer()
await download.saveAs(downloadLocation)
if (output.type === 'step') {
// stable timestamps for step files
const fileContents = await fsp.readFile(downloadLocation, 'utf-8')
const newFileContents = fileContents.replace(
/[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]+[0-9]+[0-9]\+[0-9]{2}:[0-9]{2}/g,
'1970-01-01T00:00:00.0+00:00'
)
await fsp.writeFile(downloadLocation, newFileContents)
}
return {
modelPath: downloadLocation,
imagePath: downloadLocationer('', true),
outputType: output.type,
}
}
const axisDirectionPair: Models['AxisDirectionPair_type'] = { const axisDirectionPair: Models['AxisDirectionPair_type'] = {
axis: 'z', axis: 'z',
direction: 'positive', direction: 'positive',
@ -114,114 +185,84 @@ const part001 = startSketchOn('-XZ')
// just note that only `type` and `storage` are used for selecting the drop downs is the app // just note that only `type` and `storage` are used for selecting the drop downs is the app
// the rest are only there to make typescript happy // the rest are only there to make typescript happy
exportLocations.push( exportLocations.push(
await doExport( await doExport({
{
type: 'step', type: 'step',
coords: sysType, coords: sysType,
}, })
page
)
) )
exportLocations.push( exportLocations.push(
await doExport( await doExport({
{
type: 'ply', type: 'ply',
coords: sysType, coords: sysType,
selection: { type: 'default_scene' }, selection: { type: 'default_scene' },
storage: 'ascii', storage: 'ascii',
units: 'in', units: 'in',
}, })
page
)
) )
exportLocations.push( exportLocations.push(
await doExport( await doExport({
{
type: 'ply', type: 'ply',
storage: 'binary_little_endian', storage: 'binary_little_endian',
coords: sysType, coords: sysType,
selection: { type: 'default_scene' }, selection: { type: 'default_scene' },
units: 'in', units: 'in',
}, })
page
)
) )
exportLocations.push( exportLocations.push(
await doExport( await doExport({
{
type: 'ply', type: 'ply',
storage: 'binary_big_endian', storage: 'binary_big_endian',
coords: sysType, coords: sysType,
selection: { type: 'default_scene' }, selection: { type: 'default_scene' },
units: 'in', units: 'in',
}, })
page
)
) )
exportLocations.push( exportLocations.push(
await doExport( await doExport({
{
type: 'stl', type: 'stl',
storage: 'ascii', storage: 'ascii',
coords: sysType, coords: sysType,
units: 'in', units: 'in',
selection: { type: 'default_scene' }, selection: { type: 'default_scene' },
}, })
page
)
) )
exportLocations.push( exportLocations.push(
await doExport( await doExport({
{
type: 'stl', type: 'stl',
storage: 'binary', storage: 'binary',
coords: sysType, coords: sysType,
units: 'in', units: 'in',
selection: { type: 'default_scene' }, selection: { type: 'default_scene' },
}, })
page
)
) )
exportLocations.push( exportLocations.push(
await doExport( await doExport({
{
// obj seems to be a little flaky, times out tests sometimes // obj seems to be a little flaky, times out tests sometimes
type: 'obj', type: 'obj',
coords: sysType, coords: sysType,
units: 'in', units: 'in',
}, })
page
)
) )
exportLocations.push( exportLocations.push(
await doExport( await doExport({
{
type: 'gltf', type: 'gltf',
storage: 'embedded', storage: 'embedded',
presentation: 'pretty', presentation: 'pretty',
}, })
page
)
) )
exportLocations.push( exportLocations.push(
await doExport( await doExport({
{
type: 'gltf', type: 'gltf',
storage: 'binary', storage: 'binary',
presentation: 'pretty', presentation: 'pretty',
}, })
page
)
) )
exportLocations.push( exportLocations.push(
await doExport( await doExport({
{
type: 'gltf', type: 'gltf',
storage: 'standard', storage: 'standard',
presentation: 'pretty', presentation: 'pretty',
}, })
page
)
) )
// close page to disconnect websocket since we can only have one open atm // close page to disconnect websocket since we can only have one open atm
@ -232,8 +273,6 @@ const part001 = startSketchOn('-XZ')
for (let { modelPath, imagePath, outputType } of exportLocations) { for (let { modelPath, imagePath, outputType } of exportLocations) {
// May change depending on the file being dealt with // May change depending on the file being dealt with
let cliCommand = `export ZOO_TOKEN=${secrets.snapshottoken} && zoo file snapshot --output-format=png --src-format=${outputType} ${modelPath} ${imagePath}` let cliCommand = `export ZOO_TOKEN=${secrets.snapshottoken} && zoo file snapshot --output-format=png --src-format=${outputType} ${modelPath} ${imagePath}`
const fileSize = (await fsp.stat(modelPath)).size
console.log(`Size of the file at ${modelPath}: ${fileSize} bytes`)
const parentPath = path.dirname(modelPath) const parentPath = path.dirname(modelPath)
@ -328,7 +367,7 @@ const extrudeDefaultPlane = async (context: any, page: any, plane: string) => {
localStorage.setItem('persistCode', code) localStorage.setItem('persistCode', code)
}) })
const u = await getUtils(page) const u = getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 }) await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/') await page.goto('/')
await u.waitForAuthSkipAppStart() await u.waitForAuthSkipAppStart()
@ -383,7 +422,7 @@ test.describe('extrude on default planes should be stable', () => {
}) })
test('Draft segments should look right', async ({ page, context }) => { test('Draft segments should look right', async ({ page, context }) => {
const u = await getUtils(page) const u = getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 }) await page.setViewportSize({ width: 1200, height: 500 })
const PUR = 400 / 37.5 //pixeltoUnitRatio const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.goto('/') await page.goto('/')
@ -406,7 +445,7 @@ test('Draft segments should look right', async ({ page, context }) => {
await page.mouse.click(700, 200) await page.mouse.click(700, 200)
await expect(page.locator('.cm-content')).toHaveText( await expect(page.locator('.cm-content')).toHaveText(
`const sketch001 = startSketchOn('XZ')` `const part001 = startSketchOn('-XZ')`
) )
await page.waitForTimeout(300) // TODO detect animation ending, or disable animation await page.waitForTimeout(300) // TODO detect animation ending, or disable animation
@ -414,7 +453,7 @@ test('Draft segments should look right', async ({ page, context }) => {
const startXPx = 600 const startXPx = 600
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10) await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
await expect(page.locator('.cm-content')) await expect(page.locator('.cm-content'))
.toHaveText(`const sketch001 = startSketchOn('XZ') .toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt([9.06, -12.22], %)`) |> startProfileAt([9.06, -12.22], %)`)
await page.waitForTimeout(100) await page.waitForTimeout(100)
@ -428,7 +467,7 @@ test('Draft segments should look right', async ({ page, context }) => {
await page.waitForTimeout(100) await page.waitForTimeout(100)
await expect(page.locator('.cm-content')) await expect(page.locator('.cm-content'))
.toHaveText(`const sketch001 = startSketchOn('XZ') .toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt([9.06, -12.22], %) |> startProfileAt([9.06, -12.22], %)
|> line([9.14, 0], %)`) |> line([9.14, 0], %)`)
@ -442,7 +481,7 @@ test('Draft segments should look right', async ({ page, context }) => {
}) })
test('Draft rectangles should look right', async ({ page, context }) => { test('Draft rectangles should look right', async ({ page, context }) => {
const u = await getUtils(page) const u = getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 }) await page.setViewportSize({ width: 1200, height: 500 })
const PUR = 400 / 37.5 //pixeltoUnitRatio const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.goto('/') await page.goto('/')
@ -465,10 +504,10 @@ test('Draft rectangles should look right', async ({ page, context }) => {
await page.mouse.click(700, 200) await page.mouse.click(700, 200)
await expect(page.locator('.cm-content')).toHaveText( await expect(page.locator('.cm-content')).toHaveText(
`const sketch001 = startSketchOn('XZ')` `const part001 = startSketchOn('-XZ')`
) )
await page.waitForTimeout(500) // TODO detect animation ending, or disable animation await page.waitForTimeout(300) // TODO detect animation ending, or disable animation
await u.closeDebugPanel() await u.closeDebugPanel()
const startXPx = 600 const startXPx = 600
@ -489,7 +528,7 @@ test('Draft rectangles should look right', async ({ page, context }) => {
test.describe('Client side scene scale should match engine scale', () => { test.describe('Client side scene scale should match engine scale', () => {
test('Inch scale', async ({ page }) => { test('Inch scale', async ({ page }) => {
const u = await getUtils(page) const u = getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 }) await page.setViewportSize({ width: 1200, height: 500 })
const PUR = 400 / 37.5 //pixeltoUnitRatio const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.goto('/') await page.goto('/')
@ -514,7 +553,7 @@ test.describe('Client side scene scale should match engine scale', () => {
await page.mouse.click(700, 200) await page.mouse.click(700, 200)
await expect(page.locator('.cm-content')).toHaveText( await expect(page.locator('.cm-content')).toHaveText(
`const sketch001 = startSketchOn('XZ')` `const part001 = startSketchOn('-XZ')`
) )
await page.waitForTimeout(300) // TODO detect animation ending, or disable animation await page.waitForTimeout(300) // TODO detect animation ending, or disable animation
@ -522,7 +561,7 @@ test.describe('Client side scene scale should match engine scale', () => {
const startXPx = 600 const startXPx = 600
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10) await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
await expect(page.locator('.cm-content')) await expect(page.locator('.cm-content'))
.toHaveText(`const sketch001 = startSketchOn('XZ') .toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt([9.06, -12.22], %)`) |> startProfileAt([9.06, -12.22], %)`)
await page.waitForTimeout(100) await page.waitForTimeout(100)
@ -532,7 +571,7 @@ test.describe('Client side scene scale should match engine scale', () => {
await page.waitForTimeout(100) await page.waitForTimeout(100)
await expect(page.locator('.cm-content')) await expect(page.locator('.cm-content'))
.toHaveText(`const sketch001 = startSketchOn('XZ') .toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt([9.06, -12.22], %) |> startProfileAt([9.06, -12.22], %)
|> line([9.14, 0], %)`) |> line([9.14, 0], %)`)
@ -542,7 +581,7 @@ test.describe('Client side scene scale should match engine scale', () => {
await page.mouse.click(startXPx + PUR * 30, 500 - PUR * 20) await page.mouse.click(startXPx + PUR * 30, 500 - PUR * 20)
await expect(page.locator('.cm-content')) await expect(page.locator('.cm-content'))
.toHaveText(`const sketch001 = startSketchOn('XZ') .toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt([9.06, -12.22], %) |> startProfileAt([9.06, -12.22], %)
|> line([9.14, 0], %) |> line([9.14, 0], %)
|> tangentialArcTo([27.34, -3.08], %)`) |> tangentialArcTo([27.34, -3.08], %)`)
@ -558,15 +597,12 @@ test.describe('Client side scene scale should match engine scale', () => {
// exit sketch // exit sketch
await u.openAndClearDebugPanel() await u.openAndClearDebugPanel()
await u.doAndWaitForImageDiff( await page.getByRole('button', { name: 'Exit Sketch' }).click()
() => page.getByRole('button', { name: 'Exit Sketch' }).click(),
200
)
// wait for execution done // wait for execution done
await u.expectCmdLog('[data-message-type="execution-done"]') await u.expectCmdLog('[data-message-type="execution-done"]')
await u.clearAndCloseDebugPanel() await u.clearAndCloseDebugPanel()
await page.waitForTimeout(300) await page.waitForTimeout(200)
// second screen shot should look almost identical, i.e. scale should be the same. // second screen shot should look almost identical, i.e. scale should be the same.
await expect(page).toHaveScreenshot({ await expect(page).toHaveScreenshot({
@ -592,7 +628,7 @@ test.describe('Client side scene scale should match engine scale', () => {
}), }),
} }
) )
const u = await getUtils(page) const u = getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 }) await page.setViewportSize({ width: 1200, height: 500 })
const PUR = 400 / 37.5 //pixeltoUnitRatio const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.goto('/') await page.goto('/')
@ -617,7 +653,7 @@ test.describe('Client side scene scale should match engine scale', () => {
await page.mouse.click(700, 200) await page.mouse.click(700, 200)
await expect(page.locator('.cm-content')).toHaveText( await expect(page.locator('.cm-content')).toHaveText(
`const sketch001 = startSketchOn('XZ')` `const part001 = startSketchOn('-XZ')`
) )
await page.waitForTimeout(300) // TODO detect animation ending, or disable animation await page.waitForTimeout(300) // TODO detect animation ending, or disable animation
@ -625,7 +661,7 @@ test.describe('Client side scene scale should match engine scale', () => {
const startXPx = 600 const startXPx = 600
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10) await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
await expect(page.locator('.cm-content')) await expect(page.locator('.cm-content'))
.toHaveText(`const sketch001 = startSketchOn('XZ') .toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt([230.03, -310.32], %)`) |> startProfileAt([230.03, -310.32], %)`)
await page.waitForTimeout(100) await page.waitForTimeout(100)
@ -635,7 +671,7 @@ test.describe('Client side scene scale should match engine scale', () => {
await page.waitForTimeout(100) await page.waitForTimeout(100)
await expect(page.locator('.cm-content')) await expect(page.locator('.cm-content'))
.toHaveText(`const sketch001 = startSketchOn('XZ') .toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt([230.03, -310.32], %) |> startProfileAt([230.03, -310.32], %)
|> line([232.2, 0], %)`) |> line([232.2, 0], %)`)
@ -645,7 +681,7 @@ test.describe('Client side scene scale should match engine scale', () => {
await page.mouse.click(startXPx + PUR * 30, 500 - PUR * 20) await page.mouse.click(startXPx + PUR * 30, 500 - PUR * 20)
await expect(page.locator('.cm-content')) await expect(page.locator('.cm-content'))
.toHaveText(`const sketch001 = startSketchOn('XZ') .toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt([230.03, -310.32], %) |> startProfileAt([230.03, -310.32], %)
|> line([232.2, 0], %) |> line([232.2, 0], %)
|> tangentialArcTo([694.43, -78.12], %)`) |> tangentialArcTo([694.43, -78.12], %)`)
@ -660,15 +696,12 @@ test.describe('Client side scene scale should match engine scale', () => {
// exit sketch // exit sketch
await u.openAndClearDebugPanel() await u.openAndClearDebugPanel()
await u.doAndWaitForImageDiff( await page.getByRole('button', { name: 'Exit Sketch' }).click()
() => page.getByRole('button', { name: 'Exit Sketch' }).click(),
200
)
// wait for execution done // wait for execution done
await u.expectCmdLog('[data-message-type="execution-done"]') await u.expectCmdLog('[data-message-type="execution-done"]')
await u.clearAndCloseDebugPanel() await u.clearAndCloseDebugPanel()
await page.waitForTimeout(300) await page.waitForTimeout(200)
// second screen shot should look almost identical, i.e. scale should be the same. // second screen shot should look almost identical, i.e. scale should be the same.
await expect(page).toHaveScreenshot({ await expect(page).toHaveScreenshot({
@ -678,8 +711,8 @@ test.describe('Client side scene scale should match engine scale', () => {
}) })
test('Sketch on face with none z-up', async ({ page, context }) => { test('Sketch on face with none z-up', async ({ page, context }) => {
const u = await getUtils(page) const u = getUtils(page)
await context.addInitScript(async (KCL_DEFAULT_LENGTH) => { await context.addInitScript(async () => {
localStorage.setItem( localStorage.setItem(
'persistCode', 'persistCode',
`const part001 = startSketchOn('-XZ') `const part001 = startSketchOn('-XZ')
@ -687,16 +720,16 @@ test('Sketch on face with none z-up', async ({ page, context }) => {
|> line([9.31, 10.55], %, 'seg01') |> line([9.31, 10.55], %, 'seg01')
|> line([11.91, -10.42], %) |> line([11.91, -10.42], %)
|> close(%) |> close(%)
|> extrude(${KCL_DEFAULT_LENGTH}, %) |> extrude(5 + 7, %)
const part002 = startSketchOn(part001, 'seg01') const part002 = startSketchOn(part001, 'seg01')
|> startProfileAt([8, 8], %) |> startProfileAt([8, 8], %)
|> line([4.68, 3.05], %) |> line([4.68, 3.05], %)
|> line([0, -7.79], %, 'seg02') |> line([0, -7.79], %, 'seg02')
|> close(%) |> close(%)
|> extrude(${KCL_DEFAULT_LENGTH}, %) |> extrude(5 + 7, %)
` `
) )
}, KCL_DEFAULT_LENGTH) })
await page.setViewportSize({ width: 1200, height: 500 }) await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/') await page.goto('/')
@ -732,76 +765,3 @@ const part002 = startSketchOn(part001, 'seg01')
maxDiffPixels: 100, maxDiffPixels: 100,
}) })
}) })
test('Zoom to fit on load - solid 2d', async ({ page, context }) => {
const u = await getUtils(page)
await context.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const part001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
|> line([-20, 0], %)
|> close(%)
`
)
}, KCL_DEFAULT_LENGTH)
await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/')
await u.waitForAuthSkipAppStart()
await u.openDebugPanel()
// wait for execution done
await expect(
page.locator('[data-message-type="execution-done"]')
).toHaveCount(2)
await u.closeDebugPanel()
// Wait for the second extrusion to appear
// TODO: Find a way to truly know that the objects have finished
// rendering, because an execution-done message is not sufficient.
await page.waitForTimeout(1000)
await expect(page).toHaveScreenshot({
maxDiffPixels: 100,
})
})
test('Zoom to fit on load - solid 3d', async ({ page, context }) => {
const u = await getUtils(page)
await context.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const part001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
|> line([-20, 0], %)
|> close(%)
|> extrude(10, %)
`
)
}, KCL_DEFAULT_LENGTH)
await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/')
await u.waitForAuthSkipAppStart()
await u.openDebugPanel()
// wait for execution done
await expect(
page.locator('[data-message-type="execution-done"]')
).toHaveCount(2)
await u.closeDebugPanel()
// Wait for the second extrusion to appear
// TODO: Find a way to truly know that the objects have finished
// rendering, because an execution-done message is not sufficient.
await page.waitForTimeout(1000)
await expect(page).toHaveScreenshot({
maxDiffPixels: 100,
})
})

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Some files were not shown because too many files have changed in this diff Show More