@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/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
+ }