diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e68a5b8bd72..1962b5d20663 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ ### Fixes +- `[expect]` Move typings of `.not`, `.rejects` and `.resolves` modifiers outside of `Matchers` interface ([#12346](https://github.com/facebook/jest/pull/12346)) - `[jest-phabricator]` [**BREAKING**] Convert to ESM ([#12341](https://github.com/facebook/jest/pull/12341)) ### Chore & Maintenance diff --git a/packages/expect/src/types.ts b/packages/expect/src/types.ts index 8bd31aaf7e19..d9b7f31307fd 100644 --- a/packages/expect/src/types.ts +++ b/packages/expect/src/types.ts @@ -64,18 +64,10 @@ export type ExpectedAssertionsErrors = Array<{ expected: string; }>; -interface AsymmetricMatchers { - any(sample: unknown): AsymmetricMatcher; - anything(): AsymmetricMatcher; - arrayContaining(sample: Array): AsymmetricMatcher; - closeTo(sample: number, precision?: number): AsymmetricMatcher; - objectContaining(sample: Record): AsymmetricMatcher; - stringContaining(sample: string): AsymmetricMatcher; - stringMatching(sample: string | RegExp): AsymmetricMatcher; -} - export type Expect = { - (actual: T): Matchers; + (actual: T): Matchers & + InverseMatchers & + PromiseMatchers; // TODO: this is added by test runners, not `expect` itself addSnapshotSerializer(serializer: unknown): void; assertions(numberOfAssertions: number): void; @@ -85,12 +77,48 @@ export type Expect = { getState(): State; hasAssertions(): void; setState(state: Partial): void; -} & AsymmetricMatchers & { - not: Omit; - }; +} & AsymmetricMatchers & + InverseAsymmetricMatchers; + +type InverseAsymmetricMatchers = { + /** + * Inverse next matcher. If you know how to test something, `.not` lets you test its opposite. + */ + not: Omit; +}; + +export interface AsymmetricMatchers { + any(sample: unknown): AsymmetricMatcher; + anything(): AsymmetricMatcher; + arrayContaining(sample: Array): AsymmetricMatcher; + closeTo(sample: number, precision?: number): AsymmetricMatcher; + objectContaining(sample: Record): AsymmetricMatcher; + stringContaining(sample: string): AsymmetricMatcher; + stringMatching(sample: string | RegExp): AsymmetricMatcher; +} + +type PromiseMatchers = { + /** + * Unwraps the reason of a rejected promise so any other matcher can be chained. + * If the promise is fulfilled the assertion fails. + */ + rejects: Matchers, T> & InverseMatchers, T>; + /** + * Unwraps the value of a fulfilled promise so any other matcher can be chained. + * If the promise is rejected the assertion fails. + */ + resolves: Matchers, T> & InverseMatchers, T>; +}; + +type InverseMatchers, T = unknown> = { + /** + * Inverse next matcher. If you know how to test something, `.not` lets you test its opposite. + */ + not: Matchers; +}; // This is a copy from https://github.com/DefinitelyTyped/DefinitelyTyped/blob/de6730f4463cba69904698035fafd906a72b9664/types/jest/index.d.ts#L570-L817 -export interface Matchers { +export interface Matchers, T = unknown> { /** * Ensures the last call to a mock function was provided specific args. */ @@ -99,10 +127,6 @@ export interface Matchers { * Ensure that the last call to a mock function has returned a specified value. */ lastReturnedWith(expected: unknown): R; - /** - * If you know how to test something, `.not` lets you test its opposite. - */ - not: Matchers; /** * Ensure that a mock function is called with specific arguments on an Nth call. */ @@ -111,16 +135,6 @@ export interface Matchers { * Ensure that the nth call to a mock function has returned a specified value. */ nthReturnedWith(nth: number, expected: unknown): R; - /** - * Use resolves to unwrap the value of a fulfilled promise so any other - * matcher can be chained. If the promise is rejected the assertion fails. - */ - resolves: Matchers, T>; - /** - * Unwraps the reason of a rejected promise so any other matcher can be chained. - * If the promise is fulfilled the assertion fails. - */ - rejects: Matchers, T>; /** * Checks that a value is what you expect. It uses `===` to check strict equality. * Don't use `toBe` with floating-point numbers. diff --git a/packages/jest-types/__typetests__/expect.test.ts b/packages/jest-types/__typetests__/expect.test.ts index 37a6f9efb136..2c380a0c40c8 100644 --- a/packages/jest-types/__typetests__/expect.test.ts +++ b/packages/jest-types/__typetests__/expect.test.ts @@ -57,7 +57,7 @@ expectType(expect('two').toEqual(expect.not.stringMatching(/^[No]ne/))); expectError(expect('two').toEqual(expect.not.stringMatching(1))); expectError(expect('two').toEqual(expect.not.stringMatching())); -// chainers and utilities +// modifiers and utilities expectType(expect.assertions(2)); expectError(expect.assertions()); @@ -69,10 +69,28 @@ expectType>( expect(Promise.resolve('lemon')).resolves.toBe('lemon'), ); +expectType>( + expect(Promise.resolve('lemon')).resolves.not.toBe('lemon'), +); + expectType>( expect(Promise.reject(new Error('octopus'))).rejects.toThrow('octopus'), ); +expectType>( + expect(Promise.reject(new Error('octopus'))).rejects.not.toThrow('octopus'), +); + +expectError(expect(1).not.not.toBe(2)); +expectError(expect(1).not.resolves.toBe(2)); +expectError(expect(1).not.rejects.toBe(2)); + +expectError(expect(1).resolves.resolves.toBe(2)); +expectError(expect(1).resolves.rejects.toBe(2)); + +expectError(expect(1).rejects.resolves.toBe(2)); +expectError(expect(1).rejects.rejects.toBe(2)); + // equality and relational matchers expectType(expect(2).toBe(2));