Compare commits

..

1 Commits

Author SHA1 Message Date
2458c9f6fe Test a parser bug 2024-03-27 16:05:51 -05:00
536 changed files with 7696 additions and 17342 deletions

View File

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

View File

@ -9,27 +9,15 @@ updates:
directory: '/' # Location of package manifests directory: '/' # Location of package manifests
schedule: schedule:
interval: 'daily' interval: 'daily'
reviewers:
- franknoirot
- irev-dev
- package-ecosystem: 'github-actions' # See documentation for possible values - package-ecosystem: 'github-actions' # See documentation for possible values
directory: '/' # Location of package manifests directory: '/' # Location of package manifests
schedule: schedule:
interval: 'daily' interval: 'daily'
reviewers:
- adamchalmers
- jessfraz
- package-ecosystem: 'cargo' # See documentation for possible values - package-ecosystem: 'cargo' # See documentation for possible values
directory: '/src/wasm-lib/' # Location of package manifests directory: '/src/wasm-lib/' # Location of package manifests
schedule: schedule:
interval: 'daily' interval: 'daily'
reviewers:
- adamchalmers
- jessfraz
- package-ecosystem: 'cargo' # See documentation for possible values - package-ecosystem: 'cargo' # See documentation for possible values
directory: '/src-tauri/' # Location of package manifests directory: '/src-tauri/' # Location of package manifests
schedule: schedule:
interval: 'daily' interval: 'daily'
reviewers:
- adamchalmers
- jessfraz

View File

@ -7,23 +7,23 @@ on:
- '**/Cargo.toml' - '**/Cargo.toml'
- '**/Cargo.lock' - '**/Cargo.lock'
- '**/rust-toolchain.toml' - '**/rust-toolchain.toml'
- .github/workflows/cargo-bench.yml - .github/workflows/cargo-criterion.yml
pull_request: pull_request:
paths: paths:
- '**.rs' - '**.rs'
- '**/Cargo.toml' - '**/Cargo.toml'
- '**/Cargo.lock' - '**/Cargo.lock'
- '**/rust-toolchain.toml' - '**/rust-toolchain.toml'
- .github/workflows/cargo-bench.yml - .github/workflows/cargo-criterion.yml
workflow_dispatch: workflow_dispatch:
permissions: read-all permissions: read-all
concurrency: concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true cancel-in-progress: true
name: cargo bench name: cargo criterion
jobs: jobs:
cargo-bench: cargocriterion:
name: Benchmark with iai name: cargo criterion
runs-on: ubuntu-latest-8-cores runs-on: ubuntu-latest-8-cores
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -31,12 +31,10 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: | run: |
cargo install cargo-criterion cargo install cargo-criterion
sudo apt update
sudo apt install -y valgrind
- name: Rust Cache - name: Rust Cache
uses: Swatinem/rust-cache@v2.6.1 uses: Swatinem/rust-cache@v2.6.1
- name: Benchmark kcl library - name: Benchmark kcl library
shell: bash shell: bash
run: |- run: |-
cd src/wasm-lib/kcl; cargo bench -- iai cd src/wasm-lib/kcl; cargo criterion

View File

