diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index 0d42c0b1..31839b5f 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -2932,7 +2932,9 @@ getJasmineRequireObj().toThrowError = function(j$) { return fail; } - if (!(thrown instanceof Error)) { + // Get Error constructor of thrown + if (!thrown || !thrown.constructor || !thrown.constructor.constructor || + !(thrown instanceof (thrown.constructor.constructor('return this')()).Error)) { fail.message = function() { return 'Expected function to throw an Error, but it threw ' + j$.pp(thrown) + '.'; }; return fail; } diff --git a/spec/core/matchers/toThrowErrorSpec.js b/spec/core/matchers/toThrowErrorSpec.js index c15e7129..db7050c7 100644 --- a/spec/core/matchers/toThrowErrorSpec.js +++ b/spec/core/matchers/toThrowErrorSpec.js @@ -65,6 +65,53 @@ describe("toThrowError", function() { expect(result.message()).toEqual("Expected function to throw an Error, but it threw 4."); }); + function isNotRunningInBrowser() { + return typeof document === 'undefined' + } + + function isRunningInPhantomJS(minVersion, maxVersion) { + if (!window.callPhantom) { + return false; + } + + function getVerNum(ver) { + var nums = ver.split('.'), verNum = 0; + if ((nums[0] = +nums[0])) { verNum += nums[0] * 10000; } + if ((nums[1] = +nums[1])) { verNum += nums[1] * 100; } + if ((nums[2] = +nums[2])) { verNum += nums[2]; } + return verNum; + } + + if (minVersion || maxVersion) { + var ver = (/\sPhantomJS\/([\d\.]+)\s/.exec(window.navigator.userAgent) || [])[1]; + if (!ver) { return false; } + ver = getVerNum(ver); + if (minVersion && ver < getVerNum(minVersion)) { return false; } + if (maxVersion && ver > getVerNum(maxVersion)) { return false; } + } + return true; + } + + it("passes if thrown is an instanceof Error regardless of global that contains its constructor", function() { + if (isNotRunningInBrowser() || isRunningInPhantomJS(null, '1.9.8')) { + return; + } + + var matcher = jasmineUnderTest.matchers.toThrowError(), + iframe = document.body.appendChild(document.createElement("iframe")), + iframeDocument = iframe.contentDocument, + result; + + iframeDocument.body.appendChild(iframeDocument.createElement("script")) + .textContent = "function method() { throw new Error('foo'); }"; + + result = matcher.compare(iframe.contentWindow.method); + expect(result.pass).toBe(true); + expect(result.message).toEqual("Expected function not to throw an Error, but it threw Error."); + + document.body.removeChild(iframe); + }); + it("fails with the correct message if thrown is a falsy value", function() { var matcher = jasmineUnderTest.matchers.toThrowError(), fn = function() { diff --git a/src/core/matchers/toThrowError.js b/src/core/matchers/toThrowError.js index 67a880f9..f6859c61 100644 --- a/src/core/matchers/toThrowError.js +++ b/src/core/matchers/toThrowError.js @@ -28,7 +28,9 @@ getJasmineRequireObj().toThrowError = function(j$) { return fail; } - if (!(thrown instanceof Error)) { + // Get Error constructor of thrown + if (!thrown || !thrown.constructor || !thrown.constructor.constructor || + !(thrown instanceof (thrown.constructor.constructor('return this')()).Error)) { fail.message = function() { return 'Expected function to throw an Error, but it threw ' + j$.pp(thrown) + '.'; }; return fail; }