Skip to content

Commit

Permalink
Add the capability to get browser console messages (closes #1738) (#1818
Browse files Browse the repository at this point in the history
)

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

* Rename to `getBrowserConsoleMessages`

* Address Helen's remarks

* Fix the test for node 4

* Store messages on the server

* Integrating with Roles. Refactoring.

* Fix tests.
  • Loading branch information
AlexanderMoskovkin committed Oct 9, 2017
1 parent c8da1b2 commit 7dc8cd4
Show file tree
Hide file tree
Showing 16 changed files with 297 additions and 11 deletions.
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

0 comments on commit 7dc8cd4

Please sign in to comment.