/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/
 */

// Tests the selection part of the post-app-update dialog

var gProvider;
var gWin;

const PROFILE = AddonManager.SCOPE_PROFILE;
const USER = AddonManager.SCOPE_USER;
const APP = AddonManager.SCOPE_APPLICATION;
const SYSTEM = AddonManager.SCOPE_SYSTEM;
const DIST = -1;

// The matrix of testcases for the selection part of the UI
// Note that the isActive flag has the value it had when the previous version
// of the application ran with this add-on.
var ADDONS = [
  //userDisabled   wasAppDisabled isAppDisabled  isActive  hasUpdate  autoUpdate  scope    defaultKeep  position  keepString           disableString
  [false,          true,          false,         false,    false,     true,       PROFILE, true,        42,       "enabled",           ""],               // 0
  [false,          true,          false,         false,    true,      true,       PROFILE, true,        43,       "enabled",           ""],               // 1
  [false,          true,          false,         false,    true,      false,      PROFILE, true,        52,       "unneededupdate",    ""],               // 2
  [false,          false,         false,         true,     false,     true,       PROFILE, true,        53,       "",                  "disabled"],       // 3
  [false,          false,         false,         true,     true,      true,       PROFILE, true,        54,       "",                  "disabled"],       // 4
  [false,          false,         false,         true,     true,      false,      PROFILE, true,        55,       "unneededupdate",    "disabled"],       // 5
  [false,          true,          true,          false,    false,     true,       PROFILE, true,        56,       "incompatible",      ""],               // 6
  [false,          true,          true,          false,    true,      true,       PROFILE, true,        57,       "autoupdate",        ""],               // 7
  [false,          true,          true,          false,    true,      false,      PROFILE, true,        58,       "neededupdate",      ""],               // 8
  [false,          false,         true,          true,     false,     true,       PROFILE, true,        59,       "incompatible",      "disabled"],       // 9
  [false,          true,          true,          true,     true,      true,       PROFILE, true,        44,       "autoupdate",        "disabled"],       // 10
  [false,          true,          true,          true,     true,      false,      PROFILE, true,        45,       "neededupdate",      "disabled"],       // 11
  [true,           false,         false,         false,    false,     true,       PROFILE, false,       46,       "enabled",           ""],               // 12
  [true,           false,         false,         false,    true,      true,       PROFILE, false,       47,       "enabled",           ""],               // 13
  [true,           false,         false,         false,    true,      false,      PROFILE, false,       48,       "unneededupdate",    ""],               // 14

  // userDisabled and isActive cannot be true on startup

  [true,           true,          true,          false,    false,     true,       PROFILE, false,       49,       "incompatible",      ""],               // 15
  [true,           true,          true,          false,    true,      true,       PROFILE, false,       50,       "autoupdate",        ""],               // 16
  [true,           true,          true,          false,    true,      false,      PROFILE, false,       51,       "neededupdate",      ""],               // 17

  // userDisabled and isActive cannot be true on startup

  // Being in a different scope should make little difference except no updates are possible so don't exhaustively test each
  [false,          false,         false,         true,     true,      false,      USER,    false,       0,        "",                  "disabled"],       // 18
  [true,           true,          false,         false,    true,      false,      USER,    false,       1,        "enabled",           ""],               // 19
  [false,          true,          true,          true,     true,      false,      USER,    false,       2,        "incompatible",      "disabled"],       // 20
  [true,           true,          true,          false,    true,      false,      USER,    false,       3,        "incompatible",      ""],               // 21
  [false,          false,         false,         true,     true,      false,      SYSTEM,  false,       4,        "",                  "disabled"],       // 22
  [true,           true,          false,         false,    true,      false,      SYSTEM,  false,       5,        "enabled",           ""],               // 23
  [false,          true,          true,          true,     true,      false,      SYSTEM,  false,       6,        "incompatible",      "disabled"],       // 24
  [true,           true,          true,          false,    true,      false,      SYSTEM,  false,       7,        "incompatible",      ""],               // 25
  [false,          false,         false,         true,     true,      false,      APP,     false,       8,        "",                  "disabled"],       // 26
  [true,           true,          false,         false,    true,      false,      APP,     false,       9,        "enabled",           ""],               // 27
  [false,          true,          true,          true,     true,      false,      APP,     false,       10,       "incompatible",      "disabled"],       // 28
  [true,           true,          true,          false,    true,      false,      APP,     false,       11,       "incompatible",      ""],               // 29
];

