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

Integration Tests #11186

Merged
merged 8 commits into from
Sep 27, 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
4 changes: 3 additions & 1 deletion app/ide-desktop/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"@types/semver": "^7.5.8",
"@types/tar": "^6.1.4",
"@types/yargs": "^17.0.30",
"@playwright/test": "^1.45.0",
"cross-env": "^7.0.3",
"electron": "31.2.0",
"electron-builder": "^24.13.3",
Expand All @@ -50,7 +51,8 @@
"fast-glob": "^3.2.12",
"portfinder": "^1.0.32",
"tsx": "^4.7.1",
"vite": "^5.3.5"
"vite": "^5.3.5",
"playwright": "^1.45.0"
},
"//": [
"vite is required for the watch script",
Expand Down
20 changes: 20 additions & 0 deletions app/ide-desktop/client/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/** @file Playwright browser testing configuration. */
import { defineConfig } from '@playwright/test'

/* eslint-disable @typescript-eslint/no-magic-numbers, @typescript-eslint/strict-boolean-expressions */

export default defineConfig({
testDir: './tests',
forbidOnly: !!process.env.CI,
workers: 1,
timeout: 60000,
reportSlowTests: { max: 5, threshold: 60000 },
globalSetup: './tests/setup.ts',
expect: {
timeout: 5000,
toHaveScreenshot: { threshold: 0 },
},
use: {
actionTimeout: 5000,
},
})
16 changes: 7 additions & 9 deletions app/ide-desktop/client/src/fileAssociations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,15 +80,13 @@ function getClientArguments(args = process.argv): readonly string[] {
return args.slice(separatorIndex + 1)
}
} else {
const noSandbox = args.indexOf('--no-sandbox')
if (noSandbox !== NOT_FOUND) {
let v = [...args]
v.splice(noSandbox, 1)
return v.slice(1)
} else {
// Drop the leading executable name.
return args.slice(1)
}
// Drop the leading executable name and known electron options.
return (
args
.slice(1)
// Omitting $ in --inspect and --remote-debugging-port is intentional.
.filter(option => !/^--no-sandbox$|^--inspect|^--remote-debugging-port/.test(option))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: consider inspect\b and port\b. not perfect but might as well

)
}
}

