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/jestMatchersObject.ts b/packages/expect/src/jestMatchersObject.ts index 370802661c30..9043c8b559e6 100644 --- a/packages/expect/src/jestMatchersObject.ts +++ b/packages/expect/src/jestMatchersObject.ts @@ -98,15 +98,12 @@ 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: Array) => new CustomMatcher(false, sample), + }); + Object.defineProperty(expect.not, key, { + value: (...sample: Array) => new CustomMatcher(true, sample), + }); } }); diff --git a/packages/expect/src/types.ts b/packages/expect/src/types.ts index 691941f87b18..c6c8ce39d307 100644 --- a/packages/expect/src/types.ts +++ b/packages/expect/src/types.ts @@ -14,7 +14,7 @@ import {INTERNAL_MATCHER_FLAG} from './jestMatchersObject'; export type SyncExpectationResult = { pass: boolean; - message: () => string; + message?: () => string; }; export type AsyncExpectationResult = Promise; @@ -72,257 +72,429 @@ export type ExpectedAssertionsErrors = Array<{ expected: string; }>; -interface InverseAsymmetricMatchers { +interface AsymmetricMatchers { + /** + * Matches anything that was created with the given constructor. Use it inside + * `toEqual` or `toBeCalledWith` instead of a literal value. + * + * @example + * expect(mock).toBeCalledWith(expect.any(Number)); + */ + any(sample: unknown): AsymmetricMatcher; + /** + * Matches anything but `null` or `undefined`. Use it inside `toEqual` or + * `toBeCalledWith` instead of a literal value. + * + * @example + * expect(mock).toBeCalledWith(expect.anything()); + */ + 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 = { + /** + * The `expect` function is used every time you want to test a value. You will + * rarely call `expect` by itself. + */ (actual: T): Matchers; // TODO: this is added by test runners, not `expect` itself addSnapshotSerializer(serializer: unknown): void; + /** + * Verifies that a certain number of assertions are called during a test. + */ assertions(numberOfAssertions: number): void; // TODO: remove this `T extends` - should get from some interface merging extend(matchers: MatchersObject): void; extractExpectedAssertionsErrors: () => ExpectedAssertionsErrors; getState(): State; + /** + * Verifies that at least one assertion is called during a test. + */ 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 { - /** - * Ensures the last call to a mock function was provided specific args. - */ - lastCalledWith(...args: Array): R; - /** - * Ensure that the last call to a mock function has returned a specified value. - */ - lastReturnedWith(value: 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. - */ - nthCalledWith(nthCall: number, ...args: Array): R; - /** - * Ensure that the nth call to a mock function has returned a specified value. - */ - nthReturnedWith(n: number, value: 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. + * Unwraps the value of a fulfilled promise so any other matcher can be chained. + * If the promise is rejected the assertion fails. + * + * @example + * await expect(Promise.resolve('lemon')).resolves.toBe('lemon'); */ resolves: Matchers>; /** * Unwraps the reason of a rejected promise so any other matcher can be chained. * If the promise is fulfilled the assertion fails. + * + * @example + * await expect(Promise.reject(new Error('octopus'))).rejects.toThrow('octopus'); */ rejects: Matchers>; + + // EQUALITY AND RELATIONAL MATCHERS + /** - * Checks that a value is what you expect. It uses `===` to check strict equality. - * Don't use `toBe` with floating-point numbers. + * Compares primitive values or checks referential identity of object instances. + * It calls `Object.is` to compare values. + * + * **Note:** To compare floating point numbers you should use `.toBeCloseTo`. + * In JavaScript `0.2 + 0.1` is not strictly equal to `0.3`. */ toBe(expected: unknown): R; /** - * Ensures that a mock function is called. - */ - toBeCalled(): R; - /** - * Ensures that a mock function is called an exact number of times. - */ - toBeCalledTimes(expected: number): R; - /** - * Ensure that a mock function is called with specific arguments. - */ - toBeCalledWith(...args: 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. + * Compares floating point numbers for approximate equality. The default for + * `precision` is `2`. + * + * @example + * expect(0.2 + 0.1).toBeCloseTo(0.3, 5); */ - toBeCloseTo(expected: number, numDigits?: number): R; + toBeCloseTo(expected: number, precision?: number): R; /** - * Ensure that a variable is not undefined. + * Checks that the received value is not `undefined`. */ toBeDefined(): R; /** - * When you don't care what a value is, you just want to - * ensure a value is false in a boolean context. + * Ensures a value is false in a boolean context. In JavaScript, there are six + * falsy values: `false`, `0`, `''`, `null`, `undefined`, and `NaN`. + * + * @example + * expect(0).toBeFalsy(); + * expect(() => 'true').not.toBeFalsy(); */ toBeFalsy(): R; /** - * For comparing floating point numbers. + * Compares `received > expected`. */ toBeGreaterThan(expected: number | bigint): R; /** - * For comparing floating point numbers. + * Compares `received >= expected`. */ toBeGreaterThanOrEqual(expected: number | bigint): R; /** - * Ensure that an object is an instance of a class. - * This matcher uses `instanceof` underneath. - */ - toBeInstanceOf(expected: Function): R; - /** - * For comparing floating point numbers. + * Compares `received < expected`. */ toBeLessThan(expected: number | bigint): R; /** - * For comparing floating point numbers. + * Compares `received <= expected`. */ toBeLessThanOrEqual(expected: number | bigint): R; /** - * This is the same as `.toBe(null)` but the error messages are a bit nicer. - * So use `.toBeNull()` when you want to check that something is null. + * Uses `instanceof` to ensure that the received object is an instance of + * the expected class or function. + * + * @example + * expect(new A()).toBeInstanceOf(A); + * expect(() => {}).toBeInstanceOf(Function); + */ + toBeInstanceOf(expected: Function): R; + /** + * Checks that the received value is `NaN`. + */ + toBeNaN(): R; + /** + * Checks that the received value is `null`. */ toBeNull(): R; /** - * Use when you don't care what a value is, you just want to ensure a value - * is true in a boolean context. In JavaScript, there are six falsy values: - * `false`, `0`, `''`, `null`, `undefined`, and `NaN`. Everything else is truthy. + * Ensures that a value is `true` in a boolean context. In JavaScript the value + * is truthy if it is not `false`, `0`, `''`, `null`, `undefined`, and `NaN`. + * + * @example + * expect('true').toBeTruthy(); */ toBeTruthy(): R; /** - * Used to check that a variable is undefined. + * Checks that the received value is `undefined`. */ toBeUndefined(): R; /** - * Used to check that a variable is NaN. - */ - toBeNaN(): R; - /** - * Used when you want to check that an item is in a list. - * For testing the items in the list, this uses `===`, a strict equality check. + * Check whether the expected item is among the entries of the received array, + * or whether the expected string is a substring of the received string. + * + * @example + * expect(['lemon', 'lime', 'orange']).not.toContain('grapefruit'); + * expect('citrus fruits').toContain('fruit'); */ toContain(expected: unknown): R; /** - * Used when you want to check that an item is in a list. - * For testing the items in the list, this matcher recursively checks the - * equality of all fields, rather than checking for object identity. + * Checks whether an item with a specific structure and values is among the + * entries of the received array. This matcher recursively compares all fields, + * rather than checking for object identity. + * + * @example + * const favourite = {delicious: true, sour: false}; + * expect([{delicious: true, sour: false}]).toContainEqual(favourite); */ toContainEqual(expected: unknown): R; /** - * Used when you want to check that two objects have the same value. - * This matcher recursively checks the equality of all fields, rather than checking for object identity. + * Recursively compares all properties of object instances. It calls `Object.is` + * to compare primitive values. + * + * **Note:** Use `toStrictEqual` if you want to make sure that keys with + * undefined properties, array sparseness and object types are checked. + * + * @example + * const a = {key1: 'value', key2: undefined, key3: [, 1]}; + * const b = {key1: 'value', key3: [undefined, 1]}; + * + * expect(a).toEqual(b); + * expect(a).not.toBe(b); + * expect(a).not.toStrictEqual(b); */ toEqual(expected: unknown): R; /** - * Ensures that a mock function is called. + * Tests that object instances have the same types as well as structure. + * + * Differences from `.toEqual`: + * * Keys with undefined properties are checked, e.g. `{a: undefined, b: 2}` + * does not match `{b: 2}`; + * * Array sparseness is checked, e.g. `[, 1]` does not match `[undefined, 1]`; + * * Object types are checked to be equal, e.g. a class instance with fields + * `a` and `b` will not equal a literal object with fields `a` and `b`. + * + * @example + * class A { + * constructor(key) { + * this.key = key; + * } + * } + * + * expect(new A('value')).toEqual({key: 'value'}); + * expect(new A('value')).not.toStrictEqual({key: 'value'}); */ - toHaveBeenCalled(): R; + toStrictEqual(expected: unknown): R; /** - * Ensures that a mock function is called an exact number of times. + * Checks that an object has a `.length` property and it is set to a certain + * numeric value. + * + * @example + * expect([1, 2, 3]).toHaveLength(3); + * expect('abc').not.toHaveLength(5); */ - toHaveBeenCalledTimes(expected: number): R; + toHaveLength(expected: number): R; /** - * Ensure that a mock function is called with specific arguments. + * Checks if property at provided reference `expectedPath` exists for an object. + * For checking deeply nested properties in an object you may use dot notation or + * an array containing the `expectedPath` for deep references. + * + * You can provide an optional `expectedValue` argument to compare the received + * property value (recursively for all properties of object instances, also known + * as deep equality, like the `toEqual` matcher). + * + * @example + * expect({kitchen: {area: 20, color: 'white'}}).toHaveProperty('kitchen.area', 20); + * expect({kitchen: {area: 20}}).not.toHaveProperty(['kitchen', 'color']); */ - toHaveBeenCalledWith(...args: Array): R; + toHaveProperty( + expectedPath: string | Array, + expectedValue?: unknown, + ): R; /** - * Ensure that a mock function is called with specific arguments on an Nth call. + * Matches the received string and a regular expression, or checks whether the + * expected string is a substring of the received string. + * + * @example + * expect('grapefruits').toMatch(/fruit/); + * expect('grapefruits').toMatch('fruit'); */ - toHaveBeenNthCalledWith(nthCall: number, ...args: Array): R; + toMatch(expected: string | RegExp): R; /** - * If you have a mock function, you can use `.toHaveBeenLastCalledWith` - * to test what arguments it was last called with. + * Matches object instances by checking whether the received object includes + * all properties of the expected object. Similar to `toMatch`, this matcher will + * ignore properties which do not exist in the expected object. + * + * You may also pass an array of objects, in this case each object in the received + * array will be matched with the corresponding object in the expected array. + * + * @example + * expect({a: 1, b: 2}).toMatchObject({b: 2}); + * expect([{a: 1}, {b: 2, c: true}]).toMatchObject([{a: 1}, {b: 2}]); */ - toHaveBeenLastCalledWith(...args: Array): R; + toMatchObject(expected: object | Array): R; + + // MOCK MATCHERS + /** - * 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 - * no matter what value you provided as the expected return value. + * Ensures that a mock function got called. + * + * @example + * expect(mock).toBeCalled(); */ - toHaveLastReturnedWith(expected: unknown): R; + toBeCalled(): R; /** - * Used to check that an object has a `.length` property - * and it is set to a certain numeric value. + * Ensures that a mock function got called. + * Alias for `toBeCalled`. + * + * @example + * expect(mock).toHaveBeenCalled(); */ - toHaveLength(expected: number): R; + toHaveBeenCalled(): R; + /** + * Ensures that a mock function got called exact number of times. + * + * @example + * expect(mock).toBeCalledTimes(2); + */ + toBeCalledTimes(expected: number): R; + /** + * Ensures that a mock function got called exact number of times. + * Alias for `toBeCalledTimes`. + * + * @example + * expect(mock).toHaveBeenCalledTimes(2); + */ + toHaveBeenCalledTimes(expected: number): R; + /** + * Ensure that a mock function was called with specific arguments. + * + * @example + * expect(mock).toBeCalledWith('lemon', 'mango'); + */ + toBeCalledWith(...expected: [unknown, ...Array]): R; + /** + * Ensures that a mock function was called with specific arguments. + * Alias for `toBeCalledWith`. + * + * @example + * expect(mock).toHaveBeenCalledWith('lemon', 'mango'); + */ + toHaveBeenCalledWith(...expected: [unknown, ...Array]): R; + /** + * Ensures that a mock function was last called with specific arguments. + * + * @example + * expect(mock).lastCalledWith('mango'); + */ + lastCalledWith(...expected: [unknown, ...Array]): R; /** - * Use to test the specific value that a mock function returned for the nth call. - * 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. + * Ensures that a mock function was last called with specific arguments. + * Alias for `lastCalledWith`. + * + * @example + * expect(mock).toHaveBeenLastCalledWith('mango'); */ - toHaveNthReturnedWith(nthCall: number, expected: unknown): R; + toHaveBeenLastCalledWith(...expected: [unknown, ...Array]): 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 - * the keyPath for deep references. + * Ensure that a mock function was called with specific arguments on the nth call. * - * Optionally, you can provide a value to check if it's equal to the value present at keyPath - * on the target object. This matcher uses 'deep equality' (like `toEqual()`) and recursively checks - * the equality of all fields. + * @example + * expect(mock).nthCalledWith(2, 'octopus'); + */ + nthCalledWith(nth: number, ...expected: [unknown, ...Array]): R; + /** + * Ensures that a mock function was called with specific arguments on the nth call. + * Alias for `nthCalledWith`. * * @example + * expect(mock).toHaveBeenNthCalledWith(2, 'octopus'); + */ + toHaveBeenNthCalledWith( + nth: number, + ...expected: [unknown, ...Array] + ): R; + /** + * Ensures that a mock function successfully returned (i.e., did not throw an error) + * at least once. * - * expect(houseForSale).toHaveProperty('kitchen.area', 20); + * @example + * expect(mock).toReturn(); */ - toHaveProperty(keyPath: string | Array, value?: unknown): R; + toReturn(): R; /** - * Use to test that the mock function successfully returned (i.e., did not throw an error) at least one time + * Ensures that a mock function successfully returned (i.e., did not throw an error) + * at least once. Alias for `toReturn`. + * + * @example + * expect(mock).toHaveReturned(); */ toHaveReturned(): R; /** - * Use to ensure that a mock function returned successfully (i.e., did not throw an error) an exact number of times. - * Any calls to the mock function that throw an error are not counted toward the number of times the function returned. + * Ensures that a mock function returned successfully (i.e., did not throw an error) + * an exact number of times. + * + * @example + * expect(mock).toReturnTimes(2); */ - toHaveReturnedTimes(expected: number): R; + toReturnTimes(expected: number): R; /** - * Use to ensure that a mock function returned a specific value. + * Ensures that a mock function returned successfully (i.e., did not throw an error) + * an exact number of times. Alias for `toReturnTimes`. + * + * @example + * expect(mock).toHaveReturnedTimes(2); */ - toHaveReturnedWith(expected: unknown): R; + toHaveReturnedTimes(expected: number): R; /** - * Check that a string matches a regular expression. + * Ensures that a mock function has returned a specific value. + * + * @example + * expect(mock).toReturnWith('orange'); */ - toMatch(expected: string | RegExp): R; + toReturnWith(expected: unknown): R; /** - * Used to check that a JavaScript object matches a subset of the properties of an object + * Ensures that a mock function has returned a specific value. Alias for `toReturnWith`. + * + * @example + * expect(mock).toHaveReturnedTimes('orange'); */ - toMatchObject(expected: Record | Array): R; + toHaveReturnedWith(expected: unknown): R; /** - * Ensure that a mock function has returned (as opposed to thrown) at least once. + * Ensure that the last call to a mock function has returned a specified value. + * + * @example + * expect(mock).lastReturnedWith('orange'); */ - toReturn(): R; + lastReturnedWith(expected: unknown): R; /** - * Ensure that a mock function has returned (as opposed to thrown) a specified number of times. + * Ensure that the last call to a mock function has returned a specified value. + * Alias for `lastReturnedWith`. + * + * @example + * expect(mock).toHaveLastReturnedWith('orange'); */ - toReturnTimes(count: number): R; + toHaveLastReturnedWith(expected: unknown): R; /** - * Ensure that a mock function has returned a specified value at least once. + * Ensures that the nth call to a mock function has returned a specified value. + * + * @example + * expect(mock).nthReturnedWith(1, 'Lemon'); + * expect(mock).not.nthReturnedWith(2, 'Orange'); */ - toReturnWith(value: unknown): R; + nthReturnedWith(nth: number, expected: unknown): R; /** - * Use to test that objects have the same types as well as structure. + * Ensures that the nth call to a mock function has returned a specified value. + * Alias for `nthReturnedWith`. + * + * @example + * expect(mock).toHaveNthReturnedWith(1, 'Lemon'); + * expect(mock).not.toHaveNthReturnedWith(2, 'Orange'); */ - toStrictEqual(expected: unknown): R; + toHaveNthReturnedWith(nth: number, expected: unknown): R; + + // ERROR MATCHERS + /** - * Used to test that a function throws when it is called. + * Tests 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. + * Tests that a function throws when it is called. + * Alias for `toThrow()`. */ - toThrowError(error?: unknown): R; + toThrowError(expected?: unknown): R; + + // SNAPSHOT MATCHERS /* TODO: START snapshot matchers are not from `expect`, the types should not be here */ /** 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..43376aebc835 --- /dev/null +++ b/test-types/top-level-expect-namespace.test.ts @@ -0,0 +1,262 @@ +/** + * 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)); + +// 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)`