From 7417c1a39bfca992275d74da247a8ee1019a75b7 Mon Sep 17 00:00:00 2001 From: Rhys Evans Date: Tue, 23 Jul 2024 16:12:13 +0100 Subject: [PATCH 1/8] chore: typechecking passes in full --- .gitignore | 2 +- jsconfig.json | 21 +- mr | 0 package-lock.json | 95 ++---- package.json | 4 +- packages/core/package.json | 1 + packages/core/src/Matchers.js | 2 +- packages/core/src/RequestUtils.js | 2 +- packages/core/src/Route.js | 5 - packages/core/src/index.js | 2 + .../src/node_modules/is-subset/index.d.ts | 1 + packages/core/types/CallHistory.d.ts | 76 +++++ packages/core/types/FetchMock.d.ts | 39 +++ packages/core/types/Matchers.d.ts | 48 +-- packages/core/types/RequestUtils.d.ts | 62 +++- packages/core/types/Route.d.ts | 214 ++++++------- packages/core/types/Router.d.ts | 303 +++++------------- packages/core/types/StatusTextMap.d.ts | 7 + packages/core/types/index.d.ts | 2 + 19 files changed, 412 insertions(+), 474 deletions(-) delete mode 100644 mr create mode 100644 packages/core/src/node_modules/is-subset/index.d.ts create mode 100644 packages/core/types/CallHistory.d.ts create mode 100644 packages/core/types/FetchMock.d.ts create mode 100644 packages/core/types/StatusTextMap.d.ts create mode 100644 packages/core/types/index.d.ts diff --git a/.gitignore b/.gitignore index b4acbbf9..1bc2cf7a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -/node_modules/ +node_modules/ # tooling artefacts /docs/.sass-cache/ diff --git a/jsconfig.json b/jsconfig.json index 478664f7..3cea3a7e 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -5,6 +5,8 @@ "dom", "dom.iterable" ], + "moduleResolution": "nodenext", + "module": "NodeNext", "allowJs": true, "checkJs": true, "strict": true, @@ -12,15 +14,22 @@ "noImplicitThis": true, "strictNullChecks": false, "strictFunctionTypes": true, - "types": [ ], - "noEmit": true, + "types": [ + ], "downlevelIteration": true, - "forceConsistentCasingInFileNames": true + "forceConsistentCasingInFileNames": true, + "declaration": true, + "emitDeclarationOnly": true, + "outDir": "packages/core/types", + "skipLibCheck": true, + "noEmit": false, + "target": "es2021" }, "include": [ - "packages/core/src/*.js", + "./packages/core/src/*.js" ], "exclude": [ - "node_modules" + "node_modules", + "packages/**/src/__tests__" ] -} \ No newline at end of file +} diff --git a/mr b/mr deleted file mode 100644 index e69de29b..00000000 diff --git a/package-lock.json b/package-lock.json index 98a65ab7..d4a295e6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,8 @@ "@commitlint/config-conventional": "^19.2.2", "@rollup/plugin-commonjs": "^25.0.7", "@rollup/plugin-node-resolve": "^15.2.3", + "@types/events": "^3.0.3", + "@types/globrex": "^0.1.4", "@types/node": "^20.14.10", "@vitest/browser": "^1.1.0", "@vitest/coverage-istanbul": "^1.1.0", @@ -36,7 +38,7 @@ "prettier": "^3.1.1", "rollup": "^4.9.1", "ts-to-jsdoc": "^2.1.0", - "typescript": "^3.9.10", + "typescript": "^5.5.3", "v8": "^0.1.0", "vitest": "^1.1.0", "webdriverio": "^8.27.0" @@ -2385,20 +2387,6 @@ "typescript": ">=4" } }, - "node_modules/@commitlint/load/node_modules/typescript": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", - "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", - "dev": true, - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, "node_modules/@commitlint/message": { "version": "19.0.0", "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-19.0.0.tgz", @@ -5766,20 +5754,6 @@ } } }, - "node_modules/@svgr/core/node_modules/typescript": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", - "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", - "optional": true, - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, "node_modules/@svgr/hast-util-to-babel-ast": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", @@ -5862,20 +5836,6 @@ } } }, - "node_modules/@svgr/plugin-svgo/node_modules/typescript": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", - "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", - "optional": true, - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, "node_modules/@svgr/webpack": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-8.1.0.tgz", @@ -6090,6 +6050,12 @@ "@types/estree": "*" } }, + "node_modules/@types/events": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.3.tgz", + "integrity": "sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g==", + "dev": true + }, "node_modules/@types/express": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", @@ -6112,6 +6078,12 @@ "@types/send": "*" } }, + "node_modules/@types/globrex": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@types/globrex/-/globrex-0.1.4.tgz", + "integrity": "sha512-qm82zaOxfn8Us/GGjNrQQ1XfCBUDV86DxQgIQq/p1zGHlt0xnbUiabNjN9rZUhMNvvIE2gg8iTW+GMfw0TnnLg==", + "dev": true + }, "node_modules/@types/graceful-fs": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", @@ -21079,20 +21051,6 @@ } } }, - "node_modules/postcss-loader/node_modules/typescript": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", - "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", - "optional": true, - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, "node_modules/postcss-merge-idents": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-6.0.3.tgz", @@ -25092,15 +25050,15 @@ } }, "node_modules/typescript": { - "version": "3.9.10", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz", - "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==", + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/ufo": { @@ -26264,21 +26222,6 @@ "node": ">=8" } }, - "node_modules/webdriverio/node_modules/typescript": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", - "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", - "dev": true, - "optional": true, - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, "node_modules/webdriverio/node_modules/ws": { "version": "8.13.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", diff --git a/package.json b/package.json index 3f4c311e..63b08b2a 100644 --- a/package.json +++ b/package.json @@ -59,9 +59,11 @@ "prettier": "^3.1.1", "rollup": "^4.9.1", "ts-to-jsdoc": "^2.1.0", - "typescript": "^3.9.10", + "typescript": "^5.5.3", "v8": "^0.1.0", "vitest": "^1.1.0", + "@types/events": "^3.0.3", + "@types/globrex": "^0.1.4", "webdriverio": "^8.27.0" }, "volta": { diff --git a/packages/core/package.json b/packages/core/package.json index b8475ec3..9ca20950 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -4,6 +4,7 @@ "exports": "src/index.js", "version": "0.3.0", "main": "index.js", + "types": "types/index.d.ts", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, diff --git a/packages/core/src/Matchers.js b/packages/core/src/Matchers.js index 75ee1933..5ae4c0da 100644 --- a/packages/core/src/Matchers.js +++ b/packages/core/src/Matchers.js @@ -56,7 +56,7 @@ const stringMatchers = { url.substr(-targetString.length) === targetString, glob: (targetString) => { - const urlRX = glob(targetString); + const urlRX = /** @type {{regex: RegExp}} */ (glob(targetString)); return ({ url }) => urlRX.regex.test(url); }, express: (targetString) => { diff --git a/packages/core/src/RequestUtils.js b/packages/core/src/RequestUtils.js index 78068ed0..55c6e933 100644 --- a/packages/core/src/RequestUtils.js +++ b/packages/core/src/RequestUtils.js @@ -124,7 +124,7 @@ export function getQuery(url) { /** * - * @param {Headers | [string, string][] | Record < string, string > | Object.} headers + * @param {Headers | [string, string][] | Record < string, string > | Object. | HeadersInit} headers * @returns {Object.} */ export const normalizeHeaders = (headers) => { diff --git a/packages/core/src/Route.js b/packages/core/src/Route.js index d2fb3bc3..19e8bc14 100644 --- a/packages/core/src/Route.js +++ b/packages/core/src/Route.js @@ -111,7 +111,6 @@ class Route { /** * @returns {void} */ - // @ts-ignore #validate() { if (['matched', 'unmatched'].includes(this.config.name)) { throw new Error( @@ -130,7 +129,6 @@ class Route { /** * @returns {void} */ - // @ts-ignore #sanitize() { if (this.config.method) { this.config.method = this.config.method.toLowerCase(); @@ -139,7 +137,6 @@ class Route { /** * @returns {void} */ - // @ts-ignore #generateMatcher() { const activeMatchers = Route.registeredMatchers .filter(({ name }) => name in this.config) @@ -155,7 +152,6 @@ class Route { /** * @returns {void} */ - // @ts-ignore #limit() { if (!this.config.repeat) { return; @@ -176,7 +172,6 @@ class Route { /** * @returns {void} */ - // @ts-ignore #delayResponse() { if (this.config.delay) { const { response } = this.config; diff --git a/packages/core/src/index.js b/packages/core/src/index.js index e69de29b..75a25900 100644 --- a/packages/core/src/index.js +++ b/packages/core/src/index.js @@ -0,0 +1,2 @@ +import fetchMock from './FetchMock.js'; +export default fetchMock; diff --git a/packages/core/src/node_modules/is-subset/index.d.ts b/packages/core/src/node_modules/is-subset/index.d.ts new file mode 100644 index 00000000..1ad5b509 --- /dev/null +++ b/packages/core/src/node_modules/is-subset/index.d.ts @@ -0,0 +1 @@ +export default function (Object, Object): boolean \ No newline at end of file diff --git a/packages/core/types/CallHistory.d.ts b/packages/core/types/CallHistory.d.ts new file mode 100644 index 00000000..bd6be600 --- /dev/null +++ b/packages/core/types/CallHistory.d.ts @@ -0,0 +1,76 @@ +export default CallHistory; +export type RouteConfig = import("./Route").RouteConfig; +export type RouteName = import("./Route").RouteName; +export type NormalizedRequestOptions = import("./RequestUtils").NormalizedRequestOptions; +export type RouteMatcher = import("./Matchers").RouteMatcher; +export type FetchMockConfig = import("./FetchMock").FetchMockConfig; +export type CallLog = { + arguments: any[]; + url: string; + options: NormalizedRequestOptions; + request?: Request; + signal?: AbortSignal; + route?: Route; + response?: Response; + expressParams?: { + [x: string]: string; + }; + queryParams?: { + [x: string]: string; + }; + pendingPromises: Promise[]; +}; +export type Matched = "matched"; +export type Unmatched = "unmatched"; +export type CallHistoryFilter = RouteName | Matched | Unmatched | boolean | RouteMatcher; +declare class CallHistory { + /** + * @param {FetchMockConfig} globalConfig + * @param {Router} router + */ + constructor(globalConfig: FetchMockConfig, router: Router); + /** @type {CallLog[]} */ + callLogs: CallLog[]; + config: import("./FetchMock").FetchMockConfig; + router: Router; + /** + * + * @param {CallLog} callLog + */ + recordCall(callLog: CallLog): void; + clear(): void; + /** + * + * @param {boolean} [waitForResponseMethods] + * @returns {Promise} + */ + flush(waitForResponseMethods?: boolean): Promise; + /** + * + * @param {CallHistoryFilter} filter + * @param {RouteConfig} options + * @returns {CallLog[]} + */ + calls(filter: CallHistoryFilter, options: RouteConfig): CallLog[]; + /** + * + * @param {CallHistoryFilter} filter + * @param {RouteConfig} options + * @returns {boolean} + */ + called(filter: CallHistoryFilter, options: RouteConfig): boolean; + /** + * + * @param {CallHistoryFilter} filter + * @param {RouteConfig} options + * @returns {CallLog} + */ + lastCall(filter: CallHistoryFilter, options: RouteConfig): CallLog; + /** + * @param {RouteName|RouteName[]} [routeNames] + * @returns {boolean} + */ + done(routeNames?: RouteName | RouteName[]): boolean; +} +import Route from './Route.js'; +import Router from './Router.js'; diff --git a/packages/core/types/FetchMock.d.ts b/packages/core/types/FetchMock.d.ts new file mode 100644 index 00000000..d2b66887 --- /dev/null +++ b/packages/core/types/FetchMock.d.ts @@ -0,0 +1,39 @@ +declare const _default: any; +export default _default; +export type RouteMatcher = import("./Router").RouteMatcher; +export type RouteName = import("./Route").RouteName; +export type UserRouteConfig = import("./Route").UserRouteConfig; +export type RouteResponse = import("./Router").RouteResponse; +export type MatcherDefinition = import("./Matchers").MatcherDefinition; +export type CallLog = import("./CallHistory").CallLog; +export type RouteResponseFunction = import("./Route").RouteResponseFunction; +export type FetchMockConfig = { + sendAsJson?: boolean; + includeContentLength?: boolean; + warnOnFallback?: boolean; + matchPartialBody?: boolean; + fetch?: (arg0: string | Request, arg1: RequestInit) => Promise; + Headers?: typeof Headers; + Request?: typeof Request; + Response?: typeof Response; +}; +export type FetchMockCore = { + config: FetchMockConfig; + router: Router; + callHistory: CallHistory; + createInstance: () => FetchMock; + fetchHandler: (arg0: string | Request, arg1: RequestInit) => Promise; + route: (arg0: any, arg1: any, arg2: any) => FetchMock; + catch: (arg0: RouteResponse | undefined) => FetchMock; + defineMatcher: (arg0: MatcherDefinition) => void; + removeRoutes: (arg0: object) => void; + clearHistory: () => void; +}; +/** + * } + */ +export type PresetRouteMethodName = "get" | "post" | "put" | "delete" | "head" | "patch" | "once" | "sticky" | "any" | "anyOnce" | "getOnce" | "postOnce" | "putOnce" | "deleteOnce" | "headOnce" | "patchOnce" | "getAny" | "postAny" | "putAny" | "deleteAny" | "headAny" | "patchAny" | "getAnyOnce" | "postAnyOnce" | "putAnyOnce" | "deleteAnyOnce" | "headAnyOnce" | "patchAnyOnce"; +export type PresetRoutes = any; +export type FetchMock = FetchMockCore & PresetRoutes; +import Router from './Router.js'; +import CallHistory from './CallHistory.js'; diff --git a/packages/core/types/Matchers.d.ts b/packages/core/types/Matchers.d.ts index acacfbac..3cff1ed2 100644 --- a/packages/core/types/Matchers.d.ts +++ b/packages/core/types/Matchers.d.ts @@ -1,38 +1,16 @@ - - -/** - * Mock matcher function - */ -type RouteMatcherUrl = 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 RouteMatcher = RouteMatcherUrl | RouteMatcherFunction; - -type UrlMatcher = (url: string) => boolean; - -type UrlMatcherGenerator = (pattern: string) => UrlMatcher; - -type RouteMatcherFunction = (url: string, opts: NormalizedRequestOptions, request: Request) => boolean; - -type MatcherGenerator = (route: RouteOptions) => RouteMatcherFunction; - -type MatcherDefinition = { +export function isUrlMatcher(matcher: RouteMatcher | RouteConfig): matcher is RouteMatcherUrl; +export function isFunctionMatcher(matcher: RouteMatcher | RouteConfig): matcher is RouteMatcherFunction; +/** @type {MatcherDefinition[]} */ +export const builtInMatchers: MatcherDefinition[]; +export type RouteConfig = import("./Route").RouteConfig; +export type CallLog = import("./CallHistory").CallLog; +export type RouteMatcherUrl = string | RegExp | URL; +export type UrlMatcherGenerator = (arg0: string) => RouteMatcherFunction; +export type RouteMatcherFunction = (arg0: CallLog) => boolean; +export type MatcherGenerator = (arg0: RouteConfig) => RouteMatcherFunction; +export type RouteMatcher = RouteMatcherUrl | RouteMatcherFunction; +export type MatcherDefinition = { name: string; matcher: MatcherGenerator; usesBody?: boolean; -} \ No newline at end of file +}; diff --git a/packages/core/types/RequestUtils.d.ts b/packages/core/types/RequestUtils.d.ts index fdd66853..5991aa1a 100644 --- a/packages/core/types/RequestUtils.d.ts +++ b/packages/core/types/RequestUtils.d.ts @@ -1,12 +1,52 @@ -interface DerivedRequestOptions { +/** + * @typedef DerivedRequestOptions + * @property {string} method + * @property {string} [body] + * @property {{ [key: string]: string }} [headers] + */ +/** @typedef {RequestInit | (RequestInit & DerivedRequestOptions) } NormalizedRequestOptions */ +/** @typedef {import('./CallHistory').CallLog} CallLog */ +/** + * @param {string | string | URL} url + * @returns {string} + */ +export function normalizeUrl(url: string | string | URL): string; +/** + * + * @param {string | object} url + * @param {RequestInit} options + * @returns {CallLog} + */ +export function createCallLogFromUrlAndOptions(url: string | object, options: RequestInit): CallLog; +/** + * + * @param {Request} request + * @param {RequestInit} options + * @returns {Promise} + */ +export function createCallLogFromRequest(request: Request, options: RequestInit): Promise; +/** + * @param {string} url + * @returns {string} + */ +export function getPath(url: string): string; +/** + * @param {string} url + * @returns {string} + */ +export function getQuery(url: string): string; +export function isRequest(urlOrRequest: string | Request, Request: typeof globalThis.Request): urlOrRequest is Request; +export function normalizeHeaders(headers: Headers | [string, string][] | Record | { + [x: string]: string | number; +} | HeadersInit): { + [x: string]: string; +}; +export type DerivedRequestOptions = { method: string; - body?: Promise; - headers?: { [key: string]: string | [string] } -} -type NormalizedRequestOptions = RequestInit | (RequestInit & DerivedRequestOptions) -interface NormalizedRequest { - url: string; - options: NormalizedRequestOptions; - request?: Request; - signal?: AbortSignal; -} \ No newline at end of file + body?: string; + headers?: { + [key: string]: string; + }; +}; +export type NormalizedRequestOptions = RequestInit | (RequestInit & DerivedRequestOptions); +export type CallLog = import("./CallHistory").CallLog; diff --git a/packages/core/types/Route.d.ts b/packages/core/types/Route.d.ts index ab351b1a..db375733 100644 --- a/packages/core/types/Route.d.ts +++ b/packages/core/types/Route.d.ts @@ -1,145 +1,117 @@ -declare class Route { - /** - * @param {MatcherDefinition} matcher - */ - static defineMatcher(matcher: any): void; - /** - * @overload - * @param {RouteOptions} matcher - * @param {undefined} response - * @param {undefined} options - * @param {FetchMockConfig} globalConfig - */ - /** - * @overload - * @param {RouteMatcher } matcher - * @param {RouteResponse} response - * @param {RouteOptions | string} options - * @param {FetchMockConfig} globalConfig - */ - /** - * @param {RouteMatcher | RouteOptions} matcher - * @param {RouteResponse} [response] - * @param {RouteOptions | string} [options] - * @param {FetchMockConfig} [globalConfig] - */ - constructor(matcher: any | any, response?: any, options?: any | string, globalConfig?: any); - originalInput: { - matcher: any; - response: any; - options: any; - }; - routeOptions: RouteOptions; - reset: () => void; - response: () => Promise; - matcher: RouteMatcherFunction; - #private; -} -declare namespace Route { - export const registeredMatchers: any[]; -} - - - - +export default Route; +export type RouteMatcher = import("./Matchers").RouteMatcher; +export type CallLog = import("./CallHistory").CallLog; +export type RouteMatcherFunction = import("./Matchers").RouteMatcherFunction; +export type RouteMatcherUrl = import("./Matchers").RouteMatcherUrl; +export type MatcherDefinition = import("./Matchers").MatcherDefinition; +export type FetchMockConfig = import("./FetchMock").FetchMockConfig; /** - * Mock options object + * { */ -interface RouteOptions { - /** - * A unique string naming the route. Used to subsequently retrieve - * references to the calls, grouped by name. - */ - name?: string; - - /** - * http method to match - */ +export type RouteResponseConfig = { + body?: string | {}; + status?: number; + headers?: { + [key: string]: string; + }; + throws?: Error; + redirectUrl?: string; + options?: ResponseInit; +}; +export type ResponseInitUsingHeaders = { + status: number; + statusText: string; + headers: Headers; +}; +export type RouteResponseObjectData = RouteResponseConfig | object; +export type RouteResponseData = Response | number | string | RouteResponseObjectData; +export type RouteResponsePromise = Promise; +export type RouteResponseFunction = (arg0: CallLog) => (RouteResponseData | RouteResponsePromise); +export type RouteResponse = RouteResponseData | RouteResponsePromise | RouteResponseFunction; +export type RouteName = string; +export type UserRouteConfig = { + name?: RouteName; 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?: { [key: string]: string }; - - /** - * 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 - */ + headers?: { + [key: string]: string | number; + }; + query?: { + [key: string]: string; + }; + params?: { + [key: string]: string; + }; body?: object; - + matcherFunction?: RouteMatcherFunction; + matcher?: RouteMatcher; + url?: RouteMatcherUrl; + response?: RouteResponse | RouteResponseFunction; + repeat?: number; + delay?: number; /** - * A function for arbitrary matching + * - TODO this is global */ - functionMatcher?: RouteMatcherFunction; - + sendAsJson?: boolean; /** - * as specified above + * - TODO this is global */ - matcher?: RouteMatcher; - - url?: RouteMatcherUrl; - + includeContentLength?: boolean; /** - * 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 + * - TODO this is global */ - overwriteRoutes?: boolean; - + matchPartialBody?: boolean; + sticky?: boolean; /** - * as specified above + * - TODO this shoudl not be in user config */ - response?: RouteResponse | RouteResponseFunction; - + usesBody?: boolean; + isFallback?: boolean; +}; +export type RouteConfig = UserRouteConfig & FetchMockConfig; +/** + * @class Route + */ +declare class Route { /** - * 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). + * @param {MatcherDefinition} matcher */ - repeat?: number; - + static defineMatcher(matcher: MatcherDefinition): void; + /** @type {MatcherDefinition[]} */ + static registeredMatchers: MatcherDefinition[]; /** - * integer, n, delays responding for the number of milliseconds - * specified. + * @param {RouteConfig} config */ - delay?: number; - + constructor(config: RouteConfig); + /** @type {RouteConfig} */ + config: RouteConfig; + /** @type {RouteMatcherFunction=} */ + matcher: RouteMatcherFunction | undefined; /** - * 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 + * @returns {void} */ - sendAsJson?: boolean; - + reset(): void; /** - * Automatically sets a content-length header on each response. - * @default true + * + * @param {RouteResponseConfig} responseInput + * @returns {{response: Response, responseOptions: ResponseInit, responseInput: RouteResponseConfig}} */ - includeContentLength?: boolean; - + constructResponse(responseInput: RouteResponseConfig): { + response: Response; + responseOptions: ResponseInit; + responseInput: RouteResponseConfig; + }; /** - * Match calls that only partially match a specified body json. + * + * @param {RouteResponseConfig} responseInput + * @returns {ResponseInitUsingHeaders} */ - matchPartialBody?: boolean; - + constructResponseOptions(responseInput: RouteResponseConfig): ResponseInitUsingHeaders; /** - * Avoids a route being removed when reset(), restore() or resetBehavior() are called. - * Note - this does not preserve the history of calls to the route + * + * @param {RouteResponseConfig} responseInput + * @param {ResponseInitUsingHeaders} responseOptions + * @returns {string|null} */ - sticky?: boolean; -} \ No newline at end of file + constructResponseBody(responseInput: RouteResponseConfig, responseOptions: ResponseInitUsingHeaders): string | null; + #private; +} diff --git a/packages/core/types/Router.d.ts b/packages/core/types/Router.d.ts index 0ffb915d..cf4facdc 100644 --- a/packages/core/types/Router.d.ts +++ b/packages/core/types/Router.d.ts @@ -1,219 +1,90 @@ - - -interface RouteOptionsMethodGet extends RouteOptions { - method?: 'GET'; -} - -interface RouteOptionsMethodPost extends RouteOptions { - method?: 'POST'; -} - -interface RouteOptionsMethodPut extends RouteOptions { - method?: 'PUT'; -} - -interface RouteOptionsMethodDelete extends RouteOptions { - method?: 'DELETE'; -} - -interface RouteOptionsMethodPatch extends RouteOptions { - method?: 'PATCH'; -} - -interface RouteOptionsMethodHead extends RouteOptions { - method?: 'HEAD'; -} - -interface Router { - -/** - * 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 - */ -route(matcher: RouteMatcher | RouteOptions, response: MockResponse | MockResponseFunction, options ?: RouteOptions): 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 - */ -route(options: RouteOptions): 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 forroute() 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: RouteMatcher | RouteOptions, response: MockResponse | MockResponseFunction, options ?: RouteOptions): 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 forroute() 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: RouteMatcher | RouteOptions, response: MockResponse | MockResponseFunction, options ?: RouteOptions): 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 forroute() 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: RouteMatcher | RouteOptionsMethodGet, response: MockResponse | MockResponseFunction, options ?: RouteOptionsMethodGet): 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 forroute() 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: RouteMatcher | RouteOptionsMethodGet, response: MockResponse | MockResponseFunction, options ?: RouteOptionsMethodGet): 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 forroute() 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: RouteMatcher | RouteOptionsMethodPost, response: MockResponse | MockResponseFunction, options ?: RouteOptionsMethodPost): 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 forroute() 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: RouteMatcher | RouteOptionsMethodPost, response: MockResponse | MockResponseFunction, options ?: RouteOptionsMethodPost): 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 forroute() 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: RouteMatcher | RouteOptionsMethodPut, response: MockResponse | MockResponseFunction, options ?: RouteOptionsMethodPut): 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 forroute() 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: RouteMatcher | RouteOptionsMethodPut, response: MockResponse | MockResponseFunction, options ?: RouteOptionsMethodPut): 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 forroute() 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: RouteMatcher | RouteOptionsMethodDelete, response: MockResponse | MockResponseFunction, options ?: RouteOptionsMethodDelete): 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 forroute() 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: RouteMatcher | RouteOptionsMethodDelete, response: MockResponse | MockResponseFunction, options ?: RouteOptionsMethodDelete): 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 forroute() 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: RouteMatcher | RouteOptionsMethodHead, response: MockResponse | MockResponseFunction, options ?: RouteOptionsMethodHead): 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 forroute() 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: RouteMatcher | RouteOptionsMethodHead, response: MockResponse | MockResponseFunction, options ?: RouteOptionsMethodHead): 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 forroute() 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: RouteMatcher | RouteOptionsMethodPatch, response: MockResponse | MockResponseFunction, options ?: RouteOptionsMethodPatch): 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 forroute() 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: RouteMatcher | RouteOptionsMethodPatch, response: MockResponse | MockResponseFunction, options ?: RouteOptionsMethodPatch): this; - +export default class Router { + /** + * @param {FetchMockConfig} fetchMockConfig + * @param {object} [inheritedRoutes] + * @param {Route[]} [inheritedRoutes.routes] + * @param {Route} [inheritedRoutes.fallbackRoute] + */ + constructor(fetchMockConfig: FetchMockConfig, { routes, fallbackRoute }?: { + routes?: Route[]; + fallbackRoute?: Route; + }); + /** @type {Route[]} */ + routes: Route[]; + config: import("./FetchMock").FetchMockConfig; + fallbackRoute: Route; + /** + * + * @param {Request} request + * @returns {boolean} + */ + needsToReadBody(request: Request): boolean; + /** + * @param {CallLog} callLog + * @returns {Promise} + */ + execute(callLog: CallLog): Promise; + /** + * + * @param {CallLog} callLog + * @returns {Promise<{response: Response, responseOptions: ResponseInit, responseInput: RouteResponseConfig}>} + */ + generateResponse(callLog: CallLog): Promise<{ + response: Response; + responseOptions: ResponseInit; + responseInput: RouteResponseConfig; + }>; + /** + * + * @param {Response} response + * @param {ResponseInit} responseConfig + * @param {RouteResponseConfig} responseInput + * @param {string} responseUrl + * @param {Promise[]} pendingPromises + * @returns {Response} + */ + createObservableResponse(response: Response, responseConfig: ResponseInit, responseInput: RouteResponseConfig, responseUrl: string, pendingPromises: Promise[]): Response; + /** + * @overload + * @param {UserRouteConfig} matcher + * @returns {void} + */ + addRoute(matcher: UserRouteConfig): void; + /** + * @overload + * @param {RouteMatcher } matcher + * @param {RouteResponse} response + * @param {UserRouteConfig | string} [nameOrOptions] + * @returns {void} + */ + addRoute(matcher: RouteMatcher, response: RouteResponse, nameOrOptions?: UserRouteConfig | string): void; + /** + * @param {RouteResponse} [response] + */ + setFallback(response?: RouteResponse): void; /** - * 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 + * + * @param {object} [options] + * @param {string[]} [options.names] + * @param {boolean} [options.includeSticky] + * @param {boolean} [options.includeFallback] */ - catch (response?: MockResponse | MockResponseFunction): this; - + removeRoutes({ names, includeSticky, includeFallback }?: { + names?: string[]; + includeSticky?: boolean; + includeFallback?: boolean; + }): void; } +export type UserRouteConfig = import("./Route").UserRouteConfig; +export type RouteConfig = import("./Route").RouteConfig; +export type RouteResponse = import("./Route").RouteResponse; +export type RouteResponseData = import("./Route").RouteResponseData; +export type RouteResponseObjectData = import("./Route").RouteResponseObjectData; +export type RouteResponseConfig = import("./Route").RouteResponseConfig; +export type RouteResponseFunction = import("./Route").RouteResponseFunction; +export type RouteMatcher = import("./Matchers").RouteMatcher; +export type FetchMockConfig = import("./FetchMock").FetchMockConfig; +export type FetchMock = typeof import("./FetchMock"); +export type CallLog = import("./CallHistory").CallLog; +export type ResponseConfigProp = "body" | "headers" | "throws" | "status" | "redirectUrl"; +import Route from './Route.js'; diff --git a/packages/core/types/StatusTextMap.d.ts b/packages/core/types/StatusTextMap.d.ts new file mode 100644 index 00000000..b98f5db6 --- /dev/null +++ b/packages/core/types/StatusTextMap.d.ts @@ -0,0 +1,7 @@ +export default statusTextMap; +/** + * @type {Object.} + */ +declare const statusTextMap: { + [x: number]: string; +}; diff --git a/packages/core/types/index.d.ts b/packages/core/types/index.d.ts new file mode 100644 index 00000000..1ae74276 --- /dev/null +++ b/packages/core/types/index.d.ts @@ -0,0 +1,2 @@ +export default fetchMock; +import fetchMock from './FetchMock.js'; From 10ec8aff56e3869b9e541819c13d3fa7d9014c08 Mon Sep 17 00:00:00 2001 From: Rhys Evans Date: Tue, 23 Jul 2024 22:05:07 +0100 Subject: [PATCH 2/8] refactor: use a class for fetchmockcore --- packages/core/src/FetchMock.js | 93 ++++++++++++++++++++-------------- 1 file changed, 54 insertions(+), 39 deletions(-) diff --git a/packages/core/src/FetchMock.js b/packages/core/src/FetchMock.js index fcd2bd4b..3795a46d 100644 --- a/packages/core/src/FetchMock.js +++ b/packages/core/src/FetchMock.js @@ -34,38 +34,31 @@ const defaultConfig = { Headers: globalThis.Headers, fetch: globalThis.fetch, }; -/** - * @typedef FetchMockCore - * @this {FetchMock} - * @property {FetchMockConfig} config - * @property {Router} router - * @property {CallHistory} callHistory - * @property {function():FetchMock} createInstance - * @property {function(string | Request, RequestInit): Promise} fetchHandler - * @property {function(any,any,any): FetchMock} route - * @property {function(RouteResponse=): FetchMock} catch - * @property {function(MatcherDefinition):void} defineMatcher - * @property {function(object): void} removeRoutes - * @property {function():void} clearHistory - */ -const defaultRouter = new Router(defaultConfig); +/** @typedef {FetchMockCore & PresetRoutes} FetchMock*/ -/** @type {FetchMockCore} */ -const FetchMock = { - config: defaultConfig, - router: defaultRouter, - callHistory: new CallHistory(defaultConfig, defaultRouter), - createInstance() { - const instance = Object.create(FetchMock); - instance.config = { ...this.config }; - instance.router = new Router(instance.config, { - routes: [...this.router.routes], - fallbackRoute: this.router.fallbackRoute, +class FetchMockCore { + /** + * + * @param {FetchMockConfig} config + * @param {Router} [router] + */ + constructor(config, router) { + this.config = config; + this.router = new Router(this.config, { + routes: router ? [...router.routes] : [], + fallbackRoute: router ? router.fallbackRoute : null, }); - instance.callHistory = new CallHistory(instance.config, instance.router); - return instance; - }, + this.callHistory = new CallHistory(this.config, this.router); + } + /** + * + * @returns {FetchMock} + */ + createInstance() { + const instance = new FetchMockCore({ ...this.config }, this.router); + return Object.assign(instance, PresetRoutes); + } /** * * @param {string | Request} requestInput @@ -92,7 +85,7 @@ const FetchMock = { const responsePromise = this.router.execute(callLog); callLog.pendingPromises.push(responsePromise); return responsePromise; - }, + } /** * @overload * @param {UserRouteConfig} matcher @@ -119,23 +112,45 @@ const FetchMock = { route(matcher, response, options) { this.router.addRoute(matcher, response, options); return this; - }, + } + /** + * + * @param {RouteResponse} [response] + * @returns {FetchMock} + */ catch(response) { this.router.setFallback(response); return this; - }, + } + /** + * + * @param {MatcherDefinition} matcher + */ + //eslint-disable-next-line class-methods-use-this defineMatcher(matcher) { Route.defineMatcher(matcher); - }, + } + /** + * + * @param {object} [options] + * @param {string[]} [options.names] + * @param {boolean} [options.includeSticky] + * @param {boolean} [options.includeFallback] + * @returns {FetchMock} + */ removeRoutes(options) { this.router.removeRoutes(options); return this; - }, + } + /** + * + * @returns {FetchMock} + */ clearHistory() { this.callHistory.clear(); return this; - }, -}; + } +} /** @typedef {'get' |'post' |'put' |'delete' |'head' |'patch' |'once' |'sticky' |'any' |'anyOnce' |'getOnce' |'postOnce' |'putOnce' |'deleteOnce' |'headOnce' |'patchOnce' |'getAny' |'postAny' |'putAny' |'deleteAny' |'headAny' |'patchAny' |'getAnyOnce' |'postAnyOnce' |'putAnyOnce' |'deleteAnyOnce' |'headAnyOnce' |'patchAnyOnce'} PresetRouteMethodName} */ /** @typedef {Object.} PresetRoutes */ @@ -221,7 +236,7 @@ defineGreedyShorthand('anyOnce', 'once'); ); }); -/** @typedef {FetchMockCore & PresetRoutes} FetchMock*/ -Object.assign(FetchMock, PresetRoutes); +const fetchMock = new FetchMockCore({ ...defaultConfig }).createInstance(); -export default FetchMock.createInstance(); +console.log(fetchMock); +export default fetchMock; From f42d240f8ef5c6a270ee8b355ad5177d8fdadf0b Mon Sep 17 00:00:00 2001 From: Rhys Evans Date: Tue, 23 Jul 2024 22:42:33 +0100 Subject: [PATCH 3/8] refactor!: defined route shorthand methods more declaratively have also removed {method}Any and {method}AnyOnce - not all that useful --- jsconfig.json | 3 +- packages/core/src/CallHistory.js | 3 + packages/core/src/FetchMock.js | 175 +++++++++----------- packages/core/src/RequestUtils.js | 1 - packages/core/src/Router.js | 14 -- packages/core/types/CallHistory.d.ts | 36 ---- packages/core/types/FetchHandler.d.ts | 0 packages/core/types/FetchMock.d.ts | 93 +++++++++-- packages/core/types/InstanceManagement.d.ts | 18 -- packages/core/types/Matchers.d.ts | 1 - packages/core/types/RequestUtils.d.ts | 32 ---- packages/core/types/Route.d.ts | 46 ----- packages/core/types/Router.d.ts | 55 +----- packages/core/types/StatusTextMap.d.ts | 3 - 14 files changed, 162 insertions(+), 318 deletions(-) delete mode 100644 packages/core/types/FetchHandler.d.ts delete mode 100644 packages/core/types/InstanceManagement.d.ts diff --git a/jsconfig.json b/jsconfig.json index 3cea3a7e..3c975b3c 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -23,7 +23,8 @@ "outDir": "packages/core/types", "skipLibCheck": true, "noEmit": false, - "target": "es2021" + "target": "es2021", + "removeComments": true }, "include": [ "./packages/core/src/*.js" diff --git a/packages/core/src/CallHistory.js b/packages/core/src/CallHistory.js index 1b519b22..97169b86 100644 --- a/packages/core/src/CallHistory.js +++ b/packages/core/src/CallHistory.js @@ -65,6 +65,9 @@ class CallHistory { this.callLogs.push(callLog); } + /** + * @returns {void} + */ clear() { this.callLogs.forEach(({ route }) => route.reset()); this.callLogs = []; diff --git a/packages/core/src/FetchMock.js b/packages/core/src/FetchMock.js index 3795a46d..0e1c0e9f 100644 --- a/packages/core/src/FetchMock.js +++ b/packages/core/src/FetchMock.js @@ -11,6 +11,65 @@ import * as requestUtils from './RequestUtils.js'; /** @typedef {import('./CallHistory').CallLog} CallLog */ /** @typedef {import('./Route').RouteResponseFunction} RouteResponseFunction */ +/** @typedef {'get' |'post' |'put' |'delete' |'head' |'patch' |'once' |'sticky' |'any' |'anyOnce' |'getOnce' |'postOnce' |'putOnce' |'deleteOnce' |'headOnce' |'patchOnce' } AdditionalRouteMethodName */ + +/** + * + * @param {UserRouteConfig} shorthandOptions + */ +const defineShorthand = (shorthandOptions) => { + /** + * @overload + * @param {UserRouteConfig} matcher + * @this {FetchMock} + * @returns {FetchMock} + */ + + /** + * @overload + * @param {RouteMatcher } matcher + * @param {RouteResponse} response + * @param {UserRouteConfig | string} [options] + * @this {FetchMock} + * @returns {FetchMock} + */ + + /** + * @param {RouteMatcher | UserRouteConfig} matcher + * @param {RouteResponse} [response] + * @param {UserRouteConfig | string} [options] + * @this {FetchMock} + * @returns {FetchMock} + */ + return function (matcher, response, options) { + return this.route( + //@ts-ignore + matcher, + response, + Object.assign(options || {}, shorthandOptions), + ); + }; +}; +/** + * + * @param {UserRouteConfig} shorthandOptions + */ +const defineGreedyShorthand = (shorthandOptions) => { + /** + * @param {RouteResponse} response + * @param {UserRouteConfig | string} [options] + * @this {FetchMock} + * @returns {FetchMock} + */ + return function (response, options) { + return this.route( + '*', + response, + Object.assign(options || {}, shorthandOptions), + ); + }; +}; + /** * @typedef FetchMockConfig * @property {boolean} [sendAsJson] @@ -35,9 +94,7 @@ const defaultConfig = { fetch: globalThis.fetch, }; -/** @typedef {FetchMockCore & PresetRoutes} FetchMock*/ - -class FetchMockCore { +class FetchMock { /** * * @param {FetchMockConfig} config @@ -56,8 +113,7 @@ class FetchMockCore { * @returns {FetchMock} */ createInstance() { - const instance = new FetchMockCore({ ...this.config }, this.router); - return Object.assign(instance, PresetRoutes); + return new FetchMock({ ...this.config }, this.router); } /** * @@ -89,19 +145,15 @@ class FetchMockCore { /** * @overload * @param {UserRouteConfig} matcher - * @this {FetchMock} * @returns {FetchMock} */ - /** * @overload * @param {RouteMatcher } matcher * @param {RouteResponse} response * @param {UserRouteConfig | string} [options] - * @this {FetchMock} * @returns {FetchMock} */ - /** * @param {RouteMatcher | UserRouteConfig} matcher * @param {RouteResponse} [response] @@ -116,6 +168,7 @@ class FetchMockCore { /** * * @param {RouteResponse} [response] + * @this {FetchMock} * @returns {FetchMock} */ catch(response) { @@ -136,6 +189,7 @@ class FetchMockCore { * @param {string[]} [options.names] * @param {boolean} [options.includeSticky] * @param {boolean} [options.includeFallback] + * @this {FetchMock} * @returns {FetchMock} */ removeRoutes(options) { @@ -150,93 +204,24 @@ class FetchMockCore { this.callHistory.clear(); return this; } + sticky = defineShorthand({ sticky: true }); + once = defineShorthand({ repeat: 1 }); + any = defineGreedyShorthand({}); + anyOnce = defineGreedyShorthand({ repeat: 1 }); + get = defineShorthand({ method: 'get' }); + getOnce = defineShorthand({ method: 'get', repeat: 1 }); + post = defineShorthand({ method: 'post' }); + postOnce = defineShorthand({ method: 'post', repeat: 1 }); + put = defineShorthand({ method: 'put' }); + putOnce = defineShorthand({ method: 'put', repeat: 1 }); + delete = defineShorthand({ method: 'delete' }); + deleteOnce = defineShorthand({ method: 'delete', repeat: 1 }); + head = defineShorthand({ method: 'head' }); + headOnce = defineShorthand({ method: 'head', repeat: 1 }); + patch = defineShorthand({ method: 'patch' }); + patchOnce = defineShorthand({ method: 'patch', repeat: 1 }); } -/** @typedef {'get' |'post' |'put' |'delete' |'head' |'patch' |'once' |'sticky' |'any' |'anyOnce' |'getOnce' |'postOnce' |'putOnce' |'deleteOnce' |'headOnce' |'patchOnce' |'getAny' |'postAny' |'putAny' |'deleteAny' |'headAny' |'patchAny' |'getAnyOnce' |'postAnyOnce' |'putAnyOnce' |'deleteAnyOnce' |'headAnyOnce' |'patchAnyOnce'} PresetRouteMethodName} */ -/** @typedef {Object.} PresetRoutes */ - -/** @type {PresetRoutes} */ -const PresetRoutes = {}; -/** - * - * @param {PresetRouteMethodName} methodName - * @param {string} underlyingMethod - * @param {UserRouteConfig} shorthandOptions - */ -const defineShorthand = (methodName, underlyingMethod, shorthandOptions) => { - /** - * @overload - * @param {UserRouteConfig} matcher - * @this {FetchMock} - * @returns {FetchMock} - */ - - /** - * @overload - * @param {RouteMatcher } matcher - * @param {RouteResponse} response - * @param {UserRouteConfig | string} [options] - * @this {FetchMock} - * @returns {FetchMock} - */ - - /** - * @param {RouteMatcher | UserRouteConfig} matcher - * @param {RouteResponse} [response] - * @param {UserRouteConfig | string} [options] - * @this {FetchMock} - * @returns {FetchMock} - */ - PresetRoutes[methodName] = function (matcher, response, options) { - return this[underlyingMethod]( - matcher, - response, - Object.assign(options || {}, shorthandOptions), - ); - }; -}; -/** - * - * @param {PresetRouteMethodName} methodName - * @param {string} underlyingMethod - */ -const defineGreedyShorthand = (methodName, underlyingMethod) => { - /** - * @param {RouteResponse} response - * @param {UserRouteConfig | string} [options] - * @this {FetchMock} - * @returns {FetchMock} - */ - PresetRoutes[methodName] = function (response, options) { - return this[underlyingMethod]('*', response, options); - }; -}; - -defineShorthand('sticky', 'route', { sticky: true }); -defineShorthand('once', 'route', { repeat: 1 }); -defineGreedyShorthand('any', 'route'); -defineGreedyShorthand('anyOnce', 'once'); - -['get', 'post', 'put', 'delete', 'head', 'patch'].forEach((method) => { - defineShorthand(/** @type {PresetRouteMethodName} */ (method), 'route', { - method, - }); - defineShorthand( - /** @type {PresetRouteMethodName} */ (`${method}Once`), - 'once', - { method }, - ); - defineGreedyShorthand( - /** @type {PresetRouteMethodName} */ (`${method}Any`), - method, - ); - defineGreedyShorthand( - /** @type {PresetRouteMethodName} */ (`${method}AnyOnce`), - `${method}Once`, - ); -}); - -const fetchMock = new FetchMockCore({ ...defaultConfig }).createInstance(); +const fetchMock = new FetchMock({ ...defaultConfig }).createInstance(); -console.log(fetchMock); export default fetchMock; diff --git a/packages/core/src/RequestUtils.js b/packages/core/src/RequestUtils.js index 55c6e933..95ba5e08 100644 --- a/packages/core/src/RequestUtils.js +++ b/packages/core/src/RequestUtils.js @@ -32,7 +32,6 @@ export function normalizeUrl(url) { return u.pathname + u.search; } /** - * * @param {string|Request} urlOrRequest * @param {typeof Request} Request * @returns {urlOrRequest is Request} diff --git a/packages/core/src/Router.js b/packages/core/src/Router.js index 4bc8a0b3..08006b52 100644 --- a/packages/core/src/Router.js +++ b/packages/core/src/Router.js @@ -288,20 +288,6 @@ export default class Router { }); } - /** - * @overload - * @param {UserRouteConfig} matcher - * @returns {void} - */ - - /** - * @overload - * @param {RouteMatcher } matcher - * @param {RouteResponse} response - * @param {UserRouteConfig | string} [nameOrOptions] - * @returns {void} - */ - /** * @param {RouteMatcher | UserRouteConfig} matcher * @param {RouteResponse} [response] diff --git a/packages/core/types/CallHistory.d.ts b/packages/core/types/CallHistory.d.ts index bd6be600..44b20021 100644 --- a/packages/core/types/CallHistory.d.ts +++ b/packages/core/types/CallHistory.d.ts @@ -24,52 +24,16 @@ export type Matched = "matched"; export type Unmatched = "unmatched"; export type CallHistoryFilter = RouteName | Matched | Unmatched | boolean | RouteMatcher; declare class CallHistory { - /** - * @param {FetchMockConfig} globalConfig - * @param {Router} router - */ constructor(globalConfig: FetchMockConfig, router: Router); - /** @type {CallLog[]} */ callLogs: CallLog[]; config: import("./FetchMock").FetchMockConfig; router: Router; - /** - * - * @param {CallLog} callLog - */ recordCall(callLog: CallLog): void; clear(): void; - /** - * - * @param {boolean} [waitForResponseMethods] - * @returns {Promise} - */ flush(waitForResponseMethods?: boolean): Promise; - /** - * - * @param {CallHistoryFilter} filter - * @param {RouteConfig} options - * @returns {CallLog[]} - */ calls(filter: CallHistoryFilter, options: RouteConfig): CallLog[]; - /** - * - * @param {CallHistoryFilter} filter - * @param {RouteConfig} options - * @returns {boolean} - */ called(filter: CallHistoryFilter, options: RouteConfig): boolean; - /** - * - * @param {CallHistoryFilter} filter - * @param {RouteConfig} options - * @returns {CallLog} - */ lastCall(filter: CallHistoryFilter, options: RouteConfig): CallLog; - /** - * @param {RouteName|RouteName[]} [routeNames] - * @returns {boolean} - */ done(routeNames?: RouteName | RouteName[]): boolean; } import Route from './Route.js'; diff --git a/packages/core/types/FetchHandler.d.ts b/packages/core/types/FetchHandler.d.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/packages/core/types/FetchMock.d.ts b/packages/core/types/FetchMock.d.ts index d2b66887..68e83c9a 100644 --- a/packages/core/types/FetchMock.d.ts +++ b/packages/core/types/FetchMock.d.ts @@ -1,5 +1,4 @@ -declare const _default: any; -export default _default; +export default fetchMock; export type RouteMatcher = import("./Router").RouteMatcher; export type RouteName = import("./Route").RouteName; export type UserRouteConfig = import("./Route").UserRouteConfig; @@ -7,6 +6,7 @@ export type RouteResponse = import("./Router").RouteResponse; export type MatcherDefinition = import("./Matchers").MatcherDefinition; export type CallLog = import("./CallHistory").CallLog; export type RouteResponseFunction = import("./Route").RouteResponseFunction; +export type AdditionalRouteMethodName = "get" | "post" | "put" | "delete" | "head" | "patch" | "once" | "sticky" | "any" | "anyOnce" | "getOnce" | "postOnce" | "putOnce" | "deleteOnce" | "headOnce" | "patchOnce"; export type FetchMockConfig = { sendAsJson?: boolean; includeContentLength?: boolean; @@ -17,23 +17,82 @@ export type FetchMockConfig = { Request?: typeof Request; Response?: typeof Response; }; -export type FetchMockCore = { +declare const fetchMock: FetchMock; +declare class FetchMock { + constructor(config: FetchMockConfig, router?: Router); config: FetchMockConfig; router: Router; callHistory: CallHistory; - createInstance: () => FetchMock; - fetchHandler: (arg0: string | Request, arg1: RequestInit) => Promise; - route: (arg0: any, arg1: any, arg2: any) => FetchMock; - catch: (arg0: RouteResponse | undefined) => FetchMock; - defineMatcher: (arg0: MatcherDefinition) => void; - removeRoutes: (arg0: object) => void; - clearHistory: () => void; -}; -/** - * } - */ -export type PresetRouteMethodName = "get" | "post" | "put" | "delete" | "head" | "patch" | "once" | "sticky" | "any" | "anyOnce" | "getOnce" | "postOnce" | "putOnce" | "deleteOnce" | "headOnce" | "patchOnce" | "getAny" | "postAny" | "putAny" | "deleteAny" | "headAny" | "patchAny" | "getAnyOnce" | "postAnyOnce" | "putAnyOnce" | "deleteAnyOnce" | "headAnyOnce" | "patchAnyOnce"; -export type PresetRoutes = any; -export type FetchMock = FetchMockCore & PresetRoutes; + createInstance(): FetchMock; + fetchHandler(this: FetchMock, requestInput: string | Request, requestInit?: RequestInit): Promise; + route(matcher: UserRouteConfig): FetchMock; + route(matcher: RouteMatcher, response: RouteResponse, options?: UserRouteConfig | string): FetchMock; + catch(this: FetchMock, response?: RouteResponse): FetchMock; + defineMatcher(matcher: MatcherDefinition): void; + removeRoutes(options?: { + names?: string[]; + includeSticky?: boolean; + includeFallback?: boolean; + }): FetchMock; + clearHistory(): FetchMock; + sticky: { + (this: FetchMock, matcher: UserRouteConfig): FetchMock; + (this: FetchMock, matcher: RouteMatcher, response: RouteResponse, options?: UserRouteConfig | string): FetchMock; + }; + once: { + (this: FetchMock, matcher: UserRouteConfig): FetchMock; + (this: FetchMock, matcher: RouteMatcher, response: RouteResponse, options?: UserRouteConfig | string): FetchMock; + }; + any: (this: FetchMock, response: RouteResponse, options?: UserRouteConfig | string) => FetchMock; + anyOnce: (this: FetchMock, response: RouteResponse, options?: UserRouteConfig | string) => FetchMock; + get: { + (this: FetchMock, matcher: UserRouteConfig): FetchMock; + (this: FetchMock, matcher: RouteMatcher, response: RouteResponse, options?: UserRouteConfig | string): FetchMock; + }; + getOnce: { + (this: FetchMock, matcher: UserRouteConfig): FetchMock; + (this: FetchMock, matcher: RouteMatcher, response: RouteResponse, options?: UserRouteConfig | string): FetchMock; + }; + post: { + (this: FetchMock, matcher: UserRouteConfig): FetchMock; + (this: FetchMock, matcher: RouteMatcher, response: RouteResponse, options?: UserRouteConfig | string): FetchMock; + }; + postOnce: { + (this: FetchMock, matcher: UserRouteConfig): FetchMock; + (this: FetchMock, matcher: RouteMatcher, response: RouteResponse, options?: UserRouteConfig | string): FetchMock; + }; + put: { + (this: FetchMock, matcher: UserRouteConfig): FetchMock; + (this: FetchMock, matcher: RouteMatcher, response: RouteResponse, options?: UserRouteConfig | string): FetchMock; + }; + putOnce: { + (this: FetchMock, matcher: UserRouteConfig): FetchMock; + (this: FetchMock, matcher: RouteMatcher, response: RouteResponse, options?: UserRouteConfig | string): FetchMock; + }; + delete: { + (this: FetchMock, matcher: UserRouteConfig): FetchMock; + (this: FetchMock, matcher: RouteMatcher, response: RouteResponse, options?: UserRouteConfig | string): FetchMock; + }; + deleteOnce: { + (this: FetchMock, matcher: UserRouteConfig): FetchMock; + (this: FetchMock, matcher: RouteMatcher, response: RouteResponse, options?: UserRouteConfig | string): FetchMock; + }; + head: { + (this: FetchMock, matcher: UserRouteConfig): FetchMock; + (this: FetchMock, matcher: RouteMatcher, response: RouteResponse, options?: UserRouteConfig | string): FetchMock; + }; + headOnce: { + (this: FetchMock, matcher: UserRouteConfig): FetchMock; + (this: FetchMock, matcher: RouteMatcher, response: RouteResponse, options?: UserRouteConfig | string): FetchMock; + }; + patch: { + (this: FetchMock, matcher: UserRouteConfig): FetchMock; + (this: FetchMock, matcher: RouteMatcher, response: RouteResponse, options?: UserRouteConfig | string): FetchMock; + }; + patchOnce: { + (this: FetchMock, matcher: UserRouteConfig): FetchMock; + (this: FetchMock, matcher: RouteMatcher, response: RouteResponse, options?: UserRouteConfig | string): FetchMock; + }; +} import Router from './Router.js'; import CallHistory from './CallHistory.js'; diff --git a/packages/core/types/InstanceManagement.d.ts b/packages/core/types/InstanceManagement.d.ts deleted file mode 100644 index 22c63e11..00000000 --- a/packages/core/types/InstanceManagement.d.ts +++ /dev/null @@ -1,18 +0,0 @@ -type RequestConstructor = new (input: string | Request, init ?: RequestInit) => Request; - -declare type FetchMockCore ={ - createInstance: () => FetchMock - config: FetchMockConfig; -} - -declare type FetchMock = FetchMockCore & Router - -// 5. Declaration merging -// Unlike a type alias, an interface can be defined multiple times, and will be treated as a single interface(with members of all declarations being merged). - -// // These two declarations become: -// // interface Point { x: number; y: number; } -// interface Point { x: number; } -// interface Point { y: number; } - -// const point: Point = { x: 1, y: 2 }; \ No newline at end of file diff --git a/packages/core/types/Matchers.d.ts b/packages/core/types/Matchers.d.ts index 3cff1ed2..10f7e149 100644 --- a/packages/core/types/Matchers.d.ts +++ b/packages/core/types/Matchers.d.ts @@ -1,6 +1,5 @@ export function isUrlMatcher(matcher: RouteMatcher | RouteConfig): matcher is RouteMatcherUrl; export function isFunctionMatcher(matcher: RouteMatcher | RouteConfig): matcher is RouteMatcherFunction; -/** @type {MatcherDefinition[]} */ export const builtInMatchers: MatcherDefinition[]; export type RouteConfig = import("./Route").RouteConfig; export type CallLog = import("./CallHistory").CallLog; diff --git a/packages/core/types/RequestUtils.d.ts b/packages/core/types/RequestUtils.d.ts index 5991aa1a..b389f09c 100644 --- a/packages/core/types/RequestUtils.d.ts +++ b/packages/core/types/RequestUtils.d.ts @@ -1,39 +1,7 @@ -/** - * @typedef DerivedRequestOptions - * @property {string} method - * @property {string} [body] - * @property {{ [key: string]: string }} [headers] - */ -/** @typedef {RequestInit | (RequestInit & DerivedRequestOptions) } NormalizedRequestOptions */ -/** @typedef {import('./CallHistory').CallLog} CallLog */ -/** - * @param {string | string | URL} url - * @returns {string} - */ export function normalizeUrl(url: string | string | URL): string; -/** - * - * @param {string | object} url - * @param {RequestInit} options - * @returns {CallLog} - */ export function createCallLogFromUrlAndOptions(url: string | object, options: RequestInit): CallLog; -/** - * - * @param {Request} request - * @param {RequestInit} options - * @returns {Promise} - */ export function createCallLogFromRequest(request: Request, options: RequestInit): Promise; -/** - * @param {string} url - * @returns {string} - */ export function getPath(url: string): string; -/** - * @param {string} url - * @returns {string} - */ export function getQuery(url: string): string; export function isRequest(urlOrRequest: string | Request, Request: typeof globalThis.Request): urlOrRequest is Request; export function normalizeHeaders(headers: Headers | [string, string][] | Record | { diff --git a/packages/core/types/Route.d.ts b/packages/core/types/Route.d.ts index db375733..e3a79fc2 100644 --- a/packages/core/types/Route.d.ts +++ b/packages/core/types/Route.d.ts @@ -5,9 +5,6 @@ export type RouteMatcherFunction = import("./Matchers").RouteMatcherFunction; export type RouteMatcherUrl = import("./Matchers").RouteMatcherUrl; export type MatcherDefinition = import("./Matchers").MatcherDefinition; export type FetchMockConfig = import("./FetchMock").FetchMockConfig; -/** - * { - */ export type RouteResponseConfig = { body?: string | {}; status?: number; @@ -48,70 +45,27 @@ export type UserRouteConfig = { response?: RouteResponse | RouteResponseFunction; repeat?: number; delay?: number; - /** - * - TODO this is global - */ sendAsJson?: boolean; - /** - * - TODO this is global - */ includeContentLength?: boolean; - /** - * - TODO this is global - */ matchPartialBody?: boolean; sticky?: boolean; - /** - * - TODO this shoudl not be in user config - */ usesBody?: boolean; isFallback?: boolean; }; export type RouteConfig = UserRouteConfig & FetchMockConfig; -/** - * @class Route - */ declare class Route { - /** - * @param {MatcherDefinition} matcher - */ static defineMatcher(matcher: MatcherDefinition): void; - /** @type {MatcherDefinition[]} */ static registeredMatchers: MatcherDefinition[]; - /** - * @param {RouteConfig} config - */ constructor(config: RouteConfig); - /** @type {RouteConfig} */ config: RouteConfig; - /** @type {RouteMatcherFunction=} */ matcher: RouteMatcherFunction | undefined; - /** - * @returns {void} - */ reset(): void; - /** - * - * @param {RouteResponseConfig} responseInput - * @returns {{response: Response, responseOptions: ResponseInit, responseInput: RouteResponseConfig}} - */ constructResponse(responseInput: RouteResponseConfig): { response: Response; responseOptions: ResponseInit; responseInput: RouteResponseConfig; }; - /** - * - * @param {RouteResponseConfig} responseInput - * @returns {ResponseInitUsingHeaders} - */ constructResponseOptions(responseInput: RouteResponseConfig): ResponseInitUsingHeaders; - /** - * - * @param {RouteResponseConfig} responseInput - * @param {ResponseInitUsingHeaders} responseOptions - * @returns {string|null} - */ constructResponseBody(responseInput: RouteResponseConfig, responseOptions: ResponseInitUsingHeaders): string | null; #private; } diff --git a/packages/core/types/Router.d.ts b/packages/core/types/Router.d.ts index cf4facdc..20a798e2 100644 --- a/packages/core/types/Router.d.ts +++ b/packages/core/types/Router.d.ts @@ -1,74 +1,21 @@ export default class Router { - /** - * @param {FetchMockConfig} fetchMockConfig - * @param {object} [inheritedRoutes] - * @param {Route[]} [inheritedRoutes.routes] - * @param {Route} [inheritedRoutes.fallbackRoute] - */ constructor(fetchMockConfig: FetchMockConfig, { routes, fallbackRoute }?: { routes?: Route[]; fallbackRoute?: Route; }); - /** @type {Route[]} */ routes: Route[]; config: import("./FetchMock").FetchMockConfig; fallbackRoute: Route; - /** - * - * @param {Request} request - * @returns {boolean} - */ needsToReadBody(request: Request): boolean; - /** - * @param {CallLog} callLog - * @returns {Promise} - */ execute(callLog: CallLog): Promise; - /** - * - * @param {CallLog} callLog - * @returns {Promise<{response: Response, responseOptions: ResponseInit, responseInput: RouteResponseConfig}>} - */ generateResponse(callLog: CallLog): Promise<{ response: Response; responseOptions: ResponseInit; responseInput: RouteResponseConfig; }>; - /** - * - * @param {Response} response - * @param {ResponseInit} responseConfig - * @param {RouteResponseConfig} responseInput - * @param {string} responseUrl - * @param {Promise[]} pendingPromises - * @returns {Response} - */ createObservableResponse(response: Response, responseConfig: ResponseInit, responseInput: RouteResponseConfig, responseUrl: string, pendingPromises: Promise[]): Response; - /** - * @overload - * @param {UserRouteConfig} matcher - * @returns {void} - */ - addRoute(matcher: UserRouteConfig): void; - /** - * @overload - * @param {RouteMatcher } matcher - * @param {RouteResponse} response - * @param {UserRouteConfig | string} [nameOrOptions] - * @returns {void} - */ - addRoute(matcher: RouteMatcher, response: RouteResponse, nameOrOptions?: UserRouteConfig | string): void; - /** - * @param {RouteResponse} [response] - */ + addRoute(matcher: RouteMatcher | UserRouteConfig, response?: RouteResponse, nameOrOptions?: UserRouteConfig | string): void; setFallback(response?: RouteResponse): void; - /** - * - * @param {object} [options] - * @param {string[]} [options.names] - * @param {boolean} [options.includeSticky] - * @param {boolean} [options.includeFallback] - */ removeRoutes({ names, includeSticky, includeFallback }?: { names?: string[]; includeSticky?: boolean; diff --git a/packages/core/types/StatusTextMap.d.ts b/packages/core/types/StatusTextMap.d.ts index b98f5db6..eb8c81cc 100644 --- a/packages/core/types/StatusTextMap.d.ts +++ b/packages/core/types/StatusTextMap.d.ts @@ -1,7 +1,4 @@ export default statusTextMap; -/** - * @type {Object.} - */ declare const statusTextMap: { [x: number]: string; }; From 289f9805d304ae13d3383154f10cb301f3664213 Mon Sep 17 00:00:00 2001 From: Rhys Evans Date: Tue, 23 Jul 2024 22:44:48 +0100 Subject: [PATCH 4/8] docs: documented removed methods --- docs/blog/2024-07-21-introducing-core.md | 10 ++++++++++ docs/docs/@fetch-mock/core/more-routing-methods.md | 12 ------------ 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/docs/blog/2024-07-21-introducing-core.md b/docs/blog/2024-07-21-introducing-core.md index a28a5a7f..b0c582bd 100644 --- a/docs/blog/2024-07-21-introducing-core.md +++ b/docs/blog/2024-07-21-introducing-core.md @@ -53,6 +53,16 @@ fetchMock.mock('http://my.site', 200); which keeps fetch-mock's methods much further away from any other library's workings. + +## .getAny(), .postAny(), .putAny(), .deleteAny(), .headAny(), .patchAny(), .getAnyOnce(), .postAnyOnce(), .putAnyOnce(), .deleteAnyOnce(), .headAnyOnce(), .patchAnyOnce() + +While `.getOnce()` etc feel very useful, the `any` and `anyOnce` variants added a lot of repetition to the code and types, and don't actually add much value. + +`.___AnyOnce(response, options)` + +Creates a route that responds to any single request using a particular http method. + + ### Gone, but back soon The following features will return in other libraries that wrap @fetch-mock/core for different environments. diff --git a/docs/docs/@fetch-mock/core/more-routing-methods.md b/docs/docs/@fetch-mock/core/more-routing-methods.md index 6aa2b42d..28b2ba27 100644 --- a/docs/docs/@fetch-mock/core/more-routing-methods.md +++ b/docs/docs/@fetch-mock/core/more-routing-methods.md @@ -61,18 +61,6 @@ fetchMock.purge = function (matcher, response, options) { Creates a route that only responds to a single request using a particular http method -## .getAny(), .postAny(), .putAny(), .deleteAny(), .headAny(), .patchAny() - -`.___Any(response, options)` - -Creates a route that responds to any requests using a particular http method. - -## .getAnyOnce(), .postAnyOnce(), .putAnyOnce(), .deleteAnyOnce(), .headAnyOnce(), .patchAnyOnce() - -`.___AnyOnce(response, options)` - -Creates a route that responds to any single request using a particular http method. - ## .addMatcher(options) Allows adding your own, reusable custom matchers to fetch-mock, for example a matcher for interacting with GraphQL queries, or an `isAuthorized` matcher that encapsulates the exact authorization conditions for the API you are mocking, and only requires a `true` or `false` to be input From a17d8570d2340c4fe452c3565eb2da96f4f6c555 Mon Sep 17 00:00:00 2001 From: Rhys Evans Date: Tue, 23 Jul 2024 22:45:56 +0100 Subject: [PATCH 5/8] docs: documented removed methods --- docs/blog/2024-07-21-introducing-core.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/blog/2024-07-21-introducing-core.md b/docs/blog/2024-07-21-introducing-core.md index b0c582bd..b49e3aa0 100644 --- a/docs/blog/2024-07-21-introducing-core.md +++ b/docs/blog/2024-07-21-introducing-core.md @@ -53,7 +53,6 @@ fetchMock.mock('http://my.site', 200); which keeps fetch-mock's methods much further away from any other library's workings. - ## .getAny(), .postAny(), .putAny(), .deleteAny(), .headAny(), .patchAny(), .getAnyOnce(), .postAnyOnce(), .putAnyOnce(), .deleteAnyOnce(), .headAnyOnce(), .patchAnyOnce() While `.getOnce()` etc feel very useful, the `any` and `anyOnce` variants added a lot of repetition to the code and types, and don't actually add much value. @@ -62,7 +61,6 @@ While `.getOnce()` etc feel very useful, the `any` and `anyOnce` variants added Creates a route that responds to any single request using a particular http method. - ### Gone, but back soon The following features will return in other libraries that wrap @fetch-mock/core for different environments. From a650023581d455dbefddb5e10f8753b439740e14 Mon Sep 17 00:00:00 2001 From: Rhys Evans Date: Wed, 24 Jul 2024 02:16:38 +0100 Subject: [PATCH 6/8] chore: tidy a few type definition orderings --- packages/core/src/FetchMock.js | 5 +-- packages/core/src/Matchers.js | 37 ++++++++++--------- packages/core/src/RequestUtils.js | 7 ---- .../src/__tests__/FetchMock/routing.test.js | 21 ----------- 4 files changed, 22 insertions(+), 48 deletions(-) diff --git a/packages/core/src/FetchMock.js b/packages/core/src/FetchMock.js index 0e1c0e9f..507aa966 100644 --- a/packages/core/src/FetchMock.js +++ b/packages/core/src/FetchMock.js @@ -11,8 +11,6 @@ import * as requestUtils from './RequestUtils.js'; /** @typedef {import('./CallHistory').CallLog} CallLog */ /** @typedef {import('./Route').RouteResponseFunction} RouteResponseFunction */ -/** @typedef {'get' |'post' |'put' |'delete' |'head' |'patch' |'once' |'sticky' |'any' |'anyOnce' |'getOnce' |'postOnce' |'putOnce' |'deleteOnce' |'headOnce' |'patchOnce' } AdditionalRouteMethodName */ - /** * * @param {UserRouteConfig} shorthandOptions @@ -125,7 +123,8 @@ class FetchMock { async fetchHandler(requestInput, requestInit) { // TODO move into router let callLog; - if (requestUtils.isRequest(requestInput, this.config.Request)) { + + if (requestInput instanceof this.config.Request) { callLog = await requestUtils.createCallLogFromRequest( requestInput, requestInit, diff --git a/packages/core/src/Matchers.js b/packages/core/src/Matchers.js index 5ae4c0da..88aaf86a 100644 --- a/packages/core/src/Matchers.js +++ b/packages/core/src/Matchers.js @@ -13,6 +13,19 @@ import { normalizeUrl, } from './RequestUtils.js'; +/** @typedef {string | RegExp | URL} RouteMatcherUrl */ +/** @typedef {function(string): RouteMatcherFunction} UrlMatcherGenerator */ +/** @typedef {function(CallLog): boolean} RouteMatcherFunction */ +/** @typedef {function(RouteConfig): RouteMatcherFunction} MatcherGenerator */ +/** @typedef {RouteMatcherUrl | RouteMatcherFunction} RouteMatcher */ + +/** + * @typedef MatcherDefinition + * @property {string} name + * @property {MatcherGenerator} matcher + * @property {boolean} [usesBody] + */ + /** * @param {RouteMatcher | RouteConfig} matcher * @returns {matcher is RouteMatcherUrl} @@ -29,19 +42,6 @@ export const isUrlMatcher = (matcher) => */ export const isFunctionMatcher = (matcher) => typeof matcher === 'function'; -/** @typedef {string | RegExp | URL} RouteMatcherUrl */ -/** @typedef {function(string): RouteMatcherFunction} UrlMatcherGenerator */ -/** @typedef {function(CallLog): boolean} RouteMatcherFunction */ -/** @typedef {function(RouteConfig): RouteMatcherFunction} MatcherGenerator */ -/** @typedef {RouteMatcherUrl | RouteMatcherFunction} RouteMatcher */ - -/** - * @typedef MatcherDefinition - * @property {string} name - * @property {MatcherGenerator} matcher - * @property {boolean} [usesBody] - */ - /** * @type {Object.} */ @@ -182,6 +182,12 @@ const getBodyMatcher = (route) => { ); }; }; + +/** + * @type {MatcherGenerator} + */ +const getFunctionMatcher = ({ matcherFunction }) => matcherFunction; + /** * * @param {RouteConfig} route @@ -207,10 +213,7 @@ const getFullUrlMatcher = (route, matcherUrl, query) => { }; }; -/** - * @type {MatcherGenerator} - */ -const getFunctionMatcher = ({ matcherFunction }) => matcherFunction; + /** * @type {MatcherGenerator} */ diff --git a/packages/core/src/RequestUtils.js b/packages/core/src/RequestUtils.js index 95ba5e08..136d0e32 100644 --- a/packages/core/src/RequestUtils.js +++ b/packages/core/src/RequestUtils.js @@ -31,13 +31,6 @@ export function normalizeUrl(url) { const u = new URL(url, 'http://dummy'); return u.pathname + u.search; } -/** - * @param {string|Request} urlOrRequest - * @param {typeof Request} Request - * @returns {urlOrRequest is Request} - */ -export const isRequest = (urlOrRequest, Request) => - Request.prototype.isPrototypeOf(urlOrRequest); /** * diff --git a/packages/core/src/__tests__/FetchMock/routing.test.js b/packages/core/src/__tests__/FetchMock/routing.test.js index 264ce127..17f7eb09 100644 --- a/packages/core/src/__tests__/FetchMock/routing.test.js +++ b/packages/core/src/__tests__/FetchMock/routing.test.js @@ -212,27 +212,6 @@ describe('Routing', () => { }); testChainableRoutingMethod(`${method}Once`); - - it(`has ${method}Any() shorthand`, () => { - fm[`${method}Any`]('a', { opt: 'b' }); - expect(fm.router.addRoute).toHaveBeenCalledWith('*', 'a', { - opt: 'b', - method, - }); - }); - - testChainableRoutingMethod(`${method}Any`); - - it(`has ${method}AnyOnce() shorthand`, () => { - fm[`${method}AnyOnce`]('a', { opt: 'b' }); - expect(fm.router.addRoute).toHaveBeenCalledWith('*', 'a', { - opt: 'b', - method, - repeat: 1, - }); - }); - - testChainableRoutingMethod(`${method}Any`); }); }); }); From f8c1168a62e14e6e2f9641d10ffd8bcbbd9d8df4 Mon Sep 17 00:00:00 2001 From: Rhys Evans Date: Wed, 24 Jul 2024 02:33:00 +0100 Subject: [PATCH 7/8] chore: types for core all done I think --- .husky/pre-commit | 2 +- packages/core/src/CallHistory.js | 1 + packages/core/src/Matchers.js | 2 +- packages/core/types/FetchMock.d.ts | 1 - packages/core/types/InstanceManagement.d.ts | 18 ++++++++++++++++++ packages/core/types/RequestUtils.d.ts | 1 - 6 files changed, 21 insertions(+), 4 deletions(-) create mode 100644 packages/core/types/InstanceManagement.d.ts diff --git a/.husky/pre-commit b/.husky/pre-commit index 3867a0fe..30ee92f7 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1 +1 @@ -npm run lint +npm run lint && npm run types:lint && npm run types:check diff --git a/packages/core/src/CallHistory.js b/packages/core/src/CallHistory.js index 97169b86..5bbe82e1 100644 --- a/packages/core/src/CallHistory.js +++ b/packages/core/src/CallHistory.js @@ -52,6 +52,7 @@ class CallHistory { * @param {Router} router */ constructor(globalConfig, router) { + /** @type {CallLog[]} */ this.callLogs = []; this.config = globalConfig; diff --git a/packages/core/src/Matchers.js b/packages/core/src/Matchers.js index 88aaf86a..8d7cc864 100644 --- a/packages/core/src/Matchers.js +++ b/packages/core/src/Matchers.js @@ -13,6 +13,7 @@ import { normalizeUrl, } from './RequestUtils.js'; + /** @typedef {string | RegExp | URL} RouteMatcherUrl */ /** @typedef {function(string): RouteMatcherFunction} UrlMatcherGenerator */ /** @typedef {function(CallLog): boolean} RouteMatcherFunction */ @@ -213,7 +214,6 @@ const getFullUrlMatcher = (route, matcherUrl, query) => { }; }; - /** * @type {MatcherGenerator} */ diff --git a/packages/core/types/FetchMock.d.ts b/packages/core/types/FetchMock.d.ts index 68e83c9a..092d3102 100644 --- a/packages/core/types/FetchMock.d.ts +++ b/packages/core/types/FetchMock.d.ts @@ -6,7 +6,6 @@ export type RouteResponse = import("./Router").RouteResponse; export type MatcherDefinition = import("./Matchers").MatcherDefinition; export type CallLog = import("./CallHistory").CallLog; export type RouteResponseFunction = import("./Route").RouteResponseFunction; -export type AdditionalRouteMethodName = "get" | "post" | "put" | "delete" | "head" | "patch" | "once" | "sticky" | "any" | "anyOnce" | "getOnce" | "postOnce" | "putOnce" | "deleteOnce" | "headOnce" | "patchOnce"; export type FetchMockConfig = { sendAsJson?: boolean; includeContentLength?: boolean; diff --git a/packages/core/types/InstanceManagement.d.ts b/packages/core/types/InstanceManagement.d.ts new file mode 100644 index 00000000..22c63e11 --- /dev/null +++ b/packages/core/types/InstanceManagement.d.ts @@ -0,0 +1,18 @@ +type RequestConstructor = new (input: string | Request, init ?: RequestInit) => Request; + +declare type FetchMockCore ={ + createInstance: () => FetchMock + config: FetchMockConfig; +} + +declare type FetchMock = FetchMockCore & Router + +// 5. Declaration merging +// Unlike a type alias, an interface can be defined multiple times, and will be treated as a single interface(with members of all declarations being merged). + +// // These two declarations become: +// // interface Point { x: number; y: number; } +// interface Point { x: number; } +// interface Point { y: number; } + +// const point: Point = { x: 1, y: 2 }; \ No newline at end of file diff --git a/packages/core/types/RequestUtils.d.ts b/packages/core/types/RequestUtils.d.ts index b389f09c..919c6b4e 100644 --- a/packages/core/types/RequestUtils.d.ts +++ b/packages/core/types/RequestUtils.d.ts @@ -3,7 +3,6 @@ export function createCallLogFromUrlAndOptions(url: string | object, options: Re export function createCallLogFromRequest(request: Request, options: RequestInit): Promise; export function getPath(url: string): string; export function getQuery(url: string): string; -export function isRequest(urlOrRequest: string | Request, Request: typeof globalThis.Request): urlOrRequest is Request; export function normalizeHeaders(headers: Headers | [string, string][] | Record | { [x: string]: string | number; } | HeadersInit): { From 6c2182279c72343799125b3f41bcfad08dd85b3a Mon Sep 17 00:00:00 2001 From: Rhys Evans Date: Wed, 24 Jul 2024 02:36:49 +0100 Subject: [PATCH 8/8] chore: linted --- packages/core/src/CallHistory.js | 1 - packages/core/src/Matchers.js | 1 - 2 files changed, 2 deletions(-) diff --git a/packages/core/src/CallHistory.js b/packages/core/src/CallHistory.js index 5bbe82e1..97169b86 100644 --- a/packages/core/src/CallHistory.js +++ b/packages/core/src/CallHistory.js @@ -52,7 +52,6 @@ class CallHistory { * @param {Router} router */ constructor(globalConfig, router) { - /** @type {CallLog[]} */ this.callLogs = []; this.config = globalConfig; diff --git a/packages/core/src/Matchers.js b/packages/core/src/Matchers.js index 8d7cc864..02e6615c 100644 --- a/packages/core/src/Matchers.js +++ b/packages/core/src/Matchers.js @@ -13,7 +13,6 @@ import { normalizeUrl, } from './RequestUtils.js'; - /** @typedef {string | RegExp | URL} RouteMatcherUrl */ /** @typedef {function(string): RouteMatcherFunction} UrlMatcherGenerator */ /** @typedef {function(CallLog): boolean} RouteMatcherFunction */