From 81bb4c0069b4408d4286678fe04e02d6e69c7235 Mon Sep 17 00:00:00 2001 From: Nicolas DUBIEN Date: Sun, 19 Feb 2023 17:37:22 +0000 Subject: [PATCH 01/23] Properly report Error cause from test/it --- .../__snapshots__/messages.test.ts.snap | 44 +++++- .../src/__tests__/messages.test.ts | 145 +++++++++++++++++- packages/jest-message-util/src/index.ts | 58 +++++-- 3 files changed, 231 insertions(+), 16 deletions(-) diff --git a/packages/jest-message-util/src/__tests__/__snapshots__/messages.test.ts.snap b/packages/jest-message-util/src/__tests__/__snapshots__/messages.test.ts.snap index 9f629042d425..6fdc701c7534 100644 --- a/packages/jest-message-util/src/__tests__/__snapshots__/messages.test.ts.snap +++ b/packages/jest-message-util/src/__tests__/__snapshots__/messages.test.ts.snap @@ -40,6 +40,46 @@ exports[`formatStackTrace prints code frame and stacktrace 1`] = ` " `; +exports[`formatStackTrace should properly handle deeply nested causes 1`] = ` +" Error with cause test + + intercepted by f + + at f (plop.test.js:15:11) + at Object.f (plop.test.js:20:5) + + Cause: + intercepted by g + + at g (plop.test.js:8:11) + at g (plop.test.js:13:5) + at Object.f (plop.test.js:20:5) + + Cause: + boom + + at h (plop.test.js:2:9) + at h (plop.test.js:6:5) + at g (plop.test.js:13:5) + at Object.f (plop.test.js:20:5) +" +`; + +exports[`formatStackTrace should properly handle string causes 1`] = ` +" Error with string cause test + + boom + + at f (plop.test.js:15:11) + at Object.f (plop.test.js:20:5) + + Cause: + string cause + + +" +`; + exports[`formatStackTrace should strip node internals 1`] = ` " Unix test @@ -118,12 +158,12 @@ exports[`should return the error cause if there is one 1`] = ` Test exception - at Object. (packages/jest-message-util/src/__tests__/messages.test.ts:418:17) + at Object. (packages/jest-message-util/src/__tests__/messages.test.ts:557:17) Cause: Cause Error - at Object. (packages/jest-message-util/src/__tests__/messages.test.ts:421:17) + at Object. (packages/jest-message-util/src/__tests__/messages.test.ts:560:17) " `; diff --git a/packages/jest-message-util/src/__tests__/messages.test.ts b/packages/jest-message-util/src/__tests__/messages.test.ts index 2956f0b8e62c..a363aa78f6e4 100644 --- a/packages/jest-message-util/src/__tests__/messages.test.ts +++ b/packages/jest-message-util/src/__tests__/messages.test.ts @@ -35,6 +35,8 @@ const unixStackTrace = at Object.it (build/__tests__/messages-test.js:45:41) at Object. (../jest-jasmine2/build/jasmine-pit.js:35:32) at attemptAsync (../jest-jasmine2/build/jasmine-2.4.1.js:1919:24)`; +const unixError = new Error(unixStackTrace.replace(/\n\s*at [\s\s]*/m, '')); +unixError.stack = unixStackTrace; const assertionStack = ' ' + @@ -56,6 +58,10 @@ const assertionStack = at process._tickCallback (internal/process/next_tick.js:188:7) at internal/process/next_tick.js:188:7 `; +const assertionError = new Error( + assertionStack.replace(/\n\s*at [\s\s]*/m, ''), +); +assertionError.stack = assertionStack; const vendorStack = ' ' + @@ -83,6 +89,81 @@ const babelStack = \u001b[90m 22 | \u001b[39m )\u001b[33m;\u001b[39m \u001b[90m 23 | \u001b[39m } \u001b[36melse\u001b[39m \u001b[36mif\u001b[39m (\u001b[36mtypeof\u001b[39m render \u001b[33m!==\u001b[39m \u001b[32m'function'\u001b[39m) {\u001b[0m `; +const babelError = new Error(babelStack.replace(/\n\s*at [\s\s]*/m, '')); +babelError.stack = babelStack; + +const errorWithCauseNestedNested = new Error('boom'); +errorWithCauseNestedNested.stack = `Error: boom + at h (/workspaces/jest/plop.test.js:2:9) + at h (/workspaces/jest/plop.test.js:6:5) + at g (/workspaces/jest/plop.test.js:13:5) + at Object.f (/workspaces/jest/plop.test.js:20:5) + at Promise.then.completed (/workspaces/jest/packages/jest-circus/build/utils.js:293:28) + at new Promise () + at callAsyncCircusFn (/workspaces/jest/packages/jest-circus/build/utils.js:226:10) + at _callCircusTest (/workspaces/jest/packages/jest-circus/build/run.js:248:40) + at _runTest (/workspaces/jest/packages/jest-circus/build/run.js:184:3) + at _runTestsForDescribeBlock (/workspaces/jest/packages/jest-circus/build/run.js:86:9) + at run (/workspaces/jest/packages/jest-circus/build/run.js:26:3) + at runAndTransformResultsToJestFormat (/workspaces/jest/packages/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js:120:21) + at jestAdapter (/workspaces/jest/packages/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.js:74:19) + at runTestInternal (/workspaces/jest/packages/jest-runner/build/runTest.js:281:16) + at runTest (/workspaces/jest/packages/jest-runner/build/runTest.js:341:7)`; + +const errorWithCauseNested = new Error('intercepted by g', { + cause: errorWithCauseNestedNested, +}); +errorWithCauseNested.stack = `Error: intercepted by g + at g (/workspaces/jest/plop.test.js:8:11) + at g (/workspaces/jest/plop.test.js:13:5) + at Object.f (/workspaces/jest/plop.test.js:20:5) + at Promise.then.completed (/workspaces/jest/packages/jest-circus/build/utils.js:293:28) + at new Promise () + at callAsyncCircusFn (/workspaces/jest/packages/jest-circus/build/utils.js:226:10) + at _callCircusTest (/workspaces/jest/packages/jest-circus/build/run.js:248:40) + at _runTest (/workspaces/jest/packages/jest-circus/build/run.js:184:3) + at _runTestsForDescribeBlock (/workspaces/jest/packages/jest-circus/build/run.js:86:9) + at run (/workspaces/jest/packages/jest-circus/build/run.js:26:3) + at runAndTransformResultsToJestFormat (/workspaces/jest/packages/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js:120:21) + at jestAdapter (/workspaces/jest/packages/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.js:74:19) + at runTestInternal (/workspaces/jest/packages/jest-runner/build/runTest.js:281:16) + at runTest (/workspaces/jest/packages/jest-runner/build/runTest.js:341:7)`; + +const errorWithCause = new Error('intercepted by f', { + cause: errorWithCauseNested, +}); +errorWithCause.stack = `Error: intercepted by f + at f (/workspaces/jest/plop.test.js:15:11) + at Object.f (/workspaces/jest/plop.test.js:20:5) + at Promise.then.completed (/workspaces/jest/packages/jest-circus/build/utils.js:293:28) + at new Promise () + at callAsyncCircusFn (/workspaces/jest/packages/jest-circus/build/utils.js:226:10) + at _callCircusTest (/workspaces/jest/packages/jest-circus/build/run.js:248:40) + at _runTest (/workspaces/jest/packages/jest-circus/build/run.js:184:3) + at _runTestsForDescribeBlock (/workspaces/jest/packages/jest-circus/build/run.js:86:9) + at run (/workspaces/jest/packages/jest-circus/build/run.js:26:3) + at runAndTransformResultsToJestFormat (/workspaces/jest/packages/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js:120:21) + at jestAdapter (/workspaces/jest/packages/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.js:74:19) + at runTestInternal (/workspaces/jest/packages/jest-runner/build/runTest.js:281:16) + at runTest (/workspaces/jest/packages/jest-runner/build/runTest.js:341:7)`; + +const errorWithStringCause = new Error('boom', { + cause: 'string cause', +}); +errorWithStringCause.stack = `Error: boom + at f (/workspaces/jest/plop.test.js:15:11) + at Object.f (/workspaces/jest/plop.test.js:20:5) + at Promise.then.completed (/workspaces/jest/packages/jest-circus/build/utils.js:293:28) + at new Promise () + at callAsyncCircusFn (/workspaces/jest/packages/jest-circus/build/utils.js:226:10) + at _callCircusTest (/workspaces/jest/packages/jest-circus/build/run.js:248:40) + at _runTest (/workspaces/jest/packages/jest-circus/build/run.js:184:3) + at _runTestsForDescribeBlock (/workspaces/jest/packages/jest-circus/build/run.js:86:9) + at run (/workspaces/jest/packages/jest-circus/build/run.js:26:3) + at runAndTransformResultsToJestFormat (/workspaces/jest/packages/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js:120:21) + at jestAdapter (/workspaces/jest/packages/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.js:74:19) + at runTestInternal (/workspaces/jest/packages/jest-runner/build/runTest.js:281:16) + at runTest (/workspaces/jest/packages/jest-runner/build/runTest.js:341:7)`; beforeEach(() => { jest.clearAllMocks(); @@ -94,7 +175,7 @@ it('should exclude jasmine from stack trace for Unix paths.', () => { { ancestorTitles: [], duration: undefined, - failureDetails: [], + failureDetails: [unixError], failureMessages: [unixStackTrace], fullName: 'full name', invocations: undefined, @@ -142,7 +223,7 @@ it('formatStackTrace should strip node internals', () => { { ancestorTitles: [], duration: undefined, - failureDetails: [], + failureDetails: [assertionError], failureMessages: [assertionStack], fullName: 'full name', invocations: undefined, @@ -200,7 +281,7 @@ it('retains message in babel code frame error', () => { { ancestorTitles: [], duration: undefined, - failureDetails: [], + failureDetails: [babelError], failureMessages: [babelStack], fullName: 'full name', invocations: undefined, @@ -223,6 +304,64 @@ it('retains message in babel code frame error', () => { expect(messages).toMatchSnapshot(); }); +it('formatStackTrace should properly handle deeply nested causes', () => { + const messages = formatResultsErrors( + [ + { + ancestorTitles: [], + duration: undefined, + failureDetails: [errorWithCause], + failureMessages: [errorWithCause.stack || ''], + fullName: 'full name', + invocations: undefined, + location: null, + numPassingAsserts: 0, + retryReasons: undefined, + status: 'failed', + title: 'Error with cause test', + }, + ], + { + rootDir: '', + testMatch: [], + }, + { + noStackTrace: false, + }, + ); + + expect(messages).toMatchSnapshot(); +}); + +it('formatStackTrace should properly handle string causes', () => { + const messages = formatResultsErrors( + [ + { + ancestorTitles: [], + duration: undefined, + failureDetails: [errorWithStringCause], + failureMessages: [errorWithStringCause.stack || ''], + fullName: 'full name', + invocations: undefined, + location: null, + numPassingAsserts: 0, + retryReasons: undefined, + status: 'failed', + title: 'Error with string cause test', + }, + ], + { + rootDir: '', + testMatch: [], + }, + { + noStackTrace: false, + }, + ); + + expect(messages).toMatchSnapshot(); +}); + it('codeframe', () => { jest .mocked(readFileSync) diff --git a/packages/jest-message-util/src/index.ts b/packages/jest-message-util/src/index.ts index bf03d9d547d4..f6dbf654135a 100644 --- a/packages/jest-message-util/src/index.ts +++ b/packages/jest-message-util/src/index.ts @@ -358,7 +358,11 @@ export const formatStackTrace = ( }; type FailedResults = Array<{ + /** Stringified version of the error */ content: string; + /** Error */ + rawError: unknown; + /** Execution result */ result: TestResult.AssertionResult; }>; @@ -370,8 +374,12 @@ export const formatResultsErrors = ( ): string | null => { const failedResults: FailedResults = testResults.reduce( (errors, result) => { - result.failureMessages.forEach(item => { - errors.push({content: checkForCommonEnvironmentErrors(item), result}); + result.failureMessages.forEach((item, index) => { + errors.push({ + content: item, + result, + rawError: result.failureDetails[index], + }); }); return errors; }, @@ -383,15 +391,43 @@ export const formatResultsErrors = ( } return failedResults - .map(({result, content}) => { - let {message, stack} = separateMessageFromStack(content); - stack = options.noStackTrace - ? '' - : `${STACK_TRACE_COLOR( - formatStackTrace(stack, config, options, testPath), - )}\n`; + .map(({result, content, rawError}) => { + function formatErrorStack(errorOrStack: Error | string): string { + // The stack of new Error('message') contains both the message and the stack, + // thuw we need to sanitize and clean it for proper display using separateMessageFromStack. + const sourceStack = + typeof errorOrStack === 'string' + ? errorOrStack + : errorOrStack.stack || ''; + let {message, stack} = separateMessageFromStack(sourceStack); + stack = options.noStackTrace + ? '' + : `${STACK_TRACE_COLOR( + formatStackTrace(stack, config, options, testPath), + )}\n`; + + message = checkForCommonEnvironmentErrors(message); + message = indentAllLines(message); + + let cause = ''; + if (typeof errorOrStack !== 'string' && 'cause' in errorOrStack) { + if ( + typeof errorOrStack.cause === 'string' || + types.isNativeError(errorOrStack.cause) || + errorOrStack.cause instanceof Error + ) { + const nestedCause = formatErrorStack(errorOrStack.cause); + cause = `\n${MESSAGE_INDENT}Cause:\n${nestedCause}`; + } + } + + return `${message}\n${stack}${cause}`; + } - message = indentAllLines(message); + const rootErrorOrStack: Error | string = + types.isNativeError(rawError) || rawError instanceof Error + ? rawError + : content; const title = `${chalk.bold.red( TITLE_INDENT + @@ -401,7 +437,7 @@ export const formatResultsErrors = ( result.title, )}\n`; - return `${title}\n${message}\n${stack}`; + return `${title}\n${formatErrorStack(rootErrorOrStack)}`; }) .join('\n'); }; From e972e71b2fefaf0bd04449c21edaacc736afbffe Mon Sep 17 00:00:00 2001 From: Nicolas DUBIEN Date: Sun, 19 Feb 2023 17:44:29 +0000 Subject: [PATCH 02/23] fix lint --- packages/jest-message-util/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/jest-message-util/src/index.ts b/packages/jest-message-util/src/index.ts index f6dbf654135a..4a4c11d357ae 100644 --- a/packages/jest-message-util/src/index.ts +++ b/packages/jest-message-util/src/index.ts @@ -377,8 +377,8 @@ export const formatResultsErrors = ( result.failureMessages.forEach((item, index) => { errors.push({ content: item, - result, rawError: result.failureDetails[index], + result, }); }); return errors; From e2ffe8ba569024bc92f3f6ad3d34364700a1231b Mon Sep 17 00:00:00 2001 From: Nicolas DUBIEN Date: Sun, 19 Feb 2023 17:57:15 +0000 Subject: [PATCH 03/23] rework paths in tests --- .../__snapshots__/messages.test.ts.snap | 22 ++-- .../src/__tests__/messages.test.ts | 102 +++++++++--------- 2 files changed, 62 insertions(+), 62 deletions(-) diff --git a/packages/jest-message-util/src/__tests__/__snapshots__/messages.test.ts.snap b/packages/jest-message-util/src/__tests__/__snapshots__/messages.test.ts.snap index 6fdc701c7534..27cedf55d0ec 100644 --- a/packages/jest-message-util/src/__tests__/__snapshots__/messages.test.ts.snap +++ b/packages/jest-message-util/src/__tests__/__snapshots__/messages.test.ts.snap @@ -45,23 +45,23 @@ exports[`formatStackTrace should properly handle deeply nested causes 1`] = ` intercepted by f - at f (plop.test.js:15:11) - at Object.f (plop.test.js:20:5) + at f (cause.test.js:15:11) + at Object.f (cause.test.js:20:5) Cause: intercepted by g - at g (plop.test.js:8:11) - at g (plop.test.js:13:5) - at Object.f (plop.test.js:20:5) + at g (cause.test.js:8:11) + at g (cause.test.js:13:5) + at Object.f (cause.test.js:20:5) Cause: boom - at h (plop.test.js:2:9) - at h (plop.test.js:6:5) - at g (plop.test.js:13:5) - at Object.f (plop.test.js:20:5) + at h (cause.test.js:2:9) + at h (cause.test.js:6:5) + at g (cause.test.js:13:5) + at Object.f (cause.test.js:20:5) " `; @@ -70,8 +70,8 @@ exports[`formatStackTrace should properly handle string causes 1`] = ` boom - at f (plop.test.js:15:11) - at Object.f (plop.test.js:20:5) + at f (cause.test.js:15:11) + at Object.f (cause.test.js:20:5) Cause: string cause diff --git a/packages/jest-message-util/src/__tests__/messages.test.ts b/packages/jest-message-util/src/__tests__/messages.test.ts index a363aa78f6e4..08a261745bcd 100644 --- a/packages/jest-message-util/src/__tests__/messages.test.ts +++ b/packages/jest-message-util/src/__tests__/messages.test.ts @@ -94,76 +94,76 @@ babelError.stack = babelStack; const errorWithCauseNestedNested = new Error('boom'); errorWithCauseNestedNested.stack = `Error: boom - at h (/workspaces/jest/plop.test.js:2:9) - at h (/workspaces/jest/plop.test.js:6:5) - at g (/workspaces/jest/plop.test.js:13:5) - at Object.f (/workspaces/jest/plop.test.js:20:5) - at Promise.then.completed (/workspaces/jest/packages/jest-circus/build/utils.js:293:28) + at h (cause.test.js:2:9) + at h (cause.test.js:6:5) + at g (cause.test.js:13:5) + at Object.f (cause.test.js:20:5) + at Promise.then.completed (node_modules/jest-circus/build/utils.js:293:28) at new Promise () - at callAsyncCircusFn (/workspaces/jest/packages/jest-circus/build/utils.js:226:10) - at _callCircusTest (/workspaces/jest/packages/jest-circus/build/run.js:248:40) - at _runTest (/workspaces/jest/packages/jest-circus/build/run.js:184:3) - at _runTestsForDescribeBlock (/workspaces/jest/packages/jest-circus/build/run.js:86:9) - at run (/workspaces/jest/packages/jest-circus/build/run.js:26:3) - at runAndTransformResultsToJestFormat (/workspaces/jest/packages/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js:120:21) - at jestAdapter (/workspaces/jest/packages/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.js:74:19) - at runTestInternal (/workspaces/jest/packages/jest-runner/build/runTest.js:281:16) - at runTest (/workspaces/jest/packages/jest-runner/build/runTest.js:341:7)`; + at callAsyncCircusFn (node_modules/jest-circus/build/utils.js:226:10) + at _callCircusTest (node_modules/jest-circus/build/run.js:248:40) + at _runTest (node_modules/jest-circus/build/run.js:184:3) + at _runTestsForDescribeBlock (node_modules/jest-circus/build/run.js:86:9) + at run (node_modules/jest-circus/build/run.js:26:3) + at runAndTransformResultsToJestFormat (node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js:120:21) + at jestAdapter (node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.js:74:19) + at runTestInternal (node_modules/jest-runner/build/runTest.js:281:16) + at runTest (node_modules/jest-runner/build/runTest.js:341:7)`; const errorWithCauseNested = new Error('intercepted by g', { cause: errorWithCauseNestedNested, }); errorWithCauseNested.stack = `Error: intercepted by g - at g (/workspaces/jest/plop.test.js:8:11) - at g (/workspaces/jest/plop.test.js:13:5) - at Object.f (/workspaces/jest/plop.test.js:20:5) - at Promise.then.completed (/workspaces/jest/packages/jest-circus/build/utils.js:293:28) + at g (cause.test.js:8:11) + at g (cause.test.js:13:5) + at Object.f (cause.test.js:20:5) + at Promise.then.completed (node_modules/jest-circus/build/utils.js:293:28) at new Promise () - at callAsyncCircusFn (/workspaces/jest/packages/jest-circus/build/utils.js:226:10) - at _callCircusTest (/workspaces/jest/packages/jest-circus/build/run.js:248:40) - at _runTest (/workspaces/jest/packages/jest-circus/build/run.js:184:3) - at _runTestsForDescribeBlock (/workspaces/jest/packages/jest-circus/build/run.js:86:9) - at run (/workspaces/jest/packages/jest-circus/build/run.js:26:3) - at runAndTransformResultsToJestFormat (/workspaces/jest/packages/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js:120:21) - at jestAdapter (/workspaces/jest/packages/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.js:74:19) - at runTestInternal (/workspaces/jest/packages/jest-runner/build/runTest.js:281:16) - at runTest (/workspaces/jest/packages/jest-runner/build/runTest.js:341:7)`; + at callAsyncCircusFn (node_modules/jest-circus/build/utils.js:226:10) + at _callCircusTest (node_modules/jest-circus/build/run.js:248:40) + at _runTest (node_modules/jest-circus/build/run.js:184:3) + at _runTestsForDescribeBlock (node_modules/jest-circus/build/run.js:86:9) + at run (node_modules/jest-circus/build/run.js:26:3) + at runAndTransformResultsToJestFormat (node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js:120:21) + at jestAdapter (node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.js:74:19) + at runTestInternal (node_modules/jest-runner/build/runTest.js:281:16) + at runTest (node_modules/jest-runner/build/runTest.js:341:7)`; const errorWithCause = new Error('intercepted by f', { cause: errorWithCauseNested, }); errorWithCause.stack = `Error: intercepted by f - at f (/workspaces/jest/plop.test.js:15:11) - at Object.f (/workspaces/jest/plop.test.js:20:5) - at Promise.then.completed (/workspaces/jest/packages/jest-circus/build/utils.js:293:28) + at f (cause.test.js:15:11) + at Object.f (cause.test.js:20:5) + at Promise.then.completed (node_modules/jest-circus/build/utils.js:293:28) at new Promise () - at callAsyncCircusFn (/workspaces/jest/packages/jest-circus/build/utils.js:226:10) - at _callCircusTest (/workspaces/jest/packages/jest-circus/build/run.js:248:40) - at _runTest (/workspaces/jest/packages/jest-circus/build/run.js:184:3) - at _runTestsForDescribeBlock (/workspaces/jest/packages/jest-circus/build/run.js:86:9) - at run (/workspaces/jest/packages/jest-circus/build/run.js:26:3) - at runAndTransformResultsToJestFormat (/workspaces/jest/packages/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js:120:21) - at jestAdapter (/workspaces/jest/packages/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.js:74:19) - at runTestInternal (/workspaces/jest/packages/jest-runner/build/runTest.js:281:16) - at runTest (/workspaces/jest/packages/jest-runner/build/runTest.js:341:7)`; + at callAsyncCircusFn (node_modules/jest-circus/build/utils.js:226:10) + at _callCircusTest (node_modules/jest-circus/build/run.js:248:40) + at _runTest (node_modules/jest-circus/build/run.js:184:3) + at _runTestsForDescribeBlock (node_modules/jest-circus/build/run.js:86:9) + at run (node_modules/jest-circus/build/run.js:26:3) + at runAndTransformResultsToJestFormat (node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js:120:21) + at jestAdapter (node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.js:74:19) + at runTestInternal (node_modules/jest-runner/build/runTest.js:281:16) + at runTest (node_modules/jest-runner/build/runTest.js:341:7)`; const errorWithStringCause = new Error('boom', { cause: 'string cause', }); errorWithStringCause.stack = `Error: boom - at f (/workspaces/jest/plop.test.js:15:11) - at Object.f (/workspaces/jest/plop.test.js:20:5) - at Promise.then.completed (/workspaces/jest/packages/jest-circus/build/utils.js:293:28) + at f (cause.test.js:15:11) + at Object.f (cause.test.js:20:5) + at Promise.then.completed (node_modules/jest-circus/build/utils.js:293:28) at new Promise () - at callAsyncCircusFn (/workspaces/jest/packages/jest-circus/build/utils.js:226:10) - at _callCircusTest (/workspaces/jest/packages/jest-circus/build/run.js:248:40) - at _runTest (/workspaces/jest/packages/jest-circus/build/run.js:184:3) - at _runTestsForDescribeBlock (/workspaces/jest/packages/jest-circus/build/run.js:86:9) - at run (/workspaces/jest/packages/jest-circus/build/run.js:26:3) - at runAndTransformResultsToJestFormat (/workspaces/jest/packages/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js:120:21) - at jestAdapter (/workspaces/jest/packages/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.js:74:19) - at runTestInternal (/workspaces/jest/packages/jest-runner/build/runTest.js:281:16) - at runTest (/workspaces/jest/packages/jest-runner/build/runTest.js:341:7)`; + at callAsyncCircusFn (node_modules/jest-circus/build/utils.js:226:10) + at _callCircusTest (node_modules/jest-circus/build/run.js:248:40) + at _runTest (node_modules/jest-circus/build/run.js:184:3) + at _runTestsForDescribeBlock (node_modules/jest-circus/build/run.js:86:9) + at run (node_modules/jest-circus/build/run.js:26:3) + at runAndTransformResultsToJestFormat (node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js:120:21) + at jestAdapter (node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.js:74:19) + at runTestInternal (node_modules/jest-runner/build/runTest.js:281:16) + at runTest (node_modules/jest-runner/build/runTest.js:341:7)`; beforeEach(() => { jest.clearAllMocks(); From 3c1ccebd2bf48de7d845b7d519be2e29b159d117 Mon Sep 17 00:00:00 2001 From: Nicolas DUBIEN Date: Sun, 19 Feb 2023 18:07:27 +0000 Subject: [PATCH 04/23] add support for old versions of node --- .../src/__tests__/messages.test.ts | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/packages/jest-message-util/src/__tests__/messages.test.ts b/packages/jest-message-util/src/__tests__/messages.test.ts index 08a261745bcd..7f29cec7f53a 100644 --- a/packages/jest-message-util/src/__tests__/messages.test.ts +++ b/packages/jest-message-util/src/__tests__/messages.test.ts @@ -92,6 +92,15 @@ const babelStack = const babelError = new Error(babelStack.replace(/\n\s*at [\s\s]*/m, '')); babelError.stack = babelStack; +function buildErrorWithCause(message: string, cause: unknown): Error { + const error = new Error(message, {cause}); + if (cause !== error.cause) { + // Error with cause not supported in legacy versions of node, we just polyfill it + Object.assign(error, {cause}); + } + return error; +} + const errorWithCauseNestedNested = new Error('boom'); errorWithCauseNestedNested.stack = `Error: boom at h (cause.test.js:2:9) @@ -110,9 +119,10 @@ errorWithCauseNestedNested.stack = `Error: boom at runTestInternal (node_modules/jest-runner/build/runTest.js:281:16) at runTest (node_modules/jest-runner/build/runTest.js:341:7)`; -const errorWithCauseNested = new Error('intercepted by g', { - cause: errorWithCauseNestedNested, -}); +const errorWithCauseNested = buildErrorWithCause( + 'intercepted by g', + errorWithCauseNestedNested, +); errorWithCauseNested.stack = `Error: intercepted by g at g (cause.test.js:8:11) at g (cause.test.js:13:5) @@ -129,9 +139,10 @@ errorWithCauseNested.stack = `Error: intercepted by g at runTestInternal (node_modules/jest-runner/build/runTest.js:281:16) at runTest (node_modules/jest-runner/build/runTest.js:341:7)`; -const errorWithCause = new Error('intercepted by f', { - cause: errorWithCauseNested, -}); +const errorWithCause = buildErrorWithCause( + 'intercepted by f', + errorWithCauseNested, +); errorWithCause.stack = `Error: intercepted by f at f (cause.test.js:15:11) at Object.f (cause.test.js:20:5) @@ -147,9 +158,7 @@ errorWithCause.stack = `Error: intercepted by f at runTestInternal (node_modules/jest-runner/build/runTest.js:281:16) at runTest (node_modules/jest-runner/build/runTest.js:341:7)`; -const errorWithStringCause = new Error('boom', { - cause: 'string cause', -}); +const errorWithStringCause = buildErrorWithCause('boom', 'string cause'); errorWithStringCause.stack = `Error: boom at f (cause.test.js:15:11) at Object.f (cause.test.js:20:5) From d7524b237855d112581bfd4b1f7850a4e03f903c Mon Sep 17 00:00:00 2001 From: Nicolas DUBIEN Date: Sun, 19 Feb 2023 18:14:19 +0000 Subject: [PATCH 05/23] update snap --- .../src/__tests__/__snapshots__/messages.test.ts.snap | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/jest-message-util/src/__tests__/__snapshots__/messages.test.ts.snap b/packages/jest-message-util/src/__tests__/__snapshots__/messages.test.ts.snap index 27cedf55d0ec..e1b08704a669 100644 --- a/packages/jest-message-util/src/__tests__/__snapshots__/messages.test.ts.snap +++ b/packages/jest-message-util/src/__tests__/__snapshots__/messages.test.ts.snap @@ -158,12 +158,12 @@ exports[`should return the error cause if there is one 1`] = ` Test exception - at Object. (packages/jest-message-util/src/__tests__/messages.test.ts:557:17) + at Object. (packages/jest-message-util/src/__tests__/messages.test.ts:566:17) Cause: Cause Error - at Object. (packages/jest-message-util/src/__tests__/messages.test.ts:560:17) + at Object. (packages/jest-message-util/src/__tests__/messages.test.ts:569:17) " `; From e42a55ec7dac5d39a717e773f90c1eda1fc2e6c0 Mon Sep 17 00:00:00 2001 From: Nicolas DUBIEN Date: Sun, 19 Feb 2023 19:58:24 +0100 Subject: [PATCH 06/23] Update packages/jest-message-util/src/index.ts --- packages/jest-message-util/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/jest-message-util/src/index.ts b/packages/jest-message-util/src/index.ts index 4a4c11d357ae..7d740887c3fc 100644 --- a/packages/jest-message-util/src/index.ts +++ b/packages/jest-message-util/src/index.ts @@ -410,7 +410,7 @@ export const formatResultsErrors = ( message = indentAllLines(message); let cause = ''; - if (typeof errorOrStack !== 'string' && 'cause' in errorOrStack) { + if ('cause' in errorOrStack && errorOrStack.cause) { if ( typeof errorOrStack.cause === 'string' || types.isNativeError(errorOrStack.cause) || From 56ebf8c72b74ddab1f573dfd09b3cccab1b58484 Mon Sep 17 00:00:00 2001 From: Nicolas DUBIEN Date: Sun, 19 Feb 2023 19:58:30 +0100 Subject: [PATCH 07/23] Update packages/jest-message-util/src/index.ts --- packages/jest-message-util/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/jest-message-util/src/index.ts b/packages/jest-message-util/src/index.ts index 7d740887c3fc..c94520875a3f 100644 --- a/packages/jest-message-util/src/index.ts +++ b/packages/jest-message-util/src/index.ts @@ -425,7 +425,7 @@ export const formatResultsErrors = ( } const rootErrorOrStack: Error | string = - types.isNativeError(rawError) || rawError instanceof Error + rawError && (types.isNativeError(rawError) || rawError instanceof Error) ? rawError : content; From b0514ee738e7ab0fc0cd44a7a5816137bc1ebcc7 Mon Sep 17 00:00:00 2001 From: Nicolas DUBIEN Date: Sun, 19 Feb 2023 19:07:55 +0000 Subject: [PATCH 08/23] fix compilation --- packages/jest-message-util/src/index.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/jest-message-util/src/index.ts b/packages/jest-message-util/src/index.ts index c94520875a3f..8954e2083d4a 100644 --- a/packages/jest-message-util/src/index.ts +++ b/packages/jest-message-util/src/index.ts @@ -410,7 +410,11 @@ export const formatResultsErrors = ( message = indentAllLines(message); let cause = ''; - if ('cause' in errorOrStack && errorOrStack.cause) { + if ( + typeof errorOrStack !== 'string' && + 'cause' in errorOrStack && + errorOrStack.cause + ) { if ( typeof errorOrStack.cause === 'string' || types.isNativeError(errorOrStack.cause) || From 156a1cf8dd03ac9ec1d3db177c63aa6a1a6bfdb2 Mon Sep 17 00:00:00 2001 From: Nicolas DUBIEN Date: Mon, 20 Feb 2023 08:02:27 +0000 Subject: [PATCH 09/23] extract error stack computation function --- packages/jest-message-util/src/index.ts | 87 ++++++++++++++----------- 1 file changed, 50 insertions(+), 37 deletions(-) diff --git a/packages/jest-message-util/src/index.ts b/packages/jest-message-util/src/index.ts index 8954e2083d4a..34b14f572142 100644 --- a/packages/jest-message-util/src/index.ts +++ b/packages/jest-message-util/src/index.ts @@ -366,6 +366,50 @@ type FailedResults = Array<{ result: TestResult.AssertionResult; }>; +function formatErrorStack( + errorOrStack: Error | string, + config: StackTraceConfig, + options: StackTraceOptions, + testPath?: string, +): string { + // The stack of new Error('message') contains both the message and the stack, + // thus we need to sanitize and clean it for proper display using separateMessageFromStack. + const sourceStack = + typeof errorOrStack === 'string' ? errorOrStack : errorOrStack.stack || ''; + let {message, stack} = separateMessageFromStack(sourceStack); + stack = options.noStackTrace + ? '' + : `${STACK_TRACE_COLOR( + formatStackTrace(stack, config, options, testPath), + )}\n`; + + message = checkForCommonEnvironmentErrors(message); + message = indentAllLines(message); + + let cause = ''; + if ( + typeof errorOrStack !== 'string' && + 'cause' in errorOrStack && + errorOrStack.cause + ) { + if ( + typeof errorOrStack.cause === 'string' || + types.isNativeError(errorOrStack.cause) || + errorOrStack.cause instanceof Error + ) { + const nestedCause = formatErrorStack( + errorOrStack.cause, + config, + options, + testPath, + ); + cause = `\n${MESSAGE_INDENT}Cause:\n${nestedCause}`; + } + } + + return `${message}\n${stack}${cause}`; +} + export const formatResultsErrors = ( testResults: Array, config: StackTraceConfig, @@ -392,42 +436,6 @@ export const formatResultsErrors = ( return failedResults .map(({result, content, rawError}) => { - function formatErrorStack(errorOrStack: Error | string): string { - // The stack of new Error('message') contains both the message and the stack, - // thuw we need to sanitize and clean it for proper display using separateMessageFromStack. - const sourceStack = - typeof errorOrStack === 'string' - ? errorOrStack - : errorOrStack.stack || ''; - let {message, stack} = separateMessageFromStack(sourceStack); - stack = options.noStackTrace - ? '' - : `${STACK_TRACE_COLOR( - formatStackTrace(stack, config, options, testPath), - )}\n`; - - message = checkForCommonEnvironmentErrors(message); - message = indentAllLines(message); - - let cause = ''; - if ( - typeof errorOrStack !== 'string' && - 'cause' in errorOrStack && - errorOrStack.cause - ) { - if ( - typeof errorOrStack.cause === 'string' || - types.isNativeError(errorOrStack.cause) || - errorOrStack.cause instanceof Error - ) { - const nestedCause = formatErrorStack(errorOrStack.cause); - cause = `\n${MESSAGE_INDENT}Cause:\n${nestedCause}`; - } - } - - return `${message}\n${stack}${cause}`; - } - const rootErrorOrStack: Error | string = rawError && (types.isNativeError(rawError) || rawError instanceof Error) ? rawError @@ -441,7 +449,12 @@ export const formatResultsErrors = ( result.title, )}\n`; - return `${title}\n${formatErrorStack(rootErrorOrStack)}`; + return `${title}\n${formatErrorStack( + rootErrorOrStack, + config, + options, + testPath, + )}`; }) .join('\n'); }; From 8e763e78e91ac3bbfcc942acdcd41645b51ec044 Mon Sep 17 00:00:00 2001 From: Nicolas DUBIEN Date: Mon, 20 Feb 2023 08:03:29 +0000 Subject: [PATCH 10/23] rename option failureDetails --- packages/jest-message-util/src/index.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/jest-message-util/src/index.ts b/packages/jest-message-util/src/index.ts index 34b14f572142..673e805d04c8 100644 --- a/packages/jest-message-util/src/index.ts +++ b/packages/jest-message-util/src/index.ts @@ -360,8 +360,8 @@ export const formatStackTrace = ( type FailedResults = Array<{ /** Stringified version of the error */ content: string; - /** Error */ - rawError: unknown; + /** Details related to the failure */ + failureDetails: unknown; /** Execution result */ result: TestResult.AssertionResult; }>; @@ -421,7 +421,7 @@ export const formatResultsErrors = ( result.failureMessages.forEach((item, index) => { errors.push({ content: item, - rawError: result.failureDetails[index], + failureDetails: result.failureDetails[index], result, }); }); @@ -435,10 +435,11 @@ export const formatResultsErrors = ( } return failedResults - .map(({result, content, rawError}) => { + .map(({result, content, failureDetails}) => { const rootErrorOrStack: Error | string = - rawError && (types.isNativeError(rawError) || rawError instanceof Error) - ? rawError + failureDetails && + (types.isNativeError(failureDetails) || failureDetails instanceof Error) + ? failureDetails : content; const title = `${chalk.bold.red( From 046875aef1ebab3bce382e0fc67c01a29c41f1b6 Mon Sep 17 00:00:00 2001 From: Nicolas DUBIEN Date: Mon, 20 Feb 2023 08:05:24 +0000 Subject: [PATCH 11/23] update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d2475303f20..6650f3c35fb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ### Features +- `[jest-message-util]` Add support for [Error causes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause) in `test` and `it` + ### Fixes ### Chore & Maintenance From f556c7a2f4a11345957bc39582788e6586f445b8 Mon Sep 17 00:00:00 2001 From: Nicolas DUBIEN Date: Mon, 20 Feb 2023 23:10:24 +0000 Subject: [PATCH 12/23] add failures.test.ts --- .../__snapshots__/failures.test.ts.snap | 102 ++++++++++++++++++ e2e/__tests__/failures.test.ts | 14 +++ e2e/failures/__tests__/errorWithCause.test.js | 30 ++++++ .../errorWithCauseInDescribe.test.js | 24 +++++ 4 files changed, 170 insertions(+) create mode 100644 e2e/failures/__tests__/errorWithCause.test.js create mode 100644 e2e/failures/__tests__/errorWithCauseInDescribe.test.js diff --git a/e2e/__tests__/__snapshots__/failures.test.ts.snap b/e2e/__tests__/__snapshots__/failures.test.ts.snap index 4e71842dc24b..8f73ebe034da 100644 --- a/e2e/__tests__/__snapshots__/failures.test.ts.snap +++ b/e2e/__tests__/__snapshots__/failures.test.ts.snap @@ -404,6 +404,108 @@ exports[`works with async failures 1`] = ` at Object.test (__tests__/asyncFailures.test.js:22:1)" `; +exports[`works with error with cause 1`] = ` +"FAIL __tests__/errorWithCause.test.js + ✕ error with cause in test + describe block + ✕ error with cause in describe/it + + ● error with cause in test + + error during f + + 16 | g(); + 17 | } catch (err) { + > 18 | throw new Error('error during f', {cause: err}); + | ^ + 19 | } + 20 | } + 21 | + + at f (__tests__/errorWithCause.test.js:18:11) + at Object.f (__tests__/errorWithCause.test.js:23:3) + + Cause: + error during g + + 10 | + 11 | function g() { + > 12 | throw new Error('error during g'); + | ^ + 13 | } + 14 | function f() { + 15 | try { + + at g (__tests__/errorWithCause.test.js:12:9) + at g (__tests__/errorWithCause.test.js:16:5) + at Object.f (__tests__/errorWithCause.test.js:23:3) + + ● describe block › error with cause in describe/it + + error during f + + 16 | g(); + 17 | } catch (err) { + > 18 | throw new Error('error during f', {cause: err}); + | ^ + 19 | } + 20 | } + 21 | + + at f (__tests__/errorWithCause.test.js:18:11) + at Object.f (__tests__/errorWithCause.test.js:28:5) + + Cause: + error during g + + 10 | + 11 | function g() { + > 12 | throw new Error('error during g'); + | ^ + 13 | } + 14 | function f() { + 15 | try { + + at g (__tests__/errorWithCause.test.js:12:9) + at g (__tests__/errorWithCause.test.js:16:5) + at Object.f (__tests__/errorWithCause.test.js:28:5)" +`; + +exports[`works with error with cause thrown outside tests 1`] = ` +"FAIL __tests__/errorWithCauseInDescribe.test.js + ● Test suite failed to run + + error during f + + 16 | g(); + 17 | } catch (err) { + > 18 | throw new Error('error during f', {cause: err}); + | ^ + 19 | } + 20 | } + 21 | + + at f (__tests__/errorWithCauseInDescribe.test.js:18:11) + at f (__tests__/errorWithCauseInDescribe.test.js:23:3) + at Object.describe (__tests__/errorWithCauseInDescribe.test.js:22:1) + + Cause: + error during g + + 10 | + 11 | function g() { + > 12 | throw new Error('error during g'); + | ^ + 13 | } + 14 | function f() { + 15 | try { + + at g (__tests__/errorWithCauseInDescribe.test.js:12:9) + at g (__tests__/errorWithCauseInDescribe.test.js:16:5) + at f (__tests__/errorWithCauseInDescribe.test.js:23:3) + at Object.describe (__tests__/errorWithCauseInDescribe.test.js:22:1)" +`; + exports[`works with node assert 1`] = ` "FAIL __tests__/assertionError.test.js ✕ assert diff --git a/e2e/__tests__/failures.test.ts b/e2e/__tests__/failures.test.ts index f9b61caeeaea..7ec2e26924c4 100644 --- a/e2e/__tests__/failures.test.ts +++ b/e2e/__tests__/failures.test.ts @@ -90,6 +90,20 @@ test('works with snapshot failures with hint', () => { ).toMatchSnapshot(); }); +test('works with error with cause', () => { + const {stderr} = runJest(dir, ['errorWithCause.test.js']); + const summary = normalizeDots(cleanStderr(stderr)); + + expect(summary).toMatchSnapshot(); +}); + +test('works with error with cause thrown outside tests', () => { + const {stderr} = runJest(dir, ['errorWithCauseInDescribe.test.js']); + const summary = normalizeDots(cleanStderr(stderr)); + + expect(summary).toMatchSnapshot(); +}); + test('errors after test has completed', () => { const {stderr} = runJest(dir, ['errorAfterTestComplete.test.js']); diff --git a/e2e/failures/__tests__/errorWithCause.test.js b/e2e/failures/__tests__/errorWithCause.test.js new file mode 100644 index 000000000000..ede9de39c4fd --- /dev/null +++ b/e2e/failures/__tests__/errorWithCause.test.js @@ -0,0 +1,30 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ + +'use strict'; + +function g() { + throw new Error('error during g'); +} +function f() { + try { + g(); + } catch (err) { + throw new Error('error during f', {cause: err}); + } +} + +test('error with cause in test', () => { + f(); +}); + +describe('describe block', () => { + it('error with cause in describe/it', () => { + f(); + }); +}); diff --git a/e2e/failures/__tests__/errorWithCauseInDescribe.test.js b/e2e/failures/__tests__/errorWithCauseInDescribe.test.js new file mode 100644 index 000000000000..4f4e01168115 --- /dev/null +++ b/e2e/failures/__tests__/errorWithCauseInDescribe.test.js @@ -0,0 +1,24 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ + +'use strict'; + +function g() { + throw new Error('error during g'); +} +function f() { + try { + g(); + } catch (err) { + throw new Error('error during f', {cause: err}); + } +} + +describe('error with cause in describe', () => { + f(); +}); From 39bf3172bd5b0b768174841d3b98d86890b145a8 Mon Sep 17 00:00:00 2001 From: Nicolas DUBIEN Date: Tue, 21 Feb 2023 05:49:29 +0000 Subject: [PATCH 13/23] add error with cause support on node 14 --- .../__snapshots__/failures.test.ts.snap | 136 ++++++++++-------- e2e/failures/__tests__/errorWithCause.test.js | 15 +- .../errorWithCauseInDescribe.test.js | 11 +- 3 files changed, 103 insertions(+), 59 deletions(-) diff --git a/e2e/__tests__/__snapshots__/failures.test.ts.snap b/e2e/__tests__/__snapshots__/failures.test.ts.snap index 8f73ebe034da..c9d855f1597a 100644 --- a/e2e/__tests__/__snapshots__/failures.test.ts.snap +++ b/e2e/__tests__/__snapshots__/failures.test.ts.snap @@ -409,66 +409,87 @@ exports[`works with error with cause 1`] = ` ✕ error with cause in test describe block ✕ error with cause in describe/it + ✕ error with string cause in describe/it ● error with cause in test error during f - 16 | g(); - 17 | } catch (err) { - > 18 | throw new Error('error during f', {cause: err}); - | ^ - 19 | } - 20 | } - 21 | + 10 | + 11 | function buildErrorWithCause(message: string, cause: unknown): Error { + > 12 | const error = new Error(message, {cause}); + | ^ + 13 | if (cause !== error.cause) { + 14 | // Error with cause not supported in legacy versions of node, we just polyfill it + 15 | Object.assign(error, {cause}); - at f (__tests__/errorWithCause.test.js:18:11) - at Object.f (__tests__/errorWithCause.test.js:23:3) + at buildErrorWithCause (__tests__/errorWithCause.test.js:12:17) + at buildErrorWithCause (__tests__/errorWithCause.test.js:27:11) + at Object.f (__tests__/errorWithCause.test.js:32:3) Cause: error during g - 10 | - 11 | function g() { - > 12 | throw new Error('error during g'); + 19 | + 20 | function g() { + > 21 | throw new Error('error during g'); | ^ - 13 | } - 14 | function f() { - 15 | try { + 22 | } + 23 | function f() { + 24 | try { - at g (__tests__/errorWithCause.test.js:12:9) - at g (__tests__/errorWithCause.test.js:16:5) - at Object.f (__tests__/errorWithCause.test.js:23:3) + at g (__tests__/errorWithCause.test.js:21:9) + at g (__tests__/errorWithCause.test.js:25:5) + at Object.f (__tests__/errorWithCause.test.js:32:3) ● describe block › error with cause in describe/it error during f - 16 | g(); - 17 | } catch (err) { - > 18 | throw new Error('error during f', {cause: err}); - | ^ - 19 | } - 20 | } - 21 | + 10 | + 11 | function buildErrorWithCause(message: string, cause: unknown): Error { + > 12 | const error = new Error(message, {cause}); + | ^ + 13 | if (cause !== error.cause) { + 14 | // Error with cause not supported in legacy versions of node, we just polyfill it + 15 | Object.assign(error, {cause}); - at f (__tests__/errorWithCause.test.js:18:11) - at Object.f (__tests__/errorWithCause.test.js:28:5) + at buildErrorWithCause (__tests__/errorWithCause.test.js:12:17) + at buildErrorWithCause (__tests__/errorWithCause.test.js:27:11) + at Object.f (__tests__/errorWithCause.test.js:37:5) Cause: error during g - 10 | - 11 | function g() { - > 12 | throw new Error('error during g'); + 19 | + 20 | function g() { + > 21 | throw new Error('error during g'); | ^ - 13 | } - 14 | function f() { - 15 | try { + 22 | } + 23 | function f() { + 24 | try { + + at g (__tests__/errorWithCause.test.js:21:9) + at g (__tests__/errorWithCause.test.js:25:5) + at Object.f (__tests__/errorWithCause.test.js:37:5) - at g (__tests__/errorWithCause.test.js:12:9) - at g (__tests__/errorWithCause.test.js:16:5) - at Object.f (__tests__/errorWithCause.test.js:28:5)" + ● describe block › error with string cause in describe/it + + with string cause + + 10 | + 11 | function buildErrorWithCause(message: string, cause: unknown): Error { + > 12 | const error = new Error(message, {cause}); + | ^ + 13 | if (cause !== error.cause) { + 14 | // Error with cause not supported in legacy versions of node, we just polyfill it + 15 | Object.assign(error, {cause}); + + at buildErrorWithCause (__tests__/errorWithCause.test.js:12:17) + at Object.buildErrorWithCause (__tests__/errorWithCause.test.js:41:11) + + Cause: + here is the cause" `; exports[`works with error with cause thrown outside tests 1`] = ` @@ -477,33 +498,34 @@ exports[`works with error with cause thrown outside tests 1`] = ` error during f - 16 | g(); - 17 | } catch (err) { - > 18 | throw new Error('error during f', {cause: err}); - | ^ - 19 | } - 20 | } - 21 | + 10 | + 11 | function buildErrorWithCause(message: string, cause: unknown): Error { + > 12 | const error = new Error(message, {cause}); + | ^ + 13 | if (cause !== error.cause) { + 14 | // Error with cause not supported in legacy versions of node, we just polyfill it + 15 | Object.assign(error, {cause}); - at f (__tests__/errorWithCauseInDescribe.test.js:18:11) - at f (__tests__/errorWithCauseInDescribe.test.js:23:3) - at Object.describe (__tests__/errorWithCauseInDescribe.test.js:22:1) + at buildErrorWithCause (__tests__/errorWithCauseInDescribe.test.js:12:17) + at buildErrorWithCause (__tests__/errorWithCauseInDescribe.test.js:27:11) + at f (__tests__/errorWithCauseInDescribe.test.js:32:3) + at Object.describe (__tests__/errorWithCauseInDescribe.test.js:31:1) Cause: error during g - 10 | - 11 | function g() { - > 12 | throw new Error('error during g'); + 19 | + 20 | function g() { + > 21 | throw new Error('error during g'); | ^ - 13 | } - 14 | function f() { - 15 | try { - - at g (__tests__/errorWithCauseInDescribe.test.js:12:9) - at g (__tests__/errorWithCauseInDescribe.test.js:16:5) - at f (__tests__/errorWithCauseInDescribe.test.js:23:3) - at Object.describe (__tests__/errorWithCauseInDescribe.test.js:22:1)" + 22 | } + 23 | function f() { + 24 | try { + + at g (__tests__/errorWithCauseInDescribe.test.js:21:9) + at g (__tests__/errorWithCauseInDescribe.test.js:25:5) + at f (__tests__/errorWithCauseInDescribe.test.js:32:3) + at Object.describe (__tests__/errorWithCauseInDescribe.test.js:31:1)" `; exports[`works with node assert 1`] = ` diff --git a/e2e/failures/__tests__/errorWithCause.test.js b/e2e/failures/__tests__/errorWithCause.test.js index ede9de39c4fd..75cbb911364f 100644 --- a/e2e/failures/__tests__/errorWithCause.test.js +++ b/e2e/failures/__tests__/errorWithCause.test.js @@ -8,6 +8,15 @@ 'use strict'; +function buildErrorWithCause(message: string, cause: unknown): Error { + const error = new Error(message, {cause}); + if (cause !== error.cause) { + // Error with cause not supported in legacy versions of node, we just polyfill it + Object.assign(error, {cause}); + } + return error; +} + function g() { throw new Error('error during g'); } @@ -15,7 +24,7 @@ function f() { try { g(); } catch (err) { - throw new Error('error during f', {cause: err}); + throw buildErrorWithCause('error during f', err); } } @@ -27,4 +36,8 @@ describe('describe block', () => { it('error with cause in describe/it', () => { f(); }); + + it('error with string cause in describe/it', () => { + throw buildErrorWithCause('with string cause', 'here is the cause'); + }); }); diff --git a/e2e/failures/__tests__/errorWithCauseInDescribe.test.js b/e2e/failures/__tests__/errorWithCauseInDescribe.test.js index 4f4e01168115..6dede143e5eb 100644 --- a/e2e/failures/__tests__/errorWithCauseInDescribe.test.js +++ b/e2e/failures/__tests__/errorWithCauseInDescribe.test.js @@ -8,6 +8,15 @@ 'use strict'; +function buildErrorWithCause(message: string, cause: unknown): Error { + const error = new Error(message, {cause}); + if (cause !== error.cause) { + // Error with cause not supported in legacy versions of node, we just polyfill it + Object.assign(error, {cause}); + } + return error; +} + function g() { throw new Error('error during g'); } @@ -15,7 +24,7 @@ function f() { try { g(); } catch (err) { - throw new Error('error during f', {cause: err}); + throw buildErrorWithCause('error during f', err); } } From 0e24922c26b4f52e66d4149f1db746ea3ff4dee3 Mon Sep 17 00:00:00 2001 From: Nicolas DUBIEN Date: Tue, 21 Feb 2023 05:54:38 +0000 Subject: [PATCH 14/23] update error with cause builder --- .../__snapshots__/failures.test.ts.snap | 32 +++++++++---------- e2e/failures/__tests__/errorWithCause.test.js | 14 ++++---- .../errorWithCauseInDescribe.test.js | 10 +++--- .../src/__tests__/messages.test.ts | 26 +++++++-------- 4 files changed, 42 insertions(+), 40 deletions(-) diff --git a/e2e/__tests__/__snapshots__/failures.test.ts.snap b/e2e/__tests__/__snapshots__/failures.test.ts.snap index c9d855f1597a..4a3f562eb25f 100644 --- a/e2e/__tests__/__snapshots__/failures.test.ts.snap +++ b/e2e/__tests__/__snapshots__/failures.test.ts.snap @@ -416,12 +416,12 @@ exports[`works with error with cause 1`] = ` error during f 10 | - 11 | function buildErrorWithCause(message: string, cause: unknown): Error { - > 12 | const error = new Error(message, {cause}); + 11 | function buildErrorWithCause(message: string, opts: {cause: unknown}): Error { + > 12 | const error = new Error(message, opts); | ^ - 13 | if (cause !== error.cause) { + 13 | if (opts.cause !== error.cause) { 14 | // Error with cause not supported in legacy versions of node, we just polyfill it - 15 | Object.assign(error, {cause}); + 15 | Object.assign(error, opts); at buildErrorWithCause (__tests__/errorWithCause.test.js:12:17) at buildErrorWithCause (__tests__/errorWithCause.test.js:27:11) @@ -447,12 +447,12 @@ exports[`works with error with cause 1`] = ` error during f 10 | - 11 | function buildErrorWithCause(message: string, cause: unknown): Error { - > 12 | const error = new Error(message, {cause}); + 11 | function buildErrorWithCause(message: string, opts: {cause: unknown}): Error { + > 12 | const error = new Error(message, opts); | ^ - 13 | if (cause !== error.cause) { + 13 | if (opts.cause !== error.cause) { 14 | // Error with cause not supported in legacy versions of node, we just polyfill it - 15 | Object.assign(error, {cause}); + 15 | Object.assign(error, opts); at buildErrorWithCause (__tests__/errorWithCause.test.js:12:17) at buildErrorWithCause (__tests__/errorWithCause.test.js:27:11) @@ -478,12 +478,12 @@ exports[`works with error with cause 1`] = ` with string cause 10 | - 11 | function buildErrorWithCause(message: string, cause: unknown): Error { - > 12 | const error = new Error(message, {cause}); + 11 | function buildErrorWithCause(message: string, opts: {cause: unknown}): Error { + > 12 | const error = new Error(message, opts); | ^ - 13 | if (cause !== error.cause) { + 13 | if (opts.cause !== error.cause) { 14 | // Error with cause not supported in legacy versions of node, we just polyfill it - 15 | Object.assign(error, {cause}); + 15 | Object.assign(error, opts); at buildErrorWithCause (__tests__/errorWithCause.test.js:12:17) at Object.buildErrorWithCause (__tests__/errorWithCause.test.js:41:11) @@ -499,12 +499,12 @@ exports[`works with error with cause thrown outside tests 1`] = ` error during f 10 | - 11 | function buildErrorWithCause(message: string, cause: unknown): Error { - > 12 | const error = new Error(message, {cause}); + 11 | function buildErrorWithCause(message: string, opts: {cause: unknown}): Error { + > 12 | const error = new Error(message, opts); | ^ - 13 | if (cause !== error.cause) { + 13 | if (opts.cause !== error.cause) { 14 | // Error with cause not supported in legacy versions of node, we just polyfill it - 15 | Object.assign(error, {cause}); + 15 | Object.assign(error, opts); at buildErrorWithCause (__tests__/errorWithCauseInDescribe.test.js:12:17) at buildErrorWithCause (__tests__/errorWithCauseInDescribe.test.js:27:11) diff --git a/e2e/failures/__tests__/errorWithCause.test.js b/e2e/failures/__tests__/errorWithCause.test.js index 75cbb911364f..4ff87c40f681 100644 --- a/e2e/failures/__tests__/errorWithCause.test.js +++ b/e2e/failures/__tests__/errorWithCause.test.js @@ -8,11 +8,11 @@ 'use strict'; -function buildErrorWithCause(message: string, cause: unknown): Error { - const error = new Error(message, {cause}); - if (cause !== error.cause) { +function buildErrorWithCause(message: string, opts: {cause: unknown}): Error { + const error = new Error(message, opts); + if (opts.cause !== error.cause) { // Error with cause not supported in legacy versions of node, we just polyfill it - Object.assign(error, {cause}); + Object.assign(error, opts); } return error; } @@ -24,7 +24,7 @@ function f() { try { g(); } catch (err) { - throw buildErrorWithCause('error during f', err); + throw buildErrorWithCause('error during f', {cause: err}); } } @@ -38,6 +38,8 @@ describe('describe block', () => { }); it('error with string cause in describe/it', () => { - throw buildErrorWithCause('with string cause', 'here is the cause'); + throw buildErrorWithCause('with string cause', { + cause: 'here is the cause', + }); }); }); diff --git a/e2e/failures/__tests__/errorWithCauseInDescribe.test.js b/e2e/failures/__tests__/errorWithCauseInDescribe.test.js index 6dede143e5eb..e58731e43229 100644 --- a/e2e/failures/__tests__/errorWithCauseInDescribe.test.js +++ b/e2e/failures/__tests__/errorWithCauseInDescribe.test.js @@ -8,11 +8,11 @@ 'use strict'; -function buildErrorWithCause(message: string, cause: unknown): Error { - const error = new Error(message, {cause}); - if (cause !== error.cause) { +function buildErrorWithCause(message: string, opts: {cause: unknown}): Error { + const error = new Error(message, opts); + if (opts.cause !== error.cause) { // Error with cause not supported in legacy versions of node, we just polyfill it - Object.assign(error, {cause}); + Object.assign(error, opts); } return error; } @@ -24,7 +24,7 @@ function f() { try { g(); } catch (err) { - throw buildErrorWithCause('error during f', err); + throw buildErrorWithCause('error during f', {cause: err}); } } diff --git a/packages/jest-message-util/src/__tests__/messages.test.ts b/packages/jest-message-util/src/__tests__/messages.test.ts index 7f29cec7f53a..e173f71db67d 100644 --- a/packages/jest-message-util/src/__tests__/messages.test.ts +++ b/packages/jest-message-util/src/__tests__/messages.test.ts @@ -92,11 +92,11 @@ const babelStack = const babelError = new Error(babelStack.replace(/\n\s*at [\s\s]*/m, '')); babelError.stack = babelStack; -function buildErrorWithCause(message: string, cause: unknown): Error { - const error = new Error(message, {cause}); - if (cause !== error.cause) { +function buildErrorWithCause(message: string, opts: {cause: unknown}): Error { + const error = new Error(message, opts); + if (opts.cause !== error.cause) { // Error with cause not supported in legacy versions of node, we just polyfill it - Object.assign(error, {cause}); + Object.assign(error, opts); } return error; } @@ -119,10 +119,9 @@ errorWithCauseNestedNested.stack = `Error: boom at runTestInternal (node_modules/jest-runner/build/runTest.js:281:16) at runTest (node_modules/jest-runner/build/runTest.js:341:7)`; -const errorWithCauseNested = buildErrorWithCause( - 'intercepted by g', - errorWithCauseNestedNested, -); +const errorWithCauseNested = buildErrorWithCause('intercepted by g', { + cause: errorWithCauseNestedNested, +}); errorWithCauseNested.stack = `Error: intercepted by g at g (cause.test.js:8:11) at g (cause.test.js:13:5) @@ -139,10 +138,9 @@ errorWithCauseNested.stack = `Error: intercepted by g at runTestInternal (node_modules/jest-runner/build/runTest.js:281:16) at runTest (node_modules/jest-runner/build/runTest.js:341:7)`; -const errorWithCause = buildErrorWithCause( - 'intercepted by f', - errorWithCauseNested, -); +const errorWithCause = buildErrorWithCause('intercepted by f', { + cause: errorWithCauseNested, +}); errorWithCause.stack = `Error: intercepted by f at f (cause.test.js:15:11) at Object.f (cause.test.js:20:5) @@ -158,7 +156,9 @@ errorWithCause.stack = `Error: intercepted by f at runTestInternal (node_modules/jest-runner/build/runTest.js:281:16) at runTest (node_modules/jest-runner/build/runTest.js:341:7)`; -const errorWithStringCause = buildErrorWithCause('boom', 'string cause'); +const errorWithStringCause = buildErrorWithCause('boom', { + cause: 'string cause', +}); errorWithStringCause.stack = `Error: boom at f (cause.test.js:15:11) at Object.f (cause.test.js:20:5) From cf96b7013bbf6497115247aacc7527071fce1a08 Mon Sep 17 00:00:00 2001 From: Nicolas DUBIEN Date: Tue, 21 Feb 2023 05:59:11 +0000 Subject: [PATCH 15/23] add missing failureDetails: [] --- e2e/transform/transform-esm-testrunner/test-runner.mjs | 1 + e2e/transform/transform-testrunner/test-runner.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/e2e/transform/transform-esm-testrunner/test-runner.mjs b/e2e/transform/transform-esm-testrunner/test-runner.mjs index 9f5e016375bd..bd401cd4a800 100644 --- a/e2e/transform/transform-esm-testrunner/test-runner.mjs +++ b/e2e/transform/transform-esm-testrunner/test-runner.mjs @@ -23,6 +23,7 @@ export default async function testRunner( { ancestorTitles: [], duration: 2, + failureDetails: [], failureMessages: [], fullName: 'sample test', location: null, diff --git a/e2e/transform/transform-testrunner/test-runner.ts b/e2e/transform/transform-testrunner/test-runner.ts index ab90debd3e30..4d5ee7302f76 100644 --- a/e2e/transform/transform-testrunner/test-runner.ts +++ b/e2e/transform/transform-testrunner/test-runner.ts @@ -24,6 +24,7 @@ export default async function testRunner( { ancestorTitles: [], duration: 2, + failureDetails: [], failureMessages: [], fullName: 'sample test', location: null, From d4b5e4733534ec4648abf94e9b77e29fa6a7a6ae Mon Sep 17 00:00:00 2001 From: Nicolas DUBIEN Date: Tue, 21 Feb 2023 06:53:53 +0000 Subject: [PATCH 16/23] fix new case for jasmine --- packages/jest-message-util/src/index.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/packages/jest-message-util/src/index.ts b/packages/jest-message-util/src/index.ts index 673e805d04c8..1f9de62b0a3f 100644 --- a/packages/jest-message-util/src/index.ts +++ b/packages/jest-message-util/src/index.ts @@ -436,11 +436,17 @@ export const formatResultsErrors = ( return failedResults .map(({result, content, failureDetails}) => { - const rootErrorOrStack: Error | string = - failureDetails && - (types.isNativeError(failureDetails) || failureDetails instanceof Error) - ? failureDetails - : content; + const rootErrorOrStack: Error | string = failureDetails + ? types.isNativeError(failureDetails) || failureDetails instanceof Error + ? failureDetails // receiving raw errors for jest-circus + : typeof failureDetails === 'object' && + 'error' in failureDetails && + failureDetails.error && + (types.isNativeError(failureDetails.error) || + failureDetails.error instanceof Error) + ? failureDetails.error // receiving instances of FailedAssertion for jest-jasmine + : content + : content; const title = `${chalk.bold.red( TITLE_INDENT + From 22df9f7427b8224665c0388d17e39fc0c2b7cdc2 Mon Sep 17 00:00:00 2001 From: Nicolas DUBIEN Date: Tue, 21 Feb 2023 06:54:31 +0000 Subject: [PATCH 17/23] alter test to run well on jasmine too --- .../__snapshots__/failures.test.ts.snap | 63 +++++++++---------- e2e/__tests__/failures.test.ts | 11 +++- 2 files changed, 40 insertions(+), 34 deletions(-) diff --git a/e2e/__tests__/__snapshots__/failures.test.ts.snap b/e2e/__tests__/__snapshots__/failures.test.ts.snap index 4a3f562eb25f..14c7531a152b 100644 --- a/e2e/__tests__/__snapshots__/failures.test.ts.snap +++ b/e2e/__tests__/__snapshots__/failures.test.ts.snap @@ -493,39 +493,36 @@ exports[`works with error with cause 1`] = ` `; exports[`works with error with cause thrown outside tests 1`] = ` -"FAIL __tests__/errorWithCauseInDescribe.test.js - ● Test suite failed to run - - error during f - - 10 | - 11 | function buildErrorWithCause(message: string, opts: {cause: unknown}): Error { - > 12 | const error = new Error(message, opts); - | ^ - 13 | if (opts.cause !== error.cause) { - 14 | // Error with cause not supported in legacy versions of node, we just polyfill it - 15 | Object.assign(error, opts); - - at buildErrorWithCause (__tests__/errorWithCauseInDescribe.test.js:12:17) - at buildErrorWithCause (__tests__/errorWithCauseInDescribe.test.js:27:11) - at f (__tests__/errorWithCauseInDescribe.test.js:32:3) - at Object.describe (__tests__/errorWithCauseInDescribe.test.js:31:1) - - Cause: - error during g - - 19 | - 20 | function g() { - > 21 | throw new Error('error during g'); - | ^ - 22 | } - 23 | function f() { - 24 | try { - - at g (__tests__/errorWithCauseInDescribe.test.js:21:9) - at g (__tests__/errorWithCauseInDescribe.test.js:25:5) - at f (__tests__/errorWithCauseInDescribe.test.js:32:3) - at Object.describe (__tests__/errorWithCauseInDescribe.test.js:31:1)" +"error during f + +10 | +11 | function buildErrorWithCause(message: string, opts: {cause: unknown}): Error { +> 12 | const error = new Error(message, opts); +| ^ +13 | if (opts.cause !== error.cause) { +14 | // Error with cause not supported in legacy versions of node, we just polyfill it +15 | Object.assign(error, opts); + +at buildErrorWithCause (__tests__/errorWithCauseInDescribe.test.js:12:17) +at buildErrorWithCause (__tests__/errorWithCauseInDescribe.test.js:27:11) +at f (__tests__/errorWithCauseInDescribe.test.js:32:3) +at Object.describe (__tests__/errorWithCauseInDescribe.test.js:31:1) + +Cause: +error during g + +19 | +20 | function g() { +> 21 | throw new Error('error during g'); +| ^ +22 | } +23 | function f() { +24 | try { + +at g (__tests__/errorWithCauseInDescribe.test.js:21:9) +at g (__tests__/errorWithCauseInDescribe.test.js:25:5) +at f (__tests__/errorWithCauseInDescribe.test.js:32:3) +at Object.describe (__tests__/errorWithCauseInDescribe.test.js:31:1)" `; exports[`works with node assert 1`] = ` diff --git a/e2e/__tests__/failures.test.ts b/e2e/__tests__/failures.test.ts index 7ec2e26924c4..5179cc171f77 100644 --- a/e2e/__tests__/failures.test.ts +++ b/e2e/__tests__/failures.test.ts @@ -100,8 +100,17 @@ test('works with error with cause', () => { test('works with error with cause thrown outside tests', () => { const {stderr} = runJest(dir, ['errorWithCauseInDescribe.test.js']); const summary = normalizeDots(cleanStderr(stderr)); + const sanitizedSummary = summary + .replace(/ Suite\.f /g, ' f ') // added by jasmine runner + .split('\n') + .map(line => line.trim()) // jasmine runner does not come with the same indentation + .join('\n'); - expect(summary).toMatchSnapshot(); + expect( + // jasmine runner differ from circus one in this case, we just start + // the comparison when the stack starts to be reported + sanitizedSummary.substring(sanitizedSummary.indexOf('error during f')), + ).toMatchSnapshot(); }); test('errors after test has completed', () => { From b0bd8a1b08db8a603667ca5c5626cd700ef3eebc Mon Sep 17 00:00:00 2001 From: Nicolas DUBIEN Date: Tue, 21 Feb 2023 23:24:19 +0000 Subject: [PATCH 18/23] Revert "alter test to run well on jasmine too" This reverts commit 22df9f7427b8224665c0388d17e39fc0c2b7cdc2. --- .../__snapshots__/failures.test.ts.snap | 63 ++++++++++--------- e2e/__tests__/failures.test.ts | 11 +--- 2 files changed, 34 insertions(+), 40 deletions(-) diff --git a/e2e/__tests__/__snapshots__/failures.test.ts.snap b/e2e/__tests__/__snapshots__/failures.test.ts.snap index 14c7531a152b..4a3f562eb25f 100644 --- a/e2e/__tests__/__snapshots__/failures.test.ts.snap +++ b/e2e/__tests__/__snapshots__/failures.test.ts.snap @@ -493,36 +493,39 @@ exports[`works with error with cause 1`] = ` `; exports[`works with error with cause thrown outside tests 1`] = ` -"error during f - -10 | -11 | function buildErrorWithCause(message: string, opts: {cause: unknown}): Error { -> 12 | const error = new Error(message, opts); -| ^ -13 | if (opts.cause !== error.cause) { -14 | // Error with cause not supported in legacy versions of node, we just polyfill it -15 | Object.assign(error, opts); - -at buildErrorWithCause (__tests__/errorWithCauseInDescribe.test.js:12:17) -at buildErrorWithCause (__tests__/errorWithCauseInDescribe.test.js:27:11) -at f (__tests__/errorWithCauseInDescribe.test.js:32:3) -at Object.describe (__tests__/errorWithCauseInDescribe.test.js:31:1) - -Cause: -error during g - -19 | -20 | function g() { -> 21 | throw new Error('error during g'); -| ^ -22 | } -23 | function f() { -24 | try { - -at g (__tests__/errorWithCauseInDescribe.test.js:21:9) -at g (__tests__/errorWithCauseInDescribe.test.js:25:5) -at f (__tests__/errorWithCauseInDescribe.test.js:32:3) -at Object.describe (__tests__/errorWithCauseInDescribe.test.js:31:1)" +"FAIL __tests__/errorWithCauseInDescribe.test.js + ● Test suite failed to run + + error during f + + 10 | + 11 | function buildErrorWithCause(message: string, opts: {cause: unknown}): Error { + > 12 | const error = new Error(message, opts); + | ^ + 13 | if (opts.cause !== error.cause) { + 14 | // Error with cause not supported in legacy versions of node, we just polyfill it + 15 | Object.assign(error, opts); + + at buildErrorWithCause (__tests__/errorWithCauseInDescribe.test.js:12:17) + at buildErrorWithCause (__tests__/errorWithCauseInDescribe.test.js:27:11) + at f (__tests__/errorWithCauseInDescribe.test.js:32:3) + at Object.describe (__tests__/errorWithCauseInDescribe.test.js:31:1) + + Cause: + error during g + + 19 | + 20 | function g() { + > 21 | throw new Error('error during g'); + | ^ + 22 | } + 23 | function f() { + 24 | try { + + at g (__tests__/errorWithCauseInDescribe.test.js:21:9) + at g (__tests__/errorWithCauseInDescribe.test.js:25:5) + at f (__tests__/errorWithCauseInDescribe.test.js:32:3) + at Object.describe (__tests__/errorWithCauseInDescribe.test.js:31:1)" `; exports[`works with node assert 1`] = ` diff --git a/e2e/__tests__/failures.test.ts b/e2e/__tests__/failures.test.ts index 5179cc171f77..7ec2e26924c4 100644 --- a/e2e/__tests__/failures.test.ts +++ b/e2e/__tests__/failures.test.ts @@ -100,17 +100,8 @@ test('works with error with cause', () => { test('works with error with cause thrown outside tests', () => { const {stderr} = runJest(dir, ['errorWithCauseInDescribe.test.js']); const summary = normalizeDots(cleanStderr(stderr)); - const sanitizedSummary = summary - .replace(/ Suite\.f /g, ' f ') // added by jasmine runner - .split('\n') - .map(line => line.trim()) // jasmine runner does not come with the same indentation - .join('\n'); - expect( - // jasmine runner differ from circus one in this case, we just start - // the comparison when the stack starts to be reported - sanitizedSummary.substring(sanitizedSummary.indexOf('error during f')), - ).toMatchSnapshot(); + expect(summary).toMatchSnapshot(); }); test('errors after test has completed', () => { From 238590edeb587c66da512b06f6929b218145ca38 Mon Sep 17 00:00:00 2001 From: Nicolas DUBIEN Date: Tue, 21 Feb 2023 23:24:50 +0000 Subject: [PATCH 19/23] Revert "fix new case for jasmine" This reverts commit d4b5e4733534ec4648abf94e9b77e29fa6a7a6ae. --- packages/jest-message-util/src/index.ts | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/packages/jest-message-util/src/index.ts b/packages/jest-message-util/src/index.ts index 1f9de62b0a3f..673e805d04c8 100644 --- a/packages/jest-message-util/src/index.ts +++ b/packages/jest-message-util/src/index.ts @@ -436,17 +436,11 @@ export const formatResultsErrors = ( return failedResults .map(({result, content, failureDetails}) => { - const rootErrorOrStack: Error | string = failureDetails - ? types.isNativeError(failureDetails) || failureDetails instanceof Error - ? failureDetails // receiving raw errors for jest-circus - : typeof failureDetails === 'object' && - 'error' in failureDetails && - failureDetails.error && - (types.isNativeError(failureDetails.error) || - failureDetails.error instanceof Error) - ? failureDetails.error // receiving instances of FailedAssertion for jest-jasmine - : content - : content; + const rootErrorOrStack: Error | string = + failureDetails && + (types.isNativeError(failureDetails) || failureDetails instanceof Error) + ? failureDetails + : content; const title = `${chalk.bold.red( TITLE_INDENT + From 808336b47bbb1ca0f843fb52a4905eec5b5e2b1f Mon Sep 17 00:00:00 2001 From: Nicolas DUBIEN Date: Tue, 21 Feb 2023 23:29:05 +0000 Subject: [PATCH 20/23] only test for non-jasmine --- e2e/__tests__/failures.test.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/e2e/__tests__/failures.test.ts b/e2e/__tests__/failures.test.ts index 7ec2e26924c4..21fc03200ccd 100644 --- a/e2e/__tests__/failures.test.ts +++ b/e2e/__tests__/failures.test.ts @@ -6,6 +6,7 @@ */ import * as path from 'path'; +import {isJestJasmineRun} from '@jest/test-utils'; import {extractSummary, runYarnInstall} from '../Utils'; import runJest from '../runJest'; @@ -90,19 +91,22 @@ test('works with snapshot failures with hint', () => { ).toMatchSnapshot(); }); -test('works with error with cause', () => { +(isJestJasmineRun() ? test.skip : test)('works with error with cause', () => { const {stderr} = runJest(dir, ['errorWithCause.test.js']); const summary = normalizeDots(cleanStderr(stderr)); expect(summary).toMatchSnapshot(); }); -test('works with error with cause thrown outside tests', () => { - const {stderr} = runJest(dir, ['errorWithCauseInDescribe.test.js']); - const summary = normalizeDots(cleanStderr(stderr)); +(isJestJasmineRun() ? test.skip : test)( + 'works with error with cause thrown outside tests', + () => { + const {stderr} = runJest(dir, ['errorWithCauseInDescribe.test.js']); + const summary = normalizeDots(cleanStderr(stderr)); - expect(summary).toMatchSnapshot(); -}); + expect(summary).toMatchSnapshot(); + }, +); test('errors after test has completed', () => { const {stderr} = runJest(dir, ['errorAfterTestComplete.test.js']); From fdd56caf10f7a80730e55c2e41255e13bc30d919 Mon Sep 17 00:00:00 2001 From: Nicolas DUBIEN Date: Tue, 21 Feb 2023 23:30:47 +0000 Subject: [PATCH 21/23] merge --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e894d65f8ceb..92df3138fe40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ### Features -- `[jest-message-util]` Add support for [Error causes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause) in `test` and `it` ([#13937](https://github.com/facebook/jest/pull/13935)) +- `[jest-message-util]` Add support for [Error causes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause) in `test` and `it` ([#13935](https://github.com/facebook/jest/pull/13935)) - `[jest-worker]` Add `start` method to worker farms ([#13937](https://github.com/facebook/jest/pull/13937)) ### Fixes From 81295f79f1fb3081a2d82586c2e19a0dfbf94dd5 Mon Sep 17 00:00:00 2001 From: Nicolas DUBIEN Date: Thu, 23 Feb 2023 21:37:51 +0000 Subject: [PATCH 22/23] simplify and extract nested if --- packages/jest-message-util/src/index.ts | 38 +++++++++++++------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/packages/jest-message-util/src/index.ts b/packages/jest-message-util/src/index.ts index 3eef3677c1ea..2ff495b460dc 100644 --- a/packages/jest-message-util/src/index.ts +++ b/packages/jest-message-util/src/index.ts @@ -387,6 +387,18 @@ type FailedResults = Array<{ result: TestResult.AssertionResult; }>; +function isErrorOrStackWithCause( + errorOrStack: Error | string, +): errorOrStack is Error & {cause: Error} { + return ( + typeof errorOrStack !== 'string' && + 'cause' in errorOrStack && + (typeof errorOrStack.cause === 'string' || + types.isNativeError(errorOrStack.cause) || + errorOrStack.cause instanceof Error) + ); +} + function formatErrorStack( errorOrStack: Error | string, config: StackTraceConfig, @@ -408,24 +420,14 @@ function formatErrorStack( message = indentAllLines(message); let cause = ''; - if ( - typeof errorOrStack !== 'string' && - 'cause' in errorOrStack && - errorOrStack.cause - ) { - if ( - typeof errorOrStack.cause === 'string' || - types.isNativeError(errorOrStack.cause) || - errorOrStack.cause instanceof Error - ) { - const nestedCause = formatErrorStack( - errorOrStack.cause, - config, - options, - testPath, - ); - cause = `\n${MESSAGE_INDENT}Cause:\n${nestedCause}`; - } + if (isErrorOrStackWithCause(errorOrStack)) { + const nestedCause = formatErrorStack( + errorOrStack.cause, + config, + options, + testPath, + ); + cause = `\n${MESSAGE_INDENT}Cause:\n${nestedCause}`; } return `${message}\n${stack}${cause}`; From c6fa4a9b316ce3687ef8d921425f059d0c722542 Mon Sep 17 00:00:00 2001 From: Nicolas DUBIEN Date: Thu, 23 Feb 2023 21:39:07 +0000 Subject: [PATCH 23/23] type error --- packages/jest-message-util/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/jest-message-util/src/index.ts b/packages/jest-message-util/src/index.ts index 2ff495b460dc..de851ea1d05f 100644 --- a/packages/jest-message-util/src/index.ts +++ b/packages/jest-message-util/src/index.ts @@ -389,7 +389,7 @@ type FailedResults = Array<{ function isErrorOrStackWithCause( errorOrStack: Error | string, -): errorOrStack is Error & {cause: Error} { +): errorOrStack is Error & {cause: Error | string} { return ( typeof errorOrStack !== 'string' && 'cause' in errorOrStack &&