Projekt

Obecné

Profil

Stáhnout (23.5 KB) Statistiky
| Větev: | Revize:
1
'use strict';
2

    
3
var test = require('tape');
4
var qs = require('../');
5
var utils = require('../lib/utils');
6
var iconv = require('iconv-lite');
7
var SaferBuffer = require('safer-buffer').Buffer;
8

    
9
test('stringify()', function (t) {
10
    t.test('stringifies a querystring object', function (st) {
11
        st.equal(qs.stringify({ a: 'b' }), 'a=b');
12
        st.equal(qs.stringify({ a: 1 }), 'a=1');
13
        st.equal(qs.stringify({ a: 1, b: 2 }), 'a=1&b=2');
14
        st.equal(qs.stringify({ a: 'A_Z' }), 'a=A_Z');
15
        st.equal(qs.stringify({ a: '' }), 'a=%E2%82%AC');
16
        st.equal(qs.stringify({ a: '' }), 'a=%EE%80%80');
17
        st.equal(qs.stringify({ a: 'א' }), 'a=%D7%90');
18
        st.equal(qs.stringify({ a: '𐐷' }), 'a=%F0%90%90%B7');
19
        st.end();
20
    });
21

    
22
    t.test('stringifies falsy values', function (st) {
23
        st.equal(qs.stringify(undefined), '');
24
        st.equal(qs.stringify(null), '');
25
        st.equal(qs.stringify(null, { strictNullHandling: true }), '');
26
        st.equal(qs.stringify(false), '');
27
        st.equal(qs.stringify(0), '');
28
        st.end();
29
    });
30

    
31
    t.test('adds query prefix', function (st) {
32
        st.equal(qs.stringify({ a: 'b' }, { addQueryPrefix: true }), '?a=b');
33
        st.end();
34
    });
35

    
36
    t.test('with query prefix, outputs blank string given an empty object', function (st) {
37
        st.equal(qs.stringify({}, { addQueryPrefix: true }), '');
38
        st.end();
39
    });
40

    
41
    t.test('stringifies nested falsy values', function (st) {
42
        st.equal(qs.stringify({ a: { b: { c: null } } }), 'a%5Bb%5D%5Bc%5D=');
43
        st.equal(qs.stringify({ a: { b: { c: null } } }, { strictNullHandling: true }), 'a%5Bb%5D%5Bc%5D');
44
        st.equal(qs.stringify({ a: { b: { c: false } } }), 'a%5Bb%5D%5Bc%5D=false');
45
        st.end();
46
    });
47

    
48
    t.test('stringifies a nested object', function (st) {
49
        st.equal(qs.stringify({ a: { b: 'c' } }), 'a%5Bb%5D=c');
50
        st.equal(qs.stringify({ a: { b: { c: { d: 'e' } } } }), 'a%5Bb%5D%5Bc%5D%5Bd%5D=e');
51
        st.end();
52
    });
53

    
54
    t.test('stringifies a nested object with dots notation', function (st) {
55
        st.equal(qs.stringify({ a: { b: 'c' } }, { allowDots: true }), 'a.b=c');
56
        st.equal(qs.stringify({ a: { b: { c: { d: 'e' } } } }, { allowDots: true }), 'a.b.c.d=e');
57
        st.end();
58
    });
59

    
60
    t.test('stringifies an array value', function (st) {
61
        st.equal(
62
            qs.stringify({ a: ['b', 'c', 'd'] }, { arrayFormat: 'indices' }),
63
            'a%5B0%5D=b&a%5B1%5D=c&a%5B2%5D=d',
64
            'indices => indices'
65
        );
66
        st.equal(
67
            qs.stringify({ a: ['b', 'c', 'd'] }, { arrayFormat: 'brackets' }),
68
            'a%5B%5D=b&a%5B%5D=c&a%5B%5D=d',
69
            'brackets => brackets'
70
        );
71
        st.equal(
72
            qs.stringify({ a: ['b', 'c', 'd'] }, { arrayFormat: 'comma' }),
73
            'a=b%2Cc%2Cd',
74
            'comma => comma'
75
        );
76
        st.equal(
77
            qs.stringify({ a: ['b', 'c', 'd'] }),
78
            'a%5B0%5D=b&a%5B1%5D=c&a%5B2%5D=d',
79
            'default => indices'
80
        );
81
        st.end();
82
    });
83

    
84
    t.test('omits nulls when asked', function (st) {
85
        st.equal(qs.stringify({ a: 'b', c: null }, { skipNulls: true }), 'a=b');
86
        st.end();
87
    });
88

    
89
    t.test('omits nested nulls when asked', function (st) {
90
        st.equal(qs.stringify({ a: { b: 'c', d: null } }, { skipNulls: true }), 'a%5Bb%5D=c');
91
        st.end();
92
    });
93

    
94
    t.test('omits array indices when asked', function (st) {
95
        st.equal(qs.stringify({ a: ['b', 'c', 'd'] }, { indices: false }), 'a=b&a=c&a=d');
96
        st.end();
97
    });
98

    
99
    t.test('stringifies a nested array value', function (st) {
100
        st.equal(qs.stringify({ a: { b: ['c', 'd'] } }, { arrayFormat: 'indices' }), 'a%5Bb%5D%5B0%5D=c&a%5Bb%5D%5B1%5D=d');
101
        st.equal(qs.stringify({ a: { b: ['c', 'd'] } }, { arrayFormat: 'brackets' }), 'a%5Bb%5D%5B%5D=c&a%5Bb%5D%5B%5D=d');
102
        st.equal(qs.stringify({ a: { b: ['c', 'd'] } }, { arrayFormat: 'comma' }), 'a%5Bb%5D=c%2Cd'); // a[b]=c,d
103
        st.equal(qs.stringify({ a: { b: ['c', 'd'] } }), 'a%5Bb%5D%5B0%5D=c&a%5Bb%5D%5B1%5D=d');
104
        st.end();
105
    });
106

    
107
    t.test('stringifies a nested array value with dots notation', function (st) {
108
        st.equal(
109
            qs.stringify(
110
                { a: { b: ['c', 'd'] } },
111
                { allowDots: true, encode: false, arrayFormat: 'indices' }
112
            ),
113
            'a.b[0]=c&a.b[1]=d',
114
            'indices: stringifies with dots + indices'
115
        );
116
        st.equal(
117
            qs.stringify(
118
                { a: { b: ['c', 'd'] } },
119
                { allowDots: true, encode: false, arrayFormat: 'brackets' }
120
            ),
121
            'a.b[]=c&a.b[]=d',
122
            'brackets: stringifies with dots + brackets'
123
        );
124
        st.equal(
125
            qs.stringify(
126
                { a: { b: ['c', 'd'] } },
127
                { allowDots: true, encode: false, arrayFormat: 'comma' }
128
            ),
129
            'a.b=c,d',
130
            'comma: stringifies with dots + comma'
131
        );
132
        st.equal(
133
            qs.stringify(
134
                { a: { b: ['c', 'd'] } },
135
                { allowDots: true, encode: false }
136
            ),
137
            'a.b[0]=c&a.b[1]=d',
138
            'default: stringifies with dots + indices'
139
        );
140
        st.end();
141
    });
142

    
143
    t.test('stringifies an object inside an array', function (st) {
144
        st.equal(
145
            qs.stringify({ a: [{ b: 'c' }] }, { arrayFormat: 'indices' }),
146
            'a%5B0%5D%5Bb%5D=c', // a[0][b]=c
147
            'indices => brackets'
148
        );
149
        st.equal(
150
            qs.stringify({ a: [{ b: 'c' }] }, { arrayFormat: 'brackets' }),
151
            'a%5B%5D%5Bb%5D=c', // a[][b]=c
152
            'brackets => brackets'
153
        );
154
        st.equal(
155
            qs.stringify({ a: [{ b: 'c' }] }),
156
            'a%5B0%5D%5Bb%5D=c',
157
            'default => indices'
158
        );
159

    
160
        st.equal(
161
            qs.stringify({ a: [{ b: { c: [1] } }] }, { arrayFormat: 'indices' }),
162
            'a%5B0%5D%5Bb%5D%5Bc%5D%5B0%5D=1',
163
            'indices => indices'
164
        );
165

    
166
        st.equal(
167
            qs.stringify({ a: [{ b: { c: [1] } }] }, { arrayFormat: 'brackets' }),
168
            'a%5B%5D%5Bb%5D%5Bc%5D%5B%5D=1',
169
            'brackets => brackets'
170
        );
171

    
172
        st.equal(
173
            qs.stringify({ a: [{ b: { c: [1] } }] }),
174
            'a%5B0%5D%5Bb%5D%5Bc%5D%5B0%5D=1',
175
            'default => indices'
176
        );
177

    
178
        st.end();
179
    });
180

    
181
    t.test('stringifies an array with mixed objects and primitives', function (st) {
182
        st.equal(
183
            qs.stringify({ a: [{ b: 1 }, 2, 3] }, { encode: false, arrayFormat: 'indices' }),
184
            'a[0][b]=1&a[1]=2&a[2]=3',
185
            'indices => indices'
186
        );
187
        st.equal(
188
            qs.stringify({ a: [{ b: 1 }, 2, 3] }, { encode: false, arrayFormat: 'brackets' }),
189
            'a[][b]=1&a[]=2&a[]=3',
190
            'brackets => brackets'
191
        );
192
        st.equal(
193
            qs.stringify({ a: [{ b: 1 }, 2, 3] }, { encode: false }),
194
            'a[0][b]=1&a[1]=2&a[2]=3',
195
            'default => indices'
196
        );
197

    
198
        st.end();
199
    });
200

    
201
    t.test('stringifies an object inside an array with dots notation', function (st) {
202
        st.equal(
203
            qs.stringify(
204
                { a: [{ b: 'c' }] },
205
                { allowDots: true, encode: false, arrayFormat: 'indices' }
206
            ),
207
            'a[0].b=c',
208
            'indices => indices'
209
        );
210
        st.equal(
211
            qs.stringify(
212
                { a: [{ b: 'c' }] },
213
                { allowDots: true, encode: false, arrayFormat: 'brackets' }
214
            ),
215
            'a[].b=c',
216
            'brackets => brackets'
217
        );
218
        st.equal(
219
            qs.stringify(
220
                { a: [{ b: 'c' }] },
221
                { allowDots: true, encode: false }
222
            ),
223
            'a[0].b=c',
224
            'default => indices'
225
        );
226

    
227
        st.equal(
228
            qs.stringify(
229
                { a: [{ b: { c: [1] } }] },
230
                { allowDots: true, encode: false, arrayFormat: 'indices' }
231
            ),
232
            'a[0].b.c[0]=1',
233
            'indices => indices'
234
        );
235
        st.equal(
236
            qs.stringify(
237
                { a: [{ b: { c: [1] } }] },
238
                { allowDots: true, encode: false, arrayFormat: 'brackets' }
239
            ),
240
            'a[].b.c[]=1',
241
            'brackets => brackets'
242
        );
243
        st.equal(
244
            qs.stringify(
245
                { a: [{ b: { c: [1] } }] },
246
                { allowDots: true, encode: false }
247
            ),
248
            'a[0].b.c[0]=1',
249
            'default => indices'
250
        );
251

    
252
        st.end();
253
    });
254

    
255
    t.test('does not omit object keys when indices = false', function (st) {
256
        st.equal(qs.stringify({ a: [{ b: 'c' }] }, { indices: false }), 'a%5Bb%5D=c');
257
        st.end();
258
    });
259

    
260
    t.test('uses indices notation for arrays when indices=true', function (st) {
261
        st.equal(qs.stringify({ a: ['b', 'c'] }, { indices: true }), 'a%5B0%5D=b&a%5B1%5D=c');
262
        st.end();
263
    });
264

    
265
    t.test('uses indices notation for arrays when no arrayFormat is specified', function (st) {
266
        st.equal(qs.stringify({ a: ['b', 'c'] }), 'a%5B0%5D=b&a%5B1%5D=c');
267
        st.end();
268
    });
269

    
270
    t.test('uses indices notation for arrays when no arrayFormat=indices', function (st) {
271
        st.equal(qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'indices' }), 'a%5B0%5D=b&a%5B1%5D=c');
272
        st.end();
273
    });
