Skip to content

Commit

Permalink
Enable screenshot functionality (closes DevExpress#104)
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexanderMoskovkin committed Nov 9, 2015
1 parent 28731c1 commit e8f4d57
Show file tree
Hide file tree
Showing 29 changed files with 502 additions and 533 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
"testcafe-browser-natives": "^0.10.0",
"testcafe-hammerhead": "^0.2.1",
"uglify-js": "1.2.6",
"useragent": "^2.1.7"
"useragent": "^2.1.7",
"uuid": "^2.0.1"
},
"devDependencies": {
"babel": "^5.8.23",
Expand Down
13 changes: 3 additions & 10 deletions src/client/core/transport.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,14 @@ export function fail (err, callback) {
err: err
};

transport.asyncServiceMsg(testFailMsg, function () {
callback();
});

//HACK: this helps stop current JS context execution
window.onerror = function () {
};
throw 'STOP';
transport.asyncServiceMsg(testFailMsg, callback);
}

export function assertionFailed (err) {
export function assertionFailed (err, callback) {
var assertionFailedMsg = {
cmd: COMMAND.assertionFailed,
err: err
};

transport.asyncServiceMsg(assertionFailedMsg);
transport.asyncServiceMsg(assertionFailedMsg, callback);
}
4 changes: 2 additions & 2 deletions src/client/runner/api/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -698,11 +698,11 @@ export function upload (what, path) {
);
}

export function screenshot () {
export function screenshot (filePath) {
stepIterator.asyncAction(function (iteratorCallback) {
stepIterator.takeScreenshot(function () {
iteratorCallback();
}, false);
}, filePath);
});
}

Expand Down
5 changes: 2 additions & 3 deletions src/client/runner/iframe-runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,6 @@ IFrameRunner.prototype._onAssertionFailed = function (e) {
err: e
};

this.stepIterator.state.needScreeshot = true;

messageSandbox.sendServiceMsg(msg, window.top);

