Skip to content

Commit

Permalink
Merge Frank test setup work (#3418)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
Irev-Dev and franknoirot authored Aug 14, 2024
1 parent 6ba0507 commit c17f0ab
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 96 deletions.
8 changes: 5 additions & 3 deletions e2e/playwright/electron-setup.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,11 @@ test('Electron setup', { tag: '@electron' }, async () => {
},
{
settingsKey: TEST_SETTINGS_KEY,
settings: TOML.stringify({ settings: {
app: { projectDirectory: fullPath },
} }),
settings: TOML.stringify({
settings: {
app: { projectDirectory: fullPath },
},
}),
}
)
})
98 changes: 35 additions & 63 deletions e2e/playwright/projects.spec.ts
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -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'
)
})
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

async ({ browserName }, testInfo) => {
test.skip(
browserName === 'webkit',
'Skip on Safari because `window.tearDown` does not work'
)
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)

Expand Down Expand Up @@ -111,12 +62,33 @@ const extrude001 = extrude(200, sketch001)`)
})
.toBeLessThan(10)

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)
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()
}
)
77 changes: 71 additions & 6 deletions e2e/playwright/test-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down Expand Up @@ -626,32 +630,93 @@ 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'],
timeout: 5000,
})

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 }
}
4 changes: 3 additions & 1 deletion src/lang/wasm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down
6 changes: 5 additions & 1 deletion src/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,8 @@ export const KCL_DEFAULT_CONSTANT_PREFIXES = {
/** The default KCL length expression */
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 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'
38 changes: 16 additions & 22 deletions src/lib/desktop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { components } from './machine-api'
import { isDesktop } from './isDesktop'

Check warning on line 8 in src/lib/desktop.ts

View workflow job for this annotation

GitHub Actions / playwright-electron

'isDesktop' is defined but never used

Check warning on line 8 in src/lib/desktop.ts

View workflow job for this annotation

GitHub Actions / playwright-macos (1, 4)

'isDesktop' is defined but never used

Check warning on line 8 in src/lib/desktop.ts

View workflow job for this annotation

GitHub Actions / playwright-macos (2, 4)

'isDesktop' is defined but never used

Check warning on line 8 in src/lib/desktop.ts

View workflow job for this annotation

GitHub Actions / playwright-macos (3, 4)

'isDesktop' is defined but never used

Check warning on line 8 in src/lib/desktop.ts

View workflow job for this annotation

GitHub Actions / playwright-ubuntu (1, 4)

'isDesktop' is defined but never used

Check warning on line 8 in src/lib/desktop.ts

View workflow job for this annotation

GitHub Actions / playwright-ubuntu (3, 4)

'isDesktop' is defined but never used

Check warning on line 8 in src/lib/desktop.ts

View workflow job for this annotation

GitHub Actions / playwright-ubuntu (2, 4)

'isDesktop' is defined but never used

Check warning on line 8 in src/lib/desktop.ts

View workflow job for this annotation

GitHub Actions / playwright-macos (4, 4)

'isDesktop' is defined but never used

Check warning on line 8 in src/lib/desktop.ts

View workflow job for this annotation

GitHub Actions / playwright-ubuntu (4, 4)

'isDesktop' is defined but never used
import { FileEntry } from 'wasm-lib/kcl/bindings/FileEntry'
import { SaveSettingsPayload } from 'lib/settings/settingsTypes'
import * as TOML from '@iarna/toml'

import {
defaultAppSettings,
Expand All @@ -17,15 +16,16 @@ import {
parseProjectSettings,
} from 'lang/wasm'
import { TEST_SETTINGS_KEY } from '../../e2e/playwright/storageStates'

Check warning on line 18 in src/lib/desktop.ts

View workflow job for this annotation

GitHub Actions / playwright-electron

'TEST_SETTINGS_KEY' is defined but never used

Check warning on line 18 in src/lib/desktop.ts

View workflow job for this annotation

GitHub Actions / playwright-macos (1, 4)

'TEST_SETTINGS_KEY' is defined but never used

Check warning on line 18 in src/lib/desktop.ts

View workflow job for this annotation

GitHub Actions / playwright-macos (2, 4)

'TEST_SETTINGS_KEY' is defined but never used

Check warning on line 18 in src/lib/desktop.ts

View workflow job for this annotation

GitHub Actions / playwright-macos (3, 4)

'TEST_SETTINGS_KEY' is defined but never used

Check warning on line 18 in src/lib/desktop.ts

View workflow job for this annotation

GitHub Actions / playwright-ubuntu (1, 4)

'TEST_SETTINGS_KEY' is defined but never used

Check warning on line 18 in src/lib/desktop.ts

View workflow job for this annotation

GitHub Actions / playwright-ubuntu (3, 4)

'TEST_SETTINGS_KEY' is defined but never used

Check warning on line 18 in src/lib/desktop.ts

View workflow job for this annotation

GitHub Actions / playwright-ubuntu (2, 4)

'TEST_SETTINGS_KEY' is defined but never used

Check warning on line 18 in src/lib/desktop.ts

View workflow job for this annotation

GitHub Actions / playwright-macos (4, 4)

'TEST_SETTINGS_KEY' is defined but never used

Check warning on line 18 in src/lib/desktop.ts

View workflow job for this annotation

GitHub Actions / playwright-ubuntu (4, 4)

'TEST_SETTINGS_KEY' is defined but never used
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
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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) {
Expand Down

0 comments on commit c17f0ab

Please sign in to comment.