diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index d2f65df7..2dda6331 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -504,7 +504,7 @@ getJasmineRequireObj().util = function(j$) { } function callerFile() { - var trace = new j$.StackTrace(errorWithStack().stack); + var trace = new j$.StackTrace(errorWithStack()); return trace.frames[2].file; } @@ -2503,7 +2503,7 @@ getJasmineRequireObj().ExceptionFormatter = function(j$) { return null; } - var stackTrace = new j$.StackTrace(error.stack); + var stackTrace = new j$.StackTrace(error); var lines = filterJasmine(stackTrace); var result = ''; @@ -5752,15 +5752,16 @@ getJasmineRequireObj().SpyStrategy = function(j$) { }; getJasmineRequireObj().StackTrace = function(j$) { - function StackTrace(rawTrace) { - var lines = rawTrace + function StackTrace(error) { + var lines = error.stack .split('\n') .filter(function(line) { return line !== ''; }); - if (lines[0].match(/^Error/)) { - this.message = lines.shift(); - } else { - this.message = undefined; + var extractResult = extractMessage(error.message, lines); + + if (extractResult) { + this.message = extractResult.message; + lines = extractResult.remainder; } var parseResult = tryParseFrames(lines); @@ -5828,6 +5829,34 @@ getJasmineRequireObj().StackTrace = function(j$) { } } } + + function extractMessage(message, stackLines) { + var len = messagePrefixLength(message, stackLines); + + if (len > 0) { + return { + message: stackLines.slice(0, len).join('\n'), + remainder: stackLines.slice(len) + }; + } + } + + function messagePrefixLength(message, stackLines) { + if (!stackLines[0].match(/^Error/)) { + return 0; + } + + var messageLines = message.split('\n'); + var i; + + for (i = 1; i < messageLines.length; i++) { + if (messageLines[i] !== stackLines[i]) { + return 0; + } + } + + return messageLines.length; + } return StackTrace; }; diff --git a/spec/core/ExceptionFormatterSpec.js b/spec/core/ExceptionFormatterSpec.js index e896a5fe..95a476b0 100644 --- a/spec/core/ExceptionFormatterSpec.js +++ b/spec/core/ExceptionFormatterSpec.js @@ -56,6 +56,7 @@ describe("ExceptionFormatter", function() { it("filters Jasmine stack frames from V8 style traces", function() { var error = { + message: 'nope', stack: 'Error: nope\n' + ' at fn1 (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)\n' + ' at fn2 (http://localhost:8888/__jasmine__/jasmine.js:4320:20)\n' + @@ -113,6 +114,25 @@ describe("ExceptionFormatter", function() { } }); + it("handles multiline error messages in this environment", function() { + var error, i, msg = "an error\nwith two lines"; + try { throw new Error(msg); } catch(e) { error = e; } + + if (error.stack.indexOf(msg) === -1) { + pending("Stack traces don't have messages in this environment"); + } + var subject = new jasmineUnderTest.ExceptionFormatter({ + jasmineFile: jasmine.util.jasmineFile() + }); + var result = subject.stack(error); + var lines = result.split('\n'); + + expect(lines[0]).toMatch(/an error/); + expect(lines[1]).toMatch(/with two lines/); + expect(lines[2]).toMatch(/ExceptionFormatterSpec.js/); + expect(lines[3]).toMatch(//); + }); + it("returns null if no Error provided", function() { expect(new jasmineUnderTest.ExceptionFormatter().stack()).toBeNull(); }); diff --git a/spec/core/StackTraceSpec.js b/spec/core/StackTraceSpec.js index e07b306c..dd8023f4 100644 --- a/spec/core/StackTraceSpec.js +++ b/spec/core/StackTraceSpec.js @@ -1,11 +1,14 @@ describe("StackTrace", function() { it("understands Chrome/IE/Edge style traces", function() { - var raw = - 'Error: nope\n' + - ' at UserContext. (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)\n' + - ' at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)'; + var error = { + message: 'nope', + stack: + 'Error: nope\n' + + ' at UserContext. (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)\n' + + ' at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)' + }; - var result = new jasmineUnderTest.StackTrace(raw); + var result = new jasmineUnderTest.StackTrace(error); expect(result.message).toEqual('Error: nope'); expect(result.style).toEqual('v8'); @@ -25,13 +28,36 @@ describe("StackTrace", function() { ]); }); + it("understands Chrome/IE/Edge style traces with multiline messages", function() { + var error = { + message: 'line 1\nline 2', + stack: + 'Error: line 1\nline 2\n' + + ' at UserContext. (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)\n' + + ' at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)' + }; + + var result = new jasmineUnderTest.StackTrace(error); + + expect(result.message).toEqual('Error: line 1\nline 2'); + var rawFrames = result.frames.map(function(f) { return f.raw; }); + expect(rawFrames).toEqual([ + ' at UserContext. (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)', + ' at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)', + ]); + }); + it("understands Node style traces", function() { - var raw = 'Error\n' + - ' at /somewhere/jasmine/lib/jasmine-core/jasmine.js:4255:9\n' + - ' at QueueRunner.complete [as onComplete] (/somewhere/jasmine/lib/jasmine-core/jasmine.js:579:9)\n' + - ' at Immediate. (/somewhere/jasmine/lib/jasmine-core/jasmine.js:4314:12)\n' + - ' at runCallback (timers.js:672:20)'; - var result = new jasmineUnderTest.StackTrace(raw); + var error = { + message: 'nope', + stack: + 'Error\n' + + ' at /somewhere/jasmine/lib/jasmine-core/jasmine.js:4255:9\n' + + ' at QueueRunner.complete [as onComplete] (/somewhere/jasmine/lib/jasmine-core/jasmine.js:579:9)\n' + + ' at Immediate. (/somewhere/jasmine/lib/jasmine-core/jasmine.js:4314:12)\n' + + ' at runCallback (timers.js:672:20)' + }; + var result = new jasmineUnderTest.StackTrace(error); expect(result.message).toEqual('Error'); expect(result.style).toEqual('v8'); @@ -64,10 +90,13 @@ describe("StackTrace", function() { }); it("understands Safari/Firefox/Phantom-OS X style traces", function() { - var raw = - 'http://localhost:8888/__spec__/core/UtilSpec.js:115:28\n' + - 'run@http://localhost:8888/__jasmine__/jasmine.js:4320:27'; - var result = new jasmineUnderTest.StackTrace(raw); + var error = { + message: 'nope', + stack: + 'http://localhost:8888/__spec__/core/UtilSpec.js:115:28\n' + + 'run@http://localhost:8888/__jasmine__/jasmine.js:4320:27' + }; + var result = new jasmineUnderTest.StackTrace(error); expect(result.message).toBeFalsy(); expect(result.style).toEqual('webkit'); @@ -88,20 +117,26 @@ describe("StackTrace", function() { }); it("does not mistake gibberish for Safari/Firefox/Phantom-OS X style traces", function() { - var raw = 'randomcharsnotincludingwhitespace'; - var result = new jasmineUnderTest.StackTrace(raw); + var error = { + message: 'nope', + stack: 'randomcharsnotincludingwhitespace' + }; + var result = new jasmineUnderTest.StackTrace(error); expect(result.style).toBeNull(); expect(result.frames).toEqual([ - { raw: raw } + { raw: error.stack } ]); }); it("understands Phantom-Linux style traces", function() { - var raw = - ' at UserContext. (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)\n' + - ' at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)'; + var error = { + message: 'nope', + stack: + ' at UserContext. (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)\n' + + ' at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)' + }; - var result = new jasmineUnderTest.StackTrace(raw); + var result = new jasmineUnderTest.StackTrace(error); expect(result.message).toBeFalsy(); expect(result.style).toEqual('v8'); @@ -122,10 +157,13 @@ describe("StackTrace", function() { }); it("ignores blank lines", function() { - var raw = - ' at UserContext. (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)\n'; + var error = { + message: 'nope', + stack: + ' at UserContext. (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)\n' + }; - var result = new jasmineUnderTest.StackTrace(raw); + var result = new jasmineUnderTest.StackTrace(error); expect(result.frames).toEqual([ { @@ -138,12 +176,15 @@ describe("StackTrace", function() { }); it("omits properties except 'raw' for frames that are not understood", function() { - var raw = - ' at UserContext. (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)\n' + - ' but this is quite unexpected\n' + - ' at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)'; + var error = { + message: 'nope', + stack: + ' at UserContext. (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)\n' + + ' but this is quite unexpected\n' + + ' at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)' + }; - var result = new jasmineUnderTest.StackTrace(raw); + var result = new jasmineUnderTest.StackTrace(error); expect(result.style).toEqual('v8'); expect(result.frames).toEqual([ { diff --git a/src/core/ExceptionFormatter.js b/src/core/ExceptionFormatter.js index 0ba4450f..359a8303 100644 --- a/src/core/ExceptionFormatter.js +++ b/src/core/ExceptionFormatter.js @@ -27,7 +27,7 @@ getJasmineRequireObj().ExceptionFormatter = function(j$) { return null; } - var stackTrace = new j$.StackTrace(error.stack); + var stackTrace = new j$.StackTrace(error); var lines = filterJasmine(stackTrace); var result = ''; diff --git a/src/core/StackTrace.js b/src/core/StackTrace.js index 54ca89d6..c10ec951 100644 --- a/src/core/StackTrace.js +++ b/src/core/StackTrace.js @@ -1,13 +1,14 @@ getJasmineRequireObj().StackTrace = function(j$) { - function StackTrace(rawTrace) { - var lines = rawTrace + function StackTrace(error) { + var lines = error.stack .split('\n') .filter(function(line) { return line !== ''; }); - if (lines[0].match(/^Error/)) { - this.message = lines.shift(); - } else { - this.message = undefined; + var extractResult = extractMessage(error.message, lines); + + if (extractResult) { + this.message = extractResult.message; + lines = extractResult.remainder; } var parseResult = tryParseFrames(lines); @@ -75,6 +76,34 @@ getJasmineRequireObj().StackTrace = function(j$) { } } } + + function extractMessage(message, stackLines) { + var len = messagePrefixLength(message, stackLines); + + if (len > 0) { + return { + message: stackLines.slice(0, len).join('\n'), + remainder: stackLines.slice(len) + }; + } + } + + function messagePrefixLength(message, stackLines) { + if (!stackLines[0].match(/^Error/)) { + return 0; + } + + var messageLines = message.split('\n'); + var i; + + for (i = 1; i < messageLines.length; i++) { + if (messageLines[i] !== stackLines[i]) { + return 0; + } + } + + return messageLines.length; + } return StackTrace; }; diff --git a/src/core/util.js b/src/core/util.js index c53c3aca..a3c2c3a2 100644 --- a/src/core/util.js +++ b/src/core/util.js @@ -130,7 +130,7 @@ getJasmineRequireObj().util = function(j$) { } function callerFile() { - var trace = new j$.StackTrace(errorWithStack().stack); + var trace = new j$.StackTrace(errorWithStack()); return trace.frames[2].file; }