Bug 1357641 - Part 2: Add the browser_onboarding_notification.js test, r?mossop draft
authorFischer.json <fischer.json@gmail.com>
Fri, 16 Jun 2017 17:44:06 +0800
changeset 598821 140c60b97fd163e10188de5ebfb4f57366cc4d2a
parent 597968 c0151a9808812bdf3ae72268a9eaaddd804f4923
child 599665 53ecd719396af5f2d2bf50df431d0e62a0af5e01
push id65325
push userbmo:fliu@mozilla.com
push dateThu, 22 Jun 2017 07:55:50 +0000
reviewersmossop
bugs1357641
milestone56.0a1
Bug 1357641 - Part 2: Add the browser_onboarding_notification.js test, r?mossop MozReview-Commit-ID: 8kEDXaQ0zqK
browser/base/content/test/newtab/browser_newtab_focus.js
browser/extensions/onboarding/test/browser/browser.ini
browser/extensions/onboarding/test/browser/browser_onboarding_hide_tours.js
browser/extensions/onboarding/test/browser/browser_onboarding_notification.js
browser/extensions/onboarding/test/browser/head.js
--- a/browser/base/content/test/newtab/browser_newtab_focus.js
+++ b/browser/base/content/test/newtab/browser_newtab_focus.js
@@ -2,47 +2,80 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /*
  * These tests make sure that focusing the 'New Tab Page' works as expected.
  */
 add_task(async function() {
   await pushPrefs(["accessibility.tabfocus", 7]);
 
+  // When the onboarding component is enabled, it would inject extra tour notification into
+  // the newtab page so there would be 2 more notification close button and action button
+  let onbardingEnabled = AppConstants.NIGHTLY_BUILD && Services.prefs.getBoolPref("browser.onboarding.enabled");
+
   // Focus count in new tab page.
   // 30 = 9 * 3 + 3 = 9 sites, each with link, pin and remove buttons; search
   // bar; search button; and toggle button. Additionaly there may or may not be
   // a scroll bar caused by fix to 1180387, which will eat an extra focus
   let FOCUS_COUNT = 30;
 
   // Create a new tab page.
   await setLinks("0,1,2,3,4,5,6,7,8");
   setPinnedLinks("");
 
-  await addNewTabPageTab();
+  let tab = await addNewTabPageTab();
+  if (onbardingEnabled) {
+    FOCUS_COUNT += 2;
+    await promiseTourNotificationOpened(tab.linkedBrowser);
+  }
   gURLBar.focus();
-
   // Count the focus with the enabled page.
   countFocus(FOCUS_COUNT);
-
   // Disable page and count the focus with the disabled page.
   NewTabUtils.allPages.enabled = false;
 
-  countFocus(4);
+  let expectedCount = 4;
+  if (onbardingEnabled) {
+    expectedCount += 2;
+  }
+  countFocus(expectedCount);
 
   NewTabUtils.allPages.enabled = true;
 });
 
 /**
  * Focus the urlbar and count how many focus stops to return again to the urlbar.
  */
 function countFocus(aExpectedCount) {
   let focusCount = 0;
   do {
     EventUtils.synthesizeKey("VK_TAB", {});
     if (document.activeElement == gBrowser.selectedBrowser) {
       focusCount++;
     }
   } while (document.activeElement != gURLBar.inputField);
-
   ok(focusCount == aExpectedCount || focusCount == (aExpectedCount + 1),
      "Validate focus count in the new tab page.");
 }
+
+/**
+ * Wait for the onboarding tour notification opens
+ */
+function promiseTourNotificationOpened(browser) {
+  let condition = () => {
+    return ContentTask.spawn(browser, {}, function() {
+      return new Promise(resolve => {
+        let bar = content.document.querySelector("#onboarding-notification-bar");
+        if (bar && bar.classList.contains("onboarding-opened") && bar.dataset.cssTransition == "end") {
+          resolve(true);
+          return;
+        }
+        resolve(false);
+      });
+    })
+  };
+  return BrowserTestUtils.waitForCondition(
+    condition,
+    "Should open tour notification",
+    100,
+    30
+  );
+}
--- a/browser/extensions/onboarding/test/browser/browser.ini
+++ b/browser/extensions/onboarding/test/browser/browser.ini
@@ -1,5 +1,6 @@
 [DEFAULT]
 support-files =
   head.js
 
 [browser_onboarding_hide_tours.js]
