Bug 1225832: partially fix UITour for Loop without navigator.mozLoop present. r=Standard8

This commit is contained in:
Mike de Boer 2015-12-01 12:17:28 +01:00
parent 978190afcd
commit 43df676e10
17 changed files with 376 additions and 257 deletions

View File

@ -6,10 +6,13 @@
var gTestTab;
var gContentAPI;
var gContentWindow;
var gMessageHandlers;
var loopButton;
var fakeRoom;
var loopPanel = document.getElementById("loop-notification-panel");
const { LoopRooms } = Components.utils.import("chrome://loop/content/modules/LoopRooms.jsm", {});
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() {
@ -50,12 +53,12 @@ var tests = [
ok(gettingStartedButton, "Getting Started button should be found");
let newTabPromise = waitForConditionPromise(() => {
return gBrowser.currentURI.path.contains("utm_source=firefox-browser");
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.contains("utm_content=hello-tour_OpenPanel_testPage"),
ok(gBrowser.currentURI.path.includes("utm_content=hello-tour_OpenPanel_testPage"),
"Expected URL opened (" + gBrowser.currentURI.path + ")");
yield gBrowser.removeCurrentTab();
@ -66,7 +69,7 @@ var tests = [
// 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");
var event = new loopWin.CustomEvent("GettingStartedSeen", { detail: false });
loopWin.dispatchEvent(event);
UITour.pageIDsForSession.clear();
@ -93,12 +96,12 @@ var tests = [
let newTabPromise = waitForConditionPromise(() => {
Services.console.logStringMessage(gBrowser.currentURI.path);
return gBrowser.currentURI.path.contains("utm_source=firefox-browser");
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.contains("utm_content=hello-tour_OpenPanel_testPageOldId"),
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();
@ -216,10 +219,14 @@ var tests = [
chat.close();
done();
});
chat.content.contentDocument.querySelector(".btn-copy").click();
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());
});
});
setupFakeRoom();
LoopRooms.open("fakeTourRoom");
}),
runOffline(function test_notifyLoopRoomURLEmailed(done) {
@ -241,17 +248,19 @@ var tests = [
done();
});
let chatWin = chat.content.contentWindow;
let oldComposeEmail = chatWin.navigator.wrappedJSObject.mozLoop.composeEmail;
chatWin.navigator.wrappedJSObject.mozLoop.composeEmail = function(recipient, subject, body) {
ok(recipient, "composeEmail should be invoked with at least a recipient value");
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;
chatWin.navigator.wrappedJSObject.mozLoop.composeEmail = oldComposeEmail;
reply();
};
chatWin.document.querySelector(".btn-email").click();
waitForConditionPromise(
() => chat.content.contentDocument.querySelector(".btn-email"),
"Email button should be there"
).then(() => chat.content.contentDocument.querySelector(".btn-email").click());
});
});
setupFakeRoom();
LoopRooms.open("fakeTourRoom");
}),
taskify(function* test_arrow_panel_position() {
@ -323,7 +332,7 @@ var tests = [
Services.prefs.setCharPref("loop.gettingStarted.url", gBrowser.currentURI.prePath);
let newTabPromise = waitForConditionPromise(() => {
return gBrowser.currentURI.path.contains("incomingConversation=waiting");
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
@ -346,11 +355,7 @@ function checkLoopPanelIsHidden() {
}
function setupFakeRoom() {
let room = {};
for (let prop of ["roomToken", "roomOwner", "roomUrl", "participants"])
room[prop] = "fakeTourRoom";
room.decryptedContext = {roomName: "fakeTourRoom"};
room.participants = [];
let room = Object.create(fakeRoom);
let roomsMap = new Map([
[room.roomToken, room]
]);
@ -361,6 +366,45 @@ function setupFakeRoom() {
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");
@ -377,8 +421,10 @@ if (Services.prefs.getBoolPref("loop.enabled")) {
frame.remove();
}
// Remove the stubbed rooms
// 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");

View File

@ -896,9 +896,10 @@ const kMessageHandlers = {
* message handler. The result will be sent back to
* the senders' channel.
*/
SetLoopPref: function(message) {
SetLoopPref: function(message, reply) {
let [prefName, value, prefType] = message.data;
MozLoopService.setLoopPref(prefName, value, prefType);
reply();
},
/**
@ -1077,7 +1078,11 @@ const LoopAPIInternal = {
if (!reply) {
reply = result => {
message.target.sendAsyncMessage(message.name, [seq, result]);
try {
message.target.sendAsyncMessage(message.name, [seq, result]);
} catch (ex) {
MozLoopService.log.error("Failed to send reply back to content:", ex);
}
}
}

View File

@ -91,7 +91,16 @@ loop.conversation = (function(mozL10n) {
* Conversation initialisation.
*/
function init() {
return loop.requestMulti(
// Obtain the windowId and pass it through
var locationHash = loop.shared.utils.locationData().hash;
var windowId;
var hash = locationHash.match(/#(.*)/);
if (hash) {
windowId = hash[1];
}
var requests = [
["GetAllConstants"],
["GetAllStrings"],
["GetLocale"],
@ -99,12 +108,20 @@ loop.conversation = (function(mozL10n) {
["GetLoopPref", "textChat.enabled"],
["GetLoopPref", "feedback.periodSec"],
["GetLoopPref", "feedback.dateLastSeenSec"]
).then(function(results) {
var constants = results[0];
];
var prefetch = [
["GetConversationWindowData", windowId]
];
return loop.requestMulti.apply(null, requests.concat(prefetch)).then(function(results) {
// `requestIdx` is keyed off the order of the `requests` and `prefetch`
// arrays. Be careful to update both when making changes.
var requestIdx = 0;
var constants = results[requestIdx];
// Do the initial L10n setup, we do this before anything
// else to ensure the L10n environment is setup correctly.
var stringBundle = results[1];
var locale = results[2];
var stringBundle = results[++requestIdx];
var locale = results[++requestIdx];
mozL10n.initialize({
locale: locale,
getStrings: function(key) {
@ -119,20 +136,22 @@ loop.conversation = (function(mozL10n) {
// Plug in an alternate client ID mechanism, as localStorage and cookies
// don't work in the conversation window
var currGuid = results[++requestIdx];
window.OT.overrideGuidStorage({
get: function(callback) {
callback(null, results[3]);
callback(null, currGuid);
},
set: function(guid, callback) {
// See nsIPrefBranch
var PREF_STRING = 32;
currGuid = guid;
loop.request("SetLoopPref", "ot.guid", guid, PREF_STRING);
callback(null);
}
});
// We want data channels only if the text chat preference is enabled.
var useDataChannels = results[4];
var useDataChannels = results[++requestIdx];
var dispatcher = new loop.Dispatcher();
var sdkDriver = new loop.OTSdkDriver({
@ -154,9 +173,15 @@ loop.conversation = (function(mozL10n) {
var conversationAppStore = new loop.store.ConversationAppStore({
activeRoomStore: activeRoomStore,
dispatcher: dispatcher,
feedbackPeriod: results[5],
feedbackTimestamp: results[6]
feedbackPeriod: results[++requestIdx],
feedbackTimestamp: results[++requestIdx]
});
prefetch.forEach(function(req) {
req.shift();
loop.storeRequest(req, results[++requestIdx]);
});
var roomStore = new loop.store.RoomStore(dispatcher, {
activeRoomStore: activeRoomStore,
constants: constants
@ -170,15 +195,6 @@ loop.conversation = (function(mozL10n) {
textChatStore: textChatStore
});
// Obtain the windowId and pass it through
var locationHash = loop.shared.utils.locationData().hash;
var windowId;
var hash = locationHash.match(/#(.*)/);
if (hash) {
windowId = hash[1];
}
React.render(
React.createElement(AppControllerView, {
dispatcher: dispatcher,

View File

@ -91,7 +91,16 @@ loop.conversation = (function(mozL10n) {
* Conversation initialisation.
*/
function init() {
return loop.requestMulti(
// Obtain the windowId and pass it through
var locationHash = loop.shared.utils.locationData().hash;
var windowId;
var hash = locationHash.match(/#(.*)/);
if (hash) {
windowId = hash[1];
}
var requests = [
["GetAllConstants"],
["GetAllStrings"],
["GetLocale"],
@ -99,12 +108,20 @@ loop.conversation = (function(mozL10n) {
["GetLoopPref", "textChat.enabled"],
["GetLoopPref", "feedback.periodSec"],
["GetLoopPref", "feedback.dateLastSeenSec"]
).then(function(results) {
var constants = results[0];
];
var prefetch = [
["GetConversationWindowData", windowId]
];
return loop.requestMulti.apply(null, requests.concat(prefetch)).then(function(results) {
// `requestIdx` is keyed off the order of the `requests` and `prefetch`
// arrays. Be careful to update both when making changes.
var requestIdx = 0;
var constants = results[requestIdx];
// Do the initial L10n setup, we do this before anything
// else to ensure the L10n environment is setup correctly.
var stringBundle = results[1];
var locale = results[2];
var stringBundle = results[++requestIdx];
var locale = results[++requestIdx];
mozL10n.initialize({
locale: locale,
getStrings: function(key) {
@ -119,20 +136,22 @@ loop.conversation = (function(mozL10n) {
// Plug in an alternate client ID mechanism, as localStorage and cookies
// don't work in the conversation window
var currGuid = results[++requestIdx];
window.OT.overrideGuidStorage({
get: function(callback) {
callback(null, results[3]);
callback(null, currGuid);
},
set: function(guid, callback) {
// See nsIPrefBranch
var PREF_STRING = 32;
currGuid = guid;
loop.request("SetLoopPref", "ot.guid", guid, PREF_STRING);
callback(null);
}
});
// We want data channels only if the text chat preference is enabled.
var useDataChannels = results[4];
var useDataChannels = results[++requestIdx];
var dispatcher = new loop.Dispatcher();
var sdkDriver = new loop.OTSdkDriver({
@ -154,9 +173,15 @@ loop.conversation = (function(mozL10n) {
var conversationAppStore = new loop.store.ConversationAppStore({
activeRoomStore: activeRoomStore,
dispatcher: dispatcher,
feedbackPeriod: results[5],
feedbackTimestamp: results[6]
feedbackPeriod: results[++requestIdx],
feedbackTimestamp: results[++requestIdx]
});
prefetch.forEach(function(req) {
req.shift();
loop.storeRequest(req, results[++requestIdx]);
});
var roomStore = new loop.store.RoomStore(dispatcher, {
activeRoomStore: activeRoomStore,
constants: constants
@ -170,15 +195,6 @@ loop.conversation = (function(mozL10n) {
textChatStore: textChatStore
});
// Obtain the windowId and pass it through
var locationHash = loop.shared.utils.locationData().hash;
var windowId;
var hash = locationHash.match(/#(.*)/);
if (hash) {
windowId = hash[1];
}
React.render(
<AppControllerView
dispatcher={dispatcher}

View File

@ -106,19 +106,19 @@ loop.store.ConversationAppStore = (function() {
* @param {sharedActions.GetWindowData} actionData The action data
*/
getWindowData: function(actionData) {
loop.request("GetConversationWindowData", actionData.windowId)
.then(function(windowData) {
if (!windowData) {
console.error("Failed to get the window data");
this.setStoreState({ windowType: "failed" });
return;
}
var windowData = loop.getStoredRequest(["GetConversationWindowData",
actionData.windowId]);
this.setStoreState({ windowType: windowData.type });
if (!windowData) {
console.error("Failed to get the window data");
this.setStoreState({ windowType: "failed" });
return;
}
this._dispatcher.dispatch(new loop.shared.actions.SetupWindowData(_.extend({
windowId: actionData.windowId }, windowData)));
}.bind(this));
this.setStoreState({ windowType: windowData.type });
this._dispatcher.dispatch(new loop.shared.actions.SetupWindowData(_.extend({
windowId: actionData.windowId }, windowData)));
},
/**

View File

@ -20,7 +20,7 @@ loop.panel = (function(_, mozL10n) {
loop.requestMulti(
["OpenGettingStartedTour", "getting-started"],
["SetLoopPref", "gettingStarted.seen", true]);
var event = new CustomEvent("GettingStartedSeen");
var event = new CustomEvent("GettingStartedSeen", { detail: true });
window.dispatchEvent(event);
this.closeWindow();
},
@ -94,23 +94,11 @@ loop.panel = (function(_, mozL10n) {
getInitialState: function() {
return {
terms_of_use_url: null,
privacy_notice_url: null
terms_of_use_url: loop.getStoredRequest(["GetLoopPref", "legal.ToS_url"]),
privacy_notice_url: loop.getStoredRequest(["GetLoopPref", "legal.privacy_url"])
};
},
componentWillMount: function() {
loop.requestMulti(
["GetLoopPref", "legal.ToS_url"],
["GetLoopPref", "legal.privacy_url"]
).then(function(results) {
this.setState({
terms_of_use_url: results[0],
privacy_notice_url: results[1]
});
}.bind(this));
},
handleLinkClick: function(event) {
if (!event.target || !event.target.href) {
return;
@ -197,36 +185,28 @@ loop.panel = (function(_, mozL10n) {
getInitialState: function() {
return {
doNotDisturb: false,
fxAEnabled: false,
signedIn: false
signedIn: !!loop.getStoredRequest(["GetUserProfile"]),
fxAEnabled: loop.getStoredRequest(["GetFxAEnabled"]),
doNotDisturb: loop.getStoredRequest(["GetDoNotDisturb"])
};
},
componentWillMount: function() {
this._updateState();
},
componentWillUpdate: function(nextProps, nextState) {
if (nextState.showMenu !== this.state.showMenu) {
this._updateState();
loop.requestMulti(
["GetUserProfile"],
["GetFxAEnabled"],
["GetDoNotDisturb"]
).then(function(results) {
this.setState({
signedIn: !!results[0],
fxAEnabled: results[1],
doNotDisturb: results[2]
});
}.bind(this));
}
},
_updateState: function() {
loop.requestMulti(
["GetUserProfile"],
["GetFxAEnabled"],
["GetDoNotDisturb"]
).then(function(results) {
this.setState({
signedIn: !!results[0],
fxAEnabled: results[1],
doNotDisturb: results[2]
});
}.bind(this));
},
handleClickSettingsEntry: function() {
// XXX to be implemented at the same time as unhiding the entry
},
@ -922,11 +902,11 @@ loop.panel = (function(_, mozL10n) {
getInitialState: function() {
return {
fxAEnabled: true,
hasEncryptionKey: false,
userProfile: null,
gettingStartedSeen: true,
multiProcessEnabled: false
fxAEnabled: loop.getStoredRequest(["GetFxAEnabled"]),
hasEncryptionKey: loop.getStoredRequest(["GetHasEncryptionKey"]),
userProfile: loop.getStoredRequest(["GetUserProfile"]),
gettingStartedSeen: loop.getStoredRequest(["GetLoopPref", "gettingStarted.seen"]),
multiProcessEnabled: loop.getStoredRequest(["IsMultiProcessEnabled"])
};
},
@ -983,32 +963,12 @@ loop.panel = (function(_, mozL10n) {
}.bind(this));
},
_gettingStartedSeen: function() {
loop.request("GetLoopPref", "gettingStarted.seen").then(function(result) {
this.setState({
gettingStartedSeen: result
});
}.bind(this));
_gettingStartedSeen: function(e) {
this.setState({ gettingStartedSeen: !!e.detail });
},
componentWillMount: function() {
this.updateServiceErrors();
loop.requestMulti(
["GetFxAEnabled"],
["GetHasEncryptionKey"],
["GetUserProfile"],
["GetLoopPref", "gettingStarted.seen"],
["IsMultiProcessEnabled"]
).then(function(results) {
this.setState({
fxAEnabled: results[0],
hasEncryptionKey: results[1],
userProfile: results[2],
gettingStartedSeen: results[3],
multiProcessEnabled: results[4]
});
}.bind(this));
},
componentDidMount: function() {
@ -1084,18 +1044,33 @@ loop.panel = (function(_, mozL10n) {
* Panel initialisation.
*/
function init() {
return loop.requestMulti(
var requests = [
["GetAllConstants"],
["GetAllStrings"],
["GetLocale"],
["GetPluralRule"]
).then(function(results) {
var constants = results[0];
];
var prefetch = [
["GetLoopPref", "gettingStarted.seen"],
["GetLoopPref", "legal.ToS_url"],
["GetLoopPref", "legal.privacy_url"],
["GetUserProfile"],
["GetFxAEnabled"],
["GetDoNotDisturb"],
["GetHasEncryptionKey"],
["IsMultiProcessEnabled"]
];
return loop.requestMulti.apply(null, requests.concat(prefetch)).then(function(results) {
// `requestIdx` is keyed off the order of the `requests` and `prefetch`
// arrays. Be careful to update both when making changes.
var requestIdx = 0;
var constants = results[requestIdx];
// Do the initial L10n setup, we do this before anything
// else to ensure the L10n environment is setup correctly.
var stringBundle = results[1];
var locale = results[2];
var pluralRule = results[3];
var stringBundle = results[++requestIdx];
var locale = results[++requestIdx];
var pluralRule = results[++requestIdx];
mozL10n.initialize({
locale: locale,
pluralRule: pluralRule,
@ -1109,6 +1084,11 @@ loop.panel = (function(_, mozL10n) {
}
});
prefetch.forEach(function(req) {
req.shift();
loop.storeRequest(req, results[++requestIdx]);
});
var notifications = new sharedModels.NotificationCollection();
var dispatcher = new loop.Dispatcher();
var roomStore = new loop.store.RoomStore(dispatcher, {

View File

@ -20,7 +20,7 @@ loop.panel = (function(_, mozL10n) {
loop.requestMulti(
["OpenGettingStartedTour", "getting-started"],
["SetLoopPref", "gettingStarted.seen", true]);
var event = new CustomEvent("GettingStartedSeen");
var event = new CustomEvent("GettingStartedSeen", { detail: true });
window.dispatchEvent(event);
this.closeWindow();
},
@ -94,23 +94,11 @@ loop.panel = (function(_, mozL10n) {
getInitialState: function() {
return {
terms_of_use_url: null,
privacy_notice_url: null
terms_of_use_url: loop.getStoredRequest(["GetLoopPref", "legal.ToS_url"]),
privacy_notice_url: loop.getStoredRequest(["GetLoopPref", "legal.privacy_url"])
};
},
componentWillMount: function() {
loop.requestMulti(
["GetLoopPref", "legal.ToS_url"],
["GetLoopPref", "legal.privacy_url"]
).then(function(results) {
this.setState({
terms_of_use_url: results[0],
privacy_notice_url: results[1]
});
}.bind(this));
},
handleLinkClick: function(event) {
if (!event.target || !event.target.href) {
return;
@ -197,36 +185,28 @@ loop.panel = (function(_, mozL10n) {
getInitialState: function() {
return {
doNotDisturb: false,
fxAEnabled: false,
signedIn: false
signedIn: !!loop.getStoredRequest(["GetUserProfile"]),
fxAEnabled: loop.getStoredRequest(["GetFxAEnabled"]),
doNotDisturb: loop.getStoredRequest(["GetDoNotDisturb"])
};
},
componentWillMount: function() {
this._updateState();
},
componentWillUpdate: function(nextProps, nextState) {
if (nextState.showMenu !== this.state.showMenu) {
this._updateState();
loop.requestMulti(
["GetUserProfile"],
["GetFxAEnabled"],
["GetDoNotDisturb"]
).then(function(results) {
this.setState({
signedIn: !!results[0],
fxAEnabled: results[1],
doNotDisturb: results[2]
});
}.bind(this));
}
},
_updateState: function() {
loop.requestMulti(
["GetUserProfile"],
["GetFxAEnabled"],
["GetDoNotDisturb"]
).then(function(results) {
this.setState({
signedIn: !!results[0],
fxAEnabled: results[1],
doNotDisturb: results[2]
});
}.bind(this));
},
handleClickSettingsEntry: function() {
// XXX to be implemented at the same time as unhiding the entry
},
@ -922,11 +902,11 @@ loop.panel = (function(_, mozL10n) {
getInitialState: function() {
return {
fxAEnabled: true,
hasEncryptionKey: false,
userProfile: null,
gettingStartedSeen: true,
multiProcessEnabled: false
fxAEnabled: loop.getStoredRequest(["GetFxAEnabled"]),
hasEncryptionKey: loop.getStoredRequest(["GetHasEncryptionKey"]),
userProfile: loop.getStoredRequest(["GetUserProfile"]),
gettingStartedSeen: loop.getStoredRequest(["GetLoopPref", "gettingStarted.seen"]),
multiProcessEnabled: loop.getStoredRequest(["IsMultiProcessEnabled"])
};
},
@ -983,32 +963,12 @@ loop.panel = (function(_, mozL10n) {
}.bind(this));
},
_gettingStartedSeen: function() {
loop.request("GetLoopPref", "gettingStarted.seen").then(function(result) {
this.setState({
gettingStartedSeen: result
});
}.bind(this));
_gettingStartedSeen: function(e) {
this.setState({ gettingStartedSeen: !!e.detail });
},
componentWillMount: function() {
this.updateServiceErrors();
loop.requestMulti(
["GetFxAEnabled"],
["GetHasEncryptionKey"],
["GetUserProfile"],
["GetLoopPref", "gettingStarted.seen"],
["IsMultiProcessEnabled"]
).then(function(results) {
this.setState({
fxAEnabled: results[0],
hasEncryptionKey: results[1],
userProfile: results[2],
gettingStartedSeen: results[3],
multiProcessEnabled: results[4]
});
}.bind(this));
},
componentDidMount: function() {
@ -1084,18 +1044,33 @@ loop.panel = (function(_, mozL10n) {
* Panel initialisation.
*/
function init() {
return loop.requestMulti(
var requests = [
["GetAllConstants"],
["GetAllStrings"],
["GetLocale"],
["GetPluralRule"]
).then(function(results) {
var constants = results[0];
];
var prefetch = [
["GetLoopPref", "gettingStarted.seen"],
["GetLoopPref", "legal.ToS_url"],
["GetLoopPref", "legal.privacy_url"],
["GetUserProfile"],
["GetFxAEnabled"],
["GetDoNotDisturb"],
["GetHasEncryptionKey"],
["IsMultiProcessEnabled"]
];
return loop.requestMulti.apply(null, requests.concat(prefetch)).then(function(results) {
// `requestIdx` is keyed off the order of the `requests` and `prefetch`
// arrays. Be careful to update both when making changes.
var requestIdx = 0;
var constants = results[requestIdx];
// Do the initial L10n setup, we do this before anything
// else to ensure the L10n environment is setup correctly.
var stringBundle = results[1];
var locale = results[2];
var pluralRule = results[3];
var stringBundle = results[++requestIdx];
var locale = results[++requestIdx];
var pluralRule = results[++requestIdx];
mozL10n.initialize({
locale: locale,
pluralRule: pluralRule,
@ -1109,6 +1084,11 @@ loop.panel = (function(_, mozL10n) {
}
});
prefetch.forEach(function(req) {
req.shift();
loop.storeRequest(req, results[++requestIdx]);
});
var notifications = new sharedModels.NotificationCollection();
var dispatcher = new loop.Dispatcher();
var roomStore = new loop.store.RoomStore(dispatcher, {

View File

@ -323,7 +323,7 @@ loop.roomViews = (function(mozL10n) {
},
render: function() {
if (!this.props.show) {
if (!this.props.show || !this.props.roomData.roomUrl) {
return null;
}

View File

@ -323,7 +323,7 @@ loop.roomViews = (function(mozL10n) {
},
render: function() {
if (!this.props.show) {
if (!this.props.show || !this.props.roomData.roomUrl) {
return null;
}

View File

@ -95,6 +95,37 @@ var loop = loop || {};
gListenersMap = {};
};
loop.storedRequests = {};
/**
* Store the result of a request for access at a later time.
*
* @param {Array} request Set of request parameters
* @param {mixed} result Whatever the API returned as a result
*/
loop.storeRequest = function(request, result) {
loop.storedRequests[request.join("|")] = result;
};
/**
* Retrieve the result of a request that was stored previously. If the result
* can not be found, |NULL| will be returned and an error will be logged to the
* console.
*
* @param {Array} request Set of request parameters
* @return {mixed} Whatever the result of the API request was at the time it was
* stored.
*/
loop.getStoredRequest = function(request) {
var key = request.join("|");
if (!(key in loop.storedRequests)) {
console.error("This request has not been stored!", request);
return null;
}
return loop.storedRequests[key];
};
/**
* Send multiple requests at once as a batch.
*

View File

@ -44,6 +44,7 @@ fi
TESTS="
${LOOPDIR}/test/mochitest
browser/components/uitour/test/browser_UITour_loop.js
browser/base/content/test/general/browser_devices_get_user_media_about_urls.js
browser/base/content/test/general/browser_parsable_css.js
"
@ -55,6 +56,7 @@ do
./mach mochitest $test
# UITour & get user media aren't compatible with e10s currenly.
if [ "$1" != "--skip-e10s" ] && \
[ "$test" != "browser/components/uitour/test/browser_UITour_loop.js" ] && \
[ "$test" != "browser/base/content/test/general/browser_devices_get_user_media_about_urls.js" ];
then
./mach mochitest --e10s $test

View File

@ -106,13 +106,10 @@ describe("loop.store.ConversationAppStore", function() {
getLoopPrefStub = sandbox.stub();
setLoopPrefStub = sandbox.stub();
loop.storedRequests = {
"GetConversationWindowData|42": fakeWindowData
};
LoopMochaUtils.stubLoopRequest({
GetConversationWindowData: function(windowId) {
if (windowId === "42") {
return fakeWindowData;
}
return null;
},
GetLoopPref: getLoopPrefStub,
SetLoopPref: setLoopPrefStub
});

View File

@ -57,6 +57,9 @@ describe("loop.conversation", function() {
}),
GetSelectedTabMetadata: function() {
return {};
},
GetConversationWindowData: function() {
return {};
}
});

View File

@ -77,6 +77,17 @@ describe("loop.panel", function() {
GetUserProfile: function() { return null; }
});
loop.storedRequests = {
GetFxAEnabled: true,
GetHasEncryptionKey: true,
GetUserProfile: null,
GetDoNotDisturb: false,
"GetLoopPref|gettingStarted.seen": "unseen",
"GetLoopPref|legal.ToS_url": "",
"GetLoopPref|legal.privacy_url": "",
IsMultiProcessEnabled: false
};
roomName = "First Room Name";
roomData = {
roomToken: "QzBbvGmIZWU",
@ -255,11 +266,7 @@ describe("loop.panel", function() {
});
it("should add ellipsis to text over 24chars", function() {
LoopMochaUtils.stubLoopRequest({
GetUserProfile: function() {
return { email: "reallyreallylongtext@example.com" };
}
});
loop.storedRequests.GetUserProfile = { email: "reallyreallylongtext@example.com" };
var view = createTestPanelView();
var node = view.getDOMNode().querySelector(".user-identity");
@ -349,9 +356,7 @@ describe("loop.panel", function() {
});
it("should show a signout entry when user is authenticated", function() {
LoopMochaUtils.stubLoopRequest({
GetUserProfile: function() { return { email: "test@example.com" }; }
});
loop.storedRequests.GetUserProfile = { email: "test@example.com" };
var view = mountTestComponent();
@ -374,9 +379,7 @@ describe("loop.panel", function() {
it("should open the FxA settings when the account entry is clicked",
function() {
LoopMochaUtils.stubLoopRequest({
GetUserProfile: function() { return { email: "test@example.com" }; }
});
loop.storedRequests.GetUserProfile = { email: "test@example.com" };
var view = mountTestComponent();
@ -387,9 +390,7 @@ describe("loop.panel", function() {
});
it("should sign out the user on click when authenticated", function() {
LoopMochaUtils.stubLoopRequest({
GetUserProfile: function() { return { email: "test@example.com" }; }
});
loop.storedRequests.GetUserProfile = { email: "test@example.com" };
var view = mountTestComponent();
TestUtils.Simulate.click(view.getDOMNode()
@ -542,9 +543,7 @@ describe("loop.panel", function() {
});
it("should not render a ToSView when gettingStarted.seen is false", function() {
LoopMochaUtils.stubLoopRequest({
GetLoopPref: function() { return false; }
});
loop.storedRequests["GetLoopPref|gettingStarted.seen"] = false;
var view = createTestPanelView();
expect(function() {
@ -553,18 +552,14 @@ describe("loop.panel", function() {
});
it("should render a GettingStarted view", function() {
LoopMochaUtils.stubLoopRequest({
GetLoopPref: function() { return false; }
});
loop.storedRequests["GetLoopPref|gettingStarted.seen"] = false;
var view = createTestPanelView();
TestUtils.findRenderedComponentWithType(view, loop.panel.GettingStartedView);
});
it("should not render a GettingStartedView when the view has been seen", function() {
LoopMochaUtils.stubLoopRequest({
GetLoopPref: function() { return true; }
});
loop.storedRequests["GetLoopPref|gettingStarted.seen"] = true;
var view = createTestPanelView();
try {
@ -576,9 +571,7 @@ describe("loop.panel", function() {
});
it("should render a SignInRequestView when mozLoop.hasEncryptionKey is false", function() {
LoopMochaUtils.stubLoopRequest({
GetHasEncryptionKey: function() { return false; }
});
loop.storedRequests.GetHasEncryptionKey = false;
var view = createTestPanelView();
@ -597,9 +590,7 @@ describe("loop.panel", function() {
});
it("should render a E10sNotSupported when multiprocess is enabled", function() {
LoopMochaUtils.stubLoopRequest({
IsMultiProcessEnabled: function() { return true; }
});
loop.storedRequests.IsMultiProcessEnabled = true;
var view = createTestPanelView();

View File

@ -196,7 +196,7 @@ describe("loop.roomViews", function() {
function mountTestComponent(props) {
props = _.extend({
dispatcher: dispatcher,
roomData: {},
roomData: { roomUrl: "http://invalid" },
savingContext: false,
show: true,
showEditContext: false
@ -208,9 +208,7 @@ describe("loop.roomViews", function() {
it("should dispatch an EmailRoomUrl with no description" +
" for rooms without context when the email button is pressed",
function() {
view = mountTestComponent({
roomData: { roomUrl: "http://invalid" }
});
view = mountTestComponent();
var emailBtn = view.getDOMNode().querySelector(".btn-email");
@ -250,9 +248,7 @@ describe("loop.roomViews", function() {
describe("Copy Button", function() {
beforeEach(function() {
view = mountTestComponent({
roomData: { roomUrl: "http://invalid" }
});
view = mountTestComponent();
});
it("should dispatch a CopyRoomUrl action when the copy button is pressed", function() {
@ -323,6 +319,8 @@ describe("loop.roomViews", function() {
}
});
onCallTerminatedStub = sandbox.stub();
activeRoomStore.setStoreState({ roomUrl: "http://invalid " });
});
function mountTestComponent(props) {

View File

@ -109,6 +109,50 @@ describe("loopapi-client", function() {
});
});
describe("loop.storeRequest", function() {
afterEach(function() {
loop.storedRequests = {};
});
it("should the result of a request", function() {
loop.storeRequest(["GetLoopPref"], true);
expect(loop.storedRequests).to.deep.equal({
"GetLoopPref": true
});
});
it("should the result of a request with multiple params", function() {
loop.storeRequest(["GetLoopPref", "enabled", "or", "not", "well",
"perhaps", true, 2], true);
expect(loop.storedRequests).to.deep.equal({
"GetLoopPref|enabled|or|not|well|perhaps|true|2": true
});
});
});
describe("loop.getStoredRequest", function() {
afterEach(function() {
loop.storedRequests = {};
});
it("should retrieve a result", function() {
loop.storedRequests["GetLoopPref"] = true;
expect(loop.getStoredRequest(["GetLoopPref"])).to.eql(true);
});
it("should return log and return null for invalid requests", function() {
sandbox.stub(console, "error");
expect(loop.getStoredRequest(["SomethingNeverStored"])).to.eql(null);
sinon.assert.calledOnce(console.error);
sinon.assert.calledWithExactly(console.error,
"This request has not been stored!", ["SomethingNeverStored"]);
});
});
describe("loop.requestMulti", function() {
it("should send a batch of messages", function() {
var promise = loop.requestMulti(

View File

@ -153,7 +153,6 @@ var fakeRooms = [
},
GetDoNotDisturb: function() { return true; },
GetErrors: function() {},
GetHasEncryptionKey: function() { return true; },
GetLoopPref: function(pref) {
switch (pref) {
// Ensure we skip FTE completely.
@ -179,10 +178,21 @@ var fakeRooms = [
"Rooms:GetAll": function(version) {
return [].concat(fakeRooms);
},
GetFxAEnabled: function() { return true; },
StartAlerting: function() {},
StopAlerting: function() {},
GetUserProfile: function() { return null; },
"Rooms:PushSubscription": function() {}
});
loop.storedRequests = {
GetFxAEnabled: true,
GetHasEncryptionKey: true,
GetUserProfile: null,
GetDoNotDisturb: true,
// Ensure we skip FTE completely.
"GetLoopPref|gettingStarted.seen": true,
"GetLoopPref|legal.ToS_url": null,
"GetLoopPref|legal.privacy_url": null,
IsMultiProcessEnabled: false
};
})();