UNPKG

hungry-fetch

Version:
1 lines 7.57 kB
{"version":3,"file":"hungry-fetch.modern.mjs","sources":["../src/fetch-call.ts","../src/index.ts","../src/url-pattern.ts"],"sourcesContent":["/**\n * Container for storing call-data, providing some convenience functions.\n */\nexport default class FetchCall {\n url: string;\n additionalArgs: any;\n request: RequestInit;\n\n constructor(url: string, request: RequestInit = {}, additionalArgs: any) {\n this.request = request;\n this.url = url;\n this.additionalArgs = additionalArgs;\n }\n\n /**\n * Parses the request body from json.\n * @returns The parsed object.\n */\n json(): any {\n if (typeof this.request.body !== 'string')\n throw Error(`Can't parse json from ${typeof this.request.body}`);\n\n return JSON.parse(this.request.body);\n }\n\n /**\n * Gets the body of the request.\n * @returns The body.\n */\n body(): any {\n return this.request.body;\n }\n}\n","/**\n * This monkeypatches fetch and stores all requests send with fetch.\n * fetch calls are simply resolved with no data.\n *\n * With this you can test what your code is sending via fetch.\n */\n\nimport FetchCall from './fetch-call';\nimport { testWildcardPattern } from './url-pattern';\n\nlet fetchRequests: FetchCall[] = [];\nlet mockResponses: ResponseData[] = [];\n\nexport type Body = Record<string, any> | string;\n\nexport type ResponseData = {\n pattern: string;\n body: Record<string, any> | string;\n config: {\n contentType?: string;\n headers?: { [key: string]: string };\n status?: number;\n };\n resolve?: boolean;\n};\n\nfunction getResponse(responseData: ResponseData) {\n let body;\n let contentType;\n\n if (typeof responseData.body === 'object') {\n // json response\n body = JSON.stringify(responseData.body);\n contentType = 'application/json';\n } else {\n // plain text\n body = responseData.body;\n contentType = 'text/plain';\n }\n\n const { config } = responseData;\n\n return new Response(body, {\n headers: {\n 'Content-Type': config.contentType || contentType,\n ...(config.headers || {}),\n },\n status: config.status || 200,\n });\n}\n\nfunction testUrl(url: string, pattern: string) {\n // exact match\n if (url === pattern) {\n return 1.0;\n }\n\n // any pattern\n if (pattern === '*') {\n return 0.1;\n }\n\n // match using wildcards\n if (testWildcardPattern(url, pattern)) {\n return 0.5;\n }\n\n // no match\n return 0;\n}\n\nfunction getMockResponse(url: string): ResponseData | null {\n if (mockResponses.length === 0) return null;\n\n return mockResponses.reduce<ResponseData | null>((acc, cur) => {\n const curWeight = testUrl(url, cur.pattern);\n\n if (curWeight === 0) {\n return acc;\n }\n\n if (acc === null || curWeight > testUrl(url, acc.pattern)) {\n return cur;\n }\n\n return acc;\n }, null);\n}\n\nexport function clear() {\n fetchRequests = [];\n mockResponses = [];\n}\n\nexport function calls() {\n return fetchRequests;\n}\n\nexport function lastCall() {\n if (fetchRequests.length === 0) {\n throw new Error('fetch(…) has not been called!');\n }\n\n return fetchRequests[fetchRequests.length - 1];\n}\n\nexport function singleCall() {\n if (fetchRequests.length !== 1) {\n throw new Error(\n `fetch(…) not called exactly one time! It was called ${fetchRequests.length} times.`\n );\n }\n\n return lastCall();\n}\n\n/**\n * Mock a response with a url matching the specified pattern.\n *\n * The pattern supports wildcards for parts of the url by using an asterisk (\\*).\n * Example: /category/*&#47;details\n *\n * You may also use a single asterisk \"*\" to match all routes.\n *\n * The matchers are weighted so that a perfect match has the highest priority.\n * A match using wildcard components has the second highest prority. The\n * single asterisk matcher has the lowest priority. This helps to configure\n * default responses.\n *\n * @param urlMatcher The matcher incoming urls will be checked against.\n * @param body The body of the response.\n * @param config An optional config.\n * @param resolve Set to false to reject matching fetch calls.\n */\nexport function mockResponse(\n pattern: string,\n body: Body,\n config = {},\n resolve = true\n) {\n mockResponses.push({\n pattern,\n body,\n resolve,\n config,\n });\n}\n\n(typeof window !== 'undefined' ? window : (global as any)).fetch = (\n url: string,\n request: RequestInit,\n ...args: any[]\n) =>\n new Promise<void | Response>((resolve, reject) => {\n fetchRequests.push(new FetchCall(url, request, args));\n const mockedResponse = getMockResponse(url);\n\n if (mockedResponse) {\n const response = getResponse(mockedResponse);\n\n if (mockedResponse.resolve) {\n resolve(response);\n } else {\n reject(new Error('Failed to fetch'));\n }\n } else {\n resolve();\n }\n });\n","export function testWildcardPattern(urlToCheck: string, pattern: string) {\n const splittedUrlToCheck = urlToCheck.split('/');\n const splittedPattern = pattern.split('/');\n\n if (splittedUrlToCheck.length !== splittedPattern.length) {\n return false;\n }\n\n return splittedPattern.every((patternPart, i) => {\n if (patternPart === \"*\")\n return true;\n\n return splittedUrlToCheck[i] === patternPart;\n });\n}\n"],"names":["FetchCall","constructor","url","request","additionalArgs","this","json","body","Error","JSON","parse","fetchRequests","mockResponses","testUrl","pattern","urlToCheck","splittedUrlToCheck","split","splittedPattern","length","every","patternPart","i","testWildcardPattern","clear","calls","lastCall","singleCall","mockResponse","config","resolve","push","window","global","fetch","args","Promise","reject","mockedResponse","reduce","acc","cur","curWeight","getMockResponse","response","responseData","contentType","stringify","Response","headers","status","getResponse"],"mappings":"urBAGqBA,EAKnBC,YAAYC,EAAaC,EAAuB,GAAIC,QAJpDF,gBACAE,2BACAD,eAGEE,KAAKF,QAAUA,EACfE,KAAKH,IAAMA,EACXG,KAAKD,eAAiBA,CACvB,CAMDE,OACE,GAAiC,sBAAjBH,QAAQI,KACtB,MAAMC,2CAA2CL,QAAQI,MAE3D,OAAOE,KAAKC,MAAML,KAAKF,QAAQI,KAChC,CAMDA,OACE,YAAYJ,QAAQI,IACrB,ECrBH,IAAII,EAA6B,GAC7BC,EAAgC,GAwCpC,SAASC,EAAQX,EAAaY,GAE5B,OAAIZ,IAAQY,IAKI,MAAZA,cC1D8BC,EAAoBD,GACtD,MAAME,EAAqBD,EAAWE,MAAM,KACtCC,EAAkBJ,EAAQG,MAAM,KAEtC,OAAID,EAAmBG,SAAWD,EAAgBC,QAI3CD,EAAgBE,MAAM,CAACC,EAAaC,IACrB,MAAhBD,GAGGL,EAAmBM,KAAOD,EAEpC,CDiDKE,CAAoBrB,EAAKY,OAM9B,UAoBeU,IACdb,EAAgB,GAChBC,EAAgB,EACjB,UAEea,IACd,OAAOd,CACR,UAEee,IACd,GAA6B,IAAzBf,EAAcQ,OAChB,UAAUX,MAAM,iCAGlB,OAAOG,EAAcA,EAAcQ,OAAS,EAC7C,UAEeQ,IACd,GAA6B,IAAzBhB,EAAcQ,OAChB,UAAUX,6DAC+CG,EAAcQ,iBAIzE,OAAOO,GACR,UAoBeE,EACdd,EACAP,EACAsB,EAAS,GACTC,GAAU,GAEVlB,EAAcmB,KAAK,CACjBjB,UACAP,OACAuB,UACAD,UAEH,EAEkB,oBAAXG,OAAyBA,OAAUC,QAAgBC,MAAQ,CACjEhC,EACAC,KACGgC,IAEH,IAAIC,QAAyB,CAACN,EAASO,KACrC1B,EAAcoB,KAAK,IAAI/B,EAAUE,EAAKC,EAASgC,IAC/C,MAAMG,EApFV,SAAyBpC,GACvB,OAA6B,IAAzBU,EAAcO,YAEXP,EAAc2B,OAA4B,CAACC,EAAKC,KACrD,MAAMC,EAAY7B,EAAQX,EAAKuC,EAAI3B,SAEnC,OAAkB,IAAd4B,EACKF,EAGG,OAARA,GAAgBE,EAAY7B,EAAQX,EAAKsC,EAAI1B,SACxC2B,EAGFD,GACN,KACJ,CAoE0BG,CAAgBzC,GAEvC,GAAIoC,EAAgB,CAClB,MAAMM,EApIZ,SAAqBC,GACnB,IAAItC,EACAuC,EAE6B,iBAAtBD,EAAatC,MAEtBA,EAAOE,KAAKsC,UAAUF,EAAatC,MACnCuC,EAAc,qBAGdvC,EAAOsC,EAAatC,KACpBuC,EAAc,cAGhB,MAAMjB,OAAEA,GAAWgB,EAEnB,WAAWG,SAASzC,EAAM,CACxB0C,WACE,eAAgBpB,EAAOiB,aAAeA,GAClCjB,EAAOoB,SAAW,IAExBC,OAAQrB,EAAOqB,QAAU,KAE5B,CA6GsBC,CAAYb,GAEzBA,EAAeR,QACjBA,EAAQc,GAERP,EAAO,IAAI7B,MAAM,mBAEpB,MACCsB,GACD"}