summaryrefslogtreecommitdiffstats
path: root/browser/components/migration/tests/marionette/test_refresh_firefox.py
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/migration/tests/marionette/test_refresh_firefox.py')
-rw-r--r--browser/components/migration/tests/marionette/test_refresh_firefox.py416
1 files changed, 416 insertions, 0 deletions
diff --git a/browser/components/migration/tests/marionette/test_refresh_firefox.py b/browser/components/migration/tests/marionette/test_refresh_firefox.py
new file mode 100644
index 000000000..b348a3dcd
--- /dev/null
+++ b/browser/components/migration/tests/marionette/test_refresh_firefox.py
@@ -0,0 +1,416 @@
+import os
+import shutil
+
+from marionette_harness import MarionetteTestCase
+
+
+class TestFirefoxRefresh(MarionetteTestCase):
+ _username = "marionette-test-login"
+ _password = "marionette-test-password"
+ _bookmarkURL = "about:mozilla"
+ _bookmarkText = "Some bookmark from Marionette"
+
+ _cookieHost = "firefox-refresh.marionette-test.mozilla.org"
+ _cookiePath = "some/cookie/path"
+ _cookieName = "somecookie"
+ _cookieValue = "some cookie value"
+
+ _historyURL = "http://firefox-refresh.marionette-test.mozilla.org/"
+ _historyTitle = "Test visit for Firefox Reset"
+
+ _formHistoryFieldName = "some-very-unique-marionette-only-firefox-reset-field"
+ _formHistoryValue = "special-pumpkin-value"
+
+ _expectedURLs = ["about:robots", "about:mozilla"]
+
+ def savePassword(self):
+ self.runCode("""
+ let myLogin = new global.LoginInfo(
+ "test.marionette.mozilla.com",
+ "http://test.marionette.mozilla.com/some/form/",
+ null,
+ arguments[0],
+ arguments[1],
+ "username",
+ "password"
+ );
+ Services.logins.addLogin(myLogin)
+ """, script_args=[self._username, self._password])
+
+ def createBookmark(self):
+ self.marionette.execute_script("""
+ let url = arguments[0];
+ let title = arguments[1];
+ PlacesUtils.bookmarks.insertBookmark(PlacesUtils.bookmarks.bookmarksMenuFolder,
+ makeURI(url), 0, title);
+ """, script_args=[self._bookmarkURL, self._bookmarkText])
+
+ def createHistory(self):
+ error = self.runAsyncCode("""
+ // Copied from PlacesTestUtils, which isn't available in Marionette tests.
+ let didReturn;
+ PlacesUtils.asyncHistory.updatePlaces(
+ [{title: arguments[1], uri: makeURI(arguments[0]), visits: [{
+ transitionType: Ci.nsINavHistoryService.TRANSITION_LINK,
+ visitDate: (Date.now() - 5000) * 1000,
+ referrerURI: makeURI("about:mozilla"),
+ }]
+ }],
+ {
+ handleError(resultCode, place) {
+ didReturn = true;
+ marionetteScriptFinished("Unexpected error in adding visit: " + resultCode);
+ },
+ handleResult() {},
+ handleCompletion() {
+ if (!didReturn) {
+ marionetteScriptFinished(false);
+ }
+ },
+ }
+ );
+ """, script_args=[self._historyURL, self._historyTitle])
+ if error:
+ print error
+
+ def createFormHistory(self):
+ error = self.runAsyncCode("""
+ let updateDefinition = {
+ op: "add",
+ fieldname: arguments[0],
+ value: arguments[1],
+ firstUsed: (Date.now() - 5000) * 1000,
+ };
+ let finished = false;
+ global.FormHistory.update(updateDefinition, {
+ handleError(error) {
+ finished = true;
+ marionetteScriptFinished(error);
+ },
+ handleCompletion() {
+ if (!finished) {
+ marionetteScriptFinished(false);
+ }
+ }
+ });
+ """, script_args=[self._formHistoryFieldName, self._formHistoryValue])
+ if error:
+ print error
+
+ def createCookie(self):
+ self.runCode("""
+ // Expire in 15 minutes:
+ let expireTime = Math.floor(Date.now() / 1000) + 15 * 60;
+ Services.cookies.add(arguments[0], arguments[1], arguments[2], arguments[3],
+ true, false, false, expireTime);
+ """, script_args=[self._cookieHost, self._cookiePath, self._cookieName, self._cookieValue])
+
+ def createSession(self):
+ self.runAsyncCode("""
+ const COMPLETE_STATE = Ci.nsIWebProgressListener.STATE_STOP +
+ Ci.nsIWebProgressListener.STATE_IS_NETWORK;
+ let {TabStateFlusher} = Cu.import("resource:///modules/sessionstore/TabStateFlusher.jsm", {});
+ let expectedURLs = Array.from(arguments[0])
+ gBrowser.addTabsProgressListener({
+ onStateChange(browser, webprogress, request, flags, status) {
+ try {
+ request && request.QueryInterface(Ci.nsIChannel);
+ } catch (ex) {}
+ let uriLoaded = request.originalURI && request.originalURI.spec;
+ if ((flags & COMPLETE_STATE == COMPLETE_STATE) && uriLoaded &&
+ expectedURLs.includes(uriLoaded)) {
+ TabStateFlusher.flush(browser).then(function() {
+ expectedURLs.splice(expectedURLs.indexOf(uriLoaded), 1);
+ if (!expectedURLs.length) {
+ gBrowser.removeTabsProgressListener(this);
+ marionetteScriptFinished();
+ }
+ });
+ }
+ }
+ });
+ for (let url of expectedURLs) {
+ gBrowser.addTab(url);
+ }
+ """, script_args=[self._expectedURLs])
+
+ def checkPassword(self):
+ loginInfo = self.marionette.execute_script("""
+ let ary = Services.logins.findLogins({},
+ "test.marionette.mozilla.com",
+ "http://test.marionette.mozilla.com/some/form/",
+ null, {});
+ return ary.length ? ary : {username: "null", password: "null"};
+ """)
+ self.assertEqual(len(loginInfo), 1)
+ self.assertEqual(loginInfo[0]['username'], self._username)
+ self.assertEqual(loginInfo[0]['password'], self._password)
+
+ loginCount = self.marionette.execute_script("""
+ return Services.logins.getAllLogins().length;
+ """)
+ self.assertEqual(loginCount, 1, "No other logins are present")
+
+ def checkBookmark(self):
+ titleInBookmarks = self.marionette.execute_script("""
+ let url = arguments[0];
+ let bookmarkIds = PlacesUtils.bookmarks.getBookmarkIdsForURI(makeURI(url), {}, {});
+ return bookmarkIds.length == 1 ? PlacesUtils.bookmarks.getItemTitle(bookmarkIds[0]) : "";
+ """, script_args=[self._bookmarkURL])
+ self.assertEqual(titleInBookmarks, self._bookmarkText)
+
+ def checkHistory(self):
+ historyResults = self.runAsyncCode("""
+ let placeInfos = [];
+ PlacesUtils.asyncHistory.getPlacesInfo(makeURI(arguments[0]), {
+ handleError(resultCode, place) {
+ placeInfos = null;
+ marionetteScriptFinished("Unexpected error in fetching visit: " + resultCode);
+ },
+ handleResult(placeInfo) {
+ placeInfos.push(placeInfo);
+ },
+ handleCompletion() {
+ if (placeInfos) {
+ if (!placeInfos.length) {
+ marionetteScriptFinished("No visits found");
+ } else {
+ marionetteScriptFinished(placeInfos);
+ }
+ }
+ },
+ });
+ """, script_args=[self._historyURL])
+ if type(historyResults) == str:
+ self.fail(historyResults)
+ return
+
+ historyCount = len(historyResults)
+ self.assertEqual(historyCount, 1, "Should have exactly 1 entry for URI, got %d" % historyCount)
+ if historyCount == 1:
+ self.assertEqual(historyResults[0]['title'], self._historyTitle)
+
+ def checkFormHistory(self):
+ formFieldResults = self.runAsyncCode("""
+ let results = [];
+ global.FormHistory.search(["value"], {fieldname: arguments[0]}, {
+ handleError(error) {
+ results = error;
+ },
+ handleResult(result) {
+ results.push(result);
+ },
+ handleCompletion() {
+ marionetteScriptFinished(results);
+ },
+ });
+ """, script_args=[self._formHistoryFieldName])
+ if type(formFieldResults) == str:
+ self.fail(formFieldResults)
+ return
+
+ formFieldResultCount = len(formFieldResults)
+ self.assertEqual(formFieldResultCount, 1, "Should have exactly 1 entry for this field, got %d" % formFieldResultCount)
+ if formFieldResultCount == 1:
+ self.assertEqual(formFieldResults[0]['value'], self._formHistoryValue)
+
+ formHistoryCount = self.runAsyncCode("""
+ let count;
+ let callbacks = {
+ handleResult: rv => count = rv,
+ handleCompletion() {
+ marionetteScriptFinished(count);
+ },
+ };
+ global.FormHistory.count({}, callbacks);
+ """)
+ self.assertEqual(formHistoryCount, 1, "There should be only 1 entry in the form history")
+
+ def checkCookie(self):
+ cookieInfo = self.runCode("""
+ try {
+ let cookieEnum = Services.cookies.getCookiesFromHost(arguments[0]);
+ let cookie = null;
+ while (cookieEnum.hasMoreElements()) {
+ let hostCookie = cookieEnum.getNext();
+ hostCookie.QueryInterface(Ci.nsICookie2);
+ // getCookiesFromHost returns any cookie from the BASE host.
+ if (hostCookie.rawHost != arguments[0])
+ continue;
+ if (cookie != null) {
+ return "more than 1 cookie! That shouldn't happen!";
+ }
+ cookie = hostCookie;
+ }
+ return {path: cookie.path, name: cookie.name, value: cookie.value};
+ } catch (ex) {
+ return "got exception trying to fetch cookie: " + ex;
+ }
+ """, script_args=[self._cookieHost])
+ if not isinstance(cookieInfo, dict):
+ self.fail(cookieInfo)
+ return
+ self.assertEqual(cookieInfo['path'], self._cookiePath)
+ self.assertEqual(cookieInfo['value'], self._cookieValue)
+ self.assertEqual(cookieInfo['name'], self._cookieName)
+
+ def checkSession(self):
+ tabURIs = self.runCode("""
+ return [... gBrowser.browsers].map(b => b.currentURI && b.currentURI.spec)
+ """)
+ self.assertSequenceEqual(tabURIs, ["about:welcomeback"])
+
+ tabURIs = self.runAsyncCode("""
+ let mm = gBrowser.selectedBrowser.messageManager;
+ let fs = function() {
+ content.document.getElementById("errorTryAgain").click();
+ };
+ let {TabStateFlusher} = Cu.import("resource:///modules/sessionstore/TabStateFlusher.jsm", {});
+ window.addEventListener("SSWindowStateReady", function testSSPostReset() {
+ window.removeEventListener("SSWindowStateReady", testSSPostReset, false);
+ Promise.all(gBrowser.browsers.map(b => TabStateFlusher.flush(b))).then(function() {
+ marionetteScriptFinished([... gBrowser.browsers].map(b => b.currentURI && b.currentURI.spec));
+ });
+ }, false);
+ mm.loadFrameScript("data:application/javascript,(" + fs.toString() + ")()", true);
+ """)
+ self.assertSequenceEqual(tabURIs, ["about:blank"] + self._expectedURLs)
+ pass
+
+ def checkProfile(self, hasMigrated=False):
+ self.checkPassword()
+ self.checkBookmark()
+ self.checkHistory()
+ self.checkFormHistory()
+ self.checkCookie()
+ if hasMigrated:
+ self.checkSession()
+
+ def createProfileData(self):
+ self.savePassword()
+ self.createBookmark()
+ self.createHistory()
+ self.createFormHistory()
+ self.createCookie()
+ self.createSession()
+
+ def setUpScriptData(self):
+ self.marionette.set_context(self.marionette.CONTEXT_CHROME)
+ self.marionette.execute_script("""
+ global.LoginInfo = Components.Constructor("@mozilla.org/login-manager/loginInfo;1", "nsILoginInfo", "init");
+ global.profSvc = Cc["@mozilla.org/toolkit/profile-service;1"].getService(Ci.nsIToolkitProfileService);
+ global.Preferences = Cu.import("resource://gre/modules/Preferences.jsm", {}).Preferences;
+ global.FormHistory = Cu.import("resource://gre/modules/FormHistory.jsm", {}).FormHistory;
+ """, new_sandbox=False, sandbox='system')
+
+ def runCode(self, script, *args, **kwargs):
+ return self.marionette.execute_script(script, new_sandbox=False, sandbox='system', *args, **kwargs)
+
+ def runAsyncCode(self, script, *args, **kwargs):
+ return self.marionette.execute_async_script(script, new_sandbox=False, sandbox='system', *args, **kwargs)
+
+ def setUp(self):
+ MarionetteTestCase.setUp(self)
+ self.setUpScriptData()
+
+ self.reset_profile_path = None
+ self.desktop_backup_path = None
+
+ self.createProfileData()
+
+ def tearDown(self):
+ # Force yet another restart with a clean profile to disconnect from the
+ # profile and environment changes we've made, to leave a more or less
+ # blank slate for the next person.
+ self.marionette.restart(clean=True, in_app=False)
+ self.setUpScriptData()
+
+ # Super
+ MarionetteTestCase.tearDown(self)
+
+ # Some helpers to deal with removing a load of files
+ import errno, stat
+ def handleRemoveReadonly(func, path, exc):
+ excvalue = exc[1]
+ if func in (os.rmdir, os.remove) and excvalue.errno == errno.EACCES:
+ os.chmod(path, stat.S_IRWXU| stat.S_IRWXG| stat.S_IRWXO) # 0777
+ func(path)
+ else:
+ raise
+
+ if self.desktop_backup_path:
+ shutil.rmtree(self.desktop_backup_path, ignore_errors=False, onerror=handleRemoveReadonly)
+
+ if self.reset_profile_path:
+ # Remove ourselves from profiles.ini
+ profileLeafName = os.path.basename(os.path.normpath(self.reset_profile_path))
+ self.runCode("""
+ let [salt, name] = arguments[0].split(".");
+ let profile = global.profSvc.getProfileByName(name);
+ profile.remove(false)
+ global.profSvc.flush();
+ """, script_args=[profileLeafName])
+ # And delete all the files.
+ shutil.rmtree(self.reset_profile_path, ignore_errors=False, onerror=handleRemoveReadonly)
+
+ def doReset(self):
+ self.runCode("""
+ // Ensure the current (temporary) profile is in profiles.ini:
+ let profD = Services.dirsvc.get("ProfD", Ci.nsIFile);
+ let profileName = "marionette-test-profile-" + Date.now();
+ let myProfile = global.profSvc.createProfile(profD, profileName);
+ global.profSvc.flush()
+
+ // Now add the reset parameters:
+ let env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
+ let allMarionettePrefs = Services.prefs.getChildList("marionette.");
+ let prefObj = {};
+ for (let pref of allMarionettePrefs) {
+ let prefSuffix = pref.substr("marionette.".length);
+ let prefVal = global.Preferences.get(pref);
+ prefObj[prefSuffix] = prefVal;
+ }
+ let marionetteInfo = JSON.stringify(prefObj);
+ env.set("MOZ_MARIONETTE_PREF_STATE_ACROSS_RESTARTS", marionetteInfo);
+ env.set("MOZ_RESET_PROFILE_RESTART", "1");
+ env.set("XRE_PROFILE_PATH", arguments[0]);
+ env.set("XRE_PROFILE_NAME", profileName);
+ """, script_args=[self.marionette.instance.profile.profile])
+
+ profileLeafName = os.path.basename(os.path.normpath(self.marionette.instance.profile.profile))
+
+ # Now restart the browser to get it reset:
+ self.marionette.restart(clean=False, in_app=True)
+ self.setUpScriptData()
+
+ # Determine the new profile path (we'll need to remove it when we're done)
+ self.reset_profile_path = self.runCode("""
+ let profD = Services.dirsvc.get("ProfD", Ci.nsIFile);
+ return profD.path;
+ """)
+
+ # Determine the backup path
+ self.desktop_backup_path = self.runCode("""
+ let container;
+ try {
+ container = Services.dirsvc.get("Desk", Ci.nsIFile);
+ } catch (ex) {
+ container = Services.dirsvc.get("Home", Ci.nsIFile);
+ }
+ let bundle = Services.strings.createBundle("chrome://mozapps/locale/profile/profileSelection.properties");
+ let dirName = bundle.formatStringFromName("resetBackupDirectory", [Services.appinfo.name], 1);
+ container.append(dirName);
+ container.append(arguments[0]);
+ return container.path;
+ """, script_args = [profileLeafName])
+
+ self.assertTrue(os.path.isdir(self.reset_profile_path), "Reset profile path should be present")
+ self.assertTrue(os.path.isdir(self.desktop_backup_path), "Backup profile path should be present")
+
+ def testReset(self):
+ self.checkProfile()
+
+ self.doReset()
+
+ # Now check that we're doing OK...
+ self.checkProfile(hasMigrated=True)