274

    
275
    t.test('uses repeat notation for arrays when no arrayFormat=repeat', function (st) {
276
        st.equal(qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'repeat' }), 'a=b&a=c');
277
        st.end();
278
    });
279

    
280
    t.test('uses brackets notation for arrays when no arrayFormat=brackets', function (st) {
281
        st.equal(qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'brackets' }), 'a%5B%5D=b&a%5B%5D=c');
282
        st.end();
283
    });
284

    
285
    t.test('stringifies a complicated object', function (st) {
286
        st.equal(qs.stringify({ a: { b: 'c', d: 'e' } }), 'a%5Bb%5D=c&a%5Bd%5D=e');
287
        st.end();
288
    });
289

    
290
    t.test('stringifies an empty value', function (st) {
291
        st.equal(qs.stringify({ a: '' }), 'a=');
292
        st.equal(qs.stringify({ a: null }, { strictNullHandling: true }), 'a');
293

    
294
        st.equal(qs.stringify({ a: '', b: '' }), 'a=&b=');
295
        st.equal(qs.stringify({ a: null, b: '' }, { strictNullHandling: true }), 'a&b=');
296

    
297
        st.equal(qs.stringify({ a: { b: '' } }), 'a%5Bb%5D=');
298
        st.equal(qs.stringify({ a: { b: null } }, { strictNullHandling: true }), 'a%5Bb%5D');
299
        st.equal(qs.stringify({ a: { b: null } }, { strictNullHandling: false }), 'a%5Bb%5D=');
300

    
301
        st.end();
302
    });
