Merge branch 'main' into coredump-clientstate
This commit is contained in:
19
.github/workflows/ci.yml
vendored
19
.github/workflows/ci.yml
vendored
@ -147,6 +147,14 @@ jobs:
|
||||
cp artifact/src-tauri/tauri.conf.json src-tauri/tauri.conf.json
|
||||
cp artifact/src-tauri/tauri.release.conf.json src-tauri/tauri.release.conf.json
|
||||
|
||||
- name: Update WebView2 on Windows
|
||||
if: matrix.os == 'windows-latest'
|
||||
# Workaround needed to build the tauri windows app with matching edge version.
|
||||
# From https://github.com/actions/runner-images/issues/9538
|
||||
run: |
|
||||
Invoke-WebRequest -Uri 'https://go.microsoft.com/fwlink/p/?LinkId=2124703' -OutFile 'setup.exe'
|
||||
Start-Process -FilePath setup.exe -Verb RunAs -Wait
|
||||
|
||||
- name: Install ubuntu system dependencies
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
@ -364,6 +372,17 @@ jobs:
|
||||
E2E_APPLICATION: "./src-tauri/target/${{ env.BUILD_RELEASE == 'true' && 'release' || 'debug' }}/zoo-modeling-app"
|
||||
KITTYCAD_API_TOKEN: ${{ env.BUILD_RELEASE == 'true' && secrets.KITTYCAD_API_TOKEN || secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
|
||||
- name: Run e2e tests (windows only)
|
||||
if: ${{ matrix.os == 'windows-latest' && github.event_name != 'release' && github.event_name != 'schedule' }}
|
||||
run: |
|
||||
cargo install tauri-driver --force
|
||||
yarn wdio run wdio.conf.ts
|
||||
env:
|
||||
E2E_APPLICATION: ".\\src-tauri\\target\\${{ env.BUILD_RELEASE == 'true' && 'release' || 'debug' }}\\Zoo Modeling App.exe"
|
||||
KITTYCAD_API_TOKEN: ${{ env.BUILD_RELEASE == 'true' && secrets.KITTYCAD_API_TOKEN || secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
VITE_KC_API_BASE_URL: ${{ env.BUILD_RELEASE == 'true' && 'https://api.zoo.dev' || 'https://api.dev.zoo.dev' }}
|
||||
E2E_TAURI_ENABLED: true
|
||||
TS_NODE_COMPILER_OPTIONS: '{"module": "commonjs"}'
|
||||
|
||||
publish-apps-release:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
19
README.md
19
README.md
@ -309,6 +309,25 @@ PS: for the debug panel, the following JSON is useful for snapping the camera
|
||||
|
||||
</details>
|
||||
|
||||
### Tauri e2e tests
|
||||
|
||||
#### Windows (local only until the CI edge version mismatch is fixed)
|
||||
|
||||
```
|
||||
yarn install
|
||||
yarn build:wasm
|
||||
cp src/wasm-lib/pkg/wasm_lib_bg.wasm public
|
||||
yarn vite build --mode development
|
||||
yarn tauri build --debug -b
|
||||
$env:KITTYCAD_API_TOKEN="<YOUR_KITTYCAD_API_TOKEN>"
|
||||
$env:VITE_KC_API_BASE_URL="https://api.dev.zoo.dev"
|
||||
$env:E2E_TAURI_ENABLED="true"
|
||||
$env:TS_NODE_COMPILER_OPTIONS='{"module": "commonjs"}'
|
||||
$env:E2E_APPLICATION=".\src-tauri\target\debug\Zoo Modeling App.exe"
|
||||
Stop-Process -Name msedgedriver
|
||||
yarn wdio run wdio.conf.ts
|
||||
```
|
||||
|
||||
## KCL
|
||||
|
||||
For how to contribute to KCL, [see our KCL README](https://github.com/KittyCAD/modeling-app/tree/main/src/wasm-lib/kcl).
|
||||
|
||||
@ -2440,6 +2440,95 @@ test('Extrude from command bar selects extrude line after', async ({
|
||||
})
|
||||
|
||||
test.describe('Testing constraints', () => {
|
||||
test.describe('Many segments - no modal constraints', () => {
|
||||
const cases = [
|
||||
{
|
||||
constraintName: 'Vertical',
|
||||
codeAfter: [
|
||||
`|> yLine(130.4, %)`,
|
||||
`|> yLine(77.79, %)`,
|
||||
`|> yLine(28.97, %)`,
|
||||
],
|
||||
},
|
||||
{
|
||||
codeAfter: [
|
||||
`|> xLine(74.36, %)`,
|
||||
`|> xLine(9.16, %)`,
|
||||
`|> xLine(41.19, %)`,
|
||||
],
|
||||
constraintName: 'Horizontal',
|
||||
},
|
||||
] as const
|
||||
for (const { codeAfter, constraintName } of cases) {
|
||||
test(`${constraintName}`, async ({ page }) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`const yo = 5
|
||||
const part001 = startSketchOn('XZ')
|
||||
|> startProfileAt([-7.54, -26.74], %)
|
||||
|> line([74.36, 130.4], %)
|
||||
|> line([78.92, -120.11], %)
|
||||
|> line([9.16, 77.79], %)
|
||||
|> line([41.19, 28.97], %)
|
||||
const part002 = startSketchOn('XZ')
|
||||
|> startProfileAt([299.05, 231.45], %)
|
||||
|> xLine(-425.34, %, 'seg-what')
|
||||
|> yLine(-264.06, %)
|
||||
|> xLine(segLen('seg-what', %), %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)`
|
||||
)
|
||||
})
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.goto('/')
|
||||
await u.waitForAuthSkipAppStart()
|
||||
|
||||
await page.getByText('line([74.36, 130.4], %)').click()
|
||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||
|
||||
const line1 = await u.getBoundingBox(`[data-overlay-index="${0}"]`)
|
||||
const line3 = await u.getBoundingBox(`[data-overlay-index="${2}"]`)
|
||||
const line4 = await u.getBoundingBox(`[data-overlay-index="${3}"]`)
|
||||
|
||||
// select two segments by holding down shift
|
||||
await page.mouse.click(line1.x - 20, line1.y + 20)
|
||||
await page.keyboard.down('Shift')
|
||||
await page.mouse.click(line3.x - 3, line3.y + 20)
|
||||
await page.mouse.click(line4.x - 15, line4.y + 15)
|
||||
await page.keyboard.up('Shift')
|
||||
const constraintMenuButton = page.getByRole('button', {
|
||||
name: 'Constrain',
|
||||
})
|
||||
const constraintButton = page
|
||||
.getByRole('button', {
|
||||
name: constraintName,
|
||||
})
|
||||
.first()
|
||||
|
||||
// apply the constraint
|
||||
await constraintMenuButton.click()
|
||||
await constraintButton.click()
|
||||
|
||||
// check actives lines
|
||||
const activeLinesContent = await page.locator('.cm-activeLine').all()
|
||||
await expect(activeLinesContent).toHaveLength(codeAfter.length)
|
||||
// check there are still 3 cursors (they should stay on the same lines as before constraint was applied)
|
||||
await expect(page.locator('.cm-cursor')).toHaveCount(codeAfter.length)
|
||||
|
||||
// check both cursors are where they should be after constraint is applied and the code is correct
|
||||
await Promise.all(
|
||||
activeLinesContent.map(async (line, i) => {
|
||||
await expect(page.locator('.cm-content')).toContainText(
|
||||
codeAfter[i]
|
||||
)
|
||||
// if the code is an active line then the cursor should be on that line
|
||||
await expect(line).toHaveText(codeAfter[i])
|
||||
})
|
||||
)
|
||||
})
|
||||
}
|
||||
})
|
||||
test.describe('Two segment - no modal constraints', () => {
|
||||
const cases = [
|
||||
{
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 41 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
@ -1,11 +1,19 @@
|
||||
import { browser, $, expect } from '@wdio/globals'
|
||||
import fs from 'fs/promises'
|
||||
import path from 'path'
|
||||
import os from 'os'
|
||||
|
||||
const documentsDir = `${process.env.HOME}/Documents`
|
||||
const userSettingsDir = `${process.env.HOME}/.config/dev.zoo.modeling-app`
|
||||
const defaultProjectDir = `${documentsDir}/zoo-modeling-app-projects`
|
||||
const newProjectDir = `${documentsDir}/a-different-directory`
|
||||
const userCodeDir = '/tmp/kittycad_user_code'
|
||||
const isWin32 = os.platform() === 'win32'
|
||||
const documentsDir = path.join(os.homedir(), 'Documents')
|
||||
const userSettingsDir = path.join(
|
||||
os.homedir(),
|
||||
'.config',
|
||||
'dev.zoo.modeling-app'
|
||||
)
|
||||
const defaultProjectDir = path.join(documentsDir, 'zoo-modeling-app-projects')
|
||||
const newProjectDir = path.join(documentsDir, 'a-different-directory')
|
||||
const tmp = process.env.TEMP || '/tmp'
|
||||
const userCodeDir = path.join(tmp, 'kittycad_user_code')
|
||||
|
||||
async function click(element: WebdriverIO.Element): Promise<void> {
|
||||
// Workaround for .click(), see https://github.com/tauri-apps/tauri/issues/6541
|
||||
@ -24,7 +32,7 @@ async function setDatasetValue(
|
||||
await browser.execute(`arguments[0].dataset.${property} = "${value}"`, field)
|
||||
}
|
||||
|
||||
describe('ZMA (Tauri, Linux)', () => {
|
||||
describe('ZMA (Tauri)', () => {
|
||||
it('opens the auth page and signs in', async () => {
|
||||
// Clean up filesystem from previous tests
|
||||
await new Promise((resolve) => setTimeout(resolve, 100))
|
||||
@ -42,9 +50,7 @@ describe('ZMA (Tauri, Linux)', () => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000))
|
||||
|
||||
// Get from main.rs
|
||||
const userCode = await (
|
||||
await fs.readFile('/tmp/kittycad_user_code')
|
||||
).toString()
|
||||
const userCode = await (await fs.readFile(userCodeDir)).toString()
|
||||
console.log(`Found user code ${userCode}`)
|
||||
|
||||
// Device flow: verify
|
||||
@ -92,7 +98,12 @@ describe('ZMA (Tauri, Linux)', () => {
|
||||
* to be able to skip the folder selection dialog if data-testValue
|
||||
* has a value, allowing us to test the input otherwise works.
|
||||
*/
|
||||
await setDatasetValue(projectDirInput, 'testValue', newProjectDir)
|
||||
// TODO: understand why we need to force double \ on Windows
|
||||
await setDatasetValue(
|
||||
projectDirInput,
|
||||
'testValue',
|
||||
isWin32 ? newProjectDir.replaceAll('\\', '\\\\') : newProjectDir
|
||||
)
|
||||
const projectDirButton = await $('[data-testid="project-directory-button"]')
|
||||
await click(projectDirButton)
|
||||
await new Promise((resolve) => setTimeout(resolve, 500))
|
||||
@ -102,6 +113,15 @@ describe('ZMA (Tauri, Linux)', () => {
|
||||
const nameInput = await $('[data-testid="projects-defaultProjectName"]')
|
||||
expect(await nameInput.getValue()).toEqual('project-$nnn')
|
||||
|
||||
// Setting it back (for back to back local tests)
|
||||
await new Promise((resolve) => setTimeout(resolve, 5000))
|
||||
await setDatasetValue(
|
||||
projectDirInput,
|
||||
'testValue',
|
||||
isWin32 ? defaultProjectDir.replaceAll('\\', '\\\\') : newProjectDir
|
||||
)
|
||||
await click(projectDirButton)
|
||||
|
||||
const closeButton = await $('[data-testid="settings-close-button"]')
|
||||
await click(closeButton)
|
||||
})
|
||||
@ -120,9 +140,15 @@ describe('ZMA (Tauri, Linux)', () => {
|
||||
it('opens the new file and expects a loading stream', async () => {
|
||||
const projectLink = await $('[data-testid="project-link"]')
|
||||
await click(projectLink)
|
||||
const errorText = await $('[data-testid="unexpected-error"]')
|
||||
expect(await errorText.getText()).toContain('unexpected error')
|
||||
await browser.execute('window.location.href = "tauri://localhost/home"')
|
||||
if (isWin32) {
|
||||
// TODO: actually do something to check that the stream is up
|
||||
await new Promise((resolve) => setTimeout(resolve, 5000))
|
||||
} else {
|
||||
const errorText = await $('[data-testid="unexpected-error"]')
|
||||
expect(await errorText.getText()).toContain('unexpected error')
|
||||
}
|
||||
const base = isWin32 ? 'http://tauri.localhost' : 'tauri://localhost'
|
||||
await browser.execute(`window.location.href = "${base}/home"`)
|
||||
})
|
||||
|
||||
it('signs out', async () => {
|
||||
|
||||
@ -267,7 +267,15 @@ async fn login(app: tauri::AppHandle, host: &str) -> Result<String, InvokeError>
|
||||
let e2e_tauri_enabled = env::var("E2E_TAURI_ENABLED").is_ok();
|
||||
if e2e_tauri_enabled {
|
||||
log::warn!("E2E_TAURI_ENABLED is set, won't open {} externally", auth_uri.secret());
|
||||
tokio::fs::write("/tmp/kittycad_user_code", details.user_code().secret())
|
||||
let mut temp = String::from("/tmp");
|
||||
// Overwrite with Windows variable
|
||||
match env::var("TEMP") {
|
||||
Ok(val) => temp = val,
|
||||
Err(_e) => println!("Fallback to default /tmp"),
|
||||
}
|
||||
let path = Path::new(&temp).join("kittycad_user_code");
|
||||
println!("Writing to {}", path.to_string_lossy());
|
||||
tokio::fs::write(path, details.user_code().secret())
|
||||
.await
|
||||
.map_err(|e| InvokeError::from_anyhow(e.into()))?;
|
||||
} else {
|
||||
|
||||
@ -153,7 +153,7 @@ export const ModelingMachineProvider = ({
|
||||
])
|
||||
const pathToNode = parent?.userData?.pathToNode
|
||||
const pathToNodeString = JSON.stringify(pathToNode)
|
||||
if (!parent || !pathToNode) return {}
|
||||
if (!parent || !pathToNode) return segmentHoverMap
|
||||
if (segmentHoverMap[pathToNodeString] !== undefined)
|
||||
clearTimeout(segmentHoverMap[JSON.stringify(pathToNode)])
|
||||
return {
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -8,6 +8,7 @@ const application =
|
||||
process.env.E2E_APPLICATION || `./src-tauri/target/release/zoo-modeling-app`
|
||||
|
||||
export const config = {
|
||||
hostname: '127.0.0.1',
|
||||
port: 4444,
|
||||
specs: ['./e2e/tauri/specs/**/*.ts'],
|
||||
maxInstances: 1,
|
||||
@ -16,6 +17,7 @@ export const config = {
|
||||
maxInstances: 1,
|
||||
'tauri:options': {
|
||||
application,
|
||||
webviewOptions: {}, // Windows only
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user