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 "chrome/browser/ui/webui/options/options_browsertest.h"'); 6 7 /** @const */ var MANAGED_USERS_PREF = 'profile.managed_users'; 8 9 /** 10 * Wait for the method specified by |methodName|, on the |object| object, to be 11 * called, then execute |afterFunction|. 12 */ 13 function waitForResponse(object, methodName, afterFunction) { 14 var originalCallback = object[methodName]; 15 16 // Install a wrapper that temporarily replaces the original function. 17 object[methodName] = function() { 18 object[methodName] = originalCallback; 19 originalCallback.apply(this, arguments); 20 afterFunction(); 21 }; 22 } 23 24 /** 25 * Wait for the global window.onpopstate callback to be called (after a tab 26 * history navigation), then execute |afterFunction|. 27 */ 28 function waitForPopstate(afterFunction) { 29 waitForResponse(window, 'onpopstate', afterFunction); 30 } 31 32 /** 33 * TestFixture for OptionsPage WebUI testing. 34 * @extends {testing.Test} 35 * @constructor 36 */ 37 function OptionsWebUITest() {} 38 39 OptionsWebUITest.prototype = { 40 __proto__: testing.Test.prototype, 41 42 /** @override */ 43 accessibilityIssuesAreErrors: true, 44 45 /** @override */ 46 setUp: function() { 47 // user-image-stream is a streaming video element used for capturing a 48 // user image during OOBE. 49 this.accessibilityAuditConfig.ignoreSelectors('videoWithoutCaptions', 50 '.user-image-stream'); 51 }, 52 53 /** 54 * Browse to the options page & call our preLoad(). 55 */ 56 browsePreload: 'chrome://settings-frame', 57 58 isAsync: true, 59 60 /** 61 * Register a mock handler to ensure expectations are met and options pages 62 * behave correctly. 63 */ 64 preLoad: function() { 65 this.makeAndRegisterMockHandler( 66 ['defaultZoomFactorAction', 67 'fetchPrefs', 68 'observePrefs', 69 'setBooleanPref', 70 'setIntegerPref', 71 'setDoublePref', 72 'setStringPref', 73 'setObjectPref', 74 'clearPref', 75 'coreOptionsUserMetricsAction', 76 ]); 77 78 // Register stubs for methods expected to be called before/during tests. 79 // Specific expectations can be made in the tests themselves. 80 this.mockHandler.stubs().fetchPrefs(ANYTHING); 81 this.mockHandler.stubs().observePrefs(ANYTHING); 82 this.mockHandler.stubs().coreOptionsUserMetricsAction(ANYTHING); 83 }, 84 }; 85 86 // Crashes on Mac only. See http://crbug.com/79181 87 GEN('#if defined(OS_MACOSX)'); 88 GEN('#define MAYBE_testSetBooleanPrefTriggers ' + 89 'DISABLED_testSetBooleanPrefTriggers'); 90 GEN('#else'); 91 GEN('#define MAYBE_testSetBooleanPrefTriggers testSetBooleanPrefTriggers'); 92 GEN('#endif // defined(OS_MACOSX)'); 93 94 TEST_F('OptionsWebUITest', 'MAYBE_testSetBooleanPrefTriggers', function() { 95 // TODO(dtseng): make generic to click all buttons. 96 var showHomeButton = $('show-home-button'); 97 var trueListValue = [ 98 'browser.show_home_button', 99 true, 100 'Options_Homepage_HomeButton', 101 ]; 102 // Note: this expectation is checked in testing::Test::tearDown. 103 this.mockHandler.expects(once()).setBooleanPref(trueListValue); 104 105 // Cause the handler to be called. 106 showHomeButton.click(); 107 showHomeButton.blur(); 108 testDone(); 109 }); 110 111 // Not meant to run on ChromeOS at this time. 112 // Not finishing in windows. http://crbug.com/81723 113 TEST_F('OptionsWebUITest', 'DISABLED_testRefreshStaysOnCurrentPage', 114 function() { 115 assertTrue($('search-engine-manager-page').hidden); 116 var item = $('manage-default-search-engines'); 117 item.click(); 118 119 assertFalse($('search-engine-manager-page').hidden); 120 121 window.location.reload(); 122 123 assertEquals('chrome://settings-frame/searchEngines', document.location.href); 124 assertFalse($('search-engine-manager-page').hidden); 125 testDone(); 126 }); 127 128 /** 129 * Test the default zoom factor select element. 130 */ 131 TEST_F('OptionsWebUITest', 'testDefaultZoomFactor', function() { 132 // The expected minimum length of the |defaultZoomFactor| element. 133 var defaultZoomFactorMinimumLength = 10; 134 // Verify that the zoom factor element exists. 135 var defaultZoomFactor = $('defaultZoomFactor'); 136 assertNotEquals(defaultZoomFactor, null); 137 138 // Verify that the zoom factor element has a reasonable number of choices. 139 expectGE(defaultZoomFactor.options.length, defaultZoomFactorMinimumLength); 140 141 // Simulate a change event, selecting the highest zoom value. Verify that 142 // the javascript handler was invoked once. 143 this.mockHandler.expects(once()).defaultZoomFactorAction(NOT_NULL). 144 will(callFunction(function() { })); 145 defaultZoomFactor.selectedIndex = defaultZoomFactor.options.length - 1; 146 var event = { target: defaultZoomFactor }; 147 if (defaultZoomFactor.onchange) defaultZoomFactor.onchange(event); 148 testDone(); 149 }); 150 151 // If |confirmInterstitial| is true, the OK button of the Do Not Track 152 // interstitial is pressed, otherwise the abort button is pressed. 153 OptionsWebUITest.prototype.testDoNotTrackInterstitial = 154 function(confirmInterstitial) { 155 Preferences.prefsFetchedCallback({'enable_do_not_track': {'value': false } }); 156 var buttonToClick = confirmInterstitial ? $('do-not-track-confirm-ok') 157 : $('do-not-track-confirm-cancel'); 158 var dntCheckbox = $('do-not-track-enabled'); 159 var dntOverlay = OptionsPage.registeredOverlayPages['donottrackconfirm']; 160 assertFalse(dntCheckbox.checked); 161 162 var visibleChangeCounter = 0; 163 var visibleChangeHandler = function() { 164 ++visibleChangeCounter; 165 switch (visibleChangeCounter) { 166 case 1: 167 window.setTimeout(function() { 168 assertTrue(dntOverlay.visible); 169 buttonToClick.click(); 170 }, 0); 171 break; 172 case 2: 173 window.setTimeout(function() { 174 assertFalse(dntOverlay.visible); 175 assertEquals(confirmInterstitial, dntCheckbox.checked); 176 dntOverlay.removeEventListener(visibleChangeHandler); 177 testDone(); 178 }, 0); 179 break; 180 default: 181 assertTrue(false); 182 } 183 } 184 dntOverlay.addEventListener('visibleChange', visibleChangeHandler); 185 186 if (confirmInterstitial) { 187 this.mockHandler.expects(once()).setBooleanPref( 188 ['enable_do_not_track', true, 'Options_DoNotTrackCheckbox']); 189 } else { 190 // The mock handler complains if setBooleanPref is called even though 191 // it should not be. 192 } 193 194 dntCheckbox.click(); 195 } 196 197 TEST_F('OptionsWebUITest', 'EnableDoNotTrackAndConfirmInterstitial', 198 function() { 199 this.testDoNotTrackInterstitial(true); 200 }); 201 202 TEST_F('OptionsWebUITest', 'EnableDoNotTrackAndCancelInterstitial', 203 function() { 204 this.testDoNotTrackInterstitial(false); 205 }); 206 207 // Check that the "Do not Track" preference can be correctly disabled. 208 // In order to do that, we need to enable it first. 209 TEST_F('OptionsWebUITest', 'EnableAndDisableDoNotTrack', function() { 210 Preferences.prefsFetchedCallback({'enable_do_not_track': {'value': false } }); 211 var dntCheckbox = $('do-not-track-enabled'); 212 var dntOverlay = OptionsPage.registeredOverlayPages['donottrackconfirm']; 213 assertFalse(dntCheckbox.checked); 214 215 var visibleChangeCounter = 0; 216 var visibleChangeHandler = function() { 217 ++visibleChangeCounter; 218 switch (visibleChangeCounter) { 219 case 1: 220 window.setTimeout(function() { 221 assertTrue(dntOverlay.visible); 222 $('do-not-track-confirm-ok').click(); 223 }, 0); 224 break; 225 case 2: 226 window.setTimeout(function() { 227 assertFalse(dntOverlay.visible); 228 assertTrue(dntCheckbox.checked); 229 dntOverlay.removeEventListener(visibleChangeHandler); 230 dntCheckbox.click(); 231 }, 0); 232 break; 233 default: 234 assertNotReached(); 235 } 236 } 237 dntOverlay.addEventListener('visibleChange', visibleChangeHandler); 238 239 this.mockHandler.expects(once()).setBooleanPref( 240 eq(["enable_do_not_track", true, 'Options_DoNotTrackCheckbox'])); 241 242 var verifyCorrectEndState = function() { 243 window.setTimeout(function() { 244 assertFalse(dntOverlay.visible); 245 assertFalse(dntCheckbox.checked); 246 testDone(); 247 }, 0) 248 } 249 this.mockHandler.expects(once()).setBooleanPref( 250 eq(["enable_do_not_track", false, 'Options_DoNotTrackCheckbox'])).will( 251 callFunction(verifyCorrectEndState)); 252 253 dntCheckbox.click(); 254 }); 255 256 // Verify that preventDefault() is called on 'Enter' keydown events that trigger 257 // the default button. If this doesn't happen, other elements that may get 258 // focus (by the overlay closing for instance), will execute in addition to the 259 // default button. See crbug.com/268336. 260 TEST_F('OptionsWebUITest', 'EnterPreventsDefault', function() { 261 var page = HomePageOverlay.getInstance(); 262 OptionsPage.showPageByName(page.name); 263 var event = new KeyboardEvent('keydown', { 264 'bubbles': true, 265 'cancelable': true, 266 'keyIdentifier': 'Enter' 267 }); 268 assertFalse(event.defaultPrevented); 269 page.pageDiv.dispatchEvent(event); 270 assertTrue(event.defaultPrevented); 271 testDone(); 272 }); 273 274 // Verifies that sending an empty list of indexes to move doesn't crash chrome. 275 TEST_F('OptionsWebUITest', 'emptySelectedIndexesDoesntCrash', function() { 276 chrome.send('dragDropStartupPage', [0, []]); 277 setTimeout(testDone); 278 }); 279 280 // Flaky on win. See http://crbug.com/315250 281 GEN('#if defined(OS_WIN)'); 282 GEN('#define MAYBE_OverlayShowDoesntShift DISABLED_OverlayShowDoesntShift'); 283 GEN('#else'); 284 GEN('#define MAYBE_OverlayShowDoesntShift OverlayShowDoesntShift'); 285 GEN('#endif // defined(OS_WIN)'); 286 287 // An overlay's position should remain the same as it shows. 288 TEST_F('OptionsWebUITest', 'MAYBE_OverlayShowDoesntShift', function() { 289 var overlayName = 'startup'; 290 var overlay = $('startup-overlay'); 291 var frozenPages = document.getElementsByClassName('frozen'); // Gets updated. 292 expectEquals(0, frozenPages.length); 293 294 document.addEventListener('webkitTransitionEnd', function(e) { 295 if (e.target != overlay) 296 return; 297 298 assertFalse(overlay.classList.contains('transparent')); 299 expectEquals(numFrozenPages, frozenPages.length); 300 testDone(); 301 }); 302 303 OptionsPage.navigateToPage(overlayName); 304 var numFrozenPages = frozenPages.length; 305 expectGT(numFrozenPages, 0); 306 }); 307 308 /** 309 * TestFixture for OptionsPage WebUI testing including tab history and support 310 * for preference manipulation. If you don't need the features in the C++ 311 * fixture, use the simpler OptionsWebUITest (above) instead. 312 * @extends {testing.Test} 313 * @constructor 314 */ 315 function OptionsWebUIExtendedTest() {} 316 317 OptionsWebUIExtendedTest.prototype = { 318 __proto__: testing.Test.prototype, 319 320 /** @override */ 321 browsePreload: 'chrome://settings-frame', 322 323 /** @override */ 324 typedefCppFixture: 'OptionsBrowserTest', 325 326 testGenPreamble: function() { 327 // Start with no supervised users managed by this profile. 328 GEN(' ClearPref("' + MANAGED_USERS_PREF + '");'); 329 }, 330 331 /** @override */ 332 isAsync: true, 333 334 /** @override */ 335 setUp: function () { 336 // user-image-stream is a streaming video element used for capturing a 337 // user image during OOBE. 338 this.accessibilityAuditConfig.ignoreSelectors('videoWithoutCaptions', 339 '.user-image-stream'); 340 }, 341 342 /** 343 * Asserts that two non-nested arrays are equal. The arrays must contain only 344 * plain data types, no nested arrays or other objects. 345 * @param {Array} expected An array of expected values. 346 * @param {Array} result An array of actual values. 347 * @param {boolean} doSort If true, the arrays will be sorted before being 348 * compared. 349 * @param {string} description A brief description for the array of actual 350 * values, to use in an error message if the arrays differ. 351 * @private 352 */ 353 compareArrays_: function(expected, result, doSort, description) { 354 var errorMessage = '\n' + description + ': ' + result + 355 '\nExpected: ' + expected; 356 assertEquals(expected.length, result.length, errorMessage); 357 358 var expectedSorted = expected.slice(); 359 var resultSorted = result.slice(); 360 if (doSort) { 361 expectedSorted.sort(); 362 resultSorted.sort(); 363 } 364 365 for (var i = 0; i < expectedSorted.length; ++i) { 366 assertEquals(expectedSorted[i], resultSorted[i], errorMessage); 367 } 368 }, 369 370 /** 371 * Verifies that the correct pages are currently open/visible. 372 * @param {!Array.<string>} expectedPages An array of page names expected to 373 * be open, with the topmost listed last. 374 * @param {string=} expectedUrl The URL path, including hash, expected to be 375 * open. If undefined, the topmost (last) page name in |expectedPages| 376 * will be used. In either case, 'chrome://settings-frame/' will be 377 * prepended. 378 * @private 379 */ 380 verifyOpenPages_: function(expectedPages, expectedUrl) { 381 // Check the topmost page. 382 assertEquals(null, OptionsPage.getVisibleBubble()); 383 var currentPage = OptionsPage.getTopmostVisiblePage(); 384 385 var lastExpected = expectedPages[expectedPages.length - 1]; 386 assertEquals(lastExpected, currentPage.name); 387 // We'd like to check the title too, but we have to load the settings-frame 388 // instead of the outer settings page in order to have access to 389 // OptionsPage, and setting the title from within the settings-frame fails 390 // because of cross-origin access restrictions. 391 // TODO(pamg): Add a test fixture that loads chrome://settings and uses 392 // UI elements to access sub-pages, so we can test the titles and 393 // search-page URLs. 394 var fullExpectedUrl = 'chrome://settings-frame/' + 395 (expectedUrl ? expectedUrl : lastExpected); 396 assertEquals(fullExpectedUrl, window.location.href); 397 398 // Collect open pages. 399 var allPageNames = Object.keys(OptionsPage.registeredPages).concat( 400 Object.keys(OptionsPage.registeredOverlayPages)); 401 var openPages = []; 402 for (var i = 0; i < allPageNames.length; ++i) { 403 var name = allPageNames[i]; 404 var page = OptionsPage.registeredPages[name] || 405 OptionsPage.registeredOverlayPages[name]; 406 if (page.visible) 407 openPages.push(page.name); 408 } 409 410 this.compareArrays_(expectedPages, openPages, true, 'Open pages'); 411 }, 412 413 /* 414 * Verifies that the correct URLs are listed in the history. Asynchronous. 415 * @param {!Array.<string>} expectedHistory An array of URL paths expected to 416 * be in the tab navigation history, sorted by visit time, including the 417 * current page as the last entry. The base URL (chrome://settings-frame/) 418 * will be prepended to each. An initial 'about:blank' history entry is 419 * assumed and should not be included in this list. 420 * @param {Function=} callback A function to be called after the history has 421 * been verified successfully. May be undefined. 422 * @private 423 */ 424 verifyHistory_: function(expectedHistory, callback) { 425 var self = this; 426 OptionsWebUIExtendedTest.verifyHistoryCallback = function(results) { 427 // The history always starts with a blank page. 428 assertEquals('about:blank', results.shift()); 429 var fullExpectedHistory = []; 430 for (var i = 0; i < expectedHistory.length; ++i) { 431 fullExpectedHistory.push( 432 'chrome://settings-frame/' + expectedHistory[i]); 433 } 434 self.compareArrays_(fullExpectedHistory, results, false, 'History'); 435 callback(); 436 }; 437 438 // The C++ fixture will call verifyHistoryCallback with the results. 439 chrome.send('optionsTestReportHistory'); 440 }, 441 442 /** 443 * Overrides the page callbacks for the given OptionsPage overlay to verify 444 * that they are not called. 445 * @param {Object} overlay The singleton instance of the overlay. 446 * @private 447 */ 448 prohibitChangesToOverlay_: function(overlay) { 449 overlay.initializePage = 450 overlay.didShowPage = 451 overlay.didClosePage = function() { 452 assertTrue(false, 453 'Overlay was affected when changes were prohibited.'); 454 }; 455 }, 456 }; 457 458 /* 459 * Set by verifyHistory_ to incorporate a followup callback, then called by the 460 * C++ fixture with the navigation history to be verified. 461 * @type {Function} 462 */ 463 OptionsWebUIExtendedTest.verifyHistoryCallback = null; 464 465 // Show the search page with no query string, to fall back to the settings page. 466 // Test disabled because it's flaky. crbug.com/303841 467 TEST_F('OptionsWebUIExtendedTest', 'DISABLED_ShowSearchPageNoQuery', 468 function() { 469 OptionsPage.showPageByName('search'); 470 this.verifyOpenPages_(['settings']); 471 this.verifyHistory_(['settings'], testDone); 472 }); 473 474 // Show a page without updating history. 475 TEST_F('OptionsWebUIExtendedTest', 'ShowPageNoHistory', function() { 476 this.verifyOpenPages_(['settings']); 477 // There are only two main pages, 'settings' and 'search'. It's not possible 478 // to show the search page using OptionsPage.showPageByName, because it 479 // reverts to the settings page if it has no search text set. So we show the 480 // search page by performing a search, then test showPageByName. 481 $('search-field').onsearch({currentTarget: {value: 'query'}}); 482 483 // The settings page is also still "open" (i.e., visible), in order to show 484 // the search results. Furthermore, the URL hasn't been updated in the parent 485 // page, because we've loaded the chrome-settings frame instead of the whole 486 // settings page, so the cross-origin call to set the URL fails. 487 this.verifyOpenPages_(['settings', 'search'], 'settings#query'); 488 var self = this; 489 this.verifyHistory_(['settings', 'settings#query'], function() { 490 OptionsPage.showPageByName('settings', false); 491 self.verifyOpenPages_(['settings'], 'settings#query'); 492 self.verifyHistory_(['settings', 'settings#query'], testDone); 493 }); 494 }); 495 496 TEST_F('OptionsWebUIExtendedTest', 'ShowPageWithHistory', function() { 497 // See comments for ShowPageNoHistory. 498 $('search-field').onsearch({currentTarget: {value: 'query'}}); 499 var self = this; 500 this.verifyHistory_(['settings', 'settings#query'], function() { 501 OptionsPage.showPageByName('settings', true); 502 self.verifyOpenPages_(['settings'], 'settings#query'); 503 self.verifyHistory_(['settings', 'settings#query', 'settings#query'], 504 testDone); 505 }); 506 }); 507 508 TEST_F('OptionsWebUIExtendedTest', 'ShowPageReplaceHistory', function() { 509 // See comments for ShowPageNoHistory. 510 $('search-field').onsearch({currentTarget: {value: 'query'}}); 511 var self = this; 512 this.verifyHistory_(['settings', 'settings#query'], function() { 513 OptionsPage.showPageByName('settings', true, {'replaceState': true}); 514 self.verifyOpenPages_(['settings'], 'settings#query'); 515 self.verifyHistory_(['settings', 'settings#query'], testDone); 516 }); 517 }); 518 519 // This should be identical to ShowPageWithHisory. 520 TEST_F('OptionsWebUIExtendedTest', 'NavigateToPage', function() { 521 // See comments for ShowPageNoHistory. 522 $('search-field').onsearch({currentTarget: {value: 'query'}}); 523 var self = this; 524 this.verifyHistory_(['settings', 'settings#query'], function() { 525 OptionsPage.navigateToPage('settings'); 526 self.verifyOpenPages_(['settings'], 'settings#query'); 527 self.verifyHistory_(['settings', 'settings#query', 'settings#query'], 528 testDone); 529 }); 530 }); 531 532 // Settings overlays are much more straightforward than settings pages, opening 533 // normally with none of the latter's quirks in the expected history or URL. 534 TEST_F('OptionsWebUIExtendedTest', 'ShowOverlayNoHistory', function() { 535 // Open a layer-1 overlay, not updating history. 536 OptionsPage.showPageByName('languages', false); 537 this.verifyOpenPages_(['settings', 'languages'], 'settings'); 538 539 var self = this; 540 this.verifyHistory_(['settings'], function() { 541 // Open a layer-2 overlay for which the layer-1 is a parent, not updating 542 // history. 543 OptionsPage.showPageByName('addLanguage', false); 544 self.verifyOpenPages_(['settings', 'languages', 'addLanguage'], 545 'settings'); 546 self.verifyHistory_(['settings'], testDone); 547 }); 548 }); 549 550 TEST_F('OptionsWebUIExtendedTest', 'ShowOverlayWithHistory', function() { 551 // Open a layer-1 overlay, updating history. 552 OptionsPage.showPageByName('languages', true); 553 this.verifyOpenPages_(['settings', 'languages']); 554 555 var self = this; 556 this.verifyHistory_(['settings', 'languages'], function() { 557 // Open a layer-2 overlay, updating history. 558 OptionsPage.showPageByName('addLanguage', true); 559 self.verifyOpenPages_(['settings', 'languages', 'addLanguage']); 560 self.verifyHistory_(['settings', 'languages', 'addLanguage'], testDone); 561 }); 562 }); 563 564 TEST_F('OptionsWebUIExtendedTest', 'ShowOverlayReplaceHistory', function() { 565 // Open a layer-1 overlay, updating history. 566 OptionsPage.showPageByName('languages', true); 567 var self = this; 568 this.verifyHistory_(['settings', 'languages'], function() { 569 // Open a layer-2 overlay, replacing history. 570 OptionsPage.showPageByName('addLanguage', true, {'replaceState': true}); 571 self.verifyOpenPages_(['settings', 'languages', 'addLanguage']); 572 self.verifyHistory_(['settings', 'addLanguage'], testDone); 573 }); 574 }); 575 576 // Directly show an overlay further above this page, i.e. one for which the 577 // current page is an ancestor but not a parent. 578 TEST_F('OptionsWebUIExtendedTest', 'ShowOverlayFurtherAbove', function() { 579 // Open a layer-2 overlay directly. 580 OptionsPage.showPageByName('addLanguage', true); 581 this.verifyOpenPages_(['settings', 'languages', 'addLanguage']); 582 var self = this; 583 this.verifyHistory_(['settings', 'addLanguage'], testDone); 584 }); 585 586 // Directly show a layer-2 overlay for which the layer-1 overlay is not a 587 // parent. 588 TEST_F('OptionsWebUIExtendedTest', 'ShowUnrelatedOverlay', function() { 589 // Open a layer-1 overlay. 590 OptionsPage.showPageByName('languages', true); 591 this.verifyOpenPages_(['settings', 'languages']); 592 593 var self = this; 594 this.verifyHistory_(['settings', 'languages'], function() { 595 // Open an unrelated layer-2 overlay. 596 OptionsPage.showPageByName('cookies', true); 597 self.verifyOpenPages_(['settings', 'content', 'cookies']); 598 self.verifyHistory_(['settings', 'languages', 'cookies'], testDone); 599 }); 600 }); 601 602 // Close an overlay. 603 TEST_F('OptionsWebUIExtendedTest', 'CloseOverlay', function() { 604 // Open a layer-1 overlay, then a layer-2 overlay on top of it. 605 OptionsPage.showPageByName('languages', true); 606 this.verifyOpenPages_(['settings', 'languages']); 607 OptionsPage.showPageByName('addLanguage', true); 608 this.verifyOpenPages_(['settings', 'languages', 'addLanguage']); 609 610 var self = this; 611 this.verifyHistory_(['settings', 'languages', 'addLanguage'], function() { 612 // Close the layer-2 overlay. 613 OptionsPage.closeOverlay(); 614 self.verifyOpenPages_(['settings', 'languages']); 615 self.verifyHistory_( 616 ['settings', 'languages', 'addLanguage', 'languages'], 617 function() { 618 // Close the layer-1 overlay. 619 OptionsPage.closeOverlay(); 620 self.verifyOpenPages_(['settings']); 621 self.verifyHistory_( 622 ['settings', 'languages', 'addLanguage', 'languages', 'settings'], 623 testDone); 624 }); 625 }); 626 }); 627 628 // Make sure an overlay isn't closed (even temporarily) when another overlay is 629 // opened on top. 630 TEST_F('OptionsWebUIExtendedTest', 'OverlayAboveNoReset', function() { 631 // Open a layer-1 overlay. 632 OptionsPage.showPageByName('languages', true); 633 this.verifyOpenPages_(['settings', 'languages']); 634 635 // Open a layer-2 overlay on top. This should not close 'languages'. 636 this.prohibitChangesToOverlay_(options.LanguageOptions.getInstance()); 637 OptionsPage.showPageByName('addLanguage', true); 638 this.verifyOpenPages_(['settings', 'languages', 'addLanguage']); 639 testDone(); 640 }); 641 642 TEST_F('OptionsWebUIExtendedTest', 'OverlayTabNavigation', function() { 643 // Open a layer-1 overlay, then a layer-2 overlay on top of it. 644 OptionsPage.showPageByName('languages', true); 645 OptionsPage.showPageByName('addLanguage', true); 646 var self = this; 647 648 // Go back twice, then forward twice. 649 self.verifyOpenPages_(['settings', 'languages', 'addLanguage']); 650 self.verifyHistory_(['settings', 'languages', 'addLanguage'], function() { 651 window.history.back(); 652 waitForPopstate(function() { 653 self.verifyOpenPages_(['settings', 'languages']); 654 self.verifyHistory_(['settings', 'languages'], function() { 655 window.history.back(); 656 waitForPopstate(function() { 657 self.verifyOpenPages_(['settings']); 658 self.verifyHistory_(['settings'], function() { 659 window.history.forward(); 660 waitForPopstate(function() { 661 self.verifyOpenPages_(['settings', 'languages']); 662 self.verifyHistory_(['settings', 'languages'], function() { 663 window.history.forward(); 664 waitForPopstate(function() { 665 self.verifyOpenPages_( 666 ['settings', 'languages', 'addLanguage']); 667 self.verifyHistory_( 668 ['settings', 'languages', 'addLanguage'], testDone); 669 }); 670 }); 671 }); 672 }); 673 }); 674 }); 675 }); 676 }); 677 }); 678 679 // Going "back" to an overlay that's a child of the current overlay shouldn't 680 // close the current one. 681 TEST_F('OptionsWebUIExtendedTest', 'OverlayBackToChild', function() { 682 // Open a layer-1 overlay, then a layer-2 overlay on top of it. 683 OptionsPage.showPageByName('languages', true); 684 OptionsPage.showPageByName('addLanguage', true); 685 var self = this; 686 687 self.verifyOpenPages_(['settings', 'languages', 'addLanguage']); 688 self.verifyHistory_(['settings', 'languages', 'addLanguage'], function() { 689 // Close the top overlay, then go back to it. 690 OptionsPage.closeOverlay(); 691 self.verifyOpenPages_(['settings', 'languages']); 692 self.verifyHistory_( 693 ['settings', 'languages', 'addLanguage', 'languages'], 694 function() { 695 // Going back to the 'addLanguage' page should not close 'languages'. 696 self.prohibitChangesToOverlay_(options.LanguageOptions.getInstance()); 697 window.history.back(); 698 waitForPopstate(function() { 699 self.verifyOpenPages_(['settings', 'languages', 'addLanguage']); 700 self.verifyHistory_(['settings', 'languages', 'addLanguage'], 701 testDone); 702 }); 703 }); 704 }); 705 }); 706 707 // Going back to an unrelated overlay should close the overlay and its parent. 708 TEST_F('OptionsWebUIExtendedTest', 'OverlayBackToUnrelated', function() { 709 // Open a layer-1 overlay, then an unrelated layer-2 overlay. 710 OptionsPage.showPageByName('languages', true); 711 OptionsPage.showPageByName('cookies', true); 712 var self = this; 713 self.verifyOpenPages_(['settings', 'content', 'cookies']); 714 self.verifyHistory_(['settings', 'languages', 'cookies'], function() { 715 window.history.back(); 716 waitForPopstate(function() { 717 self.verifyOpenPages_(['settings', 'languages']); 718 testDone(); 719 }); 720 }); 721 }); 722 723 // Verify history changes properly while the page is loading. 724 TEST_F('OptionsWebUIExtendedTest', 'HistoryUpdatedAfterLoading', function() { 725 var loc = location.href; 726 727 document.documentElement.classList.add('loading'); 728 assertTrue(OptionsPage.isLoading()); 729 OptionsPage.navigateToPage('searchEngines'); 730 expectNotEquals(loc, location.href); 731 732 document.documentElement.classList.remove('loading'); 733 assertFalse(OptionsPage.isLoading()); 734 OptionsPage.showDefaultPage(); 735 expectEquals(loc, location.href); 736 737 testDone(); 738 }); 739 740 // A tip should be shown or hidden depending on whether this profile manages any 741 // supervised users. 742 TEST_F('OptionsWebUIExtendedTest', 'SupervisingUsers', function() { 743 // We start managing no supervised users. 744 assertTrue($('profiles-supervised-dashboard-tip').hidden); 745 746 // Remove all supervised users, then add some, watching for the pref change 747 // notifications and UI updates in each case. Any non-empty pref dictionary 748 // is interpreted as having supervised users. 749 chrome.send('optionsTestSetPref', [MANAGED_USERS_PREF, {key: 'value'}]); 750 waitForResponse(BrowserOptions, 'updateManagesSupervisedUsers', function() { 751 assertFalse($('profiles-supervised-dashboard-tip').hidden); 752 chrome.send('optionsTestSetPref', [MANAGED_USERS_PREF, {}]); 753 waitForResponse(BrowserOptions, 'updateManagesSupervisedUsers', function() { 754 assertTrue($('profiles-supervised-dashboard-tip').hidden); 755 testDone(); 756 }); 757 }); 758 }); 759