Reuse electron window; difficult task
This commit is contained in:
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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')
|
||||||
|
@ -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 (
|
||||||
|
@ -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()
|
||||||
|
@ -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')
|
||||||
|
@ -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
|
||||||
|
@ -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) {
|
||||||
|
@ -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()
|
||||||
|
@ -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`,
|
||||||
|
@ -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) {
|
||||||
|
@ -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'],
|
||||||
|
@ -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
|
||||||
|
@ -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 })
|
||||||
|
12
src/main.ts
12
src/main.ts
@ -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])
|
||||||
})
|
})
|
||||||
|
@ -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',
|
||||||
|
Reference in New Issue
Block a user