New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: Move from PhantomJS to Chrome Debug Protocol #8069

Merged
merged 11 commits into from Dec 15, 2017
@@ -3,5 +3,4 @@ Traceback (most recent call last):
b.wait_in_text(info_field("State"), "Running")
File "/build/cockpit/test/common/testlib.py", line *, in wait_in_text
*
raise Error(res['error'])
Error: timeout
@@ -1,2 +1,2 @@
File "test/verify/check-dashboard", line 218, in testBasic
File "test/verify/check-dashboard", line 224, in testBasic
b.enter_page("/network", "10.111.113.3")
View
@@ -39,6 +39,7 @@
"babel-loader": "^6.4.1",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1",
"chrome-remote-interface": "^0.25.2",
"clean-css": "~3.4.20",
"copy-webpack-plugin": "~3.0.1",
"css-loader": "~0.23.1",
View
@@ -44,6 +44,13 @@ will be printed of the test instance.
$ ./test/verify/check-session --trace --sit
Normally each test starts its own chromium headless browser process on a
separate random port. To interactively follow what a test is doing, start the
browser manually and tell the test which debug port it should attach to:
$ chromium-browser --remote-debugging-port=9222 about:blank
$ TEST_CDP_PORT=9222 ./test/verify/check-session --trace
## Details
The verify test suite is the main test suite:
@@ -72,6 +79,10 @@ You can set these environment variables to configure the test suite:
TEST_JOBS How many tests to run in parallel. The default is 1.
TEST_CDP_PORT Attach to an actually running browser that is compatible with
the Chrome Debug Protocol, on the given port. Don't use this
with parallel tests.
## Test machines and their images
The code under test is executed in one or more dedicated virtual

This file was deleted.

Oops, something went wrong.

This file was deleted.

