Skip to content
Open
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
2 changes: 1 addition & 1 deletion .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ jobs:
test_target_name: e2e.esbuild_node22
env:
E2E_SHARD_TOTAL: 1
TESTBRIDGE_TEST_ONLY: tests/basic/{build,rebuild}.ts
TESTBRIDGE_TEST_ONLY: tests/basic/{build,rebuild,serve}.ts

e2e-package-managers:
needs: build
Expand Down
1 change: 1 addition & 0 deletions tests/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ e2e_suites(
# TODO: Clean this up.
"//:node_modules/express",
"//:node_modules/undici",
"//:node_modules/puppeteer",
],
runner = ":runner_entrypoint",
)
1 change: 1 addition & 0 deletions tests/e2e/tests/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ ts_project(
"//:node_modules/@types/semver",
"//:node_modules/express",
"//:node_modules/fast-glob",
"//:node_modules/puppeteer",
"//:node_modules/semver",
"//:node_modules/undici",
"//tests/e2e/utils",
Expand Down
3 changes: 3 additions & 0 deletions tests/e2e/tests/basic/serve.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import assert from 'node:assert/strict';
import { killAllProcesses } from '../../utils/process';
import { ngServe } from '../../utils/project';
import { executeBrowserTest } from '../../utils/puppeteer';

export default async function () {
// Serve works without HMR
Expand All @@ -11,6 +12,8 @@ export default async function () {
// Serve works with HMR
const hmrPort = await ngServe('--hmr');
await verifyResponse(hmrPort);

await executeBrowserTest({ baseUrl: `http://localhost:${hmrPort}/` });
}

async function verifyResponse(port: number): Promise<void> {
Expand Down
4 changes: 2 additions & 2 deletions tests/e2e/tests/build/jit-prod.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { getGlobalVariable } from '../../utils/env';
import { ng } from '../../utils/process';
import { updateJsonFile } from '../../utils/project';
import { executeBrowserTest } from '../../utils/puppeteer';

export default async function () {
// Make prod use JIT.
Expand All @@ -18,5 +18,5 @@ export default async function () {
});

// Test it works
await ng('e2e', '--configuration=production');
await executeBrowserTest({ configuration: 'production' });
}
1 change: 1 addition & 0 deletions tests/e2e/utils/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ ts_project(
"//:node_modules/@types/semver",
"//:node_modules/fast-glob",
"//:node_modules/protractor",
"//:node_modules/puppeteer",
"//:node_modules/semver",
"//:node_modules/verdaccio",
"//:node_modules/verdaccio-auth-memory",
Expand Down
86 changes: 86 additions & 0 deletions tests/e2e/utils/puppeteer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { type Page, launch } from 'puppeteer';
import { execAndWaitForOutputToMatch, killAllProcesses } from './process';

export interface BrowserTestOptions {
project?: string;
configuration?: string;
baseUrl?: string;
checkFn?: (page: Page) => Promise<void>;
expectedTitleText?: string;
}

export async function executeBrowserTest(options: BrowserTestOptions = {}) {
let url = options.baseUrl;
let hasStartedServer = false;

try {
if (!url) {
// Start serving and find address (1 - Webpack; 2 - Vite)
const match = /(?:open your browser on|Local:)\s+(http:\/\/localhost:\d+\/)/;
const serveArgs = ['serve', '--port=0'];
if (options.project) {
serveArgs.push(options.project);
}
if (options.configuration) {
serveArgs.push(`--configuration=${options.configuration}`);
}

const { stdout } = await execAndWaitForOutputToMatch('ng', serveArgs, match);
url = stdout.match(match)?.[1];
if (!url) {
throw new Error('Could not find serving URL');
}
hasStartedServer = true;
}

const browser = await launch({
executablePath: process.env['CHROME_BIN'],
headless: true,
args: ['--no-sandbox'],
});
try {
const page = await browser.newPage();

// Capture errors
const errors: string[] = [];
page.on('console', (msg) => {
if (msg.type() === 'error') {
errors.push(msg.text());
}
});
page.on('pageerror', (err) => {
errors.push(err.toString());
});

await page.goto(url);

if (options.checkFn) {
await options.checkFn(page);
} else {
// Default check: verify h1 content and no browser errors
const expectedText = options.expectedTitleText || 'Hello, test-project';

// Wait for the h1 element to appear and contain the expected text
await page.waitForFunction(
(selector: string, text: string) => {
const doc = (globalThis as any).document;
return doc.querySelector(selector)?.textContent?.includes(text);
},
{ timeout: 10000 }, // Max 10 seconds wait time
'h1',
expectedText,
);
}

if (errors.length > 0) {
throw new Error(`Browser console errors detected:\n${errors.join('\n')}`);
}
} finally {
await browser.close();
}
} finally {
if (hasStartedServer) {
await killAllProcesses();
}
}
}
2 changes: 1 addition & 1 deletion tests/rollup.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ for (const file of testFiles) {

export default {
input: chunks,
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
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
plugins: [
nodeResolve({
preferBuiltins: true,
Expand Down