axios-mockadptr 0.0.1-security → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of axios-mockadptr might be problematic. Click here for more details.

@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ const utils = require("./utils");
3
+
4
+ function passThroughRequest (mockAdapter, config) {
5
+ // Axios v0.17 mutates the url to include the baseURL for non hostnames
6
+ // but does not remove the baseURL from the config
7
+ let baseURL = config.baseURL;
8
+ if (baseURL && !/^https?:/.test(baseURL)) {
9
+ baseURL = undefined;
10
+ }
11
+
12
+ // Axios pre 1.2
13
+ if (typeof mockAdapter.originalAdapter === "function") {
14
+ return mockAdapter.originalAdapter(config);
15
+ }
16
+
17
+ return mockAdapter.axiosInstanceWithoutInterceptors(Object.assign({}, config, {
18
+ baseURL,
19
+ // Use the original adapter, not the mock adapter
20
+ adapter: mockAdapter.originalAdapter,
21
+ // The request transformation runs on the original axios handler already
22
+ transformRequest: [],
23
+ transformResponse: []
24
+ }));
25
+ }
26
+
27
+ async function handleRequest(mockAdapter, config) {
28
+ let url = config.url || "";
29
+ // TODO we're not hitting this `if` in any of the tests, investigate
30
+ if (
31
+ config.baseURL &&
32
+ url.substr(0, config.baseURL.length) === config.baseURL
33
+ ) {
34
+ url = url.slice(config.baseURL.length);
35
+ }
36
+
37
+ delete config.adapter;
38
+ mockAdapter.history.push(config);
39
+
40
+ const handler = utils.findHandler(
41
+ mockAdapter.handlers,
42
+ config.method,
43
+ url,
44
+ config.data,
45
+ config.params,
46
+ (config.headers && config.headers.constructor.name === "AxiosHeaders")
47
+ ? Object.assign({}, config.headers.toJSON())
48
+ : config.headers,
49
+ config.baseURL
50
+ );
51
+
52
+ if (handler) {
53
+ if (handler.replyOnce) {
54
+ utils.purgeIfReplyOnce(mockAdapter, handler);
55
+ }
56
+
57
+ if (handler.passThrough) {
58
+ // passThrough handler
59
+ return passThroughRequest(mockAdapter, config);
60
+ } else {
61
+ return utils.settle(
62
+ config,
63
+ handler.response,
64
+ getEffectiveDelay(mockAdapter, handler)
65
+ );
66
+ }
67
+ } else {
68
+ // handler not found
69
+ switch (mockAdapter.onNoMatch) {
70
+ case "passthrough":
71
+ return passThroughRequest(mockAdapter, config);
72
+ case "throwException":
73
+ throw utils.createCouldNotFindMockError(config);
74
+ default:
75
+ return utils.settle(
76
+ config,
77
+ { status: 404 },
78
+ mockAdapter.delayResponse
79
+ );
80
+ }
81
+ }
82
+ }
83
+
84
+ function getEffectiveDelay(adapter, handler) {
85
+ return typeof handler.delay === "number" ? handler.delay : adapter.delayResponse;
86
+ }
87
+
88
+ module.exports = handleRequest;
package/src/index.js ADDED
@@ -0,0 +1,285 @@
1
+ "use strict";
2
+ const handleRequest = require("./handle_request");
3
+ const utils = require("./utils");
4
+
5
+ const VERBS = [
6
+ "get",
7
+ "post",
8
+ "head",
9
+ "delete",
10
+ "patch",
11
+ "put",
12
+ "options",
13
+ "list",
14
+ "link",
15
+ "unlink",
16
+ ];
17
+
18
+ function getVerbArray() {
19
+ const arr = [];
20
+ VERBS.forEach(function (verb) {
21
+ Object.defineProperty(arr, verb, {
22
+ get () {
23
+ return arr.filter(function (h) {
24
+ return !h.method || h.method === verb;
25
+ });
26
+ },
27
+ });
28
+ });
29
+ return arr;
30
+ }
31
+
32
+ class AxiosMockAdapter {
33
+ constructor (axiosInstance, options = {}) {
34
+ this.reset();
35
+
36
+ if (axiosInstance) {
37
+ this.axiosInstance = axiosInstance;
38
+ // Clone the axios instance to remove interceptors
39
+ // this is used for the passThrough mode with axios > 1.2
40
+ this.axiosInstanceWithoutInterceptors = axiosInstance.create
41
+ ? axiosInstance.create()
42
+ : undefined;
43
+
44
+ this.originalAdapter = axiosInstance.defaults.adapter;
45
+ this.delayResponse = options.delayResponse > 0 ? options.delayResponse : null;
46
+ this.onNoMatch = options.onNoMatch || null;
47
+ axiosInstance.defaults.adapter = this.adapter();
48
+ } else {
49
+ throw new Error("Please provide an instance of axios to mock");
50
+ }
51
+ }
52
+
53
+ adapter () {
54
+ return (config) => handleRequest(this, config);
55
+ }
56
+
57
+ restore () {
58
+ if (!this.axiosInstance) return;
59
+ this.axiosInstance.defaults.adapter = this.originalAdapter;
60
+ this.axiosInstance = undefined;
61
+ }
62
+
63
+ reset () {
64
+ this.resetHandlers();
65
+ this.resetHistory();
66
+ }
67
+
68
+ resetHandlers () {
69
+ if (this.handlers) this.handlers.length = 0;
70
+ else this.handlers = getVerbArray();
71
+ }
72
+
73
+ resetHistory () {
74
+ if (this.history) this.history.length = 0;
75
+ else this.history = getVerbArray();
76
+ }
77
+ }
78
+
79
+ const methodsWithConfigsAsSecondArg = ["any", "get", "delete", "head", "options"];
80
+ function convertDataAndConfigToConfig (method, data, config) {
81
+ if (methodsWithConfigsAsSecondArg.includes(method)) {
82
+ return validateconfig(method, data || {});
83
+ } else {
84
+ return validateconfig(method, Object.assign({}, config, { data: data }));
85
+ }
86
+ }
87
+
88
+ const allowedConfigProperties = ["headers", "params", "data"];
89
+ function validateconfig (method, config) {
90
+ for (const key in config) {
91
+ if (!allowedConfigProperties.includes(key)) {
92
+ throw new Error(
93
+ `Invalid config property ${
94
+ JSON.stringify(key)
95
+ } provided to ${
96
+ toMethodName(method)
97
+ }. Config: ${
98
+ JSON.stringify(config)}`
99
+ );
100
+ }
101
+ }
102
+
103
+ return config;
104
+ }
105
+
106
+ function toMethodName (method) {
107
+ return `on${method.charAt(0).toUpperCase()}${method.slice(1)}`;
108
+ }
109
+
110
+ VERBS.concat("any").forEach(function (method) {
111
+ AxiosMockAdapter.prototype[toMethodName(method)] = function (matcher, data, config) {
112
+ const self = this;
113
+ let delay;
114
+ matcher = matcher === undefined ? /.*/ : matcher;
115
+
116
+ const paramsAndBody = convertDataAndConfigToConfig(method, data, config);
117
+
118
+ function reply (code, response, headers) {
119
+ const handler = {
120
+ url: matcher,
121
+ method: method === "any" ? undefined : method,
122
+ params: paramsAndBody.params,
123
+ data: paramsAndBody.data,
124
+ headers: paramsAndBody.headers,
125
+ replyOnce: false,
126
+ delay,
127
+ response: typeof code === "function" ? code : [
128
+ code,
129
+ response,
130
+ headers
131
+ ]
132
+ };
133
+ addHandler(method, self.handlers, handler);
134
+ return self;
135
+ }
136
+
137
+ function withDelayInMs (_delay) {
138
+ delay = _delay;
139
+ const respond = requestApi.reply.bind(requestApi);
140
+ Object.assign(respond, requestApi);
141
+ return respond;
142
+ }
143
+
144
+ function replyOnce (code, response, headers) {
145
+ const handler = {
146
+ url: matcher,
147
+ method: method === "any" ? undefined : method,
148
+ params: paramsAndBody.params,
149
+ data: paramsAndBody.data,
150
+ headers: paramsAndBody.headers,
151
+ replyOnce: true,
152
+ delay: delay,
153
+ response: typeof code === "function" ? code : [
154
+ code,
155
+ response,
156
+ headers
157
+ ]
158
+ };
159
+ addHandler(method, self.handlers, handler);
160
+ return self;
161
+ }
162
+
163
+ const requestApi = {
164
+ reply,
165
+ replyOnce,
166
+ withDelayInMs,
167
+ passThrough () {
168
+ const handler = {
169
+ passThrough: true,
170
+ method: method === "any" ? undefined : method,
171
+ url: matcher,
172
+ params: paramsAndBody.params,
173
+ data: paramsAndBody.data,
174
+ headers: paramsAndBody.headers
175
+ };
176
+ addHandler(method, self.handlers, handler);
177
+ return self;
178
+ },
179
+ abortRequest () {
180
+ return reply(async function (config) {
181
+ throw utils.createAxiosError(
182
+ "Request aborted",
183
+ config,
184
+ undefined,
185
+ "ECONNABORTED"
186
+ );
187
+ });
188
+ },
189
+ abortRequestOnce () {
190
+ return replyOnce(async function (config) {
191
+ throw utils.createAxiosError(
192
+ "Request aborted",
193
+ config,
194
+ undefined,
195
+ "ECONNABORTED"
196
+ );
197
+ });
198
+ },
199
+
200
+ networkError () {
201
+ return reply(async function (config) {
202
+ throw utils.createAxiosError("Network Error", config);
203
+ });
204
+ },
205
+
206
+ networkErrorOnce () {
207
+ return replyOnce(async function (config) {
208
+ throw utils.createAxiosError("Network Error", config);
209
+ });
210
+ },
211
+
212
+ timeout () {
213
+ return reply(async function (config) {
214
+ throw utils.createAxiosError(
215
+ config.timeoutErrorMessage ||
216
+ `timeout of ${config.timeout }ms exceeded`,
217
+ config,
218
+ undefined,
219
+ config.transitional && config.transitional.clarifyTimeoutError
220
+ ? "ETIMEDOUT"
221
+ : "ECONNABORTED"
222
+ );
223
+ });
224
+ },
225
+
226
+ timeoutOnce () {
227
+ return replyOnce(async function (config) {
228
+ throw utils.createAxiosError(
229
+ config.timeoutErrorMessage ||
230
+ `timeout of ${config.timeout }ms exceeded`,
231
+ config,
232
+ undefined,
233
+ config.transitional && config.transitional.clarifyTimeoutError
234
+ ? "ETIMEDOUT"
235
+ : "ECONNABORTED"
236
+ );
237
+ });
238
+ },
239
+ };
240
+
241
+ return requestApi;
242
+ };
243
+ });
244
+
245
+ function findInHandlers (handlers, handler) {
246
+ let index = -1;
247
+ for (let i = 0; i < handlers.length; i += 1) {
248
+ const item = handlers[i];
249
+ const comparePaths =
250
+ item.url instanceof RegExp && handler.url instanceof RegExp
251
+ ? String(item.url) === String(handler.url)
252
+ : item.url === handler.url;
253
+
254
+ const isSame =
255
+ (!item.method || item.method === handler.method) &&
256
+ comparePaths &&
257
+ utils.isEqual(item.params, handler.params) &&
258
+ utils.isEqual(item.data, handler.data) &&
259
+ utils.isEqual(item.headers, handler.headers);
260
+
261
+ if (isSame && !item.replyOnce) {
262
+ index = i;
263
+ }
264
+ }
265
+ return index;
266
+ }
267
+
268
+ function addHandler (method, handlers, handler) {
269
+ if (method === "any") {
270
+ handlers.push(handler);
271
+ } else {
272
+ const indexOfExistingHandler = findInHandlers(handlers, handler);
273
+ // handler.replyOnce indicates that a handler only runs once.
274
+ // It's supported to register muliple ones like that without
275
+ // overwriting the previous one.
276
+ if (indexOfExistingHandler > -1 && !handler.replyOnce) {
277
+ handlers.splice(indexOfExistingHandler, 1, handler);
278
+ } else {
279
+ handlers.push(handler);
280
+ }
281
+ }
282
+ }
283
+
284
+ module.exports = AxiosMockAdapter;
285
+ module.exports.default = AxiosMockAdapter;
package/src/is_blob.js ADDED
@@ -0,0 +1,28 @@
1
+ /*!
2
+ * MIT License
3
+ *
4
+ * Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
7
+ * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
8
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
9
+ * persons to whom the Software is furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
12
+ * Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
15
+ * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
17
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
18
+ */
19
+
20
+ function isBlob(value) {
21
+ if (typeof Blob === "undefined") {
22
+ return false;
23
+ }
24
+
25
+ return value instanceof Blob || Object.prototype.toString.call(value) === "[object Blob]";
26
+ }
27
+
28
+ module.exports = isBlob;
package/src/utils.js ADDED
@@ -0,0 +1,224 @@
1
+ "use strict";
2
+ const axios = require("axios");
3
+ const isEqual = require("fast-deep-equal");
4
+ const isBuffer = require("is-buffer");
5
+ const isBlob = require("./is_blob");
6
+ const toString = Object.prototype.toString;
7
+
8
+ function find(array, predicate) {
9
+ const length = array.length;
10
+ for (let i = 0; i < length; i++) {
11
+ const value = array[i];
12
+ if (predicate(value)) return value;
13
+ }
14
+ }
15
+
16
+ function isFunction(val) {
17
+ return toString.call(val) === "[object Function]";
18
+ }
19
+
20
+ function isObjectOrArray(val) {
21
+ return val !== null && typeof val === "object";
22
+ }
23
+
24
+ function isStream(val) {
25
+ return isObjectOrArray(val) && isFunction(val.pipe);
26
+ }
27
+
28
+ function isArrayBuffer(val) {
29
+ return toString.call(val) === "[object ArrayBuffer]";
30
+ }
31
+
32
+ function combineUrls(baseURL, url) {
33
+ if (baseURL) {
34
+ return `${baseURL.replace(/\/+$/, "")}/${url.replace(/^\/+/, "")}`;
35
+ }
36
+
37
+ return url;
38
+ }
39
+
40
+ function findHandler(
41
+ handlers,
42
+ method,
43
+ url,
44
+ body,
45
+ parameters,
46
+ headers,
47
+ baseURL
48
+ ) {
49
+ return find(handlers[method.toLowerCase()], function (handler) {
50
+ let matchesUrl = false;
51
+ if (typeof handler.url === "string") {
52
+ matchesUrl = isUrlMatching(url, handler.url) ||
53
+ isUrlMatching(combineUrls(baseURL, url), handler.url);
54
+ } else if (handler.url instanceof RegExp) {
55
+ matchesUrl = handler.url.test(url) ||
56
+ handler.url.test(combineUrls(baseURL, url));
57
+ }
58
+
59
+ return matchesUrl &&
60
+ isBodyOrParametersMatching(body, parameters, handler) &&
61
+ isObjectMatching(headers, handler.headers);
62
+ });
63
+ }
64
+
65
+ function isUrlMatching(url, required) {
66
+ const noSlashUrl = url[0] === "/" ? url.substr(1) : url;
67
+ const noSlashRequired = required[0] === "/" ? required.substr(1) : required;
68
+ return noSlashUrl === noSlashRequired;
69
+ }
70
+
71
+ function isBodyOrParametersMatching(body, parameters, required) {
72
+ return isObjectMatching(parameters, required.params) &&
73
+ isBodyMatching(body, required.data);
74
+ }
75
+
76
+ function isObjectMatching(actual, expected) {
77
+ if (expected === undefined) return true;
78
+ if (typeof expected.asymmetricMatch === "function") {
79
+ return expected.asymmetricMatch(actual);
80
+ }
81
+ return isEqual(actual, expected);
82
+ }
83
+
84
+ function isBodyMatching(body, requiredBody) {
85
+ if (requiredBody === undefined) {
86
+ return true;
87
+ }
88
+ let parsedBody;
89
+ try {
90
+ parsedBody = JSON.parse(body);
91
+ } catch (_e) {}
92
+
93
+ return isObjectMatching(parsedBody ? parsedBody : body, requiredBody);
94
+ }
95
+
96
+ function purgeIfReplyOnce(mock, handler) {
97
+ const index = mock.handlers.indexOf(handler);
98
+ if (index > -1) {
99
+ mock.handlers.splice(index, 1);
100
+ }
101
+ }
102
+
103
+ function transformRequest(data) {
104
+ if (
105
+ isArrayBuffer(data) ||
106
+ isBuffer(data) ||
107
+ isStream(data) ||
108
+ isBlob(data)
109
+ ) {
110
+ return data;
111
+ }
112
+
113
+ // Object and Array: returns a deep copy
114
+ if (isObjectOrArray(data)) {
115
+ return JSON.parse(JSON.stringify(data));
116
+ }
117
+
118
+ // for primitives like string, undefined, null, number
119
+ return data;
120
+ }
121
+
122
+ async function makeResponse(result, config) {
123
+ if (typeof result === "function") result = await result(config);
124
+
125
+ const status = result.status || result[0];
126
+ const data = transformRequest(result.data || result[1]);
127
+ const headers = result.headers || result[2];
128
+ if (result.config) config = result.config;
129
+
130
+ return {
131
+ status,
132
+ data,
133
+ headers,
134
+ config,
135
+ request: { responseURL: config.url }
136
+ };
137
+ }
138
+
139
+ async function settle(config, response, delay) {
140
+ if (delay > 0) await new Promise(resolve => setTimeout(resolve, delay));
141
+
142
+ const result = await makeResponse(response, config);
143
+
144
+ if (
145
+ !result.config.validateStatus ||
146
+ result.config.validateStatus(result.status)
147
+ ) {
148
+ return result;
149
+ } else {
150
+ throw createAxiosError(
151
+ `Request failed with status code ${result.status}`,
152
+ result.config,
153
+ result
154
+ );
155
+ }
156
+ }
157
+
158
+ function createAxiosError(message, config, response, code) {
159
+ // axios v0.27.0+ defines AxiosError as constructor
160
+ if (typeof axios.AxiosError === "function") {
161
+ return axios.AxiosError.from(new Error(message), code, config, null, response);
162
+ }
163
+
164
+ // handling for axios v0.26.1 and below
165
+ const error = new Error(message);
166
+ error.isAxiosError = true;
167
+ error.config = config;
168
+ if (response !== undefined) {
169
+ error.response = response;
170
+ }
171
+ if (code !== undefined) {
172
+ error.code = code;
173
+ }
174
+
175
+ error.toJSON = function toJSON() {
176
+ return {
177
+ // Standard
178
+ message: this.message,
179
+ name: this.name,
180
+ // Microsoft
181
+ description: this.description,
182
+ number: this.number,
183
+ // Mozilla
184
+ fileName: this.fileName,
185
+ lineNumber: this.lineNumber,
186
+ columnNumber: this.columnNumber,
187
+ stack: this.stack,
188
+ // Axios
189
+ config: this.config,
190
+ code: this.code,
191
+ };
192
+ };
193
+ return error;
194
+ }
195
+
196
+ function createCouldNotFindMockError(config) {
197
+ const message =
198
+ `Could not find mock for: \n${
199
+ JSON.stringify({
200
+ method: config.method,
201
+ url: config.url,
202
+ params: config.params,
203
+ headers: config.headers
204
+ }, null, 2)}`;
205
+ const error = new Error(message);
206
+ error.isCouldNotFindMockError = true;
207
+ error.url = config.url;
208
+ error.method = config.method;
209
+ return error;
210
+ }
211
+
212
+ module.exports = {
213
+ find,
214
+ findHandler,
215
+ purgeIfReplyOnce,
216
+ settle,
217
+ isObjectOrArray,
218
+ isBuffer,
219
+ isBlob,
220
+ isBodyOrParametersMatching,
221
+ isEqual,
222
+ createAxiosError,
223
+ createCouldNotFindMockError,
224
+ };