browser/base/content/test/newtab/browser_newtab_search.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/browser/base/content/test/newtab/browser_newtab_search.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,295 @@
     1.4 +/* Any copyright is dedicated to the Public Domain.
     1.5 +   http://creativecommons.org/publicdomain/zero/1.0/ */
     1.6 +
     1.7 +// See browser/components/search/test/browser_*_behavior.js for tests of actual
     1.8 +// searches.
     1.9 +
    1.10 +const ENGINE_LOGO = "searchEngineLogo.xml";
    1.11 +const ENGINE_NO_LOGO = "searchEngineNoLogo.xml";
    1.12 +
    1.13 +const SERVICE_EVENT_NAME = "ContentSearchService";
    1.14 +
    1.15 +const LOGO_LOW_DPI_SIZE = [65, 26];
    1.16 +const LOGO_HIGH_DPI_SIZE = [130, 52];
    1.17 +
    1.18 +// The test has an expected search event queue and a search event listener.
    1.19 +// Search events that are expected to happen are added to the queue, and the
    1.20 +// listener consumes the queue and ensures that each event it receives is at
    1.21 +// the head of the queue.
    1.22 +//
    1.23 +// Each item in the queue is an object { type, deferred }.  type is the
    1.24 +// expected search event type.  deferred is a Promise.defer() value that is
    1.25 +// resolved when the event is consumed.
    1.26 +var gExpectedSearchEventQueue = [];
    1.27 +
    1.28 +var gNewEngines = [];
    1.29 +
    1.30 +function runTests() {
    1.31 +  let oldCurrentEngine = Services.search.currentEngine;
    1.32 +
    1.33 +  yield addNewTabPageTab();
    1.34 +  yield whenSearchInitDone();
    1.35 +
    1.36 +  // The tab is removed at the end of the test, so there's no need to remove
    1.37 +  // this listener at the end of the test.
    1.38 +  info("Adding search event listener");
    1.39 +  getContentWindow().addEventListener(SERVICE_EVENT_NAME, searchEventListener);
    1.40 +
    1.41 +  let panel = searchPanel();
    1.42 +  is(panel.state, "closed", "Search panel should be closed initially");
    1.43 +
    1.44 +  // The panel's animation often is not finished when the test clicks on panel
    1.45 +  // children, which makes the test click the wrong children, so disable it.
    1.46 +  panel.setAttribute("animate", "false");
    1.47 +
    1.48 +  // Add the two test engines.
    1.49 +  let logoEngine = null;
    1.50 +  yield promiseNewSearchEngine(true).then(engine => {
    1.51 +    logoEngine = engine;
    1.52 +    TestRunner.next();
    1.53 +  });
    1.54 +  ok(!!logoEngine.getIconURLBySize(...LOGO_LOW_DPI_SIZE),
    1.55 +     "Sanity check: engine should have 1x logo");
    1.56 +  ok(!!logoEngine.getIconURLBySize(...LOGO_HIGH_DPI_SIZE),
    1.57 +     "Sanity check: engine should have 2x logo");
    1.58 +
    1.59 +  let noLogoEngine = null;
    1.60 +  yield promiseNewSearchEngine(false).then(engine => {
    1.61 +    noLogoEngine = engine;
    1.62 +    TestRunner.next();
    1.63 +  });
    1.64 +  ok(!noLogoEngine.getIconURLBySize(...LOGO_LOW_DPI_SIZE),
    1.65 +     "Sanity check: engine should not have 1x logo");
    1.66 +  ok(!noLogoEngine.getIconURLBySize(...LOGO_HIGH_DPI_SIZE),
    1.67 +     "Sanity check: engine should not have 2x logo");
    1.68 +
    1.69 +  // Use the search service to change the current engine to the logo engine.
    1.70 +  Services.search.currentEngine = logoEngine;
    1.71 +  yield promiseSearchEvents(["CurrentEngine"]).then(TestRunner.next);
    1.72 +  checkCurrentEngine(ENGINE_LOGO);
    1.73 +
    1.74 +  // Click the logo to open the search panel.
    1.75 +  yield Promise.all([
    1.76 +    promisePanelShown(panel),
    1.77 +    promiseClick(logoImg()),
    1.78 +  ]).then(TestRunner.next);
    1.79 +
    1.80 +  // In the search panel, click the no-logo engine.  It should become the
    1.81 +  // current engine.
    1.82 +  let noLogoBox = null;
    1.83 +  for (let box of panel.childNodes) {
    1.84 +    if (box.getAttribute("engine") == noLogoEngine.name) {
    1.85 +      noLogoBox = box;
    1.86 +      break;
    1.87 +    }
    1.88 +  }
    1.89 +  ok(noLogoBox, "Search panel should contain the no-logo engine");
    1.90 +  yield Promise.all([
    1.91 +    promiseSearchEvents(["CurrentEngine"]),
    1.92 +    promiseClick(noLogoBox),
    1.93 +  ]).then(TestRunner.next);
    1.94 +
    1.95 +  checkCurrentEngine(ENGINE_NO_LOGO);
    1.96 +
    1.97 +  // Switch back to the logo engine.
    1.98 +  Services.search.currentEngine = logoEngine;
    1.99 +  yield promiseSearchEvents(["CurrentEngine"]).then(TestRunner.next);
   1.100 +  checkCurrentEngine(ENGINE_LOGO);
   1.101 +
   1.102 +  // Open the panel again.
   1.103 +  yield Promise.all([
   1.104 +    promisePanelShown(panel),
   1.105 +    promiseClick(logoImg()),
   1.106 +  ]).then(TestRunner.next);
   1.107 +
   1.108 +  // In the search panel, click the Manage Engines box.
   1.109 +  let manageBox = $("manage");
   1.110 +  ok(!!manageBox, "The Manage Engines box should be present in the document");
   1.111 +  yield Promise.all([
   1.112 +    promiseManagerOpen(),
   1.113 +    promiseClick(manageBox),
   1.114 +  ]).then(TestRunner.next);
   1.115 +
   1.116 +  // Done.  Revert the current engine and remove the new engines.
   1.117 +  Services.search.currentEngine = oldCurrentEngine;
   1.118 +  yield promiseSearchEvents(["CurrentEngine"]).then(TestRunner.next);
   1.119 +
   1.120 +  let events = [];
   1.121 +  for (let engine of gNewEngines) {
   1.122 +    Services.search.removeEngine(engine);
   1.123 +    events.push("State");
   1.124 +  }
   1.125 +  yield promiseSearchEvents(events).then(TestRunner.next);
   1.126 +}
   1.127 +
   1.128 +function searchEventListener(event) {
   1.129 +  info("Got search event " + event.detail.type);
   1.130 +  let passed = false;
   1.131 +  let nonempty = gExpectedSearchEventQueue.length > 0;
   1.132 +  ok(nonempty, "Expected search event queue should be nonempty");
   1.133 +  if (nonempty) {
   1.134 +    let { type, deferred } = gExpectedSearchEventQueue.shift();
   1.135 +    is(event.detail.type, type, "Got expected search event " + type);
   1.136 +    if (event.detail.type == type) {
   1.137 +      passed = true;
   1.138 +      // Let gSearch respond to the event before continuing.
   1.139 +      executeSoon(() => deferred.resolve());
   1.140 +    }
   1.141 +  }
   1.142 +  if (!passed) {
   1.143 +    info("Didn't get expected event, stopping the test");
   1.144 +    getContentWindow().removeEventListener(SERVICE_EVENT_NAME,
   1.145 +                                           searchEventListener);
   1.146 +    // Set next() to a no-op so the test really does stop.
   1.147 +    TestRunner.next = function () {};
   1.148 +    TestRunner.finish();
   1.149 +  }
   1.150 +}
   1.151 +
   1.152 +function $(idSuffix) {
   1.153 +  return getContentDocument().getElementById("newtab-search-" + idSuffix);
   1.154 +}
   1.155 +
   1.156 +function promiseSearchEvents(events) {
   1.157 +  info("Expecting search events: " + events);
   1.158 +  events = events.map(e => ({ type: e, deferred: Promise.defer() }));
   1.159 +  gExpectedSearchEventQueue.push(...events);
   1.160 +  return Promise.all(events.map(e => e.deferred.promise));
   1.161 +}
   1.162 +
   1.163 +function promiseNewSearchEngine(withLogo) {
   1.164 +  let basename = withLogo ? ENGINE_LOGO : ENGINE_NO_LOGO;
   1.165 +  info("Waiting for engine to be added: " + basename);
   1.166 +
   1.167 +  // Wait for the search events triggered by adding the new engine.
   1.168 +  // engine-added engine-loaded
   1.169 +  let expectedSearchEvents = ["State", "State"];
   1.170 +  if (withLogo) {
   1.171 +    // an engine-changed for each of the two logos
   1.172 +    expectedSearchEvents.push("State", "State");
   1.173 +  }
   1.174 +  let eventPromise = promiseSearchEvents(expectedSearchEvents);
   1.175 +
   1.176 +  // Wait for addEngine().
   1.177 +  let addDeferred = Promise.defer();
   1.178 +  let url = getRootDirectory(gTestPath) + basename;
   1.179 +  Services.search.addEngine(url, Ci.nsISearchEngine.TYPE_MOZSEARCH, "", false, {
   1.180 +    onSuccess: function (engine) {
   1.181 +      info("Search engine added: " + basename);
   1.182 +      gNewEngines.push(engine);
   1.183 +      addDeferred.resolve(engine);
   1.184 +    },
   1.185 +    onError: function (errCode) {
   1.186 +      ok(false, "addEngine failed with error code " + errCode);
   1.187 +      addDeferred.reject();
   1.188 +    },
   1.189 +  });
   1.190 +
   1.191 +  // Make a new promise that wraps the previous promises.  The only point of
   1.192 +  // this is to pass the new engine to the yielder via deferred.resolve(),
   1.193 +  // which is a little nicer than passing an array whose first element is the
   1.194 +  // new engine.
   1.195 +  let deferred = Promise.defer();
   1.196 +  Promise.all([addDeferred.promise, eventPromise]).then(values => {
   1.197 +    let newEngine = values[0];
   1.198 +    deferred.resolve(newEngine);
   1.199 +  }, () => deferred.reject());
   1.200 +  return deferred.promise;
   1.201 +}
   1.202 +
   1.203 +function checkCurrentEngine(basename) {
   1.204 +  let engine = Services.search.currentEngine;
   1.205 +  ok(engine.name.contains(basename),
   1.206 +     "Sanity check: current engine: engine.name=" + engine.name +
   1.207 +     " basename=" + basename);
   1.208 +
   1.209 +  // gSearch.currentEngineName
   1.210 +  is(gSearch().currentEngineName, engine.name,
   1.211 +     "currentEngineName: " + engine.name);
   1.212 +
   1.213 +  // search bar logo
   1.214 +  let logoSize = [px * window.devicePixelRatio for (px of LOGO_LOW_DPI_SIZE)];
   1.215 +  let logoURI = engine.getIconURLBySize(...logoSize);
   1.216 +  let logo = logoImg();
   1.217 +  is(logo.hidden, !logoURI,
   1.218 +     "Logo should be visible iff engine has a logo: " + engine.name);
   1.219 +  if (logoURI) {
   1.220 +    is(logo.style.backgroundImage, 'url("' + logoURI + '")', "Logo URI");
   1.221 +  }
   1.222 +
   1.223 +  // "selected" attributes of engines in the panel
   1.224 +  let panel = searchPanel();
   1.225 +  for (let engineBox of panel.childNodes) {
   1.226 +    let engineName = engineBox.getAttribute("engine");
   1.227 +    if (engineName == engine.name) {
   1.228 +      is(engineBox.getAttribute("selected"), "true",
   1.229 +         "Engine box's selected attribute should be true for " +
   1.230 +         "selected engine: " + engineName);
   1.231 +    }
   1.232 +    else {
   1.233 +      ok(!engineBox.hasAttribute("selected"),
   1.234 +         "Engine box's selected attribute should be absent for " +
   1.235 +         "non-selected engine: " + engineName);
   1.236 +    }
   1.237 +  }
   1.238 +}
   1.239 +
   1.240 +function promisePanelShown(panel) {
   1.241 +  let deferred = Promise.defer();
   1.242 +  info("Waiting for popupshown");
   1.243 +  panel.addEventListener("popupshown", function onEvent() {
   1.244 +    panel.removeEventListener("popupshown", onEvent);
   1.245 +    is(panel.state, "open", "Panel state");
   1.246 +    executeSoon(() => deferred.resolve());
   1.247 +  });
   1.248 +  return deferred.promise;
   1.249 +}
   1.250 +
   1.251 +function promiseClick(node) {
   1.252 +  let deferred = Promise.defer();
   1.253 +  let win = getContentWindow();
   1.254 +  SimpleTest.waitForFocus(() => {
   1.255 +    EventUtils.synthesizeMouseAtCenter(node, {}, win);
   1.256 +    deferred.resolve();
   1.257 +  }, win);
   1.258 +  return deferred.promise;
   1.259 +}
   1.260 +
   1.261 +function promiseManagerOpen() {
   1.262 +  info("Waiting for the search manager window to open...");
   1.263 +  let deferred = Promise.defer();
   1.264 +  let winWatcher = Cc["@mozilla.org/embedcomp/window-watcher;1"].
   1.265 +                   getService(Ci.nsIWindowWatcher);
   1.266 +  winWatcher.registerNotification(function onWin(subj, topic, data) {
   1.267 +    if (topic == "domwindowopened" && subj instanceof Ci.nsIDOMWindow) {
   1.268 +      subj.addEventListener("load", function onLoad() {
   1.269 +        subj.removeEventListener("load", onLoad);
   1.270 +        if (subj.document.documentURI ==
   1.271 +            "chrome://browser/content/search/engineManager.xul") {
   1.272 +          winWatcher.unregisterNotification(onWin);
   1.273 +          ok(true, "Observed search manager window opened");
   1.274 +          is(subj.opener, gWindow,
   1.275 +             "Search engine manager opener should be the chrome browser " +
   1.276 +             "window containing the newtab page");
   1.277 +          executeSoon(() => {
   1.278 +            subj.close();
   1.279 +            deferred.resolve();
   1.280 +          });
   1.281 +        }
   1.282 +      });
   1.283 +    }
   1.284 +  });
   1.285 +  return deferred.promise;
   1.286 +}
   1.287 +
   1.288 +function searchPanel() {
   1.289 +  return $("panel");
   1.290 +}
   1.291 +
   1.292 +function logoImg() {
   1.293 +  return $("logo");
   1.294 +}
   1.295 +
   1.296 +function gSearch() {
   1.297 +  return getContentWindow().gSearch;
   1.298 +}

mercurial