function waitForView(aView, aCallback) {
  var view = gWin.document.getElementById(aView);
  if (view.parentNode.selectedPanel == view) {
    aCallback();
    return;
  }

  view.addEventListener("ViewChanged", function() {
    view.removeEventListener("ViewChanged", arguments.callee, false);
    aCallback();
  }, false);
}

function getString(aName) {
  if (!aName)
    return "";

  var strings = Services.strings.createBundle("chrome://mozapps/locale/extensions/selectAddons.properties");
  return strings.GetStringFromName("action." + aName);
}

function getSourceString(aSource) {
  if (!aSource)
    return "";

  var strings = Services.strings.createBundle("chrome://mozapps/locale/extensions/selectAddons.properties");
  switch (aSource) {
    case PROFILE:
      return strings.GetStringFromName("source.profile");
    case DIST:
      return strings.GetStringFromName("source.bundled");
    default:
      return strings.GetStringFromName("source.other");
  }
}

function test() {
  waitForExplicitFinish();

  gProvider = new MockProvider();

  // Set prefs for Distributed Extension Source tests.
  Services.prefs.setBoolPref("extensions.installedDistroAddon.test3@tests.mozilla.org", true);
  Services.prefs.setBoolPref("extensions.installedDistroAddon.test12@tests.mozilla.org", true);
  Services.prefs.setBoolPref("extensions.installedDistroAddon.test15@tests.mozilla.org", true);

  for (let pos in ADDONS) {
    let addonItem = ADDONS[pos];
    let addon = new MockAddon("test" + pos + "@tests.mozilla.org",
                              "Test Add-on " + pos, "extension");
    addon.version = "1.0";
    addon.userDisabled = addonItem[0];
    addon.appDisabled = addonItem[1];
    addon.isActive = addonItem[3];
    addon.applyBackgroundUpdates = addonItem[5] ? AddonManager.AUTOUPDATE_ENABLE
                                             : AddonManager.AUTOUPDATE_DISABLE;
    addon.scope = addonItem[6];

    // Remove the upgrade permission from non-profile add-ons
    if (addon.scope != AddonManager.SCOPE_PROFILE)
      addon._permissions -= AddonManager.PERM_CAN_UPGRADE;

    addon.findUpdates = function(aListener, aReason, aAppVersion, aPlatformVersion) {
      addon.appDisabled = addonItem[2];
      addon.isActive = addon.shouldBeActive;

      if (addonItem[4]) {
        var newAddon = new MockAddon(this.id, this.name, "extension");
        newAddon.version = "2.0";
        var install = new MockInstall(this.name, this.type, newAddon);
        install.existingAddon = this;
        aListener.onUpdateAvailable(this, install);
      }

      aListener.onUpdateFinished(this, AddonManager.UPDATE_STATUS_NO_ERROR);
    };

    gProvider.addAddon(addon);
  }

  gWin = Services.ww.openWindow(null,
                                "chrome://mozapps/content/extensions/selectAddons.xul",
                                "",
                                "chrome,centerscreen,dialog,titlebar",
                                null);
  waitForFocus(function() {
    waitForView("select", run_next_test);
  }, gWin);
}

function end_test() {
  gWin.close();
  finish();
}

// Minimal test for the checking UI
add_test(function checking_test() {
  // By the time we're here the progress bar should be full
  var progress = gWin.document.getElementById("checking-progress");
  is(progress.mode, "determined", "Should be a determined progress bar");
  is(progress.value, progress.max, "Should be at full progress");

  run_next_test();
});

