Skip to content

Commit

Permalink
expect: Test more precisely for class instance getters (jestjs#7477)
Browse files Browse the repository at this point in the history
  • Loading branch information
pedrottimark authored and captain-yossarian committed Jul 18, 2019
1 parent 9d2c65c commit 05c3228
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 9 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
- `[jest-haste-map]` Remove legacy condition for duplicate module detection ([#7333](https://github.com/facebook/jest/pull/7333))
- `[jest-haste-map]` Fix `require` detection with trailing commas and ignore `import typeof` modules ([#7385](https://github.com/facebook/jest/pull/7385))
- `[jest-cli]` Fix to set prettierPath via config file ([#7412](https://github.com/facebook/jest/pull/7412))
- `[expect]` Test more precisely for class instance getters ([#7477](https://github.com/facebook/jest/pull/7477))
- `[jest-cli]` Support dashed args ([#7497](https://github.com/facebook/jest/pull/7497))
- `[jest-cli]` Fix to run in band tests if watch mode enable when runInBand arg used ([#7518](https://github.com/facebook/jest/pull/7518))
- `[jest-runtime]` Fix mistake as test files when run coverage issue. ([#7506](https://github.com/facebook/jest/pull/7506))
Expand Down
40 changes: 40 additions & 0 deletions packages/expect/src/__tests__/utils.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const {
emptyObject,
getObjectSubset,
getPath,
hasOwnProperty,
subsetEquality,
} = require('../utils');

Expand Down Expand Up @@ -94,6 +95,45 @@ describe('getPath()', () => {
});
});

describe('hasOwnProperty', () => {
it('does inherit getter from class', () => {
class MyClass {
get key() {
return 'value';
}
}
expect(hasOwnProperty(new MyClass(), 'key')).toBe(true);
});

it('does not inherit setter from class', () => {
class MyClass {
set key(value) {}
}
expect(hasOwnProperty(new MyClass(), 'key')).toBe(false);
});

it('does not inherit method from class', () => {
class MyClass {
key() {}
}
expect(hasOwnProperty(new MyClass(), 'key')).toBe(false);
});

it('does not inherit property from constructor prototype', () => {
function MyClass() {}
MyClass.prototype.key = 'value';
expect(hasOwnProperty(new MyClass(), 'key')).toBe(false);
});

it('does not inherit __proto__ getter from Object', () => {
expect(hasOwnProperty({}, '__proto__')).toBe(false);
});

it('does not inherit toString method from Object', () => {
expect(hasOwnProperty({}, 'toString')).toBe(false);
});
});

describe('getObjectSubset()', () => {
[
[{a: 'b', c: 'd'}, {a: 'd'}, {a: 'b'}],
Expand Down
33 changes: 24 additions & 9 deletions packages/expect/src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,33 @@ type GetPath = {
value?: any,
};

export const hasOwnProperty = (object: Object, value: string) => {
// Account for objects created using unconventional means such as
// `Object.create(null)`, in which case the `object.constructor` is undefined
// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create#Custom_and_Null_objects
const objectConstructor = object.constructor || Object;

return (
Object.prototype.hasOwnProperty.call(object, value) ||
Object.prototype.hasOwnProperty.call(objectConstructor.prototype, value)
// Return whether object instance inherits getter from its class.
const hasGetterFromConstructor = (object: Object, key: string) => {
const constructor = object.constructor;
if (constructor === Object) {
// A literal object has Object as constructor.
// Therefore, it cannot inherit application-specific getters.
// Furthermore, Object has __proto__ getter which is not relevant.
// Array, Boolean, Number, String constructors don’t have any getters.
return false;
}
if (typeof constructor !== 'function') {
// Object.create(null) constructs object with no constructor nor prototype.
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create#Custom_and_Null_objects
return false;
}

const descriptor = Object.getOwnPropertyDescriptor(
constructor.prototype,
key,
);
return descriptor !== undefined && typeof descriptor.get === 'function';
};

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

export const getPath = (
object: Object,
propertyPath: string | Array<string>,
Expand Down

0 comments on commit 05c3228

Please sign in to comment.