Skip to content

Commit

Permalink
Move getPath from expect/utils to jest-util
Browse files Browse the repository at this point in the history
  • Loading branch information
mattphillips committed Jun 13, 2018
1 parent 611dbf8 commit 6b98ef1
Show file tree
Hide file tree
Showing 9 changed files with 148 additions and 192 deletions.
3 changes: 2 additions & 1 deletion packages/expect/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"jest-get-type": "^22.1.0",
"jest-matcher-utils": "^23.0.1",
"jest-message-util": "^23.1.0",
"jest-regex-util": "^23.0.0"
"jest-regex-util": "^23.0.0",
"jest-util": "^23.1.0"
}
}
85 changes: 1 addition & 84 deletions packages/expect/src/__tests__/utils.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,90 +9,7 @@
'use strict';

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

describe('getPath()', () => {
test('property exists', () => {
expect(getPath({a: {b: {c: 5}}}, 'a.b.c')).toEqual({
hasEndProp: true,
lastTraversedObject: {c: 5},
traversedPath: ['a', 'b', 'c'],
value: 5,
});

expect(getPath({a: {b: {c: {d: 1}}}}, 'a.b.c.d')).toEqual({
hasEndProp: true,
lastTraversedObject: {d: 1},
traversedPath: ['a', 'b', 'c', 'd'],
value: 1,
});
});

test('property doesnt exist', () => {
expect(getPath({a: {b: {}}}, 'a.b.c')).toEqual({
hasEndProp: false,
lastTraversedObject: {},
traversedPath: ['a', 'b'],
value: undefined,
});
});

test('property exist but undefined', () => {
expect(getPath({a: {b: {c: undefined}}}, 'a.b.c')).toEqual({
hasEndProp: true,
lastTraversedObject: {c: undefined},
traversedPath: ['a', 'b', 'c'],
value: undefined,
});
});

test('property is a getter on class instance', () => {
class A {
get a() {
return 'a';
}
get b() {
return {c: 'c'};
}
}

expect(getPath(new A(), 'a')).toEqual({
hasEndProp: true,
lastTraversedObject: new A(),
traversedPath: ['a'],
value: 'a',
});
expect(getPath(new A(), 'b.c')).toEqual({
hasEndProp: true,
lastTraversedObject: {c: 'c'},
traversedPath: ['b', 'c'],
value: 'c',
});
});

test('path breaks', () => {
expect(getPath({a: {}}, 'a.b.c')).toEqual({
hasEndProp: false,
lastTraversedObject: {},
traversedPath: ['a'],
value: undefined,
});
});

test('empty object at the end', () => {
expect(getPath({a: {b: {c: {}}}}, 'a.b.c.d')).toEqual({
hasEndProp: false,
lastTraversedObject: {},
traversedPath: ['a', 'b', 'c'],
value: undefined,
});
});
});
const {emptyObject, getObjectSubset, subsetEquality} = require('../utils');

