This commit is contained in:
lee-at-zoo-corp
2025-03-25 12:23:45 -04:00
parent 12bf41ab7e
commit c8177564e1
15 changed files with 102 additions and 182 deletions

View File

@ -128,7 +128,6 @@ export class ElectronZoo {
const that = this const that = this
const options = { const options = {
timeout: 120000,
args: ['.', '--no-sandbox'], args: ['.', '--no-sandbox'],
env: { env: {
...process.env, ...process.env,
@ -155,26 +154,7 @@ export class ElectronZoo {
if (!this.electron) { if (!this.electron) {
this.electron = await electron.launch(options) this.electron = await electron.launch(options)
// Mac takes quite a long time to create the first window in CI. this.page = await this.electron.firstWindow()
// Turns out we can't trust firstWindow() either. So loop.
let timeoutId: ReturnType<typeof setTimeout>
const tryToGetWindowPage = () =>
new Promise((resolve) => {
const fn = () => {
this.page = this.electron.windows()[0]
timeoutId = setTimeout(() => {
if (this.page) {
clearTimeout(timeoutId)
return resolve(undefined)
}
fn()
}, 0)
}
fn()
})
await tryToGetWindowPage()
this.context = this.electron.context() this.context = this.electron.context()
await this.context.tracing.start({ screenshots: true, snapshots: true }) await this.context.tracing.start({ screenshots: true, snapshots: true })
@ -200,7 +180,7 @@ export class ElectronZoo {
await this.context.tracing.startChunk() await this.context.tracing.startChunk()
await setup(this.context, this.page, testInfo) await setup(this.context, this.page, this.projectDirName, testInfo)
await this.cleanProjectDir() await this.cleanProjectDir()
@ -250,10 +230,11 @@ export class ElectronZoo {
// return app.reuseWindowForTest(); // return app.reuseWindowForTest();
// }); // });
await this.electron?.evaluate(({ app }, projectDirName) => { // This should not be necessary because we set this in the env when launching
// @ts-ignore can't declaration merge see main.ts // await this.electron?.evaluate(({ app }, projectDirName) => {
app.testProperty['TEST_SETTINGS_FILE_KEY'] = projectDirName // // @ts-ignore can't declaration merge see main.ts
}, this.projectDirName) // app.testProperty['TEST_SETTINGS_FILE_KEY'] = projectDirName
// }, this.projectDirName)
// Always start at the root view // Always start at the root view
await this.page.goto(this.firstUrl) await this.page.goto(this.firstUrl)

View File

@ -43,7 +43,6 @@ type DragFromHandler = (
export class SceneFixture { export class SceneFixture {
public page: Page public page: Page
public streamWrapper!: Locator public streamWrapper!: Locator
public loadingIndicator!: Locator
public networkToggleConnected!: Locator public networkToggleConnected!: Locator
public startEditSketchBtn!: Locator public startEditSketchBtn!: Locator
@ -51,7 +50,6 @@ export class SceneFixture {
this.page = page this.page = page
this.streamWrapper = page.getByTestId('stream') this.streamWrapper = page.getByTestId('stream')
this.networkToggleConnected = page.getByTestId('network-toggle-ok') this.networkToggleConnected = page.getByTestId('network-toggle-ok')
this.loadingIndicator = this.streamWrapper.getByTestId('loading')
this.startEditSketchBtn = page this.startEditSketchBtn = page
.getByRole('button', { name: 'Start Sketch' }) .getByRole('button', { name: 'Start Sketch' })
.or(page.getByRole('button', { name: 'Edit Sketch' })) .or(page.getByRole('button', { name: 'Edit Sketch' }))

View File

@ -7,7 +7,7 @@ import { expect, test } from '@e2e/playwright/zoo-test'
test( test(
'When machine-api server not found butt is disabled and shows the reason', 'When machine-api server not found butt is disabled and shows the reason',
{ tag: '@electron' }, { tag: '@electron' },
async ({ context, page }, testInfo) => { async ({ scene, cmdBar, context, page }, testInfo) => {
await context.folderSetupFn(async (dir) => { await context.folderSetupFn(async (dir) => {
const bracketDir = join(dir, 'bracket') const bracketDir = join(dir, 'bracket')
await fsp.mkdir(bracketDir, { recursive: true }) await fsp.mkdir(bracketDir, { recursive: true })
@ -23,14 +23,7 @@ test(
await page.getByText('bracket').click() await page.getByText('bracket').click()
await expect(page.getByTestId('loading')).toBeAttached() await scene.settled(cmdBar)
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 // Find the make button
const makeButton = page.getByRole('button', { name: 'Make part' }) const makeButton = page.getByRole('button', { name: 'Make part' })
// Make sure the button is visible but disabled // Make sure the button is visible but disabled
@ -47,7 +40,7 @@ test(
test( test(
'When machine-api server not found home screen & project status shows the reason', 'When machine-api server not found home screen & project status shows the reason',
{ tag: '@electron' }, { tag: '@electron' },
async ({ context, page }, testInfo) => { async ({ scene, cmdBar, context, page }, testInfo) => {
await context.folderSetupFn(async (dir) => { await context.folderSetupFn(async (dir) => {
const bracketDir = join(dir, 'bracket') const bracketDir = join(dir, 'bracket')
await fsp.mkdir(bracketDir, { recursive: true }) await fsp.mkdir(bracketDir, { recursive: true })
@ -71,14 +64,8 @@ test(
await page.getByText('bracket').click() await page.getByText('bracket').click()
await expect(page.getByTestId('loading')).toBeAttached() await scene.settled(cmdBar)
await expect(page.getByTestId('loading')).not.toBeAttached({
timeout: 20_000,
})
await expect(page.getByText(notFoundText).nth(1)).not.toBeVisible() await expect(page.getByText(notFoundText).nth(1)).not.toBeVisible()
await scene.settled(cmdBar)
await networkMachineToggle.hover()
await expect(page.getByText(notFoundText).nth(1)).toBeVisible()
}
)

View File

@ -83,7 +83,7 @@ test(
test( test(
'click help/keybindings from project page', 'click help/keybindings from project page',
{ tag: '@electron' }, { tag: '@electron' },
async ({ context, page }, testInfo) => { async ({ scene, cmdBar, context, page }, testInfo) => {
await context.folderSetupFn(async (dir) => { await context.folderSetupFn(async (dir) => {
const bracketDir = path.join(dir, 'bracket') const bracketDir = path.join(dir, 'bracket')
await fsp.mkdir(bracketDir, { recursive: true }) await fsp.mkdir(bracketDir, { recursive: true })
@ -95,17 +95,11 @@ test(
await page.setBodyDimensions({ width: 1200, height: 500 }) await page.setBodyDimensions({ width: 1200, height: 500 })
page.on('console', console.log)
// expect to see the text bracket // expect to see the text bracket
await expect(page.getByText('bracket')).toBeVisible() await expect(page.getByText('bracket')).toBeVisible()
await page.getByText('bracket').click() await page.getByText('bracket').click()
await expect(page.getByTestId('loading')).toBeAttached() await scene.settled(cmdBar)
await expect(page.getByTestId('loading')).not.toBeAttached({
timeout: 20_000,
})
// click ? button // click ? button
await page.getByTestId('help-button').click() await page.getByTestId('help-button').click()
@ -120,7 +114,7 @@ test(
test( test(
'open a file in a project works and renders, open another file in different project with errors, it should clear the scene', 'open a file in a project works and renders, open another file in different project with errors, it should clear the scene',
{ tag: '@electron' }, { tag: '@electron' },
async ({ context, page, editor }, testInfo) => { async ({ scene, cmdBar, context, page, editor }, testInfo) => {
await context.folderSetupFn(async (dir) => { await context.folderSetupFn(async (dir) => {
const bracketDir = path.join(dir, 'bracket') const bracketDir = path.join(dir, 'bracket')
await fsp.mkdir(bracketDir, { recursive: true }) await fsp.mkdir(bracketDir, { recursive: true })
@ -149,24 +143,7 @@ test(
await page.getByText('bracket').click() await page.getByText('bracket').click()
await expect(page.getByTestId('loading')).toBeAttached() await scene.settled(cmdBar)
await expect(page.getByTestId('loading')).not.toBeAttached({
timeout: 20_000,
})
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).toBeEnabled({
timeout: 20_000,
})
// gray at this pixel means the stream has loaded in the most
// user way we can verify it (pixel color)
await expect
.poll(() => u.getGreatestPixDiff(pointOnModel, [110, 110, 110]), {
timeout: 10_000,
})
.toBeLessThan(20)
}) })
await test.step('Clicking the logo takes us back to the projects page / home', async () => { await test.step('Clicking the logo takes us back to the projects page / home', async () => {
@ -209,7 +186,7 @@ test(
test( test(
'open a file in a project works and renders, open another file in different project that is empty, it should clear the scene', 'open a file in a project works and renders, open another file in different project that is empty, it should clear the scene',
{ tag: '@electron' }, { tag: '@electron' },
async ({ context, page }, testInfo) => { async ({ scene, cmdBar, context, page }, testInfo) => {
await context.folderSetupFn(async (dir) => { await context.folderSetupFn(async (dir) => {
const bracketDir = path.join(dir, 'bracket') const bracketDir = path.join(dir, 'bracket')
await fsp.mkdir(bracketDir, { recursive: true }) await fsp.mkdir(bracketDir, { recursive: true })
@ -235,24 +212,7 @@ test(
await page.getByText('bracket').click() await page.getByText('bracket').click()
await expect(page.getByTestId('loading')).toBeAttached() await scene.settled(cmdBar)
await expect(page.getByTestId('loading')).not.toBeAttached({
timeout: 20_000,
})
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).toBeEnabled({
timeout: 20_000,
})
// gray at this pixel means the stream has loaded in the most
// user way we can verify it (pixel color)
await expect
.poll(() => u.getGreatestPixDiff(pointOnModel, [125, 125, 125]), {
timeout: 10_000,
})
.toBeLessThan(15)
}) })
await test.step('Clicking the logo takes us back to the projects page / home', async () => { await test.step('Clicking the logo takes us back to the projects page / home', async () => {
@ -352,7 +312,7 @@ test(
test( test(
'open a file in a project works and renders, open another file in the same project with errors, it should clear the scene', 'open a file in a project works and renders, open another file in the same project with errors, it should clear the scene',
{ tag: '@electron' }, { tag: '@electron' },
async ({ context, page }, testInfo) => { async ({ scene, cmdBar, context, page }, testInfo) => {
if (runningOnWindows()) { if (runningOnWindows()) {
test.fixme(orRunWhenFullSuiteEnabled()) test.fixme(orRunWhenFullSuiteEnabled())
} }
@ -380,10 +340,7 @@ test(
await page.getByText('bracket').click() await page.getByText('bracket').click()
await expect(page.getByTestId('loading')).toBeAttached() await scene.settled(cmdBar)
await expect(page.getByTestId('loading')).not.toBeAttached({
timeout: 20_000,
})
await expect( await expect(
page.getByRole('button', { name: 'Start Sketch' }) page.getByRole('button', { name: 'Start Sketch' })
@ -444,10 +401,7 @@ test(
await page.getByText('broken-code').click() await page.getByText('broken-code').click()
// Gotcha: You can not use scene.settled() 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('model-state-indicator-playing')).toBeAttached()
await expect(page.getByTestId('loading')).not.toBeAttached({
timeout: 20_000,
})
// Gotcha: Scroll to the text content in code mirror because CodeMirror lazy loads DOM content // Gotcha: Scroll to the text content in code mirror because CodeMirror lazy loads DOM content
await editor.scrollToText( await editor.scrollToText(
@ -470,7 +424,7 @@ test.describe('Can export from electron app', () => {
test( test(
`Can export using ${method}`, `Can export using ${method}`,
{ tag: ['@electron', '@skipLocalEngine'] }, { tag: ['@electron', '@skipLocalEngine'] },
async ({ context, page, tronApp }, testInfo) => { async ({ scene, cmdBar, context, page, tronApp }, testInfo) => {
if (!tronApp) { if (!tronApp) {
fail() fail()
} }
@ -500,10 +454,7 @@ test.describe('Can export from electron app', () => {
await page.getByText('bracket').click() await page.getByText('bracket').click()
await expect(page.getByTestId('loading')).toBeAttached() await scene.settled(cmdBar)
await expect(page.getByTestId('loading')).not.toBeAttached({
timeout: 20_000,
})
await expect( await expect(
page.getByRole('button', { name: 'Start Sketch' }) page.getByRole('button', { name: 'Start Sketch' })
@ -813,7 +764,7 @@ test.describe(`Project management commands`, () => {
test( test(
`Rename from project page`, `Rename from project page`,
{ tag: '@electron' }, { tag: '@electron' },
async ({ context, page }, testInfo) => { async ({ context, page, scene, cmdBar }, testInfo) => {
const projectName = `my_project_to_rename` const projectName = `my_project_to_rename`
await context.folderSetupFn(async (dir) => { await context.folderSetupFn(async (dir) => {
await fsp.mkdir(`${dir}/${projectName}`, { recursive: true }) await fsp.mkdir(`${dir}/${projectName}`, { recursive: true })
@ -844,7 +795,7 @@ test.describe(`Project management commands`, () => {
page.on('console', console.log) page.on('console', console.log)
await projectHomeLink.click() await projectHomeLink.click()
await u.waitForPageLoad() await scene.settled(cmdBar)
}) })
await test.step(`Run rename command via command palette`, async () => { await test.step(`Run rename command via command palette`, async () => {
@ -901,9 +852,9 @@ test.describe(`Project management commands`, () => {
await page.setBodyDimensions({ width: 1200, height: 500 }) await page.setBodyDimensions({ width: 1200, height: 500 })
page.on('console', console.log) page.on('console', console.log)
await page.waitForTimeout(3000)
await projectHomeLink.click() await projectHomeLink.click()
await u.waitForPageLoad()
await scene.connectionEstablished()
await scene.settled(cmdBar) await scene.settled(cmdBar)
}) })
@ -927,7 +878,7 @@ test.describe(`Project management commands`, () => {
test( test(
`Rename from home page`, `Rename from home page`,
{ tag: '@electron' }, { tag: '@electron' },
async ({ context, page, homePage }, testInfo) => { async ({ context, page, homePage, scene, cmdBar }, testInfo) => {
const projectName = `my_project_to_rename` const projectName = `my_project_to_rename`
await context.folderSetupFn(async (dir) => { await context.folderSetupFn(async (dir) => {
await fsp.mkdir(`${dir}/${projectName}`, { recursive: true }) await fsp.mkdir(`${dir}/${projectName}`, { recursive: true })
@ -983,7 +934,7 @@ test.describe(`Project management commands`, () => {
test( test(
`Delete from home page`, `Delete from home page`,
{ tag: '@electron' }, { tag: '@electron' },
async ({ context, page }, testInfo) => { async ({ context, page, scene, cmdBar }, testInfo) => {
const projectName = `my_project_to_delete` const projectName = `my_project_to_delete`
await context.folderSetupFn(async (dir) => { await context.folderSetupFn(async (dir) => {
await fsp.mkdir(`${dir}/${projectName}`, { recursive: true }) await fsp.mkdir(`${dir}/${projectName}`, { recursive: true })
@ -1196,7 +1147,7 @@ test(
test( test(
'Nested directories in project without main.kcl do not create main.kcl', 'Nested directories in project without main.kcl do not create main.kcl',
{ tag: '@electron' }, { tag: '@electron' },
async ({ context, page }, testInfo) => { async ({ scene, cmdBar, context, page }, testInfo) => {
let testDir: string | undefined let testDir: string | undefined
await context.folderSetupFn(async (dir) => { await context.folderSetupFn(async (dir) => {
await fsp.mkdir(path.join(dir, 'router-template-slate', 'nested'), { await fsp.mkdir(path.join(dir, 'router-template-slate', 'nested'), {
@ -1219,10 +1170,7 @@ test(
await test.step('Open the project', async () => { await test.step('Open the project', async () => {
await page.getByText('router-template-slate').click() await page.getByText('router-template-slate').click()
await expect(page.getByTestId('loading')).toBeAttached() await scene.settled(cmdBar)
await expect(page.getByTestId('loading')).not.toBeAttached({
timeout: 20_000,
})
// It actually loads. // It actually loads.
await expect(u.codeLocator).toContainText('mounting bracket') await expect(u.codeLocator).toContainText('mounting bracket')
@ -1335,7 +1283,7 @@ test(
test( test(
'Can load a file with CRLF line endings', 'Can load a file with CRLF line endings',
{ tag: '@electron' }, { tag: '@electron' },
async ({ context, page }, testInfo) => { async ({ context, page, scene, cmdBar }, testInfo) => {
if (runningOnWindows()) { if (runningOnWindows()) {
test.fixme(orRunWhenFullSuiteEnabled()) test.fixme(orRunWhenFullSuiteEnabled())
} }
@ -1358,13 +1306,8 @@ test(
const u = await getUtils(page) const u = await getUtils(page)
await page.setBodyDimensions({ width: 1200, height: 500 }) await page.setBodyDimensions({ width: 1200, height: 500 })
page.on('console', console.log)
await page.getByText('router-template-slate').click() await page.getByText('router-template-slate').click()
await expect(page.getByTestId('loading')).toBeAttached() await scene.settled(cmdBar)
await expect(page.getByTestId('loading')).not.toBeAttached({
timeout: 20_000,
})
await expect(u.codeLocator).toContainText('routerDiameter') await expect(u.codeLocator).toContainText('routerDiameter')
await expect(u.codeLocator).toContainText('templateGap') await expect(u.codeLocator).toContainText('templateGap')
@ -1876,7 +1819,7 @@ test(
test( test(
'file pane is scrollable when there are many files', 'file pane is scrollable when there are many files',
{ tag: '@electron' }, { tag: '@electron' },
async ({ context, page }, testInfo) => { async ({ scene, cmdBar, context, page }, testInfo) => {
await context.folderSetupFn(async (dir) => { await context.folderSetupFn(async (dir) => {
const testDir = path.join(dir, 'testProject') const testDir = path.join(dir, 'testProject')
await fsp.mkdir(testDir, { recursive: true }) await fsp.mkdir(testDir, { recursive: true })
@ -1955,10 +1898,8 @@ test(
await test.step('setup, open file pane', async () => { await test.step('setup, open file pane', async () => {
await page.getByText('testProject').click() await page.getByText('testProject').click()
await expect(page.getByTestId('loading')).toBeAttached()
await expect(page.getByTestId('loading')).not.toBeAttached({ await scene.settled(cmdBar)
timeout: 20_000,
})
await page.getByTestId('files-pane-button').click() await page.getByTestId('files-pane-button').click()
}) })

View File

@ -689,6 +689,8 @@ extrude002 = extrude(profile002, length = 150)
homePage, homePage,
scene, scene,
toolbar, toolbar,
viewport,
cmdBar,
}) => { }) => {
await context.folderSetupFn(async (dir) => { await context.folderSetupFn(async (dir) => {
const legoDir = path.join(dir, 'lego') const legoDir = path.join(dir, 'lego')
@ -703,8 +705,8 @@ extrude002 = extrude(profile002, length = 150)
await homePage.openProject('lego') await homePage.openProject('lego')
await toolbar.closePane('code') await toolbar.closePane('code')
}) })
await test.step(`Waiting for the loading spinner to disappear`, async () => { await test.step(`Waiting for scene to settle`, async () => {
await scene.loadingIndicator.waitFor({ state: 'detached' }) await scene.settled(cmdBar)
}) })
await test.step(`The part should start loading quickly, not waiting until execution is complete`, async () => { await test.step(`The part should start loading quickly, not waiting until execution is complete`, async () => {
// TODO: use the viewport size to pick the center point, but the `viewport` fixture's values were wrong. // TODO: use the viewport size to pick the center point, but the `viewport` fixture's values were wrong.

View File

@ -89,7 +89,7 @@ test.describe('Test network and connection issues', () => {
test( test(
'Engine disconnect & reconnect in sketch mode', 'Engine disconnect & reconnect in sketch mode',
{ tag: '@skipLocalEngine' }, { tag: '@skipLocalEngine' },
async ({ page, homePage, toolbar }) => { async ({ page, homePage, toolbar, scene, cmdBar }) => {
test.fixme(orRunWhenFullSuiteEnabled()) test.fixme(orRunWhenFullSuiteEnabled())
const networkToggle = page.getByTestId('network-toggle') const networkToggle = page.getByTestId('network-toggle')
@ -169,7 +169,7 @@ test.describe('Test network and connection issues', () => {
// Expect the network to be up // Expect the network to be up
await expect(networkToggle).toContainText('Connected') await expect(networkToggle).toContainText('Connected')
await expect(page.getByTestId('loading-stream')).not.toBeAttached() await scene.settled(cmdBar)
// Click off the code pane. // Click off the code pane.
await page.mouse.click(100, 100) await page.mouse.click(100, 100)

View File

@ -74,7 +74,7 @@ async function waitForPageLoadWithRetry(page: Page) {
await expect(async () => { await expect(async () => {
await page.goto('/') await page.goto('/')
const errorMessage = 'App failed to load - 🔃 Retrying ...' const errorMessage = 'App failed to load - 🔃 Retrying ...'
await expect(page.getByTestId('loading'), errorMessage).not.toBeAttached({ await expect(page.getByTestId('model-state-indicator-playing'), errorMessage).not.toBeAttached({
timeout: 20_000, timeout: 20_000,
}) })
@ -87,9 +87,10 @@ async function waitForPageLoadWithRetry(page: Page) {
}).toPass({ timeout: 70_000, intervals: [1_000] }) }).toPass({ timeout: 70_000, intervals: [1_000] })
} }
// lee: This needs to be replaced by scene.settled() eventually.
async function waitForPageLoad(page: Page) { async function waitForPageLoad(page: Page) {
// wait for all spinners to be gone // wait for all spinners to be gone
await expect(page.getByTestId('loading')).not.toBeAttached({ await expect(page.getByTestId('model-state-indicator-playing')).not.toBeAttached({
timeout: 20_000, timeout: 20_000,
}) })
@ -871,9 +872,10 @@ export async function tearDown(page: Page, testInfo: TestInfo) {
export async function setup( export async function setup(
context: BrowserContext, context: BrowserContext,
page: Page, page: Page,
testInfo?: TestInfo testDir: string,
testInfo?: TestInfo,
) { ) {
await context.addInitScript( await page.addInitScript(
async ({ async ({
token, token,
settingsKey, settingsKey,
@ -905,6 +907,7 @@ export async function setup(
theme: 'dark', theme: 'dark',
}, },
...TEST_SETTINGS.project, ...TEST_SETTINGS.project,
project_directory: testDir,
onboarding_status: 'dismissed', onboarding_status: 'dismissed',
}, },
project: { project: {
@ -914,7 +917,7 @@ export async function setup(
}, },
}), }),
IS_PLAYWRIGHT_KEY, IS_PLAYWRIGHT_KEY,
PLAYWRIGHT_TEST_DIR: TEST_SETTINGS.project?.directory || '', PLAYWRIGHT_TEST_DIR: testDir,
PERSIST_MODELING_CONTEXT, PERSIST_MODELING_CONTEXT,
} }
) )
@ -931,7 +934,7 @@ export async function setup(
failOnConsoleErrors(page, testInfo) failOnConsoleErrors(page, testInfo)
// kill animations, speeds up tests and reduced flakiness // kill animations, speeds up tests and reduced flakiness
await page.emulateMedia({ reducedMotion: 'reduce' }) // await page.emulateMedia({ reducedMotion: 'reduce' })
// Trigger a navigation, since loading file:// doesn't. // Trigger a navigation, since loading file:// doesn't.
// await page.reload() // await page.reload()

View File

@ -77,7 +77,7 @@ test.describe('Testing constraints', { tag: ['@skipWin'] }, () => {
}) })
.toBe(false) .toBe(false)
}) })
test(`Remove constraints`, async ({ page, homePage }) => { test(`Remove constraints`, async ({ page, homePage, scene, cmdBar }) => {
await page.addInitScript(async () => { await page.addInitScript(async () => {
localStorage.setItem( localStorage.setItem(
'persistCode', 'persistCode',
@ -101,7 +101,7 @@ test.describe('Testing constraints', { tag: ['@skipWin'] }, () => {
await page.setBodyDimensions({ width: 1000, height: 500 }) await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await u.waitForPageLoad() await scene.settled(cmdBar)
await page.getByText('line(end = [74.36, 130.4], tag = $seg01)').click() await page.getByText('line(end = [74.36, 130.4], tag = $seg01)').click()
await page.getByRole('button', { name: 'Edit Sketch' }).click() await page.getByRole('button', { name: 'Edit Sketch' }).click()
@ -142,7 +142,7 @@ test.describe('Testing constraints', { tag: ['@skipWin'] }, () => {
}, },
] as const ] as const
for (const { testName, offset } of cases) { for (const { testName, offset } of cases) {
test(`${testName}`, async ({ page, homePage }) => { test(`${testName}`, async ({ page, homePage, scene, cmdBar }) => {
await page.addInitScript(async () => { await page.addInitScript(async () => {
localStorage.setItem( localStorage.setItem(
'persistCode', 'persistCode',
@ -166,7 +166,7 @@ test.describe('Testing constraints', { tag: ['@skipWin'] }, () => {
await page.setBodyDimensions({ width: 1200, height: 500 }) await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await u.waitForPageLoad() await scene.settled(cmdBar)
await page.getByText('line(end = [74.36, 130.4], tag = $seg01)').click() await page.getByText('line(end = [74.36, 130.4], tag = $seg01)').click()
await page.getByRole('button', { name: 'Edit Sketch' }).click() await page.getByRole('button', { name: 'Edit Sketch' }).click()
@ -250,7 +250,7 @@ test.describe('Testing constraints', { tag: ['@skipWin'] }, () => {
}, },
] as const ] as const
for (const { testName, value, constraint } of cases) { for (const { testName, value, constraint } of cases) {
test(`${constraint} - ${testName}`, async ({ page, homePage }) => { test(`${constraint} - ${testName}`, async ({ page, homePage, scene, cmdBar }) => {
await page.addInitScript(async () => { await page.addInitScript(async () => {
localStorage.setItem( localStorage.setItem(
'persistCode', 'persistCode',
@ -274,7 +274,7 @@ test.describe('Testing constraints', { tag: ['@skipWin'] }, () => {
await page.setBodyDimensions({ width: 1000, height: 500 }) await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await u.waitForPageLoad() await scene.settled(cmdBar)
await page.getByText('line(end = [74.36, 130.4])').click() await page.getByText('line(end = [74.36, 130.4])').click()
await page.getByRole('button', { name: 'Edit Sketch' }).click() await page.getByRole('button', { name: 'Edit Sketch' }).click()
@ -361,7 +361,7 @@ test.describe('Testing constraints', { tag: ['@skipWin'] }, () => {
}, },
] as const ] as const
for (const { testName, addVariable, value, constraint } of cases) { for (const { testName, addVariable, value, constraint } of cases) {
test(`${constraint} - ${testName}`, async ({ page, homePage }) => { test(`${constraint} - ${testName}`, async ({ page, homePage, scene, cmdBar }) => {
await page.addInitScript(async () => { await page.addInitScript(async () => {
localStorage.setItem( localStorage.setItem(
'persistCode', 'persistCode',
@ -385,7 +385,7 @@ test.describe('Testing constraints', { tag: ['@skipWin'] }, () => {
await page.setBodyDimensions({ width: 1200, height: 500 }) await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await u.waitForPageLoad() await scene.settled(cmdBar)
await page.getByText('line(end = [74.36, 130.4])').click() await page.getByText('line(end = [74.36, 130.4])').click()
await page.getByRole('button', { name: 'Edit Sketch' }).click() await page.getByRole('button', { name: 'Edit Sketch' }).click()
@ -475,7 +475,7 @@ test.describe('Testing constraints', { tag: ['@skipWin'] }, () => {
}, },
] as const ] as const
for (const { testName, addVariable, value, axisSelect } of cases) { for (const { testName, addVariable, value, axisSelect } of cases) {
test(`${testName}`, async ({ page, homePage }) => { test(`${testName}`, async ({ page, homePage, scene, cmdBar }) => {
await page.addInitScript(async () => { await page.addInitScript(async () => {
localStorage.setItem( localStorage.setItem(
'persistCode', 'persistCode',
@ -499,7 +499,7 @@ test.describe('Testing constraints', { tag: ['@skipWin'] }, () => {
await page.setBodyDimensions({ width: 1200, height: 500 }) await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await u.waitForPageLoad() await scene.settled(cmdBar)
await page.getByText('line(end = [74.36, 130.4])').click() await page.getByText('line(end = [74.36, 130.4])').click()
await page.getByRole('button', { name: 'Edit Sketch' }).click() await page.getByRole('button', { name: 'Edit Sketch' }).click()
@ -578,7 +578,7 @@ test.describe('Testing constraints', { tag: ['@skipWin'] }, () => {
}, },
] as const ] as const
for (const { testName, addVariable, value, constraint } of cases) { for (const { testName, addVariable, value, constraint } of cases) {
test(`${testName}`, async ({ page, homePage }) => { test(`${testName}`, async ({ page, homePage, scene, cmdBar }) => {
await page.addInitScript(async () => { await page.addInitScript(async () => {
localStorage.setItem( localStorage.setItem(
'persistCode', 'persistCode',
@ -602,7 +602,7 @@ test.describe('Testing constraints', { tag: ['@skipWin'] }, () => {
await page.setBodyDimensions({ width: 1000, height: 500 }) await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await u.waitForPageLoad() await scene.settled(cmdBar)
await page.getByText('line(end = [74.36, 130.4])').click() await page.getByText('line(end = [74.36, 130.4])').click()
await page.getByRole('button', { name: 'Edit Sketch' }).click() await page.getByRole('button', { name: 'Edit Sketch' }).click()
@ -655,7 +655,7 @@ test.describe('Testing constraints', { tag: ['@skipWin'] }, () => {
}, },
] as const ] as const
for (const { testName, addVariable, value, constraint } of cases) { for (const { testName, addVariable, value, constraint } of cases) {
test(`${testName}`, async ({ context, homePage, page, editor }) => { test(`${testName}`, async ({ context, homePage, page, editor, scene, cmdBar }) => {
// constants and locators // constants and locators
const cmdBarKclInput = page const cmdBarKclInput = page
.getByTestId('cmd-bar-arg-value') .getByTestId('cmd-bar-arg-value')
@ -689,7 +689,7 @@ part002 = startSketchOn(XZ)
await page.setBodyDimensions({ width: 1200, height: 500 }) await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await u.waitForPageLoad() await scene.settled(cmdBar)
await editor.scrollToText('line(end = [74.36, 130.4])', true) await editor.scrollToText('line(end = [74.36, 130.4])', true)
await page.getByText('line(end = [74.36, 130.4])').click() await page.getByText('line(end = [74.36, 130.4])').click()
@ -746,7 +746,7 @@ part002 = startSketchOn(XZ)
}, },
] as const ] as const
for (const { codeAfter, constraintName } of cases) { for (const { codeAfter, constraintName } of cases) {
test(`${constraintName}`, async ({ page, homePage }) => { test(`${constraintName}`, async ({ page, homePage, scene, cmdBar }) => {
await page.addInitScript(async (customCode) => { await page.addInitScript(async (customCode) => {
localStorage.setItem( localStorage.setItem(
'persistCode', 'persistCode',
@ -770,7 +770,7 @@ part002 = startSketchOn(XZ)
await page.setBodyDimensions({ width: 1000, height: 500 }) await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await u.waitForPageLoad() await scene.settled(cmdBar)
await page.getByText('line(end = [74.36, 130.4])').click() await page.getByText('line(end = [74.36, 130.4])').click()
await page.getByRole('button', { name: 'Edit Sketch' }).click() await page.getByRole('button', { name: 'Edit Sketch' }).click()
@ -848,7 +848,7 @@ part002 = startSketchOn(XZ)
}, },
] as const ] as const
for (const { codeAfter, constraintName } of cases) { for (const { codeAfter, constraintName } of cases) {
test(`${constraintName}`, async ({ page, homePage }) => { test(`${constraintName}`, async ({ page, homePage, scene, cmdBar }) => {
await page.addInitScript(async () => { await page.addInitScript(async () => {
localStorage.setItem( localStorage.setItem(
'persistCode', 'persistCode',
@ -871,7 +871,7 @@ part002 = startSketchOn(XZ)
await page.setBodyDimensions({ width: 1000, height: 500 }) await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await u.waitForPageLoad() await scene.settled(cmdBar)
await page.getByText('line(end = [74.36, 130.4])').click() await page.getByText('line(end = [74.36, 130.4])').click()
await page.getByRole('button', { name: 'Edit Sketch' }).click() await page.getByRole('button', { name: 'Edit Sketch' }).click()
@ -930,7 +930,7 @@ part002 = startSketchOn(XZ)
}, },
] as const ] as const
for (const { codeAfter, constraintName, axisClick } of cases) { for (const { codeAfter, constraintName, axisClick } of cases) {
test(`${constraintName}`, async ({ page, homePage }) => { test(`${constraintName}`, async ({ page, homePage, scene, cmdBar }) => {
await page.addInitScript(async () => { await page.addInitScript(async () => {
localStorage.setItem( localStorage.setItem(
'persistCode', 'persistCode',
@ -953,7 +953,7 @@ part002 = startSketchOn(XZ)
await page.setBodyDimensions({ width: 1200, height: 500 }) await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await u.waitForPageLoad() await scene.settled(cmdBar)
await page.getByText('line(end = [74.36, 130.4])').click() await page.getByText('line(end = [74.36, 130.4])').click()
await page.getByRole('button', { name: 'Edit Sketch' }).click() await page.getByRole('button', { name: 'Edit Sketch' }).click()
@ -994,6 +994,8 @@ part002 = startSketchOn(XZ)
test('Horizontally constrained line remains selected after applying constraint', async ({ test('Horizontally constrained line remains selected after applying constraint', async ({
page, page,
homePage, homePage,
scene,
cmdBar
}) => { }) => {
test.fixme(orRunWhenFullSuiteEnabled()) test.fixme(orRunWhenFullSuiteEnabled())
test.setTimeout(70_000) test.setTimeout(70_000)
@ -1010,7 +1012,7 @@ part002 = startSketchOn(XZ)
await page.setBodyDimensions({ width: 1200, height: 500 }) await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await u.waitForPageLoad() await scene.settled()
await page.getByText('line(end = [3.79, 2.68], tag = $seg01)').click() await page.getByText('line(end = [3.79, 2.68], tag = $seg01)').click()
await expect(page.getByRole('button', { name: 'Edit Sketch' })).toBeEnabled( await expect(page.getByRole('button', { name: 'Edit Sketch' })).toBeEnabled(

View File

@ -149,7 +149,6 @@ export function App() {
// When leaving the modeling scene, cut the engine stream. // When leaving the modeling scene, cut the engine stream.
return () => { return () => {
engineStreamActor.send({ type: EngineStreamTransition.Pause }) engineStreamActor.send({ type: EngineStreamTransition.Pause })
console.log('engineStreamActor cleanup')
} }
}, []) }, [])

View File

@ -4,16 +4,10 @@ import { hotkeyDisplay } from '@src/lib/hotkeyWrapper'
import { commandBarActor } from '@src/machines/commandBarMachine' import { commandBarActor } from '@src/machines/commandBarMachine'
export function CommandBarOpenButton() { export function CommandBarOpenButton() {
const { immediateState } = useNetworkContext()
const platform = usePlatform() const platform = usePlatform()
const isDisabled =
immediateState.type !== EngineConnectionStateType.ConnectionEstablished
return ( return (
<button <button
disabled={isDisabled}
className="group rounded-full flex items-center justify-center gap-2 px-2 py-1 bg-primary/10 dark:bg-chalkboard-90 dark:backdrop-blur-sm border-primary hover:border-primary dark:border-chalkboard-50 dark:hover:border-inherit text-primary dark:text-inherit" className="group rounded-full flex items-center justify-center gap-2 px-2 py-1 bg-primary/10 dark:bg-chalkboard-90 dark:backdrop-blur-sm border-primary hover:border-primary dark:border-chalkboard-50 dark:hover:border-inherit text-primary dark:text-inherit"
onClick={() => commandBarActor.send({ type: 'Open' })} onClick={() => commandBarActor.send({ type: 'Open' })}
data-testid="command-bar-open-button" data-testid="command-bar-open-button"

View File

@ -1175,6 +1175,8 @@ class EngineConnection extends EventTarget {
// Do not change this back to an object or any, we should only be sending the // Do not change this back to an object or any, we should only be sending the
// WebSocketRequest type! // WebSocketRequest type!
unreliableSend(message: Models['WebSocketRequest_type']) { unreliableSend(message: Models['WebSocketRequest_type']) {
if (this.unreliableDataChannel.readyState !== 'open' ) return
// TODO(paultag): Add in logic to determine the connection state and // TODO(paultag): Add in logic to determine the connection state and
// take actions if needed? // take actions if needed?
this.unreliableDataChannel?.send( this.unreliableDataChannel?.send(
@ -1185,7 +1187,7 @@ class EngineConnection extends EventTarget {
// WebSocketRequest type! // WebSocketRequest type!
send(message: Models['WebSocketRequest_type']) { send(message: Models['WebSocketRequest_type']) {
// Not connected, don't send anything // Not connected, don't send anything
if (this.websocket?.readyState === 3) return if (this.websocket?.readyState !== 1) return
// TODO(paultag): Add in logic to determine the connection state and // TODO(paultag): Add in logic to determine the connection state and
// take actions if needed? // take actions if needed?

View File

@ -73,9 +73,6 @@ export const KCL_DEFAULT_DEGREE = `360`
/** The default KCL color expression */ /** The default KCL color expression */
export const KCL_DEFAULT_COLOR = `#3c73ff` export const KCL_DEFAULT_COLOR = `#3c73ff`
/** localStorage key for the playwright test-specific app settings file */
export const TEST_SETTINGS_FILE_KEY = 'playwright-test-settings'
export const SETTINGS_FILE_NAME = 'settings.toml' export const SETTINGS_FILE_NAME = 'settings.toml'
export const TOKEN_FILE_NAME = 'token.txt' export const TOKEN_FILE_NAME = 'token.txt'
export const PROJECT_SETTINGS_FILE_NAME = 'project.toml' export const PROJECT_SETTINGS_FILE_NAME = 'project.toml'

View File

@ -17,6 +17,7 @@ import {
PROJECT_IMAGE_NAME, PROJECT_IMAGE_NAME,
PROJECT_SETTINGS_FILE_NAME, PROJECT_SETTINGS_FILE_NAME,
SETTINGS_FILE_NAME, SETTINGS_FILE_NAME,
TEST_SETTINGS_FILE_KEY,
TELEMETRY_FILE_NAME, TELEMETRY_FILE_NAME,
TELEMETRY_RAW_FILE_NAME, TELEMETRY_RAW_FILE_NAME,
TOKEN_FILE_NAME, TOKEN_FILE_NAME,
@ -456,9 +457,7 @@ const getAppFolderName = () => {
export const getAppSettingsFilePath = async () => { export const getAppSettingsFilePath = async () => {
const isTestEnv = window.electron.process.env.IS_PLAYWRIGHT === 'true' const isTestEnv = window.electron.process.env.IS_PLAYWRIGHT === 'true'
const testSettingsPath = await window.electron.getAppTestProperty( const testSettingsPath = window.electron.process.env.TEST_SETTINGS_FILE_KEY
'TEST_SETTINGS_FILE_KEY'
)
if (isTestEnv && !testSettingsPath) return SETTINGS_FILE_NAME if (isTestEnv && !testSettingsPath) return SETTINGS_FILE_NAME
const appConfig = await window.electron.getPath('appData') const appConfig = await window.electron.getPath('appData')
@ -477,9 +476,8 @@ export const getAppSettingsFilePath = async () => {
} }
const getTokenFilePath = async () => { const getTokenFilePath = async () => {
const isTestEnv = window.electron.process.env.IS_PLAYWRIGHT === 'true' const isTestEnv = window.electron.process.env.IS_PLAYWRIGHT === 'true'
const testSettingsPath = await window.electron.getAppTestProperty( const testSettingsPath = window.electron.process.env.TEST_SETTINGS_FILE_KEY
'TEST_SETTINGS_FILE_KEY'
)
const appConfig = await window.electron.getPath('appData') const appConfig = await window.electron.getPath('appData')
const fullPath = isTestEnv const fullPath = isTestEnv
? testSettingsPath ? testSettingsPath
@ -496,8 +494,13 @@ const getTokenFilePath = async () => {
} }
const getTelemetryFilePath = async () => { const getTelemetryFilePath = async () => {
const isTestEnv = window.electron.process.env.IS_PLAYWRIGHT === 'true'
const testSettingsPath = window.electron.process.env.TEST_SETTINGS_FILE_KEY
const appConfig = await window.electron.getPath('appData') const appConfig = await window.electron.getPath('appData')
const fullPath = window.electron.path.join(appConfig, getAppFolderName()) const fullPath = isTestEnv
? testSettingsPath
: window.electron.path.join(appConfig, getAppFolderName())
try { try {
await window.electron.stat(fullPath) await window.electron.stat(fullPath)
} catch (e) { } catch (e) {
@ -510,8 +513,13 @@ const getTelemetryFilePath = async () => {
} }
const getRawTelemetryFilePath = async () => { const getRawTelemetryFilePath = async () => {
const isTestEnv = window.electron.process.env.IS_PLAYWRIGHT === 'true'
const testSettingsPath = window.electron.process.env.TEST_SETTINGS_FILE_KEY
const appConfig = await window.electron.getPath('appData') const appConfig = await window.electron.getPath('appData')
const fullPath = window.electron.path.join(appConfig, getAppFolderName()) const fullPath = isTestEnv
? testSettingsPath
: window.electron.path.join(appConfig, getAppFolderName())
try { try {
await window.electron.stat(fullPath) await window.electron.stat(fullPath)
} catch (e) { } catch (e) {
@ -535,9 +543,15 @@ const getProjectSettingsFilePath = async (projectPath: string) => {
} }
export const getInitialDefaultDir = async () => { export const getInitialDefaultDir = async () => {
const isTestEnv = window.electron.process.env.IS_PLAYWRIGHT === 'true'
const testSettingsPath = window.electron.process.env.TEST_SETTINGS_FILE_KEY
if (!window.electron) { if (!window.electron) {
return '' return ''
} }
if (isTestEnv) {
return testSettingsPath
}
const dir = await window.electron.getPath('documents') const dir = await window.electron.getPath('documents')
return window.electron.path.join(dir, PROJECT_FOLDER) return window.electron.path.join(dir, PROJECT_FOLDER)
} }

View File

@ -17,9 +17,7 @@ const save_ = async (file: ModelingAppFile, toastId: string) => {
if (window.electron.process.env.IS_PLAYWRIGHT) { if (window.electron.process.env.IS_PLAYWRIGHT) {
// Skip file picker, save to the test dir downloads directory // Skip file picker, save to the test dir downloads directory
const testSettingsPath = await window.electron.getAppTestProperty( const testSettingsPath = window.electron.process.env.TEST_SETTINGS_FILE_KEY
'TEST_SETTINGS_FILE_KEY'
)
const downloadDir = window.electron.join( const downloadDir = window.electron.join(
testSettingsPath, testSettingsPath,
'downloads-during-playwright' 'downloads-during-playwright'

View File

@ -278,7 +278,9 @@ contextBridge.exposeInMainWorld('electron', {
'VITE_KC_SKIP_AUTH', 'VITE_KC_SKIP_AUTH',
'VITE_KC_CONNECTION_TIMEOUT_MS', 'VITE_KC_CONNECTION_TIMEOUT_MS',
'VITE_KC_DEV_TOKEN', 'VITE_KC_DEV_TOKEN',
'IS_PLAYWRIGHT', 'IS_PLAYWRIGHT',
'TEST_SETTINGS_FILE_KEY',
// Really we shouldn't use these and our code should use NODE_ENV // Really we shouldn't use these and our code should use NODE_ENV
'DEV', 'DEV',