-
-
Notifications
You must be signed in to change notification settings - Fork 730
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: allow parsing of empty keys in objects #433
base: main
Are you sure you want to change the base?
Conversation
test/parse.js
Outdated
st.deepEqual(qs.stringify({ '': { '': [2, 3] } }, { encode: false }), '[][0]=2&[][1]=3'); | ||
st.deepEqual(qs.stringify({ '': { '': [2, 3], a: 2 } }, { encode: false }), '[][0]=2&[][1]=3&[a]=2'); | ||
st.deepEqual(qs.parse('[][0]=2&[][1]=3', { allowEmptyKeys: true }), { '': { '': ['2', '3'] } }); | ||
st.deepEqual(qs.parse('[][0]=2&[][1]=3&[a]=2', { allowEmptyKeys: true }), { '': { '': ['2', '3'], a: '2' } }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This case fails, but this is ambiguous string which is treated as { : [ '2', '3' ] } and stringify <-> parse is not producing the same results
---
operator: deepEqual
expected: |-
{ : { : [ '2', '3' ] } }
actual: |-
{ : [ '2', '3' ] }
operator: deepEqual
expected: |-
{ : { : [ '2', '3' ], a: '2' } }
actual: |-
{ : { 0: '2', 1: '3', a: '2' } }
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's ok to mark this case as a TODO for now; there's an existing issue about a nested array/object combo
There's unlikely to be a major version any time soon; breaking changes just aren't worth it. Configuring it via option is the best bet for now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At the very least, let's add stringify tests using all of the outputs in the parse tests as input.
test/parse.js
Outdated
|
||
test('parse/stringify empty key WIP', function (t) { | ||
t.test('parses an object with empty string key', function (st) { | ||
st.deepEqual(qs.parse('=a', { allowEmptyKeys: true }), { '': 'a' }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for each of these test inputs, let's test both allowEmptyKeys true, false, and absent
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, please let me know what will be the correct place for this tests? They are testing both parse and stringify and current tests are split by this methods. To me it feels more reasonable to test them together in this case.
test/parse.js
Outdated
st.deepEqual(qs.stringify({ '': { '': [2, 3] } }, { encode: false }), '[][0]=2&[][1]=3'); | ||
st.deepEqual(qs.stringify({ '': { '': [2, 3], a: 2 } }, { encode: false }), '[][0]=2&[][1]=3&[a]=2'); | ||
st.deepEqual(qs.parse('[][0]=2&[][1]=3', { allowEmptyKeys: true }), { '': { '': ['2', '3'] } }); | ||
st.deepEqual(qs.parse('[][0]=2&[][1]=3&[a]=2', { allowEmptyKeys: true }), { '': { '': ['2', '3'], a: '2' } }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's ok to mark this case as a TODO for now; there's an existing issue about a nested array/object combo
test/parse.js
Outdated
st.deepEqual(qs.stringify(parsed, { encode: false }), testCase.stringifyOutput); | ||
st.deepEqual(qs.parse(testCase.stringifyOutput, { allowEmptyKeys: true }), testCase.withEmptyKeys); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i'd prefer to keep stringify tests in the stringify tests file; it's fine if the test cases are in a separate file both test files require.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey, sorry for the delay :) i've extracted test cases into own file and and separated stringify/parse parts accordingly
Codecov ReportPatch coverage:
Additional details and impacted files@@ Coverage Diff @@
## main #433 +/- ##
=======================================
Coverage 99.80% 99.80%
=======================================
Files 9 9
Lines 1518 1531 +13
Branches 177 179 +2
=======================================
+ Hits 1515 1528 +13
Misses 3 3
☔ View full report in Codecov by Sentry. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking great!
qs.parse('[][0]=2&[][1]=3', { allowEmptyKeys: true }), | ||
{ '': { '': ['2', '3'] } }, | ||
'array/object conversion', | ||
{ skip: 'TODO: figure out what this should do' } | ||
); | ||
|
||
st.deepEqual( | ||
qs.parse('[][0]=2&[][1]=3&[a]=2', { allowEmptyKeys: true }), | ||
{ '': { '': ['2', '3'], a: '2' } }, | ||
'array/object conversion', | ||
{ skip: 'TODO: figure out what this should do' } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's discuss these TODOs. what are your thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we parse empty keys I assume output should be equal to key-full strings?
- If we use
a[b][0]=2&a[b][1]=3
the output object is{ a: { b: [ '2', '3' ] } }
- when we replace
a
andb
with empty strings in this object:{ '': { '': [ '2', '3' ] } }
- so query string equivalent should be
[][0]=2&[][1]=3
which is<EMPTY_KEY>[<EMPTY_KEY>][0]=2
- so query string equivalent should be
Current output for [][0]=2&[][1]=3
without empty keys flag is { 0: '2', 1: '3' }
test('stringifies empty keys', function (t) { | ||
emptyTestCases.forEach(function (testCase) { | ||
t.test('stringifies an object with empty string key with ' + testCase.input, function (st) { | ||
st.deepEqual(qs.stringify(testCase.withEmptyKeys, { encode: false }), testCase.stringifyOutput); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we also add a test case where the input is a stringified noEmptyKeys
?
5e70a6a
to
c077991
Compare
{ input: '&', withEmptyKeys: {}, stringifyOutput: '', noEmptyKeys: {}, stringifyOutputNoEmpty: '' }, | ||
{ input: '&&', withEmptyKeys: {}, stringifyOutput: '', noEmptyKeys: {}, stringifyOutputNoEmpty: '' }, | ||
{ input: '&=', withEmptyKeys: { '': '' }, stringifyOutput: '=', noEmptyKeys: {}, stringifyOutputNoEmpty: '' }, | ||
{ input: '&=&', withEmptyKeys: { '': '' }, stringifyOutput: '=', noEmptyKeys: {}, stringifyOutputNoEmpty: '' }, | ||
{ input: '&=&=', withEmptyKeys: { '': ['', ''] }, stringifyOutput: '[0]=&[1]=', noEmptyKeys: {}, stringifyOutputNoEmpty: '' }, | ||
{ input: '&=&=&', withEmptyKeys: { '': ['', ''] }, stringifyOutput: '[0]=&[1]=', noEmptyKeys: {}, stringifyOutputNoEmpty: '' }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it makes me very nervous to be modifying test cases for a bug fix. is there any chance for a default algorithm to infer stringifyOutputNoEmpty
rather than needing to add it to each case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤷 I think explicit cases are better here. I assume you thought that we should default to ''
if you want a smaller diff, then it also makes to do the same for stringifyOutput
and withEmptyKeys
, which will be hard to maintain.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Explicit cases are great, it’s just that the best way to be sure there’s no breaking changes is to not have a diff on existing test cases :-)
Resolves #432
If we are happy with this implementation and want to proceed:
allowSparse
is missing there and maybe other types)Other: