Skip to content
Permalink
Browse files
fix(core): Serenity BDD reports errors with root causes correctly
  • Loading branch information
jan-molak committed Feb 13, 2019
1 parent 03454de commit 25222a9a43591f34ceae4fe1dc4192ec943da3bd
Showing 5 changed files with 41 additions and 10 deletions.
@@ -344,10 +344,17 @@ describe('SerenityBDDReporter', () => {
* @test {ExecutionCompromised}
*/
it('has been compromised', () => {
const error = new TestCompromisedError(`Test database not deployed, no point running the test`);
const dbError = new Error(`Could not connect to the database`);
dbError.stack = [
'Error: Could not connect to the database',
' at callFn (/fake/path/node_modules/db-module/index.js:56:78)',
// and so on
].join('\n');

const error = new TestCompromisedError(`Test database not deployed, no point running the test`, dbError);
error.stack = [
'Error: Test database not deployed, no point running the test',
' at callFn (/fake/path/node_modules/mocha/lib/runnable.js:326:21)',
'TestCompromisedError: Test database not deployed, no point running the test',
' at callFn (/fake/path/my-test/index.js:12:34)',
// and so on
].join('\n');

@@ -365,11 +372,23 @@ describe('SerenityBDDReporter', () => {
stackTrace: [
{
declaringClass: '',
fileName: '/fake/path/node_modules/mocha/lib/runnable.js',
lineNumber: 326,
fileName: '/fake/path/my-test/index.js',
lineNumber: 12,
methodName: 'callFn()',
},
],
rootCause: {
errorType: `Error`,
message: `Could not connect to the database`,
stackTrace: [
{
declaringClass: '',
fileName: '/fake/path/node_modules/db-module/index.js',
lineNumber: 56,
methodName: 'callFn()',
},
],
},
});
});
});
@@ -24,7 +24,10 @@
*/
export abstract class RuntimeError extends Error {

protected constructor(type: { new(...args: any[]): RuntimeError } , message: string, cause?: Error) {
protected constructor(
type: { new(...args: any[]): RuntimeError } , message: string,
public readonly cause?: Error,
) {
super(message);
Object.setPrototypeOf(this, type.prototype);
Error.captureStackTrace(this, type);
@@ -116,4 +116,5 @@ export interface ErrorDetails extends JSONObject {
fileName: string,
lineNumber: number,
}>;
rootCause?: ErrorDetails;
}
@@ -1,9 +1,17 @@
import * as ErrorStackParser from 'error-stack-parser';
import { RuntimeError } from '../../../../errors';

/** @access package */
export class ErrorRenderer {
render(error: Error) {
// todo: add diff for AssertionError
return {
...this.renderError(error),
...((error instanceof RuntimeError && error.cause) ? { rootCause: this.renderError(error.cause) } : {}),
};
}

private renderError(error: Error) {
return {
errorType: error.constructor.name,
message: error.message,
@@ -17,12 +17,12 @@ export class OutcomeMapper {
private static errorRenderer = new ErrorRenderer();

public mapOutcome(outcome: Outcome, mapAs: (result: string, error?: ErrorDetails) => void) {
const render = OutcomeMapper.errorRenderer.render;
const render = OutcomeMapper.errorRenderer;

return match<Outcome, void>(outcome).
when(ExecutionCompromised, ({ error }: ExecutionCompromised) => mapAs('COMPROMISED', render(error))).
when(ExecutionFailedWithError, ({ error }: ExecutionFailedWithError) => mapAs('ERROR', render(error))).
when(ExecutionFailedWithAssertionError, ({ error }: ExecutionFailedWithAssertionError) => mapAs('FAILURE', render(error))).
when(ExecutionCompromised, ({ error }: ExecutionCompromised) => mapAs('COMPROMISED', render.render(error))).
when(ExecutionFailedWithError, ({ error }: ExecutionFailedWithError) => mapAs('ERROR', render.render(error))).
when(ExecutionFailedWithAssertionError, ({ error }: ExecutionFailedWithAssertionError) => mapAs('FAILURE', render.render(error))).
when(ExecutionSkipped, _ => mapAs('SKIPPED')).
when(ExecutionIgnored, _ => mapAs('IGNORED')).
when(ImplementationPending, _ => mapAs('PENDING')).

0 comments on commit 25222a9

Please sign in to comment.