+[browser_onboarding_notification.js]
--- a/browser/extensions/onboarding/test/browser/browser_onboarding_hide_tours.js
+++ b/browser/extensions/onboarding/test/browser/browser_onboarding_hide_tours.js
@@ -1,16 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-const ABOUT_HOME_URL = "about:home";
-const ABOUT_NEWTAB_URL = "about:newtab";
-
 function assertOnboardingDestroyed(browser) {
   return ContentTask.spawn(browser, {}, function() {
     let expectedRemovals = [
       "#onboarding-overlay",
       "#onboarding-overlay-icon"
     ];
     for (let selector of expectedRemovals) {
       let removal = content.document.querySelector(selector);
new file mode 100644
--- /dev/null
+++ b/browser/extensions/onboarding/test/browser/browser_onboarding_notification.js
@@ -0,0 +1,109 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function test_show_tour_notifications_in_order() {
+  resetOnboardingDefaultState();
+  await SpecialPowers.pushPrefEnv({set: [["browser.onboarding.enabled", true]]});
+
+  let tourIds = TOUR_IDs;
+  let tab = null;
+  let targetTourId = null;
+  let reloadPromise = null;
+  let expectedPrefUpdate = null;
+  for (let i = 0; i < tourIds.length; ++i) {
+    expectedPrefUpdate = promisePrefUpdated("browser.onboarding.notification.lastPrompted", tourIds[i]);
+    if (tab) {
+      reloadPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+      tab.linkedBrowser.reload();
+      await reloadPromise;
+    } else {
+      tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
+      await BrowserTestUtils.loadURI(tab.linkedBrowser, ABOUT_NEWTAB_URL);
+    }
+    await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
+    await promiseTourNotificationOpened(tab.linkedBrowser);
+    targetTourId = await getCurrentNotificationTargetTourId(tab.linkedBrowser);
+    is(targetTourId, tourIds[i], "Should show tour notifications in order");
+    await expectedPrefUpdate;
+  }
+
+  expectedPrefUpdate = promisePrefUpdated("browser.onboarding.notification.lastPrompted", tourIds[0]);
+  reloadPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+  tab.linkedBrowser.reload();
+  await reloadPromise;
+  await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
+  await promiseTourNotificationOpened(tab.linkedBrowser);
+  targetTourId = await getCurrentNotificationTargetTourId(tab.linkedBrowser);
+  is(targetTourId, tourIds[0], "Should loop back to the 1st tour notification after showing all notifications");
+  await expectedPrefUpdate;
+  await BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function test_open_target_tour_from_notification() {
+  resetOnboardingDefaultState();
+  await SpecialPowers.pushPrefEnv({set: [["browser.onboarding.enabled", true]]});
+
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
+  await BrowserTestUtils.loadURI(tab.linkedBrowser, ABOUT_NEWTAB_URL);
+  await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
+  await promiseTourNotificationOpened(tab.linkedBrowser);
+  let targetTourId = await getCurrentNotificationTargetTourId(tab.linkedBrowser);
+  await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-notification-action-btn", {}, tab.linkedBrowser);
+  await promiseOnboardingOverlayOpened(tab.linkedBrowser);
+  let { activeNavItemId, activePageId } = await getCurrentActiveTour(tab.linkedBrowser);
+
+  is(targetTourId, activeNavItemId, "Should navigate to the target tour item.");
+  is(`${targetTourId}-page`, activePageId, "Should display the target tour page.");
+  await BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function test_not_show_notification_for_completed_tour() {
+  resetOnboardingDefaultState();
+  await SpecialPowers.pushPrefEnv({set: [["browser.onboarding.enabled", true]]});
+
+  let tourIds = TOUR_IDs;
+  // Make only the last tour uncompleted
+  let lastTourId = tourIds[tourIds.length - 1];
+  for (let id of tourIds) {
+    if (id != lastTourId) {
+      setTourCompletedState(id, true);
+    }
+  }
+
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
+  await BrowserTestUtils.loadURI(tab.linkedBrowser, ABOUT_NEWTAB_URL);
+  await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
+  await promiseTourNotificationOpened(tab.linkedBrowser);
+  let targetTourId = await getCurrentNotificationTargetTourId(tab.linkedBrowser);
+  is(targetTourId, lastTourId, "Should not show notification for completed tour");
+  await BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function test_skip_notification_for_completed_tour() {
+  resetOnboardingDefaultState();
+  await SpecialPowers.pushPrefEnv({set: [["browser.onboarding.enabled", true]]});
+
+  let tourIds = TOUR_IDs;
+  // Make only 2nd tour completed
+  await setTourCompletedState(tourIds[1], true);
+
+  // Test show notification for the 1st tour
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
+  await BrowserTestUtils.loadURI(tab.linkedBrowser, ABOUT_NEWTAB_URL);
+  await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
+  await promiseTourNotificationOpened(tab.linkedBrowser);
+  let targetTourId = await getCurrentNotificationTargetTourId(tab.linkedBrowser);
+  is(targetTourId, tourIds[0], "Should show notification for incompleted tour");
+
+  // Test skip the 2nd tour and show notification for the 3rd tour
+  let reloadPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+  tab.linkedBrowser.reload();
+  await reloadPromise;
+  await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
+  await promiseTourNotificationOpened(tab.linkedBrowser);
+  targetTourId = await getCurrentNotificationTargetTourId(tab.linkedBrowser);
+  is(targetTourId, tourIds[2], "Should skip notification for the completed 2nd tour");
+  await BrowserTestUtils.removeTab(tab);
+});
--- a/browser/extensions/onboarding/test/browser/head.js
+++ b/browser/extensions/onboarding/test/browser/head.js
@@ -1,12 +1,35 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
-Cu.import("resource://gre/modules/Preferences.jsm");
+let { Preferences } = Cu.import("resource://gre/modules/Preferences.jsm", {});
+
+const ABOUT_HOME_URL = "about:home";
+const ABOUT_NEWTAB_URL = "about:newtab";
+const TOUR_IDs = [
+  "onboarding-tour-private-browsing",
+  "onboarding-tour-addons",
+  "onboarding-tour-customize",
+  "onboarding-tour-search",
+  "onboarding-tour-default-browser",
+];
+
+function resetOnboardingDefaultState() {
+  // All the prefs should be reset to the default states
+  // and no need to revert back so we don't use `SpecialPowers.pushPrefEnv` here.
+  Preferences.set("browser.onboarding.hidden", false);
+  Preferences.set("browser.onboarding.notification.finished", false);
+  Preferences.set("browser.onboarding.notification.lastPrompted", "");
+  TOUR_IDs.forEach(id => Preferences.set(`browser.onboarding.tour.${id}.completed`, false));
+}
+
+function setTourCompletedState(tourId, state) {
+  Preferences.set(`browser.onboarding.tour.${tourId}.completed`, state);
+}
 
 function promiseOnboardingOverlayLoaded(browser) {
   // The onboarding overlay is init inside window.requestIdleCallback, not immediately,
   // so we use check conditions here.
   let condition = () => {
     return ContentTask.spawn(browser, {}, function() {
       return new Promise(resolve => {
         let doc = content && content.document;
@@ -52,8 +75,59 @@ function promisePrefUpdated(name, expect
     let onUpdate = actualValue => {
       Preferences.ignore(name, onUpdate);
       is(expectedValue, actualValue, `Should update the pref of ${name}`);
       resolve();
     };
     Preferences.observe(name, onUpdate);
   });
 }
+
+function promiseTourNotificationOpened(browser) {
+  let condition = () => {
+    return ContentTask.spawn(browser, {}, function() {
+      return new Promise(resolve => {
+        let bar = content.document.querySelector("#onboarding-notification-bar");
+        if (bar && bar.classList.contains("onboarding-opened") && bar.dataset.cssTransition == "end") {
+          resolve(true);
+          return;
+        }
+        resolve(false);
+      });
+    })
+  };
+  return BrowserTestUtils.waitForCondition(
+    condition,
+    "Should open tour notification",
+    100,
+    30
+  );
+}
+
+function getCurrentNotificationTargetTourId(browser) {
+  return ContentTask.spawn(browser, {}, function() {
+    let bar = content.document.querySelector("#onboarding-notification-bar");
+    return bar ? bar.dataset.targetTourId : null;
+  });
+}
+
+function getCurrentActiveTour(browser) {
+  return ContentTask.spawn(browser, {}, function() {
+    let list = content.document.querySelector("#onboarding-tour-list");
+    let items = list.querySelectorAll(".onboarding-tour-item");
+    let activeNavItemId = null;
+    for (let item of items) {
+      if (item.classList.contains("onboarding-active")) {
+        activeNavItemId = item.id;
+        break;
+      }
+    }
+    let activePageId = null;
+    let pages = content.document.querySelectorAll(".onboarding-tour-page");
+    for (let page of pages) {
+      if (page.style.display != "none") {
+        activePageId = page.id;
+        break;
+      }
+    }
+    return { activeNavItemId, activePageId };
+  });
+}