diff options
Diffstat (limited to 'browser/components/urlbar/tests/browser-tips/browser_searchTips.js')
-rw-r--r-- | browser/components/urlbar/tests/browser-tips/browser_searchTips.js | 657 |
1 files changed, 657 insertions, 0 deletions
diff --git a/browser/components/urlbar/tests/browser-tips/browser_searchTips.js b/browser/components/urlbar/tests/browser-tips/browser_searchTips.js new file mode 100644 index 0000000000..ff592bc831 --- /dev/null +++ b/browser/components/urlbar/tests/browser-tips/browser_searchTips.js @@ -0,0 +1,657 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests the Search Tips feature, which displays a prompt to use the Urlbar on +// the newtab page and on the user's default search engine's homepage. +// Specifically, it tests that the Tips appear when they should be appearing. +// This doesn't test the max-shown-count limit or the restriction on tips when +// we show the default browser prompt because those require restarting the +// browser. + +"use strict"; + +ChromeUtils.defineESModuleGetters(this, { + AppMenuNotifications: "resource://gre/modules/AppMenuNotifications.sys.mjs", + ProfileAge: "resource://gre/modules/ProfileAge.sys.mjs", + PromiseUtils: "resource://gre/modules/PromiseUtils.sys.mjs", + UrlbarPrefs: "resource:///modules/UrlbarPrefs.sys.mjs", + UrlbarProviderSearchTips: + "resource:///modules/UrlbarProviderSearchTips.sys.mjs", +}); + +XPCOMUtils.defineLazyModuleGetters(this, { + HttpServer: "resource://testing-common/httpd.js", +}); + +// These should match the same consts in UrlbarProviderSearchTips.jsm. +const MAX_SHOWN_COUNT = 4; +const LAST_UPDATE_THRESHOLD_MS = 24 * 60 * 60 * 1000; + +// We test some of the bigger Google domains. +const GOOGLE_DOMAINS = [ + "www.google.com", + "www.google.ca", + "www.google.co.uk", + "www.google.com.au", + "www.google.co.nz", +]; + +// In order for the persist tip to appear, the scheme of the +// search engine has to be the same as the scheme of the SERP url. +// withDNSRedirect() loads an http: url while the searchform +// of the default engine uses https. To enable the search term +// to be shown, we use the Example engine because it doesn't require +// a redirect. +const SEARCH_SERP_URL = "https://example.com/?q=chocolate"; + +add_setup(async function () { + await PlacesUtils.history.clear(); + await PlacesUtils.bookmarks.eraseEverything(); + + await SpecialPowers.pushPrefEnv({ + set: [ + [ + `browser.urlbar.tipShownCount.${UrlbarProviderSearchTips.TIP_TYPE.ONBOARD}`, + 0, + ], + [ + `browser.urlbar.tipShownCount.${UrlbarProviderSearchTips.TIP_TYPE.PERSIST}`, + 0, + ], + [ + `browser.urlbar.tipShownCount.${UrlbarProviderSearchTips.TIP_TYPE.REDIRECT}`, + 0, + ], + ], + }); + + // Write an old profile age so tips are actually shown. + let age = await ProfileAge(); + let originalTimes = age._times; + let date = Date.now() - LAST_UPDATE_THRESHOLD_MS - 30000; + age._times = { created: date, firstUse: date }; + await age.writeTimes(); + + // Remove update history and the current active update so tips are shown. + let updateRootDir = Services.dirsvc.get("UpdRootD", Ci.nsIFile); + let updatesFile = updateRootDir.clone(); + updatesFile.append("updates.xml"); + let activeUpdateFile = updateRootDir.clone(); + activeUpdateFile.append("active-update.xml"); + try { + updatesFile.remove(false); + } catch (e) {} + try { + activeUpdateFile.remove(false); + } catch (e) {} + + let defaultEngine = await Services.search.getDefault(); + let defaultEngineName = defaultEngine.name; + Assert.equal(defaultEngineName, "Google", "Default engine should be Google."); + + // Add a mock engine so we don't hit the network loading the SERP. + await SearchTestUtils.installSearchExtension(); + + registerCleanupFunction(async () => { + let age2 = await ProfileAge(); + age2._times = originalTimes; + await age2.writeTimes(); + await setDefaultEngine(defaultEngineName); + resetSearchTipsProvider(); + }); +}); + +// The onboarding tip should be shown on about:newtab. +add_task(async function newtab() { + await checkTab( + window, + "about:newtab", + UrlbarProviderSearchTips.TIP_TYPE.ONBOARD + ); +}); + +// The onboarding tip should be shown on about:home. +add_task(async function home() { + await checkTab( + window, + "about:home", + UrlbarProviderSearchTips.TIP_TYPE.ONBOARD + ); +}); + +// The redirect tip should be shown for www.google.com when it's the default +// engine. +add_task(async function google() { + await setDefaultEngine("Google"); + for (let domain of GOOGLE_DOMAINS) { + await withDNSRedirect(domain, "/", async url => { + await checkTab(window, url, UrlbarProviderSearchTips.TIP_TYPE.REDIRECT); + }); + } +}); + +// The redirect tip should be shown for www.google.com/webhp when it's the +// default engine. +add_task(async function googleWebhp() { + await setDefaultEngine("Google"); + for (let domain of GOOGLE_DOMAINS) { + await withDNSRedirect(domain, "/webhp", async url => { + await checkTab(window, url, UrlbarProviderSearchTips.TIP_TYPE.REDIRECT); + }); + } +}); + +// The redirect tip should be shown for the Google homepage when query strings +// are appended. +add_task(async function googleQueryString() { + await setDefaultEngine("Google"); + for (let domain of GOOGLE_DOMAINS) { + await withDNSRedirect(domain, "/webhp", async url => { + await checkTab( + window, + `${url}?hl=en`, + UrlbarProviderSearchTips.TIP_TYPE.REDIRECT + ); + }); + } +}); + +// The redirect tip should not be shown on Google results pages. +add_task(async function googleResults() { + await setDefaultEngine("Google"); + for (let domain of GOOGLE_DOMAINS) { + await withDNSRedirect(domain, "/search", async url => { + await checkTab( + window, + `${url}?q=firefox`, + UrlbarProviderSearchTips.TIP_TYPE.NONE + ); + }); + } +}); + +// The redirect tip should not be shown for www.google.com when it's not the +// default engine. +add_task(async function googleNotDefault() { + await setDefaultEngine("Bing"); + for (let domain of GOOGLE_DOMAINS) { + await withDNSRedirect(domain, "/", async url => { + await checkTab(window, url, UrlbarProviderSearchTips.TIP_TYPE.NONE); + }); + } +}); + +// The redirect tip should not be shown for www.google.com/webhp when it's not +// the default engine. +add_task(async function googleWebhpNotDefault() { + await setDefaultEngine("Bing"); + for (let domain of GOOGLE_DOMAINS) { + await withDNSRedirect(domain, "/webhp", async url => { + await checkTab(window, url, UrlbarProviderSearchTips.TIP_TYPE.NONE); + }); + } +}); + +// The redirect tip should be shown for www.bing.com when it's the default +// engine. +add_task(async function bing() { + await setDefaultEngine("Bing"); + await withDNSRedirect("www.bing.com", "/", async url => { + await checkTab(window, url, UrlbarProviderSearchTips.TIP_TYPE.REDIRECT); + }); +}); + +// The redirect tip should be shown on the Bing homepage even when Bing appends +// query strings. +add_task(async function bingQueryString() { + await setDefaultEngine("Bing"); + await withDNSRedirect("www.bing.com", "/", async url => { + await checkTab( + window, + `${url}?toWww=1`, + UrlbarProviderSearchTips.TIP_TYPE.REDIRECT + ); + }); +}); + +// The redirect tip should not be shown on Bing results pages. +add_task(async function bingResults() { + await setDefaultEngine("Bing"); + await withDNSRedirect("www.bing.com", "/search", async url => { + await checkTab( + window, + `${url}?q=firefox`, + UrlbarProviderSearchTips.TIP_TYPE.NONE + ); + }); +}); + +// The redirect tip should not be shown for www.bing.com when it's not the +// default engine. +add_task(async function bingNotDefault() { + await setDefaultEngine("Google"); + await withDNSRedirect("www.bing.com", "/", async url => { + await checkTab(window, url, UrlbarProviderSearchTips.TIP_TYPE.NONE); + }); +}); + +// The redirect tip should be shown for duckduckgo.com when it's the default +// engine. +add_task(async function ddg() { + await setDefaultEngine("DuckDuckGo"); + await withDNSRedirect("duckduckgo.com", "/", async url => { + await checkTab(window, url, UrlbarProviderSearchTips.TIP_TYPE.REDIRECT); + }); +}); + +// The redirect tip should be shown for start.duckduckgo.com when it's the +// default engine. +add_task(async function ddgStart() { + await setDefaultEngine("DuckDuckGo"); + await withDNSRedirect("start.duckduckgo.com", "/", async url => { + await checkTab(window, url, UrlbarProviderSearchTips.TIP_TYPE.REDIRECT); + }); +}); + +// The redirect tip should not be shown for duckduckgo.com when it's not the +// default engine. +add_task(async function ddgNotDefault() { + await setDefaultEngine("Google"); + await withDNSRedirect("duckduckgo.com", "/", async url => { + await checkTab(window, url, UrlbarProviderSearchTips.TIP_TYPE.NONE); + }); +}); + +// The redirect tip should not be shown for start.duckduckgo.com when it's not +// the default engine. +add_task(async function ddgStartNotDefault() { + await setDefaultEngine("Google"); + await withDNSRedirect("start.duckduckgo.com", "/", async url => { + await checkTab(window, url, UrlbarProviderSearchTips.TIP_TYPE.NONE); + }); +}); + +// The redirect tip should not be shown for duckduckgo.com/?q=foo, the search +// results page, which happens to have the same domain and path as the home +// page. +add_task(async function ddgSearchResultsPage() { + await setDefaultEngine("DuckDuckGo"); + await withDNSRedirect("duckduckgo.com", "/", async url => { + await checkTab( + window, + `${url}?q=test`, + UrlbarProviderSearchTips.TIP_TYPE.NONE + ); + }); +}); + +// The redirect tip should not be shown on a non-engine page. +add_task(async function nonEnginePage() { + await checkTab( + window, + "http://example.com/", + UrlbarProviderSearchTips.TIP_TYPE.NONE + ); +}); + +// The persist tip should show on default SERPs. +// This test also has an implied check that the SERP +// is receiving an originalURI. +// This is because the page the test is attempting to load +// will differ from the page that's actually loaded due to +// the DNS redirect. +add_task(async function persistTipOnDefault() { + await setDefaultEngine("Example"); + await SpecialPowers.pushPrefEnv({ + set: [["browser.urlbar.showSearchTerms.featureGate", true]], + }); + await checkTab( + window, + SEARCH_SERP_URL, + UrlbarProviderSearchTips.TIP_TYPE.PERSIST + ); + await SpecialPowers.popPrefEnv(); +}); + +// The persist tip should not show on non-default SERPs. +add_task(async function noPersistTipOnNonDefault() { + await setDefaultEngine("DuckDuckGo"); + await SpecialPowers.pushPrefEnv({ + set: [["browser.urlbar.showSearchTerms.featureGate", true]], + }); + await checkTab( + window, + SEARCH_SERP_URL, + UrlbarProviderSearchTips.TIP_TYPE.NONE + ); + await SpecialPowers.popPrefEnv(); +}); + +// The persist tip should only show up once a session. +add_task(async function persistTipOnceOnDefaultSerp() { + await setDefaultEngine("Example"); + await SpecialPowers.pushPrefEnv({ + set: [["browser.urlbar.showSearchTerms.featureGate", true]], + }); + await checkTab( + window, + SEARCH_SERP_URL, + UrlbarProviderSearchTips.TIP_TYPE.PERSIST + ); + await checkTab( + window, + SEARCH_SERP_URL, + UrlbarProviderSearchTips.TIP_TYPE.NONE + ); + await SpecialPowers.popPrefEnv(); +}); + +// The persist tip should not show in a window +// with a selected tab containing a non-SERP url. +add_task(async function noPersistTipInWindowWithNonSerpTab() { + await setDefaultEngine("Example"); + await SpecialPowers.pushPrefEnv({ + set: [["browser.urlbar.showSearchTerms.featureGate", true]], + }); + + // Create a new window for the SERP to be loaded into. + let newWindow = await BrowserTestUtils.openNewBrowserWindow(); + + // Focus on the original window. + window.focus(); + await waitForBrowserWindowActive(window); + + // Load the SERP in the new window to initiate a background load. + let browserLoadedPromise = BrowserTestUtils.browserLoaded( + newWindow.gBrowser.selectedBrowser, + false, + SEARCH_SERP_URL + ); + BrowserTestUtils.loadURIString( + newWindow.gBrowser.selectedBrowser, + SEARCH_SERP_URL + ); + await browserLoadedPromise; + + // Wait longer than the persist tip delay to check that the search tip + // doesn't show on the non-SERP tab. + await new Promise(resolve => + // eslint-disable-next-line mozilla/no-arbitrary-setTimeout + setTimeout(resolve, UrlbarProviderSearchTips.SHOW_PERSIST_TIP_DELAY_MS * 2) + ); + Assert.ok(!window.gURLBar.view.isOpen); + + // Clean up. + await BrowserTestUtils.closeWindow(newWindow); + await SpecialPowers.popPrefEnv(); + resetSearchTipsProvider(); +}); + +// Tips should be shown at most once per session regardless of their type. +add_task(async function oncePerSession() { + await setDefaultEngine("Google"); + await checkTab( + window, + "about:newtab", + UrlbarProviderSearchTips.TIP_TYPE.ONBOARD, + false + ); + await checkTab( + window, + "about:newtab", + UrlbarProviderSearchTips.TIP_TYPE.NONE, + false + ); + await withDNSRedirect("www.google.com", "/", async url => { + await checkTab(window, url, UrlbarProviderSearchTips.TIP_TYPE.NONE); + }); + await setDefaultEngine("Example"); + await checkTab( + window, + SEARCH_SERP_URL, + UrlbarProviderSearchTips.TIP_TYPE.NONE + ); +}); + +// The one-off search buttons should not be shown when +// a search tip is shown even though the search string is empty. +add_task(async function shortcut_buttons_with_tip() { + await checkTab( + window, + "about:newtab", + UrlbarProviderSearchTips.TIP_TYPE.ONBOARD + ); +}); + +// Don't show the persist search tip when the browser loads +// a different page from the page the tip was supposed to show on. +add_task(async function noSearchTipWhileAnotherPageLoads() { + await setDefaultEngine("Example"); + await SpecialPowers.pushPrefEnv({ + set: [["browser.urlbar.showSearchTerms.featureGate", true]], + }); + + // Create a slow endpoint. + const SLOW_PAGE = + getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "http://www.example.com" + ) + "slow-page.sjs"; + + let tab = await BrowserTestUtils.openNewForegroundTab({ + gBrowser, + url: SEARCH_SERP_URL, + }); + + // Load a slow URI to cause an onStateChange event but + // not an onLocationChange event. + BrowserTestUtils.loadURIString(tab.linkedBrowser, SLOW_PAGE); + + // Wait roughly for the amount of time it would take for the + // persist search tip to show. + await new Promise(resolve => + // eslint-disable-next-line mozilla/no-arbitrary-setTimeout + setTimeout(resolve, UrlbarProviderSearchTips.SHOW_PERSIST_TIP_DELAY_MS * 2) + ); + + // Check the search tip didn't show while the page was loading. + Assert.equal( + UrlbarPrefs.get( + `tipShownCount.${UrlbarProviderSearchTips.TIP_TYPE.PERSIST}` + ), + 0, + "The shownCount pref should be 0." + ); + + Assert.equal(false, window.gURLBar.view.isOpen, "Urlbar should be closed."); + + // Clean up. + await SpecialPowers.popPrefEnv(); + resetSearchTipsProvider(); + BrowserTestUtils.removeTab(tab); +}); + +// Show the persist search tip when the browser is still loading +// resources from the page the tip is supposed to show on. +add_task(async function searchTipWhilePageLoads() { + await SpecialPowers.pushPrefEnv({ + set: [["browser.urlbar.showSearchTerms.featureGate", true]], + }); + + // Create a search engine endpoint that will still + // be loading resources on the page load. + const SLOW_PAGE = + getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "https://www.example.com" + ) + "slow-page.html"; + + await SearchTestUtils.installSearchExtension({ + name: "Slow Engine", + search_url: SLOW_PAGE, + search_url_get_params: "search={searchTerms}", + }); + await setDefaultEngine("Slow Engine"); + + let engine = Services.search.getEngineByName("Slow Engine"); + let [expectedSearchUrl] = UrlbarUtils.getSearchQueryUrl(engine, "chocolate"); + + // Load a slow SERP. + await checkTab( + window, + expectedSearchUrl, + UrlbarProviderSearchTips.TIP_TYPE.PERSIST + ); + + // Clean up. + await SpecialPowers.popPrefEnv(); + resetSearchTipsProvider(); +}); + +// Search tips modify the userTypedValue of a tab. The next time +// the pageproxystate is updated, the existence of the userTypedValue +// can change the pageproxystate. In the case of the Persist Search Tip, +// we don't want to change the pageproxystate while the Urlbar is non-focused, +// so check that when an event causes the pageproxystate to update +// (e.g. a SERP pushing state), the pageproxystate remains the same. +add_task(async function persistSearchTipAfterPushState() { + await setDefaultEngine("Example"); + await SpecialPowers.pushPrefEnv({ + set: [["browser.urlbar.showSearchTerms.featureGate", true]], + }); + + let tab = await BrowserTestUtils.openNewForegroundTab({ + gBrowser, + url: SEARCH_SERP_URL, + }); + + // Ensure the search tip is visible. + await checkTip(window, UrlbarProviderSearchTips.TIP_TYPE.PERSIST, false); + Assert.equal( + gURLBar.getAttribute("pageproxystate"), + "valid", + "Urlbar is should be in a valid pageproxystate." + ); + + // Mock the default SERP using the History API on an exising website. + // This is to trigger another call to setURI. + await SpecialPowers.spawn(tab.linkedBrowser, [SEARCH_SERP_URL], async url => { + content.history.pushState({}, "", url); + }); + + Assert.equal( + gURLBar.getAttribute("pageproxystate"), + "valid", + "Urlbar is should be in a valid pageproxystate." + ); + + // Clean up. + await SpecialPowers.popPrefEnv(); + resetSearchTipsProvider(); + BrowserTestUtils.removeTab(tab); +}); + +// Ensure a the Persist Search Tip is non-visible when a PopupNotification +// is already visible. +add_task(async function persistSearchTipBeforePopupShown() { + await setDefaultEngine("Example"); + await SpecialPowers.pushPrefEnv({ + set: [["browser.urlbar.showSearchTerms.featureGate", true]], + }); + + let tab = await BrowserTestUtils.openNewForegroundTab({ + gBrowser, + url: SEARCH_SERP_URL, + }); + + let promisePopupShown = BrowserTestUtils.waitForEvent( + PopupNotifications.panel, + "popupshown" + ); + PopupNotifications.show( + gBrowser.selectedBrowser, + "test-notification", + "This is a sample popup.", + "geo-notification-icon" + ); + await promisePopupShown; + + // Wait roughly for the amount of time it would take for the + // persist search tip to show. + await new Promise(resolve => + // eslint-disable-next-line mozilla/no-arbitrary-setTimeout + setTimeout(resolve, UrlbarProviderSearchTips.SHOW_PERSIST_TIP_DELAY_MS * 2) + ); + + // Check the search tip didn't show while the page was loading. + Assert.equal( + UrlbarPrefs.get( + `tipShownCount.${UrlbarProviderSearchTips.TIP_TYPE.PERSIST}` + ), + 0, + "The shownCount pref should be 0." + ); + Assert.equal(false, window.gURLBar.view.isOpen, "Urlbar should be closed."); + + // Clean up. + await SpecialPowers.popPrefEnv(); + resetSearchTipsProvider(); + BrowserTestUtils.removeTab(tab); +}); + +// The Persist Search Tip should be hidden when a PopupNotification appears. +add_task(async function persistSearchTipAfterPopupShown() { + await setDefaultEngine("Example"); + await SpecialPowers.pushPrefEnv({ + set: [["browser.urlbar.showSearchTerms.featureGate", true]], + }); + let tab = await BrowserTestUtils.openNewForegroundTab({ + gBrowser, + url: SEARCH_SERP_URL, + }); + + // Ensure the search tip is visible. + await checkTip(window, UrlbarProviderSearchTips.TIP_TYPE.PERSIST, false); + + // Show a popup after the search tip is shown. + let promisePopupShown = BrowserTestUtils.waitForEvent( + PopupNotifications.panel, + "popupshown" + ); + PopupNotifications.show( + gBrowser.selectedBrowser, + "test-notification", + "This is a sample popup.", + "geo-notification-icon" + ); + await promisePopupShown; + + // The search tip should not be visible. + Assert.equal(false, window.gURLBar.view.isOpen, "Urlbar should be closed."); + Assert.equal( + gURLBar.getAttribute("pageproxystate"), + "valid", + "Urlbar is should be in a valid pageproxystate." + ); + + // Clean up. + await SpecialPowers.popPrefEnv(); + resetSearchTipsProvider(); + BrowserTestUtils.removeTab(tab); +}); + +function waitForBrowserWindowActive(win) { + return new Promise(resolve => { + if (Services.focus.activeWindow == win) { + resolve(); + } else { + win.addEventListener( + "activate", + () => { + resolve(); + }, + { once: true } + ); + } + }); +} |