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 MANAGED_USERS_PREF
= 'profile.managed_users';
10 * Wait for the method specified by |methodName|, on the |object| object, to be
11 * called, then execute |afterFunction|.
13 function waitForResponse(object
, methodName
, afterFunction
) {
14 var originalCallback
= object
[methodName
];
16 // Install a wrapper that temporarily replaces the original function.
17 object
[methodName
] = function() {
18 object
[methodName
] = originalCallback
;
19 originalCallback
.apply(this, arguments
);
25 * Wait for the global window.onpopstate callback to be called (after a tab
26 * history navigation), then execute |afterFunction|.
28 function waitForPopstate(afterFunction
) {
29 waitForResponse(window
, 'onpopstate', afterFunction
);
33 * TestFixture for OptionsPage WebUI testing.
34 * @extends {testing.Test}
37 function OptionsWebUITest() {}
39 OptionsWebUITest
.prototype = {
40 __proto__
: testing
.Test
.prototype,
43 accessibilityIssuesAreErrors
: true,
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');
54 * Browse to the options page & call our preLoad().
56 browsePreload
: 'chrome://settings-frame',
61 * Register a mock handler to ensure expectations are met and options pages
65 this.makeAndRegisterMockHandler(
66 ['defaultZoomFactorAction',
75 'coreOptionsUserMetricsAction',
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
);
86 // Crashes on Mac only. See http://crbug.com/79181
87 GEN('#if defined(OS_MACOSX)');
88 GEN('#define MAYBE_testSetBooleanPrefTriggers ' +
89 'DISABLED_testSetBooleanPrefTriggers');
91 GEN('#define MAYBE_testSetBooleanPrefTriggers testSetBooleanPrefTriggers');
92 GEN('#endif // defined(OS_MACOSX)');
94 TEST_F('OptionsWebUITest', 'MAYBE_testSetBooleanPrefTriggers', function() {
95 // TODO(dtseng): make generic to click all buttons.
96 var showHomeButton
= $('show-home-button');
98 'browser.show_home_button',
100 'Options_Homepage_HomeButton',
102 // Note: this expectation is checked in testing::Test::tearDown.
103 this.mockHandler
.expects(once()).setBooleanPref(trueListValue
);
105 // Cause the handler to be called.
106 showHomeButton
.click();
107 showHomeButton
.blur();
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',
115 assertTrue($('search-engine-manager-page').hidden
);
116 var item
= $('manage-default-search-engines');
119 assertFalse($('search-engine-manager-page').hidden
);
121 window
.location
.reload();
123 assertEquals('chrome://settings-frame/searchEngines', document
.location
.href
);
124 assertFalse($('search-engine-manager-page').hidden
);
129 * Test the default zoom factor select element.
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);
138 // Verify that the zoom factor element has a reasonable number of choices.
139 expectGE(defaultZoomFactor
.options
.length
, defaultZoomFactorMinimumLength
);
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
);
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
);
162 var visibleChangeCounter
= 0;
163 var visibleChangeHandler = function() {
164 ++visibleChangeCounter
;
165 switch (visibleChangeCounter
) {
167 window
.setTimeout(function() {
168 assertTrue(dntOverlay
.visible
);
169 buttonToClick
.click();
173 window
.setTimeout(function() {
174 assertFalse(dntOverlay
.visible
);
175 assertEquals(confirmInterstitial
, dntCheckbox
.checked
);
176 dntOverlay
.removeEventListener(visibleChangeHandler
);
184 dntOverlay
.addEventListener('visibleChange', visibleChangeHandler
);
186 if (confirmInterstitial
) {
187 this.mockHandler
.expects(once()).setBooleanPref(
188 ['enable_do_not_track', true, 'Options_DoNotTrackCheckbox']);
190 // The mock handler complains if setBooleanPref is called even though
197 TEST_F('OptionsWebUITest', 'EnableDoNotTrackAndConfirmInterstitial',
199 this.testDoNotTrackInterstitial(true);
202 TEST_F('OptionsWebUITest', 'EnableDoNotTrackAndCancelInterstitial',
204 this.testDoNotTrackInterstitial(false);
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
);
215 var visibleChangeCounter
= 0;
216 var visibleChangeHandler = function() {
217 ++visibleChangeCounter
;
218 switch (visibleChangeCounter
) {
220 window
.setTimeout(function() {
221 assertTrue(dntOverlay
.visible
);
222 $('do-not-track-confirm-ok').click();
226 window
.setTimeout(function() {
227 assertFalse(dntOverlay
.visible
);
228 assertTrue(dntCheckbox
.checked
);
229 dntOverlay
.removeEventListener(visibleChangeHandler
);
237 dntOverlay
.addEventListener('visibleChange', visibleChangeHandler
);
239 this.mockHandler
.expects(once()).setBooleanPref(
240 eq(["enable_do_not_track", true, 'Options_DoNotTrackCheckbox']));
242 var verifyCorrectEndState = function() {
243 window
.setTimeout(function() {
244 assertFalse(dntOverlay
.visible
);
245 assertFalse(dntCheckbox
.checked
);
249 this.mockHandler
.expects(once()).setBooleanPref(
250 eq(["enable_do_not_track", false, 'Options_DoNotTrackCheckbox'])).will(
251 callFunction(verifyCorrectEndState
));
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', {
266 'keyIdentifier': 'Enter'
268 assertFalse(event
.defaultPrevented
);
269 page
.pageDiv
.dispatchEvent(event
);
270 assertTrue(event
.defaultPrevented
);
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
);
280 // Flaky on win. See http://crbug.com/315250
281 GEN('#if defined(OS_WIN)');
282 GEN('#define MAYBE_OverlayShowDoesntShift DISABLED_OverlayShowDoesntShift');
284 GEN('#define MAYBE_OverlayShowDoesntShift OverlayShowDoesntShift');
285 GEN('#endif // defined(OS_WIN)');
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
);
294 document
.addEventListener('webkitTransitionEnd', function(e
) {
295 if (e
.target
!= overlay
)
298 assertFalse(overlay
.classList
.contains('transparent'));
299 expectEquals(numFrozenPages
, frozenPages
.length
);
303 OptionsPage
.navigateToPage(overlayName
);
304 var numFrozenPages
= frozenPages
.length
;
305 expectGT(numFrozenPages
, 0);
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}
315 function OptionsWebUIExtendedTest() {}
317 OptionsWebUIExtendedTest
.prototype = {
318 __proto__
: testing
.Test
.prototype,
321 browsePreload
: 'chrome://settings-frame',
324 typedefCppFixture
: 'OptionsBrowserTest',
326 testGenPreamble: function() {
327 // Start with no supervised users managed by this profile.
328 GEN(' ClearPref("' + MANAGED_USERS_PREF
+ '");');
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');
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
349 * @param {string} description A brief description for the array of actual
350 * values, to use in an error message if the arrays differ.
353 compareArrays_: function(expected
, result
, doSort
, description
) {
354 var errorMessage
= '\n' + description
+ ': ' + result
+
355 '\nExpected: ' + expected
;
356 assertEquals(expected
.length
, result
.length
, errorMessage
);
358 var expectedSorted
= expected
.slice();
359 var resultSorted
= result
.slice();
361 expectedSorted
.sort();
365 for (var i
= 0; i
< expectedSorted
.length
; ++i
) {
366 assertEquals(expectedSorted
[i
], resultSorted
[i
], errorMessage
);
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
380 verifyOpenPages_: function(expectedPages
, expectedUrl
) {
381 // Check the topmost page.
382 assertEquals(null, OptionsPage
.getVisibleBubble());
383 var currentPage
= OptionsPage
.getTopmostVisiblePage();
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
394 var fullExpectedUrl
= 'chrome://settings-frame/' +
395 (expectedUrl
? expectedUrl
: lastExpected
);
396 assertEquals(fullExpectedUrl
, window
.location
.href
);
398 // Collect open pages.
399 var allPageNames
= Object
.keys(OptionsPage
.registeredPages
).concat(
400 Object
.keys(OptionsPage
.registeredOverlayPages
));
402 for (var i
= 0; i
< allPageNames
.length
; ++i
) {
403 var name
= allPageNames
[i
];
404 var page
= OptionsPage
.registeredPages
[name
] ||
405 OptionsPage
.registeredOverlayPages
[name
];
407 openPages
.push(page
.name
);
410 this.compareArrays_(expectedPages
, openPages
, true, 'Open pages');
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.
424 verifyHistory_: function(expectedHistory
, callback
) {
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
]);
434 self
.compareArrays_(fullExpectedHistory
, results
, false, 'History');
438 // The C++ fixture will call verifyHistoryCallback with the results.
439 chrome
.send('optionsTestReportHistory');
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.
448 prohibitChangesToOverlay_: function(overlay
) {
449 overlay
.initializePage
=
450 overlay
.didShowPage
=
451 overlay
.didClosePage = function() {
453 'Overlay was affected when changes were prohibited.');
459 * Set by verifyHistory_ to incorporate a followup callback, then called by the
460 * C++ fixture with the navigation history to be verified.
463 OptionsWebUIExtendedTest
.verifyHistoryCallback
= null;
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',
469 OptionsPage
.showPageByName('search');
470 this.verifyOpenPages_(['settings']);
471 this.verifyHistory_(['settings'], testDone
);
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'}});
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');
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
);
496 TEST_F('OptionsWebUIExtendedTest', 'ShowPageWithHistory', function() {
497 // See comments for ShowPageNoHistory.
498 $('search-field').onsearch({currentTarget
: {value
: 'query'}});
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'],
508 TEST_F('OptionsWebUIExtendedTest', 'ShowPageReplaceHistory', function() {
509 // See comments for ShowPageNoHistory.
510 $('search-field').onsearch({currentTarget
: {value
: 'query'}});
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
);
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'}});
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'],
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');
540 this.verifyHistory_(['settings'], function() {
541 // Open a layer-2 overlay for which the layer-1 is a parent, not updating
543 OptionsPage
.showPageByName('addLanguage', false);
544 self
.verifyOpenPages_(['settings', 'languages', 'addLanguage'],
546 self
.verifyHistory_(['settings'], testDone
);
550 TEST_F('OptionsWebUIExtendedTest', 'ShowOverlayWithHistory', function() {
551 // Open a layer-1 overlay, updating history.
552 OptionsPage
.showPageByName('languages', true);
553 this.verifyOpenPages_(['settings', 'languages']);
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
);
564 TEST_F('OptionsWebUIExtendedTest', 'ShowOverlayReplaceHistory', function() {
565 // Open a layer-1 overlay, updating history.
566 OptionsPage
.showPageByName('languages', true);
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
);
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']);
583 this.verifyHistory_(['settings', 'addLanguage'], testDone
);
586 // Directly show a layer-2 overlay for which the layer-1 overlay is not a
588 TEST_F('OptionsWebUIExtendedTest', 'ShowUnrelatedOverlay', function() {
589 // Open a layer-1 overlay.
590 OptionsPage
.showPageByName('languages', true);
591 this.verifyOpenPages_(['settings', 'languages']);
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
);
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']);
611 this.verifyHistory_(['settings', 'languages', 'addLanguage'], function() {
612 // Close the layer-2 overlay.
613 OptionsPage
.closeOverlay();
614 self
.verifyOpenPages_(['settings', 'languages']);
616 ['settings', 'languages', 'addLanguage', 'languages'],
618 // Close the layer-1 overlay.
619 OptionsPage
.closeOverlay();
620 self
.verifyOpenPages_(['settings']);
622 ['settings', 'languages', 'addLanguage', 'languages', 'settings'],
628 // Make sure an overlay isn't closed (even temporarily) when another overlay is
630 TEST_F('OptionsWebUIExtendedTest', 'OverlayAboveNoReset', function() {
631 // Open a layer-1 overlay.
632 OptionsPage
.showPageByName('languages', true);
633 this.verifyOpenPages_(['settings', 'languages']);
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']);
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);
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']);
668 ['settings', 'languages', 'addLanguage'], testDone
);
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);
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']);
693 ['settings', 'languages', 'addLanguage', 'languages'],
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'],
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);
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']);
723 // Verify history changes properly while the page is loading.
724 TEST_F('OptionsWebUIExtendedTest', 'HistoryUpdatedAfterLoading', function() {
725 var loc
= location
.href
;
727 document
.documentElement
.classList
.add('loading');
728 assertTrue(OptionsPage
.isLoading());
729 OptionsPage
.navigateToPage('searchEngines');
730 expectNotEquals(loc
, location
.href
);
732 document
.documentElement
.classList
.remove('loading');
733 assertFalse(OptionsPage
.isLoading());
734 OptionsPage
.showDefaultPage();
735 expectEquals(loc
, location
.href
);
740 // A tip should be shown or hidden depending on whether this profile manages any
742 TEST_F('OptionsWebUIExtendedTest', 'SupervisingUsers', function() {
743 // We start managing no supervised users.
744 assertTrue($('profiles-supervised-dashboard-tip').hidden
);
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
);