303

    
304
    t.test('stringifies a null object', { skip: !Object.create }, function (st) {
305
        var obj = Object.create(null);
306
        obj.a = 'b';
307
        st.equal(qs.stringify(obj), 'a=b');
308
        st.end();
309
    });
310

    
311
    t.test('returns an empty string for invalid input', function (st) {
312
        st.equal(qs.stringify(undefined), '');
313
        st.equal(qs.stringify(false), '');
314
        st.equal(qs.stringify(null), '');
315
        st.equal(qs.stringify(''), '');
316
        st.end();
317
    });
318

    
319
    t.test('stringifies an object with a null object as a child', { skip: !Object.create }, function (st) {
320
        var obj = { a: Object.create(null) };
321

    
322
        obj.a.b = 'c';
323
        st.equal(qs.stringify(obj), 'a%5Bb%5D=c');
324
        st.end();
325
    });
326

    
327
    t.test('drops keys with a value of undefined', function (st) {
328
        st.equal(qs.stringify({ a: undefined }), '');
329

    
330
        st.equal(qs.stringify({ a: { b: undefined, c: null } }, { strictNullHandling: true }), 'a%5Bc%5D');
331
        st.equal(qs.stringify({ a: { b: undefined, c: null } }, { strictNullHandling: false }), 'a%5Bc%5D=');
332
        st.equal(qs.stringify({ a: { b: undefined, c: '' } }), 'a%5Bc%5D=');
333
        st.end();
334
    });
