michael@0: /* Any copyright is dedicated to the Public Domain. michael@0: * http://creativecommons.org/publicdomain/zero/1.0/ michael@0: */ michael@0: michael@0: /** michael@0: * Test Definition michael@0: * michael@0: * Most tests can use an array named TESTS that will perform most if not all of michael@0: * the necessary checks. Each element in the array must be an object with the michael@0: * following possible properties. Additional properties besides the ones listed michael@0: * below can be added as needed. michael@0: * michael@0: * overrideCallback (optional) michael@0: * The function to call for the next test. This is typically called when the michael@0: * wizard page changes but can also be called for other events by the previous michael@0: * test. If this property isn't defined then the defailtCallback function will michael@0: * be called. If this property is defined then all other properties are michael@0: * optional. michael@0: * michael@0: * pageid (required unless overrideCallback is specified) michael@0: * The expected pageid for the wizard. This property is required unless the michael@0: * overrideCallback property is defined. michael@0: * michael@0: * extraStartFunction (optional) michael@0: * The function to call at the beginning of the defaultCallback function. If michael@0: * the function returns true the defaultCallback function will return early michael@0: * which allows waiting for a specific condition to be evaluated in the michael@0: * function specified in the extraStartFunction property before continuing michael@0: * with the test. michael@0: * michael@0: * extraCheckFunction (optional) michael@0: * The function to call to perform extra checks in the defaultCallback michael@0: * function. michael@0: * michael@0: * extraDelayedCheckFunction (optional) michael@0: * The function to call to perform extra checks in the delayedDefaultCallback michael@0: * function. michael@0: * michael@0: * buttonStates (optional) michael@0: * A javascript object representing the expected hidden and disabled attribute michael@0: * values for the buttons of the current wizard page. The values are checked michael@0: * in the delayedDefaultCallback function. For information about the structure michael@0: * of this object refer to the getExpectedButtonStates and checkButtonStates michael@0: * functions. michael@0: * michael@0: * buttonClick (optional) michael@0: * The current wizard page button to click at the end of the michael@0: * delayedDefaultCallback function. If the buttonClick property is defined michael@0: * then the extraDelayedFinishFunction property can't be specified due to race michael@0: * conditions in some of the tests and if both of them are specified the test michael@0: * will intentionally throw. michael@0: * michael@0: * extraDelayedFinishFunction (optional) michael@0: * The function to call at the end of the delayedDefaultCallback function. michael@0: * If the extraDelayedFinishFunction property is defined then the buttonClick michael@0: * property can't be specified due to race conditions in some of the tests and michael@0: * if both of them are specified the test will intentionally throw. michael@0: * michael@0: * ranTest (should not be specified) michael@0: * When delayedDefaultCallback is called a property named ranTest is added to michael@0: * the current test so it is possible to verify that each test in the TESTS michael@0: * array has ran. michael@0: * michael@0: * prefHasUserValue (optional) michael@0: * For comparing the expected value defined by this property with the return michael@0: * value of prefHasUserValue using gPrefToCheck for the preference name in the michael@0: * checkPrefHasUserValue function. michael@0: * michael@0: * expectedRadioGroupSelectedIndex (optional) michael@0: * For comparing the expected selectedIndex attribute value of the wizard's michael@0: * license page radiogroup selectedIndex attribute in the michael@0: * checkRadioGroupSelectedIndex function. michael@0: * michael@0: * expectedRemoteContentState (optional) michael@0: * For comparing the expected remotecontent state attribute value of the michael@0: * wizard's billboard and license pages in the checkRemoteContentState and michael@0: * waitForRemoteContentLoaded functions. michael@0: * michael@0: * michael@0: * Test Add-ons michael@0: * michael@0: * All tests include the test add-ons specified in the TEST_ADDONS array and michael@0: * the only thing that can be configured is whether the noupdate test add-on is michael@0: * disabled (see below). The add-on names are in the format of typename_X where michael@0: * X is a number to make the add-on ID unique and typename is one of the values michael@0: * specified below: michael@0: * michael@0: * appdisabled michael@0: * disabled by the application due to being incompatible with the current michael@0: * toolkit version. michael@0: * michael@0: * compatible michael@0: * compatible with the current toolkit version and the update's toolkit michael@0: * version. michael@0: * michael@0: * noupdate michael@0: * the add-on is compatible with the current toolkit version and does not have michael@0: * an update to make it compatible with the update's toolkit version. Tests michael@0: * that need to have all add-ons compatible for the application update can michael@0: * disable this add-on by setting the gDisableNoUpdateAddon variable to true. michael@0: * michael@0: * updatecompatibility michael@0: * the add-on is compatible with the current toolkit version and has a michael@0: * compatibility update to make it compatible with the update's toolkit michael@0: * version. michael@0: * michael@0: * updateversion michael@0: * the add-on is compatible with the current toolkit version and has a version michael@0: * update to make it compatible with the update's toolkit version. michael@0: * michael@0: * userdisabled michael@0: * disabled by the user and compatible with the current toolkit version but michael@0: * not the update's toolkit version. This add-on will be disabled after its michael@0: * install completes. michael@0: */ michael@0: michael@0: Components.utils.import("resource://gre/modules/AddonManager.jsm"); michael@0: michael@0: // The tests have to use the pageid instead of the pageIndex due to the michael@0: // app update wizard's access method being random. michael@0: const PAGEID_DUMMY = "dummy"; // Done michael@0: const PAGEID_CHECKING = "checking"; // Done michael@0: const PAGEID_PLUGIN_UPDATES = "pluginupdatesfound"; michael@0: const PAGEID_NO_UPDATES_FOUND = "noupdatesfound"; // Done michael@0: const PAGEID_MANUAL_UPDATE = "manualUpdate"; // Tested on license load failure michael@0: const PAGEID_UNSUPPORTED = "unsupported"; // Done michael@0: const PAGEID_INCOMPAT_CHECK = "incompatibleCheck"; // Done michael@0: const PAGEID_FOUND_BASIC = "updatesfoundbasic"; // Done michael@0: const PAGEID_FOUND_BILLBOARD = "updatesfoundbillboard"; // Done michael@0: const PAGEID_LICENSE = "license"; // Done michael@0: const PAGEID_INCOMPAT_LIST = "incompatibleList"; // Done michael@0: const PAGEID_DOWNLOADING = "downloading"; // Done michael@0: const PAGEID_ERRORS = "errors"; // Done michael@0: const PAGEID_ERROR_EXTRA = "errorextra"; // Done michael@0: const PAGEID_ERROR_PATCHING = "errorpatching"; // Done michael@0: const PAGEID_FINISHED = "finished"; // Done michael@0: const PAGEID_FINISHED_BKGRD = "finishedBackground"; // Done michael@0: const PAGEID_INSTALLED = "installed"; // Done michael@0: michael@0: const UPDATE_WINDOW_NAME = "Update:Wizard"; michael@0: michael@0: const URL_HOST = "http://example.com"; michael@0: const URL_PATH_UPDATE_XML = "/chrome/toolkit/mozapps/update/tests/chrome/update.sjs"; michael@0: const REL_PATH_DATA = "chrome/toolkit/mozapps/update/tests/data"; michael@0: michael@0: const URL_HTTP_UPDATE_XML = URL_HOST + URL_PATH_UPDATE_XML; michael@0: const URL_HTTPS_UPDATE_XML = "https://example.com" + URL_PATH_UPDATE_XML; michael@0: michael@0: const URI_UPDATE_PROMPT_DIALOG = "chrome://mozapps/content/update/updates.xul"; michael@0: michael@0: const ADDON_ID_SUFFIX = "@appupdatetest.mozilla.org"; michael@0: const ADDON_PREP_DIR = "appupdateprep"; michael@0: // Preference for storing add-ons that are disabled by the tests to prevent them michael@0: // from interefering with the tests. michael@0: const PREF_DISABLEDADDONS = "app.update.test.disabledAddons"; michael@0: const PREF_EM_HOTFIX_ID = "extensions.hotfix.id"; michael@0: const TEST_ADDONS = [ "appdisabled_1", "appdisabled_2", michael@0: "compatible_1", "compatible_2", michael@0: "noupdate_1", "noupdate_2", michael@0: "updatecompatibility_1", "updatecompatibility_2", michael@0: "updateversion_1", "updateversion_2", michael@0: "userdisabled_1", "userdisabled_2", "hotfix" ]; michael@0: michael@0: var gURLData = URL_HOST + "/" + REL_PATH_DATA + "/"; michael@0: michael@0: var gTestTimeout = 240000; // 4 minutes michael@0: var gTimeoutTimer; michael@0: michael@0: // The number of SimpleTest.executeSoon calls to perform when waiting on an michael@0: // update window to close before giving up. michael@0: const CLOSE_WINDOW_TIMEOUT_MAXCOUNT = 10; michael@0: // Counter for the SimpleTest.executeSoon when waiting on an update window to michael@0: // close before giving up. michael@0: var gCloseWindowTimeoutCounter = 0; michael@0: michael@0: // The following vars are for restoring previous preference values (if present) michael@0: // when the test finishes. michael@0: var gAppUpdateEnabled; // app.update.enabled michael@0: var gAppUpdateMetroEnabled; // app.update.metro.enabled michael@0: var gAppUpdateServiceEnabled; // app.update.service.enabled michael@0: var gAppUpdateStagingEnabled; // app.update.staging.enabled michael@0: var gAppUpdateURLDefault; // app.update.url (default prefbranch) michael@0: var gAppUpdateURL; // app.update.url.override michael@0: var gExtUpdateURL; // extensions.update.url michael@0: michael@0: var gTestCounter = -1; michael@0: var gWin; michael@0: var gDocElem; michael@0: var gPrefToCheck; michael@0: var gDisableNoUpdateAddon = false; michael@0: michael@0: // Set to true to log additional information for debugging. To log additional michael@0: // information for an individual test set DEBUG_AUS_TEST to true in the test's michael@0: // onload function. michael@0: var DEBUG_AUS_TEST = true; michael@0: michael@0: #include ../shared.js michael@0: michael@0: /** michael@0: * The current test in TESTS array. michael@0: */ michael@0: this.__defineGetter__("gTest", function() { michael@0: return TESTS[gTestCounter]; michael@0: }); michael@0: michael@0: /** michael@0: * The current test's callback. This will either return the callback defined in michael@0: * the test's overrideCallback property or defaultCallback if the michael@0: * overrideCallback property is undefined. michael@0: */ michael@0: this.__defineGetter__("gCallback", function() { michael@0: return gTest.overrideCallback ? gTest.overrideCallback michael@0: : defaultCallback; michael@0: }); michael@0: michael@0: /** michael@0: * The remotecontent element for the current page if one exists or null if a michael@0: * remotecontent element doesn't exist. michael@0: */ michael@0: this.__defineGetter__("gRemoteContent", function() { michael@0: switch (gTest.pageid) { michael@0: case PAGEID_FOUND_BILLBOARD: michael@0: return gWin.document.getElementById("updateMoreInfoContent"); michael@0: case PAGEID_LICENSE: michael@0: return gWin.document.getElementById("licenseContent"); michael@0: } michael@0: return null; michael@0: }); michael@0: michael@0: /** michael@0: * The state for the remotecontent element if one exists or null if a michael@0: * remotecontent element doesn't exist. michael@0: */ michael@0: this.__defineGetter__("gRemoteContentState", function() { michael@0: if (gRemoteContent) { michael@0: return gRemoteContent.getAttribute("state"); michael@0: } michael@0: return null; michael@0: }); michael@0: michael@0: /** michael@0: * The radiogroup for the license page. michael@0: */ michael@0: this.__defineGetter__("gAcceptDeclineLicense", function() { michael@0: return gWin.document.getElementById("acceptDeclineLicense"); michael@0: }); michael@0: michael@0: /** michael@0: * The listbox for the incompatibleList page. michael@0: */ michael@0: this.__defineGetter__("gIncompatibleListbox", function() { michael@0: return gWin.document.getElementById("incompatibleListbox"); michael@0: }); michael@0: michael@0: /** michael@0: * Default test run function that can be used by most tests. This function uses michael@0: * protective measures to prevent the test from failing provided by michael@0: * |runTestDefaultWaitForWindowClosed| helper functions to prevent failure due michael@0: * to a previous test failure. michael@0: */ michael@0: function runTestDefault() { michael@0: debugDump("entering"); michael@0: michael@0: if (!("@mozilla.org/zipwriter;1" in AUS_Cc)) { michael@0: ok(false, "nsIZipWriter is required to run these tests"); michael@0: return; michael@0: } michael@0: michael@0: SimpleTest.waitForExplicitFinish(); michael@0: michael@0: runTestDefaultWaitForWindowClosed(); michael@0: } michael@0: michael@0: /** michael@0: * If an update window is found SimpleTest.executeSoon can callback before the michael@0: * update window is fully closed especially with debug builds. If an update michael@0: * window is found this function will call itself using SimpleTest.executeSoon michael@0: * up to the amount declared in CLOSE_WINDOW_TIMEOUT_MAXCOUNT until the update michael@0: * window has closed before continuing the test. michael@0: */ michael@0: function runTestDefaultWaitForWindowClosed() { michael@0: gCloseWindowTimeoutCounter++; michael@0: if (gCloseWindowTimeoutCounter > CLOSE_WINDOW_TIMEOUT_MAXCOUNT) { michael@0: try { michael@0: finishTest(); michael@0: } michael@0: catch (e) { michael@0: finishTestDefault(); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: // The update window should not be open at this time. If it is the call to michael@0: // |closeUpdateWindow| will close it and cause the test to fail. michael@0: if (closeUpdateWindow()) { michael@0: SimpleTest.executeSoon(runTestDefaultWaitForWindowClosed); michael@0: } michael@0: else { michael@0: Services.ww.registerNotification(gWindowObserver); michael@0: michael@0: gCloseWindowTimeoutCounter = 0; michael@0: michael@0: setupFiles(); michael@0: setupPrefs(); michael@0: removeUpdateDirsAndFiles(); michael@0: reloadUpdateManagerData(); michael@0: setupAddons(runTest); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Default test finish function that can be used by most tests. This function michael@0: * uses protective measures to prevent the next test from failing provided by michael@0: * |finishTestDefaultWaitForWindowClosed| helper functions to prevent failure michael@0: * due to an update window being left open. michael@0: */ michael@0: function finishTestDefault() { michael@0: debugDump("entering"); michael@0: if (gTimeoutTimer) { michael@0: gTimeoutTimer.cancel(); michael@0: gTimeoutTimer = null; michael@0: } michael@0: michael@0: if (gChannel) { michael@0: debugDump("channel = " + gChannel); michael@0: gChannel = null; michael@0: gPrefRoot.removeObserver(PREF_APP_UPDATE_CHANNEL, observer); michael@0: } michael@0: michael@0: verifyTestsRan(); michael@0: michael@0: resetPrefs(); michael@0: resetFiles(); michael@0: removeUpdateDirsAndFiles(); michael@0: reloadUpdateManagerData(); michael@0: michael@0: Services.ww.unregisterNotification(gWindowObserver); michael@0: if (gDocElem) { michael@0: gDocElem.removeEventListener("pageshow", onPageShowDefault, false); michael@0: } michael@0: michael@0: finishTestDefaultWaitForWindowClosed(); michael@0: } michael@0: michael@0: /** michael@0: * nsITimerCallback for the timeout timer to cleanly finish a test if the Update michael@0: * Window doesn't close for a test. This allows the next test to run properly if michael@0: * a previous test fails. michael@0: * michael@0: * @param aTimer michael@0: * The nsITimer that fired. michael@0: */ michael@0: function finishTestTimeout(aTimer) { michael@0: ok(false, "Test timed out. Maximum time allowed is " + (gTestTimeout / 1000) + michael@0: " seconds"); michael@0: michael@0: try { michael@0: finishTest(); michael@0: } michael@0: catch (e) { michael@0: finishTestDefault(); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * If an update window is found SimpleTest.executeSoon can callback before the michael@0: * update window is fully closed especially with debug builds. If an update michael@0: * window is found this function will call itself using SimpleTest.executeSoon michael@0: * up to the amount declared in CLOSE_WINDOW_TIMEOUT_MAXCOUNT until the update michael@0: * window has closed before finishing the test. michael@0: */ michael@0: function finishTestDefaultWaitForWindowClosed() { michael@0: gCloseWindowTimeoutCounter++; michael@0: if (gCloseWindowTimeoutCounter > CLOSE_WINDOW_TIMEOUT_MAXCOUNT) { michael@0: SimpleTest.finish(); michael@0: return; michael@0: } michael@0: michael@0: // The update window should not be open at this time. If it is the call to michael@0: // |closeUpdateWindow| will close it and cause the test to fail. michael@0: if (closeUpdateWindow()) { michael@0: SimpleTest.executeSoon(finishTestDefaultWaitForWindowClosed); michael@0: } michael@0: else { michael@0: SimpleTest.finish(); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Default callback for the wizard's documentElement pageshow listener. This michael@0: * will return early for event's where the originalTarget's nodeName is not michael@0: * wizardpage. michael@0: */ michael@0: function onPageShowDefault(aEvent) { michael@0: if (!gTimeoutTimer) { michael@0: debugDump("gTimeoutTimer is null... returning early"); michael@0: return; michael@0: } michael@0: michael@0: // Return early if the event's original target isn't for a wizardpage element. michael@0: // This check is necessary due to the remotecontent element firing pageshow. michael@0: if (aEvent.originalTarget.nodeName != "wizardpage") { michael@0: debugDump("only handles events with an originalTarget nodeName of " + michael@0: "|wizardpage|. aEvent.originalTarget.nodeName = " + michael@0: aEvent.originalTarget.nodeName + "... returning early"); michael@0: return; michael@0: } michael@0: michael@0: gTestCounter++; michael@0: gCallback(aEvent); michael@0: } michael@0: michael@0: /** michael@0: * Default callback that can be used by most tests. michael@0: */ michael@0: function defaultCallback(aEvent) { michael@0: if (!gTimeoutTimer) { michael@0: debugDump("gTimeoutTimer is null... returning early"); michael@0: return; michael@0: } michael@0: michael@0: debugDump("entering - TESTS[" + gTestCounter + "], pageid: " + gTest.pageid + michael@0: ", aEvent.originalTarget.nodeName: " + michael@0: aEvent.originalTarget.nodeName); michael@0: michael@0: if (gTest && gTest.extraStartFunction) { michael@0: debugDump("calling extraStartFunction " + gTest.extraStartFunction.name); michael@0: if (gTest.extraStartFunction(aEvent)) { michael@0: debugDump("extraStartFunction early return"); michael@0: return; michael@0: } michael@0: } michael@0: michael@0: is(gDocElem.currentPage.pageid, gTest.pageid, michael@0: "Checking currentPage.pageid equals " + gTest.pageid + " in pageshow"); michael@0: michael@0: // Perform extra checks if specified by the test michael@0: if (gTest.extraCheckFunction) { michael@0: debugDump("calling extraCheckFunction " + gTest.extraCheckFunction.name); michael@0: gTest.extraCheckFunction(); michael@0: } michael@0: michael@0: // The wizard page buttons' disabled and hidden attributes are set after the michael@0: // pageshow event so use executeSoon to allow them to be set so their disabled michael@0: // and hidden attribute values can be checked. michael@0: SimpleTest.executeSoon(delayedDefaultCallback); michael@0: } michael@0: michael@0: /** michael@0: * Delayed default callback called using executeSoon in defaultCallback which michael@0: * allows the wizard page buttons' disabled and hidden attributes to be set michael@0: * before checking their values. michael@0: */ michael@0: function delayedDefaultCallback() { michael@0: if (!gTimeoutTimer) { michael@0: debugDump("gTimeoutTimer is null... returning early"); michael@0: return; michael@0: } michael@0: michael@0: if (!gTest) { michael@0: debugDump("gTest is null... returning early"); michael@0: return; michael@0: } michael@0: michael@0: debugDump("entering - TESTS[" + gTestCounter + "], pageid: " + gTest.pageid); michael@0: michael@0: // Verify the pageid hasn't changed after executeSoon was called. michael@0: is(gDocElem.currentPage.pageid, gTest.pageid, michael@0: "Checking currentPage.pageid equals " + gTest.pageid + " after " + michael@0: "executeSoon"); michael@0: michael@0: checkButtonStates(); michael@0: michael@0: // Perform delayed extra checks if specified by the test michael@0: if (gTest.extraDelayedCheckFunction) { michael@0: debugDump("calling extraDelayedCheckFunction " + michael@0: gTest.extraDelayedCheckFunction.name); michael@0: gTest.extraDelayedCheckFunction(); michael@0: } michael@0: michael@0: // Used to verify that this test has been performed michael@0: gTest.ranTest = true; michael@0: michael@0: if (gTest.buttonClick) { michael@0: debugDump("clicking " + gTest.buttonClick + " button"); michael@0: if(gTest.extraDelayedFinishFunction) { michael@0: throw("Tests cannot have a buttonClick and an extraDelayedFinishFunction property"); michael@0: } michael@0: gDocElem.getButton(gTest.buttonClick).click(); michael@0: } michael@0: else if (gTest.extraDelayedFinishFunction) { michael@0: debugDump("calling extraDelayedFinishFunction " + michael@0: gTest.extraDelayedFinishFunction.name); michael@0: gTest.extraDelayedFinishFunction(); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Checks the wizard page buttons' disabled and hidden attributes values are michael@0: * correct. If an expected button id is not specified then the expected disabled michael@0: * and hidden attribute value is true. michael@0: */ michael@0: function checkButtonStates() { michael@0: debugDump("entering - TESTS[" + gTestCounter + "], pageid: " + gTest.pageid); michael@0: michael@0: const buttonNames = ["extra1", "extra2", "back", "next", "finish", "cancel"]; michael@0: let buttonStates = getExpectedButtonStates(); michael@0: buttonNames.forEach(function(aButtonName) { michael@0: let button = gDocElem.getButton(aButtonName); michael@0: let hasHidden = aButtonName in buttonStates && michael@0: "hidden" in buttonStates[aButtonName]; michael@0: let hidden = hasHidden ? buttonStates[aButtonName].hidden : true; michael@0: let hasDisabled = aButtonName in buttonStates && michael@0: "disabled" in buttonStates[aButtonName]; michael@0: let disabled = hasDisabled ? buttonStates[aButtonName].disabled : true; michael@0: is(button.hidden, hidden, "Checking " + aButtonName + " button " + michael@0: "hidden attribute value equals " + (hidden ? "true" : "false")); michael@0: is(button.disabled, disabled, "Checking " + aButtonName + " button " + michael@0: "disabled attribute value equals " + (disabled ? "true" : "false")); michael@0: }); michael@0: } michael@0: michael@0: /** michael@0: * Returns the expected disabled and hidden attribute values for the buttons of michael@0: * the current wizard page. michael@0: */ michael@0: function getExpectedButtonStates() { michael@0: // Allow individual tests to override the expected button states. michael@0: if (gTest.buttonStates) { michael@0: return gTest.buttonStates; michael@0: } michael@0: michael@0: switch (gTest.pageid) { michael@0: case PAGEID_CHECKING: michael@0: case PAGEID_INCOMPAT_CHECK: michael@0: return { cancel: { disabled: false, hidden: false } }; michael@0: case PAGEID_FOUND_BASIC: michael@0: case PAGEID_FOUND_BILLBOARD: michael@0: if (gTest.neverButton) { michael@0: return { extra1: { disabled: false, hidden: false }, michael@0: extra2: { disabled: false, hidden: false }, michael@0: next : { disabled: false, hidden: false } } michael@0: } michael@0: return { extra1: { disabled: false, hidden: false }, michael@0: next : { disabled: false, hidden: false } }; michael@0: case PAGEID_LICENSE: michael@0: if (gRemoteContentState != "loaded" || michael@0: gAcceptDeclineLicense.selectedIndex != 0) { michael@0: return { extra1: { disabled: false, hidden: false }, michael@0: next : { disabled: true, hidden: false } }; michael@0: } michael@0: return { extra1: { disabled: false, hidden: false }, michael@0: next : { disabled: false, hidden: false } }; michael@0: case PAGEID_INCOMPAT_LIST: michael@0: return { extra1: { disabled: false, hidden: false }, michael@0: next : { disabled: false, hidden: false } }; michael@0: case PAGEID_DOWNLOADING: michael@0: return { extra1: { disabled: false, hidden: false } }; michael@0: case PAGEID_NO_UPDATES_FOUND: michael@0: case PAGEID_MANUAL_UPDATE: michael@0: case PAGEID_UNSUPPORTED: michael@0: case PAGEID_ERRORS: michael@0: case PAGEID_ERROR_EXTRA: michael@0: case PAGEID_INSTALLED: michael@0: return { finish: { disabled: false, hidden: false } }; michael@0: case PAGEID_ERROR_PATCHING: michael@0: return { next : { disabled: false, hidden: false } }; michael@0: case PAGEID_FINISHED: michael@0: case PAGEID_FINISHED_BKGRD: michael@0: return { extra1: { disabled: false, hidden: false }, michael@0: finish: { disabled: false, hidden: false } }; michael@0: } michael@0: return null; michael@0: } michael@0: michael@0: /** michael@0: * Adds a load event listener to the current remotecontent element. michael@0: */ michael@0: function addRemoteContentLoadListener() { michael@0: debugDump("entering - TESTS[" + gTestCounter + "], pageid: " + gTest.pageid); michael@0: michael@0: gRemoteContent.addEventListener("load", remoteContentLoadListener, false); michael@0: } michael@0: michael@0: /** michael@0: * The nsIDOMEventListener for a remotecontent load event. michael@0: */ michael@0: function remoteContentLoadListener(aEvent) { michael@0: // Return early if the event's original target's nodeName isn't remotecontent. michael@0: if (aEvent.originalTarget.nodeName != "remotecontent") { michael@0: debugDump("only handles events with an originalTarget nodeName of " + michael@0: "|remotecontent|. aEvent.originalTarget.nodeName = " + michael@0: aEvent.originalTarget.nodeName); michael@0: return; michael@0: } michael@0: michael@0: gTestCounter++; michael@0: gCallback(aEvent); michael@0: } michael@0: michael@0: /** michael@0: * Waits until a remotecontent element to finish loading which is determined michael@0: * by the current test's expectedRemoteContentState property and then removes michael@0: * the event listener. michael@0: * michael@0: * Note: tests that use this function should not test the state of the michael@0: * remotecontent since this will check the expected state. michael@0: * michael@0: * @return false if the remotecontent has loaded and its state is the state michael@0: * specified in the current test's expectedRemoteContentState michael@0: * property... otherwise true. michael@0: */ michael@0: function waitForRemoteContentLoaded(aEvent) { michael@0: // Return early until the remotecontent has loaded with the state that is michael@0: // expected or isn't the event's originalTarget. michael@0: if (gRemoteContentState != gTest.expectedRemoteContentState || michael@0: aEvent.originalTarget != gRemoteContent) { michael@0: debugDump("returning early\n" + michael@0: "gRemoteContentState: " + gRemoteContentState + "\n" + michael@0: "expectedRemoteContentState: " + michael@0: gTest.expectedRemoteContentState + "\n" + michael@0: "aEvent.originalTarget.nodeName: " + michael@0: aEvent.originalTarget.nodeName); michael@0: return true; michael@0: } michael@0: michael@0: gRemoteContent.removeEventListener("load", remoteContentLoadListener, false); michael@0: return false; michael@0: } michael@0: michael@0: /** michael@0: * Compares the value of the remotecontent state attribute with the value michael@0: * specified in the test's expectedRemoteContentState property. michael@0: */ michael@0: function checkRemoteContentState() { michael@0: is(gRemoteContentState, gTest.expectedRemoteContentState, "Checking remote " + michael@0: "content state equals " + gTest.expectedRemoteContentState + " - pageid " + michael@0: gTest.pageid); michael@0: } michael@0: michael@0: /** michael@0: * Adds a select event listener to the license radiogroup element and clicks michael@0: * the radio element specified in the current test's radioClick property. michael@0: */ michael@0: function addRadioGroupSelectListenerAndClick() { michael@0: debugDump("entering - TESTS[" + gTestCounter + "], pageid: " + gTest.pageid); michael@0: michael@0: gAcceptDeclineLicense.addEventListener("select", radioGroupSelectListener, michael@0: false); michael@0: gWin.document.getElementById(gTest.radioClick).click(); michael@0: } michael@0: michael@0: /** michael@0: * The nsIDOMEventListener for the license radiogroup select event. michael@0: */ michael@0: function radioGroupSelectListener(aEvent) { michael@0: // Return early if the event's original target's nodeName isn't radiogroup. michael@0: if (aEvent.originalTarget.nodeName != "radiogroup") { michael@0: debugDump("only handles events with an originalTarget nodeName of " + michael@0: "|radiogroup|. aEvent.originalTarget.nodeName = " + michael@0: aEvent.originalTarget.nodeName); michael@0: return; michael@0: } michael@0: michael@0: gAcceptDeclineLicense.removeEventListener("select", radioGroupSelectListener, michael@0: false); michael@0: gTestCounter++; michael@0: gCallback(aEvent); michael@0: } michael@0: michael@0: /** michael@0: * Compares the value of the License radiogroup's selectedIndex attribute with michael@0: * the value specified in the test's expectedRadioGroupSelectedIndex property. michael@0: */ michael@0: function checkRadioGroupSelectedIndex() { michael@0: is(gAcceptDeclineLicense.selectedIndex, gTest.expectedRadioGroupSelectedIndex, michael@0: "Checking license radiogroup selectedIndex equals " + michael@0: gTest.expectedRadioGroupSelectedIndex); michael@0: } michael@0: michael@0: /** michael@0: * Checks that only incompatible add-ons (e.g. noupdate_X add-ons) that don't michael@0: * have an update are listed in the add-ons incompatible list. michael@0: */ michael@0: function checkIncompatbleList() { michael@0: for (let i = 0; i < gIncompatibleListbox.itemCount; i++) { michael@0: let label = gIncompatibleListbox.getItemAtIndex(i).label; michael@0: // Use indexOf since locales can change the text displayed michael@0: ok(label.indexOf("noupdate") != -1, "Checking that only incompatible " + michael@0: "add-ons that don't have an update are listed in the incompatible list"); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Compares the return value of prefHasUserValue for the preference specified in michael@0: * gPrefToCheck with the value passed in the aPrefHasValue parameter or the michael@0: * value specified in the current test's prefHasUserValue property if michael@0: * aPrefHasValue is undefined. michael@0: * michael@0: * @param aPrefHasValue (optional) michael@0: * The expected value returned from prefHasUserValue for the preference michael@0: * specified in gPrefToCheck. If aPrefHasValue is undefined the value michael@0: * of the current test's prefHasUserValue property will be used. michael@0: */ michael@0: function checkPrefHasUserValue(aPrefHasValue) { michael@0: let prefHasUserValue = aPrefHasValue === undefined ? gTest.prefHasUserValue michael@0: : aPrefHasValue; michael@0: is(Services.prefs.prefHasUserValue(gPrefToCheck), prefHasUserValue, michael@0: "Checking prefHasUserValue for preference " + gPrefToCheck + " equals " + michael@0: (prefHasUserValue ? "true" : "false")); michael@0: } michael@0: michael@0: /** michael@0: * Checks whether the link is hidden (general background update check error or michael@0: * a certificate attribute check error with an update) or not (certificate michael@0: * attribute check error without an update) on the errorextra page and that the michael@0: * app.update.cert.errors and app.update.backgroundErrors preferences do not michael@0: & have a user value. michael@0: * michael@0: * @param aShouldBeHidden (optional) michael@0: * The expected value for the label's hidden attribute for the link. If michael@0: * aShouldBeHidden is undefined the value of the current test's michael@0: * shouldBeHidden property will be used. michael@0: */ michael@0: function checkErrorExtraPage(aShouldBeHidden) { michael@0: let shouldBeHidden = aShouldBeHidden === undefined ? gTest.shouldBeHidden michael@0: : aShouldBeHidden; michael@0: is(gWin.document.getElementById("errorExtraLinkLabel").hidden, shouldBeHidden, michael@0: "Checking errorExtraLinkLabel hidden attribute equals " + michael@0: (shouldBeHidden ? "true" : "false")); michael@0: michael@0: is(gWin.document.getElementById(gTest.displayedTextElem).hidden, false, michael@0: "Checking " + gTest.displayedTextElem + " should not be hidden"); michael@0: michael@0: ok(!Services.prefs.prefHasUserValue(PREF_APP_UPDATE_CERT_ERRORS), michael@0: "Preference " + PREF_APP_UPDATE_CERT_ERRORS + " should not have a " + michael@0: "user value"); michael@0: michael@0: ok(!Services.prefs.prefHasUserValue(PREF_APP_UPDATE_BACKGROUNDERRORS), michael@0: "Preference " + PREF_APP_UPDATE_BACKGROUNDERRORS + " should not have a " + michael@0: "user value"); michael@0: } michael@0: michael@0: /** michael@0: * Gets the update version info for the update url parameters to send to michael@0: * update.sjs. michael@0: * michael@0: * @param aAppVersion (optional) michael@0: * The application version for the update snippet. If not specified the michael@0: * current application version will be used. michael@0: * @param aPlatformVersion (optional) michael@0: * The platform version for the update snippet. If not specified the michael@0: * current platform version will be used. michael@0: * @return The url parameters for the application and platform version to send michael@0: * to update.sjs. michael@0: */ michael@0: function getVersionParams(aAppVersion, aPlatformVersion) { michael@0: let appInfo = Services.appinfo; michael@0: return "&appVersion=" + (aAppVersion ? aAppVersion : appInfo.version) + michael@0: "&platformVersion=" + (aPlatformVersion ? aPlatformVersion michael@0: : appInfo.platformVersion); michael@0: } michael@0: michael@0: /** michael@0: * Gets an application version that is greater than the current application michael@0: * version. The version is created by taking the first sequence from the current michael@0: * application version and adding 1 to it. michael@0: * michael@0: * @return A version string greater than the current application version string. michael@0: */ michael@0: function getNewerAppVersion() { michael@0: let appVersion = Services.appinfo.version.split(".")[0]; michael@0: appVersion++; michael@0: return appVersion; michael@0: } michael@0: michael@0: /** michael@0: * Gets a platform version that is greater than the current platform version. michael@0: * The version is created by taking the first sequence from the current platform michael@0: * version and adding 1 to it. michael@0: * michael@0: * @return A version string greater than the current platform version string. michael@0: */ michael@0: function getNewerPlatformVersion() { michael@0: let platformVersion = Services.appinfo.platformVersion.split(".")[0]; michael@0: platformVersion++; michael@0: return platformVersion; michael@0: } michael@0: michael@0: /** michael@0: * Verifies that all tests ran. michael@0: */ michael@0: function verifyTestsRan() { michael@0: debugDump("entering"); michael@0: michael@0: // Return early if there are no tests defined. michael@0: if (!TESTS) { michael@0: return; michael@0: } michael@0: michael@0: gTestCounter = -1; michael@0: for (let i = 0; i < TESTS.length; ++i) { michael@0: gTestCounter++; michael@0: let test = TESTS[i]; michael@0: let msg = "Checking if TESTS[" + i + "] test was performed... " + michael@0: "callback function name = " + gCallback.name + ", " + michael@0: "pageid = " + test.pageid; michael@0: ok(test.ranTest, msg); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Creates a backup of files the tests need to modify so they can be restored to michael@0: * the original file when the test has finished and then modifies the files. michael@0: */ michael@0: function setupFiles() { michael@0: // Backup the updater-settings.ini file if it exists by moving it. michael@0: let baseAppDir = getAppBaseDir(); michael@0: let updateSettingsIni = baseAppDir.clone(); michael@0: updateSettingsIni.append(FILE_UPDATE_SETTINGS_INI); michael@0: if (updateSettingsIni.exists()) { michael@0: updateSettingsIni.moveTo(baseAppDir, FILE_UPDATE_SETTINGS_INI_BAK); michael@0: } michael@0: updateSettingsIni = baseAppDir.clone(); michael@0: updateSettingsIni.append(FILE_UPDATE_SETTINGS_INI); michael@0: writeFile(updateSettingsIni, UPDATE_SETTINGS_CONTENTS); michael@0: } michael@0: michael@0: /** michael@0: * Sets the most common preferences used by tests to values used by the majority michael@0: * of the tests and when necessary saves the preference's original values if michael@0: * present so they can be set back to the original values when the test has michael@0: * finished. michael@0: */ michael@0: function setupPrefs() { michael@0: if (DEBUG_AUS_TEST) { michael@0: Services.prefs.setBoolPref(PREF_APP_UPDATE_LOG, true); michael@0: } michael@0: michael@0: if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_URL_OVERRIDE)) { michael@0: gAppUpdateURL = Services.prefs.getCharPref(PREF_APP_UPDATE_URL_OVERRIDE); michael@0: } michael@0: michael@0: if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_ENABLED)) { michael@0: gAppUpdateEnabled = Services.prefs.getBoolPref(PREF_APP_UPDATE_ENABLED); michael@0: } michael@0: Services.prefs.setBoolPref(PREF_APP_UPDATE_ENABLED, true); michael@0: michael@0: if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_METRO_ENABLED)) { michael@0: gAppUpdateMetroEnabled = Services.prefs.getBoolPref(PREF_APP_UPDATE_METRO_ENABLED); michael@0: } michael@0: Services.prefs.setBoolPref(PREF_APP_UPDATE_METRO_ENABLED, true); michael@0: michael@0: if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_SERVICE_ENABLED)) { michael@0: gAppUpdateServiceEnabled = Services.prefs.getBoolPref(PREF_APP_UPDATE_SERVICE_ENABLED); michael@0: } michael@0: Services.prefs.setBoolPref(PREF_APP_UPDATE_SERVICE_ENABLED, false); michael@0: michael@0: if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_STAGING_ENABLED)) { michael@0: gAppUpdateStagingEnabled = Services.prefs.getBoolPref(PREF_APP_UPDATE_STAGING_ENABLED); michael@0: } michael@0: Services.prefs.setBoolPref(PREF_APP_UPDATE_STAGING_ENABLED, false); michael@0: michael@0: if (Services.prefs.prefHasUserValue(PREF_EXTENSIONS_UPDATE_URL)) { michael@0: gExtUpdateURL = Services.prefs.getCharPref(PREF_EXTENSIONS_UPDATE_URL); michael@0: } michael@0: let extUpdateUrl = URL_HTTP_UPDATE_XML + "?addonID=%ITEM_ID%" + michael@0: "&platformVersion=" + getNewerPlatformVersion(); michael@0: Services.prefs.setCharPref(PREF_EXTENSIONS_UPDATE_URL, extUpdateUrl); michael@0: michael@0: Services.prefs.setIntPref(PREF_APP_UPDATE_IDLETIME, 0); michael@0: Services.prefs.setIntPref(PREF_APP_UPDATE_PROMPTWAITTIME, 0); michael@0: Services.prefs.setBoolPref(PREF_EXTENSIONS_STRICT_COMPAT, true); michael@0: Services.prefs.setCharPref(PREF_EM_HOTFIX_ID, "hotfix" + ADDON_ID_SUFFIX); michael@0: } michael@0: michael@0: /** michael@0: * Restores files that were backed up for the tests and general file cleanup. michael@0: */ michael@0: function resetFiles() { michael@0: // Restore the backed up updater-settings.ini if it exists. michael@0: let baseAppDir = getAppBaseDir(); michael@0: let updateSettingsIni = baseAppDir.clone(); michael@0: updateSettingsIni.append(FILE_UPDATE_SETTINGS_INI_BAK); michael@0: if (updateSettingsIni.exists()) { michael@0: updateSettingsIni.moveTo(baseAppDir, FILE_UPDATE_SETTINGS_INI); michael@0: } michael@0: michael@0: // Not being able to remove the "updated" directory will not adversely affect michael@0: // subsequent tests so wrap it in a try block and don't test whether its michael@0: // removal was successful. michael@0: let updatedDir = getUpdatedDir(); michael@0: if (updatedDir.exists()) { michael@0: try { michael@0: removeDirRecursive(updatedDir); michael@0: } michael@0: catch (e) { michael@0: dump("Unable to remove directory\n" + michael@0: "path: " + updatedDir.path + "\n" + michael@0: "Exception: " + e + "\n"); michael@0: } michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Resets the most common preferences used by tests to their original values. michael@0: */ michael@0: function resetPrefs() { michael@0: if (gAppUpdateURL !== undefined) { michael@0: Services.prefs.setCharPref(PREF_APP_UPDATE_URL_OVERRIDE, gAppUpdateURL); michael@0: } michael@0: else if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_URL_OVERRIDE)) { michael@0: Services.prefs.clearUserPref(PREF_APP_UPDATE_URL_OVERRIDE); michael@0: } michael@0: michael@0: if (gAppUpdateURLDefault) { michael@0: gDefaultPrefBranch.setCharPref(PREF_APP_UPDATE_URL, gAppUpdateURLDefault); michael@0: } michael@0: michael@0: if (gAppUpdateEnabled !== undefined) { michael@0: Services.prefs.setBoolPref(PREF_APP_UPDATE_ENABLED, gAppUpdateEnabled); michael@0: } michael@0: else if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_ENABLED)) { michael@0: Services.prefs.clearUserPref(PREF_APP_UPDATE_ENABLED); michael@0: } michael@0: michael@0: if (gAppUpdateMetroEnabled !== undefined) { michael@0: Services.prefs.setBoolPref(PREF_APP_UPDATE_METRO_ENABLED, gAppUpdateMetroEnabled); michael@0: } michael@0: else if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_METRO_ENABLED)) { michael@0: Services.prefs.clearUserPref(PREF_APP_UPDATE_METRO_ENABLED); michael@0: } michael@0: michael@0: if (gAppUpdateServiceEnabled !== undefined) { michael@0: Services.prefs.setBoolPref(PREF_APP_UPDATE_SERVICE_ENABLED, gAppUpdateServiceEnabled); michael@0: } michael@0: else if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_SERVICE_ENABLED)) { michael@0: Services.prefs.clearUserPref(PREF_APP_UPDATE_SERVICE_ENABLED); michael@0: } michael@0: michael@0: if (gAppUpdateStagingEnabled !== undefined) { michael@0: Services.prefs.setBoolPref(PREF_APP_UPDATE_STAGING_ENABLED, gAppUpdateStagingEnabled); michael@0: } michael@0: else if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_STAGING_ENABLED)) { michael@0: Services.prefs.clearUserPref(PREF_APP_UPDATE_STAGING_ENABLED); michael@0: } michael@0: michael@0: if (gExtUpdateURL !== undefined) { michael@0: Services.prefs.setCharPref(PREF_EXTENSIONS_UPDATE_URL, gExtUpdateURL); michael@0: } michael@0: else if (Services.prefs.prefHasUserValue(PREF_EXTENSIONS_UPDATE_URL)) { michael@0: Services.prefs.clearUserPref(PREF_EXTENSIONS_UPDATE_URL); michael@0: } michael@0: michael@0: if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_IDLETIME)) { michael@0: Services.prefs.clearUserPref(PREF_APP_UPDATE_IDLETIME); michael@0: } michael@0: michael@0: if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_PROMPTWAITTIME)) { michael@0: Services.prefs.clearUserPref(PREF_APP_UPDATE_PROMPTWAITTIME); michael@0: } michael@0: michael@0: if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_URL_DETAILS)) { michael@0: Services.prefs.clearUserPref(PREF_APP_UPDATE_URL_DETAILS); michael@0: } michael@0: michael@0: if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_SHOW_INSTALLED_UI)) { michael@0: Services.prefs.clearUserPref(PREF_APP_UPDATE_SHOW_INSTALLED_UI); michael@0: } michael@0: michael@0: if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_NOTIFIEDUNSUPPORTED)) { michael@0: Services.prefs.clearUserPref(PREF_APP_UPDATE_NOTIFIEDUNSUPPORTED); michael@0: } michael@0: michael@0: if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_LOG)) { michael@0: Services.prefs.clearUserPref(PREF_APP_UPDATE_LOG); michael@0: } michael@0: michael@0: if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_CERT_ERRORS)) { michael@0: Services.prefs.clearUserPref(PREF_APP_UPDATE_CERT_ERRORS); michael@0: } michael@0: michael@0: if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_CERT_MAXERRORS)) { michael@0: Services.prefs.clearUserPref(PREF_APP_UPDATE_CERT_MAXERRORS); michael@0: } michael@0: michael@0: if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_BACKGROUNDERRORS)) { michael@0: Services.prefs.clearUserPref(PREF_APP_UPDATE_BACKGROUNDERRORS); michael@0: } michael@0: michael@0: if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_BACKGROUNDMAXERRORS)) { michael@0: Services.prefs.clearUserPref(PREF_APP_UPDATE_BACKGROUNDMAXERRORS); michael@0: } michael@0: michael@0: if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_CERT_INVALID_ATTR_NAME)) { michael@0: Services.prefs.clearUserPref(PREF_APP_UPDATE_CERT_INVALID_ATTR_NAME); michael@0: } michael@0: michael@0: if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_CERT_REQUIREBUILTIN)) { michael@0: Services.prefs.clearUserPref(PREF_APP_UPDATE_CERT_REQUIREBUILTIN); michael@0: } michael@0: michael@0: if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_CERT_CHECKATTRS)) { michael@0: Services.prefs.clearUserPref(PREF_APP_UPDATE_CERT_CHECKATTRS); michael@0: } michael@0: michael@0: try { michael@0: CERT_ATTRS.forEach(function(aCertAttrName) { michael@0: Services.prefs.clearUserPref(PREF_APP_UPDATE_CERTS_BRANCH + "1." + michael@0: aCertAttrName); michael@0: }); michael@0: } michael@0: catch (e) { michael@0: } michael@0: michael@0: try { michael@0: Services.prefs.deleteBranch(PREF_APP_UPDATE_NEVER_BRANCH); michael@0: } michael@0: catch(e) { michael@0: } michael@0: michael@0: if (Services.prefs.prefHasUserValue(PREF_EXTENSIONS_STRICT_COMPAT)) { michael@0: Services.prefs.clearUserPref(PREF_EXTENSIONS_STRICT_COMPAT); michael@0: } michael@0: michael@0: if (Services.prefs.prefHasUserValue(PREF_EM_HOTFIX_ID)) { michael@0: Services.prefs.clearUserPref(PREF_EM_HOTFIX_ID); michael@0: } michael@0: } michael@0: michael@0: function setupTimer(aTestTimeout) { michael@0: gTestTimeout = aTestTimeout; michael@0: if (gTimeoutTimer) { michael@0: gTimeoutTimer.cancel(); michael@0: gTimeoutTimer = null; michael@0: } michael@0: gTimeoutTimer = AUS_Cc["@mozilla.org/timer;1"]. michael@0: createInstance(AUS_Ci.nsITimer); michael@0: gTimeoutTimer.initWithCallback(finishTestTimeout, gTestTimeout, michael@0: AUS_Ci.nsITimer.TYPE_ONE_SHOT); michael@0: } michael@0: michael@0: /** michael@0: * Disables pre-existing add-ons so they don't interfere with the tests, michael@0: * installs the test add-ons, sets the noupdate test add-ons' userDisabled value michael@0: * for the test, and calls the callback specified in the aCallback parameter. If michael@0: * the app.update.test.disabledAddons has a user value then setting the noupdate michael@0: * test add-ons' userDisabled value for the test is the only thing that is done. michael@0: * michael@0: * @param aCallback michael@0: * A callback to call after all operations have completed. michael@0: */ michael@0: function setupAddons(aCallback) { michael@0: debugDump("entering"); michael@0: michael@0: // Sets the appropriate userDisabled value for the noupdate test add-ons based michael@0: // on the value of gDisableNoUpdateAddon and calls the callback specified in michael@0: // setupAddons aCallback parameter. michael@0: function setNoUpdateAddonsDisabledState() { michael@0: AddonManager.getAllAddons(function(aAddons) { michael@0: aAddons.forEach(function(aAddon) { michael@0: if (aAddon.name.indexOf("noupdate") != 0) michael@0: return; michael@0: michael@0: if (gDisableNoUpdateAddon) { michael@0: if (!aAddon.userDisabled) { michael@0: aAddon.userDisabled = true; michael@0: } michael@0: } michael@0: else { michael@0: if (aAddon.userDisabled) { michael@0: aAddon.userDisabled = false; michael@0: } michael@0: } michael@0: }); michael@0: // Start the timout timer before the update window is displayed so it can michael@0: // clean up tests that don't successfully display the update window. michael@0: setupTimer(gTestTimeout); michael@0: aCallback(); michael@0: }); michael@0: } michael@0: michael@0: // If the app.update.test.disabledAddons preference exists the pre-existing michael@0: // add-ons have already been disabled so they don't interfere with the tests, michael@0: // the test add-ons have already been installed, and the only thing that needs michael@0: // to be done is setting the appropriate userDisabled value for the noupdate michael@0: // test add-ons. michael@0: if (Services.prefs.prefHasUserValue(PREF_DISABLEDADDONS)) { michael@0: setNoUpdateAddonsDisabledState(); michael@0: return; michael@0: } michael@0: michael@0: // Disable all pre-existing enabled addons so they don't interfere with the michael@0: // tests. michael@0: AddonManager.getAllAddons(function(aAddons) { michael@0: let disabledAddons = []; michael@0: aAddons.forEach(function(aAddon) { michael@0: // If an addon's type equals plugin it is skipped since michael@0: // checking plugins compatibility information isn't supported at this michael@0: // time (also see bug 566787). Also, SCOPE_APPLICATION add-ons are michael@0: // excluded by app update so there is no reason to disable them. michael@0: if (aAddon.type != "plugin" && !aAddon.appDisabled && michael@0: !aAddon.userDisabled && michael@0: aAddon.scope != AddonManager.SCOPE_APPLICATION) { michael@0: disabledAddons.push(aAddon); michael@0: aAddon.userDisabled = true; michael@0: } michael@0: }); michael@0: // If there are no pre-existing add-ons the preference value will be an michael@0: // empty string. michael@0: Services.prefs.setCharPref(PREF_DISABLEDADDONS, disabledAddons.join(" ")); michael@0: michael@0: // Install the test add-ons. michael@0: let xpiFiles = getTestAddonXPIFiles(); michael@0: let xpiCount = xpiFiles.length; michael@0: let installs = []; michael@0: xpiFiles.forEach(function(aFile) { michael@0: AddonManager.getInstallForFile(aFile, function(aInstall) { michael@0: if (!aInstall) { michael@0: throw "No AddonInstall created for " + aFile.path; michael@0: } michael@0: michael@0: installs.push(aInstall); michael@0: michael@0: if (--xpiCount == 0) { michael@0: let installCount = installs.length; michael@0: function installCompleted(aInstall) { michael@0: aInstall.removeListener(listener); michael@0: michael@0: if (getAddonTestType(aInstall.addon.name) == "userdisabled") { michael@0: aInstall.addon.userDisabled = true; michael@0: } michael@0: if (--installCount == 0) { michael@0: setNoUpdateAddonsDisabledState(); michael@0: } michael@0: } michael@0: michael@0: let listener = { michael@0: onDownloadFailed: installCompleted, michael@0: onDownloadCancelled: installCompleted, michael@0: onInstallFailed: installCompleted, michael@0: onInstallCancelled: installCompleted, michael@0: onInstallEnded: installCompleted michael@0: }; michael@0: michael@0: installs.forEach(function(aInstall) { michael@0: aInstall.addListener(listener); michael@0: aInstall.install(); michael@0: }); michael@0: } michael@0: }); michael@0: }); michael@0: }); michael@0: } michael@0: michael@0: /** michael@0: * Uninstalls the test add-ons, enables add-ons that were disabled when the michael@0: * test started, and calls the callback specified in the aCallback parameter. michael@0: * michael@0: * @param aCallback michael@0: * A callback to call after all operations have completed. michael@0: */ michael@0: function resetAddons(aCallback) { michael@0: debugDump("entering"); michael@0: // If test_9999_cleanup.xul is ran by itself then the test add-ons will not michael@0: // have been installed and any pre-existing add-ons will not have been michael@0: // disabled so return early. michael@0: if (!Services.prefs.prefHasUserValue(PREF_DISABLEDADDONS)) { michael@0: debugDump("preference " + PREF_DISABLEDADDONS + " doesn't exist... " + michael@0: "returning early"); michael@0: aCallback(); michael@0: return; michael@0: } michael@0: michael@0: // Uninstall the test add-ons. michael@0: let count = TEST_ADDONS.length; michael@0: function uninstallCompleted(aAddon) { michael@0: if (--count == 0) { michael@0: AddonManager.removeAddonListener(listener); michael@0: michael@0: // Enable the pre-existing add-ons that were disabled so they wouldn't michael@0: // interfere with the tests. michael@0: let disabledAddons = Services.prefs.getCharPref(PREF_DISABLEDADDONS).split(" "); michael@0: Services.prefs.clearUserPref(PREF_DISABLEDADDONS); michael@0: AddonManager.getAllAddons(function(aAddons) { michael@0: aAddons.forEach(function(aAddon) { michael@0: if (disabledAddons.indexOf(aAddon.id)) { michael@0: aAddon.userDisabled = false; michael@0: } michael@0: }); michael@0: aCallback(); michael@0: }); michael@0: } michael@0: } michael@0: michael@0: let listener = { michael@0: onUninstalled: uninstallCompleted michael@0: }; michael@0: michael@0: AddonManager.addAddonListener(listener); michael@0: TEST_ADDONS.forEach(function(aName) { michael@0: AddonManager.getAddonByID(aName + ADDON_ID_SUFFIX, function(aAddon) { michael@0: aAddon.uninstall(); michael@0: }); michael@0: }); michael@0: } michael@0: michael@0: /** michael@0: * Helper function to get the string before the '_' character in an add-on's michael@0: * name or id which is used to determine the add-on test type used by the tests. michael@0: * michael@0: * @param aName michael@0: * The test add-on's name or id. michael@0: * @return The string before the '_' character in the string passed in the aName michael@0: * parameter. michael@0: */ michael@0: function getAddonTestType(aName) { michael@0: return aName.split("_")[0]; michael@0: } michael@0: michael@0: /** michael@0: * Helper function to create add-on xpi files for the default test add-ons. michael@0: * michael@0: * @return An array with each member being an nsILocalFile for an add-on XPI michael@0: * file. michael@0: */ michael@0: function getTestAddonXPIFiles() { michael@0: let addonPrepDir = Services.dirsvc.get(NS_APP_USER_PROFILE_50_DIR, michael@0: AUS_Ci.nsILocalFile); michael@0: addonPrepDir.append(ADDON_PREP_DIR); michael@0: michael@0: let bootstrap = addonPrepDir.clone(); michael@0: bootstrap.append("bootstrap.js"); michael@0: // If a previous test has already created bootstrap.js don't create it again. michael@0: if (!bootstrap.exists()) { michael@0: let bootstrapContents = "function install(data, reason){ }\n" + michael@0: "function startup(data, reason){ }\n" + michael@0: "function shutdown(data, reason){ }\n" + michael@0: "function uninstall(data, reason){ }\n"; michael@0: writeFile(bootstrap, bootstrapContents); michael@0: } michael@0: michael@0: let installRDF = addonPrepDir.clone(); michael@0: installRDF.append("install.rdf"); michael@0: michael@0: let xpiFiles = []; michael@0: TEST_ADDONS.forEach(function(aName) { michael@0: let xpiFile = addonPrepDir.clone(); michael@0: xpiFile.append(aName + ".xpi"); michael@0: michael@0: if (installRDF.exists()) michael@0: installRDF.remove(false); michael@0: writeFile(installRDF, getInstallRDFString(aName)); michael@0: gZipW.open(xpiFile, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE); michael@0: gZipW.addEntryFile(installRDF.leafName, michael@0: AUS_Ci.nsIZipWriter.COMPRESSION_DEFAULT, installRDF, michael@0: false); michael@0: gZipW.addEntryFile(bootstrap.leafName, michael@0: AUS_Ci.nsIZipWriter.COMPRESSION_DEFAULT, bootstrap, michael@0: false); michael@0: gZipW.close(); michael@0: xpiFiles.push(xpiFile); michael@0: }); michael@0: michael@0: return xpiFiles; michael@0: } michael@0: michael@0: /** michael@0: * Helper function to gets the string representation of the contents of the michael@0: * add-on's install.rdf file. michael@0: * michael@0: * @param aName michael@0: * The string to use for the add-on's name which is also used to michael@0: * construct the local-part in RFC 5322 format of the add-on's ID. michael@0: * @return A string representation of the contents of the add-on's install.rdf michael@0: * file. michael@0: */ michael@0: function getInstallRDFString(aName) { michael@0: let maxVersion = Services.appinfo.platformVersion; michael@0: switch (getAddonTestType(aName)) { michael@0: case "compatible": michael@0: maxVersion = getNewerPlatformVersion(); michael@0: break; michael@0: case "appdisabled": michael@0: maxVersion = "0.1"; michael@0: break; michael@0: } michael@0: michael@0: return "\n" + michael@0: "\n" + michael@0: " \n" + michael@0: " " + aName + ADDON_ID_SUFFIX + "\n" + michael@0: " 1.0\n" + michael@0: " true\n" + michael@0: " " + aName + "\n" + michael@0: " Test Description\n" + michael@0: " \n" + michael@0: " \n" + michael@0: " toolkit@mozilla.org\n" + michael@0: " undefined\n" + michael@0: " " + maxVersion + "\n" + michael@0: " \n" + michael@0: " \n" + michael@0: " \n" + michael@0: ""; michael@0: } michael@0: michael@0: /** michael@0: * Closes the update window if it is open and causes the test to fail if an michael@0: * update window is found. michael@0: * michael@0: * @return true if an update window was found, otherwise false. michael@0: */ michael@0: function closeUpdateWindow() { michael@0: let updateWindow = getUpdateWindow(); michael@0: if (!updateWindow) michael@0: return false; michael@0: michael@0: ok(false, "Found an existing Update Window from the current or a previous " + michael@0: "test... attempting to close it."); michael@0: updateWindow.close(); michael@0: return true; michael@0: } michael@0: michael@0: /** michael@0: * Gets the update window. michael@0: * michael@0: * @return The nsIDOMWindow for the Update Window if it is open and null michael@0: * if it isn't. michael@0: */ michael@0: function getUpdateWindow() { michael@0: return Services.wm.getMostRecentWindow(UPDATE_WINDOW_NAME); michael@0: } michael@0: michael@0: /** michael@0: * Helper for background check errors. michael@0: */ michael@0: var errorsPrefObserver = { michael@0: observedPref: null, michael@0: maxErrorPref: null, michael@0: michael@0: /** michael@0: * Sets up a preference observer and sets the associated maximum errors michael@0: * preference used for background notification. michael@0: * michael@0: * @param aObservePref michael@0: * The preference to observe. michael@0: * @param aMaxErrorPref michael@0: * The maximum errors preference. michael@0: * @param aMaxErrorCount michael@0: * The value to set the app.update.cert.maxErrors preference to. michael@0: */ michael@0: init: function(aObservePref, aMaxErrorPref, aMaxErrorCount) { michael@0: this.observedPref = aObservePref; michael@0: this.maxErrorPref = aMaxErrorPref; michael@0: michael@0: let maxErrors = aMaxErrorCount ? aMaxErrorCount : 2; michael@0: Services.prefs.setIntPref(aMaxErrorPref, maxErrors); michael@0: Services.prefs.addObserver(aObservePref, this, false); michael@0: }, michael@0: michael@0: /** michael@0: * Preference observer for the preference specified in |this.observedPref|. michael@0: */ michael@0: observe: function XPI_observe(aSubject, aTopic, aData) { michael@0: if (aData == this.observedPref) { michael@0: let errCount = Services.prefs.getIntPref(this.observedPref); michael@0: let errMax = Services.prefs.getIntPref(this.maxErrorPref); michael@0: if (errCount >= errMax) { michael@0: debugDump("removing pref observer"); michael@0: Services.prefs.removeObserver(this.observedPref, this); michael@0: } michael@0: else { michael@0: debugDump("notifying AUS"); michael@0: SimpleTest.executeSoon(function() { michael@0: gAUS.notify(null); michael@0: }); michael@0: } michael@0: } michael@0: } michael@0: }; michael@0: michael@0: /** michael@0: * nsIObserver for receiving window open and close notifications. michael@0: */ michael@0: var gWindowObserver = { michael@0: observe: function WO_observe(aSubject, aTopic, aData) { michael@0: let win = aSubject.QueryInterface(AUS_Ci.nsIDOMEventTarget); michael@0: michael@0: if (aTopic == "domwindowclosed") { michael@0: if (win.location != URI_UPDATE_PROMPT_DIALOG) { michael@0: debugDump("domwindowclosed event for window not being tested - " + michael@0: "location: " + win.location + "... returning early"); michael@0: return; michael@0: } michael@0: // Allow tests the ability to provide their own function (it must be michael@0: // named finishTest) for finishing the test. michael@0: try { michael@0: finishTest(); michael@0: } michael@0: catch (e) { michael@0: finishTestDefault(); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: win.addEventListener("load", function WO_observe_onLoad() { michael@0: win.removeEventListener("load", WO_observe_onLoad, false); michael@0: // Ignore windows other than the update UI window. michael@0: if (win.location != URI_UPDATE_PROMPT_DIALOG) { michael@0: debugDump("load event for window not being tested - location: " + michael@0: win.location + "... returning early"); michael@0: return; michael@0: } michael@0: michael@0: // The first wizard page should always be the dummy page. michael@0: let pageid = win.document.documentElement.currentPage.pageid; michael@0: if (pageid != PAGEID_DUMMY) { michael@0: // This should never happen but if it does this will provide a clue michael@0: // for diagnosing the cause. michael@0: ok(false, "Unexpected load event - pageid got: " + pageid + michael@0: ", expected: " + PAGEID_DUMMY + "... returning early"); michael@0: return; michael@0: } michael@0: michael@0: gWin = win; michael@0: gDocElem = gWin.document.documentElement; michael@0: gDocElem.addEventListener("pageshow", onPageShowDefault, false); michael@0: }, false); michael@0: } michael@0: };