Path: blob/trunk/javascript/webdriver/test/stacktrace_test.js
2868 views
// Licensed to the Software Freedom Conservancy (SFC) under one1// or more contributor license agreements. See the NOTICE file2// distributed with this work for additional information3// regarding copyright ownership. The SFC licenses this file4// to you under the Apache License, Version 2.0 (the5// "License"); you may not use this file except in compliance6// with the License. You may obtain a copy of the License at7//8// http://www.apache.org/licenses/LICENSE-2.09//10// Unless required by applicable law or agreed to in writing,11// software distributed under the License is distributed on an12// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY13// KIND, either express or implied. See the License for the14// specific language governing permissions and limitations15// under the License.1617goog.require('bot.Error');18goog.require('bot.ErrorCode');19goog.require('goog.string');20goog.require('goog.testing.JsUnitException');21goog.require('goog.testing.PropertyReplacer');22goog.require('goog.testing.StrictMock');23goog.require('goog.testing.jsunit');24goog.require('goog.testing.stacktrace');25goog.require('webdriver.stacktrace');26goog.require('webdriver.test.testutil');2728var stubs;2930function setUpPage() {31stubs = new goog.testing.PropertyReplacer();32}3334function tearDown() {35stubs.reset();36}373839function assertStackFrame(message, frameOrFrameString, expectedFrame) {40var frame = frameOrFrameString;41if (goog.isString(frame)) {42frame = webdriver.stacktrace.parseStackFrame_(frame);43assertNotNull(message + '\nunable to parse frame: ' + frameOrFrameString,44frame);45}46var diff = [];47for (var prop in expectedFrame) {48if (goog.isString(expectedFrame[prop]) &&49frame[prop] !== expectedFrame[prop]) {50diff.push([51prop, ': <', expectedFrame[prop], '> !== <', frame[prop], '>'52].join(''));53}54}55if (diff.length) {56fail(message +57'\nfor: <' + frameOrFrameString + '>' +58'\nexpected: <' + expectedFrame + '>' +59'\nbut was: <' + frame + '>' +60'\n ' +61diff.join('\n '));62}63}6465function assertFrame(frame, file, line) {66// Normalize path for when run through Node.js on Windows.67var url = frame.getUrl().replace(/\\/g, '/');68assertContains('/' + file, url);69assertEquals(line, frame.getLine());70}7172function testGetStacktraceFromFile() {73if (!webdriver.stacktrace.BROWSER_SUPPORTED) {74return false;75}7677var stacktrace = webdriver.test.testutil.getStackTrace();78assertFrame(stacktrace[0], 'testutil.js', 50);79assertFrame(stacktrace[1], 'stacktrace_test.js', 78);80}8182function testGetStacktraceWithUrlOnLine() {83if (!webdriver.stacktrace.BROWSER_SUPPORTED) {84return false;85}8687// The url argument is intentionally ignored here.88function getStacktraceWithUrlArgument(url) {89return webdriver.stacktrace.get();90}9192var stacktrace = getStacktraceWithUrlArgument('http://www.google.com');93assertFrame(stacktrace[0], 'stacktrace_test.js', 90);9495stacktrace = getStacktraceWithUrlArgument('http://www.google.com/search');96assertFrame(stacktrace[0], 'stacktrace_test.js', 90);97}9899function testParseStackFrameInV8() {100assertStackFrame('exception name only (node v0.8)',101' at Error (unknown source)',102new webdriver.stacktrace.Frame('', 'Error', '', 'unknown source'));103104assertStackFrame('exception name only (chrome v22)',105' at Error (<anonymous>)',106new webdriver.stacktrace.Frame('', 'Error', '', '<anonymous>'));107108assertStackFrame('context object + function name + url',109' at Object.assert (file:///.../asserts.js:29:10)',110new webdriver.stacktrace.Frame('Object', 'assert', '',111'file:///.../asserts.js:29:10'));112113assertStackFrame('context object + function name + file',114' at Object.assert (asserts.js:29:10)',115new webdriver.stacktrace.Frame('Object', 'assert', '',116'asserts.js:29:10'));117118assertStackFrame('context object + anonymous function + file',119' at Interface.<anonymous> (repl.js:182:12)',120new webdriver.stacktrace.Frame('Interface', '<anonymous>', '',121'repl.js:182:12'));122123assertStackFrame('url only',124' at http://www.example.com/jsunit.js:117:13',125new webdriver.stacktrace.Frame('', '', '',126'http://www.example.com/jsunit.js:117:13'));127128assertStackFrame('file only',129' at repl:1:57',130new webdriver.stacktrace.Frame('', '', '', 'repl:1:57'));131132assertStackFrame('function alias',133' at [object Object].exec [as execute] (file:///foo)',134new webdriver.stacktrace.Frame('[object Object]', 'exec',135'execute', 'file:///foo'));136137assertStackFrame('constructor call',138' at new Class (file:///foo)',139new webdriver.stacktrace.Frame('new ', 'Class', '', 'file:///foo'));140141assertStackFrame('object property constructor call',142' at new [object Object].foo (repl:1:2)',143new webdriver.stacktrace.Frame('new [object Object]', 'foo', '',144'repl:1:2'));145146assertStackFrame('namespaced constructor call',147' at new foo.bar.Class (foo:1:2)',148new webdriver.stacktrace.Frame('new foo.bar', 'Class', '', 'foo:1:2'));149150assertStackFrame('anonymous constructor call',151' at new <anonymous> (file:///foo)',152new webdriver.stacktrace.Frame('new ', '<anonymous>', '',153'file:///foo'));154155assertStackFrame('native function call',156' at Array.forEach (native)',157new webdriver.stacktrace.Frame('Array', 'forEach', '', 'native'));158159assertStackFrame('eval',160' at foo (eval at file://bar)',161new webdriver.stacktrace.Frame('', 'foo', '', 'eval at file://bar'));162163assertStackFrame('nested anonymous eval',164' at eval (eval at <anonymous> (unknown source), <anonymous>:2:7)',165new webdriver.stacktrace.Frame('', 'eval', '',166'eval at <anonymous> (unknown source), <anonymous>:2:7'));167168assertStackFrame('Url containing parentheses',169' at Object.assert (http://bar:4000/bar.js?value=(a):150:3)',170new webdriver.stacktrace.Frame('Object', 'assert', '',171'http://bar:4000/bar.js?value=(a):150:3'));172173assertStackFrame('Frame with non-standard leading whitespace (issue 7994)',174' at module.exports.runCucumber (/local/dir/path)',175new webdriver.stacktrace.Frame('module.exports', 'runCucumber', '',176'/local/dir/path'));177}178179function testParseClosureCanonicalStackFrame() {180assertStackFrame('unknown frame', '> (unknown)',181webdriver.stacktrace.ANONYMOUS_FRAME_);182assertStackFrame('anonymous frame', '> anonymous',183webdriver.stacktrace.ANONYMOUS_FRAME_);184185assertStackFrame('name only', '> foo',186new webdriver.stacktrace.Frame('', 'foo', '', ''));187188assertStackFrame('name and path', '> foo at http://x:123',189new webdriver.stacktrace.Frame('', 'foo', '', 'http://x:123'));190191assertStackFrame('anonymous function with path',192'> anonymous at file:///x/y/z',193new webdriver.stacktrace.Frame('', 'anonymous', '', 'file:///x/y/z'));194195assertStackFrame('anonymous function with v8 path',196'> anonymous at /x/y/z:12:34',197new webdriver.stacktrace.Frame('', 'anonymous', '', '/x/y/z:12:34'));198199assertStackFrame('context and name only',200'> foo.bar', new webdriver.stacktrace.Frame('foo', 'bar', '', ''));201202assertStackFrame('name and alias',203'> foo [as bar]', new webdriver.stacktrace.Frame('', 'foo', 'bar', ''));204205assertStackFrame('context, name, and alias',206'> foo.bar [as baz]',207new webdriver.stacktrace.Frame('foo', 'bar', 'baz', ''));208209assertStackFrame('path only', '> http://x:123',210new webdriver.stacktrace.Frame('', '', '', 'http://x:123'));211212assertStackFrame('name and arguments',213'> foo(arguments)', new webdriver.stacktrace.Frame('', 'foo', '', ''));214215assertStackFrame('full frame',216'> foo.bar(123, "abc") [as baz] at http://x:123',217new webdriver.stacktrace.Frame('foo', 'bar', 'baz', 'http://x:123'));218219assertStackFrame('name and url with sub-domain',220'> foo at http://x.y.z:80/path:1:2',221new webdriver.stacktrace.Frame('', 'foo', '',222'http://x.y.z:80/path:1:2'));223224assertStackFrame('name and url with sub-domain',225'> foo.bar.baz at http://x.y.z:80/path:1:2',226new webdriver.stacktrace.Frame('foo.bar', 'baz', '',227'http://x.y.z:80/path:1:2'));228}229230// All test strings are parsed with the conventional and long231// frame algorithms.232function testParseStackFrameInFirefox() {233var frameString = 'Error("Assertion failed")@:0';234var frame = webdriver.stacktrace.parseStackFrame_(frameString);235var expected = new webdriver.stacktrace.Frame('', 'Error', '', '');236assertObjectEquals('function name + arguments', expected, frame);237238frame = webdriver.stacktrace.parseLongFirefoxFrame_(frameString);239assertObjectEquals('function name + arguments', expected, frame);240241frameString = '()@file:///foo:42';242frame = webdriver.stacktrace.parseStackFrame_(frameString);243expected = new webdriver.stacktrace.Frame('', '', '', 'file:///foo:42');244assertObjectEquals('anonymous function', expected, frame);245246frame = webdriver.stacktrace.parseLongFirefoxFrame_(frameString);247assertObjectEquals('anonymous function', expected, frame);248249frameString = '@javascript:alert(0)';250frame = webdriver.stacktrace.parseStackFrame_(frameString);251expected = new webdriver.stacktrace.Frame('', '', '', 'javascript:alert(0)');252assertObjectEquals('anonymous function', expected, frame);253254frame = webdriver.stacktrace.parseLongFirefoxFrame_(frameString);255assertObjectEquals('anonymous function', expected, frame);256}257258function testStringRepresentation() {259var frame = new webdriver.stacktrace.Frame('window', 'foo', 'bar',260'http://x?a=1&b=2:1');261assertEquals(' at window.foo [as bar] (http://x?a=1&b=2:1)',262frame.toString());263264frame = new webdriver.stacktrace.Frame('', 'Error', '', '');265assertEquals(' at Error (<anonymous>)', frame.toString());266267assertEquals(' at <anonymous>',268webdriver.stacktrace.ANONYMOUS_FRAME_.toString());269270frame = new webdriver.stacktrace.Frame('', '', '', 'http://x:123');271assertEquals(' at http://x:123', frame.toString());272273frame = new webdriver.stacktrace.Frame('foo', 'bar', '', 'http://x:123');274assertEquals(' at foo.bar (http://x:123)', frame.toString());275276frame = new webdriver.stacktrace.Frame('new ', 'Foo', '', 'http://x:123');277assertEquals(' at new Foo (http://x:123)', frame.toString());278279frame = new webdriver.stacktrace.Frame('new foo', 'Bar', '', '');280assertEquals(' at new foo.Bar (<anonymous>)', frame.toString());281}282283// Create a stack trace string with one modest record and one long record,284// Verify that all frames are parsed. The length of the long arg is set285// to blow Firefox 3x's stack if put through a RegExp.286function testParsingLongStackTrace() {287var longArg = goog.string.buildString(288'(', goog.string.repeat('x', 1000000), ')');289var stackTrace = goog.string.buildString(290'shortFrame()@:0\n',291'longFrame',292longArg,293'@http://google.com/somescript:0\n');294var frames = webdriver.stacktrace.parse_(stackTrace);295assertEquals('number of returned frames', 2, frames.length);296var expected = new webdriver.stacktrace.Frame('', 'shortFrame', '', '');297assertStackFrame('short frame', frames[0], expected);298299expected = new webdriver.stacktrace.Frame(300'', 'longFrame', '', 'http://google.com/somescript:0');301assertStackFrame('exception name only', frames[1], expected);302}303304function testRemovesV8MessageHeaderBeforeParsingStack() {305var stack =306' at Color.red (http://x:1234)\n' +307' at Foo.bar (http://y:5678)';308309var frames = webdriver.stacktrace.parse_(stack);310assertEquals(2, frames.length);311assertEquals(' at Color.red (http://x:1234)', frames[0].toString());312assertEquals(' at Foo.bar (http://y:5678)', frames[1].toString());313}314315function testCanParseClosureJsUnitExceptions() {316stubs.set(goog.testing.stacktrace, 'get', function() {317return '> Color.red at http://x:1234\n' +318'> Foo.bar at http://y:5678';319});320321var error = new goog.testing.JsUnitException('stub');322stubs.reset();323324var frames = webdriver.stacktrace.parse_(error.stackTrace);325assertEquals(2, frames.length);326assertEquals(' at Color.red (http://x:1234)', frames[0].toString());327assertEquals(' at Foo.bar (http://y:5678)', frames[1].toString());328}329330function testFormattingAV8StyleError() {331var errorStub = {332name: 'Error',333message: 'foo',334toString: function() { return 'Error: foo'; },335stack:336'Error: foo\n' +337' at Color.red (http://x:1234)\n' +338' at Foo.bar (http://y:5678)'339};340341var ret = webdriver.stacktrace.format(errorStub);342assertEquals(errorStub, ret);343assertEquals([344'Error: foo',345' at Color.red (http://x:1234)',346' at Foo.bar (http://y:5678)'347].join('\n'), ret.stack);348}349350function testFormattingAFirefoxStyleError() {351var errorStub = {352name: 'Error',353message: 'boom',354toString: function() { return 'Error: boom'; },355stack:356'foo@file:///foo/foo.js:1\n' +357'@file:///bar/bar.js:1'358};359360var ret = webdriver.stacktrace.format(errorStub);361assertEquals(errorStub, ret);362assertEquals([363'Error: boom',364' at foo (file:///foo/foo.js:1)',365' at file:///bar/bar.js:1'366].join('\n'), ret.stack);367}368369function testInsertsAnAnonymousFrameWhenUnableToParse() {370var errorStub = {371name: 'Error',372message: 'boom',373toString: function() { return 'Error: boom'; },374stack:375'foo@file:///foo/foo.js:1\n' +376'this is unparsable garbage\n' +377'@file:///bar/bar.js:1'378};379380var ret = webdriver.stacktrace.format(errorStub);381assertEquals(errorStub, ret);382assertEquals([383'Error: boom',384' at foo (file:///foo/foo.js:1)',385' at <anonymous>',386' at file:///bar/bar.js:1'387].join('\n'), ret.stack);388}389390function testFormattingBotErrors() {391var error = new bot.Error(bot.ErrorCode.NO_SUCH_ELEMENT, 'boom');392var expectedStack = [393'NoSuchElementError: boom',394' at Color.red (http://x:1234)',395' at Foo.bar (http://y:5678)'396].join('\n');397error.stack = expectedStack;398399var ret = webdriver.stacktrace.format(error);400assertEquals(ret, error);401assertEquals(expectedStack, error.stack);402}403404function testFormatsUsingNameAndMessageIfAvailable() {405var ret = webdriver.stacktrace.format({406name: 'TypeError',407message: 'boom'408});409assertEquals('TypeError: boom\n', ret.stack);410411ret = webdriver.stacktrace.format({412message: 'boom'413});414assertEquals('boom\n', ret.stack);415416ret = webdriver.stacktrace.format({417toString: function() {418return 'Hello world'419}420});421assertEquals('Hello world\n', ret.stack);422423ret = webdriver.stacktrace.format({424name: 'TypeError',425message: 'boom',426toString: function() {427return 'Should not use this'428}429});430assertEquals('TypeError: boom\n', ret.stack);431}432433function testDoesNotFormatErrorIfOriginalStacktraceIsInAnUnexpectedFormat() {434var error = Error('testing');435var stack = error.stack = [436'Error: testing',437'..> at Color.red (http://x:1234)',438'..> at Foo.bar (http://y:5678)'439].join('\n');440441var ret = webdriver.stacktrace.format(error);442assertEquals(ret, error);443assertEquals(stack, error.stack);444}445446function testParseStackFrameInIE10() {447assertStackFrame('name and path',448' at foo (http://bar:4000/bar.js:150:3)',449new webdriver.stacktrace.Frame('', 'foo', '',450'http://bar:4000/bar.js:150:3'));451452assertStackFrame('Anonymous function',453' at Anonymous function (http://bar:4000/bar.js:150:3)',454new webdriver.stacktrace.Frame('', 'Anonymous function', '',455'http://bar:4000/bar.js:150:3'));456457assertStackFrame('Global code',458' at Global code (http://bar:4000/bar.js:150:3)',459new webdriver.stacktrace.Frame('', 'Global code', '',460'http://bar:4000/bar.js:150:3'));461462assertStackFrame('eval code',463' at foo (eval code:150:3)',464new webdriver.stacktrace.Frame('', 'foo', '', 'eval code:150:3'));465466assertStackFrame('nested eval',467' at eval code (eval code:150:3)',468new webdriver.stacktrace.Frame('', 'eval code', '', 'eval code:150:3'));469470assertStackFrame('Url containing parentheses',471' at foo (http://bar:4000/bar.js?value=(a):150:3)',472new webdriver.stacktrace.Frame('', 'foo', '',473'http://bar:4000/bar.js?value=(a):150:3'));474475assertStackFrame('Url ending with parentheses',476' at foo (http://bar:4000/bar.js?a=))',477new webdriver.stacktrace.Frame('', 'foo', '',478'http://bar:4000/bar.js?a=)'));479}480481482