Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

- `[jest-core]` Add support for `testResultsProcessor` written in ESM ([#12006](https://github.com/facebook/jest/pull/12006))
- `[jest-diff, pretty-format]` Add `compareKeys` option for custom sorting of object keys ([#11992](https://github.com/facebook/jest/pull/11992))
- `[expect]` Enhancing the `toHaveProperty` matcher to support array selection ([#12090](https://github.com/facebook/jest/pull/12090))

### Fixes

Expand Down
24 changes: 24 additions & 0 deletions packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -3551,6 +3551,30 @@ Expected path: <g>"memo"</>
Expected value: not <g>[]</>
`;

exports[`.toHaveProperty() {pass: true} expect({"a": {"b": [[{"c": [{"d": 1}]}]]}}).toHaveProperty('a.b[0][0].c[0].d', 1) 1`] = `
<d>expect(</><r>received</><d>).</>not<d>.</>toHaveProperty<d>(</><g>path</><d>, </><g>value</><d>)</>

Expected path: <g>"a.b[0][0].c[0].d"</>

Expected value: not <g>1</>
`;

exports[`.toHaveProperty() {pass: true} expect({"a": {"b": [{"c": [{"d": 1}]}]}}).toHaveProperty('a.b[0].c[0].d', 1) 1`] = `
<d>expect(</><r>received</><d>).</>not<d>.</>toHaveProperty<d>(</><g>path</><d>, </><g>value</><d>)</>

Expected path: <g>"a.b[0].c[0].d"</>

Expected value: not <g>1</>
`;

exports[`.toHaveProperty() {pass: true} expect({"a": {"b": [{"c": {"d": [{"e": 1}, {"f": 2}]}}]}}).toHaveProperty('a.b[0].c.d[1].f', 2) 1`] = `
<d>expect(</><r>received</><d>).</>not<d>.</>toHaveProperty<d>(</><g>path</><d>, </><g>value</><d>)</>

Expected path: <g>"a.b[0].c.d[1].f"</>

Expected value: not <g>2</>
`;

exports[`.toHaveProperty() {pass: true} expect({"a": {"b": [1, 2, 3]}}).toHaveProperty('a,b,1') 1`] = `
<d>expect(</><r>received</><d>).</>not<d>.</>toHaveProperty<d>(</><g>path</><d>)</>

Expand Down
3 changes: 3 additions & 0 deletions packages/expect/src/__tests__/matchers.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1885,6 +1885,9 @@ describe('.toHaveProperty()', () => {
[{a: {b: undefined}}, 'a.b', undefined],
[{a: {}}, 'a.b', undefined], // delete for breaking change in future major
[{a: {b: {c: 5}}}, 'a.b', {c: 5}],
[{a: {b: [{c: [{d: 1}]}]}}, 'a.b[0].c[0].d', 1],
[{a: {b: [{c: {d: [{e: 1}, {f: 2}]}}]}}, 'a.b[0].c.d[1].f', 2],
[{a: {b: [[{c: [{d: 1}]}]]}}, 'a.b[0][0].c[0].d', 1],
[Object.assign(Object.create(null), {property: 1}), 'property', 1],
[new Foo(), 'a', undefined],
[new Foo(), 'b', 'b'],
Expand Down
3 changes: 2 additions & 1 deletion packages/expect/src/matchers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import {
getObjectSubset,
getPath,
iterableEquality,
pathAsArray,
sparseArrayEquality,
subsetEquality,
typeEquality,
Expand Down Expand Up @@ -704,7 +705,7 @@ const matchers: MatchersObject = {

const expectedPathLength =
typeof expectedPath === 'string'
? expectedPath.split('.').length
? pathAsArray(expectedPath).length
: expectedPath.length;

if (expectedPathType === 'array' && expectedPathLength === 0) {
Expand Down
20 changes: 19 additions & 1 deletion packages/expect/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export const getPath = (
propertyPath: string | Array<string>,
): GetPath => {
if (!Array.isArray(propertyPath)) {
propertyPath = (propertyPath as string).split('.');
propertyPath = pathAsArray(propertyPath);
}

if (propertyPath.length) {
Expand Down Expand Up @@ -372,6 +372,24 @@ export const partition = <T>(
return result;
};

export const pathAsArray = (propertyPath: string): Array<any> => {
// will match everything that's not a dot or a bracket, and "" for consecutive dots.
const pattern = RegExp('[^.[\\]]+|(?=(?:\\.)(?:\\.|$))', 'g');
const properties: Array<string> = [];

// because the regex won't match the first dot, if present.
if (propertyPath[0] === '.') {
Comment thread
iifawzi marked this conversation as resolved.
properties.push('');
}

propertyPath.replace(pattern, match => {
properties.push(match);
return match;
});

return properties;
};

// Copied from https://github.com/graingert/angular.js/blob/a43574052e9775cbc1d7dd8a086752c979b0f020/src/Angular.js#L685-L693
export const isError = (value: unknown): value is Error => {
switch (Object.prototype.toString.call(value)) {
Expand Down