From f41bd3fde8831b531c7ee70c2ba88bb4357a59d5 Mon Sep 17 00:00:00 2001 From: tenorok Date: Tue, 14 Jun 2022 21:54:07 +0300 Subject: [PATCH 1/4] feat: ignore URL params on matching --- src/utils/array.js | 8 -------- src/utils/faker.js | 21 ++++++++------------- src/utils/faker.test.js | 21 +++++++++++++++------ src/utils/url.js | 5 +++++ 4 files changed, 28 insertions(+), 27 deletions(-) delete mode 100644 src/utils/array.js diff --git a/src/utils/array.js b/src/utils/array.js deleted file mode 100644 index a366eef..0000000 --- a/src/utils/array.js +++ /dev/null @@ -1,8 +0,0 @@ -export function arrayEquals(a, b) { - return ( - Array.isArray(a) && - Array.isArray(b) && - a.length === b.length && - a.every((val, index) => val === b[index]) - ); -} diff --git a/src/utils/faker.js b/src/utils/faker.js index f75fe75..2566407 100644 --- a/src/utils/faker.js +++ b/src/utils/faker.js @@ -8,7 +8,6 @@ import { getResponseHeaderMap, defaultResponseHeaders, } from './headers'; -import { arrayEquals } from './array'; import { getNormalizedUrl } from './url'; let global = @@ -34,9 +33,9 @@ export class Faker { getRequests = () => Object.values(this.requestMap); - getKey = (url = '', searchParamKeys = [], method = 'GET') => + getKey = (url = '', method = 'GET') => url && method - ? [url, ...searchParamKeys, method.toLowerCase()].join('_') + ? [url, method.toLowerCase()].join('_') : ''; makeInitialRequestMap = (requests) => { @@ -51,12 +50,9 @@ export class Faker { }; add = (request) => { - const { path, searchParamKeys } = getNormalizedUrl(request.url); - const key = this.getKey(path, searchParamKeys, request.method); + const key = this.getKey(request.url, request.method); this.requestMap[key] = { ...request, - path, - searchParamKeys, method: request.method || 'GET', status: request.status || 200, delay: request.delay || 0, @@ -66,8 +62,7 @@ export class Faker { update = (item, fieldKey, value) => { const { url, method } = item; - const { path, searchParamKeys } = getNormalizedUrl(url); - const itemKey = this.getKey(path, searchParamKeys, method); + const itemKey = this.getKey(url, method); if ( // eslint-disable-next-line no-prototype-builtins @@ -80,17 +75,17 @@ export class Faker { }; matchMock = (url, method = 'GET') => { - const { path, searchParamKeys } = getNormalizedUrl(url); + const { fullUrl } = getNormalizedUrl(url); for (let key in this.requestMap) { const { url: requestUrl, method: requestMethod } = this.requestMap[key]; - const { path: requestPath, searchParamKeys: requestSearchKeys } = + const { fullUrlEscaped } = getNormalizedUrl(requestUrl); + if ( - match(requestPath)(path) && + match(fullUrlEscaped)(fullUrl) && method == requestMethod && - arrayEquals(searchParamKeys, requestSearchKeys) && !this.requestMap[key].skip ) { return this.requestMap[key]; diff --git a/src/utils/faker.test.js b/src/utils/faker.test.js index 27db021..3e6566b 100644 --- a/src/utils/faker.test.js +++ b/src/utils/faker.test.js @@ -8,14 +8,10 @@ describe('Faker', () => { const actual = faker.getKey('', [], ''); expect(actual).toEqual(''); }); - it('should return a string binding url and method with underscore if searchParamKeys is empty', () => { - const actual = faker.getKey('google.com', [], 'GET'); + it('should return a string binding url and method with underscore', () => { + const actual = faker.getKey('google.com', 'GET'); expect(actual).toEqual('google.com_get'); }); - it('should return a string binding url, search params keys, and method with underscore', () => { - const actual = faker.getKey('google.com', ['all', 'only'], 'GET'); - expect(actual).toEqual('google.com_all_only_get'); - }); }); describe('makeInitialRequestMap', () => { const faker = new Faker(); @@ -69,6 +65,13 @@ describe('Faker', () => { response: {}, delay: 0, }, + { + url: 'http://request.com?a=1&b=2', + method: 'GET', + status: 200, + response: {}, + delay: 0, + }, ]; faker.makeInitialRequestMap(requests); @@ -88,6 +91,12 @@ describe('Faker', () => { expect(actual.method).toEqual(requests[2].method); expect(actual.skip).toEqual(false); }); + it('should return request if url matches with the query parameters', () => { + const actual = faker.matchMock('http://request.com?a=1&b=2', 'GET'); + expect(actual.url).toEqual(requests[3].url); + expect(actual.method).toEqual(requests[3].method); + expect(actual.skip).toEqual(false); + }); }); describe('restore', () => { diff --git a/src/utils/url.js b/src/utils/url.js index 9a6fe38..2f538dd 100644 --- a/src/utils/url.js +++ b/src/utils/url.js @@ -14,8 +14,13 @@ export const getNormalizedUrl = (rawUrl = '') => { searchParamKeys.push(key); } } + + const searchEscaped = url.search ? '\\' + url.search : ''; + return { path: url.host + url.pathname, searchParamKeys, + fullUrl: url.host + url.pathname + url.search, + fullUrlEscaped: url.host + url.pathname + searchEscaped, }; }; From 185395a0c21c772fdb5db838811daf64a439f559 Mon Sep 17 00:00:00 2001 From: tenorok Date: Tue, 14 Jun 2022 21:54:30 +0300 Subject: [PATCH 2/4] feat: support story options --- src/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/index.js b/src/index.js index ec087d3..45aea16 100644 --- a/src/index.js +++ b/src/index.js @@ -7,9 +7,9 @@ export default makeDecorator({ parameterName: 'mockData', // This means don't run this decorator if the notes decorator is not set skipIfNoParametersOrOptions: true, - wrapper: (getStory, context, { parameters }) => { + wrapper: (getStory, context, { options = [], parameters = [] }) => { const channel = addons.getChannel(); - faker.makeInitialRequestMap(parameters); + faker.makeInitialRequestMap(options.concat(parameters)); // Our simple API above simply sets the notes parameter to a string, // which we send to the channel From 15b0494f350b57c852168958f1d87ff0c8d91f15 Mon Sep 17 00:00:00 2001 From: tenorok Date: Tue, 14 Jun 2022 21:57:55 +0300 Subject: [PATCH 3/4] doc: update readme about url format --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bd2cf5d..46f57dd 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ There are few packages those help the developers to mock the backend requests wh | Property | Description | Required | Default | | ---------- | :------------------------------------------------------------------------------------------ | :------- | :------ | -| `url` | Supports both **named parameters** (`/:foo/:bar`) and **query parameters**.(`/foo?bar=true`) | Y | - | +| `url` | Supports both **named parameters** (`/:foo/:bar`) and **query parameters** (`/foo?bar=true`) in [path-to-regexp](https://github.com/pillarjs/path-to-regexp/blob/master/Readme.md) format. | Y | - | | `method` | Supports `GET`, `POST`, `PUT`, `PATCH` and `DELETE` methods. | - | `GET` | | `status` | All possible HTTP status codes. | - | `200` | | `response` | JSON format or function.

Response function is a function that contains request object as a parameter. See the **Custom Response** section for example. | Y | - | @@ -155,7 +155,7 @@ Default.parameters = { if (searchParams.id == 1) { return { data: 'Custom data for id 1', - }; + }; } else if (body.name === 'mock') { return { data: 'Custom data for name mock', From fb4f56d1b524a81bffd6f46fc3770b2e58c5a409 Mon Sep 17 00:00:00 2001 From: tenorok Date: Wed, 15 Jun 2022 10:38:34 +0300 Subject: [PATCH 4/4] chore: base types for typescript --- index.d.ts | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/index.d.ts b/index.d.ts index ffd8f5f..7d629ba 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,3 +1,21 @@ -declare const withMock: (storyFn: any) => any; +declare const withMock: (options: RequestInfo[]) => any; export default withMock; + +export interface RequestInfo { + url: string; + response: object | ((request: ResponseRequestParam) => object); + /** @default GET */ + method?: string; + /** @default 200 */ + status?: number; + /** @default 0 */ + delay?: number; +} + +export interface ResponseRequestParam { + method: string; + url: url; + body: RequestInit['body']; + searchParams?: Record; +}