diff options
Diffstat (limited to 'browser/base/content/test/general/browser_aboutAccounts.js')
-rw-r--r-- | browser/base/content/test/general/browser_aboutAccounts.js | 499 |
1 files changed, 499 insertions, 0 deletions
diff --git a/browser/base/content/test/general/browser_aboutAccounts.js b/browser/base/content/test/general/browser_aboutAccounts.js new file mode 100644 index 000000000..fd72a1608 --- /dev/null +++ b/browser/base/content/test/general/browser_aboutAccounts.js @@ -0,0 +1,499 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// +// Whitelisting this test. +// As part of bug 1077403, the leaking uncaught rejection should be fixed. +// +thisTestLeaksUncaughtRejectionsAndShouldBeFixed("TypeError: window.location is null"); + +XPCOMUtils.defineLazyModuleGetter(this, "Promise", + "resource://gre/modules/Promise.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "Task", + "resource://gre/modules/Task.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts", + "resource://gre/modules/FxAccounts.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "FileUtils", + "resource://gre/modules/FileUtils.jsm"); + +const CHROME_BASE = "chrome://mochitests/content/browser/browser/base/content/test/general/"; +// Preference helpers. +var changedPrefs = new Set(); + +function setPref(name, value) { + changedPrefs.add(name); + Services.prefs.setCharPref(name, value); +} + +registerCleanupFunction(function() { + // Ensure we don't pollute prefs for next tests. + for (let name of changedPrefs) { + Services.prefs.clearUserPref(name); + } +}); + +var gTests = [ +{ + desc: "Test the remote commands", + teardown: function* () { + gBrowser.removeCurrentTab(); + yield signOut(); + }, + run: function* () + { + setPref("identity.fxaccounts.remote.signup.uri", + "https://example.com/browser/browser/base/content/test/general/accounts_testRemoteCommands.html"); + let tab = yield promiseNewTabLoadEvent("about:accounts"); + let mm = tab.linkedBrowser.messageManager; + + let deferred = Promise.defer(); + + // We'll get a message when openPrefs() is called, which this test should + // arrange. + let promisePrefsOpened = promiseOneMessage(tab, "test:openPrefsCalled"); + let results = 0; + try { + mm.addMessageListener("test:response", function responseHandler(msg) { + let data = msg.data.data; + if (data.type == "testResult") { + ok(data.pass, data.info); + results++; + } else if (data.type == "testsComplete") { + is(results, data.count, "Checking number of results received matches the number of tests that should have run"); + mm.removeMessageListener("test:response", responseHandler); + deferred.resolve(); + } + }); + } catch (e) { + ok(false, "Failed to get all commands"); + deferred.reject(); + } + yield deferred.promise; + yield promisePrefsOpened; + } +}, +{ + desc: "Test action=signin - no user logged in", + teardown: () => gBrowser.removeCurrentTab(), + run: function* () + { + // When this loads with no user logged-in, we expect the "normal" URL + const expected_url = "https://example.com/?is_sign_in"; + setPref("identity.fxaccounts.remote.signin.uri", expected_url); + let [tab, url] = yield promiseNewTabWithIframeLoadEvent("about:accounts?action=signin"); + is(url, expected_url, "action=signin got the expected URL"); + // we expect the remote iframe to be shown. + yield checkVisibilities(tab, { + stage: false, // parent of 'manage' and 'intro' + manage: false, + intro: false, // this is "get started" + remote: true, + networkError: false + }); + } +}, +{ + desc: "Test action=signin - user logged in", + teardown: function* () { + gBrowser.removeCurrentTab(); + yield signOut(); + }, + run: function* () + { + // When this loads with a user logged-in, we expect the normal URL to + // have been ignored and the "manage" page to be shown. + const expected_url = "https://example.com/?is_sign_in"; + setPref("identity.fxaccounts.remote.signin.uri", expected_url); + yield setSignedInUser(); + let tab = yield promiseNewTabLoadEvent("about:accounts?action=signin"); + // about:accounts initializes after fetching the current user from Fxa - + // so we also request it - by the time we get it we know it should have + // done its thing. + yield fxAccounts.getSignedInUser(); + // we expect "manage" to be shown. + yield checkVisibilities(tab, { + stage: true, // parent of 'manage' and 'intro' + manage: true, + intro: false, // this is "get started" + remote: false, + networkError: false + }); + } +}, +{ + desc: "Test action=signin - captive portal", + teardown: () => gBrowser.removeCurrentTab(), + run: function* () + { + const signinUrl = "https://redirproxy.example.com/test"; + setPref("identity.fxaccounts.remote.signin.uri", signinUrl); + let [tab, ] = yield promiseNewTabWithIframeLoadEvent("about:accounts?action=signin"); + yield checkVisibilities(tab, { + stage: true, // parent of 'manage' and 'intro' + manage: false, + intro: false, // this is "get started" + remote: false, + networkError: true + }); + } +}, +{ + desc: "Test action=signin - offline", + teardown: () => { + gBrowser.removeCurrentTab(); + BrowserOffline.toggleOfflineStatus(); + }, + run: function* () + { + BrowserOffline.toggleOfflineStatus(); + Services.cache2.clear(); + + const signinUrl = "https://unknowndomain.cow"; + setPref("identity.fxaccounts.remote.signin.uri", signinUrl); + let [tab, ] = yield promiseNewTabWithIframeLoadEvent("about:accounts?action=signin"); + yield checkVisibilities(tab, { + stage: true, // parent of 'manage' and 'intro' + manage: false, + intro: false, // this is "get started" + remote: false, + networkError: true + }); + } +}, +{ + desc: "Test action=signup - no user logged in", + teardown: () => gBrowser.removeCurrentTab(), + run: function* () + { + const expected_url = "https://example.com/?is_sign_up"; + setPref("identity.fxaccounts.remote.signup.uri", expected_url); + let [tab, url] = yield promiseNewTabWithIframeLoadEvent("about:accounts?action=signup"); + is(url, expected_url, "action=signup got the expected URL"); + // we expect the remote iframe to be shown. + yield checkVisibilities(tab, { + stage: false, // parent of 'manage' and 'intro' + manage: false, + intro: false, // this is "get started" + remote: true, + networkError: false + }); + }, +}, +{ + desc: "Test action=signup - user logged in", + teardown: () => gBrowser.removeCurrentTab(), + run: function* () + { + const expected_url = "https://example.com/?is_sign_up"; + setPref("identity.fxaccounts.remote.signup.uri", expected_url); + yield setSignedInUser(); + let tab = yield promiseNewTabLoadEvent("about:accounts?action=signup"); + yield fxAccounts.getSignedInUser(); + // we expect "manage" to be shown. + yield checkVisibilities(tab, { + stage: true, // parent of 'manage' and 'intro' + manage: true, + intro: false, // this is "get started" + remote: false, + networkError: false + }); + }, +}, +{ + desc: "Test action=reauth", + teardown: function* () { + gBrowser.removeCurrentTab(); + yield signOut(); + }, + run: function* () + { + const expected_url = "https://example.com/?is_force_auth"; + setPref("identity.fxaccounts.remote.force_auth.uri", expected_url); + + yield setSignedInUser(); + let [, url] = yield promiseNewTabWithIframeLoadEvent("about:accounts?action=reauth"); + // The current user will be appended to the url + let expected = expected_url + "&email=foo%40example.com"; + is(url, expected, "action=reauth got the expected URL"); + }, +}, +{ + desc: "Test with migrateToDevEdition enabled (success)", + teardown: function* () { + gBrowser.removeCurrentTab(); + yield signOut(); + }, + run: function* () + { + let fxAccountsCommon = {}; + Cu.import("resource://gre/modules/FxAccountsCommon.js", fxAccountsCommon); + const pref = "identity.fxaccounts.migrateToDevEdition"; + changedPrefs.add(pref); + Services.prefs.setBoolPref(pref, true); + + // Create the signedInUser.json file that will be used as the source of + // migrated user data. + let signedInUser = { + version: 1, + accountData: { + email: "foo@example.com", + uid: "1234@lcip.org", + sessionToken: "dead", + verified: true + } + }; + // We use a sub-dir of the real profile dir as the "pretend" profile dir + // for this test. + let profD = Services.dirsvc.get("ProfD", Ci.nsIFile); + let mockDir = profD.clone(); + mockDir.append("about-accounts-mock-profd"); + mockDir.createUnique(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY); + let fxAccountsStorage = OS.Path.join(mockDir.path, fxAccountsCommon.DEFAULT_STORAGE_FILENAME); + yield OS.File.writeAtomic(fxAccountsStorage, JSON.stringify(signedInUser)); + info("Wrote file " + fxAccountsStorage); + + // this is a little subtle - we load about:robots so we get a non-remote + // tab, then we send a message which does both (a) load the URL we want and + // (b) mocks the default profile path used by about:accounts. + let tab = yield promiseNewTabLoadEvent("about:robots"); + let readyPromise = promiseOneMessage(tab, "test:load-with-mocked-profile-path-response"); + + let mm = tab.linkedBrowser.messageManager; + mm.sendAsyncMessage("test:load-with-mocked-profile-path", { + url: "about:accounts", + profilePath: mockDir.path, + }); + + let response = yield readyPromise; + // We are expecting the iframe to be on the "force reauth" URL + let expected = yield fxAccounts.promiseAccountsForceSigninURI(); + is(response.data.url, expected); + + let userData = yield fxAccounts.getSignedInUser(); + SimpleTest.isDeeply(userData, signedInUser.accountData, "All account data were migrated"); + // The migration pref will have been switched off by now. + is(Services.prefs.getBoolPref(pref), false, pref + " got the expected value"); + + yield OS.File.remove(fxAccountsStorage); + yield OS.File.removeEmptyDir(mockDir.path); + }, +}, +{ + desc: "Test with migrateToDevEdition enabled (no user to migrate)", + teardown: function* () { + gBrowser.removeCurrentTab(); + yield signOut(); + }, + run: function* () + { + const pref = "identity.fxaccounts.migrateToDevEdition"; + changedPrefs.add(pref); + Services.prefs.setBoolPref(pref, true); + + let profD = Services.dirsvc.get("ProfD", Ci.nsIFile); + let mockDir = profD.clone(); + mockDir.append("about-accounts-mock-profd"); + mockDir.createUnique(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY); + // but leave it empty, so we don't think a user is logged in. + + let tab = yield promiseNewTabLoadEvent("about:robots"); + let readyPromise = promiseOneMessage(tab, "test:load-with-mocked-profile-path-response"); + + let mm = tab.linkedBrowser.messageManager; + mm.sendAsyncMessage("test:load-with-mocked-profile-path", { + url: "about:accounts", + profilePath: mockDir.path, + }); + + let response = yield readyPromise; + // We are expecting the iframe to be on the "signup" URL + let expected = yield fxAccounts.promiseAccountsSignUpURI(); + is(response.data.url, expected); + + // and expect no signed in user. + let userData = yield fxAccounts.getSignedInUser(); + is(userData, null); + // The migration pref should have still been switched off. + is(Services.prefs.getBoolPref(pref), false, pref + " got the expected value"); + yield OS.File.removeEmptyDir(mockDir.path); + }, +}, +{ + desc: "Test observers about:accounts", + teardown: function() { + gBrowser.removeCurrentTab(); + }, + run: function* () { + setPref("identity.fxaccounts.remote.signup.uri", "https://example.com/"); + yield setSignedInUser(); + let tab = yield promiseNewTabLoadEvent("about:accounts"); + // sign the user out - the tab should have action=signin + yield signOut(); + // wait for the new load. + yield promiseOneMessage(tab, "test:document:load"); + is(tab.linkedBrowser.contentDocument.location.href, "about:accounts?action=signin"); + } +}, +{ + desc: "Test entrypoint query string, no action, no user logged in", + teardown: () => gBrowser.removeCurrentTab(), + run: function* () { + // When this loads with no user logged-in, we expect the "normal" URL + setPref("identity.fxaccounts.remote.signup.uri", "https://example.com/"); + let [, url] = yield promiseNewTabWithIframeLoadEvent("about:accounts?entrypoint=abouthome"); + is(url, "https://example.com/?entrypoint=abouthome", "entrypoint=abouthome got the expected URL"); + }, +}, +{ + desc: "Test entrypoint query string for signin", + teardown: () => gBrowser.removeCurrentTab(), + run: function* () { + // When this loads with no user logged-in, we expect the "normal" URL + const expected_url = "https://example.com/?is_sign_in"; + setPref("identity.fxaccounts.remote.signin.uri", expected_url); + let [, url] = yield promiseNewTabWithIframeLoadEvent("about:accounts?action=signin&entrypoint=abouthome"); + is(url, expected_url + "&entrypoint=abouthome", "entrypoint=abouthome got the expected URL"); + }, +}, +{ + desc: "Test entrypoint query string for signup", + teardown: () => gBrowser.removeCurrentTab(), + run: function* () { + // When this loads with no user logged-in, we expect the "normal" URL + const sign_up_url = "https://example.com/?is_sign_up"; + setPref("identity.fxaccounts.remote.signup.uri", sign_up_url); + let [, url] = yield promiseNewTabWithIframeLoadEvent("about:accounts?entrypoint=abouthome&action=signup"); + is(url, sign_up_url + "&entrypoint=abouthome", "entrypoint=abouthome got the expected URL"); + }, +}, +{ + desc: "about:accounts URL params should be copied to remote URL params " + + "when remote URL has no URL params, except for 'action'", + teardown() { + gBrowser.removeCurrentTab(); + }, + run: function* () { + let signupURL = "https://example.com/"; + setPref("identity.fxaccounts.remote.signup.uri", signupURL); + let queryStr = "email=foo%40example.com&foo=bar&baz=quux"; + let [, url] = + yield promiseNewTabWithIframeLoadEvent("about:accounts?" + queryStr + + "&action=action"); + is(url, signupURL + "?" + queryStr, "URL params are copied to signup URL"); + }, +}, +{ + desc: "about:accounts URL params should be copied to remote URL params " + + "when remote URL already has some URL params, except for 'action'", + teardown() { + gBrowser.removeCurrentTab(); + }, + run: function* () { + let signupURL = "https://example.com/?param"; + setPref("identity.fxaccounts.remote.signup.uri", signupURL); + let queryStr = "email=foo%40example.com&foo=bar&baz=quux"; + let [, url] = + yield promiseNewTabWithIframeLoadEvent("about:accounts?" + queryStr + + "&action=action"); + is(url, signupURL + "&" + queryStr, "URL params are copied to signup URL"); + }, +}, +]; // gTests + +function test() +{ + waitForExplicitFinish(); + + Task.spawn(function* () { + for (let testCase of gTests) { + info(testCase.desc); + try { + yield testCase.run(); + } finally { + yield testCase.teardown(); + } + } + + finish(); + }); +} + +function promiseOneMessage(tab, messageName) { + let mm = tab.linkedBrowser.messageManager; + let deferred = Promise.defer(); + mm.addMessageListener(messageName, function onmessage(message) { + mm.removeMessageListener(messageName, onmessage); + deferred.resolve(message); + }); + return deferred.promise; +} + +function promiseNewTabLoadEvent(aUrl) +{ + let tab = gBrowser.selectedTab = gBrowser.addTab(aUrl); + let browser = tab.linkedBrowser; + let mm = browser.messageManager; + + // give it an e10s-friendly content script to help with our tests. + mm.loadFrameScript(CHROME_BASE + "content_aboutAccounts.js", true); + // and wait for it to tell us about the load. + return promiseOneMessage(tab, "test:document:load").then( + () => tab + ); +} + +// Returns a promise which is resolved with the iframe's URL after a new +// tab is created and the iframe in that tab loads. +function promiseNewTabWithIframeLoadEvent(aUrl) { + let deferred = Promise.defer(); + let tab = gBrowser.selectedTab = gBrowser.addTab(aUrl); + let browser = tab.linkedBrowser; + let mm = browser.messageManager; + + // give it an e10s-friendly content script to help with our tests. + mm.loadFrameScript(CHROME_BASE + "content_aboutAccounts.js", true); + // and wait for it to tell us about the iframe load. + mm.addMessageListener("test:iframe:load", function onFrameLoad(message) { + mm.removeMessageListener("test:iframe:load", onFrameLoad); + deferred.resolve([tab, message.data.url]); + }); + return deferred.promise; +} + +function checkVisibilities(tab, data) { + let ids = Object.keys(data); + let mm = tab.linkedBrowser.messageManager; + let deferred = Promise.defer(); + mm.addMessageListener("test:check-visibilities-response", function onResponse(message) { + mm.removeMessageListener("test:check-visibilities-response", onResponse); + for (let id of ids) { + is(message.data[id], data[id], "Element '" + id + "' has correct visibility"); + } + deferred.resolve(); + }); + mm.sendAsyncMessage("test:check-visibilities", {ids: ids}); + return deferred.promise; +} + +// watch out - these will fire observers which if you aren't careful, may +// interfere with the tests. +function setSignedInUser(data) { + if (!data) { + data = { + email: "foo@example.com", + uid: "1234@lcip.org", + assertion: "foobar", + sessionToken: "dead", + kA: "beef", + kB: "cafe", + verified: true + } + } + return fxAccounts.setSignedInUser(data); +} + +function signOut() { + // we always want a "localOnly" signout here... + return fxAccounts.signOut(true); +} |