From d9d869a649d137a957c4a8887bf03798bc058029 Mon Sep 17 00:00:00 2001 From: BondarenkoAlex Date: Mon, 7 Oct 2024 20:23:13 +0300 Subject: [PATCH] fix: show `AggregateError` to display Closes jestjs/jest#14704 --- CHANGELOG.md | 1 + package.json | 1 + .../src/__tests__/toThrowMatchers.test.ts | 30 +++++++++++++++ packages/expect/src/__tests__/tsconfig.json | 2 +- packages/expect/src/toThrowMatchers.ts | 37 +++++++++++++------ 5 files changed, 58 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 363abee8a97e..f533846038c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,7 @@ ### Fixes +- `[expect]` Show `AggregateError` to display ([#15346](https://github.com/facebook/jest/pull/15346)) - `[babel-plugin-jest-hoist]` Use `denylist` instead of the deprecated `blacklist` for Babel 8 support ([#14109](https://github.com/jestjs/jest/pull/14109)) - `[expect]` Check error instance type for `toThrow/toThrowError` ([#14576](https://github.com/jestjs/jest/pull/14576)) - `[expect]` Improve diff for failing `expect.objectContaining` ([#15038](https://github.com/jestjs/jest/pull/15038)) diff --git a/package.json b/package.json index 9c809d4412bb..4d4e813dc2db 100644 --- a/package.json +++ b/package.json @@ -101,6 +101,7 @@ "jest-jasmine-ci": "yarn jest-jasmine --color --config jest.config.ci.mjs", "jest-coverage": "yarn jest --coverage", "lint": "eslint . --cache --ext js,jsx,cjs,mjs,ts,tsx,md", + "lint:fix": "eslint ./packages/expect/src/toThrowMatchers.ts --cache --fix", "lint:prettier-script": "prettier . \"!**/*.{js,jsx,cjs,mjs,ts,tsx}\" --cache", "lint:prettier": "yarn lint:prettier-script --write", "lint:prettier:ci": "yarn lint:prettier-script --check", diff --git a/packages/expect/src/__tests__/toThrowMatchers.test.ts b/packages/expect/src/__tests__/toThrowMatchers.test.ts index 2a7d19f68da8..c7223fae4d5d 100644 --- a/packages/expect/src/__tests__/toThrowMatchers.test.ts +++ b/packages/expect/src/__tests__/toThrowMatchers.test.ts @@ -332,6 +332,36 @@ describe('toThrow', () => { }); }); + describe('aggregate-errors', () => { + const fetchFromApi1 = Promise.reject(new Error('API 1 failed')); + const fetchFromApi2 = Promise.reject(new Error('API 2 failed')); + const promiseAny = Promise.any([fetchFromApi1, fetchFromApi2]); + + test('string', () => { + jestExpect(promiseAny).rejects.toThrow('All promises were rejected'); + }); + + test('undefined', () => { + jestExpect(promiseAny).rejects.toThrow(); + }); + + test('asymmetricMatch', () => { + jestExpect(promiseAny).rejects.toThrow( + expect.objectContaining({ + message: 'All promises were rejected', + }), + ); + }); + + test('regexp', () => { + jestExpect(promiseAny).rejects.toThrow(/All promises were rejected/); + }); + + test('class', () => { + jestExpect(promiseAny).rejects.toThrow(AggregateError); + }); + }); + describe('asymmetric', () => { describe('any-Class', () => { describe('pass', () => { diff --git a/packages/expect/src/__tests__/tsconfig.json b/packages/expect/src/__tests__/tsconfig.json index 3e19aea5423e..082b9d01c3c3 100644 --- a/packages/expect/src/__tests__/tsconfig.json +++ b/packages/expect/src/__tests__/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "../../../../tsconfig.test.json", "compilerOptions": { - "lib": ["es2022.error"], + "lib": ["es2022.error", "es2021.promise"], "types": ["node", "@jest/test-globals"] }, "include": ["./**/*"], diff --git a/packages/expect/src/toThrowMatchers.ts b/packages/expect/src/toThrowMatchers.ts index dfda024afb8c..2263323fcbe1 100644 --- a/packages/expect/src/toThrowMatchers.ts +++ b/packages/expect/src/toThrowMatchers.ts @@ -20,7 +20,11 @@ import { printReceived, printWithType, } from 'jest-matcher-utils'; -import {formatStackTrace, separateMessageFromStack} from 'jest-message-util'; +import { + formatExecError, + formatStackTrace, + separateMessageFromStack, +} from 'jest-message-util'; import { printExpectedConstructorName, printExpectedConstructorNameNot, @@ -453,19 +457,28 @@ const formatReceived = ( return ''; }; -const formatStack = (thrown: Thrown | null) => - thrown === null || !thrown.isError - ? '' - : formatStackTrace( +const formatStack = (thrown: Thrown | null) => { + if (thrown === null || !thrown.isError) { + return ''; + } else { + const config = { + rootDir: process.cwd(), + testMatch: [], + }; + const options = { + noStackTrace: false, + }; + if (thrown.value instanceof AggregateError) { + return formatExecError(thrown.value, config, options); + } else { + return formatStackTrace( separateMessageFromStack(thrown.value.stack!).stack, - { - rootDir: process.cwd(), - testMatch: [], - }, - { - noStackTrace: false, - }, + config, + options, ); + } + } +}; function createMessageAndCauseMessage(error: Error): string { if (error.cause instanceof Error) {