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