michael@0: /* vim:set ts=2 sw=2 sts=2 et: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: const TEST_URI = "http://example.com/browser/dom/tests/browser/test-console-api.html"; michael@0: michael@0: var gWindow, gLevel, gArgs, gTestDriver, gStyle; michael@0: michael@0: function test() { michael@0: waitForExplicitFinish(); michael@0: michael@0: var tab = gBrowser.addTab(TEST_URI); michael@0: gBrowser.selectedTab = tab; michael@0: var browser = gBrowser.selectedBrowser; michael@0: michael@0: registerCleanupFunction(function () { michael@0: gWindow = gLevel = gArgs = gTestDriver = null; michael@0: gBrowser.removeTab(tab); michael@0: }); michael@0: michael@0: ConsoleObserver.init(); michael@0: michael@0: browser.addEventListener("DOMContentLoaded", function onLoad(event) { michael@0: browser.removeEventListener("DOMContentLoaded", onLoad, false); michael@0: executeSoon(function test_executeSoon() { michael@0: gWindow = browser.contentWindow; michael@0: consoleAPISanityTest(); michael@0: gTestDriver = observeConsoleTest(); michael@0: gTestDriver.next(); michael@0: }); michael@0: michael@0: }, false); michael@0: } michael@0: michael@0: function testConsoleData(aMessageObject) { michael@0: let messageWindow = Services.wm.getOuterWindowWithId(aMessageObject.ID); michael@0: is(messageWindow, gWindow, "found correct window by window ID"); michael@0: michael@0: is(aMessageObject.level, gLevel, "expected level received"); michael@0: ok(aMessageObject.arguments, "we have arguments"); michael@0: michael@0: switch (gLevel) { michael@0: case "trace": { michael@0: is(aMessageObject.arguments.length, 0, "arguments.length matches"); michael@0: is(aMessageObject.stacktrace.toSource(), gArgs.toSource(), michael@0: "stack trace is correct"); michael@0: break michael@0: } michael@0: case "count": { michael@0: is(aMessageObject.counter.label, gArgs[0].label, "label matches"); michael@0: is(aMessageObject.counter.count, gArgs[0].count, "count matches"); michael@0: break; michael@0: } michael@0: default: { michael@0: is(aMessageObject.arguments.length, gArgs.length, "arguments.length matches"); michael@0: gArgs.forEach(function (a, i) { michael@0: // Waive Xray so that we don't get messed up by Xray ToString. michael@0: // michael@0: // It'd be nice to just use XPCNativeWrapper.unwrap here, but there are michael@0: // a number of dumb reasons we can't. See bug 868675. michael@0: var arg = aMessageObject.arguments[i]; michael@0: if (Components.utils.isXrayWrapper(arg)) michael@0: arg = arg.wrappedJSObject; michael@0: is(arg, a, "correct arg " + i); michael@0: }); michael@0: michael@0: if (gStyle) { michael@0: is(aMessageObject.styles.length, gStyle.length, "styles.length matches"); michael@0: is(aMessageObject.styles + "", gStyle + "", "styles match"); michael@0: } else { michael@0: ok(!aMessageObject.styles || aMessageObject.styles.length === 0, michael@0: "styles match"); michael@0: } michael@0: } michael@0: } michael@0: michael@0: gTestDriver.next(); michael@0: } michael@0: michael@0: function testLocationData(aMessageObject) { michael@0: let messageWindow = Services.wm.getOuterWindowWithId(aMessageObject.ID); michael@0: is(messageWindow, gWindow, "found correct window by window ID"); michael@0: michael@0: is(aMessageObject.level, gLevel, "expected level received"); michael@0: ok(aMessageObject.arguments, "we have arguments"); michael@0: michael@0: is(aMessageObject.filename, gArgs[0].filename, "filename matches"); michael@0: is(aMessageObject.lineNumber, gArgs[0].lineNumber, "lineNumber matches"); michael@0: is(aMessageObject.functionName, gArgs[0].functionName, "functionName matches"); michael@0: is(aMessageObject.arguments.length, gArgs[0].arguments.length, "arguments.length matches"); michael@0: gArgs[0].arguments.forEach(function (a, i) { michael@0: is(aMessageObject.arguments[i], a, "correct arg " + i); michael@0: }); michael@0: michael@0: startNativeCallbackTest(); michael@0: } michael@0: michael@0: function startNativeCallbackTest() { michael@0: // Reset the observer function to cope with the fabricated test data. michael@0: ConsoleObserver.observe = function CO_observe(aSubject, aTopic, aData) { michael@0: try { michael@0: testNativeCallback(aSubject.wrappedJSObject); michael@0: } catch (ex) { michael@0: // XXX Bug 906593 - Exceptions in this function currently aren't michael@0: // reported, because of some XPConnect weirdness, so report them manually michael@0: ok(false, "Exception thrown in CO_observe: " + ex); michael@0: } michael@0: }; michael@0: michael@0: let button = gWindow.document.getElementById("test-nativeCallback"); michael@0: ok(button, "found #test-nativeCallback button"); michael@0: EventUtils.synthesizeMouseAtCenter(button, {}, gWindow); michael@0: } michael@0: michael@0: function testNativeCallback(aMessageObject) { michael@0: is(aMessageObject.level, "log", "expected level received"); michael@0: is(aMessageObject.filename, "", "filename matches"); michael@0: is(aMessageObject.lineNumber, 0, "lineNumber matches"); michael@0: is(aMessageObject.functionName, "", "functionName matches"); michael@0: michael@0: startGroupTest(); michael@0: } michael@0: michael@0: function startGroupTest() { michael@0: // Reset the observer function to cope with the fabricated test data. michael@0: ConsoleObserver.observe = function CO_observe(aSubject, aTopic, aData) { michael@0: try { michael@0: testConsoleGroup(aSubject.wrappedJSObject); michael@0: } catch (ex) { michael@0: // XXX Bug 906593 - Exceptions in this function currently aren't michael@0: // reported, because of some XPConnect weirdness, so report them manually michael@0: ok(false, "Exception thrown in CO_observe: " + ex); michael@0: } michael@0: }; michael@0: let button = gWindow.document.getElementById("test-groups"); michael@0: ok(button, "found #test-groups button"); michael@0: EventUtils.synthesizeMouseAtCenter(button, {}, gWindow); michael@0: } michael@0: michael@0: function testConsoleGroup(aMessageObject) { michael@0: let messageWindow = Services.wm.getOuterWindowWithId(aMessageObject.ID); michael@0: is(messageWindow, gWindow, "found correct window by window ID"); michael@0: michael@0: ok(aMessageObject.level == "group" || michael@0: aMessageObject.level == "groupCollapsed" || michael@0: aMessageObject.level == "groupEnd", michael@0: "expected level received"); michael@0: michael@0: is(aMessageObject.functionName, "testGroups", "functionName matches"); michael@0: ok(aMessageObject.lineNumber >= 46 && aMessageObject.lineNumber <= 50, michael@0: "lineNumber matches"); michael@0: if (aMessageObject.level == "groupCollapsed") { michael@0: is(aMessageObject.groupName, "a group", "groupCollapsed groupName matches"); michael@0: is(aMessageObject.arguments[0], "a", "groupCollapsed arguments[0] matches"); michael@0: is(aMessageObject.arguments[1], "group", "groupCollapsed arguments[0] matches"); michael@0: } michael@0: else if (aMessageObject.level == "group") { michael@0: is(aMessageObject.groupName, "b group", "group groupName matches"); michael@0: is(aMessageObject.arguments[0], "b", "group arguments[0] matches"); michael@0: is(aMessageObject.arguments[1], "group", "group arguments[1] matches"); michael@0: } michael@0: else if (aMessageObject.level == "groupEnd") { michael@0: let groupName = Array.prototype.join.call(aMessageObject.arguments, " "); michael@0: is(groupName,"b group", "groupEnd arguments matches"); michael@0: is(aMessageObject.groupName, "b group", "groupEnd groupName matches"); michael@0: } michael@0: michael@0: if (aMessageObject.level == "groupEnd") { michael@0: startTimeTest(); michael@0: } michael@0: } michael@0: michael@0: function startTraceTest() { michael@0: gLevel = "trace"; michael@0: gArgs = [ michael@0: {filename: TEST_URI, functionName: "window.foobar585956c", language: 2, lineNumber: 6}, michael@0: {filename: TEST_URI, functionName: "foobar585956b", language: 2, lineNumber: 11}, michael@0: {filename: TEST_URI, functionName: "foobar585956a", language: 2, lineNumber: 15}, michael@0: {filename: TEST_URI, functionName: "onclick", language: 2, lineNumber: 1} michael@0: ]; michael@0: michael@0: let button = gWindow.document.getElementById("test-trace"); michael@0: ok(button, "found #test-trace button"); michael@0: EventUtils.synthesizeMouseAtCenter(button, {}, gWindow); michael@0: } michael@0: michael@0: function startLocationTest() { michael@0: // Reset the observer function to cope with the fabricated test data. michael@0: ConsoleObserver.observe = function CO_observe(aSubject, aTopic, aData) { michael@0: try { michael@0: testLocationData(aSubject.wrappedJSObject); michael@0: } catch (ex) { michael@0: // XXX Bug 906593 - Exceptions in this function currently aren't michael@0: // reported, because of some XPConnect weirdness, so report them manually michael@0: ok(false, "Exception thrown in CO_observe: " + ex); michael@0: } michael@0: }; michael@0: gLevel = "log"; michael@0: gArgs = [ michael@0: {filename: TEST_URI, functionName: "foobar646025", arguments: ["omg", "o", "d"], lineNumber: 19} michael@0: ]; michael@0: michael@0: let button = gWindow.document.getElementById("test-location"); michael@0: ok(button, "found #test-location button"); michael@0: EventUtils.synthesizeMouseAtCenter(button, {}, gWindow); michael@0: } michael@0: michael@0: function expect(level) { michael@0: gLevel = level; michael@0: gArgs = Array.slice(arguments, 1); michael@0: } michael@0: michael@0: function observeConsoleTest() { michael@0: let win = XPCNativeWrapper.unwrap(gWindow); michael@0: expect("log", "arg"); michael@0: win.console.log("arg"); michael@0: yield undefined; michael@0: michael@0: expect("info", "arg", "extra arg"); michael@0: win.console.info("arg", "extra arg"); michael@0: yield undefined; michael@0: michael@0: expect("warn", "Lesson 1: PI is approximately equal to 3"); michael@0: win.console.warn("Lesson %d: %s is approximately equal to %1.0f", michael@0: 1, michael@0: "PI", michael@0: 3.14159); michael@0: yield undefined; michael@0: michael@0: expect("warn", "Lesson 1: PI is approximately equal to 3.14"); michael@0: win.console.warn("Lesson %d: %s is approximately equal to %1.2f", michael@0: 1, michael@0: "PI", michael@0: 3.14159); michael@0: yield undefined; michael@0: michael@0: expect("warn", "Lesson 1: PI is approximately equal to 3.141590"); michael@0: win.console.warn("Lesson %d: %s is approximately equal to %f", michael@0: 1, michael@0: "PI", michael@0: 3.14159); michael@0: yield undefined; michael@0: michael@0: expect("warn", "Lesson 1: PI is approximately equal to 3.1415900"); michael@0: win.console.warn("Lesson %d: %s is approximately equal to %0.7f", michael@0: 1, michael@0: "PI", michael@0: 3.14159); michael@0: yield undefined; michael@0: michael@0: expect("log", "%d, %s, %l"); michael@0: win.console.log("%d, %s, %l"); michael@0: yield undefined; michael@0: michael@0: expect("log", "%a %b %g"); michael@0: win.console.log("%a %b %g"); michael@0: yield undefined; michael@0: michael@0: expect("log", "%a %b %g", "a", "b"); michael@0: win.console.log("%a %b %g", "a", "b"); michael@0: yield undefined; michael@0: michael@0: expect("log", "2, a, %l", 3); michael@0: win.console.log("%d, %s, %l", 2, "a", 3); michael@0: yield undefined; michael@0: michael@0: // Bug #692550 handle null and undefined. michael@0: expect("log", "null, undefined"); michael@0: win.console.log("%s, %s", null, undefined); michael@0: yield undefined; michael@0: michael@0: // Bug #696288 handle object as first argument. michael@0: let obj = { a: 1 }; michael@0: expect("log", obj, "a"); michael@0: win.console.log(obj, "a"); michael@0: yield undefined; michael@0: michael@0: expect("dir", win.toString()); michael@0: win.console.dir(win); michael@0: yield undefined; michael@0: michael@0: expect("error", "arg"); michael@0: win.console.error("arg"); michael@0: yield undefined; michael@0: michael@0: expect("exception", "arg"); michael@0: win.console.exception("arg"); michael@0: yield undefined; michael@0: michael@0: expect("log", "foobar"); michael@0: gStyle = ["color:red;foobar;;"]; michael@0: win.console.log("%cfoobar", gStyle[0]); michael@0: yield undefined; michael@0: michael@0: let obj4 = { d: 4 }; michael@0: expect("warn", "foobar", obj4, "test", "bazbazstr", "last"); michael@0: gStyle = [null, null, null, "color:blue;", "color:red"]; michael@0: win.console.warn("foobar%Otest%cbazbaz%s%clast", obj4, gStyle[3], "str", gStyle[4]); michael@0: yield undefined; michael@0: michael@0: let obj3 = { c: 3 }; michael@0: expect("info", "foobar", "bazbaz", obj3, "%comg", "color:yellow"); michael@0: gStyle = [null, "color:pink;"]; michael@0: win.console.info("foobar%cbazbaz", gStyle[1], obj3, "%comg", "color:yellow"); michael@0: yield undefined; michael@0: michael@0: gStyle = null; michael@0: let obj2 = { b: 2 }; michael@0: expect("log", "omg ", obj, " foo ", 4, obj2); michael@0: win.console.log("omg %o foo %o", obj, 4, obj2); michael@0: yield undefined; michael@0: michael@0: expect("assert", "message"); michael@0: win.console.assert(false, "message"); michael@0: yield undefined; michael@0: michael@0: expect("count", { label: "label a", count: 1 }) michael@0: win.console.count("label a"); michael@0: yield undefined; michael@0: michael@0: expect("count", { label: "label b", count: 1 }) michael@0: win.console.count("label b"); michael@0: yield undefined; michael@0: michael@0: expect("count", { label: "label a", count: 2 }) michael@0: win.console.count("label a"); michael@0: yield undefined; michael@0: michael@0: expect("count", { label: "label b", count: 2 }) michael@0: win.console.count("label b"); michael@0: yield undefined; michael@0: michael@0: startTraceTest(); michael@0: yield undefined; michael@0: michael@0: startLocationTest(); michael@0: yield undefined; michael@0: } michael@0: michael@0: function consoleAPISanityTest() { michael@0: let win = XPCNativeWrapper.unwrap(gWindow); michael@0: ok(win.console, "we have a console attached"); michael@0: ok(win.console, "we have a console attached, 2nd attempt"); michael@0: michael@0: ok(win.console.log, "console.log is here"); michael@0: ok(win.console.info, "console.info is here"); michael@0: ok(win.console.warn, "console.warn is here"); michael@0: ok(win.console.error, "console.error is here"); michael@0: ok(win.console.exception, "console.exception is here"); michael@0: ok(win.console.trace, "console.trace is here"); michael@0: ok(win.console.dir, "console.dir is here"); michael@0: ok(win.console.group, "console.group is here"); michael@0: ok(win.console.groupCollapsed, "console.groupCollapsed is here"); michael@0: ok(win.console.groupEnd, "console.groupEnd is here"); michael@0: ok(win.console.time, "console.time is here"); michael@0: ok(win.console.timeEnd, "console.timeEnd is here"); michael@0: ok(win.console.assert, "console.assert is here"); michael@0: ok(win.console.count, "console.count is here"); michael@0: } michael@0: michael@0: function startTimeTest() { michael@0: // Reset the observer function to cope with the fabricated test data. michael@0: ConsoleObserver.observe = function CO_observe(aSubject, aTopic, aData) { michael@0: try { michael@0: testConsoleTime(aSubject.wrappedJSObject); michael@0: } catch (ex) { michael@0: // XXX Bug 906593 - Exceptions in this function currently aren't michael@0: // reported, because of some XPConnect weirdness, so report them manually michael@0: ok(false, "Exception thrown in CO_observe: " + ex); michael@0: } michael@0: }; michael@0: gLevel = "time"; michael@0: gArgs = [ michael@0: {filename: TEST_URI, lineNumber: 23, functionName: "startTimer", michael@0: arguments: ["foo"], michael@0: timer: { name: "foo" }, michael@0: } michael@0: ]; michael@0: michael@0: let button = gWindow.document.getElementById("test-time"); michael@0: ok(button, "found #test-time button"); michael@0: EventUtils.synthesizeMouseAtCenter(button, {}, gWindow); michael@0: } michael@0: michael@0: function testConsoleTime(aMessageObject) { michael@0: let messageWindow = Services.wm.getOuterWindowWithId(aMessageObject.ID); michael@0: is(messageWindow, gWindow, "found correct window by window ID"); michael@0: michael@0: is(aMessageObject.level, gLevel, "expected level received"); michael@0: michael@0: is(aMessageObject.filename, gArgs[0].filename, "filename matches"); michael@0: is(aMessageObject.lineNumber, gArgs[0].lineNumber, "lineNumber matches"); michael@0: is(aMessageObject.functionName, gArgs[0].functionName, "functionName matches"); michael@0: is(aMessageObject.timer.name, gArgs[0].timer.name, "timer.name matches"); michael@0: ok(aMessageObject.timer.started, "timer.started exists"); michael@0: michael@0: gArgs[0].arguments.forEach(function (a, i) { michael@0: is(aMessageObject.arguments[i], a, "correct arg " + i); michael@0: }); michael@0: michael@0: startTimeEndTest(); michael@0: } michael@0: michael@0: function startTimeEndTest() { michael@0: // Reset the observer function to cope with the fabricated test data. michael@0: ConsoleObserver.observe = function CO_observe(aSubject, aTopic, aData) { michael@0: try { michael@0: testConsoleTimeEnd(aSubject.wrappedJSObject); michael@0: } catch (ex) { michael@0: // XXX Bug 906593 - Exceptions in this function currently aren't michael@0: // reported, because of some XPConnect weirdness, so report them manually michael@0: ok(false, "Exception thrown in CO_observe: " + ex); michael@0: } michael@0: }; michael@0: gLevel = "timeEnd"; michael@0: gArgs = [ michael@0: {filename: TEST_URI, lineNumber: 27, functionName: "stopTimer", michael@0: arguments: ["foo"], michael@0: timer: { name: "foo" }, michael@0: }, michael@0: ]; michael@0: michael@0: let button = gWindow.document.getElementById("test-timeEnd"); michael@0: ok(button, "found #test-timeEnd button"); michael@0: EventUtils.synthesizeMouseAtCenter(button, {}, gWindow); michael@0: } michael@0: michael@0: function testConsoleTimeEnd(aMessageObject) { michael@0: let messageWindow = Services.wm.getOuterWindowWithId(aMessageObject.ID); michael@0: is(messageWindow, gWindow, "found correct window by window ID"); michael@0: michael@0: is(aMessageObject.level, gLevel, "expected level received"); michael@0: ok(aMessageObject.arguments, "we have arguments"); michael@0: michael@0: is(aMessageObject.filename, gArgs[0].filename, "filename matches"); michael@0: is(aMessageObject.lineNumber, gArgs[0].lineNumber, "lineNumber matches"); michael@0: is(aMessageObject.functionName, gArgs[0].functionName, "functionName matches"); michael@0: is(aMessageObject.arguments.length, gArgs[0].arguments.length, "arguments.length matches"); michael@0: is(aMessageObject.timer.name, gArgs[0].timer.name, "timer name matches"); michael@0: is(typeof aMessageObject.timer.duration, "number", "timer duration is a number"); michael@0: info("timer duration: " + aMessageObject.timer.duration); michael@0: ok(aMessageObject.timer.duration >= 0, "timer duration is positive"); michael@0: michael@0: gArgs[0].arguments.forEach(function (a, i) { michael@0: is(aMessageObject.arguments[i], a, "correct arg " + i); michael@0: }); michael@0: michael@0: startEmptyTimerTest(); michael@0: } michael@0: michael@0: function startEmptyTimerTest() { michael@0: // Reset the observer function to cope with the fabricated test data. michael@0: ConsoleObserver.observe = function CO_observe(aSubject, aTopic, aData) { michael@0: try { michael@0: testEmptyTimer(aSubject.wrappedJSObject); michael@0: } catch (ex) { michael@0: // XXX Bug 906593 - Exceptions in this function currently aren't michael@0: // reported, because of some XPConnect weirdness, so report them manually michael@0: ok(false, "Exception thrown in CO_observe: " + ex); michael@0: } michael@0: }; michael@0: michael@0: let button = gWindow.document.getElementById("test-namelessTimer"); michael@0: ok(button, "found #test-namelessTimer button"); michael@0: EventUtils.synthesizeMouseAtCenter(button, {}, gWindow); michael@0: } michael@0: michael@0: function testEmptyTimer(aMessageObject) { michael@0: let messageWindow = Services.wm.getOuterWindowWithId(aMessageObject.ID); michael@0: is(messageWindow, gWindow, "found correct window by window ID"); michael@0: michael@0: ok(aMessageObject.level == "time" || aMessageObject.level == "timeEnd", michael@0: "expected level received"); michael@0: is(aMessageObject.arguments.length, 0, "we don't have arguments"); michael@0: ok(!aMessageObject.timer, "we don't have a timer"); michael@0: michael@0: is(aMessageObject.functionName, "namelessTimer", "functionName matches"); michael@0: ok(aMessageObject.lineNumber == 31 || aMessageObject.lineNumber == 32, michael@0: "lineNumber matches"); michael@0: // Test finished michael@0: ConsoleObserver.destroy(); michael@0: finish(); michael@0: } michael@0: michael@0: var ConsoleObserver = { michael@0: QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]), michael@0: michael@0: init: function CO_init() { michael@0: Services.obs.addObserver(this, "console-api-log-event", false); michael@0: }, michael@0: michael@0: destroy: function CO_destroy() { michael@0: Services.obs.removeObserver(this, "console-api-log-event"); michael@0: }, michael@0: michael@0: observe: function CO_observe(aSubject, aTopic, aData) { michael@0: try { michael@0: testConsoleData(aSubject.wrappedJSObject); michael@0: } catch (ex) { michael@0: // XXX Bug 906593 - Exceptions in this function currently aren't michael@0: // reported, because of some XPConnect weirdness, so report them manually michael@0: ok(false, "Exception thrown in CO_observe: " + ex); michael@0: } michael@0: } michael@0: }; michael@0: michael@0: function getWindowId(aWindow) michael@0: { michael@0: return aWindow.QueryInterface(Ci.nsIInterfaceRequestor) michael@0: .getInterface(Ci.nsIDOMWindowUtils) michael@0: .outerWindowID; michael@0: }