@depup/supertest 7.2.2-depup.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.
- package/LICENSE +23 -0
- package/README.md +471 -0
- package/index.js +67 -0
- package/lib/agent.js +100 -0
- package/lib/cookies/assertion.js +497 -0
- package/lib/cookies/index.js +32 -0
- package/lib/test.js +403 -0
- package/package.json +57 -0
package/lib/test.js
ADDED
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Module dependencies.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { inspect } = require('util');
|
|
8
|
+
const http = require('http');
|
|
9
|
+
const { STATUS_CODES } = require('http');
|
|
10
|
+
const { Server } = require('tls');
|
|
11
|
+
const { deepStrictEqual } = require('assert');
|
|
12
|
+
const { Request } = require('superagent');
|
|
13
|
+
let http2;
|
|
14
|
+
try {
|
|
15
|
+
http2 = require('http2'); // eslint-disable-line global-require
|
|
16
|
+
} catch (_) {
|
|
17
|
+
// eslint-disable-line no-empty
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** @typedef {import('superagent').Response} Response */
|
|
21
|
+
|
|
22
|
+
class Test extends Request {
|
|
23
|
+
/**
|
|
24
|
+
* Initialize a new `Test` with the given `app`,
|
|
25
|
+
* request `method` and `path`.
|
|
26
|
+
*
|
|
27
|
+
* @param {Server} app
|
|
28
|
+
* @param {String} method
|
|
29
|
+
* @param {String} path
|
|
30
|
+
* @api public
|
|
31
|
+
*/
|
|
32
|
+
constructor (app, method, path, optHttp2) {
|
|
33
|
+
super(method.toUpperCase(), path);
|
|
34
|
+
|
|
35
|
+
if (typeof app === 'function') {
|
|
36
|
+
if (optHttp2) {
|
|
37
|
+
app = http2.createServer(app); // eslint-disable-line no-param-reassign
|
|
38
|
+
} else {
|
|
39
|
+
app = http.createServer(app); // eslint-disable-line no-param-reassign
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
this.redirects(0);
|
|
44
|
+
this.buffer();
|
|
45
|
+
this.app = app;
|
|
46
|
+
this._asserts = [];
|
|
47
|
+
this.url = typeof app === 'string'
|
|
48
|
+
? app + path
|
|
49
|
+
: this.serverAddress(app, path);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Returns a URL, extracted from a server.
|
|
54
|
+
*
|
|
55
|
+
* @param {Server} app
|
|
56
|
+
* @param {String} path
|
|
57
|
+
* @returns {String} URL address
|
|
58
|
+
* @api private
|
|
59
|
+
*/
|
|
60
|
+
serverAddress(app, path) {
|
|
61
|
+
const addr = app.address();
|
|
62
|
+
|
|
63
|
+
if (!addr) this._server = app.listen(0);
|
|
64
|
+
// } else {
|
|
65
|
+
// this._server = app;
|
|
66
|
+
// }
|
|
67
|
+
const port = app.address().port;
|
|
68
|
+
const protocol = app instanceof Server ? 'https' : 'http';
|
|
69
|
+
return protocol + '://127.0.0.1:' + port + path;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Expectations:
|
|
74
|
+
*
|
|
75
|
+
* .expect(200)
|
|
76
|
+
* .expect(200, fn)
|
|
77
|
+
* .expect(200, body)
|
|
78
|
+
* .expect('Some body')
|
|
79
|
+
* .expect('Some body', fn)
|
|
80
|
+
* .expect(['json array body', { key: 'val' }])
|
|
81
|
+
* .expect('Content-Type', 'application/json')
|
|
82
|
+
* .expect('Content-Type', 'application/json', fn)
|
|
83
|
+
* .expect(fn)
|
|
84
|
+
* .expect([200, 404])
|
|
85
|
+
*
|
|
86
|
+
* @return {Test}
|
|
87
|
+
* @api public
|
|
88
|
+
*/
|
|
89
|
+
expect(a, b, c) {
|
|
90
|
+
// callback
|
|
91
|
+
if (typeof a === 'function') {
|
|
92
|
+
this._asserts.push(wrapAssertFn(a));
|
|
93
|
+
return this;
|
|
94
|
+
}
|
|
95
|
+
if (typeof b === 'function') this.end(b);
|
|
96
|
+
if (typeof c === 'function') this.end(c);
|
|
97
|
+
|
|
98
|
+
// status
|
|
99
|
+
if (typeof a === 'number') {
|
|
100
|
+
this._asserts.push(wrapAssertFn(this._assertStatus.bind(this, a)));
|
|
101
|
+
// body
|
|
102
|
+
if (typeof b !== 'function' && arguments.length > 1) {
|
|
103
|
+
this._asserts.push(wrapAssertFn(this._assertBody.bind(this, b)));
|
|
104
|
+
}
|
|
105
|
+
return this;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// multiple statuses
|
|
109
|
+
if (Array.isArray(a) && a.length > 0 && a.every(val => typeof val === 'number')) {
|
|
110
|
+
this._asserts.push(wrapAssertFn(this._assertStatusArray.bind(this, a)));
|
|
111
|
+
return this;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// header field
|
|
115
|
+
if (typeof b === 'string' || typeof b === 'number' || b instanceof RegExp) {
|
|
116
|
+
this._asserts.push(wrapAssertFn(this._assertHeader.bind(this, { name: '' + a, value: b })));
|
|
117
|
+
return this;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// body
|
|
121
|
+
this._asserts.push(wrapAssertFn(this._assertBody.bind(this, a)));
|
|
122
|
+
|
|
123
|
+
return this;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Defer invoking superagent's `.end()` until
|
|
128
|
+
* the server is listening.
|
|
129
|
+
*
|
|
130
|
+
* @param {?Function} fn
|
|
131
|
+
* @api public
|
|
132
|
+
*/
|
|
133
|
+
end(fn) {
|
|
134
|
+
const server = this._server;
|
|
135
|
+
|
|
136
|
+
super.end((err, res) => {
|
|
137
|
+
const localAssert = () => {
|
|
138
|
+
this.assert(err, res, fn);
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
if (server && server._handle) {
|
|
142
|
+
// Handle server closing with error handling for already closed servers
|
|
143
|
+
return server.close((closeError) => {
|
|
144
|
+
// Ignore ERR_SERVER_NOT_RUNNING errors as the server is already closed
|
|
145
|
+
if (closeError && closeError.code === 'ERR_SERVER_NOT_RUNNING') {
|
|
146
|
+
return localAssert();
|
|
147
|
+
}
|
|
148
|
+
// For other errors, pass them through
|
|
149
|
+
if (closeError) {
|
|
150
|
+
return localAssert();
|
|
151
|
+
}
|
|
152
|
+
localAssert();
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
localAssert();
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
return this;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Perform assertions and invoke `fn(err, res)`.
|
|
164
|
+
*
|
|
165
|
+
* @param {?Error} resError
|
|
166
|
+
* @param {Response} res
|
|
167
|
+
* @param {Function} fn
|
|
168
|
+
* @api private
|
|
169
|
+
*/
|
|
170
|
+
assert(resError, res, fn) {
|
|
171
|
+
let errorObj;
|
|
172
|
+
|
|
173
|
+
// check for unexpected network errors or server not running/reachable errors
|
|
174
|
+
// when there is no response and superagent sends back a System Error
|
|
175
|
+
// do not check further for other asserts, if any, in such case
|
|
176
|
+
// https://nodejs.org/api/errors.html#errors_common_system_errors
|
|
177
|
+
const sysErrors = {
|
|
178
|
+
ECONNREFUSED: 'Connection refused',
|
|
179
|
+
ECONNRESET: 'Connection reset by peer',
|
|
180
|
+
EPIPE: 'Broken pipe',
|
|
181
|
+
ETIMEDOUT: 'Operation timed out'
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
if (!res && resError) {
|
|
185
|
+
if (resError instanceof Error && resError.syscall === 'connect'
|
|
186
|
+
&& Object.getOwnPropertyNames(sysErrors).indexOf(resError.code) >= 0) {
|
|
187
|
+
errorObj = new Error(resError.code + ': ' + sysErrors[resError.code]);
|
|
188
|
+
} else {
|
|
189
|
+
errorObj = resError;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// asserts
|
|
194
|
+
for (let i = 0; i < this._asserts.length && !errorObj; i += 1) {
|
|
195
|
+
errorObj = this._assertFunction(this._asserts[i], res);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// set unexpected superagent error if no other error has occurred.
|
|
199
|
+
if (!errorObj && resError instanceof Error && (!res || resError.status !== res.status)) {
|
|
200
|
+
errorObj = resError;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (fn) {
|
|
204
|
+
fn.call(this, errorObj || null, res);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/*
|
|
209
|
+
* Adds a set Authorization Bearer
|
|
210
|
+
*
|
|
211
|
+
* @param {Bearer} Bearer Token
|
|
212
|
+
* Shortcut for .set('Authorization', `Bearer ${token}`)
|
|
213
|
+
*/
|
|
214
|
+
|
|
215
|
+
bearer(token) {
|
|
216
|
+
this.set('Authorization', `Bearer ${token}`);
|
|
217
|
+
return this;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/*
|
|
221
|
+
* Adds a set Authorization Bearer
|
|
222
|
+
*
|
|
223
|
+
* @param {Bearer} Bearer Token
|
|
224
|
+
* Shortcut for .set('Authorization', `Bearer ${token}`)
|
|
225
|
+
*/
|
|
226
|
+
|
|
227
|
+
bearer(token) {
|
|
228
|
+
this.set('Authorization', `Bearer ${token}`);
|
|
229
|
+
return this;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Perform assertions on a response body and return an Error upon failure.
|
|
234
|
+
*
|
|
235
|
+
* @param {Mixed} body
|
|
236
|
+
* @param {Response} res
|
|
237
|
+
* @return {?Error}
|
|
238
|
+
* @api private
|
|
239
|
+
*/// eslint-disable-next-line class-methods-use-this
|
|
240
|
+
_assertBody(body, res) {
|
|
241
|
+
const isRegexp = body instanceof RegExp;
|
|
242
|
+
|
|
243
|
+
// parsed
|
|
244
|
+
if (typeof body === 'object' && !isRegexp) {
|
|
245
|
+
try {
|
|
246
|
+
deepStrictEqual(body, res.body);
|
|
247
|
+
} catch (err) {
|
|
248
|
+
const a = inspect(body);
|
|
249
|
+
const b = inspect(res.body);
|
|
250
|
+
return error('expected ' + a + ' response body, got ' + b, body, res.body);
|
|
251
|
+
}
|
|
252
|
+
} else if (body !== res.text) {
|
|
253
|
+
// string
|
|
254
|
+
const a = inspect(body);
|
|
255
|
+
const b = inspect(res.text);
|
|
256
|
+
|
|
257
|
+
// regexp
|
|
258
|
+
if (isRegexp) {
|
|
259
|
+
if (!body.test(res.text)) {
|
|
260
|
+
return error('expected body ' + b + ' to match ' + body, body, res.body);
|
|
261
|
+
}
|
|
262
|
+
} else {
|
|
263
|
+
return error('expected ' + a + ' response body, got ' + b, body, res.body);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Perform assertions on a response header and return an Error upon failure.
|
|
270
|
+
*
|
|
271
|
+
* @param {Object} header
|
|
272
|
+
* @param {Response} res
|
|
273
|
+
* @return {?Error}
|
|
274
|
+
* @api private
|
|
275
|
+
*/// eslint-disable-next-line class-methods-use-this
|
|
276
|
+
_assertHeader(header, res) {
|
|
277
|
+
const field = header.name;
|
|
278
|
+
const actual = res.header[field.toLowerCase()];
|
|
279
|
+
const fieldExpected = header.value;
|
|
280
|
+
|
|
281
|
+
if (typeof actual === 'undefined') return new Error('expected "' + field + '" header field');
|
|
282
|
+
// This check handles header values that may be a String or single element Array
|
|
283
|
+
if ((Array.isArray(actual) && actual.toString() === fieldExpected)
|
|
284
|
+
|| fieldExpected === actual) {
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
if (fieldExpected instanceof RegExp) {
|
|
288
|
+
if (!fieldExpected.test(actual)) {
|
|
289
|
+
return new Error('expected "' + field + '" matching '
|
|
290
|
+
+ fieldExpected + ', got "' + actual + '"');
|
|
291
|
+
}
|
|
292
|
+
} else {
|
|
293
|
+
return new Error('expected "' + field + '" of "' + fieldExpected + '", got "' + actual + '"');
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Perform assertions on the response status and return an Error upon failure.
|
|
299
|
+
*
|
|
300
|
+
* @param {Number} status
|
|
301
|
+
* @param {Response} res
|
|
302
|
+
* @return {?Error}
|
|
303
|
+
* @api private
|
|
304
|
+
*/// eslint-disable-next-line class-methods-use-this
|
|
305
|
+
_assertStatus(status, res) {
|
|
306
|
+
if (res.status !== status) {
|
|
307
|
+
const a = STATUS_CODES[status];
|
|
308
|
+
const b = STATUS_CODES[res.status];
|
|
309
|
+
return new Error('expected ' + status + ' "' + a + '", got ' + res.status + ' "' + b + '"');
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Perform assertions on the response status and return an Error upon failure.
|
|
315
|
+
*
|
|
316
|
+
* @param {Array<Number>} statusArray
|
|
317
|
+
* @param {Response} res
|
|
318
|
+
* @return {?Error}
|
|
319
|
+
* @api private
|
|
320
|
+
*/// eslint-disable-next-line class-methods-use-this
|
|
321
|
+
_assertStatusArray(statusArray, res) {
|
|
322
|
+
if (!statusArray.includes(res.status)) {
|
|
323
|
+
const b = STATUS_CODES[res.status];
|
|
324
|
+
const expectedList = statusArray.join(', ');
|
|
325
|
+
return new Error(
|
|
326
|
+
'expected one of "' + expectedList + '", got ' + res.status + ' "' + b + '"'
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Performs an assertion by calling a function and return an Error upon failure.
|
|
333
|
+
*
|
|
334
|
+
* @param {Function} fn
|
|
335
|
+
* @param {Response} res
|
|
336
|
+
* @return {?Error}
|
|
337
|
+
* @api private
|
|
338
|
+
*/// eslint-disable-next-line class-methods-use-this
|
|
339
|
+
_assertFunction(fn, res) {
|
|
340
|
+
let err;
|
|
341
|
+
try {
|
|
342
|
+
err = fn(res);
|
|
343
|
+
} catch (e) {
|
|
344
|
+
err = e;
|
|
345
|
+
}
|
|
346
|
+
if (err instanceof Error) return err;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Wraps an assert function into another.
|
|
352
|
+
* The wrapper function edit the stack trace of any assertion error, prepending a more useful stack to it.
|
|
353
|
+
*
|
|
354
|
+
* @param {Function} assertFn
|
|
355
|
+
* @returns {Function} wrapped assert function
|
|
356
|
+
*/
|
|
357
|
+
|
|
358
|
+
function wrapAssertFn(assertFn) {
|
|
359
|
+
const savedStack = new Error().stack.split('\n').slice(3);
|
|
360
|
+
|
|
361
|
+
return function(res) {
|
|
362
|
+
let badStack;
|
|
363
|
+
let err;
|
|
364
|
+
try {
|
|
365
|
+
err = assertFn(res);
|
|
366
|
+
} catch (e) {
|
|
367
|
+
err = e;
|
|
368
|
+
}
|
|
369
|
+
if (err instanceof Error && err.stack) {
|
|
370
|
+
badStack = err.stack.replace(err.message, '').split('\n').slice(1);
|
|
371
|
+
err.stack = [err.toString()]
|
|
372
|
+
.concat(savedStack)
|
|
373
|
+
.concat('----')
|
|
374
|
+
.concat(badStack)
|
|
375
|
+
.join('\n');
|
|
376
|
+
}
|
|
377
|
+
return err;
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Return an `Error` with `msg` and results properties.
|
|
383
|
+
*
|
|
384
|
+
* @param {String} msg
|
|
385
|
+
* @param {Mixed} expected
|
|
386
|
+
* @param {Mixed} actual
|
|
387
|
+
* @return {Error}
|
|
388
|
+
* @api private
|
|
389
|
+
*/
|
|
390
|
+
|
|
391
|
+
function error(msg, expected, actual) {
|
|
392
|
+
const err = new Error(msg);
|
|
393
|
+
err.expected = expected;
|
|
394
|
+
err.actual = actual;
|
|
395
|
+
err.showDiff = true;
|
|
396
|
+
return err;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Expose `Test`.
|
|
401
|
+
*/
|
|
402
|
+
|
|
403
|
+
module.exports = Test;
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@depup/supertest",
|
|
3
|
+
"description": "SuperAgent driven library for testing HTTP servers",
|
|
4
|
+
"version": "7.2.2-depup.0",
|
|
5
|
+
"author": "TJ Holowaychuk",
|
|
6
|
+
"contributors": [],
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"methods": "^1.1.2",
|
|
9
|
+
"superagent": "^10.3.0",
|
|
10
|
+
"cookie-signature": "^1.2.2"
|
|
11
|
+
},
|
|
12
|
+
"devDependencies": {
|
|
13
|
+
"@commitlint/cli": "17",
|
|
14
|
+
"@commitlint/config-conventional": "17",
|
|
15
|
+
"body-parser": "^1.20.3",
|
|
16
|
+
"cookie-parser": "^1.4.7",
|
|
17
|
+
"eslint": "^8.32.0",
|
|
18
|
+
"eslint-config-airbnb-base": "^15.0.0",
|
|
19
|
+
"eslint-plugin-import": "^2.27.5",
|
|
20
|
+
"express": "^4.18.3",
|
|
21
|
+
"mocha": "^10.2.0",
|
|
22
|
+
"nock": "^13.3.8",
|
|
23
|
+
"nyc": "^15.1.0",
|
|
24
|
+
"proxyquire": "^2.1.3",
|
|
25
|
+
"should": "^13.2.3",
|
|
26
|
+
"sinon": "20.0.0"
|
|
27
|
+
},
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=14.18.0"
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"index.js",
|
|
33
|
+
"lib"
|
|
34
|
+
],
|
|
35
|
+
"keywords": [
|
|
36
|
+
"bdd",
|
|
37
|
+
"http",
|
|
38
|
+
"request",
|
|
39
|
+
"superagent",
|
|
40
|
+
"tdd",
|
|
41
|
+
"test",
|
|
42
|
+
"testing"
|
|
43
|
+
],
|
|
44
|
+
"license": "MIT",
|
|
45
|
+
"main": "index.js",
|
|
46
|
+
"repository": {
|
|
47
|
+
"type": "git",
|
|
48
|
+
"url": "https://github.com/ladjs/supertest.git"
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"coverage": "nyc report --reporter=text-lcov > coverage.lcov",
|
|
52
|
+
"lint": "eslint lib/**/*.js test/**/*.js index.js",
|
|
53
|
+
"lint:fix": "eslint --fix lib/**/*.js test/**/*.js index.js",
|
|
54
|
+
"pretest": "npm run lint --if-present",
|
|
55
|
+
"test": "nyc --reporter=html --reporter=text mocha --exit --require should --reporter spec --check-leaks"
|
|
56
|
+
}
|
|
57
|
+
}
|