/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

function test() {
  waitForExplicitFinish();

  ok(PopupNotifications, "PopupNotifications object exists");
  ok(PopupNotifications.panel, "PopupNotifications panel exists");

  setup();
  goNext();
}

var tests = [
  // Popup Notifications main actions should catch exceptions from callbacks
  { id: "Test#1",
    run: function () {
      this.testNotif = new ErrorNotification();
      showNotification(this.testNotif);
    },
    onShown: function (popup) {
      checkPopup(popup, this.testNotif);
      triggerMainCommand(popup);
    },
    onHidden: function (popup) {
      ok(this.testNotif.mainActionClicked, "main action has been triggered");
    }
  },
  // Popup Notifications secondary actions should catch exceptions from callbacks
  { id: "Test#2",
    run: function () {
      this.testNotif = new ErrorNotification();
      showNotification(this.testNotif);
    },
    onShown: function (popup) {
      checkPopup(popup, this.testNotif);
      triggerSecondaryCommand(popup, 0);
    },
    onHidden: function (popup) {
      ok(this.testNotif.secondaryActionClicked, "secondary action has been triggered");
    }
  },
  // Existing popup notification shouldn't disappear when adding a dismissed notification
  { id: "Test#3",
    run: function () {
      this.notifyObj1 = new BasicNotification(this.id);
      this.notifyObj1.id += "_1";
      this.notifyObj1.anchorID = "default-notification-icon";
      this.notification1 = showNotification(this.notifyObj1);
    },
    onShown: function (popup) {
      // Now show a dismissed notification, and check that it doesn't clobber
      // the showing one.
      this.notifyObj2 = new BasicNotification(this.id);
      this.notifyObj2.id += "_2";
      this.notifyObj2.anchorID = "geo-notification-icon";
      this.notifyObj2.options.dismissed = true;
      this.notification2 = showNotification(this.notifyObj2);

      checkPopup(popup, this.notifyObj1);

      // check that both anchor icons are showing
      is(document.getElementById("default-notification-icon").getAttribute("showing"), "true",
         "notification1 anchor should be visible");
      is(document.getElementById("geo-notification-icon").getAttribute("showing"), "true",
         "notification2 anchor should be visible");

      dismissNotification(popup);
    },
    onHidden: function(popup) {
      this.notification1.remove();
      this.notification2.remove();
    }
  },
  // Showing should be able to modify the popup data
  { id: "Test#4",
    run: function() {
      this.notifyObj = new BasicNotification(this.id);
      let normalCallback = this.notifyObj.options.eventCallback;
      this.notifyObj.options.eventCallback = function (eventName) {
        if (eventName == "showing") {
          this.mainAction.label = "Alternate Label";
        }
        normalCallback.call(this, eventName);
      };
      showNotification(this.notifyObj);
    },
    onShown: function(popup) {
      // checkPopup checks for the matching label. Note that this assumes that
      // this.notifyObj.mainAction is the same as notification.mainAction,
      // which could be a problem if we ever decided to deep-copy.
      checkPopup(popup, this.notifyObj);
      triggerMainCommand(popup);
    },
    onHidden: function() { }
  },
  // Moving a tab to a new window should remove non-swappable notifications.
  { id: "Test#5",
    run: function() {
      gBrowser.selectedTab = gBrowser.addTab("about:blank");
      let notifyObj = new BasicNotification(this.id);
      showNotification(notifyObj);
      let win = gBrowser.replaceTabWithWindow(gBrowser.selectedTab);
      whenDelayedStartupFinished(win, function() {
        let anchor = win.document.getElementById("default-notification-icon");
        win.PopupNotifications._reshowNotifications(anchor);
        ok(win.PopupNotifications.panel.childNodes.length == 0,
           "no notification displayed in new window");
        ok(notifyObj.swappingCallbackTriggered, "the swapping callback was triggered");
        ok(notifyObj.removedCallbackTriggered, "the removed callback was triggered");
        win.close();
        goNext();
      });
    }
  },
  // Moving a tab to a new window should preserve swappable notifications.
  { id: "Test#6",
    run: function* () {
      let originalBrowser = gBrowser.selectedBrowser;
      let originalWindow = window;

      gBrowser.selectedTab = gBrowser.addTab("about:blank");
      let notifyObj = new BasicNotification(this.id);
      let originalCallback = notifyObj.options.eventCallback;
      notifyObj.options.eventCallback = function (eventName) {
        originalCallback(eventName);
        return eventName == "swapping";
      };

      let notification = showNotification(notifyObj);
      let win = gBrowser.replaceTabWithWindow(gBrowser.selectedTab);
      yield whenDelayedStartupFinished(win);

      yield new Promise(resolve => {
        let originalCallback = notification.options.eventCallback;
        notification.options.eventCallback = function (eventName) {
          originalCallback(eventName);
          if (eventName == "shown") {
            resolve();
          }
        };
        info("Showing the notification again");
        notification.reshow();
      });

      checkPopup(win.PopupNotifications.panel, notifyObj);
      ok(notifyObj.swappingCallbackTriggered, "the swapping callback was triggered");
      yield BrowserTestUtils.closeWindow(win);

      // These are the same checks that PopupNotifications.jsm makes before it
      // allows a notification to open. Do not go to the next test until we are
      // sure that its attempt to display a notification will not fail.
      yield BrowserTestUtils.waitForCondition(() => originalBrowser.docShellIsActive,
                                              "The browser should be active");
      let fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
      yield BrowserTestUtils.waitForCondition(() => fm.activeWindow == originalWindow,
                                              "The window should be active")

      goNext();
    }
  },
  // the hideNotNow option
  { id: "Test#7",
    run: function () {
      this.notifyObj = new BasicNotification(this.id);
      this.notifyObj.options.hideNotNow = true;
      this.notifyObj.mainAction.dismiss = true;
      this.notification = showNotification(this.notifyObj);
    },
    onShown: function (popup) {
      // checkPopup verifies that the Not Now item is hidden, and that no separator is added.
      checkPopup(popup, this.notifyObj);
      triggerMainCommand(popup);
    },
    onHidden: function (popup) {
      this.notification.remove();
    }
  },
  // the main action callback can keep the notification.
  { id: "Test#8",
    run: function () {
      this.notifyObj = new BasicNotification(this.id);
      this.notifyObj.mainAction.dismiss = true;
      this.notification = showNotification(this.notifyObj);
    },
    onShown: function (popup) {
      checkPopup(popup, this.notifyObj);
      triggerMainCommand(popup);
    },
    onHidden: function (popup) {
      ok(this.notifyObj.dismissalCallbackTriggered, "dismissal callback was triggered");
      ok(!this.notifyObj.removedCallbackTriggered, "removed callback wasn't triggered");
      this.notification.remove();
    }
  },
  // a secondary action callback can keep the notification.
  { id: "Test#9",
    run: function () {
      this.notifyObj = new BasicNotification(this.id);
      this.notifyObj.secondaryActions[0].dismiss = true;
      this.notification = showNotification(this.notifyObj);
    },
    onShown: function (popup) {
      checkPopup(popup, this.notifyObj);
      triggerSecondaryCommand(popup, 0);
    },
    onHidden: function (popup) {
      ok(this.notifyObj.dismissalCallbackTriggered, "dismissal callback was triggered");
      ok(!this.notifyObj.removedCallbackTriggered, "removed callback wasn't triggered");
      this.notification.remove();
    }
  },
  // returning true in the showing callback should dismiss the notification.
  { id: "Test#10",
    run: function() {
      let notifyObj = new BasicNotification(this.id);
      let originalCallback = notifyObj.options.eventCallback;
      notifyObj.options.eventCallback = function (eventName) {
        originalCallback(eventName);
        return eventName == "showing";
      };

      let notification = showNotification(notifyObj);
      ok(notifyObj.showingCallbackTriggered, "the showing callback was triggered");
      ok(!notifyObj.shownCallbackTriggered, "the shown callback wasn't triggered");
      notification.remove();
      goNext();
    }
  },
  // panel updates should fire the showing and shown callbacks again.
  { id: "Test#11",
    run: function() {
      this.notifyObj = new BasicNotification(this.id);
      this.notification = showNotification(this.notifyObj);
    },
    onShown: function (popup) {
      checkPopup(popup, this.notifyObj);

      this.notifyObj.showingCallbackTriggered = false;
      this.notifyObj.shownCallbackTriggered = false;

      // Force an update of the panel. This is typically called
      // automatically when receiving 'activate' or 'TabSelect' events,
      // but from a setTimeout, which is inconvenient for the test.
      PopupNotifications._update();

      checkPopup(popup, this.notifyObj);

      this.notification.remove();
    },
    onHidden: function() { }
  },
  // A first dismissed notification shouldn't stop _update from showing a second notification
  { id: "Test#12",
    run: function () {
      this.notifyObj1 = new BasicNotification(this.id);
      this.notifyObj1.id += "_1";
      this.notifyObj1.anchorID = "default-notification-icon";
      this.notifyObj1.options.dismissed = true;
      this.notification1 = showNotification(this.notifyObj1);

      this.notifyObj2 = new BasicNotification(this.id);
      this.notifyObj2.id += "_2";
      this.notifyObj2.anchorID = "geo-notification-icon";
      this.notifyObj2.options.dismissed = true;
      this.notification2 = showNotification(this.notifyObj2);

      this.notification2.dismissed = false;
      PopupNotifications._update();
    },
    onShown: function (popup) {
      checkPopup(popup, this.notifyObj2);
      this.notification1.remove();
      this.notification2.remove();
    },
    onHidden: function(popup) { }
  },
  // The anchor icon should be shown for notifications in background windows.
  { id: "Test#13",
    run: function() {
      let notifyObj = new BasicNotification(this.id);
      notifyObj.options.dismissed = true;
      let win = gBrowser.replaceTabWithWindow(gBrowser.addTab("about:blank"));
      whenDelayedStartupFinished(win, function() {
        showNotification(notifyObj);
        let anchor = document.getElementById("default-notification-icon");
        is(anchor.getAttribute("showing"), "true", "the anchor is shown");
        win.close();
        goNext();
      });
    }
  }
];
