diff --git a/lib/assertions.js b/lib/assertions.js index b519f19d7..9732f5a17 100644 --- a/lib/assertions.js +++ b/lib/assertions.js @@ -1487,7 +1487,8 @@ module.exports = expect => { const valueKeyType = expect.findTypeOf(valueKey); if (valueKeyType.is('expect.it')) { - return valueKey(subjectKey); + expect.context.thisObject = subject; + return valueKey(subjectKey, expect.context); } else { return expect(subjectKey, 'to [exhaustively] satisfy', valueKey); } @@ -1498,24 +1499,28 @@ module.exports = expect => { expect.promise(() => { // create subject key presence object const remainingKeysInSubject = {}; - subjectType.getKeys(subject).forEach(key => { + subjectKeys.forEach(key => { remainingKeysInSubject[key] = 1; // present in subject }); // discard or mark missing each previously seen value key valueKeys.forEach(key => { - if (!remainingKeysInSubject[key]) { + if ( + !remainingKeysInSubject[key] && + (utils.numericalRegExp.test(key) || expect.flags.exhaustively) + ) { remainingKeysInSubject[key] = 2; // present in value } else { delete remainingKeysInSubject[key]; } }); - // filter outstanding keys which should not lead to an error + // check whether there are any outstanding keys we cannot account for const outstandingKeys = Object.keys(remainingKeysInSubject).filter( key => utils.numericalRegExp.test(key) || typeof key === 'symbol' || - typeof subjectType.valueForKey(subject, key) !== 'undefined' || - remainingKeysInSubject[key] === 2 + (typeof subjectType.valueForKey(subject, key) !== 'undefined' && + // key was only in the value + remainingKeysInSubject[key] === 2) ); // key checking succeeds with no outstanding keys expect(outstandingKeys.length === 0, 'to be truthy'); diff --git a/test/assertions/to-satisfy.spec.js b/test/assertions/to-satisfy.spec.js index 7f239ce5a..a007bb430 100644 --- a/test/assertions/to-satisfy.spec.js +++ b/test/assertions/to-satisfy.spec.js @@ -1451,6 +1451,65 @@ describe('to satisfy assertion', () => { ')' ); }); + + it('should support context correctly with expect.it (numerical)', () => { + function Greeter(folks) { + this.push(...(folks || [])); + + this.greet = function(prefix) { + return prefix + this.join(' & '); + }; + } + + Greeter.prototype = []; + + const clonedExpect = expect.clone(); + clonedExpect.addType({ + name: 'Greeter', + base: 'array-like', + identify: obj => obj instanceof Greeter + }); + + const expected = ['Alice', 'Bob']; + expected.greet = expect.it( + 'when called with', + ['Hello, '], + 'to equal', + 'Hello, Alice & Bob' + ); + + clonedExpect(new Greeter(['Alice', 'Bob']), 'to satisfy', expected); + }); + + it('should support context correctly with expect.it (non-numerical)', () => { + function Greeter(folks) { + this.push(...(folks || [])); + + this.greet = function(prefix) { + return prefix + this.join(' & '); + }; + } + + Greeter.prototype = []; + + const clonedExpect = expect.clone(); + clonedExpect.addType({ + name: 'Greeter', + base: 'array-like', + identify: obj => obj instanceof Greeter, + numericalPropertiesOnly: false + }); + + const expected = ['Alice', 'Bob']; + expected.greet = expect.it( + 'when called with', + ['Hello, '], + 'to equal', + 'Hello, Alice & Bob' + ); + + clonedExpect(new Greeter(['Alice', 'Bob']), 'to satisfy', expected); + }); }); describe('on arrays', () => {