Skip to content

Commit

Permalink
Merge pull request #448 from leggsimon/leggsimon/match-json-bodies
Browse files Browse the repository at this point in the history
Match json bodies
  • Loading branch information
wheresrhys authored Sep 29, 2019
2 parents e54afac + ba802d0 commit df2465c
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 4 deletions.
16 changes: 12 additions & 4 deletions docs/_api-mocking/mock_options.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: "options"
title: 'options'
position: 1.3
description: |-
An object containing further options for configuring mocking behaviour
Expand Down Expand Up @@ -28,23 +28,31 @@ parameters:
- Headers
content: |-
Match only requests that have these headers set
examples:
examples:
- |-
{"Accepts": "text/html"}
- name: body
types:
- Object
content: |-
Match only requests that send a JSON body with the exact structure and properties as the one provided here.
examples:
- |-
{ "key1": "value1", "key2": "value2" }
- name: query
types:
- Object
content: |-
Match only requests that have these query parameters set (in any order)
examples:
examples:
- |-
{"q": "cute+kittenz", "format": "gif"}
- name: params
types:
- Object
content: |-
When the `express:` keyword is used in a string matcher, match only requests with these express parameters
examples:
examples:
- |-
{"section": "feed", "user": "geoff"}
- name: functionMatcher
Expand Down
5 changes: 5 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
"babel-polyfill": "^6.26.0",
"core-js": "^2.6.9",
"glob-to-regexp": "^0.4.0",
"lodash.isequal": "^4.5.0",
"path-to-regexp": "^2.2.1",
"whatwg-url": "^6.5.0"
},
Expand Down
19 changes: 19 additions & 0 deletions src/lib/generate-matcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const {
getQuery,
normalizeUrl
} = require('./request-utils');
const isEqual = require('lodash.isequal');

const stringMatchers = {
begin: targetString => url => url.indexOf(targetString) === 0,
Expand Down Expand Up @@ -72,6 +73,23 @@ const getParamsMatcher = ({ params: expectedParams, matcher }) => {
const getFunctionMatcher = ({ matcher, functionMatcher = () => true }) =>
typeof matcher === 'function' ? matcher : functionMatcher;

const getBodyMatcher = ({ body: expectedBody }) => {
return (url, { body, method = 'get' }) => {
if (method.toLowerCase() === 'get') {
// GET requests don’t send a body so the body matcher should be ignored for them
return true;
}

let sentBody;

try {
sentBody = JSON.parse(body);
} catch (_) {}

return sentBody && isEqual(sentBody, expectedBody);
};
};

const getUrlMatcher = route => {
const { matcher, query } = route;

Expand Down Expand Up @@ -117,6 +135,7 @@ module.exports = route => {
route.method && getMethodMatcher(route),
route.headers && getHeaderMatcher(route),
route.params && getParamsMatcher(route),
route.body && getBodyMatcher(route),
getFunctionMatcher(route),
getUrlMatcher(route)
].filter(matcher => !!matcher);
Expand Down
85 changes: 85 additions & 0 deletions test/specs/routing.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,91 @@ module.exports = fetchMock => {
});
});
});

describe('body matching', () => {
it('should not match if no body provided in request', async () => {
fm.mock('http://it.at.there/', 200, { body: { foo: 'bar' } }).catch();

await fm.fetchHandler('http://it.at.there/', {
method: 'POST'
});
expect(fm.calls(true).length).to.equal(0);
});

it('should match if no content type is specified', async () => {
fm.mock('http://it.at.there/', 200, { body: { foo: 'bar' } }).catch();

await fm.fetchHandler('http://it.at.there/', {
method: 'POST',
body: JSON.stringify({ foo: 'bar' })
});
expect(fm.calls(true).length).to.equal(1);
});

it('should match if body sent matches expected body', async () => {
fm.mock('http://it.at.there/', 200, { body: { foo: 'bar' } }).catch();

await fm.fetchHandler('http://it.at.there/', {
method: 'POST',
body: JSON.stringify({ foo: 'bar' }),
headers: { 'Content-Type': 'application/json' }
});
expect(fm.calls(true).length).to.equal(1);
});

it('should not match if body sent doesn’t match expected body', async () => {
fm.mock('http://it.at.there/', 200, { body: { foo: 'bar' } }).catch();

await fm.fetchHandler('http://it.at.there/', {
method: 'POST',
body: JSON.stringify({ foo: 'woah!!!' }),
headers: { 'Content-Type': 'application/json' }
});
expect(fm.calls(true).length).to.equal(0);
});

it('should not match if body sent isn’t JSON', async () => {
fm.mock('http://it.at.there/', 200, { body: { foo: 'bar' } }).catch();

await fm.fetchHandler('http://it.at.there/', {
method: 'POST',
body: new ArrayBuffer(8),
headers: { 'Content-Type': 'application/json' }
});
expect(fm.calls(true).length).to.equal(0);
});

it('should ignore the order of the keys in the body', async () => {
fm.mock('http://it.at.there/', 200, {
body: {
foo: 'bar',
baz: 'qux'
}
}).catch();

await fm.fetchHandler('http://it.at.there/', {
method: 'POST',
body: JSON.stringify({
baz: 'qux',
foo: 'bar'
}),
headers: { 'Content-Type': 'application/json' }
});
expect(fm.calls(true).length).to.equal(1);
});

it('should ignore the body option matcher if request was GET', async () => {
fm.mock('http://it.at.there/', 200, {
body: {
foo: 'bar',
baz: 'qux'
}
}).catch();

await fm.fetchHandler('http://it.at.there/');
expect(fm.calls(true).length).to.equal(1);
});
});
});

describe('multiple routes', () => {
Expand Down

0 comments on commit df2465c

Please sign in to comment.