qs 6.2.4 → 6.3.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/test/parse.js CHANGED
@@ -3,7 +3,6 @@
3
3
  var test = require('tape');
4
4
  var qs = require('../');
5
5
  var iconv = require('iconv-lite');
6
- var SaferBuffer = require('safer-buffer').Buffer;
7
6
 
8
7
  test('parse()', function (t) {
9
8
  t.test('parses a simple string', function (st) {
@@ -65,8 +64,15 @@ test('parse()', function (t) {
65
64
  st.deepEqual(qs.parse('a[]=b&a=c'), { a: ['b', 'c'] });
66
65
  st.deepEqual(qs.parse('a[0]=b&a=c'), { a: ['b', 'c'] });
67
66
  st.deepEqual(qs.parse('a=b&a[0]=c'), { a: ['b', 'c'] });
68
- st.deepEqual(qs.parse('a[1]=b&a=c'), { a: ['b', 'c'] });
69
- st.deepEqual(qs.parse('a=b&a[1]=c'), { a: ['b', 'c'] });
67
+
68
+ st.deepEqual(qs.parse('a[1]=b&a=c', { arrayLimit: 20 }), { a: ['b', 'c'] });
69
+ st.deepEqual(qs.parse('a[]=b&a=c', { arrayLimit: 0 }), { a: ['b', 'c'] });
70
+ st.deepEqual(qs.parse('a[]=b&a=c'), { a: ['b', 'c'] });
71
+
72
+ st.deepEqual(qs.parse('a=b&a[1]=c', { arrayLimit: 20 }), { a: ['b', 'c'] });
73
+ st.deepEqual(qs.parse('a=b&a[]=c', { arrayLimit: 0 }), { a: ['b', 'c'] });
74
+ st.deepEqual(qs.parse('a=b&a[]=c'), { a: ['b', 'c'] });
75
+
70
76
  st.end();
71
77
  });
72
78
 
@@ -79,13 +85,15 @@ test('parse()', function (t) {
79
85
  t.test('allows to specify array indices', function (st) {
80
86
  st.deepEqual(qs.parse('a[1]=c&a[0]=b&a[2]=d'), { a: ['b', 'c', 'd'] });
81
87
  st.deepEqual(qs.parse('a[1]=c&a[0]=b'), { a: ['b', 'c'] });
88
+ st.deepEqual(qs.parse('a[1]=c', { arrayLimit: 20 }), { a: ['c'] });
89
+ st.deepEqual(qs.parse('a[1]=c', { arrayLimit: 0 }), { a: { 1: 'c' } });
82
90
  st.deepEqual(qs.parse('a[1]=c'), { a: ['c'] });
83
91
  st.end();
84
92
  });
85
93
 
86
- t.test('limits specific array indices to 20', function (st) {
87
- st.deepEqual(qs.parse('a[20]=a'), { a: ['a'] });
88
- st.deepEqual(qs.parse('a[21]=a'), { a: { 21: 'a' } });
94
+ t.test('limits specific array indices to arrayLimit', function (st) {
95
+ st.deepEqual(qs.parse('a[20]=a', { arrayLimit: 20 }), { a: ['a'] });
96
+ st.deepEqual(qs.parse('a[21]=a', { arrayLimit: 20 }), { a: { 21: 'a' } });
89
97
  st.end();
90
98
  });
91
99
 
@@ -123,9 +131,9 @@ test('parse()', function (t) {
123
131
  st.deepEqual(qs.parse('foo[bad]=baz&foo[]=bar&foo[]=foo'), { foo: { bad: 'baz', 0: 'bar', 1: 'foo' } });
124
132
  st.deepEqual(qs.parse('foo[0][a]=a&foo[0][b]=b&foo[1][a]=aa&foo[1][b]=bb'), { foo: [{ a: 'a', b: 'b' }, { a: 'aa', b: 'bb' }] });
125
133
 
126
- st.deepEqual(qs.parse('a[]=b&a[t]=u&a[hasOwnProperty]=c', { allowPrototypes: false }), { a: { 0: 'b', t: 'u' } });
134
+ st.deepEqual(qs.parse('a[]=b&a[t]=u&a[hasOwnProperty]=c', { allowPrototypes: false }), { a: { 0: 'b', c: true, t: 'u' } });
127
135
  st.deepEqual(qs.parse('a[]=b&a[t]=u&a[hasOwnProperty]=c', { allowPrototypes: true }), { a: { 0: 'b', t: 'u', hasOwnProperty: 'c' } });
128
- st.deepEqual(qs.parse('a[]=b&a[hasOwnProperty]=c&a[x]=y', { allowPrototypes: false }), { a: { 0: 'b', x: 'y' } });
136
+ st.deepEqual(qs.parse('a[]=b&a[hasOwnProperty]=c&a[x]=y', { allowPrototypes: false }), { a: { 0: 'b', 1: 'c', x: 'y' } });
129
137
  st.deepEqual(qs.parse('a[]=b&a[hasOwnProperty]=c&a[x]=y', { allowPrototypes: true }), { a: { 0: 'b', hasOwnProperty: 'c', x: 'y' } });
130
138
  st.end();
131
139
  });
@@ -144,6 +152,8 @@ test('parse()', function (t) {
144
152
  st.end();
145
153
  });
146
154
 
155
+ t.deepEqual(qs.parse('a[b]=c&a=d'), { a: { b: 'c', d: true } }, 'can add keys to objects');
156
+
147
157
  t.test('correctly prunes undefined values when converting an array to an object', function (st) {
148
158
  st.deepEqual(qs.parse('a[2]=b&a[99999999]=c'), { a: { 2: 'b', 99999999: 'c' } });
149
159
  st.end();
@@ -208,10 +218,10 @@ test('parse()', function (t) {
208
218
  });
209
219
 
210
220
  t.test('compacts sparse arrays', function (st) {
211
- st.deepEqual(qs.parse('a[10]=1&a[2]=2'), { a: ['2', '1'] });
212
- st.deepEqual(qs.parse('a[1][b][2][c]=1'), { a: [{ b: [{ c: '1' }] }] });
213
- st.deepEqual(qs.parse('a[1][2][3][c]=1'), { a: [[[{ c: '1' }]]] });
214
- st.deepEqual(qs.parse('a[1][2][3][c][1]=1'), { a: [[[{ c: ['1'] }]]] });
221
+ st.deepEqual(qs.parse('a[10]=1&a[2]=2', { arrayLimit: 20 }), { a: ['2', '1'] });
222
+ st.deepEqual(qs.parse('a[1][b][2][c]=1', { arrayLimit: 20 }), { a: [{ b: [{ c: '1' }] }] });
223
+ st.deepEqual(qs.parse('a[1][2][3][c]=1', { arrayLimit: 20 }), { a: [[[{ c: '1' }]]] });
224
+ st.deepEqual(qs.parse('a[1][2][3][c][1]=1', { arrayLimit: 20 }), { a: [[[{ c: ['1'] }]]] });
215
225
  st.end();
216
226
  });
217
227
 
@@ -222,7 +232,7 @@ test('parse()', function (t) {
222
232
  });
223
233
 
224
234
  t.test('parses buffers correctly', function (st) {
225
- var b = SaferBuffer.from('test');
235
+ var b = new Buffer('test');
226
236
  st.deepEqual(qs.parse({ a: b }), { a: b });
227
237
  st.end();
228
238
  });
@@ -240,12 +250,14 @@ test('parse()', function (t) {
240
250
  str = str + '&' + str;
241
251
  }
242
252
 
243
- st.doesNotThrow(function () { qs.parse(str); });
253
+ st.doesNotThrow(function () {
254
+ qs.parse(str);
255
+ });
244
256
 
245
257
  st.end();
246
258
  });
247
259
 
248
- t.test('should not throw when a native prototype has an enumerable property', function (st) {
260
+ t.test('should not throw when a native prototype has an enumerable property', { parallel: false }, function (st) {
249
261
  Object.prototype.crash = '';
250
262
  Array.prototype.crash = '';
251
263
  st.doesNotThrow(qs.parse.bind(null, 'a=b'));
@@ -290,14 +302,7 @@ test('parse()', function (t) {
290
302
  });
291
303
 
292
304
  t.test('allows disabling array parsing', function (st) {
293
- var indices = qs.parse('a[0]=b&a[1]=c', { parseArrays: false });
294
- st.deepEqual(indices, { a: { 0: 'b', 1: 'c' } });
295
- st.equal(Array.isArray(indices.a), false, 'parseArrays:false, indices case is not an array');
296
-
297
- var emptyBrackets = qs.parse('a[]=b', { parseArrays: false });
298
- st.deepEqual(emptyBrackets, { a: { 0: 'b' } });
299
- st.equal(Array.isArray(emptyBrackets.a), false, 'parseArrays:false, empty brackets case is not an array');
300
-
305
+ st.deepEqual(qs.parse('a[0]=b&a[1]=c', { parseArrays: false }), { a: { 0: 'b', 1: 'c' } });
301
306
  st.end();
302
307
  });
303
308
 
@@ -385,7 +390,7 @@ test('parse()', function (t) {
385
390
  st.end();
386
391
  });
387
392
 
388
- t.test('parses plain objects correctly', function (st) {
393
+ t.test('parses null objects correctly', { skip: !Object.create }, function (st) {
389
394
  var a = Object.create(null);
390
395
  a.b = 'c';
391
396
 
@@ -408,131 +413,9 @@ test('parse()', function (t) {
408
413
  st.end();
409
414
  });
410
415
 
411
- t.test('does not allow overwriting prototype properties', function (st) {
412
- st.deepEqual(qs.parse('a[hasOwnProperty]=b', { allowPrototypes: false }), {});
413
- st.deepEqual(qs.parse('hasOwnProperty=b', { allowPrototypes: false }), {});
414
-
415
- st.deepEqual(
416
- qs.parse('toString', { allowPrototypes: false }),
417
- {},
418
- 'bare "toString" results in {}'
419
- );
420
-
421
- st.end();
422
- });
423
-
424
416
  t.test('can allow overwriting prototype properties', function (st) {
425
- st.deepEqual(qs.parse('a[hasOwnProperty]=b', { allowPrototypes: true }), { a: { hasOwnProperty: 'b' } });
426
- st.deepEqual(qs.parse('hasOwnProperty=b', { allowPrototypes: true }), { hasOwnProperty: 'b' });
427
-
428
- st.deepEqual(
429
- qs.parse('toString', { allowPrototypes: true }),
430
- { toString: '' },
431
- 'bare "toString" results in { toString: "" }'
432
- );
433
-
434
- st.end();
435
- });
436
-
437
- t.test('params starting with a closing bracket', function (st) {
438
- st.deepEqual(qs.parse(']=toString'), { ']': 'toString' });
439
- st.deepEqual(qs.parse(']]=toString'), { ']]': 'toString' });
440
- st.deepEqual(qs.parse(']hello]=toString'), { ']hello]': 'toString' });
441
- st.end();
442
- });
443
-
444
- t.test('params starting with a starting bracket', function (st) {
445
- st.deepEqual(qs.parse('[=toString'), { '[': 'toString' });
446
- st.deepEqual(qs.parse('[[=toString'), { '[[': 'toString' });
447
- st.deepEqual(qs.parse('[hello[=toString'), { '[hello[': 'toString' });
448
- st.end();
449
- });
450
-
451
- t.test('add keys to objects', function (st) {
452
- st.deepEqual(
453
- qs.parse('a[b]=c&a=d'),
454
- { a: { b: 'c', d: true } },
455
- 'can add keys to objects'
456
- );
457
-
458
- st.deepEqual(
459
- qs.parse('a[b]=c&a=toString'),
460
- { a: { b: 'c' } },
461
- 'can not overwrite prototype'
462
- );
463
-
464
- st.deepEqual(
465
- qs.parse('a[b]=c&a=toString', { allowPrototypes: true }),
466
- { a: { b: 'c', toString: true } },
467
- 'can overwrite prototype with allowPrototypes true'
468
- );
469
-
470
- st.deepEqual(
471
- qs.parse('a[b]=c&a=toString', { plainObjects: true }),
472
- { __proto__: null, a: { __proto__: null, b: 'c', toString: true } },
473
- 'can overwrite prototype with plainObjects true'
474
- );
475
-
476
- st.end();
477
- });
478
-
479
- t.test('dunder proto is ignored', function (st) {
480
- var payload = 'categories[__proto__]=login&categories[__proto__]&categories[length]=42';
481
- var result = qs.parse(payload, { allowPrototypes: true });
482
-
483
- st.deepEqual(
484
- result,
485
- {
486
- categories: {
487
- length: '42'
488
- }
489
- },
490
- 'silent [[Prototype]] payload'
491
- );
492
-
493
- var plainResult = qs.parse(payload, { allowPrototypes: true, plainObjects: true });
494
-
495
- st.deepEqual(
496
- plainResult,
497
- {
498
- __proto__: null,
499
- categories: {
500
- __proto__: null,
501
- length: '42'
502
- }
503
- },
504
- 'silent [[Prototype]] payload: plain objects'
505
- );
506
-
507
- var query = qs.parse('categories[__proto__]=cats&categories[__proto__]=dogs&categories[some][json]=toInject', { allowPrototypes: true });
508
-
509
- st.notOk(Array.isArray(query.categories), 'is not an array');
510
- st.notOk(query.categories instanceof Array, 'is not instanceof an array');
511
- st.deepEqual(query.categories, { some: { json: 'toInject' } });
512
- st.equal(JSON.stringify(query.categories), '{"some":{"json":"toInject"}}', 'stringifies as a non-array');
513
-
514
- st.deepEqual(
515
- qs.parse('foo[__proto__][hidden]=value&foo[bar]=stuffs', { allowPrototypes: true }),
516
- {
517
- foo: {
518
- bar: 'stuffs'
519
- }
520
- },
521
- 'hidden values'
522
- );
523
-
524
- st.deepEqual(
525
- qs.parse('foo[__proto__][hidden]=value&foo[bar]=stuffs', { allowPrototypes: true, plainObjects: true }),
526
- {
527
- __proto__: null,
528
- foo: {
529
- __proto__: null,
530
- bar: 'stuffs'
531
- }
532
- },
533
- 'hidden values: plain objects'
534
- );
535
-
417
+ st.deepEqual(qs.parse('a[hasOwnProperty]=b', { allowPrototypes: true }), { a: { hasOwnProperty: 'b' } }, { prototype: false });
418
+ st.deepEqual(qs.parse('hasOwnProperty=b', { allowPrototypes: true }), { hasOwnProperty: 'b' }, { prototype: false });
536
419
  st.end();
537
420
  });
538
421
 
@@ -545,7 +428,7 @@ test('parse()', function (t) {
545
428
  st.deepEqual(qs.parse(null, { plainObjects: true }), Object.create(null));
546
429
  var expectedArray = Object.create(null);
547
430
  expectedArray.a = Object.create(null);
548
- expectedArray.a['0'] = 'b';
431
+ expectedArray.a[0] = 'b';
549
432
  expectedArray.a.c = 'd';
550
433
  st.deepEqual(qs.parse('a[]=b&a[c]=d', { plainObjects: true }), expectedArray);
551
434
  st.end();
@@ -556,20 +439,19 @@ test('parse()', function (t) {
556
439
  decoder: function (str) {
557
440
  var reg = /%([0-9A-F]{2})/ig;
558
441
  var result = [];
559
- var parts;
560
- // var last = 0;
561
- while ((parts = reg.exec(str))) {
442
+ var parts = reg.exec(str);
443
+ while (parts) {
562
444
  result.push(parseInt(parts[1], 16));
563
- // last = parts.index + parts[0].length;
445
+ parts = reg.exec(str);
564
446
  }
565
- return iconv.decode(SaferBuffer.from(result), 'shift_jis').toString();
447
+ return iconv.decode(new Buffer(result), 'shift_jis').toString();
566
448
  }
567
449
  }), { 県: '大阪府' });
568
450
  st.end();
569
451
  });
570
452
 
571
453
  t.test('throws error with wrong decoder', function (st) {
572
- st['throws'](function () {
454
+ st.throws(function () {
573
455
  qs.parse({}, { decoder: 'string' });
574
456
  }, new TypeError('Decoder has to be a function.'));
575
457
  st.end();
package/test/stringify.js CHANGED
@@ -3,7 +3,6 @@
3
3
  var test = require('tape');
4
4
  var qs = require('../');
5
5
  var iconv = require('iconv-lite');
6
- var SaferBuffer = require('safer-buffer').Buffer;
7
6
 
8
7
  test('stringify()', function (t) {
9
8
  t.test('stringifies a querystring object', function (st) {
@@ -31,7 +30,21 @@ test('stringify()', function (t) {
31
30
  });
32
31
 
33
32
  t.test('stringifies an array value', function (st) {
34
- st.equal(qs.stringify({ a: ['b', 'c', 'd'] }), 'a%5B0%5D=b&a%5B1%5D=c&a%5B2%5D=d');
33
+ st.equal(
34
+ qs.stringify({ a: ['b', 'c', 'd'] }, { arrayFormat: 'indices' }),
35
+ 'a%5B0%5D=b&a%5B1%5D=c&a%5B2%5D=d',
36
+ 'indices => indices'
37
+ );
38
+ st.equal(
39
+ qs.stringify({ a: ['b', 'c', 'd'] }, { arrayFormat: 'brackets' }),
40
+ 'a%5B%5D=b&a%5B%5D=c&a%5B%5D=d',
41
+ 'brackets => brackets'
42
+ );
43
+ st.equal(
44
+ qs.stringify({ a: ['b', 'c', 'd'] }),
45
+ 'a%5B0%5D=b&a%5B1%5D=c&a%5B2%5D=d',
46
+ 'default => indices'
47
+ );
35
48
  st.end();
36
49
  });
37
50
 
@@ -51,29 +64,149 @@ test('stringify()', function (t) {
51
64
  });
52
65
 
53
66
  t.test('stringifies a nested array value', function (st) {
67
+ st.equal(qs.stringify({ a: { b: ['c', 'd'] } }, { arrayFormat: 'indices' }), 'a%5Bb%5D%5B0%5D=c&a%5Bb%5D%5B1%5D=d');
68
+ st.equal(qs.stringify({ a: { b: ['c', 'd'] } }, { arrayFormat: 'brackets' }), 'a%5Bb%5D%5B%5D=c&a%5Bb%5D%5B%5D=d');
54
69
  st.equal(qs.stringify({ a: { b: ['c', 'd'] } }), 'a%5Bb%5D%5B0%5D=c&a%5Bb%5D%5B1%5D=d');
55
70
  st.end();
56
71
  });
57
72
 
58
73
  t.test('stringifies a nested array value with dots notation', function (st) {
59
- st.equal(qs.stringify({ a: { b: ['c', 'd'] } }, { allowDots: true, encode: false }), 'a.b[0]=c&a.b[1]=d');
74
+ st.equal(
75
+ qs.stringify(
76
+ { a: { b: ['c', 'd'] } },
77
+ { allowDots: true, encode: false, arrayFormat: 'indices' }
78
+ ),
79
+ 'a.b[0]=c&a.b[1]=d',
80
+ 'indices: stringifies with dots + indices'
81
+ );
82
+ st.equal(
83
+ qs.stringify(
84
+ { a: { b: ['c', 'd'] } },
85
+ { allowDots: true, encode: false, arrayFormat: 'brackets' }
86
+ ),
87
+ 'a.b[]=c&a.b[]=d',
88
+ 'brackets: stringifies with dots + brackets'
89
+ );
90
+ st.equal(
91
+ qs.stringify(
92
+ { a: { b: ['c', 'd'] } },
93
+ { allowDots: true, encode: false }
94
+ ),
95
+ 'a.b[0]=c&a.b[1]=d',
96
+ 'default: stringifies with dots + indices'
97
+ );
60
98
  st.end();
61
99
  });
62
100
 
63
101
  t.test('stringifies an object inside an array', function (st) {
64
- st.equal(qs.stringify({ a: [{ b: 'c' }] }), 'a%5B0%5D%5Bb%5D=c');
65
- st.equal(qs.stringify({ a: [{ b: { c: [1] } }] }), 'a%5B0%5D%5Bb%5D%5Bc%5D%5B0%5D=1');
102
+ st.equal(
103
+ qs.stringify({ a: [{ b: 'c' }] }, { arrayFormat: 'indices' }),
104
+ 'a%5B0%5D%5Bb%5D=c',
105
+ 'indices => brackets'
106
+ );
107
+ st.equal(
108
+ qs.stringify({ a: [{ b: 'c' }] }, { arrayFormat: 'brackets' }),
109
+ 'a%5B%5D%5Bb%5D=c',
110
+ 'brackets => brackets'
111
+ );
112
+ st.equal(
113
+ qs.stringify({ a: [{ b: 'c' }] }),
114
+ 'a%5B0%5D%5Bb%5D=c',
115
+ 'default => indices'
116
+ );
117
+
118
+ st.equal(
119
+ qs.stringify({ a: [{ b: { c: [1] } }] }, { arrayFormat: 'indices' }),
120
+ 'a%5B0%5D%5Bb%5D%5Bc%5D%5B0%5D=1',
121
+ 'indices => indices'
122
+ );
123
+
124
+ st.equal(
125
+ qs.stringify({ a: [{ b: { c: [1] } }] }, { arrayFormat: 'brackets' }),
126
+ 'a%5B%5D%5Bb%5D%5Bc%5D%5B%5D=1',
127
+ 'brackets => brackets'
128
+ );
129
+
130
+ st.equal(
131
+ qs.stringify({ a: [{ b: { c: [1] } }] }),
132
+ 'a%5B0%5D%5Bb%5D%5Bc%5D%5B0%5D=1',
133
+ 'default => indices'
134
+ );
135
+
66
136
  st.end();
67
137
  });
68
138
 
69
139
  t.test('stringifies an array with mixed objects and primitives', function (st) {
70
- st.equal(qs.stringify({ a: [{ b: 1 }, 2, 3] }, { encode: false }), 'a[0][b]=1&a[1]=2&a[2]=3');
140
+ st.equal(
141
+ qs.stringify({ a: [{ b: 1 }, 2, 3] }, { encode: false, arrayFormat: 'indices' }),
142
+ 'a[0][b]=1&a[1]=2&a[2]=3',
143
+ 'indices => indices'
144
+ );
145
+ st.equal(
146
+ qs.stringify({ a: [{ b: 1 }, 2, 3] }, { encode: false, arrayFormat: 'brackets' }),
147
+ 'a[][b]=1&a[]=2&a[]=3',
148
+ 'brackets => brackets'
149
+ );
150
+ st.equal(
151
+ qs.stringify({ a: [{ b: 1 }, 2, 3] }, { encode: false }),
152
+ 'a[0][b]=1&a[1]=2&a[2]=3',
153
+ 'default => indices'
154
+ );
155
+
71
156
  st.end();
72
157
  });
73
158
 
74
159
  t.test('stringifies an object inside an array with dots notation', function (st) {
75
- st.equal(qs.stringify({ a: [{ b: 'c' }] }, { allowDots: true, encode: false }), 'a[0].b=c');
76
- st.equal(qs.stringify({ a: [{ b: { c: [1] } }] }, { allowDots: true, encode: false }), 'a[0].b.c[0]=1');
160
+ st.equal(
161
+ qs.stringify(
162
+ { a: [{ b: 'c' }] },
163
+ { allowDots: true, encode: false, arrayFormat: 'indices' }
164
+ ),
165
+ 'a[0].b=c',
166
+ 'indices => indices'
167
+ );
168
+ st.equal(
169
+ qs.stringify(
170
+ { a: [{ b: 'c' }] },
171
+ { allowDots: true, encode: false, arrayFormat: 'brackets' }
172
+ ),
173
+ 'a[].b=c',
174
+ 'brackets => brackets'
175
+ );
176
+ st.equal(
177
+ qs.stringify(
178
+ { a: [{ b: 'c' }] },
179
+ { allowDots: true, encode: false }
180
+ ),
181
+ 'a[0].b=c',
182
+ 'default => indices'
183
+ );
184
+
185
+ st.equal(
186
+ qs.stringify(
187
+ { a: [{ b: { c: [1] } }] },
188
+ { allowDots: true, encode: false, arrayFormat: 'indices' }
189
+ ),
190
+ 'a[0].b.c[0]=1',
191
+ 'indices => indices'
192
+ );
193
+ st.equal(
194
+ qs.stringify(
195
+ { a: [{ b: { c: [1] } }] },
196
+ { allowDots: true, encode: false, arrayFormat: 'brackets' }
197
+ ),
198
+ 'a[].b.c[]=1',
199
+ 'brackets => brackets'
200
+ );
201
+ st.equal(
202
+ qs.stringify(
203
+ { a: [{ b: { c: [1] } }] },
204
+ { allowDots: true, encode: false }
205
+ ),
206
+ 'a[0].b.c[0]=1',
207
+ 'default => indices'
208
+ );
209
+
77
210
  st.end();
78
211
  });
79
212
 
@@ -126,7 +259,7 @@ test('stringify()', function (t) {
126
259
  st.end();
127
260
  });
128
261
 
129
- t.test('stringifies an empty object', function (st) {
262
+ t.test('stringifies a null object', { skip: !Object.create }, function (st) {
130
263
  var obj = Object.create(null);
131
264
  obj.a = 'b';
132
265
  st.equal(qs.stringify(obj), 'a=b');
@@ -141,10 +274,8 @@ test('stringify()', function (t) {
141
274
  st.end();
142
275
  });
143
276
 
144
- t.test('stringifies an object with an empty object as a child', function (st) {
145
- var obj = {
146
- a: Object.create(null)
147
- };
277
+ t.test('stringifies an object with a null object as a child', { skip: !Object.create }, function (st) {
278
+ var obj = { a: Object.create(null) };
148
279
 
149
280
  obj.a.b = 'c';
150
281
  st.equal(qs.stringify(obj), 'a%5Bb%5D=c');
@@ -194,8 +325,8 @@ test('stringify()', function (t) {
194
325
  });
195
326
 
196
327
  t.test('stringifies buffer values', function (st) {
197
- st.equal(qs.stringify({ a: SaferBuffer.from('test') }), 'a=test');
198
- st.equal(qs.stringify({ a: { b: SaferBuffer.from('test') } }), 'a%5Bb%5D=test');
328
+ st.equal(qs.stringify({ a: new Buffer('test') }), 'a=test');
329
+ st.equal(qs.stringify({ a: { b: new Buffer('test') } }), 'a%5Bb%5D=test');
199
330
  st.end();
200
331
  });
201
332
 
@@ -216,7 +347,32 @@ test('stringify()', function (t) {
216
347
  t.test('selects properties when filter=array', function (st) {
217
348
  st.equal(qs.stringify({ a: 'b' }, { filter: ['a'] }), 'a=b');
218
349
  st.equal(qs.stringify({ a: 1 }, { filter: [] }), '');
219
- st.equal(qs.stringify({ a: { b: [1, 2, 3, 4], c: 'd' }, c: 'f' }, { filter: ['a', 'b', 0, 2] }), 'a%5Bb%5D%5B0%5D=1&a%5Bb%5D%5B2%5D=3');
350
+
351
+ st.equal(
352
+ qs.stringify(
353
+ { a: { b: [1, 2, 3, 4], c: 'd' }, c: 'f' },
354
+ { filter: ['a', 'b', 0, 2], arrayFormat: 'indices' }
355
+ ),
356
+ 'a%5Bb%5D%5B0%5D=1&a%5Bb%5D%5B2%5D=3',
357
+ 'indices => indices'
358
+ );
359
+ st.equal(
360
+ qs.stringify(
361
+ { a: { b: [1, 2, 3, 4], c: 'd' }, c: 'f' },
362
+ { filter: ['a', 'b', 0, 2], arrayFormat: 'brackets' }
363
+ ),
364
+ 'a%5Bb%5D%5B%5D=1&a%5Bb%5D%5B%5D=3',
365
+ 'brackets => brackets'
366
+ );
367
+ st.equal(
368
+ qs.stringify(
369
+ { a: { b: [1, 2, 3, 4], c: 'd' }, c: 'f' },
370
+ { filter: ['a', 'b', 0, 2] }
371
+ ),
372
+ 'a%5Bb%5D%5B0%5D=1&a%5Bb%5D%5B2%5D=3',
373
+ 'default => indices'
374
+ );
375
+
220
376
  st.end();
221
377
  });
222
378
 
@@ -250,16 +406,32 @@ test('stringify()', function (t) {
250
406
  });
251
407
 
252
408
  t.test('can sort the keys', function (st) {
253
- var sort = function (a, b) { return a.localeCompare(b); };
409
+ var sort = function (a, b) {
410
+ return a.localeCompare(b);
411
+ };
254
412
  st.equal(qs.stringify({ a: 'c', z: 'y', b: 'f' }, { sort: sort }), 'a=c&b=f&z=y');
255
413
  st.equal(qs.stringify({ a: 'c', z: { j: 'a', i: 'b' }, b: 'f' }, { sort: sort }), 'a=c&b=f&z%5Bi%5D=b&z%5Bj%5D=a');
256
414
  st.end();
257
415
  });
258
416
 
259
417
  t.test('can sort the keys at depth 3 or more too', function (st) {
260
- var sort = function (a, b) { return a.localeCompare(b); };
261
- st.equal(qs.stringify({ a: 'a', z: { zj: { zjb: 'zjb', zja: 'zja' }, zi: { zib: 'zib', zia: 'zia' } }, b: 'b' }, { sort: sort, encode: false }), 'a=a&b=b&z[zi][zia]=zia&z[zi][zib]=zib&z[zj][zja]=zja&z[zj][zjb]=zjb');
262
- st.equal(qs.stringify({ a: 'a', z: { zj: { zjb: 'zjb', zja: 'zja' }, zi: { zib: 'zib', zia: 'zia' } }, b: 'b' }, { sort: null, encode: false }), 'a=a&z[zj][zjb]=zjb&z[zj][zja]=zja&z[zi][zib]=zib&z[zi][zia]=zia&b=b');
418
+ var sort = function (a, b) {
419
+ return a.localeCompare(b);
420
+ };
421
+ st.equal(
422
+ qs.stringify(
423
+ { a: 'a', z: { zj: { zjb: 'zjb', zja: 'zja' }, zi: { zib: 'zib', zia: 'zia' } }, b: 'b' },
424
+ { sort: sort, encode: false }
425
+ ),
426
+ 'a=a&b=b&z[zi][zia]=zia&z[zi][zib]=zib&z[zj][zja]=zja&z[zj][zjb]=zjb'
427
+ );
428
+ st.equal(
429
+ qs.stringify(
430
+ { a: 'a', z: { zj: { zjb: 'zjb', zja: 'zja' }, zi: { zib: 'zib', zia: 'zia' } }, b: 'b' },
431
+ { sort: null, encode: false }
432
+ ),
433
+ 'a=a&z[zj][zjb]=zjb&z[zj][zja]=zja&z[zi][zib]=zib&z[zi][zia]=zia&b=b'
434
+ );
263
435
  st.end();
264
436
  });
265
437
 
@@ -281,16 +453,14 @@ test('stringify()', function (t) {
281
453
  });
282
454
 
283
455
  t.test('throws error with wrong encoder', function (st) {
284
- st['throws'](function () {
285
- qs.stringify({}, {
286
- encoder: 'string'
287
- });
456
+ st.throws(function () {
457
+ qs.stringify({}, { encoder: 'string' });
288
458
  }, new TypeError('Encoder has to be a function.'));
289
459
  st.end();
290
460
  });
291
461
 
292
462
  t.test('can use custom encoder for a buffer object', { skip: typeof Buffer === 'undefined' }, function (st) {
293
- st.equal(qs.stringify({ a: SaferBuffer.from([1]) }, {
463
+ st.equal(qs.stringify({ a: new Buffer([1]) }, {
294
464
  encoder: function (buffer) {
295
465
  if (typeof buffer === 'string') {
296
466
  return buffer;
@@ -300,4 +470,69 @@ test('stringify()', function (t) {
300
470
  }), 'a=b');
301
471
  st.end();
302
472
  });
473
+
474
+ t.test('serializeDate option', function (st) {
475
+ var date = new Date();
476
+ st.equal(
477
+ qs.stringify({ a: date }),
478
+ 'a=' + date.toISOString().replace(/:/g, '%3A'),
479
+ 'default is toISOString'
480
+ );
481
+
482
+ var mutatedDate = new Date();
483
+ mutatedDate.toISOString = function () {
484
+ throw new SyntaxError();
485
+ };
486
+ st.throws(function () {
487
+ mutatedDate.toISOString();
488
+ }, SyntaxError);
489
+ st.equal(
490
+ qs.stringify({ a: mutatedDate }),
491
+ 'a=' + Date.prototype.toISOString.call(mutatedDate).replace(/:/g, '%3A'),
492
+ 'toISOString works even when method is not locally present'
493
+ );
494
+
495
+ var specificDate = new Date(6);
496
+ st.equal(
497
+ qs.stringify(
498
+ { a: specificDate },
499
+ { serializeDate: function (d) { return d.getTime() * 7; } }
500
+ ),
501
+ 'a=42',
502
+ 'custom serializeDate function called'
503
+ );
504
+
505
+ st.end();
506
+ });
507
+
508
+ t.test('RFC 1738 spaces serialization', function (st) {
509
+ st.equal(qs.stringify({ a: 'b c' }, { format: qs.formats.RFC1738 }), 'a=b+c');
510
+ st.equal(qs.stringify({ 'a b': 'c d' }, { format: qs.formats.RFC1738 }), 'a+b=c+d');
511
+ st.end();
512
+ });
513
+
514
+ t.test('RFC 3986 spaces serialization', function (st) {
515
+ st.equal(qs.stringify({ a: 'b c' }, { format: qs.formats.RFC3986 }), 'a=b%20c');
516
+ st.equal(qs.stringify({ 'a b': 'c d' }, { format: qs.formats.RFC3986 }), 'a%20b=c%20d');
517
+ st.end();
518
+ });
519
+
520
+ t.test('Backward compatibility to RFC 3986', function (st) {
521
+ st.equal(qs.stringify({ a: 'b c' }), 'a=b%20c');
522
+ st.end();
523
+ });
524
+
525
+ t.test('Edge cases and unknown formats', function (st) {
526
+ ['UFO1234', false, 1234, null, {}, []].forEach(
527
+ function (format) {
528
+ st.throws(
529
+ function () {
530
+ qs.stringify({ a: 'b c' }, { format: format });
531
+ },
532
+ new TypeError('Unknown format option provided.')
533
+ );
534
+ }
535
+ );
536
+ st.end();
537
+ });
303
538
  });