Merge Frank test setup work (#3418)
* Working window.electron.getPath * Loading project-specific settings in electron tests * Simplify test until we can get snapshots/traces working in electron tests * test tweaks --------- Co-authored-by: Frank Noirot <frank@kittycad.io>
This commit is contained in:
@ -38,9 +38,11 @@ test('Electron setup', { tag: '@electron' }, async () => {
|
||||
},
|
||||
{
|
||||
settingsKey: TEST_SETTINGS_KEY,
|
||||
settings: TOML.stringify({ settings: {
|
||||
settings: TOML.stringify({
|
||||
settings: {
|
||||
app: { projectDirectory: fullPath },
|
||||
} }),
|
||||
},
|
||||
}),
|
||||
}
|
||||
)
|
||||
})
|
||||
|
@ -1,9 +1,5 @@
|
||||
import { _electron as electron, test, expect } from '@playwright/test'
|
||||
import { getUtils, setup, tearDown } from './test-utils'
|
||||
import fs from 'fs/promises'
|
||||
import { secrets } from './secrets'
|
||||
import { join } from 'path'
|
||||
import { tomlStringify } from 'lang/wasm'
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { getUtils, setupElectron, tearDown } from './test-utils'
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
await tearDown(page, testInfo)
|
||||
@ -12,60 +8,15 @@ test.afterEach(async ({ page }, testInfo) => {
|
||||
test(
|
||||
'When the project folder is empty, user can create new project and open it.',
|
||||
{ tag: '@electron' },
|
||||
async ({ page: browserPage, context: browserContext }, testInfo) => {
|
||||
// create or otherwise clear the folder ./electron-test-projects-dir
|
||||
const settingsFileName = `./${testInfo.title
|
||||
.replace(/\s/gi, '-')
|
||||
.replace(/\W/gi, '')}`
|
||||
const projectDirName = settingsFileName + '-dir'
|
||||
try {
|
||||
await fs.rm(projectDirName, { recursive: true })
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
|
||||
await fs.mkdir(projectDirName)
|
||||
|
||||
// get full path for ./electron-test-projects-dir
|
||||
const fullProjectPath = await fs.realpath(projectDirName)
|
||||
|
||||
const electronApp = await electron.launch({
|
||||
args: ['.'],
|
||||
})
|
||||
const context = electronApp.context()
|
||||
const page = await electronApp.firstWindow()
|
||||
|
||||
const electronTempDirectory = await page.evaluate(async () => {
|
||||
return await window.electron.getPath(
|
||||
'temp'
|
||||
async ({ browserName }, testInfo) => {
|
||||
test.skip(
|
||||
browserName === 'webkit',
|
||||
'Skip on Safari because `window.tearDown` does not work'
|
||||
)
|
||||
})
|
||||
const tempSettingsFilePath = join(electronTempDirectory, settingsFileName)
|
||||
const settingsOverrides = tomlStringify({
|
||||
app: {
|
||||
projectDirectory: fullProjectPath,
|
||||
},
|
||||
})
|
||||
|
||||
if (settingsOverrides instanceof Error) {
|
||||
throw settingsOverrides
|
||||
}
|
||||
await fs.writeFile(tempSettingsFilePath + '.toml', settingsOverrides)
|
||||
|
||||
console.log('from within test setup', {
|
||||
settingsFileName,
|
||||
fullPath: fullProjectPath,
|
||||
electronApp,
|
||||
page,
|
||||
settingsFilePath: tempSettingsFilePath + '.toml',
|
||||
})
|
||||
|
||||
await setup(context, page, fullProjectPath)
|
||||
// Set local storage directly using evaluate
|
||||
|
||||
const { electronApp, page } = await setupElectron({ testInfo })
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.goto('http://localhost:3000/')
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
|
||||
page.on('console', console.log)
|
||||
|
||||
@ -111,12 +62,33 @@ const extrude001 = extrude(200, sketch001)`)
|
||||
})
|
||||
.toBeLessThan(10)
|
||||
|
||||
await expect(async () => {
|
||||
await page.mouse.move(0, 0, { steps: 5 })
|
||||
await page.mouse.move(pointOnModel.x, pointOnModel.y, { steps: 5 })
|
||||
await page.mouse.click(pointOnModel.x, pointOnModel.y)
|
||||
// check user can interact with model by checking it turns yellow
|
||||
await expect
|
||||
.poll(() => u.getGreatestPixDiff(pointOnModel, [176, 180, 132]))
|
||||
.toBeLessThan(10)
|
||||
}).toPass({ timeout: 40_000, intervals: [1_000] })
|
||||
|
||||
await page.getByTestId('app-logo').click()
|
||||
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'New project' })
|
||||
).toBeVisible()
|
||||
|
||||
const createProject = async (projectNum: number) => {
|
||||
await page.getByRole('button', { name: 'New project' }).click()
|
||||
await expect(page.getByText('Successfully created')).toBeVisible()
|
||||
await expect(page.getByText('Successfully created')).not.toBeVisible()
|
||||
|
||||
const projectNumStr = projectNum.toString().padStart(3, '0')
|
||||
await expect(page.getByText(`project-${projectNumStr}`)).toBeVisible()
|
||||
}
|
||||
for (let i = 1; i <= 10; i++) {
|
||||
await createProject(i)
|
||||
}
|
||||
await electronApp.close()
|
||||
}
|
||||
)
|
||||
|
@ -4,20 +4,24 @@ import {
|
||||
Download,
|
||||
TestInfo,
|
||||
BrowserContext,
|
||||
_electron as electron,
|
||||
} from '@playwright/test'
|
||||
import { EngineCommand } from 'lang/std/artifactGraph'
|
||||
import os from 'os'
|
||||
import fsp from 'fs/promises'
|
||||
import fsSync from 'fs'
|
||||
import { join } from 'path'
|
||||
import pixelMatch from 'pixelmatch'
|
||||
import { PNG } from 'pngjs'
|
||||
import { Protocol } from 'playwright-core/types/protocol'
|
||||
import type { Models } from '@kittycad/lib'
|
||||
import { APP_NAME } from 'lib/constants'
|
||||
import { APP_NAME, TEST_SETTINGS_FILE_KEY } from 'lib/constants'
|
||||
import waitOn from 'wait-on'
|
||||
import { secrets } from './secrets'
|
||||
import { TEST_SETTINGS_KEY, TEST_SETTINGS } from './storageStates'
|
||||
import * as TOML from '@iarna/toml'
|
||||
import { SaveSettingsPayload } from 'lib/settings/settingsTypes'
|
||||
import { SETTINGS_FILE_NAME } from 'lib/constants'
|
||||
|
||||
type TestColor = [number, number, number]
|
||||
export const TEST_COLORS = {
|
||||
@ -626,7 +630,11 @@ export async function tearDown(page: Page, testInfo: TestInfo) {
|
||||
|
||||
// settingsOverrides may need to be augmented to take more generic items,
|
||||
// but we'll be strict for now
|
||||
export async function setup(context: BrowserContext, page: Page, overrideDirectory?: string) {
|
||||
export async function setup(
|
||||
context: BrowserContext,
|
||||
page: Page,
|
||||
overrideDirectory?: string
|
||||
) {
|
||||
// wait for Vite preview server to be up
|
||||
await waitOn({
|
||||
resources: ['tcp:3000'],
|
||||
@ -634,24 +642,81 @@ export async function setup(context: BrowserContext, page: Page, overrideDirecto
|
||||
})
|
||||
|
||||
await context.addInitScript(
|
||||
async ({ token, settingsKey, settings }) => {
|
||||
async ({
|
||||
token,
|
||||
settingsKey,
|
||||
settings,
|
||||
appSettingsFileKey,
|
||||
appSettingsFileContent,
|
||||
}) => {
|
||||
localStorage.setItem('TOKEN_PERSIST_KEY', token)
|
||||
localStorage.setItem('persistCode', ``)
|
||||
localStorage.setItem(settingsKey, settings)
|
||||
localStorage.setItem(appSettingsFileKey, appSettingsFileContent)
|
||||
localStorage.setItem('playwright', 'true')
|
||||
},
|
||||
{
|
||||
token: secrets.token,
|
||||
appSettingsFileKey: TEST_SETTINGS_FILE_KEY,
|
||||
appSettingsFileContent:
|
||||
overrideDirectory || TEST_SETTINGS.app.projectDirectory,
|
||||
settingsKey: TEST_SETTINGS_KEY,
|
||||
settings: TOML.stringify({
|
||||
...TEST_SETTINGS,
|
||||
app: {
|
||||
...TEST_SETTINGS.app,
|
||||
projectDirectory: overrideDirectory || TEST_SETTINGS.app.projectDirectory,
|
||||
...TEST_SETTINGS.projects,
|
||||
projectDirectory:
|
||||
overrideDirectory || TEST_SETTINGS.app.projectDirectory,
|
||||
},
|
||||
}),
|
||||
} as Partial<SaveSettingsPayload>),
|
||||
}
|
||||
)
|
||||
// kill animations, speeds up tests and reduced flakiness
|
||||
await page.emulateMedia({ reducedMotion: 'reduce' })
|
||||
}
|
||||
|
||||
export async function setupElectron({
|
||||
testInfo,
|
||||
folderSetupFn,
|
||||
}: {
|
||||
testInfo: TestInfo
|
||||
folderSetupFn?: (projectDirName: string) => Promise<void>
|
||||
}) {
|
||||
// create or otherwise clear the folder
|
||||
const projectDirName = testInfo.outputPath('electron-test-projects-dir')
|
||||
try {
|
||||
if (fsSync.existsSync(projectDirName)) {
|
||||
await fsp.rm(projectDirName, { recursive: true })
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
|
||||
await fsp.mkdir(projectDirName)
|
||||
|
||||
const electronApp = await electron.launch({
|
||||
args: ['.'],
|
||||
})
|
||||
const context = electronApp.context()
|
||||
const page = await electronApp.firstWindow()
|
||||
context.on('console', console.log)
|
||||
page.on('console', console.log)
|
||||
|
||||
const tempSettingsFilePath = join(projectDirName, SETTINGS_FILE_NAME)
|
||||
const settingsOverrides = TOML.stringify({
|
||||
...TEST_SETTINGS,
|
||||
settings: {
|
||||
app: {
|
||||
...TEST_SETTINGS.app,
|
||||
projectDirectory: projectDirName,
|
||||
},
|
||||
},
|
||||
})
|
||||
await fsp.writeFile(tempSettingsFilePath, settingsOverrides)
|
||||
|
||||
await folderSetupFn?.(tempSettingsFilePath)
|
||||
|
||||
await setup(context, page, projectDirName)
|
||||
|
||||
return { electronApp, page }
|
||||
}
|
||||
|
@ -569,7 +569,9 @@ export function defaultAppSettings(): Partial<SaveSettingsPayload> {
|
||||
}
|
||||
|
||||
export function parseAppSettings(toml: string): Partial<SaveSettingsPayload> {
|
||||
return configurationToSettingsPayload(parse_app_settings(toml))
|
||||
const parsed = parse_app_settings(toml)
|
||||
console.log('within wasm.ts, parsed app settings', parsed)
|
||||
return configurationToSettingsPayload(parsed)
|
||||
}
|
||||
|
||||
export function defaultProjectSettings(): Partial<SaveSettingsPayload> {
|
||||
|
@ -57,3 +57,7 @@ export const KCL_DEFAULT_CONSTANT_PREFIXES = {
|
||||
export const KCL_DEFAULT_LENGTH = `5`
|
||||
/** localStorage key for the playwright test-specific app settings file */
|
||||
export const TEST_SETTINGS_FILE_KEY = 'playwright-test-settings'
|
||||
|
||||
export const DEFAULT_HOST = 'https://api.zoo.dev'
|
||||
export const SETTINGS_FILE_NAME = 'settings.toml'
|
||||
export const PROJECT_SETTINGS_FILE_NAME = 'project.toml'
|
||||
|
@ -8,7 +8,6 @@ import { components } from './machine-api'
|
||||
import { isDesktop } from './isDesktop'
|
||||
import { FileEntry } from 'wasm-lib/kcl/bindings/FileEntry'
|
||||
import { SaveSettingsPayload } from 'lib/settings/settingsTypes'
|
||||
import * as TOML from '@iarna/toml'
|
||||
|
||||
import {
|
||||
defaultAppSettings,
|
||||
@ -17,15 +16,16 @@ import {
|
||||
parseProjectSettings,
|
||||
} from 'lang/wasm'
|
||||
import { TEST_SETTINGS_KEY } from '../../e2e/playwright/storageStates'
|
||||
import { TEST_SETTINGS_FILE_KEY } from './constants'
|
||||
import {
|
||||
DEFAULT_HOST,
|
||||
PROJECT_ENTRYPOINT,
|
||||
PROJECT_FOLDER,
|
||||
PROJECT_SETTINGS_FILE_NAME,
|
||||
SETTINGS_FILE_NAME,
|
||||
TEST_SETTINGS_FILE_KEY,
|
||||
} from './constants'
|
||||
export { parseProjectRoute } from 'lang/wasm'
|
||||
|
||||
const DEFAULT_HOST = 'https://api.zoo.dev'
|
||||
const SETTINGS_FILE_NAME = 'settings.toml'
|
||||
const PROJECT_SETTINGS_FILE_NAME = 'project.toml'
|
||||
const PROJECT_FOLDER = 'zoo-modeling-app-projects'
|
||||
const DEFAULT_PROJECT_KCL_FILE = 'main.kcl'
|
||||
|
||||
export async function renameProjectDirectory(
|
||||
projectPath: string,
|
||||
newName: string
|
||||
@ -112,10 +112,7 @@ export async function createNewProjectDirectory(
|
||||
}
|
||||
}
|
||||
|
||||
const projectFile = window.electron.path.join(
|
||||
projectDir,
|
||||
DEFAULT_PROJECT_KCL_FILE
|
||||
)
|
||||
const projectFile = window.electron.path.join(projectDir, PROJECT_ENTRYPOINT)
|
||||
await window.electron.writeFile(projectFile, initialCode ?? '')
|
||||
const metadata = await window.electron.stat(projectFile)
|
||||
|
||||
@ -255,7 +252,7 @@ export async function getDefaultKclFileForDir(
|
||||
|
||||
let defaultFilePath = window.electron.path.join(
|
||||
projectDir,
|
||||
DEFAULT_PROJECT_KCL_FILE
|
||||
PROJECT_ENTRYPOINT
|
||||
)
|
||||
try {
|
||||
await window.electron.stat(defaultFilePath)
|
||||
@ -377,15 +374,12 @@ export async function writeProjectSettingsFile(
|
||||
|
||||
const getAppSettingsFilePath = async () => {
|
||||
const isPlaywright = window.localStorage.getItem('playwright') === 'true'
|
||||
const testDirectoryName = window.localStorage.getItem(TEST_SETTINGS_FILE_KEY) ?? ''
|
||||
const appConfig = await window.electron.getPath(
|
||||
isPlaywright ? 'temp' : 'appData'
|
||||
)
|
||||
const fullPath = window.electron.path.join(
|
||||
appConfig,
|
||||
isPlaywright ? testDirectoryName : '',
|
||||
window.electron.packageJson.name
|
||||
)
|
||||
const testSettingsPath =
|
||||
window.localStorage.getItem(TEST_SETTINGS_FILE_KEY) ?? ''
|
||||
const appConfig = await window.electron.getPath('appData')
|
||||
const fullPath = isPlaywright
|
||||
? testSettingsPath
|
||||
: window.electron.path.join(appConfig, window.electron.packageJson.name)
|
||||
try {
|
||||
await window.electron.stat(fullPath)
|
||||
} catch (e) {
|
||||
|
Reference in New Issue
Block a user