335

    
336
    t.test('url encodes values', function (st) {
337
        st.equal(qs.stringify({ a: 'b c' }), 'a=b%20c');
338
        st.end();
339
    });
340

    
341
    t.test('stringifies a date', function (st) {
342
        var now = new Date();
343
        var str = 'a=' + encodeURIComponent(now.toISOString());
344
        st.equal(qs.stringify({ a: now }), str);
345
        st.end();
346
    });
347

    
348
    t.test('stringifies the weird object from qs', function (st) {
349
        st.equal(qs.stringify({ 'my weird field': '~q1!2"\'w$5&7/z8)?' }), 'my%20weird%20field=~q1%212%22%27w%245%267%2Fz8%29%3F');
350
        st.end();
351
    });
352

    
353
    t.test('skips properties that are part of the object prototype', function (st) {
354
        Object.prototype.crash = 'test';
355
        st.equal(qs.stringify({ a: 'b' }), 'a=b');
356
        st.equal(qs.stringify({ a: { b: 'c' } }), 'a%5Bb%5D=c');
357
        delete Object.prototype.crash;
358
        st.end();
359
    });
360

    
361
    t.test('stringifies boolean values', function (st) {
362
        st.equal(qs.stringify({ a: true }), 'a=true');
363
        st.equal(qs.stringify({ a: { b: true } }), 'a%5Bb%5D=true');
364
        st.equal(qs.stringify({ b: false }), 'b=false');
365
        st.equal(qs.stringify({ b: { c: false } }), 'b%5Bc%5D=false');
366
        st.end();
367
    });
