blob: 9925d84376636535385ec2defe03b8d05f9769cb [file] [log] [blame]
Paul Lewisb8b38012020-01-22 17:18:471// Copyright 2020 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Jack Frankline245d1a2020-02-13 15:25:135/* eslint-disable no-console */
6// no-console disabled here as this is a test runner and expects to output to the console
7
Paul Lewisb8b38012020-01-22 17:18:478import * as Mocha from 'mocha';
Paul Lewisb8b38012020-01-22 17:18:479import * as puppeteer from 'puppeteer';
Paul Lewisbd88d7f2020-02-05 17:06:4310import {spawn} from 'child_process';
11import {join} from 'path';
Paul Lewisb8b38012020-01-22 17:18:4712import {store} from './helper.js';
Paul Lewisb8b38012020-01-22 17:18:4713
Paul Lewis32473f02020-02-05 15:55:1914const testListPath = process.env['TEST_LIST'];
Paul Lewisb8b38012020-01-22 17:18:4715const envChromeBinary = process.env['CHROME_BIN'];
16const envDebug = !!process.env['DEBUG'];
17const envPort = process.env['PORT'] || 9222;
Paul Lewis58248022020-02-07 14:02:0218const envNoShuffle = !!process.env['NO_SHUFFLE'];
Paul Lewisc9f5e3a2020-02-17 15:47:4919const envInteractive = !!process.env['INTERACTIVE'];
20const interactivePage = 'http://localhost:8090/test/screenshots/interactive/index.html';
Paul Lewisd4802d62020-02-06 11:26:5421const blankPage = 'data:text/html,';
Paul Lewisb8b38012020-01-22 17:18:4722const headless = !envDebug;
23const width = 1280;
24const height = 720;
25
Paul Lewis96a0b3c2020-02-06 16:02:4126let mochaRun: Mocha.Runner;
Paul Lewis285715e2020-02-05 15:23:4827let exitCode = 0;
28
Paul Lewis96a0b3c2020-02-06 16:02:4129function interruptionHandler() {
30 console.log('\n');
31 if (mochaRun) {
32 console.log('Aborting tests');
33 mochaRun.abort();
34 }
35 exitCode = 1;
36 shutdown();
37}
38
39function shutdown() {
40 console.log('\n');
41 console.log('Stopping hosted mode server');
42 hostedModeServer.kill();
43
44 console.log(`Exiting with status code ${exitCode}`);
45 process.exit(exitCode);
46}
47
48process.on('SIGINT', interruptionHandler);
49process.on('SIGTERM', interruptionHandler);
50process.on('uncaughtException', interruptionHandler);
51process.stdin.resume();
52
Tim van der Lippec14a1342020-02-10 14:21:4053if (!testListPath) {
Mathias Bynens23ee1aa2020-03-02 12:06:3854 throw new Error('Must specify a list of tests in the "TEST_LIST" environment variable.');
Tim van der Lippec14a1342020-02-10 14:21:4055}
56
57const launchArgs = [`--remote-debugging-port=${envPort}`];
58
Paul Lewis96a0b3c2020-02-06 16:02:4159// 1. Launch Chromium.
Paul Lewisb8b38012020-01-22 17:18:4760const opts: puppeteer.LaunchOptions = {
61 headless,
62 executablePath: envChromeBinary,
Paul Lewisb8b38012020-01-22 17:18:4763 defaultViewport: null,
64};
65
66// Toggle either viewport or window size depending on headless vs not.
67if (headless) {
68 opts.defaultViewport = {width, height};
69}
70else {
Tim van der Lippec14a1342020-02-10 14:21:4071 launchArgs.push(`--window-size=${width},${height}`);
Paul Lewisb8b38012020-01-22 17:18:4772}
73
Tim van der Lippec14a1342020-02-10 14:21:4074opts.args = launchArgs;
75
Paul Lewisb8b38012020-01-22 17:18:4776const launchedBrowser = puppeteer.launch(opts);
77const pages: puppeteer.Page[] = [];
78
Paul Lewisbd88d7f2020-02-05 17:06:4379// 2. Start DevTools hosted mode.
Tim van der Lippec14a1342020-02-10 14:21:4080function handleHostedModeError(data: Error) {
Tim van der Lippe54c6faa2020-02-20 15:42:5681 console.log(`Hosted mode server: ${data}`);
Paul Lewis96a0b3c2020-02-06 16:02:4182 interruptionHandler();
Paul Lewisbd88d7f2020-02-05 17:06:4383}
84
85console.log('Spawning hosted mode server');
Paul Lewis0a228d42020-02-05 17:13:2986const serverScriptPath = join(__dirname, '..', '..', 'scripts', 'hosted_mode', 'server.js');
Paul Lewisbd88d7f2020-02-05 17:06:4387const cwd = join(__dirname, '..', '..');
88const {execPath} = process;
Paul Lewis3432ad62020-02-13 09:39:3189const hostedModeServer = spawn(execPath, [serverScriptPath], { cwd });
Paul Lewisbd88d7f2020-02-05 17:06:4390hostedModeServer.on('error', handleHostedModeError);
91hostedModeServer.stderr.on('data', handleHostedModeError);
92
Tim van der Lippec14a1342020-02-10 14:21:4093interface DevToolsTarget {
94 url: string;
95 id: string;
96}
97
Paul Lewisbd88d7f2020-02-05 17:06:4398// 3. Spin up the test environment
Paul Lewisb8b38012020-01-22 17:18:4799(async function() {
100 try {
Paul Lewisc9f5e3a2020-02-17 15:47:49101 let screenshotPage: puppeteer.Page | undefined;
102 if (envInteractive) {
103 const screenshotBrowser = await puppeteer.launch({
104 headless: false,
105 executablePath: envChromeBinary,
106 defaultViewport: null,
107 args: [`--window-size=${width},${height}`],
108 });
109 screenshotPage = await screenshotBrowser.newPage();
110 await screenshotPage.goto(interactivePage, {waitUntil: ['domcontentloaded']});
111 }
112
Paul Lewisb8b38012020-01-22 17:18:47113 const browser = await launchedBrowser;
114
115 // Load the target page.
116 const srcPage = await browser.newPage();
Paul Lewisd4802d62020-02-06 11:26:54117 await srcPage.goto(blankPage);
Paul Lewisb8b38012020-01-22 17:18:47118 pages.push(srcPage);
119
120 // Now get the DevTools listings.
121 const devtools = await browser.newPage();
122 await devtools.goto(`http://localhost:${envPort}/json`);
123
124 // Find the appropriate item to inspect the target page.
125 const listing = await devtools.$('pre');
126 const json = await devtools.evaluate(listing => listing.textContent, listing);
Tim van der Lippec14a1342020-02-10 14:21:40127 const targets: DevToolsTarget[] = JSON.parse(json);
Jack Frankline245d1a2020-02-13 15:25:13128 const target = targets.find(target => target.url === blankPage);
Paul Lewis74cb8b72020-02-11 11:05:23129 if (!target) {
130 throw new Error(`Unable to find target page: ${blankPage}`);
131 }
132
133 const {id} = target;
Paul Lewisb8b38012020-01-22 17:18:47134 await devtools.close();
135
136 // Connect to the DevTools frontend.
137 const frontend = await browser.newPage();
Yang Guo49346f12020-02-06 09:52:02138 const frontendUrl = `http://localhost:8090/front_end/devtools_app.html?ws=localhost:${envPort}/devtools/page/${id}`;
Paul Lewis96a0b3c2020-02-06 16:02:41139 await frontend.goto(frontendUrl, {waitUntil: ['networkidle2', 'domcontentloaded']});
Paul Lewisb8b38012020-01-22 17:18:47140
Jack Frankline245d1a2020-02-13 15:25:13141 frontend.on('error', err => {
Paul Lewis97e4a752020-02-05 16:06:33142 console.log('Error in Frontend');
143 console.log(err);
144 });
145
Jack Frankline245d1a2020-02-13 15:25:13146 frontend.on('pageerror', err => {
Paul Lewis97e4a752020-02-05 16:06:33147 console.log('Page Error in Frontend');
148 console.log(err);
149 });
150
Paul Lewisb8b38012020-01-22 17:18:47151 const resetPages =
Philip Pfaffe664c5602020-02-03 16:29:03152 async (...enabledExperiments: string[]) => {
Paul Lewisb8b38012020-01-22 17:18:47153 // Reload the target page.
Paul Lewisd4802d62020-02-06 11:26:54154 await srcPage.goto(blankPage, {waitUntil: ['domcontentloaded']});
Paul Lewisb8b38012020-01-22 17:18:47155
156 // Clear any local storage settings.
157 await frontend.evaluate(() => localStorage.clear());
158
Jack Frankline245d1a2020-02-13 15:25:13159 await frontend.evaluate(enabledExperiments => {
Philip Pfaffe664c5602020-02-03 16:29:03160 for (const experiment of enabledExperiments) {
Tim van der Lippec14a1342020-02-10 14:21:40161 // @ts-ignore
Philip Pfaffe664c5602020-02-03 16:29:03162 globalThis.Root.Runtime.experiments.setEnabled(experiment, true);
163 }
164 }, enabledExperiments);
165
Paul Lewisb8b38012020-01-22 17:18:47166 // Reload the DevTools frontend and await the elements panel.
Paul Lewisd4802d62020-02-06 11:26:54167 await frontend.goto(blankPage, {waitUntil: ['domcontentloaded']});
168 await frontend.goto(frontendUrl, {waitUntil: ['networkidle2', 'domcontentloaded']});
Paul Lewisb8b38012020-01-22 17:18:47169 await frontend.waitForSelector('.elements');
Jack Frankline245d1a2020-02-13 15:25:13170 };
Paul Lewisb8b38012020-01-22 17:18:47171
Paul Lewisc9f5e3a2020-02-17 15:47:49172 store(browser, srcPage, frontend, screenshotPage, resetPages);
Paul Lewisb8b38012020-01-22 17:18:47173
174 // 3. Run tests.
175 do {
176 if (envDebug) {
177 logHelp();
178 }
179
180 await waitForInput();
181 await runTests();
182 if (envDebug) {
183 await resetPages();
184 }
185 } while (envDebug);
186
187 } catch (err) {
188 console.warn(err);
189 } finally {
Paul Lewisbd88d7f2020-02-05 17:06:43190 shutdown();
Paul Lewisb8b38012020-01-22 17:18:47191 }
192})();
193
194async function waitForInput() {
Jack Frankline245d1a2020-02-13 15:25:13195 return new Promise(resolve => {
Paul Lewisb8b38012020-01-22 17:18:47196 if (!envDebug) {
197 resolve();
198 return;
199 }
200
201 process.stdin.setRawMode(true);
202 process.stdin.resume();
Jack Frankline245d1a2020-02-13 15:25:13203 process.stdin.on('data', async str => {
Paul Lewisb8b38012020-01-22 17:18:47204 // Listen for ctrl+c to exit.
205 if (str.toString() === '\x03') {
Paul Lewis96a0b3c2020-02-06 16:02:41206 interruptionHandler();
Paul Lewisb8b38012020-01-22 17:18:47207 }
208 resolve();
209 });
210 });
211}
212
Paul Lewisb8b38012020-01-22 17:18:47213async function runTests() {
Tim van der Lippec14a1342020-02-10 14:21:40214 const {testList} = await import(testListPath!);
Paul Lewis58248022020-02-07 14:02:02215 const shuffledTests = shuffleTestFiles(testList);
Paul Lewis32473f02020-02-05 15:55:19216
Jack Frankline245d1a2020-02-13 15:25:13217 return new Promise(resolve => {
Paul Lewisb8b38012020-01-22 17:18:47218 const mocha = new Mocha();
Paul Lewis58248022020-02-07 14:02:02219 for (const test of shuffledTests) {
Paul Lewis32473f02020-02-05 15:55:19220 mocha.addFile(test);
Paul Lewisb8b38012020-01-22 17:18:47221 }
222 mocha.ui('bdd');
223 mocha.reporter('list');
Paul Lewisc9f5e3a2020-02-17 15:47:49224 mocha.timeout((envDebug || envInteractive) ? 300000 : 4000);
Paul Lewisb8b38012020-01-22 17:18:47225
226 mochaRun = mocha.run();
Paul Lewise27cd532020-02-03 09:45:36227 mochaRun.on('end', () => {
Paul Lewisb8b38012020-01-22 17:18:47228 (mocha as any).unloadFiles();
229 resolve();
230 });
Paul Lewis285715e2020-02-05 15:23:48231
232 mochaRun.on('fail', () => {
233 exitCode = 1;
234 });
Paul Lewisb8b38012020-01-22 17:18:47235 });
236}
237
238function logHelp() {
239 console.log('Running in debug mode.');
240 console.log(' - Press any key to run the test suite.');
241 console.log(' - Press ctrl + c to quit.');
Tim van der Lippe54c6faa2020-02-20 15:42:56242 hostedModeServer.stdout.on('data', (message: any) => {
243 console.log(`Hosted mode server: ${message}`);
244 });
Paul Lewisb8b38012020-01-22 17:18:47245}
Paul Lewis58248022020-02-07 14:02:02246
247function shuffleTestFiles(files: string[]) {
248 if (envNoShuffle) {
249 console.log('Running tests unshuffled');
250 return files;
251 }
252
Tim van der Lippec14a1342020-02-10 14:21:40253 const swap = (arr: string[], a: number, b: number) => {
Paul Lewis58248022020-02-07 14:02:02254 const temp = arr[a];
255 arr[a] = arr[b];
256 arr[b] = temp;
Tim van der Lippec14a1342020-02-10 14:21:40257 };
Paul Lewis58248022020-02-07 14:02:02258
259 for (let i = files.length; i >= 0; i--) {
260 const a = Math.floor(Math.random() * files.length);
261 const b = Math.floor(Math.random() * files.length);
262
263 swap(files, a, b);
264 }
265
266 console.log(`Running tests in the following order:\n${files.join('\n')}`);
267 return files;
268}