describe('getObjectSubset()', () => {
[
Expand Down
2 changes: 1 addition & 1 deletion packages/expect/src/matchers.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ import {
} from 'jest-matcher-utils';
import {
getObjectSubset,
getPath,
iterableEquality,
subsetEquality,
typeEquality,
isOneline,
} from './utils';
import {equals} from './jasmine_utils';
import {getPath} from 'jest-util';

type ContainIterable =
| Array<any>
Expand Down
56 changes: 0 additions & 56 deletions packages/expect/src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,66 +14,10 @@ import {
isImmutableUnorderedSet,
} from './jasmine_utils';

type GetPath = {
hasEndProp?: boolean,
lastTraversedObject: ?Object,
traversedPath: Array<string>,
value?: any,
};

export const hasOwnProperty = (object: Object, value: string) =>
Object.prototype.hasOwnProperty.call(object, value) ||
Object.prototype.hasOwnProperty.call(object.constructor.prototype, value);

export const getPath = (
object: Object,
propertyPath: string | Array<string>,
): GetPath => {
if (!Array.isArray(propertyPath)) {
propertyPath = propertyPath.split('.');
}

if (propertyPath.length) {
const lastProp = propertyPath.length === 1;
const prop = propertyPath[0];
const newObject = object[prop];

if (!lastProp && (newObject === null || newObject === undefined)) {
// This is not the last prop in the chain. If we keep recursing it will
// hit a `can't access property X of undefined | null`. At this point we
// know that the chain has broken and we can return right away.
return {
hasEndProp: false,
lastTraversedObject: object,
traversedPath: [],
};
}

const result = getPath(newObject, propertyPath.slice(1));

if (result.lastTraversedObject === null) {
result.lastTraversedObject = object;
}

result.traversedPath.unshift(prop);

if (lastProp) {
result.hasEndProp = hasOwnProperty(object, prop);
if (!result.hasEndProp) {
result.traversedPath.shift();
}
}

return result;
}

return {
lastTraversedObject: null,
traversedPath: [],
value: object,
};
};

// Strip properties from object that are not present in the subset. Useful for
// printing the diff for toMatchObject() without adding unrelated noise.
export const getObjectSubset = (object: Object, subset: Object) => {
Expand Down
1 change: 1 addition & 0 deletions packages/jest-each/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"license": "MIT",
"dependencies": {
"chalk": "^2.0.1",
"jest-util": "^23.1.0",
"pretty-format": "^23.0.1"
}
}
51 changes: 1 addition & 50 deletions packages/jest-each/src/bind.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import util from 'util';
import chalk from 'chalk';
import pretty from 'pretty-format';
import {getPath} from 'jest-util';

type Table = Array<Array<any>>;
type PrettyArgs = {
Expand Down Expand Up @@ -152,53 +153,3 @@ const applyObjectParams = (obj: any, test: Function) => {

const pluralize = (word: string, count: number) =>
word + (count === 1 ? '' : 's');

const hasOwnProperty = (object: Object, value: string) =>
Object.prototype.hasOwnProperty.call(object, value) ||
Object.prototype.hasOwnProperty.call(object.constructor.prototype, value);

const getPath = (object: Object, propertyPath: string | Array<string>) => {
if (!Array.isArray(propertyPath)) {
propertyPath = propertyPath.split('.');
}

if (propertyPath.length) {
const lastProp = propertyPath.length === 1;
const prop = propertyPath[0];
const newObject = object[prop];

if (!lastProp && (newObject === null || newObject === undefined)) {
// This is not the last prop in the chain. If we keep recursing it will
// hit a `can't access property X of undefined | null`. At this point we
// know that the chain has broken and we can return right away.
return {
hasEndProp: false,
lastTraversedObject: object,
traversedPath: [],
};
}

const result = getPath(newObject, propertyPath.slice(1));

if (result.lastTraversedObject === null) {
result.lastTraversedObject = object;
}

result.traversedPath.unshift(prop);

if (lastProp) {
result.hasEndProp = hasOwnProperty(object, prop);
if (!result.hasEndProp) {
result.traversedPath.shift();
}
}

return result;
}

return {
lastTraversedObject: null,
traversedPath: [],
value: object,
};
};
79 changes: 79 additions & 0 deletions packages/jest-util/src/__tests__/get_path.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import getPath from '../get_path';

describe('getPath()', () => {
test('property exists', () => {
expect(getPath({a: {b: {c: 5}}}, 'a.b.c')).toEqual({
hasEndProp: true,
lastTraversedObject: {c: 5},
traversedPath: ['a', 'b', 'c'],
value: 5,
});

expect(getPath({a: {b: {c: {d: 1}}}}, 'a.b.c.d')).toEqual({
hasEndProp: true,
lastTraversedObject: {d: 1},
traversedPath: ['a', 'b', 'c', 'd'],
value: 1,
});
});

test('property doesnt exist', () => {
expect(getPath({a: {b: {}}}, 'a.b.c')).toEqual({
hasEndProp: false,
lastTraversedObject: {},
traversedPath: ['a', 'b'],
value: undefined,
});
});

test('property exist but undefined', () => {
expect(getPath({a: {b: {c: undefined}}}, 'a.b.c')).toEqual({
hasEndProp: true,
lastTraversedObject: {c: undefined},
traversedPath: ['a', 'b', 'c'],
value: undefined,
});
});

test('property is a getter on class instance', () => {
class A {
get a() {
return 'a';
}
get b() {
return {c: 'c'};
}
}

expect(getPath(new A(), 'a')).toEqual({
hasEndProp: true,
lastTraversedObject: new A(),
traversedPath: ['a'],
value: 'a',
});
expect(getPath(new A(), 'b.c')).toEqual({
hasEndProp: true,
lastTraversedObject: {c: 'c'},
traversedPath: ['b', 'c'],
value: 'c',
});
});

test('path breaks', () => {
expect(getPath({a: {}}, 'a.b.c')).toEqual({
hasEndProp: false,
lastTraversedObject: {},
traversedPath: ['a'],
value: undefined,
});
});

test('empty object at the end', () => {
expect(getPath({a: {b: {c: {}}}}, 'a.b.c.d')).toEqual({
hasEndProp: false,
lastTraversedObject: {},
traversedPath: ['a', 'b', 'c'],
value: undefined,
});
});
});
61 changes: 61 additions & 0 deletions packages/jest-util/src/get_path.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
type GetPath = {
hasEndProp?: boolean,
lastTraversedObject: ?Object,
traversedPath: Array<string>,
value?: any,
};

const hasOwnProperty = (object: Object, value: string) =>
Object.prototype.hasOwnProperty.call(object, value) ||
Object.prototype.hasOwnProperty.call(object.constructor.prototype, value);

const getPath = (
object: Object,
propertyPath: string | Array<string>,
): GetPath => {
if (!Array.isArray(propertyPath)) {
propertyPath = propertyPath.split('.');
}

if (propertyPath.length) {
const lastProp = propertyPath.length === 1;
const prop = propertyPath[0];
const newObject = object[prop];

if (!lastProp && (newObject === null || newObject === undefined)) {
// This is not the last prop in the chain. If we keep recursing it will
// hit a `can't access property X of undefined | null`. At this point we
// know that the chain has broken and we can return right away.
return {
hasEndProp: false,
lastTraversedObject: object,
traversedPath: [],
};
}

const result = getPath(newObject, propertyPath.slice(1));

if (result.lastTraversedObject === null) {
result.lastTraversedObject = object;
}

result.traversedPath.unshift(prop);

if (lastProp) {
result.hasEndProp = hasOwnProperty(object, prop);
if (!result.hasEndProp) {
result.traversedPath.shift();
}
}

return result;
}

return {
lastTraversedObject: null,
traversedPath: [],
value: object,
};
};

export default getPath;
Loading

0 comments on commit 6b98ef1

Please sign in to comment.