assertKeys cannot handle some valid ES6 keys
See original GitHub issueThere seems to be two ES6 issues with assertKeys:
Issue 1
If expected
is a regular object with a key that is a Symbol, then assertKeys will not behave as expected. For example, the following assertion fails:
var key1 = Symbol()
, key2 = 42
, obj = {};
obj[key1] = 'val1';
obj[key2] = 'val2';
expect(obj).to.have.all.keys([key1, key2]);
// AssertionError: expected { '42': 'val2' } to have keys 'Symbol()', and '42'
val1 is missing from the AssertionError’s actual
because the behavior of Object.keys
is to not include Symbols in its result set. (https://github.com/chaijs/chai/blob/6b640f71451fa575f24b302e56b99379c86d8bd5/lib/chai/core/assertions.js#L1173)
val2 is showing up as the string “Symbol()” in the AssertionError’s expected
because it’s explicitly converted to a string here: (https://github.com/chaijs/chai/blob/6b640f71451fa575f24b302e56b99379c86d8bd5/lib/chai/core/assertions.js#L1187)
Issue 2
A TypeError
is thrown if any of the expected
or actual
keys in a keys assertion cannot be implicitly converted to a string by Array.prototype.sort
here: https://github.com/chaijs/chai/blob/6b640f71451fa575f24b302e56b99379c86d8bd5/lib/chai/core/assertions.js#L1251-L1252
Examples that fall victim to this issue but not the previous issue:
// key1 has no toString
var key1 = Object.create(null)
, key2 = 42;
expect(new Map([[key1, 'val1'], [key2, 'val2']])).to.have.all.keys([key1, key2]);
// TypeError: Cannot convert object to primitive value
// key1 has non-function toString
var key1 = {toString: null}
, key2 = 42;
expect(new Map([[key1, 'val1'], [key2, 'val2']])).to.have.all.keys([key1, key2]);
// TypeError: Cannot convert object to primitive value
// key1 has Symbol's special toString which rejects implicit conversion
var key1 = Symbol()
, key2 = 42;
expect(new Map([[key1, 'val1'], [key2, 'val2']])).to.have.all.keys([key1, key2]);
// TypeError: Cannot convert a Symbol value to a string
Possible Resolutions
The first issue could perhaps be resolved by checking if Symbol is implemented, and if so, first concatenating Object.getOwnPropertyNames
(Edit: Or still Object.keys
, so no unexpected behavior with enumerability) with Object.getOwnPropertySymbols
instead of using Object.keys
, and then only converting the keys to strings if they aren’t Symbols. (In the future Reflect.ownKeys
could be used for the first part).
The second issue could perhaps be resolved by removing the two sorts linked above. The sorts were added a long time ago to make the diffs more readable (https://github.com/chaijs/chai/pull/264). Unfortunately, removing the sorts will break any user-created assertKeys test that catches the AssertionError and directly references its .actual
and/or .expected
members and relies on at least one of them to be in sorted order when it isn’t already in sorted order prior to the sort call. To preserve current behavior, but still resolve the issue, here are some alternative solutions:
- Wrap the sorts in a try/catch and fall back to unsorted version on error
- Write a custom sort method that mimics Array.prototype.sort except instead of throwing it dumps unsortable elements at the end of the array in the order they appear
- Write a custom
compareFunction
to feed into Array.prototype.sort that has special behavior to dump unsortable elements to the end of the array
Issue Analytics
- State:
- Created 7 years ago
- Reactions:1
- Comments:6 (6 by maintainers)
Top GitHub Comments
Whoops, I misinterpreted the output of the test above: Chai’s
inspect
is stringifying it to{}
, notSymbol()
. It just seemed that way because of a combination of console.log and whatever outputs the diff to the screen. So, cribbing a couple of lines from node’sutil.inspect
!Using
inspect
for the sort is great! And no changes to it are needed; it already converts Symbol to “Symbol()”. Here’s a basic test using https://github.com/meeber/chai/tree/es6-keys-assertsTest
Result
(Disclaimer: Latest commit of Mocha was used so Symbol appears as “{}” in the above message)
AssertionError’s
actual
propertyAssertionError’s
expected
property