From f463e1f7aa920ea84e469874393cd5132bc13ee7 Mon Sep 17 00:00:00 2001 From: Sheel Choksi Date: Sat, 7 Sep 2013 18:28:03 -0700 Subject: [PATCH] Consistent 'this' between befores/it/afters Change the 'this' user functions are called with to be an empty object instead of the QueueRunner so that if the user puts properties on it, they won't conflict. Also, changes async specs to be called with a proper 'this', as pointed out by @Eric-Wright in #419 and #420. [finishes #56030080] --- lib/jasmine-core/jasmine.js | 7 ++-- spec/core/EnvSpec.js | 64 ++++++++++++++++++++++++++++++++++++ spec/core/QueueRunnerSpec.js | 17 ++++++++++ src/core/QueueRunner.js | 5 +-- src/core/Spec.js | 2 +- 5 files changed, 89 insertions(+), 6 deletions(-) diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index 820f8031..886e3c51 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -288,7 +288,7 @@ getJasmineRequireObj().Spec = function() { done(); }; - fn(callDone); //TODO: do we care about more than 1 arg? + fn.call(this, callDone); //TODO: do we care about more than 1 arg? }; } @@ -1440,6 +1440,7 @@ getJasmineRequireObj().QueueRunner = function() { this.clearStack = attrs.clearStack || function(fn) {fn();}; this.onException = attrs.onException || function() {}; this.catchException = attrs.catchException || function() { return true; }; + this.userContext = {}; } QueueRunner.prototype.execute = function() { @@ -1468,7 +1469,7 @@ getJasmineRequireObj().QueueRunner = function() { function attemptSync(fn) { try { - fn.call(self); + fn.call(self.userContext); } catch (e) { handleException(e); } @@ -1478,7 +1479,7 @@ getJasmineRequireObj().QueueRunner = function() { var next = function () { self.run(fns, iterativeIndex + 1); }; try { - fn.call(self, next); + fn.call(self.userContext, next); } catch (e) { handleException(e); next(); diff --git a/spec/core/EnvSpec.js b/spec/core/EnvSpec.js index bbd25fc5..f71ad8c0 100644 --- a/spec/core/EnvSpec.js +++ b/spec/core/EnvSpec.js @@ -236,6 +236,70 @@ describe("Env integration", function() { env.execute(); }); + it("calls associated befores/specs/afters with the same 'this'", function(done) { + var env = new j$.Env(); + + env.addReporter({jasmineDone: done}); + + env.describe("tests", function() { + var firstTimeThrough = true, firstSpecContext, secondSpecContext; + + env.beforeEach(function() { + if (firstTimeThrough) { + firstSpecContext = this; + } else { + secondSpecContext = this; + } + expect(this).toEqual({}); + }); + + env.it("sync spec", function() { + expect(this).toBe(firstSpecContext); + }); + + env.it("another sync spec", function() { + expect(this).toBe(secondSpecContext); + }); + + env.afterEach(function() { + if (firstTimeThrough) { + expect(this).toBe(firstSpecContext); + firstTimeThrough = false; + } else { + expect(this).toBe(secondSpecContext); + } + }); + }); + + env.execute(); + }); + + it("calls associated befores/its/afters with the same 'this' for an async spec", function(done) { + var env = new j$.Env(); + + env.addReporter({jasmineDone: done}); + + env.describe("with an async spec", function() { + var specContext; + + env.beforeEach(function() { + specContext = this; + expect(this).toEqual({}); + }); + + env.it("sync spec", function(underTestCallback) { + expect(this).toBe(specContext); + underTestCallback(); + }); + + env.afterEach(function() { + expect(this).toBe(specContext); + }); + }); + + env.execute(); + }); + it("Mock clock can be installed and used in tests", function(done) { var globalSetTimeout = jasmine.createSpy('globalSetTimeout'), delayedFunctionForGlobalClock = jasmine.createSpy('delayedFunctionForGlobalClock'), diff --git a/spec/core/QueueRunnerSpec.js b/spec/core/QueueRunnerSpec.js index 65cb0fa8..d3c220f7 100644 --- a/spec/core/QueueRunnerSpec.js +++ b/spec/core/QueueRunnerSpec.js @@ -19,6 +19,23 @@ describe("QueueRunner", function() { expect(calls).toEqual(['fn1', 'fn2']); }); + it("calls each function with a consistent 'this'-- an empty object", function() { + var fn1 = jasmine.createSpy('fn1'), + fn2 = jasmine.createSpy('fn2'), + fn3 = function(done) { asyncContext = this; done(); }, + queueRunner = new j$.QueueRunner({ + fns: [fn1, fn2, fn3] + }), + asyncContext; + + queueRunner.execute(); + + var context = fn1.calls.first().object; + expect(context).toEqual({}); + expect(fn2.calls.first().object).toBe(context); + expect(asyncContext).toBe(context); + }); + it("supports asynchronous functions, only advancing to next function after a done() callback", function() { //TODO: it would be nice if spy arity could match the fake, so we could do something like: //createSpy('asyncfn').and.callFake(function(done) {}); diff --git a/src/core/QueueRunner.js b/src/core/QueueRunner.js index fcb75ea3..689cae81 100644 --- a/src/core/QueueRunner.js +++ b/src/core/QueueRunner.js @@ -6,6 +6,7 @@ getJasmineRequireObj().QueueRunner = function() { this.clearStack = attrs.clearStack || function(fn) {fn();}; this.onException = attrs.onException || function() {}; this.catchException = attrs.catchException || function() { return true; }; + this.userContext = {}; } QueueRunner.prototype.execute = function() { @@ -34,7 +35,7 @@ getJasmineRequireObj().QueueRunner = function() { function attemptSync(fn) { try { - fn.call(self); + fn.call(self.userContext); } catch (e) { handleException(e); } @@ -44,7 +45,7 @@ getJasmineRequireObj().QueueRunner = function() { var next = function () { self.run(fns, iterativeIndex + 1); }; try { - fn.call(self, next); + fn.call(self.userContext, next); } catch (e) { handleException(e); next(); diff --git a/src/core/Spec.js b/src/core/Spec.js index 1701319b..6828c87b 100644 --- a/src/core/Spec.js +++ b/src/core/Spec.js @@ -65,7 +65,7 @@ getJasmineRequireObj().Spec = function() { done(); }; - fn(callDone); //TODO: do we care about more than 1 arg? + fn.call(this, callDone); //TODO: do we care about more than 1 arg? }; }