@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.
@@ -0,0 +1,497 @@
1
+ /** Copyright 2015 Gregory Langlais. See LICENSE.txt. */
2
+
3
+ 'use strict';
4
+
5
+ const signature = require('cookie-signature');
6
+
7
+ /**
8
+ * Assert that an object has specific properties (supports array of keys or object)
9
+ *
10
+ * @param {object} obj
11
+ * @param {object|array} props
12
+ */
13
+ function assertHasProperties(obj, props) {
14
+ if (Array.isArray(props)) {
15
+ props.forEach(function (key) {
16
+ if (!(key in obj)) {
17
+ throw new Error('expected object to have property ' + key);
18
+ }
19
+ });
20
+ } else {
21
+ Object.keys(props).forEach(function (key) {
22
+ if (!(key in obj)) {
23
+ throw new Error('expected object to have property ' + key);
24
+ }
25
+ });
26
+ }
27
+ }
28
+
29
+ /**
30
+ * Assert that an object does not have specific properties (supports array of keys or object)
31
+ * When checking with empty props, throws if object exists (matches should.js behavior)
32
+ *
33
+ * @param {object} obj
34
+ * @param {object|array} props
35
+ */
36
+ function assertNotHasProperties(obj, props) {
37
+ if (Array.isArray(props)) {
38
+ // When empty array is passed, should.js throws 'false negative fail' if object exists
39
+ if (props.length === 0) {
40
+ throw new Error('expected object to not have properties (false negative fail)');
41
+ }
42
+ props.forEach(function (key) {
43
+ if (key in obj) {
44
+ throw new Error('expected object to not have property ' + key);
45
+ }
46
+ });
47
+ } else {
48
+ // When empty object is passed, should.js throws 'false negative fail' if object exists
49
+ let keys = Object.keys(props);
50
+ if (keys.length === 0) {
51
+ throw new Error('expected object to not have properties (false negative fail)');
52
+ }
53
+ keys.forEach(function (key) {
54
+ if (key in obj) {
55
+ throw new Error('expected object to not have property ' + key);
56
+ }
57
+ });
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Assert that two values are equal
63
+ *
64
+ * @param {*} actual
65
+ * @param {*} expected
66
+ */
67
+ function assertEqual(actual, expected) {
68
+ if (actual !== expected) {
69
+ throw new Error(
70
+ 'expected ' + JSON.stringify(actual) + ' to equal ' + JSON.stringify(expected)
71
+ );
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Assert that two values are not equal
77
+ *
78
+ * @param {*} actual
79
+ * @param {*} expected
80
+ */
81
+ function assertNotEqual(actual, expected) {
82
+ if (actual === expected) {
83
+ throw new Error(
84
+ 'expected ' + JSON.stringify(actual) + ' to not equal ' + JSON.stringify(expected)
85
+ );
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Build Assertion function
91
+ *
92
+ * {object|object[]} expects cookies
93
+ * {string} expects.<name> and value of cookie
94
+ * {object} expects.options
95
+ * {string} [expects.options.domain]
96
+ * {string} [expects.options.path]
97
+ * {string} [expects.options.expires] UTC string using date.toUTCString()
98
+ * {number} [expects.options.max-age]
99
+ * {boolean} [expects.options.secure]
100
+ * {boolean} [expects.options.httponly]
101
+ * {string|string[]} [expects.secret]
102
+ *
103
+ * @param {null|string|string[]} [secret]
104
+ * @param {function|function[]} [asserts]
105
+ * @returns {Assertion}
106
+ */
107
+ module.exports = function (secret, asserts) {
108
+ let assertions = [];
109
+
110
+ if (typeof secret === 'string') secret = [secret]; // eslint-disable-line no-param-reassign
111
+ else if (!Array.isArray(secret)) secret = []; // eslint-disable-line no-param-reassign
112
+
113
+ if (Array.isArray(asserts)) assertions = asserts;
114
+ else if (typeof asserts === 'function') assertions.push(asserts);
115
+
116
+ /**
117
+ * Assertion function with static chainable methods
118
+ *
119
+ * @param {object} res
120
+ * @returns {undefined|string}
121
+ * @constructor
122
+ */
123
+ function Assertion(res) {
124
+ if (typeof res !== 'object') throw new Error('res argument must be object');
125
+
126
+ // request and response object initialization
127
+ let request = {
128
+ headers: res.req.getHeaders(),
129
+ cookies: []
130
+ };
131
+
132
+ let response = {
133
+ headers: res.headers,
134
+ cookies: []
135
+ };
136
+
137
+ // build assertions request object
138
+ if (request.headers.cookie) {
139
+ const cookies = String(request.headers.cookie);
140
+ cookies.split(/; */).forEach(function (cookie) {
141
+ request.cookies.push(Assertion.parse(cookie));
142
+ });
143
+ }
144
+
145
+ // build assertions response object
146
+ if (
147
+ Array.isArray(response.headers['set-cookie'])
148
+ && response.headers['set-cookie'].length > 0
149
+ ) {
150
+ response.headers['set-cookie'].forEach(function (val) {
151
+ response.cookies.push(Assertion.parse(val));
152
+ });
153
+ }
154
+
155
+ // run assertions
156
+ let result;
157
+ assertions.every(function (assertion) {
158
+ result = assertion(request, response);
159
+ return (typeof (result) !== 'string');
160
+ });
161
+
162
+ return result;
163
+ }
164
+
165
+ /**
166
+ * Find cookie in stack/array
167
+ *
168
+ * @param {string} name
169
+ * @param {array} stack
170
+ * @returns {object|undefined} cookie
171
+ */
172
+ Assertion.find = function (name, stack) {
173
+ let cookie;
174
+
175
+ stack.every(function (val) {
176
+ if (name !== val.name) return true;
177
+ cookie = val;
178
+ return false;
179
+ });
180
+
181
+ return cookie;
182
+ };
183
+
184
+ /**
185
+ * Parse cookie string
186
+ *
187
+ * @param {string} str
188
+ * @param {object} [options]
189
+ * @param {function} [options.decode] uri
190
+ * @param {undefined|boolean} [options.request] headers
191
+ * @returns {object}
192
+ */
193
+ Assertion.parse = function (str, options) {
194
+ if (typeof str !== 'string') throw new TypeError('argument str must be a string');
195
+
196
+ if (typeof options !== 'object') options = {}; // eslint-disable-line no-param-reassign
197
+
198
+ let decode = options.decode || decodeURIComponent;
199
+
200
+ let parts = str.split(/; */);
201
+
202
+ let cookie = {};
203
+
204
+ parts.forEach(function (part, i) {
205
+ if (i === 1) cookie.options = {};
206
+
207
+ let equalsIndex = part.indexOf('=');
208
+
209
+ // things that don't look like key=value get true flag
210
+ if (equalsIndex < 0) {
211
+ cookie.options[part.trim().toLowerCase()] = true;
212
+ return;
213
+ }
214
+
215
+ const key = part.substr(0, equalsIndex).trim().toLowerCase();
216
+ // only assign once
217
+ if (typeof cookie[key] !== 'undefined') return;
218
+
219
+ equalsIndex += 1;
220
+ let val = part.substr(equalsIndex, part.length).trim();
221
+ // quoted values
222
+ if (val[0] === '"') val = val.slice(1, -1);
223
+
224
+ let value;
225
+ try {
226
+ value = decode(val);
227
+ } catch (e) {
228
+ value = val;
229
+ }
230
+
231
+ if (i > 0) {
232
+ cookie.options[key] = value;
233
+ return;
234
+ }
235
+
236
+ cookie.name = key;
237
+ cookie.value = decode(val);
238
+ });
239
+
240
+ if (typeof cookie.options === 'undefined') cookie.options = {};
241
+
242
+ return cookie;
243
+ };
244
+
245
+ /**
246
+ * Iterate expects
247
+ *
248
+ * @param {object|object[]} expects
249
+ * @param {boolean|function} hasValues
250
+ * @param {function} [cb]
251
+ */
252
+ Assertion.expects = function (expects, hasValues, cb) {
253
+ if (!Array.isArray(expects) && typeof expects === 'object') expects = [expects]; // eslint-disable-line no-param-reassign
254
+
255
+ let resolvedCb;
256
+ let resolvedHasValues;
257
+ if (typeof cb === 'undefined' && typeof hasValues === 'function') {
258
+ resolvedCb = hasValues;
259
+ resolvedHasValues = false;
260
+ } else {
261
+ resolvedCb = cb;
262
+ resolvedHasValues = hasValues;
263
+ }
264
+
265
+ expects.forEach(function (expect) {
266
+ let options = expect.options;
267
+ if (typeof options !== 'object' && !Array.isArray(options)) {
268
+ options = (resolvedHasValues) ? {} : [];
269
+ }
270
+
271
+ resolvedCb(Object.assign({}, expect, { options }));
272
+ });
273
+ };
274
+
275
+ /**
276
+ * Assert cookies and options are set
277
+ *
278
+ * @param {object|object[]} expects cookies
279
+ * @param {undefined|boolean} [assert]
280
+ * @returns {function} Assertion
281
+ */
282
+ Assertion.set = function (expects, assert) {
283
+ if (typeof assert === 'undefined') assert = true; // eslint-disable-line no-param-reassign
284
+
285
+ Assertion.expects(expects, function (expect) {
286
+ assertions.push(function (req, res) {
287
+ // get expectation cookie
288
+ const cookie = Assertion.find(expect.name, res.cookies);
289
+
290
+ if (assert && !cookie) throw new Error('expected: ' + expect.name + ' cookie to be set');
291
+
292
+ if (assert) assertHasProperties(cookie.options, expect.options);
293
+ else if (cookie) assertNotHasProperties(cookie.options, expect.options);
294
+ });
295
+ });
296
+
297
+ return Assertion;
298
+ };
299
+
300
+ /**
301
+ * Assert cookies has been reset
302
+ *
303
+ * @param {object|object[]} expects cookies
304
+ * @param {undefined|boolean} [assert]
305
+ * @returns {function} Assertion
306
+ */
307
+ Assertion.reset = function (expects, assert) {
308
+ if (typeof assert === 'undefined') assert = true; // eslint-disable-line no-param-reassign
309
+
310
+ Assertion.expects(expects, function (expect) {
311
+ assertions.push(function (req, res) {
312
+ // get sent cookie
313
+ const cookieReq = Assertion.find(expect.name, req.cookies);
314
+ // get expectation cookie
315
+ const cookieRes = Assertion.find(expect.name, res.cookies);
316
+
317
+ if (assert && (!cookieReq || !cookieRes)) {
318
+ throw new Error('expected: ' + expect.name + ' cookie to be set');
319
+ } else if (!assert && cookieReq && cookieRes) {
320
+ throw new Error('expected: ' + expect.name + ' cookie to be set');
321
+ }
322
+ });
323
+ });
324
+
325
+ return Assertion;
326
+ };
327
+
328
+ /**
329
+ * Assert cookies is set and new
330
+ *
331
+ * @param {object|object[]} expects cookies
332
+ * @param {undefined|boolean} [assert]
333
+ * @returns {function} Assertion
334
+ */
335
+ Assertion.new = function (expects, assert) {
336
+ if (typeof assert === 'undefined') assert = true; // eslint-disable-line no-param-reassign
337
+
338
+ Assertion.expects(expects, function (expect) {
339
+ assertions.push(function (req, res) {
340
+ // get sent cookie
341
+ const cookieReq = Assertion.find(expect.name, req.cookies);
342
+ // get expectation cookie
343
+ const cookieRes = Assertion.find(expect.name, res.cookies);
344
+
345
+ if (assert) {
346
+ if (!cookieRes) throw new Error('expected: ' + expect.name + ' cookie to be set');
347
+ if (cookieReq && cookieRes) {
348
+ throw new Error('expected: ' + expect.name + ' cookie to NOT already be set');
349
+ }
350
+ } else if (!cookieReq || !cookieRes) {
351
+ throw new Error('expected: ' + expect.name + ' cookie to be set');
352
+ }
353
+ });
354
+ });
355
+
356
+ return Assertion;
357
+ };
358
+
359
+ /**
360
+ * Assert cookies expires or max-age has increased
361
+ *
362
+ * @param {object|object[]} expects cookies
363
+ * @param {undefined|boolean} [assert]
364
+ * @returns {function} Assertion
365
+ */
366
+ Assertion.renew = function (expects, assert) {
367
+ if (typeof assert === 'undefined') assert = true; // eslint-disable-line no-param-reassign
368
+
369
+ Assertion.expects(expects, true, function (expect) {
370
+ const expectExpires = new Date(expect.options.expires);
371
+ const expectMaxAge = parseFloat(expect.options['max-age']);
372
+
373
+ let baseMessage = 'expected: ' + expect.name;
374
+ if (!expectExpires.getTime() && !expectMaxAge) {
375
+ throw new Error(baseMessage + ' expects to have expires or max-age option');
376
+ }
377
+
378
+ assertions.push(function (req, res) {
379
+ // get sent cookie
380
+ const cookieReq = Assertion.find(expect.name, req.cookies);
381
+ // get expectation cookie
382
+ const cookieRes = Assertion.find(expect.name, res.cookies);
383
+
384
+ const cookieMaxAge = (expectMaxAge && cookieRes)
385
+ ? parseFloat(cookieRes.options['max-age'])
386
+ : undefined;
387
+ const cookieExpires = (expectExpires.getTime() && cookieRes)
388
+ ? new Date(cookieRes.options.expires)
389
+ : undefined;
390
+
391
+ if (assert) {
392
+ if (!cookieReq || !cookieRes) {
393
+ throw new Error(baseMessage + ' cookie to be set');
394
+ }
395
+ if (expectMaxAge && (!cookieMaxAge || cookieMaxAge <= expectMaxAge)) {
396
+ throw new Error(baseMessage + ' cookie max-age to be greater than existing value');
397
+ }
398
+
399
+ if (
400
+ expectExpires.getTime()
401
+ && (!cookieExpires.getTime() || cookieExpires <= expectExpires)
402
+ ) {
403
+ throw new Error(baseMessage + ' cookie expires to be greater than existing value');
404
+ }
405
+ } else if (cookieRes) {
406
+ if (expectMaxAge && cookieMaxAge > expectMaxAge) {
407
+ throw new Error(
408
+ baseMessage + ' cookie max-age to be less than or equal to existing value'
409
+ );
410
+ }
411
+
412
+ if (expectExpires.getTime() && cookieExpires > expectExpires) {
413
+ throw new Error(
414
+ baseMessage + ' cookie expires to be less than or equal to existing value'
415
+ );
416
+ }
417
+ }
418
+ });
419
+ });
420
+
421
+ return Assertion;
422
+ };
423
+
424
+ /**
425
+ * Assert cookies contains values
426
+ *
427
+ * @param {object|object[]} expects cookies
428
+ * @param {undefined|boolean} [assert]
429
+ * @returns {function} Assertion
430
+ */
431
+ Assertion.contain = function (expects, assert) {
432
+ if (typeof assert === 'undefined') assert = true; // eslint-disable-line no-param-reassign
433
+
434
+ Assertion.expects(expects, function (expect) {
435
+ const keys = Object.keys(expect.options);
436
+
437
+ assertions.push(function (req, res) {
438
+ // get expectation cookie
439
+ const cookie = Assertion.find(expect.name, res.cookies);
440
+
441
+ if (!cookie) throw new Error('expected: ' + expect.name + ' cookie to be set');
442
+
443
+ // check cookie values are equal
444
+ if ('value' in expect) {
445
+ try {
446
+ if (assert) assertEqual(cookie.value, expect.value);
447
+ else assertNotEqual(cookie.value, expect.value);
448
+ } catch (e) {
449
+ if (secret.length) {
450
+ let value;
451
+ secret.every(function (sec) {
452
+ value = signature.unsign(cookie.value.slice(2), sec);
453
+ return !(value && value === expect.value);
454
+ });
455
+
456
+ if (assert && !value) {
457
+ throw new Error('expected: ' + expect.name + ' value to equal ' + expect.value);
458
+ } else if (!assert && value) {
459
+ throw new Error('expected: ' + expect.name + ' value to NOT equal ' + expect.value);
460
+ }
461
+ } else throw e;
462
+ }
463
+ }
464
+
465
+ keys.forEach(function (key) {
466
+ const expected = (
467
+ key === 'max-age'
468
+ ? expect.options[key].toString()
469
+ : expect.options[key]
470
+ );
471
+ if (assert) assertEqual(cookie.options[key], expected);
472
+ else assertNotEqual(cookie.options[key], expected);
473
+ });
474
+ });
475
+ });
476
+
477
+ return Assertion;
478
+ };
479
+
480
+ /**
481
+ * Assert NOT modifier
482
+ *
483
+ * @param {function} method
484
+ * @param {...*}
485
+ */
486
+ Assertion.not = function (method) {
487
+ let args = [];
488
+
489
+ for (let i = 1; i < arguments.length; i += 1) args.push(arguments[i]);
490
+
491
+ args.push(false);
492
+
493
+ return Assertion[method].apply(Assertion, args);
494
+ };
495
+
496
+ return Assertion;
497
+ };
@@ -0,0 +1,32 @@
1
+ /** Copyright 2015 Gregory Langlais. See LICENSE.txt. */
2
+
3
+ 'use strict';
4
+
5
+ const Assertion = require('./assertion');
6
+
7
+ /**
8
+ * Construct cookies assertion (function)
9
+ *
10
+ * @param {null|string|string[]} [secret]
11
+ * @param {function(req, res)[]} [asserts] ran within returned assertion function
12
+ * @returns {function} assertion
13
+ * @constructor
14
+ */
15
+ function ExpectCookies(secret, asserts) {
16
+ return Assertion(secret, asserts);
17
+ }
18
+
19
+ // build ExpectCookies proxy methods
20
+ const assertion = Assertion();
21
+ const methods = Object.getOwnPropertyNames(assertion);
22
+
23
+ methods.forEach(function(method) {
24
+ if (typeof assertion[method] === 'function' && typeof Function[method] === 'undefined') {
25
+ ExpectCookies[method] = function() {
26
+ const newAssertion = Assertion();
27
+ return newAssertion[method].apply(newAssertion, arguments);
28
+ };
29
+ }
30
+ });
31
+
32
+ module.exports = ExpectCookies;