tenfourfox/browser/components/uitour/test/browser_UITour_loop.js
Cameron Kaiser c9b2922b70 hello FPR
2017-04-19 00:56:45 -07:00

433 lines
17 KiB
JavaScript

/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
var gTestTab;
var gContentAPI;
var gContentWindow;
var gMessageHandlers;
var loopButton;
var fakeRoom;
var loopPanel = document.getElementById("loop-notification-panel");
const { LoopAPI } = Cu.import("chrome://loop/content/modules/MozLoopAPI.jsm", {});
const { LoopRooms } = Cu.import("chrome://loop/content/modules/LoopRooms.jsm", {});
const { MozLoopServiceInternal } = Cu.import("chrome://loop/content/modules/MozLoopService.jsm", {});
function test() {
UITourTest();
}
function runOffline(fun) {
return (done) => {
Services.io.offline = true;
fun(function onComplete() {
Services.io.offline = false;
done();
});
}
}
var tests = [
taskify(function* test_gettingStartedClicked_linkOpenedWithExpectedParams() {
Services.prefs.setBoolPref("loop.gettingStarted.seen", false);
Services.prefs.setCharPref("loop.gettingStarted.url", "http://example.com");
is(loopButton.open, false, "Menu should initially be closed");
loopButton.click();
yield waitForConditionPromise(() => {
return loopButton.open;
}, "Menu should be visible after showMenu()");
gContentAPI.registerPageID("hello-tour_OpenPanel_testPage");
yield new Promise(resolve => {
gContentAPI.ping(() => resolve());
});
let loopDoc = document.getElementById("loop-notification-panel").children[0].contentDocument;
yield waitForConditionPromise(() => {
return loopDoc.readyState == 'complete';
}, "Loop notification panel document should be fully loaded.", 50);
let gettingStartedButton = loopDoc.getElementById("fte-button");
ok(gettingStartedButton, "Getting Started button should be found");
let newTabPromise = waitForConditionPromise(() => {
return gBrowser.currentURI.path.includes("utm_source=firefox-browser");
}, "New tab with utm_content=testPageNewID should have opened");
gettingStartedButton.click();
yield newTabPromise;
ok(gBrowser.currentURI.path.includes("utm_content=hello-tour_OpenPanel_testPage"),
"Expected URL opened (" + gBrowser.currentURI.path + ")");
yield gBrowser.removeCurrentTab();
checkLoopPanelIsHidden();
}),
taskify(function* test_gettingStartedClicked_linkOpenedWithExpectedParams2() {
Services.prefs.setBoolPref("loop.gettingStarted.seen", false);
// Force a refresh of the loop panel since going from seen -> unseen doesn't trigger
// automatic re-rendering.
let loopWin = document.getElementById("loop-notification-panel").children[0].contentWindow;
var event = new loopWin.CustomEvent("GettingStartedSeen", { detail: false });
loopWin.dispatchEvent(event);
UITour.pageIDsForSession.clear();
Services.prefs.setCharPref("loop.gettingStarted.url", "http://example.com");
is(loopButton.open, false, "Menu should initially be closed");
loopButton.click();
yield waitForConditionPromise(() => {
return loopButton.open;
}, "Menu should be visible after showMenu()");
gContentAPI.registerPageID("hello-tour_OpenPanel_testPageOldId");
yield new Promise(resolve => {
gContentAPI.ping(() => resolve());
});
// Set the time of the page ID to 10 hours earlier, so that it is considered "expired".
UITour.pageIDsForSession.set("hello-tour_OpenPanel_testPageOldId",
{lastSeen: Date.now() - (10 * 60 * 60 * 1000)});
let loopDoc = loopWin.document;
let gettingStartedButton = loopDoc.getElementById("fte-button");
ok(gettingStartedButton, "Getting Started button should be found");
let newTabPromise = waitForConditionPromise(() => {
Services.console.logStringMessage(gBrowser.currentURI.path);
return gBrowser.currentURI.path.includes("utm_source=firefox-browser");
}, "New tab with utm_content=testPageNewID should have opened");
gettingStartedButton.click();
yield newTabPromise;
ok(!gBrowser.currentURI.path.includes("utm_content=hello-tour_OpenPanel_testPageOldId"),
"Expected URL opened without the utm_content parameter (" +
gBrowser.currentURI.path + ")");
yield gBrowser.removeCurrentTab();
checkLoopPanelIsHidden();
}),
taskify(function* test_menu_show_hide() {
// The targets to highlight only appear after getting started is launched.
Services.prefs.setBoolPref("loop.gettingStarted.seen", true);
is(loopButton.open, false, "Menu should initially be closed");
gContentAPI.showMenu("loop");
yield waitForConditionPromise(() => {
return loopButton.open;
}, "Menu should be visible after showMenu()");
ok(loopPanel.hasAttribute("noautohide"), "@noautohide should be on the loop panel");
ok(loopPanel.hasAttribute("panelopen"), "The panel should have @panelopen");
is(loopPanel.state, "open", "The panel should be open");
ok(loopButton.hasAttribute("open"), "Loop button should know that the menu is open");
gContentAPI.hideMenu("loop");
yield waitForConditionPromise(() => {
return !loopButton.open;
}, "Menu should be hidden after hideMenu()");
checkLoopPanelIsHidden();
}),
// Test the menu was cleaned up in teardown.
taskify(function* setup_menu_cleanup() {
gContentAPI.showMenu("loop");
yield waitForConditionPromise(() => {
return loopButton.open;
}, "Menu should be visible after showMenu()");
// Leave it open so it gets torn down and we can test below that teardown was succesful.
}),
taskify(function* test_menu_cleanup() {
// Test that the open menu from above was torn down fully.
checkLoopPanelIsHidden();
}),
function test_availableTargets(done) {
gContentAPI.showMenu("loop");
gContentAPI.getConfiguration("availableTargets", (data) => {
for (let targetName of ["loop-newRoom", "loop-roomList", "loop-signInUpLink"]) {
isnot(data.targets.indexOf(targetName), -1, targetName + " should exist");
}
done();
});
},
function test_getConfigurationLoop(done) {
let gettingStartedSeen = Services.prefs.getBoolPref("loop.gettingStarted.seen");
gContentAPI.getConfiguration("loop", (data) => {
is(data.gettingStartedSeen, gettingStartedSeen,
"The configuration property should equal that of the pref");
done();
});
},
function test_hideMenuHidesAnnotations(done) {
let infoPanel = document.getElementById("UITourTooltip");
let highlightPanel = document.getElementById("UITourHighlightContainer");
gContentAPI.showMenu("loop", function menuCallback() {
gContentAPI.showHighlight("loop-roomList");
gContentAPI.showInfo("loop-newRoom", "Make a new room", "AKA. conversation");
UITour.getTarget(window, "loop-newRoom").then((target) => {
waitForPopupAtAnchor(infoPanel, target.node, Task.async(function* checkPanelIsOpen() {
isnot(loopPanel.state, "closed", "Loop panel should still be open");
ok(loopPanel.hasAttribute("noautohide"), "@noautohide should still be on the loop panel");
is(highlightPanel.getAttribute("targetName"), "loop-roomList", "Check highlight @targetname");
is(infoPanel.getAttribute("targetName"), "loop-newRoom", "Check info panel @targetname");
info("Close the loop menu and make sure the annotations inside disappear");
let hiddenPromises = [promisePanelElementHidden(window, infoPanel),
promisePanelElementHidden(window, highlightPanel)];
gContentAPI.hideMenu("loop");
yield Promise.all(hiddenPromises);
isnot(infoPanel.state, "open", "Info panel should have automatically hid");
isnot(highlightPanel.state, "open", "Highlight panel should have automatically hid");
done();
}), "Info panel should be anchored to the new room button");
});
});
},
runOffline(function test_notifyLoopChatWindowOpenedClosed(done) {
gContentAPI.observe((event, params) => {
is(event, "Loop:ChatWindowOpened", "Check Loop:ChatWindowOpened notification");
gContentAPI.observe((event, params) => {
is(event, "Loop:ChatWindowShown", "Check Loop:ChatWindowShown notification");
gContentAPI.observe((event, params) => {
is(event, "Loop:ChatWindowClosed", "Check Loop:ChatWindowClosed notification");
gContentAPI.observe((event, params) => {
ok(false, "No more notifications should have arrived");
});
});
done();
});
document.querySelector("#pinnedchats > chatbox").close();
});
LoopRooms.open("fakeTourRoom");
}),
runOffline(function test_notifyLoopRoomURLCopied(done) {
gContentAPI.observe((event, params) => {
is(event, "Loop:ChatWindowOpened", "Loop chat window should've opened");
gContentAPI.observe((event, params) => {
is(event, "Loop:ChatWindowShown", "Check Loop:ChatWindowShown notification");
let chat = document.querySelector("#pinnedchats > chatbox");
gContentAPI.observe((event, params) => {
is(event, "Loop:RoomURLCopied", "Check Loop:RoomURLCopied notification");
gContentAPI.observe((event, params) => {
is(event, "Loop:ChatWindowClosed", "Check Loop:ChatWindowClosed notification");
});
chat.close();
done();
});
let window = chat.content.contentWindow;
waitForConditionPromise(
() => chat.content.contentDocument.querySelector(".btn-copy"),
"Copy button should be there"
).then(() => chat.content.contentDocument.querySelector(".btn-copy").click());
});
});
LoopRooms.open("fakeTourRoom");
}),
runOffline(function test_notifyLoopRoomURLEmailed(done) {
gContentAPI.observe((event, params) => {
is(event, "Loop:ChatWindowOpened", "Loop chat window should've opened");
gContentAPI.observe((event, params) => {
is(event, "Loop:ChatWindowShown", "Check Loop:ChatWindowShown notification");
let chat = document.querySelector("#pinnedchats > chatbox");
let composeEmailCalled = false;
gContentAPI.observe((event, params) => {
is(event, "Loop:RoomURLEmailed", "Check Loop:RoomURLEmailed notification");
ok(composeEmailCalled, "mozLoop.composeEmail should be called");
gContentAPI.observe((event, params) => {
is(event, "Loop:ChatWindowClosed", "Check Loop:ChatWindowClosed notification");
});
chat.close();
done();
});
gMessageHandlers.ComposeEmail = function(message, reply) {
let [subject, body, recipient] = message.data;
ok(subject, "composeEmail should be invoked with at least a subject value");
composeEmailCalled = true;
reply();
};
waitForConditionPromise(
() => chat.content.contentDocument.querySelector(".btn-email"),
"Email button should be there"
).then(() => chat.content.contentDocument.querySelector(".btn-email").click());
});
});
LoopRooms.open("fakeTourRoom");
}),
taskify(function* test_arrow_panel_position() {
is(loopButton.open, false, "Menu should initially be closed");
let popup = document.getElementById("UITourTooltip");
yield showMenuPromise("loop");
let currentTarget = "loop-newRoom";
yield showInfoPromise(currentTarget, "This is " + currentTarget, "My arrow should be on the side");
is(popup.popupBoxObject.alignmentPosition, "start_before", "Check " + currentTarget + " position");
currentTarget = "loop-roomList";
yield showInfoPromise(currentTarget, "This is " + currentTarget, "My arrow should be on the side");
is(popup.popupBoxObject.alignmentPosition, "start_before", "Check " + currentTarget + " position");
currentTarget = "loop-signInUpLink";
yield showInfoPromise(currentTarget, "This is " + currentTarget, "My arrow should be underneath");
is(popup.popupBoxObject.alignmentPosition, "after_end", "Check " + currentTarget + " position");
}),
taskify(function* test_setConfiguration() {
is(Services.prefs.getBoolPref("loop.gettingStarted.resumeOnFirstJoin"), false, "pref should be false but exist");
gContentAPI.setConfiguration("Loop:ResumeTourOnFirstJoin", true);
yield waitForConditionPromise(() => {
return Services.prefs.getBoolPref("loop.gettingStarted.resumeOnFirstJoin");
}, "Pref should change to true via setConfiguration");
Services.prefs.clearUserPref("loop.gettingStarted.resumeOnFirstJoin");
}),
taskify(function* test_resumeViaMenuPanel_roomClosedTabOpen() {
Services.prefs.setBoolPref("loop.gettingStarted.resumeOnFirstJoin", true);
// Create a fake room and then add a fake non-owner participant
let roomsMap = setupFakeRoom();
roomsMap.get("fakeTourRoom").participants = [{
owner: false,
}];
// Set the tour URL to be the current page with a different query param
let gettingStartedURL = gTestTab.linkedBrowser.currentURI.resolve("?gettingstarted=1");
Services.prefs.setCharPref("loop.gettingStarted.url", gettingStartedURL);
let observationPromise = new Promise((resolve) => {
gContentAPI.observe((event, params) => {
is(event, "Loop:IncomingConversation", "Page should have been notified about incoming conversation");
is(params.conversationOpen, false, "conversationOpen should be false");
is(gBrowser.selectedTab, gTestTab, "The same tab should be selected");
resolve();
});
});
// Now open the menu while that non-owner is in the fake room to trigger resuming the tour
yield showMenuPromise("loop");
yield observationPromise;
Services.prefs.clearUserPref("loop.gettingStarted.resumeOnFirstJoin");
}),
taskify(function* test_resumeViaMenuPanel_roomClosedTabClosed() {
Services.prefs.setBoolPref("loop.gettingStarted.resumeOnFirstJoin", true);
// Create a fake room and then add a fake non-owner participant
let roomsMap = setupFakeRoom();
roomsMap.get("fakeTourRoom").participants = [{
owner: false,
}];
// Set the tour URL to a page that's not open yet
Services.prefs.setCharPref("loop.gettingStarted.url", gBrowser.currentURI.prePath);
let newTabPromise = waitForConditionPromise(() => {
return gBrowser.currentURI.path.includes("incomingConversation=waiting");
}, "New tab with incomingConversation=waiting should have opened");
// Now open the menu while that non-owner is in the fake room to trigger resuming the tour
yield showMenuPromise("loop");
yield newTabPromise;
yield gBrowser.removeCurrentTab();
Services.prefs.clearUserPref("loop.gettingStarted.resumeOnFirstJoin");
}),
];
// End tests
function checkLoopPanelIsHidden() {
ok(!loopPanel.hasAttribute("noautohide"), "@noautohide on the loop panel should have been cleaned up");
ok(!loopPanel.hasAttribute("panelopen"), "The panel shouldn't have @panelopen");
isnot(loopPanel.state, "open", "The panel shouldn't be open");
is(loopButton.hasAttribute("open"), false, "Loop button should know that the panel is closed");
}
function setupFakeRoom() {
let room = Object.create(fakeRoom);
let roomsMap = new Map([
[room.roomToken, room]
]);
LoopRooms.stubCache(roomsMap);
return roomsMap;
}
if (Services.prefs.getBoolPref("loop.enabled")) {
loopButton = window.LoopUI.toolbarButton.node;
fakeRoom = {
decryptedContext: { roomName: "fakeTourRoom" },
participants: [],
maxSize: 2,
ctime: Date.now()
};
for (let prop of ["roomToken", "roomOwner", "roomUrl"])
fakeRoom[prop] = "fakeTourRoom";
LoopAPI.stubMessageHandlers(gMessageHandlers = {
// Stub the rooms object API to fully control the test behavior.
"Rooms:*": function(action, message, reply) {
switch (action.split(":").pop()) {
case "GetAll":
reply([fakeRoom]);
break;
case "Get":
reply(fakeRoom);
break;
case "Join":
reply({
apiKey: "fakeTourRoom",
sessionToken: "fakeTourRoom",
sessionId: "fakeTourRoom",
expires: Date.now() + 240000
});
break;
case "RefreshMembership":
reply({ expires: Date.now() + 240000 });
default:
reply();
}
},
// Stub the metadata retrieval to suppress console warnings and return faster.
GetSelectedTabMetadata: function(message, reply) {
reply({ favicon: null });
}
});
registerCleanupFunction(() => {
Services.prefs.clearUserPref("loop.gettingStarted.resumeOnFirstJoin");
Services.prefs.clearUserPref("loop.gettingStarted.seen");
Services.prefs.clearUserPref("loop.gettingStarted.url");
Services.io.offline = false;
// Copied from browser/components/loop/test/mochitest/head.js
// Remove the iframe after each test. This also avoids mochitest complaining
// about leaks on shutdown as we intentionally hold the iframe open for the
// life of the application.
let frameId = loopButton.getAttribute("notificationFrameId");
let frame = document.getElementById(frameId);
if (frame) {
frame.remove();
}
// Remove the stubbed rooms.
LoopRooms.stubCache(null);
// Restore the stubbed handlers.
LoopAPI.restore();
});
} else {
ok(true, "Loop is disabled so skip the UITour Loop tests");
tests = [];
}