diff --git a/.gitignore b/.gitignore index 1f50b7b822..97a633cb57 100644 --- a/.gitignore +++ b/.gitignore @@ -38,6 +38,7 @@ test/shared/**/helper.d.ts test/shared/**/runner.d.ts test/shared/**/master.d.ts test/shared/**/worker-task.d.ts +test/shared/**/worker-message.d.ts test/shared/**/worker.d.ts test/shared/**/config.d.ts test/shared/**/text-color.d.ts diff --git a/front_end/media/PlayerListView.js b/front_end/media/PlayerListView.js index d1c59c34c7..ebbde8b865 100644 --- a/front_end/media/PlayerListView.js +++ b/front_end/media/PlayerListView.js @@ -28,6 +28,7 @@ export class PlayerEntryTreeElement extends UI.TreeOutline.TreeElement { this._playerStatus = playerStatus; this._displayContainer = displayContainer; this.setLeadingIcons([UI.Icon.Icon.create('smallicon-videoplayer-playing', 'media-player')]); + this.listItemElement.classList.add('player-entry-tree-element'); } /** @@ -66,6 +67,7 @@ export class PlayerListView extends UI.Widget.VBox { // Players active in this tab. this._playerList = this._addListSection(Common.UIString('Players')); + this._playerList.listItemElement.classList.add('player-entry-header'); } /** diff --git a/scripts/test/run_test_suite.py b/scripts/test/run_test_suite.py index f1569bd719..2c4acfcf16 100644 --- a/scripts/test/run_test_suite.py +++ b/scripts/test/run_test_suite.py @@ -26,6 +26,10 @@ def parse_options(cli_args): parser.add_argument('--chrome-binary', dest='chrome_binary', help='path to Chromium binary') parser.add_argument('--test-suite', dest='test_suite', help='path to test suite') parser.add_argument('--test-file', dest='test_file', help='an absolute path for the file to test') + parser.add_argument( + '--chrome-features', + dest='chrome_features', + help='comma separated list of strings passed to --enable-features on the chromium commandline') return parser.parse_args(cli_args) @@ -40,10 +44,12 @@ def compile_typescript(typescript_targets): return False -def run_tests(chrome_binary, test_suite_list_path, test_file=None): +def run_tests(chrome_binary, chrome_features, test_suite_list_path, test_file=None): env = os.environ.copy() env['CHROME_BIN'] = chrome_binary env['TEST_LIST'] = test_suite_list_path + if chrome_features: + env['CHROME_FEATURES'] = chrome_features if test_file is not None: env['TEST_FILE'] = test_file @@ -64,6 +70,7 @@ def run_test(): is_cygwin = sys.platform == 'cygwin' chrome_binary = None test_suite = None + chrome_features = None # Default to the downloaded / pinned Chromium binary downloaded_chrome_binary = devtools_paths.downloaded_chrome_binary_path() @@ -77,6 +84,9 @@ def run_test(): print('Unable to find a Chrome binary at \'%s\'' % chrome_binary) sys.exit(1) + if OPTIONS.chrome_features: + chrome_features = '--enable-features=%s' % OPTIONS.chrome_features + if (chrome_binary is None): print('Unable to run, no Chrome binary provided') sys.exit(1) @@ -88,7 +98,7 @@ def run_test(): test_suite = OPTIONS.test_suite test_file = OPTIONS.test_file - print('Using Chromium binary (%s)\n' % chrome_binary) + print('Using Chromium binary ({}{})\n'.format(chrome_binary, ' ' + chrome_features if chrome_features else '')) print('Using Test Suite (%s)\n' % test_suite) if test_file is not None: @@ -114,7 +124,7 @@ def run_test(): 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, test_suite_list_path, test_file=test_file) + errors_found = run_tests(chrome_binary, chrome_features, test_suite_list_path, test_file=test_file) except Exception as err: print(err) diff --git a/test/e2e/BUILD.gn b/test/e2e/BUILD.gn index 6feb416d50..f98464548e 100644 --- a/test/e2e/BUILD.gn +++ b/test/e2e/BUILD.gn @@ -13,6 +13,7 @@ ts_library("e2e") { "console", "elements", "host", + "media", "network", "rendering", "search", diff --git a/test/e2e/README.md b/test/e2e/README.md index 9f3005f62d..cdf531ba53 100644 --- a/test/e2e/README.md +++ b/test/e2e/README.md @@ -11,6 +11,15 @@ The tests therefore have a dual purpose: 1. Verify that core user stories are working as intended and are not broken by a particular DevTools frontend change. 1. Serve as documentation and reference point for how DevTools is intended to be used. +## Running tests +All tests: `npm run e2etest` (note, this requires python2 to be the default python binary!) +Some additional, optional, helpful flags: +`npm run e2etest -- --test-file=[FILTER] --chrome-binary=[LOCATION] --chrome-features=[FEATURES]` +FILTER is the name of the test file, such as `media-tab.ts` or `network-datagrid.ts`. +LOCATION is a path to the chrome executable +FEATURES is a comma separated list of chrome features passed as `--enable-features=[FEATURES]` to the chrome binary. + + ## General implementation details To that end, the "what" from the "how" are separate in end-to-end tests. diff --git a/test/e2e/helpers/BUILD.gn b/test/e2e/helpers/BUILD.gn index 0f7e89c90c..46d33b617a 100644 --- a/test/e2e/helpers/BUILD.gn +++ b/test/e2e/helpers/BUILD.gn @@ -9,6 +9,7 @@ ts_library("helpers") { sources = [ "console-helpers.ts", "elements-helpers.ts", + "media-helpers.ts", "quick_open-helpers.ts", "search-helpers.ts", "settings-helpers.ts", diff --git a/test/e2e/helpers/media-helpers.ts b/test/e2e/helpers/media-helpers.ts new file mode 100644 index 0000000000..9e68057c43 --- /dev/null +++ b/test/e2e/helpers/media-helpers.ts @@ -0,0 +1,37 @@ +// 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 {$, getBrowserAndPages, resourcesPath, waitFor} from '../../shared/helper.js'; + +export async function playMediaFile(media: string) { + const {target} = getBrowserAndPages(); + await target.goto(`${resourcesPath}/media/${media}`); + + // Need to click play manually - autoplay policy prevents it otherwise. + return new Promise(async resolve => { + await target.exposeFunction('resolve', resolve); + await target.evaluate(() => { + const videoElement = document.getElementsByName('media')[0] as HTMLVideoElement; + videoElement.addEventListener('play', () => { + resolve(); + }); + // Just in case autoplay started before we could attach an event listener. + if (!videoElement.paused || videoElement.readyState > 2) { + resolve(); + } else { + videoElement.play(); + } + }); + }); +} + +export async function getPlayerButton() { + await waitFor('.player-entry-tree-element'); + return await $('.player-entry-tree-element'); +} + +export async function getPlayerButtonText() { + const playerEntry = await getPlayerButton(); + return await playerEntry.evaluate(E => E.textContent); +} diff --git a/test/e2e/media/BUILD.gn b/test/e2e/media/BUILD.gn new file mode 100644 index 0000000000..e55d258b3b --- /dev/null +++ b/test/e2e/media/BUILD.gn @@ -0,0 +1,15 @@ +# 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("media") { + testonly = true + sources = [ "media-tab.ts" ] + + deps = [ + "../../shared", + "../helpers", + ] +} diff --git a/test/e2e/media/media-tab.ts b/test/e2e/media/media-tab.ts new file mode 100644 index 0000000000..675cac9f26 --- /dev/null +++ b/test/e2e/media/media-tab.ts @@ -0,0 +1,34 @@ +// 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 {assert} from 'chai'; +import {describe, it} from 'mocha'; + +import {resetPages} from '../../shared/helper.js'; +import {getPlayerButtonText, playMediaFile} from '../helpers/media-helpers.js'; +import {openPanelViaMoreTools} from '../helpers/settings-helpers.js'; + +function shouldRunTest() { + const features = process.env['CHROME_FEATURES']; + return features !== undefined && features.includes('MediaInspectorLogging'); +} + +describe('Media Tab', () => { + beforeEach(async () => { + await resetPages({'enabledExperiments': ['mediaInspector']}); + }); + + it('ensures video playback adds entry', async () => { + if (!shouldRunTest()) { + return; + } + + await openPanelViaMoreTools('Media'); + await playMediaFile('fisch.webm'); + const entryName = await getPlayerButtonText(); + + // Names are glitched right now, and display 32-character unguessable tokens. + assert.equal(entryName.length, 32); + }); +}); diff --git a/test/e2e/resources/media/fisch.webm b/test/e2e/resources/media/fisch.webm new file mode 100644 index 0000000000..70879ad9c9 Binary files /dev/null and b/test/e2e/resources/media/fisch.webm differ diff --git a/test/e2e/test-list.ts b/test/e2e/test-list.ts index 58763cfc01..1a5cf938bf 100644 --- a/test/e2e/test-list.ts +++ b/test/e2e/test-list.ts @@ -17,6 +17,7 @@ const tests = [ 'elements/style-pane-properties.js', 'elements/element-breadcrumbs.js', 'host/user-metrics.js', + 'media/media-tab.js', 'network/network-datagrid.js', 'rendering/vision-deficiencies.js', 'sensors/location.js', diff --git a/test/shared/config.ts b/test/shared/config.ts index fcfdb79c45..43eb8ee4f6 100644 --- a/test/shared/config.ts +++ b/test/shared/config.ts @@ -3,19 +3,20 @@ // found in the LICENSE file. interface SupportedEnvVars { - NO_SHUFFLE: boolean; // Whether or not to shuffle tests. - STRESS: boolean; // Stress test (slowdown CPU; multiple iterations) - VERBOSE: boolean; // Log stdout from the workers. - THROTTLE: number; // CPU throttle multiplier. - TEST_LIST: string; // Absolute path to the test list. - TEST_FILE: string; // Absolute path to the test file from the test list to run in isolation. - DEBUG: boolean; // Debug mode. When enabled, has longer timeouts and runs Chrome in head mode. - ITERATIONS: number; // Number of test iterations. - JOBS: number; // Number of workers to use. - SLOWMO: number; // Number of milliseconds between actions. Recommended value: 50. - CHROME_BIN: string; // Absolute path to the Chrome binary. - INTERACTIVE: boolean; // [Unused]: Placeholder for screenshot diffing. - TIMEOUT: number; // The timeout in ms to wait for tests. + NO_SHUFFLE: boolean; // Whether or not to shuffle tests. + STRESS: boolean; // Stress test (slowdown CPU; multiple iterations) + VERBOSE: boolean; // Log stdout from the workers. + THROTTLE: number; // CPU throttle multiplier. + TEST_LIST: string; // Absolute path to the test list. + TEST_FILE: string; // Absolute path to the test file from the test list to run in isolation. + DEBUG: boolean; // Debug mode. When enabled, has longer timeouts and runs Chrome in head mode. + ITERATIONS: number; // Number of test iterations. + JOBS: number; // Number of workers to use. + SLOWMO: number; // Number of milliseconds between actions. Recommended value: 50. + CHROME_BIN: string; // Absolute path to the Chrome binary. + INTERACTIVE: boolean; // [Unused]: Placeholder for screenshot diffing. + TIMEOUT: number; // The timeout in ms to wait for tests. + CHROME_FEATURES: string; // --enable-features={} for the Chrome binary. } export function getEnvVar( diff --git a/test/shared/runner.ts b/test/shared/runner.ts index 5d9287a7d6..d25ac24fec 100644 --- a/test/shared/runner.ts +++ b/test/shared/runner.ts @@ -54,7 +54,15 @@ async function runTests() { let {testList} = await import(testListPath!); if (envTestFile) { - testList = testList.filter((testFile: string) => testFile === 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); diff --git a/test/shared/worker-task.ts b/test/shared/worker-task.ts index 8638488732..4239c4e98f 100644 --- a/test/shared/worker-task.ts +++ b/test/shared/worker-task.ts @@ -22,6 +22,7 @@ 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) { @@ -60,6 +61,10 @@ export async function initBrowser(port: number) { launchArgs.push(`--window-size=${width},${height}`); } + if (envChromeFeatures !== undefined) { + launchArgs.push(envChromeFeatures); + } + opts.args = launchArgs; launchedBrowser = await puppeteer.launch(opts);