Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge Frank test setup work #3418

Merged
merged 4 commits into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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'
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'
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
Loading