From c979e5d0344ba8822b262aadd87a203588db2cca Mon Sep 17 00:00:00 2001 From: scottenock Date: Thu, 19 Sep 2024 09:54:46 +0100 Subject: [PATCH 1/4] FIX: Decode brack-separator value before splitting it with separator The original implementation was splitting the received value before it then decoded the value. Because the value was URL encoded, the split function could not find the seperator to successfully split. Adjusted the order of operations which fixes #388 --- base.js | 2 +- test/parse.js | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/base.js b/base.js index ccbdb6e..f72e9a8 100644 --- a/base.js +++ b/base.js @@ -221,7 +221,7 @@ function parserForArrayFormat(options) { const arrayValue = value === null ? [] - : value.split(options.arrayFormatSeparator).map(item => decode(item, options)); + : decode(value, options).split(options.arrayFormatSeparator) if (accumulator[key] === undefined) { accumulator[key] = arrayValue; diff --git a/test/parse.js b/test/parse.js index 3409255..3b53844 100644 --- a/test/parse.js +++ b/test/parse.js @@ -215,6 +215,16 @@ test('query strings having a brackets+separator array and format option as `brac }), {foo: ['']}); }); +test('query strings having a brackets+separator array and format option as `bracket-separator` with a URL encoded value', t => { + t.deepEqual(queryString.parse('?testA%5B%5D=1&testB%5B%5D=a%2Cb%2Cc%2Cd%2Ce%2Cf&testC=true', { + arrayFormat: 'bracket-separator', + }), { + testA: ['1'], + testB: ['a', 'b', 'c', 'd', 'e', 'f'], + testC: 'true', + }); +}); + test('query strings having = within parameters (i.e. GraphQL IDs)', t => { t.deepEqual(queryString.parse('foo=bar=&foo=ba=z='), {foo: ['bar=', 'ba=z=']}); }); From a566f3d944ba2258dcd5c52557d52b1fdfcbfb6c Mon Sep 17 00:00:00 2001 From: scottenock Date: Thu, 19 Sep 2024 10:01:55 +0100 Subject: [PATCH 2/4] FIX: adjust formatting to fix linter --- base.js | 2 +- test/parse.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/base.js b/base.js index f72e9a8..ad9e7b1 100644 --- a/base.js +++ b/base.js @@ -221,7 +221,7 @@ function parserForArrayFormat(options) { const arrayValue = value === null ? [] - : decode(value, options).split(options.arrayFormatSeparator) + : decode(value, options).split(options.arrayFormatSeparator); if (accumulator[key] === undefined) { accumulator[key] = arrayValue; diff --git a/test/parse.js b/test/parse.js index 3b53844..e66a3d6 100644 --- a/test/parse.js +++ b/test/parse.js @@ -220,8 +220,8 @@ test('query strings having a brackets+separator array and format option as `brac arrayFormat: 'bracket-separator', }), { testA: ['1'], - testB: ['a', 'b', 'c', 'd', 'e', 'f'], - testC: 'true', + testB: ['a', 'b', 'c', 'd', 'e', 'f'], + testC: 'true', }); }); From 8c0335dc689119b0e5a7743a457a1083a8fda3d0 Mon Sep 17 00:00:00 2001 From: scottenock Date: Tue, 1 Oct 2024 19:42:49 +0100 Subject: [PATCH 3/4] REFACTOR: improve readability of test to use decoded value --- test/parse.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/parse.js b/test/parse.js index e66a3d6..ea57030 100644 --- a/test/parse.js +++ b/test/parse.js @@ -216,12 +216,12 @@ test('query strings having a brackets+separator array and format option as `brac }); test('query strings having a brackets+separator array and format option as `bracket-separator` with a URL encoded value', t => { - t.deepEqual(queryString.parse('?testA%5B%5D=1&testB%5B%5D=a%2Cb%2Cc%2Cd%2Ce%2Cf&testC=true', { + const key = 'foo[]'; + const value = 'a,b,c,d,e,f'; + t.deepEqual(queryString.parse(`?${encodeURIComponent(key)}=${encodeURIComponent(value)}`, { arrayFormat: 'bracket-separator', }), { - testA: ['1'], - testB: ['a', 'b', 'c', 'd', 'e', 'f'], - testC: 'true', + foo: ['a', 'b', 'c', 'd', 'e', 'f'], }); }); From 009dd6d355d03cd8e98b7798ee4ee031020416f9 Mon Sep 17 00:00:00 2001 From: scottenock Date: Wed, 2 Oct 2024 18:23:57 +0100 Subject: [PATCH 4/4] FEAT: improve readability of tests by encoding de-coded test values --- test/parse.js | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/test/parse.js b/test/parse.js index ea57030..6e2261a 100644 --- a/test/parse.js +++ b/test/parse.js @@ -110,7 +110,8 @@ test('handle multiple values and preserve appearance order with indexes', t => { }); test('query strings params including embedded `=`', t => { - t.deepEqual(queryString.parse('?param=https%3A%2F%2Fsomeurl%3Fid%3D2837'), {param: 'https://someurl?id=2837'}); + const value = 'https://someurl?id=2837'; + t.deepEqual(queryString.parse(`param=${encodeURIComponent(value)}`), {param: 'https://someurl?id=2837'}); }); test('object properties', t => { @@ -315,7 +316,8 @@ test('decode keys and values', t => { }); test('disable decoding of keys and values', t => { - t.deepEqual(queryString.parse('tags=postal%20office,burger%2C%20fries%20and%20coke', {decode: false}), {tags: 'postal%20office,burger%2C%20fries%20and%20coke'}); + const value = 'postal office,burger, fries and coke'; + t.deepEqual(queryString.parse(`tags=${encodeURIComponent(value)}`, {decode: false}), {tags: 'postal%20office%2Cburger%2C%20fries%20and%20coke'}); }); test('number value returns as string by default', t => { @@ -386,7 +388,8 @@ test('parse throws TypeError for invalid arrayFormatSeparator', t => { }); test('query strings having comma encoded and format option as `comma`', t => { - t.deepEqual(queryString.parse('foo=zero%2Cone,two%2Cthree', {arrayFormat: 'comma'}), { + const values = ['zero,one', 'two,three']; + t.deepEqual(queryString.parse(`foo=${encodeURIComponent(values[0])},${encodeURIComponent(values[1])}`, {arrayFormat: 'comma'}), { foo: [ 'zero,one', 'two,three', @@ -402,7 +405,8 @@ test('value should not be decoded twice with `arrayFormat` option set as `separa // See https://github.com/sindresorhus/query-string/issues/242 test('value separated by encoded comma will not be parsed as array with `arrayFormat` option set to `comma`', t => { - t.deepEqual(queryString.parse('id=1%2C2%2C3', {arrayFormat: 'comma', parseNumbers: true}), { + const value = '1,2,3'; + t.deepEqual(queryString.parse(`id=${encodeURIComponent(value)}`, {arrayFormat: 'comma', parseNumbers: true}), { id: [1, 2, 3], }); }); @@ -416,7 +420,8 @@ test('query strings having (:list) colon-list-separator arrays including null va }); test('types option: can override a parsed number to be a string ', t => { - t.deepEqual(queryString.parse('phoneNumber=%2B380951234567', { + const phoneNumber = '+380951234567'; + t.deepEqual(queryString.parse(`phoneNumber=${encodeURIComponent(phoneNumber)}`, { parseNumbers: true, types: { phoneNumber: 'string', @@ -436,7 +441,7 @@ test('types option: can override a parsed boolean value to be a string', t => { }); test('types option: can override parsed numbers arrays to be string[]', t => { - t.deepEqual(queryString.parse('ids=999%2C998%2C997&items=1%2C2%2C3', { + t.deepEqual(queryString.parse('ids=999,998,997&items=1,2,3', { arrayFormat: 'comma', parseNumbers: true, types: { @@ -449,7 +454,7 @@ test('types option: can override parsed numbers arrays to be string[]', t => { }); test('types option: can override string arrays to be number[]', t => { - t.deepEqual(queryString.parse('ids=001%2C002%2C003&items=1%2C2%2C3', { + t.deepEqual(queryString.parse('ids=1,2,3&items=1,2,3', { arrayFormat: 'comma', types: { ids: 'number[]', @@ -461,7 +466,7 @@ test('types option: can override string arrays to be number[]', t => { }); test('types option: can override an array to be string', t => { - t.deepEqual(queryString.parse('ids=001%2C002%2C003&items=1%2C2%2C3', { + t.deepEqual(queryString.parse('ids=001,002,003&items=1,2,3', { arrayFormat: 'comma', parseNumbers: true, types: { @@ -498,7 +503,7 @@ test('types option: when value is not of specified type, it will safely parse th }); test('types option: array types will have no effect if arrayFormat is set to "none"', t => { - t.deepEqual(queryString.parse('ids=001%2C002%2C003&foods=apple%2Corange%2Cmango', { + t.deepEqual(queryString.parse('ids=001,002,003&foods=apple,orange,mango', { arrayFormat: 'none', types: { ids: 'number[]', @@ -522,7 +527,7 @@ test('types option: will parse the value as number if specified in type but pars }); test('types option: all supported types work in conjunction with one another', t => { - t.deepEqual(queryString.parse('ids=001%2C002%2C003&items=1%2C2%2C3&price=22%2E00&numbers=1%2C2%2C3&double=5&number=20', { + t.deepEqual(queryString.parse('ids=001,002,003&items=1,2,3&price=22.00&numbers=1,2,3&double=5&number=20', { arrayFormat: 'comma', types: { ids: 'string',