Get sketch-tests.spec.ts passing in electron

This commit is contained in:
49lf
2024-11-20 08:29:29 -05:00
parent 18f4da63a2
commit 8f0f4b539c
9 changed files with 184 additions and 103 deletions

View File

@ -20,11 +20,11 @@ export class AuthenticatedApp {
public readonly page: Page public readonly page: Page
public readonly context: BrowserContext public readonly context: BrowserContext
public readonly testInfo: TestInfo public readonly testInfo: TestInfo
public readonly viewPortSize = { width: 1000, height: 500 } public readonly viewPortSize = { width: 1200, height: 500 }
constructor(context: BrowserContext, page: Page, testInfo: TestInfo) { constructor(context: BrowserContext, page: Page, testInfo: TestInfo) {
this.page = page
this.context = context this.context = context
this.page = page
this.testInfo = testInfo this.testInfo = testInfo
} }
@ -49,7 +49,7 @@ export class AuthenticatedApp {
} }
} }
interface Fixtures { export interface Fixtures {
app: AuthenticatedApp app: AuthenticatedApp
tronApp: AuthenticatedTronApp tronApp: AuthenticatedTronApp
cmdBar: CmdBarFixture cmdBar: CmdBarFixture
@ -61,9 +61,10 @@ interface Fixtures {
export class AuthenticatedTronApp { export class AuthenticatedTronApp {
public readonly _page: Page public readonly _page: Page
public page: Page public page: Page
public readonly context: BrowserContext public context: BrowserContext
public readonly testInfo: TestInfo public readonly testInfo: TestInfo
public electronApp?: ElectronApplication public electronApp?: ElectronApplication
public readonly viewPortSize = { width: 1200, height: 500 }
constructor(context: BrowserContext, page: Page, testInfo: TestInfo) { constructor(context: BrowserContext, page: Page, testInfo: TestInfo) {
this._page = page this._page = page
@ -79,15 +80,20 @@ export class AuthenticatedTronApp {
appSettings?: Partial<SaveSettingsPayload> appSettings?: Partial<SaveSettingsPayload>
} = { fixtures: {} } } = { fixtures: {} }
) { ) {
const { electronApp, page } = await setupElectron({ const { electronApp, page, context } = await setupElectron({
testInfo: this.testInfo, testInfo: this.testInfo,
folderSetupFn: arg.folderSetupFn, folderSetupFn: arg.folderSetupFn,
cleanProjectDir: arg.cleanProjectDir, cleanProjectDir: arg.cleanProjectDir,
appSettings: arg.appSettings, appSettings: arg.appSettings,
}) })
this.page = page this.page = page
this.context = context
this.electronApp = electronApp this.electronApp = electronApp
await page.setViewportSize({ width: 1200, height: 500 })
// Setup localStorage, addCookies, reload
await setup(this.context, this.page, this.testInfo)
await page.setViewportSize(this.viewPortSize)
for (const key of unsafeTypedKeys(arg.fixtures)) { for (const key of unsafeTypedKeys(arg.fixtures)) {
const fixture = arg.fixtures[key] const fixture = arg.fixtures[key]
@ -110,13 +116,7 @@ export class AuthenticatedTronApp {
}) })
} }
export const test = base.extend<Fixtures>({ export const fixtures = {
app: async ({ page, context }, use, testInfo) => {
await use(new AuthenticatedApp(context, page, testInfo))
},
tronApp: async ({ page, context }, use, testInfo) => {
await use(new AuthenticatedTronApp(context, page, testInfo))
},
cmdBar: async ({ page }, use) => { cmdBar: async ({ page }, use) => {
await use(new CmdBarFixture(page)) await use(new CmdBarFixture(page))
}, },
@ -129,13 +129,7 @@ export const test = base.extend<Fixtures>({
scene: async ({ page }, use) => { scene: async ({ page }, use) => {
await use(new SceneFixture(page)) await use(new SceneFixture(page))
}, },
homePage: async ({ page }, use) => { homePage: async ({ page }, use, testInfo) => {
await use(new HomePageFixture(page)) await use(new HomePageFixture(page))
}, },
}) }
test.afterEach(async ({ page }, testInfo) => {
await tearDown(page, testInfo)
})
export { expect } from '@playwright/test'

View File

@ -14,10 +14,14 @@ interface HomePageState {
export class HomePageFixture { export class HomePageFixture {
public page: Page public page: Page
projectSection!: Locator
projectCard!: Locator projectCard!: Locator
projectCardTitle!: Locator projectCardTitle!: Locator
projectCardFile!: Locator projectCardFile!: Locator
projectCardFolder!: Locator projectCardFolder!: Locator
projectButtonNew!: Locator
projectButtonContinue!: Locator
projectTextName!: Locator
sortByDateBtn!: Locator sortByDateBtn!: Locator
sortByNameBtn!: Locator sortByNameBtn!: Locator
@ -28,11 +32,17 @@ export class HomePageFixture {
reConstruct = (page: Page) => { reConstruct = (page: Page) => {
this.page = page this.page = page
this.projectSection = this.page.getByTestId('home-section')
this.projectCard = this.page.getByTestId('project-link') this.projectCard = this.page.getByTestId('project-link')
this.projectCardTitle = this.page.getByTestId('project-title') this.projectCardTitle = this.page.getByTestId('project-title')
this.projectCardFile = this.page.getByTestId('project-file-count') this.projectCardFile = this.page.getByTestId('project-file-count')
this.projectCardFolder = this.page.getByTestId('project-folder-count') this.projectCardFolder = this.page.getByTestId('project-folder-count')
this.projectButtonNew = this.page.getByTestId('home-new-file')
this.projectTextName = this.page.getByTestId('cmd-bar-arg-value')
this.projectButtonContinue = this.page.getByRole('button', { name: 'Continue' })
this.sortByDateBtn = this.page.getByTestId('home-sort-by-modified') this.sortByDateBtn = this.page.getByTestId('home-sort-by-modified')
this.sortByNameBtn = this.page.getByTestId('home-sort-by-name') this.sortByNameBtn = this.page.getByTestId('home-sort-by-name')
} }
@ -91,10 +101,22 @@ export class HomePageFixture {
.toEqual(expectedState) .toEqual(expectedState)
} }
createAndGoToProject = async (projectTitle: string) => {
await expect(this.projectSection).not.toHaveText('Loading your Projects...')
await this.projectButtonNew.click()
await this.projectTextName.click()
await this.projectTextName.fill(projectTitle)
await this.projectButtonContinue.click()
}
openProject = async (projectTitle: string) => { openProject = async (projectTitle: string) => {
const projectCard = this.projectCard.locator( const projectCard = this.projectCard.locator(
this.page.getByText(projectTitle) this.page.getByText(projectTitle)
) )
await projectCard.click() await projectCard.click()
} }
goToModelingScene = async (name?: string = "testDefault") => {
await this.createAndGoToProject(name)
}
} }

View File

@ -0,0 +1,12 @@
// These tests are meant to simply test starting and stopping the electron
// application, check it can make it to the project pane, and nothing more.
// It also tests our test wrappers are working.
// Additionally this serves as a nice minimal example.
import { test as testZoo, expect } from './fixtures/fixtureSetup'
testZoo.describe('Open the application', () => {
testZoo('see the project view', async ({ tronApp: { page, context } }) => {
await expect(page.getByTestId('home-section')).toBeVisible()
})
})

View File

@ -1,5 +1,7 @@
import { test, expect, Page } from '@playwright/test' import { test, expect, Page } from './zoo-test'
import { test as test2, expect as expect2 } from './fixtures/fixtureSetup' import fs from 'node:fs/promises'
import path from 'node:path'
import { HomePageFixture } from './fixtures/homePageFixture'
import { import {
getMovementUtils, getMovementUtils,
@ -10,10 +12,6 @@ import {
} from './test-utils' } from './test-utils'
import { uuidv4, roundOff } from 'lib/utils' import { uuidv4, roundOff } from 'lib/utils'
test.beforeEach(async ({ context, page }, testInfo) => {
await setup(context, page, testInfo)
})
test.afterEach(async ({ page }, testInfo) => { test.afterEach(async ({ page }, testInfo) => {
await tearDown(page, testInfo) await tearDown(page, testInfo)
}) })
@ -22,6 +20,7 @@ test.describe('Sketch tests', () => {
test('multi-sketch file shows multiple Edit Sketch buttons', async ({ test('multi-sketch file shows multiple Edit Sketch buttons', async ({
page, page,
context, context,
homePage,
}) => { }) => {
const u = await getUtils(page) const u = await getUtils(page)
const selectionsSnippets = { const selectionsSnippets = {
@ -81,7 +80,7 @@ test.describe('Sketch tests', () => {
) )
await page.setViewportSize({ width: 1200, height: 500 }) await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart() await homePage.goToModelingScene()
// wait for execution done // wait for execution done
await u.openDebugPanel() await u.openDebugPanel()
@ -108,6 +107,8 @@ test.describe('Sketch tests', () => {
}) })
test('Can delete most of a sketch and the line tool will still work', async ({ test('Can delete most of a sketch and the line tool will still work', async ({
page, page,
center,
homePage,
}) => { }) => {
const u = await getUtils(page) const u = await getUtils(page)
await page.addInitScript(async () => { await page.addInitScript(async () => {
@ -120,12 +121,9 @@ test.describe('Sketch tests', () => {
) )
}) })
await page.setViewportSize({ width: 1200, height: 500 }) await homePage.goToModelingScene()
await u.waitForAuthSkipAppStart()
await expect(async () => { await expect(async () => {
await page.mouse.click(700, 200)
await page.getByText('tangentialArcTo([24.95, -5.38], %)').click() await page.getByText('tangentialArcTo([24.95, -5.38], %)').click()
await expect( await expect(
page.getByRole('button', { name: 'Edit Sketch' }) page.getByRole('button', { name: 'Edit Sketch' })
@ -151,6 +149,7 @@ test.describe('Sketch tests', () => {
await page.waitForTimeout(100) await page.waitForTimeout(100)
await expect(async () => { await expect(async () => {
await page.mouse.move(700, 200, { step: 25 })
await page.mouse.click(700, 200) await page.mouse.click(700, 200)
await expect.poll(u.normalisedEditorCode, { timeout: 1000 }) await expect.poll(u.normalisedEditorCode, { timeout: 1000 })
@ -161,7 +160,7 @@ test.describe('Sketch tests', () => {
`) `)
}).toPass({ timeout: 40_000, intervals: [1_000] }) }).toPass({ timeout: 40_000, intervals: [1_000] })
}) })
test('Can exit selection of face', async ({ page }) => { test('Can exit selection of face', async ({ page, homePage }) => {
// Load the app with the code panes // Load the app with the code panes
await page.addInitScript(async () => { await page.addInitScript(async () => {
localStorage.setItem('persistCode', ``) localStorage.setItem('persistCode', ``)
@ -170,7 +169,7 @@ test.describe('Sketch tests', () => {
const u = await getUtils(page) const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 }) await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart() await homePage.goToModelingScene()
await page.getByRole('button', { name: 'Start Sketch' }).click() await page.getByRole('button', { name: 'Start Sketch' }).click()
await expect( await expect(
@ -187,6 +186,7 @@ test.describe('Sketch tests', () => {
test.describe('Can edit segments by dragging their handles', () => { test.describe('Can edit segments by dragging their handles', () => {
const doEditSegmentsByDraggingHandle = async ( const doEditSegmentsByDraggingHandle = async (
page: Page, page: Page,
homePage: HomePageFixture,
openPanes: string[] openPanes: string[]
) => { ) => {
// Load the app with the code panes // Load the app with the code panes
@ -202,10 +202,9 @@ test.describe('Sketch tests', () => {
}) })
const u = await getUtils(page) const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 }) await homePage.goToModelingScene()
await u.waitForAuthSkipAppStart() await expect(
await expect(
page.getByRole('button', { name: 'Start Sketch' }) page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled() ).not.toBeDisabled()
@ -318,7 +317,7 @@ test.describe('Sketch tests', () => {
|> line([1.97, 2.06], %) |> line([1.97, 2.06], %)
|> close(%)`) |> close(%)`)
} }
test('code pane open at start-handles', async ({ page }) => { test('code pane open at start-handles', async ({ page, homePage }) => {
// Load the app with the code panes // Load the app with the code panes
await page.addInitScript(async () => { await page.addInitScript(async () => {
localStorage.setItem( localStorage.setItem(
@ -331,10 +330,10 @@ test.describe('Sketch tests', () => {
}) })
) )
}) })
await doEditSegmentsByDraggingHandle(page, ['code']) await doEditSegmentsByDraggingHandle(page, homePage, ['code'])
}) })
test('code pane closed at start-handles', async ({ page }) => { test('code pane closed at start-handles', async ({ page, homePage }) => {
// Load the app with the code panes // Load the app with the code panes
await page.addInitScript(async (persistModelingContext) => { await page.addInitScript(async (persistModelingContext) => {
localStorage.setItem( localStorage.setItem(
@ -342,12 +341,13 @@ test.describe('Sketch tests', () => {
JSON.stringify({ openPanes: [] }) JSON.stringify({ openPanes: [] })
) )
}, PERSIST_MODELING_CONTEXT) }, PERSIST_MODELING_CONTEXT)
await doEditSegmentsByDraggingHandle(page, []) await doEditSegmentsByDraggingHandle(page, homePage, [])
}) })
}) })
test('Can edit a circle center and radius by dragging its handles', async ({ test('Can edit a circle center and radius by dragging its handles', async ({
page, page,
homePage,
}) => { }) => {
const u = await getUtils(page) const u = await getUtils(page)
await page.addInitScript(async () => { await page.addInitScript(async () => {
@ -358,9 +358,8 @@ test.describe('Sketch tests', () => {
) )
}) })
await page.setViewportSize({ width: 1200, height: 500 }) await homePage.goToModelingScene()
await u.waitForAuthSkipAppStart()
await expect( await expect(
page.getByRole('button', { name: 'Start Sketch' }) page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled() ).not.toBeDisabled()
@ -434,6 +433,7 @@ test.describe('Sketch tests', () => {
}) })
test('Can edit a sketch that has been extruded in the same pipe', async ({ test('Can edit a sketch that has been extruded in the same pipe', async ({
page, page,
homePage
}) => { }) => {
const u = await getUtils(page) const u = await getUtils(page)
await page.addInitScript(async () => { await page.addInitScript(async () => {
@ -448,9 +448,8 @@ test.describe('Sketch tests', () => {
) )
}) })
await page.setViewportSize({ width: 1200, height: 500 }) await homePage.goToModelingScene()
await u.waitForAuthSkipAppStart()
await expect( await expect(
page.getByRole('button', { name: 'Start Sketch' }) page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled() ).not.toBeDisabled()
@ -504,11 +503,11 @@ test.describe('Sketch tests', () => {
await page.waitForTimeout(100) await page.waitForTimeout(100)
const lineEnd = await u.getBoundingBox('[data-overlay-index="0"]') const lineEnd = await u.getBoundingBox('[data-overlay-index="0"]')
await page.waitForTimeout(100)
await page.dragAndDrop('#stream', '#stream', { await page.dragAndDrop('#stream', '#stream', {
sourcePosition: { x: lineEnd.x - 5, y: lineEnd.y }, sourcePosition: { x: lineEnd.x - 15, y: lineEnd.y },
targetPosition: { x: lineEnd.x + dragPX, y: lineEnd.y + dragPX }, targetPosition: { x: lineEnd.x, y: lineEnd.y + 15 },
}) })
await page.waitForTimeout(100)
await expect(page.locator('.cm-content')).not.toHaveText(prevContent) await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
prevContent = await page.locator('.cm-content').innerText() prevContent = await page.locator('.cm-content').innerText()
@ -517,8 +516,8 @@ test.describe('Sketch tests', () => {
await page.dragAndDrop('#stream', '#stream', { await page.dragAndDrop('#stream', '#stream', {
sourcePosition: { x: tangentEnd.x + 10, y: tangentEnd.y - 5 }, sourcePosition: { x: tangentEnd.x + 10, y: tangentEnd.y - 5 },
targetPosition: { targetPosition: {
x: tangentEnd.x + dragPX, x: tangentEnd.x,
y: tangentEnd.y + dragPX, y: tangentEnd.y - 15,
}, },
}) })
await page.waitForTimeout(100) await page.waitForTimeout(100)
@ -528,8 +527,8 @@ test.describe('Sketch tests', () => {
await expect(page.locator('.cm-content')) await expect(page.locator('.cm-content'))
.toHaveText(`sketch001 = startSketchOn('XZ') .toHaveText(`sketch001 = startSketchOn('XZ')
|> startProfileAt([7.12, -12.68], %) |> startProfileAt([7.12, -12.68], %)
|> line([15.39, -2.78], %) |> line([12.68, -1.09], %)
|> tangentialArcTo([27.6, -3.05], %) |> tangentialArcTo([24.89, 0.68], %)
|> close(%) |> close(%)
|> extrude(5, %) |> extrude(5, %)
`) `)
@ -537,6 +536,7 @@ test.describe('Sketch tests', () => {
test('Can edit a sketch that has been revolved in the same pipe', async ({ test('Can edit a sketch that has been revolved in the same pipe', async ({
page, page,
homePage,
}) => { }) => {
const u = await getUtils(page) const u = await getUtils(page)
await page.addInitScript(async () => { await page.addInitScript(async () => {
@ -551,9 +551,8 @@ test.describe('Sketch tests', () => {
) )
}) })
await page.setViewportSize({ width: 1200, height: 500 }) await homePage.goToModelingScene()
await u.waitForAuthSkipAppStart()
await expect( await expect(
page.getByRole('button', { name: 'Start Sketch' }) page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled() ).not.toBeDisabled()
@ -636,12 +635,13 @@ test.describe('Sketch tests', () => {
|> close(%) |> close(%)
|> revolve({ axis = "X" }, %)`) |> revolve({ axis = "X" }, %)`)
}) })
test('Can add multiple sketches', async ({ page }) => { test('Can add multiple sketches', async ({ page, homePage }) => {
const u = await getUtils(page) const u = await getUtils(page)
const viewportSize = { width: 1200, height: 500 } const viewportSize = { width: 1200, height: 500 }
await page.setViewportSize(viewportSize) await page.setViewportSize(viewportSize)
await u.waitForAuthSkipAppStart() await homePage.goToModelingScene()
await u.openDebugPanel() await u.openDebugPanel()
const center = { x: viewportSize.width / 2, y: viewportSize.height / 2 } const center = { x: viewportSize.width / 2, y: viewportSize.height / 2 }
@ -738,8 +738,7 @@ test.describe('Sketch tests', () => {
const u = await getUtils(page) const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 }) await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart() await u.openDebugPanel()
await u.openDebugPanel()
const code = `sketch001 = startSketchOn('-XZ') const code = `sketch001 = startSketchOn('-XZ')
|> startProfileAt([${roundOff(scale * 69.6)}, ${roundOff(scale * 34.8)}], %) |> startProfileAt([${roundOff(scale * 69.6)}, ${roundOff(scale * 34.8)}], %)
@ -820,16 +819,19 @@ test.describe('Sketch tests', () => {
await u.expectCmdLog('[data-message-type="execution-done"]') await u.expectCmdLog('[data-message-type="execution-done"]')
await u.removeCurrentCode() await u.removeCurrentCode()
} }
test('[0, 100, 100]', async ({ page }) => { test('[0, 100, 100]', async ({ page, homePage }) => {
await homePage.goToModelingScene()
await doSnapAtDifferentScales(page, [0, 100, 100], 0.01) await doSnapAtDifferentScales(page, [0, 100, 100], 0.01)
}) })
test('[0, 10000, 10000]', async ({ page }) => { test('[0, 10000, 10000]', async ({ page, homePage }) => {
await homePage.goToModelingScene()
await doSnapAtDifferentScales(page, [0, 10000, 10000]) await doSnapAtDifferentScales(page, [0, 10000, 10000])
}) })
}) })
test('exiting a close extrude, has the extrude button enabled ready to go', async ({ test('exiting a close extrude, has the extrude button enabled ready to go', async ({
page, page,
homePage,
}) => { }) => {
// this was a regression https://github.com/KittyCAD/modeling-app/issues/2832 // this was a regression https://github.com/KittyCAD/modeling-app/issues/2832
await page.addInitScript(async () => { await page.addInitScript(async () => {
@ -849,7 +851,7 @@ test.describe('Sketch tests', () => {
const u = await getUtils(page) const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 }) await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart() await homePage.goToModelingScene()
// wait for execution done // wait for execution done
await u.openDebugPanel() await u.openDebugPanel()
@ -885,7 +887,7 @@ test.describe('Sketch tests', () => {
timeout: 10_000, timeout: 10_000,
}) })
}) })
test("Existing sketch with bad code delete user's code", async ({ page }) => { test("Existing sketch with bad code delete user's code", async ({ page, homePage }) => {
// this was a regression https://github.com/KittyCAD/modeling-app/issues/2832 // this was a regression https://github.com/KittyCAD/modeling-app/issues/2832
await page.addInitScript(async () => { await page.addInitScript(async () => {
localStorage.setItem( localStorage.setItem(
@ -905,7 +907,7 @@ extrude001 = extrude(5, sketch001)
const u = await getUtils(page) const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 }) await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart() await homePage.goToModelingScene()
await u.openDebugPanel() await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]') await u.expectCmdLog('[data-message-type="execution-done"]')
@ -1049,12 +1051,8 @@ sketch002 = startSketchOn(extrude001, 'END')
test('empty-scene default-planes act as expected', async ({ test('empty-scene default-planes act as expected', async ({
page, page,
browserName, homePage,
}) => { }) => {
test.skip(
browserName === 'webkit',
'Skip on Safari until `window.tearDown` is working there'
)
/** /**
* Tests the following things * Tests the following things
* 1) The the planes are there on load because the scene is empty * 1) The the planes are there on load because the scene is empty
@ -1067,9 +1065,7 @@ sketch002 = startSketchOn(extrude001, 'END')
*/ */
const u = await getUtils(page) const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 }) await homePage.goToModelingScene()
await u.waitForAuthSkipAppStart()
await u.openDebugPanel() await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]') await u.expectCmdLog('[data-message-type="execution-done"]')
@ -1117,7 +1113,7 @@ sketch002 = startSketchOn(extrude001, 'END')
// click start Sketch // click start Sketch
await page.getByRole('button', { name: 'Start Sketch' }).click() await page.getByRole('button', { name: 'Start Sketch' }).click()
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y, { steps: 5 }) await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y, { steps: 50 })
const hoveredColor: [number, number, number] = [93, 93, 127] const hoveredColor: [number, number, number] = [93, 93, 127]
// now that we're expecting the user to select a plan, it does respond to hover // now that we're expecting the user to select a plan, it does respond to hover
await expect await expect
@ -1144,8 +1140,6 @@ sketch002 = startSketchOn(extrude001, 'END')
` `
) )
}) })
await page.reload()
await u.waitForAuthSkipAppStart()
await u.openDebugPanel() await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]') await u.expectCmdLog('[data-message-type="execution-done"]')
@ -1159,12 +1153,8 @@ sketch002 = startSketchOn(extrude001, 'END')
test('Can attempt to sketch on revolved face', async ({ test('Can attempt to sketch on revolved face', async ({
page, page,
browserName, homePage,
}) => { }) => {
test.skip(
browserName === 'webkit',
'Skip on Safari until `window.tearDown` is working there'
)
const u = await getUtils(page) const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 }) await page.setViewportSize({ width: 1200, height: 500 })
@ -1191,7 +1181,7 @@ sketch002 = startSketchOn(extrude001, 'END')
) )
}) })
await u.waitForAuthSkipAppStart() await homePage.goToModelingScene()
await u.openDebugPanel() await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]') await u.expectCmdLog('[data-message-type="execution-done"]')
@ -1223,6 +1213,7 @@ sketch002 = startSketchOn(extrude001, 'END')
test('Can sketch on face when user defined function was used in the sketch', async ({ test('Can sketch on face when user defined function was used in the sketch', async ({
page, page,
homePage,
}) => { }) => {
const u = await getUtils(page) const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 }) await page.setViewportSize({ width: 1200, height: 500 })
@ -1279,7 +1270,7 @@ const rail = startSketchOn('XZ')
const center = { x: 600, y: 250 } const center = { x: 600, y: 250 }
const rectangleSize = 20 const rectangleSize = 20
await u.waitForAuthSkipAppStart() await homePage.goToModelingScene()
// Start a sketch // Start a sketch
await page.getByRole('button', { name: 'Start Sketch' }).click() await page.getByRole('button', { name: 'Start Sketch' }).click()
@ -1318,27 +1309,26 @@ const rail = startSketchOn('XZ')
}) })
}) })
test2.describe('Sketch mode should be toleratant to syntax errors', () => { test.describe('Sketch mode should be toleratant to syntax errors', () => {
test2( test(
'adding a syntax error, recovers after fixing', 'adding a syntax error, recovers after fixing',
{ tag: ['@skipWin'] }, { tag: ['@skipWin'] },
async ({ app, scene, editor, toolbar }) => { async ({ page, homePage, context, scene, editor, toolbar }) => {
test.skip( const file = await fs.readFile(path.resolve(__dirname, '../../', './src/wasm-lib/tests/executor/inputs/e2e-can-sketch-on-chamfer.kcl'), 'utf-8')
process.platform === 'win32', await context.addInitScript((file) => {
'a codemirror error appears in this test only on windows, that causes the test to fail only because of our "no new error" logic, but it can not be replicated locally' localStorage.setItem('persistCode', file)
) }, file)
const file = await app.getInputFile('e2e-can-sketch-on-chamfer.kcl') await homePage.goToModelingScene()
await app.initialise(file)
const [objClick] = scene.makeMouseHelpers(600, 250) const [objClick] = scene.makeMouseHelpers(600, 250)
const arrowHeadLocation = { x: 604, y: 129 } as const const arrowHeadLocation = { x: 706, y: 129 } as const
const arrowHeadWhite: [number, number, number] = [255, 255, 255] const arrowHeadWhite: [number, number, number] = [255, 255, 255]
const backgroundGray: [number, number, number] = [28, 28, 28] const backgroundGray: [number, number, number] = [28, 28, 28]
const verifyArrowHeadColor = async (c: [number, number, number]) => const verifyArrowHeadColor = async (c: [number, number, number]) =>
scene.expectPixelColor(c, arrowHeadLocation, 15) scene.expectPixelColor(c, arrowHeadLocation, 15)
await test.step('check chamfer selection changes cursor positon', async () => { await test.step('check chamfer selection changes cursor positon', async () => {
await expect2(async () => { await expect(async () => {
// sometimes initial click doesn't register // sometimes initial click doesn't register
await objClick() await objClick()
await editor.expectActiveLinesToBe([ await editor.expectActiveLinesToBe([
@ -1374,7 +1364,7 @@ test2.describe('Sketch mode should be toleratant to syntax errors', () => {
// this checks sketch segments have been drawn // this checks sketch segments have been drawn
await verifyArrowHeadColor(arrowHeadWhite) await verifyArrowHeadColor(arrowHeadWhite)
}) })
await app.page.waitForTimeout(100) await page.waitForTimeout(100)
} }
) )
}) })

View File

@ -798,6 +798,8 @@ export async function tearDown(page: Page, testInfo: TestInfo) {
// It seems it's best to give the browser about 3s to close things // It seems it's best to give the browser about 3s to close things
// It's not super reliable but we have no real other choice for now // It's not super reliable but we have no real other choice for now
await page.waitForTimeout(3000) await page.waitForTimeout(3000)
await testInfo.tronApp.close()
} }
// settingsOverrides may need to be augmented to take more generic items, // settingsOverrides may need to be augmented to take more generic items,
@ -887,8 +889,10 @@ export async function setupElectron({
? { executablePath: process.env.ELECTRON_OVERRIDE_DIST_PATH + 'electron' } ? { executablePath: process.env.ELECTRON_OVERRIDE_DIST_PATH + 'electron' }
: {}), : {}),
}) })
const context = electronApp.context() const context = electronApp.context()
const page = await electronApp.firstWindow() const page = await electronApp.firstWindow()
context.on('console', console.log) context.on('console', console.log)
page.on('console', console.log) page.on('console', console.log)
@ -924,7 +928,7 @@ export async function setupElectron({
await setup(context, page) await setup(context, page)
return { electronApp, page, dir: projectDirName } return { electronApp, page, context, dir: projectDirName }
} }
function failOnConsoleErrors(page: Page, testInfo?: TestInfo) { function failOnConsoleErrors(page: Page, testInfo?: TestInfo) {

View File

@ -0,0 +1,54 @@
import { test as playwrightTestFn } from '@playwright/test'
import { fixtures, Fixtures, AuthenticatedTronApp } from './fixtures/fixtureSetup'
export { expect, Page, BrowserContext, TestInfo } from '@playwright/test'
// Our custom decorated Zoo test object. Makes it easier to add fixtures, and
// switch between web and electron if needed.
const pwTestFnWithFixtures = playwrightTestFn.extend<Fixtures>(fixtures)
export function test(desc, objOrFn, fnMaybe) {
const hasTestConf = typeof objOrFn === 'object'
const fn = hasTestConf ? fnMaybe : objOrFn
return pwTestFnWithFixtures(desc, hasTestConf ? objOrFn : {}, async ({ page, context, cmdBar, editor, toolbar, scene, homePage }, testInfo) => {
// To switch to web, change this to AuthenticatedApp from fixtureSetup.ts
const tronApp = new AuthenticatedTronApp(
context,
page,
testInfo
)
const fixtures: Fixtures = { cmdBar, editor, toolbar, scene, homePage }
await tronApp.initialise({ fixtures })
// We need to patch this because addInitScript will bind too late in our
// electron tests, never running. We need to call reload() after each call
// to guarantee it runs.
const oldContextAddInitScript = tronApp.context.addInitScript
tronApp.context.addInitScript = async function(a, b) {
await oldContextAddInitScript.apply(this, [a, b])
await tronApp.page.reload()
}
// No idea why we mix and match page and context's addInitScript but we do
const oldPageAddInitScript = tronApp.page.addInitScript
tronApp.page.addInitScript = async function(a, b) {
await oldPageAddInitScript.apply(this, [a, b])
await tronApp.page.reload()
}
await fn({
context: tronApp.context,
page: tronApp.page,
...fixtures
}, testInfo)
testInfo.tronApp = tronApp
})
}
test.describe = pwTestFnWithFixtures.describe
test.beforeEach = pwTestFnWithFixtures.beforeEach
test.afterEach = pwTestFnWithFixtures.afterEach
test.step = pwTestFnWithFixtures.step
test.skip = pwTestFnWithFixtures.skip

View File

@ -103,7 +103,9 @@
"generate:machine-api": "npx openapi-typescript ./openapi/machine-api.json -o src/lib/machine-api.d.ts", "generate:machine-api": "npx openapi-typescript ./openapi/machine-api.json -o src/lib/machine-api.d.ts",
"tron:start": "electron-forge start", "tron:start": "electron-forge start",
"tron:package": "electron-forge package", "tron:package": "electron-forge package",
"tron:test": "NODE_ENV=development yarn playwright test --config=playwright.electron.config.ts --grep=@electron", "tron:make": "electron-forge make",
"tron:publish": "electron-forge publish",
"tron:test": "NODE_ENV=development yarn playwright test --config=playwright.electron.config.ts --grep-invert='@snapshot'",
"tronb:vite": "vite build -c vite.main.config.ts && vite build -c vite.preload.config.ts && vite build -c vite.renderer.config.ts", "tronb:vite": "vite build -c vite.main.config.ts && vite build -c vite.preload.config.ts && vite build -c vite.renderer.config.ts",
"tronb:package": "electron-builder --config electron-builder.yml", "tronb:package": "electron-builder --config electron-builder.yml",
"test-setup": "yarn install && yarn build:wasm", "test-setup": "yarn install && yarn build:wasm",

View File

@ -57,6 +57,10 @@ export default class CodeManager {
return this._code return this._code
} }
localStoragePersistCode(): string {
return safeLSGetItem(PERSIST_CODE_KEY)
}
registerCallBacks({ setCode }: { setCode: (arg: string) => void }) { registerCallBacks({ setCode }: { setCode: (arg: string) => void }) {
this.#updateState = setCode this.#updateState = setCode
} }

View File

@ -87,12 +87,13 @@ export const fileLoader: LoaderFunction = async (
) )
const isBrowserProject = params.id === decodeURIComponent(BROWSER_PATH) const isBrowserProject = params.id === decodeURIComponent(BROWSER_PATH)
let code = ''
if (!isBrowserProject && projectPathData) { if (!isBrowserProject && projectPathData) {
const { projectName, projectPath, currentFileName, currentFilePath } = const { projectName, projectPath, currentFileName, currentFilePath } =
projectPathData projectPathData
const urlObj = new URL(routerData.request.url) const urlObj = new URL(routerData.request.url)
let code = ''
if (!urlObj.pathname.endsWith('/settings')) { if (!urlObj.pathname.endsWith('/settings')) {
const fallbackFile = isDesktop() const fallbackFile = isDesktop()
@ -122,6 +123,10 @@ export const fileLoader: LoaderFunction = async (
}) })
code = normalizeLineEndings(code) code = normalizeLineEndings(code)
// If persistCode in localStorage is present, it'll persist that code
// through *anything*. INTENDED FOR TESTS.
code = codeManager.localStoragePersistCode() || code
// Update both the state and the editor's code. // Update both the state and the editor's code.
// We explicitly do not write to the file here since we are loading from // We explicitly do not write to the file here since we are loading from
// the file system and not the editor. // the file system and not the editor.
@ -149,12 +154,6 @@ export const fileLoader: LoaderFunction = async (
? await getProjectInfo(projectPath) ? await getProjectInfo(projectPath)
: null : null
console.log('maybeProjectInfo', {
maybeProjectInfo,
defaultProjectData,
projectPathData,
})
const projectData: IndexLoaderData = { const projectData: IndexLoaderData = {
code, code,
project: maybeProjectInfo ?? defaultProjectData, project: maybeProjectInfo ?? defaultProjectData,
@ -171,7 +170,7 @@ export const fileLoader: LoaderFunction = async (
} }
return { return {
code: '', code,
project: { project: {
name: BROWSER_PROJECT_NAME, name: BROWSER_PROJECT_NAME,
path: '/' + BROWSER_PROJECT_NAME, path: '/' + BROWSER_PROJECT_NAME,