Skip to content

Commit 2ef8e26

Browse files
authored
test: structure tests to use environments, closer to end user (#1713)
1 parent be06bb0 commit 2ef8e26

File tree

5 files changed

+195
-153
lines changed

5 files changed

+195
-153
lines changed

test/playwright.spec.js

Lines changed: 139 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -14,107 +14,141 @@
1414
* See the License for the specific language governing permissions and
1515
* limitations under the License.
1616
*/
17+
1718
const fs = require('fs');
1819
const path = require('path');
1920
const rm = require('rimraf').sync;
2021
const readline = require('readline');
2122
const {TestServer} = require('../utils/testserver/');
23+
const {Environment} = require('../utils/testrunner/Test');
24+
25+
const serverEnvironment = new Environment('TestServer');
26+
serverEnvironment.beforeAll(async state => {
27+
const assetsPath = path.join(__dirname, 'assets');
28+
const cachedPath = path.join(__dirname, 'assets', 'cached');
29+
30+
const port = 8907 + state.parallelIndex * 3;
31+
state.server = await TestServer.create(assetsPath, port);
32+
state.server.enableHTTPCache(cachedPath);
33+
state.server.PORT = port;
34+
state.server.PREFIX = `http://localhost:${port}`;
35+
state.server.CROSS_PROCESS_PREFIX = `http://127.0.0.1:${port}`;
36+
state.server.EMPTY_PAGE = `http://localhost:${port}/empty.html`;
37+
38+
const httpsPort = port + 1;
39+
state.httpsServer = await TestServer.createHTTPS(assetsPath, httpsPort);
40+
state.httpsServer.enableHTTPCache(cachedPath);
41+
state.httpsServer.PORT = httpsPort;
42+
state.httpsServer.PREFIX = `https://localhost:${httpsPort}`;
43+
state.httpsServer.CROSS_PROCESS_PREFIX = `https://127.0.0.1:${httpsPort}`;
44+
state.httpsServer.EMPTY_PAGE = `https://localhost:${httpsPort}/empty.html`;
2245

23-
const YELLOW_COLOR = '\x1b[33m';
24-
const RESET_COLOR = '\x1b[0m';
46+
const sourcePort = port + 2;
47+
state.sourceServer = await TestServer.create(path.join(__dirname, '..'), sourcePort);
48+
state.sourceServer.PORT = sourcePort;
49+
state.sourceServer.PREFIX = `http://localhost:${sourcePort}`;
50+
});
51+
serverEnvironment.afterAll(async({server, sourceServer, httpsServer}) => {
52+
await Promise.all([
53+
server.stop(),
54+
httpsServer.stop(),
55+
sourceServer.stop(),
56+
]);
57+
});
58+
serverEnvironment.beforeEach(async({server, httpsServer}) => {
59+
server.reset();
60+
httpsServer.reset();
61+
});
62+
63+
const goldenEnvironment = new Environment('Golden');
64+
goldenEnvironment.beforeAll(async ({browserType}) => {
65+
const { OUTPUT_DIR, GOLDEN_DIR } = require('./utils').testOptions(browserType);
66+
if (fs.existsSync(OUTPUT_DIR))
67+
rm(OUTPUT_DIR);
68+
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
69+
expect.setupGolden(GOLDEN_DIR, OUTPUT_DIR);
70+
});
2571

2672
/**
2773
* @type {TestSuite}
2874
*/
29-
module.exports.addPlaywrightTests = ({platform, products, playwrightPath, headless, slowMo, dumpProtocolOnFailure, coverage}) => {
30-
const MAC = platform === 'darwin';
31-
const LINUX = platform === 'linux';
32-
const WIN = platform === 'win32';
75+
module.exports.addPlaywrightTests = ({testRunner, products}) => {
76+
const dumpProtocolOnFailure = valueFromEnv('DEBUGP', false);
77+
const playwrightPath = require('./utils').projectRoot();
3378
const playwright = require(playwrightPath);
3479

35-
beforeAll(async state => {
36-
const assetsPath = path.join(__dirname, 'assets');
37-
const cachedPath = path.join(__dirname, 'assets', 'cached');
38-
39-
const port = 8907 + state.parallelIndex * 3;
40-
state.server = await TestServer.create(assetsPath, port);
41-
state.server.enableHTTPCache(cachedPath);
42-
state.server.PORT = port;
43-
state.server.PREFIX = `http://localhost:${port}`;
44-
state.server.CROSS_PROCESS_PREFIX = `http://127.0.0.1:${port}`;
45-
state.server.EMPTY_PAGE = `http://localhost:${port}/empty.html`;
46-
47-
const httpsPort = port + 1;
48-
state.httpsServer = await TestServer.createHTTPS(assetsPath, httpsPort);
49-
state.httpsServer.enableHTTPCache(cachedPath);
50-
state.httpsServer.PORT = httpsPort;
51-
state.httpsServer.PREFIX = `https://localhost:${httpsPort}`;
52-
state.httpsServer.CROSS_PROCESS_PREFIX = `https://127.0.0.1:${httpsPort}`;
53-
state.httpsServer.EMPTY_PAGE = `https://localhost:${httpsPort}/empty.html`;
54-
55-
const sourcePort = port + 2;
56-
state.sourceServer = await TestServer.create(path.join(__dirname, '..'), sourcePort);
57-
state.sourceServer.PORT = sourcePort;
58-
state.sourceServer.PREFIX = `http://localhost:${sourcePort}`;
80+
const playwrightEnvironment = new Environment('Playwright');
81+
playwrightEnvironment.beforeAll(async state => {
82+
state.playwright = playwright;
5983
});
60-
61-
afterAll(async({server, sourceServer, httpsServer}) => {
62-
await Promise.all([
63-
server.stop(),
64-
httpsServer.stop(),
65-
sourceServer.stop(),
66-
]);
84+
playwrightEnvironment.afterAll(async state => {
85+
delete state.playwright;
6786
});
6887

69-
beforeEach(async({server, httpsServer}) => {
70-
server.reset();
71-
httpsServer.reset();
72-
});
88+
for (const product of products) {
89+
const browserTypeEnvironment = new Environment('BrowserType');
90+
browserTypeEnvironment.beforeAll(async state => {
91+
state.browserType = state.playwright[product.toLowerCase()];
92+
});
93+
browserTypeEnvironment.afterAll(async state => {
94+
delete state.browserType;
95+
});
7396

74-
for (const productInfo of products) {
75-
const product = productInfo.product;
76-
const browserType = playwright[product.toLowerCase()];
77-
const CHROMIUM = product === 'Chromium';
78-
const FFOX = product === 'Firefox';
79-
const WEBKIT = product === 'WebKit';
80-
const defaultBrowserOptions = {
81-
handleSIGINT: false,
82-
executablePath: productInfo.executablePath,
83-
slowMo,
84-
headless,
85-
};
86-
87-
if (defaultBrowserOptions.executablePath) {
88-
console.warn(`${YELLOW_COLOR}WARN: running ${product} tests with ${defaultBrowserOptions.executablePath}${RESET_COLOR}`);
89-
} else {
90-
// Make sure the `npm install` was run after the chromium roll.
91-
if (!fs.existsSync(browserType.executablePath()))
92-
throw new Error(`Browser is not downloaded. Run 'npm install' and try to re-run tests`);
93-
}
97+
const browserEnvironment = new Environment(product);
98+
browserEnvironment.beforeAll(async state => {
99+
const { defaultBrowserOptions } = require('./utils').testOptions(state.browserType);
100+
state.browser = await state.browserType.launch(defaultBrowserOptions);
101+
state.browserServer = state.browser._ownedServer;
102+
state._stdout = readline.createInterface({ input: state.browserServer.process().stdout });
103+
state._stderr = readline.createInterface({ input: state.browserServer.process().stderr });
104+
});
105+
browserEnvironment.afterAll(async state => {
106+
await state.browserServer.close();
107+
state.browser = null;
108+
state.browserServer = null;
109+
state._stdout.close();
110+
state._stderr.close();
111+
});
112+
browserEnvironment.beforeEach(async(state, testRun) => {
113+
const dumpout = data => testRun.log(`\x1b[33m[pw:stdio:out]\x1b[0m ${data}`);
114+
const dumperr = data => testRun.log(`\x1b[31m[pw:stdio:err]\x1b[0m ${data}`);
115+
state._stdout.on('line', dumpout);
116+
state._stderr.on('line', dumperr);
117+
if (dumpProtocolOnFailure)
118+
state.browser._debugProtocol.log = data => testRun.log(`\x1b[32m[pw:protocol]\x1b[0m ${data}`);
119+
state.tearDown = async () => {
120+
state._stdout.off('line', dumpout);
121+
state._stderr.off('line', dumperr);
122+
if (dumpProtocolOnFailure)
123+
delete state.browser._debugProtocol.log;
124+
};
125+
});
126+
browserEnvironment.afterEach(async (state, test) => {
127+
if (state.browser.contexts().length !== 0) {
128+
if (test.result === 'ok')
129+
console.warn(`\nWARNING: test "${test.fullName()}" (${test.location()}) did not close all created contexts!\n`);
130+
await Promise.all(state.browser.contexts().map(context => context.close()));
131+
}
132+
await state.tearDown();
133+
});
94134

95-
const GOLDEN_DIR = path.join(__dirname, 'golden-' + product.toLowerCase());
96-
const OUTPUT_DIR = path.join(__dirname, 'output-' + product.toLowerCase());
97-
if (fs.existsSync(OUTPUT_DIR))
98-
rm(OUTPUT_DIR);
99-
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
100-
expect.setupGolden(GOLDEN_DIR, OUTPUT_DIR);
101-
102-
const testOptions = {
103-
FFOX,
104-
WEBKIT,
105-
CHROMIUM,
106-
MAC,
107-
LINUX,
108-
WIN,
109-
browserType,
110-
playwright,
111-
defaultBrowserOptions,
112-
playwrightPath,
113-
headless: !!defaultBrowserOptions.headless,
114-
OUTPUT_DIR,
115-
};
135+
const pageEnvironment = new Environment('Page');
136+
pageEnvironment.beforeEach(async state => {
137+
state.context = await state.browser.newContext();
138+
state.page = await state.context.newPage();
139+
});
140+
pageEnvironment.afterEach(async state => {
141+
await state.context.close();
142+
state.context = null;
143+
state.page = null;
144+
});
116145

117146
function loadTests(modulePath) {
147+
const testOptions = {
148+
...require('./utils').testOptions(global.browserType),
149+
playwright: global.playwright,
150+
browserType: global.browserType,
151+
};
118152
const module = require(modulePath);
119153
if (typeof module.describe === 'function')
120154
describe('', module.describe, testOptions);
@@ -124,58 +158,22 @@ module.exports.addPlaywrightTests = ({platform, products, playwrightPath, headle
124158
xdescribe('', module.xdescribe, testOptions);
125159
}
126160

127-
describe(product, () => {
128-
describe('', function() {
129-
beforeAll(async state => {
130-
state.browser = await browserType.launch(defaultBrowserOptions);
131-
state.browserServer = state.browser._ownedServer;
132-
state._stdout = readline.createInterface({ input: state.browserServer.process().stdout });
133-
state._stderr = readline.createInterface({ input: state.browserServer.process().stderr });
134-
});
161+
testRunner.collector().useEnvironment(serverEnvironment); // Custom global environment.
162+
testRunner.collector().useEnvironment(playwrightEnvironment);
135163

136-
afterAll(async state => {
137-
await state.browserServer.close();
138-
state.browser = null;
139-
state.browserServer = null;
140-
state._stdout.close();
141-
state._stderr.close();
142-
});
164+
describe(product, () => {
165+
// In addition to state, expose these two on global so that describes can access them.
166+
global.playwright = playwright;
167+
global.browserType = playwright[product.toLowerCase()];
143168

144-
beforeEach(async(state, testRun) => {
145-
const dumpout = data => testRun.log(`\x1b[33m[pw:stdio:out]\x1b[0m ${data}`);
146-
const dumperr = data => testRun.log(`\x1b[31m[pw:stdio:err]\x1b[0m ${data}`);
147-
state._stdout.on('line', dumpout);
148-
state._stderr.on('line', dumperr);
149-
if (dumpProtocolOnFailure)
150-
state.browser._debugProtocol.log = data => testRun.log(`\x1b[32m[pw:protocol]\x1b[0m ${data}`);
151-
state.tearDown = async () => {
152-
state._stdout.off('line', dumpout);
153-
state._stderr.off('line', dumperr);
154-
if (dumpProtocolOnFailure)
155-
delete state.browser._debugProtocol.log;
156-
};
157-
});
169+
testRunner.collector().useEnvironment(browserTypeEnvironment);
170+
testRunner.collector().useEnvironment(goldenEnvironment); // Custom environment.
158171

159-
afterEach(async (state, test) => {
160-
if (state.browser.contexts().length !== 0) {
161-
if (test.result === 'ok')
162-
console.warn(`\nWARNING: test "${test.fullName()}" (${test.location()}) did not close all created contexts!\n`);
163-
await Promise.all(state.browser.contexts().map(context => context.close()));
164-
}
165-
await state.tearDown();
166-
});
172+
describe('', function() {
173+
testRunner.collector().useEnvironment(browserEnvironment);
167174

168175
describe('', function() {
169-
beforeEach(async state => {
170-
state.context = await state.browser.newContext();
171-
state.page = await state.context.newPage();
172-
});
173-
174-
afterEach(async state => {
175-
await state.context.close();
176-
state.context = null;
177-
state.page = null;
178-
});
176+
testRunner.collector().useEnvironment(pageEnvironment);
179177

180178
// Page-level tests that are given a browser, a context and a page.
181179
// Each test is launched in a new browser context.
@@ -210,7 +208,7 @@ module.exports.addPlaywrightTests = ({platform, products, playwrightPath, headle
210208
loadTests('./permissions.spec.js');
211209
});
212210

213-
describe.skip(!CHROMIUM)('[Chromium]', () => {
211+
describe.skip(product !== 'Chromium')('[Chromium]', () => {
214212
loadTests('./chromium/chromium.spec.js');
215213
loadTests('./chromium/coverage.spec.js');
216214
loadTests('./chromium/pdf.spec.js');
@@ -236,14 +234,23 @@ module.exports.addPlaywrightTests = ({platform, products, playwrightPath, headle
236234
loadTests('./multiclient.spec.js');
237235
});
238236

239-
describe.skip(!CHROMIUM)('[Chromium]', () => {
237+
describe.skip(product !== 'Chromium')('[Chromium]', () => {
240238
loadTests('./chromium/launcher.spec.js');
241239
loadTests('./chromium/oopif.spec.js');
242240
loadTests('./chromium/tracing.spec.js');
243241
});
244242

245-
if (coverage)
243+
if (process.env.COVERAGE)
246244
loadTests('./apicoverage.spec.js');
245+
246+
delete global.browserType;
247+
delete global.playwright;
247248
});
248249
}
249250
};
251+
252+
function valueFromEnv(name, defaultValue) {
253+
if (!(name in process.env))
254+
return defaultValue;
255+
return JSON.parse(process.env[name]);
256+
}

test/test.js

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -50,34 +50,15 @@ utils.setupTestRunner(testRunner);
5050

5151
console.log('Testing on Node', process.version);
5252

53-
const names = ['Chromium', 'Firefox', 'WebKit'].filter(name => {
53+
const products = ['Chromium', 'Firefox', 'WebKit'].filter(name => {
5454
return process.env.BROWSER === name.toLowerCase() || process.env.BROWSER === 'all';
5555
});
56-
const products = names.map(name => {
57-
const executablePath = {
58-
'Chromium': process.env.CRPATH,
59-
'Firefox': process.env.FFPATH,
60-
'WebKit': process.env.WKPATH,
61-
}[name];
62-
return { product: name, executablePath };
63-
});
64-
65-
function valueFromEnv(name, defaultValue) {
66-
if (!(name in process.env))
67-
return defaultValue;
68-
return JSON.parse(process.env[name]);
69-
}
7056

7157
for (const [key, value] of Object.entries(testRunner.api()))
7258
global[key] = value;
7359
require('./playwright.spec.js').addPlaywrightTests({
74-
playwrightPath: utils.projectRoot(),
60+
testRunner,
7561
products,
76-
platform: os.platform(),
77-
headless: !!valueFromEnv('HEADLESS', true),
78-
slowMo: valueFromEnv('SLOW_MO', 0),
79-
dumpProtocolOnFailure: valueFromEnv('DEBUGP', false),
80-
coverage: process.env.COVERAGE,
8162
});
8263
for (const [key, value] of Object.entries(testRunner.api())) {
8364
// expect is used when running tests, while the rest of api is not.

0 commit comments

Comments
 (0)