Compare commits

..

1 Commits

Author SHA1 Message Date
7f14c7d56d void 2024-04-09 16:01:44 -05:00
412 changed files with 3986 additions and 9835 deletions

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

@ -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'
@ -285,7 +281,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 +295,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 +326,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

@ -15,7 +15,7 @@ concurrency:
cancel-in-progress: true cancel-in-progress: true
jobs: jobs:
generate-website-docs: generate-website-docs:
name: generate-website-docs name: generate-website-docs
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -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 }}

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

View File

@ -17,11 +17,11 @@ lineTo(to: [number], sketch_group: SketchGroup, tag?: String) -> SketchGroup
```js ```js
fn rectShape = (pos, w, l) => { fn rectShape = (pos, w, l) => {
const rr = startSketchOn('YZ') const rr = startSketchOn('YZ')
|> startProfileAt([pos[0] - (w / 2), pos[1] - (l / 2)], %) |> startProfileAt([pos[0] - (w / 2), pos[1] - (l / 2)], %)
|> lineTo([pos[0] + w / 2, pos[1] - (l / 2)], %, "edge1") |> lineTo([pos[0] + w / 2, pos[1] - (l / 2)], %, "edge1")
|> lineTo([pos[0] + w / 2, pos[1] + l / 2], %, "edge2") |> lineTo([pos[0] + w / 2, pos[1] + l / 2], %, "edge2")
|> lineTo([pos[0] - (w / 2), pos[1] + l / 2], %, "edge3") |> lineTo([pos[0] - (w / 2), pos[1] + l / 2], %, "edge3")
|> close(%, "edge4") |> close(%, "edge4")
return rr return rr
} }

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 }, %)"
] ]
}, },
{ {
@ -41924,7 +41924,7 @@
"unpublished": false, "unpublished": false,
"deprecated": false, "deprecated": false,
"examples": [ "examples": [
"fn rectShape = (pos, w, l) => {\n const rr = startSketchOn('YZ')\n |> startProfileAt([pos[0] - (w / 2), pos[1] - (l / 2)], %)\n |> lineTo([pos[0] + w / 2, pos[1] - (l / 2)], %, \"edge1\")\n |> lineTo([pos[0] + w / 2, pos[1] + l / 2], %, \"edge2\")\n |> lineTo([pos[0] - (w / 2), pos[1] + l / 2], %, \"edge3\")\n |> close(%, \"edge4\")\n return rr\n}\n\n// Create the mounting plate extrusion, holes, and fillets\nconst part = rectShape([0, 0], 20, 20)" "fn rectShape = (pos, w, l) => {\n const rr = startSketchOn('YZ')\n |> startProfileAt([pos[0] - (w / 2), pos[1] - (l / 2)], %)\n |> lineTo([pos[0] + w / 2, pos[1] - (l / 2)], %, \"edge1\")\n |> lineTo([pos[0] + w / 2, pos[1] + l / 2], %, \"edge2\")\n |> lineTo([pos[0] - (w / 2), pos[1] + l / 2], %, \"edge3\")\n |> close(%, \"edge4\")\n return rr\n}\n\n// Create the mounting plate extrusion, holes, and fillets\nconst part = rectShape([0, 0], 20, 20)"
] ]
}, },
{ {
@ -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"
] ]
}, },
{ {
@ -58824,7 +58824,7 @@
"deprecated": false, "deprecated": false,
"examples": [ "examples": [
"startSketchOn('XY')\n |> startProfileAt([0, 0], %)\n |> line([10, 10], %)\n |> line([20, 10], %, \"edge1\")\n |> close(%, \"edge2\")", "startSketchOn('XY')\n |> startProfileAt([0, 0], %)\n |> line([10, 10], %)\n |> line([20, 10], %, \"edge1\")\n |> close(%, \"edge2\")",
"fn cube = (pos, scale) => {\n const sg = startSketchOn('XY')\n |> startProfileAt(pos, %)\n |> line([0, scale], %)\n |> line([scale, 0], %)\n |> line([0, -scale], %)\n |> close(%)\n |> extrude(scale, %)\n\n return sg\n}\n\nconst box = cube([0, 0], 20)\n\nconst part001 = startSketchOn(box, \"start\")\n |> startProfileAt([0, 0], %)\n |> line([10, 10], %)\n |> line([20, 10], %, \"edge1\")\n |> close(%)\n |> extrude(20, %)" "fn cube = (pos, scale) => {\n const sg = startSketchOn('XY')\n |> startProfileAt(pos, %)\n |> line([0, scale], %)\n |> line([scale, 0], %)\n |> line([0, -scale], %)\n |> close(%)\n |> extrude(scale, %)\n\n return sg\n}\n\nconst box = cube([0, 0], 20)\n\nconst part001 = startSketchOn(box, \"start\")\n |> startProfileAt([0, 0], %)\n |> line([10, 10], %)\n |> line([20, 10], %, \"edge1\")\n |> close(%)\n |> extrude(20, %)"
] ]
}, },
{ {

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

@ -2,15 +2,10 @@ import { test, expect } from '@playwright/test'
import { getUtils } from './test-utils' import { getUtils } from './test-utils'
import waitOn from 'wait-on' import waitOn from 'wait-on'
import { roundOff } from 'lib/utils' import { roundOff } from 'lib/utils'
import { SaveSettingsPayload } from 'lib/settings/settingsTypes' import { basicStorageState } from './storageStates'
import { secrets } from './secrets'
import {
TEST_SETTINGS,
TEST_SETTINGS_KEY,
TEST_SETTINGS_CORRUPTED,
TEST_SETTINGS_ONBOARDING,
} from './storageStates'
import * as TOML from '@iarna/toml' import * as TOML from '@iarna/toml'
import { SaveSettingsPayload } from 'lib/settings/settingsTypes'
import { Themes } from 'lib/theme'
/* /*
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
@ -37,25 +32,13 @@ test.beforeEach(async ({ context, page }) => {
timeout: 5000, timeout: 5000,
}) })
await context.addInitScript(
async ({ token, settingsKey, settings }) => {
localStorage.setItem('TOKEN_PERSIST_KEY', token)
localStorage.setItem('persistCode', ``)
localStorage.setItem(settingsKey, settings)
},
{
token: secrets.token,
settingsKey: TEST_SETTINGS_KEY,
settings: TOML.stringify({ settings: TEST_SETTINGS }),
}
)
// 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' })
}) })
test.setTimeout(60000) test.setTimeout(60000)
test('Basic sketch', async ({ page }) => { test('Basic sketch', 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
@ -145,7 +128,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 +161,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 +308,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 +325,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 +340,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 +366,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,72 +376,81 @@ 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') { const TestSinglePlane = async ({
camPos = camCmdBackSide viewCmd,
expectedCode,
clickCoords,
}: {
viewCmd: [number, number, number]
expectedCode: string
clickCoords: { x: number; y: number }
}) => {
await u.openDebugPanel()
await u.clearCommandLogs()
await page.getByRole('button', { name: 'Start Sketch' }).click()
await u.updateCamPosition(viewCmd)
await u.closeDebugPanel()
await page.mouse.click(clickCoords.x, clickCoords.y)
await page.waitForTimeout(300) // wait for animation
await expect(page.getByRole('button', { name: 'Line' })).toBeVisible()
// draw a line
const startXPx = 600
await u.closeDebugPanel()
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
await expect(page.locator('.cm-content')).toHaveText(expectedCode)
await page.getByRole('button', { name: 'Line' }).click()
await u.openAndClearDebugPanel()
await page.getByRole('button', { name: 'Exit Sketch' }).click()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.clearCommandLogs()
await u.removeCurrentCode()
} }
const code = `const part001 = startSketchOn('${plane}') const codeTemplate = (
plane = 'XY'
) => `const part001 = startSketchOn('${plane}')
|> startProfileAt([1.14, -1.54], %)` |> startProfileAt([1.14, -1.54], %)`
await TestSinglePlane({
await u.openDebugPanel() viewCmd: camPos,
expectedCode: codeTemplate('XY'),
await u.clearCommandLogs() clickCoords: { x: 600, y: 388 }, // red plane
await page.getByRole('button', { name: 'Start Sketch' }).click() // clickCoords: { x: 600, y: 400 }, // red plane // clicks grid helper and that causes problems, should fix so that these coords work too.
await u.updateCamPosition(camPos)
await u.closeDebugPanel()
await page.mouse.click(clickCoords.x, clickCoords.y)
await page.waitForTimeout(300) // wait for animation
await expect(page.getByRole('button', { name: 'Line' })).toBeVisible()
// draw a line
const startXPx = 600
await u.closeDebugPanel()
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
await expect(page.locator('.cm-content')).toHaveText(code)
await page.getByRole('button', { name: 'Line' }).click()
await u.openAndClearDebugPanel()
await page.getByRole('button', { name: 'Exit Sketch' }).click()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.clearCommandLogs()
await u.removeCurrentCode()
}
test.describe('Can create sketches on all planes and their back sides', () => {
test('XY', async ({ page }) => {
await sketchOnPlaneAndBackSideTest(
page,
'XY',
{ x: 600, y: 388 } // red plane
// { 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 +482,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 +490,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,149 +501,145 @@ 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`)
}) })
test('Stored settings are validated and fall back to defaults', async ({ // Stored settings validation test
page, test.describe('Settings persistence and validation tests', () => {
context, // Override test setup
}) => {
const u = getUtils(page)
// Override beforeEach test setup
// with corrupted settings // with corrupted settings
await context.addInitScript( const storageState = structuredClone(basicStorageState)
async ({ settingsKey, settings }) => { const s = TOML.parse(storageState.origins[0].localStorage[2].value) as {
localStorage.setItem(settingsKey, settings) settings: SaveSettingsPayload
}, }
{ s.settings.app.theme = Themes.Dark
settingsKey: TEST_SETTINGS_KEY, s.settings.app.projectDirectory = 123 as any
settings: TOML.stringify({ settings: TEST_SETTINGS_CORRUPTED }), s.settings.modeling.defaultUnit = 'invalid' as any
} s.settings.modeling.mouseControls = `() => alert('hack the planet')` as any
) s.settings.projects.defaultProjectName = false as any
storageState.origins[0].localStorage[2].value = TOML.stringify(s)
await page.setViewportSize({ width: 1200, height: 500 }) test.use({ storageState })
await page.goto('/')
await u.waitForAuthSkipAppStart()
// Check the settings were reset test('Stored settings are validated and fall back to defaults', async ({
const storedSettings = TOML.parse( page,
await page.evaluate( }) => {
({ settingsKey }) => localStorage.getItem(settingsKey) || '{}', const u = getUtils(page)
{ settingsKey: TEST_SETTINGS_KEY } await page.setViewportSize({ width: 1200, height: 500 })
) await page.goto('/')
) as { settings: SaveSettingsPayload } await u.waitForAuthSkipAppStart()
expect(storedSettings.settings.app?.theme).toBe('dark') // Check the settings were reset
const storedSettings = TOML.parse(
await page.evaluate(() => localStorage.getItem('/user.toml') || '{}')
) as { settings: SaveSettingsPayload }
// Check that the invalid settings were removed expect(storedSettings.settings.app?.theme).toBe('dark')
expect(storedSettings.settings.modeling?.defaultUnit).toBe(undefined)
expect(storedSettings.settings.modeling?.mouseControls).toBe(undefined) // Check that the invalid settings were removed
expect(storedSettings.settings.app?.projectDirectory).toBe(undefined) expect(storedSettings.settings.modeling?.defaultUnit).toBe(undefined)
expect(storedSettings.settings.projects?.defaultProjectName).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,
}) => {
const u = getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/')
await u.waitForAuthSkipAppStart()
// Open the settings modal with the browser keyboard shortcut
await page.keyboard.press('Meta+Shift+,')
await expect(
page.getByRole('heading', { name: 'Settings', exact: true })
).toBeVisible()
await page
.locator('select[name="app-theme"]')
.selectOption({ value: 'light' })
// Verify the toast appeared
await expect(
page.getByText(`Set theme to "light" for this project`)
).toBeVisible()
// Check that the theme changed
await expect(page.locator('body')).not.toHaveClass(`body-bg dark`)
// Check that the user setting was not changed
await page.getByRole('radio', { name: 'User' }).click()
await expect(page.locator('select[name="app-theme"]')).toHaveValue('dark')
// Roll back to default "system" theme
await page
.getByText(
'themeRoll back themeRoll back to match defaultThe overall appearance of the appl'
)
.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('Project settings can be set and override user settings', async ({ // Onboarding tests
page, test.describe('Onboarding tests', () => {
}) => { // Override test setup
await page.setViewportSize({ width: 1200, height: 500 }) const storageState = structuredClone(basicStorageState)
await page.goto('/', { waitUntil: 'domcontentloaded' }) const s = TOML.parse(storageState.origins[0].localStorage[2].value) as {
await page settings: SaveSettingsPayload
.getByRole('button', { name: 'Start Sketch' }) }
.waitFor({ state: 'visible' }) s.settings.app.onboardingStatus = '/export'
storageState.origins[0].localStorage[2].value = TOML.stringify(s)
test.use({ storageState })
// Open the settings modal with the browser keyboard shortcut test('Onboarding redirects and code updating', async ({ page, context }) => {
await page.keyboard.press('Meta+Shift+,') const u = getUtils(page)
await expect( await page.setViewportSize({ width: 1200, height: 500 })
page.getByRole('heading', { name: 'Settings', exact: true }) await page.goto('/')
).toBeVisible() await u.waitForAuthSkipAppStart()
await page
.locator('select[name="app-theme"]')
.selectOption({ value: 'light' })
// Verify the toast appeared // Test that the redirect happened
await expect( await expect(page.url().split(':3000').slice(-1)[0]).toBe(
page.getByText(`Set theme to "light" for this project`) `/file/%2Fbrowser%2Fmain.kcl/onboarding/export`
).toBeVisible()
// Check that the theme changed
await expect(page.locator('body')).not.toHaveClass(`body-bg dark`)
// Check that the user setting was not changed
await page.getByRole('radio', { name: 'User' }).click()
await expect(page.locator('select[name="app-theme"]')).toHaveValue('dark')
// Roll back to default "system" theme
await page
.getByText(
'themeRoll back themeRoll back to match defaultThe overall appearance of the appl'
) )
.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 // Test that you come back to this page when you refresh
await page.getByRole('radio', { name: 'Project' }).click() await page.reload()
await expect(page.locator('select[name="app-theme"]')).toHaveValue('light') await expect(page.url().split(':3000').slice(-1)[0]).toBe(
}) `/file/%2Fbrowser%2Fmain.kcl/onboarding/export`
)
test('Onboarding redirects and code updating', async ({ page }) => { // Test that the onboarding pane loaded
const u = getUtils(page) const title = page.locator('[data-testid="onboarding-content"]')
await expect(title).toBeAttached()
// Override beforeEach test setup // Test that the code changes when you advance to the next step
await page.addInitScript( await page.locator('[data-testid="onboarding-next"]').click()
async ({ settingsKey, settings }) => { await expect(page.locator('.cm-content')).toHaveText('')
// Give some initial code, so we can test that it's cleared
localStorage.setItem('persistCode', 'const sigmaAllow = 15000')
localStorage.setItem(settingsKey, settings)
},
{
settingsKey: TEST_SETTINGS_KEY,
settings: TOML.stringify({ settings: TEST_SETTINGS_ONBOARDING }),
}
)
await page.setViewportSize({ width: 1200, height: 500 }) // Test that the code is not empty when you click on the next step
await page.goto('/') await page.locator('[data-testid="onboarding-next"]').click()
await u.waitForAuthSkipAppStart() await expect(page.locator('.cm-content')).toHaveText(/.+/)
})
// Test that the redirect happened
await expect(page.url().split(':3000').slice(-1)[0]).toBe(
`/file/%2Fbrowser%2Fmain.kcl/onboarding/export`
)
// Test that you come back to this page when you refresh
await page.reload()
await expect(page.url().split(':3000').slice(-1)[0]).toBe(
`/file/%2Fbrowser%2Fmain.kcl/onboarding/export`
)
// Test that the onboarding pane loaded
const title = page.locator('[data-testid="onboarding-content"]')
await expect(title).toBeAttached()
// Test that the code changes when you advance to the next step
await page.locator('[data-testid="onboarding-next"]').click()
await expect(page.locator('.cm-content')).toHaveText('')
// Test that the code is not empty when you click on the next step
await page.locator('[data-testid="onboarding-next"]').click()
await expect(page.locator('.cm-content')).toHaveText(/.+/)
}) })
test('Selections work on fresh and edited sketch', async ({ page }) => { test('Selections work on fresh and edited sketch', async ({ page }) => {
@ -933,21 +851,19 @@ test.describe('Command bar tests', () => {
await expect(page.locator('body')).not.toHaveClass(`body-bg dark`) await expect(page.locator('body')).not.toHaveClass(`body-bg dark`)
}) })
test('Can extrude from the command bar', async ({ page }) => { // Override test setup code
await page.addInitScript(async () => { const storageState = structuredClone(basicStorageState)
localStorage.setItem( storageState.origins[0].localStorage[1].value = `const distance = sqrt(20)
'persistCode', const part001 = startSketchOn('-XZ')
`const distance = sqrt(20) |> startProfileAt([-6.95, 4.98], %)
const part001 = startSketchOn('-XZ') |> line([25.1, 0.41], %)
|> startProfileAt([-6.95, 4.98], %) |> line([0.73, -14.93], %)
|> line([25.1, 0.41], %) |> line([-23.44, 0.52], %)
|> line([0.73, -14.93], %) |> close(%)
|> line([-23.44, 0.52], %) `
|> close(%) test.use({ storageState })
`
)
})
test('Can extrude from the command bar', 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 })
await page.goto('/') await page.goto('/')
@ -956,13 +872,15 @@ test.describe('Command bar tests', () => {
// Make sure the stream is up // 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 u.closeDebugPanel()
await expect( await expect(
page.getByRole('button', { name: 'Start Sketch' }) page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled() ).not.toBeDisabled()
await u.clearCommandLogs() await page.getByText('|> startProfileAt([-6.95, 4.98], %)').click()
await page.getByText('|> line([0.73, -14.93], %)').click() await expect(
await page.getByRole('button', { name: 'Extrude' }).isEnabled() page.getByRole('button', { name: 'Extrude' })
).not.toBeDisabled()
let cmdSearchBar = page.getByPlaceholder('Search commands') let cmdSearchBar = page.getByPlaceholder('Search commands')
await page.keyboard.press('Meta+K') await page.keyboard.press('Meta+K')
@ -980,25 +898,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)
@ -1139,9 +1055,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 +1067,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 +1096,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 +1105,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 +1152,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 +1265,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,95 +1339,92 @@ 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()
await u.clearCommandLogs() const doSnapAtDifferentScales = async (
await page.getByRole('button', { name: 'Start Sketch' }).click() camPos: [number, number, number],
await page.waitForTimeout(100) expectedCode: string
) => {
await u.clearCommandLogs()
await page.getByRole('button', { name: 'Start Sketch' }).click()
await page.waitForTimeout(100)
await u.openAndClearDebugPanel() await u.openAndClearDebugPanel()
await u.updateCamPosition(camPos) await u.updateCamPosition(camPos)
await u.closeDebugPanel() await u.closeDebugPanel()
// select a plane // select a plane
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 part001 = startSketchOn('XZ')` `const part001 = startSketchOn('XZ')`
) )
let prevContent = await page.locator('.cm-content').innerText() let prevContent = await page.locator('.cm-content').innerText()
const pointA = [700, 200] const pointA = [700, 200]
const pointB = [900, 200] const pointB = [900, 200]
const pointC = [900, 400] const pointC = [900, 400]
// draw three lines // draw three lines
await page.mouse.click(pointA[0], pointA[1]) await page.mouse.click(pointA[0], pointA[1])
await page.waitForTimeout(100) await page.waitForTimeout(100)
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 page.mouse.click(pointB[0], pointB[1]) await page.mouse.click(pointB[0], pointB[1])
await page.waitForTimeout(100) await page.waitForTimeout(100)
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 page.mouse.click(pointC[0], pointC[1]) await page.mouse.click(pointC[0], pointC[1])
await page.waitForTimeout(100) await page.waitForTimeout(100)
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 page.mouse.move(pointA[0] - 12, pointA[1] + 12) await page.mouse.move(pointA[0] - 12, pointA[1] + 12)
const pointNotQuiteA = [pointA[0] - 7, pointA[1] + 7] const pointNotQuiteA = [pointA[0] - 7, pointA[1] + 7]
await page.mouse.move(pointNotQuiteA[0], pointNotQuiteA[1], { steps: 10 }) await page.mouse.move(pointNotQuiteA[0], pointNotQuiteA[1], { steps: 10 })
await page.mouse.click(pointNotQuiteA[0], pointNotQuiteA[1]) await page.mouse.click(pointNotQuiteA[0], pointNotQuiteA[1])
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()
await page.getByRole('button', { name: 'Exit Sketch' }).click() await page.getByRole('button', { name: 'Exit Sketch' }).click()
await u.expectCmdLog('[data-message-type="execution-done"]') await u.expectCmdLog('[data-message-type="execution-done"]')
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(%)`
test('[0, 10000, 10000]', async ({ page }) => { await doSnapAtDifferentScales([0, 100, 100], codeTemplate(0.01, 0.01))
await doSnapAtDifferentScales(page, [0, 10000, 10000])
}) await doSnapAtDifferentScales([0, 10000, 10000], codeTemplate())
}) })
test('Sketch on face', async ({ page }) => { test('Sketch on face', 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')
@ -1632,44 +1549,3 @@ test('Sketch on face', async ({ page }) => {
|> 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,16 @@ 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 { basicSettings, basicStorageState } from './storageStates'
import * as TOML from '@iarna/toml' import * as TOML from '@iarna/toml'
test.beforeEach(async ({ page }) => { test.beforeEach(async ({ page }) => {
// reducedMotion kills animations, which speeds up tests and reduces flakiness // reducedMotion kills animations, which speeds up tests and reduces flakiness
await page.emulateMedia({ reducedMotion: 'reduce' }) await page.emulateMedia({ reducedMotion: 'reduce' })
})
// set the default settings test.use({
await page.addInitScript( storageState: structuredClone(basicStorageState),
async ({ token, settingsKey, settings }) => {
localStorage.setItem('TOKEN_PERSIST_KEY', token)
localStorage.setItem('persistCode', ``)
localStorage.setItem(settingsKey, settings)
},
{
token: secrets.token,
settingsKey: TEST_SETTINGS_KEY,
settings: TOML.stringify({ settings: TEST_SETTINGS }),
}
)
// Make the user avatar image always 404
// so we see the fallback menu icon for all snapshot tests
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 +316,10 @@ 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,
}) => {
await context.addInitScript(async () => { await context.addInitScript(async () => {
localStorage.setItem( localStorage.setItem(
'SETTINGS_PERSIST_KEY', 'SETTINGS_PERSIST_KEY',
@ -353,8 +336,8 @@ const extrudeDefaultPlane = async (context: any, page: any, plane: string) => {
}) })
) )
}) })
const u = getUtils(page)
const code = `const part001 = startSketchOn('${plane}') const makeCode = (plane = 'XY') => `const part001 = startSketchOn('${plane}')
|> startProfileAt([7.00, 4.40], %) |> startProfileAt([7.00, 4.40], %)
|> line([6.60, -0.20], %) |> line([6.60, -0.20], %)
|> line([2.80, 5.00], %) |> line([2.80, 5.00], %)
@ -363,11 +346,9 @@ const extrudeDefaultPlane = async (context: any, page: any, plane: string) => {
|> close(%) |> close(%)
|> extrude(10.00, %) |> extrude(10.00, %)
` `
await page.addInitScript(async (code: string) => { await context.addInitScript(async (code) => {
localStorage.setItem('persistCode', code) localStorage.setItem('persistCode', code)
}) }, makeCode('XY'))
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()
@ -377,48 +358,32 @@ const extrudeDefaultPlane = async (context: any, page: any, plane: string) => {
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(200) 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() const runSnapshotsForOtherPlanes = async (plane = 'XY') => {
await expect(page).toHaveScreenshot({ // clear code
maxDiffPixels: 100, await u.removeCurrentCode()
}) // add makeCode('XZ')
await u.openKclCodePanel() await u.openAndClearDebugPanel()
} await page.locator('.cm-content').fill(makeCode(plane))
// wait for execution done
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.clearAndCloseDebugPanel()
test.describe('extrude on default planes should be stable', () => { await page.getByText('Code').click()
test('XY', async ({ page, context }) => { await page.waitForTimeout(150)
await extrudeDefaultPlane(context, page, 'XY') await expect(page).toHaveScreenshot({
}) maxDiffPixels: 100,
})
await page.getByText('Code').click()
}
await runSnapshotsForOtherPlanes('XY')
await runSnapshotsForOtherPlanes('-XY')
test('XZ', async ({ page, context }) => { await runSnapshotsForOtherPlanes('XZ')
await extrudeDefaultPlane(context, page, 'XZ') await runSnapshotsForOtherPlanes('-XZ')
})
test('YZ', async ({ page, context }) => { await runSnapshotsForOtherPlanes('YZ')
await extrudeDefaultPlane(context, page, 'YZ') await runSnapshotsForOtherPlanes('-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 }) => { test('Draft segments should look right', async ({ page, context }) => {
@ -480,7 +445,9 @@ 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,
}) => {
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
@ -508,126 +475,75 @@ test('Draft rectangles should look right', async ({ page, context }) => {
) )
await page.waitForTimeout(300) // TODO detect animation ending, or disable animation await page.waitForTimeout(300) // TODO detect animation ending, or disable animation
await u.closeDebugPanel()
const startXPx = 600 const startXPx = 600
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
await expect(page.locator('.cm-content'))
.toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt([9.06, -12.22], %)`)
await page.waitForTimeout(100)
// Equip the rectangle tool await u.closeDebugPanel()
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 * 10)
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 30) await page.waitForTimeout(100)
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.locator('.cm-content'))
.toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt([9.06, -12.22], %)
|> line([9.14, 0], %)`)
await page.getByRole('button', { name: 'Tangential Arc' }).click()
await page.waitForTimeout(100)
await page.mouse.click(startXPx + PUR * 30, 500 - PUR * 20)
await expect(page.locator('.cm-content'))
.toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt([9.06, -12.22], %)
|> line([9.14, 0], %)
|> tangentialArcTo([27.34, -3.08], %)`)
// click tangential arc tool again to unequip it
await page.getByRole('button', { name: 'Tangential Arc' }).click()
await page.waitForTimeout(100)
// screen shot should show the sketch
await expect(page).toHaveScreenshot({
maxDiffPixels: 100,
})
// exit sketch
await u.openAndClearDebugPanel()
await page.getByRole('button', { name: 'Exit Sketch' }).click()
// wait for execution done
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.clearAndCloseDebugPanel()
await page.waitForTimeout(200)
// second screen shot should look almost identical, i.e. scale should be the same.
await expect(page).toHaveScreenshot({ await expect(page).toHaveScreenshot({
maxDiffPixels: 100, maxDiffPixels: 100,
}) })
}) })
test.describe('Client side scene scale should match engine scale', () => { test.describe('Client side scene scale should match engine scale - Millimeters', () => {
test('Inch scale', async ({ page }) => { const storageState = structuredClone(basicStorageState)
const u = getUtils(page) storageState.origins[0].localStorage[2].value = TOML.stringify({
await page.setViewportSize({ width: 1200, height: 500 }) settings: {
const PUR = 400 / 37.5 //pixeltoUnitRatio ...basicSettings,
await page.goto('/') modeling: {
await u.waitForAuthSkipAppStart() ...basicSettings.modeling,
await u.openDebugPanel() defaultUnit: 'mm',
},
await expect( },
page.getByRole('button', { name: 'Start Sketch' }) })
).not.toBeDisabled() test.use({
await expect( storageState,
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
const startXPx = 600
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
await expect(page.locator('.cm-content'))
.toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt([9.06, -12.22], %)`)
await page.waitForTimeout(100)
await u.closeDebugPanel()
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
await page.waitForTimeout(100)
await expect(page.locator('.cm-content'))
.toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt([9.06, -12.22], %)
|> line([9.14, 0], %)`)
await page.getByRole('button', { name: 'Tangential Arc' }).click()
await page.waitForTimeout(100)
await page.mouse.click(startXPx + PUR * 30, 500 - PUR * 20)
await expect(page.locator('.cm-content'))
.toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt([9.06, -12.22], %)
|> line([9.14, 0], %)
|> tangentialArcTo([27.34, -3.08], %)`)
// click tangential arc tool again to unequip it
await page.getByRole('button', { name: 'Tangential Arc' }).click()
await page.waitForTimeout(100)
// screen shot should show the sketch
await expect(page).toHaveScreenshot({
maxDiffPixels: 100,
})
// exit sketch
await u.openAndClearDebugPanel()
await page.getByRole('button', { name: 'Exit Sketch' }).click()
// wait for execution done
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.clearAndCloseDebugPanel()
await page.waitForTimeout(200)
// second screen shot should look almost identical, i.e. scale should be the same.
await expect(page).toHaveScreenshot({
maxDiffPixels: 100,
})
}) })
test('Millimeter scale', async ({ page }) => { test('Millimeters', async ({ page }) => {
await page.addInitScript(
async ({ settingsKey, settings }) => {
localStorage.setItem(settingsKey, settings)
},
{
settingsKey: TEST_SETTINGS_KEY,
settings: TOML.stringify({
settings: {
...TEST_SETTINGS,
modeling: {
...TEST_SETTINGS.modeling,
defaultUnit: 'mm',
},
},
}),
}
)
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
@ -722,7 +638,7 @@ test('Sketch on face with none z-up', async ({ page, context }) => {
|> 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([0, -7.79], %, 'seg02')
|> close(%) |> close(%)
@ -734,19 +650,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()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 35 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: 79 KiB

View File

@ -1,8 +1,9 @@
import { SaveSettingsPayload } from 'lib/settings/settingsTypes' import { SaveSettingsPayload } from 'lib/settings/settingsTypes'
import { secrets } from './secrets'
import * as TOML from '@iarna/toml'
import { Themes } from 'lib/theme' import { Themes } from 'lib/theme'
export const TEST_SETTINGS_KEY = '/user.toml' export const basicSettings = {
export const TEST_SETTINGS = {
app: { app: {
theme: Themes.Dark, theme: Themes.Dark,
onboardingStatus: 'dismissed', onboardingStatus: 'dismissed',
@ -21,26 +22,19 @@ export const TEST_SETTINGS = {
}, },
} satisfies Partial<SaveSettingsPayload> } satisfies Partial<SaveSettingsPayload>
export const TEST_SETTINGS_ONBOARDING = { export const basicStorageState = {
...TEST_SETTINGS, cookies: [],
app: { ...TEST_SETTINGS.app, onboardingStatus: '/export ' }, origins: [
} satisfies Partial<SaveSettingsPayload> {
origin: 'http://localhost:3000',
export const TEST_SETTINGS_CORRUPTED = { localStorage: [
app: { { name: 'TOKEN_PERSIST_KEY', value: secrets.token },
theme: Themes.Dark, { name: 'persistCode', value: '' },
onboardingStatus: 'dismissed', {
projectDirectory: 123 as any, name: '/user.toml',
}, value: TOML.stringify({ settings: basicSettings }),
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

@ -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

@ -3,34 +3,33 @@
"version": "0.17.3", "version": "0.17.3",
"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.5.2",
"@fortawesome/free-brands-svg-icons": "^6.5.2", "@fortawesome/free-brands-svg-icons": "^6.5.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", "@iarna/toml": "^2.2.5",
"@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": "^2.0.0-beta.7",
"@tauri-apps/plugin-dialog": "^2.0.0-beta.2", "@tauri-apps/plugin-dialog": "^2.0.0-beta.2",
"@tauri-apps/plugin-fs": "^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-http": "^2.0.0-beta.2",
"@tauri-apps/plugin-os": "^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-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.75",
"@types/react-dom": "^18.2.25", "@types/react-dom": "^18.2.22",
"@uiw/react-codemirror": "^4.21.25", "@uiw/react-codemirror": "^4.21.25",
"@xstate/inspect": "^0.8.0", "@xstate/inspect": "^0.8.0",
"@xstate/react": "^3.2.2", "@xstate/react": "^3.2.2",
@ -39,7 +38,6 @@
"decamelize": "^6.0.0", "decamelize": "^6.0.0",
"formik": "^2.4.5", "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,13 +52,13 @@
"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", "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",
@ -117,26 +115,24 @@
"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.24.3",
"@iarna/toml": "^2.2.5", "@playwright/test": "^1.39.0",
"@playwright/test": "^1.43.1", "@tauri-apps/cli": "^2.0.0-beta.12",
"@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.10",
"@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",
@ -151,12 +147,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.6",
"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"
} }
} }

View File

@ -1,4 +1,5 @@
import { defineConfig, devices } from '@playwright/test' import { defineConfig, devices } from '@playwright/test'
import { basicStorageState } from './e2e/playwright/storageStates'
/** /**
* Read environment variables from file. * Read environment variables from file.
@ -28,6 +29,9 @@ export default defineConfig({
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry', trace: 'on-first-retry',
/* Use a common shared localStorage */
storageState: basicStorageState,
}, },
/* Configure projects for major browsers */ /* Configure projects for major browsers */

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

