Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(expect): move typings of .not, .rejects and .resolves modifiers outside of Matchers interface #12346

Merged
merged 2 commits into from Feb 9, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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 {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These were not exported before. I think it might be good idea to allow extending these too.

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