if (SETTINGS.get().PLAYBACK)
Expand Down Expand Up @@ -111,7 +109,8 @@ IFrameRunner.prototype._onGetStepsSharedData = function (e) {
IFrameRunner.prototype._onTakeScreenshot = function (e) {
var msg = {
cmd: RunnerBase.IFRAME_TAKE_SCREENSHOT_REQUEST_CMD,
isFailedStep: e.isFailedStep
stepName: e.stepName,
filePath: e.filePath
};

messageSandbox.sendServiceMsg(msg, window.top);
Expand Down
8 changes: 6 additions & 2 deletions src/client/runner/runner-base.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ var RunnerBase = function () {
//NOTE: start test execution only when all content is loaded or if loading
//timeout is reached (whichever comes first).
runner._prepareStepsExecuting(function () {
if (runner.stopped)
return;

delete runner.act._onJSError;
delete runner.act._start;

Expand Down Expand Up @@ -211,7 +214,7 @@ RunnerBase.prototype._initIFrameBehavior = function () {
runner.executingStepInIFrameWindow = null;

message.err.stepNum = runner.stepIterator.state.step - 1;
runner._onAssertionFailed(message.err, true);
runner._onAssertionFailed(message.err);
break;

case RunnerBase.IFRAME_GET_SHARED_DATA_REQUEST_CMD:
Expand Down Expand Up @@ -254,7 +257,8 @@ RunnerBase.prototype._initIFrameBehavior = function () {

case RunnerBase.IFRAME_TAKE_SCREENSHOT_REQUEST_CMD:
runner._onTakeScreenshot({
isFailedStep: message.isFailedStep,
stepName: message.stepName,
filePath: message.filePath,
callback: function () {
msg = {
cmd: RunnerBase.IFRAME_TAKE_SCREENSHOT_RESPONSE_CMD
Expand Down
170 changes: 86 additions & 84 deletions src/client/runner/runner.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Promise } from 'es6-promise';
import hammerhead from './deps/hammerhead';
import testCafeCore from './deps/testcafe-core';
import RunnerBase from './runner-base';
Expand All @@ -14,7 +15,6 @@ var serviceUtils = testCafeCore.serviceUtils;

const WAITING_FOR_SERVICE_MESSAGES_COMPLETED_DELAY = 1000;


var Runner = function (startedCallback) {
var runner = this;

Expand Down Expand Up @@ -80,44 +80,80 @@ Runner.prototype._onActionRun = function () {
transport.asyncServiceMsg(msg);
};

Runner.prototype._onError = function (err) {
var runner = this;
Runner.prototype._beforeScreenshot = function () {
this.stepIterator.suspend();
this.eventEmitter.emit(RunnerBase.SCREENSHOT_CREATING_STARTED_EVENT, {});
this.savedDocumentTitle = document.title;

if (this.stopped)
return;
var assignedTitle = `[ ${window.location.toString()} ]`;

//NOTE: we should stop stepIterator to prevent playback after an error is occurred
this.stepIterator.stop();
// NOTE: we should keep page url in the
// document.title while screenshot creating run
this.checkTitleIntervalId = window.setInterval(() => {
if (document.title !== assignedTitle) {
this.savedDocumentTitle = document.title;
document.title = assignedTitle;
}
}, 50);

RunnerBase.prototype._onError.call(this, err);
document.title = assignedTitle;

return new Promise(resolve => window.setTimeout(resolve), 500);
};

Runner.prototype._afterScreenshot = function () {
window.clearInterval(this.checkTitleIntervalId);
document.title = this.savedDocumentTitle;
this.checkTitleIntervalId = null;
this.savedDocumentTitle = null;

this.eventEmitter.emit(RunnerBase.SCREENSHOT_CREATING_FINISHED_EVENT, {});
this.stepIterator.resume();

return new Promise(resolve => window.setTimeout(resolve), 100);
};

Runner.prototype._onTestFail = function (err, isAssertion) {
// NOTE: we should not create several screenshots for a step
err.screenshotRequired = SETTINGS.get().TAKE_SCREENSHOTS && SETTINGS.get().TAKE_SCREENSHOT_ON_FAILS &&
this.stepIterator.state.curStepErrors.length < 2;
err.pageUrl = document.location.toString();

return new Promise(resolve => {
if (err.screenshotRequired)
return this._beforeScreenshot().then(resolve);

resolve();
})
.then(() => new Promise(resolve => {
if (isAssertion)
transport.assertionFailed(err, resolve);
else
transport.fail(err, resolve);
}))
.then(() => new Promise(resolve => {
if (err.screenshotRequired)
return this._afterScreenshot().then(resolve);

resolve();
}));
};

if (!SETTINGS.get().TAKE_SCREENSHOT_ON_FAILS) {
this.stopped = true;
transport.fail(err, Runner.checkStatus);
Runner.prototype._onError = function (err) {
if (this.stopped)
return;
}

var setErrorMsg = {
cmd: COMMAND.setTestError,
err: err
};
this.stopped = true;
this.stepIterator.stop();

transport.asyncServiceMsg(setErrorMsg);
RunnerBase.prototype._onError.call(this, err);

this._onTakeScreenshot({
isFailedStep: true,
//TODO:
//withoutStepName: !(ERRORS.hasErrorStepName(err) && ERRORS.hasErrorStepName(err)),
callback: function () {
runner.stopped = true;
transport.fail(err, Runner.checkStatus);
}
});
this._onTestFail(err)
.then(Runner.checkStatus);
};

Runner.prototype._onAssertionFailed = function (e, inIFrame) {
this.stepIterator.state.needScreeshot = !inIFrame;
transport.assertionFailed(e.err);
Runner.prototype._onAssertionFailed = function (e) {
this._onTestFail(e.err, true);
};

Runner.prototype._onSetStepsSharedData = function (e) {
Expand All @@ -138,62 +174,28 @@ Runner.prototype._onGetStepsSharedData = function (e) {
};

Runner.prototype._onTakeScreenshot = function (e) {
var savedTitle = document.title,
windowMark = '[tc-' + Date.now() + ']',
browserName = null,
callback = e && e.callback ? e.callback : function () {
},
runner = this;

runner.eventEmitter.emit(RunnerBase.SCREENSHOT_CREATING_STARTED_EVENT, {});


if (browserUtils.isMSEdge)
browserName = 'MSEDGE';
else if (browserUtils.isSafari)
browserName = 'SAFARI';
else if (browserUtils.isOpera || browserUtils.isOperaWithWebKit)
browserName = 'OPERA';
else if (browserUtils.isWebKit)
browserName = 'CHROME';
else if (browserUtils.isMozilla)
browserName = 'FIREFOX';
else if (browserUtils.isIE)
browserName = 'IE';

var msg = {
cmd: 'CMD_TAKE_SCREENSHOT', //TODO: fix
windowMark: windowMark,
browserName: browserName,
isFailedStep: e.isFailedStep,
withoutStepName: e.withoutStepName,
url: window.location.toString()
};

var assignedTitle = savedTitle + windowMark,
checkTitleIntervalId = window.setInterval(function () {
if (document.title !== assignedTitle) {
savedTitle = document.title;
document.title = assignedTitle;
}
}, 50);

document.title = assignedTitle;

//NOTE: we should set timeouts to changing of document title
//in any case we are waiting response from server
window.setTimeout(function () {
transport.asyncServiceMsg(msg, function () {
window.clearInterval(checkTitleIntervalId);
checkTitleIntervalId = null;
document.title = savedTitle;
runner.eventEmitter.emit(RunnerBase.SCREENSHOT_CREATING_FINISHED_EVENT, {});

window.setTimeout(function () {
callback();
}, 100);
if (!SETTINGS.get().TAKE_SCREENSHOTS)
return typeof e.callback === 'function' ? e.callback() : null;

this
._beforeScreenshot()
.then(() => {
return new Promise(resolve => {
var msg = {
cmd: COMMAND.takeScreenshot,
pageUrl: document.location.toString(),
stepName: e.stepName,
filePath: e.filePath
};

transport.asyncServiceMsg(msg, resolve);
});
})
.then(this._afterScreenshot.bind(this))
.then(() => {
if (typeof e.callback === 'function')
e.callback();
});
}, 500);
};

Runner.prototype._onDialogsInfoChanged = function (info) {
Expand Down
Loading

0 comments on commit e8f4d57

Please sign in to comment.