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.
5 GEN_INCLUDE(['options_browsertest_base.js']);
6 GEN('#include "chrome/browser/ui/webui/options/options_browsertest.h"');
8 /** @const */ var SUPERVISED_USERS_PREF = 'profile.managed_users';
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
16 * @param {!Function} afterFunction A function to call after object.methodName()
19 function waitForResponse(object, methodName, afterFunction) {
20 var originalCallback = object[methodName];
22 // Install a wrapper that temporarily replaces the original function.
23 object[methodName] = function() {
24 object[methodName] = originalCallback;
25 originalCallback.apply(this, arguments);
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.
35 function waitForPopstate(afterFunction) {
36 waitForResponse(window, 'onpopstate', afterFunction);
40 * TestFixture for OptionsPage WebUI testing.
41 * @extends {testing.Test}
44 function OptionsWebUITest() {}
46 OptionsWebUITest.prototype = {
47 __proto__: OptionsBrowsertestBase.prototype,
50 * Browse to the options page & call our preLoad().
53 browsePreload: 'chrome://settings-frame',
59 * Register a mock handler to ensure expectations are met and options pages
63 this.makeAndRegisterMockHandler(
64 ['defaultZoomFactorAction',
73 'coreOptionsUserMetricsAction',
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);
85 * Wait for all targets to be hidden.
86 * @param {Array<Element>} targets
88 function waitUntilHidden(targets) {
89 function isHidden(el) { return el.hidden; }
90 function ensureTransition(el) { ensureTransitionEndEvent(el, 500); }
92 document.addEventListener('webkitTransitionEnd', function f(e) {
93 if (targets.indexOf(e.target) >= 0 && targets.every(isHidden)) {
94 document.removeEventListener(f, 'webkitTransitionEnd');
99 targets.forEach(ensureTransition);
102 // Crashes on Mac only. See http://crbug.com/79181
103 GEN('#if defined(OS_MACOSX)');
104 GEN('#define MAYBE_testSetBooleanPrefTriggers ' +
105 'DISABLED_testSetBooleanPrefTriggers');
107 GEN('#define MAYBE_testSetBooleanPrefTriggers testSetBooleanPrefTriggers');
108 GEN('#endif // defined(OS_MACOSX)');
110 TEST_F('OptionsWebUITest', 'MAYBE_testSetBooleanPrefTriggers', function() {
111 // TODO(dtseng): make generic to click all buttons.
113 document.querySelector('input[pref="browser.show_home_button"]');
114 var trueListValue = [
115 'browser.show_home_button',
117 'Options_Homepage_HomeButton',
119 // Note: this expectation is checked in testing::Test::tearDown.
120 this.mockHandler.expects(once()).setBooleanPref(trueListValue);
122 // Cause the handler to be called.
123 showHomeButton.click();
124 showHomeButton.blur();
128 // Not meant to run on ChromeOS at this time.
129 // Not finishing in windows. http://crbug.com/81723
130 TEST_F('OptionsWebUITest', 'DISABLED_testRefreshStaysOnCurrentPage',
132 assertTrue($('search-engine-manager-page').hidden);
133 var item = $('manage-default-search-engines');
136 assertFalse($('search-engine-manager-page').hidden);
138 window.location.reload();
140 assertEquals('chrome://settings-frame/searchEngines', document.location.href);
141 assertFalse($('search-engine-manager-page').hidden);
146 * Test the default zoom factor select element.
148 TEST_F('OptionsWebUITest', 'testDefaultZoomFactor', function() {
149 // The expected minimum length of the |defaultZoomFactor| element.
150 var defaultZoomFactorMinimumLength = 10;
151 // Verify that the zoom factor element exists.
152 var defaultZoomFactor = $('defaultZoomFactor');
153 assertNotEquals(defaultZoomFactor, null);
155 // Verify that the zoom factor element has a reasonable number of choices.
156 expectGE(defaultZoomFactor.options.length, defaultZoomFactorMinimumLength);
158 // Simulate a change event, selecting the highest zoom value. Verify that
159 // the javascript handler was invoked once.
160 this.mockHandler.expects(once()).defaultZoomFactorAction(NOT_NULL).
161 will(callFunction(function() { }));
162 defaultZoomFactor.selectedIndex = defaultZoomFactor.options.length - 1;
163 var event = {target: defaultZoomFactor};
164 if (defaultZoomFactor.onchange) defaultZoomFactor.onchange(event);
169 * If |confirmInterstitial| is true, the OK button of the Do Not Track
170 * interstitial is pressed, otherwise the abort button is pressed.
171 * @param {boolean} confirmInterstitial Whether to confirm the Do Not Track
174 OptionsWebUITest.prototype.testDoNotTrackInterstitial =
175 function(confirmInterstitial) {
176 Preferences.prefsFetchedCallback({'enable_do_not_track': {'value': false}});
177 var buttonToClick = confirmInterstitial ? $('do-not-track-confirm-ok') :
178 $('do-not-track-confirm-cancel');
179 var dntCheckbox = $('do-not-track-enabled');
180 var dntOverlay = PageManager.registeredOverlayPages['donottrackconfirm'];
181 assertFalse(dntCheckbox.checked);
183 var visibleChangeCounter = 0;
184 var visibleChangeHandler = function() {
185 ++visibleChangeCounter;
186 switch (visibleChangeCounter) {
188 window.setTimeout(function() {
189 assertTrue(dntOverlay.visible);
190 buttonToClick.click();
194 window.setTimeout(function() {
195 assertFalse(dntOverlay.visible);
196 assertEquals(confirmInterstitial, dntCheckbox.checked);
197 dntOverlay.removeEventListener(visibleChangeHandler);
205 dntOverlay.addEventListener('visibleChange', visibleChangeHandler);
207 if (confirmInterstitial) {
208 this.mockHandler.expects(once()).setBooleanPref(
209 ['enable_do_not_track', true, 'Options_DoNotTrackCheckbox']);
211 // The mock handler complains if setBooleanPref is called even though
218 TEST_F('OptionsWebUITest', 'EnableDoNotTrackAndConfirmInterstitial',
220 this.testDoNotTrackInterstitial(true);
223 TEST_F('OptionsWebUITest', 'EnableDoNotTrackAndCancelInterstitial',
225 this.testDoNotTrackInterstitial(false);
228 // Check that the "Do not Track" preference can be correctly disabled.
229 // In order to do that, we need to enable it first.
230 TEST_F('OptionsWebUITest', 'EnableAndDisableDoNotTrack', function() {
231 Preferences.prefsFetchedCallback({'enable_do_not_track': {'value': false}});
232 var dntCheckbox = $('do-not-track-enabled');
233 var dntOverlay = PageManager.registeredOverlayPages.donottrackconfirm;
234 assertFalse(dntCheckbox.checked);
236 var visibleChangeCounter = 0;
237 var visibleChangeHandler = function() {
238 ++visibleChangeCounter;
239 switch (visibleChangeCounter) {
241 window.setTimeout(function() {
242 assertTrue(dntOverlay.visible);
243 $('do-not-track-confirm-ok').click();
247 window.setTimeout(function() {
248 assertFalse(dntOverlay.visible);
249 assertTrue(dntCheckbox.checked);
250 dntOverlay.removeEventListener(visibleChangeHandler);
258 dntOverlay.addEventListener('visibleChange', visibleChangeHandler);
260 this.mockHandler.expects(once()).setBooleanPref(
261 eq(['enable_do_not_track', true, 'Options_DoNotTrackCheckbox']));
263 var verifyCorrectEndState = function() {
264 window.setTimeout(function() {
265 assertFalse(dntOverlay.visible);
266 assertFalse(dntCheckbox.checked);
270 this.mockHandler.expects(once()).setBooleanPref(
271 eq(['enable_do_not_track', false, 'Options_DoNotTrackCheckbox'])).will(
272 callFunction(verifyCorrectEndState));
277 // Verify that preventDefault() is called on 'Enter' keydown events that trigger
278 // the default button. If this doesn't happen, other elements that may get
279 // focus (by the overlay closing for instance), will execute in addition to the
280 // default button. See crbug.com/268336.
281 TEST_F('OptionsWebUITest', 'EnterPreventsDefault', function() {
282 var page = HomePageOverlay.getInstance();
283 PageManager.showPageByName(page.name);
284 var event = new KeyboardEvent('keydown', {
287 'keyIdentifier': 'Enter'
289 assertFalse(event.defaultPrevented);
290 page.pageDiv.dispatchEvent(event);
291 assertTrue(event.defaultPrevented);
295 // Verifies that sending an empty list of indexes to move doesn't crash chrome.
296 TEST_F('OptionsWebUITest', 'emptySelectedIndexesDoesntCrash', function() {
297 chrome.send('dragDropStartupPage', [0, []]);
298 setTimeout(testDone);
301 // This test turns out to be flaky on all platforms.
302 // See http://crbug.com/315250.
304 // An overlay's position should remain the same as it shows.
305 TEST_F('OptionsWebUITest', 'DISABLED_OverlayShowDoesntShift', function() {
306 var overlayName = 'startup';
307 var overlay = $('startup-overlay');
308 var frozenPages = document.getElementsByClassName('frozen'); // Gets updated.
309 expectEquals(0, frozenPages.length);
311 document.addEventListener('webkitTransitionEnd', function(e) {
312 if (e.target != overlay)
315 assertFalse(overlay.classList.contains('transparent'));
316 expectEquals(numFrozenPages, frozenPages.length);
320 PageManager.showPageByName(overlayName);
321 var numFrozenPages = frozenPages.length;
322 expectGT(numFrozenPages, 0);
325 GEN('#if defined(OS_CHROMEOS)');
326 // Verify that range inputs respond to touch events. Currently only Chrome OS
327 // uses slider options.
328 TEST_F('OptionsWebUITest', 'RangeInputHandlesTouchEvents', function() {
329 this.mockHandler.expects(once()).setIntegerPref([
330 'settings.touchpad.sensitivity2', 1]);
332 var touchpadRange = $('touchpad-sensitivity-range');
333 var event = document.createEvent('UIEvent');
334 event.initUIEvent('touchstart', true, true, window);
335 touchpadRange.dispatchEvent(event);
337 event = document.createEvent('UIEvent');
338 event.initUIEvent('touchmove', true, true, window);
339 touchpadRange.dispatchEvent(event);
341 touchpadRange.value = 1;
343 event = document.createEvent('UIEvent');
344 event.initUIEvent('touchend', true, true, window);
345 touchpadRange.dispatchEvent(event);
347 // touchcancel should also trigger the handler, since it
348 // changes the slider position.
349 this.mockHandler.expects(once()).setIntegerPref([
350 'settings.touchpad.sensitivity2', 2]);
352 event = document.createEvent('UIEvent');
353 event.initUIEvent('touchstart', true, true, window);
354 touchpadRange.dispatchEvent(event);
356 touchpadRange.value = 2;
358 event = document.createEvent('UIEvent');
359 event.initUIEvent('touchcancel', true, true, window);
360 touchpadRange.dispatchEvent(event);
364 GEN('#endif'); // defined(OS_CHROMEOS)
367 * TestFixture for OptionsPage WebUI testing including tab history and support
368 * for preference manipulation. If you don't need the features in the C++
369 * fixture, use the simpler OptionsWebUITest (above) instead.
370 * @extends {testing.Test}
373 function OptionsWebUIExtendedTest() {}
375 OptionsWebUIExtendedTest.prototype = {
376 __proto__: OptionsWebUITest.prototype,
379 typedefCppFixture: 'OptionsBrowserTest',
381 testGenPreamble: function() {
382 // Start with no supervised users managed by this profile.
383 GEN(' ClearPref("' + SUPERVISED_USERS_PREF + '");');
387 * Asserts that two non-nested arrays are equal. The arrays must contain only
388 * plain data types, no nested arrays or other objects.
389 * @param {Array} expected An array of expected values.
390 * @param {Array} result An array of actual values.
391 * @param {boolean} doSort If true, the arrays will be sorted before being
393 * @param {string} description A brief description for the array of actual
394 * values, to use in an error message if the arrays differ.
397 compareArrays_: function(expected, result, doSort, description) {
398 var errorMessage = '\n' + description + ': ' + result +
399 '\nExpected: ' + expected;
400 assertEquals(expected.length, result.length, errorMessage);
402 var expectedSorted = expected.slice();
403 var resultSorted = result.slice();
405 expectedSorted.sort();
409 for (var i = 0; i < expectedSorted.length; ++i) {
410 assertEquals(expectedSorted[i], resultSorted[i], errorMessage);
415 * Verifies that the correct pages are currently open/visible.
416 * @param {!Array<string>} expectedPages An array of page names expected to
417 * be open, with the topmost listed last.
418 * @param {string=} opt_expectedUrl The URL path, including hash, expected to
419 * be open. If undefined, the topmost (last) page name in |expectedPages|
420 * will be used. In either case, 'chrome://settings-frame/' will be
424 verifyOpenPages_: function(expectedPages, opt_expectedUrl) {
425 // Check the topmost page.
426 expectEquals(null, PageManager.getVisibleBubble());
427 var currentPage = PageManager.getTopmostVisiblePage();
429 var lastExpected = expectedPages[expectedPages.length - 1];
430 expectEquals(lastExpected, currentPage.name);
431 // We'd like to check the title too, but we have to load the settings-frame
432 // instead of the outer settings page in order to have access to
433 // OptionsPage, and setting the title from within the settings-frame fails
434 // because of cross-origin access restrictions.
435 // TODO(pamg): Add a test fixture that loads chrome://settings and uses
436 // UI elements to access sub-pages, so we can test the titles and
438 var expectedUrl = (typeof opt_expectedUrl == 'undefined') ?
439 lastExpected : opt_expectedUrl;
440 var fullExpectedUrl = 'chrome://settings-frame/' + expectedUrl;
441 expectEquals(fullExpectedUrl, window.location.href);
443 // Collect open pages.
444 var allPageNames = Object.keys(PageManager.registeredPages).concat(
445 Object.keys(PageManager.registeredOverlayPages));
447 for (var i = 0; i < allPageNames.length; ++i) {
448 var name = allPageNames[i];
449 var page = PageManager.registeredPages[name] ||
450 PageManager.registeredOverlayPages[name];
452 openPages.push(page.name);
455 this.compareArrays_(expectedPages, openPages, true, 'Open pages');
459 * Verifies that the correct URLs are listed in the history. Asynchronous.
460 * @param {!Array<string>} expectedHistory An array of URL paths expected to
461 * be in the tab navigation history, sorted by visit time, including the
462 * current page as the last entry. The base URL (chrome://settings-frame/)
463 * will be prepended to each. An initial 'about:blank' history entry is
464 * assumed and should not be included in this list.
465 * @param {Function=} callback A function to be called after the history has
466 * been verified successfully. May be undefined.
469 verifyHistory_: function(expectedHistory, callback) {
471 OptionsWebUIExtendedTest.verifyHistoryCallback = function(results) {
472 // The history always starts with a blank page.
473 assertEquals('about:blank', results.shift());
474 var fullExpectedHistory = [];
475 for (var i = 0; i < expectedHistory.length; ++i) {
476 fullExpectedHistory.push(
477 'chrome://settings-frame/' + expectedHistory[i]);
479 self.compareArrays_(fullExpectedHistory, results, false, 'History');
483 // The C++ fixture will call verifyHistoryCallback with the results.
484 chrome.send('optionsTestReportHistory');
488 * Overrides the page callbacks for the given PageManager overlay to verify
489 * that they are not called.
490 * @param {Object} overlay The singleton instance of the overlay.
493 prohibitChangesToOverlay_: function(overlay) {
494 overlay.initializePage =
495 overlay.didShowPage =
496 overlay.didClosePage = function() {
498 'Overlay was affected when changes were prohibited.');
504 * Set by verifyHistory_ to incorporate a followup callback, then called by the
505 * C++ fixture with the navigation history to be verified.
508 OptionsWebUIExtendedTest.verifyHistoryCallback = null;
510 // Show the search page with no query string, to fall back to the settings page.
511 // Test disabled because it's flaky. crbug.com/303841
512 TEST_F('OptionsWebUIExtendedTest', 'DISABLED_ShowSearchPageNoQuery',
514 PageManager.showPageByName('search');
515 this.verifyOpenPages_(['settings']);
516 this.verifyHistory_(['settings'], testDone);
519 // Manipulate the search page via the search field.
520 TEST_F('OptionsWebUIExtendedTest', 'ShowSearchFromField', function() {
521 $('search-field').onsearch({currentTarget: {value: 'query'}});
522 this.verifyOpenPages_(['settings', 'search'], 'search#query');
523 this.verifyHistory_(['', 'search#query'], function() {
524 $('search-field').onsearch({currentTarget: {value: 'query2'}});
525 this.verifyOpenPages_(['settings', 'search'], 'search#query2');
526 this.verifyHistory_(['', 'search#query', 'search#query2'], function() {
527 $('search-field').onsearch({currentTarget: {value: ''}});
528 this.verifyOpenPages_(['settings'], '');
529 this.verifyHistory_(['', 'search#query', 'search#query2', ''], testDone);
534 // Show a page without updating history.
535 TEST_F('OptionsWebUIExtendedTest', 'ShowPageNoHistory', function() {
536 this.verifyOpenPages_(['settings'], '');
537 PageManager.showPageByName('search', true, {hash: '#query'});
539 // The settings page is also still "open" (i.e., visible), in order to show
540 // the search results. Furthermore, the URL hasn't been updated in the parent
541 // page, because we've loaded the chrome-settings frame instead of the whole
542 // settings page, so the cross-origin call to set the URL fails.
543 this.verifyOpenPages_(['settings', 'search'], 'search#query');
545 this.verifyHistory_(['', 'search#query'], function() {
546 PageManager.showPageByName('settings', false);
547 self.verifyOpenPages_(['settings'], 'search#query');
548 self.verifyHistory_(['', 'search#query'], testDone);
552 TEST_F('OptionsWebUIExtendedTest', 'ShowPageWithHistory', function() {
553 PageManager.showPageByName('search', true, {hash: '#query'});
555 this.verifyHistory_(['', 'search#query'], function() {
556 PageManager.showPageByName('settings', true);
557 self.verifyOpenPages_(['settings'], '');
558 self.verifyHistory_(['', 'search#query', ''],
563 TEST_F('OptionsWebUIExtendedTest', 'ShowPageReplaceHistory', function() {
564 PageManager.showPageByName('search', true, {hash: '#query'});
566 this.verifyHistory_(['', 'search#query'], function() {
567 PageManager.showPageByName('settings', true, {'replaceState': true});
568 self.verifyOpenPages_(['settings'], '');
569 self.verifyHistory_(['', ''], testDone);
573 // This should be identical to ShowPageWithHisory.
574 TEST_F('OptionsWebUIExtendedTest', 'NavigateToPage', function() {
575 PageManager.showPageByName('search', true, {hash: '#query'});
577 this.verifyHistory_(['', 'search#query'], function() {
578 PageManager.showPageByName('settings');
579 self.verifyOpenPages_(['settings'], '');
580 self.verifyHistory_(['', 'search#query', ''], testDone);
584 // Settings overlays are much more straightforward than settings pages, opening
585 // normally with none of the latter's quirks in the expected history or URL.
586 TEST_F('OptionsWebUIExtendedTest', 'ShowOverlayNoHistory', function() {
587 // Open a layer-1 overlay, not updating history.
588 PageManager.showPageByName('languages', false);
589 this.verifyOpenPages_(['settings', 'languages'], '');
592 this.verifyHistory_([''], function() {
593 // Open a layer-2 overlay for which the layer-1 is a parent, not updating
595 PageManager.showPageByName('addLanguage', false);
596 self.verifyOpenPages_(['settings', 'languages', 'addLanguage'],
598 self.verifyHistory_([''], testDone);
602 TEST_F('OptionsWebUIExtendedTest', 'ShowOverlayWithHistory', function() {
603 // Open a layer-1 overlay, updating history.
604 PageManager.showPageByName('languages', true);
605 this.verifyOpenPages_(['settings', 'languages']);
608 this.verifyHistory_(['', 'languages'], function() {
609 // Open a layer-2 overlay, updating history.
610 PageManager.showPageByName('addLanguage', true);
611 self.verifyOpenPages_(['settings', 'languages', 'addLanguage']);
612 self.verifyHistory_(['', 'languages', 'addLanguage'], testDone);
616 TEST_F('OptionsWebUIExtendedTest', 'ShowOverlayReplaceHistory', function() {
617 // Open a layer-1 overlay, updating history.
618 PageManager.showPageByName('languages', true);
620 this.verifyHistory_(['', 'languages'], function() {
621 // Open a layer-2 overlay, replacing history.
622 PageManager.showPageByName('addLanguage', true, {'replaceState': true});
623 self.verifyOpenPages_(['settings', 'languages', 'addLanguage']);
624 self.verifyHistory_(['', 'addLanguage'], testDone);
628 // Directly show an overlay further above this page, i.e. one for which the
629 // current page is an ancestor but not a parent.
630 TEST_F('OptionsWebUIExtendedTest', 'ShowOverlayFurtherAbove', function() {
631 // Open a layer-2 overlay directly.
632 PageManager.showPageByName('addLanguage', true);
633 this.verifyOpenPages_(['settings', 'languages', 'addLanguage']);
635 this.verifyHistory_(['', 'addLanguage'], testDone);
638 // Directly show a layer-2 overlay for which the layer-1 overlay is not a
640 TEST_F('OptionsWebUIExtendedTest', 'ShowUnrelatedOverlay', function() {
641 // Open a layer-1 overlay.
642 PageManager.showPageByName('languages', true);
643 this.verifyOpenPages_(['settings', 'languages']);
646 this.verifyHistory_(['', 'languages'], function() {
647 // Open an unrelated layer-2 overlay.
648 PageManager.showPageByName('cookies', true);
649 self.verifyOpenPages_(['settings', 'content', 'cookies']);
650 self.verifyHistory_(['', 'languages', 'cookies'], testDone);
655 TEST_F('OptionsWebUIExtendedTest', 'CloseOverlay', function() {
656 // Open a layer-1 overlay, then a layer-2 overlay on top of it.
657 PageManager.showPageByName('languages', true);
658 this.verifyOpenPages_(['settings', 'languages']);
659 PageManager.showPageByName('addLanguage', true);
660 this.verifyOpenPages_(['settings', 'languages', 'addLanguage']);
663 this.verifyHistory_(['', 'languages', 'addLanguage'], function() {
664 // Close the layer-2 overlay.
665 PageManager.closeOverlay();
666 self.verifyOpenPages_(['settings', 'languages']);
668 ['', 'languages', 'addLanguage', 'languages'],
670 // Close the layer-1 overlay.
671 PageManager.closeOverlay();
672 self.verifyOpenPages_(['settings'], '');
674 ['', 'languages', 'addLanguage', 'languages', ''],
676 waitUntilHidden([$('overlay-container-1'), $('overlay-container-2')]);
681 // Hashes are maintained separately for each page and are preserved when
683 TEST_F('OptionsWebUIExtendedTest', 'CloseOverlayWithHashes', function() {
684 // Open an overlay on top of the search page.
685 PageManager.showPageByName('search', true, {hash: '#1'});
686 this.verifyOpenPages_(['settings', 'search'], 'search#1');
687 PageManager.showPageByName('languages', true, {hash: '#2'});
688 this.verifyOpenPages_(['settings', 'search', 'languages'],
690 PageManager.showPageByName('addLanguage', true, {hash: '#3'});
691 this.verifyOpenPages_(['settings', 'search', 'languages', 'addLanguage'],
694 this.verifyHistory_(['', 'search#1', 'languages#2', 'addLanguage#3'],
696 // Close the layer-2 overlay.
697 PageManager.closeOverlay();
698 this.verifyOpenPages_(['settings', 'search', 'languages'], 'languages#2');
700 ['', 'search#1', 'languages#2', 'addLanguage#3', 'languages#2'],
702 // Close the layer-1 overlay.
703 PageManager.closeOverlay();
704 this.verifyOpenPages_(['settings', 'search'], 'search#1');
706 ['', 'search#1', 'languages#2', 'addLanguage#3', 'languages#2',
709 waitUntilHidden([$('overlay-container-1'), $('overlay-container-2')]);
714 // Test that closing an overlay that did not push history when opening does not
715 // again push history.
716 TEST_F('OptionsWebUIExtendedTest', 'CloseOverlayNoHistory', function() {
717 // Open the do not track confirmation prompt.
718 PageManager.showPageByName('doNotTrackConfirm', false);
720 // Opening the prompt does not add to the history.
721 this.verifyHistory_([''], function() {
722 // Close the overlay.
723 PageManager.closeOverlay();
724 // Still no history changes.
725 this.verifyHistory_([''], function noop() {});
726 waitUntilHidden([$('overlay-container-1')]);
730 // Make sure an overlay isn't closed (even temporarily) when another overlay is
732 TEST_F('OptionsWebUIExtendedTest', 'OverlayAboveNoReset', function() {
733 // Open a layer-1 overlay.
734 PageManager.showPageByName('languages', true);
735 this.verifyOpenPages_(['settings', 'languages']);
737 // Open a layer-2 overlay on top. This should not close 'languages'.
738 this.prohibitChangesToOverlay_(options.LanguageOptions.getInstance());
739 PageManager.showPageByName('addLanguage', true);
740 this.verifyOpenPages_(['settings', 'languages', 'addLanguage']);
744 TEST_F('OptionsWebUIExtendedTest', 'OverlayTabNavigation', function() {
745 // Open a layer-1 overlay, then a layer-2 overlay on top of it.
746 PageManager.showPageByName('languages', true);
747 PageManager.showPageByName('addLanguage', true);
750 // Go back twice, then forward twice.
751 self.verifyOpenPages_(['settings', 'languages', 'addLanguage']);
752 self.verifyHistory_(['', 'languages', 'addLanguage'], function() {
753 window.history.back();
754 waitForPopstate(function() {
755 self.verifyOpenPages_(['settings', 'languages']);
756 self.verifyHistory_(['', 'languages'], function() {
757 window.history.back();
758 waitForPopstate(function() {
759 self.verifyOpenPages_(['settings'], '');
760 self.verifyHistory_([''], function() {
761 window.history.forward();
762 waitForPopstate(function() {
763 self.verifyOpenPages_(['settings', 'languages']);
764 self.verifyHistory_(['', 'languages'], function() {
765 window.history.forward();
766 waitForPopstate(function() {
767 self.verifyOpenPages_(
768 ['settings', 'languages', 'addLanguage']);
770 ['', 'languages', 'addLanguage'], testDone);
781 // Going "back" to an overlay that's a child of the current overlay shouldn't
782 // close the current one.
783 TEST_F('OptionsWebUIExtendedTest', 'OverlayBackToChild', function() {
784 // Open a layer-1 overlay, then a layer-2 overlay on top of it.
785 PageManager.showPageByName('languages', true);
786 PageManager.showPageByName('addLanguage', true);
789 self.verifyOpenPages_(['settings', 'languages', 'addLanguage']);
790 self.verifyHistory_(['', 'languages', 'addLanguage'], function() {
791 // Close the top overlay, then go back to it.
792 PageManager.closeOverlay();
793 self.verifyOpenPages_(['settings', 'languages']);
795 ['', 'languages', 'addLanguage', 'languages'],
797 // Going back to the 'addLanguage' page should not close 'languages'.
798 self.prohibitChangesToOverlay_(options.LanguageOptions.getInstance());
799 window.history.back();
800 waitForPopstate(function() {
801 self.verifyOpenPages_(['settings', 'languages', 'addLanguage']);
802 self.verifyHistory_(['', 'languages', 'addLanguage'],
809 // Going back to an unrelated overlay should close the overlay and its parent.
810 TEST_F('OptionsWebUIExtendedTest', 'OverlayBackToUnrelated', function() {
811 // Open a layer-1 overlay, then an unrelated layer-2 overlay.
812 PageManager.showPageByName('languages', true);
813 PageManager.showPageByName('cookies', true);
815 self.verifyOpenPages_(['settings', 'content', 'cookies']);
816 self.verifyHistory_(['', 'languages', 'cookies'], function() {
817 window.history.back();
818 waitForPopstate(function() {
819 self.verifyOpenPages_(['settings', 'languages']);
825 // Verify history changes properly while the page is loading.
826 TEST_F('OptionsWebUIExtendedTest', 'HistoryUpdatedAfterLoading', function() {
827 var loc = location.href;
829 document.documentElement.classList.add('loading');
830 assertTrue(PageManager.isLoading());
831 PageManager.showPageByName('searchEngines');
832 expectNotEquals(loc, location.href);
834 document.documentElement.classList.remove('loading');
835 assertFalse(PageManager.isLoading());
836 PageManager.showDefaultPage();
837 expectEquals(loc, location.href);
842 // A tip should be shown or hidden depending on whether this profile manages any
844 TEST_F('OptionsWebUIExtendedTest', 'SupervisingUsers', function() {
845 // We start managing no supervised users.
846 assertTrue($('profiles-supervised-dashboard-tip').hidden);
848 // Remove all supervised users, then add some, watching for the pref change
849 // notifications and UI updates in each case. Any non-empty pref dictionary
850 // is interpreted as having supervised users.
851 chrome.send('optionsTestSetPref', [SUPERVISED_USERS_PREF, {key: 'value'}]);
852 waitForResponse(BrowserOptions, 'updateManagesSupervisedUsers', function() {
853 assertFalse($('profiles-supervised-dashboard-tip').hidden);
854 chrome.send('optionsTestSetPref', [SUPERVISED_USERS_PREF, {}]);
855 waitForResponse(BrowserOptions, 'updateManagesSupervisedUsers', function() {
856 assertTrue($('profiles-supervised-dashboard-tip').hidden);
863 * TestFixture that loads the options page at a bogus URL.
864 * @extends {OptionsWebUIExtendedTest}
867 function OptionsWebUIRedirectTest() {
868 OptionsWebUIExtendedTest.call(this);
871 OptionsWebUIRedirectTest.prototype = {
872 __proto__: OptionsWebUIExtendedTest.prototype,
875 browsePreload: 'chrome://settings-frame/nonexistantPage',
878 TEST_F('OptionsWebUIRedirectTest', 'TestURL', function() {
879 assertEquals('chrome://settings-frame/', document.location.href);
880 this.verifyHistory_([''], testDone);