bug 860421 fix intermittent failure in chat tests, r=markh

This commit is contained in:
Shane Caraveo 2013-07-04 19:21:27 -07:00
parent 0866af5486
commit a86d98d23e
5 changed files with 269 additions and 198 deletions

View File

@ -620,7 +620,7 @@
<method name="handleEvent">
<parameter name="aEvent"/>
<body><![CDATA[
if (aEvent.type == "resize") {
if (aEvent.type == "resize" && aEvent.eventPhase == aEvent.AT_TARGET) {
this.resize();
}
]]></body>

View File

@ -27,6 +27,7 @@ MOCHITEST_BROWSER_FILES = \
browser_social_mozSocial_API.js \
browser_social_isVisible.js \
browser_social_chatwindow.js \
browser_social_chatwindow_resize.js \
browser_social_chatwindowfocus.js \
browser_social_multiprovider.js \
browser_social_errorPage.js \

View File

@ -287,47 +287,6 @@ var tests = {
});
},
// resize and collapse testing.
testBrowserResize: function(next, mode) {
let chats = document.getElementById("pinnedchats");
let port = Social.provider.getWorkerPort();
port.postMessage({topic: "test-init"});
get3ChatsForCollapsing(mode || "normal", function(first, second, third) {
let chatWidth = chats.getTotalChildWidth(first);
ok(chatWidth, "have a chatwidth");
let popupWidth = getPopupWidth();
ok(popupWidth, "have a popupwidth");
info("starting resize tests - each chat's width is " + chatWidth +
" and the popup width is " + popupWidth);
// Note that due to a difference between "device", "app" and "css" pixels
// we allow use 2 pixels as the minimum size difference.
resizeAndCheckWidths(first, second, third, [
[chatWidth-2, 1, "to < 1 chat width - only last should be visible."],
[chatWidth+2, 1, "2 pixels more then one fully exposed (not counting popup) - still only 1."],
[chatWidth+popupWidth+2, 1, "2 pixels more than one fully exposed (including popup) - still only 1."],
[chatWidth*2-2, 1, "second not showing by 2 pixels (not counting popup) - only 1 exposed."],
[chatWidth*2+popupWidth-2, 1, "second not showing by 2 pixelx (including popup) - only 1 exposed."],
[chatWidth*2+popupWidth+2, 2, "big enough to fit 2 - nub remains visible as first is still hidden"],
[chatWidth*3+popupWidth-2, 2, "one smaller than the size necessary to display all three - first still hidden"],
[chatWidth*3+popupWidth+2, 3, "big enough to fit all - all exposed (which removes the nub)"],
[chatWidth*3+2, 3, "now the nub is hidden we can resize back down to chatWidth*3 before overflow."],
[chatWidth*3-2, 2, "2 pixels less and the first is again collapsed (and the nub re-appears)"],
[chatWidth*2+popupWidth+2, 2, "back down to just big enough to fit 2"],
[chatWidth*2+popupWidth-2, 1, "back down to just not enough to fit 2"],
[chatWidth*3+popupWidth+2, 3, "now a large jump to make all 3 visible (ie, affects 2)"],
[chatWidth*1.5, 1, "and a large jump back down to 1 visible (ie, affects 2)"],
], function() {
closeAllChats();
port.close();
next();
});
});
},
testBrowserResizeMinimized: function(next) {
this.testBrowserResize(next, "minimized");
},
testShowWhenCollapsed: function(next) {
let port = Social.provider.getWorkerPort();
port.postMessage({topic: "test-init"});
@ -511,159 +470,3 @@ var tests = {
port.postMessage({topic: "test-worker-chat", data: chatUrl});
},
}
// And lots of helpers for the resize tests.
function get3ChatsForCollapsing(mode, cb) {
// We make one chat, then measure its size. We then resize the browser to
// ensure a second can be created fully visible but a third can not - then
// create the other 2. first will will be collapsed, second fully visible
// and the third also visible and the "selected" one.
// To make our life easier we don't go via the worker and ports so we get
// more control over creation *and* to make the code much simpler. We
// assume the worker/port stuff is individually tested above.
let chatbar = window.SocialChatBar.chatbar;
let chatWidth = undefined;
let num = 0;
is(chatbar.childNodes.length, 0, "chatbar starting empty");
is(chatbar.menupopup.childNodes.length, 0, "popup starting empty");
makeChat(mode, "first chat", function() {
// got the first one.
checkPopup();
ok(chatbar.menupopup.parentNode.collapsed, "menu selection isn't visible");
// we kinda cheat here and get the width of the first chat, assuming
// that all future chats will have the same width when open.
chatWidth = chatbar.calcTotalWidthOf(chatbar.selectedChat);
let desired = chatWidth * 2.5;
resizeWindowToChatAreaWidth(desired, function(sizedOk) {
ok(sizedOk, "can't do any tests without this width");
checkPopup();
makeChat(mode, "second chat", function() {
is(chatbar.childNodes.length, 2, "now have 2 chats");
checkPopup();
// and create the third.
makeChat(mode, "third chat", function() {
is(chatbar.childNodes.length, 3, "now have 3 chats");
checkPopup();
// XXX - this is a hacky implementation detail around the order of
// the chats. Ideally things would be a little more sane wrt the
// other in which the children were created.
let second = chatbar.childNodes[2];
let first = chatbar.childNodes[1];
let third = chatbar.childNodes[0];
ok(first.collapsed && !second.collapsed && !third.collapsed, "collapsed state as promised");
is(chatbar.selectedChat, third, "third is selected as promised")
info("have 3 chats for collapse testing - starting actual test...");
cb(first, second, third);
}, mode);
}, mode);
});
}, mode);
}
function makeChat(mode, uniqueid, cb) {
const chatUrl = "https://example.com/browser/browser/base/content/test/social/social_chat.html";
let provider = Social.provider;
window.SocialChatBar.openChat(provider, chatUrl + "?id=" + uniqueid, function(chat) {
// we can't callback immediately or we might close the chat during
// this event which upsets the implementation - it is only 1/2 way through
// handling the load event.
chat.document.title = uniqueid;
executeSoon(cb);
}, mode);
}
function checkPopup() {
// popup only showing if any collapsed popup children.
let chatbar = window.SocialChatBar.chatbar;
let numCollapsed = 0;
for (let chat of chatbar.childNodes) {
if (chat.collapsed) {
numCollapsed += 1;
// and it have a menuitem weakmap
is(chatbar.menuitemMap.get(chat).nodeName, "menuitem", "collapsed chat has a menu item");
} else {
ok(!chatbar.menuitemMap.has(chat), "open chat has no menu item");
}
}
is(chatbar.menupopup.parentNode.collapsed, numCollapsed == 0, "popup matches child collapsed state");
is(chatbar.menupopup.childNodes.length, numCollapsed, "popup has correct count of children");
// todo - check each individual elt is what we expect?
}
// Resize the main window so the chat area's boxObject is |desired| wide.
// Does a callback passing |true| if the window is now big enough or false
// if we couldn't resize large enough to satisfy the test requirement.
function resizeWindowToChatAreaWidth(desired, cb) {
let current = window.SocialChatBar.chatbar.getBoundingClientRect().width;
let delta = desired - current;
info("resizing window so chat area is " + desired + " wide, currently it is "
+ current + ". Screen avail is " + window.screen.availWidth
+ ", current outer width is " + window.outerWidth);
// WTF? Sometimes we will get fractional values due to the - err - magic
// of DevPointsPerCSSPixel etc, so we allow a couple of pixels difference.
let widthDeltaCloseEnough = function(d) {
return Math.abs(d) < 2;
}
// attempting to resize by (0,0), unsurprisingly, doesn't cause a resize
// event - so just callback saying all is well.
if (widthDeltaCloseEnough(delta)) {
cb(true);
return;
}
// On lo-res screens we may already be maxed out but still smaller than the
// requested size, so asking to resize up also will not cause a resize event.
// So just callback now saying the test must be skipped.
if (window.screen.availWidth - window.outerWidth < delta) {
info("skipping this as screen available width is less than necessary");
cb(false);
return;
}
// Otherwise we request resize and expect a resize event
window.addEventListener("resize", function resize_handler() {
// we did resize - but did we get far enough to be able to continue?
let newSize = window.SocialChatBar.chatbar.getBoundingClientRect().width;
let sizedOk = widthDeltaCloseEnough(newSize - desired);
if (!sizedOk) {
return;
}
window.removeEventListener("resize", resize_handler);
executeSoon(function() {
cb(sizedOk);
});
});
window.resizeBy(delta, 0);
}
function resizeAndCheckWidths(first, second, third, checks, cb) {
if (checks.length == 0) {
cb(); // nothing more to check!
return;
}
let [width, numExpectedVisible, why] = checks.shift();
info("Check: " + why);
info("resizing window to " + width + ", expect " + numExpectedVisible + " visible items");
resizeWindowToChatAreaWidth(width, function(sizedOk) {
checkPopup();
if (sizedOk) {
let numVisible = [first, second, third].filter(function(item) !item.collapsed).length;
is(numVisible, numExpectedVisible, "correct number of chats visible");
}
resizeAndCheckWidths(first, second, third, checks, cb);
});
}
function getPopupWidth() {
let popup = window.SocialChatBar.chatbar.menupopup;
ok(!popup.parentNode.collapsed, "asking for popup width when it is visible");
let cs = document.defaultView.getComputedStyle(popup.parentNode);
let margins = parseInt(cs.marginLeft) + parseInt(cs.marginRight);
return popup.parentNode.getBoundingClientRect().width + margins;
}
function closeAllChats() {
let chatbar = window.SocialChatBar.chatbar;
chatbar.removeAll();
}

