From 999ce9257de6683830c8e70dcda3862c3d13699e Mon Sep 17 00:00:00 2001 From: Rhys Evans Date: Sun, 20 Oct 2024 15:09:34 +0100 Subject: [PATCH] feat!: Replaced legacy fetch-mock code with fetch-mock/core code --- package-lock.json | 23 +- packages/core/.npmignore | 3 - packages/core/CHANGELOG.md | 234 ------ packages/core/LICENSE | 21 - packages/core/README.md | 28 - packages/core/package.json | 52 -- packages/fetch-mock/.npmignore | 5 +- packages/fetch-mock/CHANGELOG.md | 192 ++++- packages/fetch-mock/README.md | 53 +- packages/fetch-mock/package.json | 18 +- packages/fetch-mock/rollup.config.js | 49 -- .../{core => fetch-mock}/src/CallHistory.ts | 0 .../{core => fetch-mock}/src/FetchMock.ts | 0 packages/{core => fetch-mock}/src/Matchers.ts | 0 .../{core => fetch-mock}/src/RequestUtils.ts | 0 packages/{core => fetch-mock}/src/Route.ts | 0 packages/fetch-mock/src/Route/index.js | 129 --- packages/fetch-mock/src/Route/matchers.js | 215 ----- packages/{core => fetch-mock}/src/Router.ts | 0 .../{core => fetch-mock}/src/StatusTextMap.ts | 0 .../src/__tests__/CallHistory.test.js | 0 .../src/__tests__/FetchMock/flush.test.js | 0 .../FetchMock/instance-management.test.js | 0 .../__tests__/FetchMock/mock-and-spy.test.js | 0 .../FetchMock/response-construction.test.js | 0 .../FetchMock/response-negotiation.test.js | 0 .../src/__tests__/FetchMock/routing.test.js | 0 .../src/__tests__/Matchers/body.test.js | 0 .../src/__tests__/Matchers/express.test.js | 0 .../src/__tests__/Matchers/function.test.js | 0 .../src/__tests__/Matchers/headers.test.js | 0 .../src/__tests__/Matchers/method.test.js | 0 .../__tests__/Matchers/query-string.test.js | 0 .../Matchers/route-config-object.test.js | 0 .../src/__tests__/Matchers/url.test.js | 0 .../src/__tests__/router-integration.test.js | 0 .../src/__tests__/spec-compliance.test.js | 0 packages/fetch-mock/src/index.js | 13 - packages/{core => fetch-mock}/src/index.ts | 0 packages/fetch-mock/src/lib/fetch-handler.js | 216 ----- packages/fetch-mock/src/lib/index.js | 72 -- packages/fetch-mock/src/lib/inspecting.js | 143 ---- packages/fetch-mock/src/lib/request-utils.js | 118 --- .../fetch-mock/src/lib/response-builder.js | 164 ---- .../src/lib/set-up-and-tear-down.js | 135 --- packages/fetch-mock/src/lib/status-text.js | 66 -- .../fetch-mock/test/fixtures/fetch-proxy.js | 1 - packages/fetch-mock/test/fixtures/sw.js | 15 - .../fetch-mock/test/specs/abortable.test.js | 72 -- .../test/specs/config/constructors.test.js | 100 --- .../specs/config/fallbackToNetwork.test.js | 82 -- .../specs/config/includeContentLength.test.js | 36 - .../specs/config/matchPartialBody.test.js | 41 - .../test/specs/config/overwriteRoutes.test.js | 45 - .../test/specs/config/sendAsJson.test.js | 40 - packages/fetch-mock/test/specs/flush.test.js | 96 --- .../test/specs/global-fetch.test.js | 53 -- .../fetch-mock/test/specs/inspecting.test.js | 589 -------------- packages/fetch-mock/test/specs/repeat.test.js | 228 ------ .../test/specs/responses/client-only.test.js | 78 -- .../test/specs/responses/generation.test.js | 225 ----- .../test/specs/responses/negotiation.js | 170 ---- .../test/specs/routing/body-matching.test.js | 173 ---- .../test/specs/routing/edge-cases.test.js | 76 -- .../specs/routing/function-matching.test.js | 101 --- .../specs/routing/header-matching.test.js | 180 ---- .../test/specs/routing/matcher-object.test.js | 138 ---- .../specs/routing/method-matching.test.js | 61 -- .../specs/routing/multiple-routes.test.js | 123 --- .../test/specs/routing/naming-routes.test.js | 36 - .../routing/path-parameter-matching.test.js | 52 -- .../routing/query-string-matching.test.js | 309 ------- .../specs/routing/unmatched-calls.test.js | 42 - .../test/specs/routing/url-matching.test.js | 208 ----- .../fetch-mock/test/specs/sandbox.test.js | 140 ---- .../test/specs/set-up-and-tear-down.test.js | 202 ----- .../fetch-mock/test/specs/shorthands.test.js | 148 ---- packages/fetch-mock/test/specs/spy.test.js | 59 -- .../test/specs/sticky-routes.test.js | 133 --- .../test/specs/user-defined-matchers.test.js | 79 -- .../{core => fetch-mock}/tsconfig.cjs.json | 0 .../{core => fetch-mock}/tsconfig.esm.json | 0 packages/fetch-mock/types/index.d.ts | 766 ------------------ packages/fetch-mock/types/index.test-d.ts | 206 ----- packages/fetch-mock/types/tsconfig.json | 16 - packages/jest/package.json | 2 +- packages/jest/src/index.ts | 2 +- packages/vitest/package.json | 2 +- packages/vitest/src/index.ts | 2 +- 89 files changed, 187 insertions(+), 6889 deletions(-) delete mode 100644 packages/core/.npmignore delete mode 100644 packages/core/CHANGELOG.md delete mode 100644 packages/core/LICENSE delete mode 100644 packages/core/README.md delete mode 100644 packages/core/package.json delete mode 100644 packages/fetch-mock/rollup.config.js rename packages/{core => fetch-mock}/src/CallHistory.ts (100%) rename packages/{core => fetch-mock}/src/FetchMock.ts (100%) rename packages/{core => fetch-mock}/src/Matchers.ts (100%) rename packages/{core => fetch-mock}/src/RequestUtils.ts (100%) rename packages/{core => fetch-mock}/src/Route.ts (100%) delete mode 100644 packages/fetch-mock/src/Route/index.js delete mode 100644 packages/fetch-mock/src/Route/matchers.js rename packages/{core => fetch-mock}/src/Router.ts (100%) rename packages/{core => fetch-mock}/src/StatusTextMap.ts (100%) rename packages/{core => fetch-mock}/src/__tests__/CallHistory.test.js (100%) rename packages/{core => fetch-mock}/src/__tests__/FetchMock/flush.test.js (100%) rename packages/{core => fetch-mock}/src/__tests__/FetchMock/instance-management.test.js (100%) rename packages/{core => fetch-mock}/src/__tests__/FetchMock/mock-and-spy.test.js (100%) rename packages/{core => fetch-mock}/src/__tests__/FetchMock/response-construction.test.js (100%) rename packages/{core => fetch-mock}/src/__tests__/FetchMock/response-negotiation.test.js (100%) rename packages/{core => fetch-mock}/src/__tests__/FetchMock/routing.test.js (100%) rename packages/{core => fetch-mock}/src/__tests__/Matchers/body.test.js (100%) rename packages/{core => fetch-mock}/src/__tests__/Matchers/express.test.js (100%) rename packages/{core => fetch-mock}/src/__tests__/Matchers/function.test.js (100%) rename packages/{core => fetch-mock}/src/__tests__/Matchers/headers.test.js (100%) rename packages/{core => fetch-mock}/src/__tests__/Matchers/method.test.js (100%) rename packages/{core => fetch-mock}/src/__tests__/Matchers/query-string.test.js (100%) rename packages/{core => fetch-mock}/src/__tests__/Matchers/route-config-object.test.js (100%) rename packages/{core => fetch-mock}/src/__tests__/Matchers/url.test.js (100%) rename packages/{core => fetch-mock}/src/__tests__/router-integration.test.js (100%) rename packages/{core => fetch-mock}/src/__tests__/spec-compliance.test.js (100%) delete mode 100644 packages/fetch-mock/src/index.js rename packages/{core => fetch-mock}/src/index.ts (100%) delete mode 100755 packages/fetch-mock/src/lib/fetch-handler.js delete mode 100644 packages/fetch-mock/src/lib/index.js delete mode 100644 packages/fetch-mock/src/lib/inspecting.js delete mode 100644 packages/fetch-mock/src/lib/request-utils.js delete mode 100644 packages/fetch-mock/src/lib/response-builder.js delete mode 100644 packages/fetch-mock/src/lib/set-up-and-tear-down.js delete mode 100644 packages/fetch-mock/src/lib/status-text.js delete mode 100644 packages/fetch-mock/test/fixtures/fetch-proxy.js delete mode 100644 packages/fetch-mock/test/fixtures/sw.js delete mode 100644 packages/fetch-mock/test/specs/abortable.test.js delete mode 100644 packages/fetch-mock/test/specs/config/constructors.test.js delete mode 100644 packages/fetch-mock/test/specs/config/fallbackToNetwork.test.js delete mode 100644 packages/fetch-mock/test/specs/config/includeContentLength.test.js delete mode 100644 packages/fetch-mock/test/specs/config/matchPartialBody.test.js delete mode 100644 packages/fetch-mock/test/specs/config/overwriteRoutes.test.js delete mode 100644 packages/fetch-mock/test/specs/config/sendAsJson.test.js delete mode 100644 packages/fetch-mock/test/specs/flush.test.js delete mode 100644 packages/fetch-mock/test/specs/global-fetch.test.js delete mode 100644 packages/fetch-mock/test/specs/inspecting.test.js delete mode 100644 packages/fetch-mock/test/specs/repeat.test.js delete mode 100644 packages/fetch-mock/test/specs/responses/client-only.test.js delete mode 100644 packages/fetch-mock/test/specs/responses/generation.test.js delete mode 100644 packages/fetch-mock/test/specs/responses/negotiation.js delete mode 100644 packages/fetch-mock/test/specs/routing/body-matching.test.js delete mode 100644 packages/fetch-mock/test/specs/routing/edge-cases.test.js delete mode 100644 packages/fetch-mock/test/specs/routing/function-matching.test.js delete mode 100644 packages/fetch-mock/test/specs/routing/header-matching.test.js delete mode 100644 packages/fetch-mock/test/specs/routing/matcher-object.test.js delete mode 100644 packages/fetch-mock/test/specs/routing/method-matching.test.js delete mode 100644 packages/fetch-mock/test/specs/routing/multiple-routes.test.js delete mode 100644 packages/fetch-mock/test/specs/routing/naming-routes.test.js delete mode 100644 packages/fetch-mock/test/specs/routing/path-parameter-matching.test.js delete mode 100644 packages/fetch-mock/test/specs/routing/query-string-matching.test.js delete mode 100644 packages/fetch-mock/test/specs/routing/unmatched-calls.test.js delete mode 100644 packages/fetch-mock/test/specs/routing/url-matching.test.js delete mode 100644 packages/fetch-mock/test/specs/sandbox.test.js delete mode 100644 packages/fetch-mock/test/specs/set-up-and-tear-down.test.js delete mode 100644 packages/fetch-mock/test/specs/shorthands.test.js delete mode 100644 packages/fetch-mock/test/specs/spy.test.js delete mode 100644 packages/fetch-mock/test/specs/sticky-routes.test.js delete mode 100644 packages/fetch-mock/test/specs/user-defined-matchers.test.js rename packages/{core => fetch-mock}/tsconfig.cjs.json (100%) rename packages/{core => fetch-mock}/tsconfig.esm.json (100%) delete mode 100644 packages/fetch-mock/types/index.d.ts delete mode 100644 packages/fetch-mock/types/index.test-d.ts delete mode 100644 packages/fetch-mock/types/tsconfig.json diff --git a/package-lock.json b/package-lock.json index 21e852c8d..2277606a2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4293,10 +4293,6 @@ "resolved": "packages/codemods", "link": true }, - "node_modules/@fetch-mock/core": { - "resolved": "packages/core", - "link": true - }, "node_modules/@fetch-mock/jest": { "resolved": "packages/jest", "link": true @@ -14379,11 +14375,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-subset": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz", - "integrity": "sha512-6Ybun0IkarhmEqxXCNw/C0bna6Zb/TkfUX9UbwJtK6ObwAVCxmAP308WWTHviM/zAqXk05cdhYsUsZeGQh99iw==" - }, "node_modules/is-subset-of": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/is-subset-of/-/is-subset-of-3.1.10.tgz", @@ -26570,6 +26561,7 @@ "packages/core": { "name": "@fetch-mock/core", "version": "0.7.1", + "extraneous": true, "license": "MIT", "dependencies": { "@types/glob-to-regexp": "^0.4.4", @@ -26589,16 +26581,11 @@ "@types/glob-to-regexp": "^0.4.4", "dequal": "^2.0.3", "glob-to-regexp": "^0.4.1", - "is-subset": "^0.1.1", + "is-subset-of": "^3.1.10", "regexparam": "^3.0.0" }, "engines": { - "node": ">=8.0.0" - }, - "peerDependenciesMeta": { - "node-fetch": { - "optional": true - } + "node": ">=18.11.0" } }, "packages/jest": { @@ -26606,7 +26593,7 @@ "version": "0.1.1", "license": "MIT", "dependencies": { - "@fetch-mock/core": "^0.7.1" + "fetch-mock": "11.1.5" }, "engines": { "node": ">=18.11.0" @@ -26621,7 +26608,7 @@ "version": "0.1.5", "license": "MIT", "dependencies": { - "@fetch-mock/core": "^0.7.1" + "fetch-mock": "11.1.5" }, "engines": { "node": ">=18.11.0" diff --git a/packages/core/.npmignore b/packages/core/.npmignore deleted file mode 100644 index 39ede8b45..000000000 --- a/packages/core/.npmignore +++ /dev/null @@ -1,3 +0,0 @@ -tsconfig.* -scripts -src diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md deleted file mode 100644 index c1ebaba6c..000000000 --- a/packages/core/CHANGELOG.md +++ /dev/null @@ -1,234 +0,0 @@ -# Changelog - -## [0.7.1](https://github.com/wheresrhys/fetch-mock/compare/core-v0.7.0...core-v0.7.1) (2024-09-25) - - -### Bug Fixes - -* change export order so default is last ([bc9c41d](https://github.com/wheresrhys/fetch-mock/commit/bc9c41d04609c40e609e672254df5ff1ddf0cad9)) - -## [0.7.0](https://github.com/wheresrhys/fetch-mock/compare/core-v0.6.3...core-v0.7.0) (2024-08-30) - - -### ⚠ BREAKING CHANGES - -* remove sendAsJson option - -### Features - -* remove sendAsJson option ([4b11fc4](https://github.com/wheresrhys/fetch-mock/commit/4b11fc4a5c92c81de8f89b1993a57c2645805ddb)) -* send content=length header whenever calculable ([ef4c684](https://github.com/wheresrhys/fetch-mock/commit/ef4c684bddb617e95c54217c86b6953637f21f74)) - - -### Bug Fixes - -* force engine to be >=18.11.0 as this fixes an issue in proxying a response ([dde5e6b](https://github.com/wheresrhys/fetch-mock/commit/dde5e6beb9aee103296cf060a9f027bffb4818e9)) -* handle all types of BodyInit correctly ([0242ea2](https://github.com/wheresrhys/fetch-mock/commit/0242ea2d6c30e36418f21a37a962fe1cf84c9271)) - -## [0.6.3](https://github.com/wheresrhys/fetch-mock/compare/core-v0.6.2...core-v0.6.3) (2024-08-29) - - -### Documentation Changes - -* minor corrections ([d77978a](https://github.com/wheresrhys/fetch-mock/commit/d77978a17825f166f06cbb1bbb911e7fb8790a4a)) -* readme for @fetch-mock/core ([5fd7c5a](https://github.com/wheresrhys/fetch-mock/commit/5fd7c5a1d37e546ab8dd6e173761ae0b8fb43878)) -* wrote a README for @fetch-mock/vitest ([d2d1ea3](https://github.com/wheresrhys/fetch-mock/commit/d2d1ea3f14012772edeb77a543384b99b3475e16)) - -## [0.6.2](https://github.com/wheresrhys/fetch-mock/compare/core-v0.6.1...core-v0.6.2) (2024-08-28) - - -### Bug Fixes - -* add missing metadata to package.json files ([4ab78b9](https://github.com/wheresrhys/fetch-mock/commit/4ab78b9429a376230da2ce57bd320031c53f06ef)) - -## [0.6.1](https://github.com/wheresrhys/fetch-mock/compare/core-v0.6.0...core-v0.6.1) (2024-08-15) - - -### Features - -* export more types from @fetch-mock/core ([2bff326](https://github.com/wheresrhys/fetch-mock/commit/2bff326063a362ea4ce0adc1102130bd1c31ac9e)) - -## [0.6.0](https://github.com/wheresrhys/fetch-mock/compare/core-v0.5.0...core-v0.6.0) (2024-08-13) - - -### ⚠ BREAKING CHANGES - -* force a major release due to breaking nature of relative url API changes - -### Features - -* implemented allowRelativeUrls option ([f32bd6e](https://github.com/wheresrhys/fetch-mock/commit/f32bd6e6ba7aed03ec0fd0c7361097e85c84224a)) - - -### Bug Fixes - -* all relative url behaviour is as expected now; ([dc99eb7](https://github.com/wheresrhys/fetch-mock/commit/dc99eb783e64c8c101c9d15fc59cccc7f7ad174d)) -* force a major release due to breaking nature of relative url API changes ([6f29db8](https://github.com/wheresrhys/fetch-mock/commit/6f29db8ff79a8c7a50ad03f4c1547d8716ffb298)) - -## [0.5.0](https://github.com/wheresrhys/fetch-mock/compare/core-v0.4.12...core-v0.5.0) (2024-08-13) - - -### ⚠ BREAKING CHANGES - -* implemented desired behaviour for dot path matching -* implemented desired behaviour for protocol relative urls - -### Features - -* implemented desired behaviour for dot path matching ([b2fe6f8](https://github.com/wheresrhys/fetch-mock/commit/b2fe6f894b337ed213f55fed47f611acbdecd84f)) -* implemented desired behaviour for protocol relative urls ([06e6260](https://github.com/wheresrhys/fetch-mock/commit/06e62607dbfc7b71936b8a691e37ef9275e7cc11)) - -## [0.4.12](https://github.com/wheresrhys/fetch-mock/compare/core-v0.4.11...core-v0.4.12) (2024-08-13) - - -### Bug Fixes - -* roll back to glob-to-regexp ([b114124](https://github.com/wheresrhys/fetch-mock/commit/b11412452ed376ab2e20e03a51f0dc1de1dcdb90)) - -## [0.4.11](https://github.com/wheresrhys/fetch-mock/compare/core-v0.4.10...core-v0.4.11) (2024-08-09) - - -### Bug Fixes - -* force release of core ([6bf9b87](https://github.com/wheresrhys/fetch-mock/commit/6bf9b87f0598cb5a142d623c6285b0dca6c619d5)) - -## [0.4.10](https://github.com/wheresrhys/fetch-mock/compare/core-v0.4.9...core-v0.4.10) (2024-08-08) - - -### Bug Fixes - -* fix core package build ([90bbe76](https://github.com/wheresrhys/fetch-mock/commit/90bbe76ab384ec5cefeb17f19ca06ca386cbbde5)) - -## [0.4.9](https://github.com/wheresrhys/fetch-mock/compare/core-v0.4.8...core-v0.4.9) (2024-08-08) - - -### Bug Fixes - -* add license file to each package ([9b36f89](https://github.com/wheresrhys/fetch-mock/commit/9b36f892ed19cd381b1f8ebbd94a28773637b9ec)) - -## [0.4.8](https://github.com/wheresrhys/fetch-mock/compare/core-v0.4.7...core-v0.4.8) (2024-08-03) - - -### Documentation Changes - -* document and test behaviour with multiple missing headers ([88d0440](https://github.com/wheresrhys/fetch-mock/commit/88d0440b814a0f3309f49c30d6c81d899ebc65a6)) - -## [0.4.7](https://github.com/wheresrhys/fetch-mock/compare/core-v0.4.6...core-v0.4.7) (2024-08-02) - - -### Bug Fixes - -* correct types so that global optiosn can be passed in to route ([13e1fc6](https://github.com/wheresrhys/fetch-mock/commit/13e1fc64ca3a36f54765d588dc61d44cc92cd413)) - -## [0.4.6](https://github.com/wheresrhys/fetch-mock/compare/core-v0.4.5...core-v0.4.6) (2024-07-30) - - -### Bug Fixes - -* now more spec compliant on exceptions ([ceec07f](https://github.com/wheresrhys/fetch-mock/commit/ceec07f1c8c1be86111b4feaaab76c103885da4d)) - -## [0.4.5](https://github.com/wheresrhys/fetch-mock/compare/core-v0.4.4...core-v0.4.5) (2024-07-26) - - -### Features - -* allow spying on just one route ([a9638fc](https://github.com/wheresrhys/fetch-mock/commit/a9638fc12f60bfa28e6169a9fa736e2bbdc21a8a)) -* rename restoreGlobal to unmockGlobal ([3ad4241](https://github.com/wheresrhys/fetch-mock/commit/3ad4241f409353ac970cf26b1252b32ea6390208)) - -## [0.4.4](https://github.com/wheresrhys/fetch-mock/compare/core-v0.4.3...core-v0.4.4) (2024-07-25) - - -### Features - -* cancel readable streams as effectively as possible ([aa3b899](https://github.com/wheresrhys/fetch-mock/commit/aa3b89989bd223e788db895b03c4fabc56f061d2)) -* support multiple url matchers at once ([c83d9f9](https://github.com/wheresrhys/fetch-mock/commit/c83d9f992337eb6ff79f027a7fc2e6316ce36456)) - -## [0.4.3](https://github.com/wheresrhys/fetch-mock/compare/core-v0.4.2...core-v0.4.3) (2024-07-24) - - -### Bug Fixes - -* make a more sensible decision about matching body ([0ef50d6](https://github.com/wheresrhys/fetch-mock/commit/0ef50d62ccaa70ea09b693519ddb80d73530b38f)) - -## [0.4.2](https://github.com/wheresrhys/fetch-mock/compare/core-v0.4.1...core-v0.4.2) (2024-07-24) - - -### Features - -* make query parameters available on CallLog ([8ec57ac](https://github.com/wheresrhys/fetch-mock/commit/8ec57acdc2586102fc94a76f3f3328422e43947f)) - -## [0.4.0](https://github.com/wheresrhys/fetch-mock/compare/core-v0.3.1...core-v0.4.0) (2024-07-24) - - -### ⚠ BREAKING CHANGES - -* defined route shorthand methods more declaratively - -### refactor - -* defined route shorthand methods more declaratively ([f42d240](https://github.com/wheresrhys/fetch-mock/commit/f42d240f8ef5c6a270ee8b355ad5177d8fdadf0b)). This includes removing all the `${method}Any()` and `${method}AnyOnce()` methods. - -## [0.3.1](https://github.com/wheresrhys/fetch-mock/compare/core-v0.3.0...core-v0.3.1) (2024-07-23) - - -### Documentation Changes - -* fixed tests and documented the async behaviour ([664a6df](https://github.com/wheresrhys/fetch-mock/commit/664a6df59a77937e18f19aa161ec4900fa709bfe)) - -## [0.3.0](https://github.com/wheresrhys/fetch-mock/compare/core-v0.2.0...core-v0.3.0) (2024-07-21) - - -### ⚠ BREAKING CHANGES - -* matchers now take normalized requests as input -* renamed func to matcherFunction -* removed support for passing in a matcher under the generic name matcher -* renamed functionMatcher to func - -### refactor - -* matchers now take normalized requests as input ([da9dfe8](https://github.com/wheresrhys/fetch-mock/commit/da9dfe80475f2c95ea9a3652bfe8682ccd4c65fd)) - - -### Features - -* can now access express parameters in responses ([41e2475](https://github.com/wheresrhys/fetch-mock/commit/41e2475d64d909f5fb686f2fe3709243326f2dba)) -* removed support for passing in a matcher under the generic name matcher ([f41d8f9](https://github.com/wheresrhys/fetch-mock/commit/f41d8f909350961e40a4df9dfb4817a3eaba09cd)) -* renamed func to matcherFunction ([e5679a7](https://github.com/wheresrhys/fetch-mock/commit/e5679a72f663d5187d08934aa510951f1d438adc)) -* renamed functionMatcher to func ([4cee629](https://github.com/wheresrhys/fetch-mock/commit/4cee629b36cd618d6d5b1061c15e48aab7047969)) -* response builder function now expects a calllog ([306357d](https://github.com/wheresrhys/fetch-mock/commit/306357db486c9c7aa621f430cd08621420efc724)) - -## [0.2.0](https://github.com/wheresrhys/fetch-mock/compare/core-v0.1.1...core-v0.2.0) (2024-07-20) - - -### ⚠ BREAKING CHANGES - -* removed top level done and flush methods - -### Features - -* removed top level done and flush methods ([49ae6f7](https://github.com/wheresrhys/fetch-mock/commit/49ae6f7671a2ce10f0a31bafd3eb9e1d7ce5cf2d)) - - -### Bug Fixes - -* callhistory created with instance.config, not this.config ([87206e6](https://github.com/wheresrhys/fetch-mock/commit/87206e69e71e1270932fe322c79f0b42cac486c6)) - -## [0.1.1](https://github.com/wheresrhys/fetch-mock/compare/core-v0.1.0...core-v0.1.1) (2024-07-18) - - -### Features - -* **wip:** replace dequal, glob-to-regexp and bump path-to-regexp ([d8d8b25](https://github.com/wheresrhys/fetch-mock/commit/d8d8b259fffbd01a03d5c5bf2768ee48797b68bb)) - - -### Bug Fixes - -* replace path-to-regexp with regexparam ([4bf3e32](https://github.com/wheresrhys/fetch-mock/commit/4bf3e32f852ffc169ca354288eff86737e131480)) - -## 0.1.0 (2024-07-15) - - -### Bug Fixes - -* install core package dependencies ([9c73e76](https://github.com/wheresrhys/fetch-mock/commit/9c73e76686427237a99ababa44075ca426b22037)) diff --git a/packages/core/LICENSE b/packages/core/LICENSE deleted file mode 100644 index 3d4d688e6..000000000 --- a/packages/core/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Rhys Evans - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/packages/core/README.md b/packages/core/README.md deleted file mode 100644 index 9476b405a..000000000 --- a/packages/core/README.md +++ /dev/null @@ -1,28 +0,0 @@ -# fetch-mock - -Features include: - -- mocks most of the fetch API spec, even advanced behaviours such as streaming and aborting -- declarative matching for most aspects of a http request, including url, headers, body and query parameters -- shorthands for the most commonly used features, such as matching a http method or matching one fetch only -- support for delaying responses, or using your own async functions to define custom race conditions -- can be used as a spy to observe real network requests -- can be extended with your own reusable custom matchers that can be used both for matching fetch-calls and inspecting the results -- isomorphic, and supports either a global fetch instance or a locally required instance - -## Requirements - -@fetch-mock requires either of the following to run: - -- [Node.js](https://nodejs.org/) 18+ for full feature operation -- Any modern browser that supports the `fetch` API -- [node-fetch](https://www.npmjs.com/package/node-fetch) when testing in earlier versions of Node.js (this is untested, but should mostly work) - -## Documentation and Usage - -See the [project website](https://www.wheresrhys.co.uk/fetch-mock/) - -## License - -fetch-mock is licensed under the [MIT](https://github.com/wheresrhys/fetch-mock/blob/master/LICENSE) license. -Copyright © 2024, Rhys Evans diff --git a/packages/core/package.json b/packages/core/package.json deleted file mode 100644 index 48403893d..000000000 --- a/packages/core/package.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "name": "@fetch-mock", - "description": "Mock http requests made using fetch", - "version": "11.1.5", - "exports": { - "browser": "./dist/esm/index.js", - "import": { - "types": "./dist/esm/index.d.ts", - "default": "./dist/esm/index.js" - }, - "require": { - "types": "./dist/cjs/index.d.ts", - "default": "./dist/cjs/index.js" - } - }, - "main": "./dist/cjs/index.js", - "module": "./dist/esm/index.js", - "types": "./dist/esm/index.d.ts", - "type": "module", - "engines": { - "node": ">=18.11.0" - }, - "dependencies": { - "@types/glob-to-regexp": "^0.4.4", - "dequal": "^2.0.3", - "glob-to-regexp": "^0.4.1", - "is-subset-of": "^3.1.10", - "regexparam": "^3.0.0" - }, - "repository": { - "directory": "packages/core", - "type": "git", - "url": "git+https://github.com/wheresrhys/fetch-mock.git" - }, - "scripts": { - "build": "rm -rf dist && tsc -p tsconfig.esm.json && tsc -p tsconfig.cjs.json && node ../../scripts/declare-dist-type.js" - }, - "license": "MIT", - "author": "Rhys Evans", - "bugs": { - "url": "https://github.com/wheresrhys/fetch-mock/issues" - }, - "homepage": "http://www.wheresrhys.co.uk/fetch-mock", - "keywords": [ - "fetch", - "http", - "mock", - "testing", - "spy", - "stub" - ] -} diff --git a/packages/fetch-mock/.npmignore b/packages/fetch-mock/.npmignore index f7d17379c..39ede8b45 100644 --- a/packages/fetch-mock/.npmignore +++ b/packages/fetch-mock/.npmignore @@ -1,2 +1,3 @@ -/test/ -rollup.config.js +tsconfig.* +scripts +src diff --git a/packages/fetch-mock/CHANGELOG.md b/packages/fetch-mock/CHANGELOG.md index e138cd7d8..c1ebaba6c 100644 --- a/packages/fetch-mock/CHANGELOG.md +++ b/packages/fetch-mock/CHANGELOG.md @@ -1,81 +1,220 @@ # Changelog -## [11.1.5](https://github.com/wheresrhys/fetch-mock/compare/fetch-mock-v11.1.4...fetch-mock-v11.1.5) (2024-09-25) +## [0.7.1](https://github.com/wheresrhys/fetch-mock/compare/core-v0.7.0...core-v0.7.1) (2024-09-25) ### Bug Fixes * change export order so default is last ([bc9c41d](https://github.com/wheresrhys/fetch-mock/commit/bc9c41d04609c40e609e672254df5ff1ddf0cad9)) -## [11.1.4](https://github.com/wheresrhys/fetch-mock/compare/fetch-mock-v11.1.3...fetch-mock-v11.1.4) (2024-09-19) +## [0.7.0](https://github.com/wheresrhys/fetch-mock/compare/core-v0.6.3...core-v0.7.0) (2024-08-30) + + +### ⚠ BREAKING CHANGES + +* remove sendAsJson option + +### Features + +* remove sendAsJson option ([4b11fc4](https://github.com/wheresrhys/fetch-mock/commit/4b11fc4a5c92c81de8f89b1993a57c2645805ddb)) +* send content=length header whenever calculable ([ef4c684](https://github.com/wheresrhys/fetch-mock/commit/ef4c684bddb617e95c54217c86b6953637f21f74)) + + +### Bug Fixes + +* force engine to be >=18.11.0 as this fixes an issue in proxying a response ([dde5e6b](https://github.com/wheresrhys/fetch-mock/commit/dde5e6beb9aee103296cf060a9f027bffb4818e9)) +* handle all types of BodyInit correctly ([0242ea2](https://github.com/wheresrhys/fetch-mock/commit/0242ea2d6c30e36418f21a37a962fe1cf84c9271)) + +## [0.6.3](https://github.com/wheresrhys/fetch-mock/compare/core-v0.6.2...core-v0.6.3) (2024-08-29) ### Documentation Changes -* another occurrence of the cheatsheet ref ([875e4f6](https://github.com/wheresrhys/fetch-mock/commit/875e4f6bd7b50229b6f83e14075c1a3fcd6210a4)) -* fix link to cheatsheet ([33e75b1](https://github.com/wheresrhys/fetch-mock/commit/33e75b1cd2391c9d4c1c9770c3be4fb771f7f726)) +* minor corrections ([d77978a](https://github.com/wheresrhys/fetch-mock/commit/d77978a17825f166f06cbb1bbb911e7fb8790a4a)) +* readme for @fetch-mock/core ([5fd7c5a](https://github.com/wheresrhys/fetch-mock/commit/5fd7c5a1d37e546ab8dd6e173761ae0b8fb43878)) +* wrote a README for @fetch-mock/vitest ([d2d1ea3](https://github.com/wheresrhys/fetch-mock/commit/d2d1ea3f14012772edeb77a543384b99b3475e16)) -## [11.1.3](https://github.com/wheresrhys/fetch-mock/compare/fetch-mock-v11.1.2...fetch-mock-v11.1.3) (2024-08-28) +## [0.6.2](https://github.com/wheresrhys/fetch-mock/compare/core-v0.6.1...core-v0.6.2) (2024-08-28) ### Bug Fixes * add missing metadata to package.json files ([4ab78b9](https://github.com/wheresrhys/fetch-mock/commit/4ab78b9429a376230da2ce57bd320031c53f06ef)) -## [11.1.2](https://github.com/wheresrhys/fetch-mock/compare/fetch-mock-v11.1.1...fetch-mock-v11.1.2) (2024-08-28) +## [0.6.1](https://github.com/wheresrhys/fetch-mock/compare/core-v0.6.0...core-v0.6.1) (2024-08-15) + + +### Features + +* export more types from @fetch-mock/core ([2bff326](https://github.com/wheresrhys/fetch-mock/commit/2bff326063a362ea4ce0adc1102130bd1c31ac9e)) + +## [0.6.0](https://github.com/wheresrhys/fetch-mock/compare/core-v0.5.0...core-v0.6.0) (2024-08-13) + + +### ⚠ BREAKING CHANGES + +* force a major release due to breaking nature of relative url API changes + +### Features + +* implemented allowRelativeUrls option ([f32bd6e](https://github.com/wheresrhys/fetch-mock/commit/f32bd6e6ba7aed03ec0fd0c7361097e85c84224a)) ### Bug Fixes -* lastOptions returns RequestInit not MockOptions ([591926a](https://github.com/wheresrhys/fetch-mock/commit/591926a7c524650ccb82b16611fc8882c4a8a2b6)) +* all relative url behaviour is as expected now; ([dc99eb7](https://github.com/wheresrhys/fetch-mock/commit/dc99eb783e64c8c101c9d15fc59cccc7f7ad174d)) +* force a major release due to breaking nature of relative url API changes ([6f29db8](https://github.com/wheresrhys/fetch-mock/commit/6f29db8ff79a8c7a50ad03f4c1547d8716ffb298)) -## [11.1.1](https://github.com/wheresrhys/fetch-mock/compare/fetch-mock-v11.1.0...fetch-mock-v11.1.1) (2024-08-13) +## [0.5.0](https://github.com/wheresrhys/fetch-mock/compare/core-v0.4.12...core-v0.5.0) (2024-08-13) + + +### ⚠ BREAKING CHANGES + +* implemented desired behaviour for dot path matching +* implemented desired behaviour for protocol relative urls + +### Features + +* implemented desired behaviour for dot path matching ([b2fe6f8](https://github.com/wheresrhys/fetch-mock/commit/b2fe6f894b337ed213f55fed47f611acbdecd84f)) +* implemented desired behaviour for protocol relative urls ([06e6260](https://github.com/wheresrhys/fetch-mock/commit/06e62607dbfc7b71936b8a691e37ef9275e7cc11)) + +## [0.4.12](https://github.com/wheresrhys/fetch-mock/compare/core-v0.4.11...core-v0.4.12) (2024-08-13) ### Bug Fixes * roll back to glob-to-regexp ([b114124](https://github.com/wheresrhys/fetch-mock/commit/b11412452ed376ab2e20e03a51f0dc1de1dcdb90)) -## [11.1.0](https://github.com/wheresrhys/fetch-mock/compare/fetch-mock-v11.0.2...fetch-mock-v11.1.0) (2024-08-10) +## [0.4.11](https://github.com/wheresrhys/fetch-mock/compare/core-v0.4.10...core-v0.4.11) (2024-08-09) -### Features +### Bug Fixes -* remove debug mode from fetch-mock ([89890b6](https://github.com/wheresrhys/fetch-mock/commit/89890b6ec39077b769617f02bdf5de7b971fc52c)) +* force release of core ([6bf9b87](https://github.com/wheresrhys/fetch-mock/commit/6bf9b87f0598cb5a142d623c6285b0dca6c619d5)) -## [11.0.2](https://github.com/wheresrhys/fetch-mock/compare/fetch-mock-v11.0.1...fetch-mock-v11.0.2) (2024-08-08) +## [0.4.10](https://github.com/wheresrhys/fetch-mock/compare/core-v0.4.9...core-v0.4.10) (2024-08-08) + + +### Bug Fixes + +* fix core package build ([90bbe76](https://github.com/wheresrhys/fetch-mock/commit/90bbe76ab384ec5cefeb17f19ca06ca386cbbde5)) + +## [0.4.9](https://github.com/wheresrhys/fetch-mock/compare/core-v0.4.8...core-v0.4.9) (2024-08-08) ### Bug Fixes * add license file to each package ([9b36f89](https://github.com/wheresrhys/fetch-mock/commit/9b36f892ed19cd381b1f8ebbd94a28773637b9ec)) -## [11.0.1](https://github.com/wheresrhys/fetch-mock/compare/fetch-mock-v11.0.0...fetch-mock-v11.0.1) (2024-08-08) +## [0.4.8](https://github.com/wheresrhys/fetch-mock/compare/core-v0.4.7...core-v0.4.8) (2024-08-03) + + +### Documentation Changes + +* document and test behaviour with multiple missing headers ([88d0440](https://github.com/wheresrhys/fetch-mock/commit/88d0440b814a0f3309f49c30d6c81d899ebc65a6)) + +## [0.4.7](https://github.com/wheresrhys/fetch-mock/compare/core-v0.4.6...core-v0.4.7) (2024-08-02) ### Bug Fixes -* fixes importimng into .mts files ([98ad40e](https://github.com/wheresrhys/fetch-mock/commit/98ad40ee5351f1f2f16edf1bbcc626ce94d0d629)) +* correct types so that global optiosn can be passed in to route ([13e1fc6](https://github.com/wheresrhys/fetch-mock/commit/13e1fc64ca3a36f54765d588dc61d44cc92cd413)) -## [11.0.0](https://github.com/wheresrhys/fetch-mock/compare/fetch-mock-v10.1.1...fetch-mock-v11.0.0) (2024-08-03) +## [0.4.6](https://github.com/wheresrhys/fetch-mock/compare/core-v0.4.5...core-v0.4.6) (2024-07-30) -### ⚠ BREAKING CHANGES +### Bug Fixes + +* now more spec compliant on exceptions ([ceec07f](https://github.com/wheresrhys/fetch-mock/commit/ceec07f1c8c1be86111b4feaaab76c103885da4d)) + +## [0.4.5](https://github.com/wheresrhys/fetch-mock/compare/core-v0.4.4...core-v0.4.5) (2024-07-26) + + +### Features + +* allow spying on just one route ([a9638fc](https://github.com/wheresrhys/fetch-mock/commit/a9638fc12f60bfa28e6169a9fa736e2bbdc21a8a)) +* rename restoreGlobal to unmockGlobal ([3ad4241](https://github.com/wheresrhys/fetch-mock/commit/3ad4241f409353ac970cf26b1252b32ea6390208)) + +## [0.4.4](https://github.com/wheresrhys/fetch-mock/compare/core-v0.4.3...core-v0.4.4) (2024-07-25) + + +### Features + +* cancel readable streams as effectively as possible ([aa3b899](https://github.com/wheresrhys/fetch-mock/commit/aa3b89989bd223e788db895b03c4fabc56f061d2)) +* support multiple url matchers at once ([c83d9f9](https://github.com/wheresrhys/fetch-mock/commit/c83d9f992337eb6ff79f027a7fc2e6316ce36456)) + +## [0.4.3](https://github.com/wheresrhys/fetch-mock/compare/core-v0.4.2...core-v0.4.3) (2024-07-24) -* force fetch-mock major release ### Bug Fixes -* force fetch-mock major release ([1b31416](https://github.com/wheresrhys/fetch-mock/commit/1b314167607b15887feba2f6124a9af9cca81c47)) +* make a more sensible decision about matching body ([0ef50d6](https://github.com/wheresrhys/fetch-mock/commit/0ef50d62ccaa70ea09b693519ddb80d73530b38f)) + +## [0.4.2](https://github.com/wheresrhys/fetch-mock/compare/core-v0.4.1...core-v0.4.2) (2024-07-24) -## [10.1.1](https://github.com/wheresrhys/fetch-mock/compare/fetch-mock-v10.1.0...fetch-mock-v10.1.1) (2024-07-23) + +### Features + +* make query parameters available on CallLog ([8ec57ac](https://github.com/wheresrhys/fetch-mock/commit/8ec57acdc2586102fc94a76f3f3328422e43947f)) + +## [0.4.0](https://github.com/wheresrhys/fetch-mock/compare/core-v0.3.1...core-v0.4.0) (2024-07-24) + + +### ⚠ BREAKING CHANGES + +* defined route shorthand methods more declaratively + +### refactor + +* defined route shorthand methods more declaratively ([f42d240](https://github.com/wheresrhys/fetch-mock/commit/f42d240f8ef5c6a270ee8b355ad5177d8fdadf0b)). This includes removing all the `${method}Any()` and `${method}AnyOnce()` methods. + +## [0.3.1](https://github.com/wheresrhys/fetch-mock/compare/core-v0.3.0...core-v0.3.1) (2024-07-23) + + +### Documentation Changes + +* fixed tests and documented the async behaviour ([664a6df](https://github.com/wheresrhys/fetch-mock/commit/664a6df59a77937e18f19aa161ec4900fa709bfe)) + +## [0.3.0](https://github.com/wheresrhys/fetch-mock/compare/core-v0.2.0...core-v0.3.0) (2024-07-21) + + +### ⚠ BREAKING CHANGES + +* matchers now take normalized requests as input +* renamed func to matcherFunction +* removed support for passing in a matcher under the generic name matcher +* renamed functionMatcher to func + +### refactor + +* matchers now take normalized requests as input ([da9dfe8](https://github.com/wheresrhys/fetch-mock/commit/da9dfe80475f2c95ea9a3652bfe8682ccd4c65fd)) + + +### Features + +* can now access express parameters in responses ([41e2475](https://github.com/wheresrhys/fetch-mock/commit/41e2475d64d909f5fb686f2fe3709243326f2dba)) +* removed support for passing in a matcher under the generic name matcher ([f41d8f9](https://github.com/wheresrhys/fetch-mock/commit/f41d8f909350961e40a4df9dfb4817a3eaba09cd)) +* renamed func to matcherFunction ([e5679a7](https://github.com/wheresrhys/fetch-mock/commit/e5679a72f663d5187d08934aa510951f1d438adc)) +* renamed functionMatcher to func ([4cee629](https://github.com/wheresrhys/fetch-mock/commit/4cee629b36cd618d6d5b1061c15e48aab7047969)) +* response builder function now expects a calllog ([306357d](https://github.com/wheresrhys/fetch-mock/commit/306357db486c9c7aa621f430cd08621420efc724)) + +## [0.2.0](https://github.com/wheresrhys/fetch-mock/compare/core-v0.1.1...core-v0.2.0) (2024-07-20) + + +### ⚠ BREAKING CHANGES + +* removed top level done and flush methods + +### Features + +* removed top level done and flush methods ([49ae6f7](https://github.com/wheresrhys/fetch-mock/commit/49ae6f7671a2ce10f0a31bafd3eb9e1d7ce5cf2d)) ### Bug Fixes -* change module system declaratuions to avoid top level type: module ([ed00140](https://github.com/wheresrhys/fetch-mock/commit/ed001407792a12b5fec94013aafccbb4bbb63a87)) +* callhistory created with instance.config, not this.config ([87206e6](https://github.com/wheresrhys/fetch-mock/commit/87206e69e71e1270932fe322c79f0b42cac486c6)) -## [10.1.0](https://github.com/wheresrhys/fetch-mock/compare/fetch-mock-v10.0.8...fetch-mock-v10.1.0) (2024-07-18) +## [0.1.1](https://github.com/wheresrhys/fetch-mock/compare/core-v0.1.0...core-v0.1.1) (2024-07-18) ### Features @@ -85,16 +224,11 @@ ### Bug Fixes -* failing tests ([65ef567](https://github.com/wheresrhys/fetch-mock/commit/65ef5678ba23c53d27f3b165fe25020d96c498db)) * replace path-to-regexp with regexparam ([4bf3e32](https://github.com/wheresrhys/fetch-mock/commit/4bf3e32f852ffc169ca354288eff86737e131480)) -* wildcard import ([ff9fee6](https://github.com/wheresrhys/fetch-mock/commit/ff9fee634db8b019f1384e44d13b4121bc2d62bb)) -## [10.0.8](https://github.com/wheresrhys/fetch-mock/compare/fetch-mock-v10.0.8-alpha.1...fetch-mock-v10.0.8) (2024-07-15) +## 0.1.0 (2024-07-15) -### Miscellaneous +### Bug Fixes -* rename fetch-mock-legacy directory to fetch-mock ([95fd761](https://github.com/wheresrhys/fetch-mock/commit/95fd76115e0bfb979c9ee52a59613a3f52b1a6d6)) -* test release please again ([3a9eb12](https://github.com/wheresrhys/fetch-mock/commit/3a9eb1292f81c3872bc57e91c6a436cf6fddfb80)) -* release 10.0.8 ([7bbbf49](https://github.com/wheresrhys/fetch-mock/commit/7bbbf49aaa19e7fe2c97f86452bf153933ed5345)) -* remove legacy prepublish step ([3100e5a](https://github.com/wheresrhys/fetch-mock/commit/3100e5a659a5436a583e4e56cd668a54b47bfb8f)) +* install core package dependencies ([9c73e76](https://github.com/wheresrhys/fetch-mock/commit/9c73e76686427237a99ababa44075ca426b22037)) diff --git a/packages/fetch-mock/README.md b/packages/fetch-mock/README.md index b10b96ed6..9476b405a 100644 --- a/packages/fetch-mock/README.md +++ b/packages/fetch-mock/README.md @@ -1,7 +1,5 @@ # fetch-mock -Mock http requests made using [fetch](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch). - Features include: - mocks most of the fetch API spec, even advanced behaviours such as streaming and aborting @@ -12,58 +10,19 @@ Features include: - can be extended with your own reusable custom matchers that can be used both for matching fetch-calls and inspecting the results - isomorphic, and supports either a global fetch instance or a locally required instance -_New_ If using jest, try the new [fetch-mock-jest](https://www.npmjs.com/package/fetch-mock-jest) wrapper. - -_New_ [Cheatsheet](https://github.com/wheresrhys/fetch-mock/blob/main/docs/docs/fetch-mock/Usage/cheatsheet.md) - -![node version](https://img.shields.io/node/v/fetch-mock.svg?style=flat-square) -[![licence](https://img.shields.io/npm/l/fetch-mock.svg?style=flat-square)](https://github.com/wheresrhys/fetch-mock/blob/master/LICENSE) -![npm downloads](https://img.shields.io/npm/dm/fetch-mock.svg?style=flat-square) -[![CircleCI](https://img.shields.io/circleci/project/github/wheresrhys/fetch-mock.svg?style=flat-square)](https://circleci.com/gh/wheresrhys/workflows/fetch-mock) -[![Code coverage](https://img.shields.io/coveralls/github/wheresrhys/fetch-mock.svg?style=flat-square)](https://coveralls.io/github/wheresrhys/fetch-mock) -[![Known Vulnerabilities](https://snyk.io/test/github/wheresrhys/fetch-mock/badge.svg?targetFile=package.json&style=flat-square)](https://snyk.io/test/github/wheresrhys/fetch-mock?targetFile=package.json) - -```js -fetchMock.mock('http://example.com', 200); -const res = await fetch('http://example.com'); -assert(res.ok); -fetchMock.restore(); -``` - -## Table of Contents - -- [Requirements](#requirements) -- [Documentation and Usage](http://www.wheresrhys.co.uk/fetch-mock/) -- [License](#license) -- [Housekeeping](#housekeeping) - -**I devote a lot of time to maintaining fetch-mock for free. I don't ask for payment, but am raising money for a refugee charity - please consider donating** - ---- - ## Requirements -fetch-mock requires the following to run: +@fetch-mock requires either of the following to run: -- [Node.js](https://nodejs.org/) 8+ for full feature operation -- [Node.js](https://nodejs.org/) 0.12+ with [limitations](http://www.wheresrhys.co.uk/fetch-mock/#usageinstallation) -- [npm](https://www.npmjs.com/package/npm) (normally comes with Node.js) -- Either of the following - - [node-fetch](https://www.npmjs.com/package/node-fetch) when testing in a nodejs - - A browser that supports the `fetch` API when testing in a browser +- [Node.js](https://nodejs.org/) 18+ for full feature operation +- Any modern browser that supports the `fetch` API +- [node-fetch](https://www.npmjs.com/package/node-fetch) when testing in earlier versions of Node.js (this is untested, but should mostly work) ## Documentation and Usage -See the [project website](http://www.wheresrhys.co.uk/fetch-mock/) or [cheatsheet](https://github.com/wheresrhys/fetch-mock/blob/main/docs/docs/fetch-mock/Usage/cheatsheet.md) - -If you're using jest as your test runner, consider using [fetch-mock-jest](https://www.npmjs.com/package/fetch-mock-jest), a lightweight, jest-friendly wrapper around fetch-mock. +See the [project website](https://www.wheresrhys.co.uk/fetch-mock/) ## License fetch-mock is licensed under the [MIT](https://github.com/wheresrhys/fetch-mock/blob/master/LICENSE) license. -Copyright © 2019, Rhys Evans - -## Housekeeping - -![npm version](https://img.shields.io/npm/v/fetch-mock.svg?style=flat-square) -[![maintainability](https://api.codeclimate.com/v1/badges/7f8abbf54ec9f3d07df3/maintainability?style=flat-square)](https://codeclimate.com/github/wheresrhys/fetch-mock/maintainability) +Copyright © 2024, Rhys Evans diff --git a/packages/fetch-mock/package.json b/packages/fetch-mock/package.json index e1cce7f19..9fe39f78e 100644 --- a/packages/fetch-mock/package.json +++ b/packages/fetch-mock/package.json @@ -5,41 +5,35 @@ "exports": { "browser": "./dist/esm/index.js", "import": { - "types": "./dist/esm/types/index.d.ts", + "types": "./dist/esm/index.d.ts", "default": "./dist/esm/index.js" }, "require": { - "types": "./dist/cjs/types/index.d.ts", + "types": "./dist/cjs/index.d.ts", "default": "./dist/cjs/index.js" } }, "main": "./dist/cjs/index.js", "module": "./dist/esm/index.js", - "types": "./dist/esm/types/index.d.ts", + "types": "./dist/esm/index.d.ts", "type": "module", "engines": { - "node": ">=8.0.0" + "node": ">=18.11.0" }, "dependencies": { "@types/glob-to-regexp": "^0.4.4", "dequal": "^2.0.3", "glob-to-regexp": "^0.4.1", - "is-subset": "^0.1.1", + "is-subset-of": "^3.1.10", "regexparam": "^3.0.0" }, - "peerDependenciesMeta": { - "node-fetch": { - "optional": true - } - }, "repository": { "directory": "packages/fetch-mock", "type": "git", "url": "git+https://github.com/wheresrhys/fetch-mock.git" }, "scripts": { - "build": "rm -rf dist && rollup -c --environment FORMAT:cjs && rollup -c --environment FORMAT:esm", - "types:check": "tsd -t types/index.d.ts -f types/index.test-d.ts" + "build": "rm -rf dist && tsc -p tsconfig.esm.json && tsc -p tsconfig.cjs.json && node ../../scripts/declare-dist-type.js" }, "license": "MIT", "author": "Rhys Evans", diff --git a/packages/fetch-mock/rollup.config.js b/packages/fetch-mock/rollup.config.js deleted file mode 100644 index 591f69cc6..000000000 --- a/packages/fetch-mock/rollup.config.js +++ /dev/null @@ -1,49 +0,0 @@ -import { nodeResolve } from '@rollup/plugin-node-resolve'; -import commonjs from '@rollup/plugin-commonjs'; -import copy from 'rollup-plugin-copy'; -import { writeFile, mkdir } from 'fs/promises'; -import sourcemaps from 'rollup-plugin-sourcemaps'; - -function createPackageJson() { - const pkg = { type: longFormatNames[process.env.FORMAT] }; - - const location = `./dist/${process.env.FORMAT}`; - - return { - name: 'createPackageJson', - buildEnd: async () => { - await mkdir(location, { recursive: true }); - await writeFile(`${location}/package.json`, JSON.stringify(pkg, null, 2)); - }, - }; -} - -const longFormatNames = { - cjs: 'commonjs', - esm: 'module', -}; - -export default { - input: './src/index.js', - output: { - sourcemap: true, - dir: `./dist/${process.env.FORMAT}`, - entryFileNames: 'index.js', - format: process.env.FORMAT, - exports: 'named', - }, - plugins: [ - nodeResolve({ preferBuiltins: false }), - commonjs(), - sourcemaps(), - copy({ - targets: [ - { - src: './types/*.d.ts', - dest: `dist/${process.env.FORMAT}/types`, - }, - ], - }), - createPackageJson(), - ], -}; diff --git a/packages/core/src/CallHistory.ts b/packages/fetch-mock/src/CallHistory.ts similarity index 100% rename from packages/core/src/CallHistory.ts rename to packages/fetch-mock/src/CallHistory.ts diff --git a/packages/core/src/FetchMock.ts b/packages/fetch-mock/src/FetchMock.ts similarity index 100% rename from packages/core/src/FetchMock.ts rename to packages/fetch-mock/src/FetchMock.ts diff --git a/packages/core/src/Matchers.ts b/packages/fetch-mock/src/Matchers.ts similarity index 100% rename from packages/core/src/Matchers.ts rename to packages/fetch-mock/src/Matchers.ts diff --git a/packages/core/src/RequestUtils.ts b/packages/fetch-mock/src/RequestUtils.ts similarity index 100% rename from packages/core/src/RequestUtils.ts rename to packages/fetch-mock/src/RequestUtils.ts diff --git a/packages/core/src/Route.ts b/packages/fetch-mock/src/Route.ts similarity index 100% rename from packages/core/src/Route.ts rename to packages/fetch-mock/src/Route.ts diff --git a/packages/fetch-mock/src/Route/index.js b/packages/fetch-mock/src/Route/index.js deleted file mode 100644 index d5a05ad5c..000000000 --- a/packages/fetch-mock/src/Route/index.js +++ /dev/null @@ -1,129 +0,0 @@ -import builtInMatchers from './matchers.js'; - -const isUrlMatcher = (matcher) => - matcher instanceof RegExp || - typeof matcher === 'string' || - (typeof matcher === 'object' && 'href' in matcher); - -const isFunctionMatcher = (matcher) => typeof matcher === 'function'; - -const nameToOptions = (options) => - typeof options === 'string' ? { name: options } : options; - -class Route { - constructor(args, fetchMock) { - this.fetchMock = fetchMock; - this.init(args); - this.sanitize(); - this.validate(); - this.generateMatcher(); - this.limit(); - this.delayResponse(); - } - - validate() { - if (!('response' in this)) { - throw new Error('fetch-mock: Each route must define a response'); - } - - if (!Route.registeredMatchers.some(({ name }) => name in this)) { - throw new Error( - "fetch-mock: Each route must specify some criteria for matching calls to fetch. To match all calls use '*'", - ); - } - } - - init(args) { - const [matcher, response, nameOrOptions = {}] = args; - const routeConfig = {}; - - if (isUrlMatcher(matcher) || isFunctionMatcher(matcher)) { - routeConfig.matcher = matcher; - } else { - Object.assign(routeConfig, matcher); - } - - if (typeof response !== 'undefined') { - routeConfig.response = response; - } - - if (nameOrOptions) { - Object.assign( - routeConfig, - typeof nameOrOptions === 'string' - ? nameToOptions(nameOrOptions) - : nameOrOptions, - ); - } - - Object.assign(this, routeConfig); - } - - sanitize() { - if (this.method) { - this.method = this.method.toLowerCase(); - } - if (isUrlMatcher(this.matcher)) { - this.url = this.matcher; - delete this.matcher; - } - - this.functionMatcher = this.matcher || this.functionMatcher; - - this.identifier = this.name || this.url || this.functionMatcher; - } - - generateMatcher() { - const activeMatchers = Route.registeredMatchers - .map( - ({ name, matcher, usesBody }) => - this[name] && { matcher: matcher(this, this.fetchMock), usesBody }, - ) - .filter((matcher) => Boolean(matcher)); - - this.usesBody = activeMatchers.some(({ usesBody }) => usesBody); - - this.matcher = (url, options = {}, request) => - activeMatchers.every(({ matcher }) => matcher(url, options, request)); - } - - limit() { - if (!this.repeat) { - return; - } - - const { matcher } = this; - let timesLeft = this.repeat; - this.matcher = (url, options) => { - const match = timesLeft && matcher(url, options); - if (match) { - timesLeft--; - return true; - } - }; - this.reset = () => { - timesLeft = this.repeat; - }; - } - - delayResponse() { - if (this.delay) { - const { response } = this; - this.response = () => { - return new Promise((res) => - setTimeout(() => res(response), this.delay), - ); - }; - } - } - - static addMatcher(matcher) { - Route.registeredMatchers.push(matcher); - } -} - -Route.registeredMatchers = []; - -builtInMatchers.forEach(Route.addMatcher); - -export default Route; diff --git a/packages/fetch-mock/src/Route/matchers.js b/packages/fetch-mock/src/Route/matchers.js deleted file mode 100644 index 7a75229db..000000000 --- a/packages/fetch-mock/src/Route/matchers.js +++ /dev/null @@ -1,215 +0,0 @@ -import glob from 'glob-to-regexp'; -import * as regexparam from 'regexparam'; -import isSubset from 'is-subset'; -import { dequal as isEqual } from 'dequal'; -import { - headers as headerUtils, - getPath, - getQuery, - normalizeUrl, -} from '../lib/request-utils.js'; - -const debuggableUrlFunc = (func) => (url) => { - return func(url); -}; - -const stringMatchers = { - begin: (targetString) => - debuggableUrlFunc((url) => url.indexOf(targetString) === 0), - end: (targetString) => - debuggableUrlFunc( - (url) => url.substr(-targetString.length) === targetString, - ), - glob: (targetString) => { - const urlRX = glob(targetString); - return debuggableUrlFunc((url) => urlRX.test(url)); - }, - express: (targetString) => { - const urlRX = regexparam.parse(targetString); - return debuggableUrlFunc((url) => urlRX.pattern.test(getPath(url))); - }, - path: (targetString) => - debuggableUrlFunc((url) => getPath(url) === targetString), -}; - -const getHeaderMatcher = ({ headers: expectedHeaders }) => { - if (!expectedHeaders) { - return; - } - const expectation = headerUtils.toLowerCase(expectedHeaders); - return (url, { headers = {} }) => { - const lowerCaseHeaders = headerUtils.toLowerCase( - headerUtils.normalize(headers), - ); - return Object.keys(expectation).every((headerName) => - headerUtils.equal(lowerCaseHeaders[headerName], expectation[headerName]), - ); - }; -}; - -const getMethodMatcher = ({ method: expectedMethod }) => { - if (!expectedMethod) { - return; - } - return (url, { method }) => { - const actualMethod = method ? method.toLowerCase() : 'get'; - return expectedMethod === actualMethod; - }; -}; - -const getQueryStringMatcher = ({ query: passedQuery }) => { - if (!passedQuery) { - return; - } - - const expectedQuery = new URLSearchParams(); - for (const [key, value] of Object.entries(passedQuery)) { - if (Array.isArray(value)) { - for (const item of value) { - expectedQuery.append( - key, - typeof item === 'object' || typeof item === 'undefined' - ? '' - : item.toString(), - ); - } - } else { - expectedQuery.append( - key, - typeof value === 'object' || typeof value === 'undefined' - ? '' - : value.toString(), - ); - } - } - - const keys = Array.from(expectedQuery.keys()); - return (url) => { - const queryString = getQuery(url); - const query = new URLSearchParams(queryString); - - return keys.every((key) => { - const expectedValues = expectedQuery.getAll(key).sort(); - const actualValues = query.getAll(key).sort(); - - if (expectedValues.length !== actualValues.length) { - return false; - } - - if (Array.isArray(passedQuery[key])) { - return expectedValues.every( - (expected, index) => expected === actualValues[index], - ); - } - - return isEqual(actualValues, expectedValues); - }); - }; -}; - -const getParamsMatcher = ({ params: expectedParams, url: matcherUrl }) => { - if (!expectedParams) { - return; - } - if (!/express:/.test(matcherUrl)) { - throw new Error( - 'fetch-mock: matching on params is only possible when using an express: matcher', - ); - } - const expectedKeys = Object.keys(expectedParams); - const re = regexparam.parse(matcherUrl.replace(/^express:/, '')); - return (url) => { - const vals = re.pattern.exec(getPath(url)) || []; - vals.shift(); - const params = re.keys.reduce( - (map, paramName, i) => - vals[i] ? Object.assign(map, { [paramName]: vals[i] }) : map, - {}, - ); - return expectedKeys.every((key) => params[key] === expectedParams[key]); - }; -}; - -const getBodyMatcher = (route, fetchMock) => { - const matchPartialBody = fetchMock.getOption('matchPartialBody', route); - const { body: expectedBody } = route; - - return (url, { body, method = 'get' }) => { - if (method.toLowerCase() === 'get') { - // GET requests don’t send a body so the body matcher should be ignored for them - return true; - } - - let sentBody; - - try { - sentBody = JSON.parse(body); - } catch {} // eslint-disable-line no-empty - - return ( - sentBody && - (matchPartialBody - ? isSubset(sentBody, expectedBody) - : isEqual(sentBody, expectedBody)) - ); - }; -}; - -const getFullUrlMatcher = (route, matcherUrl, query) => { - // if none of the special syntaxes apply, it's just a simple string match - // but we have to be careful to normalize the url we check and the name - // of the route to allow for e.g. http://it.at.there being indistinguishable - // from http://it.at.there/ once we start generating Request/Url objects - const expectedUrl = normalizeUrl(matcherUrl); - if (route.identifier === matcherUrl) { - route.identifier = expectedUrl; - } - - return (matcherUrl) => { - if (query && expectedUrl.indexOf('?')) { - return matcherUrl.indexOf(expectedUrl) === 0; - } - return normalizeUrl(matcherUrl) === expectedUrl; - }; -}; - -const getFunctionMatcher = ({ functionMatcher }) => { - return (...args) => { - return functionMatcher(...args); - }; -}; - -const getUrlMatcher = (route) => { - const { url: matcherUrl, query } = route; - - if (matcherUrl === '*') { - return () => true; - } - - if (matcherUrl instanceof RegExp) { - return (url) => matcherUrl.test(url); - } - - if (matcherUrl.href) { - return getFullUrlMatcher(route, matcherUrl.href, query); - } - - for (const shorthand in stringMatchers) { - if (matcherUrl.indexOf(`${shorthand}:`) === 0) { - const urlFragment = matcherUrl.replace(new RegExp(`^${shorthand}:`), ''); - return stringMatchers[shorthand](urlFragment); - } - } - - return getFullUrlMatcher(route, matcherUrl, query); -}; - -export default [ - { name: 'query', matcher: getQueryStringMatcher }, - { name: 'method', matcher: getMethodMatcher }, - { name: 'headers', matcher: getHeaderMatcher }, - { name: 'params', matcher: getParamsMatcher }, - { name: 'body', matcher: getBodyMatcher, usesBody: true }, - { name: 'functionMatcher', matcher: getFunctionMatcher }, - { name: 'url', matcher: getUrlMatcher }, -]; diff --git a/packages/core/src/Router.ts b/packages/fetch-mock/src/Router.ts similarity index 100% rename from packages/core/src/Router.ts rename to packages/fetch-mock/src/Router.ts diff --git a/packages/core/src/StatusTextMap.ts b/packages/fetch-mock/src/StatusTextMap.ts similarity index 100% rename from packages/core/src/StatusTextMap.ts rename to packages/fetch-mock/src/StatusTextMap.ts diff --git a/packages/core/src/__tests__/CallHistory.test.js b/packages/fetch-mock/src/__tests__/CallHistory.test.js similarity index 100% rename from packages/core/src/__tests__/CallHistory.test.js rename to packages/fetch-mock/src/__tests__/CallHistory.test.js diff --git a/packages/core/src/__tests__/FetchMock/flush.test.js b/packages/fetch-mock/src/__tests__/FetchMock/flush.test.js similarity index 100% rename from packages/core/src/__tests__/FetchMock/flush.test.js rename to packages/fetch-mock/src/__tests__/FetchMock/flush.test.js diff --git a/packages/core/src/__tests__/FetchMock/instance-management.test.js b/packages/fetch-mock/src/__tests__/FetchMock/instance-management.test.js similarity index 100% rename from packages/core/src/__tests__/FetchMock/instance-management.test.js rename to packages/fetch-mock/src/__tests__/FetchMock/instance-management.test.js diff --git a/packages/core/src/__tests__/FetchMock/mock-and-spy.test.js b/packages/fetch-mock/src/__tests__/FetchMock/mock-and-spy.test.js similarity index 100% rename from packages/core/src/__tests__/FetchMock/mock-and-spy.test.js rename to packages/fetch-mock/src/__tests__/FetchMock/mock-and-spy.test.js diff --git a/packages/core/src/__tests__/FetchMock/response-construction.test.js b/packages/fetch-mock/src/__tests__/FetchMock/response-construction.test.js similarity index 100% rename from packages/core/src/__tests__/FetchMock/response-construction.test.js rename to packages/fetch-mock/src/__tests__/FetchMock/response-construction.test.js diff --git a/packages/core/src/__tests__/FetchMock/response-negotiation.test.js b/packages/fetch-mock/src/__tests__/FetchMock/response-negotiation.test.js similarity index 100% rename from packages/core/src/__tests__/FetchMock/response-negotiation.test.js rename to packages/fetch-mock/src/__tests__/FetchMock/response-negotiation.test.js diff --git a/packages/core/src/__tests__/FetchMock/routing.test.js b/packages/fetch-mock/src/__tests__/FetchMock/routing.test.js similarity index 100% rename from packages/core/src/__tests__/FetchMock/routing.test.js rename to packages/fetch-mock/src/__tests__/FetchMock/routing.test.js diff --git a/packages/core/src/__tests__/Matchers/body.test.js b/packages/fetch-mock/src/__tests__/Matchers/body.test.js similarity index 100% rename from packages/core/src/__tests__/Matchers/body.test.js rename to packages/fetch-mock/src/__tests__/Matchers/body.test.js diff --git a/packages/core/src/__tests__/Matchers/express.test.js b/packages/fetch-mock/src/__tests__/Matchers/express.test.js similarity index 100% rename from packages/core/src/__tests__/Matchers/express.test.js rename to packages/fetch-mock/src/__tests__/Matchers/express.test.js diff --git a/packages/core/src/__tests__/Matchers/function.test.js b/packages/fetch-mock/src/__tests__/Matchers/function.test.js similarity index 100% rename from packages/core/src/__tests__/Matchers/function.test.js rename to packages/fetch-mock/src/__tests__/Matchers/function.test.js diff --git a/packages/core/src/__tests__/Matchers/headers.test.js b/packages/fetch-mock/src/__tests__/Matchers/headers.test.js similarity index 100% rename from packages/core/src/__tests__/Matchers/headers.test.js rename to packages/fetch-mock/src/__tests__/Matchers/headers.test.js diff --git a/packages/core/src/__tests__/Matchers/method.test.js b/packages/fetch-mock/src/__tests__/Matchers/method.test.js similarity index 100% rename from packages/core/src/__tests__/Matchers/method.test.js rename to packages/fetch-mock/src/__tests__/Matchers/method.test.js diff --git a/packages/core/src/__tests__/Matchers/query-string.test.js b/packages/fetch-mock/src/__tests__/Matchers/query-string.test.js similarity index 100% rename from packages/core/src/__tests__/Matchers/query-string.test.js rename to packages/fetch-mock/src/__tests__/Matchers/query-string.test.js diff --git a/packages/core/src/__tests__/Matchers/route-config-object.test.js b/packages/fetch-mock/src/__tests__/Matchers/route-config-object.test.js similarity index 100% rename from packages/core/src/__tests__/Matchers/route-config-object.test.js rename to packages/fetch-mock/src/__tests__/Matchers/route-config-object.test.js diff --git a/packages/core/src/__tests__/Matchers/url.test.js b/packages/fetch-mock/src/__tests__/Matchers/url.test.js similarity index 100% rename from packages/core/src/__tests__/Matchers/url.test.js rename to packages/fetch-mock/src/__tests__/Matchers/url.test.js diff --git a/packages/core/src/__tests__/router-integration.test.js b/packages/fetch-mock/src/__tests__/router-integration.test.js similarity index 100% rename from packages/core/src/__tests__/router-integration.test.js rename to packages/fetch-mock/src/__tests__/router-integration.test.js diff --git a/packages/core/src/__tests__/spec-compliance.test.js b/packages/fetch-mock/src/__tests__/spec-compliance.test.js similarity index 100% rename from packages/core/src/__tests__/spec-compliance.test.js rename to packages/fetch-mock/src/__tests__/spec-compliance.test.js diff --git a/packages/fetch-mock/src/index.js b/packages/fetch-mock/src/index.js deleted file mode 100644 index add3c7665..000000000 --- a/packages/fetch-mock/src/index.js +++ /dev/null @@ -1,13 +0,0 @@ -import FetchMock from './lib/index.js'; -import statusTextMap from './lib/status-text.js'; - -FetchMock.statusTextMap = statusTextMap; - -FetchMock.config = Object.assign(FetchMock.config, { - Request: globalThis.Request, - Response: globalThis.Response, - Headers: globalThis.Headers, - fetch: globalThis.fetch, -}); - -export default FetchMock.createInstance(); diff --git a/packages/core/src/index.ts b/packages/fetch-mock/src/index.ts similarity index 100% rename from packages/core/src/index.ts rename to packages/fetch-mock/src/index.ts diff --git a/packages/fetch-mock/src/lib/fetch-handler.js b/packages/fetch-mock/src/lib/fetch-handler.js deleted file mode 100755 index c3e927a1d..000000000 --- a/packages/fetch-mock/src/lib/fetch-handler.js +++ /dev/null @@ -1,216 +0,0 @@ -import responseBuilder from './response-builder.js'; -import * as requestUtils from './request-utils.js'; - -const FetchMock = {}; - -const resolve = async ( - { response, responseIsFetch = false }, - url, - options, - request, -) => { - // We want to allow things like - // - function returning a Promise for a response - // - delaying (using a timeout Promise) a function's execution to generate - // a response - // Because of this we can't safely check for function before Promisey-ness, - // or vice versa. So to keep it DRY, and flexible, we keep trying until we - // have something that looks like neither Promise nor function - - while (true) { - if (typeof response === 'function') { - // in the case of falling back to the network we need to make sure we're using - // the original Request instance, not our normalised url + options - if (responseIsFetch) { - if (request) { - return response(request); - } - return response(url, options); - } - response = response(url, options, request); - } else if (typeof response.then === 'function') { - response = await response; - } else { - return response; - } - } -}; - -FetchMock.needsAsyncBodyExtraction = function ({ request }) { - return request && this.routes.some(({ usesBody }) => usesBody); -}; - -FetchMock.fetchHandler = function (url, options) { - const normalizedRequest = requestUtils.normalizeRequest( - url, - options, - this.config.Request, - ); - - if (this.needsAsyncBodyExtraction(normalizedRequest)) { - return this._extractBodyThenHandle(normalizedRequest); - } - return this._fetchHandler(normalizedRequest); -}; - -FetchMock._extractBodyThenHandle = async function (normalizedRequest) { - normalizedRequest.options.body = await normalizedRequest.options.body; - return this._fetchHandler(normalizedRequest); -}; - -FetchMock._fetchHandler = function ({ url, options, request, signal }) { - const { route, callLog } = this.executeRouter(url, options, request); - - this.recordCall(callLog); - - // this is used to power the .flush() method - let done; - this._holdingPromises.push( - new Promise((res) => { - done = res; - }), - ); - - // wrapped in this promise to make sure we respect custom Promise - // constructors defined by the user - return new Promise((res, rej) => { - if (signal) { - const abort = () => { - rej(new DOMException('The operation was aborted.', 'AbortError')); - done(); - }; - if (signal.aborted) { - abort(); - } - signal.addEventListener('abort', abort); - } - - this.generateResponse({ - route, - url, - options, - request, - callLog, - }) - .then(res, rej) - .then(done, done); - }); -}; - -FetchMock.fetchHandler.isMock = true; - -FetchMock.executeRouter = function (url, options, request) { - const callLog = { - url, - options, - request, - isUnmatched: true, - }; - if (this.getOption('fallbackToNetwork') === 'always') { - return { - route: { response: this.getNativeFetch(), responseIsFetch: true }, - // BUG - this callLog never used to get sent. Discovered the bug - // but can't fix outside a major release as it will potentially - // cause too much disruption - // - // callLog, - }; - } - - const route = this.router(url, options, request); - - if (route) { - return { - route, - callLog: { - url, - options, - request, - identifier: route.identifier, - }, - }; - } - - if (this.getOption('warnOnFallback')) { - console.warn(`Unmatched ${(options && options.method) || 'GET'} to ${url}`); - } - - if (this.fallbackResponse) { - return { route: { response: this.fallbackResponse }, callLog }; - } - - if (!this.getOption('fallbackToNetwork')) { - throw new Error( - `fetch-mock: No fallback response defined for ${ - (options && options.method) || 'GET' - } to ${url}`, - ); - } - - return { - route: { response: this.getNativeFetch(), responseIsFetch: true }, - callLog, - }; -}; - -FetchMock.generateResponse = async function ({ - route, - url, - options, - request, - callLog = {}, -}) { - const response = await resolve(route, url, options, request); - - // If the response says to throw an error, throw it - // Type checking is to deal with sinon spies having a throws property :-0 - if (response.throws && typeof response !== 'function') { - throw response.throws; - } - - // If the response is a pre-made Response, respond with it - if (this.config.Response.prototype.isPrototypeOf(response)) { - callLog.response = response; - return response; - } - - // finally, if we need to convert config into a response, we do it - const [realResponse, finalResponse] = responseBuilder({ - url, - responseConfig: response, - fetchMock: this, - route, - }); - - callLog.response = realResponse; - - return finalResponse; -}; - -FetchMock.router = function (url, options, request) { - const route = this.routes.find((route) => { - return route.matcher(url, options, request); - }); - - if (route) { - return route; - } -}; - -FetchMock.getNativeFetch = function () { - const func = this.realFetch || (this.isSandbox && this.config.fetch); - if (!func) { - throw new Error( - 'fetch-mock: Falling back to network only available on global fetch-mock, or by setting config.fetch on sandboxed fetch-mock', - ); - } - return func; -}; - -FetchMock.recordCall = function (obj) { - if (obj) { - this._calls.push(obj); - } -}; - -export default FetchMock; diff --git a/packages/fetch-mock/src/lib/index.js b/packages/fetch-mock/src/lib/index.js deleted file mode 100644 index 4d7b74f11..000000000 --- a/packages/fetch-mock/src/lib/index.js +++ /dev/null @@ -1,72 +0,0 @@ -import setUpAndTearDown from './set-up-and-tear-down.js'; -import fetchHandler from './fetch-handler.js'; -import inspecting from './inspecting.js'; -import Route from '../Route/index.js'; - -const FetchMock = { ...fetchHandler, ...setUpAndTearDown, ...inspecting }; - -FetchMock.addMatcher = function (matcher) { - Route.addMatcher(matcher); -}; - -FetchMock.config = { - fallbackToNetwork: false, - includeContentLength: true, - sendAsJson: true, - warnOnFallback: true, - overwriteRoutes: undefined, -}; - -FetchMock.createInstance = function () { - const instance = Object.create(FetchMock); - instance._uncompiledRoutes = (this._uncompiledRoutes || []).slice(); - instance.routes = instance._uncompiledRoutes.map((config) => - this.compileRoute(config), - ); - instance.fallbackResponse = this.fallbackResponse || undefined; - instance.config = { ...(this.config || FetchMock.config) }; - instance._calls = []; - instance._holdingPromises = []; - instance.bindMethods(); - return instance; -}; - -FetchMock.compileRoute = function (config) { - return new Route(config, this); -}; - -FetchMock.bindMethods = function () { - this.fetchHandler = FetchMock.fetchHandler.bind(this); - this.reset = this.restore = FetchMock.reset.bind(this); - this.resetHistory = FetchMock.resetHistory.bind(this); - this.resetBehavior = FetchMock.resetBehavior.bind(this); -}; - -FetchMock.sandbox = function () { - // this construct allows us to create a fetch-mock instance which is also - // a callable function, while circumventing circularity when defining the - // object that this function should be bound to - const fetchMockProxy = (url, options) => sandbox.fetchHandler(url, options); - - const sandbox = Object.assign( - fetchMockProxy, // Ensures that the entire returned object is a callable function - FetchMock, // prototype methods - this.createInstance(), // instance data - { - Headers: this.config.Headers, - Request: this.config.Request, - Response: this.config.Response, - }, - ); - - sandbox.bindMethods(); - sandbox.isSandbox = true; - sandbox.default = sandbox; - return sandbox; -}; - -FetchMock.getOption = function (name, route = {}) { - return name in route ? route[name] : this.config[name]; -}; - -export default FetchMock; diff --git a/packages/fetch-mock/src/lib/inspecting.js b/packages/fetch-mock/src/lib/inspecting.js deleted file mode 100644 index 8653f0b96..000000000 --- a/packages/fetch-mock/src/lib/inspecting.js +++ /dev/null @@ -1,143 +0,0 @@ -import { normalizeUrl } from './request-utils.js'; -import Route from '../Route/index.js'; - -const FetchMock = {}; -const isName = (nameOrMatcher) => - typeof nameOrMatcher === 'string' && /^[\da-zA-Z-]+$/.test(nameOrMatcher); - -const filterCallsWithMatcher = function (matcher, options = {}, calls) { - ({ matcher } = new Route([{ matcher, response: 'ok', ...options }], this)); - return calls.filter(({ url, options }) => - matcher(normalizeUrl(url), options), - ); -}; - -const formatDebug = (func) => - function (...args) { - const result = func.call(this, ...args); - return result; - }; - -const callObjToArray = (obj) => { - if (!obj) { - return undefined; - } - const { url, options, request, identifier, isUnmatched, response } = obj; - const arr = [url, options]; - arr.request = request; - arr.identifier = identifier; - arr.isUnmatched = isUnmatched; - arr.response = response; - return arr; -}; - -FetchMock.filterCalls = function (nameOrMatcher, options) { - let calls = this._calls; - let matcher = '*'; - - if ([true, 'matched'].includes(nameOrMatcher)) { - calls = calls.filter(({ isUnmatched }) => !isUnmatched); - } else if ([false, 'unmatched'].includes(nameOrMatcher)) { - calls = calls.filter(({ isUnmatched }) => isUnmatched); - } else if (isName(nameOrMatcher)) { - calls = calls.filter(({ identifier }) => identifier === nameOrMatcher); - } else if (typeof nameOrMatcher !== 'undefined') { - matcher = nameOrMatcher === '*' ? '*' : normalizeUrl(nameOrMatcher); - if (this.routes.some(({ identifier }) => identifier === matcher)) { - calls = calls.filter((call) => call.identifier === matcher); - } - } - - if ((options || matcher !== '*') && calls.length) { - if (typeof options === 'string') { - options = { method: options }; - } - calls = filterCallsWithMatcher.call(this, matcher, options, calls); - } - return calls.map(callObjToArray); -}; - -FetchMock.calls = formatDebug(function (nameOrMatcher, options) { - return this.filterCalls(nameOrMatcher, options); -}); - -FetchMock.lastCall = formatDebug(function (nameOrMatcher, options) { - return [...this.filterCalls(nameOrMatcher, options)].pop(); -}); - -FetchMock.lastUrl = formatDebug(function (nameOrMatcher, options) { - return (this.lastCall(nameOrMatcher, options) || [])[0]; -}); - -FetchMock.lastOptions = formatDebug(function (nameOrMatcher, options) { - return (this.lastCall(nameOrMatcher, options) || [])[1]; -}); - -FetchMock.lastResponse = formatDebug(function (nameOrMatcher, options) { - const { response } = this.lastCall(nameOrMatcher, options) || []; - try { - const clonedResponse = response.clone(); - return clonedResponse; - } catch { - Object.entries(response._fmResults).forEach(([name, result]) => { - response[name] = () => result; - }); - return response; - } -}); - -FetchMock.called = formatDebug(function (nameOrMatcher, options) { - return Boolean(this.filterCalls(nameOrMatcher, options).length); -}); - -FetchMock.flush = formatDebug(async function (waitForResponseMethods) { - const queuedPromises = this._holdingPromises; - this._holdingPromises = []; - - await Promise.all(queuedPromises); - if (waitForResponseMethods && this._holdingPromises.length) { - await this.flush(waitForResponseMethods); - } -}); - -FetchMock.done = formatDebug(function (nameOrMatcher) { - let routesToCheck; - - if (nameOrMatcher && typeof nameOrMatcher !== 'boolean') { - routesToCheck = [{ identifier: nameOrMatcher }]; - } else { - routesToCheck = this.routes; - } - - // Can't use array.every because would exit after first failure, which would - // break the logging - const result = routesToCheck - .map(({ identifier }) => { - if (!this.called(identifier)) { - console.warn(`Warning: ${identifier} not called`); - return false; - } - - const expectedTimes = ( - this.routes.find((r) => r.identifier === identifier) || {} - ).repeat; - - if (!expectedTimes) { - return true; - } - const actualTimes = this.filterCalls(identifier).length; - - if (expectedTimes > actualTimes) { - console.warn( - `Warning: ${identifier} only called ${actualTimes} times, but ${expectedTimes} expected`, - ); - return false; - } - return true; - }) - .every((isDone) => isDone); - - return result; -}); - -export default FetchMock; diff --git a/packages/fetch-mock/src/lib/request-utils.js b/packages/fetch-mock/src/lib/request-utils.js deleted file mode 100644 index 54ff477cf..000000000 --- a/packages/fetch-mock/src/lib/request-utils.js +++ /dev/null @@ -1,118 +0,0 @@ -// https://stackoverflow.com/a/19709846/308237 plus data: scheme -// split into 2 code paths as URL constructor does not support protocol-relative urls -const absoluteUrlRX = new RegExp('^[a-z]+://|^data:', 'i'); -const protocolRelativeUrlRX = new RegExp('^//', 'i'); - -const headersToArray = (headers) => { - // node-fetch 1 Headers - if (typeof headers.raw === 'function') { - return Object.entries(headers.raw()); - } - if (headers[Symbol.iterator]) { - return [...headers]; - } - return Object.entries(headers); -}; - -const zipObject = (entries) => - entries.reduce((obj, [key, val]) => Object.assign(obj, { [key]: val }), {}); - -export function normalizeUrl(url) { - if ( - typeof url === 'function' || - url instanceof RegExp || - /^(begin|end|glob|express|path):/.test(url) - ) { - return url; - } - if (absoluteUrlRX.test(url)) { - const u = new URL(url); - return u.href; - } - if (protocolRelativeUrlRX.test(url)) { - const u = new URL(url, 'http://dummy'); - return u.href; - } - const u = new URL(url, 'http://dummy'); - return u.pathname + u.search; -} - -export function normalizeRequest(url, options, Request) { - if (Request.prototype.isPrototypeOf(url)) { - const derivedOptions = { - method: url.method, - }; - - try { - derivedOptions.body = url.clone().text(); - } catch {} // eslint-disable-line no-empty - - const normalizedRequestObject = { - url: normalizeUrl(url.url), - options: Object.assign(derivedOptions, options), - request: url, - signal: (options && options.signal) || url.signal, - }; - - const headers = headersToArray(url.headers); - - if (headers.length) { - normalizedRequestObject.options.headers = zipObject(headers); - } - return normalizedRequestObject; - } - if ( - typeof url === 'string' || - url instanceof String || - // horrible URL object duck-typing - (typeof url === 'object' && 'href' in url) - ) { - return { - url: normalizeUrl(url), - options, - signal: options && options.signal, - }; - } - if (typeof url === 'object') { - throw new TypeError( - 'fetch-mock: Unrecognised Request object. Read the Config and Installation sections of the docs', - ); - } else { - throw new TypeError('fetch-mock: Invalid arguments passed to fetch'); - } -} - -export function getPath(url) { - const u = absoluteUrlRX.test(url) - ? new URL(url) - : new URL(url, 'http://dummy'); - return u.pathname; -} - -export function getQuery(url) { - const u = absoluteUrlRX.test(url) - ? new URL(url) - : new URL(url, 'http://dummy'); - return u.search ? u.search.substr(1) : ''; -} - -export const headers = { - normalize: (headers) => zipObject(headersToArray(headers)), - toLowerCase: (headers) => - Object.keys(headers).reduce((obj, k) => { - obj[k.toLowerCase()] = headers[k]; - return obj; - }, {}), - equal: (actualHeader, expectedHeader) => { - actualHeader = Array.isArray(actualHeader) ? actualHeader : [actualHeader]; - expectedHeader = Array.isArray(expectedHeader) - ? expectedHeader - : [expectedHeader]; - - if (actualHeader.length !== expectedHeader.length) { - return false; - } - - return actualHeader.every((val, i) => val === expectedHeader[i]); - }, -}; diff --git a/packages/fetch-mock/src/lib/response-builder.js b/packages/fetch-mock/src/lib/response-builder.js deleted file mode 100644 index 936e05d92..000000000 --- a/packages/fetch-mock/src/lib/response-builder.js +++ /dev/null @@ -1,164 +0,0 @@ -const responseConfigProps = [ - 'body', - 'headers', - 'throws', - 'status', - 'redirectUrl', -]; - -class ResponseBuilder { - constructor(options) { - Object.assign(this, options); - } - - exec() { - this.normalizeResponseConfig(); - this.constructFetchOpts(); - this.constructResponseBody(); - - const realResponse = new this.fetchMock.config.Response( - this.body, - this.options, - ); - - const proxyResponse = this.buildObservableResponse(realResponse); - return [realResponse, proxyResponse]; - } - - sendAsObject() { - if (responseConfigProps.some((prop) => this.responseConfig[prop])) { - if ( - Object.keys(this.responseConfig).every((key) => - responseConfigProps.includes(key), - ) - ) { - return false; - } - return true; - } - return true; - } - - normalizeResponseConfig() { - // If the response config looks like a status, start to generate a simple response - if (typeof this.responseConfig === 'number') { - this.responseConfig = { - status: this.responseConfig, - }; - // If the response config is not an object, or is an object that doesn't use - // any reserved properties, assume it is meant to be the body of the response - } else if (typeof this.responseConfig === 'string' || this.sendAsObject()) { - this.responseConfig = { - body: this.responseConfig, - }; - } - } - - validateStatus(status) { - if (!status) { - return 200; - } - - if ( - (typeof status === 'number' && - parseInt(status, 10) !== status && - status >= 200) || - status < 600 - ) { - return status; - } - - throw new TypeError(`fetch-mock: Invalid status ${status} passed on response object. -To respond with a JSON object that has status as a property assign the object to body -e.g. {"body": {"status: "registered"}}`); - } - - constructFetchOpts() { - this.options = this.responseConfig.options || {}; - this.options.url = this.responseConfig.redirectUrl || this.url; - this.options.status = this.validateStatus(this.responseConfig.status); - this.options.statusText = - this.fetchMock.statusTextMap[String(this.options.status)]; - - // Set up response headers. The empty object is to cope with - // new Headers(undefined) throwing in Chrome - // https://code.google.com/p/chromium/issues/detail?id=335871 - this.options.headers = new this.fetchMock.config.Headers( - this.responseConfig.headers || {}, - ); - } - - getOption(name) { - return this.fetchMock.getOption(name, this.route); - } - - convertToJson() { - // convert to json if we need to - if ( - this.getOption('sendAsJson') && - this.responseConfig.body != null && - typeof this.body === 'object' - ) { - this.body = JSON.stringify(this.body); - if (!this.options.headers.has('Content-Type')) { - this.options.headers.set('Content-Type', 'application/json'); - } - } - } - - setContentLength() { - // add a Content-Length header if we need to - if ( - this.getOption('includeContentLength') && - typeof this.body === 'string' && - !this.options.headers.has('Content-Length') - ) { - this.options.headers.set('Content-Length', this.body.length.toString()); - } - } - - constructResponseBody() { - // start to construct the body - this.body = this.responseConfig.body; - this.convertToJson(); - this.setContentLength(); - } - - buildObservableResponse(response) { - const { fetchMock } = this; - response._fmResults = {}; - // Using a proxy means we can set properties that may not be writable on - // the original Response. It also means we can track the resolution of - // promises returned by res.json(), res.text() etc - return new Proxy(response, { - get: (originalResponse, name) => { - if (this.responseConfig.redirectUrl) { - if (name === 'url') { - return this.responseConfig.redirectUrl; - } - - if (name === 'redirected') { - return true; - } - } - - if (typeof originalResponse[name] === 'function') { - return new Proxy(originalResponse[name], { - apply: (func, thisArg, args) => { - const result = func.apply(response, args); - if (result.then) { - fetchMock._holdingPromises.push(result.catch(() => null)); - originalResponse._fmResults[name] = result; - } - return result; - }, - }); - } - - return originalResponse[name]; - }, - }); - } -} - -export default (options) => new ResponseBuilder(options).exec(); diff --git a/packages/fetch-mock/src/lib/set-up-and-tear-down.js b/packages/fetch-mock/src/lib/set-up-and-tear-down.js deleted file mode 100644 index b073fcd46..000000000 --- a/packages/fetch-mock/src/lib/set-up-and-tear-down.js +++ /dev/null @@ -1,135 +0,0 @@ -const FetchMock = {}; - -FetchMock.mock = function (...args) { - if (args.length) { - this.addRoute(args); - } - - return this._mock(); -}; - -FetchMock.addRoute = function (uncompiledRoute) { - const route = this.compileRoute(uncompiledRoute); - const clashes = this.routes.filter(({ identifier, method }) => { - const isMatch = - typeof identifier === 'function' - ? identifier === route.identifier - : String(identifier) === String(route.identifier); - return isMatch && (!method || !route.method || method === route.method); - }); - - if (this.getOption('overwriteRoutes', route) === false || !clashes.length) { - this._uncompiledRoutes.push(uncompiledRoute); - return this.routes.push(route); - } - - if (this.getOption('overwriteRoutes', route) === true) { - clashes.forEach((clash) => { - const index = this.routes.indexOf(clash); - this._uncompiledRoutes.splice(index, 1, uncompiledRoute); - this.routes.splice(index, 1, route); - }); - return this.routes; - } - - if (clashes.length) { - throw new Error( - 'fetch-mock: Adding route with same name or matcher as existing route. See `overwriteRoutes` option.', - ); - } - - this._uncompiledRoutes.push(uncompiledRoute); - this.routes.push(route); -}; - -FetchMock._mock = function () { - if (!this.isSandbox) { - // Do this here rather than in the constructor to ensure it's scoped to the test - this.realFetch = this.realFetch || globalThis.fetch; - globalThis.fetch = this.fetchHandler; - } - return this; -}; - -FetchMock.catch = function (response) { - if (this.fallbackResponse) { - console.warn( - 'calling fetchMock.catch() twice - are you sure you want to overwrite the previous fallback response', - ); - } - this.fallbackResponse = response || 'ok'; - return this._mock(); -}; - -FetchMock.spy = function (route) { - // even though ._mock() is called by .mock() and .catch() we still need to - // call it here otherwise .getNativeFetch() won't be able to use the reference - // to .realFetch that ._mock() sets up - this._mock(); - return route - ? this.mock(route, this.getNativeFetch()) - : this.catch(this.getNativeFetch()); -}; - -const defineShorthand = (methodName, underlyingMethod, shorthandOptions) => { - FetchMock[methodName] = function (matcher, response, options) { - return this[underlyingMethod]( - matcher, - response, - Object.assign(options || {}, shorthandOptions), - ); - }; -}; - -const defineGreedyShorthand = (methodName, underlyingMethod) => { - FetchMock[methodName] = function (response, options) { - return this[underlyingMethod]({}, response, options); - }; -}; - -defineShorthand('sticky', 'mock', { sticky: true }); -defineShorthand('once', 'mock', { repeat: 1 }); -defineGreedyShorthand('any', 'mock'); -defineGreedyShorthand('anyOnce', 'once'); - -['get', 'post', 'put', 'delete', 'head', 'patch'].forEach((method) => { - defineShorthand(method, 'mock', { method }); - defineShorthand(`${method}Once`, 'once', { method }); - defineGreedyShorthand(`${method}Any`, method); - defineGreedyShorthand(`${method}AnyOnce`, `${method}Once`); -}); - -const getRouteRemover = - ({ sticky: removeStickyRoutes }) => - (routes) => - removeStickyRoutes ? [] : routes.filter(({ sticky }) => sticky); - -FetchMock.resetBehavior = function (options = {}) { - const removeRoutes = getRouteRemover(options); - - this.routes = removeRoutes(this.routes); - this._uncompiledRoutes = removeRoutes(this._uncompiledRoutes); - - if (this.realFetch && !this.routes.length) { - globalThis.fetch = this.realFetch; - this.realFetch = undefined; - } - - this.fallbackResponse = undefined; - return this; -}; - -FetchMock.resetHistory = function () { - this._calls = []; - this._holdingPromises = []; - this.routes.forEach((route) => route.reset && route.reset()); - return this; -}; - -FetchMock.restore = FetchMock.reset = function (options) { - this.resetBehavior(options); - this.resetHistory(); - return this; -}; - -export default FetchMock; diff --git a/packages/fetch-mock/src/lib/status-text.js b/packages/fetch-mock/src/lib/status-text.js deleted file mode 100644 index 3717a3701..000000000 --- a/packages/fetch-mock/src/lib/status-text.js +++ /dev/null @@ -1,66 +0,0 @@ -const statusTextMap = { - 100: 'Continue', - 101: 'Switching Protocols', - 102: 'Processing', - 200: 'OK', - 201: 'Created', - 202: 'Accepted', - 203: 'Non-Authoritative Information', - 204: 'No Content', - 205: 'Reset Content', - 206: 'Partial Content', - 207: 'Multi-Status', - 208: 'Already Reported', - 226: 'IM Used', - 300: 'Multiple Choices', - 301: 'Moved Permanently', - 302: 'Found', - 303: 'See Other', - 304: 'Not Modified', - 305: 'Use Proxy', - 307: 'Temporary Redirect', - 308: 'Permanent Redirect', - 400: 'Bad Request', - 401: 'Unauthorized', - 402: 'Payment Required', - 403: 'Forbidden', - 404: 'Not Found', - 405: 'Method Not Allowed', - 406: 'Not Acceptable', - 407: 'Proxy Authentication Required', - 408: 'Request Timeout', - 409: 'Conflict', - 410: 'Gone', - 411: 'Length Required', - 412: 'Precondition Failed', - 413: 'Payload Too Large', - 414: 'URI Too Long', - 415: 'Unsupported Media Type', - 416: 'Range Not Satisfiable', - 417: 'Expectation Failed', - 418: "I'm a teapot", - 421: 'Misdirected Request', - 422: 'Unprocessable Entity', - 423: 'Locked', - 424: 'Failed Dependency', - 425: 'Unordered Collection', - 426: 'Upgrade Required', - 428: 'Precondition Required', - 429: 'Too Many Requests', - 431: 'Request Header Fields Too Large', - 451: 'Unavailable For Legal Reasons', - 500: 'Internal Server Error', - 501: 'Not Implemented', - 502: 'Bad Gateway', - 503: 'Service Unavailable', - 504: 'Gateway Timeout', - 505: 'HTTP Version Not Supported', - 506: 'Variant Also Negotiates', - 507: 'Insufficient Storage', - 508: 'Loop Detected', - 509: 'Bandwidth Limit Exceeded', - 510: 'Not Extended', - 511: 'Network Authentication Required', -}; - -export default statusTextMap; diff --git a/packages/fetch-mock/test/fixtures/fetch-proxy.js b/packages/fetch-mock/test/fixtures/fetch-proxy.js deleted file mode 100644 index f2fc3c003..000000000 --- a/packages/fetch-mock/test/fixtures/fetch-proxy.js +++ /dev/null @@ -1 +0,0 @@ -export default require('node-fetch'); diff --git a/packages/fetch-mock/test/fixtures/sw.js b/packages/fetch-mock/test/fixtures/sw.js deleted file mode 100644 index 8e28c3997..000000000 --- a/packages/fetch-mock/test/fixtures/sw.js +++ /dev/null @@ -1,15 +0,0 @@ -import fetchMock from '../../src/client'; - -self.addEventListener('install', (ev) => { - fetchMock.mock(/.*/, 203); - ev.waitUntil( - fetch('http://egg.on.face/') - .then((res) => { - if (res.status !== 203) { - console.log('Fetch mock not behaving as expected'); - throw 'Unexpected status'; - } - }) - .then(fetchMock.restore), - ); -}); diff --git a/packages/fetch-mock/test/specs/abortable.test.js b/packages/fetch-mock/test/specs/abortable.test.js deleted file mode 100644 index a80341a3e..000000000 --- a/packages/fetch-mock/test/specs/abortable.test.js +++ /dev/null @@ -1,72 +0,0 @@ -import { beforeEach, describe, expect, it } from 'vitest'; - -const RESPONSE_DELAY = 50; -const ABORT_DELAY = 10; - -import fetchMock from '../../src/index.js'; -const getDelayedOk = () => - new Promise((res) => setTimeout(() => res(200), RESPONSE_DELAY)); - -const getDelayedAbortController = () => { - const controller = new AbortController(); - setTimeout(() => controller.abort(), ABORT_DELAY); - return controller; -}; - -describe('abortable fetch', () => { - let fm; - - const expectAbortError = async (...fetchArgs) => { - const result = fm.fetchHandler(...fetchArgs); - await expect(result).rejects.toThrowError( - new DOMException('The operation was aborted.', 'ABortError'), - ); - }; - - beforeEach(() => { - fm = fetchMock.createInstance(); - }); - - it('error on signal abort', () => { - fm.mock('*', getDelayedOk()); - return expectAbortError('http://a.com', { - signal: getDelayedAbortController().signal, - }); - }); - - it('error on signal abort for request object', () => { - fm.mock('*', getDelayedOk()); - return expectAbortError( - new fm.config.Request('http://a.com', { - signal: getDelayedAbortController().signal, - }), - ); - }); - - it('error when signal already aborted', () => { - fm.mock('*', 200); - const controller = new AbortController(); - controller.abort(); - return expectAbortError('http://a.com', { - signal: controller.signal, - }); - }); - - it('go into `done` state even when aborted', async () => { - fm.once('http://a.com', getDelayedOk()); - await expectAbortError('http://a.com', { - signal: getDelayedAbortController().signal, - }); - expect(fm.done()).toBe(true); - }); - - it('will flush even when aborted', async () => { - fm.mock('http://a.com', getDelayedOk()); - - await expectAbortError('http://a.com', { - signal: getDelayedAbortController().signal, - }); - await fm.flush(); - expect(fm.done()).toBe(true); - }); -}); diff --git a/packages/fetch-mock/test/specs/config/constructors.test.js b/packages/fetch-mock/test/specs/config/constructors.test.js deleted file mode 100644 index 659862443..000000000 --- a/packages/fetch-mock/test/specs/config/constructors.test.js +++ /dev/null @@ -1,100 +0,0 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; - -import fetchMock from '../../../src/index.js'; -describe('custom implementations', () => { - let fm; - beforeEach(() => { - fm = fetchMock.createInstance(); - fm.config.warnOnUnmatched = false; - }); - - afterEach(() => fm.restore()); - - describe('fetch classes', () => { - const getHeadersSpy = () => { - const spy = function (config) { - spy.callCount += 1; - if (config) { - return new fetchMock.config.Headers(config); - } - return new fetchMock.config.Headers(); - }; - spy.prototype = fetchMock.config.Headers; - spy.callCount = 0; - return spy; - }; - - const getResponseSpy = () => { - const spy = function (body, opts) { - spy.callCount += 1; - return new fetchMock.config.Response(body, opts); - }; - spy.prototype = fetchMock.config.Response; - spy.callCount = 0; - return spy; - }; - - let defaultSpies; - - beforeEach(() => { - fm = fetchMock.createInstance(); - - defaultSpies = { - Headers: getHeadersSpy(), - Response: getResponseSpy(), - }; - fm.config = Object.assign(fm.config, defaultSpies); - }); - - it('should use the configured Headers when generating a response', async () => { - const spiedReplacementHeaders = getHeadersSpy(); - fm.config.Headers = spiedReplacementHeaders; - fm.mock('*', { - status: 200, - headers: { id: 1 }, - }); - - await fetch('http://a.com'); - expect(spiedReplacementHeaders.callCount).toEqual(1); - expect(defaultSpies.Headers.callCount).toEqual(0); - }); - - it('should use the configured Request when matching', async () => { - const ReplacementRequest = function (url) { - this.url = url; - this.method = 'GET'; - this.headers = []; - }; - - fm.config.Request = ReplacementRequest; - fm.mock('*', 200); - - // As long as this is successful, it's worked, as we've correctly - // matched the request against overridden prototype. - await fetch(new ReplacementRequest('http://a.com')); - - expect(() => fetch(new fetchMock.config.Request('http://a.com'))).toThrow( - 'Unrecognised Request object', - ); - }); - - it('should use the configured Response', async () => { - const obj = { isFake: true }; - // Clone from Response interface is used internally to store copy in call log - obj.clone = () => obj; - const spiedReplacementResponse = vi.fn().mockReturnValue(obj); - fm.config.Response = spiedReplacementResponse; - - fm.mock('*', 'hello'); - - const res = await fetch('http://a.com'); - expect(res.isFake).toBe(true); - expect(spiedReplacementResponse).toHaveBeenCalledTimes(1); - expect(spiedReplacementResponse).toHaveBeenCalledWith( - 'hello', - expect.objectContaining({ status: 200 }), - ); - expect(defaultSpies.Response.callCount).toEqual(0); - }); - }); -}); diff --git a/packages/fetch-mock/test/specs/config/fallbackToNetwork.test.js b/packages/fetch-mock/test/specs/config/fallbackToNetwork.test.js deleted file mode 100644 index e572e527c..000000000 --- a/packages/fetch-mock/test/specs/config/fallbackToNetwork.test.js +++ /dev/null @@ -1,82 +0,0 @@ -import { beforeEach, describe, expect, it } from 'vitest'; - -import fetchMock from '../../../src/index.js'; - -describe('fallbackToNetwork', () => { - let fm; - beforeEach(() => { - fm = fetchMock.createInstance(); - }); - it('error by default', () => { - expect(() => fm.fetchHandler('http://unmocked.com')).toThrow(); - }); - - it('not error when configured globally', () => { - globalThis.fetch = async () => ({ status: 202 }); - fm.config.fallbackToNetwork = true; - fm.mock('http://mocked.com', 201); - expect(() => fm.fetchHandler('http://unmocked.com')).not.toThrow(); - delete globalThis.fetch; - }); - - it('actually falls back to network when configured globally', async () => { - globalThis.fetch = async () => ({ status: 202 }); - fetchMock.config.fallbackToNetwork = true; - fetchMock.mock('http://mocked.com', 201); - const res = await fetchMock.fetchHandler('http://unmocked.com'); - expect(res.status).toEqual(202); - fetchMock.restore(); - fetchMock.config.fallbackToNetwork = false; - delete globalThis.fetch; - }); - - it('actually falls back to network when configured in a sandbox properly', async () => { - const sbx = fm.sandbox(); - sbx.config.fetch = async () => ({ status: 202 }); - sbx.config.fallbackToNetwork = true; - sbx.mock('http://mocked.com', 201); - const res = await sbx('http://unmocked.com'); - expect(res.status).toEqual(202); - }); - - it('calls fetch with original Request object', async () => { - const sbx = fm.sandbox(); - let calledWith; - - sbx.config.fetch = async (req) => { - calledWith = req; - return { status: 202 }; - }; - sbx.config.fallbackToNetwork = true; - sbx.mock('http://mocked.com', 201); - const req = new sbx.config.Request('http://unmocked.com'); - await sbx(req); - expect(calledWith).toEqual(req); - }); - - describe('always', () => { - it('ignores routes that are matched', async () => { - fm.realFetch = async () => ({ status: 202 }); - fm.config.fallbackToNetwork = 'always'; - - fm.mock('http://mocked.com', 201); - const res = await fm.fetchHandler('http://unmocked.com'); - expect(res.status).toEqual(202); - }); - - it('ignores routes that are not matched', async () => { - fm.realFetch = async () => ({ status: 202 }); - - fm.config.fallbackToNetwork = 'always'; - - fm.mock('http://mocked.com', 201); - const res = await fm.fetchHandler('http://unmocked.com'); - expect(res.status).toEqual(202); - }); - }); - - describe.skip('warnOnFallback', () => { - it('warn on fallback response by default', () => {}); - it("don't warn on fallback response when configured false", () => {}); - }); -}); diff --git a/packages/fetch-mock/test/specs/config/includeContentLength.test.js b/packages/fetch-mock/test/specs/config/includeContentLength.test.js deleted file mode 100644 index 9d40c9a43..000000000 --- a/packages/fetch-mock/test/specs/config/includeContentLength.test.js +++ /dev/null @@ -1,36 +0,0 @@ -import { beforeEach, describe, expect, it } from 'vitest'; - -import fetchMock from '../../../src/index.js'; - -describe('includeContentLength', () => { - let fm; - beforeEach(() => { - fm = fetchMock.createInstance(); - }); - it('include content-length header by default', async () => { - fm.mock('*', 'content'); - const res = await fm.fetchHandler('http://it.at.there'); - expect(res.headers.get('content-length')).toEqual('7'); - }); - - it("don't include when configured false", async () => { - fm.config.includeContentLength = false; - fm.mock('*', 'content'); - const res = await fm.fetchHandler('http://it.at.there'); - expect(res.headers.get('content-length')).toBeNull(); - }); - - it('local setting can override to true', async () => { - fm.config.includeContentLength = false; - fm.mock('*', 'content', { includeContentLength: true }); - const res = await fm.fetchHandler('http://it.at.there'); - expect(res.headers.get('content-length')).toEqual('7'); - }); - - it('local setting can override to false', async () => { - fm.config.includeContentLength = true; - fm.mock('*', 'content', { includeContentLength: false }); - const res = await fm.fetchHandler('http://it.at.there'); - expect(res.headers.get('content-length')).toBeNull(); - }); -}); diff --git a/packages/fetch-mock/test/specs/config/matchPartialBody.test.js b/packages/fetch-mock/test/specs/config/matchPartialBody.test.js deleted file mode 100644 index 21b46c93e..000000000 --- a/packages/fetch-mock/test/specs/config/matchPartialBody.test.js +++ /dev/null @@ -1,41 +0,0 @@ -import { beforeEach, describe, expect, it } from 'vitest'; - -import fetchMock from '../../../src/index.js'; - -describe('matchPartialBody', () => { - let fm; - beforeEach(() => { - fm = fetchMock.createInstance(); - }); - - const postExpect = async (expectedStatus) => { - const { status } = await fm.fetchHandler('http://a.com', { - method: 'POST', - body: JSON.stringify({ a: 1, b: 2 }), - }); - expect(status).toEqual(expectedStatus); - }; - - it("don't match partial bodies by default", async () => { - fm.mock({ body: { a: 1 } }, 200).catch(404); - await postExpect(404); - }); - - it('match partial bodies when configured true', async () => { - fm.config.matchPartialBody = true; - fm.mock({ body: { a: 1 } }, 200).catch(404); - await postExpect(200); - }); - - it('local setting can override to false', async () => { - fm.config.matchPartialBody = true; - fm.mock({ body: { a: 1 }, matchPartialBody: false }, 200).catch(404); - await postExpect(404); - }); - - it('local setting can override to true', async () => { - fm.config.matchPartialBody = false; - fm.mock({ body: { a: 1 }, matchPartialBody: true }, 200).catch(404); - await postExpect(200); - }); -}); diff --git a/packages/fetch-mock/test/specs/config/overwriteRoutes.test.js b/packages/fetch-mock/test/specs/config/overwriteRoutes.test.js deleted file mode 100644 index 7b1a661ad..000000000 --- a/packages/fetch-mock/test/specs/config/overwriteRoutes.test.js +++ /dev/null @@ -1,45 +0,0 @@ -import { beforeEach, describe, expect, it } from 'vitest'; - -import fetchMock from '../../../src/index.js'; - -describe('overwriteRoutes', () => { - let fm; - beforeEach(() => { - fm = fetchMock.createInstance(); - }); - it('error on duplicate routes by default', () => { - expect(() => - fm.mock('http://a.com', 200).mock('http://a.com', 300), - ).toThrow(); - }); - - it('allow overwriting existing route', async () => { - fm.config.overwriteRoutes = true; - expect(() => - fm.mock('http://a.com', 200).mock('http://a.com', 300), - ).not.toThrow(); - - const res = await fm.fetchHandler('http://a.com'); - expect(res.status).toEqual(300); - }); - - it('allow overwriting existing route, regex matcher', async () => { - fm.config.overwriteRoutes = true; - expect(() => fm.mock(/a\.com/, 200).mock(/a\.com/, 300)).not.toThrow(); - - const res = await fm.fetchHandler('http://a.com'); - expect(res.status).toEqual(300); - }); - - it('allow adding additional routes with same matcher', async () => { - fm.config.overwriteRoutes = false; - expect(() => - fm.mock('http://a.com', 200, { repeat: 1 }).mock('http://a.com', 300), - ).not.toThrow(); - - const res = await fm.fetchHandler('http://a.com'); - expect(res.status).toEqual(200); - const res2 = await fm.fetchHandler('http://a.com'); - expect(res2.status).toEqual(300); - }); -}); diff --git a/packages/fetch-mock/test/specs/config/sendAsJson.test.js b/packages/fetch-mock/test/specs/config/sendAsJson.test.js deleted file mode 100644 index f093858ec..000000000 --- a/packages/fetch-mock/test/specs/config/sendAsJson.test.js +++ /dev/null @@ -1,40 +0,0 @@ -import { beforeEach, describe, expect, it } from 'vitest'; - -import fetchMock from '../../../src/index.js'; - -describe('sendAsJson', () => { - let fm; - beforeEach(() => { - fm = fetchMock.createInstance(); - }); - it('convert object responses to json by default', async () => { - fm.mock('*', { an: 'object' }); - const res = await fm.fetchHandler('http://it.at.there'); - expect(res.headers.get('content-type')).toEqual('application/json'); - }); - - it("don't convert when configured false", async () => { - fm.config.sendAsJson = false; - fm.mock('*', { an: 'object' }); - const res = await fm.fetchHandler('http://it.at.there'); - // can't check for existence as the spec says, in the browser, that - // a default value should be set - expect(res.headers.get('content-type')).not.toEqual('application/json'); - }); - - it('local setting can override to true', async () => { - fm.config.sendAsJson = false; - fm.mock('*', { an: 'object' }, { sendAsJson: true }); - const res = await fm.fetchHandler('http://it.at.there'); - expect(res.headers.get('content-type')).toEqual('application/json'); - }); - - it('local setting can override to false', async () => { - fm.config.sendAsJson = true; - fm.mock('*', { an: 'object' }, { sendAsJson: false }); - const res = await fm.fetchHandler('http://it.at.there'); - // can't check for existence as the spec says, in the browser, that - // a default value should be set - expect(res.headers.get('content-type')).not.toEqual('application/json'); - }); -}); diff --git a/packages/fetch-mock/test/specs/flush.test.js b/packages/fetch-mock/test/specs/flush.test.js deleted file mode 100644 index 08d1e0527..000000000 --- a/packages/fetch-mock/test/specs/flush.test.js +++ /dev/null @@ -1,96 +0,0 @@ -import { afterEach, describe, expect, it, beforeAll } from 'vitest'; - -import fetchMock from '../../src/index.js'; - -describe('flushing pending calls', () => { - let fm; - beforeAll(() => { - fm = fetchMock.createInstance(); - fm.config.warnOnUnmatched = false; - }); - afterEach(() => fm.restore()); - - it('flush resolves if all fetches have resolved', async () => { - fm.mock('http://one.com/', 200).mock('http://two.com/', 200); - // no expectation, but if it doesn't work then the promises will hang - // or reject and the test will timeout - await fm.flush(); - fetch('http://one.com'); - await fm.flush(); - fetch('http://two.com'); - await fm.flush(); - }); - - it('should resolve after fetches', async () => { - fm.mock('http://example/', 'working!'); - let data; - fetch('http://example').then(() => { - data = 'done'; - }); - await fm.flush(); - expect(data).toEqual('done'); - }); - - describe('response methods', () => { - it('should resolve after .json() if waitForResponseMethods option passed', async () => { - fm.mock('http://example/', { a: 'ok' }); - let data; - fetch('http://example/') - .then((res) => res.json()) - .then(() => { - data = 'done'; - }); - - await fm.flush(true); - expect(data).toEqual('done'); - }); - - it('should resolve after .json() if waitForResponseMethods option passed', async () => { - fm.mock('http://example/', 'bleurgh'); - let data; - fetch('http://example/') - .then((res) => res.json()) - .catch(() => { - data = 'done'; - }); - - await fm.flush(true); - expect(data).toEqual('done'); - }); - - it('should resolve after .text() if waitForResponseMethods option passed', async () => { - fm.mock('http://example/', 'working!'); - let data; - fetch('http://example/') - .then((res) => res.text()) - .then(() => { - data = 'done'; - }); - - await fm.flush(true); - expect(data).toEqual('done'); - }); - }); - - it('flush waits for unresolved promises', async () => { - fm.mock('http://one.com/', 200).mock( - 'http://two.com/', - () => new Promise((res) => setTimeout(() => res(200), 50)), - ); - - const orderedResults = []; - fetch('http://one.com/'); - fetch('http://two.com/'); - - setTimeout(() => orderedResults.push('not flush'), 25); - - await fm.flush(); - orderedResults.push('flush'); - expect(orderedResults).toEqual(['not flush', 'flush']); - }); - - it('flush resolves on expected error', async () => { - fm.mock('http://one.com/', { throws: 'Problem in space' }); - await fm.flush(); - }); -}); diff --git a/packages/fetch-mock/test/specs/global-fetch.test.js b/packages/fetch-mock/test/specs/global-fetch.test.js deleted file mode 100644 index 6c1369f55..000000000 --- a/packages/fetch-mock/test/specs/global-fetch.test.js +++ /dev/null @@ -1,53 +0,0 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; - -import fetchMock from '../../src/index.js'; - -describe('use with global fetch', () => { - let originalFetch; - - const expectToBeStubbed = (yes = true) => { - expect(globalThis.fetch).toEqual( - yes ? fetchMock.fetchHandler : originalFetch, - ); - expect(globalThis.fetch).not.toEqual( - yes ? originalFetch : fetchMock.fetchHandler, - ); - }; - - beforeEach(() => { - originalFetch = globalThis.fetch = vi.fn().mockResolvedValue(); - }); - afterEach(fetchMock.restore); - - it('replaces global fetch when mock called', () => { - fetchMock.mock('*', 200); - expectToBeStubbed(); - }); - - it('replaces global fetch when catch called', () => { - fetchMock.catch(200); - expectToBeStubbed(); - }); - - it('replaces global fetch when spy called', () => { - fetchMock.spy(); - expectToBeStubbed(); - }); - - it('restores global fetch after a mock', () => { - fetchMock.mock('*', 200).restore(); - expectToBeStubbed(false); - }); - - it('restores global fetch after a complex mock', () => { - fetchMock.mock('a', 200).mock('b', 200).spy().catch(404).restore(); - expectToBeStubbed(false); - }); - - it('not call default fetch when in mocked mode', async () => { - fetchMock.mock('*', 200); - - await globalThis.fetch('http://a.com'); - expect(originalFetch).not.toHaveBeenCalled(); - }); -}); diff --git a/packages/fetch-mock/test/specs/inspecting.test.js b/packages/fetch-mock/test/specs/inspecting.test.js deleted file mode 100644 index 3ffecc001..000000000 --- a/packages/fetch-mock/test/specs/inspecting.test.js +++ /dev/null @@ -1,589 +0,0 @@ -import { - afterEach, - beforeEach, - describe, - expect, - it, - beforeAll, - afterAll, - vi, -} from 'vitest'; -// cover case where GET, POST etc are differently named routes -// ... maybe accept method as second argument to calls, called etc -// consider case where multiple routes match.. make sure only one matcher logs calls - -import fetchMock from '../../src/index.js'; - -expect.extend({ - toReturnCalls(callsArray, expectedCalls) { - // looks like it does noting, but it makes sure a bunch of irrelevant internals - // that are passed in array indexes 2 onwards are dropped - const sanitisedCalls = callsArray.map(([url, options]) => [url, options]); - const sanitisedExpectations = expectedCalls.map(([url, options]) => [ - url, - expect.objectContaining(options), - ]); - const assertion = expect(sanitisedCalls).toEqual(sanitisedExpectations); - const passes = Boolean(assertion); - return { - // do not alter your "pass" based on isNot. Vitest does it for you - pass: passes, - message: () => (passes ? `Calls as expected` : `Calls not as expected`), - }; - }, - toEqualCall(call, expectation) { - const sanitisedCall = call.slice(0, 2); - const sanitisedExpectations = [ - expectation[0], - expectation[1] ? expect.objectContaining(expectation[1]) : expectation[1], - ]; - const assertion = expect(sanitisedCall).toEqual(sanitisedExpectations); - const passes = Boolean(assertion); - return { - // do not alter your "pass" based on isNot. Vitest does it for you - pass: passes, - message: () => (passes ? `Call as expected` : `Call not as expected`), - }; - }, -}); - -describe('inspecting', () => { - let fm; - beforeAll(() => { - fm = fetchMock.createInstance(); - fm.config.warnOnUnmatched = false; - }); - - describe('api', () => { - describe('signatures', () => { - beforeAll(() => { - fm.mock('http://a.com/', 200).mock('http://b.com/', 200); - return fm.fetchHandler('http://a.com/', { - method: 'post', - arbitraryOption: true, - }); - }); - afterAll(() => fm.restore()); - it('called() returns boolean', () => { - expect(fm.called('http://a.com/')).toBe(true); - expect(fm.called('http://b.com/')).toBe(false); - }); - it('calls() returns array of calls', () => { - expect(fm.calls('http://a.com/')).toReturnCalls([ - ['http://a.com/', { method: 'post', arbitraryOption: true }], - ]); - expect(fm.calls('http://b.com/')).toEqual([]); - }); - it('lastCall() returns array of parameters', () => { - expect(fm.lastCall('http://a.com/')).toEqualCall([ - 'http://a.com/', - { method: 'post', arbitraryOption: true }, - ]); - expect(fm.lastCall('http://b.com/')).toBeUndefined(); - }); - it('lastUrl() returns string', () => { - expect(fm.lastUrl('http://a.com/')).toEqual('http://a.com/'); - expect(fm.lastUrl('http://b.com/')).toBeUndefined(); - }); - it('lastOptions() returns object', () => { - expect(fm.lastOptions('http://a.com/')).toEqual({ - method: 'post', - arbitraryOption: true, - }); - expect(fm.lastOptions('http://b.com/')).toBeUndefined(); - }); - }); - describe('applying filters', () => { - beforeEach(() => { - vi.spyOn(fm, 'filterCalls').mockReturnValue([]); - }); - afterEach(() => { - fm.filterCalls.mockRestore(); - }); - ['called', 'calls', 'lastCall', 'lastUrl', 'lastOptions'].forEach( - (method) => { - it(`${method}() uses the internal filtering method`, () => { - fm[method]('name', { an: 'option' }); - expect(fm.filterCalls).toHaveBeenCalledWith('name', { - an: 'option', - }); - }); - }, - ); - }); - }); - - describe('filtering', () => { - afterEach(() => fm.reset()); - - const fetchUrls = (...urls) => Promise.all(urls.map(fm.fetchHandler)); - - const expectFilteredLength = - (...filter) => - (length) => - expect(fm.filterCalls(...filter).length).toEqual(length); - - const expectFilteredUrl = - (...filter) => - (url) => - expect(fm.filterCalls(...filter)[0][0]).toEqual(url); - - const expectSingleUrl = - (...filter) => - (url) => { - expectFilteredLength(...filter)(1); - expectFilteredUrl(...filter)(url); - }; - - const expectFilteredResponse = - (...filter) => - (...response) => - expect(fm.filterCalls(...filter)[0]).toEqualCall(response); - - it('returns [url, options] pairs', async () => { - fm.mock('http://a.com/', 200, { name: 'fetch-mock' }); - - await fm.fetchHandler('http://a.com/', { method: 'get' }); - expect(fm.filterCalls()[0]).toEqualCall([ - 'http://a.com/', - { method: 'get' }, - ]); - }); - - it('can retrieve all calls', async () => { - fm.mock('http://a.com/', 200).catch(); - - await fetchUrls('http://a.com/', 'http://b.com/'); - expectFilteredLength()(2); - }); - - it('can retrieve only calls matched by any route', async () => { - fm.mock('http://a.com/', 200).catch(); - - await fetchUrls('http://a.com/', 'http://b.com/'); - expectSingleUrl(true)('http://a.com/'); - expectSingleUrl('matched')('http://a.com/'); - }); - - it('can retrieve only calls not matched by any route', async () => { - fm.mock('http://a.com/', 200).catch(); - - await fetchUrls('http://a.com/', 'http://b.com/'); - expectSingleUrl(false)('http://b.com/'); - expectSingleUrl('unmatched')('http://b.com/'); - }); - - it('can retrieve only calls handled by a named route', async () => { - fm.mock('http://a.com/', 200, { name: 'a' }).catch(); - - await fetchUrls('http://a.com/', 'http://b.com/'); - expectSingleUrl('a')('http://a.com/'); - }); - - it('can retrieve only calls handled by matcher', async () => { - fm.mock('path:/path', 200).catch(); - - await fetchUrls('http://a.com/', 'http://b.com/path'); - expectSingleUrl('path:/path')('http://b.com/path'); - }); - - it('can retrieve only calls handled by a non-string matcher', async () => { - const rx = /path/; - fm.mock(rx, 200).catch(); - - await fetchUrls('http://a.com/', 'http://b.com/path'); - expectSingleUrl(rx)('http://b.com/path'); - }); - - it('can retrieve only calls which match a previously undeclared matcher', async () => { - fm.mock('http://a.com/path', 200).catch(); - - await fm.fetchHandler('http://a.com/path'); - expectSingleUrl('path:/path')('http://a.com/path'); - }); - - describe('filtered by method', () => { - it('can retrieve all calls', async () => { - fm.mock('http://a.com/', 200).catch(); - - await fm.fetchHandler('http://a.com/', { method: 'post' }); - await fm.fetchHandler('http://a.com/'); - await fm.fetchHandler('http://b.com/', { method: 'POST' }); - await fm.fetchHandler('http://b.com/'); - expectFilteredLength(undefined, 'post')(2); - expectFilteredLength(undefined, 'POST')(2); - expect( - fm - .filterCalls(undefined, 'POST') - .filter(([, options]) => options.method.toLowerCase() === 'post') - .length, - ).toEqual(2); - }); - - it('can retrieve only calls matched by any route', async () => { - fm.mock('http://a.com/', 200).catch(); - - await fm.fetchHandler('http://a.com/', { method: 'post' }); - await fm.fetchHandler('http://a.com/'); - await fm.fetchHandler('http://b.com/', { method: 'POST' }); - await fm.fetchHandler('http://b.com/'); - expectFilteredLength(true, 'post')(1); - expectFilteredLength(true, 'POST')(1); - expectFilteredResponse(true, 'POST')('http://a.com/', { - method: 'post', - }); - }); - - it('can retrieve only calls not matched by any route', async () => { - fm.mock('http://a.com/', 200).catch(); - - await fm.fetchHandler('http://a.com/', { method: 'post' }); - await fm.fetchHandler('http://a.com/'); - await fm.fetchHandler('http://b.com/', { method: 'POST' }); - await fm.fetchHandler('http://b.com/'); - expectFilteredLength(false, 'post')(1); - expectFilteredLength(false, 'POST')(1); - expectFilteredResponse(false, 'POST')('http://b.com/', { - method: 'POST', - }); - }); - - it('can retrieve only calls handled by a named route', async () => { - fm.mock('http://a.com/', 200, { name: 'a' }).catch(); - fm.mock('http://b.com/', 200, { name: 'b' }).catch(); - - await fm.fetchHandler('http://a.com/', { method: 'post' }); - await fm.fetchHandler('http://a.com/'); - await fm.fetchHandler('http://b.com/'); - expectFilteredLength('a', 'post')(1); - expectFilteredLength('a', 'POST')(1); - expectFilteredLength('b')(1); - expectFilteredResponse('a', 'POST')('http://a.com/', { - method: 'post', - }); - }); - - it('can retrieve only calls handled by matcher', async () => { - fm.mock('path:/path', 200).catch(); - - await fm.fetchHandler('http://b.com/path', { method: 'post' }); - await fm.fetchHandler('http://b.com/path'); - expectFilteredLength('path:/path', 'post')(1); - expectFilteredLength('path:/path', 'POST')(1); - expectFilteredResponse('path:/path', 'POST')('http://b.com/path', { - method: 'post', - }); - }); - - it('can retrieve only calls handled by a non-string matcher', async () => { - const rx = /path/; - fm.mock(rx, 200).catch(); - - await fm.fetchHandler('http://b.com/path', { method: 'post' }); - await fm.fetchHandler('http://b.com/path'); - expectFilteredLength(rx, 'post')(1); - expectFilteredLength(rx, 'POST')(1); - expectFilteredResponse(rx, 'POST')('http://b.com/path', { - method: 'post', - }); - }); - - it('can retrieve only calls which match a previously undeclared matcher', async () => { - fm.mock('http://a.com/path', 200).catch(); - - await fm.fetchHandler('http://b.com/path', { method: 'post' }); - await fm.fetchHandler('http://b.com/path'); - expectFilteredLength('path:/path', 'post')(1); - expectFilteredLength('path:/path', 'POST')(1); - expectFilteredResponse('path:/path', 'POST')('http://b.com/path', { - method: 'post', - }); - }); - }); - - describe('filtered by options', () => { - it('can retrieve all calls', async () => { - fm.mock('http://a.com/', 200).catch(); - - await fm.fetchHandler('http://a.com/', { - headers: { a: 'z' }, - }); - await fm.fetchHandler('http://a.com/'); - await fm.fetchHandler('http://b.com/', { - headers: { a: 'z' }, - }); - await fm.fetchHandler('http://b.com/'); - expectFilteredLength(undefined, { headers: { a: 'z' } })(2); - expect( - fm - .filterCalls(undefined, { headers: { a: 'z' } }) - .filter(([, options]) => options.headers.a).length, - ).toEqual(2); - }); - - it('can retrieve only calls matched by any route', async () => { - fm.mock('http://a.com/', 200).catch(); - - await fm.fetchHandler('http://a.com/', { - headers: { a: 'z' }, - }); - await fm.fetchHandler('http://a.com/'); - await fm.fetchHandler('http://b.com/', { - headers: { a: 'z' }, - }); - await fm.fetchHandler('http://b.com/'); - expectFilteredLength(true, { headers: { a: 'z' } })(1); - expectFilteredResponse(true, { headers: { a: 'z' } })('http://a.com/', { - headers: { a: 'z' }, - }); - }); - - it('can retrieve only calls not matched by any route', async () => { - fm.mock('http://a.com/', 200).catch(); - - await fm.fetchHandler('http://a.com/', { - headers: { a: 'z' }, - }); - await fm.fetchHandler('http://a.com/'); - await fm.fetchHandler('http://b.com/', { - headers: { a: 'z' }, - }); - await fm.fetchHandler('http://b.com/'); - expectFilteredLength(false, { headers: { a: 'z' } })(1); - expectFilteredResponse(false, { headers: { a: 'z' } })( - 'http://b.com/', - { headers: { a: 'z' } }, - ); - }); - - it('can retrieve only calls handled by a named route', async () => { - fm.mock('http://a.com/', 200, { name: 'here' }).catch(); - - await fm.fetchHandler('http://a.com/', { - headers: { a: 'z' }, - }); - await fm.fetchHandler('http://a.com/'); - expectFilteredLength('here', { headers: { a: 'z' } })(1); - expectFilteredResponse('here', { headers: { a: 'z' } })( - 'http://a.com/', - { headers: { a: 'z' } }, - ); - }); - - it('can retrieve only calls handled by matcher', async () => { - fm.mock('path:/path', 200).catch(); - - await fm.fetchHandler('http://b.com/path', { - headers: { a: 'z' }, - }); - await fm.fetchHandler('http://b.com/path'); - expectFilteredLength('path:/path', { headers: { a: 'z' } })(1); - expectFilteredResponse('path:/path', { - headers: { a: 'z' }, - })('http://b.com/path', { headers: { a: 'z' } }); - }); - - it('can retrieve only calls handled by a non-string matcher', async () => { - const rx = /path/; - fm.mock(rx, 200).catch(); - - await fm.fetchHandler('http://b.com/path', { - headers: { a: 'z' }, - }); - await fm.fetchHandler('http://b.com/path'); - expectFilteredLength(rx, { headers: { a: 'z' } })(1); - expectFilteredResponse(rx, { headers: { a: 'z' } })( - 'http://b.com/path', - { headers: { a: 'z' } }, - ); - }); - - it('can retrieve only calls handled by a body matcher', async () => { - const bodyMatcher = { body: { a: 1 } }; - fm.mock(bodyMatcher, 200).catch(); - - await fm.fetchHandler('http://b.com/path', { - method: 'post', - body: JSON.stringify({ a: 1 }), - }); - await fm.fetchHandler('http://b.com/path', { - method: 'post', - body: JSON.stringify({ a: 2 }), - }); - expectFilteredLength(true, bodyMatcher)(1); - expectFilteredResponse(true, bodyMatcher)('http://b.com/path', { - method: 'post', - body: JSON.stringify({ a: 1 }), - }); - }); - - it('can retrieve only calls handled by a partial body matcher', async () => { - const bodyMatcher = { - body: { a: 1 }, - matchPartialBody: true, - }; - fm.mock(bodyMatcher, 200).catch(); - - await fm.fetchHandler('http://b.com/path', { - method: 'post', - body: JSON.stringify({ a: 1, b: 2 }), - }); - await fm.fetchHandler('http://b.com/path', { - method: 'post', - body: JSON.stringify({ a: 2, b: 2 }), - }); - expectFilteredLength(true, bodyMatcher)(1); - expectFilteredResponse(true, bodyMatcher)('http://b.com/path', { - method: 'post', - body: JSON.stringify({ a: 1, b: 2 }), - }); - }); - - it('can retrieve only calls which match a previously undeclared matcher', async () => { - fm.mock('http://a.com/path', 200).catch(); - - await fm.fetchHandler('http://b.com/path', { - headers: { a: 'z' }, - }); - await fm.fetchHandler('http://b.com/path'); - expectFilteredLength('path:/path', { headers: { a: 'z' } })(1); - expectFilteredResponse('path:/path', { - headers: { a: 'z' }, - })('http://b.com/path', { headers: { a: 'z' } }); - }); - }); - }); - - describe('call order', () => { - it('retrieves calls in correct order', () => { - fm.mock('http://a.com/', 200).mock('http://b.com/', 200).catch(); - - fm.fetchHandler('http://a.com/'); - fm.fetchHandler('http://b.com/'); - fm.fetchHandler('http://b.com/'); - expect(fm.calls()[0][0]).toEqual('http://a.com/'); - expect(fm.calls()[1][0]).toEqual('http://b.com/'); - expect(fm.calls()[2][0]).toEqual('http://b.com/'); - fm.reset(); - }); - }); - - describe('retrieving call parameters', () => { - beforeAll(() => { - fm.mock('http://a.com/', 200); - fm.fetchHandler('http://a.com/'); - fm.fetchHandler('http://a.com/', { method: 'POST' }); - }); - afterAll(() => fm.restore()); - - it('calls (call history)', () => { - expect(fm.calls()[0]).toEqualCall(['http://a.com/', undefined]); - expect(fm.calls()[1]).toEqualCall(['http://a.com/', { method: 'POST' }]); - }); - - it('lastCall', () => { - expect(fm.lastCall()).toEqualCall(['http://a.com/', { method: 'POST' }]); - }); - - it('lastOptions', () => { - expect(fm.lastOptions()).toEqual({ method: 'POST' }); - }); - - it('lastUrl', () => { - expect(fm.lastUrl()).toEqual('http://a.com/'); - }); - - it('when called with Request instance', () => { - const req = new fm.config.Request('http://a.com/', { - method: 'POST', - }); - fm.fetchHandler(req); - const [url, callOptions] = fm.lastCall(); - - expect(url).toEqual('http://a.com/'); - expect(callOptions).toEqual(expect.objectContaining({ method: 'POST' })); - expect(fm.lastUrl()).toEqual('http://a.com/'); - const options = fm.lastOptions(); - expect(options).toEqual(expect.objectContaining({ method: 'POST' })); - expect(fm.lastCall().request).toEqual(req); - }); - - it('when called with Request instance and arbitrary option', () => { - const req = new fm.config.Request('http://a.com/', { - method: 'POST', - }); - fm.fetchHandler(req, { arbitraryOption: true }); - const [url, callOptions] = fm.lastCall(); - expect(url).toEqual('http://a.com/'); - expect(callOptions).toEqual( - expect.objectContaining({ - method: 'POST', - arbitraryOption: true, - }), - ); - expect(fm.lastUrl()).toEqual('http://a.com/'); - const options = fm.lastOptions(); - expect(options).toEqual( - expect.objectContaining({ - method: 'POST', - arbitraryOption: true, - }), - ); - expect(fm.lastCall().request).toEqual(req); - }); - - it('Not make default signal available in options when called with Request instance using signal', () => { - const req = new fm.config.Request('http://a.com/', { - method: 'POST', - }); - fm.fetchHandler(req); - const [, callOptions] = fm.lastCall(); - - expect(callOptions.signal).toBeUndefined(); - const options = fm.lastOptions(); - expect(options.signal).toBeUndefined(); - }); - }); - - describe('retrieving responses', () => { - it('exposes responses', async () => { - fm.once('*', 200).once('*', 201, { overwriteRoutes: false }); - - await fm.fetchHandler('http://a.com/'); - await fm.fetchHandler('http://a.com/'); - expect(fm.calls()[0].response.status).toEqual(200); - expect(fm.calls()[1].response.status).toEqual(201); - fm.restore(); - }); - - it('exposes Responses', async () => { - fm.once('*', new fm.config.Response('blah')); - - await fm.fetchHandler('http://a.com/'); - expect(fm.calls()[0].response.status).toEqual(200); - expect(await fm.calls()[0].response.text()).toEqual('blah'); - fm.restore(); - }); - - it('has lastResponse shorthand', async () => { - fm.once('*', 200).once('*', 201, { overwriteRoutes: false }); - - await fm.fetchHandler('http://a.com/'); - await fm.fetchHandler('http://a.com/'); - expect(fm.lastResponse().status).toEqual(201); - fm.restore(); - }); - - it('has readable response when response already read if using lastResponse', async () => { - const respBody = { foo: 'bar' }; - fm.once('*', { status: 200, body: respBody }).once('*', 201, { - overwriteRoutes: false, - }); - - const resp = await fm.fetchHandler('http://a.com/'); - - await resp.json(); - expect(await fm.lastResponse().json()).toEqual(respBody); - }); - }); -}); diff --git a/packages/fetch-mock/test/specs/repeat.test.js b/packages/fetch-mock/test/specs/repeat.test.js deleted file mode 100644 index 6048b0172..000000000 --- a/packages/fetch-mock/test/specs/repeat.test.js +++ /dev/null @@ -1,228 +0,0 @@ -import { afterEach, describe, expect, it, beforeAll, vi } from 'vitest'; - -import fetchMock from '../../src/index.js'; -describe('repeat and done()', () => { - let fm; - beforeAll(() => { - fm = fetchMock.createInstance(); - fm.config.warnOnUnmatched = false; - }); - - afterEach(() => fm.restore()); - - it('can expect a route to be called', () => { - fm.mock('http://a.com/', 200); - - expect(fm.done()).toBe(false); - expect(fm.done('http://a.com/')).toBe(false); - fm.fetchHandler('http://a.com/'); - expect(fm.done()).toBe(true); - expect(fm.done('http://a.com/')).toBe(true); - }); - - it('can expect a route to be called n times', () => { - fm.mock('http://a.com/', 200, { repeat: 2 }); - - fm.fetchHandler('http://a.com/'); - expect(fm.done()).toBe(false); - expect(fm.done('http://a.com/')).toBe(false); - fm.fetchHandler('http://a.com/'); - expect(fm.done()).toBe(true); - expect(fm.done('http://a.com/')).toBe(true); - }); - - it('regression: can expect an un-normalized url to be called n times', () => { - fm.mock('http://a.com/', 200, { repeat: 2 }); - fm.fetchHandler('http://a.com/'); - expect(fm.done()).toBe(false); - fm.fetchHandler('http://a.com/'); - expect(fm.done()).toBe(true); - }); - - it('can expect multiple routes to have been called', () => { - fm.mock('http://a.com/', 200, { - repeat: 2, - }).mock('http://b.com/', 200, { repeat: 2 }); - - fm.fetchHandler('http://a.com/'); - expect(fm.done()).toBe(false); - expect(fm.done('http://a.com/')).toBe(false); - expect(fm.done('http://b.com/')).toBe(false); - fm.fetchHandler('http://a.com/'); - expect(fm.done()).toBe(false); - expect(fm.done('http://a.com/')).toBe(true); - expect(fm.done('http://b.com/')).toBe(false); - fm.fetchHandler('http://b.com/'); - expect(fm.done()).toBe(false); - expect(fm.done('http://a.com/')).toBe(true); - expect(fm.done('http://b.com/')).toBe(false); - fm.fetchHandler('http://b.com/'); - expect(fm.done()).toBe(true); - expect(fm.done('http://a.com/')).toBe(true); - expect(fm.done('http://b.com/')).toBe(true); - }); - - // todo more tests for filtering - it('`done` filters on match types', async () => { - fm.once('http://a.com/', 200) - .once('http://b.com/', 200) - .once('http://c.com/', 200) - .catch(); - - await fm.fetchHandler('http://a.com/'); - await fm.fetchHandler('http://b.com/'); - expect(fm.done()).toBe(false); - expect(fm.done(true)).toBe(false); - expect(fm.done('http://a.com/')).toBe(true); - expect(fm.done('http://b.com/')).toBe(true); - expect(fm.done('http://c.com/')).toBe(false); - }); - - it("can tell when done if using '*'", () => { - fm.mock('*', '200'); - fm.fetchHandler('http://a.com'); - expect(fm.done()).toBe(true); - }); - - it('can tell when done if using begin:', () => { - fm.mock('begin:http', '200'); - fm.fetchHandler('http://a.com'); - expect(fm.done()).toBe(true); - }); - - it("won't mock if route already matched enough times", async () => { - fm.mock('http://a.com/', 200, { repeat: 1 }); - - await fm.fetchHandler('http://a.com/'); - try { - await fm.fetchHandler('http://a.com/'); - expect.unreachable('Previous line should throw'); - } catch {} // eslint-disable-line no-empty - }); - - it('falls back to second route if first route already done', async () => { - fm.mock('http://a.com/', 404, { - repeat: 1, - }).mock('http://a.com/', 200, { overwriteRoutes: false }); - - const res = await fm.fetchHandler('http://a.com/'); - expect(res.status).toEqual(404); - - const res2 = await fm.fetchHandler('http://a.com/'); - expect(res2.status).toEqual(200); - }); - - it('resetHistory() resets count', async () => { - fm.mock('http://a.com/', 200, { repeat: 1 }); - await fm.fetchHandler('http://a.com/'); - expect(fm.done()).toBe(true); - fm.resetHistory(); - expect(fm.done()).toBe(false); - expect(fm.done('http://a.com/')).toBe(false); - await fm.fetchHandler('http://a.com/'); - expect(fm.done()).toBe(true); - expect(fm.done('http://a.com/')).toBe(true); - }); - - it('logs unmatched calls', () => { - vi.spyOn(console, 'warn'); - fm.mock('http://a.com/', 200).mock('http://b.com/', 200, { - repeat: 2, - }); - - fm.fetchHandler('http://b.com/'); - fm.done(); - expect(console.warn).toHaveBeenCalledWith( - 'Warning: http://a.com/ not called', - ); - expect(console.warn).toHaveBeenCalledWith( - 'Warning: http://b.com/ only called 1 times, but 2 expected', - ); - - console.warn.mockClear(); - fm.done('http://a.com/'); - expect(console.warn).toHaveBeenCalledWith( - 'Warning: http://a.com/ not called', - ); - expect(console.warn).not.toHaveBeenCalledWith( - 'Warning: http://b.com/ only called 1 times, but 2 expected', - ); - console.warn.mockRestore(); - }); - - describe('sandbox isolation', () => { - it("doesn't propagate to children of global", () => { - fm.mock('http://a.com/', 200, { repeat: 1 }); - - const sb1 = fm.sandbox(); - - fm.fetchHandler('http://a.com/'); - - expect(fm.done()).toBe(true); - expect(sb1.done()).toBe(false); - - expect(() => sb1.fetchHandler('http://a.com/')).not.toThrow(); - }); - - it("doesn't propagate to global from children", () => { - fm.mock('http://a.com/', 200, { repeat: 1 }); - - const sb1 = fm.sandbox(); - - sb1.fetchHandler('http://a.com/'); - - expect(fm.done()).toBe(false); - expect(sb1.done()).toBe(true); - - expect(() => fm.fetchHandler('http://a.com/')).not.toThrow(); - }); - - it("doesn't propagate to children of sandbox", () => { - const sb1 = fm.sandbox().mock('http://a.com/', 200, { repeat: 1 }); - - const sb2 = sb1.sandbox(); - - sb1.fetchHandler('http://a.com/'); - - expect(sb1.done()).toBe(true); - expect(sb2.done()).toBe(false); - - expect(() => sb2.fetchHandler('http://a.com/')).not.toThrow(); - }); - - it("doesn't propagate to sandbox from children", () => { - const sb1 = fm.sandbox().mock('http://a.com/', 200, { repeat: 1 }); - - const sb2 = sb1.sandbox(); - - sb2.fetchHandler('http://a.com/'); - - expect(sb1.done()).toBe(false); - expect(sb2.done()).toBe(true); - - expect(() => sb1.fetchHandler('http://a.com/')).not.toThrow(); - }); - - it('Allow overwriting routes when using multiple function matchers', async () => { - const matcher1 = () => true; - - const matcher2 = () => true; - - const sb = fm.sandbox(); - - expect(() => - sb.postOnce(matcher1, 200).postOnce(matcher2, 200), - ).not.toThrow(); - - await sb('https://example.com/', { method: 'POST' }); - expect(sb.done()).toBe(false); - expect(sb.done(matcher1)).toBe(true); - expect(sb.done(matcher2)).toBe(false); - await sb('https://example.com/', { method: 'POST' }); - - expect(sb.done()).toBe(true); - expect(sb.done(matcher1)).toBe(true); - expect(sb.done(matcher2)).toBe(true); - }); - }); -}); diff --git a/packages/fetch-mock/test/specs/responses/client-only.test.js b/packages/fetch-mock/test/specs/responses/client-only.test.js deleted file mode 100644 index 96fbff891..000000000 --- a/packages/fetch-mock/test/specs/responses/client-only.test.js +++ /dev/null @@ -1,78 +0,0 @@ -import { afterEach, describe, expect, it } from 'vitest'; -// const chai = require('chai'); -// const chaiAsPromised = require('chai-as-promised'); -// chai.use(chaiAsPromised); -import fetchMock from '../../../src/index.js'; - -describe.skip('client-side only tests', () => { - afterEach(() => fetchMock.restore()); - it('not throw when passing unmatched calls through to native fetch', () => { - fetchMock.config.fallbackToNetwork = true; - fetchMock.mock(); - expect(() => fetch('http://a.com')).not.to.throw(); - fetchMock.config.fallbackToNetwork = false; - }); - - // this is because we read the body once when normalising the request and - // want to make sure fetch can still use the sullied request - it.skip('can send a body on a Request instance when spying ', async () => { - fetchMock.spy(); - const req = new fetchMock.config.Request('http://example.com', { - method: 'post', - body: JSON.stringify({ prop: 'val' }), - }); - try { - await fetch(req); - } catch (err) { - console.log(err); - expect.unreachable('Fetch should not throw or reject'); - } - }); - - it('respond with blob', async () => { - const blob = new Blob(); - fetchMock.mock('*', blob, { sendAsJson: false }); - const res = await fetch('http://a.com'); - expect(res.status).to.equal(200); - const blobData = await res.blob(); - expect(blobData).to.eql(blob); - }); - - it.skip('should cope when there is no global fetch defined', () => { - const originalFetch = globalThis.fetch; - delete globalThis.fetch; - const originalRealFetch = fetchMock.realFetch; - delete fetchMock.realFetch; - fetchMock.mock('*', 200); - expect(() => { - fetch('http://a.com'); - }).not.to.throw(); - - expect(() => { - fetchMock.calls(); - }).not.to.throw(); - fetchMock.restore(); - fetchMock.realFetch = originalRealFetch; - globalThis.fetch = originalFetch; - }); - - if (globalThis.navigator?.serviceWorker) { - it('should work within a service worker', async () => { - const registration = - await globalThis.navigator.serviceWorker.register('__sw.js'); - await new Promise((resolve, reject) => { - if (registration.installing) { - registration.installing.onstatechange = function () { - if (this.state === 'activated') { - resolve(); - } - }; - } else { - reject('No idea what happened'); - } - }); - - await registration.unregister(); - }); - } -}); diff --git a/packages/fetch-mock/test/specs/responses/generation.test.js b/packages/fetch-mock/test/specs/responses/generation.test.js deleted file mode 100644 index 800290ccc..000000000 --- a/packages/fetch-mock/test/specs/responses/generation.test.js +++ /dev/null @@ -1,225 +0,0 @@ -import { afterEach, describe, expect, it, beforeAll } from 'vitest'; - -import fetchMock from '../../../src/index.js'; - -describe('response generation', () => { - let fm; - beforeAll(() => { - fm = fetchMock.createInstance(); - fm.config.warnOnUnmatched = false; - }); - - afterEach(() => fm.restore()); - - describe('status', () => { - it('respond with a status', async () => { - fm.mock('*', 300); - const res = await fm.fetchHandler('http://a.com/'); - expect(res.status).toEqual(300); - expect(res.statusText).toEqual('Multiple Choices'); - }); - - it('should error on invalid statuses', async () => { - fm.mock('*', { status: 'not number' }); - try { - await fm.fetchHandler('http://a.com'); - expect.unreachable('Line above should throw'); - } catch (err) { - expect(err.message).toMatch( - /Invalid status not number passed on response object/, - ); - } - }); - }); - - describe('string', () => { - it('respond with a string', async () => { - fm.mock('*', 'a string'); - const res = await fm.fetchHandler('http://a.com/'); - expect(res.status).toEqual(200); - expect(res.statusText).toEqual('OK'); - expect(await res.text()).toEqual('a string'); - }); - - it('respond with an empty string', async () => { - fm.mock('*', ''); - const res = await fm.fetchHandler('http://a.com/'); - expect(res.status).toEqual(200); - expect(res.statusText).toEqual('OK'); - expect(await res.text()).toEqual(''); - }); - }); - - describe('json', () => { - it('respond with a json', async () => { - fm.mock('*', { an: 'object' }); - const res = await fm.fetchHandler('http://a.com/'); - expect(res.status).toEqual(200); - expect(res.statusText).toEqual('OK'); - expect(res.headers.get('content-type')).toEqual('application/json'); - expect(await res.json()).toEqual({ an: 'object' }); - }); - - it('convert body properties to json', async () => { - fm.mock('*', { - body: { an: 'object' }, - }); - const res = await fm.fetchHandler('http://a.com/'); - expect(res.headers.get('content-type')).toEqual('application/json'); - expect(await res.json()).toEqual({ an: 'object' }); - }); - - it('not overide existing content-type-header', async () => { - fm.mock('*', { - body: { an: 'object' }, - headers: { - 'content-type': 'text/html', - }, - }); - const res = await fm.fetchHandler('http://a.com/'); - expect(res.headers.get('content-type')).toEqual('text/html'); - expect(await res.json()).toEqual({ an: 'object' }); - }); - - it('not convert if `body` property exists', async () => { - fm.mock('*', { body: 'exists' }); - const res = await fm.fetchHandler('http://a.com/'); - expect(res.headers.get('content-type')).not.toEqual('application/json'); - }); - - it('not convert if `headers` property exists', async () => { - fm.mock('*', { headers: {} }); - const res = await fm.fetchHandler('http://a.com/'); - expect(res.headers.get('content-type')).toBeNull(); - }); - - it('not convert if `status` property exists', async () => { - fm.mock('*', { status: 300 }); - const res = await fm.fetchHandler('http://a.com/'); - expect(res.headers.get('content-type')).toBeNull(); - }); - - // in the browser the fetch spec disallows invoking res.headers on an - // object that inherits from a response, thus breaking the ability to - // read headers of a fake redirected response. - if (typeof window === 'undefined') { - it('not convert if `redirectUrl` property exists', async () => { - fm.mock('*', { - redirectUrl: 'http://url.to.hit', - }); - const res = await fm.fetchHandler('http://a.com/'); - expect(res.headers.get('content-type')).toBeNull(); - }); - } - - it('convert if non-whitelisted property exists', async () => { - fm.mock('*', { status: 300, weird: true }); - const res = await fm.fetchHandler('http://a.com/'); - expect(res.headers.get('content-type')).toEqual('application/json'); - }); - }); - - it('respond with a complex response, including headers', async () => { - fm.mock('*', { - status: 202, - body: { an: 'object' }, - headers: { - header: 'val', - }, - }); - const res = await fm.fetchHandler('http://a.com/'); - expect(res.status).toEqual(202); - expect(res.headers.get('header')).toEqual('val'); - expect(await res.json()).toEqual({ an: 'object' }); - }); - - // The fetch spec does not allow for manual url setting - // However node-fetch does, so we only run this test on the server - if (fetchMock.config.Request !== globalThis.Request) { - it('should set the url property on responses', async () => { - fm.mock('begin:http://foo.com', 200); - const res = await fm.fetchHandler('http://foo.com/path?query=string'); - expect(res.url).toEqual('http://foo.com/path?query=string'); - }); - - it('should set the url property on responses when called with Request', async () => { - fm.mock('begin:http://foo.com', 200); - const res = await fm.fetchHandler( - new fm.config.Request('http://foo.com/path?query=string'), - ); - expect(res.url).toEqual('http://foo.com/path?query=string'); - }); - } - - it('respond with a redirected response', async () => { - fm.mock('*', { - redirectUrl: 'http://b.com', - body: 'I am a redirect', - }); - const res = await fm.fetchHandler('http://a.com/'); - expect(res.redirected).toEqual(true); - expect(res.url).toEqual('http://b.com'); - expect(await res.text()).toEqual('I am a redirect'); - }); - - it('construct a response based on the request', async () => { - fm.mock('*', (url, opts) => url + opts.headers.header); - const res = await fm.fetchHandler('http://a.com/', { - headers: { header: 'val' }, - }); - expect(res.status).toEqual(200); - expect(await res.text()).toEqual('http://a.com/val'); - }); - - it('construct a response based on a Request instance', async () => { - fm.mock('*', (url, opts, request) => request.json().then(({ a }) => a)); - const res = await fm.fetchHandler( - new fm.config.Request('http://a.com', { - body: JSON.stringify({ a: 'b' }), - method: 'post', - }), - ); - expect(res.status).toEqual(200); - expect(await res.text()).toEqual('b'); - }); - - describe('content-length', () => { - it('should work on body of type string', async () => { - fm.mock('*', 'content'); - const res = await fetch('http://a.com/'); - expect(res.headers.get('content-length')).toEqual('7'); - }); - - it('should work on body of type object', async () => { - fm.mock('*', { hello: 'world' }); - const res = await fetch('http://a.com/'); - expect(res.headers.get('content-length')).toEqual('17'); - }); - - it('should not overrule explicit mocked content-length header', async () => { - fm.mock('*', { - body: { - hello: 'world', - }, - headers: { - 'Content-Length': '100', - }, - }); - const res = await fetch('http://a.com/'); - expect(res.headers.get('content-length')).toEqual('100'); - }); - - it('should be case-insensitive when checking for explicit content-length header', async () => { - fm.mock('*', { - body: { - hello: 'world', - }, - headers: { - 'CoNtEnT-LeNgTh': '100', - }, - }); - const res = await fetch('http://a.com/'); - expect(res.headers.get('content-length')).toEqual('100'); - }); - }); -}); diff --git a/packages/fetch-mock/test/specs/responses/negotiation.js b/packages/fetch-mock/test/specs/responses/negotiation.js deleted file mode 100644 index 918e83c7a..000000000 --- a/packages/fetch-mock/test/specs/responses/negotiation.js +++ /dev/null @@ -1,170 +0,0 @@ -import { afterEach, describe, expect, it, beforeAll } from 'vitest'; - -import fetchMock from '../../../src/index.js'; - -describe('response negotiation', () => { - let fm; - beforeAll(() => { - fm = fetchMock.createInstance(); - fm.config.warnOnUnmatched = false; - }); - - afterEach(() => fm.restore()); - - it('function', async () => { - fm.mock('*', (url) => url); - const res = await fm.fetchHandler('http://a.com/'); - expect(res.status).toEqual(200); - expect(await res.text()).toEqual('http://a.com/'); - }); - - it('Promise', async () => { - fm.mock('*', Promise.resolve(200)); - const res = await fm.fetchHandler('http://a.com/'); - expect(res.status).toEqual(200); - }); - - it('function that returns a Promise', async () => { - fm.mock('*', (url) => Promise.resolve(`test: ${url}`)); - const res = await fm.fetchHandler('http://a.com/'); - expect(res.status).toEqual(200); - expect(await res.text()).toEqual('test: http://a.com/'); - }); - - it('Promise for a function that returns a response', async () => { - fm.mock( - 'http://a.com/', - Promise.resolve((url) => `test: ${url}`), - ); - const res = await fm.fetchHandler('http://a.com/'); - expect(res.status).toEqual(200); - expect(await res.text()).toEqual('test: http://a.com/'); - }); - - it('delay', async () => { - fm.mock('*', 200, { delay: 20 }); - const req = fm.fetchHandler('http://a.com/'); - let resolved = false; - req.then(() => { - resolved = true; - }); - await new Promise((res) => setTimeout(res, 10)); - expect(resolved).toBe(false); - await new Promise((res) => setTimeout(res, 11)); - expect(resolved).toBe(true); - const res = await req; - expect(res.status).toEqual(200); - }); - - it("delay a function response's execution", async () => { - const startTimestamp = new Date().getTime(); - fm.mock('http://a.com/', () => ({ timestamp: new Date().getTime() }), { - delay: 20, - }); - const req = fm.fetchHandler('http://a.com/'); - let resolved = false; - req.then(() => { - resolved = true; - }); - await new Promise((res) => setTimeout(res, 10)); - expect(resolved).toBe(false); - await new Promise((res) => setTimeout(res, 11)); - expect(resolved).toBe(true); - const res = await req; - expect(res.status).toEqual(200); - const responseTimestamp = (await res.json()).timestamp; - expect(responseTimestamp - startTimestamp).toBeGreaterThanOrEqual(20); - }); - - it('pass values to delayed function', async () => { - fm.mock('*', (url) => `delayed: ${url}`, { - delay: 10, - }); - const req = fm.fetchHandler('http://a.com/'); - await new Promise((res) => setTimeout(res, 11)); - const res = await req; - expect(res.status).toEqual(200); - expect(await res.text()).toEqual('delayed: http://a.com/'); - }); - - it('call delayed response multiple times, each with the same delay', async () => { - fm.mock('*', 200, { delay: 20 }); - const req1 = fm.fetchHandler('http://a.com/'); - let resolved = false; - req1.then(() => { - resolved = true; - }); - await new Promise((res) => setTimeout(res, 10)); - expect(resolved).toBe(false); - await new Promise((res) => setTimeout(res, 11)); - expect(resolved).toBe(true); - const res1 = await req1; - expect(res1.status).toEqual(200); - const req2 = fm.fetchHandler('http://a.com/'); - resolved = false; - req2.then(() => { - resolved = true; - }); - await new Promise((res) => setTimeout(res, 10)); - expect(resolved).toBe(false); - await new Promise((res) => setTimeout(res, 11)); - expect(resolved).toBe(true); - const res2 = await req2; - expect(res2.status).toEqual(200); - }); - - it('Response', async () => { - fm.mock( - 'http://a.com/', - new fm.config.Response('http://a.com/', { status: 200 }), - ); - const res = await fm.fetchHandler('http://a.com/'); - expect(res.status).toEqual(200); - }); - - it('function that returns a Response', async () => { - fm.mock( - 'http://a.com/', - () => new fm.config.Response('http://a.com/', { status: 200 }), - ); - const res = await fm.fetchHandler('http://a.com/'); - expect(res.status).toEqual(200); - }); - - it('Promise that returns a Response', async () => { - fm.mock( - 'http://a.com/', - Promise.resolve(new fm.config.Response('http://a.com/', { status: 200 })), - ); - const res = await fm.fetchHandler('http://a.com/'); - expect(res.status).toEqual(200); - }); - - describe('rejecting', () => { - it('reject if object with `throws` property', () => { - fm.mock('*', { throws: 'as expected' }); - - return fm - .fetchHandler('http://a.com/') - .then(() => { - throw 'not as expected'; - }) - .catch((err) => { - expect(err).toEqual('as expected'); - }); - }); - - it('reject if function that returns object with `throws` property', () => { - fm.mock('*', () => ({ throws: 'as expected' })); - - return fm - .fetchHandler('http://a.com/') - .then(() => { - throw 'not as expected'; - }) - .catch((err) => { - expect(err).toEqual('as expected'); - }); - }); - }); -}); diff --git a/packages/fetch-mock/test/specs/routing/body-matching.test.js b/packages/fetch-mock/test/specs/routing/body-matching.test.js deleted file mode 100644 index a6d266421..000000000 --- a/packages/fetch-mock/test/specs/routing/body-matching.test.js +++ /dev/null @@ -1,173 +0,0 @@ -import { afterEach, describe, expect, it, beforeAll } from 'vitest'; - -import fetchMock from '../../../src/index.js'; -describe('body matching', () => { - let fm; - beforeAll(() => { - fm = fetchMock.createInstance(); - fm.config.warnOnUnmatched = false; - }); - - afterEach(() => fm.restore()); - - it('should not match if no body provided in request', async () => { - fm.mock({ body: { foo: 'bar' } }, 200).catch(); - - await fm.fetchHandler('http://a.com/', { - method: 'POST', - }); - expect(fm.calls(true).length).toEqual(0); - }); - - it('should match if no content type is specified', async () => { - fm.mock({ body: { foo: 'bar' } }, 200).catch(); - - await fm.fetchHandler('http://a.com/', { - method: 'POST', - body: JSON.stringify({ foo: 'bar' }), - }); - expect(fm.calls(true).length).toEqual(1); - }); - - it('should match when using Request', async () => { - fm.mock({ body: { foo: 'bar' } }, 200).catch(); - - await fm.fetchHandler( - new fm.config.Request('http://a.com/', { - method: 'POST', - body: JSON.stringify({ foo: 'bar' }), - }), - ); - expect(fm.calls(true).length).toEqual(1); - }); - - it('should match if body sent matches expected body', async () => { - fm.mock({ body: { foo: 'bar' } }, 200).catch(); - - await fm.fetchHandler('http://a.com/', { - method: 'POST', - body: JSON.stringify({ foo: 'bar' }), - headers: { 'Content-Type': 'application/json' }, - }); - expect(fm.calls(true).length).toEqual(1); - }); - - it('should not match if body sent doesn’t match expected body', async () => { - fm.mock({ body: { foo: 'bar' } }, 200).catch(); - - await fm.fetchHandler('http://a.com/', { - method: 'POST', - body: JSON.stringify({ foo: 'woah!!!' }), - headers: { 'Content-Type': 'application/json' }, - }); - expect(fm.calls(true).length).toEqual(0); - }); - - it('should not match if body sent isn’t JSON', async () => { - fm.mock({ body: { foo: 'bar' } }, 200).catch(); - - await fm.fetchHandler('http://a.com/', { - method: 'POST', - body: new ArrayBuffer(8), - headers: { 'Content-Type': 'application/json' }, - }); - expect(fm.calls(true).length).toEqual(0); - }); - - it('should ignore the order of the keys in the body', async () => { - fm.mock( - { - body: { - foo: 'bar', - baz: 'qux', - }, - }, - 200, - ).catch(); - - await fm.fetchHandler('http://a.com/', { - method: 'POST', - body: JSON.stringify({ - baz: 'qux', - foo: 'bar', - }), - headers: { 'Content-Type': 'application/json' }, - }); - expect(fm.calls(true).length).toEqual(1); - }); - - it('should ignore the body option matcher if request was GET', async () => { - fm.mock( - { - body: { - foo: 'bar', - baz: 'qux', - }, - }, - 200, - ).catch(); - - await fm.fetchHandler('http://a.com/'); - expect(fm.calls(true).length).toEqual(1); - }); - - describe('partial body matching', () => { - it('match when missing properties', async () => { - fm.mock({ body: { ham: 'sandwich' }, matchPartialBody: true }, 200).catch( - 404, - ); - const res = await fm.fetchHandler('http://a.com', { - method: 'POST', - body: JSON.stringify({ ham: 'sandwich', egg: 'mayonaise' }), - }); - expect(res.status).toEqual(200); - }); - - it('match when missing nested properties', async () => { - fm.mock( - { body: { meal: { ham: 'sandwich' } }, matchPartialBody: true }, - 200, - ).catch(404); - const res = await fm.fetchHandler('http://a.com', { - method: 'POST', - body: JSON.stringify({ - meal: { ham: 'sandwich', egg: 'mayonaise' }, - }), - }); - expect(res.status).toEqual(200); - }); - - it('not match when properties at wrong indentation', async () => { - fm.mock({ body: { ham: 'sandwich' }, matchPartialBody: true }, 200).catch( - 404, - ); - const res = await fm.fetchHandler('http://a.com', { - method: 'POST', - body: JSON.stringify({ meal: { ham: 'sandwich' } }), - }); - expect(res.status).toEqual(404); - }); - - it('match when starting subset of array', async () => { - fm.mock({ body: { ham: [1, 2] }, matchPartialBody: true }, 200).catch( - 404, - ); - const res = await fm.fetchHandler('http://a.com', { - method: 'POST', - body: JSON.stringify({ ham: [1, 2, 3] }), - }); - expect(res.status).toEqual(200); - }); - - it('not match when not starting subset of array', async () => { - fm.mock({ body: { ham: [1, 3] }, matchPartialBody: true }, 200).catch( - 404, - ); - const res = await fm.fetchHandler('http://a.com', { - method: 'POST', - body: JSON.stringify({ ham: [1, 2, 3] }), - }); - expect(res.status).toEqual(404); - }); - }); -}); diff --git a/packages/fetch-mock/test/specs/routing/edge-cases.test.js b/packages/fetch-mock/test/specs/routing/edge-cases.test.js deleted file mode 100644 index f899eee71..000000000 --- a/packages/fetch-mock/test/specs/routing/edge-cases.test.js +++ /dev/null @@ -1,76 +0,0 @@ -import { afterEach, describe, expect, it, beforeAll } from 'vitest'; - -import fetchMock from '../../../src/index.js'; -describe('edge cases', () => { - let fm; - beforeAll(() => { - fm = fetchMock.createInstance(); - fm.config.warnOnUnmatched = false; - }); - - afterEach(() => fm.restore()); - - it('match relative urls', async () => { - fm.mock('/a.com/', 200).catch(); - - await fm.fetchHandler('/a.com/'); - expect(fm.calls(true).length).toEqual(1); - }); - - it('match relative urls with dots', async () => { - fm.mock('/it.at/there/', 200).catch(); - - await fm.fetchHandler('/it.at/not/../there/'); - expect(fm.calls(true).length).toEqual(1); - await fm.fetchHandler('./it.at/there/'); - expect(fm.calls(true).length).toEqual(2); - }); - - it('match absolute urls with dots', async () => { - fm.mock('http://it.at/there/', 200).catch(); - - await fm.fetchHandler('http://it.at/not/../there/'); - expect(fm.calls(true).length).toEqual(1); - }); - - it('match when called with Request', async () => { - fm.post('http://a.com/', 200).catch(); - - await fm.fetchHandler( - new fm.config.Request('http://a.com/', { method: 'POST' }), - ); - expect(fm.calls(true).length).toEqual(1); - }); - - it('allow routes only differing in query strings', () => { - expect(() => { - fm.get('/xyz/abc?id=486726&id=486727', 200); - fm.get('/xyz/abc?id=486727', 200); - }).not.toThrow(); - }); - - it('express match full url', async () => { - fm.mock('express:/apps/:id', 200).catch(); - - await fm.fetchHandler('https://api.example.com/apps/abc'); - expect(fm.calls(true).length).toEqual(1); - }); - it('setup routes correctly when using object definitions', async () => { - fm.get({ - matcher: 'express:/:var', - response: 200, - }).put({ - matcher: 'express:/:var', - response: 201, - overwriteRoutes: false, - }); - - const { status } = await fm.fetchHandler('https://api.example.com/lala', { - method: 'put', - }); - // before fixing this test it was returning 200 for the put request - // because both teh .get() and .put() calls were failing to correctly - // add the choice of method to the route config - expect(status).toEqual(201); - }); -}); diff --git a/packages/fetch-mock/test/specs/routing/function-matching.test.js b/packages/fetch-mock/test/specs/routing/function-matching.test.js deleted file mode 100644 index 006cc2437..000000000 --- a/packages/fetch-mock/test/specs/routing/function-matching.test.js +++ /dev/null @@ -1,101 +0,0 @@ -import { afterEach, describe, expect, it, beforeAll } from 'vitest'; - -import fetchMock from '../../../src/index.js'; -describe('function matching', () => { - let fm; - beforeAll(() => { - fm = fetchMock.createInstance(); - fm.config.warnOnUnmatched = false; - }); - - afterEach(() => fm.restore()); - - it('match using custom function', async () => { - fm.mock( - (url, opts) => - url.indexOf('logged-in') > -1 && - opts && - opts.headers && - opts.headers.authorized === true, - 200, - ).catch(); - - await fm.fetchHandler('http://a.com/12345', { - headers: { authorized: true }, - }); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('http://a.com/logged-in'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('http://a.com/logged-in', { - headers: { authorized: true }, - }); - expect(fm.calls(true).length).toEqual(1); - }); - - it('match using custom function using request body', async () => { - fm.mock((url, opts) => opts.body === 'a string', 200).catch(); - await fm.fetchHandler('http://a.com/logged-in'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('http://a.com/logged-in', { - method: 'post', - body: 'a string', - }); - expect(fm.calls(true).length).toEqual(1); - }); - - it('match using custom function with Request', async () => { - fm.mock( - (url, options) => - url.indexOf('logged-in') > -1 && options.headers.authorized, - 200, - ).catch(); - - await fm.fetchHandler( - new fm.config.Request('http://a.com/logged-in', { - headers: { authorized: 'true' }, - }), - ); - expect(fm.calls(true).length).toEqual(1); - }); - - it('match using custom function with Request with unusual options', async () => { - // as node-fetch does not try to emulate all the WHATWG standards, we can't check for the - // same properties in the browser and nodejs - const propertyToCheck = new fm.config.Request('http://example.com').cache - ? 'credentials' - : 'compress'; - const valueToSet = propertyToCheck === 'credentials' ? 'include' : false; - - fm.mock( - (url, options, request) => request[propertyToCheck] === valueToSet, - 200, - ).catch(); - - await fm.fetchHandler(new fm.config.Request('http://a.com/logged-in')); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler( - new fm.config.Request('http://a.com/logged-in', { - [propertyToCheck]: valueToSet, - }), - ); - expect(fm.calls(true).length).toEqual(1); - }); - - it('match using custom function alongside other matchers', async () => { - fm.mock('end:profile', 200, { - functionMatcher: (url, opts) => - opts && opts.headers && opts.headers.authorized === true, - }).catch(); - - await fm.fetchHandler('http://a.com/profile'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('http://a.com/not', { - headers: { authorized: true }, - }); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('http://a.com/profile', { - headers: { authorized: true }, - }); - expect(fm.calls(true).length).toEqual(1); - }); -}); diff --git a/packages/fetch-mock/test/specs/routing/header-matching.test.js b/packages/fetch-mock/test/specs/routing/header-matching.test.js deleted file mode 100644 index 89b6930c1..000000000 --- a/packages/fetch-mock/test/specs/routing/header-matching.test.js +++ /dev/null @@ -1,180 +0,0 @@ -import { afterEach, describe, expect, it, beforeAll } from 'vitest'; - -import fetchMock from '../../../src/index.js'; -describe('header matching', () => { - let fm; - beforeAll(() => { - fm = fetchMock.createInstance(); - fm.config.warnOnUnmatched = false; - }); - - afterEach(() => fm.restore()); - - it('not match when headers not present', async () => { - fm.mock( - { - headers: { a: 'b' }, - }, - 200, - ).catch(); - - await fm.fetchHandler('http://a.com/'); - expect(fm.calls(true).length).toEqual(0); - }); - - it("not match when headers don't match", async () => { - fm.mock( - { - headers: { a: 'b' }, - }, - 200, - ).catch(); - - await fm.fetchHandler('http://a.com/', { - headers: { a: 'c' }, - }); - expect(fm.calls(true).length).toEqual(0); - }); - - it('match simple headers', async () => { - fm.mock( - { - headers: { a: 'b' }, - }, - 200, - ).catch(); - - await fm.fetchHandler('http://a.com/', { - headers: { a: 'b' }, - }); - expect(fm.calls(true).length).toEqual(1); - }); - - it('be case insensitive', async () => { - fm.mock( - { - headers: { a: 'b' }, - }, - 200, - ).catch(); - - await fm.fetchHandler('http://a.com/', { - headers: { A: 'b' }, - }); - expect(fm.calls(true).length).toEqual(1); - }); - - it('match multivalue headers', async () => { - fm.mock( - { - headers: { a: ['b', 'c'] }, - }, - 200, - ).catch(); - - await fm.fetchHandler('http://a.com/', { - headers: { a: ['b', 'c'] }, - }); - expect(fm.calls(true).length).toEqual(1); - }); - - it('not match partially satisfied multivalue headers', async () => { - fm.mock( - { - headers: { a: ['b', 'c', 'd'] }, - }, - 200, - ).catch(); - - await fm.fetchHandler('http://a.com/', { - headers: { a: ['b', 'c'] }, - }); - expect(fm.calls(true).length).toEqual(0); - }); - - it('match multiple headers', async () => { - fm.mock( - { - headers: { a: 'b', c: 'd' }, - }, - 200, - ).catch(); - - await fm.fetchHandler('http://a.com/', { - headers: { a: 'b', c: 'd' }, - }); - expect(fm.calls(true).length).toEqual(1); - }); - - it('not match unsatisfied multiple headers', async () => { - fm.mock( - { - headers: { a: 'b', c: 'd' }, - }, - 200, - ).catch(); - - await fm.fetchHandler('http://a.com/', { - headers: { a: 'b' }, - }); - expect(fm.calls(true).length).toEqual(0); - }); - - it('match Headers instance', async () => { - fm.mock( - { - headers: { a: 'b' }, - }, - 200, - ).catch(); - - await fm.fetchHandler('http://a.com/', { - headers: new fm.config.Headers({ a: 'b' }), - }); - expect(fm.calls(true).length).toEqual(1); - }); - - it('match custom Headers instance', async () => { - const customHeaderInstance = fm.createInstance(); - customHeaderInstance.config.Headers = class { - constructor(obj) { - this.obj = obj; - } - - *[Symbol.iterator]() { - yield ['a', 'b']; - } - - has() { - return true; - } - }; - - customHeaderInstance - .mock( - { - headers: { a: 'b' }, - }, - 200, - ) - .catch(); - - await customHeaderInstance.fetchHandler('http://a.com/', { - headers: new customHeaderInstance.config.Headers({ a: 'b' }), - }); - expect(customHeaderInstance.calls(true).length).toEqual(1); - }); - - it('can be used alongside function matchers', async () => { - fm.mock((url) => /person/.test(url), 200, { - headers: { a: 'b' }, - }).catch(); - - await fm.fetchHandler('http://domain.com/person'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('http://domain.com/person', { - headers: { a: 'b' }, - }); - expect(fm.calls(true).length).toEqual(1); - }); -}); diff --git a/packages/fetch-mock/test/specs/routing/matcher-object.test.js b/packages/fetch-mock/test/specs/routing/matcher-object.test.js deleted file mode 100644 index 91be54f03..000000000 --- a/packages/fetch-mock/test/specs/routing/matcher-object.test.js +++ /dev/null @@ -1,138 +0,0 @@ -import { beforeEach, describe, expect, it } from 'vitest'; - -import fetchMock from '../../../src/index.js'; -describe('matcher object', () => { - let fm; - beforeEach(() => { - fm = fetchMock.createInstance(); - }); - - it('use matcher object with matcher property', async () => { - fm.mock({ matcher: 'http://a.com' }, 200).catch(); - await fm.fetchHandler('http://a.com'); - expect(fm.calls(true).length).toEqual(1); - }); - - it('use matcher object with url property', async () => { - fm.mock({ url: 'http://a.com' }, 200).catch(); - await fm.fetchHandler('http://a.com'); - expect(fm.calls(true).length).toEqual(1); - }); - - it('can use matcher and url simultaneously', async () => { - fm.mock( - { - url: 'end:path', - matcher: (url, opts) => - opts && opts.headers && opts.headers.authorized === true, - }, - 200, - ).catch(); - - await fm.fetchHandler('http://a.com/path'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('http://a.com', { - headers: { authorized: true }, - }); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('http://a.com/path', { - headers: { authorized: true }, - }); - expect(fm.calls(true).length).toEqual(1); - }); - - it('if no url provided, match any url', async () => { - fm.mock({}, 200).catch(); - - await fm.fetchHandler('http://a.com'); - expect(fm.calls(true).length).toEqual(1); - }); - - it.skip('deprecated message on using functionMatcher (prefer matcher)', () => { - fm.mock( - { - url: 'end:profile', - functionMatcher: (url, opts) => - opts && opts.headers && opts.headers.authorized === true, - }, - 200, - ).catch(); - }); - - it('can match Headers', async () => { - fm.mock({ url: 'http://a.com', headers: { a: 'b' } }, 200).catch(); - - await fm.fetchHandler('http://a.com', { - headers: { a: 'c' }, - }); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('http://a.com', { - headers: { a: 'b' }, - }); - expect(fm.calls(true).length).toEqual(1); - }); - - it('can match query string', async () => { - fm.mock({ url: 'http://a.com', query: { a: 'b' } }, 200).catch(); - - await fm.fetchHandler('http://a.com'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('http://a.com?a=b'); - expect(fm.calls(true).length).toEqual(1); - }); - - it('can match path parameter', async () => { - fm.mock({ url: 'express:/type/:var', params: { var: 'b' } }, 200).catch(); - await fm.fetchHandler('/'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('/type/a'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('/type/b'); - expect(fm.calls(true).length).toEqual(1); - }); - - it('can match method', async () => { - fm.mock({ method: 'POST' }, 200).catch(); - - await fm.fetchHandler('http://a.com', { method: 'GET' }); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('http://a.com', { method: 'POST' }); - expect(fm.calls(true).length).toEqual(1); - }); - - it('can match body', async () => { - fm.mock({ body: { foo: 'bar' } }, 200).catch(); - - await fm.fetchHandler('http://a.com', { - method: 'POST', - }); - expect(fm.calls(true).length).toEqual(0); - - await fm.fetchHandler('http://a.com', { - method: 'POST', - body: JSON.stringify({ foo: 'bar' }), - headers: { 'Content-Type': 'application/json' }, - }); - expect(fm.calls(true).length).toEqual(1); - }); - - it('support setting overwrite routes on matcher parameter', async () => { - expect(() => - fm - .mock('http://a.com', 200) - .mock({ url: 'http://a.com', overwriteRoutes: true }, 300), - ).not.toThrow(); - - const res = await fm.fetchHandler('http://a.com'); - expect(res.status).toEqual(300); - }); - - it('support setting matchPartialBody on matcher parameter', async () => { - fm.mock({ body: { a: 1 }, matchPartialBody: true }, 200).catch(404); - const res = await fm.fetchHandler('http://a.com', { - method: 'POST', - body: JSON.stringify({ a: 1, b: 2 }), - }); - expect(res.status).toEqual(200); - }); -}); diff --git a/packages/fetch-mock/test/specs/routing/method-matching.test.js b/packages/fetch-mock/test/specs/routing/method-matching.test.js deleted file mode 100644 index 0e49b8982..000000000 --- a/packages/fetch-mock/test/specs/routing/method-matching.test.js +++ /dev/null @@ -1,61 +0,0 @@ -import { afterEach, describe, expect, it, beforeAll } from 'vitest'; - -import fetchMock from '../../../src/index.js'; -describe('method matching', () => { - let fm; - beforeAll(() => { - fm = fetchMock.createInstance(); - fm.config.warnOnUnmatched = false; - }); - - afterEach(() => fm.restore()); - - it('match any method by default', async () => { - fm.mock('*', 200).catch(); - - await fm.fetchHandler('http://a.com/', { method: 'GET' }); - expect(fm.calls(true).length).toEqual(1); - await fm.fetchHandler('http://a.com/', { method: 'POST' }); - expect(fm.calls(true).length).toEqual(2); - }); - - it('configure an exact method to match', async () => { - fm.mock({ method: 'POST' }, 200).catch(); - - await fm.fetchHandler('http://a.com/', { method: 'GET' }); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('http://a.com/', { method: 'POST' }); - expect(fm.calls(true).length).toEqual(1); - }); - - it('match implicit GET', async () => { - fm.mock({ method: 'GET' }, 200).catch(); - - await fm.fetchHandler('http://a.com/'); - expect(fm.calls(true).length).toEqual(1); - }); - - it('be case insensitive', async () => { - fm.mock({ method: 'POST' }, 200).mock({ method: 'patch' }, 200).catch(); - - await fm.fetchHandler('http://a.com/', { method: 'post' }); - expect(fm.calls(true).length).toEqual(1); - await fm.fetchHandler('http://a.com/', { method: 'PATCH' }); - expect(fm.calls(true).length).toEqual(2); - }); - - it('can be used alongside function matchers', async () => { - fm.mock( - { - method: 'POST', - functionMatcher: (url) => /a\.com/.test(url), - }, - 200, - ).catch(); - - await fm.fetchHandler('http://a.com'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('http://a.com', { method: 'POST' }); - expect(fm.calls(true).length).toEqual(1); - }); -}); diff --git a/packages/fetch-mock/test/specs/routing/multiple-routes.test.js b/packages/fetch-mock/test/specs/routing/multiple-routes.test.js deleted file mode 100644 index 4c8f5230c..000000000 --- a/packages/fetch-mock/test/specs/routing/multiple-routes.test.js +++ /dev/null @@ -1,123 +0,0 @@ -import { afterEach, describe, expect, it, beforeAll } from 'vitest'; - -import fetchMock from '../../../src/index.js'; -describe('multiple routes', () => { - let fm; - beforeAll(() => { - fm = fetchMock.createInstance(); - fm.config.warnOnUnmatched = false; - }); - - afterEach(() => fm.restore()); - - it('match several routes with one instance', async () => { - fm.mock('http://b.com/', 200).mock('http://a.com/', 200); - - await fm.fetchHandler('http://b.com/'); - expect(fm.calls(true).length).toEqual(1); - await fm.fetchHandler('http://a.com/'); - expect(fm.calls(true).length).toEqual(2); - }); - - it('match first route that matches', async () => { - fm.mock('http://a.com/', 200).mock('begin:http://a.com/', 300); - - const res = await fm.fetchHandler('http://a.com/'); - expect(fm.calls(true).length).toEqual(1); - expect(res.status).toEqual(200); - }); - - describe('duplicate routes', () => { - it('error when duplicate route added using explicit route name', () => { - expect(() => - fm - .mock('http://a.com/', 200, { name: 'jam' }) - .mock('begin:http://a.com/', 300, { name: 'jam' }), - ).toThrow(); - }); - - it('error when duplicate route added using implicit route name', () => { - expect(() => - fm.mock('http://a.com/', 200).mock('http://a.com/', 300), - ).toThrow(); - }); - - it("don't error when duplicate route added with non-clashing method", () => { - expect(() => - fm - .mock('http://a.com/', 200, { method: 'GET' }) - .mock('http://a.com/', 300, { method: 'POST' }), - ).not.toThrow(); - }); - - it('error when duplicate route added with no method', () => { - expect(() => - fm - .mock('http://a.com/', 200, { method: 'GET' }) - .mock('http://a.com/', 300), - ).toThrow(); - }); - - it('error when duplicate route added with clashing method', () => { - expect(() => - fm - .mock('http://a.com/', 200, { method: 'GET' }) - .mock('http://a.com/', 300, { method: 'GET' }), - ).toThrow(); - }); - - it('allow overwriting existing route', async () => { - expect(() => - fm - .mock('http://a.com/', 200) - .mock('http://a.com/', 300, { overwriteRoutes: true }), - ).not.toThrow(); - - const res = await fm.fetchHandler('http://a.com/'); - expect(res.status).toEqual(300); - }); - - it('overwrite correct route', async () => { - expect(() => - fm - .mock('http://bar.co/', 200) - .mock('http://foo.co/', 400) - .mock('http://bar.co/', 300, { overwriteRoutes: true }), - ).not.toThrow(); - const res = await fm.fetchHandler('http://foo.co/'); - expect(res.status).toEqual(400); - }); - - it('allow adding additional route with same matcher', async () => { - expect(() => - fm - .mock('http://a.com/', 200, { repeat: 1 }) - .mock('http://a.com/', 300, { overwriteRoutes: false }), - ).not.toThrow(); - - const res = await fm.fetchHandler('http://a.com/'); - expect(res.status).toEqual(200); - const res2 = await fm.fetchHandler('http://a.com/'); - expect(res2.status).toEqual(300); - }); - - it("don't require overwrite route when only difference is method", () => { - fm.mock('http://a.com/', 200, { method: 'POST' }) - .mock('http://a.com/', 200, { method: 'GET' }) - .catch(); - }); - - it('overwrite multiple routes', async () => { - fm.mock('http://a.com/', 200, { method: 'POST' }) - .mock('http://a.com/', 200, { method: 'GET' }) - .mock('http://a.com/', 300, { overwriteRoutes: true }) - .catch(); - const res1 = await fm.fetchHandler('http://a.com/'); - expect(res1.status).toEqual(300); - const res2 = await fm.fetchHandler('http://a.com/', { - method: 'post', - }); - expect(res2.status).toEqual(300); - }); - }); -}); diff --git a/packages/fetch-mock/test/specs/routing/naming-routes.test.js b/packages/fetch-mock/test/specs/routing/naming-routes.test.js deleted file mode 100644 index 2368480d7..000000000 --- a/packages/fetch-mock/test/specs/routing/naming-routes.test.js +++ /dev/null @@ -1,36 +0,0 @@ -import { afterEach, describe, expect, it, beforeAll } from 'vitest'; - -import fetchMock from '../../../src/index.js'; -describe('multiple routes', () => { - let fm; - beforeAll(() => { - fm = fetchMock.createInstance(); - fm.config.warnOnUnmatched = false; - }); - - afterEach(() => fm.restore()); - - it('property on first parameter', () => { - fm.mock({ url: 'http://a.com', name: 'my-name' }, 200); - fm.fetchHandler('http://a.com'); - expect(fm.called('my-name')).toBe(true); - }); - - it('property on first parameter when only one parameter supplied', () => { - fm.mock({ name: 'my-name', url: 'http://a.com', response: 200 }); - fm.fetchHandler('http://a.com'); - expect(fm.called('my-name')).toBe(true); - }); - - it('property on third parameter', () => { - fm.mock('http://a.com', 200, { name: 'my-name' }); - fm.fetchHandler('http://a.com'); - expect(fm.called('my-name')).toBe(true); - }); - - it('string in third parameter', () => { - fm.mock('http://a.com', 200, 'my-name'); - fm.fetchHandler('http://a.com'); - expect(fm.called('my-name')).toBe(true); - }); -}); diff --git a/packages/fetch-mock/test/specs/routing/path-parameter-matching.test.js b/packages/fetch-mock/test/specs/routing/path-parameter-matching.test.js deleted file mode 100644 index a7c111ae8..000000000 --- a/packages/fetch-mock/test/specs/routing/path-parameter-matching.test.js +++ /dev/null @@ -1,52 +0,0 @@ -import { afterEach, describe, expect, it, beforeAll } from 'vitest'; - -import fetchMock from '../../../src/index.js'; -describe('path parameter matching', () => { - let fm; - beforeAll(() => { - fm = fetchMock.createInstance(); - fm.config.warnOnUnmatched = false; - }); - - afterEach(() => fm.restore()); - - it('can match a path parameters', async () => { - fm.mock('express:/type/:instance', 200, { - params: { instance: 'b' }, - }).catch(); - await fm.fetchHandler('/'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('/type/a'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('/type/b'); - expect(fm.calls(true).length).toEqual(1); - }); - - it('can match multiple path parameters', async () => { - fm.mock('express:/:type/:instance', 200, { - params: { instance: 'b', type: 'cat' }, - }).catch(); - await fm.fetchHandler('/'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('/dog/a'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('/cat/a'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('/dog/b'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('/cat/b'); - expect(fm.calls(true).length).toEqual(1); - }); - - it('can match a path parameter on a full url', async () => { - fm.mock('express:/type/:instance', 200, { - params: { instance: 'b' }, - }).catch(); - await fm.fetchHandler('http://site.com/'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('http://site.com/type/a'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('http://site.com/type/b'); - expect(fm.calls(true).length).toEqual(1); - }); -}); diff --git a/packages/fetch-mock/test/specs/routing/query-string-matching.test.js b/packages/fetch-mock/test/specs/routing/query-string-matching.test.js deleted file mode 100644 index f4e280995..000000000 --- a/packages/fetch-mock/test/specs/routing/query-string-matching.test.js +++ /dev/null @@ -1,309 +0,0 @@ -import { afterEach, describe, expect, it, beforeAll } from 'vitest'; - -import fetchMock from '../../../src/index.js'; -describe('query string matching', () => { - let fm; - beforeAll(() => { - fm = fetchMock.createInstance(); - fm.config.warnOnUnmatched = false; - }); - - afterEach(() => fm.restore()); - - it('match a query string', async () => { - fm.mock( - { - query: { a: 'b', c: 'd' }, - }, - 200, - ).catch(); - - await fm.fetchHandler('http://a.com'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('http://a.com?a=b&c=d'); - expect(fm.calls(true).length).toEqual(1); - }); - - it('match a query string against a URL object', async () => { - fm.mock( - { - query: { a: 'b', c: 'd' }, - }, - 200, - ).catch(); - const url = new URL('http://a.com/path'); - url.searchParams.append('a', 'b'); - url.searchParams.append('c', 'd'); - await fm.fetchHandler(url); - expect(fm.calls(true).length).toEqual(1); - }); - - it('match a query string against a relative path', async () => { - fm.mock( - { - query: { a: 'b' }, - }, - 200, - ).catch(); - const url = '/path?a=b'; - await fm.fetchHandler(url); - expect(fm.calls(true).length).toEqual(1); - }); - - it('match multiple query strings', async () => { - fm.mock( - { - query: { a: 'b', c: 'd' }, - }, - 200, - ).catch(); - - await fm.fetchHandler('http://a.com'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('http://a.com?a=b'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('http://a.com?a=b&c=d'); - expect(fm.calls(true).length).toEqual(1); - await fm.fetchHandler('http://a.com?c=d&a=b'); - expect(fm.calls(true).length).toEqual(2); - }); - - it('ignore irrelevant query strings', async () => { - fm.mock( - { - query: { a: 'b', c: 'd' }, - }, - 200, - ).catch(); - - await fm.fetchHandler('http://a.com?a=b&c=d&e=f'); - expect(fm.calls(true).length).toEqual(1); - }); - - it('match an empty query string', async () => { - fm.mock( - { - query: { a: '' }, - }, - 200, - ).catch(); - - await fm.fetchHandler('http://a.com'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('http://a.com?a='); - expect(fm.calls(true).length).toEqual(1); - }); - - it('distinguish between query strings that only partially differ', async () => { - expect(() => - fm.mock({ query: { a: 'b', c: 'e' } }, 200).mock( - { - overwriteRoutes: false, - query: { a: 'b', c: 'd' }, - }, - 300, - ), - ).not.toThrow(); - const res = await fm.fetchHandler('http://a.com?a=b&c=d'); - expect(res.status).toEqual(300); - }); - - describe('value coercion', () => { - it('coerce integers to strings and match', async () => { - fm.mock( - { - query: { - a: 1, - }, - }, - 200, - ).catch(); - await fm.fetchHandler('http://a.com'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('http://a.com?a=1'); - expect(fm.calls(true).length).toEqual(1); - }); - - it('coerce floats to strings and match', async () => { - fm.mock( - { - query: { - a: 1.2, - }, - }, - 200, - ).catch(); - await fm.fetchHandler('http://a.com'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('http://a.com?a=1.2'); - expect(fm.calls(true).length).toEqual(1); - }); - - it('coerce booleans to strings and match', async () => { - fm.mock( - { - query: { - a: true, - }, - }, - 200, - ) - .mock( - { - query: { - b: false, - }, - overwriteRoutes: false, - }, - 200, - ) - .catch(); - await fm.fetchHandler('http://a.com'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('http://a.com?a=true'); - expect(fm.calls(true).length).toEqual(1); - await fm.fetchHandler('http://a.com?b=false'); - expect(fm.calls(true).length).toEqual(2); - }); - - it('coerce undefined to an empty string and match', async () => { - fm.mock( - { - query: { - a: undefined, - }, - }, - 200, - ).catch(); - await fm.fetchHandler('http://a.com'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('http://a.com?a='); - expect(fm.calls(true).length).toEqual(1); - }); - - it('coerce null to an empty string and match', async () => { - fm.mock( - { - query: { - a: null, - }, - }, - 200, - ).catch(); - await fm.fetchHandler('http://a.com'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('http://a.com?a='); - expect(fm.calls(true).length).toEqual(1); - }); - - it('coerce an object to an empty string and match', async () => { - fm.mock( - { - query: { - a: { b: 'c' }, - }, - }, - 200, - ).catch(); - await fm.fetchHandler('http://a.com'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('http://a.com?a='); - expect(fm.calls(true).length).toEqual(1); - }); - - it('can match a query string with different value types', async () => { - const query = { - t: true, - f: false, - u: undefined, - num: 1, - arr: ['a', undefined], - }; - fm.mock('http://a.com/', 200, { - query, - }).catch(); - - await fm.fetchHandler('http://a.com'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('http://a.com?t=true&f=false&u=&num=1&arr=a&arr='); - expect(fm.calls(true).length).toEqual(1); - }); - }); - - describe('repeated query strings', () => { - it('match repeated query strings', async () => { - fm.mock({ url: 'http://a.com/', query: { a: ['b', 'c'] } }, 200).catch(); - - await fm.fetchHandler('http://a.com'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('http://a.com?a=b'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('http://a.com?a=b&a=c'); - expect(fm.calls(true).length).toEqual(1); - await fm.fetchHandler('http://a.com?a=b&a=c&a=d'); - expect(fm.calls(true).length).toEqual(1); - }); - - it('match repeated query strings in any order', async () => { - fm.mock({ url: 'http://a.com/', query: { a: ['b', 'c'] } }, 200).catch(); - - await fm.fetchHandler('http://a.com'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('http://a.com?a=b&a=c'); - expect(fm.calls(true).length).toEqual(1); - await fm.fetchHandler('http://a.com?a=c&a=b'); - expect(fm.calls(true).length).toEqual(2); - }); - - it('match a query string array of length 1', async () => { - fm.mock({ url: 'http://a.com/', query: { a: ['b'] } }, 200).catch(); - - await fm.fetchHandler('http://a.com'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('http://a.com?a=b'); - expect(fm.calls(true).length).toEqual(1); - await fm.fetchHandler('http://a.com?a=b&a=c'); - expect(fm.calls(true).length).toEqual(1); - }); - - it('match a repeated query string with an empty value', async () => { - fm.mock( - { url: 'http://a.com/', query: { a: ['b', undefined] } }, - 200, - ).catch(); - - await fm.fetchHandler('http://a.com'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('http://a.com?a=b'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('http://a.com?a=b&a='); - expect(fm.calls(true).length).toEqual(1); - }); - }); - - describe('interoperability', () => { - it('can be used alongside query strings expressed in the url', async () => { - fm.mock('http://a.com/?c=d', 200, { - query: { a: 'b' }, - }).catch(); - - await fm.fetchHandler('http://a.com?c=d'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('http://a.com?c=d&a=b'); - expect(fm.calls(true).length).toEqual(1); - await fm.fetchHandler('http://a.com?a=b&c=d'); - expect(fm.calls(true).length).toEqual(1); - }); - - it('can be used alongside function matchers', async () => { - fm.mock((url) => /a\.com/.test(url), 200, { - query: { a: 'b' }, - }).catch(); - - await fm.fetchHandler('http://a.com'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('http://a.com?a=b'); - expect(fm.calls(true).length).toEqual(1); - }); - }); -}); diff --git a/packages/fetch-mock/test/specs/routing/unmatched-calls.test.js b/packages/fetch-mock/test/specs/routing/unmatched-calls.test.js deleted file mode 100644 index c436a16e4..000000000 --- a/packages/fetch-mock/test/specs/routing/unmatched-calls.test.js +++ /dev/null @@ -1,42 +0,0 @@ -import { afterEach, describe, expect, it, beforeAll } from 'vitest'; - -import fetchMock from '../../../src/index.js'; -describe('unmatched calls', () => { - let fm; - beforeAll(() => { - fm = fetchMock.createInstance(); - fm.config.warnOnUnmatched = false; - }); - - afterEach(() => fm.restore()); - - it('throws if any calls unmatched', () => { - fm.mock(/a/, 200); - expect(() => fm.fetchHandler('http://1')).toThrow(); - }); - - it('catch unmatched calls with empty 200 by default', async () => { - fm.catch(); - - const res = await fm.fetchHandler('http://1'); - expect(fm.calls(false).length).toEqual(1); - expect(res.status).toEqual(200); - }); - - it('can catch unmatched calls with custom response', async () => { - fm.catch({ iam: 'json' }); - - const res = await fm.fetchHandler('http://1'); - expect(fm.calls(false).length).toEqual(1); - expect(res.status).toEqual(200); - expect(await res.json()).toEqual({ iam: 'json' }); - }); - - it('can catch unmatched calls with function', async () => { - fm.catch(() => new fm.config.Response('i am text', { status: 200 })); - const res = await fm.fetchHandler('http://1'); - expect(fm.calls(false).length).toEqual(1); - expect(res.status).toEqual(200); - expect(await res.text()).toEqual('i am text'); - }); -}); diff --git a/packages/fetch-mock/test/specs/routing/url-matching.test.js b/packages/fetch-mock/test/specs/routing/url-matching.test.js deleted file mode 100644 index b0c5e67a6..000000000 --- a/packages/fetch-mock/test/specs/routing/url-matching.test.js +++ /dev/null @@ -1,208 +0,0 @@ -import { afterEach, describe, expect, it, beforeAll } from 'vitest'; - -import fetchMock from '../../../src/index.js'; - -describe('url matching', () => { - let fm; - beforeAll(() => { - fm = fetchMock.createInstance(); - fm.config.warnOnUnmatched = false; - }); - - afterEach(() => fm.restore()); - - it('match exact strings', async () => { - fm.mock('http://a.com/path', 200).catch(); - await fm.fetchHandler('http://a.com/pat'); - await fm.fetchHandler('http://a.com/paths'); - await fm.fetchHandler('http://a.co/path'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('http://a.com/path'); - await fm.fetchHandler('//a.com/path'); - expect(fm.calls(true).length).toEqual(2); - }); - - it('match string objects', async () => { - fm.mock('http://a.com/path', 200).catch(); - await fm.fetchHandler(new String('http://a.com/path')); - expect(fm.calls(true).length).toEqual(1); - }); - - it('match exact strings with relative url', async () => { - fm.mock('/path', 200).catch(); - await fm.fetchHandler('/pat'); - await fm.fetchHandler('/paths'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('/path'); - expect(fm.calls(true).length).toEqual(1); - }); - - it('match exact string against URL object', async () => { - fm.mock('http://a.com/path', 200).catch(); - const url = new URL('http://a.com/path'); - await fm.fetchHandler(url); - expect(fm.calls(true).length).toEqual(1); - }); - - it('match using URL object as matcher', async () => { - const url = new URL('http://a.com/path'); - fm.mock(url, 200).catch(); - - await fm.fetchHandler('http://a.com/path'); - expect(fm.calls(true).length).toEqual(1); - }); - - it('match begin: keyword', async () => { - fm.mock('begin:http://a.com/path', 200).catch(); - - await fm.fetchHandler('http://b.com/path'); - await fm.fetchHandler('http://a.com/pat'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('http://a.com/path'); - await fm.fetchHandler('http://a.com/paths'); - expect(fm.calls(true).length).toEqual(2); - }); - - it('match end: keyword', async () => { - fm.mock('end:com/path', 200).catch(); - await fm.fetchHandler('http://a.com/paths'); - await fm.fetchHandler('http://a.com/pat'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('http://a.com/path'); - await fm.fetchHandler('http://b.com/path'); - expect(fm.calls(true).length).toEqual(2); - }); - - it('match glob: keyword', async () => { - fm.mock('glob:/its/*/*', 200).catch(); - await fm.fetchHandler('/its/alive'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('/its/a/boy'); - await fm.fetchHandler('/its/a/girl'); - expect(fm.calls(true).length).toEqual(2); - }); - - it('match express: keyword', async () => { - fm.mock('express:/its/:word', 200).catch(); - - await fm.fetchHandler('/its/a/boy'); - await fm.fetchHandler('/its/a/girl'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('/its/alive'); - expect(fm.calls(true).length).toEqual(1); - }); - - it('match path: keyword', async () => { - fm.mock('path:/its/:word', 200).catch(); - - await fm.fetchHandler('/its/boy'); - await fm.fetchHandler('/its/:word/still'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('/its/:word'); - await fm.fetchHandler('/its/:word?brain=false'); - expect(fm.calls(true).length).toEqual(2); - }); - - it('match wildcard string', async () => { - fm.mock('*', 200); - - await fm.fetchHandler('http://a.com'); - expect(fm.calls(true).length).toEqual(1); - }); - - it('match regular expressions', async () => { - const rx = /http:\/\/a\.com\/\d+/; - fm.mock(rx, 200).catch(); - - await fm.fetchHandler('http://a.com/'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('http://a.com/12345'); - expect(fm.calls(true).length).toEqual(1); - await fm.fetchHandler('http://a.com/abcde'); - expect(fm.calls(true).length).toEqual(1); - }); - - describe('host normalisation', () => { - it('match exact pathless urls regardless of trailing slash', async () => { - fm.mock('http://a.com/', 200).mock('http://b.com', 200).catch(); - - await fm.fetchHandler('http://a.com/'); - await fm.fetchHandler('http://a.com'); - expect(fm.calls(true).length).toEqual(2); - await fm.fetchHandler('http://b.com/'); - await fm.fetchHandler('http://b.com'); - expect(fm.calls(true).length).toEqual(4); - }); - it('match protocol-relative urls with catch-all', async () => { - fm.any(200).catch(); - - await fm.fetchHandler('//a.com/path'); - expect(fm.calls(true).length).toEqual(1); - }); - }); - - describe('data: URLs', () => { - it('match exact strings', async () => { - fm.mock('data:text/plain,path', 200).catch(); - await fm.fetchHandler('data:text/plain,pat'); - await fm.fetchHandler('data:text/plain,paths'); - await fm.fetchHandler('data:text/html,path'); - expect(fm.calls(true).length).to.equal(0); - await fm.fetchHandler('data:text/plain,path'); - expect(fm.calls(true).length).to.equal(1); - }); - it('match exact string against URL object', async () => { - fm.mock('data:text/plain,path', 200).catch(); - const url = new URL('data:text/plain,path'); - await fm.fetchHandler(url); - expect(fm.calls(true).length).to.equal(1); - }); - it('match using URL object as matcher', async () => { - const url = new URL('data:text/plain,path'); - fm.mock(url, 200).catch(); - await fm.fetchHandler('data:text/plain,path'); - expect(fm.calls(true).length).to.equal(1); - }); - it('match begin: keyword', async () => { - fm.mock('begin:data:text/plain', 200).catch(); - await fm.fetchHandler('http://a.com/path'); - await fm.fetchHandler('data:text/html,path'); - expect(fm.calls(true).length).to.equal(0); - await fm.fetchHandler('data:text/plain,path'); - await fm.fetchHandler('data:text/plain;base64,cGF0aA'); - expect(fm.calls(true).length).to.equal(2); - }); - it('match end: keyword', async () => { - fm.mock('end:sky', 200).catch(); - await fm.fetchHandler('data:text/plain,blue lake'); - await fm.fetchHandler('data:text/plain,blue sky research'); - expect(fm.calls(true).length).to.equal(0); - await fm.fetchHandler('data:text/plain,blue sky'); - await fm.fetchHandler('data:text/plain,grey sky'); - expect(fm.calls(true).length).to.equal(2); - }); - it('match glob: keyword', async () => { - fm.mock('glob:data:* sky', 200).catch(); - await fm.fetchHandler('data:text/plain,blue lake'); - expect(fm.calls(true).length).to.equal(0); - await fm.fetchHandler('data:text/plain,blue sky'); - await fm.fetchHandler('data:text/plain,grey sky'); - expect(fm.calls(true).length).to.equal(2); - }); - it('match wildcard string', async () => { - fm.mock('*', 200); - await fm.fetchHandler('data:text/plain,path'); - expect(fm.calls(true).length).to.equal(1); - }); - it('match regular expressions', async () => { - const rx = /data:text\/plain,\d+/; - fm.mock(rx, 200).catch(); - await fm.fetchHandler('data:text/html,12345'); - expect(fm.calls(true).length).to.equal(0); - await fm.fetchHandler('data:text/plain,12345'); - expect(fm.calls(true).length).to.equal(1); - await fm.fetchHandler('data:text/plain,path'); - expect(fm.calls(true).length).to.equal(1); - }); - }); -}); diff --git a/packages/fetch-mock/test/specs/sandbox.test.js b/packages/fetch-mock/test/specs/sandbox.test.js deleted file mode 100644 index 0796abae8..000000000 --- a/packages/fetch-mock/test/specs/sandbox.test.js +++ /dev/null @@ -1,140 +0,0 @@ -import { describe, expect, it, beforeAll, vi } from 'vitest'; - -import fetchMock from '../../src/index.js'; -describe('sandbox', () => { - let originalFetch; - - beforeAll(() => { - originalFetch = globalThis.fetch = vi.fn().mockResolvedValue('dummy'); - }); - - it('return function', () => { - const sbx = fetchMock.sandbox(); - expect(typeof sbx).toEqual('function'); - }); - - it('inherit settings from parent instance', () => { - const sbx = fetchMock.sandbox(); - expect(sbx.config).toEqual(fetchMock.config); - }); - - it('implement full fetch-mock api', () => { - const sbx = fetchMock.sandbox(); - - for (const key in fetchMock) { - expect(typeof sbx[key]).toEqual(typeof fetchMock[key]); - } - }); - - it('delegate to its own fetch handler', () => { - const sbx = fetchMock.sandbox().mock('http://a.com', 200); - - vi.spyOn(sbx, 'fetchHandler'); - - sbx('http://a.com'); - expect(sbx.fetchHandler).toHaveBeenCalledWith('http://a.com', undefined); - }); - - it("don't interfere with global fetch", () => { - const sbx = fetchMock.sandbox().mock('http://a.com', 200); - - expect(globalThis.fetch).toEqual(originalFetch); - expect(globalThis.fetch).not.toEqual(sbx); - }); - - it("don't interfere with global fetch-mock", async () => { - const sbx = fetchMock.sandbox().mock('http://a.com', 200).catch(302); - - fetchMock.mock('http://b.com', 200).catch(301); - - expect(globalThis.fetch).toEqual(fetchMock.fetchHandler); - expect(fetchMock.fetchHandler).not.toEqual(sbx); - expect(fetchMock.fallbackResponse).not.toEqual(sbx.fallbackResponse); - expect(fetchMock.routes).not.toEqual(sbx.routes); - - const [sandboxed, globally] = await Promise.all([ - sbx('http://a.com'), - fetch('http://b.com'), - ]); - - expect(sandboxed.status).toEqual(200); - expect(globally.status).toEqual(200); - expect(sbx.called('http://a.com')).toBe(true); - expect(sbx.called('http://b.com')).toBe(false); - expect(fetchMock.called('http://b.com')).toBe(true); - expect(fetchMock.called('http://a.com')).toBe(false); - expect(sbx.called('http://a.com')).toBe(true); - fetchMock.restore(); - }); - - it("don't interfere with other sandboxes", async () => { - const sbx = fetchMock.sandbox().mock('http://a.com', 200).catch(301); - - const sbx2 = fetchMock.sandbox().mock('http://b.com', 200).catch(302); - - expect(sbx2).not.toEqual(sbx); - expect(sbx2.fallbackResponse).not.toEqual(sbx.fallbackResponse); - expect(sbx2.routes).not.toEqual(sbx.routes); - - const [res1, res2] = await Promise.all([ - sbx('http://a.com'), - sbx2('http://b.com'), - ]); - expect(res1.status).toEqual(200); - expect(res2.status).toEqual(200); - expect(sbx.called('http://a.com')).toBe(true); - expect(sbx.called('http://b.com')).toBe(false); - expect(sbx2.called('http://b.com')).toBe(true); - expect(sbx2.called('http://a.com')).toBe(false); - }); - - it('can be restored', async () => { - const sbx = fetchMock.sandbox().get('https://a.com', 200); - - const res = await sbx('https://a.com'); - expect(res.status).toEqual(200); - - sbx.restore().get('https://a.com', 500); - - const res2 = await sbx('https://a.com'); - expect(res2.status).toEqual(500); - }); - - it("can 'fork' existing sandboxes or the global fetchMock", () => { - const sbx1 = fetchMock.sandbox().mock(/a/, 200).catch(300); - - const sbx2 = sbx1.sandbox().mock(/b/, 200).catch(400); - - expect(sbx1.routes.length).toEqual(1); - expect(sbx2.routes.length).toEqual(2); - expect(sbx1.fallbackResponse).toEqual(300); - expect(sbx2.fallbackResponse).toEqual(400); - sbx1.restore(); - expect(sbx1.routes.length).toEqual(0); - expect(sbx2.routes.length).toEqual(2); - }); - - it('error if spy() is called and no fetch defined in config', () => { - const fm = fetchMock.sandbox(); - delete fm.config.fetch; - expect(() => fm.spy()).toThrow(); - }); - - it("don't error if spy() is called and fetch defined in config", () => { - const fm = fetchMock.sandbox(); - fm.config.fetch = originalFetch; - expect(() => fm.spy()).not.toThrow(); - }); - - it('exports a properly mocked node-fetch module shape', () => { - // uses node-fetch default require pattern - const { default: fetch, Headers, Request, Response } = fetchMock.sandbox(); - - expect(fetch.name).toEqual('fetchMockProxy'); - expect(new Headers()).toBeInstanceOf(fetchMock.config.Headers); - expect(new Request('http://a.com')).toBeInstanceOf( - fetchMock.config.Request, - ); - expect(new Response()).toBeInstanceOf(fetchMock.config.Response); - }); -}); diff --git a/packages/fetch-mock/test/specs/set-up-and-tear-down.test.js b/packages/fetch-mock/test/specs/set-up-and-tear-down.test.js deleted file mode 100644 index 41e599d79..000000000 --- a/packages/fetch-mock/test/specs/set-up-and-tear-down.test.js +++ /dev/null @@ -1,202 +0,0 @@ -import { - afterEach, - beforeEach, - describe, - expect, - it, - beforeAll, - vi, -} from 'vitest'; - -import fetchMock from '../../src/index.js'; -describe('Set up and tear down', () => { - let fm; - beforeAll(() => { - fm = fetchMock.createInstance(); - fm.config.warnOnUnmatched = false; - }); - afterEach(() => fm.restore()); - - const testChainableMethod = (method, ...args) => { - it(`${method}() is chainable`, () => { - expect(fm[method](...args)).toEqual(fm); - }); - - it(`${method}() has "this"`, () => { - vi.spyOn(fm, method).mockReturnThis(); - expect(fm[method](...args)).toBe(fm); - fm[method].mockRestore(); - }); - }; - - describe('mock', () => { - testChainableMethod('mock', '*', 200); - - it('can be called multiple times', () => { - expect(() => { - fm.mock('http://a.com', 200).mock('http://b.com', 200); - }).not.toThrow(); - }); - - it('can be called after fetchMock is restored', () => { - expect(() => { - fm.mock('*', 200).restore().mock('*', 200); - }).not.toThrow(); - }); - - describe('parameters', () => { - beforeEach(() => { - vi.spyOn(fm, 'compileRoute'); - vi.spyOn(fm, '_mock').mockReturnValue(fm); - }); - - afterEach(() => { - fm.compileRoute.mockRestore(); - fm._mock.mockRestore(); - }); - - it('accepts single config object', () => { - const config = { - url: '*', - response: 200, - }; - expect(() => fm.mock(config)).not.toThrow(); - expect(fm.compileRoute).toHaveBeenCalledWith([config]); - expect(fm._mock).toHaveBeenCalled(); - }); - - it('accepts matcher, route pairs', () => { - expect(() => fm.mock('*', 200)).not.toThrow(); - expect(fm.compileRoute).toHaveBeenCalledWith(['*', 200]); - expect(fm._mock).toHaveBeenCalled(); - }); - - it('accepts matcher, response, config triples', () => { - expect(() => - fm.mock('*', 'ok', { - method: 'PUT', - some: 'prop', - }), - ).not.toThrow(); - expect(fm.compileRoute).toHaveBeenCalledWith([ - '*', - 'ok', - { - method: 'PUT', - some: 'prop', - }, - ]); - expect(fm._mock).toHaveBeenCalled(); - }); - - it('expects a matcher', () => { - expect(() => fm.mock(null, 'ok')).toThrow(); - }); - - it('expects a response', () => { - expect(() => fm.mock('*')).toThrow(); - }); - - it('can be called with no parameters', () => { - expect(() => fm.mock()).not.toThrow(); - expect(fm.compileRoute).not.toHaveBeenCalled(); - expect(fm._mock).toHaveBeenCalled(); - }); - - it('should accept object responses when also passing options', () => { - expect(() => - fm.mock('*', { foo: 'bar' }, { method: 'GET' }), - ).not.toThrow(); - }); - }); - }); - - describe('reset', () => { - testChainableMethod('reset'); - - it('can be called even if no mocks set', () => { - expect(() => fm.restore()).not.toThrow(); - }); - - it('calls resetHistory', () => { - vi.spyOn(fm, 'resetHistory'); - fm.restore(); - expect(fm.resetHistory).toHaveBeenCalledTimes(1); - fm.resetHistory.mockRestore(); - }); - - it('removes all routing', () => { - fm.mock('*', 200).catch(200); - - expect(fm.routes.length).toEqual(1); - expect(fm.fallbackResponse).toBeDefined(); - - fm.restore(); - - expect(fm.routes.length).toEqual(0); - expect(fm.fallbackResponse).toBeUndefined(); - }); - - it('restore is an alias for reset', () => { - expect(fm.restore).toEqual(fm.reset); - }); - }); - - describe('resetBehavior', () => { - testChainableMethod('resetBehavior'); - - it('can be called even if no mocks set', () => { - expect(() => fm.resetBehavior()).not.toThrow(); - }); - - it('removes all routing', () => { - fm.mock('*', 200).catch(200); - - expect(fm.routes.length).toEqual(1); - expect(fm.fallbackResponse).toBeDefined(); - - fm.resetBehavior(); - - expect(fm.routes.length).toEqual(0); - expect(fm.fallbackResponse).toBeUndefined(); - }); - }); - - describe('resetHistory', () => { - testChainableMethod('resetHistory'); - - it('can be called even if no mocks set', () => { - expect(() => fm.resetHistory()).not.toThrow(); - }); - - it('resets call history', async () => { - fm.mock('*', 200).catch(200); - await fm.fetchHandler('a'); - await fm.fetchHandler('b'); - expect(fm.called()).toBe(true); - - fm.resetHistory(); - expect(fm.called()).toBe(false); - expect(fm.called('*')).toBe(false); - expect(fm.calls('*').length).toEqual(0); - expect(fm.calls(true).length).toEqual(0); - expect(fm.calls(false).length).toEqual(0); - expect(fm.calls().length).toEqual(0); - }); - }); - - describe('spy', () => { - testChainableMethod('spy'); - - it('calls catch()', () => { - vi.spyOn(fm, 'catch'); - fm.spy(); - expect(fm.catch).toHaveBeenCalledTimes(1); - fm.catch.mockRestore(); - }); - }); - - describe('catch', () => { - testChainableMethod('catch'); - }); -}); diff --git a/packages/fetch-mock/test/specs/shorthands.test.js b/packages/fetch-mock/test/specs/shorthands.test.js deleted file mode 100644 index 8b1caba15..000000000 --- a/packages/fetch-mock/test/specs/shorthands.test.js +++ /dev/null @@ -1,148 +0,0 @@ -import { - afterEach, - describe, - expect, - it, - beforeAll, - afterAll, - vi, -} from 'vitest'; - -import fetchMock from '../../src/index.js'; -describe('shorthands', () => { - let fm; - let expectRoute; - - const testChainableMethod = (method) => { - const args = fetchMock[method].length === 3 ? ['*', 200] : [200]; - - it(`${method}() is chainable`, () => { - expect(fm[method](...args)).toEqual(fm); - }); - - it(`${method}() has "this"`, () => { - vi.spyOn(fm, method).mockReturnThis(); - fm[method](...args); - expect(fm[method](...args)).toEqual(fm); - fm[method].mockRestore(); - }); - }; - - beforeAll(() => { - fm = fetchMock.createInstance(); - vi.spyOn(fm, 'compileRoute'); - fm.config.warnOnUnmatched = false; - expectRoute = (...args) => - expect(fm.compileRoute).toHaveBeenCalledWith(args); - }); - afterEach(() => { - fm.compileRoute.mockClear(); - fm.restore({ sticky: true }); - }); - - afterAll(() => fm.compileRoute.mockRestore()); - - it('has sticky() shorthand method', () => { - fm.sticky('a', 'b'); - fm.sticky('c', 'd', { opt: 'e' }); - expectRoute('a', 'b', { - sticky: true, - }); - expectRoute('c', 'd', { - opt: 'e', - sticky: true, - }); - }); - - testChainableMethod('sticky'); - - it('has once() shorthand method', () => { - fm.once('a', 'b'); - fm.once('c', 'd', { opt: 'e' }); - expectRoute('a', 'b', { - repeat: 1, - }); - expectRoute('c', 'd', { - opt: 'e', - repeat: 1, - }); - }); - - testChainableMethod('once'); - - it('has any() shorthand method', () => { - fm.any('a', { opt: 'b' }); - expectRoute({}, 'a', { - opt: 'b', - }); - }); - - testChainableMethod('any'); - - it('has anyOnce() shorthand method', () => { - fm.anyOnce('a', { opt: 'b' }); - expectRoute({}, 'a', { - opt: 'b', - repeat: 1, - }); - }); - - testChainableMethod('anyOnce'); - - describe('method shorthands', () => { - ['get', 'post', 'put', 'delete', 'head', 'patch'].forEach((method) => { - describe(method.toUpperCase(), () => { - it(`has ${method}() shorthand`, () => { - fm[method]('a', 'b'); - fm[method]('c', 'd', { opt: 'e' }); - expectRoute('a', 'b', { - method, - }); - expectRoute('c', 'd', { - opt: 'e', - method, - }); - }); - - testChainableMethod(method); - - it(`has ${method}Once() shorthand`, () => { - fm[`${method}Once`]('a', 'b'); - fm[`${method}Once`]('c', 'd', { opt: 'e' }); - expectRoute('a', 'b', { - method, - repeat: 1, - }); - expectRoute('c', 'd', { - opt: 'e', - method, - repeat: 1, - }); - }); - - testChainableMethod(`${method}Once`); - - it(`has ${method}Any() shorthand`, () => { - fm[`${method}Any`]('a', { opt: 'b' }); - expectRoute({}, 'a', { - opt: 'b', - method, - }); - }); - - testChainableMethod(`${method}Any`); - - it(`has ${method}AnyOnce() shorthand`, () => { - fm[`${method}AnyOnce`]('a', { opt: 'b' }); - expectRoute({}, 'a', { - opt: 'b', - method, - repeat: 1, - }); - }); - - testChainableMethod(`${method}Any`); - }); - }); - }); -}); diff --git a/packages/fetch-mock/test/specs/spy.test.js b/packages/fetch-mock/test/specs/spy.test.js deleted file mode 100644 index 24e61fc13..000000000 --- a/packages/fetch-mock/test/specs/spy.test.js +++ /dev/null @@ -1,59 +0,0 @@ -import { describe, expect, it, vi } from 'vitest'; - -import fetchMock from '../../src/index.js'; -describe('spy()', () => { - it('when mocking globally, spy falls through to global fetch', async () => { - const originalFetch = globalThis.fetch; - const fetchSpy = vi.fn().mockResolvedValue('example'); - - globalThis.fetch = fetchSpy; - - fetchMock.spy(); - - await globalThis.fetch('http://a.com/', { method: 'get' }); - expect(fetchSpy).toHaveBeenCalledWith( - 'http://a.com/', - { method: 'get' }, - undefined, - ); - fetchMock.restore(); - globalThis.fetch = originalFetch; - }); - - it('when mocking locally, spy falls through to configured fetch', async () => { - const fetchSpy = vi.fn().mockResolvedValue('dummy'); - - const fm = fetchMock.sandbox(); - fm.config.fetch = fetchSpy; - - fm.spy(); - await fm.fetchHandler('http://a.com/', { method: 'get' }); - expect(fetchSpy).toHaveBeenCalledWith( - 'http://a.com/', - { method: 'get' }, - undefined, - ); - fm.restore(); - }); - - it('can restrict spying to a route', async () => { - const fetchSpy = vi.fn().mockResolvedValue('dummy'); - - const fm = fetchMock.sandbox(); - fm.config.fetch = fetchSpy; - - fm.spy({ url: 'http://a.com/', method: 'get' }); - await fm.fetchHandler('http://a.com/', { method: 'get' }); - expect(fetchSpy).toHaveBeenCalledWith( - 'http://a.com/', - { method: 'get' }, - undefined, - ); - - expect(() => fm.fetchHandler('http://b.com/', { method: 'get' })).toThrow(); - expect(() => - fm.fetchHandler('http://a.com/', { method: 'post' }), - ).toThrow(); - fm.restore(); - }); -}); diff --git a/packages/fetch-mock/test/specs/sticky-routes.test.js b/packages/fetch-mock/test/specs/sticky-routes.test.js deleted file mode 100644 index 9f9c62cf9..000000000 --- a/packages/fetch-mock/test/specs/sticky-routes.test.js +++ /dev/null @@ -1,133 +0,0 @@ -import { afterEach, describe, expect, it, beforeAll, vi } from 'vitest'; - -import fetchMock from '../../src/index.js'; - -describe('sticky routes', () => { - describe('effect on routes', () => { - let fm; - beforeAll(() => { - fm = fetchMock.createInstance(); - fm.config.warnOnUnmatched = false; - }); - - afterEach(() => fm.restore({ sticky: true })); - - describe('resetting behaviour', () => { - it('behaviour resists resetBehavior calls', () => { - fm.mock('*', 200, { sticky: true }).resetBehavior(); - expect(fm.routes.length).toEqual(1); - }); - - it('behaviour resists restore calls', () => { - fm.mock('*', 200, { sticky: true }).restore(); - expect(fm.routes.length).toEqual(1); - }); - - it('behaviour resists reset calls', () => { - fm.mock('*', 200, { sticky: true }).reset(); - expect(fm.routes.length).toEqual(1); - }); - - it('behaviour does not resist resetBehavior calls when sent `sticky: true`', () => { - fm.mock('*', 200, { sticky: true }).resetBehavior({ sticky: true }); - expect(fm.routes.length).toEqual(0); - }); - - it('behaviour does not resist restore calls when sent `sticky: true`', () => { - fm.mock('*', 200, { sticky: true }).restore({ sticky: true }); - expect(fm.routes.length).toEqual(0); - }); - - it('behaviour does not resist reset calls when sent `sticky: true`', () => { - fm.mock('*', 200, { sticky: true }).reset({ sticky: true }); - expect(fm.routes.length).toEqual(0); - }); - }); - - describe('resetting history', () => { - it('history does not resist resetHistory calls', () => { - fm.mock('*', 200, { sticky: true }); - fm.fetchHandler('http://a.com'); - fm.resetHistory(); - expect(fm.called()).toBe(false); - }); - - it('history does not resist restore calls', () => { - fm.mock('*', 200, { sticky: true }); - fm.fetchHandler('http://a.com'); - fm.restore(); - expect(fm.called()).toBe(false); - }); - - it('history does not resist reset calls', () => { - fm.mock('*', 200, { sticky: true }); - fm.fetchHandler('http://a.com'); - fm.reset(); - expect(fm.called()).toBe(false); - }); - }); - - describe('multiple routes', () => { - it('can have multiple sticky routes', () => { - fm.mock('*', 200, { sticky: true }) - .mock('http://a.com', 200, { sticky: true }) - .resetBehavior(); - expect(fm.routes.length).toEqual(2); - }); - - it('can have a sticky route before non-sticky routes', () => { - fm.mock('*', 200, { sticky: true }) - .mock('http://a.com', 200) - .resetBehavior(); - expect(fm.routes.length).toEqual(1); - expect(fm.routes[0].url).toEqual('*'); - }); - - it('can have a sticky route after non-sticky routes', () => { - fm.mock('*', 200) - .mock('http://a.com', 200, { sticky: true }) - .resetBehavior(); - expect(fm.routes.length).toEqual(1); - expect(fm.routes[0].url).toEqual('http://a.com'); - }); - }); - }); - describe('global mocking', () => { - let originalFetch; - beforeAll(() => { - originalFetch = globalThis.fetch = vi.fn().mockResolvedValue(); - }); - afterEach(() => fetchMock.restore({ sticky: true })); - - it('global mocking resists resetBehavior calls', () => { - fetchMock.mock('*', 200, { sticky: true }).resetBehavior(); - expect(globalThis.fetch).not.toEqual(originalFetch); - }); - - it('global mocking does not resist resetBehavior calls when sent `sticky: true`', () => { - fetchMock - .mock('*', 200, { sticky: true }) - .resetBehavior({ sticky: true }); - expect(globalThis.fetch).toEqual(originalFetch); - }); - }); - - describe('sandboxes', () => { - it('sandboxed instances should inherit stickiness', () => { - const sbx1 = fetchMock - .sandbox() - .mock('*', 200, { sticky: true }) - .catch(300); - - const sbx2 = sbx1.sandbox().resetBehavior(); - - expect(sbx1.routes.length).toEqual(1); - expect(sbx2.routes.length).toEqual(1); - - sbx2.resetBehavior({ sticky: true }); - - expect(sbx1.routes.length).toEqual(1); - expect(sbx2.routes.length).toEqual(0); - }); - }); -}); diff --git a/packages/fetch-mock/test/specs/user-defined-matchers.test.js b/packages/fetch-mock/test/specs/user-defined-matchers.test.js deleted file mode 100644 index a087d213f..000000000 --- a/packages/fetch-mock/test/specs/user-defined-matchers.test.js +++ /dev/null @@ -1,79 +0,0 @@ -import { describe, expect, it } from 'vitest'; - -import fetchMock from '../../src/index.js'; -describe('user defined matchers', () => { - it('match on sync property', async () => { - const fm = fetchMock.createInstance(); - fm.addMatcher({ - name: 'syncMatcher', - matcher: (route) => (url) => url.indexOf(route.syncMatcher) > -1, - }); - fm.mock( - { - syncMatcher: 'a', - }, - 200, - ).catch(); - await fm.fetchHandler('http://b.com'); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler('http://a.com'); - expect(fm.calls(true).length).toEqual(1); - }); - - it('match on async body property', async () => { - const fm = fetchMock.createInstance(); - fm.addMatcher({ - name: 'bodyMatcher', - matcher: (route) => (url, options) => - JSON.parse(options.body)[route.bodyMatcher] === true, - usesBody: true, - }); - fm.mock( - { - bodyMatcher: 'a', - }, - 200, - ).catch(); - await fm.fetchHandler( - new fm.config.Request('http://a.com', { - method: 'POST', - body: JSON.stringify({ b: true }), - }), - ); - expect(fm.calls(true).length).toEqual(0); - await fm.fetchHandler( - new fm.config.Request('http://a.com', { - method: 'POST', - body: JSON.stringify({ a: true }), - }), - ); - await fm.fetchHandler('http://a.com', { - method: 'POST', - body: JSON.stringify({ a: true }), - }); - expect(fm.calls(true).length).toEqual(2); - }); - - it('not match on async body property without passing `usesBody: true`', () => { - const fm = fetchMock.createInstance(); - fm.addMatcher({ - name: 'asyncBodyMatcher', - matcher: (route) => (url, options) => - JSON.parse(options.body)[route.asyncBodyMatcher] === true, - }); - fm.mock( - { - asyncBodyMatcher: 'a', - }, - 200, - ).catch(); - expect(() => - fm.fetchHandler( - new fm.config.Request('http://a.com', { - method: 'POST', - body: JSON.stringify({ a: true }), - }), - ), - ).toThrow(); - }); -}); diff --git a/packages/core/tsconfig.cjs.json b/packages/fetch-mock/tsconfig.cjs.json similarity index 100% rename from packages/core/tsconfig.cjs.json rename to packages/fetch-mock/tsconfig.cjs.json diff --git a/packages/core/tsconfig.esm.json b/packages/fetch-mock/tsconfig.esm.json similarity index 100% rename from packages/core/tsconfig.esm.json rename to packages/fetch-mock/tsconfig.esm.json diff --git a/packages/fetch-mock/types/index.d.ts b/packages/fetch-mock/types/index.d.ts deleted file mode 100644 index ad5057df1..000000000 --- a/packages/fetch-mock/types/index.d.ts +++ /dev/null @@ -1,766 +0,0 @@ -// Project: https://github.com/wheresrhys/fetch-mock, http://www.wheresrhys.co.uk/fetch-mock -// Definitions by: Alexey Svetliakov -// Tamir Duberstein -// Risto Keravuori -// Chris Sinclair -// Matt Tennison -// Quentin Bouygues -// Fumiaki Matsushima -// Colin Doig -// Felix Chen -// Katsuya Hino -// -// Please note that I - wheresrhys - don't use Typescript -// These types have been copied in here as a convenience for (some of) -// fetch-mock's users -// If you are a Typescript user and find a problem in these types, please -// submit a PR -// -// TypeScript Version: 2.2 - -declare namespace fetchMock { - type MockRequest = Request | RequestInit; - - /** - * Mock matcher function - */ - type MockMatcherFunction = (url: string, opts: MockRequest) => boolean; - - type MockMatcherUrl = string | RegExp | URL; - - /** - * Mock matcher. Can be one of following: - * string: Either - * * an exact url to match e.g. 'http://www.site.com/page.html' - * * if the string begins with a `^`, the string following the `^` must - * begin the url e.g. '^http://www.site.com' would match - * 'http://www.site.com' or 'http://www.site.com/page.html' - * * '*' to match any url - * RegExp: A regular expression to test the url against - * Function(url, opts): A function (returning a Boolean) that is passed the - * url and opts fetch() is called with (or, if fetch() was called with one, - * the Request instance) - */ - type MockMatcher = MockMatcherUrl | MockMatcherFunction; - - /** - * Inspection filter. Can be one of the following: - * boolean: - * * true retrieves all calls matched by fetch. - * fetchMock.MATCHED is an alias for true and may be used to make tests - * more readable. - * * false retrieves all calls not matched by fetch (i.e. those handled - * by catch() or spy(). fetchMock.UNMATCHED is an alias for false and - * may be used to make tests more readable. - * MockMatcher (routeIdentifier): - * All routes have an identifier: - * * If it’s a named route, the identifier is the route’s name - * * If the route is unnamed, the identifier is the matcher passed in to - * .mock() - * All calls that were handled by the route with the given identifier - * will be retrieved - * MockMatcher (matcher): - * Any matcher compatible with the mocking api can be passed in to filter - * the calls arbitrarily. - */ - type InspectionFilter = MockMatcher | boolean; - - /** - * Either an object compatible with the mocking api or a string specifying - * a http method to filter by. This will be used to filter the list of - * calls further. - */ - type InspectionOptions = MockOptions | string; - - /** - * Mock response object - */ - interface MockResponseObject { - /** - * Set the response body - */ - body?: string | object; - - /** - * Set the response status - * @default 200 - */ - status?: number; - - /** - * Set the response headers. - */ - headers?: { [key: string]: string }; - - /** - * If this property is present then a Promise rejected with the value - * of throws is returned - */ - throws?: Error; - - /** - * The URL the response should be from (to imitate followed redirects - * - will set redirected: true on the response) - */ - redirectUrl?: string; - } - - /** - * Response: A Response instance - will be used unaltered - * number: Creates a response with this status - * string: Creates a 200 response with the string as the response body - * object: As long as the object is not a MockResponseObject it is - * converted into a json string and returned as the body of a 200 response - * If MockResponseObject was given then it's used to configure response - * Function(url, opts): A function that is passed the url and opts fetch() - * is called with and that returns any of the responses listed above - */ - type MockResponse = - | Response - | Promise - | number - | Promise - | string - | Promise - | object - | Promise - | MockResponseObject - | Promise; - - /** - * Mock response function - */ - type MockResponseFunction = (url: string, opts: MockRequest) => MockResponse; - - /** - * Mock options object - */ - interface MockOptions { - /** - * A unique string naming the route. Used to subsequently retrieve - * references to the calls, grouped by name. - * @default matcher.toString() - * - * Note: If a non-unique name is provided no error will be thrown - * (because names are optional, auto-generated ones may legitimately - * clash) - */ - name?: string; - - /** - * http method to match - */ - method?: string; - - /** - * key/value map of headers to match - */ - headers?: { [key: string]: string | number }; - - /** - * key/value map of query strings to match, in any order - */ - query?: object; - - /** - * key/value map of express style path params to match - */ - params?: { [key: string]: string }; - - /** - * JSON serialisable object literal. Allowing any object for now - * But in typescript 3.7 will change to JSON - */ - body?: object; - - /** - * A function for arbitrary matching - */ - functionMatcher?: MockMatcherFunction; - - /** - * as specified above - */ - matcher?: MockMatcher; - - url?: MockMatcherUrl; - - /** - * This option allows for existing routes in a mock to be overwritten. - * It’s also possible to define multiple routes with ‘the same’ matcher. - * Default behaviour is to error - */ - overwriteRoutes?: boolean; - - /** - * as specified above - */ - response?: MockResponse | MockResponseFunction; - - /** - * integer, n, limiting the number of times the matcher can be used. - * If the route has already been called n times the route will be - * ignored and the call to fetch() will fall through to be handled by - * any other routes defined (which may eventually result in an error - * if nothing matches it). - */ - repeat?: number; - - /** - * integer, n, delays responding for the number of milliseconds - * specified. - */ - delay?: number; - - /** - * Convert objects into JSON before delivering as stub responses. Can - * be useful to set to false globally if e.g. dealing with a lot of - * array buffers. If true, will also add content-type: application/json - * header. - * @default true - */ - sendAsJson?: boolean; - - /** - * Automatically sets a content-length header on each response. - * @default true - */ - includeContentLength?: boolean; - - /** - * Match calls that only partially match a specified body json. - */ - matchPartialBody?: boolean; - - /** - * Avoids a route being removed when reset(), restore() or resetBehavior() are called. - * Note - this does not preserve the history of calls to the route - */ - sticky?: boolean; - } - - interface MockCall extends Array { - 0: string; - 1: RequestInit | undefined; - identifier: string; - isUnmatched: boolean | undefined; - request: Request | undefined; - response: Response | undefined; - } - - interface MockOptionsMethodGet extends MockOptions { - method?: 'GET'; - } - - interface MockOptionsMethodPost extends MockOptions { - method?: 'POST'; - } - - interface MockOptionsMethodPut extends MockOptions { - method?: 'PUT'; - } - - interface MockOptionsMethodDelete extends MockOptions { - method?: 'DELETE'; - } - - interface MockOptionsMethodPatch extends MockOptions { - method?: 'PATCH'; - } - - interface MockOptionsMethodHead extends MockOptions { - method?: 'HEAD'; - } - - interface FetchMockStatic { - MATCHED: true; - UNMATCHED: false; - - /** - * Replaces fetch() with a stub which records its calls, grouped by - * route, and optionally returns a mocked Response object or passes the - * call through to fetch(). Calls to .mock() can be chained. - * @param matcher Condition for selecting which requests to mock - * @param response Configures the http response returned by the mock - * @param [options] Additional properties defining the route to mock - */ - mock( - matcher: MockMatcher | MockOptions, - response: MockResponse | MockResponseFunction, - options?: MockOptions, - ): this; - - /** - * Replaces fetch() with a stub which records its calls, grouped by - * route, and optionally returns a mocked Response object or passes the - * call through to fetch(). Calls to .mock() can be chained. - * @param options The route to mock - */ - mock(options: MockOptions): this; - - /** - * Replaces fetch() with a stub which records its calls, grouped by - * route, and optionally returns a mocked Response object or passes the - * call through to fetch(). Calls to .mock() can be chained. - * @param options The route to mock - */ - mock(): this; - - /** - * Returns a drop-in mock for fetch which can be passed to other mocking - * libraries. It implements the full fetch-mock api and maintains its - * own state independent of other instances, so tests can be run in - * parallel. - */ - sandbox(): FetchMockSandbox; - - /** - * Replaces fetch() with a stub which records its calls, grouped by - * route, and optionally returns a mocked Response object or passes the - * call through to fetch(). Shorthand for mock() which creates a route - * that persists even when restore(), reset() or resetbehavior() are called. - * Calls to .sticky() can be chained. - * @param matcher Condition for selecting which requests to mock - * @param response Configures the http response returned by the mock - * @param [options] Additional properties defining the route to mock - */ - sticky( - matcher: MockMatcher | MockOptions, - response: MockResponse | MockResponseFunction, - options?: MockOptions, - ): this; - - /** - * Replaces fetch() with a stub which records its calls, grouped by - * route, and optionally returns a mocked Response object or passes the - * call through to fetch(). Shorthand for mock() limited to being - * called one time only. Calls to .once() can be chained. - * @param matcher Condition for selecting which requests to mock - * @param response Configures the http response returned by the mock - * @param [options] Optional additional properties defining the route to mock - */ - once( - matcher: MockMatcher | MockOptions, - response: MockResponse | MockResponseFunction, - options?: MockOptions, - ): this; - - /** - * Replaces fetch() with a stub which records its calls, grouped by - * route, and optionally returns a mocked Response object or passes the - * call through to fetch(). Shorthand for mock() restricted to the GET - * method. Calls to .get() can be chained. - * @param matcher Condition for selecting which requests to mock - * @param response Configures the http response returned by the mock - * @param [options] Additional properties defining the route to mock - */ - get( - matcher: MockMatcher | MockOptionsMethodGet, - response: MockResponse | MockResponseFunction, - options?: MockOptionsMethodGet, - ): this; - - /** - * Replaces fetch() with a stub which records its calls, grouped by - * route, and optionally returns a mocked Response object or passes the - * call through to fetch(). Shorthand for mock() restricted to the GET - * method and limited to being called one time only. Calls to .getOnce() - * can be chained. - * @param matcher Condition for selecting which requests to mock - * @param response Configures the http response returned by the mock - * @param [options] Additional properties defining the route to mock - */ - getOnce( - matcher: MockMatcher | MockOptionsMethodGet, - response: MockResponse | MockResponseFunction, - options?: MockOptionsMethodGet, - ): this; - - /** - * Replaces fetch() with a stub which records its calls, grouped by - * route, and optionally returns a mocked Response object or passes the - * call through to fetch(). Shorthand for mock() restricted to the POST - * method. Calls to .post() can be chained. - * @param matcher Condition for selecting which requests to mock - * @param response Configures the http response returned by the mock - * @param [options] Additional properties defining the route to mock - */ - post( - matcher: MockMatcher | MockOptionsMethodPost, - response: MockResponse | MockResponseFunction, - options?: MockOptionsMethodPost, - ): this; - - /** - * Replaces fetch() with a stub which records its calls, grouped by - * route, and optionally returns a mocked Response object or passes the - * call through to fetch(). Shorthand for mock() restricted to the POST - * method and limited to being called one time only. Calls to .postOnce() - * can be chained. - * @param matcher Condition for selecting which requests to mock - * @param response Configures the http response returned by the mock - * @param [options] Additional properties defining the route to mock - */ - postOnce( - matcher: MockMatcher | MockOptionsMethodPost, - response: MockResponse | MockResponseFunction, - options?: MockOptionsMethodPost, - ): this; - - /** - * Replaces fetch() with a stub which records its calls, grouped by - * route, and optionally returns a mocked Response object or passes the - * call through to fetch(). Shorthand for mock() restricted to the PUT - * method. Calls to .put() can be chained. - * @param matcher Condition for selecting which requests to mock - * @param response Configures the http response returned by the mock - * @param [options] Additional properties defining the route to mock - */ - put( - matcher: MockMatcher | MockOptionsMethodPut, - response: MockResponse | MockResponseFunction, - options?: MockOptionsMethodPut, - ): this; - - /** - * Replaces fetch() with a stub which records its calls, grouped by - * route, and optionally returns a mocked Response object or passes the - * call through to fetch(). Shorthand for mock() restricted to the PUT - * method and limited to being called one time only. Calls to .putOnce() - * can be chained. - * @param matcher Condition for selecting which requests to mock - * @param response Configures the http response returned by the mock - * @param [options] Additional properties defining the route to mock - */ - putOnce( - matcher: MockMatcher | MockOptionsMethodPut, - response: MockResponse | MockResponseFunction, - options?: MockOptionsMethodPut, - ): this; - - /** - * Replaces fetch() with a stub which records its calls, grouped by - * route, and optionally returns a mocked Response object or passes the - * call through to fetch(). Shorthand for mock() restricted to the - * DELETE method. Calls to .delete() can be chained. - * @param matcher Condition for selecting which requests to mock - * @param response Configures the http response returned by the mock - * @param [options] Additional properties defining the route to mock - */ - delete( - matcher: MockMatcher | MockOptionsMethodDelete, - response: MockResponse | MockResponseFunction, - options?: MockOptionsMethodDelete, - ): this; - - /** - * Replaces fetch() with a stub which records its calls, grouped by - * route, and optionally returns a mocked Response object or passes the - * call through to fetch(). Shorthand for mock() restricted to the - * DELETE method and limited to being called one time only. Calls to - * .deleteOnce() can be chained. - * @param matcher Condition for selecting which requests to mock - * @param response Configures the http response returned by the mock - * @param [options] Additional properties defining the route to mock - */ - deleteOnce( - matcher: MockMatcher | MockOptionsMethodDelete, - response: MockResponse | MockResponseFunction, - options?: MockOptionsMethodDelete, - ): this; - - /** - * Replaces fetch() with a stub which records its calls, grouped by - * route, and optionally returns a mocked Response object or passes the - * call through to fetch(). Shorthand for mock() restricted to the HEAD - * method. Calls to .head() can be chained. - * @param matcher Condition for selecting which requests to mock - * @param response Configures the http response returned by the mock - * @param [options] Additional properties defining the route to mock - */ - head( - matcher: MockMatcher | MockOptionsMethodHead, - response: MockResponse | MockResponseFunction, - options?: MockOptionsMethodHead, - ): this; - - /** - * Replaces fetch() with a stub which records its calls, grouped by - * route, and optionally returns a mocked Response object or passes the - * call through to fetch(). Shorthand for mock() restricted to the HEAD - * method and limited to being called one time only. Calls to .headOnce() - * can be chained. - * @param matcher Condition for selecting which requests to mock - * @param response Configures the http response returned by the mock - * @param [options] Additional properties defining the route to mock - */ - headOnce( - matcher: MockMatcher | MockOptionsMethodHead, - response: MockResponse | MockResponseFunction, - options?: MockOptionsMethodHead, - ): this; - - /** - * Replaces fetch() with a stub which records its calls, grouped by - * route, and optionally returns a mocked Response object or passes the - * call through to fetch(). Shorthand for mock() restricted to the PATCH - * method. Calls to .patch() can be chained. - * @param matcher Condition for selecting which requests to mock - * @param response Configures the http response returned by the mock - * @param [options] Additional properties defining the route to mock - */ - patch( - matcher: MockMatcher | MockOptionsMethodPatch, - response: MockResponse | MockResponseFunction, - options?: MockOptionsMethodPatch, - ): this; - - /** - * Replaces fetch() with a stub which records its calls, grouped by - * route, and optionally returns a mocked Response object or passes the - * call through to fetch(). Shorthand for mock() restricted to the PATCH - * method and limited to being called one time only. Calls to .patchOnce() - * can be chained. - * @param matcher Condition for selecting which requests to mock - * @param response Configures the http response returned by the mock - * @param [options] Additional properties defining the route to mock - */ - patchOnce( - matcher: MockMatcher | MockOptionsMethodPatch, - response: MockResponse | MockResponseFunction, - options?: MockOptionsMethodPatch, - ): this; - - /** - * Chainable method that defines how to respond to calls to fetch that - * don't match any of the defined mocks. It accepts the same types of - * response as a normal call to .mock(matcher, response). It can also - * take an arbitrary function to completely customise behaviour of - * unmatched calls. If .catch() is called without any parameters then - * every unmatched call will receive a 200 response. - * @param [response] Configures the http response returned by the mock - */ - catch(response?: MockResponse | MockResponseFunction): this; - - /** - * Chainable method that records the call history of unmatched calls, - * but instead of responding with a stubbed response, the request is - * passed through to native fetch() and is allowed to communicate - * over the network. Similar to catch(). - */ - spy(response?: MockResponse | MockResponseFunction): this; - - /** - * Restores fetch() to its unstubbed state and clears all data recorded - * for its calls. reset() is an alias for restore(). - */ - restore(): this; - - /** - * Restores fetch() to its unstubbed state and clears all data recorded - * for its calls. reset() is an alias for restore(). - */ - reset(): this; - - /** - * Clears all data recorded for fetch()’s calls. It will not restore - * fetch to its default implementation. - */ - resetHistory(): this; - - /** - * Removes mocking behaviour without resetting call history. - */ - resetBehavior(): this; - - /** - * Returns a promise that resolves once all fetches handled by fetch-mock - * have resolved. - * @param [waitForBody] Wait for all body parsing methods(res.json(), - * res.text(), etc.) to resolve too. - */ - flush(waitForBody?: boolean): Promise; - - /** - * Returns an array of all calls to fetch matching the given filters. - * Each call is returned as a [url, options] array. If fetch was called - * using a Request instance, this will be available as a request - * property on this array. - * @param [filter] Allows filtering of calls to fetch based on various - * criteria - * @param [options] Either an object compatible with the mocking api or - * a string specifying a http method to filter by. This will be used to - * filter the list of calls further. - */ - calls(filter?: InspectionFilter, options?: InspectionOptions): MockCall[]; - - /** - * Returns a Boolean indicating whether any calls to fetch matched the - * given filter. - * @param [filter] Allows filtering of calls to fetch based on various - * criteria - * @param [options] Either an object compatible with the mocking api or - * a string specifying a http method to filter by. This will be used to - * filter the list of calls further. - */ - called(filter?: InspectionFilter, options?: InspectionOptions): boolean; - - /** - * Returns a Boolean indicating whether fetch was called the expected - * number of times (or has been called at least once if repeat is - * undefined for the route). - * @param [filter] Rule for matching calls to fetch. - */ - done(filter?: InspectionFilter): boolean; - - /** - * Returns the arguments for the last call to fetch matching the given - * filter. - * @param [filter] Allows filtering of calls to fetch based on various - * criteria - * @param [options] Either an object compatible with the mocking api or - * a string specifying a http method to filter by. This will be used to - * filter the list of calls further. - */ - lastCall( - filter?: InspectionFilter, - options?: InspectionOptions, - ): MockCall | undefined; - - /** - * Returns the url for the last call to fetch matching the given - * filter. If fetch was last called using a Request instance, the url - * will be extracted from this. - * @param [filter] Allows filtering of calls to fetch based on various - * criteria - * @param [options] Either an object compatible with the mocking api or - * a string specifying a http method to filter by. This will be used to - * filter the list of calls further. - */ - lastUrl( - filter?: InspectionFilter, - options?: InspectionOptions, - ): string | undefined; - - /** - * Returns the options for the call to fetch matching the given filter. - * If fetch was last called using a Request instance, a set of options - * inferred from the Request will be returned. - * @param [filter] Allows filtering of calls to fetch based on various - * criteria - * @param [options] Either an object compatible with the mocking api or - * a string specifying a http method to filter by. This will be used to - * filter the list of calls further. - */ - lastOptions( - filter?: InspectionFilter, - options?: InspectionOptions, - ): RequestInit | undefined; - - /** - * Returns the options for the call to fetch matching the given filter. - * This is an experimental feature, very difficult to implement well given - * fetch’s very private treatment of response bodies. - * When doing all the following: - - using node-fetch - - responding with a real network response (using spy() or fallbackToNetwork) - - using `fetchMock.LastResponse()` - - awaiting the body content - … the response will hang unless your source code also awaits the response body. - This is an unavoidable consequence of the nodejs implementation of streams. - * @param [filter] Allows filtering of calls to fetch based on various - * criteria - * @param [options] Either an object compatible with the mocking api or - * a string specifying a http method to filter by. This will be used to - * filter the list of calls further. - */ - lastResponse( - filter?: InspectionFilter, - options?: InspectionOptions, - ): Response | undefined; - - statusTextMap: { - [key: number]: string; - }; - - config: { - /** - * Convert objects into JSON before delivering as stub responses. - * Can be useful to set to false globally if e.g. dealing with a - * lot of array buffers. If true, will also add - * content-type: application/json header. - * @default true - */ - sendAsJson?: boolean; - - /** - * Automatically sets a content-length header on each response. - * @default true - */ - includeContentLength?: boolean; - - /** - * - true: Unhandled calls fall through to the network - * - false: Unhandled calls throw an error - * - 'always': All calls fall through to the network, effectively - * disabling fetch-mock. - * @default false - */ - fallbackToNetwork?: boolean | 'always'; - - /** - * Determines behaviour if a new route has the same name (or - * inferred name) as an existing one - * - undefined: An error will be throw when routes clash - * - true: Overwrites the existing route - * - false: Appends the new route to the list of routes - * @default undefined - */ - overwriteRoutes?: boolean; - - /** - * Print a warning if any call is caught by a fallback handler (set - * using the fallbackToNetwork option or catch()) - * @default true - */ - warnOnFallback?: boolean; - - /** - * Reference to a custom fetch implementation. - */ - fetch?: ( - input?: string | Request, - init?: RequestInit, - ) => Promise; - - /** - * Reference to the Headers constructor of a custom fetch - * implementation. - */ - Headers?: new () => Headers; - - /** - * Reference to the Request constructor of a custom fetch - * implementation. - */ - Request?: new (input: string | Request, init?: RequestInit) => Request; - - /** - * Reference to the Response constructor of a custom fetch - * implementation. - */ - Response?: new () => Response; - }; - } - - interface FetchMockSandbox extends FetchMockStatic { - /** - * Also callable as fetch(). Use `typeof fetch` in your code to define - * a field that accepts both `fetch()` and a fetch-mock sandbox. - */ - (input?: string | Request, init?: RequestInit): Promise; - } -} - -declare const fetchMock: fetchMock.FetchMockStatic; -export default fetchMock; diff --git a/packages/fetch-mock/types/index.test-d.ts b/packages/fetch-mock/types/index.test-d.ts deleted file mode 100644 index d03e77908..000000000 --- a/packages/fetch-mock/types/index.test-d.ts +++ /dev/null @@ -1,206 +0,0 @@ -import fetchMock from '..'; -fetchMock.mock(); -fetchMock.mock("http://test.com", 200); -fetchMock.mock("http://test.com", 200, { - headers: { - test: "header" - } -}); -fetchMock.mock("http://test.com", 200, { - body: { - test: [{ - string: "value", - number: 1.34, - bool: true, - }] - } -}); -fetchMock.mock("http//test.com", 200, { - query: { - searchValue: "apples" - } -}); -fetchMock.mock("express:/users/:user", 200, { - params: { - user: "someone" - } -}); -fetchMock.mock("http://test.com", 200, { - functionMatcher: (url, opts) => { - return url.includes("test.com"); - } -}); -fetchMock.mock("http://test.com", 200, { - repeat: 2 -}); -fetchMock.mock("http://test.com", 200, { - delay: 10 -}); -fetchMock.mock(/test\.com/, 200); -fetchMock.mock(() => true, 200); -fetchMock.mock((url, opts) => true, 200); -fetchMock.once("http://test.com", 200); - -fetchMock.mock(/test/, "test").mock(/test/, { a: "b" }); -fetchMock.mock(/test/, { - status: 200, - headers: { - test: "test" - }, - body: { - a: "b" - } -}); - -fetchMock.mock({ - url: "http://test.com", - response: 200, - headers: {}, - query: {}, - params: {}, - body: {}, - repeat: 1, - delay: 500, - functionMatcher: () => true -}); - -fetchMock.mock({ - url: "http://test.com", -}, 200); - -fetchMock.restore().reset().resetHistory().resetBehavior(); - -let calls: fetchMock.MockCall[] = fetchMock.calls(/https?:\/\/test.com/, { - method: 'GET', -}); -calls[0][0].toUpperCase(); -calls[0].identifier.toUpperCase(); -calls[0].isUnmatched; -calls = fetchMock.calls(); -calls = fetchMock.calls(true); -calls = fetchMock.calls("http://test.com", "GET"); - -let doneStatus: boolean = fetchMock.done(); -doneStatus = fetchMock.done(true); -doneStatus = fetchMock.done("http://test.com"); -doneStatus = fetchMock.done(/https?:\/\/test.com/); - -let calledStatus: boolean = fetchMock.called(); -calledStatus = fetchMock.called(true); -calledStatus = fetchMock.called("http://test.com"); -calledStatus = fetchMock.called(/https?:\/\/test.com/); -calledStatus = fetchMock.called("http://test.com", "GET"); -calledStatus = fetchMock.called("http://test.com", { - method: "GET", -}); -calledStatus = fetchMock.called((url: string, opts: fetchMock.MockRequest): boolean => { - return true; -}); -calledStatus = fetchMock.called(fetchMock.MATCHED); -calledStatus = fetchMock.called(fetchMock.UNMATCHED); - -let lastCall: (fetchMock.MockCall |undefined) = fetchMock.lastCall(); -lastCall = fetchMock.lastCall(/https?:\/\/test.com/, { - method: "GET", -}); -lastCall = fetchMock.lastCall("https://test.com", "GET"); - -let lastUrl: (string| undefined) = fetchMock.lastUrl(); -lastUrl = fetchMock.lastUrl(true); -lastUrl = fetchMock.lastUrl("http://test.com"); -lastUrl = fetchMock.lastUrl(/https?:\/\/test.com/); -lastUrl = fetchMock.lastUrl("http://test.com", "GET"); -lastUrl = fetchMock.lastUrl("http://test.com", { - method: "GET", -}); -let lastOptions: (RequestInit|undefined) = fetchMock.lastOptions(); -lastOptions = fetchMock.lastOptions(true); -lastOptions = fetchMock.lastOptions("http://test.com"); -lastOptions = fetchMock.lastOptions(/https?:\/\/test.com/); -lastOptions = fetchMock.lastOptions("http://test.com", "GET"); -lastOptions = fetchMock.lastOptions("http://test.com", { - method: "GET", -}); - -let lastResponse: (Response|undefined) = fetchMock.lastResponse(); -lastResponse = fetchMock.lastResponse(true); -lastResponse = fetchMock.lastResponse("http://test.com"); -lastResponse = fetchMock.lastResponse(/https?:\/\/test.com/); -lastResponse = fetchMock.lastResponse("http://test.com", "GET"); -lastResponse = fetchMock.lastResponse("http://test.com", { - method: "GET", -}); - -fetchMock.get("http://test.com", 200); -fetchMock.getOnce("http://test.com", 200); -fetchMock.post("http://test.com", 200); -fetchMock.postOnce("http://test.com", 200); -fetchMock.put("http://test.com", 200); -fetchMock.putOnce("http://test.com", 200); -fetchMock.delete("http://test.com", 200); -fetchMock.deleteOnce("http://test.com", 200); -fetchMock.head("http://test.com", 200); -fetchMock.headOnce("http://test.com", 200); -fetchMock.patch("http://test.com", 200); -fetchMock.patchOnce("http://test.com", 200); - -fetchMock.get("http://test.com", 200, {method: "GET"}); -fetchMock.get("http://test.com", 200, {method: "GET", overwriteRoutes: true}); -fetchMock.get("http://test.com", 200, {overwriteRoutes: true}); -fetchMock.post("http://test.com", 200, {method: "POST"}); -fetchMock.put("http://test.com", 200, {method: "PUT"}); -fetchMock.delete("http://test.com", 200, {method: "DELETE"}); -fetchMock.head("http://test.com", 200, {method: "HEAD"}); - -fetchMock - .mock("http://test.com", 200) - .catch(503); - -fetchMock - .mock("http://test.com", 200) - .spy(); - -const myMatcher: fetchMock.MockMatcherFunction = ( - url: string, - opts: fetchMock.MockRequest -) => true; - -fetchMock.flush().then(resolved => resolved.forEach(console.log)); -fetchMock.flush().catch(r => r); -fetchMock.flush(true).catch(r => r); - -fetchMock.get("http://test.com", { - body: 'abc', - includeContentLength: false -}); - -fetchMock.get("http://test.com", { - body: 'abc', - redirectUrl: "http://example.org" -}); - -const sandbox = fetchMock.sandbox(); -sandbox.get("http://test.com", { - body: 'abc', - redirectUrl: "http://example.org" -}); - -const stickySandbox = fetchMock.sandbox(); -stickySandbox.sticky("http://test.com", 200); -stickySandbox.mock("http://test.com", 200, { sticky: true }); - -const response: fetchMock.MockResponseObject = { - throws: new Error('error'), -}; - -fetchMock.config.sendAsJson = true; -fetchMock.config.includeContentLength = true; -fetchMock.config.fallbackToNetwork = true; -fetchMock.config.fallbackToNetwork = 'always'; -fetchMock.config.overwriteRoutes = true; -fetchMock.config.overwriteRoutes = undefined; -fetchMock.config.warnOnFallback = true; -fetchMock.config.fetch = (): Promise => new Promise(() => { }); -fetchMock.config.Headers = Headers; -fetchMock.config.Request = Request; -fetchMock.config.Response = Response; diff --git a/packages/fetch-mock/types/tsconfig.json b/packages/fetch-mock/types/tsconfig.json deleted file mode 100644 index ace21a541..000000000 --- a/packages/fetch-mock/types/tsconfig.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "lib": ["es6", "dom"], - "noImplicitAny": true, - "noImplicitThis": true, - "strictNullChecks": false, - "strictFunctionTypes": true, - "baseUrl": "../", - "typeRoots": ["../"], - "types": [], - "noEmit": true, - "forceConsistentCasingInFileNames": true - }, - "files": ["index.d.ts", "fetch-mock-tests.ts"] -} diff --git a/packages/jest/package.json b/packages/jest/package.json index 9adcf38a2..3645eb294 100644 --- a/packages/jest/package.json +++ b/packages/jest/package.json @@ -21,7 +21,7 @@ "node": ">=18.11.0" }, "dependencies": { - "@fetch-mock/core": "^0.7.1" + "fetch-mock": "11.1.5" }, "peerDependencies": { "jest": "*", diff --git a/packages/jest/src/index.ts b/packages/jest/src/index.ts index 96a0912a9..1b95a531e 100644 --- a/packages/jest/src/index.ts +++ b/packages/jest/src/index.ts @@ -2,7 +2,7 @@ import { FetchMock, defaultFetchMockConfig, RemoveRouteOptions, -} from '@fetch-mock/core'; +} from 'fetch-mock'; import './jest-extensions'; import type { Jest } from '@jest/environment'; diff --git a/packages/vitest/package.json b/packages/vitest/package.json index e6f99b5b7..9d83df9a9 100644 --- a/packages/vitest/package.json +++ b/packages/vitest/package.json @@ -21,7 +21,7 @@ "node": ">=18.11.0" }, "dependencies": { - "@fetch-mock/core": "^0.7.1" + "fetch-mock": "11.1.5" }, "peerDependencies": { "vitest": "*" diff --git a/packages/vitest/src/index.ts b/packages/vitest/src/index.ts index c88661bee..9ef977f5c 100644 --- a/packages/vitest/src/index.ts +++ b/packages/vitest/src/index.ts @@ -3,7 +3,7 @@ import { FetchMock, defaultFetchMockConfig, RemoveRouteOptions, -} from '@fetch-mock/core'; +} from 'fetch-mock'; import './vitest-extensions'; type MockResetOptions = {