35711823eb8881e7764ef88cf932a75390304ea2
[yaffs-website] / node_modules / uncss / node_modules / qs / test / parse.js
1 'use strict';
2
3 var test = require('tape');
4 var qs = require('../');
5
6 test('parse()', function (t) {
7     t.test('parses a simple string', function (st) {
8         st.deepEqual(qs.parse('0=foo'), { '0': 'foo' });
9         st.deepEqual(qs.parse('foo=c++'), { foo: 'c  ' });
10         st.deepEqual(qs.parse('a[>=]=23'), { a: { '>=': '23' } });
11         st.deepEqual(qs.parse('a[<=>]==23'), { a: { '<=>': '=23' } });
12         st.deepEqual(qs.parse('a[==]=23'), { a: { '==': '23' } });
13         st.deepEqual(qs.parse('foo', { strictNullHandling: true }), { foo: null });
14         st.deepEqual(qs.parse('foo'), { foo: '' });
15         st.deepEqual(qs.parse('foo='), { foo: '' });
16         st.deepEqual(qs.parse('foo=bar'), { foo: 'bar' });
17         st.deepEqual(qs.parse(' foo = bar = baz '), { ' foo ': ' bar = baz ' });
18         st.deepEqual(qs.parse('foo=bar=baz'), { foo: 'bar=baz' });
19         st.deepEqual(qs.parse('foo=bar&bar=baz'), { foo: 'bar', bar: 'baz' });
20         st.deepEqual(qs.parse('foo2=bar2&baz2='), { foo2: 'bar2', baz2: '' });
21         st.deepEqual(qs.parse('foo=bar&baz', { strictNullHandling: true }), { foo: 'bar', baz: null });
22         st.deepEqual(qs.parse('foo=bar&baz'), { foo: 'bar', baz: '' });
23         st.deepEqual(qs.parse('cht=p3&chd=t:60,40&chs=250x100&chl=Hello|World'), {
24             cht: 'p3',
25             chd: 't:60,40',
26             chs: '250x100',
27             chl: 'Hello|World'
28         });
29         st.end();
30     });
31
32     t.test('allows enabling dot notation', function (st) {
33         st.deepEqual(qs.parse('a.b=c'), { 'a.b': 'c' });
34         st.deepEqual(qs.parse('a.b=c', { allowDots: true }), { a: { b: 'c' } });
35         st.end();
36     });
37
38     t.deepEqual(qs.parse('a[b]=c'), { a: { b: 'c' } }, 'parses a single nested string');
39     t.deepEqual(qs.parse('a[b][c]=d'), { a: { b: { c: 'd' } } }, 'parses a double nested string');
40     t.deepEqual(
41         qs.parse('a[b][c][d][e][f][g][h]=i'),
42         { a: { b: { c: { d: { e: { f: { '[g][h]': 'i' } } } } } } },
43         'defaults to a depth of 5'
44     );
45
46     t.test('only parses one level when depth = 1', function (st) {
47         st.deepEqual(qs.parse('a[b][c]=d', { depth: 1 }), { a: { b: { '[c]': 'd' } } });
48         st.deepEqual(qs.parse('a[b][c][d]=e', { depth: 1 }), { a: { b: { '[c][d]': 'e' } } });
49         st.end();
50     });
51
52     t.deepEqual(qs.parse('a=b&a=c'), { a: ['b', 'c'] }, 'parses a simple array');
53
54     t.test('parses an explicit array', function (st) {
55         st.deepEqual(qs.parse('a[]=b'), { a: ['b'] });
56         st.deepEqual(qs.parse('a[]=b&a[]=c'), { a: ['b', 'c'] });
57         st.deepEqual(qs.parse('a[]=b&a[]=c&a[]=d'), { a: ['b', 'c', 'd'] });
58         st.end();
59     });
60
61     t.test('parses a mix of simple and explicit arrays', function (st) {
62         st.deepEqual(qs.parse('a=b&a[]=c'), { a: ['b', 'c'] });
63         st.deepEqual(qs.parse('a[]=b&a=c'), { a: ['b', 'c'] });
64         st.deepEqual(qs.parse('a[0]=b&a=c'), { a: ['b', 'c'] });
65         st.deepEqual(qs.parse('a=b&a[0]=c'), { a: ['b', 'c'] });
66         st.deepEqual(qs.parse('a[1]=b&a=c'), { a: ['b', 'c'] });
67         st.deepEqual(qs.parse('a=b&a[1]=c'), { a: ['b', 'c'] });
68         st.end();
69     });
70
71     t.test('parses a nested array', function (st) {
72         st.deepEqual(qs.parse('a[b][]=c&a[b][]=d'), { a: { b: ['c', 'd'] } });
73         st.deepEqual(qs.parse('a[>=]=25'), { a: { '>=': '25' } });
74         st.end();
75     });
76
77     t.test('allows to specify array indices', function (st) {
78         st.deepEqual(qs.parse('a[1]=c&a[0]=b&a[2]=d'), { a: ['b', 'c', 'd'] });
79         st.deepEqual(qs.parse('a[1]=c&a[0]=b'), { a: ['b', 'c'] });
80         st.deepEqual(qs.parse('a[1]=c'), { a: ['c'] });
81         st.end();
82     });
83
84     t.test('limits specific array indices to 20', function (st) {
85         st.deepEqual(qs.parse('a[20]=a'), { a: ['a'] });
86         st.deepEqual(qs.parse('a[21]=a'), { a: { '21': 'a' } });
87         st.end();
88     });
89
90     t.deepEqual(qs.parse('a[12b]=c'), { a: { '12b': 'c' } }, 'supports keys that begin with a number');
91
92     t.test('supports encoded = signs', function (st) {
93         st.deepEqual(qs.parse('he%3Dllo=th%3Dere'), { 'he=llo': 'th=ere' });
94         st.end();
95     });
96
97     t.test('is ok with url encoded strings', function (st) {
98         st.deepEqual(qs.parse('a[b%20c]=d'), { a: { 'b c': 'd' } });
99         st.deepEqual(qs.parse('a[b]=c%20d'), { a: { b: 'c d' } });
100         st.end();
101     });
102
103     t.test('allows brackets in the value', function (st) {
104         st.deepEqual(qs.parse('pets=["tobi"]'), { pets: '["tobi"]' });
105         st.deepEqual(qs.parse('operators=[">=", "<="]'), { operators: '[">=", "<="]' });
106         st.end();
107     });
108
109     t.test('allows empty values', function (st) {
110         st.deepEqual(qs.parse(''), {});
111         st.deepEqual(qs.parse(null), {});
112         st.deepEqual(qs.parse(undefined), {});
113         st.end();
114     });
115
116     t.test('transforms arrays to objects', function (st) {
117         st.deepEqual(qs.parse('foo[0]=bar&foo[bad]=baz'), { foo: { '0': 'bar', bad: 'baz' } });
118         st.deepEqual(qs.parse('foo[bad]=baz&foo[0]=bar'), { foo: { bad: 'baz', '0': 'bar' } });
119         st.deepEqual(qs.parse('foo[bad]=baz&foo[]=bar'), { foo: { bad: 'baz', '0': 'bar' } });
120         st.deepEqual(qs.parse('foo[]=bar&foo[bad]=baz'), { foo: { '0': 'bar', bad: 'baz' } });
121         st.deepEqual(qs.parse('foo[bad]=baz&foo[]=bar&foo[]=foo'), { foo: { bad: 'baz', '0': 'bar', '1': 'foo' } });
122         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' }] });
123
124         st.deepEqual(qs.parse('a[]=b&a[t]=u&a[hasOwnProperty]=c', { allowPrototypes: false }), { a: { 0: 'b', t: 'u' } });
125         st.deepEqual(qs.parse('a[]=b&a[t]=u&a[hasOwnProperty]=c', { allowPrototypes: true }), { a: { 0: 'b', t: 'u', hasOwnProperty: 'c' } });
126         st.deepEqual(qs.parse('a[]=b&a[hasOwnProperty]=c&a[x]=y', { allowPrototypes: false }), { a: { 0: 'b', x: 'y' } });
127         st.deepEqual(qs.parse('a[]=b&a[hasOwnProperty]=c&a[x]=y', { allowPrototypes: true }), { a: { 0: 'b', hasOwnProperty: 'c', x: 'y' } });
128         st.end();
129     });
130
131     t.test('transforms arrays to objects (dot notation)', function (st) {
132         st.deepEqual(qs.parse('foo[0].baz=bar&fool.bad=baz', { allowDots: true }), { foo: [{ baz: 'bar' }], fool: { bad: 'baz' } });
133         st.deepEqual(qs.parse('foo[0].baz=bar&fool.bad.boo=baz', { allowDots: true }), { foo: [{ baz: 'bar' }], fool: { bad: { boo: 'baz' } } });
134         st.deepEqual(qs.parse('foo[0][0].baz=bar&fool.bad=baz', { allowDots: true }), { foo: [[{ baz: 'bar' }]], fool: { bad: 'baz' } });
135         st.deepEqual(qs.parse('foo[0].baz[0]=15&foo[0].bar=2', { allowDots: true }), { foo: [{ baz: ['15'], bar: '2' }] });
136         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' }] });
137         st.deepEqual(qs.parse('foo.bad=baz&foo[0]=bar', { allowDots: true }), { foo: { bad: 'baz', '0': 'bar' } });
138         st.deepEqual(qs.parse('foo.bad=baz&foo[]=bar', { allowDots: true }), { foo: { bad: 'baz', '0': 'bar' } });
139         st.deepEqual(qs.parse('foo[]=bar&foo.bad=baz', { allowDots: true }), { foo: { '0': 'bar', bad: 'baz' } });
140         st.deepEqual(qs.parse('foo.bad=baz&foo[]=bar&foo[]=foo', { allowDots: true }), { foo: { bad: 'baz', '0': 'bar', '1': 'foo' } });
141         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' }] });
142         st.end();
143     });
144
145     t.test('correctly prunes undefined values when converting an array to an object', function (st) {
146         st.deepEqual(qs.parse('a[2]=b&a[99999999]=c'), { a: { '2': 'b', '99999999': 'c' } });
147         st.end();
148     });
149
150     t.test('supports malformed uri characters', function (st) {
151         st.deepEqual(qs.parse('{%:%}', { strictNullHandling: true }), { '{%:%}': null });
152         st.deepEqual(qs.parse('{%:%}='), { '{%:%}': '' });
153         st.deepEqual(qs.parse('foo=%:%}'), { foo: '%:%}' });
154         st.end();
155     });
156
157     t.test('doesn\'t produce empty keys', function (st) {
158         st.deepEqual(qs.parse('_r=1&'), { '_r': '1' });
159         st.end();
160     });
161
162     t.test('cannot access Object prototype', function (st) {
163         qs.parse('constructor[prototype][bad]=bad');
164         qs.parse('bad[constructor][prototype][bad]=bad');
165         st.equal(typeof Object.prototype.bad, 'undefined');
166         st.end();
167     });
168
169     t.test('parses arrays of objects', function (st) {
170         st.deepEqual(qs.parse('a[][b]=c'), { a: [{ b: 'c' }] });
171         st.deepEqual(qs.parse('a[0][b]=c'), { a: [{ b: 'c' }] });
172         st.end();
173     });
174
175     t.test('allows for empty strings in arrays', function (st) {
176         st.deepEqual(qs.parse('a[]=b&a[]=&a[]=c'), { a: ['b', '', 'c'] });
177         st.deepEqual(qs.parse('a[0]=b&a[1]&a[2]=c&a[19]=', { strictNullHandling: true }), { a: ['b', null, 'c', ''] });
178         st.deepEqual(qs.parse('a[0]=b&a[1]=&a[2]=c&a[19]', { strictNullHandling: true }), { a: ['b', '', 'c', null] });
179         st.deepEqual(qs.parse('a[]=&a[]=b&a[]=c'), { a: ['', 'b', 'c'] });
180         st.end();
181     });
182
183     t.test('compacts sparse arrays', function (st) {
184         st.deepEqual(qs.parse('a[10]=1&a[2]=2'), { a: ['2', '1'] });
185         st.end();
186     });
187
188     t.test('parses semi-parsed strings', function (st) {
189         st.deepEqual(qs.parse({ 'a[b]': 'c' }), { a: { b: 'c' } });
190         st.deepEqual(qs.parse({ 'a[b]': 'c', 'a[d]': 'e' }), { a: { b: 'c', d: 'e' } });
191         st.end();
192     });
193
194     t.test('parses buffers correctly', function (st) {
195         var b = new Buffer('test');
196         st.deepEqual(qs.parse({ a: b }), { a: b });
197         st.end();
198     });
199
200     t.test('continues parsing when no parent is found', function (st) {
201         st.deepEqual(qs.parse('[]=&a=b'), { '0': '', a: 'b' });
202         st.deepEqual(qs.parse('[]&a=b', { strictNullHandling: true }), { '0': null, a: 'b' });
203         st.deepEqual(qs.parse('[foo]=bar'), { foo: 'bar' });
204         st.end();
205     });
206
207     t.test('does not error when parsing a very long array', function (st) {
208         var str = 'a[]=a';
209         while (Buffer.byteLength(str) < 128 * 1024) {
210             str = str + '&' + str;
211         }
212
213         st.doesNotThrow(function () { qs.parse(str); });
214
215         st.end();
216     });
217
218     t.test('should not throw when a native prototype has an enumerable property', { parallel: false }, function (st) {
219         Object.prototype.crash = '';
220         Array.prototype.crash = '';
221         st.doesNotThrow(qs.parse.bind(null, 'a=b'));
222         st.deepEqual(qs.parse('a=b'), { a: 'b' });
223         st.doesNotThrow(qs.parse.bind(null, 'a[][b]=c'));
224         st.deepEqual(qs.parse('a[][b]=c'), { a: [{ b: 'c' }] });
225         delete Object.prototype.crash;
226         delete Array.prototype.crash;
227         st.end();
228     });
229
230     t.test('parses a string with an alternative string delimiter', function (st) {
231         st.deepEqual(qs.parse('a=b;c=d', { delimiter: ';' }), { a: 'b', c: 'd' });
232         st.end();
233     });
234
235     t.test('parses a string with an alternative RegExp delimiter', function (st) {
236         st.deepEqual(qs.parse('a=b; c=d', { delimiter: /[;,] */ }), { a: 'b', c: 'd' });
237         st.end();
238     });
239
240     t.test('does not use non-splittable objects as delimiters', function (st) {
241         st.deepEqual(qs.parse('a=b&c=d', { delimiter: true }), { a: 'b', c: 'd' });
242         st.end();
243     });
244
245     t.test('allows overriding parameter limit', function (st) {
246         st.deepEqual(qs.parse('a=b&c=d', { parameterLimit: 1 }), { a: 'b' });
247         st.end();
248     });
249
250     t.test('allows setting the parameter limit to Infinity', function (st) {
251         st.deepEqual(qs.parse('a=b&c=d', { parameterLimit: Infinity }), { a: 'b', c: 'd' });
252         st.end();
253     });
254
255     t.test('allows overriding array limit', function (st) {
256         st.deepEqual(qs.parse('a[0]=b', { arrayLimit: -1 }), { a: { '0': 'b' } });
257         st.deepEqual(qs.parse('a[-1]=b', { arrayLimit: -1 }), { a: { '-1': 'b' } });
258         st.deepEqual(qs.parse('a[0]=b&a[1]=c', { arrayLimit: 0 }), { a: { '0': 'b', '1': 'c' } });
259         st.end();
260     });
261
262     t.test('allows disabling array parsing', function (st) {
263         st.deepEqual(qs.parse('a[0]=b&a[1]=c', { parseArrays: false }), { a: { '0': 'b', '1': 'c' } });
264         st.end();
265     });
266
267     t.test('parses an object', function (st) {
268         var input = {
269             'user[name]': { 'pop[bob]': 3 },
270             'user[email]': null
271         };
272
273         var expected = {
274             user: {
275                 name: { 'pop[bob]': 3 },
276                 email: null
277             }
278         };
279
280         var result = qs.parse(input);
281
282         st.deepEqual(result, expected);
283         st.end();
284     });
285
286     t.test('parses an object in dot notation', function (st) {
287         var input = {
288             'user.name': { 'pop[bob]': 3 },
289             'user.email.': null
290         };
291
292         var expected = {
293             user: {
294                 name: { 'pop[bob]': 3 },
295                 email: null
296             }
297         };
298
299         var result = qs.parse(input, { allowDots: true });
300
301         st.deepEqual(result, expected);
302         st.end();
303     });
304
305     t.test('parses an object and not child values', function (st) {
306         var input = {
307             'user[name]': { 'pop[bob]': { 'test': 3 } },
308             'user[email]': null
309         };
310
311         var expected = {
312             user: {
313                 name: { 'pop[bob]': { 'test': 3 } },
314                 email: null
315             }
316         };
317
318         var result = qs.parse(input);
319
320         st.deepEqual(result, expected);
321         st.end();
322     });
323
324     t.test('does not blow up when Buffer global is missing', function (st) {
325         var tempBuffer = global.Buffer;
326         delete global.Buffer;
327         var result = qs.parse('a=b&c=d');
328         global.Buffer = tempBuffer;
329         st.deepEqual(result, { a: 'b', c: 'd' });
330         st.end();
331     });
332
333     t.test('does not crash when parsing circular references', function (st) {
334         var a = {};
335         a.b = a;
336
337         var parsed;
338
339         st.doesNotThrow(function () {
340             parsed = qs.parse({ 'foo[bar]': 'baz', 'foo[baz]': a });
341         });
342
343         st.equal('foo' in parsed, true, 'parsed has "foo" property');
344         st.equal('bar' in parsed.foo, true);
345         st.equal('baz' in parsed.foo, true);
346         st.equal(parsed.foo.bar, 'baz');
347         st.deepEqual(parsed.foo.baz, a);
348         st.end();
349     });
350
351     t.test('parses plain objects correctly', function (st) {
352         var a = Object.create(null);
353         a.b = 'c';
354
355         st.deepEqual(qs.parse(a), { b: 'c' });
356         var result = qs.parse({ a: a });
357         st.equal('a' in result, true, 'result has "a" property');
358         st.deepEqual(result.a, a);
359         st.end();
360     });
361
362     t.test('parses dates correctly', function (st) {
363         var now = new Date();
364         st.deepEqual(qs.parse({ a: now }), { a: now });
365         st.end();
366     });
367
368     t.test('parses regular expressions correctly', function (st) {
369         var re = /^test$/;
370         st.deepEqual(qs.parse({ a: re }), { a: re });
371         st.end();
372     });
373
374     t.test('does not allow overwriting prototype properties', function (st) {
375         st.deepEqual(qs.parse('a[hasOwnProperty]=b', { allowPrototypes: false }), {});
376         st.deepEqual(qs.parse('hasOwnProperty=b', { allowPrototypes: false }), {});
377
378         st.deepEqual(
379             qs.parse('toString', { allowPrototypes: false }),
380             {},
381             'bare "toString" results in {}'
382         );
383
384         st.end();
385     });
386
387     t.test('can allow overwriting prototype properties', function (st) {
388         st.deepEqual(qs.parse('a[hasOwnProperty]=b', { allowPrototypes: true }), { a: { hasOwnProperty: 'b' } });
389         st.deepEqual(qs.parse('hasOwnProperty=b', { allowPrototypes: true }), { hasOwnProperty: 'b' });
390
391         st.deepEqual(
392             qs.parse('toString', { allowPrototypes: true }),
393             { toString: '' },
394             'bare "toString" results in { toString: "" }'
395         );
396
397         st.end();
398     });
399
400     t.test('params starting with a closing bracket', function (st) {
401         st.deepEqual(qs.parse(']=toString'), { ']': 'toString' });
402         st.deepEqual(qs.parse(']]=toString'), { ']]': 'toString' });
403         st.deepEqual(qs.parse(']hello]=toString'), { ']hello]': 'toString' });
404         st.end();
405     });
406
407     t.test('params starting with a starting bracket', function (st) {
408         st.deepEqual(qs.parse('[=toString'), { '[': 'toString' });
409         st.deepEqual(qs.parse('[[=toString'), { '[[': 'toString' });
410         st.deepEqual(qs.parse('[hello[=toString'), { '[hello[': 'toString' });
411         st.end();
412     });
413
414     t.test('add keys to objects', function (st) {
415         st.deepEqual(
416             qs.parse('a[b]=c&a=d'),
417             { a: { b: 'c', d: true } },
418             'can add keys to objects'
419         );
420
421         st.deepEqual(
422             qs.parse('a[b]=c&a=toString'),
423             { a: { b: 'c' } },
424             'can not overwrite prototype'
425         );
426
427         st.deepEqual(
428             qs.parse('a[b]=c&a=toString', { allowPrototypes: true }),
429             { a: { b: 'c', toString: true } },
430             'can overwrite prototype with allowPrototypes true'
431         );
432
433         st.deepEqual(
434             qs.parse('a[b]=c&a=toString', { plainObjects: true }),
435             { a: { b: 'c', toString: true } },
436             'can overwrite prototype with plainObjects true'
437         );
438
439         st.end();
440     });
441
442     t.test('can return null objects', { skip: !Object.create }, function (st) {
443         var expected = Object.create(null);
444         expected.a = Object.create(null);
445         expected.a.b = 'c';
446         expected.a.hasOwnProperty = 'd';
447         st.deepEqual(qs.parse('a[b]=c&a[hasOwnProperty]=d', { plainObjects: true }), expected);
448         st.deepEqual(qs.parse(null, { plainObjects: true }), Object.create(null));
449         var expectedArray = Object.create(null);
450         expectedArray.a = Object.create(null);
451         expectedArray.a['0'] = 'b';
452         expectedArray.a.c = 'd';
453         st.deepEqual(qs.parse('a[]=b&a[c]=d', { plainObjects: true }), expectedArray);
454         st.end();
455     });
456 });