Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(88)

Side by Side Diff: chrome/test/data/webui/settings/search_engines_page_test.js

Issue 1666623006: MD Settings: Manage search engines 3/3, hooking up UI. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@manage_search_engines_handler
Patch Set: undo accidental binding conversion to one-way Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 cr.define('settings_search_engines_page', function() {
6 /**
7 * TODO(dpapad): Similar class exists in webui_resource_async_browsertest.js.
8 * Move somewhere else and re-use.
9 * @constructor
10 */
11 var PromiseResolver = function() {
12 this.promise = new Promise(function(resolve, reject) {
13 this.resolve = resolve;
14 this.reject = reject;
15 }.bind(this));
16 };
17
18 // See |defineTestBrowserProxy| function below (this class can only be defined
19 // after search_engines_browser_proxy.html has been imported.
20 var TestSearchEnginesBrowserProxy = null;
21
22 /**
23 * @return {!Promise} A promise resolved after the
24 * TestSearchEnginesBrowserProxy class is defined.
25 */
26 function defineTestBrowserProxy() {
27 if (TestSearchEnginesBrowserProxy != null)
28 return Promise.resolve();
29
30 var browserProxyImportPath =
31 'chrome://md-settings/search_engines_page/search_engines_browser_proxy.h tml';
Dan Beam 2016/02/18 03:08:19 why doesn't the page that's visited via browsePrel
dpapad 2016/02/18 19:34:51 For many reasons, see also reply at line 177. I am
32 return PolymerTest.importHtml(browserProxyImportPath).then(function() {
33 /**
34 * A test version of SearchEnginesBrowserProxy. Provides helper methods fo r
35 * allowing tests to know when a method was called, as well as specifying
36 * mock responses.
37 *
38 * @constructor
39 * @extends {settings.SearchEnginesBrowserProxy}
40 */
41 TestSearchEnginesBrowserProxy = function() {
42 /** @private {!Map<string, !PromiseResolver>} */
43 this.resolverMap_ = this.buildResolverMap_();
44
45 /** @private {!SearchEnginesInfo} */
46 this.searchEnginesInfo_ = {};
47 };
48
49 TestSearchEnginesBrowserProxy.prototype = {
50 __proto__: settings.SearchEnginesBrowserProxy.prototype,
51
52 /**
53 * @return {!Map<string, !PromiseResolver>}
54 * @private
55 */
56 buildResolverMap_: function() {
57 var resolverMap = new Map();
58 var wrapperMethods = [
59 'getSearchEnginesList',
60 'removeSearchEngine',
61 'searchEngineEditCancelled',
62 'searchEngineEditCompleted',
63 'searchEngineEditStarted',
64 'setDefaultSearchEngine',
65 'validateSearchEngineInput',
66 ];
67 wrapperMethods.forEach(function(methodName) {
68 resolverMap.set(methodName, new PromiseResolver());
69 });
70 return resolverMap;
71 },
72
73 /**
74 * @param {string} methodName
75 * @return {!Promise} A promise that is resolved when the given method i s
Dan Beam 2016/02/18 03:08:19 80 col wrap
dpapad 2016/02/18 19:34:51 Done.
76 * called.
77 */
78 whenCalled: function(methodName) {
79 return this.resolverMap_.get(methodName).promise;
80 },
81
82 /**
83 * Resets the PromiseResolver associated with the given method.
84 * @param {string} methodName
85 */
86 resetResolver: function(methodName) {
87 this.resolverMap_.set(methodName, new PromiseResolver());
88 },
89
90 /** @override */
91 setDefaultSearchEngine: function(modelIndex) {
92 this.resolverMap_.get('setDefaultSearchEngine').resolve(modelIndex);
93 },
94
95 /** @override */
96 removeSearchEngine: function(modelIndex) {
97 this.resolverMap_.get('removeSearchEngine').resolve(modelIndex);
98 },
99
100 /** @override */
101 searchEngineEditStarted: function(modelIndex) {
102 this.resolverMap_.get('searchEngineEditStarted').resolve(modelIndex);
103 },
104
105 /** @override */
106 searchEngineEditCancelled: function() {
107 this.resolverMap_.get('searchEngineEditCancelled').resolve();
108 },
109
110 /** @override */
111 searchEngineEditCompleted: function(searchEngine, keyword, queryUrl) {
112 this.resolverMap_.get('searchEngineEditCompleted').resolve();
113 },
114
115 /**
116 * Sets the response to be returned by |getSearchEnginesList|.
117 * @param {!SearchEnginesInfo}
118 */
119 setGetSearchEnginesList: function(searchEnginesInfo) {
Dan Beam 2016/02/18 03:08:19 setSearchEnginesInfo
dpapad 2016/02/18 19:34:51 Done.
120 this.searchEnginesInfo_ = searchEnginesInfo;
121 },
122
123 /** @override */
124 getSearchEnginesList: function() {
125 this.resolverMap_.get('getSearchEnginesList').resolve();
126 return Promise.resolve(this.searchEnginesInfo_);
127 },
128
129 /** @override */
130 validateSearchEngineInput: function(fieldName, fieldValue) {
131 this.resolverMap_.get('validateSearchEngineInput').resolve();
132 return Promise.resolve(true);
133 },
134 };
135 });
136 }
137
138 /** @return {!SearchEngine} */
139 var getSampleSearchEngine = function() {
Dan Beam 2016/02/18 03:08:19 get -> create
dpapad 2016/02/18 19:34:51 Done.
140 return {
141 canBeDefault: false,
142 canBeEdited: true,
143 canBeRemoved: false,
144 default: false,
145 displayName: "Google",
146 iconURL: "http://www.google.com/favicon.ico",
147 isOmniboxExtension: false,
148 keyword: "google.com",
149 modelIndex: 0,
150 name: "Google",
151 url: "https://search.foo.com/search?p=%s",
152 urlLocked: false,
153 };
154 };
155
Dan Beam 2016/02/18 03:08:19 \n\n -> \n
dpapad 2016/02/18 19:34:51 Done.
156
157 function registerDialogTests() {
158 suite('AddSearchEngineDialogTests', function() {
159 /** @type {?SettingsAddSearchEngineDialog} */
160 var dialog = null;
161 var browserProxy = null;
162
163 suiteSetup(function() {
164 return Promise.all([
165 defineTestBrowserProxy(),
166 PolymerTest.importHtml('chrome://md-settings/i18n_setup.html'),
167 PolymerTest.importHtml(
168 'chrome://md-settings/search_engines_page/search_engine_dialog.htm l'),
169 ]);
170 });
171
172 setup(function() {
173 browserProxy = new TestSearchEnginesBrowserProxy();
174 settings.SearchEnginesBrowserProxy.instance_ = browserProxy;
175 PolymerTest.clearBody();
176 dialog = document.createElement('settings-search-engine-dialog');
177 document.body.appendChild(dialog);
Dan Beam 2016/02/18 03:08:19 is appending to the body required? what if we move
dpapad 2016/02/18 19:34:51 There are other elements that the "settings-search
Dan Beam 2016/02/18 20:32:28 that's fine if other elements rely on being attach
Dan Beam 2016/02/18 20:37:55 here's an example of this: https://codereview.chro
178 });
179
180 teardown(function() { dialog.remove(); });
181
182 // Tests that the dialog calls 'searchEngineEditStarted' and
183 // 'searchEngineEditCancelled' when closed from the 'x' button.
184 test('DialogOpenAndClose', function() {
185 return browserProxy.whenCalled('searchEngineEditStarted').then(
186 function() {
187 MockInteractions.tap(dialog.$.close);
188 return browserProxy.whenCalled('searchEngineEditCancelled');
189 });
190 });
191
192 // Tests that the dialog calls 'searchEngineEditStarted' and
193 // 'searchEngineEditCancelled' when closed from the 'cancel' button.
194 test('DialogOpenAndCancel', function() {
195 return browserProxy.whenCalled('searchEngineEditStarted').then(
196 function() {
197 MockInteractions.tap(dialog.$.cancel);
198 return browserProxy.whenCalled('searchEngineEditCancelled');
199 });
200 });
201
202 // Tests the dialog to add a new search engine. Specifically
203 // - paper-input elements are empty initially.
204 // - action button initially disabled.
205 // - validation is triggered on 'focus'. 'change' is not teted because
206 // MockInteractions does not currently provide a way to trigger such
207 // events.
208 // - action button is enabled when all fields are valid.
209 // - action button triggers appropriate browser signal when tapped.
210 test('DialogAddSearchEngine', function() {
211 /**
212 * Asserts that the given paper-input element is empty.
213 * @param {string} inputId
214 */
215 var assertInputEmpty = function(inputId) {
216 var inputElement = dialog.$[inputId];
217 assertTrue(!!inputElement);
218 assertEquals(0, inputElement.value.length);
219 };
220
221 /**
222 * Asserts that the given paper-input element triggers validation when
223 * focused.
224 * @param {string} inputId
225 * @return {!Promise} A promise firing when assertion has completed.
226 */
227 var assertInputValidation = function(inputId) {
Dan Beam 2016/02/18 03:08:19 can this be named focusAndValidate()?
dpapad 2016/02/18 19:34:51 Done, but IMO "assert" prefix was making it cleare
228 browserProxy.resetResolver('validateSearchEngineInput');
229 MockInteractions.focus(dialog.$[inputId]);
230 return browserProxy.whenCalled('validateSearchEngineInput');
231 };
232
233 assertInputEmpty('searchEngine');
234 assertInputEmpty('keyword');
235 assertInputEmpty('queryUrl');
236 var actionButton = dialog.$.actionButton;
237 assertTrue(actionButton.disabled);
238
239 return assertInputValidation('searchEngine').then(function() {
240 return assertInputValidation('keyword');
241 }).then(function() {
242 return assertInputValidation('queryUrl');
243 }).then(function() {
244 // Manually set the text to a non-empty string for all fields.
245 dialog.$.searchEngine.value = 'foo';
246 dialog.$.keyword.value = 'bar';
247 dialog.$.queryUrl.value = 'baz';
248
249 // MockInteractions does not provide a way to trigger a 'change' event
250 // yet. Triggering the 'focus' event instead, to update the state of
251 // the action button.
252 return assertInputValidation('searchEngine');
253 }).then(function() {
254 // Assert that the action button has been enabled now that all input
255 // is valid and non-empty.
256 assertFalse(actionButton.disabled);
257 MockInteractions.tap(actionButton);
258 return browserProxy.whenCalled('searchEngineEditCompleted');
259 });
260 });
261
262 });
263 }
264
265 function registerEntryTests() {
266 suite('SearchEngineEntryTests', function() {
267 /** @type {?SettingsSearchEngineEntryElement} */
268 var entry = null;
269
270 var browserProxy = null;
271
272 suiteSetup(function() {
273 return Promise.all([
274 defineTestBrowserProxy(),
275 PolymerTest.importHtml(
276 'chrome://md-settings/search_engines_page/search_engine_entry.html '),
277 ]);
278 });
279
280 setup(function() {
281 browserProxy = new TestSearchEnginesBrowserProxy();
282 settings.SearchEnginesBrowserProxy.instance_ = browserProxy;
283 PolymerTest.clearBody();
284 entry = document.createElement('settings-search-engine-entry');
285 entry.set('engine', getSampleSearchEngine());
286 document.body.appendChild(entry);
287 });
288
289 teardown(function() { entry.remove(); });
290
291 test('RemoveSearchEngine', function() {
292 var deleteButton = entry.$.delete;
293 assertTrue(!!deleteButton);
294 MockInteractions.tap(deleteButton);
295 return browserProxy.whenCalled('removeSearchEngine').then(
296 function(modelIndex) {
297 assertEquals(entry.engine.modelIndex, modelIndex);
298 });
299 });
300
301 test('MakeDefault', function() {
302 var makeDefaultButton = entry.$.makeDefault;
303 assertTrue(!!makeDefaultButton);
304 MockInteractions.tap(makeDefaultButton);
305 return browserProxy.whenCalled('setDefaultSearchEngine').then(
306 function(modelIndex) {
307 assertEquals(entry.engine.modelIndex, modelIndex);
308 });
309 });
310
311 // Test that the "make default" and "remove" buttons are hidden for
312 // the default search engine.
313 test('DefaultSearchEngineHiddenButtons', function() {
314 /**
315 * @param {string} buttonId
316 * @param {boolean} visible
317 */
318 var assertButtonVisibility = function(buttonId, visible) {
319 var buttonEl = entry.$[buttonId];
320 assertTrue(!!buttonEl);
321 assertEquals(!visible, buttonEl.hidden);
322 };
323 assertButtonVisibility('makeDefault', true);
324 assertButtonVisibility('edit', true);
325 assertButtonVisibility('delete', true);
326
327 // Mark the engine as the "default" one.
328 var engine = getSampleSearchEngine();
329 engine.default = true;
330 entry.set('engine', engine);
331
332 assertButtonVisibility('makeDefault', false);
333 assertButtonVisibility('edit', true);
334 assertButtonVisibility('delete', false);
335 });
336
337 // Test that clicking the "edit" button brings up a dialog.
338 test('Edit', function() {
339 var engine = entry.engine;
340 var editButton = entry.$.edit;
341 assertTrue(!!editButton);
342 MockInteractions.tap(editButton);
343 return browserProxy.whenCalled('searchEngineEditStarted').then(
344 function(modelIndex) {
345 assertEquals(engine.modelIndex, modelIndex);
346 var dialog = entry.$$('settings-search-engine-dialog');
347 assertTrue(!!dialog);
348
349 // Check that the paper-input fields are pre-populated.
350 assertEquals(engine.displayName, dialog.$.searchEngine.value);
351 assertEquals(engine.keyword, dialog.$.keyword.value);
352 assertEquals(engine.url, dialog.$.queryUrl.value);
353
354 assertFalse(dialog.$.actionButton.disabled);
355 });
356 });
357 });
358 }
359
360 function registerPageTests() {
361 suite('SearchEnginePageTests', function() {
362 /** @type {?SettingsSearchEnginesPageElement} */
363 var page = null;
364
365 var browserProxy = null;
366
367 /** @type {!SearchEnginesInfo} */
368 var searchEnginesInfo = {
369 defaults: [getSampleSearchEngine()],
370 others: [],
371 extensions: [],
372 };
373
374 suiteSetup(function() {
375 return Promise.all([
376 defineTestBrowserProxy(),
377 PolymerTest.importHtml('chrome://md-settings/i18n_setup.html'),
378 PolymerTest.importHtml(
379 'chrome://md-settings/search_engines_page/search_engines_page.html '),
380 ]);
381 });
382
383 setup(function() {
384 browserProxy = new TestSearchEnginesBrowserProxy();
385 browserProxy.setGetSearchEnginesList(searchEnginesInfo);
386 settings.SearchEnginesBrowserProxy.instance_ = browserProxy;
387 PolymerTest.clearBody();
388 page = document.createElement('settings-search-engines-page');
389 document.body.appendChild(page);
390 });
391
392 teardown(function() { page.remove(); });
393
394 // Tests that the page is querying and displaying search engine info on
395 // startup.
396 test('Initialization', function() {
397 return browserProxy.whenCalled('getSearchEnginesList').then(function() {
398 var searchEnginesLists = Polymer.dom(page.shadowRoot).
399 querySelectorAll('settings-search-engines-list');
400 assertEquals(2, searchEnginesLists.length);
401
402 Polymer.dom.flush();
403 var defaultsList = searchEnginesLists[0];
404 var defaultsEntries = Polymer.dom(defaultsList.shadowRoot).
405 querySelectorAll('settings-search-engine-entry');
406 assertEquals(
407 searchEnginesInfo.defaults.length, defaultsEntries.length);
408
409 var othersList = searchEnginesLists[1];
410 var othersEntries = Polymer.dom(othersList.shadowRoot).
411 querySelectorAll('settings-search-engine-entry');
412 assertEquals(
413 searchEnginesInfo.others.length, othersEntries.length);
414 });
415 });
416
417 // Tests that the add search engine dialog opens when the corresponding
418 // button is tapped.
419 test('AddSearchEngineDialog', function() {
420 assertFalse(!!page.$$('settings-search-engine-dialog'));
421 var addSearchEngineButton = page.$['addSearchEngine'];
Dan Beam 2016/02/18 03:08:19 $.addSearchEngine
dpapad 2016/02/18 19:34:51 Done, but never got a reply on why this is better.
422 assertTrue(!!addSearchEngineButton);
423
424 MockInteractions.tap(addSearchEngineButton);
425 Polymer.dom.flush();
426 assertTrue(!!page.$$('settings-search-engine-dialog'));
427 });
428 });
429 }
430
431 return {
432 registerDialogTests: registerDialogTests,
433 registerEntryTests: registerEntryTests,
434 registerPageTests: registerPageTests,
435 };
436 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698