Skip to content

Commit

Permalink
Merge aeac1ef into 75f818d
Browse files Browse the repository at this point in the history
  • Loading branch information
sunesimonsen committed Sep 5, 2018
2 parents 75f818d + aeac1ef commit 3050ffa
Show file tree
Hide file tree
Showing 8 changed files with 132 additions and 160 deletions.
54 changes: 27 additions & 27 deletions documentation/api/addType.md
Expand Up @@ -299,32 +299,32 @@ block. The outputs below shows the contrast between setting the
inlineDiff = true;
expect(
{
JohnDoe: new Person('John Doe', 42),
JaneDoe: new Person('Janie Doe', 24)
'John Doe': new Person('John Doe', 42),
'Jane Doe': new Person('Janie Doe', 24)
},
'to equal',
{
JohnDoe: new Person('John Doe', 42),
JaneDoe: new Person('Jane Doe', 24)
'John Doe': new Person('John Doe', 42),
'Jane Doe': new Person('Jane Doe', 24)
}
);
```

```output
expected
{
JohnDoe: new Person('John Doe', 42),
JaneDoe: new Person('Janie Doe', 24)
'John Doe': new Person('John Doe', 42),
'Jane Doe': new Person('Janie Doe', 24)
}
to equal
{
JohnDoe: new Person('John Doe', 42),
JaneDoe: new Person('Jane Doe', 24)
'John Doe': new Person('John Doe', 42),
'Jane Doe': new Person('Jane Doe', 24)
}
{
JohnDoe: new Person('John Doe', 42),
JaneDoe: new Person(
'John Doe': new Person('John Doe', 42),
'Jane Doe': new Person(
'Janie Doe', // should be 'Jane Doe'
// -Janie Doe
// +Jane Doe
Expand All @@ -337,39 +337,39 @@ to equal
inlineDiff = false;
expect(
{
JohnDoe: new Person('John Doe', 42),
JaneDoe: new Person('Janie Doe', 24)
'John Doe': new Person('John Doe', 42),
'Jane Doe': new Person('Janie Doe', 24)
},
'to equal',
{
JohnDoe: new Person('John Doe', 42),
JaneDoe: new Person('Jane Doe', 24)
'John Doe': new Person('John Doe', 42),
'Jane Doe': new Person('Jane Doe', 24)
}
);
```

```output
expected
{
JohnDoe: new Person('John Doe', 42),
JaneDoe: new Person('Janie Doe', 24)
'John Doe': new Person('John Doe', 42),
'Jane Doe': new Person('Janie Doe', 24)
}
to equal
{
JohnDoe: new Person('John Doe', 42),
JaneDoe: new Person('Jane Doe', 24)
'John Doe': new Person('John Doe', 42),
'Jane Doe': new Person('Jane Doe', 24)
}
{
JohnDoe: new Person('John Doe', 42),
JaneDoe: new Person('Janie Doe', 24) // should equal new Person('Jane Doe', 24)
//
// new Person(
// 'Janie Doe', // should be 'Jane Doe'
// // -Janie Doe
// // +Jane Doe
// 24
// )
'John Doe': new Person('John Doe', 42),
'Jane Doe': new Person('Janie Doe', 24) // should equal new Person('Jane Doe', 24)
//
// new Person(
// 'Janie Doe', // should be 'Jane Doe'
// // -Janie Doe
// // +Jane Doe
// 24
// )
}
```

Expand Down
87 changes: 47 additions & 40 deletions lib/assertions.js
Expand Up @@ -1799,35 +1799,27 @@ module.exports = expect => {
if (valueType.is('array-like') && !subjectIsArrayLike) {
expect.fail();
}

const promiseByKey = {};
const keys = valueType.getKeys(value);
const subjectKeys = subjectType.getKeys(subject);
const valueKeys = valueType.getKeys(value);
// calculate the unique keys early given enumerability no
// longer affects what is included in the list of keys
const uniqueKeys = subjectType.uniqueKeys(subjectKeys, valueKeys);

const promiseByKey = {};
let forceExhaustivelyComparison = false;
uniqueKeys.forEach((key, index) => {
const subjectKey = subjectType.valueForKey(subject, key);
const valueKey = valueType.valueForKey(value, key);
const valueKeyType = expect.findTypeOf(valueKey);
const isDefinedSubjectKey = typeof subjectKey !== 'undefined';
const isDefinedValueKey = typeof valueKey !== 'undefined';

if (expect.flags.exhaustively) {
if (valueKeyType.is('expect.it') && !isDefinedSubjectKey) {
// ensure value only expect.it key is marked missing
forceExhaustivelyComparison = true;
}
} else {
if (isDefinedSubjectKey && !isDefinedValueKey) {
// ignore subject only keys unless we are being exhaustive
return;
if (!subjectIsArrayLike) {
// Find all non-enumerable subject keys present in value, but not returned by subjectType.getKeys:
keys.forEach(key => {
if (
Object.prototype.hasOwnProperty.call(subject, key) &&
subjectKeys.indexOf(key) === -1
) {
subjectKeys.push(key);
}
}
});
}

keys.forEach((key, index) => {
promiseByKey[key] = expect.promise(() => {
const subjectKey = subjectType.valueForKey(subject, key);
const valueKey = valueType.valueForKey(value, key);
const valueKeyType = expect.findTypeOf(valueKey);
if (valueKeyType.is('expect.it')) {
expect.context.thisObject = subject;
return valueKey(subjectKey, expect.context);
Expand All @@ -1842,8 +1834,25 @@ module.exports = expect => {
return expect.promise
.all([
expect.promise(() => {
if (forceExhaustivelyComparison) {
throw new Error('exhaustive comparison failure');
if (expect.flags.exhaustively) {
const nonOwnKeysWithDefinedValues = keys.filter(
key =>
!Object.prototype.hasOwnProperty.call(subject, key) &&
typeof subjectType.valueForKey(subject, key) !== 'undefined'
);
const valueKeysWithDefinedValues = keys.filter(
key => typeof valueType.valueForKey(value, key) !== 'undefined'
);
const subjectKeysWithDefinedValues = subjectKeys.filter(
key =>
typeof subjectType.valueForKey(subject, key) !== 'undefined'
);
expect(
valueKeysWithDefinedValues.length -
nonOwnKeysWithDefinedValues.length,
'to equal',
subjectKeysWithDefinedValues.length
);
}
}),
expect.promise.all(promiseByKey)
Expand All @@ -1854,13 +1863,16 @@ module.exports = expect => {
diff(output, diff, inspect, equal) {
output.inline = true;
const subjectIsArrayLike = subjectType.is('array-like');
// Skip missing keys expected to be missing so they don't get rendered in the diff
const keys = uniqueKeys.filter(key => {
return (
subjectType.hasKey(subject, key) ||
typeof valueType.valueForKey(value, key) !== 'undefined'
);
});
const valueKeys = valueType.getKeys(value);
const keys = subjectType
.uniqueKeys(subjectKeys, valueKeys)
.filter(key => {
// Skip missing keys expected to be missing so they don't get rendered in the diff
return (
subjectType.hasKey(subject, key) ||
typeof valueType.valueForKey(value, key) !== 'undefined'
);
});
const prefixOutput = subjectType.prefix(
output.clone(),
subject
Expand Down Expand Up @@ -2348,19 +2360,14 @@ module.exports = expect => {
}
});
},
err => {
if (err.isOperational && !err.propertyIsEnumerable('isOperational')) {
delete err.isOperational;
}

err =>
expect.withError(
() => expect.shift(err),
e => {
e.originalError = err;
throw e;
}
);
}
)
);
}
);
Expand Down
57 changes: 42 additions & 15 deletions lib/types.js
Expand Up @@ -114,15 +114,15 @@ module.exports = function(expect) {
},
getKeys: Object.getOwnPropertySymbols
? obj => {
const keys = Object.getOwnPropertyNames(obj);
const keys = Object.keys(obj);
const symbols = Object.getOwnPropertySymbols(obj);
if (symbols.length > 0) {
return keys.concat(symbols);
} else {
return keys;
}
}
: Object.getOwnPropertyNames,
: Object.keys,
// If Symbol support is not detected default to undefined which, when
// passed to Array.prototype.sort, means "natural" (asciibetical) sort.
keyComparator:
Expand Down Expand Up @@ -154,7 +154,44 @@ module.exports = function(expect) {
}
: undefined,
equal(a, b, equal) {
return utils.checkObjectEqualityUsingType(a, b, this, equal);
if (a === b) {
return true;
}

if (b.constructor !== a.constructor) {
return false;
}

const actualKeys = this.getKeys(a).filter(
key => typeof this.valueForKey(a, key) !== 'undefined'
);
const expectedKeys = this.getKeys(b).filter(
key => typeof this.valueForKey(b, key) !== 'undefined'
);

// having the same number of owned properties (keys incorporates hasOwnProperty)
if (actualKeys.length !== expectedKeys.length) {
return false;
}
//the same set of keys (although not necessarily the same order),
actualKeys.sort(this.keyComparator);
expectedKeys.sort(this.keyComparator);
// cheap key test
for (let i = 0; i < actualKeys.length; i += 1) {
if (actualKeys[i] !== expectedKeys[i]) {
return false;
}
}

//equivalent values for every corresponding key, and
// possibly expensive deep test
for (let j = 0; j < actualKeys.length; j += 1) {
const key = actualKeys[j];
if (!equal(this.valueForKey(a, key), this.valueForKey(b, key))) {
return false;
}
}
return true;
},
hasKey(obj, key) {
return key in obj;
Expand Down Expand Up @@ -762,20 +799,12 @@ module.exports = function(expect) {
'sourceId',
'sourceURL',
'stack',
'stackArray',
'__stackCleaned__',
'isOperational' // added by the promise implementation
'stackArray'
].reduce((result, prop) => {
result[prop] = true;
return result;
}, {});

if (new Error().hasOwnProperty('arguments')) {
// node.js 0.10 adds two extra non-enumerable properties to Error instances:
errorMethodBlacklist.arguments = true;
errorMethodBlacklist.type = true;
}

expect.addType({
base: 'object',
name: 'Error',
Expand All @@ -797,9 +826,7 @@ module.exports = function(expect) {
},
equal(a, b, equal) {
return (
a === b ||
(equal(a.message, b.message) &&
utils.checkObjectEqualityUsingType(a, b, this, equal))
a === b || (equal(a.message, b.message) && this.baseType.equal(a, b))
);
},
inspect(value, depth, output, inspect) {
Expand Down
42 changes: 0 additions & 42 deletions lib/utils.js
Expand Up @@ -27,48 +27,6 @@ const utils = (module.exports = {
return a === b;
}),

checkObjectEqualityUsingType(a, b, type, isEqual) {
if (a === b) {
return true;
}

if (b.constructor !== a.constructor) {
return false;
}

const actualKeys = type
.getKeys(a)
.filter(key => typeof type.valueForKey(a, key) !== 'undefined');
const expectedKeys = type
.getKeys(b)
.filter(key => typeof type.valueForKey(b, key) !== 'undefined');

// having the same number of owned properties (keys incorporates hasOwnProperty)
if (actualKeys.length !== expectedKeys.length) {
return false;
}

//the same set of keys (although not necessarily the same order),
actualKeys.sort(type.keyComparator);
expectedKeys.sort(type.keyComparator);
// cheap key test
for (let i = 0; i < actualKeys.length; i += 1) {
if (actualKeys[i] !== expectedKeys[i]) {
return false;
}
}

//equivalent values for every corresponding key, and
// possibly expensive deep test
for (let j = 0; j < actualKeys.length; j += 1) {
const key = actualKeys[j];
if (!isEqual(type.valueForKey(a, key), type.valueForKey(b, key))) {
return false;
}
}
return true;
},

duplicateArrayLikeUsingType(obj, type) {
const keys = type.getKeys(obj);

Expand Down

0 comments on commit 3050ffa

Please sign in to comment.