Bug 859858 - Fix for intermittent browser_bug664688_sandbox_update_after_navigation.js | Timed out while waiting for: window.location.href result is displayed after goBack(), browser_repeated_messages_accuracy.js | Timed out while waiting for: messages displayed; r=past

This commit is contained in:
Mihai Sucan 2013-04-10 18:22:29 +03:00
parent b65f942f9f
commit 4e5330b7b6
7 changed files with 391 additions and 170 deletions

View File

@ -13,6 +13,7 @@ function test()
const TEST_URI2 = "http://example.org/browser/browser/devtools/webconsole/test/test-console.html";
let hud;
let msgForLocation1;
waitForExplicitFinish();
@ -22,7 +23,6 @@ function test()
openConsole(gBrowser.selectedTab, pageLoad1);
}, true);
function pageLoad1(aHud)
{
hud = aHud;
@ -30,60 +30,63 @@ function test()
hud.jsterm.clearOutput();
hud.jsterm.execute("window.location.href");
waitForSuccess(waitForLocation1);
}
info("wait for window.location.href");
let waitForLocation1 = {
name: "window.location.href result is displayed",
validatorFn: function()
{
let node = hud.outputNode.getElementsByClassName("webconsole-msg-output")[0];
return node && node.textContent.indexOf(TEST_URI1) > -1;
},
successFn: function()
{
let node = hud.outputNode.getElementsByClassName("webconsole-msg-input")[0];
isnot(node.textContent.indexOf("window.location.href"), -1,
"jsterm input is also displayed");
is(hud.outputNode.textContent.indexOf("Permission denied"), -1,
"no permission denied errors");
msgForLocation1 = {
webconsole: hud,
messages: [
{
name: "window.location.href jsterm input",
text: "window.location.href",
category: CATEGORY_INPUT,
},
{
name: "window.location.href result is displayed",
text: TEST_URI1,
category: CATEGORY_OUTPUT,
},
]
};
waitForMessages(msgForLocation1).then(() => {
gBrowser.selectedBrowser.addEventListener("load", onPageLoad2, true);
content.location = TEST_URI2;
},
failureFn: finishTestWithError,
};
});
}
function onPageLoad2() {
gBrowser.selectedBrowser.removeEventListener("load", onPageLoad2, true);
is(hud.outputNode.textContent.indexOf("Permission denied"), -1,
"no permission denied errors");
hud.jsterm.clearOutput();
hud.jsterm.execute("window.location.href");
waitForSuccess(waitForLocation2);
}
info("wait for window.location.href after page navigation");
let waitForLocation2 = {
name: "window.location.href result is displayed after page navigation",
validatorFn: function()
{
let node = hud.outputNode.getElementsByClassName("webconsole-msg-output")[0];
return node && node.textContent.indexOf(TEST_URI2) > -1;
},
successFn: function()
{
let node = hud.outputNode.getElementsByClassName("webconsole-msg-input")[0];
isnot(node.textContent.indexOf("window.location.href"), -1,
"jsterm input is also displayed");
waitForMessages({
webconsole: hud,
messages: [
{
name: "window.location.href jsterm input",
text: "window.location.href",
category: CATEGORY_INPUT,
},
{
name: "window.location.href result is displayed",
text: TEST_URI2,
category: CATEGORY_OUTPUT,
},
]
}).then(() => {
is(hud.outputNode.textContent.indexOf("Permission denied"), -1,
"no permission denied errors");
gBrowser.goBack();
waitForSuccess(waitForBack);
},
failureFn: finishTestWithError,
};
});
}
let waitForBack = {
name: "go back",
@ -94,36 +97,17 @@ function test()
successFn: function()
{
hud.jsterm.clearOutput();
hud.jsterm.execute("window.location.href");
executeSoon(() => {
hud.jsterm.execute("window.location.href");
});
waitForSuccess(waitForLocation3);
info("wait for window.location.href after goBack()");
waitForMessages(msgForLocation1).then(() => executeSoon(() => {
is(hud.outputNode.textContent.indexOf("Permission denied"), -1,
"no permission denied errors");
finishTest();
}));
},
failureFn: finishTestWithError,
failureFn: finishTest,
};
let waitForLocation3 = {
name: "window.location.href result is displayed after goBack()",
validatorFn: function()
{
let node = hud.outputNode.getElementsByClassName("webconsole-msg-output")[0];
return node && node.textContent.indexOf(TEST_URI1) > -1;
},
successFn: function()
{
let node = hud.outputNode.getElementsByClassName("webconsole-msg-input")[0];
isnot(node.textContent.indexOf("window.location.href"), -1,
"jsterm input is also displayed");
is(hud.outputNode.textContent.indexOf("Permission denied"), -1,
"no permission denied errors");
executeSoon(finishTest);
},
failureFn: finishTestWithError,
};
function finishTestWithError()
{
info("output content: " + hud.outputNode.textContent);
finishTest();
}
}

