Skip to content

Commit 87fe938

Browse files
committed
feat(jest-message-util): add support for error causes
1 parent 5a9870b commit 87fe938

File tree

3 files changed

+45
-3
lines changed

3 files changed

+45
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
### Features
44

55
- `[@jest/core]` Instrument significant lifecycle events with [`performance.mark()`](https://nodejs.org/docs/latest-v16.x/api/perf_hooks.html#performancemarkname-options) ([#13859](https://github.com/facebook/jest/pull/13859))
6+
- `[jest-message-util]` Add support for [error causes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause)
67

78
### Fixes
89

packages/jest-message-util/src/__tests__/messages.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,3 +413,20 @@ it('getTopFrame should return a path for mjs files', () => {
413413

414414
expect(frame!.file).toBe(expectedFile);
415415
});
416+
417+
it('should return the error cause if there is one', () => {
418+
const error = new Error('Test exception');
419+
// We need to do it this way because in oder Javascript engines the constructor does not allow supplying a cause.
420+
error.cause = new Error('Cause Error');
421+
const message = formatExecError(
422+
error,
423+
{
424+
rootDir: '',
425+
testMatch: [],
426+
},
427+
{
428+
noStackTrace: false,
429+
},
430+
);
431+
expect(message).toEqual(expect.stringContaining('Cause'));
432+
});

packages/jest-message-util/src/index.ts

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,18 +122,20 @@ function warnAboutWrongTestEnvironment(error: string, env: 'jsdom' | 'node') {
122122
// `before/after each` hooks). If it's thrown, none of the tests in the file
123123
// are executed.
124124
export const formatExecError = (
125-
error: Error | TestResult.SerializableError | string | undefined,
125+
error: Error | TestResult.SerializableError | string | undefined | number,
126126
config: StackTraceConfig,
127127
options: StackTraceOptions,
128128
testPath?: string,
129129
reuseMessage?: boolean,
130+
noTitle?: boolean,
130131
): string => {
131132
if (!error || typeof error === 'number') {
132133
error = new Error(`Expected an Error, but "${String(error)}" was thrown`);
133134
error.stack = '';
134135
}
135136

136137
let message, stack;
138+
let cause = '';
137139

138140
if (typeof error === 'string' || !error) {
139141
error || (error = 'EMPTY ERROR');
@@ -145,6 +147,27 @@ export const formatExecError = (
145147
typeof error.stack === 'string'
146148
? error.stack
147149
: `thrown: ${prettyFormat(error, {maxDepth: 3})}`;
150+
if ('cause' in error) {
151+
const prefix = '\n\nCause:\n';
152+
if (typeof error.cause === 'string') {
153+
cause += `${prefix}${error.cause}`;
154+
} else if (typeof error.cause === 'number') {
155+
cause += `${prefix}${error.cause.toString()}`;
156+
} else if (error.cause instanceof Error) {
157+
const formatted = formatExecError(
158+
error.cause,
159+
config,
160+
options,
161+
testPath,
162+
reuseMessage,
163+
true,
164+
);
165+
cause += `${prefix}${formatted}`;
166+
}
167+
}
168+
}
169+
if (cause !== '') {
170+
cause = indentAllLines(cause);
148171
}
149172

150173
const separated = separateMessageFromStack(stack || '');
@@ -174,13 +197,14 @@ export const formatExecError = (
174197

175198
let messageToUse;
176199

177-
if (reuseMessage) {
200+
if (reuseMessage || noTitle) {
178201
messageToUse = ` ${message.trim()}`;
179202
} else {
180203
messageToUse = `${EXEC_ERROR_MESSAGE}\n\n${message}`;
181204
}
205+
const title = noTitle ? '' : `${TITLE_INDENT + TITLE_BULLET}`;
182206

183-
return `${TITLE_INDENT + TITLE_BULLET + messageToUse + stack}\n`;
207+
return `${title + messageToUse + stack + cause}\n`;
184208
};
185209

186210
const removeInternalStackEntries = (

0 commit comments

Comments
 (0)