368

    
369
    t.test('stringifies buffer values', function (st) {
370
        st.equal(qs.stringify({ a: SaferBuffer.from('test') }), 'a=test');
371
        st.equal(qs.stringify({ a: { b: SaferBuffer.from('test') } }), 'a%5Bb%5D=test');
372
        st.end();
373
    });
374

    
375
    t.test('stringifies an object using an alternative delimiter', function (st) {
376
        st.equal(qs.stringify({ a: 'b', c: 'd' }, { delimiter: ';' }), 'a=b;c=d');
377
        st.end();
378
    });
379

    
380
    t.test('doesn\'t blow up when Buffer global is missing', function (st) {
381
        var tempBuffer = global.Buffer;
382
        delete global.Buffer;
383
        var result = qs.stringify({ a: 'b', c: 'd' });
384
        global.Buffer = tempBuffer;
385
        st.equal(result, 'a=b&c=d');
386
        st.end();
387
    });
388

    
389
    t.test('selects properties when filter=array', function (st) {
390
        st.equal(qs.stringify({ a: 'b' }, { filter: ['a'] }), 'a=b');
391
        st.equal(qs.stringify({ a: 1 }, { filter: [] }), '');
392

    
393
        st.equal(
394
            qs.stringify(
395
                { a: { b: [1, 2, 3, 4], c: 'd' }, c: 'f' },
396
                { filter: ['a', 'b', 0, 2], arrayFormat: 'indices' }
397
            ),
398
            'a%5Bb%5D%5B0%5D=1&a%5Bb%5D%5B2%5D=3',
399
            'indices => indices'
400
        );
401
        st.equal(
402
            qs.stringify(
403
                { a: { b: [1, 2, 3, 4], c: 'd' }, c: 'f' },
404
                { filter: ['a', 'b', 0, 2], arrayFormat: 'brackets' }
405
            ),
406
            'a%5Bb%5D%5B%5D=1&a%5Bb%5D%5B%5D=3',
407
            'brackets => brackets'
408
        );
409
        st.equal(
410
            qs.stringify(
411
                { a: { b: [1, 2, 3, 4], c: 'd' }, c: 'f' },
412
                { filter: ['a', 'b', 0, 2] }
413
            ),
414
            'a%5Bb%5D%5B0%5D=1&a%5Bb%5D%5B2%5D=3',
415
            'default => indices'
416
        );
417

    
418
        st.end();
419
    });
420

    
421
    t.test('supports custom representations when filter=function', function (st) {
422
        var calls = 0;
423
        var obj = { a: 'b', c: 'd', e: { f: new Date(1257894000000) } };
424
        var filterFunc = function (prefix, value) {
425
            calls += 1;
426
            if (calls === 1) {
427
                st.equal(prefix, '', 'prefix is empty');
428
                st.equal(value, obj);
429
            } else if (prefix === 'c') {
430
                return void 0;
431
            } else if (value instanceof Date) {
432
                st.equal(prefix, 'e[f]');
433
                return value.getTime();
434
            }
435
            return value;
436
        };
437

    
438
        st.equal(qs.stringify(obj, { filter: filterFunc }), 'a=b&e%5Bf%5D=1257894000000');
439
        st.equal(calls, 5);
440
        st.end();
441
    });
442

    
443
    t.test('can disable uri encoding', function (st) {
444
        st.equal(qs.stringify({ a: 'b' }, { encode: false }), 'a=b');
445
        st.equal(qs.stringify({ a: { b: 'c' } }, { encode: false }), 'a[b]=c');
446
        st.equal(qs.stringify({ a: 'b', c: null }, { strictNullHandling: true, encode: false }), 'a=b&c');
447
        st.end();
448
    });
449

    
450
    t.test('can sort the keys', function (st) {
451
        var sort = function (a, b) {
452
            return a.localeCompare(b);
453
        };
454
        st.equal(qs.stringify({ a: 'c', z: 'y', b: 'f' }, { sort: sort }), 'a=c&b=f&z=y');
455
        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');
456
        st.end();
457
    });
