From 94c3360fc93a3269185f007adeac7762569bece7 Mon Sep 17 00:00:00 2001 From: Tom Mrazauskas Date: Tue, 4 Oct 2022 16:27:19 +0300 Subject: [PATCH 1/8] chore: typecheck `expect` tests --- package.json | 2 +- .../__snapshots__/spyMatchers.test.ts.snap | 92 +++ .../matchers-toContain.property.test.ts | 2 +- .../matchers-toContainEqual.property.test.ts | 2 +- .../matchers-toEqual.property.test.ts | 2 +- .../matchers-toStrictEqual.property.test.ts | 2 +- .../expect/src/__tests__/spyMatchers.test.ts | 678 ++++++++++++------ .../src/__tests__/symbolInObjects.test.ts | 2 + .../expect/src/__tests__/toEqual-dom.test.ts | 2 + tsconfig.test.json | 1 + 10 files changed, 569 insertions(+), 216 deletions(-) diff --git a/package.json b/package.json index 9867276e7f2c..46a3b794a083 100644 --- a/package.json +++ b/package.json @@ -113,7 +113,7 @@ "test": "yarn lint && yarn jest", "typecheck": "yarn typecheck:examples && yarn typecheck:tests", "typecheck:examples": "tsc -p examples/angular --noEmit && tsc -p examples/expect-extend --noEmit && tsc -p examples/typescript --noEmit", - "typecheck:tests": "tsc -b packages/{babel-jest,babel-plugin-jest-hoist,diff-sequences}/src/__tests__", + "typecheck:tests": "tsc -b packages/{babel-jest,babel-plugin-jest-hoist,diff-sequences,expect}/src/__tests__", "verify-old-ts": "node ./scripts/verifyOldTs.mjs", "verify-pnp": "node ./scripts/verifyPnP.mjs", "watch": "yarn build:js && node ./scripts/watch.mjs", diff --git a/packages/expect/src/__tests__/__snapshots__/spyMatchers.test.ts.snap b/packages/expect/src/__tests__/__snapshots__/spyMatchers.test.ts.snap index 5a0a44b6a556..0bc25e7bfdef 100644 --- a/packages/expect/src/__tests__/__snapshots__/spyMatchers.test.ts.snap +++ b/packages/expect/src/__tests__/__snapshots__/spyMatchers.test.ts.snap @@ -242,6 +242,29 @@ Received Number of returns: 3 `; +exports[`lastReturnedWith returnedWith incomplete recursive calls are handled properly 1`] = ` +expect(jest.fn()).lastReturnedWith(expected) + +Expected: undefined +Received + 3: function call has not returned yet +-> 4: function call has not returned yet + +Number of returns: 0 +Number of calls: 4 +`; + +exports[`lastReturnedWith returnedWith works with more calls than the limit 1`] = ` +expect(jest.fn()).lastReturnedWith(expected) + +Expected: "bar" +Received + 5: "foo5" +-> 6: "foo6" + +Number of returns: 6 +`; + exports[`lastReturnedWith works only on spies or jest.fn 1`] = ` expect(received).lastReturnedWith(expected) @@ -2060,6 +2083,29 @@ Received Number of returns: 3 `; +exports[`toHaveLastReturnedWith returnedWith incomplete recursive calls are handled properly 1`] = ` +expect(jest.fn()).toHaveLastReturnedWith(expected) + +Expected: undefined +Received + 3: function call has not returned yet +-> 4: function call has not returned yet + +Number of returns: 0 +Number of calls: 4 +`; + +exports[`toHaveLastReturnedWith returnedWith works with more calls than the limit 1`] = ` +expect(jest.fn()).toHaveLastReturnedWith(expected) + +Expected: "bar" +Received + 5: "foo5" +-> 6: "foo6" + +Number of returns: 6 +`; + exports[`toHaveLastReturnedWith works only on spies or jest.fn 1`] = ` expect(received).toHaveLastReturnedWith(expected) @@ -2720,6 +2766,29 @@ Expected: "foo" Number of returns: 0 `; +exports[`toHaveReturnedWith lastReturnedWith incomplete recursive calls are handled properly 1`] = ` +expect(jest.fn()).toHaveReturnedWith(expected) + +Expected: 0 +Received + 1: function call has not returned yet + 2: function call has not returned yet + 3: function call has not returned yet + +Number of returns: 0 +Number of calls: 4 +`; + +exports[`toHaveReturnedWith lastReturnedWith works with three calls 1`] = ` +expect(jest.fn()).not.toHaveReturnedWith(expected) + +Expected: not "foo3" +Received + 3: "foo3" + +Number of returns: 3 +`; + exports[`toHaveReturnedWith returnedWith incomplete recursive calls are handled properly 1`] = ` expect(jest.fn()).toHaveReturnedWith(expected) @@ -3145,6 +3214,29 @@ Expected: "foo" Number of returns: 0 `; +exports[`toReturnWith lastReturnedWith incomplete recursive calls are handled properly 1`] = ` +expect(jest.fn()).toReturnWith(expected) + +Expected: 0 +Received + 1: function call has not returned yet + 2: function call has not returned yet + 3: function call has not returned yet + +Number of returns: 0 +Number of calls: 4 +`; + +exports[`toReturnWith lastReturnedWith works with three calls 1`] = ` +expect(jest.fn()).not.toReturnWith(expected) + +Expected: not "foo3" +Received + 3: "foo3" + +Number of returns: 3 +`; + exports[`toReturnWith returnedWith incomplete recursive calls are handled properly 1`] = ` expect(jest.fn()).toReturnWith(expected) diff --git a/packages/expect/src/__tests__/matchers-toContain.property.test.ts b/packages/expect/src/__tests__/matchers-toContain.property.test.ts index 4b24febb91de..19f0bd363923 100644 --- a/packages/expect/src/__tests__/matchers-toContain.property.test.ts +++ b/packages/expect/src/__tests__/matchers-toContain.property.test.ts @@ -7,7 +7,7 @@ */ import fc from 'fast-check'; -import expect from '..'; +import expect from '../'; import { anythingSettings, assertSettings, diff --git a/packages/expect/src/__tests__/matchers-toContainEqual.property.test.ts b/packages/expect/src/__tests__/matchers-toContainEqual.property.test.ts index b85565c8231e..51bbb10d74f8 100644 --- a/packages/expect/src/__tests__/matchers-toContainEqual.property.test.ts +++ b/packages/expect/src/__tests__/matchers-toContainEqual.property.test.ts @@ -7,7 +7,7 @@ */ import fc from 'fast-check'; -import expect from '..'; +import expect from '../'; import { anythingSettings, assertSettings, diff --git a/packages/expect/src/__tests__/matchers-toEqual.property.test.ts b/packages/expect/src/__tests__/matchers-toEqual.property.test.ts index c54e39090409..3bc8cfa0da6c 100644 --- a/packages/expect/src/__tests__/matchers-toEqual.property.test.ts +++ b/packages/expect/src/__tests__/matchers-toEqual.property.test.ts @@ -7,7 +7,7 @@ */ import fc from 'fast-check'; -import expect from '..'; +import expect from '../'; import { anythingSettings, assertSettings, diff --git a/packages/expect/src/__tests__/matchers-toStrictEqual.property.test.ts b/packages/expect/src/__tests__/matchers-toStrictEqual.property.test.ts index b81858b78423..4a0a0ab3e36e 100644 --- a/packages/expect/src/__tests__/matchers-toStrictEqual.property.test.ts +++ b/packages/expect/src/__tests__/matchers-toStrictEqual.property.test.ts @@ -8,7 +8,7 @@ import * as assert from 'assert'; import fc from 'fast-check'; -import expect from '..'; +import expect from '../'; import { anythingSettings, assertSettings, diff --git a/packages/expect/src/__tests__/spyMatchers.test.ts b/packages/expect/src/__tests__/spyMatchers.test.ts index b9c248282cdb..2ed27ec691f1 100644 --- a/packages/expect/src/__tests__/spyMatchers.test.ts +++ b/packages/expect/src/__tests__/spyMatchers.test.ts @@ -24,7 +24,7 @@ declare module '../types' { } } -// Given a Jest mock function, return a minimal mock of a Jasmine spy. +// Given a Jest mock function, return a minimal mock of a spy. const createSpy = (fn: jest.Mock) => { const spy = function () {}; @@ -68,6 +68,7 @@ describe.each(['toBeCalled', 'toHaveBeenCalled'] as const)('%s', called => { const fn = jest.fn(); fn(); + // @ts-expect-error: Testing runtime error expect(() => jestExpect(fn)[called](555)).toThrowErrorMatchingSnapshot(); }); @@ -75,6 +76,7 @@ describe.each(['toBeCalled', 'toHaveBeenCalled'] as const)('%s', called => { const fn = jest.fn(); expect(() => + // @ts-expect-error: Testing runtime error jestExpect(fn).not[called](555), ).toThrowErrorMatchingSnapshot(); }); @@ -106,6 +108,7 @@ describe.each(['toBeCalledTimes', 'toHaveBeenCalledTimes'] as const)( [{}, [], true, 'a', new Map(), () => {}].forEach(value => { expect(() => + // @ts-expect-error: Testing runtime error jestExpect(fn)[calledTimes](value), ).toThrowErrorMatchingSnapshot(); }); @@ -117,6 +120,7 @@ describe.each(['toBeCalledTimes', 'toHaveBeenCalledTimes'] as const)( [{}, [], true, 'a', new Map(), () => {}].forEach(value => { expect(() => + // @ts-expect-error: Testing runtime error jestExpect(fn).not[calledTimes](value), ).toThrowErrorMatchingSnapshot(); }); @@ -189,105 +193,166 @@ describe.each([ 'toBeCalledWith', 'toHaveBeenCalledWith', ] as const)('%s', calledWith => { - const caller = function ( - callee: (...a: Array) => void, - ...args: Array - ) { - if ( - calledWith === 'nthCalledWith' || - calledWith === 'toHaveBeenNthCalledWith' - ) { - callee(1, ...args); - } else { - callee(...args); - } - }; + function isNth( + calledWith: string, + ): calledWith is 'nthCalledWith' | 'toHaveBeenNthCalledWith' { + return ( + calledWith === 'nthCalledWith' || calledWith === 'toHaveBeenNthCalledWith' + ); + } test('works only on spies or jest.fn', () => { const fn = function fn() {}; - expect(() => jestExpect(fn)[calledWith]()).toThrowErrorMatchingSnapshot(); + if (isNth(calledWith)) { + expect(() => + jestExpect(fn)[calledWith](3), + ).toThrowErrorMatchingSnapshot(); + } else { + expect(() => jestExpect(fn)[calledWith]()).toThrowErrorMatchingSnapshot(); + } }); test('works when not called', () => { const fn = jest.fn(); - caller(jestExpect(createSpy(fn)).not[calledWith], 'foo', 'bar'); - caller(jestExpect(fn).not[calledWith], 'foo', 'bar'); - expect(() => - caller(jestExpect(fn)[calledWith], 'foo', 'bar'), - ).toThrowErrorMatchingSnapshot(); + if (isNth(calledWith)) { + jestExpect(createSpy(fn)).not[calledWith](1, 'foo', 'bar'); + jestExpect(fn).not[calledWith](1, 'foo', 'bar'); + + expect(() => + jestExpect(fn)[calledWith](1, 'foo', 'bar'), + ).toThrowErrorMatchingSnapshot(); + } else { + jestExpect(createSpy(fn)).not[calledWith]('foo', 'bar'); + jestExpect(fn).not[calledWith]('foo', 'bar'); + + expect(() => + jestExpect(fn)[calledWith]('foo', 'bar'), + ).toThrowErrorMatchingSnapshot(); + } }); test('works with no arguments', () => { const fn = jest.fn(); fn(); - caller(jestExpect(createSpy(fn))[calledWith]); - caller(jestExpect(fn)[calledWith]); + + if (isNth(calledWith)) { + jestExpect(createSpy(fn))[calledWith](1); + jestExpect(fn)[calledWith](1); + } else { + jestExpect(createSpy(fn))[calledWith](); + jestExpect(fn)[calledWith](); + } }); test("works with arguments that don't match", () => { const fn = jest.fn(); fn('foo', 'bar1'); - caller(jestExpect(createSpy(fn)).not[calledWith], 'foo', 'bar'); - caller(jestExpect(fn).not[calledWith], 'foo', 'bar'); + if (isNth(calledWith)) { + jestExpect(createSpy(fn)).not[calledWith](1, 'foo', 'bar'); + jestExpect(fn).not[calledWith](1, 'foo', 'bar'); - expect(() => - caller(jestExpect(fn)[calledWith], 'foo', 'bar'), - ).toThrowErrorMatchingSnapshot(); + expect(() => + jestExpect(fn)[calledWith](1, 'foo', 'bar'), + ).toThrowErrorMatchingSnapshot(); + } else { + jestExpect(createSpy(fn)).not[calledWith]('foo', 'bar'); + jestExpect(fn).not[calledWith]('foo', 'bar'); + + expect(() => + jestExpect(fn)[calledWith]('foo', 'bar'), + ).toThrowErrorMatchingSnapshot(); + } }); test("works with arguments that don't match in number of arguments", () => { const fn = jest.fn(); fn('foo', 'bar', 'plop'); - caller(jestExpect(createSpy(fn)).not[calledWith], 'foo', 'bar'); - caller(jestExpect(fn).not[calledWith], 'foo', 'bar'); + if (isNth(calledWith)) { + jestExpect(createSpy(fn)).not[calledWith](1, 'foo', 'bar'); + jestExpect(fn).not[calledWith](1, 'foo', 'bar'); - expect(() => - caller(jestExpect(fn)[calledWith], 'foo', 'bar'), - ).toThrowErrorMatchingSnapshot(); + expect(() => + jestExpect(fn)[calledWith](1, 'foo', 'bar'), + ).toThrowErrorMatchingSnapshot(); + } else { + jestExpect(createSpy(fn)).not[calledWith]('foo', 'bar'); + jestExpect(fn).not[calledWith]('foo', 'bar'); + + expect(() => + jestExpect(fn)[calledWith]('foo', 'bar'), + ).toThrowErrorMatchingSnapshot(); + } }); test("works with arguments that don't match with matchers", () => { const fn = jest.fn(); fn('foo', 'bar'); - caller( - jestExpect(createSpy(fn)).not[calledWith], - jestExpect.any(String), - jestExpect.any(Number), - ); - caller( - jestExpect(fn).not[calledWith], - jestExpect.any(String), - jestExpect.any(Number), - ); + if (isNth(calledWith)) { + jestExpect(createSpy(fn)).not[calledWith]( + 1, + jestExpect.any(String), + jestExpect.any(Number), + ); + jestExpect(fn).not[calledWith]( + 1, + jestExpect.any(String), + jestExpect.any(Number), + ); - expect(() => - caller( - jestExpect(fn)[calledWith], + expect(() => + jestExpect(fn)[calledWith]( + 1, + jestExpect.any(String), + jestExpect.any(Number), + ), + ).toThrowErrorMatchingSnapshot(); + } else { + jestExpect(createSpy(fn)).not[calledWith]( jestExpect.any(String), jestExpect.any(Number), - ), - ).toThrowErrorMatchingSnapshot(); + ); + jestExpect(fn).not[calledWith]( + jestExpect.any(String), + jestExpect.any(Number), + ); + + expect(() => + jestExpect(fn)[calledWith]( + jestExpect.any(String), + jestExpect.any(Number), + ), + ).toThrowErrorMatchingSnapshot(); + } }); test("works with arguments that don't match with matchers even when argument is undefined", () => { const fn = jest.fn(); fn('foo', undefined); - caller( - jestExpect(createSpy(fn)).not[calledWith], - 'foo', - jestExpect.any(String), - ); - caller(jestExpect(fn).not[calledWith], 'foo', jestExpect.any(String)); + if (isNth(calledWith)) { + jestExpect(createSpy(fn)).not[calledWith]( + 1, + 'foo', + jestExpect.any(String), + ); + jestExpect(fn).not[calledWith](1, 'foo', jestExpect.any(String)); - expect(() => - caller(jestExpect(fn)[calledWith], 'foo', jestExpect.any(String)), - ).toThrowErrorMatchingSnapshot(); + expect(() => + jestExpect(fn)[calledWith](1, 'foo', jestExpect.any(String)), + ).toThrowErrorMatchingSnapshot(); + } else { + jestExpect(createSpy(fn)).not[calledWith]('foo', jestExpect.any(String)); + jestExpect(fn).not[calledWith]('foo', jestExpect.any(String)); + + expect(() => + jestExpect(fn)[calledWith]('foo', jestExpect.any(String)), + ).toThrowErrorMatchingSnapshot(); + } }); test("works with arguments that don't match in size even if one is an optional matcher", () => { @@ -295,65 +360,112 @@ describe.each([ const fn = jest.fn(); fn('foo'); - caller(jestExpect(fn).not[calledWith], 'foo', jestExpect.optionalFn()); - expect(() => - caller(jestExpect(fn)[calledWith], 'foo', jestExpect.optionalFn()), - ).toThrowErrorMatchingSnapshot(); + if (isNth(calledWith)) { + jestExpect(fn).not[calledWith](1, 'foo', jestExpect.optionalFn()); + expect(() => + jestExpect(fn)[calledWith](1, 'foo', jestExpect.optionalFn()), + ).toThrowErrorMatchingSnapshot(); + } else { + jestExpect(fn).not[calledWith]('foo', jestExpect.optionalFn()); + expect(() => + jestExpect(fn)[calledWith]('foo', jestExpect.optionalFn()), + ).toThrowErrorMatchingSnapshot(); + } }); test('works with arguments that match', () => { const fn = jest.fn(); fn('foo', 'bar'); - caller(jestExpect(createSpy(fn))[calledWith], 'foo', 'bar'); - caller(jestExpect(fn)[calledWith], 'foo', 'bar'); + if (isNth(calledWith)) { + jestExpect(createSpy(fn))[calledWith](1, 'foo', 'bar'); + jestExpect(fn)[calledWith](1, 'foo', 'bar'); - expect(() => - caller(jestExpect(fn).not[calledWith], 'foo', 'bar'), - ).toThrowErrorMatchingSnapshot(); + expect(() => + jestExpect(fn).not[calledWith](1, 'foo', 'bar'), + ).toThrowErrorMatchingSnapshot(); + } else { + jestExpect(createSpy(fn))[calledWith]('foo', 'bar'); + jestExpect(fn)[calledWith]('foo', 'bar'); + + expect(() => + jestExpect(fn).not[calledWith]('foo', 'bar'), + ).toThrowErrorMatchingSnapshot(); + } }); test('works with arguments that match with matchers', () => { const fn = jest.fn(); fn('foo', 'bar'); - caller( - jestExpect(createSpy(fn))[calledWith], - jestExpect.any(String), - jestExpect.any(String), - ); - caller( - jestExpect(fn)[calledWith], - jestExpect.any(String), - jestExpect.any(String), - ); + if (isNth(calledWith)) { + jestExpect(createSpy(fn))[calledWith]( + 1, + jestExpect.any(String), + jestExpect.any(String), + ); + jestExpect(fn)[calledWith]( + 1, + jestExpect.any(String), + jestExpect.any(String), + ); - expect(() => - caller( - jestExpect(fn).not[calledWith], + expect(() => + jestExpect(fn).not[calledWith]( + 1, + jestExpect.any(String), + jestExpect.any(String), + ), + ).toThrowErrorMatchingSnapshot(); + } else { + jestExpect(createSpy(fn))[calledWith]( jestExpect.any(String), jestExpect.any(String), - ), - ).toThrowErrorMatchingSnapshot(); + ); + jestExpect(fn)[calledWith]( + jestExpect.any(String), + jestExpect.any(String), + ); + + expect(() => + jestExpect(fn).not[calledWith]( + jestExpect.any(String), + jestExpect.any(String), + ), + ).toThrowErrorMatchingSnapshot(); + } }); test('works with trailing undefined arguments', () => { const fn = jest.fn(); fn('foo', undefined); - expect(() => - caller(jestExpect(fn)[calledWith], 'foo'), - ).toThrowErrorMatchingSnapshot(); + if (isNth(calledWith)) { + expect(() => + jestExpect(fn)[calledWith](1, 'foo'), + ).toThrowErrorMatchingSnapshot(); + } else { + expect(() => + jestExpect(fn)[calledWith]('foo'), + ).toThrowErrorMatchingSnapshot(); + } }); test('works with trailing undefined arguments if requested by the match query', () => { const fn = jest.fn(); fn('foo', undefined); - caller(jestExpect(fn)[calledWith], 'foo', undefined); - expect(() => - caller(jestExpect(fn).not[calledWith], 'foo', undefined), - ).toThrowErrorMatchingSnapshot(); + if (isNth(calledWith)) { + jestExpect(fn)[calledWith](1, 'foo', undefined); + expect(() => + jestExpect(fn).not[calledWith](1, 'foo', undefined), + ).toThrowErrorMatchingSnapshot(); + } else { + jestExpect(fn)[calledWith]('foo', undefined); + expect(() => + jestExpect(fn).not[calledWith]('foo', undefined), + ).toThrowErrorMatchingSnapshot(); + } }); test('works with trailing undefined arguments when explicitly requested as optional by matcher', () => { @@ -361,10 +473,17 @@ describe.each([ const fn = jest.fn(); fn('foo', undefined); - caller(jestExpect(fn)[calledWith], 'foo', jestExpect.optionalFn()); - expect(() => - caller(jestExpect(fn).not[calledWith], 'foo', jestExpect.optionalFn()), - ).toThrowErrorMatchingSnapshot(); + if (isNth(calledWith)) { + jestExpect(fn)[calledWith](1, 'foo', jestExpect.optionalFn()); + expect(() => + jestExpect(fn).not[calledWith](1, 'foo', jestExpect.optionalFn()), + ).toThrowErrorMatchingSnapshot(); + } else { + jestExpect(fn)[calledWith]('foo', jestExpect.optionalFn()); + expect(() => + jestExpect(fn).not[calledWith]('foo', jestExpect.optionalFn()), + ).toThrowErrorMatchingSnapshot(); + } }); test('works with Map', () => { @@ -385,15 +504,27 @@ describe.each([ fn(m1); - caller(jestExpect(fn)[calledWith], m2); - caller(jestExpect(fn).not[calledWith], m3); + if (isNth(calledWith)) { + jestExpect(fn)[calledWith](1, m2); + jestExpect(fn).not[calledWith](1, m3); - expect(() => - caller(jestExpect(fn).not[calledWith], m2), - ).toThrowErrorMatchingSnapshot(); - expect(() => - caller(jestExpect(fn)[calledWith], m3), - ).toThrowErrorMatchingSnapshot(); + expect(() => + jestExpect(fn).not[calledWith](1, m2), + ).toThrowErrorMatchingSnapshot(); + expect(() => + jestExpect(fn)[calledWith](1, m3), + ).toThrowErrorMatchingSnapshot(); + } else { + jestExpect(fn)[calledWith](m2); + jestExpect(fn).not[calledWith](m3); + + expect(() => + jestExpect(fn).not[calledWith](m2), + ).toThrowErrorMatchingSnapshot(); + expect(() => + jestExpect(fn)[calledWith](m3), + ).toThrowErrorMatchingSnapshot(); + } }); test('works with Set', () => { @@ -405,15 +536,27 @@ describe.each([ fn(s1); - caller(jestExpect(fn)[calledWith], s2); - caller(jestExpect(fn).not[calledWith], s3); + if (isNth(calledWith)) { + jestExpect(fn)[calledWith](1, s2); + jestExpect(fn).not[calledWith](1, s3); - expect(() => - caller(jestExpect(fn).not[calledWith], s2), - ).toThrowErrorMatchingSnapshot(); - expect(() => - caller(jestExpect(fn)[calledWith], s3), - ).toThrowErrorMatchingSnapshot(); + expect(() => + jestExpect(fn).not[calledWith](1, s2), + ).toThrowErrorMatchingSnapshot(); + expect(() => + jestExpect(fn)[calledWith](1, s3), + ).toThrowErrorMatchingSnapshot(); + } else { + jestExpect(fn)[calledWith](s2); + jestExpect(fn).not[calledWith](s3); + + expect(() => + jestExpect(fn).not[calledWith](s2), + ).toThrowErrorMatchingSnapshot(); + expect(() => + jestExpect(fn)[calledWith](s3), + ).toThrowErrorMatchingSnapshot(); + } }); test('works with Immutable.js objects', () => { @@ -422,25 +565,22 @@ describe.each([ const indirectlyCreated = Immutable.Map().set('a', {b: 'c'}); fn(directlyCreated, indirectlyCreated); - caller(jestExpect(fn)[calledWith], indirectlyCreated, directlyCreated); + if (isNth(calledWith)) { + jestExpect(fn)[calledWith](1, indirectlyCreated, directlyCreated); - expect(() => - caller( - jestExpect(fn).not[calledWith], - indirectlyCreated, - directlyCreated, - ), - ).toThrowErrorMatchingSnapshot(); - }); + expect(() => + jestExpect(fn).not[calledWith](1, indirectlyCreated, directlyCreated), + ).toThrowErrorMatchingSnapshot(); + } else { + jestExpect(fn)[calledWith](indirectlyCreated, directlyCreated); - const basicCalledWith = [ - 'lastCalledWith', - 'toHaveBeenLastCalledWith', - 'toBeCalledWith', - 'toHaveBeenCalledWith', - ]; + expect(() => + jestExpect(fn).not[calledWith](indirectlyCreated, directlyCreated), + ).toThrowErrorMatchingSnapshot(); + } + }); - if (basicCalledWith.indexOf(calledWith) >= 0) { + if (!isNth(calledWith)) { test('works with many arguments', () => { const fn = jest.fn(); fn('foo1', 'bar'); @@ -468,8 +608,7 @@ describe.each([ }); } - const nthCalled = ['toHaveBeenNthCalledWith', 'nthCalledWith']; - if (nthCalled.indexOf(calledWith) >= 0) { + if (isNth(calledWith)) { test('works with three calls', () => { const fn = jest.fn(); fn('foo1', 'bar'); @@ -517,11 +656,19 @@ describe.each([ const fn = jest.fn().mockName('named-mock'); fn('foo', 'bar'); - caller(jestExpect(fn)[calledWith], 'foo', 'bar'); + if (isNth(calledWith)) { + jestExpect(fn)[calledWith](1, 'foo', 'bar'); - expect(() => - caller(jestExpect(fn).not[calledWith], 'foo', 'bar'), - ).toThrowErrorMatchingSnapshot(); + expect(() => + jestExpect(fn).not[calledWith](1, 'foo', 'bar'), + ).toThrowErrorMatchingSnapshot(); + } else { + jestExpect(fn)[calledWith]('foo', 'bar'); + + expect(() => + jestExpect(fn).not[calledWith]('foo', 'bar'), + ).toThrowErrorMatchingSnapshot(); + } }); }); @@ -623,6 +770,7 @@ describe.each(['toReturn', 'toHaveReturned'] as const)('%s', returned => { const fn = jest.fn(); fn(); + // @ts-expect-error: Testing runtime error expect(() => jestExpect(fn)[returned](555)).toThrowErrorMatchingSnapshot(); }); @@ -630,6 +778,7 @@ describe.each(['toReturn', 'toHaveReturned'] as const)('%s', returned => { const fn = jest.fn(); expect(() => + // @ts-expect-error: Testing runtime error jestExpect(fn).not[returned](555), ).toThrowErrorMatchingSnapshot(); }); @@ -677,6 +826,7 @@ describe.each(['toReturnTimes', 'toHaveReturnedTimes'] as const)( [{}, [], true, 'a', new Map(), () => {}].forEach(value => { expect(() => + // @ts-expect-error: Testing runtime error jestExpect(fn)[returnedTimes](value), ).toThrowErrorMatchingSnapshot(); }); @@ -688,6 +838,7 @@ describe.each(['toReturnTimes', 'toHaveReturnedTimes'] as const)( [{}, [], true, 'a', new Map(), () => {}].forEach(value => { expect(() => + // @ts-expect-error: Testing runtime error jestExpect(fn).not[returnedTimes](value), ).toThrowErrorMatchingSnapshot(); }); @@ -841,72 +992,108 @@ describe.each([ 'toReturnWith', 'toHaveReturnedWith', ] as const)('%s', returnedWith => { - const caller = function ( - callee: (...a: Array) => void, - ...args: Array - ) { - if ( + function isNth( + returnedWith: string, + ): returnedWith is 'nthReturnedWith' | 'toHaveNthReturnedWith' { + return ( returnedWith === 'nthReturnedWith' || returnedWith === 'toHaveNthReturnedWith' - ) { - callee(1, ...args); - } else { - callee(...args); - } - }; + ); + } test('works only on spies or jest.fn', () => { const fn = function fn() {}; + // @ts-expect-error: Testing runtime error expect(() => jestExpect(fn)[returnedWith]()).toThrowErrorMatchingSnapshot(); }); test('works when not called', () => { const fn = jest.fn(); - caller(jestExpect(fn).not[returnedWith], 'foo'); - expect(() => - caller(jestExpect(fn)[returnedWith], 'foo'), - ).toThrowErrorMatchingSnapshot(); + if (isNth(returnedWith)) { + jestExpect(fn).not[returnedWith](1, 'foo'); + + expect(() => + jestExpect(fn)[returnedWith](1, 'foo'), + ).toThrowErrorMatchingSnapshot(); + } else { + jestExpect(fn).not[returnedWith]('foo'); + + expect(() => + jestExpect(fn)[returnedWith]('foo'), + ).toThrowErrorMatchingSnapshot(); + } }); test('works with no arguments', () => { const fn = jest.fn(); fn(); - caller(jestExpect(fn)[returnedWith]); + + if (isNth(returnedWith)) { + // @ts-expect-error: TODO should types be fixed? + jestExpect(fn)[returnedWith](1); + } else { + // @ts-expect-error: TODO should types be fixed? + jestExpect(fn)[returnedWith](); + } }); test('works with argument that does not match', () => { const fn = jest.fn(() => 'foo'); fn(); - caller(jestExpect(fn).not[returnedWith], 'bar'); + if (isNth(returnedWith)) { + jestExpect(fn).not[returnedWith](1, 'bar'); - expect(() => - caller(jestExpect(fn)[returnedWith], 'bar'), - ).toThrowErrorMatchingSnapshot(); + expect(() => + jestExpect(fn)[returnedWith](1, 'bar'), + ).toThrowErrorMatchingSnapshot(); + } else { + jestExpect(fn).not[returnedWith]('bar'); + + expect(() => + jestExpect(fn)[returnedWith]('bar'), + ).toThrowErrorMatchingSnapshot(); + } }); test('works with argument that does match', () => { const fn = jest.fn(() => 'foo'); fn(); - caller(jestExpect(fn)[returnedWith], 'foo'); + if (isNth(returnedWith)) { + jestExpect(fn)[returnedWith](1, 'foo'); - expect(() => - caller(jestExpect(fn).not[returnedWith], 'foo'), - ).toThrowErrorMatchingSnapshot(); + expect(() => + jestExpect(fn).not[returnedWith](1, 'foo'), + ).toThrowErrorMatchingSnapshot(); + } else { + jestExpect(fn)[returnedWith]('foo'); + + expect(() => + jestExpect(fn).not[returnedWith]('foo'), + ).toThrowErrorMatchingSnapshot(); + } }); test('works with undefined', () => { const fn = jest.fn(() => undefined); fn(); - caller(jestExpect(fn)[returnedWith], undefined); + if (isNth(returnedWith)) { + jestExpect(fn)[returnedWith](1, undefined); - expect(() => - caller(jestExpect(fn).not[returnedWith], undefined), - ).toThrowErrorMatchingSnapshot(); + expect(() => + jestExpect(fn).not[returnedWith](1, undefined), + ).toThrowErrorMatchingSnapshot(); + } else { + jestExpect(fn)[returnedWith](undefined); + + expect(() => + jestExpect(fn).not[returnedWith](undefined), + ).toThrowErrorMatchingSnapshot(); + } }); test('works with Map', () => { @@ -926,15 +1113,27 @@ describe.each([ const fn = jest.fn(() => m1); fn(); - caller(jestExpect(fn)[returnedWith], m2); - caller(jestExpect(fn).not[returnedWith], m3); + if (isNth(returnedWith)) { + jestExpect(fn)[returnedWith](1, m2); + jestExpect(fn).not[returnedWith](1, m3); - expect(() => - caller(jestExpect(fn).not[returnedWith], m2), - ).toThrowErrorMatchingSnapshot(); - expect(() => - caller(jestExpect(fn)[returnedWith], m3), - ).toThrowErrorMatchingSnapshot(); + expect(() => + jestExpect(fn).not[returnedWith](1, m2), + ).toThrowErrorMatchingSnapshot(); + expect(() => + jestExpect(fn)[returnedWith](1, m3), + ).toThrowErrorMatchingSnapshot(); + } else { + jestExpect(fn)[returnedWith](m2); + jestExpect(fn).not[returnedWith](m3); + + expect(() => + jestExpect(fn).not[returnedWith](m2), + ).toThrowErrorMatchingSnapshot(); + expect(() => + jestExpect(fn)[returnedWith](m3), + ).toThrowErrorMatchingSnapshot(); + } }); test('works with Set', () => { @@ -945,15 +1144,27 @@ describe.each([ const fn = jest.fn(() => s1); fn(); - caller(jestExpect(fn)[returnedWith], s2); - caller(jestExpect(fn).not[returnedWith], s3); + if (isNth(returnedWith)) { + jestExpect(fn)[returnedWith](1, s2); + jestExpect(fn).not[returnedWith](1, s3); - expect(() => - caller(jestExpect(fn).not[returnedWith], s2), - ).toThrowErrorMatchingSnapshot(); - expect(() => - caller(jestExpect(fn)[returnedWith], s3), - ).toThrowErrorMatchingSnapshot(); + expect(() => + jestExpect(fn).not[returnedWith](1, s2), + ).toThrowErrorMatchingSnapshot(); + expect(() => + jestExpect(fn)[returnedWith](1, s3), + ).toThrowErrorMatchingSnapshot(); + } else { + jestExpect(fn)[returnedWith](s2); + jestExpect(fn).not[returnedWith](s3); + + expect(() => + jestExpect(fn).not[returnedWith](s2), + ).toThrowErrorMatchingSnapshot(); + expect(() => + jestExpect(fn)[returnedWith](s3), + ).toThrowErrorMatchingSnapshot(); + } }); test('works with Immutable.js objects directly created', () => { @@ -961,11 +1172,19 @@ describe.each([ const fn = jest.fn(() => directlyCreated); fn(); - caller(jestExpect(fn)[returnedWith], directlyCreated); + if (isNth(returnedWith)) { + jestExpect(fn)[returnedWith](1, directlyCreated); - expect(() => - caller(jestExpect(fn).not[returnedWith], directlyCreated), - ).toThrowErrorMatchingSnapshot(); + expect(() => + jestExpect(fn).not[returnedWith](1, directlyCreated), + ).toThrowErrorMatchingSnapshot(); + } else { + jestExpect(fn)[returnedWith](directlyCreated); + + expect(() => + jestExpect(fn).not[returnedWith](directlyCreated), + ).toThrowErrorMatchingSnapshot(); + } }); test('works with Immutable.js objects indirectly created', () => { @@ -973,11 +1192,19 @@ describe.each([ const fn = jest.fn(() => indirectlyCreated); fn(); - caller(jestExpect(fn)[returnedWith], indirectlyCreated); + if (isNth(returnedWith)) { + jestExpect(fn)[returnedWith](1, indirectlyCreated); - expect(() => - caller(jestExpect(fn).not[returnedWith], indirectlyCreated), - ).toThrowErrorMatchingSnapshot(); + expect(() => + jestExpect(fn).not[returnedWith](1, indirectlyCreated), + ).toThrowErrorMatchingSnapshot(); + } else { + jestExpect(fn)[returnedWith](indirectlyCreated); + + expect(() => + jestExpect(fn).not[returnedWith](indirectlyCreated), + ).toThrowErrorMatchingSnapshot(); + } }); test('a call that throws is not considered to have returned', () => { @@ -991,14 +1218,25 @@ describe.each([ // ignore error } - // It doesn't matter what return value is tested if the call threw - caller(jestExpect(fn).not[returnedWith], 'foo'); - caller(jestExpect(fn).not[returnedWith], null); - caller(jestExpect(fn).not[returnedWith], undefined); + if (isNth(returnedWith)) { + // It doesn't matter what return value is tested if the call threw + jestExpect(fn).not[returnedWith](1, 'foo'); + jestExpect(fn).not[returnedWith](1, null); + jestExpect(fn).not[returnedWith](1, undefined); - expect(() => - caller(jestExpect(fn)[returnedWith], undefined), - ).toThrowErrorMatchingSnapshot(); + expect(() => + jestExpect(fn)[returnedWith](1, undefined), + ).toThrowErrorMatchingSnapshot(); + } else { + // It doesn't matter what return value is tested if the call threw + jestExpect(fn).not[returnedWith]('foo'); + jestExpect(fn).not[returnedWith](null); + jestExpect(fn).not[returnedWith](undefined); + + expect(() => + jestExpect(fn)[returnedWith](undefined), + ).toThrowErrorMatchingSnapshot(); + } }); test('a call that throws undefined is not considered to have returned', () => { @@ -1013,21 +1251,31 @@ describe.each([ // ignore error } - // It doesn't matter what return value is tested if the call threw - caller(jestExpect(fn).not[returnedWith], 'foo'); - caller(jestExpect(fn).not[returnedWith], null); - caller(jestExpect(fn).not[returnedWith], undefined); + if (isNth(returnedWith)) { + // It doesn't matter what return value is tested if the call threw + jestExpect(fn).not[returnedWith](1, 'foo'); + jestExpect(fn).not[returnedWith](1, null); + jestExpect(fn).not[returnedWith](1, undefined); - expect(() => - caller(jestExpect(fn)[returnedWith], undefined), - ).toThrowErrorMatchingSnapshot(); + expect(() => + jestExpect(fn)[returnedWith](1, undefined), + ).toThrowErrorMatchingSnapshot(); + } else { + // It doesn't matter what return value is tested if the call threw + jestExpect(fn).not[returnedWith]('foo'); + jestExpect(fn).not[returnedWith](null); + jestExpect(fn).not[returnedWith](undefined); + + expect(() => + jestExpect(fn)[returnedWith](undefined), + ).toThrowErrorMatchingSnapshot(); + } }); - const basicReturnedWith = ['toHaveReturnedWith', 'toReturnWith']; - if (basicReturnedWith.indexOf(returnedWith) >= 0) { + if (!isNth(returnedWith)) { describe('returnedWith', () => { test('works with more calls than the limit', () => { - const fn = jest.fn(); + const fn = jest.fn<() => string>(); fn.mockReturnValueOnce('foo1'); fn.mockReturnValueOnce('foo2'); fn.mockReturnValueOnce('foo3'); @@ -1073,11 +1321,10 @@ describe.each([ }); } - const nthReturnedWith = ['toHaveNthReturnedWith', 'nthReturnedWith']; - if (nthReturnedWith.indexOf(returnedWith) >= 0) { + if (isNth(returnedWith)) { describe('nthReturnedWith', () => { test('works with three calls', () => { - const fn = jest.fn(); + const fn = jest.fn<() => string>(); fn.mockReturnValueOnce('foo1'); fn.mockReturnValueOnce('foo2'); fn.mockReturnValueOnce('foo3'); @@ -1097,7 +1344,7 @@ describe.each([ }); test('should replace 1st, 2nd, 3rd with first, second, third', async () => { - const fn = jest.fn(); + const fn = jest.fn<() => string>(); fn.mockReturnValueOnce('foo1'); fn.mockReturnValueOnce('foo2'); fn.mockReturnValueOnce('foo3'); @@ -1139,7 +1386,7 @@ describe.each([ }); test('positive throw matcher error for n that is not integer', async () => { - const fn = jest.fn(() => 'foo'); + const fn = jest.fn<(a: string) => string>(() => 'foo'); fn('foo'); expect(() => { @@ -1148,10 +1395,11 @@ describe.each([ }); test('negative throw matcher error for n that is not number', async () => { - const fn = jest.fn(() => 'foo'); + const fn = jest.fn<(a: string) => string>(() => 'foo'); fn('foo'); expect(() => { + // @ts-expect-error: Testing runtime error jestExpect(fn).not[returnedWith](); }).toThrowErrorMatchingSnapshot(); }); @@ -1194,11 +1442,10 @@ describe.each([ }); } - const lastReturnedWith = ['toHaveLastReturnedWith', 'lastReturnedWith']; - if (lastReturnedWith.indexOf(returnedWith) >= 0) { + if (!isNth(returnedWith)) { describe('lastReturnedWith', () => { test('works with three calls', () => { - const fn = jest.fn(); + const fn = jest.fn<() => string>(); fn.mockReturnValueOnce('foo1'); fn.mockReturnValueOnce('foo2'); fn.mockReturnValueOnce('foo3'); @@ -1236,10 +1483,19 @@ describe.each([ test('includes the custom mock name in the error message', () => { const fn = jest.fn().mockName('named-mock'); - caller(jestExpect(fn).not[returnedWith], 'foo'); - expect(() => - caller(jestExpect(fn)[returnedWith], 'foo'), - ).toThrowErrorMatchingSnapshot(); + if (isNth(returnedWith)) { + jestExpect(fn).not[returnedWith](1, 'foo'); + + expect(() => + jestExpect(fn)[returnedWith](1, 'foo'), + ).toThrowErrorMatchingSnapshot(); + } else { + jestExpect(fn).not[returnedWith]('foo'); + + expect(() => + jestExpect(fn)[returnedWith]('foo'), + ).toThrowErrorMatchingSnapshot(); + } }); }); diff --git a/packages/expect/src/__tests__/symbolInObjects.test.ts b/packages/expect/src/__tests__/symbolInObjects.test.ts index 352208adbf59..d185ae483010 100644 --- a/packages/expect/src/__tests__/symbolInObjects.test.ts +++ b/packages/expect/src/__tests__/symbolInObjects.test.ts @@ -6,6 +6,8 @@ * */ +export {}; + describe('Symbol in objects', () => { test('should compare objects with Symbol keys', () => { const sym = Symbol('foo'); diff --git a/packages/expect/src/__tests__/toEqual-dom.test.ts b/packages/expect/src/__tests__/toEqual-dom.test.ts index 864536dc39ca..724372e2eb3d 100644 --- a/packages/expect/src/__tests__/toEqual-dom.test.ts +++ b/packages/expect/src/__tests__/toEqual-dom.test.ts @@ -11,6 +11,8 @@ /* eslint-env browser*/ +export {}; + describe('toEqual', () => { describe('duck type', () => { // https://github.com/facebook/jest/issues/7786 diff --git a/tsconfig.test.json b/tsconfig.test.json index b575da7b2516..9b7f1135323d 100644 --- a/tsconfig.test.json +++ b/tsconfig.test.json @@ -3,6 +3,7 @@ "compilerOptions": { "composite": false, "emitDeclarationOnly": false, + // "isolatedModules": false, "noEmit": true, "skipLibCheck": true, "types": ["@jest/test-globals"] From 3ee60fa0cc126d5591d8d36bb56d1e8230af0c70 Mon Sep 17 00:00:00 2001 From: Tom Mrazauskas Date: Tue, 4 Oct 2022 18:12:42 +0300 Subject: [PATCH 2/8] fix asymmetric matchers --- packages/expect/src/__tests__/extend.test.ts | 20 ++++++++++++------- .../expect/src/__tests__/spyMatchers.test.ts | 12 +++++------ 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/packages/expect/src/__tests__/extend.test.ts b/packages/expect/src/__tests__/extend.test.ts index 91ddb9e22907..6af0c25f5331 100644 --- a/packages/expect/src/__tests__/extend.test.ts +++ b/packages/expect/src/__tests__/extend.test.ts @@ -9,11 +9,11 @@ import {equals, iterableEquality, subsetEquality} from '@jest/expect-utils'; import {alignedAnsiStyleSerializer} from '@jest/test-utils'; import * as matcherUtils from 'jest-matcher-utils'; -import jestExpect from '../'; +import importedExpect from '../'; expect.addSnapshotSerializer(alignedAnsiStyleSerializer); -jestExpect.extend({ +importedExpect.extend({ toBeDivisibleBy(actual: number, expected: number) { const pass = actual % expected === 0; const message: () => string = pass @@ -52,11 +52,6 @@ jestExpect.extend({ }); declare module '../types' { - interface AsymmetricMatchers { - toBeDivisibleBy(expected: number): void; - toBeSymbol(expected: symbol): void; - toBeWithinRange(floor: number, ceiling: number): void; - } interface Matchers { toBeDivisibleBy(expected: number): R; toBeSymbol(expected: symbol): R; @@ -69,6 +64,17 @@ declare module '../types' { } } +const jestExpect = importedExpect as typeof importedExpect & { + not: { + toBeDivisibleBy(expected: number): void; + toBeWithinRange(floor: number, ceiling: number): void; + }; + + toBeDivisibleBy(expected: number): void; + toBeSymbol(expected: symbol): void; + toBeWithinRange(floor: number, ceiling: number): void; +}; + it('is available globally when matcher is unary', () => { jestExpect(15).toBeDivisibleBy(5); jestExpect(15).toBeDivisibleBy(3); diff --git a/packages/expect/src/__tests__/spyMatchers.test.ts b/packages/expect/src/__tests__/spyMatchers.test.ts index 2ed27ec691f1..c6d357e61a82 100644 --- a/packages/expect/src/__tests__/spyMatchers.test.ts +++ b/packages/expect/src/__tests__/spyMatchers.test.ts @@ -7,22 +7,20 @@ import * as Immutable from 'immutable'; import {alignedAnsiStyleSerializer} from '@jest/test-utils'; -import jestExpect from '../'; +import importedExpect from '../'; expect.addSnapshotSerializer(alignedAnsiStyleSerializer); -jestExpect.extend({ +importedExpect.extend({ optionalFn(fn?: unknown) { const pass = fn === undefined || typeof fn === 'function'; return {message: () => 'expect either a function or undefined', pass}; }, }); -declare module '../types' { - interface AsymmetricMatchers { - optionalFn(fn?: unknown): void; - } -} +const jestExpect = importedExpect as typeof importedExpect & { + optionalFn(fn?: unknown): void; +}; // Given a Jest mock function, return a minimal mock of a spy. const createSpy = (fn: jest.Mock) => { From d2cde2e45e909b9d9456272944424e63cfd022ed Mon Sep 17 00:00:00 2001 From: Tom Mrazauskas Date: Tue, 4 Oct 2022 18:17:31 +0300 Subject: [PATCH 3/8] isolatedModules: false --- packages/expect/src/__tests__/symbolInObjects.test.ts | 2 -- packages/expect/src/__tests__/toEqual-dom.test.ts | 2 -- tsconfig.test.json | 2 +- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/expect/src/__tests__/symbolInObjects.test.ts b/packages/expect/src/__tests__/symbolInObjects.test.ts index d185ae483010..352208adbf59 100644 --- a/packages/expect/src/__tests__/symbolInObjects.test.ts +++ b/packages/expect/src/__tests__/symbolInObjects.test.ts @@ -6,8 +6,6 @@ * */ -export {}; - describe('Symbol in objects', () => { test('should compare objects with Symbol keys', () => { const sym = Symbol('foo'); diff --git a/packages/expect/src/__tests__/toEqual-dom.test.ts b/packages/expect/src/__tests__/toEqual-dom.test.ts index 724372e2eb3d..864536dc39ca 100644 --- a/packages/expect/src/__tests__/toEqual-dom.test.ts +++ b/packages/expect/src/__tests__/toEqual-dom.test.ts @@ -11,8 +11,6 @@ /* eslint-env browser*/ -export {}; - describe('toEqual', () => { describe('duck type', () => { // https://github.com/facebook/jest/issues/7786 diff --git a/tsconfig.test.json b/tsconfig.test.json index 9b7f1135323d..694798ac6c81 100644 --- a/tsconfig.test.json +++ b/tsconfig.test.json @@ -3,7 +3,7 @@ "compilerOptions": { "composite": false, "emitDeclarationOnly": false, - // "isolatedModules": false, + "isolatedModules": false, "noEmit": true, "skipLibCheck": true, "types": ["@jest/test-globals"] From 335a948948173a5c440cf21bd0dcd6474e0194e9 Mon Sep 17 00:00:00 2001 From: Tom Mrazauskas Date: Tue, 4 Oct 2022 20:34:48 +0300 Subject: [PATCH 4/8] fix test --- .../__snapshots__/spyMatchers.test.ts.snap | 46 ------------------- .../expect/src/__tests__/spyMatchers.test.ts | 10 +++- 2 files changed, 9 insertions(+), 47 deletions(-) diff --git a/packages/expect/src/__tests__/__snapshots__/spyMatchers.test.ts.snap b/packages/expect/src/__tests__/__snapshots__/spyMatchers.test.ts.snap index 0bc25e7bfdef..4bd06cf27559 100644 --- a/packages/expect/src/__tests__/__snapshots__/spyMatchers.test.ts.snap +++ b/packages/expect/src/__tests__/__snapshots__/spyMatchers.test.ts.snap @@ -2766,29 +2766,6 @@ Expected: "foo" Number of returns: 0 `; -exports[`toHaveReturnedWith lastReturnedWith incomplete recursive calls are handled properly 1`] = ` -expect(jest.fn()).toHaveReturnedWith(expected) - -Expected: 0 -Received - 1: function call has not returned yet - 2: function call has not returned yet - 3: function call has not returned yet - -Number of returns: 0 -Number of calls: 4 -`; - -exports[`toHaveReturnedWith lastReturnedWith works with three calls 1`] = ` -expect(jest.fn()).not.toHaveReturnedWith(expected) - -Expected: not "foo3" -Received - 3: "foo3" - -Number of returns: 3 -`; - exports[`toHaveReturnedWith returnedWith incomplete recursive calls are handled properly 1`] = ` expect(jest.fn()).toHaveReturnedWith(expected) @@ -3214,29 +3191,6 @@ Expected: "foo" Number of returns: 0 `; -exports[`toReturnWith lastReturnedWith incomplete recursive calls are handled properly 1`] = ` -expect(jest.fn()).toReturnWith(expected) - -Expected: 0 -Received - 1: function call has not returned yet - 2: function call has not returned yet - 3: function call has not returned yet - -Number of returns: 0 -Number of calls: 4 -`; - -exports[`toReturnWith lastReturnedWith works with three calls 1`] = ` -expect(jest.fn()).not.toReturnWith(expected) - -Expected: not "foo3" -Received - 3: "foo3" - -Number of returns: 3 -`; - exports[`toReturnWith returnedWith incomplete recursive calls are handled properly 1`] = ` expect(jest.fn()).toReturnWith(expected) diff --git a/packages/expect/src/__tests__/spyMatchers.test.ts b/packages/expect/src/__tests__/spyMatchers.test.ts index c6d357e61a82..cf465404213b 100644 --- a/packages/expect/src/__tests__/spyMatchers.test.ts +++ b/packages/expect/src/__tests__/spyMatchers.test.ts @@ -999,6 +999,14 @@ describe.each([ ); } + function isLast( + returnedWith: string, + ): returnedWith is 'lastReturnedWith' | 'toHaveLastReturnedWith' { + return ( + returnedWith === 'lastReturnedWith' || + returnedWith === 'toHaveLastReturnedWith' + ); + } test('works only on spies or jest.fn', () => { const fn = function fn() {}; @@ -1440,7 +1448,7 @@ describe.each([ }); } - if (!isNth(returnedWith)) { + if (isLast(returnedWith)) { describe('lastReturnedWith', () => { test('works with three calls', () => { const fn = jest.fn<() => string>(); From e71b6924f9ed31da86305923a2b679d305bc3a03 Mon Sep 17 00:00:00 2001 From: Tom Mrazauskas Date: Tue, 4 Oct 2022 20:39:03 +0300 Subject: [PATCH 5/8] tweak names --- .../expect/src/__tests__/spyMatchers.test.ts | 74 +++++++++---------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/packages/expect/src/__tests__/spyMatchers.test.ts b/packages/expect/src/__tests__/spyMatchers.test.ts index cf465404213b..c2fbd30f2003 100644 --- a/packages/expect/src/__tests__/spyMatchers.test.ts +++ b/packages/expect/src/__tests__/spyMatchers.test.ts @@ -191,7 +191,7 @@ describe.each([ 'toBeCalledWith', 'toHaveBeenCalledWith', ] as const)('%s', calledWith => { - function isNth( + function isToHaveNth( calledWith: string, ): calledWith is 'nthCalledWith' | 'toHaveBeenNthCalledWith' { return ( @@ -202,7 +202,7 @@ describe.each([ test('works only on spies or jest.fn', () => { const fn = function fn() {}; - if (isNth(calledWith)) { + if (isToHaveNth(calledWith)) { expect(() => jestExpect(fn)[calledWith](3), ).toThrowErrorMatchingSnapshot(); @@ -214,7 +214,7 @@ describe.each([ test('works when not called', () => { const fn = jest.fn(); - if (isNth(calledWith)) { + if (isToHaveNth(calledWith)) { jestExpect(createSpy(fn)).not[calledWith](1, 'foo', 'bar'); jestExpect(fn).not[calledWith](1, 'foo', 'bar'); @@ -235,7 +235,7 @@ describe.each([ const fn = jest.fn(); fn(); - if (isNth(calledWith)) { + if (isToHaveNth(calledWith)) { jestExpect(createSpy(fn))[calledWith](1); jestExpect(fn)[calledWith](1); } else { @@ -248,7 +248,7 @@ describe.each([ const fn = jest.fn(); fn('foo', 'bar1'); - if (isNth(calledWith)) { + if (isToHaveNth(calledWith)) { jestExpect(createSpy(fn)).not[calledWith](1, 'foo', 'bar'); jestExpect(fn).not[calledWith](1, 'foo', 'bar'); @@ -269,7 +269,7 @@ describe.each([ const fn = jest.fn(); fn('foo', 'bar', 'plop'); - if (isNth(calledWith)) { + if (isToHaveNth(calledWith)) { jestExpect(createSpy(fn)).not[calledWith](1, 'foo', 'bar'); jestExpect(fn).not[calledWith](1, 'foo', 'bar'); @@ -290,7 +290,7 @@ describe.each([ const fn = jest.fn(); fn('foo', 'bar'); - if (isNth(calledWith)) { + if (isToHaveNth(calledWith)) { jestExpect(createSpy(fn)).not[calledWith]( 1, jestExpect.any(String), @@ -332,7 +332,7 @@ describe.each([ const fn = jest.fn(); fn('foo', undefined); - if (isNth(calledWith)) { + if (isToHaveNth(calledWith)) { jestExpect(createSpy(fn)).not[calledWith]( 1, 'foo', @@ -358,7 +358,7 @@ describe.each([ const fn = jest.fn(); fn('foo'); - if (isNth(calledWith)) { + if (isToHaveNth(calledWith)) { jestExpect(fn).not[calledWith](1, 'foo', jestExpect.optionalFn()); expect(() => jestExpect(fn)[calledWith](1, 'foo', jestExpect.optionalFn()), @@ -375,7 +375,7 @@ describe.each([ const fn = jest.fn(); fn('foo', 'bar'); - if (isNth(calledWith)) { + if (isToHaveNth(calledWith)) { jestExpect(createSpy(fn))[calledWith](1, 'foo', 'bar'); jestExpect(fn)[calledWith](1, 'foo', 'bar'); @@ -396,7 +396,7 @@ describe.each([ const fn = jest.fn(); fn('foo', 'bar'); - if (isNth(calledWith)) { + if (isToHaveNth(calledWith)) { jestExpect(createSpy(fn))[calledWith]( 1, jestExpect.any(String), @@ -438,7 +438,7 @@ describe.each([ const fn = jest.fn(); fn('foo', undefined); - if (isNth(calledWith)) { + if (isToHaveNth(calledWith)) { expect(() => jestExpect(fn)[calledWith](1, 'foo'), ).toThrowErrorMatchingSnapshot(); @@ -453,7 +453,7 @@ describe.each([ const fn = jest.fn(); fn('foo', undefined); - if (isNth(calledWith)) { + if (isToHaveNth(calledWith)) { jestExpect(fn)[calledWith](1, 'foo', undefined); expect(() => jestExpect(fn).not[calledWith](1, 'foo', undefined), @@ -471,7 +471,7 @@ describe.each([ const fn = jest.fn(); fn('foo', undefined); - if (isNth(calledWith)) { + if (isToHaveNth(calledWith)) { jestExpect(fn)[calledWith](1, 'foo', jestExpect.optionalFn()); expect(() => jestExpect(fn).not[calledWith](1, 'foo', jestExpect.optionalFn()), @@ -502,7 +502,7 @@ describe.each([ fn(m1); - if (isNth(calledWith)) { + if (isToHaveNth(calledWith)) { jestExpect(fn)[calledWith](1, m2); jestExpect(fn).not[calledWith](1, m3); @@ -534,7 +534,7 @@ describe.each([ fn(s1); - if (isNth(calledWith)) { + if (isToHaveNth(calledWith)) { jestExpect(fn)[calledWith](1, s2); jestExpect(fn).not[calledWith](1, s3); @@ -563,7 +563,7 @@ describe.each([ const indirectlyCreated = Immutable.Map().set('a', {b: 'c'}); fn(directlyCreated, indirectlyCreated); - if (isNth(calledWith)) { + if (isToHaveNth(calledWith)) { jestExpect(fn)[calledWith](1, indirectlyCreated, directlyCreated); expect(() => @@ -578,7 +578,7 @@ describe.each([ } }); - if (!isNth(calledWith)) { + if (!isToHaveNth(calledWith)) { test('works with many arguments', () => { const fn = jest.fn(); fn('foo1', 'bar'); @@ -606,7 +606,7 @@ describe.each([ }); } - if (isNth(calledWith)) { + if (isToHaveNth(calledWith)) { test('works with three calls', () => { const fn = jest.fn(); fn('foo1', 'bar'); @@ -654,7 +654,7 @@ describe.each([ const fn = jest.fn().mockName('named-mock'); fn('foo', 'bar'); - if (isNth(calledWith)) { + if (isToHaveNth(calledWith)) { jestExpect(fn)[calledWith](1, 'foo', 'bar'); expect(() => @@ -990,7 +990,7 @@ describe.each([ 'toReturnWith', 'toHaveReturnedWith', ] as const)('%s', returnedWith => { - function isNth( + function isToHaveNth( returnedWith: string, ): returnedWith is 'nthReturnedWith' | 'toHaveNthReturnedWith' { return ( @@ -999,7 +999,7 @@ describe.each([ ); } - function isLast( + function isToHaveLast( returnedWith: string, ): returnedWith is 'lastReturnedWith' | 'toHaveLastReturnedWith' { return ( @@ -1017,7 +1017,7 @@ describe.each([ test('works when not called', () => { const fn = jest.fn(); - if (isNth(returnedWith)) { + if (isToHaveNth(returnedWith)) { jestExpect(fn).not[returnedWith](1, 'foo'); expect(() => @@ -1036,7 +1036,7 @@ describe.each([ const fn = jest.fn(); fn(); - if (isNth(returnedWith)) { + if (isToHaveNth(returnedWith)) { // @ts-expect-error: TODO should types be fixed? jestExpect(fn)[returnedWith](1); } else { @@ -1049,7 +1049,7 @@ describe.each([ const fn = jest.fn(() => 'foo'); fn(); - if (isNth(returnedWith)) { + if (isToHaveNth(returnedWith)) { jestExpect(fn).not[returnedWith](1, 'bar'); expect(() => @@ -1068,7 +1068,7 @@ describe.each([ const fn = jest.fn(() => 'foo'); fn(); - if (isNth(returnedWith)) { + if (isToHaveNth(returnedWith)) { jestExpect(fn)[returnedWith](1, 'foo'); expect(() => @@ -1087,7 +1087,7 @@ describe.each([ const fn = jest.fn(() => undefined); fn(); - if (isNth(returnedWith)) { + if (isToHaveNth(returnedWith)) { jestExpect(fn)[returnedWith](1, undefined); expect(() => @@ -1119,7 +1119,7 @@ describe.each([ const fn = jest.fn(() => m1); fn(); - if (isNth(returnedWith)) { + if (isToHaveNth(returnedWith)) { jestExpect(fn)[returnedWith](1, m2); jestExpect(fn).not[returnedWith](1, m3); @@ -1150,7 +1150,7 @@ describe.each([ const fn = jest.fn(() => s1); fn(); - if (isNth(returnedWith)) { + if (isToHaveNth(returnedWith)) { jestExpect(fn)[returnedWith](1, s2); jestExpect(fn).not[returnedWith](1, s3); @@ -1178,7 +1178,7 @@ describe.each([ const fn = jest.fn(() => directlyCreated); fn(); - if (isNth(returnedWith)) { + if (isToHaveNth(returnedWith)) { jestExpect(fn)[returnedWith](1, directlyCreated); expect(() => @@ -1198,7 +1198,7 @@ describe.each([ const fn = jest.fn(() => indirectlyCreated); fn(); - if (isNth(returnedWith)) { + if (isToHaveNth(returnedWith)) { jestExpect(fn)[returnedWith](1, indirectlyCreated); expect(() => @@ -1224,7 +1224,7 @@ describe.each([ // ignore error } - if (isNth(returnedWith)) { + if (isToHaveNth(returnedWith)) { // It doesn't matter what return value is tested if the call threw jestExpect(fn).not[returnedWith](1, 'foo'); jestExpect(fn).not[returnedWith](1, null); @@ -1257,7 +1257,7 @@ describe.each([ // ignore error } - if (isNth(returnedWith)) { + if (isToHaveNth(returnedWith)) { // It doesn't matter what return value is tested if the call threw jestExpect(fn).not[returnedWith](1, 'foo'); jestExpect(fn).not[returnedWith](1, null); @@ -1278,7 +1278,7 @@ describe.each([ } }); - if (!isNth(returnedWith)) { + if (!isToHaveNth(returnedWith)) { describe('returnedWith', () => { test('works with more calls than the limit', () => { const fn = jest.fn<() => string>(); @@ -1327,7 +1327,7 @@ describe.each([ }); } - if (isNth(returnedWith)) { + if (isToHaveNth(returnedWith)) { describe('nthReturnedWith', () => { test('works with three calls', () => { const fn = jest.fn<() => string>(); @@ -1448,7 +1448,7 @@ describe.each([ }); } - if (isLast(returnedWith)) { + if (isToHaveLast(returnedWith)) { describe('lastReturnedWith', () => { test('works with three calls', () => { const fn = jest.fn<() => string>(); @@ -1490,7 +1490,7 @@ describe.each([ test('includes the custom mock name in the error message', () => { const fn = jest.fn().mockName('named-mock'); - if (isNth(returnedWith)) { + if (isToHaveNth(returnedWith)) { jestExpect(fn).not[returnedWith](1, 'foo'); expect(() => From 8922dc53c708868403e3ffefe4e7e38989800c23 Mon Sep 17 00:00:00 2001 From: Tom Mrazauskas Date: Tue, 4 Oct 2022 20:59:21 +0300 Subject: [PATCH 6/8] fix one more `any` --- packages/expect/src/__tests__/spyMatchers.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/expect/src/__tests__/spyMatchers.test.ts b/packages/expect/src/__tests__/spyMatchers.test.ts index c2fbd30f2003..2ce5bbd5fee6 100644 --- a/packages/expect/src/__tests__/spyMatchers.test.ts +++ b/packages/expect/src/__tests__/spyMatchers.test.ts @@ -698,7 +698,7 @@ describe.each(['toReturn', 'toHaveReturned'] as const)('%s', returned => { }); test('passes when at least one call does not throw', () => { - const fn = jest.fn(causeError => { + const fn = jest.fn((causeError: boolean) => { if (causeError) { throw new Error('Error!'); } @@ -893,7 +893,7 @@ describe.each(['toReturnTimes', 'toHaveReturnedTimes'] as const)( }); test('calls that throw are not counted', () => { - const fn = jest.fn(causeError => { + const fn = jest.fn((causeError: boolean) => { if (causeError) { throw new Error('Error!'); } @@ -919,7 +919,7 @@ describe.each(['toReturnTimes', 'toHaveReturnedTimes'] as const)( }); test('calls that throw undefined are not counted', () => { - const fn = jest.fn(causeError => { + const fn = jest.fn((causeError: boolean) => { if (causeError) { // eslint-disable-next-line no-throw-literal throw undefined; From c9282e976f430c5e3b235cce96e9b75e0d20a397 Mon Sep 17 00:00:00 2001 From: Tom Mrazauskas Date: Tue, 4 Oct 2022 20:59:39 +0300 Subject: [PATCH 7/8] remove `isolatedModules` --- packages/expect/src/__tests__/symbolInObjects.test.ts | 2 ++ packages/expect/src/__tests__/toEqual-dom.test.ts | 2 ++ tsconfig.test.json | 1 - 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/expect/src/__tests__/symbolInObjects.test.ts b/packages/expect/src/__tests__/symbolInObjects.test.ts index 352208adbf59..b4520ac48969 100644 --- a/packages/expect/src/__tests__/symbolInObjects.test.ts +++ b/packages/expect/src/__tests__/symbolInObjects.test.ts @@ -6,6 +6,8 @@ * */ +import {expect} from '@jest/globals'; + describe('Symbol in objects', () => { test('should compare objects with Symbol keys', () => { const sym = Symbol('foo'); diff --git a/packages/expect/src/__tests__/toEqual-dom.test.ts b/packages/expect/src/__tests__/toEqual-dom.test.ts index 864536dc39ca..c4bc68a6d9be 100644 --- a/packages/expect/src/__tests__/toEqual-dom.test.ts +++ b/packages/expect/src/__tests__/toEqual-dom.test.ts @@ -11,6 +11,8 @@ /* eslint-env browser*/ +import {expect} from '@jest/globals'; + describe('toEqual', () => { describe('duck type', () => { // https://github.com/facebook/jest/issues/7786 diff --git a/tsconfig.test.json b/tsconfig.test.json index 694798ac6c81..b575da7b2516 100644 --- a/tsconfig.test.json +++ b/tsconfig.test.json @@ -3,7 +3,6 @@ "compilerOptions": { "composite": false, "emitDeclarationOnly": false, - "isolatedModules": false, "noEmit": true, "skipLibCheck": true, "types": ["@jest/test-globals"] From 1a81f30892dedaec415f31f79e672d614cea97b9 Mon Sep 17 00:00:00 2001 From: Tom Mrazauskas Date: Tue, 4 Oct 2022 21:02:21 +0300 Subject: [PATCH 8/8] remove TODO --- packages/expect/src/__tests__/spyMatchers.test.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/expect/src/__tests__/spyMatchers.test.ts b/packages/expect/src/__tests__/spyMatchers.test.ts index 2ce5bbd5fee6..d936bf4e3f44 100644 --- a/packages/expect/src/__tests__/spyMatchers.test.ts +++ b/packages/expect/src/__tests__/spyMatchers.test.ts @@ -1037,10 +1037,8 @@ describe.each([ fn(); if (isToHaveNth(returnedWith)) { - // @ts-expect-error: TODO should types be fixed? jestExpect(fn)[returnedWith](1); } else { - // @ts-expect-error: TODO should types be fixed? jestExpect(fn)[returnedWith](); } });