Skip to content

Commit d7c6120

Browse files
Support arrays in t.like() assertion
Co-authored-by: Mark Wubben <[email protected]>
1 parent 49e5582 commit d7c6120

File tree

3 files changed

+29
-9
lines changed

3 files changed

+29
-9
lines changed

docs/03-assertions.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,9 @@ Assert that `actual` is not deeply equal to `expected`. The inverse of `.deepEqu
141141

142142
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.
143143

144-
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()`.
144+
Instead AVA derives a *comparable* value from `actual`, recursively based on the shape of `selector`. This value is then compared to `selector` using `.deepEqual()`.
145145

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

148148
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`.
149149

@@ -162,6 +162,12 @@ t.like({
162162
})
163163
```
164164

165+
You can also use arrays, but note that any indices in `actual` that are not in `selector` are ignored:
166+
167+
```js
168+
t.like([1, 2, 3], [1, 2])
169+
```
170+
165171
Finally, this returns a boolean indicating whether the assertion passed.
166172

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

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

208214
* `instanceOf`: a constructor, the thrown error must be an instance of
209215
* `is`: the thrown error must be strictly equal to `expectation.is`
210-
* `message`: the following types are valid:
216+
* `message`: the following types are valid:
211217
* *string* - it is compared against the thrown error's message
212218
* *regular expression* - it is matched against this message
213219
* *function* - it is passed the thrown error message and must return a boolean for whether the assertion passed

lib/like-selector.js

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1+
const isObject = selector => Reflect.getPrototypeOf(selector) === Object.prototype;
2+
13
export function isLikeSelector(selector) {
2-
return selector !== null
3-
&& typeof selector === 'object'
4-
&& Reflect.getPrototypeOf(selector) === Object.prototype
5-
&& Reflect.ownKeys(selector).length > 0;
4+
if (selector === null || typeof selector !== 'object') {
5+
return false;
6+
}
7+
8+
const keyCount = Reflect.ownKeys(selector).length;
9+
return (Array.isArray(selector) && keyCount > 1) || (isObject(selector) && keyCount > 0);
610
}
711

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

21-
const comparable = {};
25+
const comparable = Array.isArray(selector) ? [] : {};
2226
for (const [key, rhs] of Object.entries(selector)) {
2327
if (isLikeSelector(rhs)) {
2428
comparable[key] = selectComparable(Reflect.get(lhs, key), rhs, circular);

test-tap/assert.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -761,6 +761,16 @@ test('.like()', t => {
761761
values: [{label: 'Difference (- actual, + expected):', formatted: /{\n-\s*a: 'foo',\n\+\s*a: 'bar',\n\s*}/}],
762762
});
763763

764+
passes(t, () => assertions.like({a: [{a: 1, b: 2}]}, {a: [{a: 1}]}));
765+
passes(t, () => assertions.like([{a: 1, b: 2}], [{a: 1}]));
766+
passes(t, () => assertions.like([{a: 1, b: 2}, {c: 3}], [{a: 1}]));
767+
768+
passes(t, () => assertions.like([1, 2, 3], [1, 2, 3]));
769+
passes(t, () => assertions.like([1, 2, 3], [1, 2]));
770+
771+
fails(t, () => assertions.like([1, 2, 3], [3, 2, 1]));
772+
fails(t, () => assertions.like([1, 2], [1, 2, 3]));
773+
764774
t.end();
765775
});
766776

0 commit comments

Comments
 (0)