Expand Down
10 changes: 5 additions & 5 deletions app/ide-desktop/client/src/globals.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,12 +188,12 @@ declare global {
// @ts-expect-error The index signature is intentional to disallow unknown env vars.
readonly ENSO_SUPPORTS_VIBRANCY?: string

// === E2E test variables ===
// === Integration test variables ===

// @ts-expect-error The index signature is intentional to disallow unknown env vars.
readonly IS_IN_PLAYWRIGHT_TEST?: `${boolean}`
// @ts-expect-error The index signature is intentional to disallow unknown env vars.
readonly PWDEBUG?: '1'
readonly ENSO_TEST?: string
readonly ENSO_TEST_USER?: string
readonly ENSO_TEST_USER_PASSWORD?: string
ENSO_TEST_EXEC_PATH?: string

// === Electron watch script variables ===

Expand Down
3 changes: 3 additions & 0 deletions app/ide-desktop/client/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,9 @@ class App {
enableBlinkFeatures: argGroups.chrome.options.enableBlinkFeatures.value,
disableBlinkFeatures: argGroups.chrome.options.disableBlinkFeatures.value,
spellcheck: false,
...(process.env.ENSO_TEST != null && process.env.ENSO_TEST !== '' ?
{ partition: 'test' }
: {}),
}
const windowPreferences: electron.BrowserWindowConstructorOptions = {
webPreferences,
Expand Down
12 changes: 8 additions & 4 deletions app/ide-desktop/client/src/projectManagement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -384,11 +384,15 @@ export function getProjectRoot(subtreePath: string): string | null {

/** Get the directory that stores Enso projects. */
export function getProjectsDirectory(): string {
const documentsPath = desktopEnvironment.DOCUMENTS
if (documentsPath === undefined) {
return pathModule.join(os.homedir(), 'enso', 'projects')
if (process.env.ENSO_TEST != null && process.env.ENSO_TEST !== '') {
return pathModule.join(os.tmpdir(), 'enso-test-projects', process.env.ENSO_TEST)
} else {
return pathModule.join(documentsPath, 'enso-projects')
const documentsPath = desktopEnvironment.DOCUMENTS
if (documentsPath === undefined) {
return pathModule.join(os.homedir(), 'enso', 'projects')
} else {
return pathModule.join(documentsPath, 'enso-projects')
}
}
}

Expand Down
13 changes: 13 additions & 0 deletions app/ide-desktop/client/tests/createNewProject.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/** @file A test for basic flow of the application: open project and see if nodes appear. */

/* eslint-disable @typescript-eslint/no-magic-numbers */

import { expect } from '@playwright/test'
import { electronTest, loginAsTestUser } from './electronTest'

electronTest('Create new project', async page => {
await loginAsTestUser(page)
await expect(page.getByRole('button', { name: 'New Project', exact: true })).toBeVisible()
await page.getByRole('button', { name: 'New Project', exact: true }).click()
await expect(page.locator('.GraphNode'), {}).toBeVisible({ timeout: 30000 })
})
47 changes: 47 additions & 0 deletions app/ide-desktop/client/tests/electronTest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/** @file Commonly used functions for electron tests */

import { _electron, expect, type Page, test } from '@playwright/test'

/**
* Tests run on electron executable.
*
* Similar to playwright's test, but launches electron, and passes Page of the main window.
*/
export function electronTest(name: string, body: (page: Page) => Promise<void> | void) {
test(name, async () => {
const app = await _electron.launch({
executablePath: process.env.ENSO_TEST_EXEC_PATH ?? '',
env: { ...process.env, ['ENSO_TEST']: name },
})
const page = await app.firstWindow()
await body(page)
await app.close()
})
}

/**
* Login as test user. This function asserts that page is the login page, and uses
* credentials from ENSO_TEST_USER and ENSO_TEST_USER_PASSWORD env variables.
*/
export async function loginAsTestUser(page: Page) {
// Login screen
await expect(page.getByRole('textbox', { name: 'email' })).toBeVisible()
await expect(page.getByRole('textbox', { name: 'password' })).toBeVisible()
if (process.env.ENSO_TEST_USER == null || process.env.ENSO_TEST_USER_PASSWORD == null) {
throw Error(
'Cannot log in; `ENSO_TEST_USER` and `ENSO_TEST_USER_PASSWORD` env variables are not provided',
)
}
await page.keyboard.insertText(process.env.ENSO_TEST_USER)
await page.keyboard.press('Tab')
await page.keyboard.insertText(process.env.ENSO_TEST_USER_PASSWORD)
await page.keyboard.press('Enter')

// Accept terms screen
await expect(page.getByText('I agree')).toHaveCount(2)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i suppose this is sufficient for now. but we will probably want to extract dashboard and/or GUI actions to a shared package

await expect(page.getByRole('button')).toHaveCount(1)
for (const checkbox of await page.getByText('I agree').all()) {
await checkbox.click()
}
await page.getByRole('button').click()
}
30 changes: 30 additions & 0 deletions app/ide-desktop/client/tests/setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/** @file {@link setup} function for all tests. */

import * as fs from 'node:fs'

const POSSIBLE_EXEC_PATHS = [
'../../../dist/ide/linux-unpacked/enso',
'../../../dist/ide/win-unpacked/Enso.exe',
'../../../dist/ide/mac/Enso.app/Contents/MacOS/Enso',
'../../../dist/ide/mac-arm64/Enso.app/Contents/MacOS/Enso',
]

/**
* Setup for all tests: checks if and where electron exec is.
* @throws when no Enso package could be found.
*/
export default function setup() {
const execPath = POSSIBLE_EXEC_PATHS.find(path => {
try {
fs.accessSync(path, fs.constants.X_OK)
return true
} catch (err) {
return false
}
})
if (execPath != null) {
process.env.ENSO_TEST_EXEC_PATH = execPath
} else {
throw Error('Cannot find Enso package')
}
}
6 changes: 6 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading