Adds End2End test for the Media tab.

I had to add support for running the tests with chromium
--enable-features flags. This isn't on by default though, I'll leave
that up to you to turn on. The tests just exit early if the flag isn't
present.

Change-Id: I132ca71796d8f069a6fba0342f10f2f2b18c7b62
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2125249
Commit-Queue: Ted Meyer <tmathmeyer@chromium.org>
Reviewed-by: Tim van der Lippe <tvanderlippe@chromium.org>
This commit is contained in:
Ted Meyer
2020-03-31 16:29:03 +00:00
committed by Commit Bot
parent 68cc95ec12
commit 7e5ebf014b
14 changed files with 142 additions and 17 deletions
+1
View File
@@ -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
+2
View File
@@ -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');
}
/**
+13 -3
View File
@@ -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)
+1
View File
@@ -13,6 +13,7 @@ ts_library("e2e") {
"console",
"elements",
"host",
"media",
"network",
"rendering",
"search",
+9
View File
@@ -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.
+1
View File
@@ -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",
+37
View File
@@ -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);
}
+15
View File
@@ -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",
]
}
+34
View File
@@ -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);
});
});
Binary file not shown.
+1
View File
@@ -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',
+14 -13
View File
@@ -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<Key extends keyof SupportedEnvVars>(
+9 -1
View File
@@ -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);
+5
View File
@@ -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);