Projekt

Obecné

Profil

Stáhnout (27.1 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('parse()', function (t) {
10
    t.test('parses a simple string', function (st) {
11
        st.deepEqual(qs.parse('0=foo'), { 0: 'foo' });
12
        st.deepEqual(qs.parse('foo=c++'), { foo: 'c  ' });
13
        st.deepEqual(qs.parse('a[>=]=23'), { a: { '>=': '23' } });
14
        st.deepEqual(qs.parse('a[<=>]==23'), { a: { '<=>': '=23' } });
15
        st.deepEqual(qs.parse('a[==]=23'), { a: { '==': '23' } });
16
        st.deepEqual(qs.parse('foo', { strictNullHandling: true }), { foo: null });
17
        st.deepEqual(qs.parse('foo'), { foo: '' });
18
        st.deepEqual(qs.parse('foo='), { foo: '' });
19
        st.deepEqual(qs.parse('foo=bar'), { foo: 'bar' });
20
        st.deepEqual(qs.parse(' foo = bar = baz '), { ' foo ': ' bar = baz ' });
21
        st.deepEqual(qs.parse('foo=bar=baz'), { foo: 'bar=baz' });
22
        st.deepEqual(qs.parse('foo=bar&bar=baz'), { foo: 'bar', bar: 'baz' });
23
        st.deepEqual(qs.parse('foo2=bar2&baz2='), { foo2: 'bar2', baz2: '' });
24
        st.deepEqual(qs.parse('foo=bar&baz', { strictNullHandling: true }), { foo: 'bar', baz: null });
25
        st.deepEqual(qs.parse('foo=bar&baz'), { foo: 'bar', baz: '' });
26
        st.deepEqual(qs.parse('cht=p3&chd=t:60,40&chs=250x100&chl=Hello|World'), {
27
            cht: 'p3',
28
            chd: 't:60,40',
29
            chs: '250x100',
30
            chl: 'Hello|World'
31
        });
32
        st.end();
33
    });
34

    
35
    t.test('allows enabling dot notation', function (st) {
36
        st.deepEqual(qs.parse('a.b=c'), { 'a.b': 'c' });
37
        st.deepEqual(qs.parse('a.b=c', { allowDots: true }), { a: { b: 'c' } });
38
        st.end();
39
    });
40

    
41
    t.deepEqual(qs.parse('a[b]=c'), { a: { b: 'c' } }, 'parses a single nested string');
42
    t.deepEqual(qs.parse('a[b][c]=d'), { a: { b: { c: 'd' } } }, 'parses a double nested string');
43
    t.deepEqual(
44
        qs.parse('a[b][c][d][e][f][g][h]=i'),
45
        { a: { b: { c: { d: { e: { f: { '[g][h]': 'i' } } } } } } },
46
        'defaults to a depth of 5'
47
    );
48

    
49
    t.test('only parses one level when depth = 1', function (st) {
50
        st.deepEqual(qs.parse('a[b][c]=d', { depth: 1 }), { a: { b: { '[c]': 'd' } } });
51
        st.deepEqual(qs.parse('a[b][c][d]=e', { depth: 1 }), { a: { b: { '[c][d]': 'e' } } });
52
        st.end();
53
    });
54

    
55
    t.deepEqual(qs.parse('a=b&a=c'), { a: ['b', 'c'] }, 'parses a simple array');
56

    
57
    t.test('parses an explicit array', function (st) {
58
        st.deepEqual(qs.parse('a[]=b'), { a: ['b'] });
59
        st.deepEqual(qs.parse('a[]=b&a[]=c'), { a: ['b', 'c'] });
60
        st.deepEqual(qs.parse('a[]=b&a[]=c&a[]=d'), { a: ['b', 'c', 'd'] });
61
        st.end();
62
    });
63

    
64
    t.test('parses a mix of simple and explicit arrays', function (st) {
65
        st.deepEqual(qs.parse('a=b&a[]=c'), { a: ['b', 'c'] });
66
        st.deepEqual(qs.parse('a[]=b&a=c'), { a: ['b', 'c'] });
67
        st.deepEqual(qs.parse('a[0]=b&a=c'), { a: ['b', 'c'] });
68
        st.deepEqual(qs.parse('a=b&a[0]=c'), { a: ['b', 'c'] });
69

    
70
        st.deepEqual(qs.parse('a[1]=b&a=c', { arrayLimit: 20 }), { a: ['b', 'c'] });
71
        st.deepEqual(qs.parse('a[]=b&a=c', { arrayLimit: 0 }), { a: ['b', 'c'] });
72
        st.deepEqual(qs.parse('a[]=b&a=c'), { a: ['b', 'c'] });
73

    
74
        st.deepEqual(qs.parse('a=b&a[1]=c', { arrayLimit: 20 }), { a: ['b', 'c'] });
75
        st.deepEqual(qs.parse('a=b&a[]=c', { arrayLimit: 0 }), { a: ['b', 'c'] });
76
        st.deepEqual(qs.parse('a=b&a[]=c'), { a: ['b', 'c'] });
77

    
78
        st.end();
79
    });
80

    
81
    t.test('parses a nested array', function (st) {
82
        st.deepEqual(qs.parse('a[b][]=c&a[b][]=d'), { a: { b: ['c', 'd'] } });
83
        st.deepEqual(qs.parse('a[>=]=25'), { a: { '>=': '25' } });
84
        st.end();
85
    });
86

    
87
    t.test('allows to specify array indices', function (st) {
88
        st.deepEqual(qs.parse('a[1]=c&a[0]=b&a[2]=d'), { a: ['b', 'c', 'd'] });
89
        st.deepEqual(qs.parse('a[1]=c&a[0]=b'), { a: ['b', 'c'] });
90
        st.deepEqual(qs.parse('a[1]=c', { arrayLimit: 20 }), { a: ['c'] });
91
        st.deepEqual(qs.parse('a[1]=c', { arrayLimit: 0 }), { a: { 1: 'c' } });
92
        st.deepEqual(qs.parse('a[1]=c'), { a: ['c'] });
93
        st.end();
94
    });
95

    
96
    t.test('limits specific array indices to arrayLimit', function (st) {
97
        st.deepEqual(qs.parse('a[20]=a', { arrayLimit: 20 }), { a: ['a'] });
98
        st.deepEqual(qs.parse('a[21]=a', { arrayLimit: 20 }), { a: { 21: 'a' } });
99
        st.end();
100
    });
101

    
102
    t.deepEqual(qs.parse('a[12b]=c'), { a: { '12b': 'c' } }, 'supports keys that begin with a number');
103

    
104
    t.test('supports encoded = signs', function (st) {
105
        st.deepEqual(qs.parse('he%3Dllo=th%3Dere'), { 'he=llo': 'th=ere' });
106
        st.end();
107
    });
108

    
109
    t.test('is ok with url encoded strings', function (st) {
110
        st.deepEqual(qs.parse('a[b%20c]=d'), { a: { 'b c': 'd' } });
111
        st.deepEqual(qs.parse('a[b]=c%20d'), { a: { b: 'c d' } });
112
        st.end();
113
    });
114

    
115
    t.test('allows brackets in the value', function (st) {
116
        st.deepEqual(qs.parse('pets=["tobi"]'), { pets: '["tobi"]' });
117
        st.deepEqual(qs.parse('operators=[">=", "<="]'), { operators: '[">=", "<="]' });
118
        st.end();
119
    });
120

    
121
    t.test('allows empty values', function (st) {
122
        st.deepEqual(qs.parse(''), {});
123
        st.deepEqual(qs.parse(null), {});
124
        st.deepEqual(qs.parse(undefined), {});
125
        st.end();
126
    });
127

    
128
    t.test('transforms arrays to objects', function (st) {
129
        st.deepEqual(qs.parse('foo[0]=bar&foo[bad]=baz'), { foo: { 0: 'bar', bad: 'baz' } });
130
        st.deepEqual(qs.parse('foo[bad]=baz&foo[0]=bar'), { foo: { bad: 'baz', 0: 'bar' } });
131
        st.deepEqual(qs.parse('foo[bad]=baz&foo[]=bar'), { foo: { bad: 'baz', 0: 'bar' } });
132
        st.deepEqual(qs.parse('foo[]=bar&foo[bad]=baz'), { foo: { 0: 'bar', bad: 'baz' } });
133
        st.deepEqual(qs.parse('foo[bad]=baz&foo[]=bar&foo[]=foo'), { foo: { bad: 'baz', 0: 'bar', 1: 'foo' } });
134
        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' }] });
135

    
136
        st.deepEqual(qs.parse('a[]=b&a[t]=u&a[hasOwnProperty]=c', { allowPrototypes: false }), { a: { 0: 'b', t: 'u' } });
137
        st.deepEqual(qs.parse('a[]=b&a[t]=u&a[hasOwnProperty]=c', { allowPrototypes: true }), { a: { 0: 'b', t: 'u', hasOwnProperty: 'c' } });
138
        st.deepEqual(qs.parse('a[]=b&a[hasOwnProperty]=c&a[x]=y', { allowPrototypes: false }), { a: { 0: 'b', x: 'y' } });
139
        st.deepEqual(qs.parse('a[]=b&a[hasOwnProperty]=c&a[x]=y', { allowPrototypes: true }), { a: { 0: 'b', hasOwnProperty: 'c', x: 'y' } });
140
        st.end();
141
    });
142

    
143
    t.test('transforms arrays to objects (dot notation)', function (st) {
144
        st.deepEqual(qs.parse('foo[0].baz=bar&fool.bad=baz', { allowDots: true }), { foo: [{ baz: 'bar' }], fool: { bad: 'baz' } });
145
        st.deepEqual(qs.parse('foo[0].baz=bar&fool.bad.boo=baz', { allowDots: true }), { foo: [{ baz: 'bar' }], fool: { bad: { boo: 'baz' } } });
146
        st.deepEqual(qs.parse('foo[0][0].baz=bar&fool.bad=baz', { allowDots: true }), { foo: [[{ baz: 'bar' }]], fool: { bad: 'baz' } });
147
        st.deepEqual(qs.parse('foo[0].baz[0]=15&foo[0].bar=2', { allowDots: true }), { foo: [{ baz: ['15'], bar: '2' }] });
148
        st.deepEqual(qs.parse('foo[0].baz[0]=15&foo[0].baz[1]=16&foo[0].bar=2', { allowDots: true }), { foo: [{ baz: ['15', '16'], bar: '2' }] });
149
        st.deepEqual(qs.parse('foo.bad=baz&foo[0]=bar', { allowDots: true }), { foo: { bad: 'baz', 0: 'bar' } });
150
        st.deepEqual(qs.parse('foo.bad=baz&foo[]=bar', { allowDots: true }), { foo: { bad: 'baz', 0: 'bar' } });
151
        st.deepEqual(qs.parse('foo[]=bar&foo.bad=baz', { allowDots: true }), { foo: { 0: 'bar', bad: 'baz' } });
152
        st.deepEqual(qs.parse('foo.bad=baz&foo[]=bar&foo[]=foo', { allowDots: true }), { foo: { bad: 'baz', 0: 'bar', 1: 'foo' } });
153
        st.deepEqual(qs.parse('foo[0].a=a&foo[0].b=b&foo[1].a=aa&foo[1].b=bb', { allowDots: true }), { foo: [{ a: 'a', b: 'b' }, { a: 'aa', b: 'bb' }] });
154
        st.end();
155
    });
156

    
157
    t.test('correctly prunes undefined values when converting an array to an object', function (st) {
158
        st.deepEqual(qs.parse('a[2]=b&a[99999999]=c'), { a: { 2: 'b', 99999999: 'c' } });
159
        st.end();
160
    });
161

    
162
    t.test('supports malformed uri characters', function (st) {
163
        st.deepEqual(qs.parse('{%:%}', { strictNullHandling: true }), { '{%:%}': null });
164
        st.deepEqual(qs.parse('{%:%}='), { '{%:%}': '' });
165
        st.deepEqual(qs.parse('foo=%:%}'), { foo: '%:%}' });
166
        st.end();
167
    });
168

    
169
    t.test('doesn\'t produce empty keys', function (st) {
170
        st.deepEqual(qs.parse('_r=1&'), { _r: '1' });
171
        st.end();
172
    });
173

    
174
    t.test('cannot access Object prototype', function (st) {
175
        qs.parse('constructor[prototype][bad]=bad');
176
        qs.parse('bad[constructor][prototype][bad]=bad');
177
        st.equal(typeof Object.prototype.bad, 'undefined');
178
        st.end();
179
    });
180

    
181
    t.test('parses arrays of objects', function (st) {
182
        st.deepEqual(qs.parse('a[][b]=c'), { a: [{ b: 'c' }] });
183
        st.deepEqual(qs.parse('a[0][b]=c'), { a: [{ b: 'c' }] });
184
        st.end();
185
    });
186

    
187
    t.test('allows for empty strings in arrays', function (st) {
188
        st.deepEqual(qs.parse('a[]=b&a[]=&a[]=c'), { a: ['b', '', 'c'] });
189

    
190
        st.deepEqual(
191
            qs.parse('a[0]=b&a[1]&a[2]=c&a[19]=', { strictNullHandling: true, arrayLimit: 20 }),
192
            { a: ['b', null, 'c', ''] },
193
            'with arrayLimit 20 + array indices: null then empty string works'
194
        );
195
        st.deepEqual(
196
            qs.parse('a[]=b&a[]&a[]=c&a[]=', { strictNullHandling: true, arrayLimit: 0 }),
197
            { a: ['b', null, 'c', ''] },
198
            'with arrayLimit 0 + array brackets: null then empty string works'
199
        );
200

    
201
        st.deepEqual(
202
            qs.parse('a[0]=b&a[1]=&a[2]=c&a[19]', { strictNullHandling: true, arrayLimit: 20 }),
203
            { a: ['b', '', 'c', null] },
204
            'with arrayLimit 20 + array indices: empty string then null works'
205
        );
206
        st.deepEqual(
207
            qs.parse('a[]=b&a[]=&a[]=c&a[]', { strictNullHandling: true, arrayLimit: 0 }),
208
            { a: ['b', '', 'c', null] },
209
            'with arrayLimit 0 + array brackets: empty string then null works'
210
        );
211

    
212
        st.deepEqual(
213
            qs.parse('a[]=&a[]=b&a[]=c'),
214
            { a: ['', 'b', 'c'] },
215
            'array brackets: empty strings work'
216
        );
217
        st.end();
218
    });
219

    
220
    t.test('compacts sparse arrays', function (st) {
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'] }]]] });
225
        st.end();
226
    });
227

    
228
    t.test('parses semi-parsed strings', function (st) {
229
        st.deepEqual(qs.parse({ 'a[b]': 'c' }), { a: { b: 'c' } });
230
        st.deepEqual(qs.parse({ 'a[b]': 'c', 'a[d]': 'e' }), { a: { b: 'c', d: 'e' } });
231
        st.end();
232
    });
233

    
234
    t.test('parses buffers correctly', function (st) {
235
        var b = SaferBuffer.from('test');
236
        st.deepEqual(qs.parse({ a: b }), { a: b });
237
        st.end();
238
    });
239

    
240
    t.test('parses jquery-param strings', function (st) {
241
        // readable = 'filter[0][]=int1&filter[0][]==&filter[0][]=77&filter[]=and&filter[2][]=int2&filter[2][]==&filter[2][]=8'
242
        var encoded = 'filter%5B0%5D%5B%5D=int1&filter%5B0%5D%5B%5D=%3D&filter%5B0%5D%5B%5D=77&filter%5B%5D=and&filter%5B2%5D%5B%5D=int2&filter%5B2%5D%5B%5D=%3D&filter%5B2%5D%5B%5D=8';
243
        var expected = { filter: [['int1', '=', '77'], 'and', ['int2', '=', '8']] };
244
        st.deepEqual(qs.parse(encoded), expected);
245
        st.end();
246
    });
247

    
248
    t.test('continues parsing when no parent is found', function (st) {
249
        st.deepEqual(qs.parse('[]=&a=b'), { 0: '', a: 'b' });
250
        st.deepEqual(qs.parse('[]&a=b', { strictNullHandling: true }), { 0: null, a: 'b' });
251
        st.deepEqual(qs.parse('[foo]=bar'), { foo: 'bar' });
252
        st.end();
253
    });
254

    
255
    t.test('does not error when parsing a very long array', function (st) {
256
        var str = 'a[]=a';
257
        while (Buffer.byteLength(str) < 128 * 1024) {
258
            str = str + '&' + str;
259
        }
260

    
261
        st.doesNotThrow(function () {
262
            qs.parse(str);
263
        });
264

    
265
        st.end();
266
    });
267

    
268
    t.test('should not throw when a native prototype has an enumerable property', function (st) {
269
        Object.prototype.crash = '';
270
        Array.prototype.crash = '';
271
        st.doesNotThrow(qs.parse.bind(null, 'a=b'));
272
        st.deepEqual(qs.parse('a=b'), { a: 'b' });
273
        st.doesNotThrow(qs.parse.bind(null, 'a[][b]=c'));
274
        st.deepEqual(qs.parse('a[][b]=c'), { a: [{ b: 'c' }] });
275
        delete Object.prototype.crash;
276
        delete Array.prototype.crash;
277
        st.end();
278
    });
279

    
280
    t.test('parses a string with an alternative string delimiter', function (st) {
281
        st.deepEqual(qs.parse('a=b;c=d', { delimiter: ';' }), { a: 'b', c: 'd' });
282
        st.end();
283
    });
284

    
285
    t.test('parses a string with an alternative RegExp delimiter', function (st) {
286
        st.deepEqual(qs.parse('a=b; c=d', { delimiter: /[;,] */ }), { a: 'b', c: 'd' });
287
        st.end();
288
    });
289

    
290
    t.test('does not use non-splittable objects as delimiters', function (st) {
291
        st.deepEqual(qs.parse('a=b&c=d', { delimiter: true }), { a: 'b', c: 'd' });
292
        st.end();
293
    });
294

    
295
    t.test('allows overriding parameter limit', function (st) {
296
        st.deepEqual(qs.parse('a=b&c=d', { parameterLimit: 1 }), { a: 'b' });
297
        st.end();
298
    });
299

    
300
    t.test('allows setting the parameter limit to Infinity', function (st) {
301
        st.deepEqual(qs.parse('a=b&c=d', { parameterLimit: Infinity }), { a: 'b', c: 'd' });
302
        st.end();
303
    });
304

    
305
    t.test('allows overriding array limit', function (st) {
306
        st.deepEqual(qs.parse('a[0]=b', { arrayLimit: -1 }), { a: { 0: 'b' } });
307
        st.deepEqual(qs.parse('a[-1]=b', { arrayLimit: -1 }), { a: { '-1': 'b' } });
308
        st.deepEqual(qs.parse('a[0]=b&a[1]=c', { arrayLimit: 0 }), { a: { 0: 'b', 1: 'c' } });
309
        st.end();
310
    });
311

    
312
    t.test('allows disabling array parsing', function (st) {
313
        var indices = qs.parse('a[0]=b&a[1]=c', { parseArrays: false });
314
        st.deepEqual(indices, { a: { 0: 'b', 1: 'c' } });
315
        st.equal(Array.isArray(indices.a), false, 'parseArrays:false, indices case is not an array');
316

    
317
        var emptyBrackets = qs.parse('a[]=b', { parseArrays: false });
318
        st.deepEqual(emptyBrackets, { a: { 0: 'b' } });
319
        st.equal(Array.isArray(emptyBrackets.a), false, 'parseArrays:false, empty brackets case is not an array');
320

    
321
        st.end();
322
    });
323

    
324
    t.test('allows for query string prefix', function (st) {
325
        st.deepEqual(qs.parse('?foo=bar', { ignoreQueryPrefix: true }), { foo: 'bar' });
326
        st.deepEqual(qs.parse('foo=bar', { ignoreQueryPrefix: true }), { foo: 'bar' });
327
        st.deepEqual(qs.parse('?foo=bar', { ignoreQueryPrefix: false }), { '?foo': 'bar' });
328
        st.end();
329
    });
330

    
331
    t.test('parses an object', function (st) {
332
        var input = {
333
            'user[name]': { 'pop[bob]': 3 },
334
            'user[email]': null
335
        };
336

    
337
        var expected = {
338
            user: {
339
                name: { 'pop[bob]': 3 },
340
                email: null
341
            }
342
        };
343

    
344
        var result = qs.parse(input);
345

    
346
        st.deepEqual(result, expected);
347
        st.end();
348
    });
349

    
350
    t.test('parses string with comma as array divider', function (st) {
351
        st.deepEqual(qs.parse('foo=bar,tee', { comma: true }), { foo: ['bar', 'tee'] });
352
        st.deepEqual(qs.parse('foo[bar]=coffee,tee', { comma: true }), { foo: { bar: ['coffee', 'tee'] } });
353
        st.deepEqual(qs.parse('foo=', { comma: true }), { foo: '' });
354
        st.deepEqual(qs.parse('foo', { comma: true }), { foo: '' });
355
        st.deepEqual(qs.parse('foo', { comma: true, strictNullHandling: true }), { foo: null });
356
        st.end();
357
    });
358

    
359
    t.test('parses an object in dot notation', function (st) {
360
        var input = {
361
            'user.name': { 'pop[bob]': 3 },
362
            'user.email.': null
363
        };
364

    
365
        var expected = {
366
            user: {
367
                name: { 'pop[bob]': 3 },
368
                email: null
369
            }
370
        };
371

    
372
        var result = qs.parse(input, { allowDots: true });
373

    
374
        st.deepEqual(result, expected);
375
        st.end();
376
    });
377

    
378
    t.test('parses an object and not child values', function (st) {
379
        var input = {
380
            'user[name]': { 'pop[bob]': { test: 3 } },
381
            'user[email]': null
382
        };
383

    
384
        var expected = {
385
            user: {
386
                name: { 'pop[bob]': { test: 3 } },
387
                email: null
388
            }
389
        };
390

    
391
        var result = qs.parse(input);
392

    
393
        st.deepEqual(result, expected);
394
        st.end();
395
    });
396

    
397
    t.test('does not blow up when Buffer global is missing', function (st) {
398
        var tempBuffer = global.Buffer;
399
        delete global.Buffer;
400
        var result = qs.parse('a=b&c=d');
401
        global.Buffer = tempBuffer;
402
        st.deepEqual(result, { a: 'b', c: 'd' });
403
        st.end();
404
    });
405

    
406
    t.test('does not crash when parsing circular references', function (st) {
407
        var a = {};
408
        a.b = a;
409

    
410
        var parsed;
411

    
412
        st.doesNotThrow(function () {
413
            parsed = qs.parse({ 'foo[bar]': 'baz', 'foo[baz]': a });
414
        });
415

    
416
        st.equal('foo' in parsed, true, 'parsed has "foo" property');
417
        st.equal('bar' in parsed.foo, true);
418
        st.equal('baz' in parsed.foo, true);
419
        st.equal(parsed.foo.bar, 'baz');
420
        st.deepEqual(parsed.foo.baz, a);
421
        st.end();
422
    });
423

    
424
    t.test('does not crash when parsing deep objects', function (st) {
425
        var parsed;
426
        var str = 'foo';
427

    
428
        for (var i = 0; i < 5000; i++) {
429
            str += '[p]';
430
        }
431

    
432
        str += '=bar';
433

    
434
        st.doesNotThrow(function () {
435
            parsed = qs.parse(str, { depth: 5000 });
436
        });
437

    
438
        st.equal('foo' in parsed, true, 'parsed has "foo" property');
439

    
440
        var depth = 0;
441
        var ref = parsed.foo;
442
        while ((ref = ref.p)) {
443
            depth += 1;
444
        }
445

    
446
        st.equal(depth, 5000, 'parsed is 5000 properties deep');
447

    
448
        st.end();
449
    });
450

    
451
    t.test('parses null objects correctly', { skip: !Object.create }, function (st) {
452
        var a = Object.create(null);
453
        a.b = 'c';
454

    
455
        st.deepEqual(qs.parse(a), { b: 'c' });
456
        var result = qs.parse({ a: a });
457
        st.equal('a' in result, true, 'result has "a" property');
458
        st.deepEqual(result.a, a);
459
        st.end();
460
    });
461

    
462
    t.test('parses dates correctly', function (st) {
463
        var now = new Date();
464
        st.deepEqual(qs.parse({ a: now }), { a: now });
465
        st.end();
466
    });
467

    
468
    t.test('parses regular expressions correctly', function (st) {
469
        var re = /^test$/;
470
        st.deepEqual(qs.parse({ a: re }), { a: re });
471
        st.end();
472
    });
473

    
474
    t.test('does not allow overwriting prototype properties', function (st) {
475
        st.deepEqual(qs.parse('a[hasOwnProperty]=b', { allowPrototypes: false }), {});
476
        st.deepEqual(qs.parse('hasOwnProperty=b', { allowPrototypes: false }), {});
477

    
478
        st.deepEqual(
479
            qs.parse('toString', { allowPrototypes: false }),
480
            {},
481
            'bare "toString" results in {}'
482
        );
483

    
484
        st.end();
485
    });
486

    
487
    t.test('can allow overwriting prototype properties', function (st) {
488
        st.deepEqual(qs.parse('a[hasOwnProperty]=b', { allowPrototypes: true }), { a: { hasOwnProperty: 'b' } });
489
        st.deepEqual(qs.parse('hasOwnProperty=b', { allowPrototypes: true }), { hasOwnProperty: 'b' });
490

    
491
        st.deepEqual(
492
            qs.parse('toString', { allowPrototypes: true }),
493
            { toString: '' },
494
            'bare "toString" results in { toString: "" }'
495
        );
496

    
497
        st.end();
498
    });
499

    
500
    t.test('params starting with a closing bracket', function (st) {
501
        st.deepEqual(qs.parse(']=toString'), { ']': 'toString' });
502
        st.deepEqual(qs.parse(']]=toString'), { ']]': 'toString' });
503
        st.deepEqual(qs.parse(']hello]=toString'), { ']hello]': 'toString' });
504
        st.end();
505
    });
506

    
507
    t.test('params starting with a starting bracket', function (st) {
508
        st.deepEqual(qs.parse('[=toString'), { '[': 'toString' });
509
        st.deepEqual(qs.parse('[[=toString'), { '[[': 'toString' });
510
        st.deepEqual(qs.parse('[hello[=toString'), { '[hello[': 'toString' });
511
        st.end();
512
    });
513

    
514
    t.test('add keys to objects', function (st) {
515
        st.deepEqual(
516
            qs.parse('a[b]=c&a=d'),
517
            { a: { b: 'c', d: true } },
518
            'can add keys to objects'
519
        );
520

    
521
        st.deepEqual(
522
            qs.parse('a[b]=c&a=toString'),
523
            { a: { b: 'c' } },
524
            'can not overwrite prototype'
525
        );
526

    
527
        st.deepEqual(
528
            qs.parse('a[b]=c&a=toString', { allowPrototypes: true }),
529
            { a: { b: 'c', toString: true } },
530
            'can overwrite prototype with allowPrototypes true'
531
        );
532

    
533
        st.deepEqual(
534
            qs.parse('a[b]=c&a=toString', { plainObjects: true }),
535
            { a: { b: 'c', toString: true } },
536
            'can overwrite prototype with plainObjects true'
537
        );
538

    
539
        st.end();
540
    });
541

    
542
    t.test('can return null objects', { skip: !Object.create }, function (st) {
543
        var expected = Object.create(null);
544
        expected.a = Object.create(null);
545
        expected.a.b = 'c';
546
        expected.a.hasOwnProperty = 'd';
547
        st.deepEqual(qs.parse('a[b]=c&a[hasOwnProperty]=d', { plainObjects: true }), expected);
548
        st.deepEqual(qs.parse(null, { plainObjects: true }), Object.create(null));
549
        var expectedArray = Object.create(null);
550
        expectedArray.a = Object.create(null);
551
        expectedArray.a[0] = 'b';
552
        expectedArray.a.c = 'd';
553
        st.deepEqual(qs.parse('a[]=b&a[c]=d', { plainObjects: true }), expectedArray);
554
        st.end();
555
    });
556

    
557
    t.test('can parse with custom encoding', function (st) {
558
        st.deepEqual(qs.parse('%8c%a7=%91%e5%8d%e3%95%7b', {
559
            decoder: function (str) {
560
                var reg = /%([0-9A-F]{2})/ig;
561
                var result = [];
562
                var parts = reg.exec(str);
563
                while (parts) {
564
                    result.push(parseInt(parts[1], 16));
565
                    parts = reg.exec(str);
566
                }
567
                return String(iconv.decode(SaferBuffer.from(result), 'shift_jis'));
568
            }
569
        }), { : '大阪府' });
570
        st.end();
571
    });
572

    
573
    t.test('receives the default decoder as a second argument', function (st) {
574
        st.plan(1);
575
        qs.parse('a', {
576
            decoder: function (str, defaultDecoder) {
577
                st.equal(defaultDecoder, utils.decode);
578
            }
579
        });
580
        st.end();
581
    });
582

    
583
    t.test('throws error with wrong decoder', function (st) {
584
        st['throws'](function () {
585
            qs.parse({}, { decoder: 'string' });
586
        }, new TypeError('Decoder has to be a function.'));
587
        st.end();
588
    });
589

    
590
    t.test('does not mutate the options argument', function (st) {
591
        var options = {};
592
        qs.parse('a[b]=true', options);
593
        st.deepEqual(options, {});
594
        st.end();
595
    });
596

    
597
    t.test('throws if an invalid charset is specified', function (st) {
598
        st['throws'](function () {
599
            qs.parse('a=b', { charset: 'foobar' });
600
        }, new TypeError('The charset option must be either utf-8, iso-8859-1, or undefined'));
601
        st.end();
602
    });
603

    
604
    t.test('parses an iso-8859-1 string if asked to', function (st) {
605
        st.deepEqual(qs.parse('%A2=%BD', { charset: 'iso-8859-1' }), { '¢': '½' });
606
        st.end();
607
    });
608

    
609
    var urlEncodedCheckmarkInUtf8 = '%E2%9C%93';
610
    var urlEncodedOSlashInUtf8 = '%C3%B8';
611
    var urlEncodedNumCheckmark = '%26%2310003%3B';
612
    var urlEncodedNumSmiley = '%26%239786%3B';
613

    
614
    t.test('prefers an utf-8 charset specified by the utf8 sentinel to a default charset of iso-8859-1', function (st) {
615
        st.deepEqual(qs.parse('utf8=' + urlEncodedCheckmarkInUtf8 + '&' + urlEncodedOSlashInUtf8 + '=' + urlEncodedOSlashInUtf8, { charsetSentinel: true, charset: 'iso-8859-1' }), { ø: 'ø' });
616
        st.end();
617
    });
618

    
619
    t.test('prefers an iso-8859-1 charset specified by the utf8 sentinel to a default charset of utf-8', function (st) {
620
        st.deepEqual(qs.parse('utf8=' + urlEncodedNumCheckmark + '&' + urlEncodedOSlashInUtf8 + '=' + urlEncodedOSlashInUtf8, { charsetSentinel: true, charset: 'utf-8' }), { 'ø': 'ø' });
621
        st.end();
622
    });
623

    
624
    t.test('does not require the utf8 sentinel to be defined before the parameters whose decoding it affects', function (st) {
625
        st.deepEqual(qs.parse('a=' + urlEncodedOSlashInUtf8 + '&utf8=' + urlEncodedNumCheckmark, { charsetSentinel: true, charset: 'utf-8' }), { a: 'ø' });
626
        st.end();
627
    });
628

    
629
    t.test('should ignore an utf8 sentinel with an unknown value', function (st) {
630
        st.deepEqual(qs.parse('utf8=foo&' + urlEncodedOSlashInUtf8 + '=' + urlEncodedOSlashInUtf8, { charsetSentinel: true, charset: 'utf-8' }), { ø: 'ø' });
631
        st.end();
632
    });
633

    
634
    t.test('uses the utf8 sentinel to switch to utf-8 when no default charset is given', function (st) {
635
        st.deepEqual(qs.parse('utf8=' + urlEncodedCheckmarkInUtf8 + '&' + urlEncodedOSlashInUtf8 + '=' + urlEncodedOSlashInUtf8, { charsetSentinel: true }), { ø: 'ø' });
636
        st.end();
637
    });
638

    
639
    t.test('uses the utf8 sentinel to switch to iso-8859-1 when no default charset is given', function (st) {
640
        st.deepEqual(qs.parse('utf8=' + urlEncodedNumCheckmark + '&' + urlEncodedOSlashInUtf8 + '=' + urlEncodedOSlashInUtf8, { charsetSentinel: true }), { 'ø': 'ø' });
641
        st.end();
642
    });
643

    
644
    t.test('interprets numeric entities in iso-8859-1 when `interpretNumericEntities`', function (st) {
645
        st.deepEqual(qs.parse('foo=' + urlEncodedNumSmiley, { charset: 'iso-8859-1', interpretNumericEntities: true }), { foo: '' });
646
        st.end();
647
    });
648

    
649
    t.test('handles a custom decoder returning `null`, in the `iso-8859-1` charset, when `interpretNumericEntities`', function (st) {
650
        st.deepEqual(qs.parse('foo=&bar=' + urlEncodedNumSmiley, {
651
            charset: 'iso-8859-1',
652
            decoder: function (str, defaultDecoder, charset) {
653
                return str ? defaultDecoder(str, defaultDecoder, charset) : null;
654
            },
655
            interpretNumericEntities: true
656
        }), { foo: null, bar: '' });
657
        st.end();
658
    });
659

    
660
    t.test('does not interpret numeric entities in iso-8859-1 when `interpretNumericEntities` is absent', function (st) {
661
        st.deepEqual(qs.parse('foo=' + urlEncodedNumSmiley, { charset: 'iso-8859-1' }), { foo: '&#9786;' });
662
        st.end();
663
    });
664

    
665
    t.test('does not interpret numeric entities when the charset is utf-8, even when `interpretNumericEntities`', function (st) {
666
        st.deepEqual(qs.parse('foo=' + urlEncodedNumSmiley, { charset: 'utf-8', interpretNumericEntities: true }), { foo: '&#9786;' });
667
        st.end();
668
    });
669

    
670
    t.test('does not interpret %uXXXX syntax in iso-8859-1 mode', function (st) {
671
        st.deepEqual(qs.parse('%u263A=%u263A', { charset: 'iso-8859-1' }), { '%u263A': '%u263A' });
672
        st.end();
673
    });
674

    
675
    t.end();
676
});
(2-2/4)