Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix params comparisons for post and put requests - Breaking Change #387

Merged
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,9 +226,11 @@ mock
.onPost(
"/product",
{ id: 1 },
expect.objectContaining({
Authorization: expect.stringMatching(/^Basic /),
})
{
headers: expect.objectContaining({
Authorization: expect.stringMatching(/^Basic /),
})
}
)
.reply(204);
```
Expand Down
41 changes: 36 additions & 5 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,15 +79,46 @@ MockAdapter.prototype.reset = reset;
MockAdapter.prototype.resetHandlers = resetHandlers;
MockAdapter.prototype.resetHistory = resetHistory;

var methodsWithConfigsAsSecondArg = ["any", "get", "delete", "head", "options"];
function convertDataAndConfigToConfig (method, data, config) {
if (methodsWithConfigsAsSecondArg.includes(method)) {
return validateconfig(method, data || {});
} else {
return validateconfig(method, Object.assign({}, config || {}, { data: data }));
marcbachmann marked this conversation as resolved.
Show resolved Hide resolved
}
}

var allowedConfigs = ['headers', 'params', 'data'];
function validateconfig (method, config) {
for (var key in config) {
if (!allowedConfigs.includes(key)) {
throw new Error(
'Invalid config attribute ' +
key +
' provided to ' +
toMethodName(method) +
'. Config: ' +
JSON.stringify(config)
);
}
}

return config;
}

function toMethodName (method) {
return "on" + method.charAt(0).toUpperCase() + method.slice(1);
}

VERBS.concat("any").forEach(function (method) {
var methodName = "on" + method.charAt(0).toUpperCase() + method.slice(1);
MockAdapter.prototype[methodName] = function (matcher, body, requestHeaders) {
MockAdapter.prototype[toMethodName(method)] = function (matcher, data, config) {
var _this = this;
var matcher = matcher === undefined ? /.*/ : matcher;
var delay;
var paramsAndBody = convertDataAndConfigToConfig(method, data, config);

function reply(code, response, headers) {
var handler = [matcher, body, requestHeaders, code, response, headers, false, delay];
var handler = [matcher, paramsAndBody, paramsAndBody.headers, code, response, headers, false, delay];
addHandler(method, _this.handlers, handler);
return _this;
}
Expand All @@ -100,7 +131,7 @@ VERBS.concat("any").forEach(function (method) {
}

function replyOnce(code, response, headers) {
var handler = [matcher, body, requestHeaders, code, response, headers, true, delay];
var handler = [matcher, paramsAndBody, paramsAndBody.headers, code, response, headers, true, delay];
addHandler(method, _this.handlers, handler);
return _this;
}
Expand All @@ -113,7 +144,7 @@ VERBS.concat("any").forEach(function (method) {
withDelayInMs: withDelayInMs,

passThrough: function passThrough() {
var handler = [matcher, body];
var handler = [matcher, paramsAndBody];
addHandler(method, _this.handlers, handler);
return _this;
},
Expand Down
32 changes: 12 additions & 20 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,20 +48,18 @@ function findHandler(
baseURL
) {
return find(handlers[method.toLowerCase()], function (handler) {
var matchesUrl = false;
if (typeof handler[0] === "string") {
return (
(isUrlMatching(url, handler[0]) ||
isUrlMatching(combineUrls(baseURL, url), handler[0])) &&
isBodyOrParametersMatching(method, body, parameters, handler[1]) &&
isObjectMatching(headers, handler[2])
);
matchesUrl = isUrlMatching(url, handler[0]) ||
isUrlMatching(combineUrls(baseURL, url), handler[0]);
} else if (handler[0] instanceof RegExp) {
return (
(handler[0].test(url) || handler[0].test(combineUrls(baseURL, url))) &&
isBodyOrParametersMatching(method, body, parameters, handler[1]) &&
isObjectMatching(headers, handler[2])
);
matchesUrl = handler[0].test(url) ||
handler[0].test(combineUrls(baseURL, url));
}

return matchesUrl &&
isBodyOrParametersMatching(body, parameters, handler[1]) &&
isObjectMatching(headers, handler[2]);
});
}

Expand All @@ -71,15 +69,9 @@ function isUrlMatching(url, required) {
return noSlashUrl === noSlashRequired;
}

function isBodyOrParametersMatching(method, body, parameters, required) {
var allowedParamsMethods = ["delete", "get", "head", "options"];
if (allowedParamsMethods.indexOf(method.toLowerCase()) >= 0) {
var data = required ? required.data : undefined;
var params = required ? required.params : undefined;
return isObjectMatching(parameters, params) && isBodyMatching(body, data);
} else {
return isBodyMatching(body, required);
}
function isBodyOrParametersMatching(body, parameters, required) {
return isObjectMatching(parameters, required && required.params) &&
isBodyMatching(body, required && required.data);
}

function isObjectMatching(actual, expected) {
Expand Down
52 changes: 29 additions & 23 deletions test/basics.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ describe("MockAdapter basics", function () {
.reply(200);

return instance
.get("/withParams", { params: { bar: "foo", foo: "bar" }, in: true })
.get("/withParams", { params: { bar: "foo", foo: "bar" } })
.then(function (response) {
expect(response.status).to.equal(200);
});
Expand All @@ -153,7 +153,7 @@ describe("MockAdapter basics", function () {
.reply(200);

return instance
.delete("/withParams", { params: { bar: "foo", foo: "bar" }, in: true })
.delete("/withParams", { params: { bar: "foo", foo: "bar" } })
.then(function (response) {
expect(response.status).to.equal(200);
});
Expand All @@ -177,33 +177,33 @@ describe("MockAdapter basics", function () {
.reply(200);

return instance
.head("/withParams", { params: { bar: "foo", foo: "bar" }, in: true })
.head("/withParams", { params: { bar: "foo", foo: "bar" } })
.then(function (response) {
expect(response.status).to.equal(200);
});
});

it("can't pass query params for post to match to a handler", function () {
it("can pass query params for post to match to a handler", function () {
mock
.onPost("/withParams", { params: { foo: "bar", bar: "foo" } })
.onPost("/withParams", undefined, { params: { foo: "bar", bar: "foo" } })
.reply(200);

return instance
.post("/withParams", { params: { foo: "bar", bar: "foo" }, in: true })
.catch(function (error) {
expect(error.response.status).to.equal(404);
.post("/withParams", { some: 'body' }, { params: { foo: "bar", bar: "foo" } })
.then(function (response) {
expect(response.status).to.equal(200);
});
});

it("can't pass query params for put to match to a handler", function () {
it("can pass query params for put to match to a handler", function () {
mock
.onPut("/withParams", { params: { foo: "bar", bar: "foo" } })
.onPut("/withParams", undefined, { params: { foo: "bar", bar: "foo" } })
.reply(200);

return instance
.put("/withParams", { params: { bar: "foo", foo: "bar" }, in: true })
.catch(function (error) {
expect(error.response.status).to.equal(404);
.put("/withParams", { some: 'body' }, { params: { bar: "foo", foo: "bar" } })
.then(function (response) {
expect(response.status).to.equal(200);
});
});

Expand All @@ -221,7 +221,7 @@ describe("MockAdapter basics", function () {
});
});

it("does not match when parameters are wrong", function () {
it("does not match when params are wrong", function () {
mock
.onGet("/withParams", { params: { foo: "bar", bar: "foo" } })
.reply(200);
Expand All @@ -232,7 +232,7 @@ describe("MockAdapter basics", function () {
});
});

it("does not match when parameters are missing", function () {
it("does not match when params are missing", function () {
mock
.onGet("/withParams", { params: { foo: "bar", bar: "foo" } })
.reply(200);
Expand All @@ -241,7 +241,7 @@ describe("MockAdapter basics", function () {
});
});

it("matches when parameters were not expected", function () {
it("matches when params were not expected", function () {
mock.onGet("/withParams").reply(200);
return instance
.get("/withParams", { params: { foo: "bar", bar: "foo" } })
Expand All @@ -251,18 +251,18 @@ describe("MockAdapter basics", function () {
});

it("can pass a body to match to a handler", function () {
mock.onPost("/withBody", { body: { is: "passed" }, in: true }).reply(200);
mock.onPost("/withBody", { somecontent: { is: "passed" } }).reply(200);

return instance
.post("/withBody", { body: { is: "passed" }, in: true })
.post("/withBody", { somecontent: { is: "passed" } })
.then(function (response) {
expect(response.status).to.equal(200);
});
});

it("does not match when body is wrong", function () {
var body = { body: { is: "passed" }, in: true };
mock.onPatch("/wrongObjBody", body).reply(200);
var matcher = { somecontent: { is: "passed" } };
mock.onPatch("/wrongObjBody", matcher).reply(200);

return instance
.patch("/wrongObjBody", { wrong: "body" })
Expand Down Expand Up @@ -294,18 +294,24 @@ describe("MockAdapter basics", function () {
"Header-test": "test-header",
};

mock.onPost("/withHeaders", undefined, headers).reply(200);
mock.onPost("/withHeaders", undefined, { headers: headers }).reply(200);

return instance
.post("/withHeaders", undefined, { headers: headers })
.then(function (response) {
expect(response.status).to.equal(200);

return instance
.post("/withHeaders", undefined, { headers: { Accept: 'no-match' } })
.catch(function (err) {
expect(err.response.status).to.equal(404);
});
});
});

it("does not match when request header is wrong", function () {
var headers = { "Header-test": "test-header" };
mock.onPatch("/wrongObjHeader", undefined, headers).reply(200);
mock.onPatch("/wrongObjHeader", undefined, { headers: headers }).reply(200);

return instance
.patch("/wrongObjHeader", undefined, {
Expand Down Expand Up @@ -850,7 +856,7 @@ describe("MockAdapter basics", function () {
});
});

it("allows overwriting mocks with parameters", function () {
it("allows overwriting mocks with params", function () {
mock
.onGet("/users", { params: { searchText: "John" } })
.reply(500)
Expand Down
10 changes: 9 additions & 1 deletion test/on_any.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ describe("MockAdapter onAny", function () {
{ object: { with: { deep: "property" } }, array: ["1", "abc"] },
"a",
];
mock.onAny("/anyWithBody", body).reply(200);
mock.onAny("/anyWithBody", { data: body }).reply(200);

return instance
.put("/anyWithBody", body)
Expand All @@ -54,6 +54,14 @@ describe("MockAdapter onAny", function () {
})
.then(function (response) {
expect(response.status).to.equal(200);

return instance.post("/anyWithBody")
.then(function () {
throw new Error("should not get here");
})
.catch(function (err) {
expect(err.response.status).to.equal(404);
});
});
});

Expand Down
14 changes: 7 additions & 7 deletions test/utils.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,17 +85,17 @@ describe("utility functions", function () {

context("isBodyOrParametersMatching", function() {
it('delete has params only', function () {
expect(isBodyOrParametersMatching('delete', null, { 'a': 2 }, { 'params': { 'a': 2 } } )).to.be.true;
expect(isBodyOrParametersMatching('delete', null, { 'a': 2 }, { 'params': { 'b': 2 } } )).to.be.false;
expect(isBodyOrParametersMatching(null, { 'a': 2 }, { 'params': { 'a': 2 } } )).to.be.true;
expect(isBodyOrParametersMatching(null, { 'a': 2 }, { 'params': { 'b': 2 } } )).to.be.false;
});
it('delete has data only', function () {
expect(isBodyOrParametersMatching('delete', { 'x': 1 }, null, { 'data': { 'x': 1 } })).to.be.true;
expect(isBodyOrParametersMatching('delete', { 'x': 1 }, null, { 'data': { 'y': 1 } })).to.be.false;
expect(isBodyOrParametersMatching({ 'x': 1 }, null, { 'data': { 'x': 1 } })).to.be.true;
expect(isBodyOrParametersMatching({ 'x': 1 }, null, { 'data': { 'y': 1 } })).to.be.false;
});
it('delete has body and params', function () {
expect(isBodyOrParametersMatching('delete', { 'x': 1 }, { 'a': 2 }, { 'data': { 'x': 1 }, 'params': { 'a': 2 } })).to.be.true;
expect(isBodyOrParametersMatching('delete', { 'x': 1 }, { 'a': 2 }, { 'data': { 'x': 1 }, 'params': { 'b': 2 } })).to.be.false;
expect(isBodyOrParametersMatching('delete', { 'x': 1 }, { 'a': 2 }, { 'data': { 'y': 1 }, 'params': { 'a': 2 } })).to.be.false;
expect(isBodyOrParametersMatching({ 'x': 1 }, { 'a': 2 }, { 'data': { 'x': 1 }, 'params': { 'a': 2 } })).to.be.true;
expect(isBodyOrParametersMatching({ 'x': 1 }, { 'a': 2 }, { 'data': { 'x': 1 }, 'params': { 'b': 2 } })).to.be.false;
expect(isBodyOrParametersMatching({ 'x': 1 }, { 'a': 2 }, { 'data': { 'y': 1 }, 'params': { 'a': 2 } })).to.be.false;
});
});
});
43 changes: 26 additions & 17 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,25 +33,34 @@ interface AsymmetricMatcher {
asymmetricMatch: Function;
}

interface RequestDataMatcher {
[index: string]: any;
params?: {
[index: string]: any;
};
interface ParamsMatcher {
[param: string]: any;
}

interface HeadersMatcher {
[header: string]: string;
}

type UrlMatcher = string | RegExp;
type AsymmetricParamsMatcher = AsymmetricMatcher | ParamsMatcher;
type AsymmetricHeadersMatcher = AsymmetricMatcher | HeadersMatcher;
type AsymmetricRequestDataMatcher = AsymmetricMatcher | any;

type AsymmetricRequestDataMatcher = AsymmetricMatcher | RequestDataMatcher;
interface ConfigMatcher {
params?: AsymmetricParamsMatcher;
headers?: AsymmetricHeadersMatcher;
data?: AsymmetricRequestDataMatcher;
}

type RequestMatcherFunc = (
matcher?: string | RegExp,
body?: string | AsymmetricRequestDataMatcher,
headers?: AsymmetricHeadersMatcher
matcher?: UrlMatcher,
body?: AsymmetricRequestDataMatcher,
config?: ConfigMatcher
) => MockAdapter.RequestHandler;

type NoBodyRequestMatcherFunc = (
matcher?: UrlMatcher,
config?: ConfigMatcher
) => MockAdapter.RequestHandler;

declare class MockAdapter {
Expand All @@ -67,17 +76,17 @@ declare class MockAdapter {

history: { [method: string]: AxiosRequestConfig[] };

onGet: RequestMatcherFunc;
onList: NoBodyRequestMatcherFunc;
onOptions: NoBodyRequestMatcherFunc;
onAny: NoBodyRequestMatcherFunc;
onLink: NoBodyRequestMatcherFunc;
onUnlink: NoBodyRequestMatcherFunc;
marcbachmann marked this conversation as resolved.
Show resolved Hide resolved
onGet: NoBodyRequestMatcherFunc;
onHead: NoBodyRequestMatcherFunc;
onDelete: NoBodyRequestMatcherFunc;
onPost: RequestMatcherFunc;
onPut: RequestMatcherFunc;
onHead: RequestMatcherFunc;
onDelete: RequestMatcherFunc;
onPatch: RequestMatcherFunc;
onList: RequestMatcherFunc;
onOptions: RequestMatcherFunc;
onAny: RequestMatcherFunc;
onLink: RequestMatcherFunc;
onUnlink: RequestMatcherFunc;
}

export = MockAdapter;
Loading
Loading