// Tests that the selection UI behaves correctly
add_test(function selection_test() {
  function check_state() {
    var str = addon[keep.checked ? 9 : 10];
    var expected = getString(str);
    var showCheckbox = str == "neededupdate" || str == "unneededupdate";
    is(action.textContent, expected, "Action message should have the right text");
    is(!is_hidden(update), showCheckbox, "Checkbox should have the right visibility");
    is(is_hidden(action), showCheckbox, "Message should have the right visibility");
    if (showCheckbox)
      ok(update.checked, "Optional update checkbox should be checked");

    if (keep.checked) {
      is(row.hasAttribute("active"), !addon[2] || hasUpdate,
       "Add-on will be active if it isn't appDisabled or an update is available");

      if (showCheckbox) {
        info("Flipping update checkbox");
        EventUtils.synthesizeMouseAtCenter(update, { }, gWin);
        is(row.hasAttribute("active"), str == "unneededupdate",
           "If the optional update isn't needed then the add-on will still be active");

        info("Flipping update checkbox");
        EventUtils.synthesizeMouseAtCenter(update, { }, gWin);
        is(row.hasAttribute("active"), !addon[2] || hasUpdate,
         "Add-on will be active if it isn't appDisabled or an update is available");
      }
    }
    else {
      ok(!row.hasAttribute("active"), "Add-on won't be active when not keeping");

      if (showCheckbox) {
        info("Flipping update checkbox");
        EventUtils.synthesizeMouseAtCenter(update, { }, gWin);
        ok(!row.hasAttribute("active"),
           "Unchecking the update checkbox shouldn't make the add-on active");

        info("Flipping update checkbox");
        EventUtils.synthesizeMouseAtCenter(update, { }, gWin);
        ok(!row.hasAttribute("active"),
           "Re-checking the update checkbox shouldn't make the add-on active");
      }
    }
  }

  is(gWin.document.getElementById("view-deck").selectedPanel.id, "select",
     "Should be on the right view");

  var pos = 0;
  var scrollbox = gWin.document.getElementById("select-scrollbox");
  var scrollBoxObject = scrollbox.boxObject.QueryInterface(Ci.nsIScrollBoxObject);
  for (var row = gWin.document.getElementById("select-rows").firstChild; row; row = row.nextSibling) {
    // Ignore separators but increase the position by a large amount so we
    // can verify they were in the right place
    if (row.localName == "separator") {
      pos += 30;
      continue;
    }

    is(row._addon.type, "extension", "Should only be listing extensions");

    // Ignore non-test add-ons that may be present
    if (row.id.substr(-18) != "@tests.mozilla.org")
      continue;

    var id = parseInt(row.id.substring(4, row.id.length - 18));
    var addon = ADDONS[id];

    info("Testing add-on " + id);
    scrollBoxObject.ensureElementIsVisible(row);
    var keep = gWin.document.getAnonymousElementByAttribute(row, "anonid", "keep");
    var action = gWin.document.getAnonymousElementByAttribute(row, "class", "addon-action-message");
    var update = gWin.document.getAnonymousElementByAttribute(row, "anonid", "update");
    var source = gWin.document.getAnonymousElementByAttribute(row, "class", "addon-source");

    if (id == 3 || id == 12 || id == 15) {
      // Distro Installed To Profile
      is(source.textContent, getSourceString(DIST), "Source message should have the right text for Distributed Addons");
    } else {
      is(source.textContent, getSourceString(addon[6]), "Source message should have the right text");
    }

    // Non-profile add-ons don't appear to have updates since we won't install
    // them
    var hasUpdate = addon[4] && addon[6] == PROFILE;

    is(pos, addon[8], "Should have been in the right position");
    is(keep.checked, addon[7], "Keep checkbox should be in the right state");

    check_state();

    info("Flipping keep");
    EventUtils.synthesizeMouseAtCenter(keep, { }, gWin);
    is(keep.checked, !addon[7], "Keep checkbox should be in the right state");

    check_state();

    pos++;
  }

  is(pos, 60, "Should have seen the right number of add-ons");

  run_next_test();
});