458

    
459
    t.test('can sort the keys at depth 3 or more too', function (st) {
460
        var sort = function (a, b) {
461
            return a.localeCompare(b);
462
        };
463
        st.equal(
464
            qs.stringify(
465
                { a: 'a', z: { zj: { zjb: 'zjb', zja: 'zja' }, zi: { zib: 'zib', zia: 'zia' } }, b: 'b' },
466
                { sort: sort, encode: false }
467
            ),
468
            'a=a&b=b&z[zi][zia]=zia&z[zi][zib]=zib&z[zj][zja]=zja&z[zj][zjb]=zjb'
469
        );
470
        st.equal(
471
            qs.stringify(
472
                { a: 'a', z: { zj: { zjb: 'zjb', zja: 'zja' }, zi: { zib: 'zib', zia: 'zia' } }, b: 'b' },
473
                { sort: null, encode: false }
474
            ),
475
            'a=a&z[zj][zjb]=zjb&z[zj][zja]=zja&z[zi][zib]=zib&z[zi][zia]=zia&b=b'
476
        );
477
        st.end();
478
    });
479

    
480
    t.test('can stringify with custom encoding', function (st) {
481
        st.equal(qs.stringify({ : '大阪府', '': '' }, {
482
            encoder: function (str) {
483
                if (str.length === 0) {
484
                    return '';
485
                }
486
                var buf = iconv.encode(str, 'shiftjis');
487
                var result = [];
488
                for (var i = 0; i < buf.length; ++i) {
489
                    result.push(buf.readUInt8(i).toString(16));
490
                }
491
                return '%' + result.join('%');
492
            }
493
        }), '%8c%a7=%91%e5%8d%e3%95%7b&=');
494
        st.end();
495
    });
496

    
497
    t.test('receives the default encoder as a second argument', function (st) {
498
        st.plan(2);
499
        qs.stringify({ a: 1 }, {
500
            encoder: function (str, defaultEncoder) {
501
                st.equal(defaultEncoder, utils.encode);
502
            }
503
        });
504
        st.end();
505
    });
506

    
507
    t.test('throws error with wrong encoder', function (st) {
508
        st['throws'](function () {
509
            qs.stringify({}, { encoder: 'string' });
510
        }, new TypeError('Encoder has to be a function.'));
511
        st.end();
512
    });
513

    
514
    t.test('can use custom encoder for a buffer object', { skip: typeof Buffer === 'undefined' }, function (st) {
515
        st.equal(qs.stringify({ a: SaferBuffer.from([1]) }, {
516
            encoder: function (buffer) {
517
                if (typeof buffer === 'string') {
518
                    return buffer;
519
                }
520
                return String.fromCharCode(buffer.readUInt8(0) + 97);
521
            }
522
        }), 'a=b');
523
        st.end();
524
    });
525

    
526
    t.test('serializeDate option', function (st) {
527
        var date = new Date();
528
        st.equal(
529
            qs.stringify({ a: date }),
530
            'a=' + date.toISOString().replace(/:/g, '%3A'),
531
            'default is toISOString'
532
        );
533

    
534
        var mutatedDate = new Date();
535
        mutatedDate.toISOString = function () {
536
            throw new SyntaxError();
537
        };
538
        st['throws'](function () {
539
            mutatedDate.toISOString();
540
        }, SyntaxError);
541
        st.equal(
542
            qs.stringify({ a: mutatedDate }),
543
            'a=' + Date.prototype.toISOString.call(mutatedDate).replace(/:/g, '%3A'),
544
            'toISOString works even when method is not locally present'
545
        );
546

    
547
        var specificDate = new Date(6);
548
        st.equal(
549
            qs.stringify(
550
                { a: specificDate },
551
                { serializeDate: function (d) { return d.getTime() * 7; } }
552
            ),
553
            'a=42',
554
            'custom serializeDate function called'
555
        );
556

    
557
        st.end();
558
    });
559

    
560
    t.test('RFC 1738 spaces serialization', function (st) {
561
        st.equal(qs.stringify({ a: 'b c' }, { format: qs.formats.RFC1738 }), 'a=b+c');
562
        st.equal(qs.stringify({ 'a b': 'c d' }, { format: qs.formats.RFC1738 }), 'a+b=c+d');
563
        st.end();
564
    });
565

    
566
    t.test('RFC 3986 spaces serialization', function (st) {
567
        st.equal(qs.stringify({ a: 'b c' }, { format: qs.formats.RFC3986 }), 'a=b%20c');
568
        st.equal(qs.stringify({ 'a b': 'c d' }, { format: qs.formats.RFC3986 }), 'a%20b=c%20d');
569
        st.end();
570
    });