478
src-tauri/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -12,22 +12,21 @@ rust-version = "1.70"
# 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 = "2.0.0-beta", 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 = "2.0.0-beta", features = [ "devtools", "unstable"] }
tauri-plugin-dialog = { version = "2.0.0-beta.5" } tauri-plugin-dialog = { version = "2.0.0-beta.0" }
tauri-plugin-fs = { version = "2.0.0-beta.5" } tauri-plugin-fs = { version = "2.0.0-beta.0" }
tauri-plugin-http = { version = "2.0.0-beta.5" } tauri-plugin-http = { version = "2.0.0-beta.0" }
tauri-plugin-os = { version = "2.0.0-beta.2" } tauri-plugin-os = { version = "2.0.0-beta.0" }
tauri-plugin-process = { version = "2.0.0-beta.2" } tauri-plugin-shell = { version = "2.0.0-beta.0" }
tauri-plugin-shell = { version = "2.0.0-beta.2" } tauri-plugin-updater = { version = "2.0.0-beta.0" }
tauri-plugin-updater = { version = "2.0.0-beta.4" }
tokio = { version = "1.37.0", features = ["time"] } tokio = { version = "1.37.0", features = ["time"] }
toml = "0.8.2" toml = "0.8.2"

View File

@ -77,9 +77,7 @@
"os:allow-arch", "os:allow-arch",
"os:allow-exe-extension", "os:allow-exe-extension",
"os:allow-locale", "os:allow-locale",
"os:allow-hostname", "os:allow-hostname"
"process:allow-restart",
"updater:default"
], ],
"platforms": [ "platforms": [
"linux", "linux",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

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