Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 397abf5

Browse files
committed
test: migrate build and misc E2E tests to Puppeteer
Replaces the Protractor-based `ng e2e` execution with the new Puppeteer `executeBrowserTest` utility in various build and misc E2E tests. The following tests were updated: - express-engine-csp-nonce - express-engine-ngmodule - express-engine-standalone - app-shell-with-service-worker - auto-csp - worker - trusted-types This migration involves implementing custom `checkFn` logic to replicate the assertions previously handled by Protractor, such as verifying server-rendered content, console logs, and service worker states.
1 parent 95e35e5 commit 397abf5

File tree

7 files changed

+232
-344
lines changed

7 files changed

+232
-344
lines changed

‎tests/e2e/tests/build/app-shell/app-shell-with-service-worker.ts‎

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { appendToFile, expectFileToMatch, writeFile } from '../../../utils/fs';
33
import { installPackage } from '../../../utils/packages';
44
import { ng } from '../../../utils/process';
55
import { updateJsonFile } from '../../../utils/project';
6+
import { executeBrowserTest } from '../../../utils/puppeteer';
67

78
const snapshots = require('../../../ng-snapshot/package.json');
89

@@ -31,26 +32,26 @@ export default async function () {
3132
}
3233
}
3334

34-
await writeFile(
35-
'e2e/app.e2e-spec.ts',
36-
`
37-
import { browser, by, element } from 'protractor';
35+
await ng('build');
36+
await expectFileToMatch('dist/test-project/browser/index.html', /app-shellworks!/);
3837

39-
it('should have ngsw in normal state', () => {
40-
browser.get('/');
38+
await executeBrowserTest({
39+
configuration: 'production',
40+
checkFn: async (page) => {
4141
// Wait for service worker to load.
42-
browser.sleep(2000);
43-
browser.waitForAngularEnabled(false);
44-
browser.get('/ngsw/state');
45-
// Should have updated, and be in normal state.
46-
expect(element(by.css('pre')).getText()).not.toContain('Last update check: never');
47-
expect(element(by.css('pre')).getText()).toContain('Driver state: NORMAL');
48-
});
49-
`,
50-
);
42+
await new Promise((resolve) => setTimeout(resolve, 2000));
5143

52-
awaitng('build');
53-
await expectFileToMatch('dist/test-project/browser/index.html', /app-shellworks!/);
44+
constbaseUrl=page.url();
45+
await page.goto(newURL('/ngsw/state', baseUrl).href);
5446

55-
await ng('e2e', '--configuration=production');
47+
// Should have updated, and be in normal state.
48+
const preText = await page.$eval('pre', (el) => el.textContent);
49+
if (preText?.includes('Last update check: never')) {
50+
throw new Error(`Expected service worker to have checked for updates, but got: ${preText}`);
51+
}
52+
if (!preText?.includes('Driver state: NORMAL')) {
53+
throw new Error(`Expected service worker driver state to be NORMAL, but got: ${preText}`);
54+
}
55+
},
56+
});
5657
}

‎tests/e2e/tests/build/auto-csp.ts‎

Lines changed: 46 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import assert from 'node:assert';
2+
import { setTimeout } from 'node:timers/promises';
23
import { getGlobalVariable } from '../../utils/env';
34
import { expectFileToMatch, writeFile, writeMultipleFiles } from '../../utils/fs';
45
import { findFreePort } from '../../utils/network';
56
import { execAndWaitForOutputToMatch, ng } from '../../utils/process';
67
import { updateJsonFile } from '../../utils/project';
8+
import { executeBrowserTest } from '../../utils/puppeteer';
79

810
const CSP_META_TAG = /<metahttp-equiv="Content-Security-Policy"/;
911

