Skip to content

Commit d7cce99

Browse files
authored
Merge pull request #3 from nutgaard/feat/issue-2
feat(middleware): Added delay and failureRate middleware, and combine…
2 parents 83a3f16 + 51d0e80 commit d7cce99

File tree

5 files changed

+156
-10
lines changed

5 files changed

+156
-10
lines changed

README.md

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,27 @@ npm install yet-another-fetch-mock --save-dev
1414
### Setup
1515

1616
```javascript
17+
const loggingMiddleware: Middleware = (request, response) => {
18+
console.log('response', response);
19+
return response;
20+
}
21+
22+
1723
const mock = FetchMock.configure({
18-
enableFallback: true,
19-
middleware: (requestArgs, response) => {
20-
console.log('response', response);
21-
return response;
22-
}
24+
enableFallback: true, // default: true
25+
middleware: loggingMiddleware // default: (req, resp) => resp
26+
});
27+
28+
29+
const delayedErrorMock = FetchMock.configure({
30+
middleware: MiddlewareUtils.combine(
31+
MiddlewareUtils.delayMiddleware(200),
32+
MiddlewareUtils.failurerateMiddleware(0.2)
33+
)
2334
});
2435
```
2536

37+
2638
### Examples
2739
```typescript
2840
mock.get('/my-url', { key: 'value' }); // Returns the object as the json-response

src/middleware-utils.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { HandlerArgument, Middleware, ResponseData } from './types';
2+
3+
const defaultFailure: ResponseData = {
4+
status: 500,
5+
statusText: 'Internal server error'
6+
};
7+
8+
export default class MiddlewareUtils {
9+
static combine(...middlewares: Middleware[]): Middleware {
10+
return (request: HandlerArgument, response: ResponseData) => {
11+
return middlewares.reduce(
12+
(currentResponse, middleware) => currentResponse.then(resp => middleware(request, resp)),
13+
Promise.resolve(response)
14+
);
15+
};
16+
}
17+
18+
static delayMiddleware(delayMs: number): Middleware {
19+
return (request: HandlerArgument, response: ResponseData) => {
20+
return new Promise<ResponseData>(resolve => {
21+
setTimeout(() => resolve(response), delayMs);
22+
});
23+
};
24+
}
25+
26+
static failurerateMiddleware(
27+
probabilityOfFailure: number,
28+
failure: ResponseData = defaultFailure
29+
): Middleware {
30+
return (request: HandlerArgument, response: ResponseData) => {
31+
return new Promise<ResponseData>(resolve => {
32+
const rnd = Math.random();
33+
34+
if (rnd < probabilityOfFailure) {
35+
resolve(failure);
36+
} else {
37+
resolve(response);
38+
}
39+
});
40+
};
41+
}
42+
}

src/types.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,12 @@ export interface Route {
4949
matcher: RouteMatcher;
5050
handler: MockHandlerFunction;
5151
}
52+
53+
export type Middleware = (
54+
request: HandlerArgument,
55+
response: ResponseData
56+
) => ResponseData | Promise<ResponseData>;
5257
export interface Configuration {
5358
enableFallback: boolean;
54-
middleware: (
55-
request: HandlerArgument,
56-
response: ResponseData
57-
) => ResponseData | Promise<ResponseData>;
59+
middleware: Middleware;
5860
}

test/math-mock.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
export default class MathMock {
2+
private static oldMath = global.Math;
3+
private static newMath = Object.create(MathMock.oldMath);
4+
5+
static setup() {
6+
global.Math = MathMock.newMath;
7+
}
8+
9+
static teardown() {
10+
global.Math = MathMock.oldMath;
11+
}
12+
13+
static fixRandom(num: number) {
14+
MathMock.newMath.random = () => num;
15+
}
16+
}

test/yet-another-fetch-mock.test.ts

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ import 'isomorphic-fetch';
22
import MatcherUtils from './../src/matcher-utils';
33
import ResponseUtils from './../src/response-utils';
44
import FetchMock from '../src/yet-another-fetch-mock';
5-
import { HandlerArgument, RequestUrl } from '../src/types';
5+
import { HandlerArgument, RequestUrl, ResponseData } from '../src/types';
66
import { findBody, findPathParams } from '../src/internal-utils';
7+
import MiddlewareUtils from '../src/middleware-utils';
8+
import MathMock from './math-mock';
79

810
function fetchToJson(url: string, options?: RequestInit) {
911
return fetch(url, options).then(resp => resp.json());
@@ -240,3 +242,75 @@ describe('FetchMock', () => {
240242
});
241243
});
242244
});
245+
246+
describe('middleware-utils', () => {
247+
beforeAll(MathMock.setup);
248+
afterAll(MathMock.teardown);
249+
250+
it('should combine middlewares', done => {
251+
MathMock.fixRandom(0.2);
252+
const delay = jest.fn(MiddlewareUtils.delayMiddleware(100));
253+
const failure = jest.fn(MiddlewareUtils.failurerateMiddleware(0.3, { status: 1337 }));
254+
const startTime = new Date().getTime();
255+
256+
const combined = MiddlewareUtils.combine(delay, failure);
257+
const result = combined({} as HandlerArgument, 'data' as ResponseData);
258+
259+
(result as Promise<ResponseData>).then(res => {
260+
const endTime = new Date().getTime();
261+
expect(endTime - startTime).toBeGreaterThanOrEqual(100);
262+
263+
expect(res.status).toBe(1337);
264+
expect(delay).toHaveBeenCalledTimes(1);
265+
expect(failure).toHaveBeenCalledTimes(1);
266+
done();
267+
});
268+
});
269+
270+
it('should delay the response', done => {
271+
const delay = MiddlewareUtils.delayMiddleware(100);
272+
const result = delay({} as HandlerArgument, 'delayed' as ResponseData);
273+
const startTime = new Date().getTime();
274+
275+
(result as Promise<String>).then(res => {
276+
const endTime = new Date().getTime();
277+
278+
expect(endTime - startTime).toBeGreaterThanOrEqual(100);
279+
expect(res).toBe('delayed');
280+
done();
281+
});
282+
});
283+
284+
it('should have a random failure rate', done => {
285+
MathMock.fixRandom(0.2);
286+
const delay = MiddlewareUtils.failurerateMiddleware(0.3);
287+
const result = delay({} as HandlerArgument, 'normal-response' as ResponseData);
288+
289+
(result as Promise<ResponseData>).then(res => {
290+
expect(res.status).toBe(500);
291+
done();
292+
});
293+
});
294+
295+
it('should have a random failure rate2', done => {
296+
MathMock.fixRandom(0.4);
297+
const delay = MiddlewareUtils.failurerateMiddleware(0.3);
298+
const result = delay({} as HandlerArgument, 'normal-response' as ResponseData);
299+
300+
(result as Promise<String>).then(res => {
301+
expect(res).toBe('normal-response');
302+
done();
303+
});
304+
});
305+
306+
it('should support custom error response', done => {
307+
MathMock.fixRandom(0.2);
308+
const delay = MiddlewareUtils.failurerateMiddleware(0.3, { status: 1337 });
309+
const result = delay({} as HandlerArgument, 'normal-response' as ResponseData);
310+
311+
(result as Promise<ResponseData>).then(res => {
312+
expect(res.status).toBe(1337);
313+
done();
314+
});
315+
});
316+
});

0 commit comments

Comments
 (0)