Skip to content

Commit

Permalink
fix unhandled promise rejection in chain assertions (closes #2852) (#…
Browse files Browse the repository at this point in the history
…2874)

* [WIP]fix unhandled promise rejection in chain assertions (closes #2852)

* async to promise

* depth to default

* lint

* promiseThen to originalThen
  • Loading branch information
AlexKamaev authored and helen-dikareva committed Sep 21, 2018
1 parent 6e42f17 commit 66b8c31
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 13 deletions.
11 changes: 7 additions & 4 deletions src/api/test-controller/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ import {
import { WaitCommand, DebugCommand } from '../../test-run/commands/observation';
import assertRequestHookType from '../request-hooks/assert-type';

const originalThen = Promise.resolve().then;

export default class TestController {
constructor (testRun) {
this.testRun = testRun;
Expand All @@ -62,10 +64,8 @@ export default class TestController {
// await t2.click('#btn3'); // <-- without check it will set callsiteWithoutAwait = null, so we will lost tracking
_createExtendedPromise (promise, callsite) {
const extendedPromise = promise.then(identity);
const originalThen = extendedPromise.then;
const markCallsiteAwaited = () => this.callsitesWithoutAwait.delete(callsite);


extendedPromise.then = function () {
markCallsiteAwaited();

Expand All @@ -84,11 +84,14 @@ export default class TestController {
const callsite = getCallsiteForMethod(apiMethodName);
const executor = createTaskExecutor(callsite);

this.executionChain = this.executionChain.then(executor);
this.executionChain.then = originalThen;
this.executionChain = this.executionChain.then(executor);

this.callsitesWithoutAwait.add(callsite);

return this._createExtendedPromise(this.executionChain, callsite);
this.executionChain = this._createExtendedPromise(this.executionChain, callsite);

return this.executionChain;
}

_enqueueCommand (apiMethodName, CmdCtor, cmdArgs) {
Expand Down
3 changes: 2 additions & 1 deletion src/utils/handle-errors.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { UnhandledPromiseRejectionError, UncaughtExceptionError } from '../errors/test-run';
import util from 'util';

const runningTests = {};
let handlingTestErrors = false;
Expand Down Expand Up @@ -32,7 +33,7 @@ function formatUnhandledRejectionReason (reason) {
if (reason instanceof Error)
return reason.stack;

return Object.prototype.toString.call(reason);
return util.inspect(reason);
}

function onUnhandledRejection (reason) {
Expand Down
71 changes: 63 additions & 8 deletions test/server/error-handle-test.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
const Promise = require('pinkie');
const expect = require('chai').expect;
const createTestCafe = require('../../lib/');
const types = require('../../lib/errors/test-run/type');
const handleErrors = require('../../lib/utils/handle-errors');
const Promise = require('pinkie');
const expect = require('chai').expect;
const createTestCafe = require('../../lib/');
const types = require('../../lib/errors/test-run/type');
const handleErrors = require('../../lib/utils/handle-errors');
const TestController = require('../../lib/api/test-controller');
const AssertionExecutor = require('../../lib/assertions/executor');


class TestRunMock {
Expand All @@ -15,16 +17,69 @@ class TestRunMock {
addError (err) {
this.errors.push(err);
}

executeCommand (command, callsite) {
return new AssertionExecutor(command, 0, callsite).run();
}

}

describe('Global error handlers', () => {
it('unhandled promise rejection on chain assertions', () => {
let unhandledRejection = false;

const throwErrorOnUnhandledRejection = () => {
unhandledRejection = true;
};

process.once('unhandledRejection', throwErrorOnUnhandledRejection);

return Promise.resolve()
.then(() => {
return new TestController(new TestRunMock('', '')).expect(10).eql(5).expect(10).eql(10);
})
.catch(() => {
process.removeListener('unhandledRejection', throwErrorOnUnhandledRejection);

expect(unhandledRejection).eql(false);
});
});

it('format UnhandledPromiseRejection reason', () => {
handleErrors.registerErrorHandlers();
handleErrors.startHandlingTestErrors();

const reasons = [new Error('test'), null, void 0, 1, 'string message', true, { a: 1 }];
const testRunMocks = reasons.map((reason, index) => new TestRunMock(index, reason));
const expectedErrors = ['Error: test', '[object Null]', 'undefined', '1', 'string message', 'true', '[object Object]'];
const obj = { a: 1, b: { c: 'd', e: { f: { g: 'too deep' } } } };

obj.circular = obj;

const reasons = [
new Error('test'),
null,
void 0,
1,
'string message',
true,
obj,
_ => _,
[1, 2],
/regex/
];

const testRunMocks = reasons.map((reason, index) => new TestRunMock(index, reason));

const expectedErrors = [
'Error: test',
'null',
'undefined',
'1',
'string message',
'true',
'{ a: 1, b: { c: \'d\', e: { f: [Object] } }, circular: [Circular] }',
'[Function]',
'[ 1, 2 ]',
'/regex/'
];

testRunMocks.forEach(testRun => {
handleErrors.addRunningTest(testRun);
Expand Down

0 comments on commit 66b8c31

Please sign in to comment.