Compare commits
26 Commits
jtran/recu
...
franknoiro
Author | SHA1 | Date | |
---|---|---|---|
bf36e62b97 | |||
3e45d7ac4e | |||
259279fd53 | |||
0ef6eac239 | |||
c674feb782 | |||
fba3d7c5c1 | |||
8b8fb696d0 | |||
d05f3c00b9 | |||
2541e0c0ea | |||
5e5a204244 | |||
032c2fdd24 | |||
27883e7800 | |||
1ccb810e23 | |||
1c83f148d9 | |||
c7f533b38e | |||
2b711d216f | |||
c67511f67c | |||
d9423219d1 | |||
3f270d8bcf | |||
4c7b72329d | |||
4c060f3d2f | |||
f3afbe8a7b | |||
dad7a84798 | |||
1a560fdc6a | |||
54eea741b5 | |||
24cc00e9c5 |
@ -25,6 +25,7 @@
|
||||
"files": ["e2e/**/*.ts"], // Update the pattern based on your file structure
|
||||
"rules": {
|
||||
"@typescript-eslint/no-floating-promises": "warn",
|
||||
"suggest-no-throw/suggest-no-throw": "off",
|
||||
"testing-library/prefer-screen-queries": "off"
|
||||
}
|
||||
},
|
||||
|
2
.github/workflows/build-test-web.yml
vendored
@ -44,6 +44,8 @@ jobs:
|
||||
- run: yarn build:wasm
|
||||
- run: yarn xstate:typegen
|
||||
- run: yarn tsc
|
||||
- name: Lint
|
||||
run: yarn eslint --max-warnings 0 src e2e
|
||||
|
||||
|
||||
check-typos:
|
||||
|
31
.github/workflows/label-issues.yml
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
name: Label Issues
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
permissions:
|
||||
issues: write
|
||||
jobs:
|
||||
label:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Check if issue opener is ZooSpiritWolf
|
||||
id: check_opener
|
||||
uses: actions/github-script@v4
|
||||
with:
|
||||
script: |
|
||||
const issueOpener = context.payload.issue.user.login;
|
||||
return issueOpener === 'ZooSpiritWolf';
|
||||
|
||||
- name: Add labels
|
||||
if: steps.check_opener.outputs.result == 'true'
|
||||
uses: actions/github-script@v4
|
||||
with:
|
||||
script: |
|
||||
github.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.payload.issue.number,
|
||||
labels: ['bug', 'regression', 'high-priority']
|
||||
});
|
2
.github/workflows/playwright.yml
vendored
@ -346,7 +346,7 @@ jobs:
|
||||
run: yarn build:wasm
|
||||
- name: build electron
|
||||
shell: bash
|
||||
run: yarn electron:package
|
||||
run: yarn tron:package
|
||||
- uses: actions/download-artifact@v4
|
||||
if: ${{ !cancelled() && (success() || failure()) }}
|
||||
continue-on-error: true
|
||||
|
@ -101,7 +101,7 @@ This will start the application and hot-reload on changed.
|
||||
|
||||
Devtools can be opened with the usual Cmd/Ctrl-Shift-I.
|
||||
|
||||
To build, run `yarn electron:package`.
|
||||
To build, run `yarn tron:package`.
|
||||
|
||||
## Checking out commits / Bisecting
|
||||
|
||||
|
@ -9,6 +9,7 @@ test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
test.describe('Copilot ghost text', () => {
|
||||
// eslint-disable-next-line jest/valid-title
|
||||
test.skip(true, 'Needs to get covered again')
|
||||
|
||||
test('completes code in empty file', async ({ page }) => {
|
||||
|
188
e2e/playwright/desktop-export.spec.ts
Normal file
@ -0,0 +1,188 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { getUtils, setupElectron, tearDown } from './test-utils'
|
||||
import fsp from 'fs/promises'
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
|
||||
test(
|
||||
'export works on the first try',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
test.skip(
|
||||
process.platform === 'win32',
|
||||
'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557'
|
||||
)
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
await Promise.all([fsp.mkdir(`${dir}/bracket`, { recursive: true })])
|
||||
await Promise.all([
|
||||
fsp.copyFile(
|
||||
'src/wasm-lib/tests/executor/inputs/router-template-slate.kcl',
|
||||
`${dir}/bracket/other.kcl`
|
||||
),
|
||||
fsp.copyFile(
|
||||
'src/wasm-lib/tests/executor/inputs/focusrite_scarlett_mounting_braket.kcl',
|
||||
`${dir}/bracket/main.kcl`
|
||||
),
|
||||
])
|
||||
},
|
||||
})
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
|
||||
page.on('console', console.log)
|
||||
|
||||
await test.step('on open of project', async () => {
|
||||
await expect(page.getByText(`bracket`)).toBeVisible()
|
||||
|
||||
// open the project
|
||||
await page.getByText(`bracket`).click()
|
||||
|
||||
// wait for the project to load
|
||||
await expect(page.getByTestId('loading')).toBeAttached()
|
||||
await expect(page.getByTestId('loading')).not.toBeAttached({
|
||||
timeout: 20_000,
|
||||
})
|
||||
|
||||
// expect zero errors in guter
|
||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||
|
||||
// export the model
|
||||
const exportButton = page.getByTestId('export-pane-button')
|
||||
await expect(exportButton).toBeVisible()
|
||||
|
||||
const gltfOption = page.getByText('glTF')
|
||||
const submitButton = page.getByText('Confirm Export')
|
||||
const exportingToastMessage = page.getByText(`Exporting...`)
|
||||
const errorToastMessage = page.getByText(`Error while exporting`)
|
||||
const engineErrorToastMessage = page.getByText(`Nothing to export`)
|
||||
const alreadyExportingToastMessage = page.getByText(`Already exporting`)
|
||||
|
||||
// Click the export button
|
||||
await exportButton.click()
|
||||
|
||||
await expect(gltfOption).toBeVisible()
|
||||
await expect(page.getByText('STL')).toBeVisible()
|
||||
|
||||
await page.keyboard.press('Enter')
|
||||
|
||||
// Click the checkbox
|
||||
await expect(submitButton).toBeVisible()
|
||||
|
||||
await page.waitForTimeout(500)
|
||||
|
||||
await page.keyboard.press('Enter')
|
||||
|
||||
// Find the toast.
|
||||
// Look out for the toast message
|
||||
await expect(exportingToastMessage).toBeVisible()
|
||||
await expect(alreadyExportingToastMessage).not.toBeVisible()
|
||||
|
||||
// Expect it to succeed.
|
||||
await expect(errorToastMessage).not.toBeVisible()
|
||||
await expect(engineErrorToastMessage).not.toBeVisible()
|
||||
|
||||
const successToastMessage = page.getByText(`Exported successfully`)
|
||||
await expect(successToastMessage).toBeVisible()
|
||||
await expect(exportingToastMessage).not.toBeVisible()
|
||||
|
||||
await test.step('Check the export size', async () => {
|
||||
await expect
|
||||
.poll(
|
||||
async () => {
|
||||
try {
|
||||
const outputGltf = await fsp.readFile('output.gltf')
|
||||
return outputGltf.byteLength
|
||||
} catch (e) {
|
||||
return 0
|
||||
}
|
||||
},
|
||||
{ timeout: 15_000 }
|
||||
)
|
||||
.toBe(477327)
|
||||
|
||||
// clean up output.gltf
|
||||
await fsp.rm('output.gltf')
|
||||
})
|
||||
})
|
||||
|
||||
await test.step('on open of file in file pane', async () => {
|
||||
const u = await getUtils(page)
|
||||
await u.openFilePanel()
|
||||
|
||||
const otherKclButton = page.getByRole('button', { name: 'other.kcl' })
|
||||
|
||||
// Click the file
|
||||
await otherKclButton.click()
|
||||
|
||||
// Close the file pane
|
||||
await u.closeFilePanel()
|
||||
|
||||
// wait for it to finish executing (todo: make this more robust)
|
||||
await page.waitForTimeout(1000)
|
||||
// expect zero errors in guter
|
||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||
|
||||
// export the model
|
||||
const exportButton = page.getByTestId('export-pane-button')
|
||||
await expect(exportButton).toBeVisible()
|
||||
|
||||
const gltfOption = page.getByText('glTF')
|
||||
const submitButton = page.getByText('Confirm Export')
|
||||
const exportingToastMessage = page.getByText(`Exporting...`)
|
||||
const errorToastMessage = page.getByText(`Error while exporting`)
|
||||
const engineErrorToastMessage = page.getByText(`Nothing to export`)
|
||||
const alreadyExportingToastMessage = page.getByText(`Already exporting`)
|
||||
|
||||
// Click the export button
|
||||
await exportButton.click()
|
||||
|
||||
await expect(gltfOption).toBeVisible()
|
||||
await expect(page.getByText('STL')).toBeVisible()
|
||||
|
||||
await page.keyboard.press('Enter')
|
||||
|
||||
// Click the checkbox
|
||||
await expect(submitButton).toBeVisible()
|
||||
|
||||
await page.keyboard.press('Enter')
|
||||
|
||||
// Find the toast.
|
||||
// Look out for the toast message
|
||||
await expect(exportingToastMessage).toBeVisible()
|
||||
await expect(alreadyExportingToastMessage).not.toBeVisible()
|
||||
|
||||
// Expect it to succeed.
|
||||
await expect(errorToastMessage).not.toBeVisible()
|
||||
await expect(engineErrorToastMessage).not.toBeVisible()
|
||||
|
||||
const successToastMessage = page.getByText(`Exported successfully`)
|
||||
await expect(successToastMessage).toBeVisible()
|
||||
await expect(exportingToastMessage).not.toBeVisible()
|
||||
|
||||
await test.step('Check the export size', async () => {
|
||||
await expect
|
||||
.poll(
|
||||
async () => {
|
||||
try {
|
||||
const outputGltf = await fsp.readFile('output.gltf')
|
||||
return outputGltf.byteLength
|
||||
} catch (e) {
|
||||
return 0
|
||||
}
|
||||
},
|
||||
{ timeout: 15_000 }
|
||||
)
|
||||
.toBe(105022)
|
||||
|
||||
// clean up output.gltf
|
||||
await fsp.rm('output.gltf')
|
||||
})
|
||||
await electronApp.close()
|
||||
})
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
@ -714,17 +714,15 @@ test.describe('Editor tests', () => {
|
||||
|> close(%)`)
|
||||
})
|
||||
|
||||
// failing for the same reason as "Can edit a sketch that has been extruded in the same pipe"
|
||||
// please fix together
|
||||
test.fixme('Can undo a sketch modification with ctrl+z', async ({ page }) => {
|
||||
test('Can undo a sketch modification with ctrl+z', async ({ page }) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`const sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([4.61, -14.01], %)
|
||||
|> startProfileAt([4.61, -10.01], %)
|
||||
|> line([12.73, -0.09], %)
|
||||
|> tangentialArcTo([24.95, -5.38], %)
|
||||
|> tangentialArcTo([24.95, -0.38], %)
|
||||
|> close(%)
|
||||
|> extrude(5, %)`
|
||||
)
|
||||
@ -759,11 +757,11 @@ test.describe('Editor tests', () => {
|
||||
})
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
const startPX = [665, 458]
|
||||
const startPX = [665, 397]
|
||||
|
||||
const dragPX = 40
|
||||
|
||||
await page.getByText('startProfileAt([4.61, -14.01], %)').click()
|
||||
await page.getByText('startProfileAt([4.61, -10.01], %)').click()
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Edit Sketch' })
|
||||
).toBeVisible()
|
||||
@ -801,7 +799,7 @@ test.describe('Editor tests', () => {
|
||||
// drag tangentialArcTo handle
|
||||
const tangentEnd = await u.getBoundingBox('[data-overlay-index="1"]')
|
||||
await page.dragAndDrop('#stream', '#stream', {
|
||||
sourcePosition: { x: tangentEnd.x, y: tangentEnd.y - 5 },
|
||||
sourcePosition: { x: tangentEnd.x + 10, y: tangentEnd.y - 5 },
|
||||
targetPosition: {
|
||||
x: tangentEnd.x + dragPX,
|
||||
y: tangentEnd.y + dragPX,
|
||||
@ -813,12 +811,12 @@ test.describe('Editor tests', () => {
|
||||
// expect the code to have changed
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([7.12, -16.82], %)
|
||||
|> line([15.4, -2.74], %)
|
||||
|> tangentialArcTo([24.95, -5.38], %)
|
||||
|> line([2.65, -2.69], %)
|
||||
|> close(%)
|
||||
|> extrude(5, %)`)
|
||||
|> startProfileAt([7.12, -12.68], %)
|
||||
|> line([15.39, -2.78], %)
|
||||
|> tangentialArcTo([27.6, -3.05], %)
|
||||
|> close(%)
|
||||
|> extrude(5, %)
|
||||
`)
|
||||
|
||||
// Hit undo
|
||||
await page.keyboard.down('Control')
|
||||
@ -827,11 +825,11 @@ test.describe('Editor tests', () => {
|
||||
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([7.12, -16.82], %)
|
||||
|> line([15.4, -2.74], %)
|
||||
|> tangentialArcTo([24.95, -5.38], %)
|
||||
|> close(%)
|
||||
|> extrude(5, %)`)
|
||||
|> startProfileAt([7.12, -12.68], %)
|
||||
|> line([15.39, -2.78], %)
|
||||
|> tangentialArcTo([24.95, -0.38], %)
|
||||
|> close(%)
|
||||
|> extrude(5, %)`)
|
||||
|
||||
// Hit undo again.
|
||||
await page.keyboard.down('Control')
|
||||
@ -840,11 +838,12 @@ test.describe('Editor tests', () => {
|
||||
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([7.12, -16.82], %)
|
||||
|> line([12.73, -0.09], %)
|
||||
|> tangentialArcTo([24.95, -5.38], %)
|
||||
|> close(%)
|
||||
|> extrude(5, %)`)
|
||||
|> startProfileAt([7.12, -12.68], %)
|
||||
|> line([12.73, -0.09], %)
|
||||
|> tangentialArcTo([24.95, -0.38], %)
|
||||
|> close(%)
|
||||
|> extrude(5, %)
|
||||
`)
|
||||
|
||||
// Hit undo again.
|
||||
await page.keyboard.down('Control')
|
||||
@ -854,9 +853,9 @@ test.describe('Editor tests', () => {
|
||||
await page.waitForTimeout(100)
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([4.61, -14.01], %)
|
||||
|> startProfileAt([4.61, -10.01], %)
|
||||
|> line([12.73, -0.09], %)
|
||||
|> tangentialArcTo([24.95, -5.38], %)
|
||||
|> tangentialArcTo([24.95, -0.38], %)
|
||||
|> close(%)
|
||||
|> extrude(5, %)`)
|
||||
})
|
||||
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 71 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 71 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 71 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 71 KiB |
102
e2e/playwright/machines.spec.ts
Normal file
@ -0,0 +1,102 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { setupElectron, tearDown } from './test-utils'
|
||||
import fsp from 'fs/promises'
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
|
||||
test(
|
||||
'When machine-api server not found butt is disabled and shows the reason',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
test.skip(
|
||||
process.platform === 'win32',
|
||||
'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557'
|
||||
)
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
await fsp.mkdir(`${dir}/bracket`, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
'src/wasm-lib/tests/executor/inputs/focusrite_scarlett_mounting_braket.kcl',
|
||||
`${dir}/bracket/main.kcl`
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
|
||||
await expect(page.getByText('bracket')).toBeVisible()
|
||||
|
||||
await page.getByText('bracket').click()
|
||||
|
||||
await expect(page.getByTestId('loading')).toBeAttached()
|
||||
await expect(page.getByTestId('loading')).not.toBeAttached({
|
||||
timeout: 20_000,
|
||||
})
|
||||
|
||||
const notFoundText = 'Machine API server was not discovered'
|
||||
await expect(page.getByText(notFoundText).first()).not.toBeVisible()
|
||||
|
||||
// Find the make button
|
||||
const makeButton = page.getByRole('button', { name: 'Make' })
|
||||
// Make sure the button is visible but disabled
|
||||
await expect(makeButton).toBeVisible()
|
||||
await expect(makeButton).toBeDisabled()
|
||||
|
||||
// When you hover over the button, the tooltip should show
|
||||
// that the machine-api server is not found
|
||||
await makeButton.hover()
|
||||
await expect(page.getByText(notFoundText).first()).toBeVisible()
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'When machine-api server not found home screen & project status shows the reason',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
test.skip(
|
||||
process.platform === 'win32',
|
||||
'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557'
|
||||
)
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
await fsp.mkdir(`${dir}/bracket`, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
'src/wasm-lib/tests/executor/inputs/focusrite_scarlett_mounting_braket.kcl',
|
||||
`${dir}/bracket/main.kcl`
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
|
||||
const notFoundText = 'Machine API server was not discovered'
|
||||
|
||||
await expect(page.getByText(notFoundText)).not.toBeVisible()
|
||||
|
||||
const networkMachineToggle = page.getByTestId('network-machine-toggle')
|
||||
await networkMachineToggle.hover()
|
||||
await expect(page.getByText(notFoundText)).toBeVisible()
|
||||
|
||||
await expect(page.getByText('bracket')).toBeVisible()
|
||||
|
||||
await page.getByText('bracket').click()
|
||||
|
||||
await expect(page.getByTestId('loading')).toBeAttached()
|
||||
await expect(page.getByTestId('loading')).not.toBeAttached({
|
||||
timeout: 20_000,
|
||||
})
|
||||
|
||||
await expect(page.getByText(notFoundText).nth(1)).not.toBeVisible()
|
||||
|
||||
await networkMachineToggle.hover()
|
||||
await expect(page.getByText(notFoundText).nth(1)).toBeVisible()
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
@ -347,6 +347,10 @@ test(
|
||||
'Restarting onboarding on desktop takes one attempt',
|
||||
{ tag: '@electron' },
|
||||
async ({ browser: _ }, testInfo) => {
|
||||
test.skip(
|
||||
process.platform === 'win32',
|
||||
'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557'
|
||||
)
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
|
@ -2,6 +2,7 @@ import { test, expect } from '@playwright/test'
|
||||
import {
|
||||
doExport,
|
||||
getUtils,
|
||||
isOutOfViewInScrollContainer,
|
||||
Paths,
|
||||
setupElectron,
|
||||
tearDown,
|
||||
@ -14,10 +15,128 @@ test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
})
|
||||
|
||||
test(
|
||||
'click help/keybindings from home page',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async () => {},
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
|
||||
page.on('console', console.log)
|
||||
|
||||
// click ? button
|
||||
await page.getByTestId('help-button').click()
|
||||
await expect(page.getByTestId('keybindings-button')).toBeVisible()
|
||||
// Click keyboard shortcuts button.
|
||||
await page.getByTestId('keybindings-button').click()
|
||||
// Make sure the keyboard shortcuts modal is visible.
|
||||
await expect(page.getByText('Enter Sketch Mode')).toBeVisible()
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'click help/keybindings from project page',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
test.skip(
|
||||
process.platform === 'win32',
|
||||
'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557'
|
||||
)
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
await fsp.mkdir(`${dir}/bracket`, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
'src/wasm-lib/tests/executor/inputs/focusrite_scarlett_mounting_braket.kcl',
|
||||
`${dir}/bracket/main.kcl`
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
|
||||
page.on('console', console.log)
|
||||
|
||||
page.on('console', console.log)
|
||||
|
||||
// expect to see the text bracket
|
||||
await expect(page.getByText('bracket')).toBeVisible()
|
||||
|
||||
await page.getByText('bracket').click()
|
||||
|
||||
await expect(page.getByTestId('loading')).toBeAttached()
|
||||
await expect(page.getByTestId('loading')).not.toBeAttached({
|
||||
timeout: 20_000,
|
||||
})
|
||||
|
||||
// click ? button
|
||||
await page.getByTestId('help-button').click()
|
||||
await expect(page.getByTestId('keybindings-button')).toBeVisible()
|
||||
// Click keyboard shortcuts button.
|
||||
await page.getByTestId('keybindings-button').click()
|
||||
// Make sure the keyboard shortcuts modal is visible.
|
||||
await expect(page.getByText('Enter Sketch Mode')).toBeVisible()
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'when code with error first loads you get errors in console',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
test.skip(
|
||||
process.platform === 'win32',
|
||||
'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557'
|
||||
)
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
await fsp.mkdir(`${dir}/broken-code`, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
'src/wasm-lib/tests/executor/inputs/broken-code-test.kcl',
|
||||
`${dir}/broken-code/main.kcl`
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
|
||||
await expect(page.getByText('broken-code')).toBeVisible()
|
||||
|
||||
await page.getByText('broken-code').click()
|
||||
|
||||
await expect(page.getByTestId('loading')).toBeAttached()
|
||||
await expect(page.getByTestId('loading')).not.toBeAttached({
|
||||
timeout: 20_000,
|
||||
})
|
||||
|
||||
// error in guter
|
||||
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
|
||||
|
||||
// error text on hover
|
||||
await page.hover('.cm-lint-marker-error')
|
||||
const crypticErrorText = `Expected a tag declarator`
|
||||
await expect(page.getByText(crypticErrorText).first()).toBeVisible()
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'Can export from electron app',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
test.skip(
|
||||
process.platform === 'win32',
|
||||
'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557'
|
||||
)
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
@ -106,6 +225,10 @@ test(
|
||||
'Rename and delete projects, also spam arrow keys when renaming',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
test.skip(
|
||||
process.platform === 'win32',
|
||||
'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557'
|
||||
)
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
@ -394,6 +517,10 @@ test(
|
||||
'Deleting projects, can delete individual project, can still create projects after deleting all',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
test.skip(
|
||||
process.platform === 'win32',
|
||||
'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557'
|
||||
)
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
})
|
||||
@ -488,6 +615,10 @@ test(
|
||||
'Can sort projects on home page',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
test.skip(
|
||||
process.platform === 'win32',
|
||||
'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557'
|
||||
)
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
})
|
||||
@ -610,6 +741,10 @@ test(
|
||||
'When the project folder is empty, user can create new project and open it.',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
test.skip(
|
||||
process.platform === 'win32',
|
||||
'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557'
|
||||
)
|
||||
const { electronApp, page } = await setupElectron({ testInfo })
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
@ -694,6 +829,10 @@ test(
|
||||
'Opening a project should successfully load the stream, (regression test that this also works when switching between projects)',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
test.skip(
|
||||
process.platform === 'win32',
|
||||
'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557'
|
||||
)
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
@ -907,6 +1046,10 @@ test(
|
||||
'Search projects on desktop home',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName: _ }, testInfo) => {
|
||||
test.skip(
|
||||
process.platform === 'win32',
|
||||
'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557'
|
||||
)
|
||||
const projectData = [
|
||||
['basic bracket', 'focusrite_scarlett_mounting_braket.kcl'],
|
||||
['basic-cube', 'basic_fillet_cube_end.kcl'],
|
||||
@ -964,10 +1107,198 @@ test(
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'file pane is scrollable when there are many files',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
test.skip(
|
||||
process.platform === 'win32',
|
||||
'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557'
|
||||
)
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
await fsp.mkdir(`${dir}/testProject`, { recursive: true })
|
||||
const fileNames = [
|
||||
'angled_line.kcl',
|
||||
'basic_fillet_cube_close_opposite.kcl',
|
||||
'basic_fillet_cube_end.kcl',
|
||||
'basic_fillet_cube_next_adjacent.kcl',
|
||||
'basic_fillet_cube_previous_adjacent.kcl',
|
||||
'basic_fillet_cube_start.kcl',
|
||||
'big_number_angle_to_match_length_x.kcl',
|
||||
'big_number_angle_to_match_length_y.kcl',
|
||||
'close_arc.kcl',
|
||||
'computed_var.kcl',
|
||||
'cube-embedded.gltf',
|
||||
'cube.bin',
|
||||
'cube.glb',
|
||||
'cube.gltf',
|
||||
'cube.kcl',
|
||||
'cube.mtl',
|
||||
'cube.obj',
|
||||
'cylinder.kcl',
|
||||
'dimensions_match.kcl',
|
||||
'extrude-custom-plane.kcl',
|
||||
'extrude-inside-fn-with-tags.kcl',
|
||||
'fillet-and-shell.kcl',
|
||||
'fillet_duplicate_tags.kcl',
|
||||
'focusrite_scarlett_mounting_braket.kcl',
|
||||
'function_sketch.kcl',
|
||||
'function_sketch_with_position.kcl',
|
||||
'global-tags.kcl',
|
||||
'helix_ccw.kcl',
|
||||
'helix_defaults.kcl',
|
||||
'helix_defaults_negative_extrude.kcl',
|
||||
'helix_with_length.kcl',
|
||||
'i_shape.kcl',
|
||||
'kittycad_svg.kcl',
|
||||
'lego.kcl',
|
||||
'math.kcl',
|
||||
'member_expression_sketch_group.kcl',
|
||||
'mike_stress_test.kcl',
|
||||
'negative_args.kcl',
|
||||
'order-sketch-extrude-in-order.kcl',
|
||||
'order-sketch-extrude-out-of-order.kcl',
|
||||
'parametric.kcl',
|
||||
'parametric_with_tan_arc.kcl',
|
||||
'pattern_vase.kcl',
|
||||
'pentagon_fillet_sugar.kcl',
|
||||
'pipe_as_arg.kcl',
|
||||
'pipes_on_pipes.kcl',
|
||||
'riddle.kcl',
|
||||
'riddle_small.kcl',
|
||||
'router-template-slate.kcl',
|
||||
'scoped-tags.kcl',
|
||||
'server-rack-heavy.kcl',
|
||||
'server-rack-lite.kcl',
|
||||
'sketch_on_face.kcl',
|
||||
'sketch_on_face_circle_tagged.kcl',
|
||||
'sketch_on_face_end.kcl',
|
||||
'sketch_on_face_end_negative_extrude.kcl',
|
||||
'sketch_on_face_start.kcl',
|
||||
'tan_arc_x_line.kcl',
|
||||
'tangential_arc.kcl',
|
||||
]
|
||||
for (const fileName of fileNames) {
|
||||
await fsp.copyFile(
|
||||
`src/wasm-lib/tests/executor/inputs/${fileName}`,
|
||||
`${dir}/testProject/${fileName}`
|
||||
)
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
|
||||
page.on('console', console.log)
|
||||
|
||||
await test.step('setup, open file pane', async () => {
|
||||
await page.getByText('testProject').click()
|
||||
await expect(page.getByTestId('loading')).toBeAttached()
|
||||
await expect(page.getByTestId('loading')).not.toBeAttached({
|
||||
timeout: 20_000,
|
||||
})
|
||||
|
||||
await page.getByTestId('files-pane-button').click()
|
||||
})
|
||||
|
||||
await test.step('check the last file is out of view initially, and can be scrolled to', async () => {
|
||||
const element = page.getByText('tangential_arc.kcl')
|
||||
const container = page.getByTestId('file-pane-scroll-container')
|
||||
|
||||
await expect(await isOutOfViewInScrollContainer(element, container)).toBe(
|
||||
true
|
||||
)
|
||||
await element.scrollIntoViewIfNeeded()
|
||||
await expect(await isOutOfViewInScrollContainer(element, container)).toBe(
|
||||
false
|
||||
)
|
||||
})
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'select all in code editor does not actually select all, just what is visible (regression)',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
test.skip(
|
||||
process.platform === 'win32',
|
||||
'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557'
|
||||
)
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
// src/wasm-lib/tests/executor/inputs/mike_stress_test.kcl
|
||||
const name = 'mike_stress_test'
|
||||
await fsp.mkdir(`${dir}/${name}`, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
`src/wasm-lib/tests/executor/inputs/${name}.kcl`,
|
||||
`${dir}/${name}/main.kcl`
|
||||
)
|
||||
},
|
||||
})
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
|
||||
page.on('console', console.log)
|
||||
|
||||
await page.getByText('mike_stress_test').click()
|
||||
|
||||
const modifier =
|
||||
process.platform === 'win32' || process.platform === 'linux'
|
||||
? 'Control'
|
||||
: 'Meta'
|
||||
|
||||
await test.step('select all in code editor, check its length', async () => {
|
||||
await u.codeLocator.click()
|
||||
// expect u.codeLocator to have some text
|
||||
await expect(u.codeLocator).toContainText('line(')
|
||||
await page.keyboard.down(modifier)
|
||||
await page.keyboard.press('KeyA')
|
||||
await page.keyboard.up(modifier)
|
||||
|
||||
// check the length of the selected text
|
||||
const selectedText = await page.evaluate(() => {
|
||||
const selection = window.getSelection()
|
||||
return selection ? selection.toString() : ''
|
||||
})
|
||||
// even though if the user copied the text into their clipboard they would get the full text
|
||||
// it seems that the selection is limited to what is visible
|
||||
// we just want to check we did select something, and later we've verify it's empty
|
||||
expect(selectedText.length).toBeGreaterThan(10)
|
||||
})
|
||||
|
||||
await test.step('delete all the text, select again and verify there are no characters left', async () => {
|
||||
await page.keyboard.press('Backspace')
|
||||
|
||||
await page.keyboard.down(modifier)
|
||||
await page.keyboard.press('KeyA')
|
||||
await page.keyboard.up(modifier)
|
||||
|
||||
// check the length of the selected text
|
||||
const selectedText = await page.evaluate(() => {
|
||||
const selection = window.getSelection()
|
||||
return selection ? selection.toString() : ''
|
||||
})
|
||||
expect(selectedText.length).toBe(0)
|
||||
await expect(u.codeLocator).toHaveText('')
|
||||
})
|
||||
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'Settings persist across restarts',
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName }, testInfo) => {
|
||||
test.skip(
|
||||
process.platform === 'win32',
|
||||
'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557'
|
||||
)
|
||||
await test.step('We can change a user setting like theme', async () => {
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
|
@ -236,9 +236,13 @@ const sketch001 = startSketchAt([-0, -0])
|
||||
page,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async (code) => {
|
||||
localStorage.setItem('persistCode', code)
|
||||
}, TEST_CODE_TRIGGER_ENGINE_EXPORT_ERROR)
|
||||
await page.addInitScript(
|
||||
async ({ code }) => {
|
||||
localStorage.setItem('persistCode', code)
|
||||
;(window as any).playwrightSkipFilePicker = true
|
||||
},
|
||||
{ code: TEST_CODE_TRIGGER_ENGINE_EXPORT_ERROR }
|
||||
)
|
||||
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
|
||||
@ -325,7 +329,7 @@ const sketch001 = startSketchAt([-0, -0])
|
||||
await expect(exportingToastMessage).toBeVisible()
|
||||
|
||||
// Expect it to succeed.
|
||||
await expect(exportingToastMessage).not.toBeVisible()
|
||||
await expect(exportingToastMessage).not.toBeVisible({ timeout: 15_000 })
|
||||
await expect(errorToastMessage).not.toBeVisible()
|
||||
await expect(engineErrorToastMessage).not.toBeVisible()
|
||||
|
||||
@ -337,6 +341,7 @@ const sketch001 = startSketchAt([-0, -0])
|
||||
}) => {
|
||||
// This is being weird on ubuntu and windows.
|
||||
test.skip(
|
||||
// eslint-disable-next-line jest/valid-title
|
||||
process.platform === 'linux' || process.platform === 'win32',
|
||||
'This test is being weird on ubuntu'
|
||||
)
|
||||
@ -420,6 +425,10 @@ const sketch001 = startSketchAt([-0, -0])
|
||||
`Network health indicator only appears in modeling view`,
|
||||
{ tag: '@electron' },
|
||||
async ({ browserName: _ }, testInfo) => {
|
||||
test.skip(
|
||||
process.platform === 'win32',
|
||||
'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557'
|
||||
)
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
@ -472,7 +481,7 @@ async function clickExportButton(page: Page) {
|
||||
// Click the export button
|
||||
await exportButton.click()
|
||||
|
||||
// Click the stl.
|
||||
// Click the gltf.
|
||||
const gltfOption = page.getByRole('option', { name: 'glTF' })
|
||||
await expect(gltfOption).toBeVisible()
|
||||
|
||||
|
@ -344,111 +344,108 @@ test.describe('Sketch tests', () => {
|
||||
})
|
||||
})
|
||||
|
||||
// failing for the same reason as "Can undo a sketch modification with ctrl+z"
|
||||
// please fix together
|
||||
test.fixme(
|
||||
'Can edit a sketch that has been extruded in the same pipe',
|
||||
async ({ page }) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`const sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([4.61, -14.01], %)
|
||||
test('Can edit a sketch that has been extruded in the same pipe', async ({
|
||||
page,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`const sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([4.61, -10.01], %)
|
||||
|> line([12.73, -0.09], %)
|
||||
|> tangentialArcTo([24.95, -5.38], %)
|
||||
|> tangentialArcTo([24.95, -0.38], %)
|
||||
|> close(%)
|
||||
|> extrude(5, %)`
|
||||
)
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Start Sketch' })
|
||||
).not.toBeDisabled()
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Start Sketch' })
|
||||
).not.toBeDisabled()
|
||||
|
||||
await page.waitForTimeout(100)
|
||||
await u.openAndClearDebugPanel()
|
||||
await u.sendCustomCmd({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'default_camera_look_at',
|
||||
vantage: { x: 0, y: -1250, z: 580 },
|
||||
center: { x: 0, y: 0, z: 0 },
|
||||
up: { x: 0, y: 0, z: 1 },
|
||||
},
|
||||
})
|
||||
await page.waitForTimeout(100)
|
||||
await u.sendCustomCmd({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'default_camera_get_settings',
|
||||
},
|
||||
})
|
||||
await page.waitForTimeout(100)
|
||||
await page.waitForTimeout(100)
|
||||
await u.openAndClearDebugPanel()
|
||||
await u.sendCustomCmd({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'default_camera_look_at',
|
||||
vantage: { x: 0, y: -1250, z: 580 },
|
||||
center: { x: 0, y: 0, z: 0 },
|
||||
up: { x: 0, y: 0, z: 1 },
|
||||
},
|
||||
})
|
||||
await page.waitForTimeout(100)
|
||||
await u.sendCustomCmd({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'default_camera_get_settings',
|
||||
},
|
||||
})
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
const startPX = [665, 458]
|
||||
const startPX = [665, 397]
|
||||
|
||||
const dragPX = 40
|
||||
const dragPX = 40
|
||||
|
||||
await page.getByText('startProfileAt([4.61, -14.01], %)').click()
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Edit Sketch' })
|
||||
).toBeVisible()
|
||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||
await page.waitForTimeout(400)
|
||||
let prevContent = await page.locator('.cm-content').innerText()
|
||||
await page.getByText('startProfileAt([4.61, -10.01], %)').click()
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Edit Sketch' })
|
||||
).toBeVisible()
|
||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||
await page.waitForTimeout(400)
|
||||
let prevContent = await page.locator('.cm-content').innerText()
|
||||
|
||||
await expect(page.getByTestId('segment-overlay')).toHaveCount(2)
|
||||
await expect(page.getByTestId('segment-overlay')).toHaveCount(2)
|
||||
|
||||
// drag startProfieAt handle
|
||||
await page.dragAndDrop('#stream', '#stream', {
|
||||
sourcePosition: { x: startPX[0], y: startPX[1] },
|
||||
targetPosition: { x: startPX[0] + dragPX, y: startPX[1] + dragPX },
|
||||
})
|
||||
await page.waitForTimeout(100)
|
||||
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
||||
prevContent = await page.locator('.cm-content').innerText()
|
||||
// drag startProfieAt handle
|
||||
await page.dragAndDrop('#stream', '#stream', {
|
||||
sourcePosition: { x: startPX[0], y: startPX[1] },
|
||||
targetPosition: { x: startPX[0] + dragPX, y: startPX[1] + dragPX },
|
||||
})
|
||||
await page.waitForTimeout(100)
|
||||
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
||||
prevContent = await page.locator('.cm-content').innerText()
|
||||
|
||||
// drag line handle
|
||||
await page.waitForTimeout(100)
|
||||
// drag line handle
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
const lineEnd = await u.getBoundingBox('[data-overlay-index="0"]')
|
||||
await page.waitForTimeout(100)
|
||||
await page.dragAndDrop('#stream', '#stream', {
|
||||
sourcePosition: { x: lineEnd.x - 5, y: lineEnd.y },
|
||||
targetPosition: { x: lineEnd.x + dragPX, y: lineEnd.y + dragPX },
|
||||
})
|
||||
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
||||
prevContent = await page.locator('.cm-content').innerText()
|
||||
const lineEnd = await u.getBoundingBox('[data-overlay-index="0"]')
|
||||
await page.waitForTimeout(100)
|
||||
await page.dragAndDrop('#stream', '#stream', {
|
||||
sourcePosition: { x: lineEnd.x - 5, y: lineEnd.y },
|
||||
targetPosition: { x: lineEnd.x + dragPX, y: lineEnd.y + dragPX },
|
||||
})
|
||||
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
||||
prevContent = await page.locator('.cm-content').innerText()
|
||||
|
||||
// drag tangentialArcTo handle
|
||||
const tangentEnd = await u.getBoundingBox('[data-overlay-index="1"]')
|
||||
await page.dragAndDrop('#stream', '#stream', {
|
||||
sourcePosition: { x: tangentEnd.x, y: tangentEnd.y - 5 },
|
||||
targetPosition: {
|
||||
x: tangentEnd.x + dragPX,
|
||||
y: tangentEnd.y + dragPX,
|
||||
},
|
||||
})
|
||||
await page.waitForTimeout(100)
|
||||
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
||||
// drag tangentialArcTo handle
|
||||
const tangentEnd = await u.getBoundingBox('[data-overlay-index="1"]')
|
||||
await page.dragAndDrop('#stream', '#stream', {
|
||||
sourcePosition: { x: tangentEnd.x + 10, y: tangentEnd.y - 5 },
|
||||
targetPosition: {
|
||||
x: tangentEnd.x + dragPX,
|
||||
y: tangentEnd.y + dragPX,
|
||||
},
|
||||
})
|
||||
await page.waitForTimeout(100)
|
||||
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
||||
|
||||
// expect the code to have changed
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([7.12, -16.82], %)
|
||||
|> line([15.4, -2.74], %)
|
||||
|> tangentialArcTo([24.95, -5.38], %)
|
||||
|> line([2.65, -2.69], %)
|
||||
|> close(%)
|
||||
|> extrude(5, %)`)
|
||||
}
|
||||
)
|
||||
// expect the code to have changed
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`const sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([7.12, -12.68], %)
|
||||
|> line([15.39, -2.78], %)
|
||||
|> tangentialArcTo([27.6, -3.05], %)
|
||||
|> close(%)
|
||||
|> extrude(5, %)
|
||||
`)
|
||||
})
|
||||
|
||||
test('Can edit a sketch that has been revolved in the same pipe', async ({
|
||||
page,
|
||||
@ -602,7 +599,7 @@ test.describe('Sketch tests', () => {
|
||||
await expect(u.codeLocator).toHaveText(codeStr)
|
||||
|
||||
// exit the sketch, reset relative clicker
|
||||
click00r(undefined, undefined)
|
||||
await click00r(undefined, undefined)
|
||||
await u.openAndClearDebugPanel()
|
||||
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
|
@ -53,6 +53,7 @@ test(
|
||||
async ({ page, context }) => {
|
||||
// skip on macos and windows.
|
||||
test.skip(
|
||||
// eslint-disable-next-line jest/valid-title
|
||||
process.platform === 'darwin' || process.platform === 'win32',
|
||||
'Skip on macos and windows'
|
||||
)
|
||||
|
@ -5,6 +5,7 @@ import {
|
||||
TestInfo,
|
||||
BrowserContext,
|
||||
_electron as electron,
|
||||
Locator,
|
||||
} from '@playwright/test'
|
||||
import { EngineCommand } from 'lang/std/artifactGraph'
|
||||
import os from 'os'
|
||||
@ -94,6 +95,8 @@ async function expectCmdLog(page: Page, locatorStr: string, timeout = 5000) {
|
||||
await expect(page.locator(locatorStr).last()).toBeVisible({ timeout })
|
||||
}
|
||||
|
||||
// Ignoring the lint since I assume someone will want to use this for a test.
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
async function waitForDefaultPlanesToBeVisible(page: Page) {
|
||||
await page.waitForFunction(
|
||||
() =>
|
||||
@ -145,6 +148,27 @@ async function closeDebugPanel(page: Page) {
|
||||
}
|
||||
}
|
||||
|
||||
async function openFilePanel(page: Page) {
|
||||
const fileLocator = page.getByTestId('files-pane-button')
|
||||
await expect(fileLocator).toBeVisible()
|
||||
const isOpen = (await fileLocator?.getAttribute('aria-pressed')) === 'true'
|
||||
|
||||
if (!isOpen) {
|
||||
await fileLocator.click()
|
||||
await expect(fileLocator).toHaveAttribute('aria-pressed', 'true')
|
||||
}
|
||||
}
|
||||
|
||||
async function closeFilePanel(page: Page) {
|
||||
const fileLocator = page.getByTestId('files-pane-button')
|
||||
await expect(fileLocator).toBeVisible()
|
||||
const isOpen = (await fileLocator?.getAttribute('aria-pressed')) === 'true'
|
||||
if (isOpen) {
|
||||
await fileLocator.click()
|
||||
await expect(fileLocator).not.toHaveAttribute('aria-pressed', 'true')
|
||||
}
|
||||
}
|
||||
|
||||
async function waitForCmdReceive(page: Page, commandType: string) {
|
||||
return page
|
||||
.locator(`[data-receive-command-type="${commandType}"]`)
|
||||
@ -171,7 +195,8 @@ export const wiggleMove = async (
|
||||
const isElVis = await page.locator(locator).isVisible()
|
||||
if (isElVis) return
|
||||
}
|
||||
const [x1, y1] = [0, Math.sin((tau / steps) * j * freq) * amplitude]
|
||||
// x1 is 0.
|
||||
const y1 = Math.sin((tau / steps) * j * freq) * amplitude
|
||||
const [x2, y2] = [
|
||||
Math.cos(-ang * deg) * i - Math.sin(-ang * deg) * y1,
|
||||
Math.sin(-ang * deg) * i + Math.cos(-ang * deg) * y1,
|
||||
@ -317,6 +342,8 @@ export async function getUtils(page: Page) {
|
||||
closeKclCodePanel: () => closeKclCodePanel(page),
|
||||
openDebugPanel: () => openDebugPanel(page),
|
||||
closeDebugPanel: () => closeDebugPanel(page),
|
||||
openFilePanel: () => openFilePanel(page),
|
||||
closeFilePanel: () => closeFilePanel(page),
|
||||
openAndClearDebugPanel: async () => {
|
||||
await openDebugPanel(page)
|
||||
return clearCommandLogs(page)
|
||||
@ -452,7 +479,10 @@ export async function getUtils(page: Page) {
|
||||
return page.evaluate('window.tearDown()')
|
||||
}
|
||||
|
||||
cdpSession?.send('Network.emulateNetworkConditions', networkOptions)
|
||||
return cdpSession?.send(
|
||||
'Network.emulateNetworkConditions',
|
||||
networkOptions
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -745,3 +775,22 @@ export async function setupElectron({
|
||||
|
||||
return { electronApp, page }
|
||||
}
|
||||
|
||||
export async function isOutOfViewInScrollContainer(
|
||||
element: Locator,
|
||||
container: Locator
|
||||
): Promise<boolean> {
|
||||
const elementBox = await element.boundingBox({ timeout: 5_000 })
|
||||
const containerBox = await container.boundingBox({ timeout: 5_000 })
|
||||
|
||||
let isOutOfView = false
|
||||
if (elementBox && containerBox)
|
||||
return (
|
||||
elementBox.y + elementBox.height > containerBox.y + containerBox.height ||
|
||||
elementBox.y < containerBox.y ||
|
||||
elementBox.x + elementBox.width > containerBox.x + containerBox.width ||
|
||||
elementBox.x < containerBox.x
|
||||
)
|
||||
|
||||
return isOutOfView
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ test.describe('Testing constraints', () => {
|
||||
page.getByRole('button', { name: 'Exit Sketch' })
|
||||
).not.toBeVisible()
|
||||
})
|
||||
test(`Test remove constraints`, async ({ page }) => {
|
||||
test(`Remove constraints`, async ({ page }) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
|
@ -977,10 +977,6 @@ const part001 = startSketchOn('XZ')
|
||||
const hoverPos = { x: segmentToDelete.x, y: segmentToDelete.y }
|
||||
await page.mouse.move(0, 0)
|
||||
await page.waitForTimeout(1000)
|
||||
let x = 0,
|
||||
y = 0
|
||||
x = hoverPos.x + Math.cos(ang * deg) * 32
|
||||
y = hoverPos.y - Math.sin(ang * deg) * 32
|
||||
await page.mouse.move(hoverPos.x, hoverPos.y)
|
||||
await wiggleMove(
|
||||
page,
|
||||
|
@ -4,7 +4,6 @@ import { getUtils, setup, setupElectron, tearDown } from './test-utils'
|
||||
import { SaveSettingsPayload } from 'lib/settings/settingsTypes'
|
||||
import { TEST_SETTINGS_KEY, TEST_SETTINGS_CORRUPTED } from './storageStates'
|
||||
import * as TOML from '@iarna/toml'
|
||||
import { APP_NAME } from 'lib/constants'
|
||||
|
||||
test.beforeEach(async ({ context, page }) => {
|
||||
await setup(context, page)
|
||||
@ -193,6 +192,10 @@ test.describe('Testing settings', () => {
|
||||
`Project settings override user settings on desktop`,
|
||||
{ tag: '@electron' },
|
||||
async ({ browser: _ }, testInfo) => {
|
||||
test.skip(
|
||||
process.platform === 'win32',
|
||||
'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557'
|
||||
)
|
||||
const { electronApp, page } = await setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn: async (dir) => {
|
||||
@ -205,7 +208,6 @@ test.describe('Testing settings', () => {
|
||||
})
|
||||
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
const u = await getUtils(page)
|
||||
|
||||
page.on('console', console.log)
|
||||
|
||||
|
@ -1,7 +1,5 @@
|
||||
import { test, expect, Page } from '@playwright/test'
|
||||
import * as fsp from 'fs/promises'
|
||||
import { getUtils, setup, setupElectron, tearDown } from './test-utils'
|
||||
import { join } from 'path'
|
||||
import { getUtils, setup, tearDown } from './test-utils'
|
||||
|
||||
test.beforeEach(async ({ context, page }) => {
|
||||
await setup(context, page)
|
||||
|
22
package.json
@ -19,7 +19,7 @@
|
||||
"@codemirror/search": "^6.5.6",
|
||||
"@codemirror/state": "^6.4.1",
|
||||
"@codemirror/theme-one-dark": "^6.1.2",
|
||||
"@csstools/postcss-oklab-function": "^3.0.16",
|
||||
"@csstools/postcss-oklab-function": "^4.0.2",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.5.2",
|
||||
"@fortawesome/free-brands-svg-icons": "^6.5.2",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.4.2",
|
||||
@ -53,7 +53,7 @@
|
||||
"react-json-view": "^1.21.3",
|
||||
"react-modal": "^3.16.1",
|
||||
"react-modal-promise": "^1.0.2",
|
||||
"react-router-dom": "^6.23.1",
|
||||
"react-router-dom": "^6.26.1",
|
||||
"sketch-helpers": "^0.0.4",
|
||||
"three": "^0.166.1",
|
||||
"ua-parser-js": "^1.0.37",
|
||||
@ -86,17 +86,17 @@
|
||||
"build:wasm-clean": "yarn wasm-prep && yarn build:wasm",
|
||||
"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",
|
||||
"lint": "eslint --fix src",
|
||||
"lint": "eslint --fix src e2e",
|
||||
"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",
|
||||
"postinstall": "yarn xstate:typegen",
|
||||
"xstate:typegen": "yarn xstate typegen \"src/**/*.ts?(x)\"",
|
||||
"make:dev": "make dev",
|
||||
"generate:machine-api": "npx openapi-typescript ./openapi/machine-api.json -o src/lib/machine-api.d.ts",
|
||||
"electron:start": "electron-forge start",
|
||||
"electron:package": "electron-forge package",
|
||||
"electron:make": "electron-forge make",
|
||||
"electron:publish": "electron-forge publish",
|
||||
"electron:e2e:local": "NODE_ENV=development yarn playwright test --config=playwright.electron.config.ts --grep=@electron"
|
||||
"tron:start": "electron-forge start",
|
||||
"tron:package": "electron-forge package",
|
||||
"tron:make": "electron-forge make",
|
||||
"tron:publish": "electron-forge publish",
|
||||
"tron:test": "NODE_ENV=development yarn playwright test --config=playwright.electron.config.ts --grep=@electron"
|
||||
},
|
||||
"prettier": {
|
||||
"trailingComma": "es5",
|
||||
@ -130,14 +130,14 @@
|
||||
"@electron/fuses": "^1.8.0",
|
||||
"@iarna/toml": "^2.2.5",
|
||||
"@lezer/generator": "^1.7.1",
|
||||
"@playwright/test": "^1.45.1",
|
||||
"@playwright/test": "^1.46.1",
|
||||
"@testing-library/jest-dom": "^5.14.1",
|
||||
"@testing-library/react": "^15.0.2",
|
||||
"@types/d3-force": "^3.0.10",
|
||||
"@types/electron": "^1.6.10",
|
||||
"@types/isomorphic-fetch": "^0.0.39",
|
||||
"@types/mocha": "^10.0.6",
|
||||
"@types/node": "^18.19.31",
|
||||
"@types/node": "^22.4.1",
|
||||
"@types/pixelmatch": "^5.2.6",
|
||||
"@types/pngjs": "^6.0.4",
|
||||
"@types/react": "^18.3.2",
|
||||
@ -156,7 +156,7 @@
|
||||
"@xstate/cli": "^0.5.17",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"d3-force": "^3.0.0",
|
||||
"electron": "^31.2.1",
|
||||
"electron": "^31.4.0",
|
||||
"eslint": "^8.0.1",
|
||||
"eslint-config-react-app": "^7.0.1",
|
||||
"eslint-plugin-css-modules": "^2.12.0",
|
||||
|
@ -185,6 +185,8 @@ export function Toolbar({
|
||||
maybeIconConfig[0].disabled
|
||||
}
|
||||
name={maybeIconConfig[0].title}
|
||||
// aria-description is still in ARIA 1.3 draft.
|
||||
// eslint-disable-next-line jsx-a11y/aria-props
|
||||
aria-description={maybeIconConfig[0].description}
|
||||
onClick={() =>
|
||||
maybeIconConfig[0].onClick(configCallbackProps)
|
||||
@ -225,6 +227,8 @@ export function Toolbar({
|
||||
(!itemConfig.showTitle ? ' !px-0' : '')
|
||||
}
|
||||
name={itemConfig.title}
|
||||
// aria-description is still in ARIA 1.3 draft.
|
||||
// eslint-disable-next-line jsx-a11y/aria-props
|
||||
aria-description={itemConfig.description}
|
||||
aria-pressed={itemConfig.isActive}
|
||||
disabled={
|
||||
|
@ -52,24 +52,21 @@ const DownloadAppBanner = () => {
|
||||
</a>{' '}
|
||||
to download the app for the best experience.
|
||||
</p>
|
||||
<p className="mt-6">
|
||||
If you're on Linux and the browser is your only way to use the app,
|
||||
you can permanently dismiss this banner by{' '}
|
||||
<a
|
||||
onClick={() => {
|
||||
setIsBannerDismissed(true)
|
||||
settings.send({
|
||||
type: 'set.app.dismissWebBanner',
|
||||
data: { level: 'user', value: true },
|
||||
})
|
||||
}}
|
||||
href="/"
|
||||
className="!text-warn-80 dark:!text-warn-80 dark:hover:!text-warn-70 underline"
|
||||
>
|
||||
toggling the App > Dismiss Web Banner setting
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
{!navigator?.userAgent.includes('Chrome') && (
|
||||
<p className="mt-6">
|
||||
If you want to stay here on the web-app, we currently only support
|
||||
Chrome. Please use{' '}
|
||||
<a
|
||||
href="https://www.google.com/chrome/"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
className="!text-warn-80 dark:!text-warn-80 dark:hover:!text-warn-70 underline"
|
||||
>
|
||||
this link
|
||||
</a>{' '}
|
||||
to download it.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
</Dialog>
|
||||
|
@ -464,7 +464,10 @@ export const FileTreeInner = ({
|
||||
}, [documentHasFocus])
|
||||
|
||||
return (
|
||||
<div className="overflow-auto pb-12 absolute inset-0">
|
||||
<div
|
||||
className="overflow-auto pb-12 absolute inset-0"
|
||||
data-testid="file-pane-scroll-container"
|
||||
>
|
||||
<ul
|
||||
className="m-0 p-0 text-sm"
|
||||
onClickCapture={(e) => {
|
||||
|
@ -23,7 +23,10 @@ export function HelpMenu(props: React.PropsWithChildren) {
|
||||
|
||||
return (
|
||||
<Popover className="relative">
|
||||
<Popover.Button className="grid p-0 m-0 border-none rounded-full place-content-center">
|
||||
<Popover.Button
|
||||
className="grid p-0 m-0 border-none rounded-full place-content-center"
|
||||
data-testid="help-button"
|
||||
>
|
||||
<CustomIcon
|
||||
name="questionMark"
|
||||
className="rounded-full w-7 h-7 bg-chalkboard-110 dark:bg-chalkboard-80 text-chalkboard-10"
|
||||
@ -95,6 +98,7 @@ export function HelpMenu(props: React.PropsWithChildren) {
|
||||
: PATHS.HOME + PATHS.SETTINGS_KEYBINDINGS
|
||||
navigate(targetPath)
|
||||
}}
|
||||
data-testid="keybindings-button"
|
||||
>
|
||||
Keyboard shortcuts
|
||||
</HelpMenuItem>
|
||||
|
@ -63,8 +63,10 @@ export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
|
||||
data: { name: 'Make', groupId: 'modeling' },
|
||||
})
|
||||
},
|
||||
hide: () => machineManager.machineCount() === 0,
|
||||
hideOnPlatform: 'web',
|
||||
hide: () => !isDesktop(),
|
||||
disable: () => {
|
||||
return machineManager.noMachinesReason()
|
||||
},
|
||||
},
|
||||
]
|
||||
const filteredActions: SidebarAction[] = sidebarActions.filter(
|
||||
@ -186,6 +188,7 @@ export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
|
||||
iconSize: 'md',
|
||||
}}
|
||||
onClick={action.action}
|
||||
disabledText={action.disable?.()}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
@ -238,6 +241,7 @@ interface ModelingPaneButtonProps
|
||||
onClick: () => void
|
||||
paneIsOpen?: boolean
|
||||
showBadge?: BadgeInfoComputed
|
||||
disabledText?: string
|
||||
}
|
||||
|
||||
function ModelingPaneButton({
|
||||
@ -245,6 +249,7 @@ function ModelingPaneButton({
|
||||
onClick,
|
||||
paneIsOpen,
|
||||
showBadge,
|
||||
disabledText,
|
||||
...props
|
||||
}: ModelingPaneButtonProps) {
|
||||
useHotkeys(paneConfig.keybinding, onClick, {
|
||||
@ -258,6 +263,8 @@ function ModelingPaneButton({
|
||||
onClick={onClick}
|
||||
name={paneConfig.title}
|
||||
data-testid={paneConfig.id + '-pane-button'}
|
||||
disabled={disabledText !== undefined}
|
||||
aria-disabled={disabledText !== undefined}
|
||||
{...props}
|
||||
>
|
||||
<ActionIcon
|
||||
@ -284,6 +291,7 @@ function ModelingPaneButton({
|
||||
>
|
||||
<span className="flex-1">
|
||||
{paneConfig.title}
|
||||
{disabledText !== undefined ? ` (${disabledText})` : ''}
|
||||
{paneIsOpen !== undefined ? ` pane` : ''}
|
||||
</span>
|
||||
<kbd className="hotkey text-xs capitalize">
|
||||
@ -326,4 +334,5 @@ export type SidebarAction = {
|
||||
action: () => void
|
||||
hideOnPlatform?: 'desktop' | 'web'
|
||||
hide?: boolean | (() => boolean)
|
||||
disable?: () => string | undefined
|
||||
}
|
||||
|
@ -9,7 +9,9 @@ export const NetworkMachineIndicator = ({
|
||||
}: {
|
||||
className?: string
|
||||
}) => {
|
||||
const machineCount = Object.keys(machineManager.machines).length
|
||||
const machineCount = machineManager.machineCount()
|
||||
const reason = machineManager.noMachinesReason()
|
||||
|
||||
return isDesktop() ? (
|
||||
<Popover className="relative">
|
||||
<Popover.Button
|
||||
@ -26,7 +28,7 @@ export const NetworkMachineIndicator = ({
|
||||
</p>
|
||||
)}
|
||||
<Tooltip position="top-right" wrapperClassName="ui-open:hidden">
|
||||
Network machines ({machineCount})
|
||||
Network machines ({machineCount}) {reason && `: ${reason}`}
|
||||
</Tooltip>
|
||||
</Popover.Button>
|
||||
<Popover.Panel
|
||||
|
Before Width: | Height: | Size: 180 KiB After Width: | Height: | Size: 180 KiB |
@ -154,10 +154,6 @@ export function buildCommandArgument<
|
||||
} satisfies Omit<CommandArgument<O, T>, 'inputType'>
|
||||
|
||||
if (arg.inputType === 'options') {
|
||||
if (!(arg.options || arg.optionsFromContext)) {
|
||||
throw new Error('Options must be provided for options input type')
|
||||
}
|
||||
|
||||
return {
|
||||
inputType: arg.inputType,
|
||||
...baseCommandArgument,
|
||||
|
@ -38,7 +38,10 @@ export async function renameProjectDirectory(
|
||||
|
||||
// Make sure the new name does not exist.
|
||||
const newPath = window.electron.path.join(
|
||||
projectPath.split('/').slice(0, -1).join('/'),
|
||||
projectPath
|
||||
.split(window.electron.sep)
|
||||
.slice(0, -1)
|
||||
.join(window.electron.sep),
|
||||
newName
|
||||
)
|
||||
try {
|
||||
@ -179,7 +182,7 @@ const collectAllFilesRecursiveFrom = async (path: string) => {
|
||||
return Promise.reject(new Error(`Path ${path} is not a directory`))
|
||||
}
|
||||
|
||||
const pathParts = path.split('/')
|
||||
const pathParts = path.split(window.electron.sep)
|
||||
let entry: FileEntry = {
|
||||
name: pathParts.slice(-1)[0],
|
||||
path,
|
||||
|
@ -38,6 +38,9 @@ const bracket = startSketchOn('XY')
|
||||
tags: [getPreviousAdjacentEdge(outerEdge)]
|
||||
}, %)`
|
||||
|
||||
/**
|
||||
* @throws Error if the search text is not found in the example code.
|
||||
*/
|
||||
function findLineInExampleCode({
|
||||
searchText,
|
||||
example = bracket,
|
||||
@ -48,6 +51,8 @@ function findLineInExampleCode({
|
||||
const lines = example.split('\n')
|
||||
const lineNumber = lines.findIndex((l) => l.includes(searchText)) + 1
|
||||
if (lineNumber === 0) {
|
||||
// We are exporting a constant, so we don't want to return an Error.
|
||||
// eslint-disable-next-line suggest-no-throw/suggest-no-throw
|
||||
throw new Error(
|
||||
`Could not find the line with search text "${searchText}" in the example code. Was it removed?`
|
||||
)
|
||||
|
@ -51,6 +51,19 @@ export class MachineManager {
|
||||
return this._machineApiIp
|
||||
}
|
||||
|
||||
// Get the reason message for why there are no machines.
|
||||
noMachinesReason(): string | undefined {
|
||||
if (this.machineCount() > 0) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
if (this.machineApiIp === null) {
|
||||
return 'Machine API server was not discovered'
|
||||
}
|
||||
|
||||
return 'Machine API server was discovered, but no machines are available'
|
||||
}
|
||||
|
||||
get currentMachine(): components['schemas']['Machine'] | null {
|
||||
return this._currentMachine
|
||||
}
|
||||
|
19
src/main.ts
@ -131,8 +131,6 @@ ipcMain.handle('kittycad', (event, data) => {
|
||||
)(data.args)
|
||||
})
|
||||
|
||||
const SERVICE_NAME = '_machine-api._tcp.local.'
|
||||
|
||||
ipcMain.handle('find_machine_api', () => {
|
||||
const timeoutAfterMs = 5000
|
||||
return new Promise((resolve, reject) => {
|
||||
@ -144,8 +142,19 @@ ipcMain.handle('find_machine_api', () => {
|
||||
resolve(null)
|
||||
})
|
||||
console.log('Looking for machine API...')
|
||||
bonjourEt.find({ type: SERVICE_NAME }, (service: Service) => {
|
||||
resolve(service.fqdn)
|
||||
})
|
||||
bonjourEt.find(
|
||||
{ protocol: 'tcp', type: 'machine-api' },
|
||||
(service: Service) => {
|
||||
console.log('Found machine API!', JSON.stringify(service))
|
||||
if (!service.addresses || service.addresses?.length === 0) {
|
||||
console.log('No addresses found for machine API!')
|
||||
return resolve(null)
|
||||
}
|
||||
const ip = service.addresses[0]
|
||||
const port = service.port
|
||||
// We want to return the ip address of the machine API.
|
||||
resolve(`${ip}:${port}`)
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
|
76
src/wasm-lib/Cargo.lock
generated
@ -169,7 +169,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.74",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -180,7 +180,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.74",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -191,7 +191,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.74",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -443,7 +443,7 @@ dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.74",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -643,7 +643,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim 0.10.0",
|
||||
"syn 2.0.74",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -654,7 +654,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn 2.0.74",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -709,7 +709,7 @@ checksum = "4078275de501a61ceb9e759d37bdd3d7210e654dbc167ac1a3678ef4435ed57b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.74",
|
||||
"syn 2.0.75",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
@ -738,7 +738,7 @@ dependencies = [
|
||||
"rustfmt-wrapper",
|
||||
"serde",
|
||||
"serde_tokenstream",
|
||||
"syn 2.0.74",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -749,7 +749,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.74",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -776,7 +776,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.74",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -948,7 +948,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.74",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1038,7 +1038,7 @@ dependencies = [
|
||||
"inflections",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.74",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1462,7 +1462,7 @@ dependencies = [
|
||||
"pretty_assertions",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.74",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1841,7 +1841,7 @@ dependencies = [
|
||||
"regex",
|
||||
"regex-syntax 0.8.3",
|
||||
"structmeta",
|
||||
"syn 2.0.74",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1894,7 +1894,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.74",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2058,7 +2058,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"pyo3-macros-backend",
|
||||
"quote",
|
||||
"syn 2.0.74",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2071,7 +2071,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"pyo3-build-config",
|
||||
"quote",
|
||||
"syn 2.0.74",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2533,7 +2533,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde_derive_internals",
|
||||
"syn 2.0.74",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2607,7 +2607,7 @@ checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.74",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2618,7 +2618,7 @@ checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.74",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2642,7 +2642,7 @@ checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.74",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2663,7 +2663,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"syn 2.0.74",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2800,7 +2800,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"structmeta-derive",
|
||||
"syn 2.0.74",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2811,7 +2811,7 @@ checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.74",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2855,9 +2855,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.74"
|
||||
version = "2.0.75"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7"
|
||||
checksum = "f6af063034fc1935ede7be0122941bafa9bacb949334d090b77ca98b5817c7d9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -2878,7 +2878,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.74",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2985,7 +2985,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.74",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3056,9 +3056,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.39.2"
|
||||
version = "1.39.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1"
|
||||
checksum = "9babc99b9923bfa4804bd74722ff02c0381021eafa4db9949217e3be8e84fff5"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
@ -3080,7 +3080,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.74",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3233,7 +3233,7 @@ checksum = "84fd902d4e0b9a4b27f2f440108dc034e1758628a9b702f8ec61ad66355422fa"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.74",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3261,7 +3261,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.74",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3338,7 +3338,7 @@ checksum = "c88cc88fd23b5a04528f3a8436024f20010a16ec18eb23c164b1242f65860130"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.74",
|
||||
"syn 2.0.75",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
@ -3502,7 +3502,7 @@ dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.74",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3563,7 +3563,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.74",
|
||||
"syn 2.0.75",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@ -3598,7 +3598,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.74",
|
||||
"syn 2.0.75",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@ -3924,7 +3924,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.74",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -17,7 +17,7 @@ gloo-utils = "0.2.0"
|
||||
kcl-lib = { path = "kcl" }
|
||||
kittycad.workspace = true
|
||||
serde_json = "1.0.125"
|
||||
tokio = { version = "1.39.2", features = ["sync"] }
|
||||
tokio = { version = "1.39.3", features = ["sync"] }
|
||||
toml = "0.8.19"
|
||||
uuid = { version = "1.10.0", features = ["v4", "js", "serde"] }
|
||||
wasm-bindgen = "0.2.91"
|
||||
@ -30,7 +30,7 @@ image = { version = "0.25.1", default-features = false, features = ["png"] }
|
||||
kittycad = { workspace = true, default-features = true }
|
||||
pretty_assertions = "1.4.0"
|
||||
reqwest = { version = "0.11.26", default-features = false }
|
||||
tokio = { version = "1.39.2", features = ["rt-multi-thread", "macros", "time"] }
|
||||
tokio = { version = "1.39.3", features = ["rt-multi-thread", "macros", "time"] }
|
||||
twenty-twenty = "0.8"
|
||||
uuid = { version = "1.10.0", features = ["v4", "js", "serde"] }
|
||||
|
||||
|
@ -20,7 +20,7 @@ quote = "1"
|
||||
regex = "1.10"
|
||||
serde = { version = "1.0.208", features = ["derive"] }
|
||||
serde_tokenstream = "0.2"
|
||||
syn = { version = "2.0.74", features = ["full"] }
|
||||
syn = { version = "2.0.75", features = ["full"] }
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = "1.0.86"
|
||||
|
@ -15,7 +15,7 @@ databake = "0.1.8"
|
||||
kcl-lib = { path = "../kcl" }
|
||||
proc-macro2 = "1"
|
||||
quote = "1"
|
||||
syn = { version = "2.0.74", features = ["full"] }
|
||||
syn = { version = "2.0.75", features = ["full"] }
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "1.4.0"
|
||||
|
@ -12,4 +12,4 @@ kcl-lib = { version = "0.2", path = "../kcl" }
|
||||
pico-args = "0.5.0"
|
||||
serde = { version = "1.0.208", features = ["derive"] }
|
||||
serde_json = "1.0.125"
|
||||
tokio = { version = "1.39.2", features = ["macros", "rt-multi-thread"] }
|
||||
tokio = { version = "1.39.3", features = ["macros", "rt-multi-thread"] }
|
||||
|
@ -50,7 +50,7 @@ zip = { version = "2.0.0", default-features = false }
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
js-sys = { version = "0.3.69" }
|
||||
tokio = { version = "1.39.2", features = ["sync", "time"] }
|
||||
tokio = { version = "1.39.3", features = ["sync", "time"] }
|
||||
tower-lsp = { version = "0.20.0", default-features = false, features = ["runtime-agnostic"] }
|
||||
wasm-bindgen = "0.2.91"
|
||||
wasm-bindgen-futures = "0.4.42"
|
||||
@ -59,7 +59,7 @@ web-sys = { version = "0.3.69", features = ["console"] }
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
approx = "0.5"
|
||||
bson = { version = "2.11.0", features = ["uuid-1", "chrono"] }
|
||||
tokio = { version = "1.39.2", features = ["full"] }
|
||||
tokio = { version = "1.39.3", features = ["full"] }
|
||||
tokio-tungstenite = { version = "0.23.1", features = ["rustls-tls-native-roots"] }
|
||||
tower-lsp = { version = "0.20.0", features = ["proposed"] }
|
||||
|
||||
|
60
src/wasm-lib/tests/executor/inputs/broken-code-test.kcl
Normal file
@ -0,0 +1,60 @@
|
||||
// Shelf Bracket
|
||||
// This is a shelf bracket made out of 6061-T6 aluminum sheet metal. The required thickness is calculated based on a point load of 300 lbs applied to the end of the shelf. There are two brackets holding up the shelf, so the moment experienced is divided by 2. The shelf is 1 foot long from the wall.
|
||||
|
||||
|
||||
// Define our bracket feet lengths
|
||||
const shelfMountL = 8 // The length of the bracket holding up the shelf is 6 inches
|
||||
const wallMountL = 6 // the length of the bracket
|
||||
|
||||
|
||||
// Define constants required to calculate the thickness needed to support 300 lbs
|
||||
const sigmaAllow = 35000 // psi
|
||||
const width = 6 // inch
|
||||
const p = 300 // Force on shelf - lbs
|
||||
const L = 12 // inches
|
||||
const M = L * p / 2 // Moment experienced at fixed end of bracket
|
||||
const FOS = 2 // Factor of safety of 2 to be conservative
|
||||
|
||||
|
||||
// Calculate the thickness off the bending stress and factor of safety
|
||||
const thickness = sqrt(6 * M * FOS / (width * sigmaAllow))
|
||||
|
||||
// 0.25 inch fillet radius
|
||||
const filletR = 0.25
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Sketch the bracket and extrude with fillets
|
||||
const bracket = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([0, wallMountL], %, 'outerEdge')
|
||||
|> line([-shelfMountL, 0], %)
|
||||
|> line([0, -thickness], %)
|
||||
|> line([shelfMountL - thickness, 0], %, 'innerEdge')
|
||||
|> line([0, -wallMountL + thickness], %)
|
||||
|> close(%)
|
||||
|> extrude(width, %)
|
||||
|> fillet({
|
||||
radius: filletR,
|
||||
tags: [
|
||||
getPreviousAdjacentEdge('innerEdge', %)
|
||||
]
|
||||
}, %)
|
||||
|> fillet({
|
||||
radius: filletR + thickness,
|
||||
tags: [
|
||||
getPreviousAdjacentEdge('outerEdge', %)
|
||||
]
|
||||
}, %)
|
||||
|
169
yarn.lock
@ -1228,56 +1228,56 @@
|
||||
dependencies:
|
||||
"@jridgewell/trace-mapping" "0.3.9"
|
||||
|
||||
"@csstools/color-helpers@^4.2.1":
|
||||
version "4.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@csstools/color-helpers/-/color-helpers-4.2.1.tgz#da573554220ccb59757f12de62bf70c6b15645d4"
|
||||
integrity sha512-CEypeeykO9AN7JWkr1OEOQb0HRzZlPWGwV0Ya6DuVgFdDi6g3ma/cPZ5ZPZM4AWQikDpq/0llnGGlIL+j8afzw==
|
||||
"@csstools/color-helpers@^5.0.1":
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@csstools/color-helpers/-/color-helpers-5.0.1.tgz#829f1c76f5800b79c51c709e2f36821b728e0e10"
|
||||
integrity sha512-MKtmkA0BX87PKaO1NFRTFH+UnkgnmySQOvNxJubsadusqPEC2aJ9MOQiMceZJJ6oitUl/i0L6u0M1IrmAOmgBA==
|
||||
|
||||
"@csstools/css-calc@^1.2.4":
|
||||
version "1.2.4"
|
||||
resolved "https://registry.yarnpkg.com/@csstools/css-calc/-/css-calc-1.2.4.tgz#9d9fb0dca33666cf97659f8f2c343ed0210e0e73"
|
||||
integrity sha512-tfOuvUQeo7Hz+FcuOd3LfXVp+342pnWUJ7D2y8NUpu1Ww6xnTbHLpz018/y6rtbHifJ3iIEf9ttxXd8KG7nL0Q==
|
||||
"@csstools/css-calc@^2.0.1":
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@csstools/css-calc/-/css-calc-2.0.1.tgz#1675297b19f0933c729fdd7f4f5279b855ae724f"
|
||||
integrity sha512-e59V+sNp6e5m+9WnTUydA1DQO70WuKUdseflRpWmXxocF/h5wWGIxUjxfvLtajcmwstH0vm6l0reKMzcyI757Q==
|
||||
|
||||
"@csstools/css-color-parser@^2.0.4":
|
||||
version "2.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@csstools/css-color-parser/-/css-color-parser-2.0.5.tgz#ce1fe52f23f35f37bea2cf61ac865115aa17880a"
|
||||
integrity sha512-lRZSmtl+DSjok3u9hTWpmkxFZnz7stkbZxzKc08aDUsdrWwhSgWo8yq9rq9DaFUtbAyAq2xnH92fj01S+pwIww==
|
||||
"@csstools/css-color-parser@^3.0.2":
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@csstools/css-color-parser/-/css-color-parser-3.0.2.tgz#710abb97142d58bcefc3a5e032a55a246895351c"
|
||||
integrity sha512-mNg7A6HnNjlm0we/pDS9dUafOuBxcanN0TBhEGeIk6zZincuk0+mAbnBqfVs29NlvWHZ8diwTG6g5FeU8246sA==
|
||||
dependencies:
|
||||
"@csstools/color-helpers" "^4.2.1"
|
||||
"@csstools/css-calc" "^1.2.4"
|
||||
"@csstools/color-helpers" "^5.0.1"
|
||||
"@csstools/css-calc" "^2.0.1"
|
||||
|
||||
"@csstools/css-parser-algorithms@^2.7.1":
|
||||
version "2.7.1"
|
||||
resolved "https://registry.yarnpkg.com/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.7.1.tgz#6d93a8f7d8aeb7cd9ed0868f946e46f021b6aa70"
|
||||
integrity sha512-2SJS42gxmACHgikc1WGesXLIT8d/q2l0UFM7TaEeIzdFCE/FPMtTiizcPGGJtlPo2xuQzY09OhrLTzRxqJqwGw==
|
||||
"@csstools/css-parser-algorithms@^3.0.1":
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.1.tgz#f14ade63bae5f6025ac85c7d03fe47a7ca0e58af"
|
||||
integrity sha512-lSquqZCHxDfuTg/Sk2hiS0mcSFCEBuj49JfzPHJogDBT0mGCyY5A1AQzBWngitrp7i1/HAZpIgzF/VjhOEIJIg==
|
||||
|
||||
"@csstools/css-tokenizer@^2.4.1":
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@csstools/css-tokenizer/-/css-tokenizer-2.4.1.tgz#1d8b2e200197cf5f35ceb07ca2dade31f3a00ae8"
|
||||
integrity sha512-eQ9DIktFJBhGjioABJRtUucoWR2mwllurfnM8LuNGAqX3ViZXaUchqk+1s7jjtkFiT9ySdACsFEA3etErkALUg==
|
||||
"@csstools/css-tokenizer@^3.0.1":
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@csstools/css-tokenizer/-/css-tokenizer-3.0.1.tgz#9dd9b10084f3011290f96789598091e5bcb3c29a"
|
||||
integrity sha512-UBqaiu7kU0lfvaP982/o3khfXccVlHPWp0/vwwiIgDF0GmqqqxoiXC/6FCjlS9u92f7CoEz6nXKQnrn1kIAkOw==
|
||||
|
||||
"@csstools/postcss-oklab-function@^3.0.16":
|
||||
version "3.0.19"
|
||||
resolved "https://registry.yarnpkg.com/@csstools/postcss-oklab-function/-/postcss-oklab-function-3.0.19.tgz#3bd0719914780fb53558af11958d0f4e6d2f952e"
|
||||
integrity sha512-e3JxXmxjU3jpU7TzZrsNqSX4OHByRC3XjItV3Ieo/JEQmLg5rdOL4lkv/1vp27gXemzfNt44F42k/pn0FpE21Q==
|
||||
"@csstools/postcss-oklab-function@^4.0.2":
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@csstools/postcss-oklab-function/-/postcss-oklab-function-4.0.2.tgz#3d36974fbb7c3a589d52756e4eb029eaa29e4735"
|
||||
integrity sha512-2iSK/T77PHMeorakBAk/WLxSodfIJ/lmi6nxEkuruXfhGH7fByZim4Fw6ZJf4B73SVieRSH2ep8zvYkA2ZfRtA==
|
||||
dependencies:
|
||||
"@csstools/css-color-parser" "^2.0.4"
|
||||
"@csstools/css-parser-algorithms" "^2.7.1"
|
||||
"@csstools/css-tokenizer" "^2.4.1"
|
||||
"@csstools/postcss-progressive-custom-properties" "^3.3.0"
|
||||
"@csstools/utilities" "^1.0.0"
|
||||
"@csstools/css-color-parser" "^3.0.2"
|
||||
"@csstools/css-parser-algorithms" "^3.0.1"
|
||||
"@csstools/css-tokenizer" "^3.0.1"
|
||||
"@csstools/postcss-progressive-custom-properties" "^4.0.0"
|
||||
"@csstools/utilities" "^2.0.0"
|
||||
|
||||
"@csstools/postcss-progressive-custom-properties@^3.3.0":
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-3.3.0.tgz#20177d3fc61d8f170c4ee1686f3d2ab6eec27bbb"
|
||||
integrity sha512-W2oV01phnILaRGYPmGFlL2MT/OgYjQDrL9sFlbdikMFi6oQkFki9B86XqEWR7HCsTZFVq7dbzr/o71B75TKkGg==
|
||||
"@csstools/postcss-progressive-custom-properties@^4.0.0":
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-4.0.0.tgz#ecdb85bcdb1852d73970a214a376684a91f82bdc"
|
||||
integrity sha512-XQPtROaQjomnvLUSy/bALTR5VCtTVUFwYs1SblvYgLSeTo2a/bMNwUwo2piXw5rTv/FEYiy5yPSXBqg9OKUx7Q==
|
||||
dependencies:
|
||||
postcss-value-parser "^4.2.0"
|
||||
|
||||
"@csstools/utilities@^1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@csstools/utilities/-/utilities-1.0.0.tgz#42f3c213f2fb929324d465684ab9f46a0febd4bb"
|
||||
integrity sha512-tAgvZQe/t2mlvpNosA4+CkMiZ2azISW5WPAcdSalZlEjQvUfghHxfQcrCiK/7/CrfAWVxyM88kGFYO82heIGDg==
|
||||
"@csstools/utilities@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@csstools/utilities/-/utilities-2.0.0.tgz#f7ff0fee38c9ffb5646d47b6906e0bc8868bde60"
|
||||
integrity sha512-5VdOr0Z71u+Yp3ozOx8T11N703wIFGVRgOWbOZMKgglPJsWA54MRIoMNVMa7shUToIhx5J8vX4sOZgD2XiihiQ==
|
||||
|
||||
"@electron-forge/cli@^7.4.0":
|
||||
version "7.4.0"
|
||||
@ -2075,12 +2075,12 @@
|
||||
resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33"
|
||||
integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==
|
||||
|
||||
"@playwright/test@^1.45.1":
|
||||
version "1.45.3"
|
||||
resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.45.3.tgz#22e9c38b3081d6674b28c6e22f784087776c72e5"
|
||||
integrity sha512-UKF4XsBfy+u3MFWEH44hva1Q8Da28G6RFtR2+5saw+jgAFQV5yYnB1fu68Mz7fO+5GJF3wgwAIs0UelU8TxFrA==
|
||||
"@playwright/test@^1.46.1":
|
||||
version "1.46.1"
|
||||
resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.46.1.tgz#a8dfdcd623c4c23bb1b7ea588058aad41055c188"
|
||||
integrity sha512-Fq6SwLujA/DOIvNC2EL/SojJnkKf/rAwJ//APpJJHRyMi1PdKrY3Az+4XNQ51N4RTbItbIByQ0jgd1tayq1aeA==
|
||||
dependencies:
|
||||
playwright "1.45.3"
|
||||
playwright "1.46.1"
|
||||
|
||||
"@react-hook/latest@^1.0.2":
|
||||
version "1.0.3"
|
||||
@ -2100,10 +2100,10 @@
|
||||
"@react-hook/latest" "^1.0.2"
|
||||
"@react-hook/passive-layout-effect" "^1.2.0"
|
||||
|
||||
"@remix-run/router@1.19.0":
|
||||
version "1.19.0"
|
||||
resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.19.0.tgz#745dbffbce67f05386d57ca22c51dfd85c979593"
|
||||
integrity sha512-zDICCLKEwbVYTS6TjYaWtHXxkdoUvD/QXvyVZjGCsWz5vyH7aFeONlPffPdW+Y/t6KT0MgXb2Mfjun9YpWN1dA==
|
||||
"@remix-run/router@1.19.1":
|
||||
version "1.19.1"
|
||||
resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.19.1.tgz#984771bfd1de2715f42394c87fb716c1349e014f"
|
||||
integrity sha512-S45oynt/WH19bHbIXjtli6QmwNYvaz+vtnubvNpNDvUOoA/OWh6j1OikIP3G+v5GHdxyC6EXoChG3HgYGEUfcg==
|
||||
|
||||
"@replit/codemirror-interact@^6.3.1":
|
||||
version "6.3.1"
|
||||
@ -2478,19 +2478,12 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.7.tgz#4c620090f28ca7f905a94b706f74dc5b57b44f2f"
|
||||
integrity sha512-GN8yJ1mNTcFcah/wKEFIJckJx9iJLoMSzWcfRRuxz/Jk+U6KQNnml+etbtxFK8lPjzOw3zp4Ha/kjSst9fsHYw==
|
||||
|
||||
"@types/node@*":
|
||||
version "22.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.0.2.tgz#9fb1a2b31970871e8bf696f0e8a40d2e6d2bd04e"
|
||||
integrity sha512-yPL6DyFwY5PiMVEwymNeqUTKsDczQBJ/5T7W/46RwLU/VH+AA8aT5TZkvBviLKLbbm0hlfftEkGrNzfRk/fofQ==
|
||||
"@types/node@*", "@types/node@^22.4.1":
|
||||
version "22.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.4.1.tgz#9b595d292c65b94c20923159e2ce947731b6fdce"
|
||||
integrity sha512-1tbpb9325+gPnKK0dMm+/LMriX0vKxf6RnB0SZUqfyVkQ4fMgUSySqhxE/y8Jvs4NyF1yHzTfG9KlnkIODxPKg==
|
||||
dependencies:
|
||||
undici-types "~6.11.1"
|
||||
|
||||
"@types/node@^18.19.31":
|
||||
version "18.19.42"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.42.tgz#b54ed4752c85427906aab40917b0f7f3d724bf72"
|
||||
integrity sha512-d2ZFc/3lnK2YCYhos8iaNIYu9Vfhr92nHiyJHRltXWjXUBjEE+A4I58Tdbnw4VhggSW+2j5y5gTrLs4biNnubg==
|
||||
dependencies:
|
||||
undici-types "~5.26.4"
|
||||
undici-types "~6.19.2"
|
||||
|
||||
"@types/node@^20.9.0":
|
||||
version "20.14.13"
|
||||
@ -4203,10 +4196,10 @@ electron-winstaller@^5.3.0:
|
||||
optionalDependencies:
|
||||
"@electron/windows-sign" "^1.1.2"
|
||||
|
||||
electron@*, electron@^31.2.1:
|
||||
version "31.3.1"
|
||||
resolved "https://registry.yarnpkg.com/electron/-/electron-31.3.1.tgz#de5f21f10db1ba0568e0cdd7ae76ec40a4b800c3"
|
||||
integrity sha512-9fiuWlRhBfygtcT+auRd/WdBK/f8LZZcrpx0RjpXhH2DPTP/PfnkC4JB1PW55qCbGbh4wAgkYbf4ExIag8oGCA==
|
||||
electron@*, electron@^31.4.0:
|
||||
version "31.4.0"
|
||||
resolved "https://registry.yarnpkg.com/electron/-/electron-31.4.0.tgz#fd55fd882117b09fd68b2d182c8fda2dbb7ef454"
|
||||
integrity sha512-YTwKoAA+nrJMlI1TTHnIXLYWoQLKnhbkz0qxZcI7Hadcy0UaFMFs9xzwvH2MnrRpVJy7RKo49kVGuvSdRl8zMA==
|
||||
dependencies:
|
||||
"@electron/get" "^2.0.0"
|
||||
"@types/node" "^20.9.0"
|
||||
@ -7226,17 +7219,17 @@ pkg-types@^1.0.3, pkg-types@^1.1.1:
|
||||
mlly "^1.7.1"
|
||||
pathe "^1.1.2"
|
||||
|
||||
playwright-core@1.45.3:
|
||||
version "1.45.3"
|
||||
resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.45.3.tgz#e77bc4c78a621b96c3e629027534ee1d25faac93"
|
||||
integrity sha512-+ym0jNbcjikaOwwSZycFbwkWgfruWvYlJfThKYAlImbxUgdWFO2oW70ojPm4OpE4t6TAo2FY/smM+hpVTtkhDA==
|
||||
playwright-core@1.46.1:
|
||||
version "1.46.1"
|
||||
resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.46.1.tgz#28f3ab35312135dda75b0c92a3e5c0e7edb9cc8b"
|
||||
integrity sha512-h9LqIQaAv+CYvWzsZ+h3RsrqCStkBHlgo6/TJlFst3cOTlLghBQlJwPOZKQJTKNaD3QIB7aAVQ+gfWbN3NXB7A==
|
||||
|
||||
playwright@1.45.3:
|
||||
version "1.45.3"
|
||||
resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.45.3.tgz#75143f73093a6e1467f7097083d2f0846fb8dd2f"
|
||||
integrity sha512-QhVaS+lpluxCaioejDZ95l4Y4jSFCsBvl2UZkpeXlzxmqS+aABr5c82YmfMHrL6x27nvrvykJAFpkzT2eWdJww==
|
||||
playwright@1.46.1:
|
||||
version "1.46.1"
|
||||
resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.46.1.tgz#ea562bc48373648e10420a10c16842f0b227c218"
|
||||
integrity sha512-oPcr1yqoXLCkgKtD5eNUPLiN40rYEM39odNpIb6VE6S7/15gJmA1NzVv6zJYusV0e7tzvkU/utBFNa/Kpxmwng==
|
||||
dependencies:
|
||||
playwright-core "1.45.3"
|
||||
playwright-core "1.46.1"
|
||||
optionalDependencies:
|
||||
fsevents "2.3.2"
|
||||
|
||||
@ -7560,20 +7553,20 @@ react-refresh@^0.14.2:
|
||||
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.2.tgz#3833da01ce32da470f1f936b9d477da5c7028bf9"
|
||||
integrity sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==
|
||||
|
||||
react-router-dom@^6.23.1:
|
||||
version "6.26.0"
|
||||
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.26.0.tgz#8debe13295c58605c04f93018d659a763245e58c"
|
||||
integrity sha512-RRGUIiDtLrkX3uYcFiCIxKFWMcWQGMojpYZfcstc63A1+sSnVgILGIm9gNUA6na3Fm1QuPGSBQH2EMbAZOnMsQ==
|
||||
react-router-dom@^6.26.1:
|
||||
version "6.26.1"
|
||||
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.26.1.tgz#a408892b41767a49dc94b3564b0e7d8e3959f623"
|
||||
integrity sha512-veut7m41S1fLql4pLhxeSW3jlqs+4MtjRLj0xvuCEXsxusJCbs6I8yn9BxzzDX2XDgafrccY6hwjmd/bL54tFw==
|
||||
dependencies:
|
||||
"@remix-run/router" "1.19.0"
|
||||
react-router "6.26.0"
|
||||
"@remix-run/router" "1.19.1"
|
||||
react-router "6.26.1"
|
||||
|
||||
react-router@6.26.0:
|
||||
version "6.26.0"
|
||||
resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.26.0.tgz#d5af4c46835b202348ef2b7ddacd32a2db539fde"
|
||||
integrity sha512-wVQq0/iFYd3iZ9H2l3N3k4PL8EEHcb0XlU2Na8nEwmiXgIUElEH6gaJDtUQxJ+JFzmIXaQjfdpcGWaM6IoQGxg==
|
||||
react-router@6.26.1:
|
||||
version "6.26.1"
|
||||
resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.26.1.tgz#88c64837e05ffab6899a49df2a1484a22471e4ce"
|
||||
integrity sha512-kIwJveZNwp7teQRI5QmwWo39A5bXRyqpH0COKKmPnyD2vBvDwgFXSqDUYtt1h+FEyfnE8eXr7oe0MxRzVwCcvQ==
|
||||
dependencies:
|
||||
"@remix-run/router" "1.19.0"
|
||||
"@remix-run/router" "1.19.1"
|
||||
|
||||
react-textarea-autosize@^8.3.2:
|
||||
version "8.5.3"
|
||||
@ -8765,10 +8758,10 @@ undici-types@~5.26.4:
|
||||
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"
|
||||
integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==
|
||||
|
||||
undici-types@~6.11.1:
|
||||
version "6.11.1"
|
||||
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.11.1.tgz#432ea6e8efd54a48569705a699e62d8f4981b197"
|
||||
integrity sha512-mIDEX2ek50x0OlRgxryxsenE5XaQD4on5U2inY7RApK3SOJpofyw7uW2AyfMKkhAxXIceo2DeWGVGwyvng1GNQ==
|
||||
undici-types@~6.19.2:
|
||||
version "6.19.6"
|
||||
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.6.tgz#e218c3df0987f4c0e0008ca00d6b6472d9b89b36"
|
||||
integrity sha512-e/vggGopEfTKSvj4ihnOLTsqhrKRN3LeO6qSN/GxohhuRv8qH9bNQ4B8W7e/vFL+0XTnmHPB4/kegunZGA4Org==
|
||||
|
||||
unicode-canonical-property-names-ecmascript@^2.0.0:
|
||||
version "2.0.0"
|
||||
|