diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index eb9b54dc..4ce3c980 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -1128,6 +1128,17 @@ getJasmineRequireObj().Env = function(j$) { } }); + this.setDefaultSpyStrategy = function(defaultStrategyFn) { + if (!currentRunnable()) { + throw new Error( + 'Default spy strategy must be set in a before function or a spec' + ); + } + runnableResources[ + currentRunnable().id + ].defaultStrategyFn = defaultStrategyFn; + }; + this.addSpyStrategy = function(name, fn) { if (!currentRunnable()) { throw new Error( @@ -1206,7 +1217,8 @@ getJasmineRequireObj().Env = function(j$) { spies: [], customEqualityTesters: [], customMatchers: {}, - customSpyStrategies: {} + customSpyStrategies: {}, + defaultStrategyFn: undefined }; if (runnableResources[parentRunnableId]) { @@ -1216,6 +1228,8 @@ getJasmineRequireObj().Env = function(j$) { resources.customMatchers = j$.util.clone( runnableResources[parentRunnableId].customMatchers ); + resources.defaultStrategyFn = + runnableResources[parentRunnableId].defaultStrategyFn; } runnableResources[id] = resources; @@ -1653,6 +1667,15 @@ getJasmineRequireObj().Env = function(j$) { return {}; }, + function getDefaultStrategyFn() { + var runnable = currentRunnable(); + + if (runnable) { + return runnableResources[runnable.id].defaultStrategyFn; + } + + return undefined; + }, function getPromise() { return customPromise || global.Promise; } @@ -6760,6 +6783,22 @@ getJasmineRequireObj().interface = function(jasmine, env) { return env.addSpyStrategy(name, factory); }; + /** + * Set the default spy strategy for the current scope of specs. + * + * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}. + * @name jasmine.setDefaultSpyStrategy + * @function + * @param {Function} defaultStrategyFn - a function that assigns a strategy + * @example + * beforeEach(function() { + * jasmine.setDefaultSpyStrategy(and => and.returnValue(true)); + * }); + */ + jasmine.setDefaultSpyStrategy = function(defaultStrategyFn) { + return env.setDefaultSpyStrategy(defaultStrategyFn); + }; + return jasmineInterface; }; @@ -6777,7 +6816,13 @@ getJasmineRequireObj().Spy = function(j$) { * @constructor * @name Spy */ - function Spy(name, originalFn, customStrategies, getPromise) { + function Spy( + name, + originalFn, + customStrategies, + defaultStrategyFn, + getPromise + ) { var numArgs = typeof originalFn === 'function' ? originalFn.length : 0, wrapper = makeFunc(numArgs, function() { return spy.apply(this, Array.prototype.slice.call(arguments)); @@ -6894,6 +6939,10 @@ getJasmineRequireObj().Spy = function(j$) { }; wrapper.calls = callTracker; + if (defaultStrategyFn) { + defaultStrategyFn(wrapper.and); + } + return wrapper; } @@ -6967,11 +7016,17 @@ getJasmineRequireObj().Spy = function(j$) { }; getJasmineRequireObj().SpyFactory = function(j$) { - function SpyFactory(getCustomStrategies, getPromise) { + function SpyFactory(getCustomStrategies, getDefaultStrategyFn, getPromise) { var self = this; this.createSpy = function(name, originalFn) { - return j$.Spy(name, originalFn, getCustomStrategies(), getPromise); + return j$.Spy( + name, + originalFn, + getCustomStrategies(), + getDefaultStrategyFn(), + getPromise + ); }; this.createSpyObj = function(baseName, methodNames, propertyNames) { diff --git a/spec/core/integration/DefaultSpyStrategySpec.js b/spec/core/integration/DefaultSpyStrategySpec.js new file mode 100644 index 00000000..0b02c572 --- /dev/null +++ b/spec/core/integration/DefaultSpyStrategySpec.js @@ -0,0 +1,68 @@ +describe('Default Spy Strategy (Integration)', function() { + var env; + + beforeEach(function() { + env = new jasmineUnderTest.Env(); + env.configure({random: false}); + }); + + it('allows defining a default spy strategy', function(done) { + env.describe('suite with default strategy', function() { + env.beforeEach(function() { + env.setDefaultSpyStrategy(function (and) { + and.returnValue(42); + }); + }); + + env.it('spec in suite', function() { + var spy = env.createSpy('something'); + expect(spy()).toBe(42); + }); + }); + + env.it('spec not in suite', function() { + var spy = env.createSpy('something'); + expect(spy()).toBeUndefined(); + }); + + function jasmineDone(result) { + expect(result.overallStatus).toEqual('passed'); + done(); + } + + env.addReporter({ jasmineDone: jasmineDone }); + env.execute(); + }); + + it('uses the default spy strategy defined when the spy is created', function (done) { + env.it('spec', function() { + var a = env.createSpy('a'); + env.setDefaultSpyStrategy(function (and) { and.returnValue(42); }); + var b = env.createSpy('b'); + env.setDefaultSpyStrategy(function (and) { and.stub(); }); + var c = env.createSpy('c'); + env.setDefaultSpyStrategy(); + var d = env.createSpy('d'); + + expect(a()).toBeUndefined(); + expect(b()).toBe(42); + expect(c()).toBeUndefined(); + expect(d()).toBeUndefined(); + + // Check our assumptions about which spies are "configured" (this matters because + // spies that use withArgs() behave differently if they are not configured). + expect(a.and.isConfigured()).toBe(false); + expect(b.and.isConfigured()).toBe(true); + expect(c.and.isConfigured()).toBe(true); + expect(d.and.isConfigured()).toBe(false); + }); + + function jasmineDone(result) { + expect(result.overallStatus).toEqual('passed'); + done(); + } + + env.addReporter({ jasmineDone: jasmineDone }); + env.execute(); + }); +}); diff --git a/src/core/Env.js b/src/core/Env.js index d769d1c4..6e4e79e7 100644 --- a/src/core/Env.js +++ b/src/core/Env.js @@ -222,6 +222,17 @@ getJasmineRequireObj().Env = function(j$) { } }); + this.setDefaultSpyStrategy = function(defaultStrategyFn) { + if (!currentRunnable()) { + throw new Error( + 'Default spy strategy must be set in a before function or a spec' + ); + } + runnableResources[ + currentRunnable().id + ].defaultStrategyFn = defaultStrategyFn; + }; + this.addSpyStrategy = function(name, fn) { if (!currentRunnable()) { throw new Error( @@ -300,7 +311,8 @@ getJasmineRequireObj().Env = function(j$) { spies: [], customEqualityTesters: [], customMatchers: {}, - customSpyStrategies: {} + customSpyStrategies: {}, + defaultStrategyFn: undefined }; if (runnableResources[parentRunnableId]) { @@ -310,6 +322,8 @@ getJasmineRequireObj().Env = function(j$) { resources.customMatchers = j$.util.clone( runnableResources[parentRunnableId].customMatchers ); + resources.defaultStrategyFn = + runnableResources[parentRunnableId].defaultStrategyFn; } runnableResources[id] = resources; @@ -747,6 +761,15 @@ getJasmineRequireObj().Env = function(j$) { return {}; }, + function getDefaultStrategyFn() { + var runnable = currentRunnable(); + + if (runnable) { + return runnableResources[runnable.id].defaultStrategyFn; + } + + return undefined; + }, function getPromise() { return customPromise || global.Promise; } diff --git a/src/core/Spy.js b/src/core/Spy.js index c3a0f3fb..ffbb0103 100644 --- a/src/core/Spy.js +++ b/src/core/Spy.js @@ -12,7 +12,13 @@ getJasmineRequireObj().Spy = function(j$) { * @constructor * @name Spy */ - function Spy(name, originalFn, customStrategies, getPromise) { + function Spy( + name, + originalFn, + customStrategies, + defaultStrategyFn, + getPromise + ) { var numArgs = typeof originalFn === 'function' ? originalFn.length : 0, wrapper = makeFunc(numArgs, function() { return spy.apply(this, Array.prototype.slice.call(arguments)); @@ -129,6 +135,10 @@ getJasmineRequireObj().Spy = function(j$) { }; wrapper.calls = callTracker; + if (defaultStrategyFn) { + defaultStrategyFn(wrapper.and); + } + return wrapper; } diff --git a/src/core/SpyFactory.js b/src/core/SpyFactory.js index d9cdc228..80c6b954 100644 --- a/src/core/SpyFactory.js +++ b/src/core/SpyFactory.js @@ -1,9 +1,15 @@ getJasmineRequireObj().SpyFactory = function(j$) { - function SpyFactory(getCustomStrategies, getPromise) { + function SpyFactory(getCustomStrategies, getDefaultStrategyFn, getPromise) { var self = this; this.createSpy = function(name, originalFn) { - return j$.Spy(name, originalFn, getCustomStrategies(), getPromise); + return j$.Spy( + name, + originalFn, + getCustomStrategies(), + getDefaultStrategyFn(), + getPromise + ); }; this.createSpyObj = function(baseName, methodNames, propertyNames) { diff --git a/src/core/requireInterface.js b/src/core/requireInterface.js index cba23c3d..3738954a 100644 --- a/src/core/requireInterface.js +++ b/src/core/requireInterface.js @@ -354,5 +354,21 @@ getJasmineRequireObj().interface = function(jasmine, env) { return env.addSpyStrategy(name, factory); }; + /** + * Set the default spy strategy for the current scope of specs. + * + * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}. + * @name jasmine.setDefaultSpyStrategy + * @function + * @param {Function} defaultStrategyFn - a function that assigns a strategy + * @example + * beforeEach(function() { + * jasmine.setDefaultSpyStrategy(and => and.returnValue(true)); + * }); + */ + jasmine.setDefaultSpyStrategy = function(defaultStrategyFn) { + return env.setDefaultSpyStrategy(defaultStrategyFn); + }; + return jasmineInterface; };