Skip to content

Commit

Permalink
Support arrays in t.like() assertion
Browse files Browse the repository at this point in the history
Co-authored-by: Mark Wubben <mark@novemberborn.net>
  • Loading branch information
tommy-mitchell and novemberborn committed May 24, 2023
1 parent 49e5582 commit d7c6120
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 9 deletions.
14 changes: 10 additions & 4 deletions docs/03-assertions.md
Expand Up @@ -141,9 +141,9 @@ Assert that `actual` is not deeply equal to `expected`. The inverse of `.deepEqu

Assert that `actual` is like `selector`. This is a variant of `.deepEqual()`, however `selector` does not need to have the same enumerable properties as `actual` does.

Instead AVA derives a *comparable* object from `actual`, based on the deeply-nested properties of `selector`. This object is then compared to `selector` using `.deepEqual()`.
Instead AVA derives a *comparable* value from `actual`, recursively based on the shape of `selector`. This value is then compared to `selector` using `.deepEqual()`.

Any values in `selector` that are not regular objects should be deeply equal to the corresponding values in `actual`.
Any values in `selector` that are not arrays or regular objects should be deeply equal to the corresponding values in `actual`.

In the following example, the `map` property of `actual` must be deeply equal to that of `selector`. However `nested.qux` is ignored, because it's not in `selector`.

Expand All @@ -162,6 +162,12 @@ t.like({
})
```

You can also use arrays, but note that any indices in `actual` that are not in `selector` are ignored:

```js
t.like([1, 2, 3], [1, 2])
```

Finally, this returns a boolean indicating whether the assertion passed.

### `.throws(fn, expectation?, message?)`
Expand All @@ -172,7 +178,7 @@ Assert that an error is thrown. `fn` must be a function which should throw. The

* `instanceOf`: a constructor, the thrown error must be an instance of
* `is`: the thrown error must be strictly equal to `expectation.is`
* `message`: the following types are valid:
* `message`: the following types are valid:
* *string* - it is compared against the thrown error's message
* *regular expression* - it is matched against this message
* *function* - it is passed the thrown error message and must return a boolean for whether the assertion passed
Expand Down Expand Up @@ -207,7 +213,7 @@ The thrown value *must* be an error. It is returned so you can run more assertio

* `instanceOf`: a constructor, the thrown error must be an instance of
* `is`: the thrown error must be strictly equal to `expectation.is`
* `message`: the following types are valid:
* `message`: the following types are valid:
* *string* - it is compared against the thrown error's message
* *regular expression* - it is matched against this message
* *function* - it is passed the thrown error message and must return a boolean for whether the assertion passed
Expand Down
14 changes: 9 additions & 5 deletions lib/like-selector.js
@@ -1,8 +1,12 @@
const isObject = selector => Reflect.getPrototypeOf(selector) === Object.prototype;

export function isLikeSelector(selector) {
return selector !== null
&& typeof selector === 'object'
&& Reflect.getPrototypeOf(selector) === Object.prototype
&& Reflect.ownKeys(selector).length > 0;
if (selector === null || typeof selector !== 'object') {
return false;
}

const keyCount = Reflect.ownKeys(selector).length;
return (Array.isArray(selector) && keyCount > 1) || (isObject(selector) && keyCount > 0);
}

export const CIRCULAR_SELECTOR = new Error('Encountered a circular selector');
Expand All @@ -18,7 +22,7 @@ export function selectComparable(lhs, selector, circular = new Set()) {
return lhs;
}

const comparable = {};
const comparable = Array.isArray(selector) ? [] : {};
for (const [key, rhs] of Object.entries(selector)) {
if (isLikeSelector(rhs)) {
comparable[key] = selectComparable(Reflect.get(lhs, key), rhs, circular);
Expand Down
10 changes: 10 additions & 0 deletions test-tap/assert.js
Expand Up @@ -761,6 +761,16 @@ test('.like()', t => {
values: [{label: 'Difference (- actual, + expected):', formatted: /{\n-\s*a: 'foo',\n\+\s*a: 'bar',\n\s*}/}],
});

passes(t, () => assertions.like({a: [{a: 1, b: 2}]}, {a: [{a: 1}]}));
passes(t, () => assertions.like([{a: 1, b: 2}], [{a: 1}]));
passes(t, () => assertions.like([{a: 1, b: 2}, {c: 3}], [{a: 1}]));

passes(t, () => assertions.like([1, 2, 3], [1, 2, 3]));
passes(t, () => assertions.like([1, 2, 3], [1, 2]));

fails(t, () => assertions.like([1, 2, 3], [3, 2, 1]));
fails(t, () => assertions.like([1, 2], [1, 2, 3]));

t.end();
});

Expand Down

0 comments on commit d7c6120

Please sign in to comment.