Skip to content

Commit

Permalink
fix(expect): move typings of .not, .rejects and .resolves modif…
Browse files Browse the repository at this point in the history
…iers outside of `Matchers` interface (#12346)
  • Loading branch information
mrazauskas committed Feb 9, 2022
1 parent 7f7b3fb commit e9eced4
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 30 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -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
Expand Down
72 changes: 43 additions & 29 deletions packages/expect/src/types.ts
Expand Up @@ -64,18 +64,10 @@ export type ExpectedAssertionsErrors = Array<{
expected: string;
}>;

interface AsymmetricMatchers {
any(sample: unknown): AsymmetricMatcher;
anything(): AsymmetricMatcher;
arrayContaining(sample: Array<unknown>): AsymmetricMatcher;
closeTo(sample: number, precision?: number): AsymmetricMatcher;
objectContaining(sample: Record<string, unknown>): AsymmetricMatcher;
stringContaining(sample: string): AsymmetricMatcher;
stringMatching(sample: string | RegExp): AsymmetricMatcher;
}

export type Expect<State extends MatcherState = MatcherState> = {
<T = unknown>(actual: T): Matchers<void, T>;
<T = unknown>(actual: T): Matchers<void, T> &
InverseMatchers<void, T> &
PromiseMatchers<T>;
// TODO: this is added by test runners, not `expect` itself
addSnapshotSerializer(serializer: unknown): void;
assertions(numberOfAssertions: number): void;
Expand All @@ -85,12 +77,48 @@ export type Expect<State extends MatcherState = MatcherState> = {
getState(): State;
hasAssertions(): void;
setState(state: Partial<State>): void;
} & AsymmetricMatchers & {
not: Omit<AsymmetricMatchers, 'any' | 'anything'>;
};
} & AsymmetricMatchers &
InverseAsymmetricMatchers;

type InverseAsymmetricMatchers = {
/**
* Inverse next matcher. If you know how to test something, `.not` lets you test its opposite.
*/
not: Omit<AsymmetricMatchers, 'any' | 'anything'>;
};

export interface AsymmetricMatchers {
any(sample: unknown): AsymmetricMatcher;
anything(): AsymmetricMatcher;
arrayContaining(sample: Array<unknown>): AsymmetricMatcher;
closeTo(sample: number, precision?: number): AsymmetricMatcher;
objectContaining(sample: Record<string, unknown>): AsymmetricMatcher;
stringContaining(sample: string): AsymmetricMatcher;
stringMatching(sample: string | RegExp): AsymmetricMatcher;
}

type PromiseMatchers<T = unknown> = {
/**
* Unwraps the reason of a rejected promise so any other matcher can be chained.
* If the promise is fulfilled the assertion fails.
*/
rejects: Matchers<Promise<void>, T> & InverseMatchers<Promise<void>, 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<Promise<void>, T> & InverseMatchers<Promise<void>, T>;
};

type InverseMatchers<R extends void | Promise<void>, T = unknown> = {
/**
* Inverse next matcher. If you know how to test something, `.not` lets you test its opposite.
*/
not: Matchers<R, T>;
};

// This is a copy from https://github.com/DefinitelyTyped/DefinitelyTyped/blob/de6730f4463cba69904698035fafd906a72b9664/types/jest/index.d.ts#L570-L817
export interface Matchers<R, T = unknown> {
export interface Matchers<R extends void | Promise<void>, T = unknown> {
/**
* Ensures the last call to a mock function was provided specific args.
*/
Expand All @@ -99,10 +127,6 @@ export interface Matchers<R, T = unknown> {
* 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<R, T>;
/**
* Ensure that a mock function is called with specific arguments on an Nth call.
*/
Expand All @@ -111,16 +135,6 @@ export interface Matchers<R, T = unknown> {
* 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<Promise<R>, 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<Promise<R>, T>;
/**
* Checks that a value is what you expect. It uses `===` to check strict equality.
* Don't use `toBe` with floating-point numbers.
Expand Down
20 changes: 19 additions & 1 deletion packages/jest-types/__typetests__/expect.test.ts
Expand Up @@ -57,7 +57,7 @@ expectType<void>(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<void>(expect.assertions(2));
expectError(expect.assertions());
Expand All @@ -69,10 +69,28 @@ expectType<Promise<void>>(
expect(Promise.resolve('lemon')).resolves.toBe('lemon'),
);

expectType<Promise<void>>(
expect(Promise.resolve('lemon')).resolves.not.toBe('lemon'),
);

expectType<Promise<void>>(
expect(Promise.reject(new Error('octopus'))).rejects.toThrow('octopus'),
);

expectType<Promise<void>>(
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<void>(expect(2).toBe(2));
Expand Down

0 comments on commit e9eced4

Please sign in to comment.