571

    
572
    t.test('Backward compatibility to RFC 3986', function (st) {
573
        st.equal(qs.stringify({ a: 'b c' }), 'a=b%20c');
574
        st.end();
575
    });
576

    
577
    t.test('Edge cases and unknown formats', function (st) {
578
        ['UFO1234', false, 1234, null, {}, []].forEach(
579
            function (format) {
580
                st['throws'](
581
                    function () {
582
                        qs.stringify({ a: 'b c' }, { format: format });
583
                    },
584
                    new TypeError('Unknown format option provided.')
585
                );
586
            }
587
        );
588
        st.end();
589
    });
590

    
591
    t.test('encodeValuesOnly', function (st) {
592
        st.equal(
593
            qs.stringify(
594
                { a: 'b', c: ['d', 'e=f'], f: [['g'], ['h']] },
595
                { encodeValuesOnly: true }
596
            ),
597
            'a=b&c[0]=d&c[1]=e%3Df&f[0][0]=g&f[1][0]=h'
598
        );
599
        st.equal(
600
            qs.stringify(
601
                { a: 'b', c: ['d', 'e'], f: [['g'], ['h']] }
602
            ),
603
            'a=b&c%5B0%5D=d&c%5B1%5D=e&f%5B0%5D%5B0%5D=g&f%5B1%5D%5B0%5D=h'
604
        );
605
        st.end();
606
    });
607

    
608
    t.test('encodeValuesOnly - strictNullHandling', function (st) {
609
        st.equal(
610
            qs.stringify(
611
                { a: { b: null } },
612
                { encodeValuesOnly: true, strictNullHandling: true }
613
            ),
614
            'a[b]'
615
        );
616
        st.end();
617
    });
618

    
619
    t.test('throws if an invalid charset is specified', function (st) {
620
        st['throws'](function () {
621
            qs.stringify({ a: 'b' }, { charset: 'foobar' });
622
        }, new TypeError('The charset option must be either utf-8, iso-8859-1, or undefined'));
623
        st.end();
624
    });
625

    
626
    t.test('respects a charset of iso-8859-1', function (st) {
627
        st.equal(qs.stringify({ æ: 'æ' }, { charset: 'iso-8859-1' }), '%E6=%E6');
628
        st.end();
629
    });
630

    
631
    t.test('encodes unrepresentable chars as numeric entities in iso-8859-1 mode', function (st) {
632
        st.equal(qs.stringify({ a: '' }, { charset: 'iso-8859-1' }), 'a=%26%239786%3B');
633
        st.end();
634
    });
635

    
636
    t.test('respects an explicit charset of utf-8 (the default)', function (st) {
637
        st.equal(qs.stringify({ a: 'æ' }, { charset: 'utf-8' }), 'a=%C3%A6');
638
        st.end();
639
    });
640

    
641
    t.test('adds the right sentinel when instructed to and the charset is utf-8', function (st) {
642
        st.equal(qs.stringify({ a: 'æ' }, { charsetSentinel: true, charset: 'utf-8' }), 'utf8=%E2%9C%93&a=%C3%A6');
643
        st.end();
644
    });
645

    
646
    t.test('adds the right sentinel when instructed to and the charset is iso-8859-1', function (st) {
647
        st.equal(qs.stringify({ a: 'æ' }, { charsetSentinel: true, charset: 'iso-8859-1' }), 'utf8=%26%2310003%3B&a=%E6');
648
        st.end();
649
    });
650

    
651
    t.test('does not mutate the options argument', function (st) {
652
        var options = {};
653
        qs.stringify({}, options);
654
        st.deepEqual(options, {});
655
        st.end();
656
    });
657

    
658
    t.test('strictNullHandling works with custom filter', function (st) {
659
        var filter = function (prefix, value) {
660
            return value;
661
        };
662

    
663
        var options = { strictNullHandling: true, filter: filter };
664
        st.equal(qs.stringify({ key: null }, options), 'key');
665
        st.end();
666
    });
667

    
668
    t.test('strictNullHandling works with null serializeDate', function (st) {
669
        var serializeDate = function () {
670
            return null;
671
        };
672
        var options = { strictNullHandling: true, serializeDate: serializeDate };
673
        var date = new Date();
674
        st.equal(qs.stringify({ key: date }, options), 'key');
675
        st.end();
676
    });
677

    
678
    t.end();
679
});
(3-3/4)