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,46 +120,45 @@ test.describe('Code pane and errors', () => {
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 ({
page,
homePage,
context,
}) => {
// Load the app with the working starter code
await context.addInitScript((code) => {
localStorage.setItem('persistCode', code)
}, TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW)
test.fixme(
'When error is not in view you can click the badge to scroll to it',
async ({ page, homePage, context }) => {
// Load the app with the working starter code
await context.addInitScript((code) => {
localStorage.setItem('persistCode', code)
}, TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW)
await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene()
await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene()
await page.waitForTimeout(1000)
await page.waitForTimeout(1000)
// Ensure badge is present
const codePaneButtonHolder = page.locator('#code-button-holder')
await expect(codePaneButtonHolder).toContainText('notification')
// Ensure badge is present
const codePaneButtonHolder = page.locator('#code-button-holder')
await expect(codePaneButtonHolder).toContainText('notification')
// Ensure we have no errors in the gutter, since error out of view.
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
// Ensure we have no errors in the gutter, since error out of view.
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
// Click the badge.
const badge = page.locator('#code-badge')
await expect(badge).toBeVisible()
await badge.click()
// Click the badge.
const badge = page.locator('#code-badge')
await expect(badge).toBeVisible()
await badge.click()
// Ensure we have an error diagnostic.
await expect(page.locator('.cm-lint-marker-error').first()).toBeVisible()
// Ensure we have an error diagnostic.
await expect(page.locator('.cm-lint-marker-error').first()).toBeVisible()
// Hover over the error to see the error message
await page.hover('.cm-lint-marker-error')
await expect(
page
.getByText(
'Modeling command failed: [ApiError { error_code: InternalEngine, message: "Solid3D revolve failed: sketch profile must lie entirely on one side of the revolution axis" }]'
)
.first()
).toBeVisible()
})
// Hover over the error to see the error message
await page.hover('.cm-lint-marker-error')
await expect(
page
.getByText(
'Modeling command failed: [ApiError { error_code: InternalEngine, message: "Solid3D revolve failed: sketch profile must lie entirely on one side of the revolution axis" }]'
)
.first()
).toBeVisible()
}
)
test('When error is not in view WITH LINTS you can click the badge to scroll to it', async ({
context,

View File

@ -568,15 +568,14 @@ test.describe('Editor tests', () => {
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
})
test.fixme('error with 2 source ranges gets 2 diagnostics', async ({
page,
homePage,
}) => {
const u = await getUtils(page)
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`length = .750
test.fixme(
'error with 2 source ranges gets 2 diagnostics',
async ({ page, homePage }) => {
const u = await getUtils(page)
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`length = .750
width = 0.500
height = 0.500
dia = 4
@ -591,52 +590,53 @@ test.describe('Editor tests', () => {
return squareHoleSketch
}
`
)
})
await page.setBodyDimensions({ width: 1000, height: 500 })
)
})
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await u.waitForPageLoad()
await page.waitForTimeout(1000)
await homePage.goToModelingScene()
await u.waitForPageLoad()
await page.waitForTimeout(1000)
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.closeDebugPanel()
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.closeDebugPanel()
// check no error to begin with
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
// check no error to begin with
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
// Click on the bottom of the code editor to add a new line
await u.codeLocator.click()
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('Enter')
await page.keyboard.type(`extrusion = startSketchOn('XY')
// Click on the bottom of the code editor to add a new line
await u.codeLocator.click()
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('Enter')
await page.keyboard.type(`extrusion = startSketchOn('XY')
|> circle({ center: [0, 0], radius: dia/2 }, %)
|> hole(squareHole(length, width, height), %)
|> extrude(height, %)`)
// error in gutter
await expect(page.locator('.cm-lint-marker-error').first()).toBeVisible()
await page.hover('.cm-lint-marker-error:first-child')
await expect(
page.getByText('Expected 2 arguments, got 3').first()
).toBeVisible()
// error in gutter
await expect(page.locator('.cm-lint-marker-error').first()).toBeVisible()
await page.hover('.cm-lint-marker-error:first-child')
await expect(
page.getByText('Expected 2 arguments, got 3').first()
).toBeVisible()
// Make sure there are two diagnostics
await expect(page.locator('.cm-lint-marker-error')).toHaveCount(2)
})
// Make sure there are two diagnostics
await expect(page.locator('.cm-lint-marker-error')).toHaveCount(2)
}
)
test('if your kcl gets an error from the engine it is inlined', async ({
context,
page,

View File

@ -29,7 +29,7 @@ export class EditorFixture {
reConstruct = (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.diagnosticsGutterIcon = page.locator('.cm-lint-marker-error')
this.activeLine = this.page.locator('.cm-activeLine')

View File

@ -79,7 +79,7 @@ export class AuthenticatedTronApp {
appSettings?: Partial<SaveSettingsPayload>
} = { fixtures: {} }
) {
const { electronApp, page, context, dir } = await setupElectron({
const { electronApp, page, context, dir, options } = await setupElectron({
testInfo: this.testInfo,
folderSetupFn: arg.folderSetupFn,
cleanProjectDir: arg.cleanProjectDir,
@ -96,8 +96,6 @@ export class AuthenticatedTronApp {
// Setup localStorage, addCookies, reload
await setup(this.context, this.page, this.testInfo)
await page.setViewportSize(this.viewPortSize)
for (const key of unsafeTypedKeys(arg.fixtures)) {
const fixture = arg.fixtures[key]
if (

View File

@ -851,7 +851,9 @@ const shellPointAndClickCapCases = [
]
shellPointAndClickCapCases.forEach(({ shouldPreselect }) => {
test(`Shell point-and-click cap (preselected sketches: ${shouldPreselect})`, async ({
app,
context,
page,
homePage,
scene,
editor,
toolbar,
@ -861,7 +863,11 @@ shellPointAndClickCapCases.forEach(({ shouldPreselect }) => {
|> circle({ center = [0, 0], radius = 30 }, %)
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
const testPoint = { x: 575, y: 200 }
@ -888,7 +894,7 @@ shellPointAndClickCapCases.forEach(({ shouldPreselect }) => {
commandName: 'Shell',
})
await clickOnCap()
await app.page.waitForTimeout(500)
await page.waitForTimeout(500)
await cmdBar.progressCmdBar()
await cmdBar.progressCmdBar()
await cmdBar.expectState({
@ -904,7 +910,7 @@ shellPointAndClickCapCases.forEach(({ shouldPreselect }) => {
} else {
await test.step(`Preselect the cap`, async () => {
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 () => {
@ -936,8 +942,9 @@ shellPointAndClickCapCases.forEach(({ shouldPreselect }) => {
})
test('Shell point-and-click wall', async ({
app,
context,
page,
homePage,
scene,
editor,
toolbar,
@ -952,7 +959,11 @@ test('Shell point-and-click wall', async ({
|> close(%)
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
const testPoint = { x: 580, y: 180 }
@ -983,7 +994,7 @@ extrude001 = extrude(40, sketch001)
await clickOnCap()
await page.keyboard.down('Shift')
await clickOnWall()
await app.page.waitForTimeout(500)
await page.waitForTimeout(500)
await page.keyboard.up('Shift')
await cmdBar.progressCmdBar()
await cmdBar.progressCmdBar()

View File

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

View File

@ -47,7 +47,6 @@ test.beforeEach(async ({ page }) => {
test.setTimeout(60_000)
// 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.
// 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 { 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) => {
return text.replace(/\s+/g, '')
}
@ -723,7 +824,7 @@ const moveDownloadedFileTo = async (page: Page, toLocation: string) => {
const files = await fsp.readdir(downloadDir)
return files.length
})
.toBe(1)
.toBeGreaterThan(0)
// Go through the downloads dir and move files to new location
const files = await fsp.readdir(downloadDir)
@ -867,10 +968,12 @@ export async function setup(
settings,
IS_PLAYWRIGHT_KEY,
PLAYWRIGHT_TEST_DIR,
PERSIST_MODELING_CONTEXT,
}) => {
localStorage.clear()
localStorage.setItem('TOKEN_PERSIST_KEY', token)
localStorage.setItem('persistCode', ``)
localStorage.setItem(PERSIST_MODELING_CONTEXT, JSON.stringify({openPanes: ['code']}))
localStorage.setItem(settingsKey, settings)
localStorage.setItem(IS_PLAYWRIGHT_KEY, 'true')
localStorage.setItem('PLAYWRIGHT_TEST_DIR', PLAYWRIGHT_TEST_DIR)
@ -891,6 +994,7 @@ export async function setup(
}),
IS_PLAYWRIGHT_KEY,
PLAYWRIGHT_TEST_DIR: TEST_SETTINGS.app.projectDirectory,
PERSIST_MODELING_CONTEXT,
}
)
@ -909,12 +1013,15 @@ export async function setup(
await page.emulateMedia({ reducedMotion: 'reduce' })
// 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({
testInfo,
folderSetupFn,
cleanProjectDir = true,
appSettings,
}: {
@ -937,7 +1044,7 @@ export async function setupElectron({
await fsp.mkdir(projectDirName)
}
const electronApp = await electron.launch({
const options = {
args: ['.', '--no-sandbox'],
env: {
...process.env,
@ -947,15 +1054,19 @@ export async function setupElectron({
...(process.env.ELECTRON_OVERRIDE_DIST_PATH
? { executablePath: process.env.ELECTRON_OVERRIDE_DIST_PATH + 'electron' }
: {}),
})
}
const context = electronApp.context()
const page = await electronApp.firstWindow()
// Do this once and then reuse window on subsequent calls.
if (!electronApp) {
electronApp = await electron.launch(options)
}
page.TEST_SETTINGS_FILE_KEY = projectDirName
context.on('console', console.log)
page.on('console', console.log)
if (!context || !page) {
context = electronApp.context()
page = await electronApp.firstWindow()
context.on('console', console.log)
page.on('console', console.log)
}
if (cleanProjectDir) {
const tempSettingsFilePath = path.join(projectDirName, SETTINGS_FILE_NAME)
@ -985,11 +1096,7 @@ export async function setupElectron({
await fsp.writeFile(tempSettingsFilePath, settingsOverrides)
}
await folderSetupFn?.(projectDirName)
await setup(context, page)
return { electronApp, page, context, dir: projectDirName }
return { electronApp, page, context, dir: projectDirName, options }
}
function failOnConsoleErrors(page: Page, testInfo?: TestInfo) {

View File

@ -62,7 +62,7 @@ test.describe('Testing constraints', () => {
page.getByRole('button', { name: 'Exit Sketch' })
).toBeVisible()
await page.waitForTimeout(500) // wait for animation
await page.waitForTimeout(2500) // wait for animation
// Exit sketch
await page.mouse.move(startXPx + PUR * 15, 250 - PUR * 10)
@ -668,7 +668,7 @@ test.describe('Testing constraints', () => {
},
] as const
for (const { testName, addVariable, value, constraint } of cases) {
test(`${testName}`, async ({ page }) => {
test(`${testName}`, async ({ context, homePage, page }) => {
// constants and locators
const cmdBarKclInput = page
.getByTestId('cmd-bar-arg-value')
@ -698,9 +698,9 @@ part002 = startSketchOn('XZ')
)
})
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.getByRole('button', { name: 'Edit Sketch' }).click()

View File

@ -162,91 +162,94 @@ test.describe('Testing settings', () => {
await expect(hotkey).toHaveText(text)
})
test.fixme('Project and user settings can be reset', async ({ page, homePage }) => {
const u = await getUtils(page)
await test.step(`Setup`, async () => {
await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene()
await u.waitForPageLoad()
await page.waitForTimeout(1000)
})
// Selectors and constants
const projectSettingsTab = page.getByRole('radio', { name: 'Project' })
const userSettingsTab = page.getByRole('radio', { name: 'User' })
const resetButton = (level: SettingsLevel) =>
page.getByRole('button', {
name: `Reset ${level}-level settings`,
})
const themeColorSetting = page.locator('#themeColor').getByRole('slider')
const settingValues = {
default: '259',
user: '120',
project: '50',
}
const resetToast = (level: SettingsLevel) =>
page.getByText(`${level}-level settings were reset`)
await test.step(`Open the settings modal`, async () => {
await page.getByRole('link', { name: 'Settings' }).last().click()
await expect(
page.getByRole('heading', { name: 'Settings', exact: true })
).toBeVisible()
})
await test.step('Set up theme color', async () => {
// Verify we're looking at the project-level settings,
// and it's set to default value
await expect(projectSettingsTab).toBeChecked()
await expect(themeColorSetting).toHaveValue(settingValues.default)
// Set project-level value to 50
await themeColorSetting.fill(settingValues.project)
// Set user-level value to 120
await userSettingsTab.click()
await themeColorSetting.fill(settingValues.user)
await projectSettingsTab.click()
})
await test.step('Reset project settings', async () => {
// Click the reset settings button.
await resetButton('project').click()
await expect(resetToast('project')).toBeVisible()
await expect(resetToast('project')).not.toBeVisible()
// Verify it is now set to the inherited user value
await expect(themeColorSetting).toHaveValue(settingValues.user)
await test.step(`Check that the user settings did not change`, async () => {
await userSettingsTab.click()
await expect(themeColorSetting).toHaveValue(settingValues.user)
test.fixme(
'Project and user settings can be reset',
async ({ page, homePage }) => {
const u = await getUtils(page)
await test.step(`Setup`, async () => {
await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene()
await u.waitForPageLoad()
await page.waitForTimeout(1000)
})
await test.step(`Set project-level again to test the user-level reset`, async () => {
await projectSettingsTab.click()
// Selectors and constants
const projectSettingsTab = page.getByRole('radio', { name: 'Project' })
const userSettingsTab = page.getByRole('radio', { name: 'User' })
const resetButton = (level: SettingsLevel) =>
page.getByRole('button', {
name: `Reset ${level}-level settings`,
})
const themeColorSetting = page.locator('#themeColor').getByRole('slider')
const settingValues = {
default: '259',
user: '120',
project: '50',
}
const resetToast = (level: SettingsLevel) =>
page.getByText(`${level}-level settings were reset`)
await test.step(`Open the settings modal`, async () => {
await page.getByRole('link', { name: 'Settings' }).last().click()
await expect(
page.getByRole('heading', { name: 'Settings', exact: true })
).toBeVisible()
})
await test.step('Set up theme color', async () => {
// Verify we're looking at the project-level settings,
// and it's set to default value
await expect(projectSettingsTab).toBeChecked()
await expect(themeColorSetting).toHaveValue(settingValues.default)
// Set project-level value to 50
await themeColorSetting.fill(settingValues.project)
// Set user-level value to 120
await userSettingsTab.click()
})
})
await test.step('Reset user settings', async () => {
// Click the reset settings button.
await resetButton('user').click()
await expect(resetToast('user')).toBeVisible()
await expect(resetToast('user')).not.toBeVisible()
// Verify it is now set to the default value
await expect(themeColorSetting).toHaveValue(settingValues.default)
await test.step(`Check that the project settings did not change`, async () => {
await themeColorSetting.fill(settingValues.user)
await projectSettingsTab.click()
await expect(themeColorSetting).toHaveValue(settingValues.project)
})
})
})
await test.step('Reset project settings', async () => {
// Click the reset settings button.
await resetButton('project').click()
await expect(resetToast('project')).toBeVisible()
await expect(resetToast('project')).not.toBeVisible()
// Verify it is now set to the inherited user value
await expect(themeColorSetting).toHaveValue(settingValues.user)
await test.step(`Check that the user settings did not change`, async () => {
await userSettingsTab.click()
await expect(themeColorSetting).toHaveValue(settingValues.user)
})
await test.step(`Set project-level again to test the user-level reset`, async () => {
await projectSettingsTab.click()
await themeColorSetting.fill(settingValues.project)
await userSettingsTab.click()
})
})
await test.step('Reset user settings', async () => {
// Click the reset settings button.
await resetButton('user').click()
await expect(resetToast('user')).toBeVisible()
await expect(resetToast('user')).not.toBeVisible()
// Verify it is now set to the default value
await expect(themeColorSetting).toHaveValue(settingValues.default)
await test.step(`Check that the project settings did not change`, async () => {
await projectSettingsTab.click()
await expect(themeColorSetting).toHaveValue(settingValues.project)
})
})
}
)
test.fixme(
`Project settings override user settings on desktop`,

View File

@ -66,6 +66,8 @@ type PWFunction = (
testInfo: TestInfo
) => void | Promise<void>
let firstUrl = ''
// 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
// 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
// creation. Before they used to do this by their own electronSetup({...})
// calls.
if (tronApp instanceof AuthenticatedTronApp) {
tronApp.context.folderSetupFn = function (fn) {
return fn(tronApp.dir).then(() => ({
tronApp.context.folderSetupFn = async function (fn) {
return fn(tronApp.dir)
.then(() => tronApp.page.reload())
.then(() => ({
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
// but I dont blame it, the logic to know is complex
if (fn) {

View File

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

View File

@ -391,7 +391,7 @@ const getAppFolderName = () => {
export const getAppSettingsFilePath = async () => {
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 fullPath = isTestEnv
? testSettingsPath
@ -408,7 +408,7 @@ export const getAppSettingsFilePath = async () => {
}
const getTokenFilePath = async () => {
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 fullPath = isTestEnv
? testSettingsPath

View File

@ -18,8 +18,9 @@ const save_ = async (file: ModelingAppFile, toastId: string) => {
if (window.electron.process.env.IS_PLAYWRIGHT) {
// 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(
window.electron.process.env.TEST_SETTINGS_FILE_KEY,
testSettingsPath,
'downloads-during-playwright'
)
await window.electron.mkdir(downloadDir, { recursive: true })

View File

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

View File

@ -31,6 +31,7 @@ const onUpdateDownloadStart = (
const onUpdateError = (callback: (value: Error) => void) =>
ipcRenderer.on('update-error', (_event: any, value) => callback(value))
const appRestart = () => ipcRenderer.invoke('app.restart')
const getAppTestProperty = (propertyName: string) => ipcRenderer.invoke('app.testProperty', propertyName)
const isMac = os.platform() === 'darwin'
const isWindows = os.platform() === 'win32'
@ -163,14 +164,15 @@ contextBridge.exposeInMainWorld('electron', {
isWindows,
isLinux,
},
// Use this to access dynamic properties from the node side.
// INTENDED ONLY TO BE USED FOR TESTS.
getAppTestProperty,
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(
{},
exposeProcessEnvs([
'NODE_ENV',
'TEST_SETTINGS_FILE_KEY',
'VITE_KC_API_WS_MODELING_URL',
'VITE_KC_API_BASE_URL',
'VITE_KC_SITE_BASE_URL',