From db159b1abc1590c8f211fbdb7a3caae9d308aa22 Mon Sep 17 00:00:00 2001 From: IDRISSI Mohamed Date: Fri, 22 Nov 2024 12:22:31 +0100 Subject: [PATCH 01/20] Fix "Edit this page" links Already proposed a fix in [this commit](https://github.com/wheresrhys/fetch-mock/commit/649e7e54e8278aa1a142a90a1597827ed4b286ff) but didn't notice that the **docs** folder has a nested **docs** folder --- docs/docusaurus.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index c8855b0c..1b287d47 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -39,7 +39,7 @@ const config = { sidebarPath: './sidebars.js', // Please change this to your repo. // Remove this to remove the "edit this page" links. - editUrl: 'https://github.com/wheresrhys/fetch-mock/edit/main', + editUrl: 'https://github.com/wheresrhys/fetch-mock/edit/main/docs', }, theme: { customCss: './src/css/custom.css', From 033048a47ffc07508fc0cb2ce79078b4facb86fb Mon Sep 17 00:00:00 2001 From: hanseltime Date: Tue, 26 Nov 2024 13:49:50 -0700 Subject: [PATCH 02/20] fix: adding types to jest matchers --- packages/jest/src/index.ts | 16 +++++ packages/jest/src/jest-extensions.ts | 38 +++++++----- packages/jest/src/types.ts | 91 ++++++++++++++++++++++++++++ 3 files changed, 130 insertions(+), 15 deletions(-) create mode 100644 packages/jest/src/types.ts diff --git a/packages/jest/src/index.ts b/packages/jest/src/index.ts index 81b1e257..7c78ca2d 100644 --- a/packages/jest/src/index.ts +++ b/packages/jest/src/index.ts @@ -5,6 +5,8 @@ import { } from 'fetch-mock'; import './jest-extensions.js'; import type { Jest } from '@jest/environment'; +import type { FetchMockMatchers } from './types.js'; +export { FetchMockMatchers } from './types.js'; type MockResetOptions = { includeSticky: boolean; @@ -55,3 +57,17 @@ const fetchMockJest = new FetchMockJest({ }); export default fetchMockJest; + +/* eslint-disable @typescript-eslint/no-namespace */ +/** + * Export types on the expect object + */ +declare global { + namespace jest { + // Type-narrow expect for FetchMock + interface Expect { + (actual: FetchMock): FetchMockMatchers; + } + } +} +/* eslint-enable @typescript-eslint/no-namespace */ diff --git a/packages/jest/src/jest-extensions.ts b/packages/jest/src/jest-extensions.ts index 09b927be..5e817583 100644 --- a/packages/jest/src/jest-extensions.ts +++ b/packages/jest/src/jest-extensions.ts @@ -6,7 +6,16 @@ import type { CallHistoryFilter, UserRouteConfig, } from 'fetch-mock'; -const methodlessExtensions = { +import { + HumanVerbMethodNames, + HumanVerbs, + RawFetchMockMatchers, +} from './types.js'; + +const methodlessExtensions: Pick< + RawFetchMockMatchers, + HumanVerbMethodNames<'Fetched'> +> = { toHaveFetched: ( { fetchMock }: { fetchMock: FetchMock }, filter: CallHistoryFilter, @@ -128,25 +137,24 @@ function scopeExpectationNameToMethod(name: string, humanVerb: string): string { return name.replace('Fetched', humanVerb); } -[ - 'Got:get', - 'Posted:post', - 'Put:put', - 'Deleted:delete', - 'FetchedHead:head', - 'Patched:patch', -].forEach((verbs) => { - const [humanVerb, method] = verbs.split(':'); +const expectMethodNameToMethodMap: { + [humanVerb in Exclude]: string; +} = { + Got: 'get', + Posted: 'post', + Put: 'put', + Deleted: 'delete', + FetchedHead: 'head', + Patched: 'patch', +}; - const extensions: { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - [key: string]: (...args: any[]) => SyncExpectationResult; - } = Object.fromEntries( +Object.entries(expectMethodNameToMethodMap).forEach(([humanVerb, method]) => { + const extensions = Object.fromEntries( Object.entries(methodlessExtensions).map(([name, func]) => [ scopeExpectationNameToMethod(name, humanVerb), scopeExpectationFunctionToMethod(func, method), ]), - ); + ) as Omit>; expect.extend(extensions); }); diff --git a/packages/jest/src/types.ts b/packages/jest/src/types.ts new file mode 100644 index 00000000..74480178 --- /dev/null +++ b/packages/jest/src/types.ts @@ -0,0 +1,91 @@ +import type { CallHistoryFilter, FetchMock, UserRouteConfig } from 'fetch-mock'; +import type { SyncExpectationResult } from 'expect'; + +export type HumanVerbs = + | 'Got' + | 'Posted' + | 'Put' + | 'Deleted' + | 'FetchedHead' + | 'Patched' + | 'Fetched'; + +/** + * Verify that a particular call for the HTTP method implied in the function name + * has occurred + */ +export type ToHaveFunc = ( + filter: CallHistoryFilter, + options: UserRouteConfig, +) => SyncExpectationResult; + +/** + * Verify that a particular Nth call for the HTTP method implied in the function name + * has occurred + */ +export type ToHaveNthFunc = ( + n: number, + filter: CallHistoryFilter, + options: UserRouteConfig, +) => SyncExpectationResult; + +/** + * Verify that a particular call for the HTTP method implied in the function name + * has been made N times + */ +export type ToHaveTimesFunc = ( + times: number, + filter: CallHistoryFilter, + options: UserRouteConfig, +) => SyncExpectationResult; + +export type FetchMockMatchers = { + toHaveFetched: ToHaveFunc; + toHaveLastFetched: ToHaveFunc; + toHaveFetchedTimes: ToHaveTimesFunc; + toHaveNthFetched: ToHaveNthFunc; + toHaveGot: ToHaveFunc; + toHaveLastGot: ToHaveFunc; + toHaveGotTimes: ToHaveTimesFunc; + toHaveNthGot: ToHaveNthFunc; + toHavePosted: ToHaveFunc; + toHaveLastPosted: ToHaveFunc; + toHavePostedTimes: ToHaveTimesFunc; + toHaveNthPosted: ToHaveNthFunc; + toHavePut: ToHaveFunc; + toHaveLastPut: ToHaveFunc; + toHavePutTimes: ToHaveTimesFunc; + toHaveNthPut: ToHaveNthFunc; + toHaveDeleted: ToHaveFunc; + toHaveLastDeleted: ToHaveFunc; + toHaveDeletedTimes: ToHaveTimesFunc; + toHaveNthDeleted: ToHaveNthFunc; + toHaveFetchedHead: ToHaveFunc; + toHaveLastFetchedHead: ToHaveFunc; + toHaveFetchedHeadTimes: ToHaveTimesFunc; + toHaveNthFetchedHead: ToHaveNthFunc; + toHavePatched: ToHaveFunc; + toHaveLastPatched: ToHaveFunc; + toHavePatchedTimes: ToHaveTimesFunc; + toHaveNthPatched: ToHaveNthFunc; +}; + +// types for use doing some intermediate type checking in extensions to make sure things don't get out of sync +/** + * This type allows us to take the Matcher type and creat another one + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type RawMatcher any> = ( + input: { fetchMock: FetchMock }, + ...args: Parameters +) => ReturnType; + +export type RawFetchMockMatchers = { + [k in keyof FetchMockMatchers]: RawMatcher; +}; + +export type HumanVerbMethodNames = + | `toHave${M}` + | `toHaveLast${M}` + | `toHave${M}Times` + | `toHaveNth${M}`; From 9e41a8165bd2caf2cda1d88615be907fcf6f0bc4 Mon Sep 17 00:00:00 2001 From: hanseltime Date: Tue, 26 Nov 2024 15:45:44 -0700 Subject: [PATCH 03/20] fix: adding jestMock extension support to match documentation --- .../jest/src/__tests__/extensions.spec.js | 131 +++++++++++------- packages/jest/src/index.ts | 1 + packages/jest/src/jest-extensions.ts | 30 +++- packages/jest/src/types.ts | 9 +- 4 files changed, 117 insertions(+), 54 deletions(-) diff --git a/packages/jest/src/__tests__/extensions.spec.js b/packages/jest/src/__tests__/extensions.spec.js index 63f03498..ac834dd3 100644 --- a/packages/jest/src/__tests__/extensions.spec.js +++ b/packages/jest/src/__tests__/extensions.spec.js @@ -3,16 +3,24 @@ import { describe, it, beforeAll, afterAll, expect } from '@jest/globals'; import fetchMockModule from '../index'; const fetchMock = fetchMockModule.default; -describe('expect extensions', () => { - [ - 'Fetched', - 'Got:get', - 'Posted:post', - 'Put:put', - 'Deleted:delete', - 'FetchedHead:head', - 'Patched:patch', - ].forEach((verbs) => { +const humanVerbToMethods = [ + 'Fetched', + 'Got:get', + 'Posted:post', + 'Put:put', + 'Deleted:delete', + 'FetchedHead:head', + 'Patched:patch', +]; + +// initialize a mock here so fetch is patched across all tests +fetchMock.mockGlobal(); + +describe.each([ + ['patched fetch input', fetch], + ['fetchMock input', fetchMock], +])('expect extensions %s', (_str, expectInput) => { + humanVerbToMethods.forEach((verbs) => { const [humanVerb, method] = verbs.split(':'); describe(`${humanVerb} expectations`, () => { describe('when no calls', () => { @@ -21,24 +29,26 @@ describe('expect extensions', () => { }); afterAll(() => fetchMock.mockReset()); it(`toHave${humanVerb} should be falsy`, () => { - expect(fetch).not[`toHave${humanVerb}`]('http://example.com/path'); + expect(expectInput).not[`toHave${humanVerb}`]( + 'http://example.com/path', + ); }); it(`toHaveLast${humanVerb} should be falsy`, () => { - expect(fetch).not[`toHaveLast${humanVerb}`]( + expect(expectInput).not[`toHaveLast${humanVerb}`]( 'http://example.com/path', ); }); it(`toHaveNth${humanVerb} should be falsy`, () => { - expect(fetch).not[`toHaveNth${humanVerb}`]( + expect(expectInput).not[`toHaveNth${humanVerb}`]( 1, 'http://example.com/path', ); }); it(`toHave${humanVerb}Times should be falsy`, () => { - expect(fetch).not[`toHave${humanVerb}Times`]( + expect(expectInput).not[`toHave${humanVerb}Times`]( 1, 'http://example.com/path', ); @@ -63,15 +73,17 @@ describe('expect extensions', () => { afterAll(() => fetchMock.mockReset()); it('matches with just url', () => { - expect(fetch)[`toHave${humanVerb}`]('http://example.com/path'); + expect(expectInput)[`toHave${humanVerb}`]('http://example.com/path'); }); it('matches with fetch-mock matcher', () => { - expect(fetch)[`toHave${humanVerb}`]('begin:http://example.com/path'); + expect(expectInput)[`toHave${humanVerb}`]( + 'begin:http://example.com/path', + ); }); it('matches with matcher and options', () => { - expect(fetch)[`toHave${humanVerb}`]('http://example.com/path', { + expect(expectInput)[`toHave${humanVerb}`]('http://example.com/path', { headers: { test: 'header', }, @@ -79,15 +91,18 @@ describe('expect extensions', () => { }); it("doesn't match if matcher but not options is correct", () => { - expect(fetch).not[`toHave${humanVerb}`]('http://example.com/path', { - headers: { - test: 'not-header', + expect(expectInput).not[`toHave${humanVerb}`]( + 'http://example.com/path', + { + headers: { + test: 'not-header', + }, }, - }); + ); }); it("doesn't match if options but not matcher is correct", () => { - expect(fetch).not[`toHave${humanVerb}`]( + expect(expectInput).not[`toHave${humanVerb}`]( 'http://example-no.com/path', { headers: { @@ -110,25 +125,30 @@ describe('expect extensions', () => { afterAll(() => fetchMock.mockReset()); it('matches with just url', () => { - expect(fetch)[`toHaveLast${humanVerb}`]('http://example.com/path'); + expect(expectInput)[`toHaveLast${humanVerb}`]( + 'http://example.com/path', + ); }); it('matches with fetch-mock matcher', () => { - expect(fetch)[`toHaveLast${humanVerb}`]( + expect(expectInput)[`toHaveLast${humanVerb}`]( 'begin:http://example.com/path', ); }); it('matches with matcher and options', () => { - expect(fetch)[`toHaveLast${humanVerb}`]('http://example.com/path', { - headers: { - test: 'header', + expect(expectInput)[`toHaveLast${humanVerb}`]( + 'http://example.com/path', + { + headers: { + test: 'header', + }, }, - }); + ); }); it("doesn't match if matcher but not options is correct", () => { - expect(fetch).not[`toHaveLast${humanVerb}`]( + expect(expectInput).not[`toHaveLast${humanVerb}`]( 'http://example.com/path', { headers: { @@ -139,7 +159,7 @@ describe('expect extensions', () => { }); it("doesn't match if options but not matcher is correct", () => { - expect(fetch).not[`toHaveLast${humanVerb}`]( + expect(expectInput).not[`toHaveLast${humanVerb}`]( 'http://example-no.com/path', { headers: { @@ -169,18 +189,21 @@ describe('expect extensions', () => { afterAll(() => fetchMock.mockReset()); it('matches with just url', () => { - expect(fetch)[`toHaveNth${humanVerb}`](2, 'http://example2.com/path'); + expect(expectInput)[`toHaveNth${humanVerb}`]( + 2, + 'http://example2.com/path', + ); }); it('matches with fetch-mock matcher', () => { - expect(fetch)[`toHaveNth${humanVerb}`]( + expect(expectInput)[`toHaveNth${humanVerb}`]( 2, 'begin:http://example2.com/path', ); }); it('matches with matcher and options', () => { - expect(fetch)[`toHaveNth${humanVerb}`]( + expect(expectInput)[`toHaveNth${humanVerb}`]( 2, 'http://example2.com/path', { @@ -192,7 +215,7 @@ describe('expect extensions', () => { }); it("doesn't match if matcher but not options is correct", () => { - expect(fetch).not[`toHaveNth${humanVerb}`]( + expect(expectInput).not[`toHaveNth${humanVerb}`]( 2, 'http://example2.com/path', { @@ -204,7 +227,7 @@ describe('expect extensions', () => { }); it("doesn't match if options but not matcher is correct", () => { - expect(fetch).not[`toHaveNth${humanVerb}`]( + expect(expectInput).not[`toHaveNth${humanVerb}`]( 2, 'http://example-no.com/path', { @@ -216,7 +239,7 @@ describe('expect extensions', () => { }); it("doesn't match if wrong n", () => { - expect(fetch).not[`toHaveNth${humanVerb}`]( + expect(expectInput).not[`toHaveNth${humanVerb}`]( 1, 'http://example2.com/path', ); @@ -242,21 +265,21 @@ describe('expect extensions', () => { afterAll(() => fetchMock.mockReset()); it('matches with just url', () => { - expect(fetch)[`toHave${humanVerb}Times`]( + expect(expectInput)[`toHave${humanVerb}Times`]( 2, 'http://example.com/path', ); }); it('matches with fetch-mock matcher', () => { - expect(fetch)[`toHave${humanVerb}Times`]( + expect(expectInput)[`toHave${humanVerb}Times`]( 2, 'begin:http://example.com/path', ); }); it('matches with matcher and options', () => { - expect(fetch)[`toHave${humanVerb}Times`]( + expect(expectInput)[`toHave${humanVerb}Times`]( 2, 'http://example.com/path', { @@ -268,7 +291,7 @@ describe('expect extensions', () => { }); it("doesn't match if matcher but not options is correct", () => { - expect(fetch).not[`toHave${humanVerb}Times`]( + expect(expectInput).not[`toHave${humanVerb}Times`]( 2, 'http://example.com/path', { @@ -280,7 +303,7 @@ describe('expect extensions', () => { }); it("doesn't match if options but not matcher is correct", () => { - expect(fetch).not[`toHave${humanVerb}Times`]( + expect(expectInput).not[`toHave${humanVerb}Times`]( 2, 'http://example-no.com/path', { @@ -292,14 +315,14 @@ describe('expect extensions', () => { }); it("doesn't match if too few calls", () => { - expect(fetch).not[`toHave${humanVerb}Times`]( + expect(expectInput).not[`toHave${humanVerb}Times`]( 1, 'http://example.com/path', ); }); it("doesn't match if too many calls", () => { - expect(fetch).not[`toHave${humanVerb}Times`]( + expect(expectInput).not[`toHave${humanVerb}Times`]( 3, 'http://example.com/path', ); @@ -326,15 +349,29 @@ describe('expect extensions', () => { }); afterAll(() => fetchMock.mockReset()); // it('toBeDone should be falsy only if routes defined', () => { - // expect(fetch).not.toBeDone(); - // expect(fetch).not.toBeDone('my-route'); + // expect(expectInput).not.toBeDone(); + // expect(expectInput).not.toBeDone('my-route'); // }); it('matches with just url', () => { - expect(fetch).toBeDone('route1'); + expect(expectInput).toBeDone('route1'); }); it("doesn't match if too few calls", () => { - expect(fetch).not.toBeDone('route2'); + expect(expectInput).not.toBeDone('route2'); + }); + }); +}); + +describe('expect extensions: bad inputs', () => { + humanVerbToMethods.forEach((verbs) => { + const [humanVerb] = verbs.split(':'); + it(`${humanVerb} - throws an error if we the input is not patched with fetchMock`, () => { + expect(() => { + // This simulates a "fetch" implementation that doesn't have fetchMock + expect({})[`toHave${humanVerb}`]('http://example.com/path'); + }).toThrow( + 'Unable to get fetchMock instance! Please make sure you passed a patched fetch or fetchMock!', + ); }); }); }); diff --git a/packages/jest/src/index.ts b/packages/jest/src/index.ts index 7c78ca2d..0cfc3508 100644 --- a/packages/jest/src/index.ts +++ b/packages/jest/src/index.ts @@ -67,6 +67,7 @@ declare global { // Type-narrow expect for FetchMock interface Expect { (actual: FetchMock): FetchMockMatchers; + (actual: typeof fetch): FetchMockMatchers; } } } diff --git a/packages/jest/src/jest-extensions.ts b/packages/jest/src/jest-extensions.ts index 5e817583..74a1c7fe 100644 --- a/packages/jest/src/jest-extensions.ts +++ b/packages/jest/src/jest-extensions.ts @@ -1,6 +1,6 @@ import { expect } from '@jest/globals'; import type { SyncExpectationResult } from 'expect'; -import type { +import { FetchMock, RouteName, CallHistoryFilter, @@ -9,18 +9,32 @@ import type { import { HumanVerbMethodNames, HumanVerbs, + PatchedFetch, RawFetchMockMatchers, } from './types.js'; +function getFetchMockFromInput(input: PatchedFetch | FetchMock) { + const fetchMock = (input as PatchedFetch)['fetchMock'] + ? (input as PatchedFetch).fetchMock + : input; + if (!fetchMock || !(fetchMock instanceof FetchMock)) { + throw new Error( + 'Unable to get fetchMock instance! Please make sure you passed a patched fetch or fetchMock!', + ); + } + return fetchMock; +} + const methodlessExtensions: Pick< RawFetchMockMatchers, HumanVerbMethodNames<'Fetched'> > = { toHaveFetched: ( - { fetchMock }: { fetchMock: FetchMock }, + input: PatchedFetch | FetchMock, filter: CallHistoryFilter, options: UserRouteConfig, ): SyncExpectationResult => { + const fetchMock = getFetchMockFromInput(input); if (fetchMock.callHistory.called(filter, options)) { return { pass: true, message: () => 'fetch was called as expected' }; } @@ -31,10 +45,11 @@ const methodlessExtensions: Pick< }; }, toHaveLastFetched: ( - { fetchMock }: { fetchMock: FetchMock }, + input: PatchedFetch | FetchMock, filter: CallHistoryFilter, options: UserRouteConfig, ): SyncExpectationResult => { + const fetchMock = getFetchMockFromInput(input); const allCalls = fetchMock.callHistory.calls(); if (!allCalls.length) { return { @@ -57,11 +72,12 @@ const methodlessExtensions: Pick< }, toHaveNthFetched: ( - { fetchMock }: { fetchMock: FetchMock }, + input: PatchedFetch | FetchMock, n: number, filter: CallHistoryFilter, options: UserRouteConfig, ): SyncExpectationResult => { + const fetchMock = getFetchMockFromInput(input); const nthCall = fetchMock.callHistory.calls()[n - 1]; const matchingCalls = fetchMock.callHistory.calls(filter, options); if (matchingCalls.some((call) => call === nthCall)) { @@ -78,11 +94,12 @@ const methodlessExtensions: Pick< }, toHaveFetchedTimes: ( - { fetchMock }: { fetchMock: FetchMock }, + input: PatchedFetch | FetchMock, times: number, filter: CallHistoryFilter, options: UserRouteConfig, ): SyncExpectationResult => { + const fetchMock = getFetchMockFromInput(input); const calls = fetchMock.callHistory.calls(filter, options); if (calls.length === times) { return { @@ -102,9 +119,10 @@ expect.extend(methodlessExtensions); expect.extend({ toBeDone: ( - { fetchMock }: { fetchMock: FetchMock }, + input: PatchedFetch | FetchMock, routes: RouteName | RouteName[], ): SyncExpectationResult => { + const fetchMock = getFetchMockFromInput(input); const done = fetchMock.callHistory.done(routes); if (done) { return { pass: true, message: () => '' }; diff --git a/packages/jest/src/types.ts b/packages/jest/src/types.ts index 74480178..18509a05 100644 --- a/packages/jest/src/types.ts +++ b/packages/jest/src/types.ts @@ -71,12 +71,19 @@ export type FetchMockMatchers = { }; // types for use doing some intermediate type checking in extensions to make sure things don't get out of sync +/** + * This reflects the Object.assign that FetchMock does on the fetch function + */ +export type PatchedFetch = { + fetchMock: FetchMock; +}; + /** * This type allows us to take the Matcher type and creat another one */ // eslint-disable-next-line @typescript-eslint/no-explicit-any type RawMatcher any> = ( - input: { fetchMock: FetchMock }, + input: PatchedFetch | FetchMock, ...args: Parameters ) => ReturnType; From 68b24e74f508a42dcfa795c040019eff446281d6 Mon Sep 17 00:00:00 2001 From: hanseltime Date: Tue, 26 Nov 2024 16:20:32 -0700 Subject: [PATCH 04/20] fix: add not type definitions --- packages/jest/src/index.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/jest/src/index.ts b/packages/jest/src/index.ts index 0cfc3508..411f2c0a 100644 --- a/packages/jest/src/index.ts +++ b/packages/jest/src/index.ts @@ -66,8 +66,12 @@ declare global { namespace jest { // Type-narrow expect for FetchMock interface Expect { - (actual: FetchMock): FetchMockMatchers; - (actual: typeof fetch): FetchMockMatchers; + (actual: FetchMock): FetchMockMatchers & { + not: FetchMockMatchers; + }; + (actual: typeof fetch): FetchMockMatchers & { + not: FetchMockMatchers; + }; } } } From 67c672f1335538948e1f2d35d08dc1893eea26e1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 29 Nov 2024 12:18:37 +0000 Subject: [PATCH 05/20] chore: release main --- .release-please-manifest.json | 2 +- package-lock.json | 2 +- packages/jest/CHANGELOG.md | 9 +++++++++ packages/jest/package.json | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index e87620c2..46cf827a 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,6 +1,6 @@ { "packages/fetch-mock": "12.2.0", "packages/vitest": "0.2.6", - "packages/jest": "0.2.6", + "packages/jest": "0.2.7", "packages/codemods": "0.1.2" } diff --git a/package-lock.json b/package-lock.json index fe3fa365..69eec241 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28539,7 +28539,7 @@ }, "packages/jest": { "name": "@fetch-mock/jest", - "version": "0.2.6", + "version": "0.2.7", "license": "MIT", "dependencies": { "fetch-mock": "^12.2.0" diff --git a/packages/jest/CHANGELOG.md b/packages/jest/CHANGELOG.md index 3e428351..ae6def53 100644 --- a/packages/jest/CHANGELOG.md +++ b/packages/jest/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## [0.2.7](https://github.com/wheresrhys/fetch-mock/compare/jest-v0.2.6...jest-v0.2.7) (2024-11-29) + + +### Bug Fixes + +* add not type definitions ([68b24e7](https://github.com/wheresrhys/fetch-mock/commit/68b24e74f508a42dcfa795c040019eff446281d6)) +* adding jestMock extension support to match documentation ([9e41a81](https://github.com/wheresrhys/fetch-mock/commit/9e41a8165bd2caf2cda1d88615be907fcf6f0bc4)) +* adding types to jest matchers ([033048a](https://github.com/wheresrhys/fetch-mock/commit/033048a47ffc07508fc0cb2ce79078b4facb86fb)) + ## [0.2.6](https://github.com/wheresrhys/fetch-mock/compare/jest-v0.2.5...jest-v0.2.6) (2024-11-15) diff --git a/packages/jest/package.json b/packages/jest/package.json index 9a46cd0a..912cfd8a 100644 --- a/packages/jest/package.json +++ b/packages/jest/package.json @@ -1,7 +1,7 @@ { "name": "@fetch-mock/jest", "description": "jest wrapper for fetch-mock", - "version": "0.2.6", + "version": "0.2.7", "exports": { "browser": "./dist/esm/index.js", "import": { From 093c990c56f919e3e06f71baec42dbd159573d5b Mon Sep 17 00:00:00 2001 From: Rhys Evans Date: Fri, 29 Nov 2024 12:20:49 +0000 Subject: [PATCH 06/20] docs: document that fetch can be passed into expectations in jest wrapper --- docs/docs/wrappers/index.md | 3 +++ docs/docs/wrappers/jest.md | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/docs/wrappers/index.md b/docs/docs/wrappers/index.md index 96e06bce..207086a0 100644 --- a/docs/docs/wrappers/index.md +++ b/docs/docs/wrappers/index.md @@ -2,3 +2,6 @@ sidebar_label: Wrappers sidebar_position: 8 --- + +- [@fetch-mock/jest](/fetch-mock/docs/wrappers/jest) +- [@fetch-mock/vitest](/fetch-mock/docs/wrappers/vitest) diff --git a/docs/docs/wrappers/jest.md b/docs/docs/wrappers/jest.md index 346b9e71..292548d2 100644 --- a/docs/docs/wrappers/jest.md +++ b/docs/docs/wrappers/jest.md @@ -70,7 +70,7 @@ Note that these **will not** clear any sticky routes added to fetchMock. You wil ### Expect extensions -These are added to jest automatically and are available on any expect call that is passed fetchMock as an argument. Their behaviour is similar to the jest expectation methods mentioned in the comments below +These are added to jest automatically and are available on any expect call that is passed `fetchMock` (or `fetch`, if it has been mocked globally by fetchMock) as an argument. Their behaviour is similar to the jest expectation methods mentioned in the comments below ```js expect(fetchMock).toHaveFetched(filter, options); // .toHaveBeenCalled()/.toHaveBeenCalledWith() From 872beabb189611c978e647f0af83b6c967dbd38b Mon Sep 17 00:00:00 2001 From: Rhys Evans Date: Tue, 3 Dec 2024 15:04:05 +0000 Subject: [PATCH 07/20] docs: added more docs on how methods no longer mock fetch by default --- docs/docs/API/more-routing-methods.md | 2 ++ docs/docs/Usage/upgrade-guide.md | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/docs/docs/API/more-routing-methods.md b/docs/docs/API/more-routing-methods.md index 5ebca9ca..d1554efc 100644 --- a/docs/docs/API/more-routing-methods.md +++ b/docs/docs/API/more-routing-methods.md @@ -7,6 +7,8 @@ sidebar_label: More routing methods These methods allow defining routes for common use cases while avoiding writing hard to read configuration objects. They all return the fetchMock instance, and are therefor chainable. Unless noted otherwise, each of the methods below have the same signature as `.route()`. +> Note that in older versions of fetch-mock these methods also replaced global `fetch` with your mock. From version 12 onwards this is no longer the case, and you will need to start your tests with `fetchMock.mockGlobal()` or similar. [Read the mocking and spying docs](/fetch-mock/docs/API/mocking-and-spying). + ## .catch() `.catch(response)` diff --git a/docs/docs/Usage/upgrade-guide.md b/docs/docs/Usage/upgrade-guide.md index e1096e6b..e2528ba7 100644 --- a/docs/docs/Usage/upgrade-guide.md +++ b/docs/docs/Usage/upgrade-guide.md @@ -16,6 +16,10 @@ Previously this was true in browsers, but in node.js the node-fetch library was This combined adding a route with mocking the global instance of `fetch`. These are now split into 2 methods: `.route()` and `.mockGlobal()`. +### Routing convenience methods no longer mock `fetch` + +Similar to the last point, `.once()`, `.get()`, `.post()`, `getOnce()`, `postOnce()` and all other routing convenience methods no longer mock the global instance of `fetch`, and you will need to call `.mockGlobal()` explicitly. + ### Reset methods changed `.reset()`, `.restore()`, `.resetBehavior()` and `.resetHistory()` have been removed and replaced with [methods that are more granular and clearly named](/fetch-mock/docs/API/resetting). Note that the [jest](/fetch-mock/docs/wrappers/jest) and [vitest](/fetch-mock/docs/wrappers/vitest) wrappers for fetch-mock still implement `.mockClear()`, `mockReset()` and `mockRestore()`. From 28b4584dfbd87aa88aa5a2e12806357abec5286a Mon Sep 17 00:00:00 2001 From: Jamy Golden Date: Wed, 4 Dec 2024 12:26:55 +0100 Subject: [PATCH 08/20] Fix docks/API/route example syntax error --- docs/docs/API/route/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/API/route/index.md b/docs/docs/API/route/index.md index b0a162e4..54aeb361 100644 --- a/docs/docs/API/route/index.md +++ b/docs/docs/API/route/index.md @@ -108,7 +108,7 @@ fetchMock .route('*', 'ok') .route('*', 404) .route('*', {results: []}) - .route('*', {throw: new Error('Bad kitty'))) + .route('*', {throw: new Error('Bad kitty')}) .route('*', new Promise(res => setTimeout(res, 1000, 404))) .route('*', (url, opts) => { status: 302, From 59d25eb3afc0c13a87560100dcbcbf523cb9282a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 15 Dec 2024 13:43:14 +0000 Subject: [PATCH 09/20] build(deps): bump nanoid from 3.3.7 to 3.3.8 Bumps [nanoid](https://github.com/ai/nanoid) from 3.3.7 to 3.3.8. - [Release notes](https://github.com/ai/nanoid/releases) - [Changelog](https://github.com/ai/nanoid/blob/main/CHANGELOG.md) - [Commits](https://github.com/ai/nanoid/compare/3.3.7...3.3.8) --- updated-dependencies: - dependency-name: nanoid dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 69eec241..09022eb9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20946,16 +20946,15 @@ "link": true }, "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, From 9d47c333a097ed9d1bd68f24bd745d200f3982b3 Mon Sep 17 00:00:00 2001 From: Yihao Gao Date: Wed, 25 Dec 2024 23:31:15 +1100 Subject: [PATCH 10/20] fix: incorrect Jest extension TypeScript type --- package-lock.json | 2 +- package.json | 1 + ...{extensions.spec.js => extensions.spec.ts} | 101 ++++++++++++++---- packages/jest/src/index.ts | 5 + packages/jest/src/jest-extensions.ts | 2 +- packages/jest/src/types.ts | 99 +++++++++-------- 6 files changed, 143 insertions(+), 67 deletions(-) rename packages/jest/src/__tests__/{extensions.spec.js => extensions.spec.ts} (77%) diff --git a/package-lock.json b/package-lock.json index 09022eb9..b53f6512 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,6 +31,7 @@ "eslint": "9.14.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.2.1", + "expect-type": "^1.1.0", "globals": "^15.9.0", "husky": "^9.0.11", "jest": "^29.7.0", @@ -12852,7 +12853,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.1.0.tgz", "integrity": "sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==", - "license": "Apache-2.0", "engines": { "node": ">=12.0.0" } diff --git a/package.json b/package.json index 03e9f9a7..3cd4efba 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ "eslint": "9.14.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.2.1", + "expect-type": "^1.1.0", "globals": "^15.9.0", "husky": "^9.0.11", "jest": "^29.7.0", diff --git a/packages/jest/src/__tests__/extensions.spec.js b/packages/jest/src/__tests__/extensions.spec.ts similarity index 77% rename from packages/jest/src/__tests__/extensions.spec.js rename to packages/jest/src/__tests__/extensions.spec.ts index ac834dd3..5c25b72c 100644 --- a/packages/jest/src/__tests__/extensions.spec.js +++ b/packages/jest/src/__tests__/extensions.spec.ts @@ -1,17 +1,17 @@ -import { describe, it, beforeAll, afterAll, expect } from '@jest/globals'; +import { afterAll, beforeAll, describe, expect, it } from '@jest/globals'; -import fetchMockModule from '../index'; -const fetchMock = fetchMockModule.default; +import fetchMock from '../index'; +import { expectTypeOf } from 'expect-type'; const humanVerbToMethods = [ - 'Fetched', - 'Got:get', - 'Posted:post', - 'Put:put', - 'Deleted:delete', - 'FetchedHead:head', - 'Patched:patch', -]; + { humanVerb: 'Fetched', method: 'get' }, + { humanVerb: 'Got', method: 'get' }, + { humanVerb: 'Posted', method: 'post' }, + { humanVerb: 'Put', method: 'put' }, + { humanVerb: 'Deleted', method: 'delete' }, + { humanVerb: 'FetchedHead', method: 'head' }, + { humanVerb: 'Patched', method: 'patch' }, +] as const; // initialize a mock here so fetch is patched across all tests fetchMock.mockGlobal(); @@ -20,8 +20,7 @@ describe.each([ ['patched fetch input', fetch], ['fetchMock input', fetchMock], ])('expect extensions %s', (_str, expectInput) => { - humanVerbToMethods.forEach((verbs) => { - const [humanVerb, method] = verbs.split(':'); + humanVerbToMethods.forEach(({ humanVerb, method }) => { describe(`${humanVerb} expectations`, () => { describe('when no calls', () => { beforeAll(() => { @@ -29,18 +28,21 @@ describe.each([ }); afterAll(() => fetchMock.mockReset()); it(`toHave${humanVerb} should be falsy`, () => { + expect(expectInput).not[`toHave${humanVerb}`](); expect(expectInput).not[`toHave${humanVerb}`]( 'http://example.com/path', ); }); it(`toHaveLast${humanVerb} should be falsy`, () => { + expect(expectInput).not[`toHaveLast${humanVerb}`](); expect(expectInput).not[`toHaveLast${humanVerb}`]( 'http://example.com/path', ); }); it(`toHaveNth${humanVerb} should be falsy`, () => { + expect(expectInput).not[`toHaveNth${humanVerb}`](1); expect(expectInput).not[`toHaveNth${humanVerb}`]( 1, 'http://example.com/path', @@ -48,6 +50,7 @@ describe.each([ }); it(`toHave${humanVerb}Times should be falsy`, () => { + expect(expectInput).not[`toHave${humanVerb}Times`](1); expect(expectInput).not[`toHave${humanVerb}Times`]( 1, 'http://example.com/path', @@ -58,13 +61,13 @@ describe.each([ beforeAll(() => { fetchMock.mockGlobal().route('*', 200); fetch('http://example.com/path2', { - method: method || 'get', + method, headers: { test: 'header', }, }); fetch('http://example.com/path', { - method: method || 'get', + method, headers: { test: 'header', }, @@ -72,6 +75,10 @@ describe.each([ }); afterAll(() => fetchMock.mockReset()); + it('matches without any matcher supplied', () => { + expect(expectInput)[`toHave${humanVerb}`](); + }); + it('matches with just url', () => { expect(expectInput)[`toHave${humanVerb}`]('http://example.com/path'); }); @@ -111,12 +118,19 @@ describe.each([ }, ); }); + + it('should not be any', () => { + expectTypeOf(expect(expectInput)[`toHave${humanVerb}`]).not.toBeAny(); + expectTypeOf( + expect(expectInput).not[`toHave${humanVerb}`], + ).not.toBeAny(); + }); }); describe(`toHaveLast${humanVerb}`, () => { beforeAll(() => { fetchMock.mockGlobal().route('*', 200); fetch('http://example.com/path', { - method: method || 'get', + method, headers: { test: 'header', }, @@ -124,6 +138,10 @@ describe.each([ }); afterAll(() => fetchMock.mockReset()); + it('matches without any matcher supplied', () => { + expect(expectInput)[`toHaveLast${humanVerb}`](); + }); + it('matches with just url', () => { expect(expectInput)[`toHaveLast${humanVerb}`]( 'http://example.com/path', @@ -168,19 +186,28 @@ describe.each([ }, ); }); + + it('should not be any', () => { + expectTypeOf( + expect(expectInput)[`toHaveLast${humanVerb}`], + ).not.toBeAny(); + expectTypeOf( + expect(expectInput).not[`toHaveLast${humanVerb}`], + ).not.toBeAny(); + }); }); describe(`toHaveNth${humanVerb}`, () => { beforeAll(() => { fetchMock.mockGlobal().route('*', 200); fetch('http://example1.com/path', { - method: method || 'get', + method, headers: { test: 'header', }, }); fetch('http://example2.com/path', { - method: method || 'get', + method, headers: { test: 'header', }, @@ -188,6 +215,10 @@ describe.each([ }); afterAll(() => fetchMock.mockReset()); + it('matches without any matcher supplied', () => { + expect(expectInput)[`toHaveNth${humanVerb}`](2); + }); + it('matches with just url', () => { expect(expectInput)[`toHaveNth${humanVerb}`]( 2, @@ -244,19 +275,28 @@ describe.each([ 'http://example2.com/path', ); }); + + it('should not be any', () => { + expectTypeOf( + expect(expectInput)[`toHaveNth${humanVerb}`], + ).not.toBeAny(); + expectTypeOf( + expect(expectInput).not[`toHaveNth${humanVerb}`], + ).not.toBeAny(); + }); }); describe(`toHave${humanVerb}Times`, () => { beforeAll(() => { fetchMock.mockGlobal().route('*', 200); fetch('http://example.com/path', { - method: method || 'get', + method, headers: { test: 'header', }, }); fetch('http://example.com/path', { - method: method || 'get', + method, headers: { test: 'header', }, @@ -264,6 +304,10 @@ describe.each([ }); afterAll(() => fetchMock.mockReset()); + it('matches without any matcher supplied', () => { + expect(expectInput)[`toHave${humanVerb}Times`](2); + }); + it('matches with just url', () => { expect(expectInput)[`toHave${humanVerb}Times`]( 2, @@ -327,6 +371,15 @@ describe.each([ 'http://example.com/path', ); }); + + it('should not be any', () => { + expectTypeOf( + expect(expectInput)[`toHave${humanVerb}Times`], + ).not.toBeAny(); + expectTypeOf( + expect(expectInput).not[`toHave${humanVerb}Times`], + ).not.toBeAny(); + }); }); }); }); @@ -359,12 +412,16 @@ describe.each([ it("doesn't match if too few calls", () => { expect(expectInput).not.toBeDone('route2'); }); + + it('should not be any', () => { + expectTypeOf(expect(expectInput).toBeDone).not.toBeAny(); + expectTypeOf(expect(expectInput).not.toBeDone).not.toBeAny(); + }); }); }); describe('expect extensions: bad inputs', () => { - humanVerbToMethods.forEach((verbs) => { - const [humanVerb] = verbs.split(':'); + humanVerbToMethods.forEach(({ humanVerb }) => { it(`${humanVerb} - throws an error if we the input is not patched with fetchMock`, () => { expect(() => { // This simulates a "fetch" implementation that doesn't have fetchMock diff --git a/packages/jest/src/index.ts b/packages/jest/src/index.ts index 411f2c0a..5f695a9e 100644 --- a/packages/jest/src/index.ts +++ b/packages/jest/src/index.ts @@ -76,3 +76,8 @@ declare global { } } /* eslint-enable @typescript-eslint/no-namespace */ + +declare module '@jest/expect' { + // eslint-disable-next-line @typescript-eslint/no-empty-object-type + interface Matchers extends FetchMockMatchers {} +} diff --git a/packages/jest/src/jest-extensions.ts b/packages/jest/src/jest-extensions.ts index 74a1c7fe..ca1d12fc 100644 --- a/packages/jest/src/jest-extensions.ts +++ b/packages/jest/src/jest-extensions.ts @@ -172,7 +172,7 @@ Object.entries(expectMethodNameToMethodMap).forEach(([humanVerb, method]) => { scopeExpectationNameToMethod(name, humanVerb), scopeExpectationFunctionToMethod(func, method), ]), - ) as Omit>; + ) as Omit | 'toBeDone'>; expect.extend(extensions); }); diff --git a/packages/jest/src/types.ts b/packages/jest/src/types.ts index 18509a05..1e9d1fdf 100644 --- a/packages/jest/src/types.ts +++ b/packages/jest/src/types.ts @@ -1,4 +1,9 @@ -import type { CallHistoryFilter, FetchMock, UserRouteConfig } from 'fetch-mock'; +import type { + CallHistoryFilter, + FetchMock, + RouteName, + UserRouteConfig, +} from 'fetch-mock'; import type { SyncExpectationResult } from 'expect'; export type HumanVerbs = @@ -14,60 +19,66 @@ export type HumanVerbs = * Verify that a particular call for the HTTP method implied in the function name * has occurred */ -export type ToHaveFunc = ( - filter: CallHistoryFilter, - options: UserRouteConfig, -) => SyncExpectationResult; +export type ToHaveFunc = ( + filter?: CallHistoryFilter, + options?: UserRouteConfig, +) => R; /** * Verify that a particular Nth call for the HTTP method implied in the function name * has occurred */ -export type ToHaveNthFunc = ( +export type ToHaveNthFunc = ( n: number, - filter: CallHistoryFilter, - options: UserRouteConfig, -) => SyncExpectationResult; + filter?: CallHistoryFilter, + options?: UserRouteConfig, +) => R; /** * Verify that a particular call for the HTTP method implied in the function name * has been made N times */ -export type ToHaveTimesFunc = ( +export type ToHaveTimesFunc = ( times: number, - filter: CallHistoryFilter, - options: UserRouteConfig, -) => SyncExpectationResult; + filter?: CallHistoryFilter, + options?: UserRouteConfig, +) => R; -export type FetchMockMatchers = { - toHaveFetched: ToHaveFunc; - toHaveLastFetched: ToHaveFunc; - toHaveFetchedTimes: ToHaveTimesFunc; - toHaveNthFetched: ToHaveNthFunc; - toHaveGot: ToHaveFunc; - toHaveLastGot: ToHaveFunc; - toHaveGotTimes: ToHaveTimesFunc; - toHaveNthGot: ToHaveNthFunc; - toHavePosted: ToHaveFunc; - toHaveLastPosted: ToHaveFunc; - toHavePostedTimes: ToHaveTimesFunc; - toHaveNthPosted: ToHaveNthFunc; - toHavePut: ToHaveFunc; - toHaveLastPut: ToHaveFunc; - toHavePutTimes: ToHaveTimesFunc; - toHaveNthPut: ToHaveNthFunc; - toHaveDeleted: ToHaveFunc; - toHaveLastDeleted: ToHaveFunc; - toHaveDeletedTimes: ToHaveTimesFunc; - toHaveNthDeleted: ToHaveNthFunc; - toHaveFetchedHead: ToHaveFunc; - toHaveLastFetchedHead: ToHaveFunc; - toHaveFetchedHeadTimes: ToHaveTimesFunc; - toHaveNthFetchedHead: ToHaveNthFunc; - toHavePatched: ToHaveFunc; - toHaveLastPatched: ToHaveFunc; - toHavePatchedTimes: ToHaveTimesFunc; - toHaveNthPatched: ToHaveNthFunc; +/** + * Verify that a particular route names(s) has been called + */ +export type ToBeDoneFunc = (routes?: RouteName | RouteName[]) => R; + +export type FetchMockMatchers = { + toHaveFetched: ToHaveFunc; + toHaveLastFetched: ToHaveFunc; + toHaveFetchedTimes: ToHaveTimesFunc; + toHaveNthFetched: ToHaveNthFunc; + toHaveGot: ToHaveFunc; + toHaveLastGot: ToHaveFunc; + toHaveGotTimes: ToHaveTimesFunc; + toHaveNthGot: ToHaveNthFunc; + toHavePosted: ToHaveFunc; + toHaveLastPosted: ToHaveFunc; + toHavePostedTimes: ToHaveTimesFunc; + toHaveNthPosted: ToHaveNthFunc; + toHavePut: ToHaveFunc; + toHaveLastPut: ToHaveFunc; + toHavePutTimes: ToHaveTimesFunc; + toHaveNthPut: ToHaveNthFunc; + toHaveDeleted: ToHaveFunc; + toHaveLastDeleted: ToHaveFunc; + toHaveDeletedTimes: ToHaveTimesFunc; + toHaveNthDeleted: ToHaveNthFunc; + toHaveFetchedHead: ToHaveFunc; + toHaveLastFetchedHead: ToHaveFunc; + toHaveFetchedHeadTimes: ToHaveTimesFunc; + toHaveNthFetchedHead: ToHaveNthFunc; + toHavePatched: ToHaveFunc; + toHaveLastPatched: ToHaveFunc; + toHavePatchedTimes: ToHaveTimesFunc; + toHaveNthPatched: ToHaveNthFunc; + toBeDone: ToBeDoneFunc; }; // types for use doing some intermediate type checking in extensions to make sure things don't get out of sync @@ -88,7 +99,9 @@ type RawMatcher any> = ( ) => ReturnType; export type RawFetchMockMatchers = { - [k in keyof FetchMockMatchers]: RawMatcher; + [k in keyof FetchMockMatchers]: RawMatcher< + FetchMockMatchers[k] + >; }; export type HumanVerbMethodNames = From 17206d782746e6b9186054f3af5ce963f653c16a Mon Sep 17 00:00:00 2001 From: Theron Luhn Date: Thu, 9 Jan 2025 11:31:54 -0800 Subject: [PATCH 11/20] Change `lastCall` return type to `CallLog | undefined` --- packages/fetch-mock/src/CallHistory.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/fetch-mock/src/CallHistory.ts b/packages/fetch-mock/src/CallHistory.ts index d796ba13..577bbcc6 100644 --- a/packages/fetch-mock/src/CallHistory.ts +++ b/packages/fetch-mock/src/CallHistory.ts @@ -127,7 +127,7 @@ class CallHistory { called(filter?: CallHistoryFilter, options?: RouteConfig): boolean { return Boolean(this.calls(filter, options).length); } - lastCall(filter?: CallHistoryFilter, options?: RouteConfig): CallLog | void { + lastCall(filter?: CallHistoryFilter, options?: RouteConfig): CallLog | undefined { return this.calls(filter, options).pop(); } From 217f8775632afb6365d09b4e7680a2ecb469c022 Mon Sep 17 00:00:00 2001 From: Theron Luhn Date: Thu, 9 Jan 2025 11:42:28 -0800 Subject: [PATCH 12/20] Lint fix. --- packages/fetch-mock/src/CallHistory.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/fetch-mock/src/CallHistory.ts b/packages/fetch-mock/src/CallHistory.ts index 577bbcc6..5959ab6c 100644 --- a/packages/fetch-mock/src/CallHistory.ts +++ b/packages/fetch-mock/src/CallHistory.ts @@ -127,7 +127,10 @@ class CallHistory { called(filter?: CallHistoryFilter, options?: RouteConfig): boolean { return Boolean(this.calls(filter, options).length); } - lastCall(filter?: CallHistoryFilter, options?: RouteConfig): CallLog | undefined { + lastCall( + filter?: CallHistoryFilter, + options?: RouteConfig, + ): CallLog | undefined { return this.calls(filter, options).pop(); } From 556086f28b8c7f6d28f9c9cf2c3da204d8966f75 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 11 Jan 2025 17:16:36 +0000 Subject: [PATCH 13/20] chore: release main --- .release-please-manifest.json | 2 +- package-lock.json | 2 +- packages/jest/CHANGELOG.md | 7 +++++++ packages/jest/package.json | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 46cf827a..6c7ffef6 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,6 +1,6 @@ { "packages/fetch-mock": "12.2.0", "packages/vitest": "0.2.6", - "packages/jest": "0.2.7", + "packages/jest": "0.2.8", "packages/codemods": "0.1.2" } diff --git a/package-lock.json b/package-lock.json index b53f6512..88747aaa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28538,7 +28538,7 @@ }, "packages/jest": { "name": "@fetch-mock/jest", - "version": "0.2.7", + "version": "0.2.8", "license": "MIT", "dependencies": { "fetch-mock": "^12.2.0" diff --git a/packages/jest/CHANGELOG.md b/packages/jest/CHANGELOG.md index ae6def53..7c860ad6 100644 --- a/packages/jest/CHANGELOG.md +++ b/packages/jest/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [0.2.8](https://github.com/wheresrhys/fetch-mock/compare/jest-v0.2.7...jest-v0.2.8) (2025-01-11) + + +### Bug Fixes + +* incorrect Jest extension TypeScript type ([9d47c33](https://github.com/wheresrhys/fetch-mock/commit/9d47c333a097ed9d1bd68f24bd745d200f3982b3)) + ## [0.2.7](https://github.com/wheresrhys/fetch-mock/compare/jest-v0.2.6...jest-v0.2.7) (2024-11-29) diff --git a/packages/jest/package.json b/packages/jest/package.json index 912cfd8a..cef5d253 100644 --- a/packages/jest/package.json +++ b/packages/jest/package.json @@ -1,7 +1,7 @@ { "name": "@fetch-mock/jest", "description": "jest wrapper for fetch-mock", - "version": "0.2.7", + "version": "0.2.8", "exports": { "browser": "./dist/esm/index.js", "import": { From 027c76210e74243206e94e9ac27e6376a327b3bc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Jan 2025 04:34:56 +0000 Subject: [PATCH 14/20] build(deps): bump vite from 5.4.11 to 5.4.14 Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.4.11 to 5.4.14. - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/v5.4.14/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v5.4.14/packages/vite) --- updated-dependencies: - dependency-name: vite dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 88747aaa..aa54a8da 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27331,9 +27331,9 @@ } }, "node_modules/vite": { - "version": "5.4.11", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz", - "integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==", + "version": "5.4.14", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.14.tgz", + "integrity": "sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==", "license": "MIT", "dependencies": { "esbuild": "^0.21.3", From cb429dd7b132d564a5713ab1ddffca8ad7214ed1 Mon Sep 17 00:00:00 2001 From: Rhys Evans Date: Tue, 28 Jan 2025 16:23:28 +0000 Subject: [PATCH 15/20] test: add failing test for failure to spy in browsers --- .../src/__tests__/FetchMock/mock-and-spy.test.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/fetch-mock/src/__tests__/FetchMock/mock-and-spy.test.js b/packages/fetch-mock/src/__tests__/FetchMock/mock-and-spy.test.js index 36524f0d..7e891600 100644 --- a/packages/fetch-mock/src/__tests__/FetchMock/mock-and-spy.test.js +++ b/packages/fetch-mock/src/__tests__/FetchMock/mock-and-spy.test.js @@ -59,6 +59,7 @@ describe('mock and spy', () => { describe('.spy()', () => { testChainableMethod('spy'); testChainableMethod('spyGlobal'); + it('passes all requests through to the network by default', async () => { vi.spyOn(fm.config, 'fetch'); fm.spy(); @@ -134,5 +135,12 @@ describe('mock and spy', () => { method: 'post', }); }); + + it('can call actual native fetch without erroring', async () => { + fm.spyGlobal(); + await expect( + fm.fetchHandler('http://example.com/'), + ).resolves.toBeInstanceOf(Response); + }); }); }); From bfaa5f33c133af17a0bd097d2d3dbcb01966a0a8 Mon Sep 17 00:00:00 2001 From: Rhys Evans Date: Tue, 28 Jan 2025 16:29:38 +0000 Subject: [PATCH 16/20] fix: fix failure to spy in browsers --- packages/fetch-mock/src/FetchMock.ts | 4 ++-- .../src/__tests__/FetchMock/mock-and-spy.test.js | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/fetch-mock/src/FetchMock.ts b/packages/fetch-mock/src/FetchMock.ts index aad608c0..6f6db09f 100644 --- a/packages/fetch-mock/src/FetchMock.ts +++ b/packages/fetch-mock/src/FetchMock.ts @@ -164,10 +164,10 @@ export class FetchMock { ): FetchMock { if (matcher) { //@ts-expect-error TODO findo out how to overload an overload - this.route(matcher, ({ args }) => this.config.fetch(...args), name); + this.route(matcher, ({ args }) => this.config.fetch.bind(globalThis)(...args), name); } else { //@ts-expect-error TODO findo out how to overload an overload - this.catch(({ args }) => this.config.fetch(...args)); + this.catch(({ args }) => this.config.fetch.bind(globalThis)(...args)); } return this; diff --git a/packages/fetch-mock/src/__tests__/FetchMock/mock-and-spy.test.js b/packages/fetch-mock/src/__tests__/FetchMock/mock-and-spy.test.js index 7e891600..4a7701b6 100644 --- a/packages/fetch-mock/src/__tests__/FetchMock/mock-and-spy.test.js +++ b/packages/fetch-mock/src/__tests__/FetchMock/mock-and-spy.test.js @@ -135,12 +135,12 @@ describe('mock and spy', () => { method: 'post', }); }); - - it('can call actual native fetch without erroring', async () => { - fm.spyGlobal(); - await expect( - fm.fetchHandler('http://example.com/'), - ).resolves.toBeInstanceOf(Response); - }); + const isBrowser = Boolean(globalThis.location); + if (isBrowser) { + it('can call actual native fetch without erroring', async () => { + fm.spyGlobal(); + await expect(fm.fetchHandler('/')).resolves.toBeInstanceOf(Response); + }); + } }); }); From 7ff59159cf3e770249db6b4216c1764291cb8c8d Mon Sep 17 00:00:00 2001 From: Rhys Evans Date: Tue, 28 Jan 2025 16:31:15 +0000 Subject: [PATCH 17/20] fix: force release of @fetch-mock/jest Incorrect commit messages meant last fix was unreleased --- packages/jest/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/jest/CHANGELOG.md b/packages/jest/CHANGELOG.md index 7c860ad6..7aed5ade 100644 --- a/packages/jest/CHANGELOG.md +++ b/packages/jest/CHANGELOG.md @@ -1,5 +1,6 @@ # Changelog + ## [0.2.8](https://github.com/wheresrhys/fetch-mock/compare/jest-v0.2.7...jest-v0.2.8) (2025-01-11) From 44736b82b0543155b308cd5266cfba7fabcf3adf Mon Sep 17 00:00:00 2001 From: Rhys Evans Date: Tue, 28 Jan 2025 16:57:41 +0000 Subject: [PATCH 18/20] test: refine test so it runs in node too --- packages/fetch-mock/src/FetchMock.ts | 14 ++++++++++---- .../src/__tests__/FetchMock/mock-and-spy.test.js | 15 ++++++++------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/packages/fetch-mock/src/FetchMock.ts b/packages/fetch-mock/src/FetchMock.ts index 6f6db09f..c1ce5dc2 100644 --- a/packages/fetch-mock/src/FetchMock.ts +++ b/packages/fetch-mock/src/FetchMock.ts @@ -162,12 +162,18 @@ export class FetchMock { matcher?: RouteMatcher | UserRouteConfig, name?: RouteName, ): FetchMock { + const boundFetch = this.config.fetch.bind(globalThis); if (matcher) { - //@ts-expect-error TODO findo out how to overload an overload - this.route(matcher, ({ args }) => this.config.fetch.bind(globalThis)(...args), name); + this.route( + // @ts-expect-error + matcher, + // @ts-expect-error + ({ args }) => boundFetch(...args), + name, + ); } else { - //@ts-expect-error TODO findo out how to overload an overload - this.catch(({ args }) => this.config.fetch.bind(globalThis)(...args)); + // @ts-expect-error + this.catch(({ args }) => boundFetch(...args)); } return this; diff --git a/packages/fetch-mock/src/__tests__/FetchMock/mock-and-spy.test.js b/packages/fetch-mock/src/__tests__/FetchMock/mock-and-spy.test.js index 4a7701b6..94d53e01 100644 --- a/packages/fetch-mock/src/__tests__/FetchMock/mock-and-spy.test.js +++ b/packages/fetch-mock/src/__tests__/FetchMock/mock-and-spy.test.js @@ -135,12 +135,13 @@ describe('mock and spy', () => { method: 'post', }); }); - const isBrowser = Boolean(globalThis.location); - if (isBrowser) { - it('can call actual native fetch without erroring', async () => { - fm.spyGlobal(); - await expect(fm.fetchHandler('/')).resolves.toBeInstanceOf(Response); - }); - } + + it('can call actual native fetch without erroring', async () => { + fm.spyGlobal(); + const isBrowser = Boolean(globalThis.location); + // avoids getting caught by a cors error + const testUrl = isBrowser ? '/' : 'http://a.com/'; + await expect(fm.fetchHandler(testUrl)).resolves.toBeInstanceOf(Response); + }); }); }); From 0fc590a15b6fb150dc6329364d2e4692661a3bfe Mon Sep 17 00:00:00 2001 From: Rhys Evans Date: Tue, 28 Jan 2025 17:17:49 +0000 Subject: [PATCH 19/20] chore: type errors and linting --- packages/fetch-mock/src/FetchMock.ts | 6 +++--- .../fetch-mock/src/__tests__/FetchMock/mock-and-spy.test.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/fetch-mock/src/FetchMock.ts b/packages/fetch-mock/src/FetchMock.ts index c1ce5dc2..ca54c639 100644 --- a/packages/fetch-mock/src/FetchMock.ts +++ b/packages/fetch-mock/src/FetchMock.ts @@ -165,14 +165,14 @@ export class FetchMock { const boundFetch = this.config.fetch.bind(globalThis); if (matcher) { this.route( - // @ts-expect-error + // @ts-expect-error related to the overloading of .route() matcher, - // @ts-expect-error + // @ts-expect-error this is just args from a fetch call being passed into a bound fetch - no idea why the error ({ args }) => boundFetch(...args), name, ); } else { - // @ts-expect-error + // @ts-expect-error this is just args from a fetch call being passed into a bound fetch - no idea why the error this.catch(({ args }) => boundFetch(...args)); } diff --git a/packages/fetch-mock/src/__tests__/FetchMock/mock-and-spy.test.js b/packages/fetch-mock/src/__tests__/FetchMock/mock-and-spy.test.js index 94d53e01..dd883190 100644 --- a/packages/fetch-mock/src/__tests__/FetchMock/mock-and-spy.test.js +++ b/packages/fetch-mock/src/__tests__/FetchMock/mock-and-spy.test.js @@ -140,7 +140,7 @@ describe('mock and spy', () => { fm.spyGlobal(); const isBrowser = Boolean(globalThis.location); // avoids getting caught by a cors error - const testUrl = isBrowser ? '/' : 'http://a.com/'; + const testUrl = isBrowser ? '/' : 'http://example.com/'; await expect(fm.fetchHandler(testUrl)).resolves.toBeInstanceOf(Response); }); }); From 999a7af9284f24bff54fed671718b5fd883a5b5d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 28 Jan 2025 17:26:07 +0000 Subject: [PATCH 20/20] chore: release main --- .release-please-manifest.json | 6 +++--- package-lock.json | 10 +++++----- packages/fetch-mock/CHANGELOG.md | 7 +++++++ packages/fetch-mock/package.json | 2 +- packages/jest/CHANGELOG.md | 14 ++++++++++++++ packages/jest/package.json | 4 ++-- packages/vitest/CHANGELOG.md | 9 +++++++++ packages/vitest/package.json | 4 ++-- 8 files changed, 43 insertions(+), 13 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 6c7ffef6..cea869c1 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,6 +1,6 @@ { - "packages/fetch-mock": "12.2.0", - "packages/vitest": "0.2.6", - "packages/jest": "0.2.8", + "packages/fetch-mock": "12.2.1", + "packages/vitest": "0.2.7", + "packages/jest": "0.2.9", "packages/codemods": "0.1.2" } diff --git a/package-lock.json b/package-lock.json index 88747aaa..30717457 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28523,7 +28523,7 @@ } }, "packages/fetch-mock": { - "version": "12.2.0", + "version": "12.2.1", "license": "MIT", "dependencies": { "@types/glob-to-regexp": "^0.4.4", @@ -28538,10 +28538,10 @@ }, "packages/jest": { "name": "@fetch-mock/jest", - "version": "0.2.8", + "version": "0.2.9", "license": "MIT", "dependencies": { - "fetch-mock": "^12.2.0" + "fetch-mock": "^12.2.1" }, "engines": { "node": ">=18.11.0" @@ -28553,10 +28553,10 @@ }, "packages/vitest": { "name": "@fetch-mock/vitest", - "version": "0.2.6", + "version": "0.2.7", "license": "MIT", "dependencies": { - "fetch-mock": "^12.2.0" + "fetch-mock": "^12.2.1" }, "engines": { "node": ">=18.11.0" diff --git a/packages/fetch-mock/CHANGELOG.md b/packages/fetch-mock/CHANGELOG.md index 35572ae9..10b55059 100644 --- a/packages/fetch-mock/CHANGELOG.md +++ b/packages/fetch-mock/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [12.2.1](https://github.com/wheresrhys/fetch-mock/compare/fetch-mock-v12.2.0...fetch-mock-v12.2.1) (2025-01-28) + + +### Bug Fixes + +* fix failure to spy in browsers ([bfaa5f3](https://github.com/wheresrhys/fetch-mock/commit/bfaa5f33c133af17a0bd097d2d3dbcb01966a0a8)) + ## [12.2.0](https://github.com/wheresrhys/fetch-mock/compare/fetch-mock-v12.1.0...fetch-mock-v12.2.0) (2024-11-15) diff --git a/packages/fetch-mock/package.json b/packages/fetch-mock/package.json index c8fdef99..ece9e07c 100644 --- a/packages/fetch-mock/package.json +++ b/packages/fetch-mock/package.json @@ -1,7 +1,7 @@ { "name": "fetch-mock", "description": "Mock http requests made using fetch", - "version": "12.2.0", + "version": "12.2.1", "exports": { "browser": "./dist/esm/index.js", "import": { diff --git a/packages/jest/CHANGELOG.md b/packages/jest/CHANGELOG.md index 7aed5ade..46fd1c80 100644 --- a/packages/jest/CHANGELOG.md +++ b/packages/jest/CHANGELOG.md @@ -1,6 +1,20 @@ # Changelog +## [0.2.9](https://github.com/wheresrhys/fetch-mock/compare/jest-v0.2.8...jest-v0.2.9) (2025-01-28) + + +### Bug Fixes + +* force release of @fetch-mock/jest ([7ff5915](https://github.com/wheresrhys/fetch-mock/commit/7ff59159cf3e770249db6b4216c1764291cb8c8d)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * fetch-mock bumped from ^12.2.0 to ^12.2.1 + ## [0.2.8](https://github.com/wheresrhys/fetch-mock/compare/jest-v0.2.7...jest-v0.2.8) (2025-01-11) diff --git a/packages/jest/package.json b/packages/jest/package.json index cef5d253..7ab2f32e 100644 --- a/packages/jest/package.json +++ b/packages/jest/package.json @@ -1,7 +1,7 @@ { "name": "@fetch-mock/jest", "description": "jest wrapper for fetch-mock", - "version": "0.2.8", + "version": "0.2.9", "exports": { "browser": "./dist/esm/index.js", "import": { @@ -21,7 +21,7 @@ "node": ">=18.11.0" }, "dependencies": { - "fetch-mock": "^12.2.0" + "fetch-mock": "^12.2.1" }, "peerDependencies": { "jest": "*", diff --git a/packages/vitest/CHANGELOG.md b/packages/vitest/CHANGELOG.md index f8735b66..878ececb 100644 --- a/packages/vitest/CHANGELOG.md +++ b/packages/vitest/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## [0.2.7](https://github.com/wheresrhys/fetch-mock/compare/vitest-v0.2.6...vitest-v0.2.7) (2025-01-28) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * fetch-mock bumped from ^12.2.0 to ^12.2.1 + ## [0.2.6](https://github.com/wheresrhys/fetch-mock/compare/vitest-v0.2.5...vitest-v0.2.6) (2024-11-15) diff --git a/packages/vitest/package.json b/packages/vitest/package.json index 4e315517..04a67ebf 100644 --- a/packages/vitest/package.json +++ b/packages/vitest/package.json @@ -1,7 +1,7 @@ { "name": "@fetch-mock/vitest", "description": "Vitest wrapper for fetch-mock", - "version": "0.2.6", + "version": "0.2.7", "exports": { "browser": "./dist/esm/index.js", "import": { @@ -21,7 +21,7 @@ "node": ">=18.11.0" }, "dependencies": { - "fetch-mock": "^12.2.0" + "fetch-mock": "^12.2.1" }, "peerDependencies": { "vitest": "*"