Oops, something went wrong.
View
@@ -0,0 +1,238 @@
#!/usr/bin/env node
/*
* This file is part of Cockpit.
*
* Copyright (C) 2017 Red Hat, Inc.
*
* Cockpit is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Cockpit is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Cockpit; If not, see <http://www.gnu.org/licenses/>.
*/
/* cdp-driver -- A command-line JSON input/output wrapper around
* chrome-remote-interface (Chrome Debug Protocol).
* See https://chromedevtools.github.io/devtools-protocol/
* This needs support for protocol version 1.3.
*
* Set $TEST_CDP_DEBUG environment variable to enable additional
* frame/execution context debugging.
*/
const CDP = require('chrome-remote-interface');
var enable_debug = false;
function debug(msg) {
if (enable_debug)
process.stderr.write("CDP: " + msg + "\n");
}
/**
* Format response to the client
*/
function fatal() {
console.error.apply(console.error, arguments);
process.exit(1);
}
function fail(err) {
process.stdout.write(JSON.stringify({"error": err}) + '\n');
}
function success(result) {
process.stdout.write(JSON.stringify({"result": result === undefined ? null : result}) + '\n');
}
/**
* Record console.*() calls and Log messages so that we can forward them to
* stderr and dump them on test failure
*/
var messages = [];
function setupLogging(client) {
client.Runtime.enable();
client.Runtime.consoleAPICalled(info => {
let msg = info.type + ": " + info.args.map(v => (v.value || "").toString()).join(" ");
messages.push(msg);
process.stderr.write("> " + msg + "\n")
});
client.Log.enable();
client.Log.entryAdded(entry => process.stderr.write("CDP: " + JSON.stringify(entry["entry"]) + "\n"));
}
/**
* Frame tracking
*
* For tests to be able to select the current frame (by its name) and make
* subsequent queries apply to that, we need to track frame name → frameId →
* executionContextId. Frame and context IDs can even change through page
* operations (e. g. in systemd/logs.js when reporting a crash is complete),
* so we also need a helper function to explicitly wait for a particular frame
* to load. This is very laborious, see this issue for discussing improvements:
* https://github.com/ChromeDevTools/devtools-protocol/issues/72
*/
var frameIdToContextId = {};
var frameNameToFrameId = {};
// set these to wait for a frame to be loaded
var frameWaitName = null;
var frameWaitPromiseResolve = null;
function setupFrameTracking(client) {
client.Page.enable();
// map frame names to frame IDs; root frame has no name, no need to track that
client.Page.frameNavigated(info => {
debug("frameNavigated " + JSON.stringify(info));
if (info.frame.name)
frameNameToFrameId[info.frame.name] = info.frame.id;
// were we waiting for this frame to be loaded?
if (frameWaitPromiseResolve && frameWaitName === info.frame.name) {
frameWaitPromiseResolve();
frameWaitPromiseResolve = null;
}
});
// track execution contexts so that we can map between context and frame IDs
client.Runtime.executionContextCreated(info => {
debug("executionContextCreated " + JSON.stringify(info));
frameIdToContextId[info.context.auxData.frameId] = info.context.id;
});
client.Runtime.executionContextDestroyed(info => {
debug("executionContextDestroyed " + info.executionContextId);
for (let frameId in frameIdToContextId) {
if (frameIdToContextId[frameId] == info.executionContextId) {
delete frameIdToContextId[frameId];
break;
}
}
});
}
// helper functions for testlib.py which are too unwieldy to be poked in from Python
function getFrameExecId(frame) {
var frameId = frameNameToFrameId[frame];
if (!frameId)
throw Error(`Frame ${frame} is unknown`);
var execId = frameIdToContextId[frameId];
if (!execId)
throw Error(`Frame ${frame} (${frameId}) has no executionContextId`);
return execId;
}
function expectLoadFrame(name, timeout) {
return new Promise((resolve, reject) => {
let tm = setTimeout( () => reject("timed out waiting for frame load"), timeout );
// we can only have one Page.frameNavigated() handler, so let our handler above resolve this promise
frameWaitName = name;
new Promise((fwpResolve, fwpReject) => { frameWaitPromiseResolve = fwpResolve })
.then(() => {
// For the frame to be fully valid for queries, it also needs the corresponding
// executionContextCreated() signal. This might happen before or after frameNavigated(), so wait in case
// it happens afterwards.
function pollExecId() {
if (frameIdToContextId[frameNameToFrameId[name]]) {
clearTimeout(tm);
resolve();
} else {
setTimeout(pollExecId, 100);
}
}
pollExecId();
});
});
}
/**
* SSL handling
*/
// secure by default; tests can override to "continue"
// https://chromedevtools.github.io/devtools-protocol/1-3/Security/#type-CertificateErrorAction
var ssl_bad_certificate_action = "cancel";
function setupSSLCertHandling(client) {
client.Security.enable();
client.Security.setOverrideCertificateErrors({override: true});
client.Security.certificateError(info => {
process.stderr.write(`CDP: Security.certificateError ${JSON.stringify(info)}; action: ${ssl_bad_certificate_action}\n`);
client.Security.handleCertificateError({ eventId: info.eventId, action: ssl_bad_certificate_action });
});
}
/**
* Main input/process loop
*
* Read one line with a JS expression, eval() it, and respond with the result:
* success <JSON formatted return value>
* fail <JSON formatted error>
* EOF shuts down the client.
*/
process.stdin.setEncoding('utf8');
if (process.env["TEST_CDP_DEBUG"])
enable_debug = true;
options = { };
if (process.argv.length >= 3) {
options.port = parseInt(process.argv[2]);
if (!options.port) {
process.stderr.write("Usage: cdp-driver.js [port]\n");
process.exit(1);
}
}
CDP.New(options)
.then(target => {
target.port = options.port;
CDP({target: target})
.then(client => {
setupLogging(client);
setupFrameTracking(client);
setupSSLCertHandling(client);
let input_buf = '';
process.stdin
.on('data', chunk => {
input_buf += chunk;
while (true) {
let i = input_buf.indexOf('\n');
if (i < 0)
break;
// run the command
eval(input_buf.slice(0, i))
.then(success)
.catch(fail);
input_buf = input_buf.slice(i+1);
}
})
.on('end', () => {
CDP.Close(target)
.then(() => process.exit(0))
.catch(fatal);
});
})
.catch(fatal);
})
.catch(fatal);

This file was deleted.

Oops, something went wrong.
Oops, something went wrong.
ProTip! Use n and p to navigate between commits in a pull request.