Skip to content
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
6 changes: 5 additions & 1 deletion lib/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,11 @@ globals.setImmediate(() => {
runner.on('test', test);

process.on('ava-run', options => {
runner.run(options).then(exit);
runner.run(options)
.then(exit)
.catch(err => {
process.emit('uncaughtException', err);
});
});

process.on('ava-init-exit', () => {
Expand Down
15 changes: 14 additions & 1 deletion lib/test-worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,20 @@ process.on('unhandledRejection', throwsHelper);

process.on('uncaughtException', exception => {
throwsHelper(exception);
send('uncaughtException', {exception: serializeError(exception)});

let serialized;
try {
serialized = serializeError(exception);
} catch (ignore) { // eslint-disable-line unicorn/catch-error-name
// Avoid using serializeError
const err = new Error('Failed to serialize uncaught exception');
serialized = {
name: err.name,
message: err.message,
stack: err.stack
};
}
send('uncaughtException', {exception: serialized});
});

// If AVA was not required, show an error
Expand Down
14 changes: 6 additions & 8 deletions lib/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -240,10 +240,9 @@ class Test {
this._setAssertError(new Error(`Do not return ${asyncType} from tests declared via \`test.cb(...)\`, if you want to return a promise simply declare the test via \`test(...)\``));
}

ret.then(
() => {
this.exit();
},
// Convert to a Bluebird promise
return Promise.resolve(ret).then(
() => this.exit(),
err => {
if (!(err instanceof Error)) {
err = new assert.AssertionError({
Expand All @@ -254,10 +253,9 @@ class Test {
}

this._setAssertError(err);
this.exit();
});

return this.promise().promise;
return this.exit();
}
);
}

if (this.metadata.callback && !this.threwSync) {
Expand Down
14 changes: 14 additions & 0 deletions test/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -412,3 +412,17 @@ test('workers ensure test files load the same version of ava', t => {
t.end();
});
});

test('worker errors are treated as uncaught exceptions', t => {
execCli(['--no-color', '--verbose', 'test.js'], {dirname: 'fixture/trigger-worker-exception'}, (_, __, stderr) => {
t.match(stderr, /Forced error/);
t.end();
});
});

test('uncaught exceptions are raised for worker errors even if the error cannot be serialized', t => {
execCli(['--no-color', '--verbose', 'test-fallback.js'], {dirname: 'fixture/trigger-worker-exception'}, (_, __, stderr) => {
t.match(stderr, /Failed to serialize uncaught exception/);
t.end();
});
});
23 changes: 23 additions & 0 deletions test/fixture/trigger-worker-exception/hack.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
'use strict';

require('../../../lib/serialize-error');

const serializeModule = require.cache[require.resolve('../../../lib/serialize-error')];

const original = serializeModule.exports;
let restored = false;
let restoreAfterFirstCall = false;
serializeModule.exports = error => {
if (restored) {
return original(error);
}
if (restoreAfterFirstCall) {
restored = true;
}

throw new Error('Forced error');
};

exports.restoreAfterFirstCall = () => {
restoreAfterFirstCall = true;
};
5 changes: 5 additions & 0 deletions test/fixture/trigger-worker-exception/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"ava": {
"require": "./hack.js"
}
}
5 changes: 5 additions & 0 deletions test/fixture/trigger-worker-exception/test-fallback.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import test from '../../../';

test(async () => {
throw new Error('Hi :)');
});
8 changes: 8 additions & 0 deletions test/fixture/trigger-worker-exception/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import test from '../../../';

import { restoreAfterFirstCall } from './hack';
restoreAfterFirstCall();

test(async () => {
throw new Error('Hi :)');
});