Port shared test runner logic into separate package
The shared test runner logic was reimplementing parts of Mocha,
in particular the test logging and filtering. Moreover, it was
booting the hosted mode server and puppeteer outside of Mocha.
Mocha supports root level hooks [1]. These hooks allow us to
perform work before a test starts. Moreover, by using the
before and after hook, we can run logic before and after
all tests.
By using these hooks, we can extract the "boot the hosted mode
server and hookup puppeteer" part to these root hooks.
Additionally, we can reset the pages in the `beforeEach`, which
means that tests themselves don't have to reset the pages.
We also put the implementation code into third_party/conductor,
as we would like to reuse this logic for the Puppeteer tests.
The Puppeteer test suite now also uses Mocha and has very
similar requirements as to our DevTools tests. By extracting
from DevTools, we can look into expanding the test runner to
other usecases, but that is out of scope for now.
[1]: https://mochajs.org/#root-level-hooks
Bug: 1071369
Change-Id: Ie9f954359d9de84da564b74b6f5517dd535db008
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2150458
Commit-Queue: Tim van der Lippe <[email protected]>
Reviewed-by: Paul Lewis <[email protected]>
diff --git a/scripts/devtools_paths.py b/scripts/devtools_paths.py
index 035c5fa..543cfa6 100644
--- a/scripts/devtools_paths.py
+++ b/scripts/devtools_paths.py
@@ -58,6 +58,10 @@
return path.join(node_modules_path(), 'eslint', 'bin', 'eslint.js')
+def mocha_path():
+ return path.join(node_modules_path(), 'mocha', 'bin', 'mocha')
+
+
def check_localizable_resources_path():
return path.join(devtools_root_path(), 'scripts', 'localization', 'check_localizable_resources.js')
diff --git a/scripts/test/run_test_suite.py b/scripts/test/run_test_suite.py
index 2c4acfc..14c3ee4 100644
--- a/scripts/test/run_test_suite.py
+++ b/scripts/test/run_test_suite.py
@@ -14,12 +14,17 @@
import sys
import signal
-scripts_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+ROOT_DIRECTORY = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '..')
+scripts_path = os.path.join(ROOT_DIRECTORY, 'scripts')
sys.path.append(scripts_path)
import devtools_paths
import test_helpers
+CONDUCTOR_DIRECTORY = os.path.join(ROOT_DIRECTORY, 'test', 'conductor')
+CONDUCTOR_HOOKS = os.path.join(CONDUCTOR_DIRECTORY, 'mocha_hooks.js')
+E2E_TEST_LOCATION = os.path.join(ROOT_DIRECTORY, 'test', 'e2e', '**/*_test.js')
+
def parse_options(cli_args):
parser = argparse.ArgumentParser(description='Run tests')
@@ -44,7 +49,7 @@
return False
-def run_tests(chrome_binary, chrome_features, test_suite_list_path, test_file=None):
+def run_tests(chrome_binary, chrome_features, test_suite, test_suite_list_path, test_file=None):
env = os.environ.copy()
env['CHROME_BIN'] = chrome_binary
env['TEST_LIST'] = test_suite_list_path
@@ -55,8 +60,15 @@
env['TEST_FILE'] = test_file
cwd = devtools_paths.devtools_root_path()
- runner_path = os.path.join(cwd, 'test', 'shared', 'runner.js')
- exec_command = [devtools_paths.node_path(), runner_path]
+ if test_suite == 'e2e':
+ exec_command = [
+ devtools_paths.node_path(),
+ devtools_paths.mocha_path(), '--file', CONDUCTOR_HOOKS,
+ '"%s"' % E2E_TEST_LOCATION
+ ]
+ else:
+ runner_path = os.path.join(cwd, 'test', 'shared', 'runner.js')
+ exec_command = [devtools_paths.node_path(), runner_path]
exit_code = test_helpers.popen(exec_command, cwd=cwd, env=env)
if exit_code != 0:
@@ -107,16 +119,16 @@
cwd = devtools_paths.devtools_root_path()
shared_path = os.path.join(cwd, 'test', 'shared')
test_suite_path = os.path.join(cwd, 'test', test_suite)
- typescript_paths = [
- {
+ typescript_paths = [{
+ 'name': 'conductor',
+ 'path': CONDUCTOR_DIRECTORY
+ }, {
'name': 'shared',
'path': shared_path
- },
- {
+ }, {
'name': 'suite',
'path': test_suite_path
- }
- ]
+ }]
errors_found = False
try:
@@ -124,7 +136,7 @@
if (errors_found):
raise Exception('Typescript failed to compile')
test_suite_list_path = os.path.join(test_suite_path, 'test-list.js')
- errors_found = run_tests(chrome_binary, chrome_features, test_suite_list_path, test_file=test_file)
+ errors_found = run_tests(chrome_binary, chrome_features, test_suite, test_suite_list_path, test_file=test_file)
except Exception as err:
print(err)
diff --git a/test/BUILD.gn b/test/BUILD.gn
index 3626fc7..34c68fd 100644
--- a/test/BUILD.gn
+++ b/test/BUILD.gn
@@ -6,7 +6,6 @@
deps = [
"e2e",
"perf",
- "screenshots",
"shared",
"unittests/front_end",
]
diff --git a/test/conductor/.gitignore b/test/conductor/.gitignore
new file mode 100644
index 0000000..522e972
--- /dev/null
+++ b/test/conductor/.gitignore
@@ -0,0 +1,3 @@
+*.js
+*.d.ts
+*.tsbuildinfo
\ No newline at end of file
diff --git a/test/conductor/BUILD.gn b/test/conductor/BUILD.gn
new file mode 100644
index 0000000..80fe91e
--- /dev/null
+++ b/test/conductor/BUILD.gn
@@ -0,0 +1,14 @@
+# Copyright 2020 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../../third_party/typescript/typescript.gni")
+
+ts_library("conductor") {
+ testonly = true
+ sources = [
+ "hooks.ts",
+ "mocha_hooks.ts",
+ "puppeteer-state.ts",
+ ]
+}
diff --git a/test/conductor/hooks.ts b/test/conductor/hooks.ts
new file mode 100644
index 0000000..2706b97
--- /dev/null
+++ b/test/conductor/hooks.ts
@@ -0,0 +1,156 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/* eslint-disable no-console */
+
+import {ChildProcessWithoutNullStreams, spawn} from 'child_process';
+import * as path from 'path';
+import * as puppeteer from 'puppeteer';
+
+import {getBrowserAndPages, setBrowserAndPages} from './puppeteer-state.js';
+
+const HOSTED_MODE_SERVER_PATH = path.join(__dirname, '..', '..', 'scripts', 'hosted_mode', 'server.js');
+const EMPTY_PAGE = 'data:text/html,';
+const DEFAULT_TAB = {
+ name: 'elements',
+ selector: '.elements',
+};
+
+const cwd = path.join(__dirname, '..', '..');
+const {execPath} = process;
+const width = 1280;
+const height = 720;
+
+const envPort = 9222;
+const headless = !process.env['DEBUG'];
+const envSlowMo = process.env['STRESS'] ? 50 : undefined;
+const envThrottleRate = process.env['STRESS'] ? 3 : 1;
+
+let hostedModeServer: ChildProcessWithoutNullStreams;
+let browser: puppeteer.Browser;
+let frontendUrl: string;
+
+interface DevToolsTarget {
+ url: string;
+ id: string;
+}
+
+function handleHostedModeError(error: Error) {
+ throw new Error(`Hosted mode server: ${error}`);
+}
+
+const envChromeBinary = process.env['CHROME_BIN'];
+
+async function loadTargetPageAndDevToolsFrontend() {
+ const launchArgs = [`--remote-debugging-port=${envPort}`];
+ const opts: puppeteer.LaunchOptions = {
+ headless,
+ executablePath: envChromeBinary,
+ defaultViewport: null,
+ slowMo: envSlowMo,
+ };
+
+ // Toggle either viewport or window size depending on headless vs not.
+ if (headless) {
+ opts.defaultViewport = {width, height};
+ } else {
+ launchArgs.push(`--window-size=${width},${height}`);
+ }
+
+ opts.args = launchArgs;
+
+ browser = await puppeteer.launch(opts);
+ // Load the target page.
+ const srcPage = await browser.newPage();
+ await srcPage.goto(EMPTY_PAGE);
+
+ // Now get the DevTools listings.
+ const devtools = await browser.newPage();
+ await devtools.goto(`http://localhost:${envPort}/json`);
+
+ // Find the appropriate item to inspect the target page.
+ const listing = await devtools.$('pre');
+ const json = await devtools.evaluate(listing => listing.textContent, listing);
+ const targets: DevToolsTarget[] = JSON.parse(json);
+ const target = targets.find(target => target.url === EMPTY_PAGE);
+ if (!target) {
+ throw new Error(`Unable to find target page: ${EMPTY_PAGE}`);
+ }
+
+ const {id} = target;
+ await devtools.close();
+
+ // Connect to the DevTools frontend.
+ const frontend = await browser.newPage();
+ frontendUrl = `http://localhost:8090/front_end/devtools_app.html?ws=localhost:${envPort}/devtools/page/${id}`;
+ await frontend.goto(frontendUrl, {waitUntil: ['networkidle2', 'domcontentloaded']});
+
+ frontend.on('error', error => {
+ throw new Error(`Error in Frontend: ${error}`);
+ });
+
+ frontend.on('pageerror', error => {
+ throw new Error(`Page error in Frontend: ${error}`);
+ });
+
+ setBrowserAndPages({target: srcPage, frontend, browser});
+}
+
+export async function resetPages() {
+ const {target, frontend} = getBrowserAndPages();
+ // Reload the target page.
+ await target.goto(EMPTY_PAGE, {waitUntil: ['domcontentloaded']});
+
+ // Clear any local storage settings.
+ await frontend.evaluate(() => localStorage.clear());
+
+ await reloadDevTools();
+}
+
+export async function reloadDevTools(options: {selectedPanel?: {name: string, selector?: string}} = {}) {
+ const {frontend} = getBrowserAndPages();
+
+ // For the unspecified case wait for loading, then wait for the elements panel.
+ const {selectedPanel = DEFAULT_TAB} = options;
+
+ if (selectedPanel.name !== DEFAULT_TAB.name) {
+ await frontend.evaluate(name => {
+ // @ts-ignore
+ globalThis.localStorage.setItem('panel-selectedTab', `"${name}"`);
+ }, selectedPanel.name);
+ }
+
+ // Reload the DevTools frontend and await the elements panel.
+ await frontend.goto(EMPTY_PAGE, {waitUntil: ['domcontentloaded']});
+ await frontend.goto(frontendUrl, {waitUntil: ['domcontentloaded']});
+
+ if (selectedPanel.selector) {
+ await frontend.waitForSelector(selectedPanel.selector);
+ }
+
+ // Under stress conditions throttle the CPU down.
+ if (envThrottleRate !== 1) {
+ console.log(`Throttling CPU: ${envThrottleRate}x slowdown`);
+
+ const client = await frontend.target().createCDPSession();
+ await client.send('Emulation.setCPUThrottlingRate', {rate: envThrottleRate});
+ }
+}
+
+export async function globalSetup() {
+ console.log('Spawning hosted mode server');
+
+ hostedModeServer = spawn(execPath, [HOSTED_MODE_SERVER_PATH], {cwd});
+ hostedModeServer.on('error', handleHostedModeError);
+ hostedModeServer.stderr.on('data', handleHostedModeError);
+
+ await loadTargetPageAndDevToolsFrontend();
+}
+
+export async function globalTeardown() {
+ console.log('Stopping hosted mode server');
+ hostedModeServer.kill();
+
+ await browser.close();
+}
diff --git a/test/conductor/mocha_hooks.ts b/test/conductor/mocha_hooks.ts
new file mode 100644
index 0000000..bad951c
--- /dev/null
+++ b/test/conductor/mocha_hooks.ts
@@ -0,0 +1,41 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {globalSetup, globalTeardown, resetPages} from './hooks.js';
+
+/* eslint-disable no-console */
+
+before(async function() {
+ // It can take arbitrarly long on bots to boot up a server and
+ // startup DevTools. Since this timeout only applies for this
+ // hook, we can let it arbitrarily take a long time, while still
+ // enforcing tests to run reasonably quick (2 seconds by default).
+ this.timeout(0);
+
+ await globalSetup();
+
+ if (process.env['DEBUG']) {
+ console.log('Running in debug mode.');
+ console.log(' - Press any key to run the test suite.');
+ console.log(' - Press ctrl + c to quit.');
+
+ await new Promise(resolve => {
+ const {stdin} = process;
+
+ stdin.on('data', () => {
+ stdin.pause();
+ resolve();
+ });
+ });
+ }
+});
+
+after(async () => {
+ await globalTeardown();
+});
+
+beforeEach(async function() {
+ this.timeout(3000);
+ await resetPages();
+});
diff --git a/test/conductor/puppeteer-state.ts b/test/conductor/puppeteer-state.ts
new file mode 100644
index 0000000..f79fc7b
--- /dev/null
+++ b/test/conductor/puppeteer-state.ts
@@ -0,0 +1,43 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import * as puppeteer from 'puppeteer';
+
+let target: puppeteer.Page;
+let frontend: puppeteer.Page;
+let browser: puppeteer.Browser;
+
+export interface BrowserAndPages {
+ target: puppeteer.Page;
+ frontend: puppeteer.Page;
+ browser: puppeteer.Browser;
+}
+
+export const setBrowserAndPages = (newValues: BrowserAndPages) => {
+ if (target || frontend || browser) {
+ throw new Error('Can\'t set the puppeteer browser twice.');
+ }
+
+ ({target, frontend, browser} = newValues);
+};
+
+export const getBrowserAndPages = (): BrowserAndPages => {
+ if (!target) {
+ throw new Error('Unable to locate target page. Was it stored first?');
+ }
+
+ if (!frontend) {
+ throw new Error('Unable to locate DevTools frontend page. Was it stored first?');
+ }
+
+ if (!browser) {
+ throw new Error('Unable to locate browser instance. Was it stored first?');
+ }
+
+ return {
+ target,
+ frontend,
+ browser,
+ };
+};
diff --git a/test/conductor/tsconfig.json b/test/conductor/tsconfig.json
new file mode 100644
index 0000000..e2729fb
--- /dev/null
+++ b/test/conductor/tsconfig.json
@@ -0,0 +1,11 @@
+{
+ "compilerOptions": {
+ "module": "commonjs",
+ "baseUrl": ".",
+ "composite": true,
+
+ "checkJs": false,
+ "allowJs": false
+ },
+ "extends": "../../tsconfig.base.json"
+}
diff --git a/test/e2e/application/cookies_test.ts b/test/e2e/application/cookies_test.ts
index 082a19e..b6f2b9e 100644
--- a/test/e2e/application/cookies_test.ts
+++ b/test/e2e/application/cookies_test.ts
@@ -5,7 +5,7 @@
import {assert} from 'chai';
import {describe, it} from 'mocha';
-import {getBrowserAndPages, resetPages, resourcesPath} from '../../shared/helper.js';
+import {getBrowserAndPages, resourcesPath} from '../../shared/helper.js';
import {doubleClickSourceTreeItem, getDataGridData, navigateToApplicationTab} from '../helpers/application-helpers.js';
@@ -13,10 +13,6 @@
const DOMAIN_SELECTOR = `${COOKIES_SELECTOR} + ol > [aria-label="http://localhost:8090"]`;
describe('The Application Tab', async () => {
- beforeEach(async () => {
- await resetPages();
- });
-
afterEach(async () => {
const {target} = getBrowserAndPages();
await target.deleteCookie({name: 'foo'});
diff --git a/test/e2e/application/session-storage_test.ts b/test/e2e/application/session-storage_test.ts
index 4b07d04..74fdf12 100644
--- a/test/e2e/application/session-storage_test.ts
+++ b/test/e2e/application/session-storage_test.ts
@@ -5,7 +5,7 @@
import {assert} from 'chai';
import {describe, it} from 'mocha';
-import {getBrowserAndPages, resetPages} from '../../shared/helper.js';
+import {getBrowserAndPages} from '../../shared/helper.js';
import {doubleClickSourceTreeItem, getDataGridData, navigateToApplicationTab} from '../helpers/application-helpers.js';
@@ -13,10 +13,6 @@
const DOMAIN_SELECTOR = `${SESSION_STORAGE_SELECTOR} + ol > [aria-label="http://localhost:8090"]`;
describe('The Application Tab', async () => {
- beforeEach(async () => {
- await resetPages();
- });
-
it('shows Session Storage keys and values', async () => {
const {target} = getBrowserAndPages();
await navigateToApplicationTab(target, 'session-storage');
diff --git a/test/e2e/application/websql-database_test.ts b/test/e2e/application/websql-database_test.ts
index a8d827b..05f2f87 100644
--- a/test/e2e/application/websql-database_test.ts
+++ b/test/e2e/application/websql-database_test.ts
@@ -5,7 +5,7 @@
import {assert} from 'chai';
import {describe, it} from 'mocha';
-import {$, debuggerStatement, getBrowserAndPages, resetPages, waitFor} from '../../shared/helper.js';
+import {$, debuggerStatement, getBrowserAndPages, waitFor} from '../../shared/helper.js';
import {doubleClickSourceTreeItem, navigateToApplicationTab} from '../helpers/application-helpers.js';
@@ -13,10 +13,6 @@
const DATABASES_SELECTOR = `${WEB_SQL_SELECTOR} + ol`;
describe('The Application Tab', async () => {
- beforeEach(async () => {
- await resetPages();
- });
-
it('shows WebSQL database', async () => {
const {target, frontend} = getBrowserAndPages();
await navigateToApplicationTab(target, 'websql-database');
diff --git a/test/e2e/console/console-message-format_test.ts b/test/e2e/console/console-message-format_test.ts
index 5e936e3..b3ba8dc 100644
--- a/test/e2e/console/console-message-format_test.ts
+++ b/test/e2e/console/console-message-format_test.ts
@@ -5,14 +5,9 @@
import {assert} from 'chai';
import {describe, it} from 'mocha';
-import {resetPages} from '../../shared/helper.js';
import {getConsoleMessages, showVerboseMessages} from '../helpers/console-helpers.js';
describe('The Console Tab', async () => {
- beforeEach(async () => {
- await resetPages();
- });
-
it('shows BigInts formatted', async () => {
const messages = await getConsoleMessages('big-int');
diff --git a/test/e2e/console/console-repl-mode_test.ts b/test/e2e/console/console-repl-mode_test.ts
index f582c47..31e88ce 100644
--- a/test/e2e/console/console-repl-mode_test.ts
+++ b/test/e2e/console/console-repl-mode_test.ts
@@ -4,13 +4,12 @@
import {assert} from 'chai';
import {describe, it} from 'mocha';
-import {click, getBrowserAndPages, resetPages, typeText, waitFor, waitForNone} from '../../shared/helper.js';
+import {click, getBrowserAndPages, typeText, waitFor, waitForNone} from '../../shared/helper.js';
import {CONSOLE_TAB_SELECTOR, focusConsolePrompt} from '../helpers/console-helpers.js';
-describe('The Console Tab', async () => {
- beforeEach(async () => {
- await resetPages();
- });
+describe('The Console Tab', async function() {
+ // The tests in this suite are particularly slow, as they perform a lot of actions
+ this.timeout(10000);
it('allows re-declaration of let variables', async () => {
const {frontend} = getBrowserAndPages();
diff --git a/test/e2e/elements/element-breadcrumbs_test.ts b/test/e2e/elements/element-breadcrumbs_test.ts
index 52a84ad..0d577ea 100644
--- a/test/e2e/elements/element-breadcrumbs_test.ts
+++ b/test/e2e/elements/element-breadcrumbs_test.ts
@@ -4,14 +4,16 @@
import {assert} from 'chai';
import {describe, it} from 'mocha';
-import {click, getBrowserAndPages, resetPages, resourcesPath, waitForElementWithTextContent} from '../../shared/helper.js';
-import {assertContentOfSelectedElementsNode, assertSelectedElementsNodeTextIncludes, expandSelectedNodeRecursively, getBreadcrumbsTextContent, getSelectedBreadcrumbTextContent, waitForElementsStyleSection} from '../helpers/elements-helpers.js';
+import {click, getBrowserAndPages, resourcesPath, waitForElementWithTextContent} from '../../shared/helper.js';
+import {assertContentOfSelectedElementsNode, assertSelectedElementsNodeTextIncludes, expandSelectedNodeRecursively, getBreadcrumbsTextContent, getSelectedBreadcrumbTextContent, waitForElementsStyleSection, waitForSelectedTreeElementSelectorWithTextcontent} from '../helpers/elements-helpers.js';
+
+const EXPECTED_TEXT_CONTENT = `<div class=\u200B"div2">\u200B
+ last child
+ \u200B</div>\u200B`;
describe('Element breadcrumbs', async () => {
- beforeEach(async () => {
- await resetPages();
-
+ beforeEach(async function() {
const {target} = getBrowserAndPages();
await target.goto(`${resourcesPath}/elements/element-breadcrumbs.html`);
await waitForElementsStyleSection();
@@ -21,11 +23,11 @@
// expand the tree and then navigate down to the target node
await expandSelectedNodeRecursively();
- const targetChildNode = await waitForElementWithTextContent('div2');
+ const targetChildNode = await waitForElementWithTextContent(EXPECTED_TEXT_CONTENT);
await click(targetChildNode);
// double check we got to the node we expect
- await assertSelectedElementsNodeTextIncludes('<div class=\u200B"div2">\u200B');
+ await waitForSelectedTreeElementSelectorWithTextcontent(EXPECTED_TEXT_CONTENT);
await assertSelectedElementsNodeTextIncludes('last child');
});
diff --git a/test/e2e/elements/pseudo-states_test.ts b/test/e2e/elements/pseudo-states_test.ts
index d113e14..3a3a06a 100644
--- a/test/e2e/elements/pseudo-states_test.ts
+++ b/test/e2e/elements/pseudo-states_test.ts
@@ -5,17 +5,13 @@
import {assert} from 'chai';
import {describe, it} from 'mocha';
-import {debuggerStatement, getBrowserAndPages, resetPages, resourcesPath} from '../../shared/helper.js';
+import {debuggerStatement, getBrowserAndPages, resourcesPath} from '../../shared/helper.js';
import {assertContentOfSelectedElementsNode, assertGutterDecorationForDomNodeExists, forcePseudoState, getComputedStylesForDomNode, removePseudoState, waitForDomNodeToBeHidden, waitForDomNodeToBeVisible, waitForElementsStyleSection} from '../helpers/elements-helpers.js';
const TARGET_SHOWN_ON_HOVER_SELECTOR = '.show-on-hover';
const TARGET_SHOWN_ON_FOCUS_SELECTOR = '.show-on-focus';
describe('The Elements tab', async () => {
- beforeEach(async () => {
- await resetPages();
- });
-
it('can force :hover state for selected DOM node', async () => {
const {target, frontend} = getBrowserAndPages();
diff --git a/test/e2e/elements/selection-after-delete_test.ts b/test/e2e/elements/selection-after-delete_test.ts
index 2e8e04f..4a65790 100644
--- a/test/e2e/elements/selection-after-delete_test.ts
+++ b/test/e2e/elements/selection-after-delete_test.ts
@@ -4,21 +4,17 @@
import {describe, it} from 'mocha';
-import {click, getBrowserAndPages, resetPages, resourcesPath, waitFor, waitForElementWithTextContent} from '../../shared/helper.js';
-import {assertContentOfSelectedElementsNode, expandSelectedNodeRecursively, getContentOfSelectedNode, waitForSelectedNodeChange} from '../helpers/elements-helpers.js';
+import {click, getBrowserAndPages, resourcesPath, waitForElementWithTextContent} from '../../shared/helper.js';
+import {assertContentOfSelectedElementsNode, expandSelectedNodeRecursively, getContentOfSelectedNode, waitForElementsStyleSection, waitForSelectedNodeChange} from '../helpers/elements-helpers.js';
describe('The Elements tab', async () => {
- beforeEach(async () => {
- await resetPages();
- });
-
- it('can delete elements in the tree', async () => {
+ it.skip('[crbug.com/1071851]: can delete elements in the tree', async () => {
const {target, frontend} = getBrowserAndPages();
await target.goto(`${resourcesPath}/elements/selection-after-delete.html`);
// Wait for the file to be loaded and selectors to be shown
- await waitFor('.styles-selector');
+ await waitForElementsStyleSection();
// Sanity check to make sure we have the correct node selected after opening a file
await assertContentOfSelectedElementsNode('<body>\u200B');
diff --git a/test/e2e/elements/shadowroot-styles_test.ts b/test/e2e/elements/shadowroot-styles_test.ts
index 66d22c3..1cd0464 100644
--- a/test/e2e/elements/shadowroot-styles_test.ts
+++ b/test/e2e/elements/shadowroot-styles_test.ts
@@ -5,14 +5,10 @@
import {assert} from 'chai';
import {describe, it} from 'mocha';
-import {$$, getBrowserAndPages, resetPages, resourcesPath, waitFor, waitForFunction} from '../../shared/helper.js';
+import {$$, getBrowserAndPages, resourcesPath, waitFor, waitForFunction} from '../../shared/helper.js';
import {assertContentOfSelectedElementsNode, waitForChildrenOfSelectedElementNode} from '../helpers/elements-helpers.js';
describe('The Elements Tab', async () => {
- beforeEach(async () => {
- await resetPages();
- });
-
it('can show styles in shadow roots', async () => {
const {target, frontend} = getBrowserAndPages();
diff --git a/test/e2e/elements/sidebar-event-listeners-remove_test.ts b/test/e2e/elements/sidebar-event-listeners-remove_test.ts
index 6b82a29..503a121 100644
--- a/test/e2e/elements/sidebar-event-listeners-remove_test.ts
+++ b/test/e2e/elements/sidebar-event-listeners-remove_test.ts
@@ -5,12 +5,11 @@
import {assert} from 'chai';
import {describe, it} from 'mocha';
-import {click, resetPages} from '../../shared/helper.js';
+import {click} from '../../shared/helper.js';
import {getDisplayedEventListenerNames, getFirstNodeForEventListener, loadEventListenersAndSelectButtonNode, openEventListenersPaneAndWaitForListeners} from '../helpers/event-listeners-helpers.js';
describe('Removing event listeners in the elements sidebar', async () => {
beforeEach(async () => {
- await resetPages();
await loadEventListenersAndSelectButtonNode();
});
diff --git a/test/e2e/elements/sidebar-event-listeners_test.ts b/test/e2e/elements/sidebar-event-listeners_test.ts
index 77881ee..b9ed75f 100644
--- a/test/e2e/elements/sidebar-event-listeners_test.ts
+++ b/test/e2e/elements/sidebar-event-listeners_test.ts
@@ -5,12 +5,11 @@
import {assert} from 'chai';
import {describe, it} from 'mocha';
-import {doubleClick, resetPages} from '../../shared/helper.js';
+import {doubleClick} from '../../shared/helper.js';
import {getDisplayedEventListenerNames, getEventListenerProperties, getFirstNodeForEventListener, loadEventListenersAndSelectButtonNode, openEventListenersPaneAndWaitForListeners} from '../helpers/event-listeners-helpers.js';
describe('Event listeners in the elements sidebar', async () => {
beforeEach(async () => {
- await resetPages();
await loadEventListenersAndSelectButtonNode();
});
diff --git a/test/e2e/elements/style-pane-properties_test.ts b/test/e2e/elements/style-pane-properties_test.ts
index 0876235..c6cad30 100644
--- a/test/e2e/elements/style-pane-properties_test.ts
+++ b/test/e2e/elements/style-pane-properties_test.ts
@@ -6,7 +6,7 @@
import {describe, it} from 'mocha';
import * as puppeteer from 'puppeteer';
-import {click, getBrowserAndPages, resetPages, resourcesPath, waitFor} from '../../shared/helper.js';
+import {click, getBrowserAndPages, resourcesPath, waitFor} from '../../shared/helper.js';
import {assertContentOfSelectedElementsNode, getAriaLabelSelectorFromPropertiesSelector, getDisplayedCSSPropertyNames, waitForElementsStyleSection} from '../helpers/elements-helpers.js';
const PROPERTIES_TO_DELETE_SELECTOR = '#properties-to-delete';
@@ -22,10 +22,6 @@
};
describe('The Elements Tab', async () => {
- beforeEach(async () => {
- await resetPages();
- });
-
it('can remove a CSS property when its name or value is deleted', async () => {
const {target, frontend} = getBrowserAndPages();
diff --git a/test/e2e/helpers/elements-helpers.ts b/test/e2e/helpers/elements-helpers.ts
index 1887031..9fef8fb 100644
--- a/test/e2e/helpers/elements-helpers.ts
+++ b/test/e2e/helpers/elements-helpers.ts
@@ -5,7 +5,7 @@
import {performance} from 'perf_hooks';
import * as puppeteer from 'puppeteer';
-import {$, $$, click, getBrowserAndPages, timeout, waitFor} from '../../shared/helper.js';
+import {$, $$, click, getBrowserAndPages, timeout, waitFor, waitForFunction} from '../../shared/helper.js';
const SELECTED_TREE_ELEMENT_SELECTOR = '.selected[role="treeitem"]';
const CSS_PROPERTY_NAME_SELECTOR = '.webkit-css-property';
@@ -49,6 +49,14 @@
assert.include(selectedTextContent, expectedTextContent);
};
+export const waitForSelectedTreeElementSelectorWithTextcontent = async (expectedTextContent: string) => {
+ await waitForFunction(async () => {
+ const selectedNode = await $(SELECTED_TREE_ELEMENT_SELECTOR);
+ const selectedTextContent = await selectedNode.evaluate(node => node.textContent);
+ return selectedTextContent === expectedTextContent;
+ }, 'Did not find a select elements tree element with textcontent');
+};
+
export const waitForChildrenOfSelectedElementNode = async () => {
await waitFor(`${SELECTED_TREE_ELEMENT_SELECTOR} + ol > li`);
};
diff --git a/test/e2e/helpers/search-helpers.ts b/test/e2e/helpers/search-helpers.ts
index 0b784f2..87da097 100644
--- a/test/e2e/helpers/search-helpers.ts
+++ b/test/e2e/helpers/search-helpers.ts
@@ -14,7 +14,7 @@
break;
default:
- await frontend.keyboard.down('Ctrl');
+ await frontend.keyboard.down('Control');
await frontend.keyboard.down('Shift');
}
@@ -27,7 +27,7 @@
break;
default:
- await frontend.keyboard.up('Ctrl');
+ await frontend.keyboard.up('Control');
await frontend.keyboard.up('Shift');
}
}
diff --git a/test/e2e/host/user-metrics_test.ts b/test/e2e/host/user-metrics_test.ts
index 8090ab9..2fe2685 100644
--- a/test/e2e/host/user-metrics_test.ts
+++ b/test/e2e/host/user-metrics_test.ts
@@ -6,7 +6,7 @@
import {describe, it} from 'mocha';
import * as puppeteer from 'puppeteer';
-import {click, getBrowserAndPages, platform, resetPages, waitFor} from '../../shared/helper.js';
+import {click, getBrowserAndPages, platform, reloadDevTools, waitFor} from '../../shared/helper.js';
import {openPanelViaMoreTools} from '../helpers/settings-helpers.js';
interface UserMetric {
@@ -97,7 +97,6 @@
describe('User Metrics', () => {
beforeEach(async () => {
- await resetPages();
const {frontend} = getBrowserAndPages();
await beginCatchEvents(frontend);
});
@@ -233,16 +232,17 @@
]);
});
- it('tracks panel loading', async () => {
+ it.skip('[crbug.com/1071850]: tracks panel loading', async () => {
// We specify the selected panel here because the default behavior is to go to the
// elements panel, but this means we won't get the PanelLoaded event. Instead we
// request that the resetPages helper sets the timeline as the target panel, and
// we wait for the timeline in the test. This means, in turn, we get the PanelLoaded
// event.
- await resetPages({selectedPanel: {name: 'timeline'}});
+ await reloadDevTools({selectedPanel: {name: 'timeline'}});
const {frontend} = getBrowserAndPages();
await beginCatchEvents(frontend);
+
await waitFor('.timeline');
await assertCapturedEvents([
diff --git a/test/e2e/lighthouse/generate-report.ts b/test/e2e/lighthouse/generate-report.ts
index 4945d37..8c79c8a 100644
--- a/test/e2e/lighthouse/generate-report.ts
+++ b/test/e2e/lighthouse/generate-report.ts
@@ -5,14 +5,10 @@
import {assert} from 'chai';
import {describe, it} from 'mocha';
-import {getBrowserAndPages, resetPages, resourcesPath} from '../../shared/helper.js';
+import {getBrowserAndPages, resourcesPath} from '../../shared/helper.js';
import {isGenerateReportButtonDisabled, navigateToLighthouseTab} from '../helpers/lighthouse-helpers.js';
describe('The Lighthouse Tab', async () => {
- beforeEach(async () => {
- await resetPages();
- });
-
it('shows a button to generate a new report', async () => {
const {target} = getBrowserAndPages();
await navigateToLighthouseTab(target, 'empty');
diff --git a/test/e2e/media/media-tab_test.ts b/test/e2e/media/media-tab_test.ts
index 675cac9..8e22c2d 100644
--- a/test/e2e/media/media-tab_test.ts
+++ b/test/e2e/media/media-tab_test.ts
@@ -5,7 +5,7 @@
import {assert} from 'chai';
import {describe, it} from 'mocha';
-import {resetPages} from '../../shared/helper.js';
+import {enableExperiment} from '../../shared/helper.js';
import {getPlayerButtonText, playMediaFile} from '../helpers/media-helpers.js';
import {openPanelViaMoreTools} from '../helpers/settings-helpers.js';
@@ -16,7 +16,7 @@
describe('Media Tab', () => {
beforeEach(async () => {
- await resetPages({'enabledExperiments': ['mediaInspector']});
+ await enableExperiment('mediaInspector');
});
it('ensures video playback adds entry', async () => {
diff --git a/test/e2e/network/network-datagrid_test.ts b/test/e2e/network/network-datagrid_test.ts
index fd4695b..bc24375 100644
--- a/test/e2e/network/network-datagrid_test.ts
+++ b/test/e2e/network/network-datagrid_test.ts
@@ -6,7 +6,7 @@
import {describe, it} from 'mocha';
import * as puppeteer from 'puppeteer';
-import {$, click, getBrowserAndPages, resetPages, resourcesPath, waitFor} from '../../shared/helper.js';
+import {$, click, getBrowserAndPages, resourcesPath, waitFor} from '../../shared/helper.js';
async function navigateToNetworkTab(target: puppeteer.Page, testName: string) {
await target.goto(`${resourcesPath}/network/${testName}`);
@@ -16,10 +16,6 @@
}
describe('The Network Tab', async () => {
- beforeEach(async () => {
- await resetPages();
- });
-
it('can click on checkbox label to toggle checkbox', async () => {
const {target} = getBrowserAndPages();
await navigateToNetworkTab(target, 'resources-from-cache.html');
diff --git a/test/e2e/rendering/vision-deficiencies_test.ts b/test/e2e/rendering/vision-deficiencies_test.ts
index e69f81d..3ec3153 100644
--- a/test/e2e/rendering/vision-deficiencies_test.ts
+++ b/test/e2e/rendering/vision-deficiencies_test.ts
@@ -5,14 +5,10 @@
import {assert} from 'chai';
import {describe, it} from 'mocha';
-import {$, resetPages} from '../../shared/helper.js';
+import {$} from '../../shared/helper.js';
import {openPanelViaMoreTools} from '../helpers/settings-helpers.js';
describe('Rendering pane', () => {
- beforeEach(async () => {
- await resetPages();
- });
-
it('includes UI for simulating vision deficiencies', async () => {
await openPanelViaMoreTools('Rendering');
diff --git a/test/e2e/search/search-scope_test.ts b/test/e2e/search/search-scope_test.ts
index 6ce87f0..ef709c1 100644
--- a/test/e2e/search/search-scope_test.ts
+++ b/test/e2e/search/search-scope_test.ts
@@ -4,14 +4,10 @@
import {assert} from 'chai';
import {describe, it} from 'mocha';
-import {$, $$, getBrowserAndPages, resetPages, resourcesPath, waitFor} from '../../shared/helper.js';
+import {$, $$, getBrowserAndPages, resourcesPath, waitFor} from '../../shared/helper.js';
import {triggerFindDialog} from '../helpers/search-helpers.js';
describe('The Search Panel', async () => {
- beforeEach(async () => {
- await resetPages();
- });
-
it('provides results across scopes', async () => {
const {target, frontend} = getBrowserAndPages();
const SEARCH_QUERY = '[aria-label="Search Query"]';
@@ -74,18 +70,18 @@
}));
assert.deepEqual(entries, [
- {line: '1', content: 'div.searchTestUniqueString {'},
- {line: '5', content: 'div.searchTestUniqueString:hover {'},
- {line: '6', content: '/* another searchTestUniqueString occurence */'},
- {line: '1', content: 'function searchTestUniqueString() {'},
- {line: '3', content: '// searchTestUniqueString two occurences on the same line searchTestUniqueString'},
- {line: '4', content: '// searchTestUniqueString on the next line.'},
- {line: '9', content: 'searchTestUniqueString();'},
- {line: '10', content: '// SEARCHTestUniqueString();'},
- {line: '5', content: '…eval("function searchTestUniqueString() {}");</script>'},
- {line: '7', content: '<div>searchTestUniqueString</div>'},
- {line: '9', content: '<!-- searchTestUniqueString -->'},
- {line: '11', content: '<div id="searchTestUniqueString">div text</div>'},
+ {line: '7', content: 'div.searchTestUniqueString {'},
+ {line: '11', content: 'div.searchTestUniqueString:hover {'},
+ {line: '12', content: '/* another searchTestUniqueString occurence */'},
+ {line: '4', content: 'function searchTestUniqueString() {'},
+ {line: '6', content: '// searchTestUniqueString two occurences on the same line searchTestUniqueString'},
+ {line: '7', content: '// searchTestUniqueString on the next line.'},
+ {line: '12', content: 'searchTestUniqueString();'},
+ {line: '13', content: '// SEARCHTestUniqueString();'},
+ {line: '8', content: '…eval(\'function searchTestUniqueString() {}\');</script>'},
+ {line: '10', content: '<div>searchTestUniqueString</div>'},
+ {line: '12', content: '<!-- searchTestUniqueString -->'},
+ {line: '14', content: '<div id="searchTestUniqueString">div text</div>'},
]);
});
});
diff --git a/test/e2e/sensors/location_test.ts b/test/e2e/sensors/location_test.ts
index a2e4a67..4e7f36c 100644
--- a/test/e2e/sensors/location_test.ts
+++ b/test/e2e/sensors/location_test.ts
@@ -5,12 +5,11 @@
import {assert} from 'chai';
import {describe, it} from 'mocha';
-import {$, resetPages} from '../../shared/helper.js';
+import {$} from '../../shared/helper.js';
import {openPanelViaMoreTools} from '../helpers/settings-helpers.js';
describe('Sensors panel', () => {
beforeEach(async () => {
- await resetPages();
await openPanelViaMoreTools('Sensors');
});
diff --git a/test/e2e/snippets/context-menu_test.ts b/test/e2e/snippets/context-menu_test.ts
index 7ce995b..f04430b 100644
--- a/test/e2e/snippets/context-menu_test.ts
+++ b/test/e2e/snippets/context-menu_test.ts
@@ -5,15 +5,11 @@
import {assert} from 'chai';
import {describe, it} from 'mocha';
-import {getBrowserAndPages, resetPages, typeText} from '../../shared/helper.js';
+import {getBrowserAndPages, typeText} from '../../shared/helper.js';
import {getAvailableSnippets, openCommandMenu, showSnippetsAutocompletion} from '../helpers/quick_open-helpers.js';
import {createNewSnippet, openSnippetsSubPane, openSourcesPanel} from '../helpers/sources-helpers.js';
describe('Snippets subpane', () => {
- beforeEach(async () => {
- await resetPages();
- });
-
it('can show newly created snippets show up in command menu', async () => {
const {frontend} = getBrowserAndPages();
diff --git a/test/e2e/sources/can-break-with-wasm-sourcemaps_test.ts b/test/e2e/sources/can-break-with-wasm-sourcemaps_test.ts
index 9deb928..b8be0bd 100644
--- a/test/e2e/sources/can-break-with-wasm-sourcemaps_test.ts
+++ b/test/e2e/sources/can-break-with-wasm-sourcemaps_test.ts
@@ -5,15 +5,11 @@
import {assert} from 'chai';
import {describe, it} from 'mocha';
-import {getBrowserAndPages, resetPages} from '../../shared/helper.js';
+import {getBrowserAndPages} from '../../shared/helper.js';
import {addBreakpointForLine, openSourceCodeEditorForFile, retrieveTopCallFrameScriptLocation} from '../helpers/sources-helpers.js';
describe('The Sources Tab', async () => {
- beforeEach(async () => {
- await resetPages();
- });
-
it('can add breakpoint for a sourcemapped wasm module', async () => {
const {target, frontend} = getBrowserAndPages();
diff --git a/test/e2e/sources/can-format-sourcecode_test.ts b/test/e2e/sources/can-format-sourcecode_test.ts
index 4ece0a6..db4d1d6 100644
--- a/test/e2e/sources/can-format-sourcecode_test.ts
+++ b/test/e2e/sources/can-format-sourcecode_test.ts
@@ -6,7 +6,7 @@
import {describe, it} from 'mocha';
import * as puppeteer from 'puppeteer';
-import {click, getBrowserAndPages, resetPages, waitFor} from '../../shared/helper.js';
+import {click, getBrowserAndPages, waitFor} from '../../shared/helper.js';
import {addBreakpointForLine, openSourceCodeEditorForFile, retrieveTopCallFrameScriptLocation} from '../helpers/sources-helpers.js';
const PRETTY_PRINT_BUTTON = '[aria-label="Pretty print minified-sourcecode.js"]';
@@ -27,10 +27,9 @@
}
-describe('The Sources Tab', async () => {
- beforeEach(async () => {
- await resetPages();
- });
+describe('The Sources Tab', async function() {
+ // The tests in this suite are particularly slow, as they perform a lot of actions
+ this.timeout(10000);
it('can format a JavaScript file', async () => {
const {target, frontend} = getBrowserAndPages();
diff --git a/test/e2e/sources/can-show-files-after-loading_test.ts b/test/e2e/sources/can-show-files-after-loading_test.ts
index cc615d9..7aa3689 100644
--- a/test/e2e/sources/can-show-files-after-loading_test.ts
+++ b/test/e2e/sources/can-show-files-after-loading_test.ts
@@ -5,7 +5,7 @@
import {assert} from 'chai';
import {describe, it} from 'mocha';
-import {getBrowserAndPages, resetPages} from '../../shared/helper.js';
+import {getBrowserAndPages} from '../../shared/helper.js';
import {listenForSourceFilesAdded, openFileInSourcesPanel, retrieveSourceFilesAdded, waitForAdditionalSourceFiles} from '../helpers/sources-helpers.js';
@@ -16,10 +16,6 @@
}
describe('The Sources Tab', async () => {
- beforeEach(async () => {
- await resetPages();
- });
-
it('can show JavaScript files after dynamic loading', async () => {
const {target, frontend} = getBrowserAndPages();
diff --git a/test/e2e/sources/can-show-multiple-workers_test.ts b/test/e2e/sources/can-show-multiple-workers_test.ts
index 945ee41..43151f5 100644
--- a/test/e2e/sources/can-show-multiple-workers_test.ts
+++ b/test/e2e/sources/can-show-multiple-workers_test.ts
@@ -5,7 +5,7 @@
import {assert} from 'chai';
import {describe, it} from 'mocha';
-import {click, getBrowserAndPages, resetPages, resourcesPath, waitFor} from '../../shared/helper.js';
+import {click, getBrowserAndPages, resourcesPath, waitFor} from '../../shared/helper.js';
import {createSelectorsForWorkerFile, expandFileTree, NestedFileSelector} from '../helpers/sources-helpers.js';
const WORKER1_SELECTORS = createSelectorsForFile('worker1.js');
@@ -21,10 +21,9 @@
return workerFile.asElement()!.evaluate(node => node.textContent);
}
-describe('The Sources Tab', async () => {
- beforeEach(async () => {
- await resetPages();
- });
+describe('The Sources Tab', async function() {
+ // The tests in this suite are particularly slow, as they perform a lot of actions
+ this.timeout(10000);
it('can show multiple dedicated workers with different scripts', async () => {
const {target} = getBrowserAndPages();
diff --git a/test/e2e/sources/debug-raw-wasm_test.ts b/test/e2e/sources/debug-raw-wasm_test.ts
index 892622e..efc4411 100644
--- a/test/e2e/sources/debug-raw-wasm_test.ts
+++ b/test/e2e/sources/debug-raw-wasm_test.ts
@@ -5,14 +5,10 @@
import {assert} from 'chai';
import {describe, it} from 'mocha';
-import {$, click, getBrowserAndPages, resetPages, resourcesPath} from '../../shared/helper.js';
+import {$, click, getBrowserAndPages, resourcesPath} from '../../shared/helper.js';
import {addBreakpointForLine, openSourceCodeEditorForFile, RESUME_BUTTON, retrieveTopCallFrameScriptLocation} from '../helpers/sources-helpers.js';
describe('Source Tab', async () => {
- beforeEach(async () => {
- await resetPages();
- });
-
it('can add a breakpoint in raw wasm', async () => {
const {target, frontend} = getBrowserAndPages();
@@ -25,10 +21,6 @@
});
describe('Raw-Wasm', async () => {
- beforeEach(async () => {
- await resetPages();
- });
-
it('displays correct location in Wasm source', async () => {
const {target, frontend} = getBrowserAndPages();
diff --git a/test/e2e/sources/debugger-language-plugins_test.ts b/test/e2e/sources/debugger-language-plugins_test.ts
index 6958493..74bd70b 100644
--- a/test/e2e/sources/debugger-language-plugins_test.ts
+++ b/test/e2e/sources/debugger-language-plugins_test.ts
@@ -5,7 +5,7 @@
import {assert} from 'chai';
import {describe, it} from 'mocha';
-import {$, click, getBrowserAndPages, resetPages, resourcesPath, waitFor} from '../../shared/helper.js';
+import {$, click, enableExperiment, getBrowserAndPages, resourcesPath, waitFor} from '../../shared/helper.js';
import {addBreakpointForLine, listenForSourceFilesAdded, openFileInEditor, openFileInSourcesPanel, openSourcesPanel, PAUSE_ON_EXCEPTION_BUTTON, retrieveSourceFilesAdded, retrieveTopCallFrameScriptLocation, waitForAdditionalSourceFiles} from '../helpers/sources-helpers.js';
// TODO: Remove once Chromium updates its version of Node.js to 12+.
@@ -23,7 +23,8 @@
// plugins as of yet.
describe('The Debugger Language Plugins', async () => {
beforeEach(async () => {
- await resetPages({'enabledExperiments': ['wasmDWARFDebugging', 'protocolMonitor']});
+ await enableExperiment('wasmDWARFDebugging');
+
const {frontend} = getBrowserAndPages();
await frontend.evaluate(() => {
/* eslint-disable @typescript-eslint/no-unused-vars */
diff --git a/test/e2e/sources/dwarf-cxx-language-plugin_test.ts b/test/e2e/sources/dwarf-cxx-language-plugin_test.ts
index 7d4fc33..a0afc59 100644
--- a/test/e2e/sources/dwarf-cxx-language-plugin_test.ts
+++ b/test/e2e/sources/dwarf-cxx-language-plugin_test.ts
@@ -5,12 +5,12 @@
import {assert} from 'chai';
import {describe, it} from 'mocha';
-import {$, click, getBrowserAndPages, resetPages, resourcesPath, waitFor, waitForFunction} from '../../shared/helper.js';
+import {$, click, enableExperiment, getBrowserAndPages, resourcesPath, waitFor, waitForFunction} from '../../shared/helper.js';
import {addBreakpointForLine, listenForSourceFilesAdded, openFileInEditor, openFileInSourcesPanel, openSourcesPanel, PAUSE_ON_EXCEPTION_BUTTON, retrieveSourceFilesAdded, retrieveTopCallFrameScriptLocation, waitForAdditionalSourceFiles} from '../helpers/sources-helpers.js';
describe('The CXX DWARF Language Plugin', async () => {
beforeEach(async () => {
- await resetPages({'enabledExperiments': ['wasmDWARFDebugging']});
+ await enableExperiment('wasmDWARFDebugging');
});
beforeEach(function() {
diff --git a/test/e2e/sources/script-in-multiple-workers_test.ts b/test/e2e/sources/script-in-multiple-workers_test.ts
index ff31145..fd95822 100644
--- a/test/e2e/sources/script-in-multiple-workers_test.ts
+++ b/test/e2e/sources/script-in-multiple-workers_test.ts
@@ -6,7 +6,7 @@
import {describe, it} from 'mocha';
import * as puppeteer from 'puppeteer';
-import {click, getBrowserAndPages, resetPages, resourcesPath, waitFor} from '../../shared/helper.js';
+import {click, getBrowserAndPages, resourcesPath, waitFor} from '../../shared/helper.js';
import {addBreakpointForLine, createSelectorsForWorkerFile, getBreakpointDecorators, getOpenSources, openNestedWorkerFile, PAUSE_BUTTON, RESUME_BUTTON} from '../helpers/sources-helpers.js';
async function validateSourceTabs() {
@@ -15,10 +15,9 @@
}
-describe('Multi-Workers', async () => {
- beforeEach(async () => {
- await resetPages();
- });
+describe('Multi-Workers', async function() {
+ // The tests in this suite are particularly slow, as they perform a lot of actions
+ this.timeout(10000);
[false, true].forEach(sourceMaps => {
const withOrWithout = sourceMaps ? 'with source maps' : 'without source maps';
@@ -145,7 +144,7 @@
// Check breakpoints
await validateBreakpoints(frontend);
- }).timeout(10000);
+ });
it(`hits breakpoints added to workers ${withOrWithout}`, async () => {
const {target, frontend} = getBrowserAndPages();
diff --git a/test/perf/application/boot-perf.ts b/test/perf/application/boot-perf.ts
index bfc17ac..e750b2a 100644
--- a/test/perf/application/boot-perf.ts
+++ b/test/perf/application/boot-perf.ts
@@ -3,7 +3,8 @@
// found in the LICENSE file.
import {performance} from 'perf_hooks';
-import {resetPages} from '../../shared/helper.js';
+
+import {reloadDevTools} from '../../shared/helper.js';
import {storeGeneratedResults} from '../helpers/perf-helper.js';
interface PerfTimings {
@@ -11,17 +12,13 @@
}
describe('Boot performance', () => {
- beforeEach(async () => {
- await resetPages();
- });
-
it('runs 37 times', async () => {
const times: PerfTimings = {
bootperf: [],
};
for (let run = 0; run < 37; run++) {
const start = performance.now();
- await resetPages();
+ await reloadDevTools();
// Ensure only 2 decimal places.
const timeTaken = (performance.now() - start).toFixed(2);
diff --git a/test/screenshots/hello-world/hello-world.ts b/test/screenshots/hello-world/hello-world.ts
index 83fb20f..a800970 100644
--- a/test/screenshots/hello-world/hello-world.ts
+++ b/test/screenshots/hello-world/hello-world.ts
@@ -3,14 +3,11 @@
// found in the LICENSE file.
import {describe, it} from 'mocha';
-import {click, getBrowserAndPages, resetPages, resourcesPath} from '../../shared/helper.js';
-import {assertPageScreenshotUnchanged, assertElementScreenshotUnchanged} from '../../shared/screenshot.js';
+
+import {click, getBrowserAndPages, resourcesPath} from '../../shared/helper.js';
+import {assertElementScreenshotUnchanged, assertPageScreenshotUnchanged} from '../../shared/screenshot.js';
describe('hello world', () => {
- beforeEach(async () => {
- await resetPages();
- });
-
it('takes a screenshot', async () => {
const {target, frontend} = getBrowserAndPages();
await target.goto(`${resourcesPath}/console/built-ins.html`);
diff --git a/test/shared/BUILD.gn b/test/shared/BUILD.gn
index 9372142..bcdab3c 100644
--- a/test/shared/BUILD.gn
+++ b/test/shared/BUILD.gn
@@ -9,8 +9,7 @@
sources = [
"config.ts",
"helper.ts",
- "runner.ts",
- "screenshot.ts",
"text-color.ts",
]
+ deps = [ "../conductor" ]
}
diff --git a/test/shared/helper.ts b/test/shared/helper.ts
index 26cc32e..e2a66f0 100644
--- a/test/shared/helper.ts
+++ b/test/shared/helper.ts
@@ -2,9 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+import * as os from 'os';
import {performance} from 'perf_hooks';
import * as puppeteer from 'puppeteer';
-import * as os from 'os';
+
+import {reloadDevTools} from '../conductor/hooks.js';
+import {getBrowserAndPages} from '../conductor/puppeteer-state.js';
export let platform: string;
switch (os.platform()) {
@@ -21,24 +24,6 @@
break;
}
-interface BrowserAndPages {
- browser: puppeteer.Browser;
- target: puppeteer.Page;
- frontend: puppeteer.Page;
- screenshot?: puppeteer.Page;
-}
-
-const targetPage = Symbol('TargetPage');
-const frontEndPage = Symbol('DevToolsPage');
-const screenshotPage = Symbol('ScreenshotPage');
-const browserInstance = Symbol('BrowserInstance');
-
-interface ResetPages {
- (opts?: {enabledExperiments?: string[], selectedPanel?: {name: string, selector?: string}}): void
-}
-
-export let resetPages: ResetPages;
-
// TODO: Remove once Chromium updates its version of Node.js to 12+.
const globalThis: any = global;
@@ -50,7 +35,7 @@
* dances.
*/
const collectAllElementsFromPage = async (root?: puppeteer.JSHandle) => {
- const frontend: puppeteer.Page = globalThis[frontEndPage];
+ const {frontend} = getBrowserAndPages();
await frontend.evaluate(root => {
const container = (self as any);
container.__elements = [];
@@ -100,7 +85,7 @@
export const click = async (
selector: string|puppeteer.JSHandle,
options?: {root?: puppeteer.JSHandle, clickOptions?: puppeteer.ClickOptions}) => {
- const frontend: puppeteer.Page = globalThis[frontEndPage];
+ const {frontend} = getBrowserAndPages();
if (!frontend) {
throw new Error('Unable to locate DevTools frontend page. Was it stored first?');
}
@@ -132,7 +117,7 @@
};
export const typeText = async (text: string) => {
- const frontend: puppeteer.Page = globalThis[frontEndPage];
+ const {frontend} = getBrowserAndPages();
if (!frontend) {
throw new Error('Unable to locate DevTools frontend page. Was it stored first?');
}
@@ -142,7 +127,7 @@
// Get a single element handle, across Shadow DOM boundaries.
export const $ = async (selector: string, root?: puppeteer.JSHandle) => {
- const frontend: puppeteer.Page = globalThis[frontEndPage];
+ const {frontend} = getBrowserAndPages();
if (!frontend) {
throw new Error('Unable to locate DevTools frontend page. Was it stored first?');
}
@@ -160,7 +145,7 @@
// Get multiple element handles, across Shadow DOM boundaries.
export const $$ = async (selector: string, root?: puppeteer.JSHandle) => {
- const frontend: puppeteer.Page = globalThis[frontEndPage];
+ const {frontend} = getBrowserAndPages();
if (!frontend) {
throw new Error('Unable to locate DevTools frontend page. Was it stored first?');
}
@@ -179,7 +164,7 @@
* @param root The root of the search.
*/
export const $textContent = async (textContent: string, root?: puppeteer.JSHandle) => {
- const frontend: puppeteer.Page = globalThis[frontEndPage];
+ const {frontend} = getBrowserAndPages();
if (!frontend) {
throw new Error('Unable to locate DevTools frontend page. Was it stored first?');
}
@@ -274,35 +259,16 @@
});
};
-export const store =
- (browser: puppeteer.Browser, target: puppeteer.Page, frontend: puppeteer.Page, screenshot: puppeteer.Page | undefined,
- reset: ResetPages) => {
- globalThis[browserInstance] = browser;
- globalThis[targetPage] = target;
- globalThis[frontEndPage] = frontend;
- globalThis[screenshotPage] = screenshot;
- resetPages = reset;
- };
+export const resourcesPath = 'http://localhost:8090/test/e2e/resources';
-export const getBrowserAndPages = (): BrowserAndPages => {
- if (!globalThis[targetPage]) {
- throw new Error('Unable to locate target page. Was it stored first?');
- }
+export const enableExperiment = async (experiment: string) => {
+ const {frontend} = getBrowserAndPages();
+ await frontend.evaluate(experiment => {
+ // @ts-ignore
+ globalThis.Root.Runtime.experiments.setEnabled(experiment, true);
+ }, experiment);
- if (!globalThis[frontEndPage]) {
- throw new Error('Unable to locate DevTools frontend page. Was it stored first?');
- }
-
- if (!globalThis[browserInstance]) {
- throw new Error('Unable to locate browser instance. Was it stored first?');
- }
-
- return {
- browser: globalThis[browserInstance],
- target: globalThis[targetPage],
- frontend: globalThis[frontEndPage],
- screenshot: globalThis[screenshotPage],
- };
+ await reloadDevTools();
};
-export const resourcesPath = 'http://localhost:8090/test/e2e/resources';
+export {getBrowserAndPages, reloadDevTools};
diff --git a/test/shared/master.ts b/test/shared/master.ts
deleted file mode 100644
index 90c0597..0000000
--- a/test/shared/master.ts
+++ /dev/null
@@ -1,198 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/* eslint-disable no-console */
-// no-console disabled here as this is a test runner and expects to output to the console
-
-import {ChildProcessWithoutNullStreams, spawn} from 'child_process';
-import * as cluster from 'cluster';
-import {join} from 'path';
-
-import {getEnvVar} from './config.js';
-import {WorkerMessage} from './worker-message.js';
-
-const envDebug = getEnvVar('DEBUG');
-
-// Lock to one worker to avoid flakiness.
-// TODO: unlock when it's clear why there are flakes.
-const envJobs = getEnvVar('JOBS', envDebug ? 1 : 1);
-
-const workerCount = envJobs;
-const taskList: string[] = [];
-const ports = new Map<cluster.Worker, number>();
-
-export function purge() {
- taskList.length = 0;
-}
-
-export function killWorkers() {
- const workerNames = Object.keys(cluster.workers);
- for (const workerName of workerNames) {
- const worker = cluster.workers[workerName];
- if (!worker) {
- continue;
- }
-
- worker.process.kill('SIGTERM');
- }
-}
-
-export function enqueue(tasks: string[]) {
- for (const task of tasks) {
- taskList.push(task);
- }
-
- // Wake up the workers.
- const workerNames = Object.keys(cluster.workers);
- for (const workerName of workerNames) {
- const worker = cluster.workers[workerName];
- if (!worker) {
- continue;
- }
-
- worker.send({ping: true});
- }
-}
-
-let hostedModeServer: ChildProcessWithoutNullStreams;
-let exitCode = 0;
-let finishCount = 0;
-export function init() {
- finishCount = workerCount;
- console.log(`Creating ${workerCount} worker${(workerCount === 1 ? '' : 's')}.`);
-
- // Silence the stdout of individual workers.
- cluster.settings.silent = true;
-
- for (let w = 0; w < workerCount; w++) {
- const worker = cluster.fork({silent: true});
- worker.on('message', onWorkerMessage);
- worker.on('exit', onWorkerDisconnect);
-
- // Pipe through all errors.
- if (worker.process.stderr) {
- worker.process.stderr.pipe(process.stderr);
- }
-
- ports.set(worker, 9222 + w);
- }
-
- console.log('Spawning hosted mode server');
- const serverScriptPath = join(__dirname, '..', '..', 'scripts', 'hosted_mode', 'server.js');
- const cwd = join(__dirname, '..', '..');
- const {execPath} = process;
- hostedModeServer = spawn(execPath, [serverScriptPath], {cwd});
- hostedModeServer.on('error', handleHostedModeError);
- hostedModeServer.stderr.on('data', handleHostedModeError);
-
- // Debug mode: write out server responses.
- if (envDebug) {
- hostedModeServer.stdout.on('data', (message: any) => {
- console.log(`Hosted mode server: ${message}`);
- });
- }
-}
-
-function handleHostedModeError(data: Error) {
- console.log(`Hosted mode server: ${data}`);
- interruptionHandler();
-}
-
-export function interruptionHandler() {
- exitCode = 1;
- shutdown();
-}
-
-function shutdown() {
- killWorkers();
-
- console.log('\n');
- console.log('Stopping hosted mode server');
- hostedModeServer.kill();
-
- console.log(`Exiting with status code ${exitCode}`);
- process.exit(exitCode);
-}
-
-function locateWorkerByPid(pid: number): cluster.Worker {
- const workerName = Object.keys(cluster.workers).find(key => {
- const worker = cluster.workers[key];
- if (!worker) {
- return;
- }
-
- return worker.process.pid === pid;
- });
-
- if (!workerName) {
- throw new Error(`Received message from pid ${pid}, but was unable to find a corresponding worker`);
- }
-
- return cluster.workers[workerName] as cluster.Worker;
-}
-
-const envVerbose = getEnvVar('VERBOSE');
-function log(pid: number, msg: string, alwaysShow = false) {
- const showMessages = envVerbose || alwaysShow;
- if (!showMessages) {
- return;
- }
-
- console.log(`Worker (${pid}): ${msg}`);
-}
-
-function onWorkerMessage({pid, details}: {pid: number, details: WorkerMessage}) {
- const worker = locateWorkerByPid(pid);
-
- switch (details) {
- case 'requestTask': {
- const task = taskList.shift();
- if (!task) {
- log(pid, 'Finished');
- finishCount--;
- if (finishCount === 0 && !envDebug) {
- shutdown();
- }
- break;
- }
-
- log(pid, `Requested task, being given ${task}`);
- worker.send({
- type: 'task',
- task,
- });
- break;
- }
-
- case 'notifyInit': {
- log(pid, 'Ready - Sending port');
- worker.send({
- port: ports.get(worker),
- });
- break;
- }
-
- case 'failure': {
- log(pid, 'Failure');
- exitCode = 1;
- break;
- }
-
- // All other messages, e.g. pass/fail from tests.
- default: {
- console.log(details);
- break;
- }
- }
-}
-
-function onWorkerDisconnect(code: number, signal: NodeJS.Signals) {
- if (signal) {
- console.log('Worker killed by ' + signal);
- } else if (code) {
- console.log('Worker exited with code ' + code);
- } else {
- console.log('Worker exited successfully');
- }
-}
diff --git a/test/shared/runner.ts b/test/shared/runner.ts
deleted file mode 100644
index d25ac24..0000000
--- a/test/shared/runner.ts
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/* eslint-disable no-console */
-// no-console disabled here as this is a test runner and expects to output to the console
-
-import * as cluster from 'cluster';
-import {getEnvVar} from './config.js';
-import {color, TextColor} from './text-color.js';
-
-const role = cluster.isMaster ? './master' : './worker';
-const roleImpl = require(role);
-roleImpl.init();
-
-const envNoShuffle = getEnvVar('NO_SHUFFLE', false);
-const testListPath = getEnvVar('TEST_LIST');
-const envTestFile = getEnvVar('TEST_FILE');
-const envDebug = getEnvVar('DEBUG', false);
-const envStressTest = getEnvVar('STRESS', false);
-let envIterations = getEnvVar('ITERATIONS', envStressTest ? 37 : 1);
-
-if (!testListPath) {
- throw new Error('Must specify a list of tests in the "TEST_LIST" environment variable.');
-}
-
-function shuffleTestFiles(files: string[]) {
- if (envNoShuffle) {
- console.log('Running tests unshuffled');
- return files;
- }
-
- const swap = (arr: string[], a: number, b: number) => {
- const temp = arr[a];
- arr[a] = arr[b];
- arr[b] = temp;
- };
-
- for (let i = files.length; i >= 0; i--) {
- const a = Math.floor(Math.random() * files.length);
- const b = Math.floor(Math.random() * files.length);
-
- swap(files, a, b);
- }
-
- console.log(`Enqueuing tests in the following order:\n${files.join('\n')}`);
- return files;
-}
-
-async function runTests() {
- if (!cluster.isMaster) {
- return;
- }
-
- let {testList} = await import(testListPath!);
- if (envTestFile) {
- let filterFile = envTestFile;
- if (filterFile.endsWith('.ts')) // users might get confused and try to filter the ts file
- {
- filterFile = filterFile.replace(/\.[^.]+$/, '.js');
- } else if (!filterFile.endsWith('.js')) // Maybe the user left the .js off?
- {
- filterFile += '.js';
- }
- testList = testList.filter((testFile: string) => testFile.endsWith(filterFile));
- }
-
- const shuffledTests = shuffleTestFiles(testList);
-
- if (envIterations > 1) {
- console.log(`${color('Iterations:', TextColor.MAGENTA)} ${envIterations}`);
- }
-
- do {
- if (envDebug) {
- logHelp();
- await waitForInput();
- }
-
- roleImpl.enqueue(shuffledTests);
- } while (envDebug || --envIterations > 0);
-}
-
-function logHelp() {
- console.log(color('Running in debug mode.', TextColor.MAGENTA));
- console.log(color(' - Press any key to run the test suite.', TextColor.MAGENTA));
- console.log(color(' - Press ctrl + c to quit.', TextColor.MAGENTA));
-}
-
-async function waitForInput() {
- return new Promise(resolve => {
- process.stdin.setRawMode(true);
- process.stdin.resume();
- process.stdin.on('data', async str => {
- // Listen for ctrl+c to exit.
- if (str.toString() === '\x03') {
- roleImpl.interruptionHandler();
- }
- resolve();
- });
- });
-}
-
-runTests();
diff --git a/test/shared/screenshot.ts b/test/shared/screenshot.ts
deleted file mode 100644
index 666ae90..0000000
--- a/test/shared/screenshot.ts
+++ /dev/null
@@ -1,166 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/* eslint-disable no-console */
-// no-console disabled here as this is a test runner and expects to output to the console
-
-import {assert} from 'chai';
-import * as childProcess from 'child_process';
-import * as fs from 'fs';
-import * as path from 'path';
-import {join} from 'path';
-import * as puppeteer from 'puppeteer';
-import * as rimraf from 'rimraf';
-
-import {getBrowserAndPages, platform} from './helper.js';
-
-const goldensScreenshotFolder = join(__dirname, `../screenshots/goldens/${platform}`);
-const generatedScreenshotFolder = join(__dirname, `../screenshots/.generated/${platform}`);
-
-// Delete and create the generated images.
-if (fs.existsSync(generatedScreenshotFolder)) {
- rimraf.sync(generatedScreenshotFolder);
-}
-
-fs.mkdirSync(goldensScreenshotFolder, {recursive: true});
-fs.mkdirSync(generatedScreenshotFolder, {recursive: true});
-
-const defaultScreenshotOpts: puppeteer.ScreenshotOptions = {
- type: 'png',
- encoding: 'binary',
-};
-
-export const assertElementScreenshotUnchanged = async (element: puppeteer.ElementHandle | null, fileName: string, options: Partial<puppeteer.ScreenshotOptions> = {}) => {
-
- if (!element) {
- assert.fail(`Given element for test ${fileName} was not found.`);
- return;
- }
-
- return assertScreenshotUnchanged(element, fileName, options);
-};
-
-export const assertPageScreenshotUnchanged = async (page: puppeteer.Page, fileName: string, options: Partial<puppeteer.ScreenshotOptions> = {}) => {
- return assertScreenshotUnchanged(page, fileName, {
- ...options,
- fullPage: true,
- });
-};
-
-const assertScreenshotUnchanged =
- async (elementOrPage: puppeteer.ElementHandle | puppeteer.Page, fileName: string, options: Partial<puppeteer.ScreenshotOptions> = {}) => {
- try {
- const goldensScreenshotPath = join(goldensScreenshotFolder, fileName);
- const generatedScreenshotPath = join(generatedScreenshotFolder, fileName);
-
- if (fs.existsSync(generatedScreenshotPath)) {
- throw new Error(`${generatedScreenshotPath} already exists.`);
- }
-
- const opts = {...defaultScreenshotOpts, ...options, path: generatedScreenshotPath};
- await elementOrPage.screenshot(opts);
-
- // In the event that a golden does not exist, assume the generated screenshot is the new golden.
- if (!fs.existsSync(goldensScreenshotPath)) {
- console.log('Golden does not exist, using generated screenshot.');
- setGoldenToGenerated(goldensScreenshotPath, generatedScreenshotPath);
- }
-
- return compare(goldensScreenshotPath, generatedScreenshotPath, fileName);
- } catch (error) {
- throw new Error(`Error occurred when comparing screenhots: ${error.stack}`);
- }
-};
-
-interface ImageDiff {
- rawMisMatchPercentage: number;
- diffPath: string;
-}
-
-async function imageDiff(golden: string, generated: string, isInteractive = false) {
- const imageDiffPath = join(__dirname, `../screenshots/image_diff/${platform}/image_diff`);
- return new Promise<ImageDiff>(async (resolve, reject) => {
- try {
- const imageDiff: ImageDiff = {rawMisMatchPercentage: 0, diffPath: ''};
- const diffText = await exec(`${imageDiffPath} --histogram ${golden} ${generated}`);
-
- // Parse out the number from the cmd output, i.e. diff: 48.9% failed => 48.9
- imageDiff.rawMisMatchPercentage = Number(diffText.replace(/^diff:\s/, '').replace(/%.*/, ''));
-
- if (Number.isNaN(imageDiff.rawMisMatchPercentage)) {
- reject('Unable to compare images');
- }
-
- // Only create a diff image if necessary.
- if (isInteractive || imageDiff.rawMisMatchPercentage > 0) {
- imageDiff.diffPath = join(path.dirname(generated), `${path.basename(generated, '.png')}-diff.png`);
- await exec(`${imageDiffPath} --diff ${golden} ${generated} ${imageDiff.diffPath}`);
- }
-
- resolve(imageDiff);
- } catch (error) {
- reject(new Error(`Error when running image_diff: ${error.stack}`));
- }
- });
-}
-
-async function exec(cmd: string) {
- return new Promise<string>((resolve, reject) => {
- let commandOutput = '';
- try {
- commandOutput = childProcess.execSync(cmd, {encoding: 'utf8'});
- resolve(commandOutput);
- } catch (error) {
- // image_diff will exit with a status code of 1 if the diff is too big, so
- // this needs to be caught, but the outcome is the same - we want to send
- // back the string for processing.
- if (error.stdout && error.stdout.indexOf('diff') === -1) {
- reject(new Error(`Comparing diff failed. stdout: "${error.stdout}"`));
- return;
- }
-
- resolve(error.stdout);
- }
- });
-}
-
-async function compare(golden: string, generated: string, fileName: string) {
- const {screenshot} = getBrowserAndPages();
- if (screenshot) {
- await screenshot.evaluate(opts => {
- (self as any).setState(opts);
- }, {type: 'status', msg: `Comparing ${fileName} to generated image...`});
- }
-
- const isInteractive = typeof screenshot !== 'undefined';
- const {rawMisMatchPercentage, diffPath} = await imageDiff(golden, generated, isInteractive);
-
- // Interactively allow the user to choose.
- if (screenshot && diffPath) {
- const root = join(__dirname, '../..');
- const left = golden.replace(root, '');
- const right = generated.replace(root, '');
- const diff = diffPath.replace(root, '');
- const type = 'outcome';
-
- await screenshot.evaluate(opts => {
- (self as any).setState(opts);
- }, {type, left, right, diff, rawMisMatchPercentage, fileName});
-
- const elementHandle = await screenshot.waitForSelector('.choice', {timeout: 0});
- const choice = await elementHandle.evaluate(node => node.getAttribute('data-choice'));
-
- // If they choose the test output, copy the generated screenshot over the golden.
- if (choice === 'generated') {
- setGoldenToGenerated(golden, generated);
- }
- } else { // Assert no change.
- assert.isBelow(rawMisMatchPercentage, 1, `There is a ${rawMisMatchPercentage}% difference`);
- }
-}
-
-function setGoldenToGenerated(golden: string, generated: string) {
- console.log(`Copying ${generated} to ${golden}.`);
- fs.copyFileSync(generated, golden);
-}
diff --git a/test/shared/tsconfig.json b/test/shared/tsconfig.json
index 12f1aec..a212d66 100644
--- a/test/shared/tsconfig.json
+++ b/test/shared/tsconfig.json
@@ -7,5 +7,10 @@
"checkJs": false,
"allowJs": false
},
+ "references": [
+ {
+ "path": "../conductor"
+ }
+ ],
"extends": "../../tsconfig.base.json",
}
diff --git a/test/shared/worker-message.ts b/test/shared/worker-message.ts
deleted file mode 100644
index 9a0a637..0000000
--- a/test/shared/worker-message.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-export type WorkerMessage = 'requestTask'|'notifyInit'|'failure';
diff --git a/test/shared/worker-task.ts b/test/shared/worker-task.ts
deleted file mode 100644
index f9e09f6..0000000
--- a/test/shared/worker-task.ts
+++ /dev/null
@@ -1,286 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/* eslint-disable no-console */
-// no-console disabled here as this is a test runner and expects to output to the console.
-
-import * as Mocha from 'mocha';
-import * as puppeteer from 'puppeteer';
-
-import {getEnvVar} from './config.js';
-import {logFailure, logToStdOut, store} from './helper.js';
-import {color, TextColor} from './text-color.js';
-
-interface DevToolsTarget {
- url: string;
- id: string;
-}
-
-const envStress = getEnvVar('STRESS');
-const envSlowMo = getEnvVar('SLOWMO', envStress ? 50 : undefined);
-const envChromeBinary = getEnvVar('CHROME_BIN');
-const envInteractive = getEnvVar('INTERACTIVE');
-const envDebug = getEnvVar('DEBUG');
-const envChromeFeatures = getEnvVar('CHROME_FEATURES');
-
-let defaultTimeout = 5000;
-if (envDebug || envInteractive) {
- defaultTimeout = 300000;
-} else if (envStress) {
- defaultTimeout = 10000;
-}
-
-const envTimeout = getEnvVar('TIMEOUT', defaultTimeout);
-const envThrottleRate = getEnvVar('THROTTLE', envStress ? 3 : 1);
-
-const interactivePage = 'http://localhost:8090/test/screenshots/interactive/index.html';
-const blankPage = 'data:text/html,';
-const headless = !envDebug;
-const width = 1280;
-const height = 720;
-
-let mochaRun: Mocha.Runner;
-let launchedBrowser: puppeteer.Browser;
-let envPort = 9222;
-export async function initBrowser(port: number) {
- envPort = port;
-
- const launchArgs = [`--remote-debugging-port=${envPort}`];
- const opts: puppeteer.LaunchOptions = {
- headless,
- executablePath: envChromeBinary,
- defaultViewport: null,
- slowMo: envSlowMo,
- };
-
- // Toggle either viewport or window size depending on headless vs not.
- if (headless) {
- opts.defaultViewport = {width, height};
- } else {
- launchArgs.push(`--window-size=${width},${height}`);
- }
-
- if (envChromeFeatures !== undefined) {
- launchArgs.push(envChromeFeatures);
- }
-
- opts.args = launchArgs;
- launchedBrowser = await puppeteer.launch(opts);
-
- try {
- let screenshotPage: puppeteer.Page|undefined;
- if (envInteractive) {
- const screenshotBrowser = await puppeteer.launch({
- headless: false,
- executablePath: envChromeBinary,
- defaultViewport: null,
- args: [`--window-size=${width},${height}`],
- });
- screenshotPage = await screenshotBrowser.newPage();
- await screenshotPage.goto(interactivePage, {waitUntil: ['domcontentloaded']});
- }
-
- // Load the target page.
- const srcPage = await launchedBrowser.newPage();
- await srcPage.goto(blankPage);
-
- // Now get the DevTools listings.
- const devtools = await launchedBrowser.newPage();
- await devtools.goto(`http://localhost:${envPort}/json`);
-
- // Find the appropriate item to inspect the target page.
- const listing = await devtools.$('pre');
- const json = await devtools.evaluate(listing => listing.textContent, listing);
- const targets: DevToolsTarget[] = JSON.parse(json);
- const target = targets.find(target => target.url === blankPage);
- if (!target) {
- throw new Error(`Unable to find target page: ${blankPage}`);
- }
-
- const {id} = target;
- await devtools.close();
-
- // Connect to the DevTools frontend.
- const frontend = await launchedBrowser.newPage();
- const frontendUrl = `http://localhost:8090/front_end/devtools_app.html?ws=localhost:${envPort}/devtools/page/${id}`;
- await frontend.goto(frontendUrl, {waitUntil: ['networkidle2', 'domcontentloaded']});
-
- frontend.on('error', err => {
- console.error(color('Error in Frontend', TextColor.RED));
- console.error(err);
- logFailure();
- });
-
- frontend.on('pageerror', err => {
- console.error(color('Page Error in Frontend', TextColor.RED));
- console.error(err);
- logFailure();
- });
-
- const resetPages =
- async (opts: {enabledExperiments?: string[], selectedPanel?: {name: string, selector?: string}} = {}) => {
- // Reload the target page.
- await srcPage.goto(blankPage, {waitUntil: ['domcontentloaded']});
-
- // Clear any local storage settings.
- await frontend.evaluate(() => localStorage.clear());
-
- const {enabledExperiments} = opts;
- let {selectedPanel} = opts;
- await frontend.evaluate(enabledExperiments => {
- for (const experiment of enabledExperiments) {
- // @ts-ignore
- globalThis.Root.Runtime.experiments.setEnabled(experiment, true);
- }
- }, enabledExperiments || []);
-
- if (selectedPanel) {
- await frontend.evaluate(name => {
- // @ts-ignore
- globalThis.localStorage.setItem('panel-selectedTab', `"${name}"`);
- }, selectedPanel.name);
- }
-
- // Reload the DevTools frontend and await the elements panel.
- await frontend.goto(blankPage, {waitUntil: ['domcontentloaded']});
- await frontend.goto(frontendUrl, {waitUntil: ['domcontentloaded']});
-
- // Default to elements if no other panel is defined.
- if (!selectedPanel) {
- selectedPanel = {
- name: 'elements',
- selector: '.elements',
- };
- }
-
- if (!selectedPanel.selector) {
- return;
- }
-
- // For the unspecified case wait for loading, then wait for the elements panel.
- await frontend.waitForSelector(selectedPanel.selector);
-
- // Under stress conditions throttle the CPU down.
- if (envThrottleRate !== 1) {
- logToStdOut(`${color('Throttling CPU:', TextColor.MAGENTA)} ${envThrottleRate}x slowdown`);
-
- const client = await frontend.target().createCDPSession();
- await client.send('Emulation.setCPUThrottlingRate', {rate: envThrottleRate});
- }
-
- if (envSlowMo) {
- logToStdOut(`${color('Slow mo:', TextColor.MAGENTA)} ${envSlowMo}ms per step`);
- }
- };
-
- store(launchedBrowser, srcPage, frontend, screenshotPage, resetPages);
- } catch (err) {
- console.warn(err);
- }
-}
-
-interface TestResult {
- state?: string;
- timedOut: boolean;
- title: string;
- duration: number;
- err?: {
- name: string,
- message: string,
- showDiff: boolean,
- actual: string,
- expected: string,
- stack: string,
- }
-}
-
-function formatTestResult(suiteTitle: string, timeout: number, testResult: TestResult) {
- let output = '';
- let stateColor = TextColor.GREEN;
- if (!testResult.state) {
- stateColor = TextColor.CYAN;
- } else if (testResult.state !== 'passed') {
- stateColor = TextColor.RED;
- }
-
- const testColor = testResult.state ? TextColor.DIM : TextColor.CYAN;
- let state = capitalize(testResult.state);
-
- if (testResult.timedOut || testResult.duration > timeout) {
- state += ' - Timed out';
- }
-
- // [Passed].
- output += `[${color(state, stateColor)}] `;
-
- // Foo can be read by bar.
- output += `${color(`${suiteTitle} ${testResult.title}`, testColor)}`;
-
- // : 300ms.
- if (testResult.duration) {
- const durationColor = testResult.duration > timeout ? TextColor.RED : TextColor.CYAN;
- output += `: ${color(`${testResult.duration}ms`, durationColor)}`;
- }
- output += '\n';
-
- if (testResult.err) {
- const {message, stack, actual, expected} = testResult.err;
- output += `\n${color(capitalize(message), TextColor.RED)}\n${stack}\n\n`;
-
- if (actual && expected) {
- output += color(`Actual: ${actual}\n\n`, TextColor.RED);
- output += color(`Expected: ${expected}\n`, TextColor.GREEN);
- }
- }
-
- return output;
-}
-
-export async function runTest(test: string): Promise<{code: number, output: string}> {
- let output = color(`Worker (${process.pid}): ${test}\n`, TextColor.DIM);
- let suiteTitle = '';
- let code = 0;
- return new Promise(resolve => {
- const mocha = new Mocha();
- mocha.addFile(test);
- mocha.ui('bdd');
- mocha.reporter('list');
- mocha.timeout(envTimeout);
-
- mochaRun = mocha.run();
- mochaRun.on('end', () => {
- (mocha as any).unloadFiles();
- resolve({output, code});
- });
-
- mochaRun.on('suite', (suite: {title: string}) => {
- suiteTitle = suite.title;
- });
-
- mochaRun.on('test end', (testResult: TestResult) => {
- output += formatTestResult(suiteTitle, envTimeout, testResult);
- });
-
- mochaRun.on('fail', (testResult: TestResult) => {
- console.log(testResult);
- code = 1;
- });
- });
-}
-
-export function cancelTest() {
- if (!mochaRun) {
- return;
- }
-
- mochaRun.abort();
-}
-
-function capitalize(str?: string) {
- if (!str) {
- return 'Skipped';
- }
-
- return `${str[0].toUpperCase()}${str.slice(1)}`;
-}
diff --git a/test/shared/worker.ts b/test/shared/worker.ts
deleted file mode 100644
index 54c0362..0000000
--- a/test/shared/worker.ts
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/* eslint-disable no-console */
-// no-console disabled here as this is a test runner and expects to output to the console.
-
-import {getEnvVar} from './config.js';
-import {WorkerMessage} from './worker-message.js';
-import {cancelTest, initBrowser, runTest} from './worker-task.js';
-
-const envVerbose = getEnvVar('VERBOSE');
-function log(msg: string) {
- if (!envVerbose) {
- return;
- }
-
- console.log(`Worker (${process.pid}): ${msg}`);
-}
-
-function send(msg: WorkerMessage) {
- // Process.send only exists in workers, but TS doesn't know that we are in a worker
- // so we have to be overly cautious here.
- if (!process.send) {
- return;
- }
-
- process.send({
- pid: process.pid,
- details: msg,
- });
-}
-
-function requestTask() {
- send('requestTask');
-}
-
-let isSleeping = false;
-async function onMessage(message: {ping?: boolean, task: string|null, port?: number}) {
- const {ping, task, port} = message;
- if (ping) {
- if (!isSleeping) {
- return;
- }
-
- log('Pinged. Waking.');
- isSleeping = false;
- process.nextTick(requestTask);
- }
-
- if (port) {
- log(`Initializing browser on port ${port}.`);
- await initBrowser(port);
-
- // Once booted, ask for a task.
- isSleeping = true;
- process.nextTick(requestTask);
- return;
- }
-
- if (!task) {
- log('Sleeping.');
- isSleeping = true;
- return;
- }
-
- isSleeping = false;
- log(`Running tests from ${task}`);
- const {code, output} = await runTest(task);
-
- // Force the cast in this case as an exception to the norm.
- send(output as WorkerMessage);
-
- if (code === 1) {
- send('failure');
- }
-
- process.nextTick(requestTask);
-}
-
-function interruptionHandler() {
- log('Shutting down - aborting tests.');
- cancelTest();
-}
-
-export function init() {
- process.on('message', onMessage);
- send('notifyInit');
-}
-
-process.on('SIGINT', interruptionHandler);
-process.on('SIGTERM', interruptionHandler);
-process.on('uncaughtException', interruptionHandler);
-process.stdin.resume();