Skip to content

Commit 42c1d75

Browse files
quantizorcpojer
authored andcommitted
add inverse expect asymmetric matchers (#5517)
* add inverse expect helpers * linting * switch to expect.not.* format, fix string validation in asymmetric matcher * add back mistakenly removed test * lint docs * add changelog entry
1 parent 74bf072 commit 42c1d75

File tree

7 files changed

+229
-31
lines changed

7 files changed

+229
-31
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
promises ([#5670](https://github.com/facebook/jest/pull/5670))
1515
* `[expect]` Add isError to utils
1616
([#5670](https://github.com/facebook/jest/pull/5670))
17+
* `[expect]` Add inverse matchers (`expect.not.arrayContaining`, etc.,
18+
[#5517](https://github.com/facebook/jest/pull/5517))
1719

1820
### Fixes
1921

docs/ExpectAPI.md

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,37 @@ test('prepareState prepares a valid state', () => {
273273
The `expect.hasAssertions()` call ensures that the `prepareState` callback
274274
actually gets called.
275275

276+
### `expect.not.arrayContaining(array)`
277+
278+
`expect.not.arrayContaining(array)` matches a received array which contains none
279+
of the elements in the expected array. That is, the expected array **is not a
280+
subset** of the received array.
281+
282+
It is the inverse of `expect.arrayContaining`.
283+
284+
### `expect.not.objectContaining(object)`
285+
286+
`expect.not.objectContaining(object)` matches any received object that does not
287+
recursively match the expected properties. That is, the expected object **is not
288+
a subset** of the received object. Therefore, it matches a received object which
289+
contains properties that are **not** in the expected object.
290+
291+
It is the inverse of `expect.objectContaining`.
292+
293+
### `expect.not.stringContaining(string)`
294+
295+
`expect.not.stringContaining(string)` matches any received string that does not
296+
contain the exact expected string.
297+
298+
It is the inverse of `expect.stringContaining`.
299+
300+
### `expect.not.stringMatching(regexp)`
301+
302+
`expect.not.stringMatching(regexp)` matches any received string that does not
303+
match the expected regexp.
304+
305+
It is the inverse of `expect.stringMatching`.
306+
276307
### `expect.objectContaining(object)`
277308

278309
`expect.objectContaining(object)` matches any received object that recursively
@@ -302,8 +333,6 @@ test('onPress gets called with the right thing', () => {
302333

303334
### `expect.stringContaining(string)`
304335

305-
##### available in Jest **19.0.0+**
306-
307336
`expect.stringContaining(string)` matches any received string that contains the
308337
exact expected string.
309338

packages/expect/src/__tests__/asymmetric_matchers.test.js

Lines changed: 102 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,13 @@ const {
1313
any,
1414
anything,
1515
arrayContaining,
16+
arrayNotContaining,
1617
objectContaining,
18+
objectNotContaining,
1719
stringContaining,
20+
stringNotContaining,
1821
stringMatching,
22+
stringNotMatching,
1923
} = require('../asymmetric_matchers');
2024

2125
test('Any.asymmetricMatch()', () => {
@@ -89,6 +93,27 @@ test('ArrayContaining throws for non-arrays', () => {
8993
}).toThrow();
9094
});
9195

96+
test('ArrayNotContaining matches', () => {
97+
jestExpect(arrayNotContaining(['foo']).asymmetricMatch(['bar'])).toBe(true);
98+
});
99+
100+
test('ArrayNotContaining does not match', () => {
101+
[
102+
arrayNotContaining([]).asymmetricMatch('jest'),
103+
arrayNotContaining(['foo']).asymmetricMatch(['foo']),
104+
arrayNotContaining(['foo']).asymmetricMatch(['foo', 'bar']),
105+
arrayNotContaining([]).asymmetricMatch({}),
106+
].forEach(test => {
107+
jestExpect(test).toEqual(false);
108+
});
109+
});
110+
111+
test('ArrayNotContaining throws for non-arrays', () => {
112+
jestExpect(() => {
113+
arrayNotContaining('foo').asymmetricMatch([]);
114+
}).toThrow();
115+
});
116+
92117
test('ObjectContaining matches', () => {
93118
[
94119
objectContaining({}).asymmetricMatch('jest'),
@@ -139,32 +164,106 @@ test('ObjectContaining throws for non-objects', () => {
139164
jestExpect(() => objectContaining(1337).asymmetricMatch()).toThrow();
140165
});
141166

167+
test('ObjectNotContaining matches', () => {
168+
[
169+
objectNotContaining({}).asymmetricMatch('jest'),
170+
objectNotContaining({foo: 'foo'}).asymmetricMatch({bar: 'bar'}),
171+
objectNotContaining({foo: 'foo'}).asymmetricMatch({foo: 'foox'}),
172+
objectNotContaining({foo: undefined}).asymmetricMatch({}),
173+
].forEach(test => {
174+
jestExpect(test).toEqual(true);
175+
});
176+
});
177+
178+
test('ObjectNotContaining does not match', () => {
179+
[
180+
objectNotContaining({foo: 'foo'}).asymmetricMatch({
181+
foo: 'foo',
182+
jest: 'jest',
183+
}),
184+
objectNotContaining({foo: undefined}).asymmetricMatch({foo: undefined}),
185+
objectNotContaining({
186+
first: objectNotContaining({second: {}}),
187+
}).asymmetricMatch({first: {second: {}}}),
188+
].forEach(test => {
189+
jestExpect(test).toEqual(false);
190+
});
191+
});
192+
193+
test('ObjectNotContaining throws for non-objects', () => {
194+
jestExpect(() => objectNotContaining(1337).asymmetricMatch()).toThrow();
195+
});
196+
142197
test('StringContaining matches string against string', () => {
143198
jestExpect(stringContaining('en*').asymmetricMatch('queen*')).toBe(true);
144199
jestExpect(stringContaining('en').asymmetricMatch('queue')).toBe(false);
145-
jestExpect(stringContaining('en').asymmetricMatch({})).toBe(false);
146200
});
147201

148202
test('StringContaining throws for non-strings', () => {
149203
jestExpect(() => {
150204
stringContaining([1]).asymmetricMatch('queen');
151205
}).toThrow();
206+
207+
jestExpect(() => {
208+
stringContaining('en*').asymmetricMatch(1);
209+
}).toThrow();
210+
});
211+
212+
test('StringNotContaining matches string against string', () => {
213+
jestExpect(stringNotContaining('en*').asymmetricMatch('queen*')).toBe(false);
214+
jestExpect(stringNotContaining('en').asymmetricMatch('queue')).toBe(true);
215+
});
216+
217+
test('StringNotContaining throws for non-strings', () => {
218+
jestExpect(() => {
219+
stringNotContaining([1]).asymmetricMatch('queen');
220+
}).toThrow();
221+
222+
jestExpect(() => {
223+
stringNotContaining('en*').asymmetricMatch(1);
224+
}).toThrow();
152225
});
153226

154227
test('StringMatching matches string against regexp', () => {
155228
jestExpect(stringMatching(/en/).asymmetricMatch('queen')).toBe(true);
156229
jestExpect(stringMatching(/en/).asymmetricMatch('queue')).toBe(false);
157-
jestExpect(stringMatching(/en/).asymmetricMatch({})).toBe(false);
158230
});
159231

160232
test('StringMatching matches string against string', () => {
161233
jestExpect(stringMatching('en').asymmetricMatch('queen')).toBe(true);
162234
jestExpect(stringMatching('en').asymmetricMatch('queue')).toBe(false);
163-
jestExpect(stringMatching('en').asymmetricMatch({})).toBe(false);
164235
});
165236

166237
test('StringMatching throws for non-strings and non-regexps', () => {
167238
jestExpect(() => {
168239
stringMatching([1]).asymmetricMatch('queen');
169240
}).toThrow();
170241
});
242+
243+
test('StringMatching throws for non-string actual values', () => {
244+
jestExpect(() => {
245+
stringMatching('en').asymmetricMatch(1);
246+
}).toThrow();
247+
});
248+
249+
test('StringNotMatching matches string against regexp', () => {
250+
jestExpect(stringNotMatching(/en/).asymmetricMatch('queen')).toBe(false);
251+
jestExpect(stringNotMatching(/en/).asymmetricMatch('queue')).toBe(true);
252+
});
253+
254+
test('StringNotMatching matches string against string', () => {
255+
jestExpect(stringNotMatching('en').asymmetricMatch('queen')).toBe(false);
256+
jestExpect(stringNotMatching('en').asymmetricMatch('queue')).toBe(true);
257+
});
258+
259+
test('StringNotMatching throws for non-strings and non-regexps', () => {
260+
jestExpect(() => {
261+
stringNotMatching([1]).asymmetricMatch('queen');
262+
}).toThrow();
263+
});
264+
265+
test('StringNotMatching throws for non-string actual values', () => {
266+
jestExpect(() => {
267+
stringNotMatching('en').asymmetricMatch(1);
268+
}).toThrow();
269+
});

packages/expect/src/__tests__/utils.test.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
'use strict';
1010

1111
const {stringify} = require('jest-matcher-utils');
12-
const {getObjectSubset, getPath} = require('../utils');
12+
const {emptyObject, getObjectSubset, getPath} = require('../utils');
1313

1414
describe('getPath()', () => {
1515
test('property exists', () => {
@@ -107,3 +107,18 @@ describe('getObjectSubset()', () => {
107107
);
108108
});
109109
});
110+
111+
describe('emptyObject()', () => {
112+
test('matches an empty object', () => {
113+
expect(emptyObject({})).toBe(true);
114+
});
115+
116+
test('does not match an object with keys', () => {
117+
expect(emptyObject({foo: undefined})).toBe(false);
118+
});
119+
120+
test('does not match a non-object', () => {
121+
expect(emptyObject(null)).toBe(false);
122+
expect(emptyObject(34)).toBe(false);
123+
});
124+
});

0 commit comments

Comments
 (0)