| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 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 GEN_INCLUDE(['options_browsertest_base.js']); | |
| 6 GEN('#include "chrome/browser/ui/webui/options/options_browsertest.h"'); | |
| 7 | |
| 8 /** @const */ var SUPERVISED_USERS_PREF = 'profile.managed_users'; | |
| 9 | |
| 10 /** | |
| 11 * Wait for the method specified by |methodName|, on the |object| object, to be | |
| 12 * called, then execute |afterFunction|. | |
| 13 * @param {*} object Object with callable property named |methodName|. | |
| 14 * @param {string} methodName The name of the property on |object| to use as a | |
| 15 * callback. | |
| 16 * @param {!Function} afterFunction A function to call after object.methodName() | |
| 17 * is called. | |
| 18 */ | |
| 19 function waitForResponse(object, methodName, afterFunction) { | |
| 20 var originalCallback = object[methodName]; | |
| 21 | |
| 22 // Install a wrapper that temporarily replaces the original function. | |
| 23 object[methodName] = function() { | |
| 24 object[methodName] = originalCallback; | |
| 25 originalCallback.apply(this, arguments); | |
| 26 afterFunction(); | |
| 27 }; | |
| 28 } | |
| 29 | |
| 30 /** | |
| 31 * Wait for the global window.onpopstate callback to be called (after a tab | |
| 32 * history navigation), then execute |afterFunction|. | |
| 33 * @param {!Function} afterFunction A function to call after pop state events. | |
| 34 */ | |
| 35 function waitForPopstate(afterFunction) { | |
| 36 waitForResponse(window, 'onpopstate', afterFunction); | |
| 37 } | |
| 38 | |
| 39 /** | |
| 40 * TestFixture for OptionsPage WebUI testing. | |
| 41 * @extends {testing.Test} | |
| 42 * @constructor | |
| 43 */ | |
| 44 function OptionsWebUITest() {} | |
| 45 | |
| 46 OptionsWebUITest.prototype = { | |
| 47 __proto__: OptionsBrowsertestBase.prototype, | |
| 48 | |
| 49 /** | |
| 50 * Browse to the options page & call our preLoad(). | |
| 51 * @override | |
| 52 */ | |
| 53 browsePreload: 'chrome://settings-frame', | |
| 54 | |
| 55 /** @override */ | |
| 56 isAsync: true, | |
| 57 | |
| 58 /** | |
| 59 * Register a mock handler to ensure expectations are met and options pages | |
| 60 * behave correctly. | |
| 61 */ | |
| 62 preLoad: function() { | |
| 63 this.makeAndRegisterMockHandler( | |
| 64 ['defaultZoomFactorAction', | |
| 65 'fetchPrefs', | |
| 66 'observePrefs', | |
| 67 'setBooleanPref', | |
| 68 'setIntegerPref', | |
| 69 'setDoublePref', | |
| 70 'setStringPref', | |
| 71 'setObjectPref', | |
| 72 'clearPref', | |
| 73 'coreOptionsUserMetricsAction', | |
| 74 ]); | |
| 75 | |
| 76 // Register stubs for methods expected to be called before/during tests. | |
| 77 // Specific expectations can be made in the tests themselves. | |
| 78 this.mockHandler.stubs().fetchPrefs(ANYTHING); | |
| 79 this.mockHandler.stubs().observePrefs(ANYTHING); | |
| 80 this.mockHandler.stubs().coreOptionsUserMetricsAction(ANYTHING); | |
| 81 }, | |
| 82 | |
| 83 /** @override */ | |
| 84 setUp: function() { | |
| 85 OptionsBrowsertestBase.prototype.setUp.call(this); | |
| 86 | |
| 87 // Enable when failure is resolved. | |
| 88 // AX_ARIA_10: http://crbug.com/559329 | |
| 89 this.accessibilityAuditConfig.ignoreSelectors( | |
| 90 'unsupportedAriaAttribute', | |
| 91 '#profiles-list'); | |
| 92 | |
| 93 var linkWithUnclearPurposeSelectors = [ | |
| 94 '#sync-overview > A', | |
| 95 '#privacy-explanation > A', | |
| 96 '#languages-section > .settings-row > A', | |
| 97 '#cloudprint-options-mdns > .settings-row > A', | |
| 98 '#cups-printers-section > .settings-row > A', | |
| 99 '#do-not-track-confirm-overlay > .action-area > .hbox.stretch > A', | |
| 100 ]; | |
| 101 | |
| 102 // Enable when failure is resolved. | |
| 103 // AX_TEXT_04: http://crbug.com/559318 | |
| 104 this.accessibilityAuditConfig.ignoreSelectors( | |
| 105 'linkWithUnclearPurpose', | |
| 106 linkWithUnclearPurposeSelectors); | |
| 107 | |
| 108 // Causes testDefaultZoomFactor to flake. See http://crbug.com/611233. | |
| 109 var requiredOwnedAriaRoleMissingSelectors = [ | |
| 110 '#default-search-engine-list', | |
| 111 '#other-search-engine-list', | |
| 112 ]; | |
| 113 | |
| 114 // Enable when failure is resolved. | |
| 115 // AX_ARIA_08: http://crbug.com/606657 | |
| 116 this.accessibilityAuditConfig.ignoreSelectors( | |
| 117 'requiredOwnedAriaRoleMissing', | |
| 118 requiredOwnedAriaRoleMissingSelectors); | |
| 119 }, | |
| 120 }; | |
| 121 | |
| 122 /** | |
| 123 * Wait for all targets to be hidden. | |
| 124 * @param {Array<Element>} targets | |
| 125 */ | |
| 126 function waitUntilHidden(targets) { | |
| 127 function isHidden(el) { return el.hidden; } | |
| 128 function ensureTransition(el) { ensureTransitionEndEvent(el, 500); } | |
| 129 | |
| 130 document.addEventListener('transitionend', function f(e) { | |
| 131 if (targets.indexOf(e.target) >= 0 && targets.every(isHidden)) { | |
| 132 document.removeEventListener(f, 'transitionend'); | |
| 133 testDone(); | |
| 134 } | |
| 135 }); | |
| 136 | |
| 137 targets.forEach(ensureTransition); | |
| 138 } | |
| 139 | |
| 140 // Crashes on Mac only. See http://crbug.com/79181 | |
| 141 GEN('#if defined(OS_MACOSX)'); | |
| 142 GEN('#define MAYBE_testSetBooleanPrefTriggers ' + | |
| 143 'DISABLED_testSetBooleanPrefTriggers'); | |
| 144 GEN('#else'); | |
| 145 GEN('#define MAYBE_testSetBooleanPrefTriggers testSetBooleanPrefTriggers'); | |
| 146 GEN('#endif // defined(OS_MACOSX)'); | |
| 147 | |
| 148 TEST_F('OptionsWebUITest', 'MAYBE_testSetBooleanPrefTriggers', function() { | |
| 149 // TODO(dtseng): make generic to click all buttons. | |
| 150 var showHomeButton = | |
| 151 document.querySelector('input[pref="browser.show_home_button"]'); | |
| 152 var trueListValue = [ | |
| 153 'browser.show_home_button', | |
| 154 true, | |
| 155 'Options_Homepage_HomeButton', | |
| 156 ]; | |
| 157 // Note: this expectation is checked in testing::Test::tearDown. | |
| 158 this.mockHandler.expects(once()).setBooleanPref(trueListValue); | |
| 159 | |
| 160 // Cause the handler to be called. | |
| 161 showHomeButton.click(); | |
| 162 showHomeButton.blur(); | |
| 163 testDone(); | |
| 164 }); | |
| 165 | |
| 166 // Not meant to run on ChromeOS at this time. | |
| 167 // Not finishing in windows. http://crbug.com/81723 | |
| 168 TEST_F('OptionsWebUITest', 'DISABLED_testRefreshStaysOnCurrentPage', | |
| 169 function() { | |
| 170 assertTrue($('search-engine-manager-page').hidden); | |
| 171 var item = $('manage-default-search-engines'); | |
| 172 item.click(); | |
| 173 | |
| 174 assertFalse($('search-engine-manager-page').hidden); | |
| 175 | |
| 176 window.location.reload(); | |
| 177 | |
| 178 assertEquals('chrome://settings-frame/searchEngines', document.location.href); | |
| 179 assertFalse($('search-engine-manager-page').hidden); | |
| 180 testDone(); | |
| 181 }); | |
| 182 | |
| 183 /** | |
| 184 * Test the default zoom factor select element. | |
| 185 */ | |
| 186 TEST_F('OptionsWebUITest', 'testDefaultZoomFactor', function() { | |
| 187 // The expected minimum length of the |defaultZoomFactor| element. | |
| 188 var defaultZoomFactorMinimumLength = 10; | |
| 189 // Verify that the zoom factor element exists. | |
| 190 var defaultZoomFactor = $('defaultZoomFactor'); | |
| 191 assertNotEquals(defaultZoomFactor, null); | |
| 192 | |
| 193 // Verify that the zoom factor element has a reasonable number of choices. | |
| 194 expectGE(defaultZoomFactor.options.length, defaultZoomFactorMinimumLength); | |
| 195 | |
| 196 // Simulate a change event, selecting the highest zoom value. Verify that | |
| 197 // the javascript handler was invoked once. | |
| 198 this.mockHandler.expects(once()).defaultZoomFactorAction(NOT_NULL). | |
| 199 will(callFunction(function() { })); | |
| 200 defaultZoomFactor.selectedIndex = defaultZoomFactor.options.length - 1; | |
| 201 var event = {target: defaultZoomFactor}; | |
| 202 if (defaultZoomFactor.onchange) defaultZoomFactor.onchange(event); | |
| 203 testDone(); | |
| 204 }); | |
| 205 | |
| 206 /** | |
| 207 * If |confirmInterstitial| is true, the OK button of the Do Not Track | |
| 208 * interstitial is pressed, otherwise the abort button is pressed. | |
| 209 * @param {boolean} confirmInterstitial Whether to confirm the Do Not Track | |
| 210 * interstitial. | |
| 211 */ | |
| 212 OptionsWebUITest.prototype.testDoNotTrackInterstitial = | |
| 213 function(confirmInterstitial) { | |
| 214 Preferences.prefsFetchedCallback({'enable_do_not_track': {'value': false}}); | |
| 215 var buttonToClick = confirmInterstitial ? $('do-not-track-confirm-ok') : | |
| 216 $('do-not-track-confirm-cancel'); | |
| 217 var dntCheckbox = $('do-not-track-enabled'); | |
| 218 var dntOverlay = PageManager.registeredOverlayPages['donottrackconfirm']; | |
| 219 assertFalse(dntCheckbox.checked); | |
| 220 | |
| 221 var visibleChangeCounter = 0; | |
| 222 var visibleChangeHandler = function() { | |
| 223 ++visibleChangeCounter; | |
| 224 switch (visibleChangeCounter) { | |
| 225 case 1: | |
| 226 window.setTimeout(function() { | |
| 227 assertTrue(dntOverlay.visible); | |
| 228 buttonToClick.click(); | |
| 229 }, 0); | |
| 230 break; | |
| 231 case 2: | |
| 232 window.setTimeout(function() { | |
| 233 assertFalse(dntOverlay.visible); | |
| 234 assertEquals(confirmInterstitial, dntCheckbox.checked); | |
| 235 dntOverlay.removeEventListener('visibleChange', visibleChangeHandler); | |
| 236 testDone(); | |
| 237 }, 0); | |
| 238 break; | |
| 239 default: | |
| 240 assertTrue(false); | |
| 241 } | |
| 242 }; | |
| 243 dntOverlay.addEventListener('visibleChange', visibleChangeHandler); | |
| 244 | |
| 245 if (confirmInterstitial) { | |
| 246 this.mockHandler.expects(once()).setBooleanPref( | |
| 247 ['enable_do_not_track', true, 'Options_DoNotTrackCheckbox']); | |
| 248 } else { | |
| 249 // The mock handler complains if setBooleanPref is called even though | |
| 250 // it should not be. | |
| 251 } | |
| 252 | |
| 253 dntCheckbox.click(); | |
| 254 }; | |
| 255 | |
| 256 TEST_F('OptionsWebUITest', 'EnableDoNotTrackAndConfirmInterstitial', | |
| 257 function() { | |
| 258 this.testDoNotTrackInterstitial(true); | |
| 259 }); | |
| 260 | |
| 261 TEST_F('OptionsWebUITest', 'EnableDoNotTrackAndCancelInterstitial', | |
| 262 function() { | |
| 263 this.testDoNotTrackInterstitial(false); | |
| 264 }); | |
| 265 | |
| 266 // Check that the "Do not Track" preference can be correctly disabled. | |
| 267 // In order to do that, we need to enable it first. | |
| 268 TEST_F('OptionsWebUITest', 'EnableAndDisableDoNotTrack', function() { | |
| 269 Preferences.prefsFetchedCallback({'enable_do_not_track': {'value': false}}); | |
| 270 var dntCheckbox = $('do-not-track-enabled'); | |
| 271 var dntOverlay = PageManager.registeredOverlayPages.donottrackconfirm; | |
| 272 assertFalse(dntCheckbox.checked); | |
| 273 | |
| 274 var visibleChangeCounter = 0; | |
| 275 var visibleChangeHandler = function() { | |
| 276 ++visibleChangeCounter; | |
| 277 switch (visibleChangeCounter) { | |
| 278 case 1: | |
| 279 window.setTimeout(function() { | |
| 280 assertTrue(dntOverlay.visible); | |
| 281 $('do-not-track-confirm-ok').click(); | |
| 282 }, 0); | |
| 283 break; | |
| 284 case 2: | |
| 285 window.setTimeout(function() { | |
| 286 assertFalse(dntOverlay.visible); | |
| 287 assertTrue(dntCheckbox.checked); | |
| 288 dntOverlay.removeEventListener('visibleChange', visibleChangeHandler); | |
| 289 dntCheckbox.click(); | |
| 290 }, 0); | |
| 291 break; | |
| 292 default: | |
| 293 assertNotReached(); | |
| 294 } | |
| 295 }; | |
| 296 dntOverlay.addEventListener('visibleChange', visibleChangeHandler); | |
| 297 | |
| 298 this.mockHandler.expects(once()).setBooleanPref( | |
| 299 eq(['enable_do_not_track', true, 'Options_DoNotTrackCheckbox'])); | |
| 300 | |
| 301 var verifyCorrectEndState = function() { | |
| 302 window.setTimeout(function() { | |
| 303 assertFalse(dntOverlay.visible); | |
| 304 assertFalse(dntCheckbox.checked); | |
| 305 testDone(); | |
| 306 }, 0); | |
| 307 }; | |
| 308 this.mockHandler.expects(once()).setBooleanPref( | |
| 309 eq(['enable_do_not_track', false, 'Options_DoNotTrackCheckbox'])).will( | |
| 310 callFunction(verifyCorrectEndState)); | |
| 311 | |
| 312 dntCheckbox.click(); | |
| 313 }); | |
| 314 | |
| 315 // Fails on chromeos, http://crbug.com/660867 | |
| 316 // Verify that preventDefault() is called on 'Enter' keydown events that trigger | |
| 317 // the default button. If this doesn't happen, other elements that may get | |
| 318 // focus (by the overlay closing for instance), will execute in addition to the | |
| 319 // default button. See crbug.com/268336. | |
| 320 TEST_F('OptionsWebUITest', 'DISABLED_EnterPreventsDefault', function() { | |
| 321 var page = HomePageOverlay.getInstance(); | |
| 322 PageManager.showPageByName(page.name); | |
| 323 var event = new KeyboardEvent('keydown', { | |
| 324 'bubbles': true, | |
| 325 'cancelable': true, | |
| 326 'key': 'Enter' | |
| 327 }); | |
| 328 assertFalse(event.defaultPrevented); | |
| 329 page.pageDiv.dispatchEvent(event); | |
| 330 assertTrue(event.defaultPrevented); | |
| 331 testDone(); | |
| 332 }); | |
| 333 | |
| 334 // Verifies that sending an empty list of indexes to move doesn't crash chrome. | |
| 335 TEST_F('OptionsWebUITest', 'emptySelectedIndexesDoesntCrash', function() { | |
| 336 chrome.send('dragDropStartupPage', [0, []]); | |
| 337 setTimeout(testDone); | |
| 338 }); | |
| 339 | |
| 340 // This test turns out to be flaky on all platforms. | |
| 341 // See http://crbug.com/315250. | |
| 342 | |
| 343 // An overlay's position should remain the same as it shows. | |
| 344 TEST_F('OptionsWebUITest', 'DISABLED_OverlayShowDoesntShift', function() { | |
| 345 var overlayName = 'startup'; | |
| 346 var overlay = $('startup-overlay'); | |
| 347 var frozenPages = document.getElementsByClassName('frozen'); // Gets updated. | |
| 348 expectEquals(0, frozenPages.length); | |
| 349 | |
| 350 document.addEventListener('transitionend', function(e) { | |
| 351 if (e.target != overlay) | |
| 352 return; | |
| 353 | |
| 354 assertFalse(overlay.classList.contains('transparent')); | |
| 355 expectEquals(numFrozenPages, frozenPages.length); | |
| 356 testDone(); | |
| 357 }); | |
| 358 | |
| 359 PageManager.showPageByName(overlayName); | |
| 360 var numFrozenPages = frozenPages.length; | |
| 361 expectGT(numFrozenPages, 0); | |
| 362 }); | |
| 363 | |
| 364 GEN('#if defined(OS_CHROMEOS)'); | |
| 365 // Verify that range inputs respond to touch events. Currently only Chrome OS | |
| 366 // uses slider options. | |
| 367 TEST_F('OptionsWebUITest', 'RangeInputHandlesTouchEvents', function() { | |
| 368 this.mockHandler.expects(once()).setIntegerPref([ | |
| 369 'settings.touchpad.sensitivity2', 1]); | |
| 370 | |
| 371 var touchpadRange = $('touchpad-sensitivity-range'); | |
| 372 var event = document.createEvent('UIEvent'); | |
| 373 event.initUIEvent('touchstart', true, true, window); | |
| 374 touchpadRange.dispatchEvent(event); | |
| 375 | |
| 376 event = document.createEvent('UIEvent'); | |
| 377 event.initUIEvent('touchmove', true, true, window); | |
| 378 touchpadRange.dispatchEvent(event); | |
| 379 | |
| 380 touchpadRange.value = 1; | |
| 381 | |
| 382 event = document.createEvent('UIEvent'); | |
| 383 event.initUIEvent('touchend', true, true, window); | |
| 384 touchpadRange.dispatchEvent(event); | |
| 385 | |
| 386 // touchcancel should also trigger the handler, since it | |
| 387 // changes the slider position. | |
| 388 this.mockHandler.expects(once()).setIntegerPref([ | |
| 389 'settings.touchpad.sensitivity2', 2]); | |
| 390 | |
| 391 event = document.createEvent('UIEvent'); | |
| 392 event.initUIEvent('touchstart', true, true, window); | |
| 393 touchpadRange.dispatchEvent(event); | |
| 394 | |
| 395 touchpadRange.value = 2; | |
| 396 | |
| 397 event = document.createEvent('UIEvent'); | |
| 398 event.initUIEvent('touchcancel', true, true, window); | |
| 399 touchpadRange.dispatchEvent(event); | |
| 400 | |
| 401 testDone(); | |
| 402 }); | |
| 403 GEN('#endif'); // defined(OS_CHROMEOS) | |
| 404 | |
| 405 /** | |
| 406 * TestFixture for OptionsPage WebUI testing including tab history and support | |
| 407 * for preference manipulation. If you don't need the features in the C++ | |
| 408 * fixture, use the simpler OptionsWebUITest (above) instead. | |
| 409 * @extends {testing.Test} | |
| 410 * @constructor | |
| 411 */ | |
| 412 function OptionsWebUIExtendedTest() {} | |
| 413 | |
| 414 OptionsWebUIExtendedTest.prototype = { | |
| 415 __proto__: OptionsWebUITest.prototype, | |
| 416 | |
| 417 /** @override */ | |
| 418 typedefCppFixture: 'OptionsBrowserTest', | |
| 419 | |
| 420 /** @override */ | |
| 421 setUp: function() { | |
| 422 OptionsWebUITest.prototype.setUp.call(this); | |
| 423 | |
| 424 // Enable when failure is resolved. | |
| 425 // AX_ARIA_10: http://crbug.com/559329 | |
| 426 this.accessibilityAuditConfig.ignoreSelectors( | |
| 427 'unsupportedAriaAttribute', | |
| 428 '#profiles-list'); | |
| 429 | |
| 430 var controlsWithoutLabelSelectors = [ | |
| 431 '#cookies-view-page > .content-area.cookies-list-content-area > *', | |
| 432 '#other-search-engine-list > .deletable-item > DIV > *', | |
| 433 ]; | |
| 434 | |
| 435 // Enable when failure is resolved. | |
| 436 // AX_TEXT_01: http://crbug.com/559330 | |
| 437 this.accessibilityAuditConfig.ignoreSelectors( | |
| 438 'controlsWithoutLabel', | |
| 439 controlsWithoutLabelSelectors); | |
| 440 | |
| 441 var linkWithUnclearPurposeSelectors = [ | |
| 442 '#sync-overview > A', | |
| 443 '#privacy-explanation > A', | |
| 444 '#languages-section > .settings-row > A', | |
| 445 '#cloudprint-options-mdns > .settings-row > A', | |
| 446 // Selectors below only affect ChromeOS tests. | |
| 447 '#privacy-section > DIV > DIV:nth-of-type(9) > A', | |
| 448 '#accessibility-learn-more', | |
| 449 ]; | |
| 450 | |
| 451 // Enable when failure is resolved. | |
| 452 // AX_TEXT_04: http://crbug.com/559326 | |
| 453 this.accessibilityAuditConfig.ignoreSelectors( | |
| 454 'linkWithUnclearPurpose', | |
| 455 linkWithUnclearPurposeSelectors); | |
| 456 | |
| 457 var requiredOwnedAriaRoleMissingSelectors = [ | |
| 458 '#default-search-engine-list', | |
| 459 '#other-search-engine-list', | |
| 460 ]; | |
| 461 | |
| 462 // Enable when failure is resolved. | |
| 463 // AX_ARIA_08: http://crbug.com/605689 | |
| 464 this.accessibilityAuditConfig.ignoreSelectors( | |
| 465 'requiredOwnedAriaRoleMissing', | |
| 466 requiredOwnedAriaRoleMissingSelectors); | |
| 467 }, | |
| 468 | |
| 469 testGenPreamble: function() { | |
| 470 // Start with no supervised users managed by this profile. | |
| 471 GEN(' ClearPref("' + SUPERVISED_USERS_PREF + '");'); | |
| 472 }, | |
| 473 | |
| 474 /** | |
| 475 * Asserts that two non-nested arrays are equal. The arrays must contain only | |
| 476 * plain data types, no nested arrays or other objects. | |
| 477 * @param {Array} expected An array of expected values. | |
| 478 * @param {Array} result An array of actual values. | |
| 479 * @param {boolean} doSort If true, the arrays will be sorted before being | |
| 480 * compared. | |
| 481 * @param {string} description A brief description for the array of actual | |
| 482 * values, to use in an error message if the arrays differ. | |
| 483 * @private | |
| 484 */ | |
| 485 compareArrays_: function(expected, result, doSort, description) { | |
| 486 var errorMessage = '\n' + description + ': ' + result + | |
| 487 '\nExpected: ' + expected; | |
| 488 assertEquals(expected.length, result.length, errorMessage); | |
| 489 | |
| 490 var expectedSorted = expected.slice(); | |
| 491 var resultSorted = result.slice(); | |
| 492 if (doSort) { | |
| 493 expectedSorted.sort(); | |
| 494 resultSorted.sort(); | |
| 495 } | |
| 496 | |
| 497 for (var i = 0; i < expectedSorted.length; ++i) { | |
| 498 assertEquals(expectedSorted[i], resultSorted[i], errorMessage); | |
| 499 } | |
| 500 }, | |
| 501 | |
| 502 /** | |
| 503 * Verifies that the correct pages are currently open/visible. | |
| 504 * @param {!Array<string>} expectedPages An array of page names expected to | |
| 505 * be open, with the topmost listed last. | |
| 506 * @param {string=} opt_expectedUrl The URL path, including hash, expected to | |
| 507 * be open. If undefined, the topmost (last) page name in |expectedPages| | |
| 508 * will be used. In either case, 'chrome://settings-frame/' will be | |
| 509 * prepended. | |
| 510 * @private | |
| 511 */ | |
| 512 verifyOpenPages_: function(expectedPages, opt_expectedUrl) { | |
| 513 // Check the topmost page. | |
| 514 expectEquals(null, PageManager.getVisibleBubble()); | |
| 515 var currentPage = PageManager.getTopmostVisiblePage(); | |
| 516 | |
| 517 var lastExpected = expectedPages[expectedPages.length - 1]; | |
| 518 expectEquals(lastExpected, currentPage.name); | |
| 519 // We'd like to check the title too, but we have to load the settings-frame | |
| 520 // instead of the outer settings page in order to have access to | |
| 521 // OptionsPage, and setting the title from within the settings-frame fails | |
| 522 // because of cross-origin access restrictions. | |
| 523 // TODO(pamg): Add a test fixture that loads chrome://settings and uses | |
| 524 // UI elements to access sub-pages, so we can test the titles and | |
| 525 // search-page URLs. | |
| 526 var expectedUrl = (typeof opt_expectedUrl == 'undefined') ? | |
| 527 lastExpected : opt_expectedUrl; | |
| 528 var fullExpectedUrl = 'chrome://settings-frame/' + expectedUrl; | |
| 529 expectEquals(fullExpectedUrl, window.location.href); | |
| 530 | |
| 531 // Collect open pages. | |
| 532 var allPageNames = Object.keys(PageManager.registeredPages).concat( | |
| 533 Object.keys(PageManager.registeredOverlayPages)); | |
| 534 var openPages = []; | |
| 535 for (var i = 0; i < allPageNames.length; ++i) { | |
| 536 var name = allPageNames[i]; | |
| 537 var page = PageManager.registeredPages[name] || | |
| 538 PageManager.registeredOverlayPages[name]; | |
| 539 if (page.visible) | |
| 540 openPages.push(page.name); | |
| 541 } | |
| 542 | |
| 543 this.compareArrays_(expectedPages, openPages, true, 'Open pages'); | |
| 544 }, | |
| 545 | |
| 546 /* | |
| 547 * Verifies that the correct URLs are listed in the history. Asynchronous. | |
| 548 * @param {!Array<string>} expectedHistory An array of URL paths expected to | |
| 549 * be in the tab navigation history, sorted by visit time, including the | |
| 550 * current page as the last entry. The base URL (chrome://settings-frame/) | |
| 551 * will be prepended to each. An initial 'about:blank' history entry is | |
| 552 * assumed and should not be included in this list. | |
| 553 * @param {Function=} callback A function to be called after the history has | |
| 554 * been verified successfully. May be undefined. | |
| 555 * @private | |
| 556 */ | |
| 557 verifyHistory_: function(expectedHistory, callback) { | |
| 558 var self = this; | |
| 559 OptionsWebUIExtendedTest.verifyHistoryCallback = function(results) { | |
| 560 // The history always starts with a blank page. | |
| 561 assertEquals('about:blank', results.shift()); | |
| 562 var fullExpectedHistory = []; | |
| 563 for (var i = 0; i < expectedHistory.length; ++i) { | |
| 564 fullExpectedHistory.push( | |
| 565 'chrome://settings-frame/' + expectedHistory[i]); | |
| 566 } | |
| 567 self.compareArrays_(fullExpectedHistory, results, false, 'History'); | |
| 568 callback(); | |
| 569 }; | |
| 570 | |
| 571 // The C++ fixture will call verifyHistoryCallback with the results. | |
| 572 chrome.send('optionsTestReportHistory'); | |
| 573 }, | |
| 574 | |
| 575 /** | |
| 576 * Overrides the page callbacks for the given PageManager overlay to verify | |
| 577 * that they are not called. | |
| 578 * @param {Object} overlay The singleton instance of the overlay. | |
| 579 * @private | |
| 580 */ | |
| 581 prohibitChangesToOverlay_: function(overlay) { | |
| 582 overlay.initializePage = | |
| 583 overlay.didShowPage = | |
| 584 overlay.didClosePage = function() { | |
| 585 assertTrue(false, | |
| 586 'Overlay was affected when changes were prohibited.'); | |
| 587 }; | |
| 588 }, | |
| 589 }; | |
| 590 | |
| 591 /** | |
| 592 * Set by verifyHistory_ to incorporate a followup callback, then called by the | |
| 593 * C++ fixture with the navigation history to be verified. | |
| 594 * @type {Function} | |
| 595 */ | |
| 596 OptionsWebUIExtendedTest.verifyHistoryCallback = null; | |
| 597 | |
| 598 // Show the search page with no query string, to fall back to the settings page. | |
| 599 // Test disabled because it's flaky. crbug.com/303841 | |
| 600 TEST_F('OptionsWebUIExtendedTest', 'DISABLED_ShowSearchPageNoQuery', | |
| 601 function() { | |
| 602 PageManager.showPageByName('search'); | |
| 603 this.verifyOpenPages_(['settings']); | |
| 604 this.verifyHistory_(['settings'], testDone); | |
| 605 }); | |
| 606 | |
| 607 // Manipulate the search page via the search field. | |
| 608 TEST_F('OptionsWebUIExtendedTest', 'ShowSearchFromField', function() { | |
| 609 $('search-field').onsearch({currentTarget: {value: 'query'}}); | |
| 610 this.verifyOpenPages_(['settings', 'search'], 'search#query'); | |
| 611 this.verifyHistory_(['', 'search#query'], function() { | |
| 612 $('search-field').onsearch({currentTarget: {value: 'query2'}}); | |
| 613 this.verifyOpenPages_(['settings', 'search'], 'search#query2'); | |
| 614 this.verifyHistory_(['', 'search#query', 'search#query2'], function() { | |
| 615 $('search-field').onsearch({currentTarget: {value: ''}}); | |
| 616 this.verifyOpenPages_(['settings'], ''); | |
| 617 this.verifyHistory_(['', 'search#query', 'search#query2', ''], testDone); | |
| 618 }.bind(this)); | |
| 619 }.bind(this)); | |
| 620 }); | |
| 621 | |
| 622 // Show a page without updating history. | |
| 623 TEST_F('OptionsWebUIExtendedTest', 'ShowPageNoHistory', function() { | |
| 624 this.verifyOpenPages_(['settings'], ''); | |
| 625 PageManager.showPageByName('search', true, {hash: '#query'}); | |
| 626 | |
| 627 // The settings page is also still "open" (i.e., visible), in order to show | |
| 628 // the search results. Furthermore, the URL hasn't been updated in the parent | |
| 629 // page, because we've loaded the chrome-settings frame instead of the whole | |
| 630 // settings page, so the cross-origin call to set the URL fails. | |
| 631 this.verifyOpenPages_(['settings', 'search'], 'search#query'); | |
| 632 var self = this; | |
| 633 this.verifyHistory_(['', 'search#query'], function() { | |
| 634 PageManager.showPageByName('settings', false); | |
| 635 self.verifyOpenPages_(['settings'], 'search#query'); | |
| 636 self.verifyHistory_(['', 'search#query'], testDone); | |
| 637 }); | |
| 638 }); | |
| 639 | |
| 640 TEST_F('OptionsWebUIExtendedTest', 'ShowPageWithHistory', function() { | |
| 641 PageManager.showPageByName('search', true, {hash: '#query'}); | |
| 642 var self = this; | |
| 643 this.verifyHistory_(['', 'search#query'], function() { | |
| 644 PageManager.showPageByName('settings', true); | |
| 645 self.verifyOpenPages_(['settings'], ''); | |
| 646 self.verifyHistory_(['', 'search#query', ''], | |
| 647 testDone); | |
| 648 }); | |
| 649 }); | |
| 650 | |
| 651 TEST_F('OptionsWebUIExtendedTest', 'ShowPageReplaceHistory', function() { | |
| 652 PageManager.showPageByName('search', true, {hash: '#query'}); | |
| 653 var self = this; | |
| 654 this.verifyHistory_(['', 'search#query'], function() { | |
| 655 PageManager.showPageByName('settings', true, {'replaceState': true}); | |
| 656 self.verifyOpenPages_(['settings'], ''); | |
| 657 self.verifyHistory_(['', ''], testDone); | |
| 658 }); | |
| 659 }); | |
| 660 | |
| 661 // This should be identical to ShowPageWithHisory. | |
| 662 TEST_F('OptionsWebUIExtendedTest', 'NavigateToPage', function() { | |
| 663 PageManager.showPageByName('search', true, {hash: '#query'}); | |
| 664 var self = this; | |
| 665 this.verifyHistory_(['', 'search#query'], function() { | |
| 666 PageManager.showPageByName('settings'); | |
| 667 self.verifyOpenPages_(['settings'], ''); | |
| 668 self.verifyHistory_(['', 'search#query', ''], testDone); | |
| 669 }); | |
| 670 }); | |
| 671 | |
| 672 // Settings overlays are much more straightforward than settings pages, opening | |
| 673 // normally with none of the latter's quirks in the expected history or URL. | |
| 674 TEST_F('OptionsWebUIExtendedTest', 'ShowOverlayNoHistory', function() { | |
| 675 // Open a layer-1 overlay, not updating history. | |
| 676 PageManager.showPageByName('languages', false); | |
| 677 this.verifyOpenPages_(['settings', 'languages'], ''); | |
| 678 | |
| 679 var self = this; | |
| 680 this.verifyHistory_([''], function() { | |
| 681 // Open a layer-2 overlay for which the layer-1 is a parent, not updating | |
| 682 // history. | |
| 683 PageManager.showPageByName('addLanguage', false); | |
| 684 self.verifyOpenPages_(['settings', 'languages', 'addLanguage'], | |
| 685 ''); | |
| 686 self.verifyHistory_([''], testDone); | |
| 687 }); | |
| 688 }); | |
| 689 | |
| 690 TEST_F('OptionsWebUIExtendedTest', 'ShowOverlayWithHistory', function() { | |
| 691 // Open a layer-1 overlay, updating history. | |
| 692 PageManager.showPageByName('languages', true); | |
| 693 this.verifyOpenPages_(['settings', 'languages']); | |
| 694 | |
| 695 var self = this; | |
| 696 this.verifyHistory_(['', 'languages'], function() { | |
| 697 // Open a layer-2 overlay, updating history. | |
| 698 PageManager.showPageByName('addLanguage', true); | |
| 699 self.verifyOpenPages_(['settings', 'languages', 'addLanguage']); | |
| 700 self.verifyHistory_(['', 'languages', 'addLanguage'], testDone); | |
| 701 }); | |
| 702 }); | |
| 703 | |
| 704 TEST_F('OptionsWebUIExtendedTest', 'ShowOverlayReplaceHistory', function() { | |
| 705 // Open a layer-1 overlay, updating history. | |
| 706 PageManager.showPageByName('languages', true); | |
| 707 var self = this; | |
| 708 this.verifyHistory_(['', 'languages'], function() { | |
| 709 // Open a layer-2 overlay, replacing history. | |
| 710 PageManager.showPageByName('addLanguage', true, {'replaceState': true}); | |
| 711 self.verifyOpenPages_(['settings', 'languages', 'addLanguage']); | |
| 712 self.verifyHistory_(['', 'addLanguage'], testDone); | |
| 713 }); | |
| 714 }); | |
| 715 | |
| 716 // Directly show an overlay further above this page, i.e. one for which the | |
| 717 // current page is an ancestor but not a parent. | |
| 718 TEST_F('OptionsWebUIExtendedTest', 'ShowOverlayFurtherAbove', function() { | |
| 719 // Open a layer-2 overlay directly. | |
| 720 PageManager.showPageByName('addLanguage', true); | |
| 721 this.verifyOpenPages_(['settings', 'languages', 'addLanguage']); | |
| 722 var self = this; | |
| 723 this.verifyHistory_(['', 'addLanguage'], testDone); | |
| 724 }); | |
| 725 | |
| 726 // Directly show a layer-2 overlay for which the layer-1 overlay is not a | |
| 727 // parent. | |
| 728 TEST_F('OptionsWebUIExtendedTest', 'ShowUnrelatedOverlay', function() { | |
| 729 // Open a layer-1 overlay. | |
| 730 PageManager.showPageByName('languages', true); | |
| 731 this.verifyOpenPages_(['settings', 'languages']); | |
| 732 | |
| 733 var self = this; | |
| 734 this.verifyHistory_(['', 'languages'], function() { | |
| 735 // Open an unrelated layer-2 overlay. | |
| 736 PageManager.showPageByName('cookies', true); | |
| 737 self.verifyOpenPages_(['settings', 'content', 'cookies']); | |
| 738 self.verifyHistory_(['', 'languages', 'cookies'], testDone); | |
| 739 }); | |
| 740 }); | |
| 741 | |
| 742 // Close an overlay. | |
| 743 TEST_F('OptionsWebUIExtendedTest', 'CloseOverlay', function() { | |
| 744 // Open a layer-1 overlay, then a layer-2 overlay on top of it. | |
| 745 PageManager.showPageByName('languages', true); | |
| 746 this.verifyOpenPages_(['settings', 'languages']); | |
| 747 PageManager.showPageByName('addLanguage', true); | |
| 748 this.verifyOpenPages_(['settings', 'languages', 'addLanguage']); | |
| 749 | |
| 750 var self = this; | |
| 751 this.verifyHistory_(['', 'languages', 'addLanguage'], function() { | |
| 752 // Close the layer-2 overlay. | |
| 753 PageManager.closeOverlay(); | |
| 754 self.verifyOpenPages_(['settings', 'languages']); | |
| 755 self.verifyHistory_( | |
| 756 ['', 'languages', 'addLanguage', 'languages'], | |
| 757 function() { | |
| 758 // Close the layer-1 overlay. | |
| 759 PageManager.closeOverlay(); | |
| 760 self.verifyOpenPages_(['settings'], ''); | |
| 761 self.verifyHistory_( | |
| 762 ['', 'languages', 'addLanguage', 'languages', ''], | |
| 763 function noop() {}); | |
| 764 waitUntilHidden([$('overlay-container-1'), $('overlay-container-2')]); | |
| 765 }); | |
| 766 }); | |
| 767 }); | |
| 768 | |
| 769 // Hashes are maintained separately for each page and are preserved when | |
| 770 // overlays close. | |
| 771 TEST_F('OptionsWebUIExtendedTest', 'CloseOverlayWithHashes', function() { | |
| 772 // Open an overlay on top of the search page. | |
| 773 PageManager.showPageByName('search', true, {hash: '#1'}); | |
| 774 this.verifyOpenPages_(['settings', 'search'], 'search#1'); | |
| 775 PageManager.showPageByName('languages', true, {hash: '#2'}); | |
| 776 this.verifyOpenPages_(['settings', 'search', 'languages'], | |
| 777 'languages#2'); | |
| 778 PageManager.showPageByName('addLanguage', true, {hash: '#3'}); | |
| 779 this.verifyOpenPages_(['settings', 'search', 'languages', 'addLanguage'], | |
| 780 'addLanguage#3'); | |
| 781 | |
| 782 this.verifyHistory_(['', 'search#1', 'languages#2', 'addLanguage#3'], | |
| 783 function() { | |
| 784 // Close the layer-2 overlay. | |
| 785 PageManager.closeOverlay(); | |
| 786 this.verifyOpenPages_(['settings', 'search', 'languages'], 'languages#2'); | |
| 787 this.verifyHistory_( | |
| 788 ['', 'search#1', 'languages#2', 'addLanguage#3', 'languages#2'], | |
| 789 function() { | |
| 790 // Close the layer-1 overlay. | |
| 791 PageManager.closeOverlay(); | |
| 792 this.verifyOpenPages_(['settings', 'search'], 'search#1'); | |
| 793 this.verifyHistory_( | |
| 794 ['', 'search#1', 'languages#2', 'addLanguage#3', 'languages#2', | |
| 795 'search#1'], | |
| 796 function noop() {}); | |
| 797 waitUntilHidden([$('overlay-container-1'), $('overlay-container-2')]); | |
| 798 }.bind(this)); | |
| 799 }.bind(this)); | |
| 800 }); | |
| 801 | |
| 802 // Test that closing an overlay that did not push history when opening does not | |
| 803 // again push history. | |
| 804 TEST_F('OptionsWebUIExtendedTest', 'CloseOverlayNoHistory', function() { | |
| 805 // Open the do not track confirmation prompt. | |
| 806 PageManager.showPageByName('doNotTrackConfirm', false); | |
| 807 | |
| 808 // Opening the prompt does not add to the history. | |
| 809 this.verifyHistory_([''], function() { | |
| 810 // Close the overlay. | |
| 811 PageManager.closeOverlay(); | |
| 812 // Still no history changes. | |
| 813 this.verifyHistory_([''], function noop() {}); | |
| 814 waitUntilHidden([$('overlay-container-1')]); | |
| 815 }.bind(this)); | |
| 816 }); | |
| 817 | |
| 818 // Make sure an overlay isn't closed (even temporarily) when another overlay is | |
| 819 // opened on top. | |
| 820 TEST_F('OptionsWebUIExtendedTest', 'OverlayAboveNoReset', function() { | |
| 821 // Open a layer-1 overlay. | |
| 822 PageManager.showPageByName('languages', true); | |
| 823 this.verifyOpenPages_(['settings', 'languages']); | |
| 824 | |
| 825 // Open a layer-2 overlay on top. This should not close 'languages'. | |
| 826 this.prohibitChangesToOverlay_(options.LanguageOptions.getInstance()); | |
| 827 PageManager.showPageByName('addLanguage', true); | |
| 828 this.verifyOpenPages_(['settings', 'languages', 'addLanguage']); | |
| 829 testDone(); | |
| 830 }); | |
| 831 | |
| 832 TEST_F('OptionsWebUIExtendedTest', 'OverlayTabNavigation', function() { | |
| 833 // Open a layer-1 overlay, then a layer-2 overlay on top of it. | |
| 834 PageManager.showPageByName('languages', true); | |
| 835 PageManager.showPageByName('addLanguage', true); | |
| 836 var self = this; | |
| 837 | |
| 838 // Go back twice, then forward twice. | |
| 839 self.verifyOpenPages_(['settings', 'languages', 'addLanguage']); | |
| 840 self.verifyHistory_(['', 'languages', 'addLanguage'], function() { | |
| 841 window.history.back(); | |
| 842 waitForPopstate(function() { | |
| 843 self.verifyOpenPages_(['settings', 'languages']); | |
| 844 self.verifyHistory_(['', 'languages'], function() { | |
| 845 window.history.back(); | |
| 846 waitForPopstate(function() { | |
| 847 self.verifyOpenPages_(['settings'], ''); | |
| 848 self.verifyHistory_([''], function() { | |
| 849 window.history.forward(); | |
| 850 waitForPopstate(function() { | |
| 851 self.verifyOpenPages_(['settings', 'languages']); | |
| 852 self.verifyHistory_(['', 'languages'], function() { | |
| 853 window.history.forward(); | |
| 854 waitForPopstate(function() { | |
| 855 self.verifyOpenPages_( | |
| 856 ['settings', 'languages', 'addLanguage']); | |
| 857 self.verifyHistory_( | |
| 858 ['', 'languages', 'addLanguage'], testDone); | |
| 859 }); | |
| 860 }); | |
| 861 }); | |
| 862 }); | |
| 863 }); | |
| 864 }); | |
| 865 }); | |
| 866 }); | |
| 867 }); | |
| 868 | |
| 869 // Going "back" to an overlay that's a child of the current overlay shouldn't | |
| 870 // close the current one. | |
| 871 TEST_F('OptionsWebUIExtendedTest', 'OverlayBackToChild', function() { | |
| 872 // Open a layer-1 overlay, then a layer-2 overlay on top of it. | |
| 873 PageManager.showPageByName('languages', true); | |
| 874 PageManager.showPageByName('addLanguage', true); | |
| 875 var self = this; | |
| 876 | |
| 877 self.verifyOpenPages_(['settings', 'languages', 'addLanguage']); | |
| 878 self.verifyHistory_(['', 'languages', 'addLanguage'], function() { | |
| 879 // Close the top overlay, then go back to it. | |
| 880 PageManager.closeOverlay(); | |
| 881 self.verifyOpenPages_(['settings', 'languages']); | |
| 882 self.verifyHistory_( | |
| 883 ['', 'languages', 'addLanguage', 'languages'], | |
| 884 function() { | |
| 885 // Going back to the 'addLanguage' page should not close 'languages'. | |
| 886 self.prohibitChangesToOverlay_(options.LanguageOptions.getInstance()); | |
| 887 window.history.back(); | |
| 888 waitForPopstate(function() { | |
| 889 self.verifyOpenPages_(['settings', 'languages', 'addLanguage']); | |
| 890 self.verifyHistory_(['', 'languages', 'addLanguage'], | |
| 891 testDone); | |
| 892 }); | |
| 893 }); | |
| 894 }); | |
| 895 }); | |
| 896 | |
| 897 // Going back to an unrelated overlay should close the overlay and its parent. | |
| 898 TEST_F('OptionsWebUIExtendedTest', 'OverlayBackToUnrelated', function() { | |
| 899 // Open a layer-1 overlay, then an unrelated layer-2 overlay. | |
| 900 PageManager.showPageByName('languages', true); | |
| 901 PageManager.showPageByName('cookies', true); | |
| 902 var self = this; | |
| 903 self.verifyOpenPages_(['settings', 'content', 'cookies']); | |
| 904 self.verifyHistory_(['', 'languages', 'cookies'], function() { | |
| 905 window.history.back(); | |
| 906 waitForPopstate(function() { | |
| 907 self.verifyOpenPages_(['settings', 'languages']); | |
| 908 testDone(); | |
| 909 }); | |
| 910 }); | |
| 911 }); | |
| 912 | |
| 913 // Verify history changes properly while the page is loading. | |
| 914 TEST_F('OptionsWebUIExtendedTest', 'HistoryUpdatedAfterLoading', function() { | |
| 915 var loc = location.href; | |
| 916 | |
| 917 document.documentElement.classList.add('loading'); | |
| 918 assertTrue(PageManager.isLoading()); | |
| 919 PageManager.showPageByName('searchEngines'); | |
| 920 expectNotEquals(loc, location.href); | |
| 921 | |
| 922 document.documentElement.classList.remove('loading'); | |
| 923 assertFalse(PageManager.isLoading()); | |
| 924 PageManager.showDefaultPage(); | |
| 925 expectEquals(loc, location.href); | |
| 926 | |
| 927 testDone(); | |
| 928 }); | |
| 929 | |
| 930 // A tip should be shown or hidden depending on whether this profile manages any | |
| 931 // supervised users. | |
| 932 TEST_F('OptionsWebUIExtendedTest', 'SupervisingUsers', function() { | |
| 933 // We start managing no supervised users. | |
| 934 assertTrue($('profiles-supervised-dashboard-tip').hidden); | |
| 935 | |
| 936 // Remove all supervised users, then add some, watching for the pref change | |
| 937 // notifications and UI updates in each case. Any non-empty pref dictionary | |
| 938 // is interpreted as having supervised users. | |
| 939 chrome.send('optionsTestSetPref', [SUPERVISED_USERS_PREF, {key: 'value'}]); | |
| 940 waitForResponse(BrowserOptions, 'updateManagesSupervisedUsers', function() { | |
| 941 assertFalse($('profiles-supervised-dashboard-tip').hidden); | |
| 942 chrome.send('optionsTestSetPref', [SUPERVISED_USERS_PREF, {}]); | |
| 943 waitForResponse(BrowserOptions, 'updateManagesSupervisedUsers', function() { | |
| 944 assertTrue($('profiles-supervised-dashboard-tip').hidden); | |
| 945 testDone(); | |
| 946 }); | |
| 947 }); | |
| 948 }); | |
| 949 | |
| 950 /** | |
| 951 * TestFixture that loads the options page at a bogus URL. | |
| 952 * @extends {OptionsWebUIExtendedTest} | |
| 953 * @constructor | |
| 954 */ | |
| 955 function OptionsWebUIRedirectTest() { | |
| 956 OptionsWebUIExtendedTest.call(this); | |
| 957 } | |
| 958 | |
| 959 OptionsWebUIRedirectTest.prototype = { | |
| 960 __proto__: OptionsWebUIExtendedTest.prototype, | |
| 961 | |
| 962 /** @override */ | |
| 963 browsePreload: 'chrome://settings-frame/nonexistantPage', | |
| 964 }; | |
| 965 | |
| 966 TEST_F('OptionsWebUIRedirectTest', 'TestURL', function() { | |
| 967 assertEquals('chrome://settings-frame/', document.location.href); | |
| 968 this.verifyHistory_([''], testDone); | |
| 969 }); | |
| OLD | NEW |