Reuse electron window; difficult task

This commit is contained in:
49lf
2024-12-11 12:25:03 -05:00
parent f9bb026580
commit 764a0e8f88
16 changed files with 387 additions and 238 deletions

View File

@ -120,11 +120,9 @@ test.describe('Code pane and errors', () => {
await expect(page.locator('.cm-tooltip').first()).toBeVisible() await expect(page.locator('.cm-tooltip').first()).toBeVisible()
}) })
test.fixme('When error is not in view you can click the badge to scroll to it', async ({ test.fixme(
page, 'When error is not in view you can click the badge to scroll to it',
homePage, async ({ page, homePage, context }) => {
context,
}) => {
// Load the app with the working starter code // Load the app with the working starter code
await context.addInitScript((code) => { await context.addInitScript((code) => {
localStorage.setItem('persistCode', code) localStorage.setItem('persistCode', code)
@ -159,7 +157,8 @@ test.describe('Code pane and errors', () => {
) )
.first() .first()
).toBeVisible() ).toBeVisible()
}) }
)
test('When error is not in view WITH LINTS you can click the badge to scroll to it', async ({ test('When error is not in view WITH LINTS you can click the badge to scroll to it', async ({
context, context,

View File

@ -568,10 +568,9 @@ test.describe('Editor tests', () => {
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible() await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
}) })
test.fixme('error with 2 source ranges gets 2 diagnostics', async ({ test.fixme(
page, 'error with 2 source ranges gets 2 diagnostics',
homePage, async ({ page, homePage }) => {
}) => {
const u = await getUtils(page) const u = await getUtils(page)
await page.addInitScript(async () => { await page.addInitScript(async () => {
localStorage.setItem( localStorage.setItem(
@ -636,7 +635,8 @@ test.describe('Editor tests', () => {
// Make sure there are two diagnostics // Make sure there are two diagnostics
await expect(page.locator('.cm-lint-marker-error')).toHaveCount(2) await expect(page.locator('.cm-lint-marker-error')).toHaveCount(2)
}) }
)
test('if your kcl gets an error from the engine it is inlined', async ({ test('if your kcl gets an error from the engine it is inlined', async ({
context, context,
page, page,

View File

@ -29,7 +29,7 @@ export class EditorFixture {
reConstruct = (page: Page) => { reConstruct = (page: Page) => {
this.page = page this.page = page
this.codeContent = page.locator('.cm-content') this.codeContent = page.locator('.cm-content[data-language="kcl"]')
this.diagnosticsTooltip = page.locator('.cm-tooltip-lint') this.diagnosticsTooltip = page.locator('.cm-tooltip-lint')
this.diagnosticsGutterIcon = page.locator('.cm-lint-marker-error') this.diagnosticsGutterIcon = page.locator('.cm-lint-marker-error')
this.activeLine = this.page.locator('.cm-activeLine') this.activeLine = this.page.locator('.cm-activeLine')

View File

@ -79,7 +79,7 @@ export class AuthenticatedTronApp {
appSettings?: Partial<SaveSettingsPayload> appSettings?: Partial<SaveSettingsPayload>
} = { fixtures: {} } } = { fixtures: {} }
) { ) {
const { electronApp, page, context, dir } = await setupElectron({ const { electronApp, page, context, dir, options } = await setupElectron({
testInfo: this.testInfo, testInfo: this.testInfo,
folderSetupFn: arg.folderSetupFn, folderSetupFn: arg.folderSetupFn,
cleanProjectDir: arg.cleanProjectDir, cleanProjectDir: arg.cleanProjectDir,
@ -96,8 +96,6 @@ export class AuthenticatedTronApp {
// Setup localStorage, addCookies, reload // Setup localStorage, addCookies, reload
await setup(this.context, this.page, this.testInfo) 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]
if ( if (

View File

@ -851,7 +851,9 @@ const shellPointAndClickCapCases = [
] ]
shellPointAndClickCapCases.forEach(({ shouldPreselect }) => { shellPointAndClickCapCases.forEach(({ shouldPreselect }) => {
test(`Shell point-and-click cap (preselected sketches: ${shouldPreselect})`, async ({ test(`Shell point-and-click cap (preselected sketches: ${shouldPreselect})`, async ({
app, context,
page,
homePage,
scene, scene,
editor, editor,
toolbar, toolbar,
@ -861,7 +863,11 @@ shellPointAndClickCapCases.forEach(({ shouldPreselect }) => {
|> circle({ center = [0, 0], radius = 30 }, %) |> circle({ center = [0, 0], radius = 30 }, %)
extrude001 = extrude(30, sketch001) extrude001 = extrude(30, sketch001)
` `
await app.initialise(initialCode) await context.addInitScript((initialCode) => {
localStorage.setItem('persistCode', initialCode)
}, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
// One dumb hardcoded screen pixel value // One dumb hardcoded screen pixel value
const testPoint = { x: 575, y: 200 } const testPoint = { x: 575, y: 200 }
@ -888,7 +894,7 @@ shellPointAndClickCapCases.forEach(({ shouldPreselect }) => {
commandName: 'Shell', commandName: 'Shell',
}) })
await clickOnCap() await clickOnCap()
await app.page.waitForTimeout(500) await page.waitForTimeout(500)
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await cmdBar.expectState({ await cmdBar.expectState({
@ -904,7 +910,7 @@ shellPointAndClickCapCases.forEach(({ shouldPreselect }) => {
} else { } else {
await test.step(`Preselect the cap`, async () => { await test.step(`Preselect the cap`, async () => {
await clickOnCap() await clickOnCap()
await app.page.waitForTimeout(500) await page.waitForTimeout(500)
}) })
await test.step(`Go through the command bar flow with a preselected face (cap)`, async () => { await test.step(`Go through the command bar flow with a preselected face (cap)`, async () => {
@ -936,8 +942,9 @@ shellPointAndClickCapCases.forEach(({ shouldPreselect }) => {
}) })
test('Shell point-and-click wall', async ({ test('Shell point-and-click wall', async ({
app, context,
page, page,
homePage,
scene, scene,
editor, editor,
toolbar, toolbar,
@ -952,7 +959,11 @@ test('Shell point-and-click wall', async ({
|> close(%) |> close(%)
extrude001 = extrude(40, sketch001) extrude001 = extrude(40, sketch001)
` `
await app.initialise(initialCode) await context.addInitScript((initialCode) => {
localStorage.setItem('persistCode', initialCode)
}, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
// One dumb hardcoded screen pixel value // One dumb hardcoded screen pixel value
const testPoint = { x: 580, y: 180 } const testPoint = { x: 580, y: 180 }
@ -983,7 +994,7 @@ extrude001 = extrude(40, sketch001)
await clickOnCap() await clickOnCap()
await page.keyboard.down('Shift') await page.keyboard.down('Shift')
await clickOnWall() await clickOnWall()
await app.page.waitForTimeout(500) await page.waitForTimeout(500)
await page.keyboard.up('Shift') await page.keyboard.up('Shift')
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()

View File

@ -115,21 +115,21 @@ 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', 'yyyyyyyyy 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 }, testInfo) => { async ({ context, page }, testInfo) => {
await context.folderSetupFn(async (dir) => { await context.folderSetupFn(async (dir) => {
const bracketDir = join(dir, 'bracket') const bracketDir = path.join(dir, 'bracket')
await fsp.mkdir(bracketDir, { recursive: true }) await fsp.mkdir(bracketDir, { recursive: true })
await fsp.copyFile( await fsp.copyFile(
executorInputPath('focusrite_scarlett_mounting_braket.kcl'), executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
join(bracketDir, 'main.kcl') path.join(bracketDir, 'main.kcl')
) )
const errorDir = join(dir, 'broken-code') const errorDir = path.join(dir, 'broken-code')
await fsp.mkdir(errorDir, { recursive: true }) await fsp.mkdir(errorDir, { recursive: true })
await fsp.copyFile( await fsp.copyFile(
executorInputPath('broken-code-test.kcl'), executorInputPath('broken-code-test.kcl'),
join(errorDir, 'main.kcl') path.join(errorDir, 'main.kcl')
) )
}) })
@ -199,19 +199,19 @@ 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', 'aaayyyyyyyy 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 ({ context, page }, testInfo) => {
await context.folderSetupFn(async (dir) => { await context.folderSetupFn(async (dir) => {
const bracketDir = join(dir, 'bracket') const bracketDir = path.join(dir, 'bracket')
await fsp.mkdir(bracketDir, { recursive: true }) await fsp.mkdir(bracketDir, { recursive: true })
await fsp.copyFile( await fsp.copyFile(
executorInputPath('focusrite_scarlett_mounting_braket.kcl'), executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
join(bracketDir, 'main.kcl') path.join(bracketDir, 'main.kcl')
) )
const emptyDir = join(dir, 'empty') const emptyDir = path.join(dir, 'empty')
await fsp.mkdir(emptyDir, { recursive: true }) await fsp.mkdir(emptyDir, { recursive: true })
await fsp.writeFile(join(emptyDir, 'main.kcl'), '') await fsp.writeFile(path.join(emptyDir, 'main.kcl'), '')
}) })
await page.setBodyDimensions({ width: 1200, height: 500 }) await page.setBodyDimensions({ width: 1200, height: 500 })
@ -276,18 +276,18 @@ test(
) )
test( test(
'open a file in a project works and renders, open empty file, it should clear the scene', 'nooooooooooooo open a file in a project works and renders, open empty file, it should clear the scene',
{ tag: '@electron' }, { tag: '@electron' },
async ({ browserName }, testInfo) => { async ({ context, page }, testInfo) => {
await context.folderSetupFn(async (dir) => { await context.folderSetupFn(async (dir) => {
const bracketDir = join(dir, 'bracket') const bracketDir = path.join(dir, 'bracket')
await fsp.mkdir(bracketDir, { recursive: true }) await fsp.mkdir(bracketDir, { recursive: true })
await fsp.copyFile( await fsp.copyFile(
executorInputPath('focusrite_scarlett_mounting_braket.kcl'), executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
join(bracketDir, 'main.kcl') path.join(bracketDir, 'main.kcl')
) )
await fsp.writeFile(join(bracketDir, 'empty.kcl'), '') await fsp.writeFile(path.join(bracketDir, 'empty.kcl'), '')
}) })
await page.setBodyDimensions({ width: 1200, height: 500 }) await page.setBodyDimensions({ width: 1200, height: 500 })
@ -297,17 +297,13 @@ test(
const pointOnModel = { x: 630, y: 280 } const pointOnModel = { x: 630, y: 280 }
await test.step('Opening the bracket project should load the stream', async () => { await test.step('Opening the bracket project should load the stream', async () => {
// 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 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' })
).toBeEnabled({ ).toBeEnabled({
@ -347,27 +343,25 @@ 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', 'xxxxx 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 ({ browserName }, testInfo) => { async ({ context, page }, testInfo) => {
await context.folderSetupFn(async (dir) => { const { dir } = await context.folderSetupFn(async (dir) => {
const bracketDir = join(dir, 'bracket') const bracketDir = path.join(dir, 'bracket')
await fsp.mkdir(bracketDir, { recursive: true }) await fsp.mkdir(bracketDir, { recursive: true })
await fsp.copyFile( await fsp.copyFile(
executorInputPath('focusrite_scarlett_mounting_braket.kcl'), executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
join(bracketDir, 'main.kcl') path.join(bracketDir, 'main.kcl')
) )
await fsp.copyFile( await fsp.copyFile(
executorInputPath('broken-code-test.kcl'), executorInputPath('broken-code-test.kcl'),
join(bracketDir, 'broken-code-test.kcl') path.join(bracketDir, 'broken-code-test.kcl')
) )
}) })
await page.setBodyDimensions({ width: 1200, height: 500 }) await page.setBodyDimensions({ width: 1200, height: 500 })
const u = await getUtils(page) const u = await getUtils(page)
page.on('console', console.log)
const pointOnModel = { x: 630, y: 280 } const pointOnModel = { x: 630, y: 280 }
await test.step('Opening the bracket project should load the stream', async () => { await test.step('Opening the bracket project should load the stream', async () => {
@ -399,7 +393,7 @@ test(
// open the file pane. // open the file pane.
await page.getByTestId('files-pane-button').click() await page.getByTestId('files-pane-button').click()
// OPen the other file. // Open the other file.
const file = page.getByRole('button', { name: 'broken-code-test.kcl' }) const file = page.getByRole('button', { name: 'broken-code-test.kcl' })
await expect(file).toBeVisible() await expect(file).toBeVisible()
@ -997,7 +991,7 @@ test.describe(`Project management commands`, () => {
test( test(
'File in the file pane should open with a single click', 'File in the file pane should open with a single click',
{ tag: '@electron' }, { tag: '@electron' },
async ({ context, page }, testInfo) => { async ({ context, homePage, page }, testInfo) => {
const projectName = 'router-template-slate' const projectName = 'router-template-slate'
await context.folderSetupFn(async (dir) => { await context.folderSetupFn(async (dir) => {
await fsp.mkdir(`${dir}/${projectName}`, { recursive: true }) await fsp.mkdir(`${dir}/${projectName}`, { recursive: true })
@ -1010,16 +1004,14 @@ test(
`${dir}/${projectName}/otherThingToClickOn.kcl` `${dir}/${projectName}/otherThingToClickOn.kcl`
) )
}) })
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) page.on('console', console.log)
await page.getByText(projectName).click() await page.getByText(projectName).click()
await expect(page.getByTestId('loading')).toBeAttached() await u.waitForPageLoad()
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')

View File

@ -47,7 +47,6 @@ test.beforeEach(async ({ page }) => {
test.setTimeout(60_000) test.setTimeout(60_000)
// We test this end to end already - getting this to work on web just to take // We test this end to end already - getting this to work on web just to take
// a snapshot of it feels weird. I'd rather our regular tests fail. // a snapshot of it feels weird. I'd rather our regular tests fail.
// The primary failure is doExport now relies on the filesystem. We can follow // The primary failure is doExport now relies on the filesystem. We can follow

View File

@ -28,6 +28,107 @@ import { isErrorWhitelisted } from './lib/console-error-whitelist'
import { isArray } from 'lib/utils' import { isArray } from 'lib/utils'
import { reportRejection } from 'lib/trap' import { reportRejection } from 'lib/trap'
// The below is copied from playwright-core because it exports none of them :(
import { Env, BrowserContextOptions } from 'playwright-core'
import type * as channels from '@protocol/channels';
// Copied from playwright-core
function envObjectToArray(env: Env): { name: string, value: string }[] {
const result: { name: string, value: string }[] = [];
for (const name in env) {
if (!Object.is(env[name], undefined))
result.push({ name, value: String(env[name]) });
}
return result;
}
// Copied from playwright-core
export async function toClientCertificatesProtocol(certs?: BrowserContextOptions['clientCertificates']): Promise<channels.PlaywrightNewRequestParams['clientCertificates']> {
if (!certs)
return undefined;
const bufferizeContent = async (value?: Buffer, path?: string): Promise<Buffer | undefined> => {
if (value)
return value;
if (path)
return await fs.promises.readFile(path);
};
return await Promise.all(certs.map(async cert => ({
origin: cert.origin,
cert: await bufferizeContent(cert.cert, cert.certPath),
key: await bufferizeContent(cert.key, cert.keyPath),
pfx: await bufferizeContent(cert.pfx, cert.pfxPath),
passphrase: cert.passphrase,
})));
}
// Copied from playwright-core
function toAcceptDownloadsProtocol(acceptDownloads?: boolean) {
if (acceptDownloads === undefined)
return undefined;
if (acceptDownloads)
return 'accept';
return 'deny';
}
// Copied from playwright-core
function prepareRecordHarOptions(options: BrowserContextOptions['recordHar']): channels.RecordHarOptions | undefined {
if (!options)
return;
return {
path: options.path,
content: options.content || (options.omitContent ? 'omit' : undefined),
urlGlob: isString(options.urlFilter) ? options.urlFilter : undefined,
urlRegexSource: isRegExp(options.urlFilter) ? options.urlFilter.source : undefined,
urlRegexFlags: isRegExp(options.urlFilter) ? options.urlFilter.flags : undefined,
mode: options.mode
};
}
// Copied from playwright-core
async function prepareStorageState(options: BrowserContextOptions): Promise<channels.BrowserNewContextParams['storageState']> {
if (typeof options.storageState !== 'string')
return options.storageState;
try {
return JSON.parse(await fs.promises.readFile(options.storageState, 'utf8'));
} catch (e) {
rewriteErrorMessage(e, `Error reading storage state from ${options.storageState}:\n` + e.message);
throw e;
}
}
// Copied from playwright-core
async function prepareBrowserContextParams(options: BrowserContextOptions): Promise<channels.BrowserNewContextParams> {
if (options.videoSize && !options.videosPath)
throw new Error(`"videoSize" option requires "videosPath" to be specified`);
if (options.extraHTTPHeaders)
network.validateHeaders(options.extraHTTPHeaders);
const contextParams: channels.BrowserNewContextParams = {
...options,
viewport: options.viewport === null ? undefined : options.viewport,
noDefaultViewport: options.viewport === null,
extraHTTPHeaders: options.extraHTTPHeaders ? headersObjectToArray(options.extraHTTPHeaders) : undefined,
storageState: await prepareStorageState(options),
serviceWorkers: options.serviceWorkers,
recordHar: prepareRecordHarOptions(options.recordHar),
colorScheme: options.colorScheme === null ? 'no-override' : options.colorScheme,
reducedMotion: options.reducedMotion === null ? 'no-override' : options.reducedMotion,
forcedColors: options.forcedColors === null ? 'no-override' : options.forcedColors,
acceptDownloads: toAcceptDownloadsProtocol(options.acceptDownloads),
clientCertificates: await toClientCertificatesProtocol(options.clientCertificates),
};
if (!contextParams.recordVideo && options.videosPath) {
contextParams.recordVideo = {
dir: options.videosPath,
size: options.videoSize
};
}
if (contextParams.recordVideo && contextParams.recordVideo.dir)
contextParams.recordVideo.dir = path.resolve(process.cwd(), contextParams.recordVideo.dir);
return contextParams;
}
const toNormalizedCode = (text: string) => { const toNormalizedCode = (text: string) => {
return text.replace(/\s+/g, '') return text.replace(/\s+/g, '')
} }
@ -723,7 +824,7 @@ const moveDownloadedFileTo = async (page: Page, toLocation: string) => {
const files = await fsp.readdir(downloadDir) const files = await fsp.readdir(downloadDir)
return files.length return files.length
}) })
.toBe(1) .toBeGreaterThan(0)
// Go through the downloads dir and move files to new location // Go through the downloads dir and move files to new location
const files = await fsp.readdir(downloadDir) const files = await fsp.readdir(downloadDir)
@ -867,10 +968,12 @@ export async function setup(
settings, settings,
IS_PLAYWRIGHT_KEY, IS_PLAYWRIGHT_KEY,
PLAYWRIGHT_TEST_DIR, PLAYWRIGHT_TEST_DIR,
PERSIST_MODELING_CONTEXT,
}) => { }) => {
localStorage.clear() localStorage.clear()
localStorage.setItem('TOKEN_PERSIST_KEY', token) localStorage.setItem('TOKEN_PERSIST_KEY', token)
localStorage.setItem('persistCode', ``) localStorage.setItem('persistCode', ``)
localStorage.setItem(PERSIST_MODELING_CONTEXT, JSON.stringify({openPanes: ['code']}))
localStorage.setItem(settingsKey, settings) localStorage.setItem(settingsKey, settings)
localStorage.setItem(IS_PLAYWRIGHT_KEY, 'true') localStorage.setItem(IS_PLAYWRIGHT_KEY, 'true')
localStorage.setItem('PLAYWRIGHT_TEST_DIR', PLAYWRIGHT_TEST_DIR) localStorage.setItem('PLAYWRIGHT_TEST_DIR', PLAYWRIGHT_TEST_DIR)
@ -891,6 +994,7 @@ export async function setup(
}), }),
IS_PLAYWRIGHT_KEY, IS_PLAYWRIGHT_KEY,
PLAYWRIGHT_TEST_DIR: TEST_SETTINGS.app.projectDirectory, PLAYWRIGHT_TEST_DIR: TEST_SETTINGS.app.projectDirectory,
PERSIST_MODELING_CONTEXT,
} }
) )
@ -909,12 +1013,15 @@ export async function setup(
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()
} }
let electronApp = undefined
let context = undefined
let page = undefined
export async function setupElectron({ export async function setupElectron({
testInfo, testInfo,
folderSetupFn,
cleanProjectDir = true, cleanProjectDir = true,
appSettings, appSettings,
}: { }: {
@ -937,7 +1044,7 @@ export async function setupElectron({
await fsp.mkdir(projectDirName) await fsp.mkdir(projectDirName)
} }
const electronApp = await electron.launch({ const options = {
args: ['.', '--no-sandbox'], args: ['.', '--no-sandbox'],
env: { env: {
...process.env, ...process.env,
@ -947,15 +1054,19 @@ export async function setupElectron({
...(process.env.ELECTRON_OVERRIDE_DIST_PATH ...(process.env.ELECTRON_OVERRIDE_DIST_PATH
? { executablePath: process.env.ELECTRON_OVERRIDE_DIST_PATH + 'electron' } ? { executablePath: process.env.ELECTRON_OVERRIDE_DIST_PATH + 'electron' }
: {}), : {}),
}) }
const context = electronApp.context() // Do this once and then reuse window on subsequent calls.
const page = await electronApp.firstWindow() if (!electronApp) {
electronApp = await electron.launch(options)
page.TEST_SETTINGS_FILE_KEY = projectDirName }
if (!context || !page) {
context = electronApp.context()
page = await electronApp.firstWindow()
context.on('console', console.log) context.on('console', console.log)
page.on('console', console.log) page.on('console', console.log)
}
if (cleanProjectDir) { if (cleanProjectDir) {
const tempSettingsFilePath = path.join(projectDirName, SETTINGS_FILE_NAME) const tempSettingsFilePath = path.join(projectDirName, SETTINGS_FILE_NAME)
@ -985,11 +1096,7 @@ export async function setupElectron({
await fsp.writeFile(tempSettingsFilePath, settingsOverrides) await fsp.writeFile(tempSettingsFilePath, settingsOverrides)
} }
await folderSetupFn?.(projectDirName) return { electronApp, page, context, dir: projectDirName, options }
await setup(context, page)
return { electronApp, page, context, dir: projectDirName }
} }
function failOnConsoleErrors(page: Page, testInfo?: TestInfo) { function failOnConsoleErrors(page: Page, testInfo?: TestInfo) {

View File

@ -62,7 +62,7 @@ test.describe('Testing constraints', () => {
page.getByRole('button', { name: 'Exit Sketch' }) page.getByRole('button', { name: 'Exit Sketch' })
).toBeVisible() ).toBeVisible()
await page.waitForTimeout(500) // wait for animation await page.waitForTimeout(2500) // wait for animation
// Exit sketch // Exit sketch
await page.mouse.move(startXPx + PUR * 15, 250 - PUR * 10) await page.mouse.move(startXPx + PUR * 15, 250 - PUR * 10)
@ -668,7 +668,7 @@ test.describe('Testing constraints', () => {
}, },
] as const ] as const
for (const { testName, addVariable, value, constraint } of cases) { for (const { testName, addVariable, value, constraint } of cases) {
test(`${testName}`, async ({ page }) => { test(`${testName}`, async ({ context, homePage, page }) => {
// constants and locators // constants and locators
const cmdBarKclInput = page const cmdBarKclInput = page
.getByTestId('cmd-bar-arg-value') .getByTestId('cmd-bar-arg-value')
@ -698,9 +698,9 @@ part002 = startSketchOn('XZ')
) )
}) })
const u = await getUtils(page) const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 }) await page.setBodyDimensions({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart() await homePage.goToModelingScene()
await page.getByText('line([74.36, 130.4], %)').click() await page.getByText('line([74.36, 130.4], %)').click()
await page.getByRole('button', { name: 'Edit Sketch' }).click() await page.getByRole('button', { name: 'Edit Sketch' }).click()

View File

@ -162,7 +162,9 @@ test.describe('Testing settings', () => {
await expect(hotkey).toHaveText(text) await expect(hotkey).toHaveText(text)
}) })
test.fixme('Project and user settings can be reset', async ({ page, homePage }) => { test.fixme(
'Project and user settings can be reset',
async ({ page, homePage }) => {
const u = await getUtils(page) const u = await getUtils(page)
await test.step(`Setup`, async () => { await test.step(`Setup`, async () => {
await page.setBodyDimensions({ width: 1200, height: 500 }) await page.setBodyDimensions({ width: 1200, height: 500 })
@ -246,7 +248,8 @@ test.describe('Testing settings', () => {
await expect(themeColorSetting).toHaveValue(settingValues.project) await expect(themeColorSetting).toHaveValue(settingValues.project)
}) })
}) })
}) }
)
test.fixme( test.fixme(
`Project settings override user settings on desktop`, `Project settings override user settings on desktop`,

View File

@ -66,6 +66,8 @@ type PWFunction = (
testInfo: TestInfo testInfo: TestInfo
) => void | Promise<void> ) => void | Promise<void>
let firstUrl = ''
// The below error is due to the extreme type spaghetti going on. playwright/ // The below error is due to the extreme type spaghetti going on. playwright/
// types/test.d.ts does not export 2 functions (below is one of them) but tsc // types/test.d.ts does not export 2 functions (below is one of them) but tsc
// is trying to use a interface name it can't see. // is trying to use a interface name it can't see.
@ -213,17 +215,44 @@ export const test = (
) )
} }
await tronApp.page.setBodyDimensions(tronApp.viewPortSize)
// We need to expose this in order for some tests that require folder // We need to expose this in order for some tests that require folder
// creation. Before they used to do this by their own electronSetup({...}) // creation. Before they used to do this by their own electronSetup({...})
// calls. // calls.
if (tronApp instanceof AuthenticatedTronApp) { if (tronApp instanceof AuthenticatedTronApp) {
tronApp.context.folderSetupFn = function (fn) { tronApp.context.folderSetupFn = async function (fn) {
return fn(tronApp.dir).then(() => ({ return fn(tronApp.dir)
.then(() => tronApp.page.reload())
.then(() => ({
dir: tronApp.dir, dir: tronApp.dir,
})) }))
} }
} }
if (!firstUrl) {
await tronApp.page.getByText('Your Projects').count();
firstUrl = tronApp.page.url()
}
// Due to the app controlling its own window context we need to inject new
// options and context here.
// NOTE TO LEE: Seems to destroy page context when calling an electron loadURL.
// await tronApp.electronApp.evaluate(({ app }) => {
// return app.reuseWindowForTest();
// });
await tronApp.electronApp.evaluate(({ app }, projectDirName) => {
console.log("ABCDEFGHI", app.testProperty['TEST_SETTINGS_FILE_KEY'])
app.testProperty['TEST_SETTINGS_FILE_KEY'] = projectDirName
}, tronApp.dir)
// Always start at the root view
await tronApp.page.goto(firstUrl)
// Force a hard reload, destroying the stream and other state
await tronApp.page.reload()
// tsc aint smart enough to know this'll never be undefined // tsc aint smart enough to know this'll never be undefined
// but I dont blame it, the logic to know is complex // but I dont blame it, the logic to know is complex
if (fn) { if (fn) {

View File

@ -13,7 +13,7 @@ export default defineConfig({
/* Do not retry */ /* Do not retry */
retries: 0, retries: 0,
/* Different amount of parallelism on CI and local. */ /* Different amount of parallelism on CI and local. */
workers: 1, workers: 30,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */ /* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: [ reporter: [
['dot'], ['dot'],

View File

@ -391,7 +391,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 = window.electron.process.env.TEST_SETTINGS_FILE_KEY const testSettingsPath = await window.electron.getAppTestProperty('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
@ -408,7 +408,7 @@ 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 = window.electron.process.env.TEST_SETTINGS_FILE_KEY const testSettingsPath = await window.electron.getAppTestProperty('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

View File

@ -18,8 +18,9 @@ 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('TEST_SETTINGS_FILE_KEY')
const downloadDir = window.electron.join( const downloadDir = window.electron.join(
window.electron.process.env.TEST_SETTINGS_FILE_KEY, testSettingsPath,
'downloads-during-playwright' 'downloads-during-playwright'
) )
await window.electron.mkdir(downloadDir, { recursive: true }) await window.electron.mkdir(downloadDir, { recursive: true })

View File

@ -61,8 +61,8 @@ if (process.defaultApp) {
// Must be done before ready event. // Must be done before ready event.
registerStartupListeners() registerStartupListeners()
const createWindow = (filePath?: string): BrowserWindow => { const createWindow = (filePath?: string, reuse?: boolean): BrowserWindow => {
const newWindow = new BrowserWindow({ const newWindow = reuse ? mainWindow : new BrowserWindow({
autoHideMenuBar: true, autoHideMenuBar: true,
show: false, show: false,
width: 1800, width: 1800,
@ -110,7 +110,9 @@ const createWindow = (filePath?: string): BrowserWindow => {
// Open the DevTools. // Open the DevTools.
// mainWindow.webContents.openDevTools() // mainWindow.webContents.openDevTools()
if (!reuse) {
newWindow.show() newWindow.show()
}
return newWindow return newWindow
} }
@ -141,6 +143,12 @@ app.resizeWindow = async (width: number, height: number) => {
return mainWindow?.setSize(width, height) return mainWindow?.setSize(width, height)
} }
app.testProperty = {}
ipcMain.handle('app.testProperty', (event, propertyName) => {
return app.testProperty[propertyName]
})
ipcMain.handle('app.resizeWindow', (event, data) => { ipcMain.handle('app.resizeWindow', (event, data) => {
return mainWindow?.setSize(data[0], data[1]) return mainWindow?.setSize(data[0], data[1])
}) })

View File

@ -31,6 +31,7 @@ const onUpdateDownloadStart = (
const onUpdateError = (callback: (value: Error) => void) => const onUpdateError = (callback: (value: Error) => void) =>
ipcRenderer.on('update-error', (_event: any, value) => callback(value)) ipcRenderer.on('update-error', (_event: any, value) => callback(value))
const appRestart = () => ipcRenderer.invoke('app.restart') const appRestart = () => ipcRenderer.invoke('app.restart')
const getAppTestProperty = (propertyName: string) => ipcRenderer.invoke('app.testProperty', propertyName)
const isMac = os.platform() === 'darwin' const isMac = os.platform() === 'darwin'
const isWindows = os.platform() === 'win32' const isWindows = os.platform() === 'win32'
@ -163,14 +164,15 @@ contextBridge.exposeInMainWorld('electron', {
isWindows, isWindows,
isLinux, isLinux,
}, },
// Use this to access dynamic properties from the node side.
// INTENDED ONLY TO BE USED FOR TESTS.
getAppTestProperty,
process: { process: {
// Setter/getter has to be created because // These are read-only over the boundary.
// these are read-only over the boundary.
env: Object.assign( env: Object.assign(
{}, {},
exposeProcessEnvs([ exposeProcessEnvs([
'NODE_ENV', 'NODE_ENV',
'TEST_SETTINGS_FILE_KEY',
'VITE_KC_API_WS_MODELING_URL', 'VITE_KC_API_WS_MODELING_URL',
'VITE_KC_API_BASE_URL', 'VITE_KC_API_BASE_URL',
'VITE_KC_SITE_BASE_URL', 'VITE_KC_SITE_BASE_URL',