Skip to content

Commit dc97aed

Browse files
authored
test-browser: use native event handling (#8823)
Previously the browser test integration with Selenium was written to poll the browser for logs and test events at a fixed interval. With playwright, we can hook directly into the browser events, and handle them immediately. This reduces lag for general logging, result reporting, and test completion.
1 parent 741fcc1 commit dc97aed

File tree

3 files changed

+80
-145
lines changed

3 files changed

+80
-145
lines changed

bin/test-browser.js

Lines changed: 49 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,10 @@ testUrl += '?';
5757
testUrl += new URLSearchParams(pickBy(qs, identity));
5858

5959
class RemoteRunner {
60-
constructor() {
60+
constructor(browser) {
61+
this.browser = browser;
6162
this.handlers = {};
62-
this.completed = false;
63-
this.failed = false;
63+
this.handleEvent = this.handleEvent.bind(this);
6464
}
6565

6666
on(name, handler) {
@@ -72,46 +72,47 @@ class RemoteRunner {
7272
handlers[name].push(handler);
7373
}
7474

75-
handleEvents(events) {
76-
var handlers = this.handlers;
77-
78-
events.forEach((event) => {
79-
this.completed = this.completed || event.name === 'end';
80-
this.failed = this.failed || event.name === 'fail';
81-
75+
async handleEvent(event) {
76+
try {
8277
var additionalProps = ['pass', 'fail', 'pending'].indexOf(event.name) === -1 ? {} : {
8378
slow: event.obj.slow ? function () { return event.obj.slow; } : function () { return 60; },
8479
fullTitle: event.obj.fullTitle ? function () { return event.obj.fullTitle; } : undefined
8580
};
8681
var obj = Object.assign({}, event.obj, additionalProps);
8782

88-
handlers[event.name].forEach(function (handler) {
83+
this.handlers[event.name].forEach(function (handler) {
8984
handler(obj, event.err);
9085
});
9186

92-
if (event.logs && event.logs.length > 0) {
93-
event.logs.forEach(function (line) {
94-
if (line.type === 'log') {
95-
console.log(line.content);
96-
} else if (line.type === 'error') {
97-
console.error(line.content);
98-
} else {
99-
console.error('Invalid log line', line);
100-
}
101-
});
102-
console.log();
87+
switch (event.name) {
88+
case 'fail': this.handleFailed(); break;
89+
case 'end': this.handleEnd(); break;
10390
}
104-
});
105-
}
91+
} catch (e) {
92+
console.error('Tests failed:', e);
10693

107-
bail() {
108-
var handlers = this.handlers;
94+
await this.browser.close();
95+
process.exit(3);
96+
}
97+
}
10998

110-
handlers['end'].forEach(function (handler) {
111-
handler();
112-
});
99+
async handleEnd(failed) {
100+
await this.browser.close();
101+
process.exit(!process.env.PERF && failed ? 1 : 0);
102+
}
113103

114-
this.completed = true;
104+
handleFailed() {
105+
if (bail) {
106+
try {
107+
this.handlers['end'].forEach(function (handler) {
108+
handler();
109+
});
110+
} catch (e) {
111+
console.log('An error occurred while bailing:', e);
112+
} finally {
113+
this.handleEnd(true);
114+
}
115+
}
115116
}
116117
}
117118

@@ -142,7 +143,13 @@ async function startTest() {
142143

143144
console.log('Starting', browserName, 'on', testUrl);
144145

145-
const runner = new RemoteRunner();
146+
const options = {
147+
headless: true,
148+
};
149+
const browser = await playwright[browserName].launch(options);
150+
const page = await browser.newPage();
151+
152+
const runner = new RemoteRunner(browser);
146153
new MochaSpecReporter(runner);
147154
new BenchmarkConsoleReporter(runner);
148155

@@ -154,11 +161,14 @@ async function startTest() {
154161
new BenchmarkJsonReporter(runner);
155162
}
156163

157-
const options = {
158-
headless: true,
159-
};
160-
const browser = await playwright[browserName].launch(options);
161-
const page = await browser.newPage();
164+
page.exposeFunction('handleMochaEvent', runner.handleEvent);
165+
page.addInitScript(() => {
166+
window.addEventListener('message', (e) => {
167+
if (e.data.type === 'mocha') {
168+
window.handleMochaEvent(e.data.details);
169+
}
170+
});
171+
});
162172

163173
page.on('pageerror', err => {
164174
if (browserName === 'webkit' && err.toString()
@@ -174,46 +184,14 @@ async function startTest() {
174184
process.exit(1);
175185
});
176186

177-
if (process.env.BROWSER_CONSOLE) {
178-
page.on('console', message => {
179-
const { url, lineNumber } = message.location();
180-
console.log('BROWSER', message.type().toUpperCase(), `${url}:${lineNumber}`, message.text());
181-
});
182-
}
187+
page.on('console', message => {
188+
console.log(message.text());
189+
});
183190

184191
await page.goto(testUrl);
185192

186193
const userAgent = await page.evaluate('navigator.userAgent');
187194
console.log('Testing on:', userAgent);
188-
189-
const interval = setInterval(async () => {
190-
try {
191-
const events = await page.evaluate('window.testEvents()');
192-
runner.handleEvents(events);
193-
194-
if (runner.completed || (runner.failed && bail)) {
195-
if (!runner.completed && runner.failed) {
196-
try {
197-
runner.bail();
198-
} catch (e) {
199-
// Temporary debugging of bailing failure
200-
console.log('An error occurred while bailing:');
201-
console.log(e);
202-
}
203-
}
204-
205-
clearInterval(interval);
206-
await browser.close();
207-
process.exit(!process.env.PERF && runner.failed ? 1 : 0);
208-
}
209-
} catch (e) {
210-
console.error('Tests failed:', e);
211-
212-
clearInterval(interval);
213-
await browser.close();
214-
process.exit(3);
215-
}
216-
}, 1000);
217195
}
218196

219197
devserver.start(function () {

tests/integration/webrunner.js

Lines changed: 19 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -8,72 +8,31 @@
88
window.removeEventListener("load", startTests);
99

1010
if (remote) {
11-
// Capture logs for test runner output
12-
var logs = [];
13-
14-
(function () {
15-
16-
function serializeLogItem(obj, filter, space) {
17-
if (typeof obj === 'string') {
18-
return obj;
19-
} else if (obj instanceof Error) {
20-
return obj.stack;
21-
} else {
22-
return JSON.stringify(obj, filter, space);
23-
}
24-
}
25-
26-
function wrappedLog(oldLog, type) {
27-
return function () {
28-
var args = Array.prototype.slice.call(arguments);
29-
logs.push({
30-
type,
31-
content: args.map(function (arg) {
32-
return serializeLogItem(arg);
33-
}).join(' ')
34-
});
35-
oldLog.apply(console, arguments);
36-
};
37-
}
38-
39-
console.log = wrappedLog(console.log, 'log');
40-
console.error = wrappedLog(console.error, 'error');
41-
42-
})();
43-
44-
// Capture test events for test runner output
45-
var testEventsBuffer = [];
46-
47-
window.testEvents = function () {
48-
var events = testEventsBuffer;
49-
testEventsBuffer = [];
50-
return events;
51-
};
52-
5311
mocha.reporter(function (runner) {
5412
var eventNames = ['start', 'end', 'suite', 'suite end', 'pass', 'pending', 'fail'];
5513
eventNames.forEach(function (name) {
5614
runner.on(name, function (obj, err) {
57-
testEventsBuffer.push({
58-
name,
59-
obj: obj && {
60-
root: obj.root,
61-
title: obj.title,
62-
duration: obj.duration,
63-
slow: typeof obj.slow === 'function' ? obj.slow() : undefined,
64-
fullTitle: typeof obj.fullTitle === 'function' ? obj.fullTitle() : undefined
65-
},
66-
err: err && {
67-
actual: err.actual,
68-
expected: err.expected,
69-
showDiff: err.showDiff,
70-
message: err.message,
71-
stack: err.stack,
72-
uncaught: err.uncaught
15+
window.postMessage({
16+
type: 'mocha',
17+
details: {
18+
name,
19+
obj: obj && {
20+
root: obj.root,
21+
title: obj.title,
22+
duration: obj.duration,
23+
slow: typeof obj.slow === 'function' ? obj.slow() : undefined,
24+
fullTitle: typeof obj.fullTitle === 'function' ? obj.fullTitle() : undefined
25+
},
26+
err: err && {
27+
actual: err.actual,
28+
expected: err.expected,
29+
showDiff: err.showDiff,
30+
message: err.message,
31+
stack: err.stack,
32+
uncaught: err.uncaught
33+
},
7334
},
74-
logs
7535
});
76-
logs = [];
7736
});
7837
});
7938
});

tests/performance/perf.reporter.js

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,12 @@ var results = {
99
tests: {}
1010
};
1111

12-
// Capture test events for test runner output
13-
var testEventsBuffer = [];
14-
15-
global.testEvents = function () {
16-
var events = testEventsBuffer;
17-
testEventsBuffer = [];
18-
return events;
19-
};
12+
function emitMochaEvent(details) {
13+
// NodeJS perf testing just reports with console.log().
14+
if (typeof window !== 'undefined') {
15+
window.postMessage({ type: 'mocha', details });
16+
}
17+
}
2018

2119
// fix for Firefox max timing entries capped to 150:
2220
// https://bugzilla.mozilla.org/show_bug.cgi?id=1331135
@@ -38,11 +36,11 @@ exports.log = log;
3836

3937
exports.startSuite = function (suiteName) {
4038
log('Starting suite: ' + suiteName + '\n\n');
41-
testEventsBuffer.push({ name: 'suite', obj: { title: suiteName } });
39+
emitMochaEvent({ name: 'suite', obj: { title: suiteName } });
4240
};
4341

4442
exports.endSuite = function (suiteName) {
45-
testEventsBuffer.push({ name: 'suite end', obj: { title: suiteName } });
43+
emitMochaEvent({ name: 'suite end', obj: { title: suiteName } });
4644
};
4745

4846
exports.start = function (testCase, iter) {
@@ -61,8 +59,8 @@ exports.end = function (testCase) {
6159
obj.numIterations = obj.iterations.length;
6260
delete obj.iterations; // keep it simple when reporting
6361
log('median: ' + obj.median + ' ms\n');
64-
testEventsBuffer.push({ name: 'pass', obj: { title: testCase.name } });
65-
testEventsBuffer.push({ name: 'benchmark:result', obj });
62+
emitMochaEvent({ name: 'pass', obj: { title: testCase.name } });
63+
emitMochaEvent({ name: 'benchmark:result', obj });
6664
};
6765

6866
exports.startIteration = function (testCase) {
@@ -75,7 +73,7 @@ exports.endIteration = function (testCase) {
7573
};
7674

7775
exports.startAll = function () {
78-
testEventsBuffer.push({ name: 'start' });
76+
emitMochaEvent({ name: 'start' });
7977
};
8078

8179
exports.complete = function (adapter) {
@@ -97,6 +95,6 @@ exports.complete = function (adapter) {
9795
results.adapter = adapter;
9896
console.log('=>', JSON.stringify(results, null, ' '), '<=');
9997
log('\nTests Complete!\n\n');
100-
testEventsBuffer.push({ name: 'end', obj: results });
98+
emitMochaEvent({ name: 'end', obj: results });
10199
};
102100

0 commit comments

Comments
 (0)