View File

@ -0,0 +1,90 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
function test() {
requestLongerTimeout(2); // only debug builds seem to need more time...
waitForExplicitFinish();
let manifest = { // normal provider
name: "provider 1",
origin: "https://example.com",
sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
iconURL: "https://example.com/browser/browser/base/content/test/moz.png",
// added for test purposes
chatURL: "https://example.com/browser/browser/base/content/test/social/social_chat.html"
};
let oldwidth = window.outerWidth; // we futz with these, so we restore them
let oldleft = window.screenX;
window.moveTo(0, window.screenY)
let postSubTest = function(cb) {
let chats = document.getElementById("pinnedchats");
ok(chats.children.length == 0, "no chatty children left behind");
cb();
};
runSocialTestWithProvider(manifest, function (finishcb) {
let port = Social.provider.getWorkerPort();
ok(port, "provider has a port");
port.postMessage({topic: "test-init"});
// we require a logged in user for chats, wait for that
waitForCondition(function() {
let sbrowser = document.getElementById("social-sidebar-browser");
return Social.provider &&
Social.provider.profile &&
Social.provider.profile.displayName &&
sbrowser.docShellIsActive;
}, function() {
// executeSoon to let the browser UI observers run first
runSocialTests(tests, undefined, postSubTest, function() {
window.moveTo(oldleft, window.screenY)
window.resizeTo(oldwidth, window.outerHeight);
port.close();
finishcb();
});
},
"waitForProviderLoad: provider profile was not set");
});
}
var tests = {
// resize and collapse testing.
testBrowserResize: function(next, mode) {
let chats = document.getElementById("pinnedchats");
get3ChatsForCollapsing(mode || "normal", function(first, second, third) {
let chatWidth = chats.getTotalChildWidth(first);
ok(chatWidth, "have a chatwidth");
let popupWidth = getPopupWidth();
ok(popupWidth, "have a popupwidth");
info("starting resize tests - each chat's width is " + chatWidth +
" and the popup width is " + popupWidth);
// Note that due to a difference between "device", "app" and "css" pixels
// we allow use 2 pixels as the minimum size difference.
resizeAndCheckWidths(first, second, third, [
[chatWidth-2, 1, "to < 1 chat width - only last should be visible."],
[chatWidth+2, 1, "2 pixels more then one fully exposed (not counting popup) - still only 1."],
[chatWidth+popupWidth+2, 1, "2 pixels more than one fully exposed (including popup) - still only 1."],
[chatWidth*2-2, 1, "second not showing by 2 pixels (not counting popup) - only 1 exposed."],
[chatWidth*2+popupWidth-2, 1, "second not showing by 2 pixelx (including popup) - only 1 exposed."],
[chatWidth*2+popupWidth+2, 2, "big enough to fit 2 - nub remains visible as first is still hidden"],
[chatWidth*3+popupWidth-2, 2, "one smaller than the size necessary to display all three - first still hidden"],
[chatWidth*3+popupWidth+2, 3, "big enough to fit all - all exposed (which removes the nub)"],
[chatWidth*3+2, 3, "now the nub is hidden we can resize back down to chatWidth*3 before overflow."],
[chatWidth*3-2, 2, "2 pixels less and the first is again collapsed (and the nub re-appears)"],
[chatWidth*2+popupWidth+2, 2, "back down to just big enough to fit 2"],
[chatWidth*2+popupWidth-2, 1, "back down to just not enough to fit 2"],
[chatWidth*3+popupWidth+2, 3, "now a large jump to make all 3 visible (ie, affects 2)"],
[chatWidth*1.5, 1, "and a large jump back down to 1 visible (ie, affects 2)"],
], function() {
closeAllChats();
next();
});
});
},
testBrowserResizeMinimized: function(next) {
this.testBrowserResize(next);
}
}

