@nocobase/plugin-multi-keyword-filter 2.2.17 → 2.2.19
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/dist/client/103.b39ee70786cb13a7.js +10 -0
- package/dist/client/index.js +1 -1
- package/dist/client-v2/103.dfed67c95bcd8a1d.js +10 -0
- package/dist/client-v2/index.js +1 -1
- package/dist/externalVersion.js +4 -4
- package/dist/node_modules/qs/.editorconfig +4 -1
- package/dist/node_modules/qs/.github/SECURITY.md +11 -0
- package/dist/node_modules/qs/.github/THREAT_MODEL.md +78 -0
- package/dist/node_modules/qs/dist/qs.js +90 -2036
- package/dist/node_modules/qs/lib/index.js +1 -1
- package/dist/node_modules/qs/lib/parse.js +215 -66
- package/dist/node_modules/qs/lib/stringify.js +90 -32
- package/dist/node_modules/qs/lib/utils.js +189 -53
- package/dist/node_modules/qs/node_modules/es-define-property/.eslintrc +13 -0
- package/dist/node_modules/qs/node_modules/es-define-property/.github/FUNDING.yml +12 -0
- package/dist/node_modules/qs/node_modules/es-define-property/.nycrc +9 -0
- package/dist/node_modules/qs/node_modules/es-define-property/index.d.ts +3 -0
- package/dist/node_modules/qs/node_modules/es-define-property/index.js +14 -0
- package/dist/node_modules/qs/node_modules/es-define-property/package.json +81 -0
- package/dist/node_modules/qs/node_modules/es-define-property/test/index.js +56 -0
- package/dist/node_modules/qs/node_modules/side-channel/.editorconfig +9 -0
- package/dist/node_modules/qs/node_modules/side-channel/.eslintrc +12 -0
- package/dist/node_modules/qs/node_modules/side-channel/.github/FUNDING.yml +12 -0
- package/dist/node_modules/qs/node_modules/side-channel/.nycrc +13 -0
- package/dist/node_modules/qs/node_modules/side-channel/index.d.ts +14 -0
- package/dist/node_modules/qs/node_modules/side-channel/index.js +46 -0
- package/dist/node_modules/qs/node_modules/side-channel/package.json +85 -0
- package/dist/node_modules/qs/node_modules/side-channel/test/index.js +120 -0
- package/dist/node_modules/qs/node_modules/side-channel-list/.editorconfig +9 -0
- package/dist/node_modules/qs/node_modules/side-channel-list/.eslintrc +11 -0
- package/dist/node_modules/qs/node_modules/side-channel-list/.github/FUNDING.yml +12 -0
- package/dist/node_modules/qs/node_modules/side-channel-list/.nycrc +13 -0
- package/dist/node_modules/qs/node_modules/side-channel-list/index.d.ts +13 -0
- package/dist/node_modules/qs/node_modules/side-channel-list/index.js +111 -0
- package/dist/node_modules/qs/node_modules/side-channel-list/list.d.ts +14 -0
- package/dist/node_modules/qs/node_modules/side-channel-list/package.json +77 -0
- package/dist/node_modules/qs/node_modules/side-channel-list/test/index.js +154 -0
- package/dist/node_modules/qs/package.json +1 -1
- package/dist/node_modules/qs/test/empty-keys-cases.js +261 -31
- package/dist/node_modules/qs/test/parse.js +1281 -57
- package/dist/node_modules/qs/test/stringify.js +851 -58
- package/dist/node_modules/qs/test/utils.js +505 -3
- package/package.json +5 -2
- package/dist/client/103.513149b773e262dc.js +0 -10
- package/dist/client-v2/103.8bcbf628d0af8457.js +0 -10
- package/dist/node_modules/qs/.eslintrc +0 -38
|
@@ -6,8 +6,10 @@ var utils = require('../lib/utils');
|
|
|
6
6
|
var iconv = require('iconv-lite');
|
|
7
7
|
var SaferBuffer = require('safer-buffer').Buffer;
|
|
8
8
|
var hasSymbols = require('has-symbols');
|
|
9
|
+
var mockProperty = require('mock-property');
|
|
9
10
|
var emptyTestCases = require('./empty-keys-cases').emptyTestCases;
|
|
10
|
-
var
|
|
11
|
+
var hasProto = require('has-proto')();
|
|
12
|
+
var hasBigInt = require('has-bigints')();
|
|
11
13
|
|
|
12
14
|
test('stringify()', function (t) {
|
|
13
15
|
t.test('stringifies a querystring object', function (st) {
|
|
@@ -64,6 +66,276 @@ test('stringify()', function (t) {
|
|
|
64
66
|
st.end();
|
|
65
67
|
});
|
|
66
68
|
|
|
69
|
+
t.test('encodes dot in key of object when encodeDotInKeys and allowDots is provided', function (st) {
|
|
70
|
+
st.equal(
|
|
71
|
+
qs.stringify(
|
|
72
|
+
{ 'name.obj': { first: 'John', last: 'Doe' } },
|
|
73
|
+
{ allowDots: false, encodeDotInKeys: false }
|
|
74
|
+
),
|
|
75
|
+
'name.obj%5Bfirst%5D=John&name.obj%5Blast%5D=Doe',
|
|
76
|
+
'with allowDots false and encodeDotInKeys false'
|
|
77
|
+
);
|
|
78
|
+
st.equal(
|
|
79
|
+
qs.stringify(
|
|
80
|
+
{ 'name.obj': { first: 'John', last: 'Doe' } },
|
|
81
|
+
{ allowDots: true, encodeDotInKeys: false }
|
|
82
|
+
),
|
|
83
|
+
'name.obj.first=John&name.obj.last=Doe',
|
|
84
|
+
'with allowDots true and encodeDotInKeys false'
|
|
85
|
+
);
|
|
86
|
+
st.equal(
|
|
87
|
+
qs.stringify(
|
|
88
|
+
{ 'name.obj': { first: 'John', last: 'Doe' } },
|
|
89
|
+
{ allowDots: false, encodeDotInKeys: true }
|
|
90
|
+
),
|
|
91
|
+
'name%252Eobj%5Bfirst%5D=John&name%252Eobj%5Blast%5D=Doe',
|
|
92
|
+
'with allowDots false and encodeDotInKeys true'
|
|
93
|
+
);
|
|
94
|
+
st.equal(
|
|
95
|
+
qs.stringify(
|
|
96
|
+
{ 'name.obj': { first: 'John', last: 'Doe' } },
|
|
97
|
+
{ allowDots: true, encodeDotInKeys: true }
|
|
98
|
+
),
|
|
99
|
+
'name%252Eobj.first=John&name%252Eobj.last=Doe',
|
|
100
|
+
'with allowDots true and encodeDotInKeys true'
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
st.equal(
|
|
104
|
+
qs.stringify(
|
|
105
|
+
{ 'name.obj.subobject': { 'first.godly.name': 'John', last: 'Doe' } },
|
|
106
|
+
{ allowDots: false, encodeDotInKeys: false }
|
|
107
|
+
),
|
|
108
|
+
'name.obj.subobject%5Bfirst.godly.name%5D=John&name.obj.subobject%5Blast%5D=Doe',
|
|
109
|
+
'with allowDots false and encodeDotInKeys false'
|
|
110
|
+
);
|
|
111
|
+
st.equal(
|
|
112
|
+
qs.stringify(
|
|
113
|
+
{ 'name.obj.subobject': { 'first.godly.name': 'John', last: 'Doe' } },
|
|
114
|
+
{ allowDots: true, encodeDotInKeys: false }
|
|
115
|
+
),
|
|
116
|
+
'name.obj.subobject.first.godly.name=John&name.obj.subobject.last=Doe',
|
|
117
|
+
'with allowDots false and encodeDotInKeys false'
|
|
118
|
+
);
|
|
119
|
+
st.equal(
|
|
120
|
+
qs.stringify(
|
|
121
|
+
{ 'name.obj.subobject': { 'first.godly.name': 'John', last: 'Doe' } },
|
|
122
|
+
{ allowDots: false, encodeDotInKeys: true }
|
|
123
|
+
),
|
|
124
|
+
'name%252Eobj%252Esubobject%5Bfirst.godly.name%5D=John&name%252Eobj%252Esubobject%5Blast%5D=Doe',
|
|
125
|
+
'with allowDots false and encodeDotInKeys true'
|
|
126
|
+
);
|
|
127
|
+
st.equal(
|
|
128
|
+
qs.stringify(
|
|
129
|
+
{ 'name.obj.subobject': { 'first.godly.name': 'John', last: 'Doe' } },
|
|
130
|
+
{ allowDots: true, encodeDotInKeys: true }
|
|
131
|
+
),
|
|
132
|
+
'name%252Eobj%252Esubobject.first%252Egodly%252Ename=John&name%252Eobj%252Esubobject.last=Doe',
|
|
133
|
+
'with allowDots true and encodeDotInKeys true'
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
st.end();
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
t.test('encodes dot in key of object with a primitive value when encodeDotInKeys and allowDots is provided', function (st) {
|
|
140
|
+
st.equal(
|
|
141
|
+
qs.stringify(
|
|
142
|
+
{ 'name.obj': 'John' },
|
|
143
|
+
{ allowDots: true, encodeDotInKeys: true }
|
|
144
|
+
),
|
|
145
|
+
'name%252Eobj=John',
|
|
146
|
+
'with allowDots true and encodeDotInKeys true'
|
|
147
|
+
);
|
|
148
|
+
st.equal(
|
|
149
|
+
qs.stringify(
|
|
150
|
+
{ 'name.obj': 'John' },
|
|
151
|
+
{ allowDots: false, encodeDotInKeys: true }
|
|
152
|
+
),
|
|
153
|
+
'name%252Eobj=John',
|
|
154
|
+
'with allowDots false and encodeDotInKeys true'
|
|
155
|
+
);
|
|
156
|
+
st.equal(
|
|
157
|
+
qs.stringify(
|
|
158
|
+
{ 'name.obj': 'John' },
|
|
159
|
+
{ encodeDotInKeys: true, allowDots: true, encodeValuesOnly: true }
|
|
160
|
+
),
|
|
161
|
+
'name%2Eobj=John',
|
|
162
|
+
'with encodeValuesOnly true'
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
// With `encodeValuesOnly` the key encoder is skipped, so the dot is encoded once
|
|
166
|
+
// (`%2E`) rather than twice. That single `%2E` decodes back to a structural dot, so
|
|
167
|
+
// unlike the default output below this does not round-trip to the original dotted key
|
|
168
|
+
// (a pre-existing `encodeValuesOnly` + `encodeDotInKeys` limitation, not new here).
|
|
169
|
+
st.deepEqual(
|
|
170
|
+
qs.parse(
|
|
171
|
+
qs.stringify(
|
|
172
|
+
{ 'name.obj': 'John' },
|
|
173
|
+
{ encodeDotInKeys: true, allowDots: true, encodeValuesOnly: true }
|
|
174
|
+
),
|
|
175
|
+
{ allowDots: true, decodeDotInKeys: true }
|
|
176
|
+
),
|
|
177
|
+
{ name: { obj: 'John' } },
|
|
178
|
+
'with encodeValuesOnly the single-encoded dot decodes structurally (does not round-trip)'
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
st.deepEqual(
|
|
182
|
+
qs.parse(
|
|
183
|
+
qs.stringify({ 'name.obj': 'John' }, { allowDots: true, encodeDotInKeys: true }),
|
|
184
|
+
{ allowDots: true, decodeDotInKeys: true }
|
|
185
|
+
),
|
|
186
|
+
{ 'name.obj': 'John' },
|
|
187
|
+
'round-trips a dotted key with a primitive value'
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
st.equal(
|
|
191
|
+
qs.stringify({ 'a.b': null }, { allowDots: true, encodeDotInKeys: true, strictNullHandling: true }),
|
|
192
|
+
'a%252Eb',
|
|
193
|
+
'encodes a dotted key with a null value'
|
|
194
|
+
);
|
|
195
|
+
st.equal(
|
|
196
|
+
qs.stringify({ 'a.b': '1', 'c.d': '2' }, { allowDots: true, encodeDotInKeys: true }),
|
|
197
|
+
'a%252Eb=1&c%252Ed=2',
|
|
198
|
+
'encodes dots in every top-level key, not just the first'
|
|
199
|
+
);
|
|
200
|
+
st.equal(
|
|
201
|
+
qs.stringify({ 'a.b.c': 'x' }, { allowDots: true, encodeDotInKeys: true }),
|
|
202
|
+
'a%252Eb%252Ec=x',
|
|
203
|
+
'encodes every dot in a key'
|
|
204
|
+
);
|
|
205
|
+
var primitiveKey = qs.stringify({ 'a.b': 'c' }, { allowDots: true, encodeDotInKeys: true }).split('=')[0];
|
|
206
|
+
st.equal(
|
|
207
|
+
qs.stringify({ 'a.b': { x: 'c' } }, { allowDots: true, encodeDotInKeys: true }).indexOf(primitiveKey + '.'),
|
|
208
|
+
0,
|
|
209
|
+
'a top-level dotted key encodes identically for primitive and object values'
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
st.end();
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
// Characterization of the CURRENT (buggy) behavior: with `allowDots` + `encodeDotInKeys`,
|
|
216
|
+
// `stringify` percent-encodes the structural `.` separators that `allowDots` inserts between
|
|
217
|
+
// nested keys, not just the literal dots inside a key, which breaks the stringify -> parse
|
|
218
|
+
// round-trip. These assertions pin that behavior so it cannot change silently: the fix in
|
|
219
|
+
// https://github.com/ljharb/qs/pull/564 changes every output below, so landing that fix MUST
|
|
220
|
+
// update this test (that is the point — it forces the behavior change to be explicit).
|
|
221
|
+
t.test('[characterization] encodeDotInKeys currently over-encodes structural separator dots (pre-#564)', function (st) {
|
|
222
|
+
st.equal(
|
|
223
|
+
qs.stringify({ a: { b: { c: 'd' } } }, { allowDots: true, encodeDotInKeys: true }),
|
|
224
|
+
'a%252Eb.c=d',
|
|
225
|
+
'over-encodes the a->b separator (#564: should become a.b.c=d)'
|
|
226
|
+
);
|
|
227
|
+
st.equal(
|
|
228
|
+
qs.stringify({ a: { b: { c: { d: 'e' } } } }, { allowDots: true, encodeDotInKeys: true }),
|
|
229
|
+
'a%252Eb%252Ec.d=e',
|
|
230
|
+
'clobbers every separator but the last (#564: should become a.b.c.d=e)'
|
|
231
|
+
);
|
|
232
|
+
st.equal(
|
|
233
|
+
qs.stringify({ 'a.b': { c: { d: 'e' } } }, { allowDots: true, encodeDotInKeys: true }),
|
|
234
|
+
'a%252Eb%252Ec.d=e',
|
|
235
|
+
'top-level dotted key: deeper separators also get encoded (#564: should become a%252Eb.c.d=e)'
|
|
236
|
+
);
|
|
237
|
+
st.equal(
|
|
238
|
+
qs.stringify({ a: { 'b.c': { d: 'e' } } }, { allowDots: true, encodeDotInKeys: true }),
|
|
239
|
+
'a%252Eb%252Ec.d=e',
|
|
240
|
+
'nested dotted key + object value: the separator around it is encoded (#564: should become a.b%252Ec.d=e)'
|
|
241
|
+
);
|
|
242
|
+
st.equal(
|
|
243
|
+
qs.stringify({ a: { b: { 'c.d': { e: 'f' } } } }, { allowDots: true, encodeDotInKeys: true }),
|
|
244
|
+
'a%252Eb%252Ec%252Ed.e=f',
|
|
245
|
+
'deeply nested dotted key: all separators encoded (#564: should become a.b.c%252Ed.e=f)'
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
st.equal(
|
|
249
|
+
qs.stringify({ a: { b: [1, 2] } }, { allowDots: true, encodeDotInKeys: true }),
|
|
250
|
+
'a%252Eb%5B0%5D=1&a%252Eb%5B1%5D=2',
|
|
251
|
+
'array value under nested keys: separator encoded (#564: should become a.b%5B0%5D=1&a.b%5B1%5D=2)'
|
|
252
|
+
);
|
|
253
|
+
st.equal(
|
|
254
|
+
qs.stringify({ a: { 'b.c': [1, 2] } }, { allowDots: true, encodeDotInKeys: true }),
|
|
255
|
+
'a%252Eb%252Ec%5B0%5D=1&a%252Eb%252Ec%5B1%5D=2',
|
|
256
|
+
'array value under nested dotted key (#564: should become a.b%252Ec%5B0%5D=1&a.b%252Ec%5B1%5D=2)'
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
st.equal(
|
|
260
|
+
qs.stringify({ a: { 'b.c': { d: 'e' } } }, { allowDots: false, encodeDotInKeys: true }),
|
|
261
|
+
'a%5Bb%252Ec%5D%5Bd%5D=e',
|
|
262
|
+
'allowDots:false object value encodes the nested-key dot, unlike the primitive-value case (#564: should become a%5Bb.c%5D%5Bd%5D=e)'
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
st.deepEqual(
|
|
266
|
+
qs.parse(
|
|
267
|
+
qs.stringify({ a: { b: { c: 'd' } } }, { allowDots: true, encodeDotInKeys: true }),
|
|
268
|
+
{ allowDots: true, decodeDotInKeys: true }
|
|
269
|
+
),
|
|
270
|
+
{ 'a.b': { c: 'd' } },
|
|
271
|
+
'the over-encoding breaks the round-trip: {a:{b:{c}}} comes back as {"a.b":{c}} (#564 restores it)'
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
st.end();
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
t.test('should encode dot in key of object, and automatically set allowDots to `true` when encodeDotInKeys is true and allowDots in undefined', function (st) {
|
|
278
|
+
st.equal(
|
|
279
|
+
qs.stringify(
|
|
280
|
+
{ 'name.obj.subobject': { 'first.godly.name': 'John', last: 'Doe' } },
|
|
281
|
+
{ encodeDotInKeys: true }
|
|
282
|
+
),
|
|
283
|
+
'name%252Eobj%252Esubobject.first%252Egodly%252Ename=John&name%252Eobj%252Esubobject.last=Doe',
|
|
284
|
+
'with allowDots undefined and encodeDotInKeys true'
|
|
285
|
+
);
|
|
286
|
+
st.end();
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
t.test('should encode dot in key of object when encodeDotInKeys and allowDots is provided, and nothing else when encodeValuesOnly is provided', function (st) {
|
|
290
|
+
st.equal(
|
|
291
|
+
qs.stringify({ 'name.obj': { first: 'John', last: 'Doe' } }, {
|
|
292
|
+
encodeDotInKeys: true, allowDots: true, encodeValuesOnly: true
|
|
293
|
+
}),
|
|
294
|
+
'name%2Eobj.first=John&name%2Eobj.last=Doe'
|
|
295
|
+
);
|
|
296
|
+
|
|
297
|
+
st.equal(
|
|
298
|
+
qs.stringify({ 'name.obj.subobject': { 'first.godly.name': 'John', last: 'Doe' } }, { allowDots: true, encodeDotInKeys: true, encodeValuesOnly: true }),
|
|
299
|
+
'name%2Eobj%2Esubobject.first%2Egodly%2Ename=John&name%2Eobj%2Esubobject.last=Doe'
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
st.end();
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
t.test('throws when `commaRoundTrip` is not a boolean', function (st) {
|
|
306
|
+
st['throws'](
|
|
307
|
+
function () { qs.stringify({}, { commaRoundTrip: 'not a boolean' }); },
|
|
308
|
+
TypeError,
|
|
309
|
+
'throws when `commaRoundTrip` is not a boolean'
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
st.end();
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
t.test('throws when `encodeDotInKeys` is not a boolean', function (st) {
|
|
316
|
+
st['throws'](
|
|
317
|
+
function () { qs.stringify({ a: [], b: 'zz' }, { encodeDotInKeys: 'foobar' }); },
|
|
318
|
+
TypeError
|
|
319
|
+
);
|
|
320
|
+
|
|
321
|
+
st['throws'](
|
|
322
|
+
function () { qs.stringify({ a: [], b: 'zz' }, { encodeDotInKeys: 0 }); },
|
|
323
|
+
TypeError
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
st['throws'](
|
|
327
|
+
function () { qs.stringify({ a: [], b: 'zz' }, { encodeDotInKeys: NaN }); },
|
|
328
|
+
TypeError
|
|
329
|
+
);
|
|
330
|
+
|
|
331
|
+
st['throws'](
|
|
332
|
+
function () { qs.stringify({ a: [], b: 'zz' }, { encodeDotInKeys: null }); },
|
|
333
|
+
TypeError
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
st.end();
|
|
337
|
+
});
|
|
338
|
+
|
|
67
339
|
t.test('adds query prefix', function (st) {
|
|
68
340
|
st.equal(qs.stringify({ a: 'b' }, { addQueryPrefix: true }), '?a=b');
|
|
69
341
|
st.end();
|
|
@@ -87,7 +359,7 @@ test('stringify()', function (t) {
|
|
|
87
359
|
st.end();
|
|
88
360
|
});
|
|
89
361
|
|
|
90
|
-
t.test('stringifies a nested object with dots notation', function (st) {
|
|
362
|
+
t.test('`allowDots` option: stringifies a nested object with dots notation', function (st) {
|
|
91
363
|
st.equal(qs.stringify({ a: { b: 'c' } }, { allowDots: true }), 'a.b=c');
|
|
92
364
|
st.equal(qs.stringify({ a: { b: { c: { d: 'e' } } } }, { allowDots: true }), 'a.b.c.d=e');
|
|
93
365
|
st.end();
|
|
@@ -109,6 +381,11 @@ test('stringify()', function (t) {
|
|
|
109
381
|
'a=b%2Cc%2Cd',
|
|
110
382
|
'comma => comma'
|
|
111
383
|
);
|
|
384
|
+
st.equal(
|
|
385
|
+
qs.stringify({ a: ['b', 'c', 'd'] }, { arrayFormat: 'comma', commaRoundTrip: true }),
|
|
386
|
+
'a=b%2Cc%2Cd',
|
|
387
|
+
'comma round trip => comma'
|
|
388
|
+
);
|
|
112
389
|
st.equal(
|
|
113
390
|
qs.stringify({ a: ['b', 'c', 'd'] }),
|
|
114
391
|
'a%5B0%5D=b&a%5B1%5D=c&a%5B2%5D=d',
|
|
@@ -117,18 +394,108 @@ test('stringify()', function (t) {
|
|
|
117
394
|
st.end();
|
|
118
395
|
});
|
|
119
396
|
|
|
120
|
-
t.test('
|
|
121
|
-
st.equal(
|
|
122
|
-
|
|
123
|
-
|
|
397
|
+
t.test('`skipNulls` option', function (st) {
|
|
398
|
+
st.equal(
|
|
399
|
+
qs.stringify({ a: 'b', c: null }, { skipNulls: true }),
|
|
400
|
+
'a=b',
|
|
401
|
+
'omits nulls when asked'
|
|
402
|
+
);
|
|
403
|
+
|
|
404
|
+
st.equal(
|
|
405
|
+
qs.stringify({ a: { b: 'c', d: null } }, { skipNulls: true }),
|
|
406
|
+
'a%5Bb%5D=c',
|
|
407
|
+
'omits nested nulls when asked'
|
|
408
|
+
);
|
|
124
409
|
|
|
125
|
-
t.test('omits nested nulls when asked', function (st) {
|
|
126
|
-
st.equal(qs.stringify({ a: { b: 'c', d: null } }, { skipNulls: true }), 'a%5Bb%5D=c');
|
|
127
410
|
st.end();
|
|
128
411
|
});
|
|
129
412
|
|
|
130
413
|
t.test('omits array indices when asked', function (st) {
|
|
131
414
|
st.equal(qs.stringify({ a: ['b', 'c', 'd'] }, { indices: false }), 'a=b&a=c&a=d');
|
|
415
|
+
|
|
416
|
+
st.end();
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
t.test('omits object key/value pair when value is empty array', function (st) {
|
|
420
|
+
st.equal(qs.stringify({ a: [], b: 'zz' }), 'b=zz');
|
|
421
|
+
|
|
422
|
+
st.end();
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
t.test('should not omit object key/value pair when value is empty array and when asked', function (st) {
|
|
426
|
+
st.equal(qs.stringify({ a: [], b: 'zz' }), 'b=zz');
|
|
427
|
+
st.equal(qs.stringify({ a: [], b: 'zz' }, { allowEmptyArrays: false }), 'b=zz');
|
|
428
|
+
st.equal(qs.stringify({ a: [], b: 'zz' }, { allowEmptyArrays: true }), 'a[]&b=zz');
|
|
429
|
+
|
|
430
|
+
st.end();
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
t.test('should throw when allowEmptyArrays is not of type boolean', function (st) {
|
|
434
|
+
st['throws'](
|
|
435
|
+
function () { qs.stringify({ a: [], b: 'zz' }, { allowEmptyArrays: 'foobar' }); },
|
|
436
|
+
TypeError
|
|
437
|
+
);
|
|
438
|
+
|
|
439
|
+
st['throws'](
|
|
440
|
+
function () { qs.stringify({ a: [], b: 'zz' }, { allowEmptyArrays: 0 }); },
|
|
441
|
+
TypeError
|
|
442
|
+
);
|
|
443
|
+
|
|
444
|
+
st['throws'](
|
|
445
|
+
function () { qs.stringify({ a: [], b: 'zz' }, { allowEmptyArrays: NaN }); },
|
|
446
|
+
TypeError
|
|
447
|
+
);
|
|
448
|
+
|
|
449
|
+
st['throws'](
|
|
450
|
+
function () { qs.stringify({ a: [], b: 'zz' }, { allowEmptyArrays: null }); },
|
|
451
|
+
TypeError
|
|
452
|
+
);
|
|
453
|
+
|
|
454
|
+
st.end();
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
t.test('allowEmptyArrays + strictNullHandling', function (st) {
|
|
458
|
+
st.equal(
|
|
459
|
+
qs.stringify(
|
|
460
|
+
{ testEmptyArray: [] },
|
|
461
|
+
{ strictNullHandling: true, allowEmptyArrays: true }
|
|
462
|
+
),
|
|
463
|
+
'testEmptyArray[]'
|
|
464
|
+
);
|
|
465
|
+
|
|
466
|
+
st.end();
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
t.test('allowEmptyArrays only short-circuits arrays with no own keys', function (st) {
|
|
470
|
+
st.equal(
|
|
471
|
+
qs.stringify({ a: [] }, { allowEmptyArrays: true }),
|
|
472
|
+
'a[]',
|
|
473
|
+
'a genuinely empty array is still emitted as `[]`'
|
|
474
|
+
);
|
|
475
|
+
|
|
476
|
+
var withKey = [];
|
|
477
|
+
withKey.extra = 'x';
|
|
478
|
+
st.equal(
|
|
479
|
+
qs.stringify({ a: withKey }, { allowEmptyArrays: true }),
|
|
480
|
+
'a%5Bextra%5D=x',
|
|
481
|
+
'an own key on an empty array is serialized rather than dropped'
|
|
482
|
+
);
|
|
483
|
+
st.equal(
|
|
484
|
+
qs.stringify({ a: withKey }, { allowEmptyArrays: true }),
|
|
485
|
+
qs.stringify({ a: withKey }),
|
|
486
|
+
'the allowEmptyArrays fast path matches the default path when the array has own keys'
|
|
487
|
+
);
|
|
488
|
+
|
|
489
|
+
var container = {};
|
|
490
|
+
var cyclic = [];
|
|
491
|
+
cyclic.back = container;
|
|
492
|
+
container.a = cyclic;
|
|
493
|
+
st['throws'](
|
|
494
|
+
function () { qs.stringify(container, { allowEmptyArrays: true }); },
|
|
495
|
+
RangeError,
|
|
496
|
+
'a cycle through an empty array with an own key is still detected'
|
|
497
|
+
);
|
|
498
|
+
|
|
132
499
|
st.end();
|
|
133
500
|
});
|
|
134
501
|
|
|
@@ -156,6 +523,7 @@ test('stringify()', function (t) {
|
|
|
156
523
|
s2t.equal(qs.stringify({ a: ['c', 'd'] }, { encodeValuesOnly: true, arrayFormat: 'indices' }), 'a[0]=c&a[1]=d');
|
|
157
524
|
s2t.equal(qs.stringify({ a: ['c', 'd'] }, { encodeValuesOnly: true, arrayFormat: 'brackets' }), 'a[]=c&a[]=d');
|
|
158
525
|
s2t.equal(qs.stringify({ a: ['c', 'd'] }, { encodeValuesOnly: true, arrayFormat: 'comma' }), 'a=c,d');
|
|
526
|
+
s2t.equal(qs.stringify({ a: ['c', 'd'] }, { encodeValuesOnly: true, arrayFormat: 'comma', commaRoundTrip: true }), 'a=c,d');
|
|
159
527
|
s2t.equal(qs.stringify({ a: ['c', 'd'] }, { encodeValuesOnly: true }), 'a[0]=c&a[1]=d');
|
|
160
528
|
|
|
161
529
|
s2t.end();
|
|
@@ -165,6 +533,9 @@ test('stringify()', function (t) {
|
|
|
165
533
|
s2t.equal(qs.stringify({ a: ['c,d', 'e'] }, { encodeValuesOnly: true, arrayFormat: 'comma' }), 'a=c%2Cd,e');
|
|
166
534
|
s2t.equal(qs.stringify({ a: ['c,d', 'e'] }, { arrayFormat: 'comma' }), 'a=c%2Cd%2Ce');
|
|
167
535
|
|
|
536
|
+
s2t.equal(qs.stringify({ a: ['c,d', 'e'] }, { encodeValuesOnly: true, arrayFormat: 'comma', commaRoundTrip: true }), 'a=c%2Cd,e');
|
|
537
|
+
s2t.equal(qs.stringify({ a: ['c,d', 'e'] }, { arrayFormat: 'comma', commaRoundTrip: true }), 'a=c%2Cd%2Ce');
|
|
538
|
+
|
|
168
539
|
s2t.end();
|
|
169
540
|
});
|
|
170
541
|
|
|
@@ -255,36 +626,44 @@ test('stringify()', function (t) {
|
|
|
255
626
|
|
|
256
627
|
t.test('stringifies an object inside an array', function (st) {
|
|
257
628
|
st.equal(
|
|
258
|
-
qs.stringify({ a: [{ b: 'c' }] }, { arrayFormat: 'indices' }),
|
|
259
|
-
'a
|
|
260
|
-
'indices =>
|
|
629
|
+
qs.stringify({ a: [{ b: 'c' }] }, { arrayFormat: 'indices', encodeValuesOnly: true }),
|
|
630
|
+
'a[0][b]=c',
|
|
631
|
+
'indices => indices'
|
|
632
|
+
);
|
|
633
|
+
st.equal(
|
|
634
|
+
qs.stringify({ a: [{ b: 'c' }] }, { arrayFormat: 'repeat', encodeValuesOnly: true }),
|
|
635
|
+
'a[b]=c',
|
|
636
|
+
'repeat => repeat'
|
|
261
637
|
);
|
|
262
638
|
st.equal(
|
|
263
|
-
qs.stringify({ a: [{ b: 'c' }] }, { arrayFormat: 'brackets' }),
|
|
264
|
-
'a
|
|
639
|
+
qs.stringify({ a: [{ b: 'c' }] }, { arrayFormat: 'brackets', encodeValuesOnly: true }),
|
|
640
|
+
'a[][b]=c',
|
|
265
641
|
'brackets => brackets'
|
|
266
642
|
);
|
|
267
643
|
st.equal(
|
|
268
|
-
qs.stringify({ a: [{ b: 'c' }] }),
|
|
269
|
-
'a
|
|
644
|
+
qs.stringify({ a: [{ b: 'c' }] }, { encodeValuesOnly: true }),
|
|
645
|
+
'a[0][b]=c',
|
|
270
646
|
'default => indices'
|
|
271
647
|
);
|
|
272
648
|
|
|
273
649
|
st.equal(
|
|
274
|
-
qs.stringify({ a: [{ b: { c: [1] } }] }, { arrayFormat: 'indices' }),
|
|
275
|
-
'a
|
|
650
|
+
qs.stringify({ a: [{ b: { c: [1] } }] }, { arrayFormat: 'indices', encodeValuesOnly: true }),
|
|
651
|
+
'a[0][b][c][0]=1',
|
|
276
652
|
'indices => indices'
|
|
277
653
|
);
|
|
278
|
-
|
|
279
654
|
st.equal(
|
|
280
|
-
qs.stringify({ a: [{ b: { c: [1] } }] }, { arrayFormat: '
|
|
281
|
-
'a
|
|
655
|
+
qs.stringify({ a: [{ b: { c: [1] } }] }, { arrayFormat: 'repeat', encodeValuesOnly: true }),
|
|
656
|
+
'a[b][c]=1',
|
|
657
|
+
'repeat => repeat'
|
|
658
|
+
);
|
|
659
|
+
st.equal(
|
|
660
|
+
qs.stringify({ a: [{ b: { c: [1] } }] }, { arrayFormat: 'brackets', encodeValuesOnly: true }),
|
|
661
|
+
'a[][b][c][]=1',
|
|
282
662
|
'brackets => brackets'
|
|
283
663
|
);
|
|
284
|
-
|
|
285
664
|
st.equal(
|
|
286
|
-
qs.stringify({ a: [{ b: { c: [1] } }] }),
|
|
287
|
-
'a
|
|
665
|
+
qs.stringify({ a: [{ b: { c: [1] } }] }, { encodeValuesOnly: true }),
|
|
666
|
+
'a[0][b][c][0]=1',
|
|
288
667
|
'default => indices'
|
|
289
668
|
);
|
|
290
669
|
|
|
@@ -386,17 +765,17 @@ test('stringify()', function (t) {
|
|
|
386
765
|
st.end();
|
|
387
766
|
});
|
|
388
767
|
|
|
389
|
-
t.test('uses indices notation for arrays when
|
|
768
|
+
t.test('uses indices notation for arrays when arrayFormat=indices', function (st) {
|
|
390
769
|
st.equal(qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'indices' }), 'a%5B0%5D=b&a%5B1%5D=c');
|
|
391
770
|
st.end();
|
|
392
771
|
});
|
|
393
772
|
|
|
394
|
-
t.test('uses repeat notation for arrays when
|
|
773
|
+
t.test('uses repeat notation for arrays when arrayFormat=repeat', function (st) {
|
|
395
774
|
st.equal(qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'repeat' }), 'a=b&a=c');
|
|
396
775
|
st.end();
|
|
397
776
|
});
|
|
398
777
|
|
|
399
|
-
t.test('uses brackets notation for arrays when
|
|
778
|
+
t.test('uses brackets notation for arrays when arrayFormat=brackets', function (st) {
|
|
400
779
|
st.equal(qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'brackets' }), 'a%5B%5D=b&a%5B%5D=c');
|
|
401
780
|
st.end();
|
|
402
781
|
});
|
|
@@ -443,10 +822,51 @@ test('stringify()', function (t) {
|
|
|
443
822
|
st.end();
|
|
444
823
|
});
|
|
445
824
|
|
|
446
|
-
t.test('
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
825
|
+
t.test('does not crash on null/undefined entries in arrayFormat=comma with encodeValuesOnly', function (st) {
|
|
826
|
+
st.doesNotThrow(
|
|
827
|
+
function () { qs.stringify({ a: [null, 'b'] }, { arrayFormat: 'comma', encodeValuesOnly: true }); },
|
|
828
|
+
'does not pass a raw null array entry to the encoder'
|
|
829
|
+
);
|
|
830
|
+
st.doesNotThrow(
|
|
831
|
+
function () { qs.stringify({ a: [undefined, 'b'] }, { arrayFormat: 'comma', encodeValuesOnly: true }); },
|
|
832
|
+
'does not pass a raw undefined array entry to the encoder'
|
|
833
|
+
);
|
|
834
|
+
st.doesNotThrow(
|
|
835
|
+
function () { qs.stringify({ a: [null] }, { arrayFormat: 'comma', encodeValuesOnly: true }); },
|
|
836
|
+
'does not crash on a single-null array'
|
|
837
|
+
);
|
|
838
|
+
|
|
839
|
+
st.equal(
|
|
840
|
+
qs.stringify({ a: [null, 'b'] }, { arrayFormat: 'comma', encodeValuesOnly: true }),
|
|
841
|
+
'a=,b',
|
|
842
|
+
'null entry joins as empty, comma stays unencoded under encodeValuesOnly'
|
|
843
|
+
);
|
|
844
|
+
st.equal(
|
|
845
|
+
qs.stringify({ a: [undefined, 'b'] }, { arrayFormat: 'comma', encodeValuesOnly: true }),
|
|
846
|
+
'a=,b',
|
|
847
|
+
'undefined entry joins as empty, comma stays unencoded under encodeValuesOnly'
|
|
848
|
+
);
|
|
849
|
+
st.equal(
|
|
850
|
+
qs.stringify({ a: [null] }, { arrayFormat: 'comma', encodeValuesOnly: true }),
|
|
851
|
+
'a=',
|
|
852
|
+
'single-null array stringifies as empty value'
|
|
853
|
+
);
|
|
854
|
+
st.equal(
|
|
855
|
+
qs.stringify({ a: [null] }, { arrayFormat: 'comma', encodeValuesOnly: true, strictNullHandling: true }),
|
|
856
|
+
'a',
|
|
857
|
+
'strictNullHandling drops the equals sign for a single-null array'
|
|
858
|
+
);
|
|
859
|
+
st.equal(
|
|
860
|
+
qs.stringify({ a: [null] }, { arrayFormat: 'comma', encodeValuesOnly: true, skipNulls: true }),
|
|
861
|
+
'',
|
|
862
|
+
'skipNulls drops a single-null array entirely'
|
|
863
|
+
);
|
|
864
|
+
|
|
865
|
+
st.end();
|
|
866
|
+
});
|
|
867
|
+
|
|
868
|
+
t.test('stringifies a null object', { skip: !hasProto }, function (st) {
|
|
869
|
+
st.equal(qs.stringify({ __proto__: null, a: 'b' }), 'a=b');
|
|
450
870
|
st.end();
|
|
451
871
|
});
|
|
452
872
|
|
|
@@ -458,11 +878,8 @@ test('stringify()', function (t) {
|
|
|
458
878
|
st.end();
|
|
459
879
|
});
|
|
460
880
|
|
|
461
|
-
t.test('stringifies an object with a null object as a child', { skip: !
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
obj.a.b = 'c';
|
|
465
|
-
st.equal(qs.stringify(obj), 'a%5Bb%5D=c');
|
|
881
|
+
t.test('stringifies an object with a null object as a child', { skip: !hasProto }, function (st) {
|
|
882
|
+
st.equal(qs.stringify({ a: { __proto__: null, b: 'c' } }), 'a%5Bb%5D=c');
|
|
466
883
|
st.end();
|
|
467
884
|
});
|
|
468
885
|
|
|
@@ -493,10 +910,11 @@ test('stringify()', function (t) {
|
|
|
493
910
|
});
|
|
494
911
|
|
|
495
912
|
t.test('skips properties that are part of the object prototype', function (st) {
|
|
496
|
-
Object.prototype
|
|
913
|
+
st.intercept(Object.prototype, 'crash', { value: 'test' });
|
|
914
|
+
|
|
497
915
|
st.equal(qs.stringify({ a: 'b' }), 'a=b');
|
|
498
916
|
st.equal(qs.stringify({ a: { b: 'c' } }), 'a%5Bb%5D=c');
|
|
499
|
-
|
|
917
|
+
|
|
500
918
|
st.end();
|
|
501
919
|
});
|
|
502
920
|
|
|
@@ -514,16 +932,45 @@ test('stringify()', function (t) {
|
|
|
514
932
|
st.end();
|
|
515
933
|
});
|
|
516
934
|
|
|
935
|
+
t.test('does not throw on a non-callable `constructor.isBuffer` key', function (st) {
|
|
936
|
+
var input = { a: { constructor: { isBuffer: 'x' } } };
|
|
937
|
+
st.doesNotThrow(function () { qs.stringify(input); });
|
|
938
|
+
st.equal(qs.stringify(input), 'a%5Bconstructor%5D%5BisBuffer%5D=x');
|
|
939
|
+
st.equal(qs.stringify({ a: { constructor: { isBuffer: { b: 'c' } } } }), 'a%5Bconstructor%5D%5BisBuffer%5D%5Bb%5D=c');
|
|
940
|
+
st.equal(qs.stringify({ a: { constructor: { isBuffer: ['b'] } } }), 'a%5Bconstructor%5D%5BisBuffer%5D%5B0%5D=b');
|
|
941
|
+
st.equal(qs.stringify({ a: { constructor: { isBuffer: true } } }), 'a%5Bconstructor%5D%5BisBuffer%5D=true');
|
|
942
|
+
st.end();
|
|
943
|
+
});
|
|
944
|
+
|
|
945
|
+
t.test('round-trips a parsed `constructor[isBuffer]` key', function (st) {
|
|
946
|
+
var str = 'x%5Bconstructor%5D%5BisBuffer%5D=y';
|
|
947
|
+
|
|
948
|
+
var plain = qs.parse(str, { plainObjects: true });
|
|
949
|
+
st.deepEqual(plain, { __proto__: null, x: { __proto__: null, constructor: { __proto__: null, isBuffer: 'y' } } }, 'plainObjects keeps the key');
|
|
950
|
+
st.doesNotThrow(function () { qs.stringify(plain); });
|
|
951
|
+
st.equal(qs.stringify(plain), str, 'plainObjects round-trips');
|
|
952
|
+
|
|
953
|
+
var proto = qs.parse(str, { allowPrototypes: true });
|
|
954
|
+
st.deepEqual(proto, { x: { constructor: { isBuffer: 'y' } } }, 'allowPrototypes keeps the key');
|
|
955
|
+
st.doesNotThrow(function () { qs.stringify(proto); });
|
|
956
|
+
st.equal(qs.stringify(proto), str, 'allowPrototypes round-trips');
|
|
957
|
+
|
|
958
|
+
st.equal(qs.stringify(qs.parse(str)), '', 'default options drop the key');
|
|
959
|
+
st.end();
|
|
960
|
+
});
|
|
961
|
+
|
|
517
962
|
t.test('stringifies an object using an alternative delimiter', function (st) {
|
|
518
963
|
st.equal(qs.stringify({ a: 'b', c: 'd' }, { delimiter: ';' }), 'a=b;c=d');
|
|
519
964
|
st.end();
|
|
520
965
|
});
|
|
521
966
|
|
|
522
967
|
t.test('does not blow up when Buffer global is missing', function (st) {
|
|
523
|
-
var
|
|
524
|
-
|
|
968
|
+
var restore = mockProperty(global, 'Buffer', { 'delete': true });
|
|
969
|
+
|
|
525
970
|
var result = qs.stringify({ a: 'b', c: 'd' });
|
|
526
|
-
|
|
971
|
+
|
|
972
|
+
restore();
|
|
973
|
+
|
|
527
974
|
st.equal(result, 'a=b&c=d');
|
|
528
975
|
st.end();
|
|
529
976
|
});
|
|
@@ -572,9 +1019,17 @@ test('stringify()', function (t) {
|
|
|
572
1019
|
};
|
|
573
1020
|
|
|
574
1021
|
st.equal(
|
|
575
|
-
qs.stringify({ filters: { $and: [p1, p2] } }, { encodeValuesOnly: true }),
|
|
1022
|
+
qs.stringify({ filters: { $and: [p1, p2] } }, { encodeValuesOnly: true, arrayFormat: 'indices' }),
|
|
576
1023
|
'filters[$and][0][function]=gte&filters[$and][0][arguments][0][function]=hour_of_day&filters[$and][0][arguments][1]=0&filters[$and][1][function]=lte&filters[$and][1][arguments][0][function]=hour_of_day&filters[$and][1][arguments][1]=23'
|
|
577
1024
|
);
|
|
1025
|
+
st.equal(
|
|
1026
|
+
qs.stringify({ filters: { $and: [p1, p2] } }, { encodeValuesOnly: true, arrayFormat: 'brackets' }),
|
|
1027
|
+
'filters[$and][][function]=gte&filters[$and][][arguments][][function]=hour_of_day&filters[$and][][arguments][]=0&filters[$and][][function]=lte&filters[$and][][arguments][][function]=hour_of_day&filters[$and][][arguments][]=23'
|
|
1028
|
+
);
|
|
1029
|
+
st.equal(
|
|
1030
|
+
qs.stringify({ filters: { $and: [p1, p2] } }, { encodeValuesOnly: true, arrayFormat: 'repeat' }),
|
|
1031
|
+
'filters[$and][function]=gte&filters[$and][arguments][function]=hour_of_day&filters[$and][arguments]=0&filters[$and][function]=lte&filters[$and][arguments][function]=hour_of_day&filters[$and][arguments]=23'
|
|
1032
|
+
);
|
|
578
1033
|
|
|
579
1034
|
st.end();
|
|
580
1035
|
});
|
|
@@ -611,6 +1066,35 @@ test('stringify()', function (t) {
|
|
|
611
1066
|
st.end();
|
|
612
1067
|
});
|
|
613
1068
|
|
|
1069
|
+
t.test('skips null/undefined entries in filter=array', function (st) {
|
|
1070
|
+
st.doesNotThrow(
|
|
1071
|
+
function () { qs.stringify({ a: 'b', undefined: 'x' }, { filter: ['a', undefined] }); },
|
|
1072
|
+
'does not pass a raw undefined filter entry to the encoder'
|
|
1073
|
+
);
|
|
1074
|
+
st.doesNotThrow(
|
|
1075
|
+
function () { qs.stringify({ a: 'b', 'null': 'x' }, { filter: ['a', null] }); },
|
|
1076
|
+
'does not pass a raw null filter entry to the encoder'
|
|
1077
|
+
);
|
|
1078
|
+
|
|
1079
|
+
st.equal(
|
|
1080
|
+
qs.stringify({ a: 'b', undefined: 'x', c: 'd' }, { filter: ['a', undefined, 'c'] }),
|
|
1081
|
+
'a=b&c=d',
|
|
1082
|
+
'undefined filter entry is skipped, remaining keys are kept'
|
|
1083
|
+
);
|
|
1084
|
+
st.equal(
|
|
1085
|
+
qs.stringify({ a: 'b', 'null': 'x', c: 'd' }, { filter: ['a', null, 'c'] }),
|
|
1086
|
+
'a=b&c=d',
|
|
1087
|
+
'null filter entry is skipped, remaining keys are kept'
|
|
1088
|
+
);
|
|
1089
|
+
st.equal(
|
|
1090
|
+
qs.stringify({ a: 'b', 'null': 'x' }, { filter: [null] }),
|
|
1091
|
+
'',
|
|
1092
|
+
'filter array containing only null yields empty string'
|
|
1093
|
+
);
|
|
1094
|
+
|
|
1095
|
+
st.end();
|
|
1096
|
+
});
|
|
1097
|
+
|
|
614
1098
|
t.test('supports custom representations when filter=function', function (st) {
|
|
615
1099
|
var calls = 0;
|
|
616
1100
|
var obj = { a: 'b', c: 'd', e: { f: new Date(1257894000000) } };
|
|
@@ -633,6 +1117,66 @@ test('stringify()', function (t) {
|
|
|
633
1117
|
st.end();
|
|
634
1118
|
});
|
|
635
1119
|
|
|
1120
|
+
t.test('serializes Date values unless filter transforms them', function (st) {
|
|
1121
|
+
var obj = {
|
|
1122
|
+
a: new Date(Date.UTC(2023, 2, 7, 9, 2, 41, 260)),
|
|
1123
|
+
b: [new Date(Date.UTC(2023, 2, 7, 9, 2, 41, 260))]
|
|
1124
|
+
};
|
|
1125
|
+
var iso = '2023-03-07T09%3A02%3A41.260Z';
|
|
1126
|
+
st.equal(
|
|
1127
|
+
qs.stringify(obj, { filter: function (prefix, value) { return value; } }),
|
|
1128
|
+
'a=' + iso + '&b%5B0%5D=' + iso,
|
|
1129
|
+
'Dates returned unchanged by a filter are serialized'
|
|
1130
|
+
);
|
|
1131
|
+
st.equal(
|
|
1132
|
+
qs.stringify({ a: new Date(0) }, { filter: function (prefix, value) { return value instanceof Date ? 'kept' : value; } }),
|
|
1133
|
+
'a=kept',
|
|
1134
|
+
'a filter that transforms a Date wins over default serialization'
|
|
1135
|
+
);
|
|
1136
|
+
st.end();
|
|
1137
|
+
});
|
|
1138
|
+
|
|
1139
|
+
t.test('applies default Date handling to whatever a filter function returns', function (st) {
|
|
1140
|
+
var date = new Date(Date.UTC(2023, 2, 7, 9, 2, 41, 260));
|
|
1141
|
+
var iso = '2023-03-07T09%3A02%3A41.260Z';
|
|
1142
|
+
var identity = function (prefix, value) { return value; };
|
|
1143
|
+
|
|
1144
|
+
st.equal(
|
|
1145
|
+
qs.stringify({ a: { b: date } }, { filter: identity }),
|
|
1146
|
+
'a%5Bb%5D=' + iso,
|
|
1147
|
+
'a nested Date is serialized through a filter'
|
|
1148
|
+
);
|
|
1149
|
+
st.equal(
|
|
1150
|
+
qs.stringify(
|
|
1151
|
+
{ a: date },
|
|
1152
|
+
{ filter: identity, serializeDate: function (d) { return String(d.getTime()); } }
|
|
1153
|
+
),
|
|
1154
|
+
'a=1678179761260',
|
|
1155
|
+
'`serializeDate` is honored after a filter'
|
|
1156
|
+
);
|
|
1157
|
+
st.equal(
|
|
1158
|
+
qs.stringify({ a: [date, 'x'] }, { filter: identity, arrayFormat: 'comma' }),
|
|
1159
|
+
'a=' + iso + '%2Cx',
|
|
1160
|
+
'Dates inside a comma array are serialized through a filter, not stringified as locale text'
|
|
1161
|
+
);
|
|
1162
|
+
st.equal(
|
|
1163
|
+
qs.stringify({ a: 'x' }, { filter: function (prefix, value) { return prefix === 'a' ? date : value; } }),
|
|
1164
|
+
'a=' + iso,
|
|
1165
|
+
'a Date produced by the filter is serialized'
|
|
1166
|
+
);
|
|
1167
|
+
st.equal(
|
|
1168
|
+
qs.stringify({ a: date, b: 1 }, { filter: ['a'] }),
|
|
1169
|
+
'a=' + iso,
|
|
1170
|
+
'an array filter still serializes Dates'
|
|
1171
|
+
);
|
|
1172
|
+
st['throws'](
|
|
1173
|
+
function () { qs.stringify({ a: new Date(NaN) }, { filter: identity }); },
|
|
1174
|
+
RangeError,
|
|
1175
|
+
'an invalid Date through a filter throws like the no-filter path'
|
|
1176
|
+
);
|
|
1177
|
+
st.end();
|
|
1178
|
+
});
|
|
1179
|
+
|
|
636
1180
|
t.test('can disable uri encoding', function (st) {
|
|
637
1181
|
st.equal(qs.stringify({ a: 'b' }, { encode: false }), 'a=b');
|
|
638
1182
|
st.equal(qs.stringify({ a: { b: 'c' } }, { encode: false }), 'a[b]=c');
|
|
@@ -687,13 +1231,28 @@ test('stringify()', function (t) {
|
|
|
687
1231
|
st.end();
|
|
688
1232
|
});
|
|
689
1233
|
|
|
1234
|
+
t.test('receives the default encoder as a second argument', function (st) {
|
|
1235
|
+
st.plan(8);
|
|
1236
|
+
|
|
1237
|
+
qs.stringify({ a: 1, b: new Date(), c: true, d: [1] }, {
|
|
1238
|
+
encoder: function (str) {
|
|
1239
|
+
st.match(typeof str, /^(?:string|number|boolean)$/);
|
|
1240
|
+
return '';
|
|
1241
|
+
}
|
|
1242
|
+
});
|
|
1243
|
+
|
|
1244
|
+
st.end();
|
|
1245
|
+
});
|
|
1246
|
+
|
|
690
1247
|
t.test('receives the default encoder as a second argument', function (st) {
|
|
691
1248
|
st.plan(2);
|
|
1249
|
+
|
|
692
1250
|
qs.stringify({ a: 1 }, {
|
|
693
1251
|
encoder: function (str, defaultEncoder) {
|
|
694
1252
|
st.equal(defaultEncoder, utils.encode);
|
|
695
1253
|
}
|
|
696
1254
|
});
|
|
1255
|
+
|
|
697
1256
|
st.end();
|
|
698
1257
|
});
|
|
699
1258
|
|
|
@@ -821,16 +1380,53 @@ test('stringify()', function (t) {
|
|
|
821
1380
|
st.equal(
|
|
822
1381
|
qs.stringify(
|
|
823
1382
|
{ a: 'b', c: ['d', 'e=f'], f: [['g'], ['h']] },
|
|
824
|
-
{ encodeValuesOnly: true }
|
|
1383
|
+
{ encodeValuesOnly: true, arrayFormat: 'indices' }
|
|
825
1384
|
),
|
|
826
|
-
'a=b&c[0]=d&c[1]=e%3Df&f[0][0]=g&f[1][0]=h'
|
|
1385
|
+
'a=b&c[0]=d&c[1]=e%3Df&f[0][0]=g&f[1][0]=h',
|
|
1386
|
+
'encodeValuesOnly + indices'
|
|
827
1387
|
);
|
|
828
1388
|
st.equal(
|
|
829
1389
|
qs.stringify(
|
|
830
|
-
{ a: 'b', c: ['d', 'e'], f: [['g'], ['h']] }
|
|
1390
|
+
{ a: 'b', c: ['d', 'e=f'], f: [['g'], ['h']] },
|
|
1391
|
+
{ encodeValuesOnly: true, arrayFormat: 'brackets' }
|
|
831
1392
|
),
|
|
832
|
-
'a=b&c
|
|
1393
|
+
'a=b&c[]=d&c[]=e%3Df&f[][]=g&f[][]=h',
|
|
1394
|
+
'encodeValuesOnly + brackets'
|
|
833
1395
|
);
|
|
1396
|
+
st.equal(
|
|
1397
|
+
qs.stringify(
|
|
1398
|
+
{ a: 'b', c: ['d', 'e=f'], f: [['g'], ['h']] },
|
|
1399
|
+
{ encodeValuesOnly: true, arrayFormat: 'repeat' }
|
|
1400
|
+
),
|
|
1401
|
+
'a=b&c=d&c=e%3Df&f=g&f=h',
|
|
1402
|
+
'encodeValuesOnly + repeat'
|
|
1403
|
+
);
|
|
1404
|
+
|
|
1405
|
+
st.equal(
|
|
1406
|
+
qs.stringify(
|
|
1407
|
+
{ a: 'b', c: ['d', 'e'], f: [['g'], ['h']] },
|
|
1408
|
+
{ arrayFormat: 'indices' }
|
|
1409
|
+
),
|
|
1410
|
+
'a=b&c%5B0%5D=d&c%5B1%5D=e&f%5B0%5D%5B0%5D=g&f%5B1%5D%5B0%5D=h',
|
|
1411
|
+
'no encodeValuesOnly + indices'
|
|
1412
|
+
);
|
|
1413
|
+
st.equal(
|
|
1414
|
+
qs.stringify(
|
|
1415
|
+
{ a: 'b', c: ['d', 'e'], f: [['g'], ['h']] },
|
|
1416
|
+
{ arrayFormat: 'brackets' }
|
|
1417
|
+
),
|
|
1418
|
+
'a=b&c%5B%5D=d&c%5B%5D=e&f%5B%5D%5B%5D=g&f%5B%5D%5B%5D=h',
|
|
1419
|
+
'no encodeValuesOnly + brackets'
|
|
1420
|
+
);
|
|
1421
|
+
st.equal(
|
|
1422
|
+
qs.stringify(
|
|
1423
|
+
{ a: 'b', c: ['d', 'e'], f: [['g'], ['h']] },
|
|
1424
|
+
{ arrayFormat: 'repeat' }
|
|
1425
|
+
),
|
|
1426
|
+
'a=b&c=d&c=e&f=g&f=h',
|
|
1427
|
+
'no encodeValuesOnly + repeat'
|
|
1428
|
+
);
|
|
1429
|
+
|
|
834
1430
|
st.end();
|
|
835
1431
|
});
|
|
836
1432
|
|
|
@@ -845,6 +1441,28 @@ test('stringify()', function (t) {
|
|
|
845
1441
|
st.end();
|
|
846
1442
|
});
|
|
847
1443
|
|
|
1444
|
+
t.test('strictNullHandling: applies the formatter to the encoded key (RFC1738)', function (st) {
|
|
1445
|
+
st.equal(
|
|
1446
|
+
qs.stringify(
|
|
1447
|
+
{ 'a b': null, 'c d': 'e f' },
|
|
1448
|
+
{ strictNullHandling: false, format: 'RFC1738' }
|
|
1449
|
+
),
|
|
1450
|
+
'a+b=&c+d=e+f',
|
|
1451
|
+
'without: as expected'
|
|
1452
|
+
);
|
|
1453
|
+
|
|
1454
|
+
st.equal(
|
|
1455
|
+
qs.stringify(
|
|
1456
|
+
{ 'a b': null, 'c d': 'e f' },
|
|
1457
|
+
{ strictNullHandling: true, format: 'RFC1738' }
|
|
1458
|
+
),
|
|
1459
|
+
'a+b&c+d=e+f',
|
|
1460
|
+
'with: as expected'
|
|
1461
|
+
);
|
|
1462
|
+
|
|
1463
|
+
st.end();
|
|
1464
|
+
});
|
|
1465
|
+
|
|
848
1466
|
t.test('throws if an invalid charset is specified', function (st) {
|
|
849
1467
|
st['throws'](function () {
|
|
850
1468
|
qs.stringify({ a: 'b' }, { charset: 'foobar' });
|
|
@@ -867,13 +1485,25 @@ test('stringify()', function (t) {
|
|
|
867
1485
|
st.end();
|
|
868
1486
|
});
|
|
869
1487
|
|
|
870
|
-
t.test('
|
|
871
|
-
st.equal(
|
|
872
|
-
|
|
873
|
-
|
|
1488
|
+
t.test('`charsetSentinel` option', function (st) {
|
|
1489
|
+
st.equal(
|
|
1490
|
+
qs.stringify({ a: 'æ' }, { charsetSentinel: true, charset: 'utf-8' }),
|
|
1491
|
+
'utf8=%E2%9C%93&a=%C3%A6',
|
|
1492
|
+
'adds the right sentinel when instructed to and the charset is utf-8'
|
|
1493
|
+
);
|
|
1494
|
+
|
|
1495
|
+
st.equal(
|
|
1496
|
+
qs.stringify({ a: 'æ' }, { charsetSentinel: true, charset: 'iso-8859-1' }),
|
|
1497
|
+
'utf8=%26%2310003%3B&a=%E6',
|
|
1498
|
+
'adds the right sentinel when instructed to and the charset is iso-8859-1'
|
|
1499
|
+
);
|
|
1500
|
+
|
|
1501
|
+
st.equal(
|
|
1502
|
+
qs.stringify({ a: 1, b: 2 }, { charsetSentinel: true, delimiter: ';' }),
|
|
1503
|
+
'utf8=%E2%9C%93;a=1;b=2',
|
|
1504
|
+
'uses the configured delimiter after the sentinel'
|
|
1505
|
+
);
|
|
874
1506
|
|
|
875
|
-
t.test('adds the right sentinel when instructed to and the charset is iso-8859-1', function (st) {
|
|
876
|
-
st.equal(qs.stringify({ a: 'æ' }, { charsetSentinel: true, charset: 'iso-8859-1' }), 'utf8=%26%2310003%3B&a=%E6');
|
|
877
1507
|
st.end();
|
|
878
1508
|
});
|
|
879
1509
|
|
|
@@ -916,6 +1546,15 @@ test('stringify()', function (t) {
|
|
|
916
1546
|
};
|
|
917
1547
|
|
|
918
1548
|
st.deepEqual(qs.stringify({ KeY: 'vAlUe' }, { encoder: encoder }), 'key=VALUE');
|
|
1549
|
+
|
|
1550
|
+
var noopEncoder = function () { return 'x'; };
|
|
1551
|
+
noopEncoder();
|
|
1552
|
+
st['throws'](
|
|
1553
|
+
function () { encoder('x', noopEncoder, 'utf-8', 'unknown'); },
|
|
1554
|
+
'this should never happen! type: unknown',
|
|
1555
|
+
'encoder throws for unexpected type'
|
|
1556
|
+
);
|
|
1557
|
+
|
|
919
1558
|
st.end();
|
|
920
1559
|
});
|
|
921
1560
|
|
|
@@ -924,13 +1563,15 @@ test('stringify()', function (t) {
|
|
|
924
1563
|
var withArray = { a: { b: [{ c: 'd', e: 'f' }] } };
|
|
925
1564
|
|
|
926
1565
|
st.equal(qs.stringify(obj, { encode: false }), 'a[b][c]=d&a[b][e]=f', 'no array, no arrayFormat');
|
|
927
|
-
st.equal(qs.stringify(obj, { encode: false, arrayFormat: '
|
|
1566
|
+
st.equal(qs.stringify(obj, { encode: false, arrayFormat: 'brackets' }), 'a[b][c]=d&a[b][e]=f', 'no array, bracket');
|
|
928
1567
|
st.equal(qs.stringify(obj, { encode: false, arrayFormat: 'indices' }), 'a[b][c]=d&a[b][e]=f', 'no array, indices');
|
|
1568
|
+
st.equal(qs.stringify(obj, { encode: false, arrayFormat: 'repeat' }), 'a[b][c]=d&a[b][e]=f', 'no array, repeat');
|
|
929
1569
|
st.equal(qs.stringify(obj, { encode: false, arrayFormat: 'comma' }), 'a[b][c]=d&a[b][e]=f', 'no array, comma');
|
|
930
1570
|
|
|
931
1571
|
st.equal(qs.stringify(withArray, { encode: false }), 'a[b][0][c]=d&a[b][0][e]=f', 'array, no arrayFormat');
|
|
932
|
-
st.equal(qs.stringify(withArray, { encode: false, arrayFormat: '
|
|
1572
|
+
st.equal(qs.stringify(withArray, { encode: false, arrayFormat: 'brackets' }), 'a[b][][c]=d&a[b][][e]=f', 'array, bracket');
|
|
933
1573
|
st.equal(qs.stringify(withArray, { encode: false, arrayFormat: 'indices' }), 'a[b][0][c]=d&a[b][0][e]=f', 'array, indices');
|
|
1574
|
+
st.equal(qs.stringify(withArray, { encode: false, arrayFormat: 'repeat' }), 'a[b][c]=d&a[b][e]=f', 'array, repeat');
|
|
934
1575
|
st.equal(
|
|
935
1576
|
qs.stringify(withArray, { encode: false, arrayFormat: 'comma' }),
|
|
936
1577
|
'???',
|
|
@@ -943,10 +1584,42 @@ test('stringify()', function (t) {
|
|
|
943
1584
|
|
|
944
1585
|
t.test('stringifies sparse arrays', function (st) {
|
|
945
1586
|
/* eslint no-sparse-arrays: 0 */
|
|
946
|
-
st.equal(qs.stringify({ a: [, '2', , , '1'] }, { encodeValuesOnly: true }), 'a[1]=2&a[4]=1');
|
|
947
|
-
st.equal(qs.stringify({ a: [,
|
|
948
|
-
st.equal(qs.stringify({ a: [,
|
|
949
|
-
|
|
1587
|
+
st.equal(qs.stringify({ a: [, '2', , , '1'] }, { encodeValuesOnly: true, arrayFormat: 'indices' }), 'a[1]=2&a[4]=1');
|
|
1588
|
+
st.equal(qs.stringify({ a: [, '2', , , '1'] }, { encodeValuesOnly: true, arrayFormat: 'brackets' }), 'a[]=2&a[]=1');
|
|
1589
|
+
st.equal(qs.stringify({ a: [, '2', , , '1'] }, { encodeValuesOnly: true, arrayFormat: 'repeat' }), 'a=2&a=1');
|
|
1590
|
+
|
|
1591
|
+
st.equal(qs.stringify({ a: [, { b: [, , { c: '1' }] }] }, { encodeValuesOnly: true, arrayFormat: 'indices' }), 'a[1][b][2][c]=1');
|
|
1592
|
+
st.equal(qs.stringify({ a: [, { b: [, , { c: '1' }] }] }, { encodeValuesOnly: true, arrayFormat: 'brackets' }), 'a[][b][][c]=1');
|
|
1593
|
+
st.equal(qs.stringify({ a: [, { b: [, , { c: '1' }] }] }, { encodeValuesOnly: true, arrayFormat: 'repeat' }), 'a[b][c]=1');
|
|
1594
|
+
|
|
1595
|
+
st.equal(qs.stringify({ a: [, [, , [, , , { c: '1' }]]] }, { encodeValuesOnly: true, arrayFormat: 'indices' }), 'a[1][2][3][c]=1');
|
|
1596
|
+
st.equal(qs.stringify({ a: [, [, , [, , , { c: '1' }]]] }, { encodeValuesOnly: true, arrayFormat: 'brackets' }), 'a[][][][c]=1');
|
|
1597
|
+
st.equal(qs.stringify({ a: [, [, , [, , , { c: '1' }]]] }, { encodeValuesOnly: true, arrayFormat: 'repeat' }), 'a[c]=1');
|
|
1598
|
+
|
|
1599
|
+
st.equal(qs.stringify({ a: [, [, , [, , , { c: [, '1'] }]]] }, { encodeValuesOnly: true, arrayFormat: 'indices' }), 'a[1][2][3][c][1]=1');
|
|
1600
|
+
st.equal(qs.stringify({ a: [, [, , [, , , { c: [, '1'] }]]] }, { encodeValuesOnly: true, arrayFormat: 'brackets' }), 'a[][][][c][]=1');
|
|
1601
|
+
st.equal(qs.stringify({ a: [, [, , [, , , { c: [, '1'] }]]] }, { encodeValuesOnly: true, arrayFormat: 'repeat' }), 'a[c]=1');
|
|
1602
|
+
|
|
1603
|
+
st.end();
|
|
1604
|
+
});
|
|
1605
|
+
|
|
1606
|
+
t.test('encodes a very long string', function (st) {
|
|
1607
|
+
var chars = [];
|
|
1608
|
+
var expected = [];
|
|
1609
|
+
for (var i = 0; i < 5e3; i++) {
|
|
1610
|
+
chars.push(' ' + i);
|
|
1611
|
+
|
|
1612
|
+
expected.push('%20' + i);
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1615
|
+
var obj = {
|
|
1616
|
+
foo: chars.join('')
|
|
1617
|
+
};
|
|
1618
|
+
|
|
1619
|
+
st.equal(
|
|
1620
|
+
qs.stringify(obj, { arrayFormat: 'brackets', charset: 'utf-8' }),
|
|
1621
|
+
'foo=' + expected.join('')
|
|
1622
|
+
);
|
|
950
1623
|
|
|
951
1624
|
st.end();
|
|
952
1625
|
});
|
|
@@ -957,7 +1630,21 @@ test('stringify()', function (t) {
|
|
|
957
1630
|
test('stringifies empty keys', function (t) {
|
|
958
1631
|
emptyTestCases.forEach(function (testCase) {
|
|
959
1632
|
t.test('stringifies an object with empty string key with ' + testCase.input, function (st) {
|
|
960
|
-
st.deepEqual(
|
|
1633
|
+
st.deepEqual(
|
|
1634
|
+
qs.stringify(testCase.withEmptyKeys, { encode: false, arrayFormat: 'indices' }),
|
|
1635
|
+
testCase.stringifyOutput.indices,
|
|
1636
|
+
'test case: ' + testCase.input + ', indices'
|
|
1637
|
+
);
|
|
1638
|
+
st.deepEqual(
|
|
1639
|
+
qs.stringify(testCase.withEmptyKeys, { encode: false, arrayFormat: 'brackets' }),
|
|
1640
|
+
testCase.stringifyOutput.brackets,
|
|
1641
|
+
'test case: ' + testCase.input + ', brackets'
|
|
1642
|
+
);
|
|
1643
|
+
st.deepEqual(
|
|
1644
|
+
qs.stringify(testCase.withEmptyKeys, { encode: false, arrayFormat: 'repeat' }),
|
|
1645
|
+
testCase.stringifyOutput.repeat,
|
|
1646
|
+
'test case: ' + testCase.input + ', repeat'
|
|
1647
|
+
);
|
|
961
1648
|
|
|
962
1649
|
st.end();
|
|
963
1650
|
});
|
|
@@ -966,6 +1653,112 @@ test('stringifies empty keys', function (t) {
|
|
|
966
1653
|
t.test('edge case with object/arrays', function (st) {
|
|
967
1654
|
st.deepEqual(qs.stringify({ '': { '': [2, 3] } }, { encode: false }), '[][0]=2&[][1]=3');
|
|
968
1655
|
st.deepEqual(qs.stringify({ '': { '': [2, 3], a: 2 } }, { encode: false }), '[][0]=2&[][1]=3&[a]=2');
|
|
1656
|
+
st.deepEqual(qs.stringify({ '': { '': [2, 3] } }, { encode: false, arrayFormat: 'indices' }), '[][0]=2&[][1]=3');
|
|
1657
|
+
st.deepEqual(qs.stringify({ '': { '': [2, 3], a: 2 } }, { encode: false, arrayFormat: 'indices' }), '[][0]=2&[][1]=3&[a]=2');
|
|
1658
|
+
|
|
1659
|
+
st.end();
|
|
1660
|
+
});
|
|
1661
|
+
|
|
1662
|
+
t.test('stringifies non-string keys', function (st) {
|
|
1663
|
+
var S = Object('abc');
|
|
1664
|
+
S.toString = function () {
|
|
1665
|
+
return 'd';
|
|
1666
|
+
};
|
|
1667
|
+
var actual = qs.stringify({ a: 'b', 'false': {}, 1e+22: 'c', d: 'e' }, {
|
|
1668
|
+
filter: ['a', false, null, 10000000000000000000000, S],
|
|
1669
|
+
allowDots: true,
|
|
1670
|
+
encodeDotInKeys: true
|
|
1671
|
+
});
|
|
1672
|
+
|
|
1673
|
+
st.equal(actual, 'a=b&1e%2B22=c&d=e', 'stringifies correctly');
|
|
1674
|
+
|
|
1675
|
+
st.end();
|
|
1676
|
+
});
|
|
1677
|
+
|
|
1678
|
+
t.test('round-trips keys containing percent-encoded bracket text', function (st) {
|
|
1679
|
+
var cases = [
|
|
1680
|
+
{ 'a%5Bb': 'c' },
|
|
1681
|
+
{ 'a%5Db': 'c' },
|
|
1682
|
+
{ 'a%255Bb': 'c' },
|
|
1683
|
+
{ 'a%255Db': 'c' },
|
|
1684
|
+
{ a: { 'b%5Bc': 'd' } },
|
|
1685
|
+
{ a: { 'b%255Bc': 'd' } },
|
|
1686
|
+
{ 'a%5B%255Bb': 'c' }
|
|
1687
|
+
];
|
|
1688
|
+
for (var i = 0; i < cases.length; i++) {
|
|
1689
|
+
st.deepEqual(
|
|
1690
|
+
qs.parse(qs.stringify(cases[i])),
|
|
1691
|
+
cases[i],
|
|
1692
|
+
'round-trips ' + JSON.stringify(cases[i])
|
|
1693
|
+
);
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
st.end();
|
|
1697
|
+
});
|
|
1698
|
+
|
|
1699
|
+
t.test('parses input containing percent-encoded bracket text without mangling', function (st) {
|
|
1700
|
+
st.deepEqual(qs.parse('a%25255Bb=c'), { 'a%255Bb': 'c' }, 'a%25255Bb decodes to a%255Bb, not a%5Bb');
|
|
1701
|
+
st.deepEqual(qs.parse('a%25255Db=c'), { 'a%255Db': 'c' }, 'a%25255Db decodes to a%255Db, not a%5Db');
|
|
1702
|
+
st.deepEqual(qs.parse('a%5Bb%25255Bc%5D=d'), { a: { 'b%255Bc': 'd' } }, 'nested %25255B decodes to %255B inside segment, not %5B');
|
|
1703
|
+
|
|
1704
|
+
st.end();
|
|
1705
|
+
});
|
|
1706
|
+
|
|
1707
|
+
t.test('limits nesting depth with the `depth` option', function (st) {
|
|
1708
|
+
st.equal(
|
|
1709
|
+
qs.stringify({ a: { b: { c: 'd' } } }),
|
|
1710
|
+
'a%5Bb%5D%5Bc%5D=d',
|
|
1711
|
+
'the default depth is unlimited and does not alter output'
|
|
1712
|
+
);
|
|
1713
|
+
st.equal(
|
|
1714
|
+
qs.stringify({ a: { b: { c: 'd' } } }, { depth: 2 }),
|
|
1715
|
+
'a%5Bb%5D%5Bc%5D=d',
|
|
1716
|
+
'does not throw when nesting is within the depth limit'
|
|
1717
|
+
);
|
|
1718
|
+
st.equal(
|
|
1719
|
+
qs.stringify({ a: { b: 'c' } }, { depth: Infinity }),
|
|
1720
|
+
'a%5Bb%5D=c',
|
|
1721
|
+
'an explicit Infinity depth is unlimited'
|
|
1722
|
+
);
|
|
1723
|
+
st.equal(
|
|
1724
|
+
qs.stringify({ a: { b: { c: { d: 'e' } } } }, { depth: null }),
|
|
1725
|
+
qs.stringify({ a: { b: { c: { d: 'e' } } } }),
|
|
1726
|
+
'a non-number depth is ignored and treated as unlimited'
|
|
1727
|
+
);
|
|
1728
|
+
st.equal(
|
|
1729
|
+
qs.stringify({ a: 'b' }, { depth: 0 }),
|
|
1730
|
+
'a=b',
|
|
1731
|
+
'a flat object serializes with a depth of 0'
|
|
1732
|
+
);
|
|
1733
|
+
|
|
1734
|
+
var deep = {};
|
|
1735
|
+
var cursor = deep;
|
|
1736
|
+
for (var i = 0; i < 250; i++) {
|
|
1737
|
+
cursor.a = {};
|
|
1738
|
+
cursor = cursor.a;
|
|
1739
|
+
}
|
|
1740
|
+
cursor.b = 'c';
|
|
1741
|
+
st.equal(typeof qs.stringify(deep), 'string', 'the default depth serializes deeply nested objects without throwing');
|
|
1742
|
+
|
|
1743
|
+
st['throws'](
|
|
1744
|
+
function () { qs.stringify({ a: { b: { c: { d: 'e' } } } }, { depth: 2 }); },
|
|
1745
|
+
RangeError,
|
|
1746
|
+
'throws a RangeError when nesting exceeds the depth limit'
|
|
1747
|
+
);
|
|
1748
|
+
st['throws'](
|
|
1749
|
+
function () { qs.stringify({ a: { b: 'c' } }, { depth: 0 }); },
|
|
1750
|
+
RangeError,
|
|
1751
|
+
'a depth of 0 throws for any nested object'
|
|
1752
|
+
);
|
|
1753
|
+
|
|
1754
|
+
var caught;
|
|
1755
|
+
try {
|
|
1756
|
+
qs.stringify({ a: { b: { c: { d: 'e' } } } }, { depth: 2 });
|
|
1757
|
+
} catch (e) {
|
|
1758
|
+
caught = e;
|
|
1759
|
+
}
|
|
1760
|
+
st.ok(caught instanceof RangeError, 'the thrown value is a RangeError');
|
|
1761
|
+
st.equal(caught.message, 'Input depth exceeded depth option of 2', 'the RangeError names the exceeded depth');
|
|
969
1762
|
|
|
970
1763
|
st.end();
|
|
971
1764
|
});
|