diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index c95aef3e..25e70185 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -65,6 +65,7 @@ var getJasmineRequireObj = (function(jasmineGlobal) { j$.Clock = jRequire.Clock(); j$.DelayedFunctionScheduler = jRequire.DelayedFunctionScheduler(j$); j$.Env = jRequire.Env(j$); + j$.deprecatingThisProxy = jRequire.deprecatingThisProxy(j$); j$.StackTrace = jRequire.StackTrace(j$); j$.ExceptionFormatter = jRequire.ExceptionFormatter(j$); j$.ExpectationFilterChain = jRequire.ExpectationFilterChain(); @@ -1956,7 +1957,7 @@ getJasmineRequireObj().Env = function(j$) { var declarationError = null; try { - specDefinitions.call(suite); + specDefinitions.call(j$.deprecatingThisProxy(suite, self)); } catch (e) { declarationError = e; } @@ -3367,6 +3368,39 @@ getJasmineRequireObj().DelayedFunctionScheduler = function(j$) { return DelayedFunctionScheduler; }; +/* eslint-disable compat/compat */ +// TODO: Remove this in the next major release. +getJasmineRequireObj().deprecatingThisProxy = function(j$) { + var msg = "Access to 'this' in describe functions is deprecated."; + + try { + new Proxy({}, {}); + } catch (e) { + // Environment does not support Poxy. + return function(suite) { + return suite; + }; + } + + function DeprecatingThisProxyHandler(env) { + this._env = env; + } + + DeprecatingThisProxyHandler.prototype.get = function(target, prop, receiver) { + this._env.deprecated(msg); + return target[prop]; + }; + + DeprecatingThisProxyHandler.prototype.set = function(target, prop, value) { + this._env.deprecated(msg); + return (target[prop] = value); + }; + + return function(suite, env) { + return new Proxy(suite, new DeprecatingThisProxyHandler(env)); + }; +}; + getJasmineRequireObj().errors = function() { function ExpectationFailed() {} diff --git a/spec/core/EnvSpec.js b/spec/core/EnvSpec.js index 75979537..12000f34 100644 --- a/spec/core/EnvSpec.js +++ b/spec/core/EnvSpec.js @@ -440,4 +440,47 @@ describe('Env', function() { done(); }); }); + + it("deprecates access to 'this' in describes", function() { + jasmine.getEnv().requireProxy(); + var msg = "Access to 'this' in describe functions is deprecated.", + ran = false; + spyOn(env, 'deprecated'); + + env.describe('a suite', function() { + expect(this.description).toEqual('a suite'); + expect(env.deprecated).toHaveBeenCalledWith(msg); + env.deprecated.calls.reset(); + + this.foo = 1; + expect(env.deprecated).toHaveBeenCalledWith(msg); + expect(this.foo).toEqual(1); + env.deprecated.calls.reset(); + + expect(this.getFullName()).toEqual('a suite'); + expect(env.deprecated).toHaveBeenCalledWith(msg); + env.deprecated.calls.reset(); + + env.it('has a spec'); + ran = true; + }); + + expect(ran).toBeTrue(); + }); + + // TODO: Remove this in the next major version. Suites were never meant to be + // exposed via describe 'this' in >= 2.0, and user code should not rely on it. + // This spec is just here to make sure we don't break user code that *does* + // rely on it in older browsers (without Proxy) while deprecating it. + it("sets 'this' to the Suite in describes", function() { + var suiteThis; + spyOn(env, 'deprecated'); + + env.describe('a suite', function() { + suiteThis = this; + env.it('has a spec'); + }); + + expect(suiteThis).toBeInstanceOf(jasmineUnderTest.Suite); + }); }); diff --git a/spec/helpers/checkForProxy.js b/spec/helpers/checkForProxy.js new file mode 100644 index 00000000..b4062d0b --- /dev/null +++ b/spec/helpers/checkForProxy.js @@ -0,0 +1,17 @@ +/* eslint-disable compat/compat */ +(function(env) { + function hasProxyConstructor() { + try { + new Proxy({}, {}); + return true; + } catch (e) { + return false; + } + } + + env.requireProxy = function() { + if (!hasProxyConstructor()) { + env.pending('Environment does not support Proxy'); + } + }; +})(jasmine.getEnv()); diff --git a/spec/support/jasmine-browser.js b/spec/support/jasmine-browser.js index 9062bf8a..1fc16d01 100644 --- a/spec/support/jasmine-browser.js +++ b/spec/support/jasmine-browser.js @@ -21,6 +21,7 @@ module.exports = { 'helpers/generator.js', 'helpers/BrowserFlags.js', 'helpers/checkForMap.js', + 'helpers/checkForProxy.js', 'helpers/checkForSet.js', 'helpers/checkForSymbol.js', 'helpers/checkForUrl.js', diff --git a/spec/support/jasmine.json b/spec/support/jasmine.json index 1a0ca3b2..d6369bf2 100644 --- a/spec/support/jasmine.json +++ b/spec/support/jasmine.json @@ -8,6 +8,7 @@ "helpers/asyncAwait.js", "helpers/generator.js", "helpers/checkForMap.js", + "helpers/checkForProxy.js", "helpers/checkForSet.js", "helpers/checkForSymbol.js", "helpers/checkForUrl.js", diff --git a/src/core/Env.js b/src/core/Env.js index 138711e0..813c4843 100644 --- a/src/core/Env.js +++ b/src/core/Env.js @@ -987,7 +987,7 @@ getJasmineRequireObj().Env = function(j$) { var declarationError = null; try { - specDefinitions.call(suite); + specDefinitions.call(j$.deprecatingThisProxy(suite, self)); } catch (e) { declarationError = e; } diff --git a/src/core/deprecatingThisProxy.js b/src/core/deprecatingThisProxy.js new file mode 100644 index 00000000..6f924b18 --- /dev/null +++ b/src/core/deprecatingThisProxy.js @@ -0,0 +1,32 @@ +/* eslint-disable compat/compat */ +// TODO: Remove this in the next major release. +getJasmineRequireObj().deprecatingThisProxy = function(j$) { + var msg = "Access to 'this' in describe functions is deprecated."; + + try { + new Proxy({}, {}); + } catch (e) { + // Environment does not support Poxy. + return function(suite) { + return suite; + }; + } + + function DeprecatingThisProxyHandler(env) { + this._env = env; + } + + DeprecatingThisProxyHandler.prototype.get = function(target, prop, receiver) { + this._env.deprecated(msg); + return target[prop]; + }; + + DeprecatingThisProxyHandler.prototype.set = function(target, prop, value) { + this._env.deprecated(msg); + return (target[prop] = value); + }; + + return function(suite, env) { + return new Proxy(suite, new DeprecatingThisProxyHandler(env)); + }; +}; diff --git a/src/core/requireCore.js b/src/core/requireCore.js index aac94b87..13cc6cc5 100644 --- a/src/core/requireCore.js +++ b/src/core/requireCore.js @@ -43,6 +43,7 @@ var getJasmineRequireObj = (function(jasmineGlobal) { j$.Clock = jRequire.Clock(); j$.DelayedFunctionScheduler = jRequire.DelayedFunctionScheduler(j$); j$.Env = jRequire.Env(j$); + j$.deprecatingThisProxy = jRequire.deprecatingThisProxy(j$); j$.StackTrace = jRequire.StackTrace(j$); j$.ExceptionFormatter = jRequire.ExceptionFormatter(j$); j$.ExpectationFilterChain = jRequire.ExpectationFilterChain();