Chromium Code Reviews| OLD | NEW |
|---|---|
| (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 }); | |
| OLD | NEW |