Skip to content

Commit e22705b

Browse files
committed
test: migrate E2E tests to puppeteer instead of protractor
1 parent 8a29544 commit e22705b

File tree

6 files changed

+92
-2
lines changed

6 files changed

+92
-2
lines changed

tests/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ e2e_suites(
7373
# TODO: Clean this up.
7474
"//:node_modules/express",
7575
"//:node_modules/undici",
76+
"//:node_modules/puppeteer",
7677
],
7778
runner = ":runner_entrypoint",
7879
)

tests/e2e/tests/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ ts_project(
1212
"//:node_modules/@types/semver",
1313
"//:node_modules/express",
1414
"//:node_modules/fast-glob",
15+
"//:node_modules/puppeteer",
1516
"//:node_modules/semver",
1617
"//:node_modules/undici",
1718
"//tests/e2e/utils",

tests/e2e/tests/build/jit-prod.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { getGlobalVariable } from '../../utils/env';
22
import { ng } from '../../utils/process';
33
import { updateJsonFile } from '../../utils/project';
4+
import { runE2eTest } from '../../utils/puppeteer';
45

56
export default async function () {
67
// Make prod use JIT.
@@ -18,5 +19,5 @@ export default async function () {
1819
});
1920

2021
// Test it works
21-
await ng('e2e', '--configuration=production');
22+
await runE2eTest({ configuration: 'production' });
2223
}

tests/e2e/utils/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ ts_project(
1515
"//:node_modules/@types/semver",
1616
"//:node_modules/fast-glob",
1717
"//:node_modules/protractor",
18+
"//:node_modules/puppeteer",
1819
"//:node_modules/semver",
1920
"//:node_modules/verdaccio",
2021
"//:node_modules/verdaccio-auth-memory",

tests/e2e/utils/puppeteer.ts

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import { type Page, launch } from 'puppeteer';
2+
import { execAndWaitForOutputToMatch, killAllProcesses } from './process';
3+
4+
export interface E2eTestOptions {
5+
project?: string;
6+
configuration?: string;
7+
baseUrl?: string;
8+
checkFn?: (page: Page) => Promise<void>;
9+
expectedTitleText?: string;
10+
}
11+
12+
export async function runE2eTest(options: E2eTestOptions = {}) {
13+
let url = options.baseUrl;
14+
let hasStartedServer = false;
15+
16+
try {
17+
if (!url) {
18+
// Start serving
19+
const match = /open your browser on (http:\/\/localhost:\d+\/)/;
20+
const serveArgs = ['serve', '--port=0'];
21+
if (options.project) {
22+
serveArgs.push(options.project);
23+
}
24+
if (options.configuration) {
25+
serveArgs.push(`--configuration=${options.configuration}`);
26+
}
27+
28+
const { stdout } = await execAndWaitForOutputToMatch('ng', serveArgs, match);
29+
url = stdout.match(match)?.[1];
30+
if (!url) {
31+
throw new Error('Could not find serving URL');
32+
}
33+
hasStartedServer = true;
34+
}
35+
36+
const browser = await launch({
37+
executablePath: process.env['CHROME_BIN'],
38+
headless: true,
39+
args: ['--no-sandbox'],
40+
});
41+
try {
42+
const page = await browser.newPage();
43+
44+
// Capture errors
45+
const errors: string[] = [];
46+
page.on('console', (msg) => {
47+
if (msg.type() === 'error') {
48+
errors.push(msg.text());
49+
}
50+
});
51+
page.on('pageerror', (err) => {
52+
errors.push(err.toString());
53+
});
54+
55+
await page.goto(url);
56+
57+
if (options.checkFn) {
58+
await options.checkFn(page);
59+
} else {
60+
// Default check: verify h1 content and no browser errors
61+
const expectedText = options.expectedTitleText || 'Hello, test-project';
62+
63+
// Wait for the h1 element to appear and contain the expected text
64+
await page.waitForFunction(
65+
(selector: string, text: string) => {
66+
const doc = (globalThis as any).document;
67+
return doc.querySelector(selector)?.textContent?.includes(text);
68+
},
69+
{ timeout: 5000 }, // Max 5 seconds wait time
70+
'h1',
71+
expectedText,
72+
);
73+
}
74+
75+
if (errors.length > 0) {
76+
throw new Error(`Browser console errors detected:\n${errors.join('\n')}`);
77+
}
78+
} finally {
79+
await browser.close();
80+
}
81+
} finally {
82+
if (hasStartedServer) {
83+
await killAllProcesses();
84+
}
85+
}
86+
}

tests/rollup.config.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ for (const file of testFiles) {
2525

2626
export default {
2727
input: chunks,
28-
external: ['undici'], // This cannot be bundled as `node:sqlite` is experimental in node.js 22. Remove once this feature is no longer behind a flag
28+
external: ['undici', 'puppeteer'], // This cannot be bundled as `node:sqlite` is experimental in node.js 22. Remove once this feature is no longer behind a flag
2929
plugins: [
3030
nodeResolve({
3131
preferBuiltins: true,

0 commit comments

Comments
 (0)