diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index e79fe8f2..b7188584 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -1490,12 +1490,18 @@ getJasmineRequireObj().Env = function(j$) { }; var makeMatchersUtil = function() { - var customEqualityTesters = - runnableResources[currentRunnable().id].customEqualityTesters; - return new j$.MatchersUtil({ - customTesters: customEqualityTesters, - pp: makePrettyPrinter() - }); + const cr = currentRunnable(); + + if (cr) { + const customEqualityTesters = + runnableResources[cr.id].customEqualityTesters; + return new j$.MatchersUtil({ + customTesters: customEqualityTesters, + pp: makePrettyPrinter() + }); + } else { + return new j$.MatchersUtil({ pp: j$.basicPrettyPrinter_ }); + } }; var expectationFactory = function(actual, spec) { @@ -2026,7 +2032,8 @@ getJasmineRequireObj().Env = function(j$) { } return undefined; - } + }, + makeMatchersUtil ); var spyRegistry = new j$.SpyRegistry({ @@ -8647,11 +8654,6 @@ getJasmineRequireObj().Spy = function(j$) { }; })(); - var matchersUtil = new j$.MatchersUtil({ - customTesters: [], - pp: j$.makePrettyPrinter() - }); - /** * @classdesc _Note:_ Do not construct this directly. Use {@link spyOn}, * {@link spyOnProperty}, {@link jasmine.createSpy}, or @@ -8659,19 +8661,24 @@ getJasmineRequireObj().Spy = function(j$) { * @class Spy * @hideconstructor */ - function Spy(name, originalFn, customStrategies, defaultStrategyFn) { + function Spy(name, matchersUtil, optionals) { + const { originalFn, customStrategies, defaultStrategyFn } = optionals || {}; + var numArgs = typeof originalFn === 'function' ? originalFn.length : 0, wrapper = makeFunc(numArgs, function(context, args, invokeNew) { return spy(context, args, invokeNew); }), - strategyDispatcher = new SpyStrategyDispatcher({ - name: name, - fn: originalFn, - getSpy: function() { - return wrapper; + strategyDispatcher = new SpyStrategyDispatcher( + { + name: name, + fn: originalFn, + getSpy: function() { + return wrapper; + }, + customStrategies: customStrategies }, - customStrategies: customStrategies - }), + matchersUtil + ), callTracker = new j$.CallTracker(), spy = function(context, args, invokeNew) { /** @@ -8783,11 +8790,11 @@ getJasmineRequireObj().Spy = function(j$) { return wrapper; } - function SpyStrategyDispatcher(strategyArgs) { + function SpyStrategyDispatcher(strategyArgs, matchersUtil) { var baseStrategy = new j$.SpyStrategy(strategyArgs); var argsStrategies = new StrategyDict(function() { return new j$.SpyStrategy(strategyArgs); - }); + }, matchersUtil); this.and = baseStrategy; @@ -8816,9 +8823,10 @@ getJasmineRequireObj().Spy = function(j$) { }; } - function StrategyDict(strategyFactory) { + function StrategyDict(strategyFactory, matchersUtil) { this.strategies = []; this.strategyFactory = strategyFactory; + this.matchersUtil = matchersUtil; } StrategyDict.prototype.any = function() { @@ -8843,7 +8851,7 @@ getJasmineRequireObj().Spy = function(j$) { var i; for (i = 0; i < this.strategies.length; i++) { - if (matchersUtil.equals(args, this.strategies[i].args)) { + if (this.matchersUtil.equals(args, this.strategies[i].args)) { return this.strategies[i].strategy; } } @@ -8853,16 +8861,19 @@ getJasmineRequireObj().Spy = function(j$) { }; getJasmineRequireObj().SpyFactory = function(j$) { - function SpyFactory(getCustomStrategies, getDefaultStrategyFn) { + function SpyFactory( + getCustomStrategies, + getDefaultStrategyFn, + getMatchersUtil + ) { var self = this; this.createSpy = function(name, originalFn) { - return j$.Spy( - name, + return j$.Spy(name, getMatchersUtil(), { originalFn, - getCustomStrategies(), - getDefaultStrategyFn() - ); + customStrategies: getCustomStrategies(), + defaultStrategyFn: getDefaultStrategyFn() + }); }; this.createSpyObj = function(baseName, methodNames, propertyNames) { diff --git a/spec/core/SpySpec.js b/spec/core/SpySpec.js index 6271c900..bfc08e98 100644 --- a/spec/core/SpySpec.js +++ b/spec/core/SpySpec.js @@ -231,7 +231,7 @@ describe('Spies', function() { expect(spy('baz', 'grault', 'waldo')).toEqual(42); }); - it('uses custom equality testers when selecting a strategy', function() { + it('uses asymmetric equality testers when selecting a strategy', function() { var spy = env.createSpy('foo'); spy.and.returnValue(42); spy.withArgs(jasmineUnderTest.any(String)).and.returnValue(-1); @@ -240,6 +240,23 @@ describe('Spies', function() { expect(spy({})).toEqual(42); }); + it('uses the provided matchersUtil selecting a strategy', function() { + const matchersUtil = new jasmineUnderTest.MatchersUtil({ + customTesters: [ + function(a, b) { + if ((a === 'bar' && b === 'baz') || (a === 'baz' && b === 'bar')) { + return true; + } + } + ] + }); + const spy = new jasmineUnderTest.Spy('aSpy', matchersUtil); + spy.and.returnValue('default strategy return value'); + spy.withArgs('bar').and.returnValue('custom strategy return value'); + expect(spy('foo')).toEqual('default strategy return value'); + expect(spy('baz')).toEqual('custom strategy return value'); + }); + it('can reconfigure an argument-specific strategy', function() { var spy = env.createSpy('foo'); spy.withArgs('foo').and.returnValue(42); diff --git a/spec/core/integration/EnvSpec.js b/spec/core/integration/EnvSpec.js index 462893d9..2d0b0072 100644 --- a/spec/core/integration/EnvSpec.js +++ b/spec/core/integration/EnvSpec.js @@ -3413,4 +3413,56 @@ describe('Env integration', function() { done(); }); }); + + it('uses custom equality testers in Spy#withArgs', async function() { + env.it('a spec', function() { + const createSpySpy = env.createSpy('via createSpy'); + const spiedOn = { foo: function() {} }; + env.spyOn(spiedOn, 'foo'); + const spyObj = env.createSpyObj('spyObj', ['foo']); + const spiedOnAllFuncs = { foo: function() {} }; + env.spyOnAllFunctions(spiedOnAllFuncs); + + for (spy of [ + createSpySpy, + spiedOn.foo, + spyObj.foo, + spiedOnAllFuncs.foo + ]) { + spy.and.returnValue('default strategy'); + spy.withArgs(42).and.returnValue('custom strategy'); + } + + env.addCustomEqualityTester(function(a, b) { + if ((a === 'x' && b === 42) || (a === 42 && b === 'x')) { + return true; + } + }); + + env + .expect(createSpySpy('x')) + .withContext('createSpy') + .toEqual('custom strategy'); + env + .expect(spiedOn.foo('x')) + .withContext('spyOn') + .toEqual('custom strategy'); + env + .expect(spyObj.foo('x')) + .withContext('createSpyObj') + .toEqual('custom strategy'); + env + .expect(spiedOnAllFuncs.foo('x')) + .withContext('spyOnAllFunctions') + .toEqual('custom strategy'); + }); + + let failedExpectations; + env.addReporter({ + specDone: r => (failedExpectations = r.failedExpectations) + }); + + await env.execute(); + expect(failedExpectations).toEqual([]); + }); }); diff --git a/src/core/Env.js b/src/core/Env.js index 4ee1eae1..ddd9d4ee 100644 --- a/src/core/Env.js +++ b/src/core/Env.js @@ -327,12 +327,18 @@ getJasmineRequireObj().Env = function(j$) { }; var makeMatchersUtil = function() { - var customEqualityTesters = - runnableResources[currentRunnable().id].customEqualityTesters; - return new j$.MatchersUtil({ - customTesters: customEqualityTesters, - pp: makePrettyPrinter() - }); + const cr = currentRunnable(); + + if (cr) { + const customEqualityTesters = + runnableResources[cr.id].customEqualityTesters; + return new j$.MatchersUtil({ + customTesters: customEqualityTesters, + pp: makePrettyPrinter() + }); + } else { + return new j$.MatchersUtil({ pp: j$.basicPrettyPrinter_ }); + } }; var expectationFactory = function(actual, spec) { @@ -863,7 +869,8 @@ getJasmineRequireObj().Env = function(j$) { } return undefined; - } + }, + makeMatchersUtil ); var spyRegistry = new j$.SpyRegistry({ diff --git a/src/core/Spy.js b/src/core/Spy.js index 7b7e422b..26d4b6b4 100644 --- a/src/core/Spy.js +++ b/src/core/Spy.js @@ -7,11 +7,6 @@ getJasmineRequireObj().Spy = function(j$) { }; })(); - var matchersUtil = new j$.MatchersUtil({ - customTesters: [], - pp: j$.makePrettyPrinter() - }); - /** * @classdesc _Note:_ Do not construct this directly. Use {@link spyOn}, * {@link spyOnProperty}, {@link jasmine.createSpy}, or @@ -19,19 +14,24 @@ getJasmineRequireObj().Spy = function(j$) { * @class Spy * @hideconstructor */ - function Spy(name, originalFn, customStrategies, defaultStrategyFn) { + function Spy(name, matchersUtil, optionals) { + const { originalFn, customStrategies, defaultStrategyFn } = optionals || {}; + var numArgs = typeof originalFn === 'function' ? originalFn.length : 0, wrapper = makeFunc(numArgs, function(context, args, invokeNew) { return spy(context, args, invokeNew); }), - strategyDispatcher = new SpyStrategyDispatcher({ - name: name, - fn: originalFn, - getSpy: function() { - return wrapper; + strategyDispatcher = new SpyStrategyDispatcher( + { + name: name, + fn: originalFn, + getSpy: function() { + return wrapper; + }, + customStrategies: customStrategies }, - customStrategies: customStrategies - }), + matchersUtil + ), callTracker = new j$.CallTracker(), spy = function(context, args, invokeNew) { /** @@ -143,11 +143,11 @@ getJasmineRequireObj().Spy = function(j$) { return wrapper; } - function SpyStrategyDispatcher(strategyArgs) { + function SpyStrategyDispatcher(strategyArgs, matchersUtil) { var baseStrategy = new j$.SpyStrategy(strategyArgs); var argsStrategies = new StrategyDict(function() { return new j$.SpyStrategy(strategyArgs); - }); + }, matchersUtil); this.and = baseStrategy; @@ -176,9 +176,10 @@ getJasmineRequireObj().Spy = function(j$) { }; } - function StrategyDict(strategyFactory) { + function StrategyDict(strategyFactory, matchersUtil) { this.strategies = []; this.strategyFactory = strategyFactory; + this.matchersUtil = matchersUtil; } StrategyDict.prototype.any = function() { @@ -203,7 +204,7 @@ getJasmineRequireObj().Spy = function(j$) { var i; for (i = 0; i < this.strategies.length; i++) { - if (matchersUtil.equals(args, this.strategies[i].args)) { + if (this.matchersUtil.equals(args, this.strategies[i].args)) { return this.strategies[i].strategy; } } diff --git a/src/core/SpyFactory.js b/src/core/SpyFactory.js index 808ec647..03bd6035 100644 --- a/src/core/SpyFactory.js +++ b/src/core/SpyFactory.js @@ -1,14 +1,17 @@ getJasmineRequireObj().SpyFactory = function(j$) { - function SpyFactory(getCustomStrategies, getDefaultStrategyFn) { + function SpyFactory( + getCustomStrategies, + getDefaultStrategyFn, + getMatchersUtil + ) { var self = this; this.createSpy = function(name, originalFn) { - return j$.Spy( - name, + return j$.Spy(name, getMatchersUtil(), { originalFn, - getCustomStrategies(), - getDefaultStrategyFn() - ); + customStrategies: getCustomStrategies(), + defaultStrategyFn: getDefaultStrategyFn() + }); }; this.createSpyObj = function(baseName, methodNames, propertyNames) {