diff options
Diffstat (limited to 'browser/base/content/test/newtab/browser_newtab_search.js')
-rw-r--r-- | browser/base/content/test/newtab/browser_newtab_search.js | 247 |
1 files changed, 247 insertions, 0 deletions
diff --git a/browser/base/content/test/newtab/browser_newtab_search.js b/browser/base/content/test/newtab/browser_newtab_search.js new file mode 100644 index 000000000..19ed4ba74 --- /dev/null +++ b/browser/base/content/test/newtab/browser_newtab_search.js @@ -0,0 +1,247 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// See browser/components/search/test/browser_*_behavior.js for tests of actual +// searches. + +Cu.import("resource://gre/modules/Task.jsm"); + +const ENGINE_NO_LOGO = { + name: "searchEngineNoLogo.xml", + numLogos: 0, +}; + +const ENGINE_FAVICON = { + name: "searchEngineFavicon.xml", + logoPrefix1x: "data:image/png;base64,AAABAAIAICAAAAEAIACoEAAAJgAAABAQAAABACAAaAQAAM4QAAAoAAAAIAAAAEAAAAABACAAAAAAAAAQAAATCwAAEwsA", + numLogos: 1, +}; +ENGINE_FAVICON.logoPrefix2x = ENGINE_FAVICON.logoPrefix1x; + +const ENGINE_1X_LOGO = { + name: "searchEngine1xLogo.xml", + logoPrefix1x: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEEAAAAaCAIAAABn3KYmAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gkTADEw", + numLogos: 1, +}; +ENGINE_1X_LOGO.logoPrefix2x = ENGINE_1X_LOGO.logoPrefix1x; + +const ENGINE_2X_LOGO = { + name: "searchEngine2xLogo.xml", + logoPrefix2x: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIIAAAA0CAIAAADJ8nfCAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gkTADMU", + numLogos: 1, +}; +ENGINE_2X_LOGO.logoPrefix1x = ENGINE_2X_LOGO.logoPrefix2x; + +const ENGINE_1X_2X_LOGO = { + name: "searchEngine1x2xLogo.xml", + logoPrefix1x: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEEAAAAaCAIAAABn3KYmAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gkTADIG", + logoPrefix2x: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIIAAAA0CAIAAADJ8nfCAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gkTADMo", + numLogos: 2, +}; + +const ENGINE_SUGGESTIONS = { + name: "searchSuggestionEngine.xml", + numLogos: 0, +}; + +// The test has an expected search event queue and a search event listener. +// Search events that are expected to happen are added to the queue, and the +// listener consumes the queue and ensures that each event it receives is at +// the head of the queue. +let gExpectedSearchEventQueue = []; +let gExpectedSearchEventResolver = null; + +let gNewEngines = []; + +add_task(function* () { + let oldCurrentEngine = Services.search.currentEngine; + + yield* addNewTabPageTab(); + + // The tab is removed at the end of the test, so there's no need to remove + // this listener at the end of the test. + info("Adding search event listener"); + yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () { + const SERVICE_EVENT_NAME = "ContentSearchService"; + content.addEventListener(SERVICE_EVENT_NAME, function (event) { + sendAsyncMessage("test:search-event", { eventType: event.detail.type }); + }); + }); + + let mm = gBrowser.selectedBrowser.messageManager; + mm.addMessageListener("test:search-event", function (message) { + let eventType = message.data.eventType; + if (!gExpectedSearchEventResolver) { + ok(false, "Got search event " + eventType + " with no promise assigned"); + } + + let expectedEventType = gExpectedSearchEventQueue.shift(); + is(eventType, expectedEventType, "Got expected search event " + expectedEventType); + if (!gExpectedSearchEventQueue.length) { + gExpectedSearchEventResolver(); + gExpectedSearchEventResolver = null; + } + }); + + // Add the engine without any logos and switch to it. + let noLogoEngine = yield promiseNewSearchEngine(ENGINE_NO_LOGO); + let searchEventsPromise = promiseSearchEvents(["CurrentEngine"]); + Services.search.currentEngine = noLogoEngine; + yield searchEventsPromise; + yield* checkCurrentEngine(ENGINE_NO_LOGO); + + // Add the engine with favicon and switch to it. + let faviconEngine = yield promiseNewSearchEngine(ENGINE_FAVICON); + searchEventsPromise = promiseSearchEvents(["CurrentEngine"]); + Services.search.currentEngine = faviconEngine; + yield searchEventsPromise; + yield* checkCurrentEngine(ENGINE_FAVICON); + + // Add the engine with a 1x-DPI logo and switch to it. + let logo1xEngine = yield promiseNewSearchEngine(ENGINE_1X_LOGO); + searchEventsPromise = promiseSearchEvents(["CurrentEngine"]); + Services.search.currentEngine = logo1xEngine; + yield searchEventsPromise; + yield* checkCurrentEngine(ENGINE_1X_LOGO); + + // Add the engine with a 2x-DPI logo and switch to it. + let logo2xEngine = yield promiseNewSearchEngine(ENGINE_2X_LOGO); + searchEventsPromise = promiseSearchEvents(["CurrentEngine"]); + Services.search.currentEngine = logo2xEngine; + yield searchEventsPromise; + yield* checkCurrentEngine(ENGINE_2X_LOGO); + + // Add the engine with 1x- and 2x-DPI logos and switch to it. + let logo1x2xEngine = yield promiseNewSearchEngine(ENGINE_1X_2X_LOGO); + searchEventsPromise = promiseSearchEvents(["CurrentEngine"]); + Services.search.currentEngine = logo1x2xEngine; + yield searchEventsPromise; + yield* checkCurrentEngine(ENGINE_1X_2X_LOGO); + + // Add the engine that provides search suggestions and switch to it. + let suggestionEngine = yield promiseNewSearchEngine(ENGINE_SUGGESTIONS); + searchEventsPromise = promiseSearchEvents(["CurrentEngine"]); + Services.search.currentEngine = suggestionEngine; + yield searchEventsPromise; + yield* checkCurrentEngine(ENGINE_SUGGESTIONS); + + // Avoid intermittent failures. + yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () { + content.gSearch._contentSearchController.remoteTimeout = 5000; + }); + + // Type an X in the search input. This is only a smoke test. See + // browser_searchSuggestionUI.js for comprehensive content search suggestion + // UI tests. + let suggestionsOpenPromise = new Promise(resolve => { + mm.addMessageListener("test:newtab-suggestions-open", function onResponse(message) { + mm.removeMessageListener("test:newtab-suggestions-open", onResponse); + resolve(); + }); + }); + + yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () { + let table = content.document.getElementById("searchSuggestionTable"); + + let input = content.document.getElementById("newtab-search-text"); + input.focus(); + + info("Waiting for suggestions table to open"); + let observer = new content.MutationObserver(() => { + if (input.getAttribute("aria-expanded") == "true") { + observer.disconnect(); + Assert.ok(!table.hidden, "Search suggestion table unhidden"); + sendAsyncMessage("test:newtab-suggestions-open", {}); + } + }); + observer.observe(input, { + attributes: true, + attributeFilter: ["aria-expanded"], + }); + }); + + let suggestionsPromise = promiseSearchEvents(["Suggestions"]); + + EventUtils.synthesizeKey("x", {}); + + // Wait for the search suggestions to become visible and for the Suggestions + // message. + yield suggestionsOpenPromise; + yield suggestionsPromise; + + // Empty the search input, causing the suggestions to be hidden. + EventUtils.synthesizeKey("a", { accelKey: true }); + EventUtils.synthesizeKey("VK_DELETE", {}); + + yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () { + Assert.ok(content.document.getElementById("searchSuggestionTable").hidden, + "Search suggestion table hidden"); + }); + + // Done. Revert the current engine and remove the new engines. + searchEventsPromise = promiseSearchEvents(["CurrentEngine"]); + Services.search.currentEngine = oldCurrentEngine; + yield searchEventsPromise; + + let events = Array(gNewEngines.length).fill("CurrentState", 0, gNewEngines.length); + searchEventsPromise = promiseSearchEvents(events); + + for (let engine of gNewEngines) { + Services.search.removeEngine(engine); + } + yield searchEventsPromise; +}); + +function promiseSearchEvents(events) { + info("Expecting search events: " + events); + return new Promise(resolve => { + gExpectedSearchEventQueue.push(...events); + gExpectedSearchEventResolver = resolve; + }); +} + +function promiseNewSearchEngine({name: basename, numLogos}) { + info("Waiting for engine to be added: " + basename); + + // Wait for the search events triggered by adding the new engine. + // engine-added engine-loaded + let expectedSearchEvents = ["CurrentState", "CurrentState"]; + // engine-changed for each of the logos + for (let i = 0; i < numLogos; i++) { + expectedSearchEvents.push("CurrentState"); + } + let eventPromise = promiseSearchEvents(expectedSearchEvents); + + // Wait for addEngine(). + let addEnginePromise = new Promise((resolve, reject) => { + let url = getRootDirectory(gTestPath) + basename; + Services.search.addEngine(url, null, "", false, { + onSuccess: function (engine) { + info("Search engine added: " + basename); + gNewEngines.push(engine); + resolve(engine); + }, + onError: function (errCode) { + ok(false, "addEngine failed with error code " + errCode); + reject(); + }, + }); + }); + + return Promise.all([addEnginePromise, eventPromise]).then(([newEngine, _]) => { + return newEngine; + }); +} + +function* checkCurrentEngine(engineInfo) +{ + let engine = Services.search.currentEngine; + ok(engine.name.includes(engineInfo.name), + "Sanity check: current engine: engine.name=" + engine.name + + " basename=" + engineInfo.name); + + yield ContentTask.spawn(gBrowser.selectedBrowser, { name: engine.name }, function* (args) { + Assert.equal(content.gSearch._contentSearchController.defaultEngine.name, + args.name, "currentEngineName: " + args.name); + }); +} |