diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index 75cc8024..404e96ef 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -2272,11 +2272,35 @@ getJasmineRequireObj().ObjectContaining = function(j$) { this.sample = sample; } + function getPrototype(obj) { + if (Object.getPrototypeOf) { + return Object.getPrototypeOf(obj); + } + + if (obj.constructor.prototype == obj) { + return null; + } + + return obj.constructor.prototype; + } + + function hasProperty(obj, property) { + if (!obj) { + return false; + } + + if (Object.prototype.hasOwnProperty.call(obj, property)) { + return true; + } + + return hasProperty(getPrototype(obj), property); + } + ObjectContaining.prototype.asymmetricMatch = function(other) { if (typeof(this.sample) !== 'object') { throw new Error('You must provide an object to objectContaining, not \''+this.sample+'\'.'); } for (var property in this.sample) { - if (!Object.prototype.hasOwnProperty.call(other, property) || + if (!hasProperty(other, property) || !j$.matchersUtil.equals(this.sample[property], other[property])) { return false; } diff --git a/spec/core/asymmetric_equality/ObjectContainingSpec.js b/spec/core/asymmetric_equality/ObjectContainingSpec.js index d87f03e2..3f2fe07e 100644 --- a/spec/core/asymmetric_equality/ObjectContainingSpec.js +++ b/spec/core/asymmetric_equality/ObjectContainingSpec.js @@ -55,4 +55,35 @@ describe("ObjectContaining", function() { expect(containing.asymmetricMatch({})).toBe(false); }); + + it("matches defined properties", function(){ + // IE 8 doesn't support `definePropery` on non-DOM nodes + if (jasmine.getEnv().ieVersion < 9) { return; } + + var containing = new j$.ObjectContaining({ foo: "fooVal" }); + + var definedPropertyObject = {}; + Object.defineProperty(definedPropertyObject, "foo", { + get: function() { return "fooVal" } + }); + expect(containing.asymmetricMatch(definedPropertyObject)).toBe(true); + }); + + it("matches prototype properties", function(){ + var containing = new j$.ObjectContaining({ foo: "fooVal" }); + + var prototypeObject = {foo: "fooVal"}; + var obj; + + if (Object.create) { + obj = Object.create(prototypeObject); + } else { + function Foo() {} + Foo.prototype = prototypeObject; + Foo.prototype.constructor = Foo; + obj = new Foo(); + } + + expect(containing.asymmetricMatch(obj)).toBe(true); + }); }); diff --git a/src/core/asymmetric_equality/ObjectContaining.js b/src/core/asymmetric_equality/ObjectContaining.js index db8bbb1d..7788c786 100644 --- a/src/core/asymmetric_equality/ObjectContaining.js +++ b/src/core/asymmetric_equality/ObjectContaining.js @@ -4,11 +4,35 @@ getJasmineRequireObj().ObjectContaining = function(j$) { this.sample = sample; } + function getPrototype(obj) { + if (Object.getPrototypeOf) { + return Object.getPrototypeOf(obj); + } + + if (obj.constructor.prototype == obj) { + return null; + } + + return obj.constructor.prototype; + } + + function hasProperty(obj, property) { + if (!obj) { + return false; + } + + if (Object.prototype.hasOwnProperty.call(obj, property)) { + return true; + } + + return hasProperty(getPrototype(obj), property); + } + ObjectContaining.prototype.asymmetricMatch = function(other) { if (typeof(this.sample) !== 'object') { throw new Error('You must provide an object to objectContaining, not \''+this.sample+'\'.'); } for (var property in this.sample) { - if (!Object.prototype.hasOwnProperty.call(other, property) || + if (!hasProperty(other, property) || !j$.matchersUtil.equals(this.sample[property], other[property])) { return false; }