Bug 1357641 - Part 2: Add the browser_onboarding_notification.js test, r?mossop
MozReview-Commit-ID: 8kEDXaQ0zqK
--- 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 };
+ });
+}