@@ -67,55 +69,6 @@ export default async function () {
6769
</body>
6870
</html>
6971
`,
70-
'e2e/src/app.e2e-spec.ts': `
71-
import { browser, by, element } from 'protractor';
72-
import * as webdriver from 'selenium-webdriver';
73-
74-
function allConsoleWarnMessagesAndErrors() {
75-
return browser
76-
.manage()
77-
.logs()
78-
.get('browser')
79-
.then(function (browserLog: any[]) {
80-
const warnMessages: any[] = [];
81-
browserLog.filter((logEntry) => {
82-
const msg = logEntry.message;
83-
console.log('>> ' + msg);
84-
if (logEntry.level.value >= webdriver.logging.Level.INFO.value) {
85-
warnMessages.push(msg);
86-
}
87-
});
88-
return warnMessages;
89-
});
90-
}
91-
92-
describe('Hello world E2E Tests', () => {
93-
beforeAll(async () => {
94-
await browser.waitForAngularEnabled(true);
95-
});
96-
97-
it('should display: Welcome and run all scripts in order', async () => {
98-
// Load the page without waiting for Angular since it is not bootstrapped automatically.
99-
await browser.driver.get(browser.baseUrl);
100-
101-
// Test the contents.
102-
expect(await element(by.css('h1')).getText()).toMatch('Hello');
103-
104-
// Make sure all scripts ran and there were no client side errors.
105-
const consoleMessages = await allConsoleWarnMessagesAndErrors();
106-
expect(consoleMessages.length).toEqual(4); // No additional errors
107-
// Extract just the printed messages from the console data.
108-
const printedMessages = consoleMessages.map(m => m.match(/"(.*?)"/)[1]);
109-
expect(printedMessages).toEqual([
110-
// All messages printed in order because execution order is preserved.
111-
"Inline Script Head",
112-
"Inline Script Body: 1339",
113-
"First External Script: 1338",
114-
"Second External Script: 1337",
115-
]);
116-
});
117-
});
118-
`,
11972
});
12073

12174
async function spawnServer(): Promise<number> {
@@ -137,7 +90,49 @@ export default async function () {
13790
// Make sure if contains the critical CSS inlining CSP code.
13891
await expectFileToMatch('dist/test-project/browser/index.html', 'ngCspMedia');
13992

140-
// Make sure that our e2e protractor tests run to confirm that our angular project runs.
93+
// Make sure that our e2e tests run to confirm that our angular project runs.
14194
const port = await spawnServer();
142-
await ng('e2e', `--base-url=http://localhost:${port}`, '--dev-server-target=');
95+
await executeBrowserTest({
96+
baseUrl: `http://localhost:${port}/`,
97+
checkFn: async (page) => {
98+
const warnMessages: string[] = [];
99+
page.on('console', (msg) => {
100+
if (msg.type() === 'warning') {
101+
warnMessages.push(msg.text());
102+
}
103+
});
104+
105+
// Reload to ensure we capture messages from the start if needed,
106+
// although executeBrowserTest already navigated.
107+
await page.reload();
108+
109+
// Wait for the expected number of warnings
110+
let retries = 50;
111+
while (warnMessages.length < 4 && retries > 0) {
112+
await setTimeout(100);
113+
retries--;
114+
}
115+
116+
if (warnMessages.length !== 4) {
117+
throw new Error(
118+
`Expected 4 console warnings, but got ${warnMessages.length}:\n${warnMessages.join('\n')}`,
119+
);
120+
}
121+
122+
const expectedMessages = [
123+
'Inline Script Head',
124+
'Inline Script Body: 1339',
125+
'First External Script: 1338',
126+
'Second External Script: 1337',
127+
];
128+
129+
for (let i = 0; i < expectedMessages.length; i++) {
130+
if (!warnMessages[i].includes(expectedMessages[i])) {
131+
throw new Error(
132+
`Expected warning ${i} to include '${expectedMessages[i]}', but got '${warnMessages[i]}'`,
133+
);
134+
}
135+
}
136+
},
137+
});
143138
}

‎tests/e2e/tests/build/server-rendering/express-engine-csp-nonce.ts‎

Lines changed: 48 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { findFreePort } from '../../../utils/network';
44
import { installWorkspacePackages } from '../../../utils/packages';
55
import { execAndWaitForOutputToMatch, ng } from '../../../utils/process';
66
import { updateJsonFile, updateServerFileForEsbuild, useSha } from '../../../utils/project';
7+
import { executeBrowserTest } from '../../../utils/puppeteer';
78

