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