Skip to content
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

Add the capability to get browser console messages (closes #1738) #1818

Merged
merged 7 commits into from
Oct 9, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/api/test-controller/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
SwitchToMainWindowCommand,
SetNativeDialogHandlerCommand,
GetNativeDialogHistoryCommand,
GetBrowserConsoleMessagesCommand,
SetTestSpeedCommand,
SetPageLoadTimeoutCommand,
UseRoleCommand
Expand Down Expand Up @@ -239,6 +240,12 @@ export default class TestController {
return this.testRun.executeCommand(new GetNativeDialogHistoryCommand(), callsite);
}

_getBrowserConsoleMessages$ () {
var callsite = getCallsiteForMethod('getBrowserConsoleMessages');

return this.testRun.executeCommand(new GetBrowserConsoleMessagesCommand(), callsite);
}

_expect$ (actual) {
return new Assertion(actual, this);
}
Expand Down
45 changes: 45 additions & 0 deletions src/client/driver/driver.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import {
CurrentIframeNotFoundError,
CurrentIframeIsInvisibleError
} from '../../errors/test-run';

import BrowserConsoleMessages from '../../test-run/browser-console-messages';
import NativeDialogTracker from './native-dialog-tracker';

import { SetNativeDialogHandlerMessage, TYPE as MESSAGE_TYPE } from './driver-link/messages';
Expand Down Expand Up @@ -59,6 +61,7 @@ const ACTIVE_IFRAME_SELECTOR = 'testcafe|driver|active-iframe-sele
const TEST_SPEED = 'testcafe|driver|test-speed';
const ASSERTION_RETRIES_TIMEOUT = 'testcafe|driver|assertion-retries-timeout';
const ASSERTION_RETRIES_START_TIME = 'testcafe|driver|assertion-retries-start-time';
const CONSOLE_MESSAGES = 'testcafe|driver|console-messages';
const CHECK_IFRAME_DRIVER_LINK_DELAY = 500;

const ACTION_IFRAME_ERROR_CTORS = {
Expand Down Expand Up @@ -114,6 +117,7 @@ export default class Driver {

hammerhead.on(hammerhead.EVENTS.uncaughtJsError, err => this._onJsError(err));
hammerhead.on(hammerhead.EVENTS.unhandledRejection, err => this._onJsError(err));
hammerhead.on(hammerhead.EVENTS.consoleMethCalled, e => this._onConsoleMessage(e));
}

set speed (val) {
Expand All @@ -124,6 +128,14 @@ export default class Driver {
return this.contextStorage.getItem(TEST_SPEED);
}

get consoleMessages () {
return new BrowserConsoleMessages(this.contextStorage.getItem(CONSOLE_MESSAGES));
}

set consoleMessages (messages) {
return this.contextStorage.setItem(CONSOLE_MESSAGES, messages ? messages.getCopy() : null);
}

// Error handling
_onJsError (err) {
// NOTE: we should not send any message to the server if we've
Expand Down Expand Up @@ -156,6 +168,26 @@ export default class Driver {
return false;
}

// Console messages
_onConsoleMessage (e) {
const meth = e.meth;

const args = e.args.map(arg => {
if (arg === null)
return 'null';

if (arg === void 0)
return 'undefined';

return arg.toString();
});

const messages = this.consoleMessages;

messages.addMessage(meth, Array.prototype.slice.call(args).join(' '));

this.consoleMessages = messages;
}

// Status
_addPendingErrorToStatus (status) {
Expand All @@ -173,12 +205,18 @@ export default class Driver {
status.pageError = status.pageError || dialogError;
}

_addConsoleMessagesToStatus (status) {
status.consoleMessages = this.consoleMessages;
this.consoleMessages = null;
}

_sendStatus (status) {
// NOTE: We should not modify the status if it is resent after
// the page load because the server has cached the response
if (!status.resent) {
this._addPendingErrorToStatus(status);
this._addUnexpectedDialogErrorToStatus(status);
this._addConsoleMessagesToStatus(status);
}

this.contextStorage.setItem(PENDING_STATUS, status);
Expand Down Expand Up @@ -327,6 +365,10 @@ export default class Driver {
}));
}

_onGetBrowserConsoleMessagesCommand () {
this._onReady(new DriverStatus({ isCommandResult: true }));
}

_onNavigateToCommand (command) {
this.contextStorage.setItem(this.COMMAND_EXECUTING_FLAG, true);

Expand Down Expand Up @@ -489,6 +531,9 @@ export default class Driver {
else if (command.type === COMMAND_TYPE.getNativeDialogHistory)
this._onGetNativeDialogHistoryCommand(command);

else if (command.type === COMMAND_TYPE.getBrowserConsoleMessages)
this._onGetBrowserConsoleMessagesCommand(command);

else if (command.type === COMMAND_TYPE.setTestSpeed)
this._onSetTestSpeedCommand(command);

Expand Down
4 changes: 3 additions & 1 deletion src/client/driver/status.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export default class DriverStatus extends Assignable {
this.pageError = null;
this.resent = false;
this.result = null;
this.consoleMessages = null;

this._assignFrom(obj, true);
}
Expand All @@ -21,7 +22,8 @@ export default class DriverStatus extends Assignable {
{ name: 'isCommandResult' },
{ name: 'executionError' },
{ name: 'pageError' },
{ name: 'result' }
{ name: 'result' },
{ name: 'consoleMessages' }
];
}
}
6 changes: 4 additions & 2 deletions src/test-run/bookmark.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export default class TestRunBookmark {
this.pageLoadTimeout = testRun.pageLoadTimeout;
this.ctx = testRun.ctx;
this.fixtureCtx = testRun.fixtureCtx;
this.consoleMessages = testRun.consoleMessages;
}

async init () {
Expand Down Expand Up @@ -94,8 +95,9 @@ export default class TestRunBookmark {

this.testRun.phase = TEST_RUN_PHASE.inBookmarkRestore;

this.testRun.ctx = this.ctx;
this.testRun.fixtureCtx = this.fixtureCtx;
this.testRun.ctx = this.ctx;
this.testRun.fixtureCtx = this.fixtureCtx;
this.testRun.consoleMessages = this.consoleMessages;

try {
await this._restoreSpeed();
Expand Down
46 changes: 46 additions & 0 deletions src/test-run/browser-console-messages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// -------------------------------------------------------------
// WARNING: this file is used by both the client and the server.
// Do not use any browser or node-specific API!
// -------------------------------------------------------------
import { assignIn } from 'lodash';
import Assignable from '../utils/assignable';


export default class BrowserConsoleMessages extends Assignable {
constructor (obj) {
super();

this.log = [];
this.info = [];
this.warn = [];
this.error = [];

this._assignFrom(obj);
}

_getAssignableProperties () {
return [
{ name: 'log' },
{ name: 'info' },
{ name: 'warn' },
{ name: 'error' }
];
}

concat (consoleMessages) {
this.log = this.log.concat(consoleMessages.log);
this.info = this.info.concat(consoleMessages.info);
this.warn = this.warn.concat(consoleMessages.warn);
this.error = this.error.concat(consoleMessages.error);
}

addMessage (type, msg) {
this[type].push(msg);
}

getCopy () {
const { log, info, warn, error } = this;

return assignIn({}, { log, info, warn, error });
}
}
6 changes: 6 additions & 0 deletions src/test-run/commands/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,12 @@ export class GetNativeDialogHistoryCommand {
}
}

export class GetBrowserConsoleMessagesCommand {
constructor () {
this.type = TYPE.getBrowserConsoleMessages;
}
}

export class SetTestSpeedCommand extends Assignable {
constructor (obj) {
super(obj);
Expand Down
1 change: 1 addition & 0 deletions src/test-run/commands/type.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export default {
switchToMainWindow: 'switch-to-main-window',
setNativeDialogHandler: 'set-native-dialog-handler',
getNativeDialogHistory: 'get-native-dialog-history',
getBrowserConsoleMessages: 'get-browser-console-messages',
setTestSpeed: 'set-test-speed',
setPageLoadTimeout: 'set-page-load-timeout',
debug: 'debug',
Expand Down
20 changes: 17 additions & 3 deletions src/test-run/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import ROLE_PHASE from '../role/phase';
import TestRunBookmark from './bookmark';
import ClientFunctionBuilder from '../client-functions/client-function-builder';
import ReporterPluginHost from '../reporter/plugin-host';

import BrowserConsoleMessages from './browser-console-messages';

import { TakeScreenshotOnFailCommand } from './commands/browser-manipulation';
import { SetNativeDialogHandlerCommand, SetTestSpeedCommand, SetPageLoadTimeoutCommand } from './commands/actions';
Expand Down Expand Up @@ -73,6 +73,8 @@ export default class TestRun extends Session {
this.speed = this.opts.speed;
this.pageLoadTimeout = this.opts.pageLoadTimeout;

this.consoleMessages = new BrowserConsoleMessages();

this.pendingRequest = null;
this.pendingPageError = null;

Expand Down Expand Up @@ -269,6 +271,12 @@ export default class TestRun extends Session {
return this.executeCommand(new PrepareBrowserManipulationCommand(command.type), callsite);
}

async _enqueueBrowserConsoleMessagesCommand (command, callsite) {
await this._enqueueCommand(command, callsite);

return this.consoleMessages.getCopy();
}

async _enqueueSetBreakpointCommand (callsite, error) {
debugLogger.showBreakpoint(this.id, this.browserConnection.userAgent, callsite, error);

Expand Down Expand Up @@ -344,6 +352,8 @@ export default class TestRun extends Session {

var currentTaskRejectedByError = pageError && this._handlePageErrorStatus(pageError);

this.consoleMessages.concat(driverStatus.consoleMessages);

if (!currentTaskRejectedByError && driverStatus.isCommandResult) {
if (this.currentDriverTask.command.type === COMMAND_TYPE.testDone) {
this._resolveCurrentDriverTask();
Expand Down Expand Up @@ -426,6 +436,9 @@ export default class TestRun extends Session {
if (command.type === COMMAND_TYPE.assertion)
return this._executeAssertion(command, callsite);

if (command.type === COMMAND_TYPE.getBrowserConsoleMessages)
return await this._enqueueBrowserConsoleMessagesCommand(command, callsite);

return this._enqueueCommand(command, callsite);
}

Expand All @@ -448,8 +461,9 @@ export default class TestRun extends Session {
}

async switchToCleanRun () {
this.ctx = Object.create(null);
this.fixtureCtx = Object.create(null);
this.ctx = Object.create(null);
this.fixtureCtx = Object.create(null);
this.consoleMessages = new BrowserConsoleMessages();

this.useStateSnapshot(null);

Expand Down
8 changes: 8 additions & 0 deletions test/functional/fixtures/api/es-next/console/pages/empty.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<!DOCTYPE html>
<html>
<head>
<title>Console messages (empty page)</title>
</head>
<body>
</body>
</html>
27 changes: 27 additions & 0 deletions test/functional/fixtures/api/es-next/console/pages/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<!DOCTYPE html>
<html>
<head>
<title>Console messages</title>
</head>
<body>
<button id="trigger-messages">Trigger Messages</button>

<a id="reload" href="./empty.html">Reload</a>

<script>
var counter = 1;

function triggerMessages () {
console.log('log' + counter);
console.warn('warn' + counter);
console.error('error' + counter);
console.info('info' + counter);

counter++;
}

triggerMessages();
document.querySelector('#trigger-messages').addEventListener('click', triggerMessages);
</script>
</body>
</html>
9 changes: 9 additions & 0 deletions test/functional/fixtures/api/es-next/console/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
describe('[API] t.getBrowserConsoleMessages()', function () {
it('Should return messages from the console', function () {
return runTests('./testcafe-fixtures/console-test.js', 't.getBrowserConsoleMessages');
});

it('Should format messages if several args were passed', function () {
return runTests('./testcafe-fixtures/console-test.js', 'messages formatting');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
fixture `getBrowserConsoleMessages`;


test
.page `http://localhost:3000/fixtures/api/es-next/console/pages/index.html`
('t.getBrowserConsoleMessages', async t => {
let messages = await t.getBrowserConsoleMessages();

await t
.expect(messages.error).eql(['error1'])
.expect(messages.warn).eql(['warn1'])
.expect(messages.log).eql(['log1'])
.expect(messages.info).eql(['info1'])

.click('#trigger-messages')

// Check the driver keeps the messages between page reloads
.click('#reload');

// Changes in the getBrowserConsoleMessages result object should
// not affect the console messages state in the test run.
messages.log.push('unexpected');

messages = await t.getBrowserConsoleMessages();

await t
.expect(messages.error).eql(['error1', 'error2'])
.expect(messages.warn).eql(['warn1', 'warn2'])
.expect(messages.log).eql(['log1', 'log2'])
.expect(messages.info).eql(['info1', 'info2']);
});

test
.page `http://localhost:3000/fixtures/api/es-next/console/pages/empty.html`
('messages formatting', async t => {
/* eslint-disable no-console */
await t.eval(() => console.log('a', 1, null, void 0, ['b', 2], { c: 3 }));
/* eslint-enable no-console */

const { log } = await t.getBrowserConsoleMessages();

await t.expect(log[0]).eql('a 1 null undefined b,2 [object Object]');
});
2 changes: 1 addition & 1 deletion test/functional/fixtures/api/es-next/roles/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ describe('[API] t.useRole()', function () {
return runTests('./testcafe-fixtures/configuration-test.js', 'Clear configuration', TEST_WITH_IFRAME_FAILED_RUN_OPTIONS)
.catch(function (errs) {
expect(errs[0]).contains('- Error in Role initializer - A native alert dialog was invoked');
expect(errs[0]).contains('> 32 | await t.click(showAlertBtn);');
expect(errs[0]).contains('> 34 | await t.click(showAlertBtn);');
});
});

Expand Down
Loading