@ -104,11 +104,7 @@ jobs:
run: | run: |
VERSION=$(date +'%-y.%-m.%-d') yarn bump-jsons VERSION=$(date +'%-y.%-m.%-d') yarn bump-jsons
echo "$(jq --arg url 'https://dl.zoo.dev/releases/modeling-app/nightly/last_update.json' \ echo "$(jq --arg url 'https://dl.zoo.dev/releases/modeling-app/nightly/last_update.json' \
'.plugins.updater.endpoints[]=$url' src-tauri/tauri.release.conf.json --indent 2)" > src-tauri/tauri.release.conf.json '.tauri.updater.endpoints[]=$url' src-tauri/tauri.release.conf.json --indent 2)" > src-tauri/tauri.release.conf.json
echo "$(jq --arg id 'dev.zoo.modeling-app-nightly' \
'.identifier=$id' src-tauri/tauri.release.conf.json --indent 2)" > src-tauri/tauri.release.conf.json
echo "$(jq --arg name 'Zoo Modeling App (Nightly)' \
'.productName=$name' src-tauri/tauri.release.conf.json --indent 2)" > src-tauri/tauri.release.conf.json
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v3
if: github.event_name == 'schedule' if: github.event_name == 'schedule'
@ -129,9 +125,6 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
os: [macos-14, ubuntu-latest, windows-latest] os: [macos-14, ubuntu-latest, windows-latest]
env:
TAURI_ARGS_MACOS: ${{ matrix.os == 'macos-14' && '--target universal-apple-darwin' || '' }}
TAURI_ARGS_UBUNTU: ${{ matrix.os == 'ubuntu-latest' && '--bundles' || '' }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -151,12 +144,10 @@ jobs:
sudo apt-get update && sudo apt-get update &&
sudo apt-get install -y sudo apt-get install -y
libgtk-3-dev libgtk-3-dev
libayatana-appindicator3-dev libgtksourceview-3.0-dev
webkit2gtk-4.0
libappindicator3-dev
webkit2gtk-driver webkit2gtk-driver
libsoup-3.0-dev
libjavascriptcoregtk-4.1-dev
libwebkit2gtk-4.1-dev
at-spi2-core
xvfb xvfb
- name: Sync node version and setup cache - name: Sync node version and setup cache
@ -170,9 +161,7 @@ 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'
@ -235,14 +224,14 @@ jobs:
with: with:
includeRelease: false includeRelease: false
includeDebug: true includeDebug: true
args: "${{ env.TAURI_ARGS_MACOS }} ${{ env.TAURI_ARGS_UBUNTU }}" args: ${{ matrix.os == 'macos-14' && '--target universal-apple-darwin' || '' }}
- 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' }}
env: env:
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }} APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
@ -251,7 +240,7 @@ jobs:
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
TAURI_CONF_ARGS: "--config ${{ matrix.os == 'windows-latest' && 'src-tauri\\tauri.release.conf.json' || 'src-tauri/tauri.release.conf.json' }}" TAURI_CONF_ARGS: "--config ${{ matrix.os == 'windows-latest' && 'src-tauri\\tauri.release.conf.json' || 'src-tauri/tauri.release.conf.json' }}"
with: with:
args: "${{ env.TAURI_CONF_ARGS }} ${{ env.TAURI_ARGS_MACOS }} ${{ env.TAURI_ARGS_UBUNTU }}" args: "${{ matrix.os == 'macos-14' && '--target universal-apple-darwin' || '' }} ${{ env.TAURI_CONF_ARGS }}"
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v3
if: matrix.os != 'ubuntu-latest' if: matrix.os != 'ubuntu-latest'
@ -261,11 +250,10 @@ 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: false if: matrix.os == 'ubuntu-latest'
run: | run: |
cargo install tauri-driver cargo install tauri-driver@0.1.3
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
@ -285,7 +273,6 @@ jobs:
NOTES: ${{ github.event_name == 'release' && github.event.release.body || format('Nightly build, commit {0}', github.sha) }} NOTES: ${{ github.event_name == 'release' && github.event.release.body || format('Nightly build, commit {0}', github.sha) }}
BUCKET_DIR: ${{ github.event_name == 'release' && 'dl.kittycad.io/releases/modeling-app' || 'dl.kittycad.io/releases/modeling-app/nightly' }} BUCKET_DIR: ${{ github.event_name == 'release' && 'dl.kittycad.io/releases/modeling-app' || 'dl.kittycad.io/releases/modeling-app/nightly' }}
WEBSITE_DIR: ${{ github.event_name == 'release' && 'dl.zoo.dev/releases/modeling-app' || 'dl.zoo.dev/releases/modeling-app/nightly' }} WEBSITE_DIR: ${{ github.event_name == 'release' && 'dl.zoo.dev/releases/modeling-app' || 'dl.zoo.dev/releases/modeling-app/nightly' }}
URL_CODED_NAME: ${{ github.event_name == 'schedule' && 'Zoo%20Modeling%20App%20%28Nightly%29' || 'Zoo%20Modeling%20App' }}
steps: steps:
- uses: actions/download-artifact@v3 - uses: actions/download-artifact@v3
@ -300,9 +287,9 @@ jobs:
--arg pub_date "${PUB_DATE}" \ --arg pub_date "${PUB_DATE}" \
--arg notes "${NOTES}" \ --arg notes "${NOTES}" \
--arg darwin_sig "$DARWIN_SIG" \ --arg darwin_sig "$DARWIN_SIG" \
--arg darwin_url "$RELEASE_DIR/macos/${{ env.URL_CODED_NAME }}.app.tar.gz" \ --arg darwin_url "$RELEASE_DIR/macos/Zoo%20Modeling%20App.app.tar.gz" \
--arg windows_sig "$WINDOWS_SIG" \ --arg windows_sig "$WINDOWS_SIG" \
--arg windows_url "$RELEASE_DIR/msi/${{ env.URL_CODED_NAME }}_${VERSION_NO_V}_x64_en-US.msi.zip" \ --arg windows_url "$RELEASE_DIR/msi/Zoo%20Modeling%20App_${VERSION_NO_V}_x64_en-US.msi.zip" \
'{ '{
"version": $version, "version": $version,
"pub_date": $pub_date, "pub_date": $pub_date,
@ -331,8 +318,8 @@ jobs:
--arg version "${VERSION}" \ --arg version "${VERSION}" \
--arg pub_date "${PUB_DATE}" \ --arg pub_date "${PUB_DATE}" \
--arg notes "${NOTES}" \ --arg notes "${NOTES}" \
--arg darwin_url "$RELEASE_DIR/dmg/${{ env.URL_CODED_NAME }}_${VERSION_NO_V}_universal.dmg" \ --arg darwin_url "$RELEASE_DIR/dmg/Zoo%20Modeling%20App_${VERSION_NO_V}_universal.dmg" \
--arg windows_url "$RELEASE_DIR/msi/${{ env.URL_CODED_NAME }}_${VERSION_NO_V}_x64_en-US.msi" \ --arg windows_url "$RELEASE_DIR/msi/Zoo%20Modeling%20App_${VERSION_NO_V}_x64_en-US.msi" \
'{ '{
"version": $version, "version": $version,
"pub_date": $pub_date, "pub_date": $pub_date,

View File

@ -56,9 +56,6 @@ jobs:
gh pr create --title "Update KCL docs" \ gh pr create --title "Update KCL docs" \
--body "Updating the generated kcl docs cc @jessfraz @franknoirot merge this" \ --body "Updating the generated kcl docs cc @jessfraz @franknoirot merge this" \
--head "$NEW_BRANCH" \ --head "$NEW_BRANCH" \
--reviewer jessfraz \
--reviewer irev-dev \
--reviewer franknoirot \
--base main || true --base main || true
env: env:
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}

1
.gitignore vendored
View File

@ -51,6 +51,5 @@ e2e/playwright/export-snapshots/*
## generated files ## generated files
src/**/*.typegen.ts src/**/*.typegen.ts
src-tauri/gen
src/wasm-lib/grackle/stdlib_cube_partial.json src/wasm-lib/grackle/stdlib_cube_partial.json

View File

@ -281,7 +281,7 @@ https://github.com/KittyCAD/modeling-app/assets/29681384/6f5e8e85-1003-4fd9-be7f
<details> <details>
<summary> <summary>
PS: for the debug panel, the following JSON is useful for snapping the camera Ps for the debug panel, the following JSON is useful for snapping the camera
</summary> </summary>
```JSON ```JSON

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 120 KiB

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

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

@ -26564,7 +26564,7 @@
"unpublished": false, "unpublished": false,
"deprecated": false, "deprecated": false,
"examples": [ "examples": [
"const box = startSketchOn('XY')\n |> startProfileAt([0, 0], %)\n |> line([0, 10], %)\n |> line([10, 0], %)\n |> line([0, -10], %, 'revolveAxis')\n |> close(%)\n |> extrude(10, %)\n\nconst sketch001 = startSketchOn('XY')\n |> startProfileAt([0, -10], %)\n |> line([0, -10], %)\n |> line([2, 0], %)\n |> line([0, 10], %)\n |> close(%)\n |> revolve({\n axis: getEdge('revolveAxis', box),\n angle: 90\n }, %)" "const box = startSketchOn('XY')\n |> startProfileAt([0, 0], %)\n |> line([0, 10], %)\n |> line([10, 0], %)\n |> line([0, -10], %, 'revolveAxis')\n |> close(%)\n |> extrude(10, %)\n\nconst sketch001 = startSketchOn(box, \"revolveAxis\")\n |> startProfileAt([5, 10], %)\n |> line([0, -10], %)\n |> line([2, 0], %)\n |> line([0, 10], %)\n |> close(%)\n |> revolve({\n axis: getEdge('revolveAxis', box),\n angle: 90\n }, %)"
] ]
}, },
{ {
@ -42165,7 +42165,7 @@
"format": "double" "format": "double"
}, },
"center": { "center": {
"description": "The center about which to make the pattern. This is a 2D vector.", "description": "The center about which to make th pattern. This is a 2D vector.",
"type": "array", "type": "array",
"items": { "items": {
"type": "number", "type": "number",
@ -44168,7 +44168,7 @@
"minItems": 3 "minItems": 3
}, },
"center": { "center": {
"description": "The center about which to make the pattern. This is a 3D vector.", "description": "The center about which to make th pattern. This is a 3D vector.",
"type": "array", "type": "array",
"items": { "items": {
"type": "number", "type": "number",
@ -49363,21 +49363,21 @@
"description": "X-axis.", "description": "X-axis.",
"type": "string", "type": "string",
"enum": [ "enum": [
"X" "x"
] ]
}, },
{ {
"description": "Y-axis.", "description": "Y-axis.",
"type": "string", "type": "string",
"enum": [ "enum": [
"Y" "y"
] ]
}, },
{ {
"description": "Z-axis.", "description": "Z-axis.",
"type": "string", "type": "string",
"enum": [ "enum": [
"Z" "z"
] ]
}, },
{ {
@ -51191,7 +51191,6 @@
"const part001 = startSketchOn('XY')\n |> startProfileAt([4, 12], %)\n |> line([2, 0], %)\n |> line([0, -6], %)\n |> line([4, -6], %)\n |> line([0, -6], %)\n |> line([-3.75, -4.5], %)\n |> line([0, -5.5], %)\n |> line([-2, 0], %)\n |> close(%)\n |> revolve({ axis: 'y' }, %) // default angle is 360", "const part001 = startSketchOn('XY')\n |> startProfileAt([4, 12], %)\n |> line([2, 0], %)\n |> line([0, -6], %)\n |> line([4, -6], %)\n |> line([0, -6], %)\n |> line([-3.75, -4.5], %)\n |> line([0, -5.5], %)\n |> line([-2, 0], %)\n |> close(%)\n |> revolve({ axis: 'y' }, %) // default angle is 360",
"// A donut shape.\nconst sketch001 = startSketchOn('XY')\n |> circle([15, 0], 5, %)\n |> revolve({ angle: 360, axis: 'y' }, %)", "// A donut shape.\nconst sketch001 = startSketchOn('XY')\n |> circle([15, 0], 5, %)\n |> revolve({ angle: 360, axis: 'y' }, %)",
"const part001 = startSketchOn('XY')\n |> startProfileAt([4, 12], %)\n |> line([2, 0], %)\n |> line([0, -6], %)\n |> line([4, -6], %)\n |> line([0, -6], %)\n |> line([-3.75, -4.5], %)\n |> line([0, -5.5], %)\n |> line([-2, 0], %)\n |> close(%)\n |> revolve({ axis: 'y', angle: 180 }, %)", "const part001 = startSketchOn('XY')\n |> startProfileAt([4, 12], %)\n |> line([2, 0], %)\n |> line([0, -6], %)\n |> line([4, -6], %)\n |> line([0, -6], %)\n |> line([-3.75, -4.5], %)\n |> line([0, -5.5], %)\n |> line([-2, 0], %)\n |> close(%)\n |> revolve({ axis: 'y', angle: 180 }, %)",
"const part001 = startSketchOn('XY')\n |> startProfileAt([4, 12], %)\n |> line([2, 0], %)\n |> line([0, -6], %)\n |> line([4, -6], %)\n |> line([0, -6], %)\n |> line([-3.75, -4.5], %)\n |> line([0, -5.5], %)\n |> line([-2, 0], %)\n |> close(%)\n |> revolve({ axis: 'y', angle: 180 }, %)\nconst part002 = startSketchOn(part001, 'end')\n |> startProfileAt([4.5, -5], %)\n |> line([0, 5], %)\n |> line([5, 0], %)\n |> line([0, -5], %)\n |> close(%)\n |> extrude(5, %)",
"const box = startSketchOn('XY')\n |> startProfileAt([0, 0], %)\n |> line([0, 20], %)\n |> line([20, 0], %)\n |> line([0, -20], %)\n |> close(%)\n |> extrude(20, %)\n\nconst sketch001 = startSketchOn(box, \"END\")\n |> circle([10, 10], 4, %)\n |> revolve({ angle: -90, axis: 'y' }, %)", "const box = startSketchOn('XY')\n |> startProfileAt([0, 0], %)\n |> line([0, 20], %)\n |> line([20, 0], %)\n |> line([0, -20], %)\n |> close(%)\n |> extrude(20, %)\n\nconst sketch001 = startSketchOn(box, \"END\")\n |> circle([10, 10], 4, %)\n |> revolve({ angle: -90, axis: 'y' }, %)",
"const box = startSketchOn('XY')\n |> startProfileAt([0, 0], %)\n |> line([0, 20], %)\n |> line([20, 0], %)\n |> line([0, -20], %, 'revolveAxis')\n |> close(%)\n |> extrude(20, %)\n\nconst sketch001 = startSketchOn(box, \"END\")\n |> circle([10, 10], 4, %)\n |> revolve({\n angle: 90,\n axis: getOppositeEdge('revolveAxis', box)\n }, %)" "const box = startSketchOn('XY')\n |> startProfileAt([0, 0], %)\n |> line([0, 20], %)\n |> line([20, 0], %)\n |> line([0, -20], %, 'revolveAxis')\n |> close(%)\n |> extrude(20, %)\n\nconst sketch001 = startSketchOn(box, \"END\")\n |> circle([10, 10], 4, %)\n |> revolve({\n angle: 90,\n axis: getOppositeEdge('revolveAxis', box)\n }, %)"
] ]

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: 224 KiB

After

Width:  |  Height:  |  Size: 221 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 224 KiB

After

Width:  |  Height:  |  Size: 221 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 224 KiB

After

Width:  |  Height:  |  Size: 221 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 165 KiB

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 165 KiB

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 165 KiB

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 165 KiB

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 224 KiB

After

Width:  |  Height:  |  Size: 221 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 165 KiB

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 165 KiB

After

Width:  |  Height:  |  Size: 163 KiB

View File

@ -1,16 +1,10 @@
import { test, expect } from '@playwright/test' import { test, expect } from '@playwright/test'
import { secrets } from './secrets'
import { getUtils } from './test-utils' import { getUtils } from './test-utils'
import waitOn from 'wait-on' import waitOn from 'wait-on'
import { Themes } from '../../src/lib/theme'
import { initialSettings } from '../../src/lib/settings/initialSettings'
import { roundOff } from 'lib/utils' import { roundOff } from 'lib/utils'
import { SaveSettingsPayload } from 'lib/settings/settingsTypes'
import { secrets } from './secrets'
import {
TEST_SETTINGS,
TEST_SETTINGS_KEY,
TEST_SETTINGS_CORRUPTED,
TEST_SETTINGS_ONBOARDING,
} from './storageStates'
import * as TOML from '@iarna/toml'
/* /*
debug helper: unfortunately we do rely on exact coord mouse clicks in a few places debug helper: unfortunately we do rely on exact coord mouse clicks in a few places
@ -36,19 +30,24 @@ test.beforeEach(async ({ context, page }) => {
resources: ['tcp:3000'], resources: ['tcp:3000'],
timeout: 5000, timeout: 5000,
}) })
await context.addInitScript(async (token) => {
await context.addInitScript(
async ({ token, settingsKey, settings }) => {
localStorage.setItem('TOKEN_PERSIST_KEY', token) localStorage.setItem('TOKEN_PERSIST_KEY', token)
localStorage.setItem('persistCode', ``) localStorage.setItem('persistCode', ``)
localStorage.setItem(settingsKey, settings) localStorage.setItem(
}, 'SETTINGS_PERSIST_KEY',
{ JSON.stringify({
token: secrets.token, baseUnit: 'in',
settingsKey: TEST_SETTINGS_KEY, cameraControls: 'KittyCAD',
settings: TOML.stringify({ settings: TEST_SETTINGS }), defaultDirectory: '',
} defaultProjectName: 'project-$nnn',
onboardingStatus: 'dismissed',
showDebugPanel: true,
textWrapping: 'On',
theme: 'system',
unitSystem: 'imperial',
})
) )
}, secrets.token)
// kill animations, speeds up tests and reduced flakiness // kill animations, speeds up tests and reduced flakiness
await page.emulateMedia({ reducedMotion: 'reduce' }) await page.emulateMedia({ reducedMotion: 'reduce' })
}) })
@ -145,7 +144,6 @@ test('Can moving camera', async ({ page, context }) => {
await page.goto('/') await page.goto('/')
await u.waitForAuthSkipAppStart() await u.waitForAuthSkipAppStart()
await u.openAndClearDebugPanel() await u.openAndClearDebugPanel()
await u.closeKclCodePanel()
const camPos: [number, number, number] = [0, 85, 85] const camPos: [number, number, number] = [0, 85, 85]
const bakeInRetries = async ( const bakeInRetries = async (
@ -179,8 +177,6 @@ test('Can moving camera', async ({ page, context }) => {
}, 300) }, 300)
await u.openAndClearDebugPanel() await u.openAndClearDebugPanel()
await page.getByTestId('cam-x-position').isVisible()
const vals = await Promise.all([ const vals = await Promise.all([
page.getByTestId('cam-x-position').inputValue(), page.getByTestId('cam-x-position').inputValue(),
page.getByTestId('cam-y-position').inputValue(), page.getByTestId('cam-y-position').inputValue(),
@ -328,61 +324,9 @@ test('if you write invalid kcl you get inlined errors', async ({ page }) => {
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible() await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
}) })
/* Ignore this test for now since its causing engine to crash test('executes on load', async ({ page, context }) => {
*
* test('if your kcl gets an error from the engine it is inlined', async ({
page,
}) => {
const u = getUtils(page) const u = getUtils(page)
await page.addInitScript(async () => { await context.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const box = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> line([0, 10], %)
|> line([10, 0], %)
|> line([0, -10], %, 'revolveAxis')
|> close(%)
|> extrude(10, %)
const sketch001 = startSketchOn(box, "revolveAxis")
|> startProfileAt([5, 10], %)
|> line([0, -10], %)
|> line([2, 0], %)
|> line([0, 10], %)
|> close(%)
|> revolve({
axis: getEdge('revolveAxis', box),
angle: 90
}, %)
`
)
})
await page.setViewportSize({ width: 1000, height: 500 })
await page.goto('/')
await u.waitForAuthSkipAppStart()
u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.closeDebugPanel()
// error in guter
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
// error text on hover
await page.hover('.cm-lint-marker-error')
await expect(
page.getByText(
'sketch profile must lie entirely on one side of the revolution axis'
)
).toBeVisible()
})*/
test('executes on load', async ({ page }) => {
const u = getUtils(page)
await page.addInitScript(async () => {
localStorage.setItem( localStorage.setItem(
'persistCode', 'persistCode',
`const part001 = startSketchOn('-XZ') `const part001 = startSketchOn('-XZ')
@ -397,11 +341,7 @@ test('executes on load', async ({ page }) => {
await u.waitForAuthSkipAppStart() await u.waitForAuthSkipAppStart()
// expand variables section // expand variables section
const variablesTabButton = page.getByRole('tab', { await page.getByText('Variables').click()
name: 'Variables',
exact: false,
})
await variablesTabButton.click()
// can find part001 in the variables summary (pretty-json-container, makes sure we're not looking in the code editor) // can find part001 in the variables summary (pretty-json-container, makes sure we're not looking in the code editor)
// part001 only shows up in the variables summary if it's been executed // part001 only shows up in the variables summary if it's been executed
@ -416,20 +356,16 @@ test('executes on load', async ({ page }) => {
).toBeVisible() ).toBeVisible()
}) })
test('re-executes', async ({ page }) => { test('re-executes', async ({ page, context }) => {
const u = getUtils(page) const u = getUtils(page)
await page.addInitScript(async () => { await context.addInitScript(async (token) => {
localStorage.setItem('persistCode', `const myVar = 5`) localStorage.setItem('persistCode', `const myVar = 5`)
}) })
await page.setViewportSize({ width: 1000, height: 500 }) await page.setViewportSize({ width: 1000, height: 500 })
await page.goto('/') await page.goto('/')
await u.waitForAuthSkipAppStart() await u.waitForAuthSkipAppStart()
const variablesTabButton = page.getByRole('tab', { await page.getByText('Variables').click()
name: 'Variables',
exact: false,
})
await variablesTabButton.click()
// expect to see "myVar:5" // expect to see "myVar:5"
await expect( await expect(
page.locator('.pretty-json-container >> text=myVar:5') page.locator('.pretty-json-container >> text=myVar:5')
@ -446,11 +382,9 @@ test('re-executes', async ({ page }) => {
).toBeVisible() ).toBeVisible()
}) })
const sketchOnPlaneAndBackSideTest = async ( test('Can create sketches on all planes and their back sides', async ({
page: any, page,
plane: string, }) => {
clickCoords: { x: number; y: number }
) => {
const u = getUtils(page) const u = getUtils(page)
const PUR = 400 / 37.5 //pixeltoUnitRatio const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.setViewportSize({ width: 1200, height: 500 }) await page.setViewportSize({ width: 1200, height: 500 })
@ -458,20 +392,22 @@ const sketchOnPlaneAndBackSideTest = async (
await u.waitForAuthSkipAppStart() await u.waitForAuthSkipAppStart()
await u.openDebugPanel() await u.openDebugPanel()
const camCmdBackSide: [number, number, number] = [-100, -100, -100] const camPos: [number, number, number] = [100, 100, 100]
let camPos: [number, number, number] = [100, 100, 100]
if (plane === '-XY' || plane === '-YZ' || plane === '-XZ') {
camPos = camCmdBackSide
}
const code = `const part001 = startSketchOn('${plane}')
|> startProfileAt([1.14, -1.54], %)`
const TestSinglePlane = async ({
viewCmd,
expectedCode,
clickCoords,
}: {
viewCmd: [number, number, number]
expectedCode: string
clickCoords: { x: number; y: number }
}) => {
await u.openDebugPanel() await u.openDebugPanel()
await u.clearCommandLogs() await u.clearCommandLogs()
await page.getByRole('button', { name: 'Start Sketch' }).click() await page.getByRole('button', { name: 'Start Sketch' }).click()
await u.updateCamPosition(camPos) await u.updateCamPosition(viewCmd)
await u.closeDebugPanel() await u.closeDebugPanel()
await page.mouse.click(clickCoords.x, clickCoords.y) await page.mouse.click(clickCoords.x, clickCoords.y)
@ -485,7 +421,7 @@ const sketchOnPlaneAndBackSideTest = async (
await u.closeDebugPanel() await u.closeDebugPanel()
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')).toHaveText(code) await expect(page.locator('.cm-content')).toHaveText(expectedCode)
await page.getByRole('button', { name: 'Line' }).click() await page.getByRole('button', { name: 'Line' }).click()
await u.openAndClearDebugPanel() await u.openAndClearDebugPanel()
@ -496,34 +432,41 @@ const sketchOnPlaneAndBackSideTest = async (
await u.removeCurrentCode() await u.removeCurrentCode()
} }
test.describe('Can create sketches on all planes and their back sides', () => { const codeTemplate = (
test('XY', async ({ page }) => { plane = 'XY'
await sketchOnPlaneAndBackSideTest( ) => `const part001 = startSketchOn('${plane}')
page, |> startProfileAt([1.14, -1.54], %)`
'XY', await TestSinglePlane({
{ x: 600, y: 388 } // red plane viewCmd: camPos,
// { x: 600, y: 400 }, // red plane // clicks grid helper and that causes problems, should fix so that these coords work too. expectedCode: codeTemplate('XY'),
) clickCoords: { x: 600, y: 388 }, // red plane
// clickCoords: { x: 600, y: 400 }, // red plane // clicks grid helper and that causes problems, should fix so that these coords work too.
}) })
await TestSinglePlane({
test('YZ', async ({ page }) => { viewCmd: camPos,
await sketchOnPlaneAndBackSideTest(page, 'YZ', { x: 700, y: 250 }) // green plane expectedCode: codeTemplate('YZ'),
clickCoords: { x: 700, y: 250 }, // green plane
}) })
await TestSinglePlane({
test('XZ', async ({ page }) => { viewCmd: camPos,
await sketchOnPlaneAndBackSideTest(page, 'XZ', { x: 700, y: 80 }) // blue plane expectedCode: codeTemplate('XZ'),
clickCoords: { x: 700, y: 80 }, // blue plane
}) })
const camCmdBackSide: [number, number, number] = [-100, -100, -100]
test('-XY', async ({ page }) => { await TestSinglePlane({
await sketchOnPlaneAndBackSideTest(page, '-XY', { x: 600, y: 118 }) // back of red plane viewCmd: camCmdBackSide,
expectedCode: codeTemplate('-XY'),
clickCoords: { x: 601, y: 118 }, // back of red plane
}) })
await TestSinglePlane({
test('-YZ', async ({ page }) => { viewCmd: camCmdBackSide,
await sketchOnPlaneAndBackSideTest(page, '-YZ', { x: 700, y: 219 }) // back of green plane expectedCode: codeTemplate('-YZ'),
clickCoords: { x: 730, y: 219 }, // back of green plane
}) })
await TestSinglePlane({
test('-XZ', async ({ page }) => { viewCmd: camCmdBackSide,
await sketchOnPlaneAndBackSideTest(page, '-XZ', { x: 700, y: 427 }) // back of blue plane expectedCode: codeTemplate('-XZ'),
clickCoords: { x: 680, y: 427 }, // back of blue plane
}) })
}) })
@ -555,8 +498,7 @@ test('Auto complete works', async ({ page }) => {
// expect there to be three auto complete options // expect there to be three auto complete options
await expect(page.locator('.cm-completionLabel')).toHaveCount(3) await expect(page.locator('.cm-completionLabel')).toHaveCount(3)
await page.getByText('startSketchOn').click() await page.getByText('startSketchOn').click()
await page.keyboard.type("'XZ'") await page.keyboard.type("('XY')")
await page.keyboard.press('Tab')
await page.keyboard.press('Enter') await page.keyboard.press('Enter')
await page.keyboard.type(' |> startProfi') await page.keyboard.type(' |> startProfi')
// expect there be a single auto complete option that we can just hit enter on // expect there be a single auto complete option that we can just hit enter on
@ -564,11 +506,7 @@ test('Auto complete works', async ({ page }) => {
await page.waitForTimeout(100) await page.waitForTimeout(100)
await page.keyboard.press('Enter') // accepting the auto complete, not a new line await page.keyboard.press('Enter') // accepting the auto complete, not a new line
await page.keyboard.press('Tab') await page.keyboard.type('([0,0], %)')
await page.keyboard.type('12')
await page.keyboard.press('Tab')
await page.keyboard.press('Tab')
await page.keyboard.press('Tab')
await page.keyboard.press('Enter') await page.keyboard.press('Enter')
await page.keyboard.type(' |> lin') await page.keyboard.type(' |> lin')
@ -579,122 +517,81 @@ test('Auto complete works', async ({ page }) => {
await page.keyboard.press('ArrowDown') await page.keyboard.press('ArrowDown')
await page.keyboard.press('Enter') await page.keyboard.press('Enter')
// finish line with comment // finish line with comment
await page.keyboard.type('5') await page.keyboard.type('(5, %) // lin')
await page.keyboard.press('Tab')
await page.keyboard.press('Tab')
await page.keyboard.type(' // lin')
await page.waitForTimeout(100) await page.waitForTimeout(100)
// there shouldn't be any auto complete options for 'lin' in the comment // there shouldn't be any auto complete options for 'lin' in the comment
await expect(page.locator('.cm-completionLabel')).not.toBeVisible() await expect(page.locator('.cm-completionLabel')).not.toBeVisible()
await expect(page.locator('.cm-content')) await expect(page.locator('.cm-content'))
.toHaveText(`const part001 = startSketchOn('XZ') .toHaveText(`const part001 = startSketchOn('XY')
|> startProfileAt([3.14, 12], %) |> startProfileAt([0,0], %)
|> xLine(5, %) // lin`) |> xLine(5, %) // lin`)
}) })
// Stored settings validation test
test('Stored settings are validated and fall back to defaults', async ({ test('Stored settings are validated and fall back to defaults', async ({
page, page,
context, context,
}) => { }) => {
const u = getUtils(page)
// Override beforeEach test setup // Override beforeEach test setup
// with corrupted settings // with corrupted settings
await context.addInitScript( await context.addInitScript(async () => {
async ({ settingsKey, settings }) => { const storedSettings = JSON.parse(
localStorage.setItem(settingsKey, settings) localStorage.getItem('SETTINGS_PERSIST_KEY') || '{}'
},
{
settingsKey: TEST_SETTINGS_KEY,
settings: TOML.stringify({ settings: TEST_SETTINGS_CORRUPTED }),
}
) )
await page.setViewportSize({ width: 1200, height: 500 }) // Corrupt the settings
await page.goto('/') storedSettings.baseUnit = 'invalid'
await u.waitForAuthSkipAppStart() storedSettings.cameraControls = `() => alert('hack the planet')`
storedSettings.defaultDirectory = 123
storedSettings.defaultProjectName = false
// Check the settings were reset localStorage.setItem('SETTINGS_PERSIST_KEY', JSON.stringify(storedSettings))
const storedSettings = TOML.parse(
await page.evaluate(
({ settingsKey }) => localStorage.getItem(settingsKey) || '{}',
{ settingsKey: TEST_SETTINGS_KEY }
)
) as { settings: SaveSettingsPayload }
expect(storedSettings.settings.app?.theme).toBe('dark')
// Check that the invalid settings were removed
expect(storedSettings.settings.modeling?.defaultUnit).toBe(undefined)
expect(storedSettings.settings.modeling?.mouseControls).toBe(undefined)
expect(storedSettings.settings.app?.projectDirectory).toBe(undefined)
expect(storedSettings.settings.projects?.defaultProjectName).toBe(undefined)
}) })
test('Project settings can be set and override user settings', async ({
page,
}) => {
await page.setViewportSize({ width: 1200, height: 500 }) await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/', { waitUntil: 'domcontentloaded' }) await page.goto('/', { waitUntil: 'domcontentloaded' })
await page
.getByRole('button', { name: 'Start Sketch' })
.waitFor({ state: 'visible' })
// Open the settings modal with the browser keyboard shortcut
await page.keyboard.press('Meta+Shift+,')
// Check the toast appeared
await expect( await expect(
page.getByRole('heading', { name: 'Settings', exact: true }) page.getByText(`Error validating persisted settings:`, {
exact: false,
})
).toBeVisible() ).toBeVisible()
await page
.locator('select[name="app-theme"]')
.selectOption({ value: 'light' })
// Verify the toast appeared // Check the settings were reset
await expect( const storedSettings = JSON.parse(
page.getByText(`Set theme to "light" for this project`) await page.evaluate(
).toBeVisible() () => localStorage.getItem('SETTINGS_PERSIST_KEY') || '{}'
// Check that the theme changed )
await expect(page.locator('body')).not.toHaveClass(`body-bg dark`) )
await expect(storedSettings.baseUnit).toBe(initialSettings.baseUnit)
// Check that the user setting was not changed await expect(storedSettings.cameraControls).toBe(
await page.getByRole('radio', { name: 'User' }).click() initialSettings.cameraControls
await expect(page.locator('select[name="app-theme"]')).toHaveValue('dark') )
await expect(storedSettings.defaultDirectory).toBe(
// Roll back to default "system" theme initialSettings.defaultDirectory
await page )
.getByText( await expect(storedSettings.defaultProjectName).toBe(
'themeRoll back themeRoll back to match defaultThe overall appearance of the appl' initialSettings.defaultProjectName
) )
.hover()
await page
.getByRole('button', {
name: 'Roll back theme ; Has tooltip: Roll back to match default',
})
.click()
await expect(page.locator('select[name="app-theme"]')).toHaveValue('system')
// Check that the project setting did not change
await page.getByRole('radio', { name: 'Project' }).click()
await expect(page.locator('select[name="app-theme"]')).toHaveValue('light')
}) })
test('Onboarding redirects and code updating', async ({ page }) => { // Onboarding tests
test('Onboarding redirects and code updating', async ({ page, context }) => {
const u = getUtils(page) const u = getUtils(page)
// Override beforeEach test setup // Override beforeEach test setup
await page.addInitScript( await context.addInitScript(async () => {
async ({ settingsKey, settings }) => {
// Give some initial code, so we can test that it's cleared // Give some initial code, so we can test that it's cleared
localStorage.setItem('persistCode', 'const sigmaAllow = 15000') localStorage.setItem('persistCode', 'const sigmaAllow = 15000')
localStorage.setItem(settingsKey, settings)
}, const storedSettings = JSON.parse(
{ localStorage.getItem('SETTINGS_PERSIST_KEY') || '{}'
settingsKey: TEST_SETTINGS_KEY,
settings: TOML.stringify({ settings: TEST_SETTINGS_ONBOARDING }),
}
) )
storedSettings.onboardingStatus = '/export'
localStorage.setItem('SETTINGS_PERSIST_KEY', JSON.stringify(storedSettings))
})
await page.setViewportSize({ width: 1200, height: 500 }) await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/') await page.goto('/')
@ -702,13 +599,13 @@ test('Onboarding redirects and code updating', async ({ page }) => {
// Test that the redirect happened // Test that the redirect happened
await expect(page.url().split(':3000').slice(-1)[0]).toBe( await expect(page.url().split(':3000').slice(-1)[0]).toBe(
`/file/%2Fbrowser%2Fmain.kcl/onboarding/export` `/file/new/onboarding/export`
) )
// Test that you come back to this page when you refresh // Test that you come back to this page when you refresh
await page.reload() await page.reload()
await expect(page.url().split(':3000').slice(-1)[0]).toBe( await expect(page.url().split(':3000').slice(-1)[0]).toBe(
`/file/%2Fbrowser%2Fmain.kcl/onboarding/export` `/file/new/onboarding/export`
) )
// Test that the onboarding pane loaded // Test that the onboarding pane loaded
@ -882,11 +779,12 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
await selectionSequence() await selectionSequence()
}) })
test.describe('Command bar tests', () => {
test('Command bar works and can change a setting', async ({ page }) => { test('Command bar works and can change a setting', async ({ page }) => {
// Brief boilerplate // Brief boilerplate
const u = getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 }) await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/', { waitUntil: 'domcontentloaded' }) await page.goto('/')
await u.waitForAuthSkipAppStart()
let cmdSearchBar = page.getByPlaceholder('Search commands') let cmdSearchBar = page.getByPlaceholder('Search commands')
@ -907,37 +805,33 @@ test.describe('Command bar tests', () => {
// Try typing in the command bar // Try typing in the command bar
await page.keyboard.type('theme') await page.keyboard.type('theme')
const themeOption = page.getByRole('option', { const themeOption = page.getByRole('option', { name: 'Set Theme' })
name: 'Settings · app · theme',
})
await expect(themeOption).toBeVisible() await expect(themeOption).toBeVisible()
await themeOption.click() await themeOption.click()
const themeInput = page.getByPlaceholder('Select an option') const themeInput = page.getByPlaceholder('system')
await expect(themeInput).toBeVisible() await expect(themeInput).toBeVisible()
await expect(themeInput).toBeFocused() await expect(themeInput).toBeFocused()
// Select dark theme // Select dark theme
await page.keyboard.press('ArrowDown') await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown') await page.keyboard.press('ArrowUp')
await page.keyboard.press('ArrowDown') await expect(page.getByRole('option', { name: Themes.Dark })).toHaveAttribute(
await expect(page.getByRole('option', { name: 'system' })).toHaveAttribute(
'data-headlessui-state', 'data-headlessui-state',
'active' 'active'
) )
await page.keyboard.press('Enter') await page.keyboard.press('Enter')
// Check the toast appeared // Check the toast appeared
await expect( await expect(page.getByText(`Set Theme to "${Themes.Dark}"`)).toBeVisible()
page.getByText(`Set theme to "system" for this project`)
).toBeVisible()
// Check that the theme changed // Check that the theme changed
await expect(page.locator('body')).not.toHaveClass(`body-bg dark`) await expect(page.locator('body')).toHaveClass(`body-bg ${Themes.Dark}`)
}) })
test('Can extrude from the command bar', async ({ page }) => { test('Can extrude from the command bar', async ({ page, context }) => {
await page.addInitScript(async () => { await context.addInitScript(async (token) => {
localStorage.setItem( localStorage.setItem(
'persistCode', 'persistCode',
`const distance = sqrt(20) `
const distance = sqrt(20)
const part001 = startSketchOn('-XZ') const part001 = startSketchOn('-XZ')
|> startProfileAt([-6.95, 4.98], %) |> startProfileAt([-6.95, 4.98], %)
|> line([25.1, 0.41], %) |> line([25.1, 0.41], %)
@ -952,24 +846,23 @@ test.describe('Command bar tests', () => {
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()
// Make sure the stream is up
await u.openDebugPanel() await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]') await u.expectCmdLog('[data-message-type="execution-done"]')
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled()
await u.clearCommandLogs()
await page.getByText('|> line([0.73, -14.93], %)').click()
await page.getByRole('button', { name: 'Extrude' }).isEnabled()
let cmdSearchBar = page.getByPlaceholder('Search commands') let cmdSearchBar = page.getByPlaceholder('Search commands')
await page.keyboard.press('Meta+K') await page.keyboard.press('Meta+K')
await expect(cmdSearchBar).toBeVisible() await expect(cmdSearchBar).toBeVisible()
// Search for extrude command and choose it // Search for extrude command and choose it
await page.getByRole('option', { name: 'Extrude' }).click() await page.getByRole('option', { name: 'Extrude' }).click()
await expect(page.locator('#arg-form > label')).toContainText(
'Please select one face'
)
await expect(page.getByRole('button', { name: 'selection' })).toBeDisabled()
// Click to select face and set distance
await page.getByText('|> startProfileAt([-6.95, 4.98], %)').click()
await page.getByRole('button', { name: 'Continue' }).click()
// Assert that we're on the distance step // Assert that we're on the distance step
await expect(page.getByRole('button', { name: 'distance' })).toBeDisabled() await expect(page.getByRole('button', { name: 'distance' })).toBeDisabled()
@ -980,25 +873,23 @@ test.describe('Command bar tests', () => {
await expect(page.getByPlaceholder('Variable name')).toHaveValue( await expect(page.getByPlaceholder('Variable name')).toHaveValue(
'distance001' 'distance001'
) )
await expect(page.getByRole('button', { name: 'Continue' })).toBeEnabled()
const continueButton = page.getByRole('button', { name: 'Continue' }) await page.getByRole('button', { name: 'Continue' }).click()
const submitButton = page.getByRole('button', { name: 'Submit command' })
await continueButton.click()
// Review step and argument hotkeys // Review step and argument hotkeys
await expect(submitButton).toBeEnabled() await expect(
page.getByRole('button', { name: 'Submit command' })
).toBeEnabled()
await page.keyboard.press('Backspace') await page.keyboard.press('Backspace')
// Assert we're back on the distance step
await expect( await expect(
page.getByRole('button', { name: 'Distance 12', exact: false }) page.getByRole('button', { name: 'Distance 12', exact: false })
).toBeDisabled() ).toBeDisabled()
await page.keyboard.press('Enter')
await continueButton.click() await expect(page.getByText('Confirm Extrude')).toBeVisible()
await submitButton.click()
// Check that the code was updated // Check that the code was updated
await u.waitForCmdReceive('extrude') await page.keyboard.press('Enter')
// Unfortunately this indentation seems to matter for the test // Unfortunately this indentation seems to matter for the test
await expect(page.locator('.cm-content')).toHaveText( await expect(page.locator('.cm-content')).toHaveText(
`const distance = sqrt(20) `const distance = sqrt(20)
@ -1012,7 +903,6 @@ const part001 = startSketchOn('-XZ')
|> extrude(distance001, %)`.replace(/(\r\n|\n|\r)/gm, '') // remove newlines |> extrude(distance001, %)`.replace(/(\r\n|\n|\r)/gm, '') // remove newlines
) )
}) })
})
test('Can add multiple sketches', async ({ page }) => { test('Can add multiple sketches', async ({ page }) => {
const u = getUtils(page) const u = getUtils(page)
@ -1139,9 +1029,9 @@ const part002 = startSketchOn('XY')
) )
}) })
test('ProgramMemory can be serialised', async ({ page }) => { test('ProgramMemory can be serialised', async ({ page, context }) => {
const u = getUtils(page) const u = getUtils(page)
await page.addInitScript(async () => { await context.addInitScript(async () => {
localStorage.setItem( localStorage.setItem(
'persistCode', 'persistCode',
`const part = startSketchOn('XY') `const part = startSketchOn('XY')
@ -1151,7 +1041,7 @@ test('ProgramMemory can be serialised', async ({ page }) => {
|> line([0, -1], %) |> line([0, -1], %)
|> close(%) |> close(%)
|> extrude(1, %) |> extrude(1, %)
|> patternLinear3d({ |> patternLinear({
axis: [1, 0, 1], axis: [1, 0, 1],
repetitions: 3, repetitions: 3,
distance: 6 distance: 6
@ -1180,6 +1070,7 @@ test('ProgramMemory can be serialised', async ({ page }) => {
test("Various pipe expressions should and shouldn't allow edit and or extrude", async ({ test("Various pipe expressions should and shouldn't allow edit and or extrude", async ({
page, page,
context,
}) => { }) => {
const u = getUtils(page) const u = getUtils(page)
const selectionsSnippets = { const selectionsSnippets = {
@ -1188,7 +1079,7 @@ test("Various pipe expressions should and shouldn't allow edit and or extrude",
extrudeAndEditAllowed: '|> startProfileAt([15.72, 4.7], %)', extrudeAndEditAllowed: '|> startProfileAt([15.72, 4.7], %)',
editOnly: '|> startProfileAt([15.79, -14.6], %)', editOnly: '|> startProfileAt([15.79, -14.6], %)',
} }
await page.addInitScript( await context.addInitScript(
async ({ async ({
extrudeAndEditBlocked, extrudeAndEditBlocked,
extrudeAndEditBlockedInFunction, extrudeAndEditBlockedInFunction,
@ -1235,7 +1126,7 @@ fn yohey = (pos) => {
}, },
selectionsSnippets selectionsSnippets
) )
await page.setViewportSize({ width: 1200, height: 1000 }) await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/') await page.goto('/')
await u.waitForAuthSkipAppStart() await u.waitForAuthSkipAppStart()
@ -1348,9 +1239,12 @@ test('Deselecting line tool should mean nothing happens on click', async ({
previousCodeContent = await page.locator('.cm-content').innerText() previousCodeContent = await page.locator('.cm-content').innerText()
}) })
test('Can edit segments by dragging their handles', async ({ page }) => { test('Can edit segments by dragging their handles', async ({
page,
context,
}) => {
const u = getUtils(page) const u = getUtils(page)
await page.addInitScript(async () => { await context.addInitScript(async () => {
localStorage.setItem( localStorage.setItem(
'persistCode', 'persistCode',
`const part001 = startSketchOn('-XZ') `const part001 = startSketchOn('-XZ')
@ -1419,29 +1313,22 @@ test('Can edit segments by dragging their handles', async ({ page }) => {
|> tangentialArcTo([27.6, -3.25], %)`) |> tangentialArcTo([27.6, -3.25], %)`)
}) })
const doSnapAtDifferentScales = async ( test('Snap to close works (at any scale)', async ({ page }) => {
page: any,
camPos: [number, number, number],
scale = 1,
fudge = 0
) => {
const u = 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()
await u.openDebugPanel() await u.openDebugPanel()
const code = `const part001 = startSketchOn('XZ')
|> startProfileAt([${roundOff(scale * 87.68)}, ${roundOff(scale * 43.84)}], %)
|> line([${roundOff(scale * 175.36)}, 0], %)
|> line([0, -${roundOff(scale * 175.36) + fudge}], %)
|> close(%)`
await expect( await expect(
page.getByRole('button', { name: 'Start Sketch' }) page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled() ).not.toBeDisabled()
await expect(page.getByRole('button', { name: 'Start Sketch' })).toBeVisible() await expect(page.getByRole('button', { name: 'Start Sketch' })).toBeVisible()
const doSnapAtDifferentScales = async (
camPos: [number, number, number],
expectedCode: string
) => {
await u.clearCommandLogs() await u.clearCommandLogs()
await page.getByRole('button', { name: 'Start Sketch' }).click() await page.getByRole('button', { name: 'Start Sketch' }).click()
await page.waitForTimeout(100) await page.waitForTimeout(100)
@ -1486,7 +1373,7 @@ const doSnapAtDifferentScales = async (
await expect(page.locator('.cm-content')).not.toHaveText(prevContent) await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
prevContent = await page.locator('.cm-content').innerText() prevContent = await page.locator('.cm-content').innerText()
await expect(page.locator('.cm-content')).toHaveText(code) await expect(page.locator('.cm-content')).toHaveText(expectedCode)
// exit sketch // exit sketch
await u.openAndClearDebugPanel() await u.openAndClearDebugPanel()
@ -1495,19 +1382,23 @@ const doSnapAtDifferentScales = async (
await u.removeCurrentCode() await u.removeCurrentCode()
} }
test.describe('Snap to close works (at any scale)', () => { const codeTemplate = (
test('[0, 100, 100]', async ({ page }) => { scale = 1,
await doSnapAtDifferentScales(page, [0, 100, 100], 0.01, 0.01) fudge = 0
) => `const part001 = startSketchOn('XZ')
|> startProfileAt([${roundOff(scale * 87.68)}, ${roundOff(scale * 43.84)}], %)
|> line([${roundOff(scale * 175.36)}, 0], %)
|> line([0, -${roundOff(scale * 175.36) + fudge}], %)
|> close(%)`
await doSnapAtDifferentScales([0, 100, 100], codeTemplate(0.01, 0.01))
await doSnapAtDifferentScales([0, 10000, 10000], codeTemplate())
}) })
test('[0, 10000, 10000]', async ({ page }) => { test('Sketch on face', async ({ page, context }) => {
await doSnapAtDifferentScales(page, [0, 10000, 10000])
})
})
test('Sketch on face', async ({ page }) => {
const u = getUtils(page) const u = getUtils(page)
await page.addInitScript(async () => { await context.addInitScript(async () => {
localStorage.setItem( localStorage.setItem(
'persistCode', 'persistCode',
`const part001 = startSketchOn('-XZ') `const part001 = startSketchOn('-XZ')
@ -1579,13 +1470,9 @@ test('Sketch on face', async ({ page }) => {
await page.getByText('startProfileAt([1.03, 1.03], %)').click() await page.getByText('startProfileAt([1.03, 1.03], %)').click()
await expect(page.getByRole('button', { name: 'Edit Sketch' })).toBeVisible() await expect(page.getByRole('button', { name: 'Edit Sketch' })).toBeVisible()
await page.getByRole('button', { name: 'Edit Sketch' }).click() await page.getByRole('button', { name: 'Edit Sketch' }).click()
await page.setViewportSize({ width: 1200, height: 1200 })
await u.openAndClearDebugPanel()
await u.updateCamPosition([452, -152, 1166])
await u.closeDebugPanel()
await page.waitForTimeout(200) await page.waitForTimeout(200)
const pointToDragFirst = [787, 565] const pointToDragFirst = [691, 237]
await page.mouse.move(pointToDragFirst[0], pointToDragFirst[1]) await page.mouse.move(pointToDragFirst[0], pointToDragFirst[1])
await page.mouse.down() await page.mouse.down()
await page.mouse.move(pointToDragFirst[0] - 20, pointToDragFirst[1], { await page.mouse.move(pointToDragFirst[0] - 20, pointToDragFirst[1], {
@ -1599,9 +1486,7 @@ test('Sketch on face', async ({ page }) => {
await expect(page.locator('.cm-content')) await expect(page.locator('.cm-content'))
.toContainText(`const part002 = startSketchOn(part001, 'seg01') .toContainText(`const part002 = startSketchOn(part001, 'seg01')
|> startProfileAt([1.03, 1.03], %) |> startProfileAt([1.03, 1.03], %)
|> line([${process?.env?.CI ? 2.74 : 2.93}, -${ |> line([2.81, -0.33], %)
process?.env?.CI ? 0.24 : 0.2
}], %)
|> line([-4.44, -2.13], %) |> line([-4.44, -2.13], %)
|> close(%)`) |> close(%)`)
@ -1616,7 +1501,6 @@ test('Sketch on face', async ({ page }) => {
await page.getByRole('button', { name: 'Extrude' }).click() await page.getByRole('button', { name: 'Extrude' }).click()
await expect(page.getByTestId('command-bar')).toBeVisible() await expect(page.getByTestId('command-bar')).toBeVisible()
await page.waitForTimeout(100)
await page.keyboard.press('Enter') await page.keyboard.press('Enter')
await expect(page.getByText('Confirm Extrude')).toBeVisible() await expect(page.getByText('Confirm Extrude')).toBeVisible()
@ -1625,51 +1509,8 @@ test('Sketch on face', async ({ page }) => {
await expect(page.locator('.cm-content')) await expect(page.locator('.cm-content'))
.toContainText(`const part002 = startSketchOn(part001, 'seg01') .toContainText(`const part002 = startSketchOn(part001, 'seg01')
|> startProfileAt([1.03, 1.03], %) |> startProfileAt([1.03, 1.03], %)
|> line([${process?.env?.CI ? 2.74 : 2.93}, -${ |> line([2.81, -0.33], %)
process?.env?.CI ? 0.24 : 0.2
}], %)
|> line([-4.44, -2.13], %) |> line([-4.44, -2.13], %)
|> close(%) |> close(%)
|> extrude(5 + 7, %)`) |> extrude(5 + 7, %)`)
}) })
test('Can code mod a line length', async ({ page }) => {
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const part001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
|> xLine(-20, %)
`
)
})
const u = getUtils(page)
const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/')
await u.waitForAuthSkipAppStart()
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.closeDebugPanel()
// Click the line of code for xLine.
await page.getByText(`xLine(-20, %)`).click() // TODO remove this and reinstate // await topHorzSegmentClick()
await page.waitForTimeout(100)
// enter sketch again
await page.getByRole('button', { name: 'Edit Sketch' }).click()
await page.waitForTimeout(300) // wait for animation
const startXPx = 500
await page.mouse.move(startXPx + PUR * 15, 250 - PUR * 10)
await page.mouse.click(615, 133)
await page.getByRole('button', { name: 'length', exact: true }).click()
await page.getByText('Add constraining value').click()
await expect(page.locator('.cm-content')).toHaveText(
`const length001 = 20const part001 = startSketchOn('XY') |> startProfileAt([-10, -10], %) |> line([20, 0], %) |> line([0, 20], %) |> xLine(-length001, %)`
)
})

View File

@ -7,36 +7,28 @@ import { spawn } from 'child_process'
import { APP_NAME } 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 * as TOML from '@iarna/toml'
test.beforeEach(async ({ page }) => { test.beforeEach(async ({ context, page }) => {
// reducedMotion kills animations, which speeds up tests and reduces flakiness await context.addInitScript(async (token) => {
await page.emulateMedia({ reducedMotion: 'reduce' })
// set the default settings
await page.addInitScript(
async ({ token, settingsKey, settings }) => {
localStorage.setItem('TOKEN_PERSIST_KEY', token) localStorage.setItem('TOKEN_PERSIST_KEY', token)
localStorage.setItem('persistCode', ``) localStorage.setItem('persistCode', ``)
localStorage.setItem(settingsKey, settings) localStorage.setItem(
}, 'SETTINGS_PERSIST_KEY',
{ JSON.stringify({
token: secrets.token, baseUnit: 'in',
settingsKey: TEST_SETTINGS_KEY, cameraControls: 'KittyCAD',
settings: TOML.stringify({ settings: TEST_SETTINGS }), defaultDirectory: '',
} defaultProjectName: 'project-$nnn',
onboardingStatus: 'dismissed',
showDebugPanel: true,
textWrapping: 'On',
theme: 'dark',
unitSystem: 'imperial',
})
) )
}, secrets.token)
// Make the user avatar image always 404 // reducedMotion kills animations, which speeds up tests and reduces flakiness
// so we see the fallback menu icon for all snapshot tests await page.emulateMedia({ reducedMotion: 'reduce' })
await page.route('https://lh3.googleusercontent.com/**', async (route) => {
await route.fulfill({
status: 404,
contentType: 'text/plain',
body: 'Not Found!',
})
})
}) })
test.setTimeout(60_000) test.setTimeout(60_000)
@ -336,7 +328,64 @@ const part001 = startSketchOn('-XZ')
} }
}) })
const extrudeDefaultPlane = async (context: any, page: any, plane: string) => { test('extrude on each default plane should be stable', async ({
page,
context,
}) => {
const u = getUtils(page)
const makeCode = (plane = 'XY') => `const part001 = startSketchOn('${plane}')
|> startProfileAt([7.00, 4.40], %)
|> line([6.60, -0.20], %)
|> line([2.80, 5.00], %)
|> line([-5.60, 4.40], %)
|> line([-5.40, -3.80], %)
|> close(%)
|> extrude(10.00, %)
`
await context.addInitScript(async (code) => {
localStorage.setItem('persistCode', code)
}, makeCode('XY'))
await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/')
await u.waitForAuthSkipAppStart()
// wait for execution done
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.clearAndCloseDebugPanel()
await page.getByText('Code').click()
await expect(page).toHaveScreenshot({
maxDiffPixels: 100,
})
await page.getByText('Code').click()
const runSnapshotsForOtherPlanes = async (plane = 'XY') => {
// clear code
await u.removeCurrentCode()
// add makeCode('XZ')
await page.locator('.cm-content').fill(makeCode(plane))
// wait for execution done
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.clearAndCloseDebugPanel()
await page.getByText('Code').click()
await expect(page).toHaveScreenshot({
maxDiffPixels: 100,
})
await page.getByText('Code').click()
}
await runSnapshotsForOtherPlanes('-XY')
await runSnapshotsForOtherPlanes('XZ')
await runSnapshotsForOtherPlanes('-XZ')
await runSnapshotsForOtherPlanes('YZ')
await runSnapshotsForOtherPlanes('-YZ')
})
test('Draft segments should look right', async ({ page, context }) => {
await context.addInitScript(async () => { await context.addInitScript(async () => {
localStorage.setItem( localStorage.setItem(
'SETTINGS_PERSIST_KEY', 'SETTINGS_PERSIST_KEY',
@ -353,75 +402,6 @@ const extrudeDefaultPlane = async (context: any, page: any, plane: string) => {
}) })
) )
}) })
const code = `const part001 = startSketchOn('${plane}')
|> startProfileAt([7.00, 4.40], %)
|> line([6.60, -0.20], %)
|> line([2.80, 5.00], %)
|> line([-5.60, 4.40], %)
|> line([-5.40, -3.80], %)
|> close(%)
|> extrude(10.00, %)
`
await page.addInitScript(async (code: string) => {
localStorage.setItem('persistCode', code)
})
const u = getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/')
await u.waitForAuthSkipAppStart()
// wait for execution done
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.clearAndCloseDebugPanel()
await page.waitForTimeout(200)
// clear code
await u.removeCurrentCode()
await u.openAndClearDebugPanel()
await u.doAndWaitForImageDiff(
() => page.locator('.cm-content').fill(code),
200
)
// wait for execution done
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.clearAndCloseDebugPanel()
await u.closeKclCodePanel()
await expect(page).toHaveScreenshot({
maxDiffPixels: 100,
})
await u.openKclCodePanel()
}
test.describe('extrude on default planes should be stable', () => {
test('XY', async ({ page, context }) => {
await extrudeDefaultPlane(context, page, 'XY')
})
test('XZ', async ({ page, context }) => {
await extrudeDefaultPlane(context, page, 'XZ')
})
test('YZ', async ({ page, context }) => {
await extrudeDefaultPlane(context, page, 'YZ')
})
test('-XY', async ({ page, context }) => {
await extrudeDefaultPlane(context, page, '-XY')
})
test('-XZ', async ({ page, context }) => {
await extrudeDefaultPlane(context, page, '-XZ')
})
test('-YZ', async ({ page, context }) => {
await extrudeDefaultPlane(context, page, '-YZ')
})
})
test('Draft segments should look right', async ({ page, context }) => {
const u = 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
@ -480,7 +460,26 @@ test('Draft segments should look right', async ({ page, context }) => {
}) })
}) })
test('Draft rectangles should look right', async ({ page, context }) => { test('Client side scene scale should match engine scale inch', async ({
page,
context,
}) => {
await context.addInitScript(async () => {
localStorage.setItem(
'SETTINGS_PERSIST_KEY',
JSON.stringify({
baseUnit: 'in',
cameraControls: 'KittyCAD',
defaultDirectory: '',
defaultProjectName: 'project-$nnn',
onboardingStatus: 'dismissed',
showDebugPanel: true,
textWrapping: 'On',
theme: 'dark',
unitSystem: 'imperial',
})
)
})
const u = 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
@ -507,55 +506,6 @@ test('Draft rectangles should look right', async ({ page, context }) => {
`const part001 = startSketchOn('-XZ')` `const part001 = startSketchOn('-XZ')`
) )
await page.waitForTimeout(300) // TODO detect animation ending, or disable animation
await u.closeDebugPanel()
const startXPx = 600
// Equip the rectangle tool
await page.getByRole('button', { name: 'Line' }).click()
await page.getByRole('button', { name: 'Rectangle' }).click()
// Draw the rectangle
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 30)
await page.mouse.move(startXPx + PUR * 10, 500 - PUR * 10, { steps: 5 })
// Ensure the draft rectangle looks the same as it usually does
await expect(page).toHaveScreenshot({
maxDiffPixels: 100,
})
})
test.describe('Client side scene scale should match engine scale', () => {
test('Inch scale', async ({ page }) => {
const u = getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.goto('/')
await u.waitForAuthSkipAppStart()
await u.openDebugPanel()
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled()
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).toBeVisible()
// click on "Start Sketch" button
await u.clearCommandLogs()
await u.doAndWaitForImageDiff(
() => page.getByRole('button', { name: 'Start Sketch' }).click(),
200
)
// select a plane
await page.mouse.click(700, 200)
await expect(page.locator('.cm-content')).toHaveText(
`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
const startXPx = 600 const startXPx = 600
@ -610,24 +560,26 @@ test.describe('Client side scene scale should match engine scale', () => {
}) })
}) })
test('Millimeter scale', async ({ page }) => { test('Client side scene scale should match engine scale mm', async ({
await page.addInitScript( page,
async ({ settingsKey, settings }) => { context,
localStorage.setItem(settingsKey, settings) }) => {
}, await context.addInitScript(async () => {
{ localStorage.setItem(
settingsKey: TEST_SETTINGS_KEY, 'SETTINGS_PERSIST_KEY',
settings: TOML.stringify({ JSON.stringify({
settings: { baseUnit: 'mm',
...TEST_SETTINGS, cameraControls: 'KittyCAD',
modeling: { defaultDirectory: '',
...TEST_SETTINGS.modeling, defaultProjectName: 'project-$nnn',
defaultUnit: 'mm', onboardingStatus: 'dismissed',
}, showDebugPanel: true,
}, textWrapping: 'On',
}), theme: 'dark',
} unitSystem: 'metric',
})
) )
})
const u = 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
@ -638,9 +590,7 @@ test.describe('Client side scene scale should match engine scale', () => {
await expect( await expect(
page.getByRole('button', { name: 'Start Sketch' }) page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled() ).not.toBeDisabled()
await expect( await expect(page.getByRole('button', { name: 'Start Sketch' })).toBeVisible()
page.getByRole('button', { name: 'Start Sketch' })
).toBeVisible()
// click on "Start Sketch" button // click on "Start Sketch" button
await u.clearCommandLogs() await u.clearCommandLogs()
@ -708,7 +658,6 @@ test.describe('Client side scene scale should match engine scale', () => {
maxDiffPixels: 100, maxDiffPixels: 100,
}) })
}) })
})
test('Sketch on face with none z-up', async ({ page, context }) => { test('Sketch on face with none z-up', async ({ page, context }) => {
const u = getUtils(page) const u = getUtils(page)
@ -717,14 +666,14 @@ test('Sketch on face with none z-up', async ({ page, context }) => {
'persistCode', 'persistCode',
`const part001 = startSketchOn('-XZ') `const part001 = startSketchOn('-XZ')
|> startProfileAt([1.4, 2.47], %) |> startProfileAt([1.4, 2.47], %)
|> line([9.31, 10.55], %, 'seg01') |> line({ to: [9.31, 10.55], tag: 'seg01' }, %)
|> line([11.91, -10.42], %) |> line([11.91, -10.42], %)
|> close(%) |> close(%)
|> extrude(5 + 7, %) |> extrude(5 + 7, %)
const part002 = startSketchOn(part001, 'seg01') const part002 = startSketchOn(part001, 'seg01')
|> startProfileAt([8, 8], %) |> startProfileAt([-2.89, 1.82], %)
|> line([4.68, 3.05], %) |> line([4.68, 3.05], %)
|> line([0, -7.79], %, 'seg02') |> line({ to: [0, -7.79], tag: 'seg02' }, %)
|> close(%) |> close(%)
|> extrude(5 + 7, %) |> extrude(5 + 7, %)
` `
@ -734,19 +683,6 @@ const part002 = startSketchOn(part001, 'seg01')
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()
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( await expect(
page.getByRole('button', { name: 'Start Sketch' }) page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled() ).not.toBeDisabled()
@ -764,4 +700,6 @@ const part002 = startSketchOn(part001, 'seg01')
await expect(page).toHaveScreenshot({ await expect(page).toHaveScreenshot({
maxDiffPixels: 100, maxDiffPixels: 100,
}) })
await page.waitForTimeout(200)
}) })

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 65 KiB

View File

@ -1,46 +0,0 @@
import { SaveSettingsPayload } from 'lib/settings/settingsTypes'
import { Themes } from 'lib/theme'
export const TEST_SETTINGS_KEY = '/user.toml'
export const TEST_SETTINGS = {
app: {
theme: Themes.Dark,
onboardingStatus: 'dismissed',
projectDirectory: '',
},
modeling: {
defaultUnit: 'in',
mouseControls: 'KittyCAD',
showDebugPanel: true,
},
projects: {
defaultProjectName: 'project-$nnn',
},
textEditor: {
textWrapping: true,
},
} satisfies Partial<SaveSettingsPayload>
export const TEST_SETTINGS_ONBOARDING = {
...TEST_SETTINGS,
app: { ...TEST_SETTINGS.app, onboardingStatus: '/export ' },
} satisfies Partial<SaveSettingsPayload>
export const TEST_SETTINGS_CORRUPTED = {
app: {
theme: Themes.Dark,
onboardingStatus: 'dismissed',
projectDirectory: 123 as any,
},
modeling: {
defaultUnit: 'invalid' as any,
mouseControls: `() => alert('hack the planet')` as any,
showDebugPanel: true,
},
projects: {
defaultProjectName: false as any,
},
textEditor: {
textWrapping: true,
},
} satisfies Partial<SaveSettingsPayload>

View File

@ -33,7 +33,7 @@ async function clearCommandLogs(page: Page) {
} }
async function expectCmdLog(page: Page, locatorStr: string) { async function expectCmdLog(page: Page, locatorStr: string) {
await expect(page.locator(locatorStr).last()).toBeVisible() await expect(page.locator(locatorStr)).toBeVisible()
} }
async function waitForDefaultPlanesToBeVisible(page: Page) { async function waitForDefaultPlanesToBeVisible(page: Page) {
@ -44,44 +44,26 @@ async function waitForDefaultPlanesToBeVisible(page: Page) {
) )
} }
async function openKclCodePanel(page: Page) {
const paneLocator = page.getByRole('tab', { name: 'KCL Code', exact: false })
const isOpen = (await paneLocator?.getAttribute('aria-selected')) === 'true'
if (!isOpen) {
await paneLocator.click()
await paneLocator.and(page.locator('[aria-selected="true"]')).waitFor()
}
}
async function closeKclCodePanel(page: Page) {
const paneLocator = page.getByRole('tab', { name: 'KCL Code', exact: false })
const isOpen = (await paneLocator?.getAttribute('aria-selected')) === 'true'
if (isOpen) {
await paneLocator.click()
await paneLocator
.and(page.locator(':not([aria-selected="true"])'))
.waitFor()
}
}
async function openDebugPanel(page: Page) { async function openDebugPanel(page: Page) {
const debugLocator = page.getByRole('tab', { name: 'Debug', exact: false }) const isOpen =
const isOpen = (await debugLocator?.getAttribute('aria-selected')) === 'true' (await page
.locator('[data-testid="debug-panel"]')
?.getAttribute('open')) === ''
if (!isOpen) { if (!isOpen) {
await debugLocator.click() await page.getByText('Debug').click()
await debugLocator.and(page.locator('[aria-selected="true"]')).waitFor() await page.getByTestId('debug-panel').and(page.locator('[open]')).waitFor()
} }
} }
async function closeDebugPanel(page: Page) { async function closeDebugPanel(page: Page) {
const debugLocator = page.getByRole('tab', { name: 'Debug', exact: false }) const isOpen =
const isOpen = (await debugLocator?.getAttribute('aria-selected')) === 'true' (await page.getByTestId('debug-panel')?.getAttribute('open')) === ''
if (isOpen) { if (isOpen) {
await debugLocator.click() await page.getByText('Debug').click()
await debugLocator await page
.and(page.locator(':not([aria-selected="true"])')) .getByTestId('debug-panel')
.and(page.locator(':not([open])'))
.waitFor() .waitFor()
} }
} }
@ -99,19 +81,20 @@ export function getUtils(page: Page) {
removeCurrentCode: () => removeCurrentCode(page), removeCurrentCode: () => removeCurrentCode(page),
sendCustomCmd: (cmd: EngineCommand) => sendCustomCmd(page, cmd), sendCustomCmd: (cmd: EngineCommand) => sendCustomCmd(page, cmd),
updateCamPosition: async (xyz: [number, number, number]) => { updateCamPosition: async (xyz: [number, number, number]) => {
const fillInput = async (axis: 'x' | 'y' | 'z', value: number) => { const fillInput = async () => {
await page.fill(`[data-testid="cam-${axis}-position"]`, String(value)) await page.fill('[data-testid="cam-x-position"]', String(xyz[0]))
await page.waitForTimeout(100) await page.fill('[data-testid="cam-y-position"]', String(xyz[1]))
await page.fill('[data-testid="cam-z-position"]', String(xyz[2]))
} }
await fillInput()
await fillInput('x', xyz[0]) await page.waitForTimeout(100)
await fillInput('y', xyz[1]) await fillInput()
await fillInput('z', xyz[2]) await page.waitForTimeout(100)
await fillInput()
await page.waitForTimeout(100)
}, },
clearCommandLogs: () => clearCommandLogs(page), clearCommandLogs: () => clearCommandLogs(page),
expectCmdLog: (locatorStr: string) => expectCmdLog(page, locatorStr), expectCmdLog: (locatorStr: string) => expectCmdLog(page, locatorStr),
openKclCodePanel: () => openKclCodePanel(page),
closeKclCodePanel: () => closeKclCodePanel(page),
openDebugPanel: () => openDebugPanel(page), openDebugPanel: () => openDebugPanel(page),
closeDebugPanel: () => closeDebugPanel(page), closeDebugPanel: () => closeDebugPanel(page),
openAndClearDebugPanel: async () => { openAndClearDebugPanel: async () => {

View File

@ -1,10 +1,7 @@
import { browser, $, expect } from '@wdio/globals' import { browser, $, expect } from '@wdio/globals'
import fs from 'fs/promises' import fs from 'fs/promises'
const documentsDir = `${process.env.HOME}/Documents` const defaultDir = `${process.env.HOME}/Documents/zoo-modeling-app-projects`
const userSettingsFile = `${process.env.HOME}/.config/dev.zoo.modeling-app/user.toml`
const defaultProjectDir = `${documentsDir}/zoo-modeling-app-projects`
const newProjectDir = `${documentsDir}/a-different-directory`
const userCodeDir = '/tmp/kittycad_user_code' const userCodeDir = '/tmp/kittycad_user_code'
async function click(element: WebdriverIO.Element): Promise<void> { async function click(element: WebdriverIO.Element): Promise<void> {
@ -13,25 +10,12 @@ async function click(element: WebdriverIO.Element): Promise<void> {
await browser.execute('arguments[0].click();', element) await browser.execute('arguments[0].click();', element)
} }
/* Shoutout to @Sheap on Github for a great workaround utility:
* https://github.com/tauri-apps/tauri/issues/6541#issue-1638944060
*/
async function setDatasetValue(
field: WebdriverIO.Element,
property: string,
value: string
) {
await browser.execute(`arguments[0].dataset.${property} = "${value}"`, field)
}
describe('ZMA (Tauri, Linux)', () => { describe('ZMA (Tauri, Linux)', () => {
it('opens the auth page and signs in', async () => { it('opens the auth page and signs in', async () => {
// Clean up filesystem from previous tests // Clean up filesystem from previous tests
await new Promise((resolve) => setTimeout(resolve, 100)) await new Promise((resolve) => setTimeout(resolve, 100))
await fs.rm(defaultProjectDir, { force: true, recursive: true }) await fs.rm(defaultDir, { force: true, recursive: true })
await fs.rm(userCodeDir, { force: true }) await fs.rm(userCodeDir, { force: true })
await fs.rm(userSettingsFile, { force: true })
await fs.mkdir(newProjectDir, { recursive: true })
const signInButton = await $('[data-testid="sign-in-button"]') const signInButton = await $('[data-testid="sign-in-button"]')
expect(await signInButton.getText()).toEqual('Sign in') expect(await signInButton.getText()).toEqual('Sign in')
@ -81,25 +65,13 @@ describe('ZMA (Tauri, Linux)', () => {
const settingsButton = await $('[data-testid="settings-button"]') const settingsButton = await $('[data-testid="settings-button"]')
await click(settingsButton) await click(settingsButton)
const projectDirInput = await $('[data-testid="project-directory-input"]') const defaultDirInput = await $('[data-testid="default-directory-input"]')
expect(await projectDirInput.getValue()).toEqual(defaultProjectDir) expect(await defaultDirInput.getValue()).toEqual(defaultDir)
/* const nameInput = await $('[data-testid="name-input"]')
* We've set up the project directory input (in initialSettings.tsx)
* to be able to skip the folder selection dialog if data-testValue
* has a value, allowing us to test the input otherwise works.
*/
await setDatasetValue(projectDirInput, 'testValue', newProjectDir)
const projectDirButton = await $('[data-testid="project-directory-button"]')
await click(projectDirButton)
await new Promise((resolve) => setTimeout(resolve, 500))
// This line is broken. I need a different way to grab the toast
await expect(await $('div*=Set project directory to')).toBeDisplayed()
const nameInput = await $('[data-testid="projects-defaultProjectName"]')
expect(await nameInput.getValue()).toEqual('project-$nnn') expect(await nameInput.getValue()).toEqual('project-$nnn')
const closeButton = await $('[data-testid="settings-close-button"]') const closeButton = await $('[data-testid="close-button"]')
await click(closeButton) await click(closeButton)
}) })

View File

@ -1,45 +1,36 @@
{ {
"name": "untitled-app", "name": "untitled-app",
"version": "0.17.3", "version": "0.17.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@codemirror/autocomplete": "^6.16.0", "@codemirror/autocomplete": "^6.15.0",
"@fortawesome/fontawesome-svg-core": "^6.5.2", "@fortawesome/fontawesome-svg-core": "^6.4.2",
"@fortawesome/free-brands-svg-icons": "^6.5.2", "@fortawesome/free-brands-svg-icons": "^6.4.2",
"@fortawesome/free-solid-svg-icons": "^6.4.2", "@fortawesome/free-solid-svg-icons": "^6.4.2",
"@fortawesome/react-fontawesome": "^0.2.0", "@fortawesome/react-fontawesome": "^0.2.0",
"@headlessui/react": "^1.7.18", "@headlessui/react": "^1.7.18",
"@headlessui/tailwindcss": "^0.2.0", "@headlessui/tailwindcss": "^0.2.0",
"@kittycad/lib": "^0.0.58", "@kittycad/lib": "^0.0.56",
"@lezer/javascript": "^1.4.9", "@lezer/javascript": "^1.4.9",
"@open-rpc/client-js": "^1.8.1", "@open-rpc/client-js": "^1.8.1",
"@react-hook/resize-observer": "^1.2.6", "@react-hook/resize-observer": "^1.2.6",
"@replit/codemirror-interact": "^6.3.1", "@replit/codemirror-interact": "^6.3.0",
"@tauri-apps/api": "2.0.0-beta.8", "@tauri-apps/api": "^1.5.3",
"@tauri-apps/plugin-dialog": "^2.0.0-beta.2",
"@tauri-apps/plugin-fs": "^2.0.0-beta.2",
"@tauri-apps/plugin-http": "^2.0.0-beta.2",
"@tauri-apps/plugin-os": "^2.0.0-beta.2",
"@tauri-apps/plugin-process": "^2.0.0-beta.2",
"@tauri-apps/plugin-shell": "^2.0.0-beta.2",
"@tauri-apps/plugin-updater": "^2.0.0-beta.2",
"@testing-library/jest-dom": "^5.14.1", "@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^15.0.2", "@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.5.2", "@testing-library/user-event": "^14.5.2",
"@ts-stack/markdown": "^1.5.0", "@ts-stack/markdown": "^1.5.0",
"@tweenjs/tween.js": "^23.1.1", "@tweenjs/tween.js": "^23.1.1",
"@types/node": "^18.19.31", "@types/node": "^18.19.26",
"@types/react": "^18.2.77", "@types/react": "^18.2.67",
"@types/react-dom": "^18.2.25", "@types/react-dom": "^18.2.22",
"@uiw/react-codemirror": "^4.21.25", "@uiw/react-codemirror": "^4.21.24",
"@xstate/inspect": "^0.8.0", "@xstate/inspect": "^0.8.0",
"@xstate/react": "^3.2.2", "@xstate/react": "^3.2.2",
"crypto-js": "^4.2.0", "crypto-js": "^4.2.0",
"debounce-promise": "^3.1.2", "debounce-promise": "^3.1.2",
"decamelize": "^6.0.0", "formik": "^2.4.3",
"formik": "^2.4.5",
"fuse.js": "^7.0.0", "fuse.js": "^7.0.0",
"html2canvas-pro": "^1.4.3",
"http-server": "^14.1.1", "http-server": "^14.1.1",
"json-rpc-2.0": "^1.6.0", "json-rpc-2.0": "^1.6.0",
"jszip": "^3.10.1", "jszip": "^3.10.1",
@ -54,18 +45,19 @@
"react-modal-promise": "^1.0.2", "react-modal-promise": "^1.0.2",
"react-router-dom": "^6.22.3", "react-router-dom": "^6.22.3",
"sketch-helpers": "^0.0.4", "sketch-helpers": "^0.0.4",
"swr": "^2.2.5", "swr": "^2.2.2",
"three": "^0.163.0", "tauri-plugin-fs-extra-api": "https://github.com/tauri-apps/tauri-plugin-fs-extra#v1",
"three": "^0.160.0",
"toml": "^3.0.0",
"ts-node": "^10.9.2", "ts-node": "^10.9.2",
"typescript": "^5.4.5", "typescript": "^5.4.3",
"ua-parser-js": "^1.0.37",
"uuid": "^9.0.1", "uuid": "^9.0.1",
"vitest": "^1.5.0", "vitest": "^1.4.0",
"vscode-jsonrpc": "^8.1.0", "vscode-jsonrpc": "^8.1.0",
"vscode-languageserver-protocol": "^3.17.5", "vscode-languageserver-protocol": "^3.17.5",
"wasm-pack": "^0.12.1", "wasm-pack": "^0.12.1",
"web-vitals": "^3.5.2", "web-vitals": "^3.5.2",
"ws": "^8.16.0", "ws": "^8.13.0",
"xstate": "^4.38.2", "xstate": "^4.38.2",
"zustand": "^4.5.2" "zustand": "^4.5.2"
}, },
@ -92,7 +84,7 @@
"remove-importmeta": "sed -i 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\"; sed -i '' 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\" || echo \"sed for both mac and linux\"", "remove-importmeta": "sed -i 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\"; sed -i '' 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\" || echo \"sed for both mac and linux\"",
"wasm-prep": "rm -rf src/wasm-lib/pkg && mkdir src/wasm-lib/pkg && rm -rf src/wasm-lib/kcl/bindings", "wasm-prep": "rm -rf src/wasm-lib/pkg && mkdir src/wasm-lib/pkg && rm -rf src/wasm-lib/kcl/bindings",
"lint": "eslint --fix src", "lint": "eslint --fix src",
"bump-jsons": "echo \"$(jq --arg v \"$VERSION\" '.version=$v' package.json --indent 2)\" > package.json && echo \"$(jq --arg v \"$VERSION\" '.version=$v' src-tauri/tauri.conf.json --indent 2)\" > src-tauri/tauri.conf.json", "bump-jsons": "echo \"$(jq --arg v \"$VERSION\" '.version=$v' package.json --indent 2)\" > package.json && echo \"$(jq --arg v \"$VERSION\" '.package.version=$v' src-tauri/tauri.conf.json --indent 2)\" > src-tauri/tauri.conf.json",
"postinstall": "yarn xstate:typegen", "postinstall": "yarn xstate:typegen",
"xstate:typegen": "yarn xstate typegen \"src/**/*.ts?(x)\"" "xstate:typegen": "yarn xstate typegen \"src/**/*.ts?(x)\""
}, },
@ -116,33 +108,31 @@
}, },
"devDependencies": { "devDependencies": {
"@babel/plugin-proposal-private-property-in-object": "^7.21.11", "@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@babel/preset-env": "^7.24.3", "@babel/preset-env": "^7.23.3",
"@iarna/toml": "^2.2.5", "@playwright/test": "^1.39.0",
"@playwright/test": "^1.43.1", "@tauri-apps/cli": "^1.5.11",
"@tauri-apps/cli": "^2.0.0-beta.13",
"@types/crypto-js": "^4.2.2", "@types/crypto-js": "^4.2.2",
"@types/debounce-promise": "^3.1.9", "@types/debounce-promise": "^3.1.9",
"@types/pixelmatch": "^5.2.6", "@types/pixelmatch": "^5.2.6",
"@types/pngjs": "^6.0.4", "@types/pngjs": "^6.0.4",
"@types/react-modal": "^3.16.3", "@types/react-modal": "^3.16.3",
"@types/three": "^0.163.0", "@types/three": "^0.160.0",
"@types/ua-parser-js": "^0.7.39",
"@types/uuid": "^9.0.8", "@types/uuid": "^9.0.8",
"@types/wait-on": "^5.3.4", "@types/wait-on": "^5.3.4",
"@types/wicg-file-system-access": "^2023.10.5", "@types/wicg-file-system-access": "^2023.10.5",
"@types/ws": "^8.5.10", "@types/ws": "^8.5.5",
"@vitejs/plugin-react": "^4.2.1", "@vitejs/plugin-react": "^4.2.1",
"@wdio/cli": "^8.24.3", "@wdio/cli": "^8.24.3",
"@wdio/globals": "^8.36.0", "@wdio/globals": "^8.24.3",
"@wdio/local-runner": "^8.36.0", "@wdio/local-runner": "^8.35.1",
"@wdio/mocha-framework": "^8.36.0", "@wdio/mocha-framework": "^8.35.0",
"@wdio/spec-reporter": "^8.36.0", "@wdio/spec-reporter": "^8.32.4",
"@xstate/cli": "^0.5.17", "@xstate/cli": "^0.5.17",
"autoprefixer": "^10.4.19", "autoprefixer": "^10.4.19",
"eslint": "^8.57.0", "eslint": "^8.57.0",
"eslint-config-react-app": "^7.0.1", "eslint-config-react-app": "^7.0.1",
"eslint-plugin-css-modules": "^2.12.0", "eslint-plugin-css-modules": "^2.12.0",
"happy-dom": "^14.3.10", "happy-dom": "^14.3.1",
"husky": "^9.0.11", "husky": "^9.0.11",
"pixelmatch": "^5.3.0", "pixelmatch": "^5.3.0",
"pngjs": "^7.0.0", "pngjs": "^7.0.0",
@ -151,12 +141,12 @@
"prettier": "^2.8.0", "prettier": "^2.8.0",
"setimmediate": "^1.0.5", "setimmediate": "^1.0.5",
"tailwindcss": "^3.4.1", "tailwindcss": "^3.4.1",
"vite": "^5.2.9", "vite": "^5.2.2",
"vite-plugin-eslint": "^1.8.1", "vite-plugin-eslint": "^1.8.1",
"vite-plugin-package-version": "^1.1.0", "vite-plugin-package-version": "^1.1.0",
"vite-tsconfig-paths": "^4.3.2", "vite-tsconfig-paths": "^4.3.2",
"vitest-webgl-canvas-mock": "^1.1.0", "vitest-webgl-canvas-mock": "^1.1.0",
"wait-on": "^7.2.0", "wait-on": "^7.2.0",
"yarn": "^1.22.22" "yarn": "^1.22.19"
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 327 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 36 KiB

2589
src-tauri/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -7,28 +7,22 @@ license = ""
repository = "https://github.com/KittyCAD/modeling-app" repository = "https://github.com/KittyCAD/modeling-app"
default-run = "app" default-run = "app"
edition = "2021" edition = "2021"
rust-version = "1.70" rust-version = "1.60"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[build-dependencies] [build-dependencies]
tauri-build = { version = "2.0.0-beta.12", features = [] } tauri-build = { version = "1.5.1", features = [] }
[dependencies] [dependencies]
anyhow = "1" anyhow = "1"
kittycad = "0.2.67" kittycad = "0.2.63"
oauth2 = "4.4.2" oauth2 = "4.4.2"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
tauri = { version = "2.0.0-beta.15", features = [ "devtools", "unstable"] } tauri = { version = "1.6.1", features = [ "os-all", "dialog-all", "fs-all", "http-request", "path-all", "shell-open", "shell-open-api", "devtools"] }
tauri-plugin-dialog = { version = "2.0.0-beta.5" } tauri-plugin-fs-extra = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
tauri-plugin-fs = { version = "2.0.0-beta.5" } tokio = { version = "1.36.0", features = ["time"] }
tauri-plugin-http = { version = "2.0.0-beta.5" }
tauri-plugin-os = { version = "2.0.0-beta.2" }
tauri-plugin-process = { version = "2.0.0-beta.2" }
tauri-plugin-shell = { version = "2.0.0-beta.2" }
tauri-plugin-updater = { version = "2.0.0-beta.4" }
tokio = { version = "1.37.0", features = ["time"] }
toml = "0.8.2" toml = "0.8.2"
[features] [features]

View File

@ -1,89 +0,0 @@
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main-capability",
"description": "Capability for the main window",
"context": "local",
"windows": [
"main"
],
"permissions": [
"path:default",
"event:default",
"window:default",
"app:default",
"resources:default",
"menu:default",
"tray:default",
"fs:allow-create",
"fs:allow-read-file",
"fs:allow-read-text-file",
"fs:allow-write-file",
"fs:allow-write-text-file",
"fs:allow-read-dir",
"fs:allow-copy-file",
"fs:allow-mkdir",
"fs:allow-remove",
"fs:allow-remove",
"fs:allow-rename",
"fs:allow-exists",
"fs:allow-stat",
{
"identifier": "fs:scope",
"allow": [
{
"path": "$HOME/**/*"
},
{
"path": "$HOME/.config"
},
{
"path": "$HOME/.config/**/*"
},
{
"path": "$APPCONFIG"
},
{
"path": "$APPCONFIG/**/*"
},
{
"path": "$DOCUMENT"
},
{
"path": "$DOCUMENT/**/*"
}
]
},
"shell:allow-open",
"dialog:allow-open",
"dialog:allow-save",
"dialog:allow-message",
"dialog:allow-ask",
"dialog:allow-confirm",
{
"identifier": "http:default",
"allow": [
"https://dev.kittycad.io/*",
"https://dev.zoo.dev/*",
"https://kittycad.io/*",
"https://zoo.dev/*",
"https://api.dev.kittycad.io/*",
"https://api.dev.zoo.dev/*"
]
},
"os:allow-platform",
"os:allow-version",
"os:allow-os-type",
"os:allow-family",
"os:allow-arch",
"os:allow-exe-extension",
"os:allow-locale",
"os:allow-hostname",
"process:allow-restart",
"updater:default"
],
"platforms": [
"linux",
"macOS",
"windows"
]
}

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