diff --git a/lib/jasmine-core/boot.js b/lib/jasmine-core/boot.js index 72f45b5e..4655042c 100644 --- a/lib/jasmine-core/boot.js +++ b/lib/jasmine-core/boot.js @@ -1,5 +1,5 @@ /* -Copyright (c) 2008-2017 Pivotal Labs +Copyright (c) 2008-2018 Pivotal Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/lib/jasmine-core/jasmine-html.js b/lib/jasmine-core/jasmine-html.js index 50de0be7..f682bbdb 100644 --- a/lib/jasmine-core/jasmine-html.js +++ b/lib/jasmine-core/jasmine-html.js @@ -1,5 +1,5 @@ /* -Copyright (c) 2008-2017 Pivotal Labs +Copyright (c) 2008-2018 Pivotal Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index 9b7290fa..26de0a3f 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -4916,6 +4916,19 @@ getJasmineRequireObj().interface = function(jasmine, env) { return env.createSpyObj(baseName, methodNames); }; + /** + * Add a custom 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.addSpyStrategy + * @function + * @param {String} name - The name of the strategy (i.e. what you call from `and`) + * @param {Function} factory - Factory function that returns the plan to be executed. + */ + jasmine.addSpyStrategy = function(name, factory) { + return env.addSpyStrategy(identifier, factory); + }; + return jasmineInterface; }; @@ -5091,18 +5104,18 @@ getJasmineRequireObj().SpyFactory = function(j$) { this.createSpy = function(name, originalFn) { return j$.Spy(name, originalFn, getCustomStrategies()); }; - + this.createSpyObj = function(baseName, methodNames) { var baseNameIsCollection = j$.isObject_(baseName) || j$.isArray_(baseName); - + if (baseNameIsCollection && j$.util.isUndefined(methodNames)) { methodNames = baseName; baseName = 'unknown'; } - + var obj = {}; var spiesWereSet = false; - + if (j$.isArray_(methodNames)) { for (var i = 0; i < methodNames.length; i++) { obj[methodNames[i]] = self.createSpy(baseName + '.' + methodNames[i]); @@ -5117,11 +5130,11 @@ getJasmineRequireObj().SpyFactory = function(j$) { } } } - + if (!spiesWereSet) { throw 'createSpyObj requires a non-empty array or object of method names to create spies for'; } - + return obj; }; } @@ -5284,20 +5297,24 @@ getJasmineRequireObj().SpyStrategy = function(j$) { var k, cs = options.customStrategies || {}; for (k in cs) { if (j$.util.has(cs, k) && !this[k]) { - this[k] = function() { - var plan = cs[k].apply(null, arguments); - - if (!j$.isFunction_(plan)) { - throw new Error('Spy strategy must return a function'); - } - - this.plan = plan; - return this.getSpy(); - }; + this[k] = createCustomPlan(cs[k]); } } } + function createCustomPlan(factory) { + return function() { + var plan = factory.apply(null, arguments); + + if (!j$.isFunction_(plan)) { + throw new Error('Spy strategy must return a function'); + } + + this.plan = plan; + return this.getSpy(); + }; + } + /** * Execute the current spy strategy. * @name SpyStrategy#exec diff --git a/lib/jasmine-core/node_boot.js b/lib/jasmine-core/node_boot.js index ce90a3ba..2b2d7adf 100644 --- a/lib/jasmine-core/node_boot.js +++ b/lib/jasmine-core/node_boot.js @@ -1,5 +1,5 @@ /* -Copyright (c) 2008-2017 Pivotal Labs +Copyright (c) 2008-2018 Pivotal Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/spec/core/integration/CustomSpyStrategiesSpec.js b/spec/core/integration/CustomSpyStrategiesSpec.js index 59e3b380..ba82362b 100644 --- a/spec/core/integration/CustomSpyStrategiesSpec.js +++ b/spec/core/integration/CustomSpyStrategiesSpec.js @@ -95,4 +95,44 @@ describe('Custom Spy Strategies (Integration)', function() { env.addReporter({ jasmineDone: jasmineDone }); env.execute(); }); + + it('allows multiple custom strategies to be used', function(done) { + var plan1 = jasmine.createSpy('plan 1').and.returnValue(42), + strategy1 = jasmine.createSpy('strat 1').and.returnValue(plan1), + plan2 = jasmine.createSpy('plan 2').and.returnValue(24), + strategy2 = jasmine.createSpy('strat 2').and.returnValue(plan2), + specDone = jasmine.createSpy('specDone'); + + env.beforeEach(function() { + env.addSpyStrategy('frobnicate', strategy1); + env.addSpyStrategy('jiggle', strategy2); + }); + + env.it('frobnicates', function() { + plan1.calls.reset(); + plan2.calls.reset(); + var spy = env.createSpy('spy').and.frobnicate(); + expect(spy()).toEqual(42); + expect(plan1).toHaveBeenCalled(); + expect(plan2).not.toHaveBeenCalled(); + }); + + env.it('jiggles', function() { + plan1.calls.reset(); + plan2.calls.reset(); + var spy = env.createSpy('spy').and.jiggle(); + expect(spy()).toEqual(24); + expect(plan1).not.toHaveBeenCalled(); + expect(plan2).toHaveBeenCalled(); + }); + + function jasmineDone(result) { + expect(result.overallStatus).toEqual('passed'); + expect(specDone.calls.count()).toBe(2); + done(); + } + + env.addReporter({ jasmineDone: jasmineDone, specDone: specDone }); + env.execute(); + }); }); diff --git a/src/core/SpyFactory.js b/src/core/SpyFactory.js index 5d54f9df..d4821321 100644 --- a/src/core/SpyFactory.js +++ b/src/core/SpyFactory.js @@ -6,18 +6,18 @@ getJasmineRequireObj().SpyFactory = function(j$) { this.createSpy = function(name, originalFn) { return j$.Spy(name, originalFn, getCustomStrategies()); }; - + this.createSpyObj = function(baseName, methodNames) { var baseNameIsCollection = j$.isObject_(baseName) || j$.isArray_(baseName); - + if (baseNameIsCollection && j$.util.isUndefined(methodNames)) { methodNames = baseName; baseName = 'unknown'; } - + var obj = {}; var spiesWereSet = false; - + if (j$.isArray_(methodNames)) { for (var i = 0; i < methodNames.length; i++) { obj[methodNames[i]] = self.createSpy(baseName + '.' + methodNames[i]); @@ -32,11 +32,11 @@ getJasmineRequireObj().SpyFactory = function(j$) { } } } - + if (!spiesWereSet) { throw 'createSpyObj requires a non-empty array or object of method names to create spies for'; } - + return obj; }; } diff --git a/src/core/SpyStrategy.js b/src/core/SpyStrategy.js index 6563beb7..ae5d4451 100644 --- a/src/core/SpyStrategy.js +++ b/src/core/SpyStrategy.js @@ -20,20 +20,24 @@ getJasmineRequireObj().SpyStrategy = function(j$) { var k, cs = options.customStrategies || {}; for (k in cs) { if (j$.util.has(cs, k) && !this[k]) { - this[k] = function() { - var plan = cs[k].apply(null, arguments); - - if (!j$.isFunction_(plan)) { - throw new Error('Spy strategy must return a function'); - } - - this.plan = plan; - return this.getSpy(); - }; + this[k] = createCustomPlan(cs[k]); } } } + function createCustomPlan(factory) { + return function() { + var plan = factory.apply(null, arguments); + + if (!j$.isFunction_(plan)) { + throw new Error('Spy strategy must return a function'); + } + + this.plan = plan; + return this.getSpy(); + }; + } + /** * Execute the current spy strategy. * @name SpyStrategy#exec diff --git a/src/core/requireInterface.js b/src/core/requireInterface.js index ac0279d7..52505ecd 100644 --- a/src/core/requireInterface.js +++ b/src/core/requireInterface.js @@ -280,5 +280,18 @@ getJasmineRequireObj().interface = function(jasmine, env) { return env.createSpyObj(baseName, methodNames); }; + /** + * Add a custom 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.addSpyStrategy + * @function + * @param {String} name - The name of the strategy (i.e. what you call from `and`) + * @param {Function} factory - Factory function that returns the plan to be executed. + */ + jasmine.addSpyStrategy = function(name, factory) { + return env.addSpyStrategy(identifier, factory); + }; + return jasmineInterface; };