89
export default async function () {
910
const useWebpackBuilder = !getGlobalVariable('argv')['esbuild'];
@@ -46,90 +47,6 @@ export default async function () {
4647
</body>
4748
</html>
4849
`,
49-
'e2e/src/app.e2e-spec.ts': `
50-
import { browser, by, element } from 'protractor';
51-
import * as webdriver from 'selenium-webdriver';
52-
53-
function verifyNoBrowserErrors() {
54-
return browser
55-
.manage()
56-
.logs()
57-
.get('browser')
58-
.then(function (browserLog: any[]) {
59-
const errors: any[] = [];
60-
browserLog.filter((logEntry) => {
61-
const msg = logEntry.message;
62-
console.log('>> ' + msg);
63-
if (logEntry.level.value >= webdriver.logging.Level.INFO.value) {
64-
errors.push(msg);
65-
}
66-
});
67-
expect(errors).toEqual([]);
68-
});
69-
}
70-
71-
describe('Hello world E2E Tests', () => {
72-
beforeAll(async () => {
73-
await browser.waitForAngularEnabled(false);
74-
});
75-
76-
it('should display: Welcome', async () => {
77-
// Load the page without waiting for Angular since it is not bootstrapped automatically.
78-
await browser.driver.get(browser.baseUrl);
79-
80-
expect(
81-
await element(by.css('style[ng-app-id="ng"]')).getText()
82-
).not.toBeNull();
83-
84-
// Test the contents from the server.
85-
expect(await element(by.css('h1')).getText()).toMatch('Hello');
86-
87-
// Bootstrap the client side app.
88-
await browser.executeScript('doBootstrap()');
89-
90-
// Retest the contents after the client bootstraps.
91-
expect(await element(by.css('h1')).getText()).toMatch('Hello');
92-
93-
// Make sure the server styles got replaced by client side ones.
94-
expect(
95-
await element(by.css('style[ng-app-id="ng"]')).isPresent()
96-
).toBeFalsy();
97-
expect(await element(by.css('style')).getText()).toMatch('');
98-
99-
// Make sure there were no client side errors.
100-
await verifyNoBrowserErrors();
101-
});
102-
103-
it('stylesheets should be configured to load asynchronously', async () => {
104-
// Load the page without waiting for Angular since it is not bootstrapped automatically.
105-
await browser.driver.get(browser.baseUrl);
106-
107-
// Test the contents from the server.
108-
const linkTag = await browser.driver.findElement(
109-
by.css('link[rel="stylesheet"]')
110-
);
111-
expect(await linkTag.getAttribute('media')).toMatch('all');
112-
expect(await linkTag.getAttribute('ngCspMedia')).toBeNull();
113-
expect(await linkTag.getAttribute('onload')).toBeNull();
114-
115-
// Make sure there were no client side errors.
116-
await verifyNoBrowserErrors();
117-
});
118-
119-
it('style tags all have a nonce attribute', async () => {
120-
// Load the page without waiting for Angular since it is not bootstrapped automatically.
121-
await browser.driver.get(browser.baseUrl);
122-
123-
// Test the contents from the server.
124-
for (const s of await browser.driver.findElements(by.css('style'))) {
125-
expect(await s.getAttribute('nonce')).toBe('{% nonce %}');
126-
}
127-
128-
// Make sure there were no client side errors.
129-
await verifyNoBrowserErrors();
130-
});
131-
});
132-
`,
13350
});
13451

13552
async function spawnServer(): Promise<number> {
@@ -158,5 +75,51 @@ export default async function () {
15875
}
15976

16077
const port = await spawnServer();
161-
await ng('e2e', `--base-url=http://localhost:${port}`, '--dev-server-target=');
78+
await executeBrowserTest({
79+
baseUrl: `http://localhost:${port}/`,
80+
checkFn: async (page) => {
81+
// Test the contents from the server.
82+
const h1Text = await page.$eval('h1', (el) => el.textContent);
83+
if (!h1Text?.includes('Hello')) {
84+
throw new Error(`Expected h1 to contain 'Hello', but got '${h1Text}'`);
85+
}
86+
87+
const serverStylePresent = await page.evaluate(
88+
() => !!(globalThis as any).document.querySelector('style[ng-app-id="ng"]'),
89+
);
90+
if (!serverStylePresent) {
91+
throw new Error('Expected server-side style to be present');
92+
}
93+
94+
// style tags all have a nonce attribute
95+
const nonces = await page.$$eval('style', (styles) =>
96+
styles.map((s) => s.getAttribute('nonce')),
97+
);
98+
for (const nonce of nonces) {
99+
if (nonce !== '{% nonce %}') {
100+
throw new Error(`Expected nonce to be '{% nonce %}', but got '${nonce}'`);
101+
}
102+
}
103+
104+
// stylesheets should be configured to load asynchronously
105+
const linkMedia = await page.$eval('link[rel="stylesheet"]', (el) =>
106+
el.getAttribute('media'),
107+
);
108+
if (linkMedia !== 'all') {
109+
throw new Error(`Expected link media to be 'all', but got '${linkMedia}'`);
110+
}
111+
112+
// Bootstrap the client side app.
113+
await page.evaluate('window.doBootstrap()');
114+
115+
// Wait for server style to be removed by client
116+
await page.waitForSelector('style[ng-app-id="ng"]', { hidden: true });
117+
118+
// Retest the contents after the client bootstraps.
119+
const h1TextPost = await page.$eval('h1', (el) => el.textContent);
120+
if (!h1TextPost?.includes('Hello')) {
121+
throw new Error(`Expected h1 to contain 'Hello' after bootstrap, but got '${h1TextPost}'`);
122+
}
123+
},
124+
});
162125
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /