From 752a36d3ff67013feeb504a1176ab4dc865e0118 Mon Sep 17 00:00:00 2001 From: "Christopher Amavisca, Greg Cobb and Sheel Choksi" Date: Wed, 5 Mar 2014 10:28:37 -0800 Subject: [PATCH] Manage spys/matchers/custom equalities for beforeAll - Refactor expectations to take list of matchers - Add spyRegistry to manage runnables' spies - Add clone util [#66789174] --- spec/core/EnvSpec.js | 63 ------ spec/core/ExpectationSpec.js | 68 ++---- spec/core/PrettyPrintSpec.js | 5 +- spec/core/SpyRegistrySpec.js | 55 +++++ spec/core/integration/CustomMatchersSpec.js | 66 +++--- spec/core/integration/EnvSpec.js | 234 ++++++++++++++++---- spec/core/integration/SpecRunningSpec.js | 9 +- src/core/Env.js | 131 +++++------ src/core/Expectation.js | 20 +- src/core/SpyRegistry.js | 46 ++++ src/core/requireCore.js | 1 + src/core/util.js | 15 ++ 12 files changed, 441 insertions(+), 272 deletions(-) create mode 100644 spec/core/SpyRegistrySpec.js create mode 100644 src/core/SpyRegistry.js diff --git a/spec/core/EnvSpec.js b/spec/core/EnvSpec.js index 86898f56..f8867885 100644 --- a/spec/core/EnvSpec.js +++ b/spec/core/EnvSpec.js @@ -5,68 +5,6 @@ describe("Env", function() { env = new j$.Env(); }); - it('removes all spies when env is executed', function(done) { - var originalFoo = function() {}, - testObj = { - foo: originalFoo - }, - firstSpec = jasmine.createSpy('firstSpec').and.callFake(function() { - env.spyOn(testObj, 'foo'); - }), - secondSpec = jasmine.createSpy('secondSpec').and.callFake(function() { - expect(testObj.foo).toBe(originalFoo); - }); - env.describe('test suite', function() { - env.it('spec 0', firstSpec); - env.it('spec 1', secondSpec); - }); - - var assertions = function() { - expect(firstSpec).toHaveBeenCalled(); - expect(secondSpec).toHaveBeenCalled(); - done(); - }; - - env.addReporter({ jasmineDone: assertions }); - - env.execute(); - }); - - describe("#spyOn", function() { - it("checks for the existence of the object", function() { - expect(function() { - env.spyOn(void 0, 'pants'); - }).toThrowError(/could not find an object/); - }); - - it("checks for the existence of the method", function() { - var subject = {}; - - expect(function() { - env.spyOn(subject, 'pants'); - }).toThrowError(/method does not exist/); - }); - - it("checks if it has already been spied upon", function() { - var subject = { spiedFunc: function() {} }; - - env.spyOn(subject, 'spiedFunc'); - - expect(function() { - env.spyOn(subject, 'spiedFunc'); - }).toThrowError(/has already been spied upon/); - }); - - it("overrides the method on the object and returns the spy", function() { - var originalFunctionWasCalled = false; - var subject = { spiedFunc: function() { originalFunctionWasCalled = true; } }; - - var spy = env.spyOn(subject, 'spiedFunc'); - - expect(subject.spiedFunc).toEqual(spy); - }); - }); - describe("#pending", function() { it("throws the Pending Spec exception", function() { expect(function() { @@ -82,4 +20,3 @@ describe("Env", function() { }); }); }); - diff --git a/spec/core/ExpectationSpec.js b/spec/core/ExpectationSpec.js index c5528aa1..b253672b 100644 --- a/spec/core/ExpectationSpec.js +++ b/spec/core/ExpectationSpec.js @@ -1,14 +1,14 @@ describe("Expectation", function() { - it(".addMatchers makes matchers available to any expectation", function() { + it("makes custom matchers available to this expectation", function() { var matchers = { toFoo: function() {}, toBar: function() {} }, expectation; - j$.Expectation.addMatchers(matchers); - - expectation = new j$.Expectation({}); + expectation = new j$.Expectation({ + customMatchers: matchers + }); expect(expectation.toFoo).toBeDefined(); expect(expectation.toBar).toBeDefined(); @@ -27,25 +27,6 @@ describe("Expectation", function() { expect(expectation.toQuux).toBeDefined(); }); - it(".resetMatchers should keep only core matchers", function() { - var matchers = { - toFoo: function() {} - }, - coreMatchers = { - toQuux: function() {} - }, - expectation; - - j$.Expectation.addCoreMatchers(coreMatchers); - j$.Expectation.addMatchers(matchers); - j$.Expectation.resetMatchers(); - - expectation = new j$.Expectation({}); - - expect(expectation.toQuux).toBeDefined(); - expect(expectation.toFoo).toBeUndefined(); - }); - it("Factory builds an expectation/negative expectation", function() { var builtExpectation = j$.Expectation.Factory(); @@ -65,10 +46,9 @@ describe("Expectation", function() { addExpectationResult = jasmine.createSpy("addExpectationResult"), expectation; - j$.Expectation.addMatchers(matchers); - expectation = new j$.Expectation({ util: util, + customMatchers: matchers, customEqualityTesters: customEqualityTesters, actual: "an actual", addExpectationResult: addExpectationResult @@ -94,10 +74,9 @@ describe("Expectation", function() { addExpectationResult = jasmine.createSpy("addExpectationResult"), expectation; - j$.Expectation.addMatchers(matchers); - expectation = new j$.Expectation({ util: util, + customMatchers: matchers, actual: "an actual", addExpectationResult: addExpectationResult }); @@ -121,10 +100,8 @@ describe("Expectation", function() { addExpectationResult = jasmine.createSpy("addExpectationResult"), expectation; - j$.Expectation.addMatchers(matchers); - expectation = new j$.Expectation({ - matchers: matchers, + customMatchers: matchers, util: util, actual: "an actual", addExpectationResult: addExpectationResult @@ -155,10 +132,8 @@ describe("Expectation", function() { addExpectationResult = jasmine.createSpy("addExpectationResult"), expectation; - j$.Expectation.addMatchers(matchers); - expectation = new j$.Expectation({ - matchers: matchers, + customMatchers: matchers, util: util, actual: "an actual", addExpectationResult: addExpectationResult @@ -191,10 +166,9 @@ describe("Expectation", function() { addExpectationResult = jasmine.createSpy("addExpectationResult"), expectation; - j$.Expectation.addMatchers(matchers); - expectation = new j$.Expectation({ actual: "an actual", + customMatchers: matchers, addExpectationResult: addExpectationResult }); @@ -225,10 +199,8 @@ describe("Expectation", function() { addExpectationResult = jasmine.createSpy("addExpectationResult"), expectation; - j$.Expectation.addMatchers(matchers); - expectation = new j$.Expectation({ - matchers: matchers, + customMatchers: matchers, actual: "an actual", addExpectationResult: addExpectationResult }); @@ -259,10 +231,8 @@ describe("Expectation", function() { actual = "an actual", expectation; - j$.Expectation.addMatchers(matchers); - expectation = new j$.Expectation({ - matchers: matchers, + customMatchers: matchers, actual: "an actual", addExpectationResult: addExpectationResult, isNot: true @@ -294,10 +264,8 @@ describe("Expectation", function() { actual = "an actual", expectation; - j$.Expectation.addMatchers(matchers); - expectation = new j$.Expectation({ - matchers: matchers, + customMatchers: matchers, actual: "an actual", util: util, addExpectationResult: addExpectationResult, @@ -332,10 +300,8 @@ describe("Expectation", function() { actual = "an actual", expectation; - j$.Expectation.addMatchers(matchers); - expectation = new j$.Expectation({ - matchers: matchers, + customMatchers: matchers, actual: "an actual", addExpectationResult: addExpectationResult, isNot: true @@ -365,10 +331,8 @@ describe("Expectation", function() { actual = "an actual", expectation; - j$.Expectation.addMatchers(matchers); - expectation = new j$.Expectation({ - matchers: matchers, + customMatchers: matchers, actual: "an actual", addExpectationResult: addExpectationResult, isNot: true @@ -403,10 +367,8 @@ describe("Expectation", function() { actual = "an actual", expectation; - j$.Expectation.addMatchers(matchers); - expectation = new j$.Expectation({ - matchers: matchers, + customMatchers: matchers, actual: "an actual", addExpectationResult: addExpectationResult, isNot: true diff --git a/spec/core/PrettyPrintSpec.js b/spec/core/PrettyPrintSpec.js index 7b7c21ab..0f767c87 100644 --- a/spec/core/PrettyPrintSpec.js +++ b/spec/core/PrettyPrintSpec.js @@ -109,7 +109,9 @@ describe("j$.pp", function () { }, env = new j$.Env(); - env.spyOn(TestObject, 'someFunction'); + var spyRegistry = new j$.SpyRegistry({currentSpies: function() {return [];}}); + + spyRegistry.spyOn(TestObject, 'someFunction'); expect(j$.pp(TestObject.someFunction)).toEqual("spy on someFunction"); expect(j$.pp(j$.createSpy("something"))).toEqual("spy on something"); @@ -132,4 +134,3 @@ describe("j$.pp", function () { expect(j$.pp(obj)).toEqual("{ foo : 'bar' }"); }); }); - diff --git a/spec/core/SpyRegistrySpec.js b/spec/core/SpyRegistrySpec.js new file mode 100644 index 00000000..36dcf68c --- /dev/null +++ b/spec/core/SpyRegistrySpec.js @@ -0,0 +1,55 @@ +describe("SpyRegistry", function() { + describe("#spyOn", function() { + it("checks for the existence of the object", function() { + var spyRegistry = new j$.SpyRegistry(); + expect(function() { + spyRegistry.spyOn(void 0, 'pants'); + }).toThrowError(/could not find an object/); + }); + + it("checks for the existence of the method", function() { + var spyRegistry = new j$.SpyRegistry(), + subject = {}; + + expect(function() { + spyRegistry.spyOn(subject, 'pants'); + }).toThrowError(/method does not exist/); + }); + + it("checks if it has already been spied upon", function() { + var spies = [], + spyRegistry = new j$.SpyRegistry({currentSpies: function() { return spies; }}), + subject = { spiedFunc: function() {} }; + + spyRegistry.spyOn(subject, 'spiedFunc'); + + expect(function() { + spyRegistry.spyOn(subject, 'spiedFunc'); + }).toThrowError(/has already been spied upon/); + }); + + it("overrides the method on the object and returns the spy", function() { + var originalFunctionWasCalled = false, + spyRegistry = new j$.SpyRegistry(), + subject = { spiedFunc: function() { originalFunctionWasCalled = true; } }; + + var spy = spyRegistry.spyOn(subject, 'spiedFunc'); + + expect(subject.spiedFunc).toEqual(spy); + }); + }); + + describe("#clearSpies", function() { + it("restores the original functions on the spied-upon objects", function() { + var spies = [], + spyRegistry = new j$.SpyRegistry({currentSpies: function() { return spies; }}), + originalFunction = function() {}, + subject = { spiedFunc: originalFunction }; + + spyRegistry.spyOn(subject, 'spiedFunc'); + spyRegistry.clearSpies(); + + expect(subject.spiedFunc).toBe(originalFunction); + }); + }); +}); diff --git a/spec/core/integration/CustomMatchersSpec.js b/spec/core/integration/CustomMatchersSpec.js index ee3e49a0..6c03d3d5 100644 --- a/spec/core/integration/CustomMatchersSpec.js +++ b/spec/core/integration/CustomMatchersSpec.js @@ -38,13 +38,13 @@ describe("Custom Matchers (Integration)", function() { }); it("passes the spec if the custom matcher passes", function(done) { - env.addMatchers({ - toBeReal: function() { - return { compare: function() { return { pass: true }; } }; - } - }); - env.it("spec using custom matcher", function() { + env.addMatchers({ + toBeReal: function() { + return { compare: function() { return { pass: true }; } }; + } + }); + env.expect(true).toBeReal(); }); @@ -57,16 +57,16 @@ describe("Custom Matchers (Integration)", function() { }); it("uses the negative compare function for a negative comparison, if provided", function(done) { - env.addMatchers({ - toBeReal: function() { - return { - compare: function() { return { pass: true }; }, - negativeCompare: function() { return { pass: true }; } - }; - } - }); - env.it("spec with custom negative comparison matcher", function() { + env.addMatchers({ + toBeReal: function() { + return { + compare: function() { return { pass: true }; }, + negativeCompare: function() { return { pass: true }; } + }; + } + }); + env.expect(true).not.toBeReal(); }); @@ -79,17 +79,17 @@ describe("Custom Matchers (Integration)", function() { }); it("generates messages with the same rules as built in matchers absent a custom message", function(done) { - env.addMatchers({ - toBeReal: function() { - return { - compare: function() { - return { pass: false }; + env.it('spec with an expectation', function() { + env.addMatchers({ + toBeReal: function() { + return { + compare: function() { + return { pass: false }; + } } } - } - }); + }); - env.it('spec with an expectation', function() { env.expect("a").toBeReal(); }); @@ -103,13 +103,14 @@ describe("Custom Matchers (Integration)", function() { it("passes the expected and actual arguments to the comparison function", function(done) { var argumentSpy = jasmine.createSpy("argument spy").and.returnValue({ pass: true }); - env.addMatchers({ - toBeReal: function() { - return { compare: argumentSpy }; - } - }); env.it('spec with an expectation', function () { + env.addMatchers({ + toBeReal: function() { + return { compare: argumentSpy }; + } + }); + env.expect(true).toBeReal(); env.expect(true).toBeReal("arg"); env.expect(true).toBeReal("arg1", "arg2"); @@ -130,12 +131,13 @@ describe("Custom Matchers (Integration)", function() { argumentSpy = jasmine.createSpy("argument spy").and.returnValue(matcherFactory), customEqualityFn = function() { return true; }; - env.addCustomEqualityTester(customEqualityFn); - env.addMatchers({ - toBeReal: argumentSpy - }); env.it("spec with expectation", function() { + env.addCustomEqualityTester(customEqualityFn); + env.addMatchers({ + toBeReal: argumentSpy + }); + env.expect(true).toBeReal(); }); diff --git a/spec/core/integration/EnvSpec.js b/spec/core/integration/EnvSpec.js index 08c06ee2..6715d26a 100644 --- a/spec/core/integration/EnvSpec.js +++ b/spec/core/integration/EnvSpec.js @@ -326,30 +326,93 @@ describe("Env integration", function() { env.execute([secondSuite.id, firstSpec.id]); }); - it("Functions can be spied on and have their calls tracked", function () { + it("Functions can be spied on and have their calls tracked", function (done) { var env = new j$.Env(); var originalFunctionWasCalled = false; var subject = { spiedFunc: function() { originalFunctionWasCalled = true; } }; - var spy = env.spyOn(subject, 'spiedFunc'); + env.addReporter({jasmineDone: done}); - expect(subject.spiedFunc).toEqual(spy); + env.it("works with spies", function() { + var spy = env.spyOn(subject, 'spiedFunc'); - expect(subject.spiedFunc.calls.any()).toEqual(false); - expect(subject.spiedFunc.calls.count()).toEqual(0); + expect(subject.spiedFunc).toEqual(spy); + expect(subject.spiedFunc.calls.any()).toEqual(false); + expect(subject.spiedFunc.calls.count()).toEqual(0); - subject.spiedFunc('foo'); + subject.spiedFunc('foo'); - expect(subject.spiedFunc.calls.any()).toEqual(true); - expect(subject.spiedFunc.calls.count()).toEqual(1); - expect(subject.spiedFunc.calls.mostRecent().args).toEqual(['foo']); - expect(subject.spiedFunc.calls.mostRecent().object).toEqual(subject); - expect(originalFunctionWasCalled).toEqual(false); + expect(subject.spiedFunc.calls.any()).toEqual(true); + expect(subject.spiedFunc.calls.count()).toEqual(1); + expect(subject.spiedFunc.calls.mostRecent().args).toEqual(['foo']); + expect(subject.spiedFunc.calls.mostRecent().object).toEqual(subject); + expect(originalFunctionWasCalled).toEqual(false); - subject.spiedFunc('bar'); - expect(subject.spiedFunc.calls.count()).toEqual(2); - expect(subject.spiedFunc.calls.mostRecent().args).toEqual(['bar']); + subject.spiedFunc('bar'); + expect(subject.spiedFunc.calls.count()).toEqual(2); + expect(subject.spiedFunc.calls.mostRecent().args).toEqual(['bar']); + }); + + env.execute(); + }); + + it('removes all spies added in a spec after the spec is complete', function(done) { + var env = new j$.Env(), + originalFoo = function() {}, + testObj = { + foo: originalFoo + }, + firstSpec = jasmine.createSpy('firstSpec').and.callFake(function() { + env.spyOn(testObj, 'foo'); + }), + secondSpec = jasmine.createSpy('secondSpec').and.callFake(function() { + expect(testObj.foo).toBe(originalFoo); + }); + env.describe('test suite', function() { + env.it('spec 0', firstSpec); + env.it('spec 1', secondSpec); + }); + + var assertions = function() { + expect(firstSpec).toHaveBeenCalled(); + expect(secondSpec).toHaveBeenCalled(); + done(); + }; + + env.addReporter({ jasmineDone: assertions }); + + env.execute(); + }); + + it('removes all spies added in a suite after the suite is complete', function(done) { + var env = new j$.Env(), + originalFoo = function() {}, + testObj = { + foo: originalFoo + }; + + env.describe('test suite', function() { + env.beforeAll(function() { env.spyOn(testObj, 'foo');}) + + env.it('spec 0', function() { + expect(j$.isSpy(testObj.foo)).toBe(true); + }); + + env.it('spec 1', function() { + expect(j$.isSpy(testObj.foo)).toBe(true); + }); + }); + + env.describe('another suite', function() { + env.it('spec 2', function() { + expect(j$.isSpy(testObj.foo)).toBe(false); + }); + }); + + env.addReporter({ jasmineDone: done }); + + env.execute(); }); it("Mock clock can be installed and used in tests", function(done) { @@ -542,11 +605,7 @@ describe("Env integration", function() { it("Custom equality testers should be per spec", function(done) { var env = new j$.Env({global: { setTimeout: setTimeout }}), reporter = jasmine.createSpyObj('fakeReporter', [ - "jasmineStarted", "jasmineDone", - "suiteStarted", - "suiteDone", - "specStarted", "specDone" ]); @@ -576,30 +635,42 @@ describe("Env integration", function() { env.execute(); }); - it("Custom matchers should be per spec", function() { + it("Custom equality testers should be per suite", function(done) { var env = new j$.Env({global: { setTimeout: setTimeout }}), - matchers = { - toFoo: function() {} - }, reporter = jasmine.createSpyObj('fakeReporter', [ - "jasmineStarted", "jasmineDone", - "suiteStarted", - "suiteDone", - "specStarted", "specDone" ]); + reporter.jasmineDone.and.callFake(function() { + var firstSpecResult = reporter.specDone.calls.first().args[0], + secondSpecResult = reporter.specDone.calls.argsFor(0)[0], + thirdSpecResult = reporter.specDone.calls.mostRecent().args[0]; + + expect(firstSpecResult.status).toEqual("passed"); + expect(secondSpecResult.status).toEqual("passed"); + expect(thirdSpecResult.status).toEqual("failed"); + + done(); + }); + env.addReporter(reporter); - env.describe("testing custom matchers", function() { - env.it("with a custom matcher", function() { - env.addMatchers(matchers); - expect(env.expect().toFoo).toBeDefined(); + env.describe("testing custom equality testers", function() { + env.beforeAll(function() { env.addCustomEqualityTester(function(a, b) { return true; }); }); + + env.it("with a custom tester", function() { + env.expect("a").toEqual("b"); }); - env.it("without a custom matcher", function() { - expect(env.expect().toFoo).toBeUndefined(); + env.it("with the same custom tester", function() { + env.expect("a").toEqual("b"); + }); + }); + + env.describe("another suite", function() { + env.it("without the custom tester", function(){ + env.expect("a").toEqual("b"); }); }); @@ -609,11 +680,7 @@ describe("Env integration", function() { it("Custom equality testers for toContain should be per spec", function(done) { var env = new j$.Env({global: { setTimeout: setTimeout }}), reporter = jasmine.createSpyObj('fakeReporter', [ - "jasmineStarted", "jasmineDone", - "suiteStarted", - "suiteDone", - "specStarted", "specDone" ]); @@ -636,11 +703,104 @@ describe("Env integration", function() { }); env.it("without a custom tester", function() { - env.expect("a").toContain("b"); + env.expect(["a"]).toContain("b"); }); }); env.execute(); }); + + it("Custom equality testers for toContain should be per suite", function(done) { + var env = new j$.Env({global: { setTimeout: setTimeout }}), + reporter = jasmine.createSpyObj('fakeReporter', [ + "jasmineDone", + "specDone" + ]); + + reporter.jasmineDone.and.callFake(function() { + var firstSpecResult = reporter.specDone.calls.first().args[0], + secondSpecResult = reporter.specDone.calls.argsFor(1)[0], + thirdSpecResult = reporter.specDone.calls.mostRecent().args[0]; + + expect(firstSpecResult.status).toEqual("passed"); + expect(secondSpecResult.status).toEqual("passed"); + expect(thirdSpecResult.status).toEqual("failed"); + + done(); + }); + + env.addReporter(reporter); + + env.describe("testing custom equality testers", function() { + env.beforeAll(function() { env.addCustomEqualityTester(function(a, b) { return true; })}); + + env.it("with a custom tester", function() { + env.expect(["a"]).toContain("b"); + }); + + env.it("also with the custom tester", function() { + env.expect(["a"]).toContain("b"); + }); + }); + + env.describe("another suite", function() { + env.it("without the custom tester", function() { + env.expect(["a"]).toContain("b"); + }); + }); + + env.execute(); + }); + + it("Custom matchers should be per spec", function(done) { + var env = new j$.Env({global: { setTimeout: setTimeout }}), + matchers = { + toFoo: function() {} + }; + + env.describe("testing custom matchers", function() { + env.it("with a custom matcher", function() { + env.addMatchers(matchers); + expect(env.expect().toFoo).toBeDefined(); + }); + + env.it("without a custom matcher", function() { + expect(env.expect().toFoo).toBeUndefined(); + }); + }); + + env.addReporter({jasmineDone: done}); + + env.execute(); + }); + + it("Custom matchers should be per suite", function(done) { + var env = new j$.Env({global: { setTimeout: setTimeout }}), + matchers = { + toFoo: function() {} + }; + + env.describe("testing custom matchers", function() { + env.beforeAll(function() { env.addMatchers(matchers); }); + + env.it("with a custom matcher", function() { + expect(env.expect().toFoo).toBeDefined(); + }); + + env.it("with the same custom matcher", function() { + expect(env.expect().toFoo).toBeDefined(); + }); + }); + + env.describe("another suite", function() { + env.it("no longer has the custom matcher", function() { + expect(env.expect().toFoo).not.toBeDefined(); + }); + }); + + env.addReporter({jasmineDone: done}); + + env.execute(); + }); }); diff --git a/spec/core/integration/SpecRunningSpec.js b/spec/core/integration/SpecRunningSpec.js index 3adf8d06..bc155b4c 100644 --- a/spec/core/integration/SpecRunningSpec.js +++ b/spec/core/integration/SpecRunningSpec.js @@ -236,11 +236,14 @@ describe("jasmine spec running", function () { }); }); - suite.execute(function() { + var assertions = function() { expect(specInADisabledSuite).not.toHaveBeenCalled(); done(); - }); - }); + }; + + env.addReporter({jasmineDone: assertions}); + + env.execute(); it("should set all pending specs to pending when a suite is run", function(done) { var pendingSpec, diff --git a/src/core/Env.js b/src/core/Env.js index 3fb1447c..02410685 100644 --- a/src/core/Env.js +++ b/src/core/Env.js @@ -14,11 +14,19 @@ getJasmineRequireObj().Env = function(j$) { this.clock = new j$.Clock(global, new j$.DelayedFunctionScheduler(), new j$.MockDate(global)); var runnableLookupTable = {}; - - var spies = []; + var runnableResources = {}; var currentSpec = null; - var currentSuite = null; + var currentlyExecutingSuites = []; + var currentDeclarationSuite = null; + + var currentSuite = function() { + return currentlyExecutingSuites[currentlyExecutingSuites.length - 1]; + } + + var currentRunnable = function() { + return currentSpec || currentSuite(); + } var reporter = new j$.ReportDispatcher([ 'jasmineStarted', @@ -33,11 +41,15 @@ getJasmineRequireObj().Env = function(j$) { return true; }; - var equalityTesters = []; - - var customEqualityTesters = []; this.addCustomEqualityTester = function(tester) { - customEqualityTesters.push(tester); + runnableResources[currentRunnable().id].customEqualityTesters.push(tester); + }; + + this.addMatchers = function(matchersToAdd) { + var customMatchers = runnableResources[currentRunnable().id].customMatchers; + for (var matcherName in matchersToAdd) { + customMatchers[matcherName] = matchersToAdd[matcherName]; + } }; j$.Expectation.addCoreMatchers(j$.matchers); @@ -55,7 +67,8 @@ getJasmineRequireObj().Env = function(j$) { var expectationFactory = function(actual, spec) { return j$.Expectation.Factory({ util: j$.matchersUtil, - customEqualityTesters: customEqualityTesters, + customEqualityTesters: runnableResources[spec.id].customEqualityTesters, + customMatchers: runnableResources[spec.id].customMatchers, actual: actual, addExpectationResult: addExpectationResult }); @@ -65,9 +78,20 @@ getJasmineRequireObj().Env = function(j$) { } }; - var specStarted = function(spec) { - currentSpec = spec; - reporter.specStarted(spec.result); + var defaultResourcesForRunnable = function(id, parentRunnableId) { + var resources = {spies: [], customEqualityTesters: [], customMatchers: {}}; + + if(runnableResources[parentRunnableId]){ + resources.customEqualityTesters = j$.util.clone(runnableResources[parentRunnableId].customEqualityTesters); + resources.customMatchers = j$.util.clone(runnableResources[parentRunnableId].customMatchers); + } + + runnableResources[id] = resources; + }; + + var clearResourcesForRunnable = function(id) { + spyRegistry.clearSpies(); + delete runnableResources[id]; }; var beforeFns = function(suite) { @@ -149,7 +173,8 @@ getJasmineRequireObj().Env = function(j$) { resultCallback: function() {} // TODO - hook this up }); runnableLookupTable[topSuite.id] = topSuite; - currentSuite = topSuite; + defaultResourcesForRunnable(topSuite.id); + currentDeclarationSuite = topSuite; this.topSuite = function() { return topSuite; @@ -175,36 +200,12 @@ getJasmineRequireObj().Env = function(j$) { reporter.addReporter(reporterToAdd); }; - this.addMatchers = function(matchersToAdd) { - j$.Expectation.addMatchers(matchersToAdd); - }; + var spyRegistry = new j$.SpyRegistry({currentSpies: function() { + return runnableResources[currentRunnable().id].spies; + }}); - this.spyOn = function(obj, methodName) { - if (j$.util.isUndefined(obj)) { - throw new Error('spyOn could not find an object to spy upon for ' + methodName + '()'); - } - - if (j$.util.isUndefined(obj[methodName])) { - throw new Error(methodName + '() method does not exist'); - } - - if (obj[methodName] && j$.isSpy(obj[methodName])) { - //TODO?: should this return the current spy? Downside: may cause user confusion about spy state - throw new Error(methodName + ' has already been spied upon'); - } - - var spy = j$.createSpy(methodName, obj[methodName]); - - spies.push({ - spy: spy, - baseObj: obj, - methodName: methodName, - originalValue: obj[methodName] - }); - - obj[methodName] = spy; - - return spy; + this.spyOn = function() { + return spyRegistry.spyOn.apply(spyRegistry, arguments); }; var suiteFactory = function(description) { @@ -212,24 +213,32 @@ getJasmineRequireObj().Env = function(j$) { env: self, id: getNextSuiteId(), description: description, - parentSuite: currentSuite, + parentSuite: currentDeclarationSuite, queueRunner: queueRunnerFactory, onStart: suiteStarted, resultCallback: function(attrs) { + clearResourcesForRunnable(suite.id); + currentlyExecutingSuites.pop(); reporter.suiteDone(attrs); } }); runnableLookupTable[suite.id] = suite; return suite; + + function suiteStarted(suite) { + currentlyExecutingSuites.push(suite); + defaultResourcesForRunnable(suite.id, suite.parentSuite.id); + reporter.suiteStarted(suite.result); + } }; this.describe = function(description, specDefinitions) { var suite = suiteFactory(description); - var parentSuite = currentSuite; + var parentSuite = currentDeclarationSuite; parentSuite.addChild(suite); - currentSuite = suite; + currentDeclarationSuite = suite; var declarationError = null; try { @@ -244,7 +253,7 @@ getJasmineRequireObj().Env = function(j$) { }); } - currentSuite = parentSuite; + currentDeclarationSuite = parentSuite; return suite; }; @@ -284,30 +293,22 @@ getJasmineRequireObj().Env = function(j$) { return spec; - function removeAllSpies() { - for (var i = 0; i < spies.length; i++) { - var spyEntry = spies[i]; - spyEntry.baseObj[spyEntry.methodName] = spyEntry.originalValue; - } - spies = []; - } - function specResultCallback(result) { - removeAllSpies(); - j$.Expectation.resetMatchers(); - customEqualityTesters = []; + clearResourcesForRunnable(spec.id); currentSpec = null; reporter.specDone(result); } - }; - var suiteStarted = function(suite) { - reporter.suiteStarted(suite.result); + function specStarted(spec) { + currentSpec = spec; + defaultResourcesForRunnable(spec.id, suite.id); + reporter.specStarted(spec.result); + } }; this.it = function(description, fn) { - var spec = specFactory(description, fn, currentSuite); - currentSuite.addChild(spec); + var spec = specFactory(description, fn, currentDeclarationSuite); + currentDeclarationSuite.addChild(spec); return spec; }; @@ -322,19 +323,19 @@ getJasmineRequireObj().Env = function(j$) { }; this.beforeEach = function(beforeEachFunction) { - currentSuite.beforeEach({ fn: beforeEachFunction, timeout: function() { return j$.DEFAULT_TIMEOUT_INTERVAL; } }); + currentDeclarationSuite.beforeEach({ fn: beforeEachFunction, timeout: function() { return j$.DEFAULT_TIMEOUT_INTERVAL; } }); }; this.beforeAll = function(beforeAllFunction) { - currentSuite.beforeAll({ fn: beforeAllFunction, timeout: function() { return j$.DEFAULT_TIMEOUT_INTERVAL; } }); + currentDeclarationSuite.beforeAll({ fn: beforeAllFunction, timeout: function() { return j$.DEFAULT_TIMEOUT_INTERVAL; } }); }; this.afterEach = function(afterEachFunction) { - currentSuite.afterEach({ fn: afterEachFunction, timeout: function() { return j$.DEFAULT_TIMEOUT_INTERVAL; } }); + currentDeclarationSuite.afterEach({ fn: afterEachFunction, timeout: function() { return j$.DEFAULT_TIMEOUT_INTERVAL; } }); }; this.afterAll = function(afterAllFunction) { - currentSuite.afterAll({ fn: afterAllFunction, timeout: function() { return j$.DEFAULT_TIMEOUT_INTERVAL; } }); + currentDeclarationSuite.afterAll({ fn: afterAllFunction, timeout: function() { return j$.DEFAULT_TIMEOUT_INTERVAL; } }); }; this.pending = function() { diff --git a/src/core/Expectation.js b/src/core/Expectation.js index 626a5cec..ca0695e4 100644 --- a/src/core/Expectation.js +++ b/src/core/Expectation.js @@ -1,7 +1,5 @@ getJasmineRequireObj().Expectation = function() { - var matchers = {}; - function Expectation(options) { this.util = options.util || { buildFailureMessage: function() {} }; this.customEqualityTesters = options.customEqualityTesters || []; @@ -9,8 +7,9 @@ getJasmineRequireObj().Expectation = function() { this.addExpectationResult = options.addExpectationResult || function(){}; this.isNot = options.isNot; - for (var matcherName in matchers) { - this[matcherName] = matchers[matcherName]; + var customMatchers = options.customMatchers || {}; + for (var matcherName in customMatchers) { + this[matcherName] = Expectation.prototype.wrapCompare(matcherName, customMatchers[matcherName]); } } @@ -77,19 +76,6 @@ getJasmineRequireObj().Expectation = function() { } }; - Expectation.addMatchers = function(matchersToAdd) { - for (var name in matchersToAdd) { - var matcher = matchersToAdd[name]; - matchers[name] = Expectation.prototype.wrapCompare(name, matcher); - } - }; - - Expectation.resetMatchers = function() { - for (var name in matchers) { - delete matchers[name]; - } - }; - Expectation.Factory = function(options) { options = options || {}; diff --git a/src/core/SpyRegistry.js b/src/core/SpyRegistry.js new file mode 100644 index 00000000..5532127a --- /dev/null +++ b/src/core/SpyRegistry.js @@ -0,0 +1,46 @@ +getJasmineRequireObj().SpyRegistry = function(j$) { + + function SpyRegistry(options) { + var options = options || {}; + var currentSpies = options.currentSpies || function() { return []; }; + + this.spyOn = function(obj, methodName) { + if (j$.util.isUndefined(obj)) { + throw new Error('spyOn could not find an object to spy upon for ' + methodName + '()'); + } + + if (j$.util.isUndefined(obj[methodName])) { + throw new Error(methodName + '() method does not exist'); + } + + if (obj[methodName] && j$.isSpy(obj[methodName])) { + //TODO?: should this return the current spy? Downside: may cause user confusion about spy state + throw new Error(methodName + ' has already been spied upon'); + } + + var spy = j$.createSpy(methodName, obj[methodName]); + + currentSpies().push({ + spy: spy, + baseObj: obj, + methodName: methodName, + originalValue: obj[methodName] + }); + + obj[methodName] = spy; + + return spy; + }; + + this.clearSpies = function() { + var spies = currentSpies(); + for (var i = 0; i < spies.length; i++) { + var spyEntry = spies[i]; + spyEntry.baseObj[spyEntry.methodName] = spyEntry.originalValue; + } + }; + } + + return SpyRegistry; +}; + diff --git a/src/core/requireCore.js b/src/core/requireCore.js index 528489b6..466a7921 100644 --- a/src/core/requireCore.js +++ b/src/core/requireCore.js @@ -28,6 +28,7 @@ getJasmineRequireObj().core = function(jRequire) { j$.QueueRunner = jRequire.QueueRunner(j$); j$.ReportDispatcher = jRequire.ReportDispatcher(); j$.Spec = jRequire.Spec(j$); + j$.SpyRegistry = jRequire.SpyRegistry(j$); j$.SpyStrategy = jRequire.SpyStrategy(); j$.Suite = jRequire.Suite(); j$.Timer = jRequire.Timer(); diff --git a/src/core/util.js b/src/core/util.js index 5285b8d7..9c6f4878 100644 --- a/src/core/util.js +++ b/src/core/util.js @@ -30,5 +30,20 @@ getJasmineRequireObj().util = function() { return obj === void 0; }; + util.clone = function(obj) { + if (Object.prototype.toString.apply(obj) === "[object Array]") { + return obj.slice(); + } + + var cloned = {}; + for (var prop in obj) { + if (obj.hasOwnProperty(prop)) { + cloned[prop] = obj[prop]; + } + } + + return cloned; + } + return util; };