Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* Any copyright is dedicated to the Public Domain.
2 http://creativecommons.org/publicdomain/zero/1.0/ */
4 // See browser/components/search/test/browser_*_behavior.js for tests of actual
5 // searches.
7 const ENGINE_LOGO = "searchEngineLogo.xml";
8 const ENGINE_NO_LOGO = "searchEngineNoLogo.xml";
10 const SERVICE_EVENT_NAME = "ContentSearchService";
12 const LOGO_LOW_DPI_SIZE = [65, 26];
13 const LOGO_HIGH_DPI_SIZE = [130, 52];
15 // The test has an expected search event queue and a search event listener.
16 // Search events that are expected to happen are added to the queue, and the
17 // listener consumes the queue and ensures that each event it receives is at
18 // the head of the queue.
19 //
20 // Each item in the queue is an object { type, deferred }. type is the
21 // expected search event type. deferred is a Promise.defer() value that is
22 // resolved when the event is consumed.
23 var gExpectedSearchEventQueue = [];
25 var gNewEngines = [];
27 function runTests() {
28 let oldCurrentEngine = Services.search.currentEngine;
30 yield addNewTabPageTab();
31 yield whenSearchInitDone();
33 // The tab is removed at the end of the test, so there's no need to remove
34 // this listener at the end of the test.
35 info("Adding search event listener");
36 getContentWindow().addEventListener(SERVICE_EVENT_NAME, searchEventListener);
38 let panel = searchPanel();
39 is(panel.state, "closed", "Search panel should be closed initially");
41 // The panel's animation often is not finished when the test clicks on panel
42 // children, which makes the test click the wrong children, so disable it.
43 panel.setAttribute("animate", "false");
45 // Add the two test engines.
46 let logoEngine = null;
47 yield promiseNewSearchEngine(true).then(engine => {
48 logoEngine = engine;
49 TestRunner.next();
50 });
51 ok(!!logoEngine.getIconURLBySize(...LOGO_LOW_DPI_SIZE),
52 "Sanity check: engine should have 1x logo");
53 ok(!!logoEngine.getIconURLBySize(...LOGO_HIGH_DPI_SIZE),
54 "Sanity check: engine should have 2x logo");
56 let noLogoEngine = null;
57 yield promiseNewSearchEngine(false).then(engine => {
58 noLogoEngine = engine;
59 TestRunner.next();
60 });
61 ok(!noLogoEngine.getIconURLBySize(...LOGO_LOW_DPI_SIZE),
62 "Sanity check: engine should not have 1x logo");
63 ok(!noLogoEngine.getIconURLBySize(...LOGO_HIGH_DPI_SIZE),
64 "Sanity check: engine should not have 2x logo");
66 // Use the search service to change the current engine to the logo engine.
67 Services.search.currentEngine = logoEngine;
68 yield promiseSearchEvents(["CurrentEngine"]).then(TestRunner.next);
69 checkCurrentEngine(ENGINE_LOGO);
71 // Click the logo to open the search panel.
72 yield Promise.all([
73 promisePanelShown(panel),
74 promiseClick(logoImg()),
75 ]).then(TestRunner.next);
77 // In the search panel, click the no-logo engine. It should become the
78 // current engine.
79 let noLogoBox = null;
80 for (let box of panel.childNodes) {
81 if (box.getAttribute("engine") == noLogoEngine.name) {
82 noLogoBox = box;
83 break;
84 }
85 }
86 ok(noLogoBox, "Search panel should contain the no-logo engine");
87 yield Promise.all([
88 promiseSearchEvents(["CurrentEngine"]),
89 promiseClick(noLogoBox),
90 ]).then(TestRunner.next);
92 checkCurrentEngine(ENGINE_NO_LOGO);
94 // Switch back to the logo engine.
95 Services.search.currentEngine = logoEngine;
96 yield promiseSearchEvents(["CurrentEngine"]).then(TestRunner.next);
97 checkCurrentEngine(ENGINE_LOGO);
99 // Open the panel again.
100 yield Promise.all([
101 promisePanelShown(panel),
102 promiseClick(logoImg()),
103 ]).then(TestRunner.next);
105 // In the search panel, click the Manage Engines box.
106 let manageBox = $("manage");
107 ok(!!manageBox, "The Manage Engines box should be present in the document");
108 yield Promise.all([
109 promiseManagerOpen(),
110 promiseClick(manageBox),
111 ]).then(TestRunner.next);
113 // Done. Revert the current engine and remove the new engines.
114 Services.search.currentEngine = oldCurrentEngine;
115 yield promiseSearchEvents(["CurrentEngine"]).then(TestRunner.next);
117 let events = [];
118 for (let engine of gNewEngines) {
119 Services.search.removeEngine(engine);
120 events.push("State");
121 }
122 yield promiseSearchEvents(events).then(TestRunner.next);
123 }
125 function searchEventListener(event) {
126 info("Got search event " + event.detail.type);
127 let passed = false;
128 let nonempty = gExpectedSearchEventQueue.length > 0;
129 ok(nonempty, "Expected search event queue should be nonempty");
130 if (nonempty) {
131 let { type, deferred } = gExpectedSearchEventQueue.shift();
132 is(event.detail.type, type, "Got expected search event " + type);
133 if (event.detail.type == type) {
134 passed = true;
135 // Let gSearch respond to the event before continuing.
136 executeSoon(() => deferred.resolve());
137 }
138 }
139 if (!passed) {
140 info("Didn't get expected event, stopping the test");
141 getContentWindow().removeEventListener(SERVICE_EVENT_NAME,
142 searchEventListener);
143 // Set next() to a no-op so the test really does stop.
144 TestRunner.next = function () {};
145 TestRunner.finish();
146 }
147 }
149 function $(idSuffix) {
150 return getContentDocument().getElementById("newtab-search-" + idSuffix);
151 }
153 function promiseSearchEvents(events) {
154 info("Expecting search events: " + events);
155 events = events.map(e => ({ type: e, deferred: Promise.defer() }));
156 gExpectedSearchEventQueue.push(...events);
157 return Promise.all(events.map(e => e.deferred.promise));
158 }
160 function promiseNewSearchEngine(withLogo) {
161 let basename = withLogo ? ENGINE_LOGO : ENGINE_NO_LOGO;
162 info("Waiting for engine to be added: " + basename);
164 // Wait for the search events triggered by adding the new engine.
165 // engine-added engine-loaded
166 let expectedSearchEvents = ["State", "State"];
167 if (withLogo) {
168 // an engine-changed for each of the two logos
169 expectedSearchEvents.push("State", "State");
170 }
171 let eventPromise = promiseSearchEvents(expectedSearchEvents);
173 // Wait for addEngine().
174 let addDeferred = Promise.defer();
175 let url = getRootDirectory(gTestPath) + basename;
176 Services.search.addEngine(url, Ci.nsISearchEngine.TYPE_MOZSEARCH, "", false, {
177 onSuccess: function (engine) {
178 info("Search engine added: " + basename);
179 gNewEngines.push(engine);
180 addDeferred.resolve(engine);
181 },
182 onError: function (errCode) {
183 ok(false, "addEngine failed with error code " + errCode);
184 addDeferred.reject();
185 },
186 });
188 // Make a new promise that wraps the previous promises. The only point of
189 // this is to pass the new engine to the yielder via deferred.resolve(),
190 // which is a little nicer than passing an array whose first element is the
191 // new engine.
192 let deferred = Promise.defer();
193 Promise.all([addDeferred.promise, eventPromise]).then(values => {
194 let newEngine = values[0];
195 deferred.resolve(newEngine);
196 }, () => deferred.reject());
197 return deferred.promise;
198 }
200 function checkCurrentEngine(basename) {
201 let engine = Services.search.currentEngine;
202 ok(engine.name.contains(basename),
203 "Sanity check: current engine: engine.name=" + engine.name +
204 " basename=" + basename);
206 // gSearch.currentEngineName
207 is(gSearch().currentEngineName, engine.name,
208 "currentEngineName: " + engine.name);
210 // search bar logo
211 let logoSize = [px * window.devicePixelRatio for (px of LOGO_LOW_DPI_SIZE)];
212 let logoURI = engine.getIconURLBySize(...logoSize);
213 let logo = logoImg();
214 is(logo.hidden, !logoURI,
215 "Logo should be visible iff engine has a logo: " + engine.name);
216 if (logoURI) {
217 is(logo.style.backgroundImage, 'url("' + logoURI + '")', "Logo URI");
218 }
220 // "selected" attributes of engines in the panel
221 let panel = searchPanel();
222 for (let engineBox of panel.childNodes) {
223 let engineName = engineBox.getAttribute("engine");
224 if (engineName == engine.name) {
225 is(engineBox.getAttribute("selected"), "true",
226 "Engine box's selected attribute should be true for " +
227 "selected engine: " + engineName);
228 }
229 else {
230 ok(!engineBox.hasAttribute("selected"),
231 "Engine box's selected attribute should be absent for " +
232 "non-selected engine: " + engineName);
233 }
234 }
235 }
237 function promisePanelShown(panel) {
238 let deferred = Promise.defer();
239 info("Waiting for popupshown");
240 panel.addEventListener("popupshown", function onEvent() {
241 panel.removeEventListener("popupshown", onEvent);
242 is(panel.state, "open", "Panel state");
243 executeSoon(() => deferred.resolve());
244 });
245 return deferred.promise;
246 }
248 function promiseClick(node) {
249 let deferred = Promise.defer();
250 let win = getContentWindow();
251 SimpleTest.waitForFocus(() => {
252 EventUtils.synthesizeMouseAtCenter(node, {}, win);
253 deferred.resolve();
254 }, win);
255 return deferred.promise;
256 }
258 function promiseManagerOpen() {
259 info("Waiting for the search manager window to open...");
260 let deferred = Promise.defer();
261 let winWatcher = Cc["@mozilla.org/embedcomp/window-watcher;1"].
262 getService(Ci.nsIWindowWatcher);
263 winWatcher.registerNotification(function onWin(subj, topic, data) {
264 if (topic == "domwindowopened" && subj instanceof Ci.nsIDOMWindow) {
265 subj.addEventListener("load", function onLoad() {
266 subj.removeEventListener("load", onLoad);
267 if (subj.document.documentURI ==
268 "chrome://browser/content/search/engineManager.xul") {
269 winWatcher.unregisterNotification(onWin);
270 ok(true, "Observed search manager window opened");
271 is(subj.opener, gWindow,
272 "Search engine manager opener should be the chrome browser " +
273 "window containing the newtab page");
274 executeSoon(() => {
275 subj.close();
276 deferred.resolve();
277 });
278 }
279 });
280 }
281 });
282 return deferred.promise;
283 }
285 function searchPanel() {
286 return $("panel");
287 }
289 function logoImg() {
290 return $("logo");
291 }
293 function gSearch() {
294 return getContentWindow().gSearch;
295 }