From e336b2520998fb4c3b00da3e7c6eac0bc8c53c4b Mon Sep 17 00:00:00 2001 From: mrazauskas <72159681+mrazauskas@users.noreply.github.com> Date: Mon, 11 Oct 2021 19:56:15 +0300 Subject: [PATCH 1/4] fix(expect): add types test and improve types --- .../src/__tests__/assertionCounts.test.ts | 1 + packages/expect/src/index.ts | 2 +- packages/expect/src/jestMatchersObject.ts | 17 +- packages/expect/src/types.ts | 100 +++-- test-types/top-level-expect-namespace.test.ts | 348 ++++++++++++++++++ test-types/top-level-globals.test.ts | 40 -- 6 files changed, 407 insertions(+), 101 deletions(-) create mode 100644 test-types/top-level-expect-namespace.test.ts diff --git a/packages/expect/src/__tests__/assertionCounts.test.ts b/packages/expect/src/__tests__/assertionCounts.test.ts index 672edbca6756..45a37e3a5c4b 100644 --- a/packages/expect/src/__tests__/assertionCounts.test.ts +++ b/packages/expect/src/__tests__/assertionCounts.test.ts @@ -37,6 +37,7 @@ describe('.hasAssertions()', () => { it('throws if expected is not undefined', () => { jestExpect(() => { + // @ts-expect-error jestExpect.hasAssertions(2); }).toThrowErrorMatchingSnapshot(); }); diff --git a/packages/expect/src/index.ts b/packages/expect/src/index.ts index 24d939675bf1..648074fdba90 100644 --- a/packages/expect/src/index.ts +++ b/packages/expect/src/index.ts @@ -431,7 +431,7 @@ const expectExport = expect as Expect; declare namespace expectExport { export type MatcherState = JestMatcherState; - export interface Matchers extends MatcherInterface {} + export interface Matchers extends MatcherInterface {} } export = expectExport; diff --git a/packages/expect/src/jestMatchersObject.ts b/packages/expect/src/jestMatchersObject.ts index 370802661c30..3b15af89015e 100644 --- a/packages/expect/src/jestMatchersObject.ts +++ b/packages/expect/src/jestMatchersObject.ts @@ -98,15 +98,14 @@ export const setMatchers = ( } } - expect[key] = (...sample: [unknown, ...Array]) => - new CustomMatcher(false, ...sample); - if (!expect.not) { - throw new Error( - '`expect.not` is not defined - please report this bug to https://github.com/facebook/jest', - ); - } - expect.not[key] = (...sample: [unknown, ...Array]) => - new CustomMatcher(true, ...sample); + Object.defineProperty(expect, key, { + value: (...sample: [unknown, ...Array]) => + new CustomMatcher(false, ...sample), + }); + Object.defineProperty(expect.not, key, { + value: (...sample: [unknown, ...Array]) => + new CustomMatcher(true, ...sample), + }); } }); diff --git a/packages/expect/src/types.ts b/packages/expect/src/types.ts index 691941f87b18..9a9c2524c476 100644 --- a/packages/expect/src/types.ts +++ b/packages/expect/src/types.ts @@ -72,26 +72,17 @@ export type ExpectedAssertionsErrors = Array<{ expected: string; }>; -interface InverseAsymmetricMatchers { +interface AsymmetricMatchers { + any(sample: unknown): AsymmetricMatcher; + anything(): AsymmetricMatcher; arrayContaining(sample: Array): AsymmetricMatcher; objectContaining(sample: Record): AsymmetricMatcher; - stringContaining(expected: string): AsymmetricMatcher; - stringMatching(expected: string | RegExp): AsymmetricMatcher; -} - -interface AsymmetricMatchers extends InverseAsymmetricMatchers { - any(expectedObject: unknown): AsymmetricMatcher; - anything(): AsymmetricMatcher; -} - -// Should use interface merging somehow -interface ExtraAsymmetricMatchers { - // at least one argument is needed - that's probably wrong. Should allow `expect.toBeDivisibleBy2()` like `expect.anything()` - [id: string]: (...sample: [unknown, ...Array]) => AsymmetricMatcher; + stringContaining(sample: string): AsymmetricMatcher; + stringMatching(sample: string | RegExp): AsymmetricMatcher; } export type Expect = { - (actual: T): Matchers; + (actual: T): Matchers; // TODO: this is added by test runners, not `expect` itself addSnapshotSerializer(serializer: unknown): void; assertions(numberOfAssertions: number): void; @@ -101,43 +92,42 @@ export type Expect = { getState(): State; hasAssertions(): void; setState(state: Partial): void; -} & AsymmetricMatchers & - ExtraAsymmetricMatchers & { - not: InverseAsymmetricMatchers & ExtraAsymmetricMatchers; +} & AsymmetricMatchers & { + not: Omit; }; // 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 { /** * Ensures the last call to a mock function was provided specific args. */ - lastCalledWith(...args: Array): R; + lastCalledWith(...expected: [unknown, ...Array]): R; /** * Ensure that the last call to a mock function has returned a specified value. */ - lastReturnedWith(value: unknown): R; + lastReturnedWith(expected: unknown): R; /** * If you know how to test something, `.not` lets you test its opposite. */ - not: Matchers; + not: Matchers; /** * Ensure that a mock function is called with specific arguments on an Nth call. */ - nthCalledWith(nthCall: number, ...args: Array): R; + nthCalledWith(nth: number, ...expected: [unknown, ...Array]): R; /** * Ensure that the nth call to a mock function has returned a specified value. */ - nthReturnedWith(n: number, value: unknown): R; + 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>; + 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>; + 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. @@ -154,13 +144,13 @@ export interface Matchers { /** * Ensure that a mock function is called with specific arguments. */ - toBeCalledWith(...args: Array): R; + toBeCalledWith(...expected: [unknown, ...Array]): R; /** * Using exact equality with floating point numbers is a bad idea. * Rounding means that intuitive things fail. - * The default for numDigits is 2. + * The default for `precision` is 2. */ - toBeCloseTo(expected: number, numDigits?: number): R; + toBeCloseTo(expected: number, precision?: number): R; /** * Ensure that a variable is not undefined. */ @@ -182,7 +172,7 @@ export interface Matchers { * Ensure that an object is an instance of a class. * This matcher uses `instanceof` underneath. */ - toBeInstanceOf(expected: Function): R; + toBeInstanceOf(expected: unknown): R; /** * For comparing floating point numbers. */ @@ -237,16 +227,19 @@ export interface Matchers { /** * Ensure that a mock function is called with specific arguments. */ - toHaveBeenCalledWith(...args: Array): R; + toHaveBeenCalledWith(...expected: [unknown, ...Array]): R; /** * Ensure that a mock function is called with specific arguments on an Nth call. */ - toHaveBeenNthCalledWith(nthCall: number, ...args: Array): R; + toHaveBeenNthCalledWith( + nth: number, + ...expected: [unknown, ...Array] + ): R; /** * If you have a mock function, you can use `.toHaveBeenLastCalledWith` * to test what arguments it was last called with. */ - toHaveBeenLastCalledWith(...args: Array): R; + toHaveBeenLastCalledWith(...expected: [unknown, ...Array]): R; /** * Use to test the specific value that a mock function last returned. * If the last call to the mock function threw an error, then this matcher will fail @@ -263,7 +256,7 @@ export interface Matchers { * If the nth call to the mock function threw an error, then this matcher will fail * no matter what value you provided as the expected return value. */ - toHaveNthReturnedWith(nthCall: number, expected: unknown): R; + toHaveNthReturnedWith(nth: number, expected: unknown): R; /** * Use to check if property at provided reference keyPath exists for an object. * For checking deeply nested properties in an object you may use dot notation or an array containing @@ -277,7 +270,10 @@ export interface Matchers { * * expect(houseForSale).toHaveProperty('kitchen.area', 20); */ - toHaveProperty(keyPath: string | Array, value?: unknown): R; + toHaveProperty( + expectedPath: string | Array, + expectedValue?: unknown, + ): R; /** * Use to test that the mock function successfully returned (i.e., did not throw an error) at least one time */ @@ -298,7 +294,9 @@ export interface Matchers { /** * Used to check that a JavaScript object matches a subset of the properties of an object */ - toMatchObject(expected: Record | Array): R; + toMatchObject( + expected: Record | Array>, + ): R; /** * Ensure that a mock function has returned (as opposed to thrown) at least once. */ @@ -306,11 +304,11 @@ export interface Matchers { /** * Ensure that a mock function has returned (as opposed to thrown) a specified number of times. */ - toReturnTimes(count: number): R; + toReturnTimes(expected: number): R; /** * Ensure that a mock function has returned a specified value at least once. */ - toReturnWith(value: unknown): R; + toReturnWith(expected: unknown): R; /** * Use to test that objects have the same types as well as structure. */ @@ -318,45 +316,45 @@ export interface Matchers { /** * Used to test that a function throws when it is called. */ - toThrow(error?: unknown): R; + toThrow(expected?: unknown): R; /** * If you want to test that a specific error is thrown inside a function. */ - toThrowError(error?: unknown): R; + toThrowError(expected?: unknown): R; /* TODO: START snapshot matchers are not from `expect`, the types should not be here */ /** * This ensures that a value matches the most recent snapshot with property matchers. * Check out [the Snapshot Testing guide](https://jestjs.io/docs/snapshot-testing) for more information. */ - toMatchSnapshot( - propertyMatchers: Partial, - snapshotName?: string, - ): R; + toMatchSnapshot(hint?: string): R; /** * This ensures that a value matches the most recent snapshot. * Check out [the Snapshot Testing guide](https://jestjs.io/docs/snapshot-testing) for more information. */ - toMatchSnapshot(snapshotName?: string): R; + toMatchSnapshot>( + propertyMatchers: Partial, + hint?: string, + ): R; /** * This ensures that a value matches the most recent snapshot with property matchers. * Instead of writing the snapshot value to a .snap file, it will be written into the source code automatically. * Check out [the Snapshot Testing guide](https://jestjs.io/docs/snapshot-testing) for more information. */ - toMatchInlineSnapshot( - propertyMatchers: Partial, - snapshot?: string, - ): R; + toMatchInlineSnapshot(snapshot?: string): R; /** * This ensures that a value matches the most recent snapshot with property matchers. * Instead of writing the snapshot value to a .snap file, it will be written into the source code automatically. * Check out [the Snapshot Testing guide](https://jestjs.io/docs/snapshot-testing) for more information. */ - toMatchInlineSnapshot(snapshot?: string): R; + toMatchInlineSnapshot>( + propertyMatchers: Partial, + snapshot?: string, + ): R; /** * Used to test that a function throws a error matching the most recent snapshot when it is called. */ - toThrowErrorMatchingSnapshot(): R; + toThrowErrorMatchingSnapshot(hint?: string): R; /** * Used to test that a function throws a error matching the most recent snapshot when it is called. * Instead of writing the snapshot value to a .snap file, it will be written into the source code automatically. diff --git a/test-types/top-level-expect-namespace.test.ts b/test-types/top-level-expect-namespace.test.ts new file mode 100644 index 000000000000..69451c74f299 --- /dev/null +++ b/test-types/top-level-expect-namespace.test.ts @@ -0,0 +1,348 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @type ./empty.d.ts + */ + +import {expectError, expectType} from 'mlh-tsd'; +import {expect} from '@jest/globals'; + +// asymmetric matchers + +expectType(expect('value').toEqual(expect.any(String))); +expectError(expect(123).toEqual(expect.any())); +expectError(expect('value').toEqual(expect.not.any(Number))); + +expectType(expect(jest.fn()).toBeCalledWith(expect.anything())); +expectError(expect(jest.fn()).toBeCalledWith(expect.anything(true))); +expectError(expect(jest.fn()).toBeCalledWith(expect.not.anything())); + +expectType(expect(['A', 'B']).toEqual(expect.arrayContaining(['A']))); +expectError(expect(['A']).toEqual(expect.arrayContaining('A'))); +expectError(expect(['A']).toEqual(expect.arrayContaining())); +expectType(expect(['B']).toEqual(expect.not.arrayContaining(['A']))); +expectError(expect(['A']).toEqual(expect.not.arrayContaining('A'))); +expectError(expect(['A']).toEqual(expect.not.arrayContaining())); + +expectType(expect({a: 1}).toEqual(expect.objectContaining({a: 1}))); +expectError(expect({a: 1}).toEqual(expect.objectContaining(1))); +expectError(expect({a: 1}).toEqual(expect.objectContaining())); +expectType(expect({b: 2}).toEqual(expect.not.objectContaining({a: 1}))); +expectError(expect({a: 1}).toEqual(expect.not.objectContaining(1))); +expectError(expect({a: 1}).toEqual(expect.not.objectContaining())); + +expectType(expect('one').toEqual(expect.stringContaining('n'))); +expectError(expect('two').toEqual(expect.stringContaining(2))); +expectError(expect('three').toEqual(expect.stringContaining())); +expectType(expect('one').toEqual(expect.not.stringContaining('m'))); +expectError(expect('two').toEqual(expect.not.stringContaining(2))); +expectError(expect('three').toEqual(expect.not.stringContaining())); + +expectType(expect('one').toEqual(expect.stringMatching(/^[No]ne/))); +expectError(expect('one').toEqual(expect.stringMatching(2))); +expectError(expect('one').toEqual(expect.stringMatching())); +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 + +expectType(expect.assertions(2)); +expectError(expect.assertions()); + +expectType(expect.hasAssertions()); +expectError(expect.hasAssertions(true)); + +expectType>( + expect(Promise.resolve('lemon')).resolves.toBe('lemon'), +); + +expectType>( + expect(Promise.reject(new Error('octopus'))).rejects.toThrow('octopus'), +); + +// equality and relational matchers + +expectType(expect(2).toBe(2)); +expectType(expect('three').not.toBe('four')); +expectError(expect(false).toBe()); + +expectType(expect(0.2 + 0.1).toBeCloseTo(0.3)); +expectType(expect(0.2 + 0.1).toBeCloseTo(0.3, 5)); +expectError(expect(0.2 + 0.1).toBeCloseTo()); +expectError(expect(0.2 + 0.1).toBeCloseTo('three')); +expectError(expect(BigInt(0.2 + 0.1)).toBeCloseTo(BigInt(0.3))); +expectError(expect(0.2 + 0.1).toBeCloseTo(0.3, false)); + +expectType(expect('value').toBeDefined()); +expectError(expect(true).not.toBeDefined(false)); + +expectType(expect(0).toBeFalsy()); +expectError(expect(true).not.toBeFalsy(true)); + +expectType(expect(10).toBeGreaterThan(5)); +expectType(expect(BigInt(5.65)).toBeGreaterThan(BigInt(5.61))); +expectError(expect(10).toBeGreaterThan()); +expectError(expect(10).toBeGreaterThan('1')); + +expectType(expect(10).toBeGreaterThanOrEqual(5)); +expectType(expect(BigInt(5.65)).toBeGreaterThanOrEqual(BigInt(5.61))); +expectError(expect(10).toBeGreaterThanOrEqual()); +expectError(expect(10).toBeGreaterThanOrEqual('1')); + +expectType(expect(5).toBeLessThan(10)); +expectType(expect(BigInt(5.61)).toBeLessThan(BigInt(5.65))); +expectError(expect(1).toBeLessThan()); +expectError(expect(1).toBeLessThan('10')); + +expectType(expect(5).toBeLessThanOrEqual(10)); +expectType(expect(BigInt(5.61)).toBeLessThanOrEqual(BigInt(5.65))); +expectError(expect(1).toBeLessThanOrEqual()); +expectError(expect(1).toBeLessThanOrEqual('10')); + +expectType(expect(() => {}).toBeInstanceOf(Function)); +expectError(expect(() => {}).toBeInstanceOf('function')); + +expectType(expect(Number('ten')).toBeNaN()); +expectError(expect(Number('10')).not.toBeNaN(true)); + +expectType(expect(null).toBeNull()); +expectError(expect('not null').not.toBeNull(true)); + +expectType(expect('true').toBeTruthy()); +expectError(expect(false).not.toBeTruthy(true)); + +expectType(expect(undefined).toBeUndefined()); +expectError(expect('value').not.toBeUndefined(false)); + +expectType(expect(['lemon', 'lime']).not.toContain('orange')); +expectType(expect('citrus fruits').toContain('fruit')); + +const a = {key1: true, key2: false}; +expectType(expect([{key1: true, key2: false}]).toContainEqual(a)); + +expectType(expect({a: 1, b: undefined}).toEqual({a: 1})); +expectError(expect({a: 1}).toEqual()); + +expectType(expect({a: 1, b: 2}).toStrictEqual({a: 1, b: 2})); +expectError(expect({a: 1}).toStrictEqual()); + +expectType(expect([1, 2, 3]).toHaveLength(3)); +expectType(expect('abc').not.toHaveLength(5)); +expectError(expect('abc').toHaveLength()); + +expectType( + expect({kitchen: {area: 20}}).toHaveProperty('kitchen.area', 20), +); +expectType( + expect({kitchen: {area: 20}}).not.toHaveProperty(['kitchen', 'color']), +); +expectError(expect({kitchen: {area: 20}}).toHaveProperty()); +expectError(expect({kitchen: {area: 20}}).toHaveProperty(true)); + +expectType(expect('grapefruits').toMatch(/fruit/)); +expectType(expect('grapefruits').toMatch('fruit')); +expectError(expect('grapefruits').toMatch(true)); + +expectType(expect({a: 1, b: 2}).toMatchObject({b: 2})); +expectType( + expect([{a: 1}, {b: 2, c: true}]).toMatchObject([{a: 1}, {b: 2}]), +); +expectError(expect({c: true}).toMatchObject(true)); +expectError(expect({c: true}).toMatchObject()); + +// error matchers + +expectType(expect(() => {}).toThrow()); +expectType(expect(() => {}).toThrow(/error/)); +expectType(expect(() => {}).toThrow('error')); +expectType(expect(() => {}).toThrow(Error)); +expectType(expect(() => {}).toThrow(new Error('error'))); + +expectType(expect(() => {}).toThrowError()); +expectType(expect(() => {}).toThrowError(/error/)); +expectType(expect(() => {}).toThrowError('error')); +expectType(expect(() => {}).toThrowError(Error)); +expectType(expect(() => {}).toThrowError(new Error('error'))); + +// mock matchers + +expectType(expect(jest.fn()).toBeCalled()); +expectError(expect(jest.fn()).toBeCalled('value')); +expectType(expect(jest.fn()).toHaveBeenCalled()); +expectError(expect(jest.fn()).toHaveBeenCalled(false)); + +expectType(expect(jest.fn()).toBeCalledTimes(1)); +expectError(expect(jest.fn()).toBeCalledTimes('twice')); +expectError(expect(jest.fn()).toBeCalledTimes()); +expectType(expect(jest.fn()).toHaveBeenCalledTimes(3)); +expectError(expect(jest.fn()).toHaveBeenCalledTimes(true)); +expectError(expect(jest.fn()).toHaveBeenCalledTimes()); + +expectType(expect(jest.fn()).toBeCalledWith('value')); +expectType(expect(jest.fn()).toBeCalledWith('value', 123)); +// expectError(expect(jest.fn()).toBeCalledWith()); +expectType(expect(jest.fn()).toHaveBeenCalledWith(123)); +expectType(expect(jest.fn()).toHaveBeenCalledWith(123, 'value')); +// expectError(expect(jest.fn()).toHaveBeenCalledWith()); + +expectType(expect(jest.fn()).lastCalledWith('value')); +expectType(expect(jest.fn()).lastCalledWith('value', 123)); +// expectError(expect(jest.fn()).lastCalledWith()); +expectType(expect(jest.fn()).toHaveBeenLastCalledWith(123)); +expectType(expect(jest.fn()).toHaveBeenLastCalledWith(123, 'value')); +// expectError(expect(jest.fn()).toHaveBeenLastCalledWith()); + +expectType(expect(jest.fn()).nthCalledWith(1, 'value')); +expectType(expect(jest.fn()).nthCalledWith(1, 'value', 123)); +// expectError(expect(jest.fn()).nthCalledWith()); +// expectError(expect(jest.fn()).nthCalledWith(2)); +expectType(expect(jest.fn()).toHaveBeenNthCalledWith(1, 'value')); +expectType(expect(jest.fn()).toHaveBeenNthCalledWith(1, 'value', 123)); +// expectError(expect(jest.fn()).toHaveBeenNthCalledWith()); +// expectError(expect(jest.fn()).toHaveBeenNthCalledWith(2)); + +expectType(expect(jest.fn()).toReturn()); +expectError(expect(jest.fn()).toReturn('value')); +expectType(expect(jest.fn()).toHaveReturned()); +expectError(expect(jest.fn()).toHaveReturned(false)); + +expectType(expect(jest.fn()).toReturnTimes(1)); +expectError(expect(jest.fn()).toReturnTimes('twice')); +expectError(expect(jest.fn()).toReturnTimes()); +expectType(expect(jest.fn()).toHaveReturnedTimes(3)); +expectError(expect(jest.fn()).toHaveReturnedTimes(true)); +expectError(expect(jest.fn()).toHaveReturnedTimes()); + +expectType(expect(jest.fn()).toReturnWith('value')); +expectError(expect(jest.fn()).toReturnWith()); +expectType(expect(jest.fn()).toHaveReturnedWith(123)); +expectError(expect(jest.fn()).toHaveReturnedWith()); + +expectType(expect(jest.fn()).lastReturnedWith('value')); +expectError(expect(jest.fn()).lastReturnedWith()); +expectType(expect(jest.fn()).toHaveLastReturnedWith(123)); +expectError(expect(jest.fn()).toHaveLastReturnedWith()); + +expectType(expect(jest.fn()).nthReturnedWith(1, 'value')); +expectError(expect(jest.fn()).nthReturnedWith()); +expectError(expect(jest.fn()).nthReturnedWith(2)); +expectType(expect(jest.fn()).toHaveNthReturnedWith(1, 'value')); +expectError(expect(jest.fn()).toHaveNthReturnedWith()); +expectError(expect(jest.fn()).toHaveNthReturnedWith(2)); + +// snapshot matchers + +expectType(expect({a: 1}).toMatchSnapshot()); +expectType(expect({a: 1}).toMatchSnapshot('hint')); +expectError(expect({a: 1}).toMatchSnapshot(true)); + +expectType( + expect({ + date: new Date(), + name: 'John Doe', + }).toMatchSnapshot({ + date: expect.any(Date), + name: expect.any(String), + }), +); + +expectType( + expect({ + date: new Date(), + name: 'John Doe', + }).toMatchSnapshot( + { + date: expect.any(Date), + name: expect.any(String), + }, + 'hint', + ), +); + +expectError( + expect({ + date: new Date(), + name: 'John Doe', + }).toMatchSnapshot({ + date: expect.any(Date), + time: expect.any(Date), + }), +); + +expectType(expect('abc').toMatchInlineSnapshot()); +expectType(expect('abc').toMatchInlineSnapshot('inline snapshot here')); +expectError(expect('abc').toMatchInlineSnapshot(true)); + +expectType( + expect({ + date: new Date(), + name: 'John Doe', + }).toMatchInlineSnapshot({ + date: expect.any(Date), + name: expect.any(String), + }), +); + +expectType( + expect({ + date: new Date(), + name: 'John Doe', + }).toMatchInlineSnapshot( + { + date: expect.any(Date), + name: expect.any(String), + }, + 'inline snapshot here', + ), +); + +expectError( + expect({ + date: new Date(), + name: 'John Doe', + }).toMatchInlineSnapshot({ + date: expect.any(Date), + time: expect.any(Date), + }), +); + +expectType(expect(jest.fn()).toThrowErrorMatchingSnapshot()); +expectType(expect(jest.fn()).toThrowErrorMatchingSnapshot('hint')); +expectError(expect(jest.fn()).toThrowErrorMatchingSnapshot(true)); + +expectType(expect(jest.fn()).toThrowErrorMatchingInlineSnapshot()); +expectType( + expect(jest.fn()).toThrowErrorMatchingInlineSnapshot('inline snapshot here'), +); +expectError(expect(jest.fn()).toThrowErrorMatchingInlineSnapshot(true)); + +// extend + +expectType( + expect.extend({ + toBeDivisibleBy(actual: number, expected: number) { + expectType(this.isNot); + + const pass = actual % expected === 0; + const message = pass + ? () => + `expected ${this.utils.printReceived( + actual, + )} not to be divisible by ${expected}` + : () => + `expected ${this.utils.printReceived( + actual, + )} to be divisible by ${expected}`; + + return {message, pass}; + }, + }), +); + +// TODO +// expect(4).toBeDivisibleBy(2); +// expect.toBeDivisibleBy(2); diff --git a/test-types/top-level-globals.test.ts b/test-types/top-level-globals.test.ts index 544cba2bef5c..d37807961542 100644 --- a/test-types/top-level-globals.test.ts +++ b/test-types/top-level-globals.test.ts @@ -14,7 +14,6 @@ import { beforeAll, beforeEach, describe, - expect, test, } from '@jest/globals'; import type {Global} from '@jest/types'; @@ -109,42 +108,3 @@ expectType(describe.only.each(testTable)(testName, fn)); expectType(describe.only.each(testTable)(testName, fn, timeout)); expectType(describe.skip.each(testTable)(testName, fn)); expectType(describe.skip.each(testTable)(testName, fn, timeout)); - -/// expect - -expectType(expect(2).toBe(2)); -expectType>(expect(2).resolves.toBe(2)); - -expectType(expect(() => {}).toThrow()); -expectType(expect(() => {}).toThrow(/error/)); -expectType(expect(() => {}).toThrow('error')); -expectType(expect(() => {}).toThrow(Error)); -expectType(expect(() => {}).toThrow(new Error('error'))); - -expectType(expect('Hello').toEqual(expect.any(String))); - -// this currently does not error due to `[id: string]` in ExtraAsymmetricMatchers - we should have nothing there and force people to use interface merging -// expectError(expect('Hello').toEqual(expect.not.any(Number))); - -expectType( - expect.extend({ - toBeDivisibleBy(actual: number, expected: number) { - expectType(this.isNot); - - const pass = actual % expected === 0; - const message = pass - ? () => - `expected ${this.utils.printReceived( - actual, - )} not to be divisible by ${expected}` - : () => - `expected ${this.utils.printReceived( - actual, - )} to be divisible by ${expected}`; - - return {message, pass}; - }, - }), -); - -// TODO: some way of calling `expect(4).toBeDivisbleBy(2)` and `expect.toBeDivisbleBy(2)` From 75e24f841faa644c6a9a957438265bba1917f183 Mon Sep 17 00:00:00 2001 From: mrazauskas <72159681+mrazauskas@users.noreply.github.com> Date: Mon, 11 Oct 2021 20:09:46 +0300 Subject: [PATCH 2/4] fix-types-test --- test-types/top-level-expect-namespace.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-types/top-level-expect-namespace.test.ts b/test-types/top-level-expect-namespace.test.ts index 69451c74f299..20193065d97c 100644 --- a/test-types/top-level-expect-namespace.test.ts +++ b/test-types/top-level-expect-namespace.test.ts @@ -104,7 +104,7 @@ expectError(expect(1).toBeLessThanOrEqual()); expectError(expect(1).toBeLessThanOrEqual('10')); expectType(expect(() => {}).toBeInstanceOf(Function)); -expectError(expect(() => {}).toBeInstanceOf('function')); +expectError(expect(() => {}).toBeInstanceOf()); expectType(expect(Number('ten')).toBeNaN()); expectError(expect(Number('10')).not.toBeNaN(true)); From 084ef2b90c04eb947b4745347a84ab52a309c836 Mon Sep 17 00:00:00 2001 From: mrazauskas <72159681+mrazauskas@users.noreply.github.com> Date: Mon, 11 Oct 2021 20:19:17 +0300 Subject: [PATCH 3/4] fix: remove unused eslint-disable --- packages/expect/src/types.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/expect/src/types.ts b/packages/expect/src/types.ts index 9a9c2524c476..53286977a83a 100644 --- a/packages/expect/src/types.ts +++ b/packages/expect/src/types.ts @@ -6,8 +6,6 @@ * */ -/* eslint-disable local/ban-types-eventually */ - import type {Config} from '@jest/types'; import type * as jestMatcherUtils from 'jest-matcher-utils'; import {INTERNAL_MATCHER_FLAG} from './jestMatchersObject'; From e5ff48ed6dff8c8c691e036113f9eaaf732f04c5 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Fri, 15 Oct 2021 12:03:40 +0200 Subject: [PATCH 4/4] Update CHANGELOG.md --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b706156bc4b3..758d2c412a16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,8 @@ ### Fixes -- `[jest-runtime]` Ensure absolute paths can be resolved within test modules ([11943](https://github.com/facebook/jest/pull/11943)) +- `[expect]` Tweak and improve types ([#11949](https://github.com/facebook/jest/pull/11949)) +- `[jest-runtime]` Ensure absolute paths can be resolved within test modules ([#11943](https://github.com/facebook/jest/pull/11943)) ### Chore & Maintenance