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

fix: more tolerant error handling for errors raised into reporter plugin methods #7260

Merged
merged 2 commits into from
Sep 1, 2022
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: 6 additions & 0 deletions src/errors/process-test-fn-error.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import {
UncaughtNonErrorObjectInTestCode,
ExternalAssertionLibraryError,
} from './test-run';
import debug from 'debug';

const debugLog = debug('testcafe:errors');


function isAssertionErrorCallsiteFrame (frame) {
const filename = frame.getFileName();
Expand All @@ -22,6 +26,8 @@ function isAssertionErrorCallsiteFrame (frame) {
}

export default function processTestFnError (err) {
debugLog('processTestFnError: %O', err);

if (err && (err.isTestCafeError || err instanceof TestCafeErrorList))
return err;

Expand Down
17 changes: 15 additions & 2 deletions src/errors/runtime/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import renderTemplate from '../../utils/render-template';
import renderCallsiteSync from '../../utils/render-callsite-sync';
import { RUNTIME_ERRORS } from '../types';
import getRenderers from '../../utils/get-renderes';
import util from 'util';

const ERROR_SEPARATOR = '\n\n';

Expand Down Expand Up @@ -128,10 +129,22 @@ export class CompositeError extends Error {

export class ReporterPluginError extends GeneralError {
constructor ({ name, method, originalError }) {
const code = RUNTIME_ERRORS.uncaughtErrorInReporter;
const code = RUNTIME_ERRORS.uncaughtErrorInReporter;
const preparedStack = ReporterPluginError._prepareStack(originalError);

super(code, method, name, originalError.stack);
super(code, method, name, preparedStack);
}

static _prepareStack (err) {
if (!err?.stack) {
const inspectedObject = util.inspect(err);

return `No stack trace is available for a raised error.\nRaised error object inspection:\n${inspectedObject}`;
}

return err.stack;
}

}

export class TimeoutError extends GeneralError {
Expand Down
6 changes: 6 additions & 0 deletions src/reporter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import fs from 'fs';
import MessageBus from '../utils/message-bus';
import BrowserConnection from '../browser/connection';
import { Dictionary } from '../configuration/interfaces';
import debug from 'debug';

interface PendingPromise {
resolve: Function | null;
Expand Down Expand Up @@ -121,6 +122,8 @@ interface ReportWarningEventArguments {
actionId?: string;
}

const debugLog = debug('testcafe:reporter');

export default class Reporter {
public readonly plugin: ReporterPluginHost;
public readonly messageBus: MessageBus;
Expand Down Expand Up @@ -175,6 +178,9 @@ export default class Reporter {
originalError,
});

debugLog('Plugin error: %O', uncaughtError);
debugLog('Plugin error: initialObject: %O', initialObject);

if (initialObject)
await initialObject.emit('error', uncaughtError);
else
Expand Down
27 changes: 27 additions & 0 deletions test/server/reporter-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const Videos = require('../../lib/video-recorder/videos
const delay = require('../../lib/utils/delay');
const { ReporterPluginError } = require('../../lib/errors/runtime');
const WarningLog = require('../../lib/notifications/warning-log');
const util = require('util');


describe('Reporter', () => {
Expand Down Expand Up @@ -1402,4 +1403,30 @@ describe('Reporter', () => {
expect(lastErr.message).startsWith(`The "${method}" method of the "customReporter" reporter produced an uncaught error. Error details:\nError: oops`);
}
});

it('More tolerant error handling for reporter plugin methods', async () => {
const taskMock = new TaskMock();
const reporter = new Reporter({
[ReporterPluginMethod.reportTaskStart]: (error) => {
throw error;
},
}, taskMock._messageBus, null, 'customReporter');

const errs = [
void 0,
null,
{ size: 'big' },
new Error('oops'),
];

for (const err of errs) {
try {
await reporter.dispatchToPlugin({ method: ReporterPluginMethod.reportTaskStart, args: [err] });
}
catch (e) {
expect(e).instanceOf(ReporterPluginError);
expect(e.stack).contains(err?.stack || util.inspect(err));
}
}
});
});