View File

@ -32,7 +32,7 @@ function consoleOpened(hud)
// Check for network requests.
let xhr = new XMLHttpRequest();
xhr.onload = () => info("xhr loaded, status is: " + xhr.status);
xhr.onload = () => console.log("xhr loaded, status is: " + xhr.status);
xhr.open("get", TEST_URI, true);
xhr.send();

View File

@ -20,100 +20,97 @@ function test() {
function consoleOpened(hud) {
// Check that css warnings are not coalesced if they come from different lines.
waitForSuccess({
name: "css warnings displayed",
validatorFn: function()
{
return hud.outputNode.querySelectorAll(".webconsole-msg-cssparser")
.length == 2;
},
successFn: testCSSRepeats.bind(null, hud),
failureFn: finishTest,
});
}
info("waiting for 2 css warnings");
function repeatCountForNode(aNode) {
return aNode.querySelector(".webconsole-msg-repeat").getAttribute("value");
waitForMessages({
webconsole: hud,
messages: [{
name: "two css warnings",
category: CATEGORY_CSS,
count: 2,
repeats: 1,
}],
}).then(testCSSRepeats.bind(null, hud));
}
function testCSSRepeats(hud) {
let msgs = hud.outputNode.querySelectorAll(".webconsole-msg-cssparser");
is(repeatCountForNode(msgs[0]), 1, "no repeats for the first css warning");
is(repeatCountForNode(msgs[1]), 1, "no repeats for the second css warning");
browser.addEventListener("load", function onLoad() {
browser.removeEventListener("load", onLoad, true);
testAfterReload(hud);
info("wait for repeats after page reload");
waitForMessages({
webconsole: hud,
messages: [{
name: "two css warnings, repeated twice",
category: CATEGORY_CSS,
repeats: 2,
count: 2,
}],
}).then(testCSSRepeatsAfterReload.bind(null, hud));
}, true);
content.location.reload();
}
function testAfterReload(hud) {
let repeats;
waitForSuccess({
name: "message repeats increased",
validatorFn: () => {
repeats = hud.outputNode.querySelectorAll(".webconsole-msg-cssparser " +
".webconsole-msg-repeat");
return repeats.length == 2 &&
repeats[0].getAttribute("value") == 2 &&
repeats[1].getAttribute("value") == 2;
},
successFn: testCSSRepeatsAfterReload.bind(null, hud),
failureFn: () => {
let repeats0 = repeats[0] ? repeats[0].getAttribute("value") : "undefined";
let repeats1 = repeats[1] ? repeats[1].getAttribute("value") : "undefined";
info("repeats.length " + repeats.length);
info("repeats[0] value " + repeats0);
info("repeats[1] value " + repeats1);
finishTest();
},
});
}
function testCSSRepeatsAfterReload(hud) {
let msgs = hud.outputNode.querySelectorAll(".webconsole-msg-cssparser");
is(msgs.length, 2, "two css warnings after reload");
is(repeatCountForNode(msgs[0]), 2, "two repeats for the first css warning");
is(repeatCountForNode(msgs[1]), 2, "two repeats for the second css warning");
hud.jsterm.clearOutput();
hud.jsterm.clearOutput(true);
content.wrappedJSObject.testConsole();
waitForSuccess({
name: "console API messages displayed",
validatorFn: function()
{
return hud.outputNode.querySelectorAll(".webconsole-msg-console")
.length == 3;
},
successFn: testConsoleRepeats.bind(null, hud),
failureFn: finishTest,
});
info("wait for repeats with the console API");
waitForMessages({
webconsole: hud,
messages: [
{
name: "console.log 'foo repeat' repeated twice",
category: CATEGORY_WEBDEV,
severity: SEVERITY_LOG,
repeats: 2,
},
{
name: "console.log 'foo repeat' repeated once",
category: CATEGORY_WEBDEV,
severity: SEVERITY_LOG,
repeats: 1,
},
{
name: "console.error 'foo repeat' repeated once",
category: CATEGORY_WEBDEV,
severity: SEVERITY_ERROR,
repeats: 1,
},
],
}).then(testConsoleRepeats.bind(null, hud));
}
function testConsoleRepeats(hud) {
let msgs = hud.outputNode.querySelectorAll(".webconsole-msg-console");
is(repeatCountForNode(msgs[0]), 2, "repeats for the first console message");
is(repeatCountForNode(msgs[1]), 1,
"no repeats for the second console log message");
is(repeatCountForNode(msgs[2]), 1, "no repeats for the console.error message");
hud.jsterm.clearOutput();
hud.jsterm.clearOutput(true);
hud.jsterm.execute("undefined");
content.console.log("undefined");
waitForSuccess({
name: "messages displayed",
validatorFn: function()
{
return hud.outputNode.querySelector(".webconsole-msg-console");
},
successFn: function() {
is(hud.outputNode.childNodes.length, 3,
"correct number of messages displayed");
executeSoon(finishTest);
},
failureFn: finishTest,
});
info("make sure console API messages are not coalesced with jsterm output");
waitForMessages({
webconsole: hud,
messages: [
{
name: "'undefined' jsterm input message",
text: "undefined",
category: CATEGORY_INPUT,
repeats: 1,
},
{
name: "'undefined' jsterm output message",
text: "undefined",
category: CATEGORY_OUTPUT,
repeats: 1,
},
{
name: "'undefined' console.log message",
text: "undefined",
category: CATEGORY_WEBDEV,
repeats: 1,
},
],
}).then(finishTest);
}

View File

@ -76,6 +76,8 @@ function onpopupshown2(aEvent)
menupopups[1].addEventListener("popuphidden", function _onhidden(aEvent) {
menupopups[1].removeEventListener(aEvent.type, _onhidden, false);
info("menupopups[1] hidden");
// Reopen the context menu.
menupopups[1].addEventListener("popupshown", onpopupshown2b, false);
executeSoon(function() {
@ -105,6 +107,8 @@ function onpopupshown2b(aEvent)
menupopups[1].addEventListener("popuphidden", function _onhidden(aEvent) {
menupopups[1].removeEventListener(aEvent.type, _onhidden, false);
info("menupopups[1] hidden");
// Switch to tab 1 and open the Web Console context menu from there.
gBrowser.selectedTab = tabs[runCount*2];
waitForFocus(function() {
@ -119,7 +123,7 @@ function onpopupshown2b(aEvent)
menupopups[0] = huds[0].ui.rootElement.querySelector("menupopup");
menupopups[0].addEventListener("popupshown", onpopupshown1, false);
menupopups[0].openPopup();
executeSoon(() => menupopups[0].openPopup());
}, tabs[runCount*2].linkedBrowser.contentWindow);
}, false);
@ -143,6 +147,8 @@ function onpopupshown1(aEvent)
menupopups[0].addEventListener("popuphidden", function _onhidden(aEvent) {
menupopups[0].removeEventListener(aEvent.type, _onhidden, false);
info("menupopups[0] hidden");
gBrowser.selectedTab = tabs[runCount*2 + 1];
waitForFocus(function() {
// Reopen the context menu from tab 2.
@ -174,10 +180,13 @@ function onpopupshown2c(aEvent)
menupopups[1].addEventListener("popuphidden", function _onhidden(aEvent) {
menupopups[1].removeEventListener(aEvent.type, _onhidden, false);
info("menupopups[1] hidden");
// Done if on second run
closeConsole(gBrowser.selectedTab, function() {
if (runCount == 0) {
runCount++;
info("start second run");
executeSoon(test);
}
else {

View File

@ -10,8 +10,6 @@
const TEST_URI = "data:text/html;charset=utf-8,<p>test for bug 642108.";
const LOG_LIMIT = 20;
const CATEGORY_CSS = 1;
const SEVERITY_WARNING = 1;
function test() {
addTab(TEST_URI);

View File

@ -16,6 +16,22 @@ Components.utils.import("resource://gre/modules/devtools/Console.jsm", tempScope
let console = tempScope.console;
let Promise = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {}).Promise;
let gPendingOutputTest = 0;
// The various categories of messages.
const CATEGORY_NETWORK = 0;
const CATEGORY_CSS = 1;
const CATEGORY_JS = 2;
const CATEGORY_WEBDEV = 3;
const CATEGORY_INPUT = 4;
const CATEGORY_OUTPUT = 5;
// The possible message severities.
const SEVERITY_ERROR = 0;
const SEVERITY_WARNING = 1;
const SEVERITY_INFO = 2;
const SEVERITY_LOG = 3;
const WEBCONSOLE_STRINGS_URI = "chrome://browser/locale/devtools/webconsole.properties";
let WCU_l10n = new WebConsoleUtils.l10n(WEBCONSOLE_STRINGS_URI);
@ -230,11 +246,53 @@ function waitForOpenContextMenu(aContextMenu, aOptions) {
eventDetails, targetElement.ownerDocument.defaultView);
}
/**
* Dump the output of all open Web Consoles - used only for debugging purposes.
*/
function dumpConsoles()
{
if (gPendingOutputTest) {
console.log("dumpConsoles");
for each (let hud in HUDService.hudReferences) {
if (!hud.outputNode) {
console.debug("no output content for", hud.hudId);
continue;
}
console.debug("output content for", hud.hudId);
for (let elem of hud.outputNode.childNodes) {
let text = getMessageElementText(elem);
let repeats = elem.querySelector(".webconsole-msg-repeat");
if (repeats) {
repeats = repeats.getAttribute("value");
}
console.debug("date", elem.timestamp,
"class", elem.className,
"category", elem.category,
"severity", elem.severity,
"repeats", repeats,
"clipboardText", elem.clipboardText,
"text", text);
}
}
gPendingOutputTest = 0;
}
}
function finishTest()
{
browser = hudId = hud = filterBox = outputNode = cs = null;
dumpConsoles();
if (HUDConsoleUI.browserConsole) {
let hud = HUDConsoleUI.browserConsole;
if (hud.jsterm) {
hud.jsterm.clearOutput(true);
}
HUDConsoleUI.toggleBrowserConsole().then(finishTest);
return;
}
@ -244,6 +302,7 @@ function finishTest()
finish();
return;
}
if (hud.jsterm) {
hud.jsterm.clearOutput(true);
}
@ -255,6 +314,8 @@ function finishTest()
function tearDown()
{
dumpConsoles();
if (HUDConsoleUI.browserConsole) {
HUDConsoleUI.toggleBrowserConsole();
}
@ -767,3 +828,161 @@ function openDebugger(aOptions = {})
return deferred.promise;
}
/**
* Get the full text displayed by a Web Console message.
*
* @param nsIDOMElement aElement
* The message element from the Web Console output.
* @return string
* The full text displayed by the given message element.
*/
function getMessageElementText(aElement)
{
let text = aElement.textContent;
let labels = aElement.querySelectorAll("label");
for (let label of labels) {
text += " " + label.getAttribute("value");
}
return text;
}
/**
* Wait for messages in the Web Console output.
*
* @param object aOptions
* Options for what you want to wait for:
* - webconsole: the webconsole instance you work with.
* - messages: an array of objects that tells which messages to wait for.
* Properties:
* - text: string or RegExp to match the textContent of each new
* message.
* - repeats: the number of message repeats, as displayed by the Web
* Console.
* - category: match message category. See CATEGORY_* constants at
* the top of this file.
* - severity: match message severity. See SEVERITY_* constants at
* the top of this file.
* - count: how many unique web console messages should be matched by
* this rule.
* @return object
* A Promise object is returned once the messages you want are found.
*/
function waitForMessages(aOptions)
{
gPendingOutputTest++;
let webconsole = aOptions.webconsole;
let rules = WebConsoleUtils.cloneObject(aOptions.messages, true);
let rulesMatched = 0;
let listenerAdded = false;
let deferred = Promise.defer();
function checkMessage(aRule, aElement)
{
if (aRule.text) {
let elemText = getMessageElementText(aElement);
let matched = false;
if (typeof aRule.text == "string") {
matched = elemText.indexOf(aRule.text) > -1;
}
else if (aRule.text instanceof RegExp) {
matched = aRule.text.test(elemText);
}
if (!matched) {
return false;
}
}
if (aRule.category) {
if (aElement.category != aRule.category) {
return false;
}
}
if (aRule.severity) {
if (aElement.severity != aRule.severity) {
return false;
}
}
if (aRule.repeats) {
let repeats = aElement.querySelector(".webconsole-msg-repeat");
if (!repeats || repeats.getAttribute("value") != aRule.repeats) {
return false;
}
}
let count = aRule.count || 1;
if (!aRule.matched) {
aRule.matched = new Set();
}
aRule.matched.add(aElement);
return aRule.matched.size == count;
}
function onMessagesAdded(aEvent, aNewElements)
{
for (let elem of aNewElements) {
for (let rule of rules) {
if (rule._ruleMatched) {
continue;
}
let matched = checkMessage(rule, elem);
if (matched) {
rule._ruleMatched = true;
rulesMatched++;
ok(1, "matched rule: " + displayRule(rule));
if (maybeDone()) {
return;
}
}
}
}
}
function maybeDone()
{
if (rulesMatched == rules.length) {
if (listenerAdded) {
webconsole.ui.off("messages-added", onMessagesAdded);
}
gPendingOutputTest--;
deferred.resolve(rules);
return true;
}
return false;
}
function testCleanup() {
if (rulesMatched == rules.length) {
return;
}
if (webconsole.ui) {
webconsole.ui.off("messages-added", onMessagesAdded);
}
for (let rule of rules) {
if (!rule._ruleMatched) {
console.log("failed to match rule: " + displayRule(rule));
}
}
}
function displayRule(aRule)
{
return aRule.name || aRule.text;
}
executeSoon(() => {
onMessagesAdded("messages-added", webconsole.outputNode.childNodes);
if (rulesMatched != rules.length) {
listenerAdded = true;
registerCleanupFunction(testCleanup);
webconsole.ui.on("messages-added", onMessagesAdded);
}
});
return deferred.promise;
}

View File

@ -199,6 +199,8 @@ function WebConsoleFrame(aWebConsoleOwner)
this._outputTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
this._outputTimerInitialized = false;
EventEmitter.decorate(this);
}
WebConsoleFrame.prototype = {
@ -879,14 +881,15 @@ WebConsoleFrame.prototype = {
* @private
* @param nsIDOMNode aNode
* The message node to be filtered or not.
* @returns boolean
* True if the message is filtered, false otherwise.
* @returns nsIDOMNode|null
* Returns the duplicate node if the message was filtered, null
* otherwise.
*/
_filterRepeatedMessage: function WCF__filterRepeatedMessage(aNode)
{
let repeatNode = aNode.getElementsByClassName("webconsole-msg-repeat")[0];
if (!repeatNode) {
return false;
return null;
}
let uid = repeatNode._uid;
@ -905,7 +908,7 @@ WebConsoleFrame.prototype = {
aNode.classList.contains("webconsole-msg-error"))) {
let lastMessage = this.outputNode.lastChild;
if (!lastMessage) {
return false;
return null;
}
let lastRepeatNode = lastMessage
@ -917,10 +920,10 @@ WebConsoleFrame.prototype = {
if (dupeNode) {
this.mergeFilteredMessageNode(dupeNode, aNode);
return true;
return dupeNode;
}
return false;
return null;
},
/**
@ -1692,10 +1695,14 @@ WebConsoleFrame.prototype = {
let hudIdSupportsString = WebConsoleUtils.supportsString(this.hudId);
// Output the current batch of messages.
let newOrUpdatedNodes = new Set();
for (let item of batch) {
let node = this._outputMessageFromQueue(hudIdSupportsString, item);
if (node) {
lastVisibleNode = node;
let result = this._outputMessageFromQueue(hudIdSupportsString, item);
if (result) {
newOrUpdatedNodes.add(result.isRepeated || result.node);
if (result.visible && result.node == this.outputNode.lastChild) {
lastVisibleNode = result.node;
}
}
}
@ -1736,6 +1743,8 @@ WebConsoleFrame.prototype = {
scrollBox.scrollTop -= oldScrollHeight - scrollBox.scrollHeight;
}
this.emit("messages-added", newOrUpdatedNodes);
// If the queue is not empty, schedule another flush.
if (this._outputQueue.length > 0) {
this._initOutputTimer();
@ -1772,9 +1781,12 @@ WebConsoleFrame.prototype = {
* The HUD ID as an nsISupportsString.
* @param array aItem
* An item from the output queue - this item represents a message.
* @return nsIDOMElement|undefined
* The DOM element of the message if the message is visible, undefined
* otherwise.
* @return object
* An object that holds the following properties:
* - node: the DOM element of the message.
* - isRepeated: the DOM element of the original message, if this is
* a repeated message, otherwise null.
* - visible: boolean that tells if the message is visible.
*/
_outputMessageFromQueue:
function WCF__outputMessageFromQueue(aHudIdSupportsString, aItem)
@ -1785,7 +1797,7 @@ WebConsoleFrame.prototype = {
methodOrNode.apply(this, args || []) :
methodOrNode;
if (!node) {
return;
return null;
}
let afterNode = node._outputAfterNode;
@ -1797,14 +1809,16 @@ WebConsoleFrame.prototype = {
let isRepeated = this._filterRepeatedMessage(node);
let lastVisible = !isRepeated && !isFiltered;
let visible = !isRepeated && !isFiltered;
if (!isRepeated) {
this.outputNode.insertBefore(node,
afterNode ? afterNode.nextSibling : null);
this._pruneCategoriesQueue[node.category] = true;
if (afterNode) {
lastVisible = this.outputNode.lastChild == node;
}
let nodeID = node.getAttribute("id");
Services.obs.notifyObservers(aHudIdSupportsString,
"web-console-message-created", nodeID);
}
if (node._onOutput) {
@ -1812,11 +1826,11 @@ WebConsoleFrame.prototype = {
delete node._onOutput;
}
let nodeID = node.getAttribute("id");
Services.obs.notifyObservers(aHudIdSupportsString,
"web-console-message-created", nodeID);
return lastVisible ? node : null;
return {
visible: visible,
node: node,
isRepeated: isRepeated,
};
},
/**