Skip to content

Commit f9cc074

Browse files
committed
Adding support and tests for Safari 6 and Firefox 14
1 parent 17294ff commit f9cc074

File tree

4 files changed

+75
-42
lines changed

4 files changed

+75
-42
lines changed

stacktrace.js

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ printStackTrace.implementation.prototype = {
5656
mode: function(e) {
5757
if (e['arguments'] && e.stack) {
5858
return 'chrome';
59+
} else if (e.stack && e.sourceURL) {
60+
return 'safari';
5961
} else if (typeof e.message === 'string' && typeof window !== 'undefined' && window.opera) {
6062
// e.message.indexOf("Backtrace:") > -1 -> opera
6163
// !e.stacktrace -> opera
@@ -131,21 +133,27 @@ printStackTrace.implementation.prototype = {
131133
return stack;
132134
},
133135

136+
/**
137+
* Given an Error object, return a formatted Array based on Safari's stack string.
138+
*
139+
* @param e - Error object to inspect
140+
* @return Array<String> of function calls, files and line numbers
141+
*/
142+
safari: function(e) {
143+
return e.stack.replace(/\[native code\]\n/m, '').replace(/^@/gm, '{anonymous}()@').split('\n');
144+
},
145+
134146
/**
135147
* Given an Error object, return a formatted Array based on Firefox's stack string.
136148
*
137149
* @param e - Error object to inspect
138150
* @return Array<String> of function calls, files and line numbers
139151
*/
140152
firefox: function(e) {
141-
return e.stack.replace(/(?:\n@:0)?\s+$/m, '').replace(/^\(/gm, '{anonymous}(').split('\n');
153+
return e.stack.replace(/(?:\n@:0)?\s+$/m, '').replace(/^[\(@]/gm, '{anonymous}()@').split('\n');
142154
},
143155

144156
opera11: function(e) {
145-
// "Error thrown at line 42, column 12 in <anonymous function>() in file://localhost/G:/js/stacktrace.js:\n"
146-
// "Error thrown at line 42, column 12 in <anonymous function: createException>() in file://localhost/G:/js/stacktrace.js:\n"
147-
// "called from line 7, column 4 in bar(n) in file://localhost/G:/js/test/functional/testcase1.html:\n"
148-
// "called from line 15, column 3 in file://localhost/G:/js/test/functional/testcase1.html:\n"
149157
var ANON = '{anonymous}', lineRE = /^.*line (\d+), column (\d+)(?: in (.+))? in (\S+):$/;
150158
var lines = e.stacktrace.split('\n'), result = [];
151159

@@ -220,7 +228,7 @@ printStackTrace.implementation.prototype = {
220228
return result;
221229
},
222230

223-
// Safari, IE, and others
231+
// Safari 5-, IE 9-, and others
224232
other: function(curr) {
225233
var ANON = '{anonymous}', fnRE = /function\s*([\w\-$]+)?\s*\(/i, stack = [], fn, args, maxStackSize = 10;
226234
while (curr && curr['arguments'] && stack.length < maxStackSize) {
@@ -235,7 +243,7 @@ printStackTrace.implementation.prototype = {
235243
/**
236244
* Given arguments array as a String, subsituting type names for non-string types.
237245
*
238-
* @param {Arguments} object
246+
* @param {Arguments} args
239247
* @return {Array} of Strings with stringified arguments
240248
*/
241249
stringifyArguments: function(args) {

test/CapturedExceptions.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,3 +240,23 @@ CapturedExceptions.firefox_7 = {
240240
"@file:///G:/js/test/functional/testcase1.html:24\n" +
241241
""
242242
};
243+
244+
CapturedExceptions.firefox_14 = {
245+
message: "x is null",
246+
stack: "@file:///Users/eric/src/javascript-stacktrace/test/functional/ExceptionLab.html:48\n" +
247+
"dumpException3@file:///Users/eric/src/javascript-stacktrace/test/functional/ExceptionLab.html:52\n" +
248+
"onclick@file:///Users/eric/src/javascript-stacktrace/test/functional/ExceptionLab.html:1\n" +
249+
"",
250+
fileName: "file:///Users/eric/src/javascript-stacktrace/test/functional/ExceptionLab.html",
251+
lineNumber: 48
252+
};
253+
254+
CapturedExceptions.safari_6 = {
255+
message: "'null' is not an object (evaluating 'x.undef')",
256+
stack: "@file:///Users/eric/src/javascript-stacktrace/test/functional/ExceptionLab.html:48\n" +
257+
"dumpException3@file:///Users/eric/src/javascript-stacktrace/test/functional/ExceptionLab.html:52\n" +
258+
"onclick@file:///Users/eric/src/javascript-stacktrace/test/functional/ExceptionLab.html:82\n" +
259+
"[native code]",
260+
line: 48,
261+
sourceURL: "file:///Users/eric/src/javascript-stacktrace/test/functional/ExceptionLab.html"
262+
};

test/TestStacktrace.js

Lines changed: 38 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,13 @@
7070

7171
test("mode", function() {
7272
expect(1);
73-
equals("chrome firefox other opera9 opera10a opera10b opera11".indexOf(pst.mode(UnitTest.fn.createGenericError())) >= 0, true);
73+
equals("chrome safari firefox other opera9 opera10a opera10b opera11".indexOf(pst.mode(UnitTest.fn.createGenericError())) >= 0, true);
7474
});
7575

7676
test("run mode", function() {
7777
expect(1);
7878
var p = new printStackTrace.implementation();
79-
p.other = p.firefox = p.chrome = p.opera9 = p.opera10a = p.opera10b = p.opera11 = function() {
79+
p.other = p.firefox = p.chrome = p.safari = p.opera9 = p.opera10a = p.opera10b = p.opera11 = function() {
8080
equals(1, 1, 'called mode() successfully');
8181
};
8282
p.run();
@@ -245,15 +245,15 @@
245245
ok(!this.toInstrument._instrumented, 'function deinstrumented');
246246
this.toInstrument = this.callback = null;
247247
});
248-
248+
249249
test("firefox", function() {
250250
var e = [], ex;
251-
var stack = 'f1(1,"abc")@file.js:40\n' +
252-
'()@file.js:41\n' +
253-
'@:0 \n' +
254-
'f44()@file.js:494';
251+
var fakeStack = 'f1@file.js:40\n' +
252+
'@file.js:41\n' +
253+
'@:0 \n' +
254+
'f44@file.js:494';
255255
e.push({
256-
stack: stack
256+
stack: fakeStack
257257
}); // test saved Firefox stacktrace
258258
function f1(arg1, arg2) {
259259
try {
@@ -272,9 +272,9 @@
272272
expect(3 * e.length);
273273
for (var i = 0; i < e.length; i++) {
274274
var stack = pst.firefox(e[i]);
275-
// equals(stack.join("\n"), '', 'debug');
276-
equals(stack[0].indexOf('f1(1,"abc")') >= 0, true, 'f1');
277-
equals(stack[1].indexOf('{anonymous}()@') >= 0, true, 'f2 anonymous');
275+
//equals(stack.join("\n"), '', 'debug');
276+
equals(stack[0].indexOf('f1') === 0, true, 'f1');
277+
equals(stack[1].indexOf('{anonymous}()') === 0, true, 'f2 anonymous');
278278
equals(stack[2].indexOf('@:0'), -1, '@:0 discarded');
279279
}
280280
});
@@ -615,13 +615,17 @@
615615
});
616616

617617
test("guessAnonymousFunction exception", function() {
618+
// FIXME: this test seems to affect guessAnonymousFunction opera11
618619
expect(1);
619620
var p = new printStackTrace.implementation();
621+
var oldGetSource = p.getSource;
620622
p.getSource = function() {
621623
throw 'permission denied';
622624
};
623625
var file = 'file:///test';
624626
equals(p.guessAnonymousFunction(file, 2), 'getSource failed with url: file:///test, exception: permission denied');
627+
// Reset mocked function
628+
p.getSource = oldGetSource;
625629
});
626630

627631
test("guessAnonymousFunctions firefox", function() {
@@ -641,12 +645,12 @@
641645
}
642646
})();
643647

644-
expect(results.length * 1);
648+
expect(results.length);
645649
for (var i = 0; i < results.length; ++i) {
646650
//equals(results[i], '', 'stack trace');
647651
var functions = p.guessAnonymousFunctions(results[i]);
648-
// equals(functions.join("\n"), '', 'stack trace after guessing');
649-
equals(functions[2].substring(0, 4), 'f2()', 'guessed f2 as 3rd result: ' + functions[2]);
652+
//equals(functions.join("\n"), '', 'stack trace after guessing');
653+
equals(functions[2].substring(0, 2), 'f2', 'guessed f2 as 3rd result: ' + functions[2]);
650654
//equals(functions[2].indexOf('f2'), 0, 'guessed f2 as 3rd result');
651655
}
652656
});
@@ -677,19 +681,20 @@
677681
equals(functions[2].indexOf('f2'), 0, 'guessed f2 in ' + functions[2]);
678682
}
679683
});
680-
684+
681685
// Test for issue #34
682686
test("guessAnonymousFunctions chrome with eval", function() {
683-
var unit = new printStackTrace.implementation(),
684-
expected = '{anonymous}()@eval at buildTmplFn (http://domain.com/file.js:17:10)',
685-
actual = unit.guessAnonymousFunctions([expected]);
687+
var unit = new printStackTrace.implementation();
688+
var expected = '{anonymous}()@eval at buildTmplFn (http://domain.com/file.js:17:10)';
689+
var actual = unit.guessAnonymousFunctions([expected]);
686690
expect(1);
687691
// Nothing should change since no anonymous function in stack
688692
equals(expected, actual);
689693
});
690694

691695
test("guessAnonymousFunctions opera9", function() {
692-
var results = [], p = new printStackTrace.implementation();
696+
var results = [];
697+
var p = new printStackTrace.implementation();
693698
var file = 'http://' + window.location.hostname + '/file.js';
694699
p.sourceCache[file] = ['var f2 = function() {', 'bar();', '};'];
695700
results.push(['{anonymous}()@' + file + ':2 -- bar();']);
@@ -716,7 +721,8 @@
716721

717722
test("guessAnonymousFunctions opera10", function() {
718723
// FIXME: currently failing in Opera 10.60
719-
var results = [], p = new printStackTrace.implementation();
724+
var results = [];
725+
var p = new printStackTrace.implementation();
720726
var file = 'http://' + window.location.hostname + '/file.js';
721727
p.sourceCache[file] = ['var f2 = function() {', 'var b = 2;', '};'];
722728
results.push(["{anonymous}()@" + file + ":1:1", "{anonymous}()@" + file + ":1:1"]);
@@ -743,7 +749,8 @@
743749
});
744750

745751
test("guessAnonymousFunctions opera11", function() {
746-
var results = [], p = new printStackTrace.implementation();
752+
var results = [];
753+
var p = new printStackTrace.implementation();
747754
var file = 'http://' + window.location.hostname + '/file.js';
748755
p.sourceCache[file] = ['var f2 = function() {', 'bar();', '};'];
749756
results.push(["{anonymous}()@" + file + ":2:1 -- bar();"]);
@@ -753,8 +760,7 @@
753760
this.undef();
754761
} catch (e) {
755762
if (p.mode(e) == 'opera11') {
756-
//alert("e.stacktrace: " + e.stacktrace);
757-
results.push(p.run(e));
763+
results.push(p.run());
758764
}
759765
}
760766
};
@@ -771,24 +777,22 @@
771777

772778
test("guessAnonymousFunctions other", function() {
773779
var results = [];
774-
var p = new printStackTrace.implementation(), mode = p.mode(UnitTest.fn.createGenericError());
775-
p._mode = 'other';
780+
var p = new printStackTrace.implementation();
776781
var file = 'http://' + window.location.hostname + '/file.js';
777782
p.sourceCache[file] = ['var f2 = function() {', 'var b = 2;', '};'];
778783
results.push(['{anonymous}()']);
779784

780-
if (mode == 'other') {
781-
var f2 = function() {
782-
try {
783-
this.undef();
784-
} catch (e) {
785+
(function f2() {
786+
try {
787+
this.undef();
788+
} catch (e) {
789+
if (p.mode(e) == 'other') {
785790
results.push(p.run());
786791
}
787-
};
788-
f2();
789-
}
792+
}
793+
})();
790794

791-
expect(1 * results.length);
795+
expect(results.length);
792796
for (var i = 0; i < results.length; ++i) {
793797
//equals((results[i]), '', 'debug');
794798
equals(p.guessAnonymousFunctions(results[i])[0].indexOf('{anonymous}'), 0, 'no file and line number in "other" mode');

test/jsTestDriver.conf

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ server: http://localhost:4224
33
load:
44
- lib/qunit.js
55
- lib/equiv.js
6+
- lib/sinon-1.2.0.js
67
- lib/sinon-qunit-1.0.0.js
78
- lib/QUnitAdapter.js
89
- ../stacktrace.js
@@ -17,4 +18,4 @@ plugin:
1718
module: "com.google.jstestdriver.coverage.CoverageModule"
1819
args: useCoberturaFormat
1920

20-
timeout: 120
21+
timeout: 120

0 commit comments

Comments
 (0)