/* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Tests search mode preview. */ "use strict"; const TEST_ENGINE_NAME = "Test"; add_setup(async function () { await SearchTestUtils.installSearchExtension({ name: TEST_ENGINE_NAME, keyword: "@test", }); await SpecialPowers.pushPrefEnv({ set: [["browser.urlbar.suggest.quickactions", false]], }); registerCleanupFunction(async () => { await PlacesUtils.history.clear(); }); }); /** * @param {Node} button * A one-off button. * @param {boolean} [isPreview] * Whether the expected search mode should be a preview. Defaults to true. * @returns {object} * The search mode object expected when that one-off is selected. */ function getExpectedSearchMode(button, isPreview = true) { let expectedSearchMode = { entry: "oneoff", isPreview, }; if (button.engine) { expectedSearchMode.engineName = button.engine.name; let engine = Services.search.getEngineByName(button.engine.name); if (engine.isGeneralPurposeEngine) { expectedSearchMode.source = UrlbarUtils.RESULT_SOURCE.SEARCH; } } else { expectedSearchMode.source = button.source; } return expectedSearchMode; } // Tests that cycling through token alias engines enters search mode preview. add_task(async function tokenAlias() { await UrlbarTestUtils.promiseAutocompleteResultPopup({ window, value: "@", }); let result; while (gURLBar.searchMode?.engineName != TEST_ENGINE_NAME) { EventUtils.synthesizeKey("KEY_ArrowDown"); let index = UrlbarTestUtils.getSelectedRowIndex(window); result = await UrlbarTestUtils.getDetailsOfResultAt(window, index); let expectedSearchMode = { engineName: result.searchParams.engine, isPreview: true, entry: "keywordoffer", }; let engine = Services.search.getEngineByName(result.searchParams.engine); if (engine.isGeneralPurposeEngine) { expectedSearchMode.source = UrlbarUtils.RESULT_SOURCE.SEARCH; } await UrlbarTestUtils.assertSearchMode(window, expectedSearchMode); } let searchPromise = UrlbarTestUtils.promiseSearchComplete(window); EventUtils.synthesizeKey("KEY_Enter"); await searchPromise; // Test that we are in confirmed search mode. await UrlbarTestUtils.assertSearchMode(window, { engineName: result.searchParams.engine, entry: "keywordoffer", }); await UrlbarTestUtils.exitSearchMode(window); }); // Tests that starting to type a query exits search mode preview in favour of // full search mode. add_task(async function startTyping() { await UrlbarTestUtils.promiseAutocompleteResultPopup({ window, value: "@", }); while (gURLBar.searchMode?.engineName != TEST_ENGINE_NAME) { EventUtils.synthesizeKey("KEY_ArrowDown"); } await UrlbarTestUtils.assertSearchMode(window, { engineName: TEST_ENGINE_NAME, isPreview: true, entry: "keywordoffer", }); let searchPromise = UrlbarTestUtils.promiseSearchComplete(window); EventUtils.synthesizeKey("M"); await searchPromise; await UrlbarTestUtils.assertSearchMode(window, { engineName: TEST_ENGINE_NAME, entry: "keywordoffer", }); await UrlbarTestUtils.exitSearchMode(window); }); // Tests that highlighting a search shortcut Top Site enters search mode // preview. add_task(async function topSites() { // Enable search shortcut Top Sites. await PlacesUtils.history.clear(); await updateTopSites( sites => sites && sites[0] && sites[0].searchTopSite, true ); await UrlbarTestUtils.promiseAutocompleteResultPopup({ window, value: "", fireInputEvent: true, }); // We previously verified that the first Top Site is a search shortcut. EventUtils.synthesizeKey("KEY_ArrowDown"); let searchTopSite = await UrlbarTestUtils.getDetailsOfResultAt(window, 0); await UrlbarTestUtils.assertSearchMode(window, { engineName: searchTopSite.searchParams.engine, isPreview: true, entry: "topsites_urlbar", }); await UrlbarTestUtils.exitSearchMode(window); }); // Tests that search mode preview is exited when the view is closed. add_task(async function closeView() { await UrlbarTestUtils.promiseAutocompleteResultPopup({ window, value: "@", }); while (gURLBar.searchMode?.engineName != TEST_ENGINE_NAME) { EventUtils.synthesizeKey("KEY_ArrowDown"); } await UrlbarTestUtils.assertSearchMode(window, { engineName: TEST_ENGINE_NAME, isPreview: true, entry: "keywordoffer", }); // We should close search mode when closing the view. await UrlbarTestUtils.promisePopupClose(window, () => gURLBar.blur()); await UrlbarTestUtils.assertSearchMode(window, null); // Check search mode isn't re-entered when re-opening the view. await UrlbarTestUtils.promisePopupOpen(window, () => { if (gURLBar.getAttribute("pageproxystate") == "invalid") { gURLBar.handleRevert(); } EventUtils.synthesizeMouseAtCenter(gURLBar.inputField, {}); }); await UrlbarTestUtils.assertSearchMode(window, null); await UrlbarTestUtils.promisePopupClose(window); }); // Tests that search more preview is exited when the user switches tabs. add_task(async function tabSwitch() { await UrlbarTestUtils.promiseAutocompleteResultPopup({ window, value: "@", }); while (gURLBar.searchMode?.engineName != TEST_ENGINE_NAME) { EventUtils.synthesizeKey("KEY_ArrowDown"); } await UrlbarTestUtils.assertSearchMode(window, { engineName: TEST_ENGINE_NAME, isPreview: true, entry: "keywordoffer", }); // Open a new tab then switch back to the original tab. let tab1 = gBrowser.selectedTab; let tab2 = await BrowserTestUtils.openNewForegroundTab(gBrowser); await BrowserTestUtils.switchTab(gBrowser, tab1); await UrlbarTestUtils.assertSearchMode(window, null); BrowserTestUtils.removeTab(tab2); }); // Tests that search mode is previewed when the user down arrows through the // one-offs. add_task(async function oneOff_downArrow() { await UrlbarTestUtils.promiseAutocompleteResultPopup({ window, value: "", }); let oneOffs = UrlbarTestUtils.getOneOffSearchButtons(window); await TestUtils.waitForCondition( () => !oneOffs._rebuilding, "Waiting for one-offs to finish rebuilding" ); let resultCount = UrlbarTestUtils.getResultCount(window); // Key down through all results. for (let i = 0; i < resultCount; i++) { EventUtils.synthesizeKey("KEY_ArrowDown"); } // Key down again. The first one-off should be selected. EventUtils.synthesizeKey("KEY_ArrowDown"); // Check for the one-off's search mode previews. while (oneOffs.selectedButton != oneOffs.settingsButton) { await UrlbarTestUtils.assertSearchMode( window, getExpectedSearchMode(oneOffs.selectedButton) ); EventUtils.synthesizeKey("KEY_ArrowDown"); } // Check that selecting the search settings button leaves search mode preview. Assert.equal( oneOffs.selectedButton, oneOffs.settingsButton, "The settings button is selected." ); await UrlbarTestUtils.assertSearchMode(window, null); // Closing the view should also exit search mode preview. await UrlbarTestUtils.promisePopupClose(window); await UrlbarTestUtils.assertSearchMode(window, null); }); // Tests that search mode is previewed when the user Alt+down arrows through the // one-offs. This subtest also highlights a keywordoffer result (the first Top // Site) before Alt+Arrowing to the one-offs. This checks that the search mode // previews from keywordoffer results are overwritten by selected one-offs. add_task(async function oneOff_alt_downArrow() { // Add some visits to a URL so we have multiple Top Sites. await PlacesUtils.history.clear(); for (let i = 0; i < 5; i++) { await PlacesTestUtils.addVisits("https://example.com/"); } await updateTopSites( sites => sites && sites[0]?.searchTopSite && sites[1]?.url == "https://example.com/", true ); await UrlbarTestUtils.promiseAutocompleteResultPopup({ window, value: "", }); let oneOffs = UrlbarTestUtils.getOneOffSearchButtons(window); await TestUtils.waitForCondition( () => !oneOffs._rebuilding, "Waiting for one-offs to finish rebuilding" ); // Key down to the first result and check that it enters search mode preview. EventUtils.synthesizeKey("KEY_ArrowDown"); let searchTopSite = await UrlbarTestUtils.getDetailsOfResultAt(window, 0); await UrlbarTestUtils.assertSearchMode(window, { engineName: searchTopSite.searchParams.engine, isPreview: true, entry: "topsites_urlbar", }); // Alt+down. The first one-off should be selected. EventUtils.synthesizeKey("KEY_ArrowDown", { altKey: true }); // Check for the one-offs' search mode previews. while (oneOffs.selectedButton) { await UrlbarTestUtils.assertSearchMode( window, getExpectedSearchMode(oneOffs.selectedButton) ); EventUtils.synthesizeKey("KEY_ArrowDown", { altKey: true }); } // Now key down without a modifier. We should move to the second result and // have no search mode preview. EventUtils.synthesizeKey("KEY_ArrowDown"); Assert.equal( UrlbarTestUtils.getSelectedRowIndex(window), 1, "The second result is selected." ); await UrlbarTestUtils.assertSearchMode(window, null); // Arrow back up to the keywordoffer result and check for search mode preview. EventUtils.synthesizeKey("KEY_ArrowUp"); await UrlbarTestUtils.assertSearchMode(window, { engineName: searchTopSite.searchParams.engine, isPreview: true, entry: "topsites_urlbar", }); await PlacesUtils.history.clear(); await UrlbarTestUtils.promisePopupClose(window); await UrlbarTestUtils.assertSearchMode(window, null); }); // Tests that search mode is previewed when the user is in full search mode // and down arrows through the one-offs. add_task(async function fullSearchMode_oneOff_downArrow() { await UrlbarTestUtils.promiseAutocompleteResultPopup({ window, value: "", }); let oneOffs = UrlbarTestUtils.getOneOffSearchButtons(window); await TestUtils.waitForCondition( () => !oneOffs._rebuilding, "Waiting for one-offs to finish rebuilding" ); let oneOffButtons = oneOffs.getSelectableButtons(true); await UrlbarTestUtils.enterSearchMode(window); let expectedSearchMode = getExpectedSearchMode(oneOffButtons[0], false); // Sanity check: we are in the correct search mode. await UrlbarTestUtils.assertSearchMode(window, expectedSearchMode); // Key down through all results. let resultCount = UrlbarTestUtils.getResultCount(window); for (let i = 0; i < resultCount; i++) { EventUtils.synthesizeKey("KEY_ArrowDown"); // If the result is a shortcut, it will enter preview mode. let result = await UrlbarTestUtils.getDetailsOfResultAt(window, i); await UrlbarTestUtils.assertSearchMode( window, Object.assign(expectedSearchMode, { isPreview: !!result.searchParams.keyword, }) ); } // Key down again. The first one-off should be selected. EventUtils.synthesizeKey("KEY_ArrowDown"); // Check that we show the correct preview as we cycle through the one-offs. while (oneOffs.selectedButton != oneOffs.settingsButton) { await UrlbarTestUtils.assertSearchMode( window, getExpectedSearchMode(oneOffs.selectedButton, true) ); EventUtils.synthesizeKey("KEY_ArrowDown"); } // We should still be in the same search mode after cycling through all the // one-offs. await UrlbarTestUtils.assertSearchMode(window, expectedSearchMode); await UrlbarTestUtils.exitSearchMode(window); await UrlbarTestUtils.promisePopupClose(window); }); // Tests that search mode is previewed when the user is in full search mode // and alt+down arrows through the one-offs. This subtest also checks that // exiting full search mode while alt+arrowing through the one-offs enters // search mode preview for subsequent one-offs. add_task(async function fullSearchMode_oneOff_alt_downArrow() { await UrlbarTestUtils.promiseAutocompleteResultPopup({ window, value: "", }); let oneOffs = UrlbarTestUtils.getOneOffSearchButtons(window); let oneOffButtons = oneOffs.getSelectableButtons(true); await TestUtils.waitForCondition( () => !oneOffs._rebuilding, "Waiting for one-offs to finish rebuilding" ); await UrlbarTestUtils.enterSearchMode(window); let expectedSearchMode = getExpectedSearchMode(oneOffButtons[0], false); await UrlbarTestUtils.assertSearchMode(window, expectedSearchMode); // Key down to the first result. EventUtils.synthesizeKey("KEY_ArrowDown"); // Alt+down. The first one-off should be selected. EventUtils.synthesizeKey("KEY_ArrowDown", { altKey: true }); // Cycle through the first half of the one-offs and verify that search mode // preview is entered. Assert.greater( oneOffButtons.length, 1, "Sanity check: We should have at least two one-offs." ); for (let i = 1; i < oneOffButtons.length / 2; i++) { await UrlbarTestUtils.assertSearchMode( window, getExpectedSearchMode(oneOffs.selectedButton, true) ); EventUtils.synthesizeKey("KEY_ArrowDown", { altKey: true }); } // Now click out of search mode. await UrlbarTestUtils.exitSearchMode(window, { clickClose: true }); // Now check for the remaining one-offs' search mode previews. EventUtils.synthesizeKey("KEY_ArrowDown", { altKey: true }); while (oneOffs.selectedButton) { await UrlbarTestUtils.assertSearchMode( window, getExpectedSearchMode(oneOffs.selectedButton, true) ); EventUtils.synthesizeKey("KEY_ArrowDown", { altKey: true }); } await UrlbarTestUtils.promisePopupClose(window); await UrlbarTestUtils.assertSearchMode(window, null); }); // Tests that the original search mode is preserved when going through some // one-off buttons and then back down in the results list. add_task(async function fullSearchMode_oneOff_restore_on_down() { info("Add a few visits to top sites"); for (let i = 0; i < 5; i++) { await PlacesTestUtils.addVisits([ "https://1.example.com/", "https://2.example.com/", "https://3.example.com/", ]); } await updateTopSites(sites => sites?.length > 2, false); await UrlbarTestUtils.promiseAutocompleteResultPopup({ window, value: "", }); let oneOffs = UrlbarTestUtils.getOneOffSearchButtons(window); let oneOffButtons = oneOffs.getSelectableButtons(true); await TestUtils.waitForCondition( () => !oneOffs._rebuilding, "Waiting for one-offs to finish rebuilding" ); await UrlbarTestUtils.enterSearchMode(window, { source: UrlbarUtils.RESULT_SOURCE.HISTORY, }); let expectedSearchMode = getExpectedSearchMode( oneOffButtons.find(b => b.source == UrlbarUtils.RESULT_SOURCE.HISTORY), false ); await UrlbarTestUtils.assertSearchMode(window, expectedSearchMode); info("Down to the first result"); EventUtils.synthesizeKey("KEY_ArrowDown"); await UrlbarTestUtils.assertSearchMode(window, expectedSearchMode); info("Alt+down to the first one-off."); Assert.greater( oneOffButtons.length, 1, "Sanity check: We should have at least two one-offs." ); EventUtils.synthesizeKey("KEY_ArrowDown", { altKey: true }); await UrlbarTestUtils.assertSearchMode( window, getExpectedSearchMode(oneOffs.selectedButton, true) ); info("Go again down through the list of results"); EventUtils.synthesizeKey("KEY_ArrowDown"); await UrlbarTestUtils.assertSearchMode(window, expectedSearchMode); // Now do a similar test without initial search mode. info("Exit search mode."); await UrlbarTestUtils.exitSearchMode(window); await UrlbarTestUtils.promiseAutocompleteResultPopup({ window, value: "", }); info("Down to the first result"); EventUtils.synthesizeKey("KEY_ArrowDown"); await UrlbarTestUtils.assertSearchMode(window, null); info("select a one-off to start preview"); EventUtils.synthesizeKey("KEY_ArrowDown", { altKey: true }); await UrlbarTestUtils.assertSearchMode( window, getExpectedSearchMode(oneOffs.selectedButton, true) ); info("Go again through the list of results"); EventUtils.synthesizeKey("KEY_ArrowDown"); await UrlbarTestUtils.assertSearchMode(window, null); await UrlbarTestUtils.promisePopupClose(window); await PlacesUtils.history.clear(); });