View File

@ -345,3 +345,180 @@ function loadIntoTab(tab, url, callback) {
tab.linkedBrowser.loadURI(url);
}
// chat test help functions
// And lots of helpers for the resize tests.
function get3ChatsForCollapsing(mode, cb) {
// We make one chat, then measure its size. We then resize the browser to
// ensure a second can be created fully visible but a third can not - then
// create the other 2. first will will be collapsed, second fully visible
// and the third also visible and the "selected" one.
// To make our life easier we don't go via the worker and ports so we get
// more control over creation *and* to make the code much simpler. We
// assume the worker/port stuff is individually tested above.
let chatbar = window.SocialChatBar.chatbar;
let chatWidth = undefined;
let num = 0;
is(chatbar.childNodes.length, 0, "chatbar starting empty");
is(chatbar.menupopup.childNodes.length, 0, "popup starting empty");
makeChat(mode, "first chat", function() {
// got the first one.
checkPopup();
ok(chatbar.menupopup.parentNode.collapsed, "menu selection isn't visible");
// we kinda cheat here and get the width of the first chat, assuming
// that all future chats will have the same width when open.
chatWidth = chatbar.calcTotalWidthOf(chatbar.selectedChat);
let desired = chatWidth * 2.5;
resizeWindowToChatAreaWidth(desired, function(sizedOk) {
ok(sizedOk, "can't do any tests without this width");
checkPopup();
makeChat(mode, "second chat", function() {
is(chatbar.childNodes.length, 2, "now have 2 chats");
checkPopup();
// and create the third.
makeChat(mode, "third chat", function() {
is(chatbar.childNodes.length, 3, "now have 3 chats");
checkPopup();
// XXX - this is a hacky implementation detail around the order of
// the chats. Ideally things would be a little more sane wrt the
// other in which the children were created.
let second = chatbar.childNodes[2];
let first = chatbar.childNodes[1];
let third = chatbar.childNodes[0];
ok(first.collapsed && !second.collapsed && !third.collapsed, "collapsed state as promised");
is(chatbar.selectedChat, third, "third is selected as promised")
info("have 3 chats for collapse testing - starting actual test...");
cb(first, second, third);
}, mode);
}, mode);
});
}, mode);
}
function makeChat(mode, uniqueid, cb) {
info("making a chat window '" + uniqueid +"'");
const chatUrl = "https://example.com/browser/browser/base/content/test/social/social_chat.html";
let provider = Social.provider;
let isOpened = window.SocialChatBar.openChat(provider, chatUrl + "?id=" + uniqueid, function(chat) {
info("chat window has opened");
// we can't callback immediately or we might close the chat during
// this event which upsets the implementation - it is only 1/2 way through
// handling the load event.
chat.document.title = uniqueid;
executeSoon(cb);
}, mode);
if (!isOpened) {
ok(false, "unable to open chat window, no provider? more failures to come");
executeSoon(cb);
}
}
function checkPopup() {
// popup only showing if any collapsed popup children.
let chatbar = window.SocialChatBar.chatbar;
let numCollapsed = 0;
for (let chat of chatbar.childNodes) {
if (chat.collapsed) {
numCollapsed += 1;
// and it have a menuitem weakmap
is(chatbar.menuitemMap.get(chat).nodeName, "menuitem", "collapsed chat has a menu item");
} else {
ok(!chatbar.menuitemMap.has(chat), "open chat has no menu item");
}
}
is(chatbar.menupopup.parentNode.collapsed, numCollapsed == 0, "popup matches child collapsed state");
is(chatbar.menupopup.childNodes.length, numCollapsed, "popup has correct count of children");
// todo - check each individual elt is what we expect?
}
// Resize the main window so the chat area's boxObject is |desired| wide.
// Does a callback passing |true| if the window is now big enough or false
// if we couldn't resize large enough to satisfy the test requirement.
function resizeWindowToChatAreaWidth(desired, cb, count = 0) {
let current = window.SocialChatBar.chatbar.getBoundingClientRect().width;
let delta = desired - current;
info(count + ": resizing window so chat area is " + desired + " wide, currently it is "
+ current + ". Screen avail is " + window.screen.availWidth
+ ", current outer width is " + window.outerWidth);
// WTF? Sometimes we will get fractional values due to the - err - magic
// of DevPointsPerCSSPixel etc, so we allow a couple of pixels difference.
let widthDeltaCloseEnough = function(d) {
return Math.abs(d) < 2;
}
// attempting to resize by (0,0), unsurprisingly, doesn't cause a resize
// event - so just callback saying all is well.
if (widthDeltaCloseEnough(delta)) {
info(count + ": skipping this as screen width is close enough");
executeSoon(function() {
cb(true);
});
return;
}
// On lo-res screens we may already be maxed out but still smaller than the
// requested size, so asking to resize up also will not cause a resize event.
// So just callback now saying the test must be skipped.
if (window.screen.availWidth - window.outerWidth < delta) {
info(count + ": skipping this as screen available width is less than necessary");
executeSoon(function() {
cb(false);
});
return;
}
function resize_handler(event) {
// for whatever reason, sometimes we get called twice for different event
// phases, only handle one of them.
if (event.eventPhase != event.AT_TARGET)
return;
// we did resize - but did we get far enough to be able to continue?
let newSize = window.SocialChatBar.chatbar.getBoundingClientRect().width;
let sizedOk = widthDeltaCloseEnough(newSize - desired);
if (!sizedOk)
return;
window.removeEventListener("resize", resize_handler);
info(count + ": resized window width is " + newSize);
executeSoon(function() {
cb(sizedOk);
});
}
// Otherwise we request resize and expect a resize event
window.addEventListener("resize", resize_handler);
window.resizeBy(delta, 0);
}
function resizeAndCheckWidths(first, second, third, checks, cb) {
if (checks.length == 0) {
cb(); // nothing more to check!
return;
}
let count = checks.length;
let [width, numExpectedVisible, why] = checks.shift();
info("<< Check " + count + ": " + why);
info(count + ": " + "resizing window to " + width + ", expect " + numExpectedVisible + " visible items");
resizeWindowToChatAreaWidth(width, function(sizedOk) {
checkPopup();
ok(sizedOk, count+": window resized correctly");
if (sizedOk) {
let numVisible = [first, second, third].filter(function(item) !item.collapsed).length;
is(numVisible, numExpectedVisible, count + ": " + "correct number of chats visible");
}
info(">> Check " + count);
resizeAndCheckWidths(first, second, third, checks, cb);
}, count);
}
function getPopupWidth() {
let popup = window.SocialChatBar.chatbar.menupopup;
ok(!popup.parentNode.collapsed, "asking for popup width when it is visible");
let cs = document.defaultView.getComputedStyle(popup.parentNode);
let margins = parseInt(cs.marginLeft) + parseInt(cs.marginRight);
return popup.parentNode.getBoundingClientRect().width + margins;
}
function closeAllChats() {
let chatbar = window.SocialChatBar.chatbar;
chatbar.removeAll();
}