From fcb77867955a12a9d8ad3f5cb302270e58071775 Mon Sep 17 00:00:00 2001 From: Tom Mrazauskas Date: Sat, 28 May 2022 16:05:01 +0300 Subject: [PATCH 1/2] feat: infer each argument types from the tables --- docs/GlobalAPI.md | 107 ++- .../jest-types/__typetests__/each.test.ts | 667 ++++++++++++++++++ .../jest-types/__typetests__/globals.test.ts | 586 ++++++++------- packages/jest-types/src/Global.ts | 61 +- 4 files changed, 1113 insertions(+), 308 deletions(-) create mode 100644 packages/jest-types/__typetests__/each.test.ts diff --git a/docs/GlobalAPI.md b/docs/GlobalAPI.md index 42cdd9f9c872..ad68f1389c15 100644 --- a/docs/GlobalAPI.md +++ b/docs/GlobalAPI.md @@ -9,7 +9,7 @@ In your test files, Jest puts each of these methods and objects into the global import TOCInline from '@theme/TOCInline'; - + --- @@ -934,3 +934,108 @@ const add = (a, b) => a + b; test.todo('add should be associative'); ``` + +## TypeScript Usage + +:::info + +These TypeScript usage tips and caveats are only applicable if you import from `'@jest/globals'`: + +```ts +import {describe, test} from '@jest/globals'; +``` + +::: + +### `.each` + +The `.each` modifier offers few different ways to define a table of the test cases. Some of the APIs have caveats related with the type inference of the arguments which are passed to `describe` or `test` callback functions. Let's take a look at each of them. + +:::note + +For simplicity `test.each` is picked for the examples, but the type inference is identical in all cases where `.each` modifier can be used: `describe.each`, `test.concurrent.only.each`, `test.skip.each`, etc. + +::: + +#### Array of objects + +The array of objects API is most verbose, but it makes the type inference a painless task. A `table` can be inlined: + +```ts +test.each([ + {name: 'a', path: 'path/to/a', count: 1, write: true}, + {name: 'b', path: 'path/to/b', count: 3}, +])('inline table', ({name, path, count, write}) => { + // arguments are typed as expected, e.g. `write: boolean | undefined` +}); +``` + +Or declared separately as a variable: + +```ts +const table = [ + {a: 1, b: 2, expected: 'three', extra: true}, + {a: 3, b: 4, expected: 'seven', extra: false}, + {a: 5, b: 6, expected: 'eleven'}, +]; + +test.each(table)('table as a variable', ({a, b, expected, extra}) => { + // again everything is typed as expected, e.g. `extra: boolean | undefined` +}); +``` + +#### Array of arrays + +The array of arrays style will work smoothly with inlined tables: + +```ts +test.each([ + [1, 2, 'three', true], + [3, 4, 'seven', false], + [5, 6, 'eleven'], +])('inline table example', (a, b, expected, extra) => { + // arguments are typed as expected, e.g. `extra: boolean | undefined` +}); +``` + +However, if a table is declared as a separate variable, it must be typed as an array of tuples for correct type inference (this is not needed only if all elements of a row are of the same type): + +```ts +const table: Array<[number, number, string, boolean?]> = [ + [1, 2, 'three', true], + [3, 4, 'seven', false], + [5, 6, 'eleven'], +]; + +test.each(table)('table as a variable example', (a, b, expected, extra) => { + // without the annotation types are incorrect, e.g. `a: number | string | boolean` +}); +``` + +#### Template literal + +If all values are of the same type, the template literal API will type the arguments correctly: + +```ts +test.each` + a | b | expected + ${1} | ${2} | ${3} + ${3} | ${4} | ${7} + ${5} | ${6} | ${11} +`('template literal example', ({a, b, expected}) => { + // all arguments are of type `number` +}); +``` + +Otherwise it will require a generic type argument: + +```ts +test.each<{a: number; b: number; expected: string; extra?: boolean}>` + a | b | expected | extra + ${1} | ${2} | ${'three'} | ${true} + ${3} | ${4} | ${'seven'} | ${false} + ${5} | ${6} | ${'eleven'} +`('template literal example', ({a, b, expected, extra}) => { + // without the generic argument in this case types would default to `unknown` +}); +``` diff --git a/packages/jest-types/__typetests__/each.test.ts b/packages/jest-types/__typetests__/each.test.ts new file mode 100644 index 000000000000..147153b3b914 --- /dev/null +++ b/packages/jest-types/__typetests__/each.test.ts @@ -0,0 +1,667 @@ +/** + * 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. + */ + +import {expectError, expectType} from 'tsd-lite'; +import {describe, test} from '@jest/globals'; + +const list = [1, 2, 3]; +const tupleList: [number, number, string] = [1, 2, 'three']; +const table = [ + [1, 2, 'three'], + [3, 4, 'seven'], +]; +const tupleTable: Array<[number, number, string, boolean?]> = [ + [1, 2, 'three', true], + [3, 4, 'seven', false], + [5, 6, 'eleven'], +]; +const objectTable = [ + {a: 1, b: 2, expected: 'three', extra: true}, + {a: 3, b: 4, expected: 'seven', extra: false}, + {a: 5, b: 6, expected: 'eleven'}, +]; + +// test.each + +expectType( + test.each(list)('some test', (a, b, expected) => { + expectType(a); + expectType(b); + expectType(expected); + }), +); +expectType( + test.each(list)( + 'some test', + (a, b, expected) => { + expectType(a); + expectType(b); + expectType(expected); + }, + 1000, + ), +); + +expectType( + test.each(tupleList)('some test', (a, b, expected) => { + expectType(a); + expectType(b); + expectType(expected); + }), +); +expectType( + test.each(tupleList)( + 'some test', + (a, b, expected) => { + expectType(a); + expectType(b); + expectType(expected); + }, + 1000, + ), +); + +expectType( + test.each([3, 4, 'seven'])('some test', (a, b, expected) => { + expectType(a); + expectType(b); + expectType(expected); + }), +); +expectType( + test.each([3, 4, 'seven'])( + 'some test', + (a, b, expected) => { + expectType(a); + expectType(b); + expectType(expected); + }, + 1000, + ), +); + +expectType( + test.each(table)('some test', (a, b, expected) => { + expectType(a); + expectType(b); + expectType(expected); + }), +); +expectType( + test.each(table)( + 'some test', + (a, b, expected) => { + expectType(a); + expectType(b); + expectType(expected); + }, + 1000, + ), +); + +expectType( + test.each(tupleTable)('some test', (a, b, expected, extra) => { + expectType(a); + expectType(b); + expectType(expected); + expectType(extra); + }), +); +expectType( + test.each(tupleTable)( + 'some test', + (a, b, expected, extra) => { + expectType(a); + expectType(b); + expectType(expected); + expectType(extra); + }, + 1000, + ), +); + +expectType( + test.each([ + [1, 2, 'three'], + [3, 4, 'seven'], + ])('some test', (a, b, expected) => { + expectType(a); + expectType(b); + expectType(expected); + }), +); +expectType( + test.each([ + [1, 2, 'three'], + [3, 4, 'seven'], + ])( + 'some test', + (a, b, expected) => { + expectType(a); + expectType(b); + expectType(expected); + }, + 1000, + ), +); + +expectType( + test.each(objectTable)('some test', ({a, b, expected, extra}) => { + expectType(a); + expectType(b); + expectType(expected); + expectType(extra); + }), +); +expectType( + test.each([ + {a: 1, b: 2, expected: 'three', extra: true}, + {a: 3, b: 4, expected: 'seven', extra: false}, + {a: 5, b: 6, expected: 'eleven'}, + ])( + 'some test', + ({a, b, expected, extra}) => { + expectType(a); + expectType(b); + expectType(expected); + expectType(extra); + }, + 1000, + ), +); + +expectType( + test.each` + a | b | expected + ${1} | ${1} | ${2} + ${1} | ${2} | ${3} + ${2} | ${1} | ${3} + `('some test', ({a, b, expected}) => { + expectType(a); + expectType(b); + expectType(expected); + }), +); +expectType( + test.each` + item | expected + ${'a'} | ${true} + ${'b'} | ${false} + `('some test', ({item, expected}) => { + expectType(item); + expectType(expected); + }), +); +expectType( + test.each<{item: string; expected: boolean}>` + item | expected + ${'a'} | ${true} + ${'b'} | ${false} + `('some test', ({item, expected}) => { + expectType(item); + expectType(expected); + }), +); +expectType( + test.each` + a | b | expected + ${1} | ${1} | ${2} + ${1} | ${2} | ${3} + ${2} | ${1} | ${3} + `( + 'some test', + ({a, b, expected}) => { + expectType(a); + expectType(b); + expectType(expected); + }, + 1000, + ), +); +expectType( + test.each` + item | expected + ${'a'} | ${true} + ${'b'} | ${false} + `( + 'some test', + ({item, expected}) => { + expectType(item); + expectType(expected); + }, + 1000, + ), +); +expectType( + test.each<{item: string; expected: boolean}>` + item | expected + ${'a'} | ${true} + ${'b'} | ${false} + `( + 'some test', + ({item, expected}) => { + expectType(item); + expectType(expected); + }, + 1000, + ), +); + +expectError(test.each()); +expectError(test.each('abc')); +expectError(test.each(() => {})); + +expectType(test.only.each); +expectType(test.skip.each); + +// test.concurrent.each + +expectType( + test.concurrent.each(list)('some test', async (a, b, expected) => { + expectType(a); + expectType(b); + expectType(expected); + }), +); +expectType( + test.concurrent.each(list)( + 'some test', + async (a, b, expected) => { + expectType(a); + expectType(b); + expectType(expected); + }, + 1000, + ), +); + +expectType( + test.concurrent.each(tupleList)('some test', async (a, b, expected) => { + expectType(a); + expectType(b); + expectType(expected); + }), +); +expectType( + test.concurrent.each(tupleList)( + 'some test', + async (a, b, expected) => { + expectType(a); + expectType(b); + expectType(expected); + }, + 1000, + ), +); + +expectType( + test.concurrent.each([3, 4, 'seven'])('some test', async (a, b, expected) => { + expectType(a); + expectType(b); + expectType(expected); + }), +); +expectType( + test.concurrent.each([3, 4, 'seven'])( + 'some test', + async (a, b, expected) => { + expectType(a); + expectType(b); + expectType(expected); + }, + 1000, + ), +); + +expectType( + test.concurrent.each(table)('some test', async (a, b, expected) => { + expectType(a); + expectType(b); + expectType(expected); + }), +); +expectType( + test.concurrent.each(table)( + 'some test', + async (a, b, expected) => { + expectType(a); + expectType(b); + expectType(expected); + }, + 1000, + ), +); + +expectType( + test.concurrent.each(tupleTable)( + 'some test', + async (a, b, expected, extra) => { + expectType(a); + expectType(b); + expectType(expected); + expectType(extra); + }, + ), +); +expectType( + test.concurrent.each(tupleTable)( + 'some test', + async (a, b, expected, extra) => { + expectType(a); + expectType(b); + expectType(expected); + expectType(extra); + }, + 1000, + ), +); + +expectType( + test.concurrent.each` + a | b | expected + ${1} | ${1} | ${2} + ${1} | ${2} | ${3} + ${2} | ${1} | ${3} + `('some test', async ({a, b, expected}) => { + expectType(a); + expectType(b); + expectType(expected); + }), +); +expectType( + test.concurrent.each` + item | expected + ${'a'} | ${true} + ${'b'} | ${false} + `('some test', async ({item, expected}) => { + expectType(item); + expectType(expected); + }), +); +expectType( + test.concurrent.each<{item: string; expected: boolean}>` + item | expected + ${'a'} | ${true} + ${'b'} | ${false} + `('some test', async ({item, expected}) => { + expectType(item); + expectType(expected); + }), +); +expectType( + test.concurrent.each` + a | b | expected + ${1} | ${1} | ${2} + ${1} | ${2} | ${3} + ${2} | ${1} | ${3} + `( + 'some test', + async ({a, b, expected}) => { + expectType(a); + expectType(b); + expectType(expected); + }, + 1000, + ), +); + +expectType( + test.each` + item | expected + ${'a'} | ${true} + ${'b'} | ${false} + `( + 'some test', + ({item, expected}) => { + expectType(item); + expectType(expected); + }, + 1000, + ), +); +expectType( + test.each<{item: string; expected: boolean}>` + item | expected + ${'a'} | ${true} + ${'b'} | ${false} + `( + 'some test', + ({item, expected}) => { + expectType(item); + expectType(expected); + }, + 1000, + ), +); + +expectError(test.concurrent.each()); +expectError(test.concurrent.each('abc')); +expectError(test.concurrent.each(() => {})); + +expectType(test.concurrent.only.each); +expectType(test.concurrent.skip.each); + +// describe.each + +expectType( + describe.each(list)('describe each', (a, b, expected) => { + expectType(a); + expectType(b); + expectType(expected); + }), +); +expectType( + describe.each(list)( + 'describe each', + (a, b, expected) => { + expectType(a); + expectType(b); + expectType(expected); + }, + 1000, + ), +); + +expectType( + describe.each(tupleList)('describe each', (a, b, expected) => { + expectType(a); + expectType(b); + expectType(expected); + }), +); +expectType( + describe.each(tupleList)( + 'describe each', + (a, b, expected) => { + expectType(a); + expectType(b); + expectType(expected); + }, + 1000, + ), +); + +expectType( + describe.each([3, 4, 'seven'])('describe each', (a, b, expected) => { + expectType(a); + expectType(b); + expectType(expected); + }), +); +expectType( + describe.each([3, 4, 'seven'])( + 'describe each', + (a, b, expected) => { + expectType(a); + expectType(b); + expectType(expected); + }, + 1000, + ), +); + +expectType( + describe.each(table)('describe each', (a, b, expected) => { + expectType(a); + expectType(b); + expectType(expected); + }), +); +expectType( + describe.each(table)( + 'describe each', + (a, b, expected) => { + expectType(a); + expectType(b); + expectType(expected); + }, + 1000, + ), +); + +expectType( + describe.each(tupleTable)('describe each', (a, b, expected, extra) => { + expectType(a); + expectType(b); + expectType(expected); + expectType(extra); + }), +); +expectType( + describe.each(tupleTable)( + 'describe each', + (a, b, expected, extra) => { + expectType(a); + expectType(b); + expectType(expected); + expectType(extra); + }, + 1000, + ), +); + +expectType( + describe.each([ + [1, 2, 'three'], + [3, 4, 'seven'], + ])('describe each', (a, b, expected) => { + expectType(a); + expectType(b); + expectType(expected); + }), +); +expectType( + describe.each([ + [1, 2, 'three'], + [3, 4, 'seven'], + ])( + 'describe each', + (a, b, expected) => { + expectType(a); + expectType(b); + expectType(expected); + }, + 1000, + ), +); + +expectType( + describe.each(objectTable)('describe each', ({a, b, expected, extra}) => { + expectType(a); + expectType(b); + expectType(expected); + expectType(extra); + }), +); +expectType( + describe.each([ + {a: 1, b: 2, expected: 'three', extra: true}, + {a: 3, b: 4, expected: 'seven', extra: false}, + {a: 5, b: 6, expected: 'eleven'}, + ])( + 'describe each', + ({a, b, expected, extra}) => { + expectType(a); + expectType(b); + expectType(expected); + expectType(extra); + }, + 1000, + ), +); + +expectType( + describe.each` + a | b | expected + ${1} | ${1} | ${2} + ${1} | ${2} | ${3} + ${2} | ${1} | ${3} + `('describe each', ({a, b, expected}) => { + expectType(a); + expectType(b); + expectType(expected); + }), +); +expectType( + describe.each<{ + a: number; + b: number; + expected: string; + }>` + a | b | expected + ${1} | ${1} | ${2} + ${1} | ${2} | ${3} + ${2} | ${1} | ${3} + `('describe each', ({a, b, expected}) => { + expectType(a); + expectType(b); + expectType(expected); + }), +); +expectType( + describe.each` + a | b | expected + ${1} | ${1} | ${2} + ${1} | ${2} | ${3} + ${2} | ${1} | ${3} + `( + 'describe each', + ({a, b, expected}) => { + expectType(a); + expectType(b); + expectType(expected); + }, + 1000, + ), +); +expectType( + describe.each<{ + a: number; + b: number; + expected: string; + }>` + a | b | expected + ${1} | ${1} | ${2} + ${1} | ${2} | ${3} + ${2} | ${1} | ${3} + `( + 'describe each', + ({a, b, expected}) => { + expectType(a); + expectType(b); + expectType(expected); + }, + 1000, + ), +); + +expectError(describe.each()); +expectError(describe.each('abc')); +expectError(describe.each(() => {})); + +expectType(describe.only.each); +expectType(describe.skip.each); diff --git a/packages/jest-types/__typetests__/globals.test.ts b/packages/jest-types/__typetests__/globals.test.ts index e4bbd6fb6c6b..24fd9db72efb 100644 --- a/packages/jest-types/__typetests__/globals.test.ts +++ b/packages/jest-types/__typetests__/globals.test.ts @@ -17,55 +17,198 @@ import { import type {Global} from '@jest/types'; const fn = () => {}; -const doneFn: Global.DoneTakingTestFn = done => { - done(); -}; const asyncFn = async () => {}; const genFn = function* () {}; -const timeout = 5; -const testName = 'Test name'; -const list = [1, 2, 3]; const table = [ - [1, 2], - [3, 4], + [1, 2, 3], + [4, 5, 6], ]; -const readonlyTable = [[1, 2], 'one'] as const; -// https://jestjs.io/docs/api#methods -expectType(afterAll(fn)); -expectType(afterAll(asyncFn)); -expectType(afterAll(genFn)); -expectType(afterAll(fn, timeout)); -expectType(afterAll(asyncFn, timeout)); -expectType(afterAll(genFn, timeout)); -expectType(afterEach(fn)); -expectType(afterEach(asyncFn)); -expectType(afterEach(genFn)); -expectType(afterEach(fn, timeout)); -expectType(afterEach(asyncFn, timeout)); -expectType(afterEach(genFn, timeout)); +const testName = 'Test name'; +const timeout = 5; + +// done + +test(testName, done => { + done(); +}); + +test(testName, done => { + done('error message'); +}); + +test(testName, done => { + done(new Error('message')); +}); + +test(testName, done => { + expectType(done); +}); + +test(testName, done => { + expectError(done(123)); +}); + +// beforeAll + expectType(beforeAll(fn)); expectType(beforeAll(asyncFn)); expectType(beforeAll(genFn)); +expectType( + beforeAll(done => { + expectType(done); + }), +); + expectType(beforeAll(fn, timeout)); expectType(beforeAll(asyncFn, timeout)); expectType(beforeAll(genFn, timeout)); +expectType( + beforeAll(done => { + expectType(done); + }, timeout), +); + +expectError(beforeAll()); +expectError(beforeAll('abc')); + +expectError( + beforeAll(async done => { + done(); + }), +); +expectError( + beforeAll(function* (done) { + done(); + }), +); + +// beforeEach + expectType(beforeEach(fn)); expectType(beforeEach(asyncFn)); expectType(beforeEach(genFn)); +expectType( + beforeEach(done => { + expectType(done); + }), +); + expectType(beforeEach(fn, timeout)); expectType(beforeEach(asyncFn, timeout)); expectType(beforeEach(genFn, timeout)); +expectType( + beforeEach(done => { + expectType(done); + }, timeout), +); + +expectError(beforeEach()); +expectError(beforeEach('abc')); + +expectError( + beforeEach(async done => { + done(); + }), +); +expectError( + beforeEach(function* (done) { + done(); + }), +); + +// afterAll + +expectType(afterAll(fn)); +expectType(afterAll(asyncFn)); +expectType(afterAll(genFn)); +expectType( + afterAll(done => { + expectType(done); + }), +); + +expectType(afterAll(fn, timeout)); +expectType(afterAll(asyncFn, timeout)); +expectType(afterAll(genFn, timeout)); +expectType( + afterAll(done => { + expectType(done); + }, timeout), +); + +expectError(afterAll()); +expectError(afterAll('abc')); + +expectError( + afterAll(async done => { + done(); + }), +); +expectError( + afterAll(function* (done) { + done(); + }), +); + +// afterEach + +expectType(afterEach(fn)); +expectType(afterEach(asyncFn)); +expectType(afterEach(genFn)); +expectType( + afterEach(done => { + expectType(done); + }), +); + +expectType(afterEach(fn, timeout)); +expectType(afterEach(asyncFn, timeout)); +expectType(afterEach(genFn, timeout)); +expectType( + afterEach(done => { + expectType(done); + }, timeout), +); + +expectError(afterEach()); +expectError(afterEach('abc')); + +expectError( + afterEach(async done => { + done(); + }), +); +expectError( + afterEach(function* (done) { + done(); + }), +); + +// test expectType(test(testName, fn)); expectType(test(testName, asyncFn)); -expectType(test(testName, doneFn)); expectType(test(testName, genFn)); +expectType( + test(testName, done => { + expectType(done); + }), +); + expectType(test(testName, fn, timeout)); expectType(test(testName, asyncFn, timeout)); -expectType(test(testName, doneFn, timeout)); expectType(test(testName, genFn, timeout)); +expectType( + test( + testName, + done => { + expectType(done); + }, + timeout, + ), +); expectType(test(123, fn)); expectType(test(() => {}, fn)); @@ -73,14 +216,11 @@ expectType(test(function named() {}, fn)); expectType(test(class {}, fn)); expectType(test(class Named {}, fn)); -// wrong arguments +expectError(test()); expectError(test(testName)); expectError(test(testName, timeout)); - -// wrong return value expectError(test(testName, () => 42)); -// mixing done callback and promise/generator expectError( test(testName, async done => { done(); @@ -92,66 +232,36 @@ expectError( }), ); -expectType(test(testName, fn)); -expectType(test(testName, asyncFn)); -expectType(test(testName, genFn)); +// test.concurrent -expectType(test.each(list)(testName, fn)); -expectType(test.each(list)(testName, fn, timeout)); -expectType(test.each(table)(testName, fn)); -expectType(test.each(table)(testName, fn, timeout)); -expectType(test.each(readonlyTable)(testName, fn)); -expectType(test.each(readonlyTable)(testName, fn, timeout)); - -expectType(test.each(list)(123, fn)); -expectType(test.each(list)(() => {}, fn)); -expectType(test.each(list)(function named() {}, fn)); -expectType(test.each(list)(class {}, fn)); -expectType(test.each(list)(class Named {}, fn)); - -expectType(test.only.each(list)(testName, fn)); -expectType(test.only.each(list)(testName, fn, timeout)); -expectType(test.only.each(table)(testName, fn)); -expectType(test.only.each(table)(testName, fn, timeout)); -expectType(test.only.each(readonlyTable)(testName, fn)); -expectType(test.only.each(readonlyTable)(testName, fn, timeout)); - -expectType(test.skip.each(list)(testName, fn)); -expectType(test.skip.each(list)(testName, fn, timeout)); -expectType(test.skip.each(table)(testName, fn)); -expectType(test.skip.each(table)(testName, fn, timeout)); -expectType(test.skip.each(readonlyTable)(testName, fn)); -expectType(test.skip.each(readonlyTable)(testName, fn, timeout)); +expectType(test.concurrent(testName, asyncFn)); +expectType(test.concurrent(testName, asyncFn, timeout)); -expectType(test.skip(123, fn)); -expectType(test.skip(() => {}, fn)); -expectType(test.skip(function named() {}, fn)); -expectType(test.skip(class {}, fn)); -expectType(test.skip(class Named {}, fn)); +expectType(test.concurrent(123, asyncFn)); +expectType(test.concurrent(() => {}, asyncFn)); +expectType(test.concurrent(function named() {}, asyncFn)); +expectType(test.concurrent(class {}, asyncFn)); +expectType(test.concurrent(class Named {}, asyncFn)); -expectType(test.skip.each(list)(123, fn)); -expectType(test.skip.each(list)(() => {}, fn)); -expectType(test.skip.each(list)(function named() {}, fn)); -expectType(test.skip.each(list)(class {}, fn)); -expectType(test.skip.each(list)(class Named {}, fn)); +expectError(test.concurrent(testName, fn)); -expectType(test.failing(123, fn)); -expectType(test.failing(() => {}, fn)); -expectType(test.failing(function named() {}, fn)); -expectType(test.failing(class {}, fn)); -expectType(test.failing(class Named {}, fn)); +// test.concurrent.each + +expectType(test.concurrent.each(table)(testName, asyncFn)); +expectType(test.concurrent.each(table)(testName, asyncFn, timeout)); -expectType(test.skip.failing(123, fn)); -expectType(test.skip.failing(() => {}, fn)); -expectType(test.skip.failing(function named() {}, fn)); -expectType(test.skip.failing(class {}, fn)); -expectType(test.skip.failing(class Named {}, fn)); +expectType(test.concurrent.each(table)(123, asyncFn)); +expectType(test.concurrent.each(table)(() => {}, asyncFn)); +expectType(test.concurrent.each(table)(function named() {}, asyncFn)); +expectType(test.concurrent.each(table)(class {}, asyncFn)); +expectType(test.concurrent.each(table)(class Named {}, asyncFn)); -expectType(test.only.failing(123, fn)); -expectType(test.only.failing(() => {}, fn)); -expectType(test.only.failing(function named() {}, fn)); -expectType(test.only.failing(class {}, fn)); -expectType(test.only.failing(class Named {}, fn)); +expectError(test.concurrent.each(table)(testName, fn)); + +// test.concurrent.failing + +expectType(test.concurrent.failing(testName, asyncFn)); +expectType(test.concurrent.failing(testName, asyncFn, timeout)); expectType(test.concurrent.failing(123, asyncFn)); expectType(test.concurrent.failing(() => {}, asyncFn)); @@ -159,252 +269,132 @@ expectType(test.concurrent.failing(function named() {}, asyncFn)); expectType(test.concurrent.failing(class {}, asyncFn)); expectType(test.concurrent.failing(class Named {}, asyncFn)); -expectType(test.concurrent.skip.failing(123, asyncFn)); -expectType(test.concurrent.skip.failing(() => {}, asyncFn)); -expectType(test.concurrent.skip.failing(function named() {}, asyncFn)); -expectType(test.concurrent.skip.failing(class {}, asyncFn)); -expectType(test.concurrent.skip.failing(class Named {}, asyncFn)); +expectError(test.concurrent.failing(testName, fn)); -expectType(test.concurrent.only.failing(123, asyncFn)); -expectType(test.concurrent.only.failing(() => {}, asyncFn)); -expectType(test.concurrent.only.failing(function named() {}, asyncFn)); -expectType(test.concurrent.only.failing(class {}, asyncFn)); -expectType(test.concurrent.only.failing(class Named {}, asyncFn)); +// test.concurrent.only -expectType( - test.each` - a | b | expected - ${1} | ${1} | ${2} - ${1} | ${2} | ${3} - ${2} | ${1} | ${3} - `(testName, fn), -); +expectType(test.concurrent.only.each); +expectType(test.concurrent.only.failing); -expectType( - test.each` - a | b | expected - ${1} | ${1} | ${2} - ${1} | ${2} | ${3} - ${2} | ${1} | ${3} - `(testName, fn, timeout), -); +// test.concurrent.skip -expectType( - test.only.each` - a | b | expected - ${1} | ${1} | ${2} - ${1} | ${2} | ${3} - ${2} | ${1} | ${3} - `(testName, fn), -); +expectType(test.concurrent.skip.each); +expectType(test.concurrent.skip.failing); -expectType( - test.only.each` - a | b | expected - ${1} | ${1} | ${2} - ${1} | ${2} | ${3} - ${2} | ${1} | ${3} - `(testName, fn, timeout), -); +// test.each -expectType( - test.skip.each` - a | b | expected - ${1} | ${1} | ${2} - ${1} | ${2} | ${3} - ${2} | ${1} | ${3} - `(testName, fn), -); +expectType(test.each(table)(testName, fn)); +expectType(test.each(table)(testName, fn, timeout)); -expectType( - test.skip.each` - a | b | expected - ${1} | ${1} | ${2} - ${1} | ${2} | ${3} - ${2} | ${1} | ${3} - `(testName, fn, timeout), -); +expectType(test.each(table)(123, fn)); +expectType(test.each(table)(() => {}, fn)); +expectType(test.each(table)(function named() {}, fn)); +expectType(test.each(table)(class {}, fn)); +expectType(test.each(table)(class Named {}, fn)); -expectType(test.concurrent(testName, asyncFn)); -expectType(test.concurrent(testName, asyncFn, timeout)); +// test.failing -expectType(test.concurrent.each(list)(testName, asyncFn)); -expectType(test.concurrent.each(list)(testName, asyncFn, timeout)); -expectType(test.concurrent.each(table)(testName, asyncFn)); -expectType(test.concurrent.each(table)(testName, asyncFn, timeout)); -expectType(test.concurrent.each(readonlyTable)(testName, asyncFn)); -expectType( - test.concurrent.each(readonlyTable)(testName, asyncFn, timeout), -); +expectType(test.failing(testName, fn)); +expectType(test.failing(testName, fn, timeout)); -expectType(test.concurrent.only.each(list)(testName, asyncFn)); -expectType(test.concurrent.only.each(list)(testName, asyncFn, timeout)); -expectType(test.concurrent.only.each(table)(testName, asyncFn)); -expectType(test.concurrent.only.each(table)(testName, asyncFn, timeout)); -expectType(test.concurrent.only.each(readonlyTable)(testName, asyncFn)); -expectType( - test.concurrent.only.each(readonlyTable)(testName, asyncFn, timeout), -); +expectType(test.failing(123, fn)); +expectType(test.failing(() => {}, fn)); +expectType(test.failing(function named() {}, fn)); +expectType(test.failing(class {}, fn)); +expectType(test.failing(class Named {}, fn)); -expectType(test.concurrent.skip.each(list)(testName, asyncFn)); -expectType(test.concurrent.skip.each(list)(testName, asyncFn, timeout)); -expectType(test.concurrent.skip.each(table)(testName, asyncFn)); -expectType(test.concurrent.skip.each(table)(testName, asyncFn, timeout)); -expectType(test.concurrent.skip.each(readonlyTable)(testName, asyncFn)); -expectType( - test.concurrent.skip.each(readonlyTable)(testName, asyncFn, timeout), -); +// test.only -expectType( - test.concurrent.each` - a | b | expected - ${1} | ${1} | ${2} - ${1} | ${2} | ${3} - ${2} | ${1} | ${3} -`(testName, asyncFn), -); +expectType(test.only(testName, fn)); +expectType(test.only(testName, fn, timeout)); -expectType( - test.concurrent.each` - a | b | expected - ${1} | ${1} | ${2} - ${1} | ${2} | ${3} - ${2} | ${1} | ${3} -`(testName, asyncFn, timeout), -); +expectType(test.only(123, fn)); +expectType(test.only(() => {}, fn)); +expectType(test.only(function named() {}, fn)); +expectType(test.only(class {}, fn)); +expectType(test.only(class Named {}, fn)); -expectType( - test.concurrent.only.each` - a | b | expected - ${1} | ${1} | ${2} - ${1} | ${2} | ${3} - ${2} | ${1} | ${3} -`(testName, asyncFn), -); +expectType(test.only.each); +expectType(test.only.failing); -expectType( - test.concurrent.only.each` - a | b | expected - ${1} | ${1} | ${2} - ${1} | ${2} | ${3} - ${2} | ${1} | ${3} -`(testName, asyncFn, timeout), -); +// test.skip -expectType( - test.concurrent.skip.each` - a | b | expected - ${1} | ${1} | ${2} - ${1} | ${2} | ${3} - ${2} | ${1} | ${3} -`(testName, asyncFn), -); +expectType(test.skip(testName, fn)); +expectType(test.skip(testName, fn, timeout)); -expectType( - test.concurrent.skip.each` - a | b | expected - ${1} | ${1} | ${2} - ${1} | ${2} | ${3} - ${2} | ${1} | ${3} -`(testName, asyncFn, timeout), -); +expectType(test.skip(123, fn)); +expectType(test.skip(() => {}, fn)); +expectType(test.skip(function named() {}, fn)); +expectType(test.skip(class {}, fn)); +expectType(test.skip(class Named {}, fn)); + +expectType(test.skip.each); +expectType(test.skip.failing); + +// test.todo + +expectType(test.todo(testName)); + +expectType(test.todo(123)); +expectType(test.todo(() => {})); +expectType(test.todo(function named() {})); +expectType(test.todo(class {})); +expectType(test.todo(class Named {})); + +expectError(test.todo()); +expectError(test.todo(testName, fn)); + +// describe expectType(describe(testName, fn)); + +expectError(describe()); +expectError(describe(fn)); +expectError(describe(testName, fn, timeout)); + expectType(describe(123, fn)); expectType(describe(() => {}, fn)); expectType(describe(function named() {}, fn)); expectType(describe(class {}, fn)); expectType(describe(class Named {}, fn)); -expectType(describe.each(list)(testName, fn)); -expectType(describe.each(list)(testName, fn, timeout)); expectType(describe.each(table)(testName, fn)); expectType(describe.each(table)(testName, fn, timeout)); -expectType(describe.each(readonlyTable)(testName, fn)); -expectType(describe.each(readonlyTable)(testName, fn, timeout)); - -expectType(describe.each(list)(testName, fn)); -expectType(describe.each(list)(123, fn)); -expectType(describe.each(list)(() => {}, fn)); -expectType(describe.each(list)(function named() {}, fn)); -expectType(describe.each(list)(class Named {}, fn)); - -expectType(describe.only.each(list)(testName, fn)); -expectType(describe.only.each(list)(testName, fn, timeout)); -expectType(describe.only.each(table)(testName, fn)); -expectType(describe.only.each(table)(testName, fn, timeout)); -expectType(describe.only.each(readonlyTable)(testName, fn)); -expectType(describe.only.each(readonlyTable)(testName, fn, timeout)); - -expectType(describe.only.each(list)(testName, fn)); -expectType(describe.only.each(list)(123, fn)); -expectType(describe.only.each(list)(() => {}, fn)); -expectType(describe.only.each(list)(function named() {}, fn)); -expectType(describe.only.each(list)(class Named {}, fn)); - -expectType(describe.skip.each(list)(testName, fn)); -expectType(describe.skip.each(list)(testName, fn, timeout)); -expectType(describe.skip.each(table)(testName, fn)); -expectType(describe.skip.each(table)(testName, fn, timeout)); -expectType(describe.skip.each(readonlyTable)(testName, fn)); -expectType(describe.skip.each(readonlyTable)(testName, fn, timeout)); - -expectType(describe.skip.each(list)(testName, fn)); -expectType(describe.skip.each(list)(123, fn)); -expectType(describe.skip.each(list)(() => {}, fn)); -expectType(describe.skip.each(list)(function named() {}, fn)); -expectType(describe.skip.each(list)(class Named {}, fn)); -expectType( - describe.each` - a | b | expected - ${1} | ${1} | ${2} - ${1} | ${2} | ${3} - ${2} | ${1} | ${3} - `(testName, fn), -); +expectType(describe.each(table)(testName, fn)); +expectType(describe.each(table)(123, fn)); +expectType(describe.each(table)(() => {}, fn)); +expectType(describe.each(table)(function named() {}, fn)); +expectType(describe.each(table)(class Named {}, fn)); -expectType( - describe.each` - a | b | expected - ${1} | ${1} | ${2} - ${1} | ${2} | ${3} - ${2} | ${1} | ${3} - `(testName, fn, timeout), -); +// describe.only -expectType( - describe.only.each` - a | b | expected - ${1} | ${1} | ${2} - ${1} | ${2} | ${3} - ${2} | ${1} | ${3} - `(testName, fn), -); +expectType(describe.only(testName, fn)); -expectType( - describe.only.each` - a | b | expected - ${1} | ${1} | ${2} - ${1} | ${2} | ${3} - ${2} | ${1} | ${3} - `(testName, fn, timeout), -); +expectError(describe.only()); +expectError(describe.only(fn)); +expectError(describe.only(testName, fn, timeout)); -expectType( - describe.skip.each` - a | b | expected - ${1} | ${1} | ${2} - ${1} | ${2} | ${3} - ${2} | ${1} | ${3} - `(testName, fn), -); +expectType(describe.only(123, fn)); +expectType(describe.only(() => {}, fn)); +expectType(describe.only(function named() {}, fn)); +expectType(describe.only(class {}, fn)); +expectType(describe.only(class Named {}, fn)); -expectType( - describe.skip.each` - a | b | expected - ${1} | ${1} | ${2} - ${1} | ${2} | ${3} - ${2} | ${1} | ${3} - `(testName, fn, timeout), -); +expectType(describe.only.each); + +// describe.skip + +expectType(describe.skip(testName, fn)); + +expectError(describe.skip()); +expectError(describe.skip(fn)); +expectError(describe.skip(testName, fn, timeout)); + +expectType(describe.skip(123, fn)); +expectType(describe.skip(() => {}, fn)); +expectType(describe.skip(function named() {}, fn)); +expectType(describe.skip(class {}, fn)); +expectType(describe.skip(class Named {}, fn)); + +expectType(describe.skip.each); diff --git a/packages/jest-types/src/Global.ts b/packages/jest-types/src/Global.ts index aa17a4c8f851..d21bcf62782f 100644 --- a/packages/jest-types/src/Global.ts +++ b/packages/jest-types/src/Global.ts @@ -55,12 +55,55 @@ export type EachTestFn = ( ...args: ReadonlyArray ) => ReturnType; -type Each = - | (( - table: EachTable, - ...taggedTemplateData: TemplateData - ) => (name: Name, test: EachTestFn, timeout?: number) => void) - | (() => () => void); +interface Each { + >(table: ReadonlyArray): ( + name: string | NameLike, + fn: (arg: T) => ReturnType, + timeout?: number, + ) => void; + + ]>(table: ReadonlyArray): ( + name: string | NameLike, + fn: (...args: T) => ReturnType, + timeout?: number, + ) => void; + + ]>(table: T): ( + name: string | NameLike, + fn: (...args: T) => ReturnType, + timeout?: number, + ) => void; + + >(table: ReadonlyArray): ( + name: string | NameLike, + fn: (...args: T) => ReturnType, + timeout?: number, + ) => void; + + >(table: T): ( + name: string | NameLike, + fn: (...args: T) => ReturnType, + timeout?: number, + ) => void; + + ( + strings: TemplateStringsArray, + ...expressions: Array + ): ( + name: string | NameLike, + fn: (arg: Record) => ReturnType, + timeout?: number, + ) => void; + + >( + strings: TemplateStringsArray, + ...expressions: Array + ): ( + name: string | NameLike, + fn: (arg: T) => ReturnType, + timeout?: number, + ) => void; +} export interface HookBase { (fn: HookFn, timeout?: number): void; @@ -68,7 +111,7 @@ export interface HookBase { export interface ItBase { (testName: TestNameLike, fn: TestFn, timeout?: number): void; - each: Each; + each: Each; failing(testName: TestNameLike, fn: TestFn, timeout?: number): void; } @@ -80,7 +123,7 @@ export interface It extends ItBase { export interface ItConcurrentBase { (testName: TestNameLike, testFn: ConcurrentTestFn, timeout?: number): void; - each: Each; + each: Each; failing(testName: TestNameLike, fn: ConcurrentTestFn, timeout?: number): void; } @@ -95,7 +138,7 @@ export interface ItConcurrent extends It { export interface DescribeBase { (blockName: BlockNameLike, blockFn: BlockFn): void; - each: Each; + each: Each; } export interface Describe extends DescribeBase { From 6a5939feeb96f73c1e651887ba43dd65f5366656 Mon Sep 17 00:00:00 2001 From: Tom Mrazauskas Date: Sat, 28 May 2022 16:38:31 +0300 Subject: [PATCH 2/2] add changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f5e645f4dfb..dbea73a75fcd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - `[jest]` Expose `Config` type ([#12848](https://github.com/facebook/jest/pull/12848)) - `[@jest/reporters]` Improve `GitHubActionsReporter`s annotation format ([#12826](https://github.com/facebook/jest/pull/12826)) +- `[@jest/types]` Infer argument types passed to `test` and `describe` callback functions from `each` tables ([#12885](https://github.com/facebook/jest/pull/12885)) ### Fixes