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 "chrome/browser/ui/webui/options/options_browsertest.h"');
7 /** @const */ var SUPERVISED_USERS_PREF = 'profile.managed_users';
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
15 * @param {!Function} afterFunction A function to call after object.methodName()
18 function waitForResponse(object, methodName, afterFunction) {
19 var originalCallback = object[methodName];
21 // Install a wrapper that temporarily replaces the original function.
22 object[methodName] = function() {
23 object[methodName] = originalCallback;
24 originalCallback.apply(this, arguments);
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.
34 function waitForPopstate(afterFunction) {
35 waitForResponse(window, 'onpopstate', afterFunction);
39 * TestFixture for OptionsPage WebUI testing.
40 * @extends {testing.Test}
43 function OptionsWebUITest() {}
45 OptionsWebUITest.prototype = {
46 __proto__: testing.Test.prototype,
49 accessibilityIssuesAreErrors: true,
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');
60 * Browse to the options page & call our preLoad().
62 browsePreload: 'chrome://settings-frame',
67 * Register a mock handler to ensure expectations are met and options pages
71 this.makeAndRegisterMockHandler(
72 ['defaultZoomFactorAction',
81 'coreOptionsUserMetricsAction',
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);
92 // Crashes on Mac only. See http://crbug.com/79181
93 GEN('#if defined(OS_MACOSX)');
94 GEN('#define MAYBE_testSetBooleanPrefTriggers ' +
95 'DISABLED_testSetBooleanPrefTriggers');
97 GEN('#define MAYBE_testSetBooleanPrefTriggers testSetBooleanPrefTriggers');
98 GEN('#endif // defined(OS_MACOSX)');
100 TEST_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',
106 'Options_Homepage_HomeButton',
108 // Note: this expectation is checked in testing::Test::tearDown.
109 this.mockHandler.expects(once()).setBooleanPref(trueListValue);
111 // Cause the handler to be called.
112 showHomeButton.click();
113 showHomeButton.blur();
117 // Not meant to run on ChromeOS at this time.
118 // Not finishing in windows. http://crbug.com/81723
119 TEST_F('OptionsWebUITest', 'DISABLED_testRefreshStaysOnCurrentPage',
121 assertTrue($('search-engine-manager-page').hidden);
122 var item = $('manage-default-search-engines');
125 assertFalse($('search-engine-manager-page').hidden);
127 window.location.reload();
129 assertEquals('chrome://settings-frame/searchEngines', document.location.href);
130 assertFalse($('search-engine-manager-page').hidden);
135 * Test the default zoom factor select element.
137 TEST_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);
144 // Verify that the zoom factor element has a reasonable number of choices.
145 expectGE(defaultZoomFactor.options.length, defaultZoomFactorMinimumLength);
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);
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
163 OptionsWebUITest.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 = PageManager.registeredOverlayPages['donottrackconfirm'];
170 assertFalse(dntCheckbox.checked);
172 var visibleChangeCounter = 0;
173 var visibleChangeHandler = function() {
174 ++visibleChangeCounter;
175 switch (visibleChangeCounter) {
177 window.setTimeout(function() {
178 assertTrue(dntOverlay.visible);
179 buttonToClick.click();
183 window.setTimeout(function() {
184 assertFalse(dntOverlay.visible);
185 assertEquals(confirmInterstitial, dntCheckbox.checked);
186 dntOverlay.removeEventListener(visibleChangeHandler);
194 dntOverlay.addEventListener('visibleChange', visibleChangeHandler);
196 if (confirmInterstitial) {
197 this.mockHandler.expects(once()).setBooleanPref(
198 ['enable_do_not_track', true, 'Options_DoNotTrackCheckbox']);
200 // The mock handler complains if setBooleanPref is called even though
207 TEST_F('OptionsWebUITest', 'EnableDoNotTrackAndConfirmInterstitial',
209 this.testDoNotTrackInterstitial(true);
212 TEST_F('OptionsWebUITest', 'EnableDoNotTrackAndCancelInterstitial',
214 this.testDoNotTrackInterstitial(false);
217 // Check that the "Do not Track" preference can be correctly disabled.
218 // In order to do that, we need to enable it first.
219 TEST_F('OptionsWebUITest', 'EnableAndDisableDoNotTrack', function() {
220 Preferences.prefsFetchedCallback({'enable_do_not_track': {'value': false}});
221 var dntCheckbox = $('do-not-track-enabled');
222 var dntOverlay = PageManager.registeredOverlayPages.donottrackconfirm;
223 assertFalse(dntCheckbox.checked);
225 var visibleChangeCounter = 0;
226 var visibleChangeHandler = function() {
227 ++visibleChangeCounter;
228 switch (visibleChangeCounter) {
230 window.setTimeout(function() {
231 assertTrue(dntOverlay.visible);
232 $('do-not-track-confirm-ok').click();
236 window.setTimeout(function() {
237 assertFalse(dntOverlay.visible);
238 assertTrue(dntCheckbox.checked);
239 dntOverlay.removeEventListener(visibleChangeHandler);
247 dntOverlay.addEventListener('visibleChange', visibleChangeHandler);
249 this.mockHandler.expects(once()).setBooleanPref(
250 eq(['enable_do_not_track', true, 'Options_DoNotTrackCheckbox']));
252 var verifyCorrectEndState = function() {
253 window.setTimeout(function() {
254 assertFalse(dntOverlay.visible);
255 assertFalse(dntCheckbox.checked);
259 this.mockHandler.expects(once()).setBooleanPref(
260 eq(['enable_do_not_track', false, 'Options_DoNotTrackCheckbox'])).will(
261 callFunction(verifyCorrectEndState));
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.
270 TEST_F('OptionsWebUITest', 'EnterPreventsDefault', function() {
271 var page = HomePageOverlay.getInstance();
272 PageManager.showPageByName(page.name);
273 var event = new KeyboardEvent('keydown', {
276 'keyIdentifier': 'Enter'
278 assertFalse(event.defaultPrevented);
279 page.pageDiv.dispatchEvent(event);
280 assertTrue(event.defaultPrevented);
284 // Verifies that sending an empty list of indexes to move doesn't crash chrome.
285 TEST_F('OptionsWebUITest', 'emptySelectedIndexesDoesntCrash', function() {
286 chrome.send('dragDropStartupPage', [0, []]);
287 setTimeout(testDone);
290 // This test turns out to be flaky on all platforms.
291 // See http://crbug.com/315250.
293 // An overlay's position should remain the same as it shows.
294 TEST_F('OptionsWebUITest', 'DISABLED_OverlayShowDoesntShift', function() {
295 var overlayName = 'startup';
296 var overlay = $('startup-overlay');
297 var frozenPages = document.getElementsByClassName('frozen'); // Gets updated.
298 expectEquals(0, frozenPages.length);
300 document.addEventListener('webkitTransitionEnd', function(e) {
301 if (e.target != overlay)
304 assertFalse(overlay.classList.contains('transparent'));
305 expectEquals(numFrozenPages, frozenPages.length);
309 PageManager.showPageByName(overlayName);
310 var numFrozenPages = frozenPages.length;
311 expectGT(numFrozenPages, 0);
314 GEN('#if defined(OS_CHROMEOS)');
315 // Verify that range inputs respond to touch events. Currently only Chrome OS
316 // uses slider options.
317 TEST_F('OptionsWebUITest', 'RangeInputHandlesTouchEvents', function() {
318 this.mockHandler.expects(once()).setIntegerPref([
319 'settings.touchpad.sensitivity2', 1]);
321 var touchpadRange = $('touchpad-sensitivity-range');
322 var event = document.createEvent('UIEvent');
323 event.initUIEvent('touchstart', true, true, window);
324 touchpadRange.dispatchEvent(event);
326 event = document.createEvent('UIEvent');
327 event.initUIEvent('touchmove', true, true, window);
328 touchpadRange.dispatchEvent(event);
330 touchpadRange.value = 1;
332 event = document.createEvent('UIEvent');
333 event.initUIEvent('touchend', true, true, window);
334 touchpadRange.dispatchEvent(event);
336 // touchcancel should also trigger the handler, since it
337 // changes the slider position.
338 this.mockHandler.expects(once()).setIntegerPref([
339 'settings.touchpad.sensitivity2', 2]);
341 event = document.createEvent('UIEvent');
342 event.initUIEvent('touchstart', true, true, window);
343 touchpadRange.dispatchEvent(event);
345 touchpadRange.value = 2;
347 event = document.createEvent('UIEvent');
348 event.initUIEvent('touchcancel', true, true, window);
349 touchpadRange.dispatchEvent(event);
353 GEN('#endif'); // defined(OS_CHROMEOS)
356 * TestFixture for OptionsPage WebUI testing including tab history and support
357 * for preference manipulation. If you don't need the features in the C++
358 * fixture, use the simpler OptionsWebUITest (above) instead.
359 * @extends {testing.Test}
362 function OptionsWebUIExtendedTest() {}
364 OptionsWebUIExtendedTest.prototype = {
365 __proto__: testing.Test.prototype,
368 browsePreload: 'chrome://settings-frame',
371 typedefCppFixture: 'OptionsBrowserTest',
373 testGenPreamble: function() {
374 // Start with no supervised users managed by this profile.
375 GEN(' ClearPref("' + SUPERVISED_USERS_PREF + '");');
383 // user-image-stream is a streaming video element used for capturing a
384 // user image during OOBE.
385 this.accessibilityAuditConfig.ignoreSelectors('videoWithoutCaptions',
386 '.user-image-stream');
390 * Asserts that two non-nested arrays are equal. The arrays must contain only
391 * plain data types, no nested arrays or other objects.
392 * @param {Array} expected An array of expected values.
393 * @param {Array} result An array of actual values.
394 * @param {boolean} doSort If true, the arrays will be sorted before being
396 * @param {string} description A brief description for the array of actual
397 * values, to use in an error message if the arrays differ.
400 compareArrays_: function(expected, result, doSort, description) {
401 var errorMessage = '\n' + description + ': ' + result +
402 '\nExpected: ' + expected;
403 assertEquals(expected.length, result.length, errorMessage);
405 var expectedSorted = expected.slice();
406 var resultSorted = result.slice();
408 expectedSorted.sort();
412 for (var i = 0; i < expectedSorted.length; ++i) {
413 assertEquals(expectedSorted[i], resultSorted[i], errorMessage);
418 * Verifies that the correct pages are currently open/visible.
419 * @param {!Array.<string>} expectedPages An array of page names expected to
420 * be open, with the topmost listed last.
421 * @param {string=} opt_expectedUrl The URL path, including hash, expected to
422 * be open. If undefined, the topmost (last) page name in |expectedPages|
423 * will be used. In either case, 'chrome://settings-frame/' will be
427 verifyOpenPages_: function(expectedPages, opt_expectedUrl) {
428 // Check the topmost page.
429 expectEquals(null, PageManager.getVisibleBubble());
430 var currentPage = PageManager.getTopmostVisiblePage();
432 var lastExpected = expectedPages[expectedPages.length - 1];
433 expectEquals(lastExpected, currentPage.name);
434 // We'd like to check the title too, but we have to load the settings-frame
435 // instead of the outer settings page in order to have access to
436 // OptionsPage, and setting the title from within the settings-frame fails
437 // because of cross-origin access restrictions.
438 // TODO(pamg): Add a test fixture that loads chrome://settings and uses
439 // UI elements to access sub-pages, so we can test the titles and
441 var expectedUrl = (typeof opt_expectedUrl == 'undefined') ?
442 lastExpected : opt_expectedUrl;
443 var fullExpectedUrl = 'chrome://settings-frame/' + expectedUrl;
444 expectEquals(fullExpectedUrl, window.location.href);
446 // Collect open pages.
447 var allPageNames = Object.keys(PageManager.registeredPages).concat(
448 Object.keys(PageManager.registeredOverlayPages));
450 for (var i = 0; i < allPageNames.length; ++i) {
451 var name = allPageNames[i];
452 var page = PageManager.registeredPages[name] ||
453 PageManager.registeredOverlayPages[name];
455 openPages.push(page.name);
458 this.compareArrays_(expectedPages, openPages, true, 'Open pages');
462 * Verifies that the correct URLs are listed in the history. Asynchronous.
463 * @param {!Array.<string>} expectedHistory An array of URL paths expected to
464 * be in the tab navigation history, sorted by visit time, including the
465 * current page as the last entry. The base URL (chrome://settings-frame/)
466 * will be prepended to each. An initial 'about:blank' history entry is
467 * assumed and should not be included in this list.
468 * @param {Function=} callback A function to be called after the history has
469 * been verified successfully. May be undefined.
472 verifyHistory_: function(expectedHistory, callback) {
474 OptionsWebUIExtendedTest.verifyHistoryCallback = function(results) {
475 // The history always starts with a blank page.
476 assertEquals('about:blank', results.shift());
477 var fullExpectedHistory = [];
478 for (var i = 0; i < expectedHistory.length; ++i) {
479 fullExpectedHistory.push(
480 'chrome://settings-frame/' + expectedHistory[i]);
482 self.compareArrays_(fullExpectedHistory, results, false, 'History');
486 // The C++ fixture will call verifyHistoryCallback with the results.
487 chrome.send('optionsTestReportHistory');
491 * Overrides the page callbacks for the given PageManager overlay to verify
492 * that they are not called.
493 * @param {Object} overlay The singleton instance of the overlay.
496 prohibitChangesToOverlay_: function(overlay) {
497 overlay.initializePage =
498 overlay.didShowPage =
499 overlay.didClosePage = function() {
501 'Overlay was affected when changes were prohibited.');
507 * Set by verifyHistory_ to incorporate a followup callback, then called by the
508 * C++ fixture with the navigation history to be verified.
511 OptionsWebUIExtendedTest.verifyHistoryCallback = null;
513 // Show the search page with no query string, to fall back to the settings page.
514 // Test disabled because it's flaky. crbug.com/303841
515 TEST_F('OptionsWebUIExtendedTest', 'DISABLED_ShowSearchPageNoQuery',
517 PageManager.showPageByName('search');
518 this.verifyOpenPages_(['settings']);
519 this.verifyHistory_(['settings'], testDone);
522 // Show a page without updating history.
523 TEST_F('OptionsWebUIExtendedTest', 'ShowPageNoHistory', function() {
524 this.verifyOpenPages_(['settings'], '');
525 // There are only two main pages, 'settings' and 'search'. It's not possible
526 // to show the search page using PageManager.showPageByName, because it
527 // reverts to the settings page if it has no search text set. So we show the
528 // search page by performing a search, then test showPageByName.
529 $('search-field').onsearch({currentTarget: {value: 'query'}});
531 // The settings page is also still "open" (i.e., visible), in order to show
532 // the search results. Furthermore, the URL hasn't been updated in the parent
533 // page, because we've loaded the chrome-settings frame instead of the whole
534 // settings page, so the cross-origin call to set the URL fails.
535 this.verifyOpenPages_(['settings', 'search'], 'search#query');
537 this.verifyHistory_(['', 'search#query'], function() {
538 PageManager.showPageByName('settings', false);
539 self.verifyOpenPages_(['settings'], 'search#query');
540 self.verifyHistory_(['', 'search#query'], testDone);
544 TEST_F('OptionsWebUIExtendedTest', 'ShowPageWithHistory', function() {
545 // See comments for ShowPageNoHistory.
546 $('search-field').onsearch({currentTarget: {value: 'query'}});
548 this.verifyHistory_(['', 'search#query'], function() {
549 PageManager.showPageByName('settings', true);
550 self.verifyOpenPages_(['settings'], '#query');
551 self.verifyHistory_(['', 'search#query', '#query'],
556 TEST_F('OptionsWebUIExtendedTest', 'ShowPageReplaceHistory', function() {
557 // See comments for ShowPageNoHistory.
558 $('search-field').onsearch({currentTarget: {value: 'query'}});
560 this.verifyHistory_(['', 'search#query'], function() {
561 PageManager.showPageByName('settings', true, {'replaceState': true});
562 self.verifyOpenPages_(['settings'], '#query');
563 self.verifyHistory_(['', '#query'], testDone);
567 // This should be identical to ShowPageWithHisory.
568 TEST_F('OptionsWebUIExtendedTest', 'NavigateToPage', function() {
569 // See comments for ShowPageNoHistory.
570 $('search-field').onsearch({currentTarget: {value: 'query'}});
572 this.verifyHistory_(['', 'search#query'], function() {
573 PageManager.showPageByName('settings');
574 self.verifyOpenPages_(['settings'], '#query');
575 self.verifyHistory_(['', 'search#query', '#query'],
580 // Settings overlays are much more straightforward than settings pages, opening
581 // normally with none of the latter's quirks in the expected history or URL.
582 TEST_F('OptionsWebUIExtendedTest', 'ShowOverlayNoHistory', function() {
583 // Open a layer-1 overlay, not updating history.
584 PageManager.showPageByName('languages', false);
585 this.verifyOpenPages_(['settings', 'languages'], '');
588 this.verifyHistory_([''], function() {
589 // Open a layer-2 overlay for which the layer-1 is a parent, not updating
591 PageManager.showPageByName('addLanguage', false);
592 self.verifyOpenPages_(['settings', 'languages', 'addLanguage'],
594 self.verifyHistory_([''], testDone);
598 TEST_F('OptionsWebUIExtendedTest', 'ShowOverlayWithHistory', function() {
599 // Open a layer-1 overlay, updating history.
600 PageManager.showPageByName('languages', true);
601 this.verifyOpenPages_(['settings', 'languages']);
604 this.verifyHistory_(['', 'languages'], function() {
605 // Open a layer-2 overlay, updating history.
606 PageManager.showPageByName('addLanguage', true);
607 self.verifyOpenPages_(['settings', 'languages', 'addLanguage']);
608 self.verifyHistory_(['', 'languages', 'addLanguage'], testDone);
612 TEST_F('OptionsWebUIExtendedTest', 'ShowOverlayReplaceHistory', function() {
613 // Open a layer-1 overlay, updating history.
614 PageManager.showPageByName('languages', true);
616 this.verifyHistory_(['', 'languages'], function() {
617 // Open a layer-2 overlay, replacing history.
618 PageManager.showPageByName('addLanguage', true, {'replaceState': true});
619 self.verifyOpenPages_(['settings', 'languages', 'addLanguage']);
620 self.verifyHistory_(['', 'addLanguage'], testDone);
624 // Directly show an overlay further above this page, i.e. one for which the
625 // current page is an ancestor but not a parent.
626 TEST_F('OptionsWebUIExtendedTest', 'ShowOverlayFurtherAbove', function() {
627 // Open a layer-2 overlay directly.
628 PageManager.showPageByName('addLanguage', true);
629 this.verifyOpenPages_(['settings', 'languages', 'addLanguage']);
631 this.verifyHistory_(['', 'addLanguage'], testDone);
634 // Directly show a layer-2 overlay for which the layer-1 overlay is not a
636 TEST_F('OptionsWebUIExtendedTest', 'ShowUnrelatedOverlay', function() {
637 // Open a layer-1 overlay.
638 PageManager.showPageByName('languages', true);
639 this.verifyOpenPages_(['settings', 'languages']);
642 this.verifyHistory_(['', 'languages'], function() {
643 // Open an unrelated layer-2 overlay.
644 PageManager.showPageByName('cookies', true);
645 self.verifyOpenPages_(['settings', 'content', 'cookies']);
646 self.verifyHistory_(['', 'languages', 'cookies'], testDone);
651 TEST_F('OptionsWebUIExtendedTest', 'CloseOverlay', function() {
652 // Open a layer-1 overlay, then a layer-2 overlay on top of it.
653 PageManager.showPageByName('languages', true);
654 this.verifyOpenPages_(['settings', 'languages']);
655 PageManager.showPageByName('addLanguage', true);
656 this.verifyOpenPages_(['settings', 'languages', 'addLanguage']);
659 this.verifyHistory_(['', 'languages', 'addLanguage'], function() {
660 // Close the layer-2 overlay.
661 PageManager.closeOverlay();
662 self.verifyOpenPages_(['settings', 'languages']);
664 ['', 'languages', 'addLanguage', 'languages'],
666 // Close the layer-1 overlay.
667 PageManager.closeOverlay();
668 self.verifyOpenPages_(['settings'], '');
670 ['', 'languages', 'addLanguage', 'languages', ''],
676 // Test that closing an overlay that did not push history when opening does not
677 // again push history.
678 TEST_F('OptionsWebUIExtendedTest', 'CloseOverlayNoHistory', function() {
679 // Open the do not track confirmation prompt.
680 PageManager.showPageByName('doNotTrackConfirm', false);
682 // Opening the prompt does not add to the history.
683 this.verifyHistory_([''], function() {
684 // Close the overlay.
685 PageManager.closeOverlay();
686 // Still no history changes.
687 this.verifyHistory_([''], testDone);
691 // Make sure an overlay isn't closed (even temporarily) when another overlay is
693 TEST_F('OptionsWebUIExtendedTest', 'OverlayAboveNoReset', function() {
694 // Open a layer-1 overlay.
695 PageManager.showPageByName('languages', true);
696 this.verifyOpenPages_(['settings', 'languages']);
698 // Open a layer-2 overlay on top. This should not close 'languages'.
699 this.prohibitChangesToOverlay_(options.LanguageOptions.getInstance());
700 PageManager.showPageByName('addLanguage', true);
701 this.verifyOpenPages_(['settings', 'languages', 'addLanguage']);
705 TEST_F('OptionsWebUIExtendedTest', 'OverlayTabNavigation', function() {
706 // Open a layer-1 overlay, then a layer-2 overlay on top of it.
707 PageManager.showPageByName('languages', true);
708 PageManager.showPageByName('addLanguage', true);
711 // Go back twice, then forward twice.
712 self.verifyOpenPages_(['settings', 'languages', 'addLanguage']);
713 self.verifyHistory_(['', 'languages', 'addLanguage'], function() {
714 window.history.back();
715 waitForPopstate(function() {
716 self.verifyOpenPages_(['settings', 'languages']);
717 self.verifyHistory_(['', 'languages'], function() {
718 window.history.back();
719 waitForPopstate(function() {
720 self.verifyOpenPages_(['settings'], '');
721 self.verifyHistory_([''], function() {
722 window.history.forward();
723 waitForPopstate(function() {
724 self.verifyOpenPages_(['settings', 'languages']);
725 self.verifyHistory_(['', 'languages'], function() {
726 window.history.forward();
727 waitForPopstate(function() {
728 self.verifyOpenPages_(
729 ['settings', 'languages', 'addLanguage']);
731 ['', 'languages', 'addLanguage'], testDone);
742 // Going "back" to an overlay that's a child of the current overlay shouldn't
743 // close the current one.
744 TEST_F('OptionsWebUIExtendedTest', 'OverlayBackToChild', 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 self.verifyOpenPages_(['settings', 'languages', 'addLanguage']);
751 self.verifyHistory_(['', 'languages', 'addLanguage'], function() {
752 // Close the top overlay, then go back to it.
753 PageManager.closeOverlay();
754 self.verifyOpenPages_(['settings', 'languages']);
756 ['', 'languages', 'addLanguage', 'languages'],
758 // Going back to the 'addLanguage' page should not close 'languages'.
759 self.prohibitChangesToOverlay_(options.LanguageOptions.getInstance());
760 window.history.back();
761 waitForPopstate(function() {
762 self.verifyOpenPages_(['settings', 'languages', 'addLanguage']);
763 self.verifyHistory_(['', 'languages', 'addLanguage'],
770 // Going back to an unrelated overlay should close the overlay and its parent.
771 TEST_F('OptionsWebUIExtendedTest', 'OverlayBackToUnrelated', function() {
772 // Open a layer-1 overlay, then an unrelated layer-2 overlay.
773 PageManager.showPageByName('languages', true);
774 PageManager.showPageByName('cookies', true);
776 self.verifyOpenPages_(['settings', 'content', 'cookies']);
777 self.verifyHistory_(['', 'languages', 'cookies'], function() {
778 window.history.back();
779 waitForPopstate(function() {
780 self.verifyOpenPages_(['settings', 'languages']);
786 // Verify history changes properly while the page is loading.
787 TEST_F('OptionsWebUIExtendedTest', 'HistoryUpdatedAfterLoading', function() {
788 var loc = location.href;
790 document.documentElement.classList.add('loading');
791 assertTrue(PageManager.isLoading());
792 PageManager.showPageByName('searchEngines');
793 expectNotEquals(loc, location.href);
795 document.documentElement.classList.remove('loading');
796 assertFalse(PageManager.isLoading());
797 PageManager.showDefaultPage();
798 expectEquals(loc, location.href);
803 // A tip should be shown or hidden depending on whether this profile manages any
805 TEST_F('OptionsWebUIExtendedTest', 'SupervisingUsers', function() {
806 // We start managing no supervised users.
807 assertTrue($('profiles-supervised-dashboard-tip').hidden);
809 // Remove all supervised users, then add some, watching for the pref change
810 // notifications and UI updates in each case. Any non-empty pref dictionary
811 // is interpreted as having supervised users.
812 chrome.send('optionsTestSetPref', [SUPERVISED_USERS_PREF, {key: 'value'}]);
813 waitForResponse(BrowserOptions, 'updateManagesSupervisedUsers', function() {
814 assertFalse($('profiles-supervised-dashboard-tip').hidden);
815 chrome.send('optionsTestSetPref', [SUPERVISED_USERS_PREF, {}]);
816 waitForResponse(BrowserOptions, 'updateManagesSupervisedUsers', function() {
817 assertTrue($('profiles-supervised-dashboard-tip').hidden);
824 * TestFixture that loads the options page at a bogus URL.
825 * @extends {OptionsWebUIExtendedTest}
828 function OptionsWebUIRedirectTest() {
829 OptionsWebUIExtendedTest.call(this);
832 OptionsWebUIRedirectTest.prototype = {
833 __proto__: OptionsWebUIExtendedTest.prototype,
836 browsePreload: 'chrome://settings-frame/nonexistantPage',
839 TEST_F('OptionsWebUIRedirectTest', 'TestURL', function() {
840 assertEquals('chrome://settings-frame/', document.location.href);
841 this.verifyHistory_([''], testDone);