diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index f980d6c4..3eb4f61b 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -6471,13 +6471,25 @@ getJasmineRequireObj().SpyRegistry = function(j$) { throw new Error('spyOnAllFunctions could not find an object to spy upon'); } - for (var prop in obj) { - if (Object.prototype.hasOwnProperty.call(obj, prop) && obj[prop] instanceof Function) { - var descriptor = Object.getOwnPropertyDescriptor(obj, prop); - if ((descriptor.writable || descriptor.set) && descriptor.configurable) { - this.spyOn(obj, prop); + var pointer = obj, + props = [], + prop, + descriptor; + + while (pointer) { + for (prop in pointer) { + if (Object.prototype.hasOwnProperty.call(pointer, prop) && pointer[prop] instanceof Function) { + descriptor = Object.getOwnPropertyDescriptor(pointer, prop); + if ((descriptor.writable || descriptor.set) && descriptor.configurable) { + props.push(prop); + } } } + pointer = Object.getPrototypeOf(pointer); + } + + for (var i = 0; i < props.length; i++) { + this.spyOn(obj, props[i]); } return obj; diff --git a/spec/core/SpyRegistrySpec.js b/spec/core/SpyRegistrySpec.js index 920502d1..c7f53fe1 100644 --- a/spec/core/SpyRegistrySpec.js +++ b/spec/core/SpyRegistrySpec.js @@ -222,7 +222,7 @@ describe("SpyRegistry", function() { }).toThrowError(/spyOnAllFunctions could not find an object to spy upon/); }); - it("overrides all writable and configurable functions of the object", function() { + it("overrides all writable and configurable functions of the object and its parents", function() { var spyRegistry = new jasmineUnderTest.SpyRegistry({createSpy: function() { return 'I am a spy'; }}); @@ -234,7 +234,7 @@ describe("SpyRegistry", function() { var noop5 = createNoop(); var parent = { - notSpied1: noop1 + parentSpied1: noop1 }; var subject = Object.create(parent); Object.defineProperty(subject, 'spied1', { @@ -291,7 +291,7 @@ describe("SpyRegistry", function() { var spiedObject = spyRegistry.spyOnAllFunctions(subject); - expect(subject.notSpied1).toBe(noop1); + expect(subject.parentSpied1).toBe('I am a spy'); expect(subject.notSpied2).toBe(noop2); expect(subject.notSpied3).toBe(noop3); expect(subject.notSpied4).toBe(noop4); @@ -303,6 +303,42 @@ describe("SpyRegistry", function() { expect(subject.spied4).toBe('I am a spy'); expect(spiedObject).toBe(subject); }); + + it("overrides prototype methods on the object", function() { + var spyRegistry = new jasmineUnderTest.SpyRegistry({createSpy: function() { + return 'I am a spy'; + }}); + + var noop1 = function() { }; + var noop2 = function() { }; + + var MyClass = function() { + this.spied1 = noop1; + }; + MyClass.prototype.spied2 = noop2; + + var subject = new MyClass(); + spyRegistry.spyOnAllFunctions(subject); + + expect(subject.spied1).toBe('I am a spy'); + expect(subject.spied2).toBe('I am a spy'); + expect(MyClass.prototype.spied2).toBe(noop2); + }); + + it("does not override non-enumerable properties (like Object.prototype methods)", function() { + var spyRegistry = new jasmineUnderTest.SpyRegistry({createSpy: function() { + return 'I am a spy'; + }}); + var subject = { + spied1: function() { } + }; + + spyRegistry.spyOnAllFunctions(subject); + + expect(subject.spied1).toBe('I am a spy'); + expect(subject.toString).not.toBe('I am a spy'); + expect(subject.hasOwnProperty).not.toBe('I am a spy'); + }); }); describe("#clearSpies", function() { diff --git a/src/core/SpyRegistry.js b/src/core/SpyRegistry.js index 17fd1971..595ce461 100644 --- a/src/core/SpyRegistry.js +++ b/src/core/SpyRegistry.js @@ -125,13 +125,25 @@ getJasmineRequireObj().SpyRegistry = function(j$) { throw new Error('spyOnAllFunctions could not find an object to spy upon'); } - for (var prop in obj) { - if (Object.prototype.hasOwnProperty.call(obj, prop) && obj[prop] instanceof Function) { - var descriptor = Object.getOwnPropertyDescriptor(obj, prop); - if ((descriptor.writable || descriptor.set) && descriptor.configurable) { - this.spyOn(obj, prop); + var pointer = obj, + props = [], + prop, + descriptor; + + while (pointer) { + for (prop in pointer) { + if (Object.prototype.hasOwnProperty.call(pointer, prop) && pointer[prop] instanceof Function) { + descriptor = Object.getOwnPropertyDescriptor(pointer, prop); + if ((descriptor.writable || descriptor.set) && descriptor.configurable) { + props.push(prop); + } } } + pointer = Object.getPrototypeOf(pointer); + } + + for (var i = 0; i < props.length; i++) { + this.spyOn(obj, props[i]); } return obj;