Massive extinction event for waitForExecutionDone; try to stop projects view switching from crashing

This commit is contained in:
lee-at-zoo-corp
2025-03-21 19:33:59 -04:00
parent 4c9851efbf
commit fbefff490c
19 changed files with 113 additions and 106 deletions

View File

@ -15,6 +15,7 @@ test.describe('Code pane and errors', { tag: ['@skipWin'] }, () => {
page,
homePage,
scene,
cmdBar,
}) => {
const u = await getUtils(page)
@ -36,7 +37,7 @@ extrude001 = extrude(sketch001, length = 5)`
await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
await scene.settled(cmdBar)
// Ensure no badge is present
const codePaneButtonHolder = page.locator('#code-button-holder')
@ -171,6 +172,8 @@ extrude001 = extrude(sketch001, length = 5)`
context,
page,
homePage,
scene,
cmdBar,
}) => {
// Load the app with the working starter code
await context.addInitScript((code) => {
@ -180,9 +183,7 @@ extrude001 = extrude(sketch001, length = 5)`
await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene()
// FIXME: await scene.waitForExecutionDone() does not work. It still fails.
// I needed to increase this timeout to get this to pass.
await page.waitForTimeout(10000)
await scene.settled(cmdBar)
// Ensure badge is present
const codePaneButtonHolder = page.locator('#code-button-holder')

View File

@ -11,7 +11,7 @@ import { expect, test } from '@e2e/playwright/zoo-test'
test(
'export works on the first try',
{ tag: ['@electron', '@skipLocalEngine'] },
async ({ page, context, scene, tronApp }, testInfo) => {
async ({ page, context, scene, tronApp, cmdBar }, testInfo) => {
if (!tronApp) {
fail()
}
@ -47,7 +47,8 @@ test(
const exportButton = page.getByTestId('export-pane-button')
await expect(exportButton).toBeVisible()
await scene.waitForExecutionDone()
// Wait for the model to finish loading
await scene.settled(cmdBar)
const gltfOption = page.getByText('glTF')
const submitButton = page.getByText('Confirm Export')
@ -120,8 +121,7 @@ test(
// Close the file pane
await u.closeFilePanel()
// FIXME: await scene.waitForExecutionDone() does not work. The modeling indicator stays in -receive-reliable and not execution done
await page.waitForTimeout(10000)
await scene.settled(cmdBar)
// expect zero errors in guter
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()

View File

@ -64,7 +64,7 @@ test.describe('Feature Tree pane', () => {
test(
'User can go to definition and go to function definition',
{ tag: '@electron' },
async ({ context, homePage, scene, editor, toolbar }) => {
async ({ context, homePage, scene, editor, toolbar, cmdBar }) => {
await context.folderSetupFn(async (dir) => {
const bracketDir = join(dir, 'test-sample')
await fsp.mkdir(bracketDir, { recursive: true })
@ -86,7 +86,7 @@ test.describe('Feature Tree pane', () => {
sortBy: 'last-modified-desc',
})
await homePage.openProject('test-sample')
await scene.waitForExecutionDone()
await scene.settled(cmdBar)
await editor.closePane()
await toolbar.openFeatureTreePane()
})
@ -254,7 +254,7 @@ test.describe('Feature Tree pane', () => {
sortBy: 'last-modified-desc',
})
await homePage.openProject('test-sample')
await scene.waitForExecutionDone()
await scene.settled(cmdBar)
await toolbar.openFeatureTreePane()
})
@ -339,7 +339,7 @@ test.describe('Feature Tree pane', () => {
sortBy: 'last-modified-desc',
})
await homePage.openProject('test-sample')
await scene.waitForExecutionDone()
await scene.settled(cmdBar)
await toolbar.openFeatureTreePane()
})
@ -414,8 +414,7 @@ profile003 = startProfileAt([0, -4.93], sketch001)
const planeColor: [number, number, number] = [74, 74, 74]
await homePage.openProject('test-sample')
// FIXME: @lf94 has a better way to verify execution completion, in a PR rn
await scene.waitForExecutionDone()
await scene.settled(cmdBar)
await test.step(`Verify we see the sketch`, async () => {
await scene.expectPixelColor(sketchColor, testPoint, 10)

View File

@ -47,12 +47,6 @@ export class SceneFixture {
public networkToggleConnected!: Locator
public startEditSketchBtn!: Locator
get exeIndicator() {
return this.page
.getByTestId('model-state-indicator-execution-done')
.or(this.page.getByTestId('model-state-indicator-receive-reliable'))
}
constructor(page: Page) {
this.page = page
this.streamWrapper = page.getByTestId('stream')
@ -231,10 +225,6 @@ export class SceneFixture {
}
}
waitForExecutionDone = async () => {
await expect(this.exeIndicator).toBeVisible({ timeout: 30000 })
}
connectionEstablished = async () => {
const timeout = 30000
await expect(this.networkToggleConnected).toBeVisible({ timeout })
@ -243,6 +233,9 @@ export class SceneFixture {
settled = async (cmdBar: CmdBarFixture) => {
const u = await getUtils(this.page)
await expect(this.startEditSketchBtn).not.toBeDisabled()
await expect(this.startEditSketchBtn).toBeVisible()
await cmdBar.openCmdBar()
await cmdBar.chooseCommand('Settings · app · show debug panel')
await cmdBar.selectOption({ name: 'on' }).click()
@ -250,10 +243,6 @@ export class SceneFixture {
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.closeDebugPanel()
await this.waitForExecutionDone()
await expect(this.startEditSketchBtn).not.toBeDisabled()
await expect(this.startEditSketchBtn).toBeVisible()
}
expectPixelColor = async (

View File

@ -84,12 +84,6 @@ export class ToolbarFixture {
return this.page.getByTestId('app-logo')
}
get exeIndicator() {
return this.page
.getByTestId('model-state-indicator-receive-reliable')
.or(this.page.getByTestId('model-state-indicator-execution-done'))
}
startSketchPlaneSelection = async () =>
doAndWaitForImageDiff(this.page, () => this.startSketchBtn.click(), 500)
@ -173,7 +167,7 @@ export class ToolbarFixture {
) => {
await this.filePane.getByText(fileName).click()
if (wait) {
await expect(this.exeIndicator).toBeVisible({ timeout: 15_000 })
await scene.settled(cmdBar)
}
}
selectCenterRectangle = async () => {

View File

@ -31,7 +31,7 @@ test.describe('Point-and-click tests', () => {
localStorage.setItem('persistCode', file)
}, file)
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
await scene.settled(cmdBar)
const [clickCircle, moveToCircle] = scene.makeMouseHelpers(582, 217)
@ -186,6 +186,7 @@ test.describe('Point-and-click tests', () => {
editor,
toolbar,
scene,
cmdBar
}) => {
const file = await fs.readFile(
path.resolve(
@ -200,9 +201,7 @@ test.describe('Point-and-click tests', () => {
}, file)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await expect(
page.getByTestId('model-state-indicator-receive-reliable')
).toBeVisible()
await scene.settled(cmdBar)
const sketchOnAChamfer = _sketchOnAChamfer(page, editor, toolbar, scene)
@ -377,6 +376,7 @@ profile001 = startProfileAt([205.96, 254.59], sketch002)
editor,
toolbar,
scene,
cmdBar,
}) => {
const file = await fs.readFile(
path.resolve(
@ -392,7 +392,7 @@ profile001 = startProfileAt([205.96, 254.59], sketch002)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
await scene.settled(cmdBar)
const sketchOnAChamfer = _sketchOnAChamfer(page, editor, toolbar, scene)
@ -1579,6 +1579,7 @@ extrude001 = extrude(profile001, length = 100)
page,
homePage,
scene,
cmdBar
}) => {
const initialCode = `sketch001 = startSketchOn(XZ)
|> circle(center = [0, 0], radius = 30)
@ -1592,7 +1593,7 @@ loft001 = loft([sketch001, sketch002])
}, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
await scene.settled(cmdBar)
// One dumb hardcoded screen pixel value
const testPoint = { x: 575, y: 200 }
@ -1687,7 +1688,7 @@ sketch002 = startSketchOn(XZ)
}, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
await scene.settled(cmdBar)
// One dumb hardcoded screen pixel value
const [clickOnSketch1] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
@ -1826,7 +1827,7 @@ sketch002 = startSketchOn(XZ)
}, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
await scene.settled(cmdBar)
// One dumb hardcoded screen pixel value
const testPoint = { x: 700, y: 250 }
@ -2521,7 +2522,7 @@ extrude001 = extrude(sketch001, length = -12)
}, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
await scene.settled(cmdBar)
})
// Test 1: Command bar flow with preselected edges
@ -2771,6 +2772,7 @@ extrude001 = extrude(sketch001, length = -12)
scene,
editor,
toolbar,
cmdBar,
}) => {
// Code samples
const initialCode = `@settings(defaultLengthUnit = in)
@ -2814,7 +2816,7 @@ chamfer04 = chamfer(extrude001, length = 5, tags = [getOppositeEdge(seg02)])
}, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
await scene.settled(cmdBar)
// verify modeling scene is loaded
await scene.expectPixelColor(
@ -2938,7 +2940,7 @@ extrude001 = extrude(sketch001, length = 30)
}, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
await scene.settled(cmdBar)
// One dumb hardcoded screen pixel value
const testPoint = { x: 575, y: 200 }
@ -3079,7 +3081,7 @@ extrude001 = extrude(sketch001, length = 40)
}, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
await scene.settled(cmdBar)
// One dumb hardcoded screen pixel value
const testPoint = { x: 580, y: 180 }
@ -3218,7 +3220,7 @@ extrude002 = extrude(sketch002, length = 50)
}, initialCode)
await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
await scene.settled(cmdBar)
// One dumb hardcoded screen pixel value
const testPoint = { x: 580, y: 320 }
@ -3306,7 +3308,7 @@ profile001 = startProfileAt([-20, 20], sketch001)
}, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
await scene.settled(cmdBar)
await toolbar.openPane('feature-tree')
// One dumb hardcoded screen pixel value
@ -3386,7 +3388,7 @@ sweep001 = sweep(sketch001, path = sketch002)
}, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
await scene.settled(cmdBar)
// One dumb hardcoded screen pixel value
const testPoint = { x: 500, y: 250 }
@ -3462,7 +3464,7 @@ segAng(rectangleSegmentA002),
}, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
await scene.settled(cmdBar)
// select line of code
const codeToSelecton = `segAng(rectangleSegmentA002) - 90,`
@ -3541,7 +3543,7 @@ sketch002 = startSketchOn(extrude001, rectangleSegmentA001)
}, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
await scene.settled(cmdBar)
// select line of code
const codeToSelecton = `center = [-11.34, 10.0]`
@ -3628,7 +3630,7 @@ sketch003 = startSketchOn(extrude001, 'START')
}, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
await scene.settled(cmdBar)
// select line of code
const codeToSelecton = `center = [-0.69, 0.56]`
@ -3703,7 +3705,7 @@ extrude001 = extrude(profile001, length = 100)
}, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
await scene.settled(cmdBar)
// One dumb hardcoded screen pixel value
const testPoint = { x: 500, y: 250 }

View File

@ -443,7 +443,8 @@ test(
await expect(page.getByText('broken-code')).toBeVisible()
await page.getByText('broken-code').click()
// Gotcha: You can not use scene.waitForExecutionDone() since the KCL code is going to fail
// Gotcha: You can not use scene.settled() since the KCL code is going to fail
await expect(page.getByTestId('loading')).toBeAttached()
await expect(page.getByTestId('loading')).not.toBeAttached({
timeout: 20_000,
})

View File

@ -63,7 +63,7 @@ test.describe('edit with AI example snapshots', () => {
localStorage.setItem('persistCode', file)
}, file)
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
await scene.settled(cmdBar)
const body1CapCoords = { x: 571, y: 351 }
const [clickBody1Cap] = scene.makeMouseHelpers(

View File

@ -61,7 +61,7 @@ test.describe('Prompt-to-edit tests', { tag: '@skipWin' }, () => {
localStorage.setItem('persistCode', file)
}, file)
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
await scene.settled(cmdBar)
const body1CapCoords = { x: 571, y: 311 }
const greenCheckCoords = { x: 565, y: 305 }
@ -156,7 +156,7 @@ test.describe('Prompt-to-edit tests', { tag: '@skipWin' }, () => {
localStorage.setItem('persistCode', file)
}, file)
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
await scene.settled(cmdBar)
const body1CapCoords = { x: 571, y: 311 }
const [clickBody1Cap] = scene.makeMouseHelpers(
@ -212,7 +212,7 @@ test.describe('Prompt-to-edit tests', { tag: '@skipWin' }, () => {
localStorage.setItem('persistCode', file)
}, file)
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
await scene.settled(cmdBar)
const submittingToast = page.getByText('Submitting to Text-to-CAD API...')
const successToast = page.getByText('Prompt to edit successful')
@ -281,7 +281,7 @@ test.describe('Prompt-to-edit tests', { tag: '@skipWin' }, () => {
localStorage.setItem('persistCode', file)
}, file)
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
await scene.settled(cmdBar)
const submittingToast = page.getByText('Submitting to Text-to-CAD API...')
const successToast = page.getByText('Prompt to edit successful')

View File

@ -762,7 +762,7 @@ plane002 = offsetPlane(XZ, offset = -2 * x)`
)
})
await homePage.openProject('test-sample')
await scene.waitForExecutionDone()
await scene.settled(cmdBar)
await expect(toolbar.startSketchBtn).toBeEnabled({ timeout: 20_000 })
const operationButton = await toolbar.getFeatureTreeOperation(
'Offset Plane',

View File

@ -22,6 +22,7 @@ test.describe('Sketch tests', { tag: ['@skipWin'] }, () => {
context,
homePage,
scene,
cmdBar,
}) => {
const u = await getUtils(page)
const selectionsSnippets = {
@ -82,7 +83,7 @@ test.describe('Sketch tests', { tag: ['@skipWin'] }, () => {
await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
await scene.settled(cmdBar)
// wait for execution done
await u.openDebugPanel()
@ -108,6 +109,7 @@ test.describe('Sketch tests', { tag: ['@skipWin'] }, () => {
page,
scene,
homePage,
cmdBar,
}) => {
const u = await getUtils(page)
await page.addInitScript(async () => {
@ -122,7 +124,7 @@ sketch001 = startSketchOn(XZ)
})
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
await scene.settled(cmdBar)
await scene.expectPixelColor(TEST_COLORS.WHITE, { x: 587, y: 270 }, 15)
@ -673,6 +675,7 @@ sketch001 = startSketchOn(XZ)
homePage,
scene,
editor,
cmdBar,
}) => {
const u = await getUtils(page)
await page.addInitScript(async () => {
@ -689,7 +692,7 @@ sketch001 = startSketchOn(XZ)
})
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
await scene.settled(cmdBar)
await expect(
page.getByRole('button', { name: 'Start Sketch' })
@ -2359,7 +2362,7 @@ profile003 = circle(sketch001, center = [6.92, -4.2], radius = 3.16)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
await scene.settled(cmdBar)
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled()
@ -2965,6 +2968,7 @@ test.describe(`Click based selection don't brick the app when clicked out of ran
toolbar,
editor,
homePage,
cmdBar,
}) => {
// We seed the scene with a single offset plane
await context.addInitScript(() => {
@ -2982,7 +2986,7 @@ test.describe(`Click based selection don't brick the app when clicked out of ran
})
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
await scene.settled(cmdBar)
await test.step(`format the code`, async () => {
// doesn't contain condensed version
@ -3047,6 +3051,7 @@ test.describe('Redirecting to home page and back to the original file should cle
toolbar,
editor,
homePage,
cmdBar,
}) => {
// We seed the scene with a single offset plane
await context.addInitScript(() => {
@ -3059,7 +3064,7 @@ test.describe('Redirecting to home page and back to the original file should cle
)
})
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
await scene.settled(cmdBar)
const [objClick] = scene.makeMouseHelpers(634, 274)
await objClick()

View File

@ -1129,7 +1129,7 @@ test.describe('Electron constraint tests', () => {
sortBy: 'last-modified-desc',
})
await homePage.openProject('test-sample')
await scene.waitForExecutionDone()
await scene.settled(cmdBar)
})
async function clickOnFirstSegmentLabel() {

View File

@ -528,6 +528,8 @@ profile001 = startProfileAt([7.49, 9.96], sketch001)
test('Hovering over 3d features highlights code, clicking puts the cursor in the right place and sends selection id to engine', async ({
page,
homePage,
scene,
cmdBar,
}) => {
const u = await getUtils(page)
await page.addInitScript(async (KCL_DEFAULT_LENGTH) => {
@ -779,11 +781,7 @@ part001 = startSketchOn(XZ)
)
`)
await expect(
page
.getByTestId('model-state-indicator-receive-reliable')
.or(page.getByTestId('model-state-indicator-execution-done'))
).toBeVisible()
await scene.settled(cmdBar)
await u.openAndClearDebugPanel()
await u.sendCustomCmd({
@ -953,6 +951,7 @@ part001 = startSketchOn(XZ)
page,
homePage,
scene,
cmdBar,
}) => {
const cases = [
{
@ -989,7 +988,7 @@ part001 = startSketchOn(XZ)
await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
await scene.settled(cmdBar)
await u.openAndClearDebugPanel()
await u.sendCustomCmd({
@ -1024,6 +1023,7 @@ part001 = startSketchOn(XZ)
page,
homePage,
scene,
cmdBar,
}) => {
await page.addInitScript(async () => {
localStorage.setItem(
@ -1043,7 +1043,7 @@ part001 = startSketchOn(XZ)
await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
await scene.settled(cmdBar)
await u.openAndClearDebugPanel()
await u.sendCustomCmd({

View File

@ -145,6 +145,14 @@ export function App() {
}
}, [lastCommandType])
useEffect(() => {
// When leaving the modeling scene, cut the engine stream.
return () => {
engineStreamActor.send({ type: EngineStreamTransition.Pause })
console.log('engineStreamActor cleanup')
}
}, [])
return (
<div className="relative h-full flex flex-col" ref={ref}>
<AppHeader

View File

@ -26,7 +26,7 @@ export const ModelStateIndicator = () => {
className += 'text-secondary'
icon = (
<FontAwesomeIcon
data-testid={dataTestId + '-execution-done'}
data-testid={dataTestId + '-playing'}
icon={faPlay}
width="20"
height="20"

View File

@ -90,6 +90,7 @@ export const KclEditorPane = () => {
return () => {
kclEditorActor.send({ type: 'setKclEditorMounted', data: false })
kclEditorActor.send({ type: 'setLastSelectionEvent', data: undefined })
console.log('kclEditorActor cleanup')
}
}, [])

View File

@ -305,6 +305,8 @@ class EngineConnection extends EventTarget {
)
isUsingConnectionLite: boolean = false
timeoutToForceConnectId: ReturnType<typeof setTimeout> = setTimeout(() => {}, 3000)
constructor({
engineCommandManager,
url,
@ -595,7 +597,7 @@ class EngineConnection extends EventTarget {
// Sometimes the remote end doesn't report the end of candidates.
// They have 3 seconds to.
setTimeout(() => {
this.timeoutToForceConnectId = setTimeout(() => {
if (that.initiateConnectionExclusive()) {
console.warn('connected after 3 second delay')
}
@ -1192,6 +1194,8 @@ class EngineConnection extends EventTarget {
)
}
disconnectAll() {
clearTimeout(this.timeoutToForceConnectId)
if (this.websocket?.readyState === 1) {
this.websocket?.close()
}

View File

@ -213,7 +213,7 @@ export function createSettings() {
streamIdleMode: new Setting<number | undefined>({
defaultValue: undefined,
hideOnLevel: 'project',
description: 'Toggle stream idling, saving bandwidth and battery',
description: 'Save bandwidth & battery',
validate: (v) =>
v === undefined ||
(typeof v === 'number' &&
@ -239,10 +239,10 @@ export function createSettings() {
}
return (
<div className="flex item-center gap-4 px-2 m-0 py-0">
<div className="flex flex-col">
<input
type="checkbox"
<div className="flex item-center gap-4 m-0 py-0">
<Toggle
offLabel="Off"
onLabel="On"
checked={settingValueInStorage !== undefined}
onChange={(event) => {
if (timeoutId) {
@ -265,8 +265,6 @@ export function createSettings() {
}}
className="block w-4 h-4"
/>
<div></div>
</div>
<div className="flex flex-col grow">
<input
type="range"

View File

@ -297,11 +297,16 @@ export const engineStreamMachine = setup({
},
},
[EngineStreamState.Resuming]: {
reenter: true,
invoke: {
src: EngineStreamTransition.StartOrReconfigureEngine,
input: (args) => args,
},
on: {
// The stream can be paused as it's resuming.
[EngineStreamTransition.Pause]: {
target: EngineStreamState.Paused,
},
[EngineStreamTransition.SetMediaStream]: {
actions: [
assign({ mediaStream: ({ context, event }) => event.mediaStream }),