OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 GEN_INCLUDE(['options_browsertest_base.js']); | |
6 GEN('#include "chrome/browser/ui/webui/options/options_browsertest.h"'); | |
7 | |
8 /** @const */ var SUPERVISED_USERS_PREF = 'profile.managed_users'; | |
9 | |
10 /** | |
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 | |
15 * callback. | |
16 * @param {!Function} afterFunction A function to call after object.methodName() | |
17 * is called. | |
18 */ | |
19 function waitForResponse(object, methodName, afterFunction) { | |
20 var originalCallback = object[methodName]; | |
21 | |
22 // Install a wrapper that temporarily replaces the original function. | |
23 object[methodName] = function() { | |
24 object[methodName] = originalCallback; | |
25 originalCallback.apply(this, arguments); | |
26 afterFunction(); | |
27 }; | |
28 } | |
29 | |
30 /** | |
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. | |
34 */ | |
35 function waitForPopstate(afterFunction) { | |
36 waitForResponse(window, 'onpopstate', afterFunction); | |
37 } | |
38 | |
39 /** | |
40 * TestFixture for OptionsPage WebUI testing. | |
41 * @extends {testing.Test} | |
42 * @constructor | |
43 */ | |
44 function OptionsWebUITest() {} | |
45 | |
46 OptionsWebUITest.prototype = { | |
47 __proto__: OptionsBrowsertestBase.prototype, | |
48 | |
49 /** | |
50 * Browse to the options page & call our preLoad(). | |
51 * @override | |
52 */ | |
53 browsePreload: 'chrome://settings-frame', | |
54 | |
55 /** @override */ | |
56 isAsync: true, | |
57 | |
58 /** | |
59 * Register a mock handler to ensure expectations are met and options pages | |
60 * behave correctly. | |
61 */ | |
62 preLoad: function() { | |
63 this.makeAndRegisterMockHandler( | |
64 ['defaultZoomFactorAction', | |
65 'fetchPrefs', | |
66 'observePrefs', | |
67 'setBooleanPref', | |
68 'setIntegerPref', | |
69 'setDoublePref', | |
70 'setStringPref', | |
71 'setObjectPref', | |
72 'clearPref', | |
73 'coreOptionsUserMetricsAction', | |
74 ]); | |
75 | |
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); | |
81 }, | |
82 | |
83 /** @override */ | |
84 setUp: function() { | |
85 OptionsBrowsertestBase.prototype.setUp.call(this); | |
86 | |
87 // Enable when failure is resolved. | |
88 // AX_ARIA_10: http://crbug.com/559329 | |
89 this.accessibilityAuditConfig.ignoreSelectors( | |
90 'unsupportedAriaAttribute', | |
91 '#profiles-list'); | |
92 | |
93 var linkWithUnclearPurposeSelectors = [ | |
94 '#sync-overview > A', | |
95 '#privacy-explanation > A', | |
96 '#languages-section > .settings-row > A', | |
97 '#cloudprint-options-mdns > .settings-row > A', | |
98 '#cups-printers-section > .settings-row > A', | |
99 '#do-not-track-confirm-overlay > .action-area > .hbox.stretch > A', | |
100 ]; | |
101 | |
102 // Enable when failure is resolved. | |
103 // AX_TEXT_04: http://crbug.com/559318 | |
104 this.accessibilityAuditConfig.ignoreSelectors( | |
105 'linkWithUnclearPurpose', | |
106 linkWithUnclearPurposeSelectors); | |
107 | |
108 // Causes testDefaultZoomFactor to flake. See http://crbug.com/611233. | |
109 var requiredOwnedAriaRoleMissingSelectors = [ | |
110 '#default-search-engine-list', | |
111 '#other-search-engine-list', | |
112 ]; | |
113 | |
114 // Enable when failure is resolved. | |
115 // AX_ARIA_08: http://crbug.com/606657 | |
116 this.accessibilityAuditConfig.ignoreSelectors( | |
117 'requiredOwnedAriaRoleMissing', | |
118 requiredOwnedAriaRoleMissingSelectors); | |
119 }, | |
120 }; | |
121 | |
122 /** | |
123 * Wait for all targets to be hidden. | |
124 * @param {Array<Element>} targets | |
125 */ | |
126 function waitUntilHidden(targets) { | |
127 function isHidden(el) { return el.hidden; } | |
128 function ensureTransition(el) { ensureTransitionEndEvent(el, 500); } | |
129 | |
130 document.addEventListener('transitionend', function f(e) { | |
131 if (targets.indexOf(e.target) >= 0 && targets.every(isHidden)) { | |
132 document.removeEventListener(f, 'transitionend'); | |
133 testDone(); | |
134 } | |
135 }); | |
136 | |
137 targets.forEach(ensureTransition); | |
138 } | |
139 | |
140 // Crashes on Mac only. See http://crbug.com/79181 | |
141 GEN('#if defined(OS_MACOSX)'); | |
142 GEN('#define MAYBE_testSetBooleanPrefTriggers ' + | |
143 'DISABLED_testSetBooleanPrefTriggers'); | |
144 GEN('#else'); | |
145 GEN('#define MAYBE_testSetBooleanPrefTriggers testSetBooleanPrefTriggers'); | |
146 GEN('#endif // defined(OS_MACOSX)'); | |
147 | |
148 TEST_F('OptionsWebUITest', 'MAYBE_testSetBooleanPrefTriggers', function() { | |
149 // TODO(dtseng): make generic to click all buttons. | |
150 var showHomeButton = | |
151 document.querySelector('input[pref="browser.show_home_button"]'); | |
152 var trueListValue = [ | |
153 'browser.show_home_button', | |
154 true, | |
155 'Options_Homepage_HomeButton', | |
156 ]; | |
157 // Note: this expectation is checked in testing::Test::tearDown. | |
158 this.mockHandler.expects(once()).setBooleanPref(trueListValue); | |
159 | |
160 // Cause the handler to be called. | |
161 showHomeButton.click(); | |
162 showHomeButton.blur(); | |
163 testDone(); | |
164 }); | |
165 | |
166 // Not meant to run on ChromeOS at this time. | |
167 // Not finishing in windows. http://crbug.com/81723 | |
168 TEST_F('OptionsWebUITest', 'DISABLED_testRefreshStaysOnCurrentPage', | |
169 function() { | |
170 assertTrue($('search-engine-manager-page').hidden); | |
171 var item = $('manage-default-search-engines'); | |
172 item.click(); | |
173 | |
174 assertFalse($('search-engine-manager-page').hidden); | |
175 | |
176 window.location.reload(); | |
177 | |
178 assertEquals('chrome://settings-frame/searchEngines', document.location.href); | |
179 assertFalse($('search-engine-manager-page').hidden); | |
180 testDone(); | |
181 }); | |
182 | |
183 /** | |
184 * Test the default zoom factor select element. | |
185 */ | |
186 TEST_F('OptionsWebUITest', 'testDefaultZoomFactor', function() { | |
187 // The expected minimum length of the |defaultZoomFactor| element. | |
188 var defaultZoomFactorMinimumLength = 10; | |
189 // Verify that the zoom factor element exists. | |
190 var defaultZoomFactor = $('defaultZoomFactor'); | |
191 assertNotEquals(defaultZoomFactor, null); | |
192 | |
193 // Verify that the zoom factor element has a reasonable number of choices. | |
194 expectGE(defaultZoomFactor.options.length, defaultZoomFactorMinimumLength); | |
195 | |
196 // Simulate a change event, selecting the highest zoom value. Verify that | |
197 // the javascript handler was invoked once. | |
198 this.mockHandler.expects(once()).defaultZoomFactorAction(NOT_NULL). | |
199 will(callFunction(function() { })); | |
200 defaultZoomFactor.selectedIndex = defaultZoomFactor.options.length - 1; | |
201 var event = {target: defaultZoomFactor}; | |
202 if (defaultZoomFactor.onchange) defaultZoomFactor.onchange(event); | |
203 testDone(); | |
204 }); | |
205 | |
206 /** | |
207 * If |confirmInterstitial| is true, the OK button of the Do Not Track | |
208 * interstitial is pressed, otherwise the abort button is pressed. | |
209 * @param {boolean} confirmInterstitial Whether to confirm the Do Not Track | |
210 * interstitial. | |
211 */ | |
212 OptionsWebUITest.prototype.testDoNotTrackInterstitial = | |
213 function(confirmInterstitial) { | |
214 Preferences.prefsFetchedCallback({'enable_do_not_track': {'value': false}}); | |
215 var buttonToClick = confirmInterstitial ? $('do-not-track-confirm-ok') : | |
216 $('do-not-track-confirm-cancel'); | |
217 var dntCheckbox = $('do-not-track-enabled'); | |
218 var dntOverlay = PageManager.registeredOverlayPages['donottrackconfirm']; | |
219 assertFalse(dntCheckbox.checked); | |
220 | |
221 var visibleChangeCounter = 0; | |
222 var visibleChangeHandler = function() { | |
223 ++visibleChangeCounter; | |
224 switch (visibleChangeCounter) { | |
225 case 1: | |
226 window.setTimeout(function() { | |
227 assertTrue(dntOverlay.visible); | |
228 buttonToClick.click(); | |
229 }, 0); | |
230 break; | |
231 case 2: | |
232 window.setTimeout(function() { | |
233 assertFalse(dntOverlay.visible); | |
234 assertEquals(confirmInterstitial, dntCheckbox.checked); | |
235 dntOverlay.removeEventListener('visibleChange', visibleChangeHandler); | |
236 testDone(); | |
237 }, 0); | |
238 break; | |
239 default: | |
240 assertTrue(false); | |
241 } | |
242 }; | |
243 dntOverlay.addEventListener('visibleChange', visibleChangeHandler); | |
244 | |
245 if (confirmInterstitial) { | |
246 this.mockHandler.expects(once()).setBooleanPref( | |
247 ['enable_do_not_track', true, 'Options_DoNotTrackCheckbox']); | |
248 } else { | |
249 // The mock handler complains if setBooleanPref is called even though | |
250 // it should not be. | |
251 } | |
252 | |
253 dntCheckbox.click(); | |
254 }; | |
255 | |
256 TEST_F('OptionsWebUITest', 'EnableDoNotTrackAndConfirmInterstitial', | |
257 function() { | |
258 this.testDoNotTrackInterstitial(true); | |
259 }); | |
260 | |
261 TEST_F('OptionsWebUITest', 'EnableDoNotTrackAndCancelInterstitial', | |
262 function() { | |
263 this.testDoNotTrackInterstitial(false); | |
264 }); | |
265 | |
266 // Check that the "Do not Track" preference can be correctly disabled. | |
267 // In order to do that, we need to enable it first. | |
268 TEST_F('OptionsWebUITest', 'EnableAndDisableDoNotTrack', function() { | |
269 Preferences.prefsFetchedCallback({'enable_do_not_track': {'value': false}}); | |
270 var dntCheckbox = $('do-not-track-enabled'); | |
271 var dntOverlay = PageManager.registeredOverlayPages.donottrackconfirm; | |
272 assertFalse(dntCheckbox.checked); | |
273 | |
274 var visibleChangeCounter = 0; | |
275 var visibleChangeHandler = function() { | |
276 ++visibleChangeCounter; | |
277 switch (visibleChangeCounter) { | |
278 case 1: | |
279 window.setTimeout(function() { | |
280 assertTrue(dntOverlay.visible); | |
281 $('do-not-track-confirm-ok').click(); | |
282 }, 0); | |
283 break; | |
284 case 2: | |
285 window.setTimeout(function() { | |
286 assertFalse(dntOverlay.visible); | |
287 assertTrue(dntCheckbox.checked); | |
288 dntOverlay.removeEventListener('visibleChange', visibleChangeHandler); | |
289 dntCheckbox.click(); | |
290 }, 0); | |
291 break; | |
292 default: | |
293 assertNotReached(); | |
294 } | |
295 }; | |
296 dntOverlay.addEventListener('visibleChange', visibleChangeHandler); | |
297 | |
298 this.mockHandler.expects(once()).setBooleanPref( | |
299 eq(['enable_do_not_track', true, 'Options_DoNotTrackCheckbox'])); | |
300 | |
301 var verifyCorrectEndState = function() { | |
302 window.setTimeout(function() { | |
303 assertFalse(dntOverlay.visible); | |
304 assertFalse(dntCheckbox.checked); | |
305 testDone(); | |
306 }, 0); | |
307 }; | |
308 this.mockHandler.expects(once()).setBooleanPref( | |
309 eq(['enable_do_not_track', false, 'Options_DoNotTrackCheckbox'])).will( | |
310 callFunction(verifyCorrectEndState)); | |
311 | |
312 dntCheckbox.click(); | |
313 }); | |
314 | |
315 // Fails on chromeos, http://crbug.com/660867 | |
316 // Verify that preventDefault() is called on 'Enter' keydown events that trigger | |
317 // the default button. If this doesn't happen, other elements that may get | |
318 // focus (by the overlay closing for instance), will execute in addition to the | |
319 // default button. See crbug.com/268336. | |
320 TEST_F('OptionsWebUITest', 'DISABLED_EnterPreventsDefault', function() { | |
321 var page = HomePageOverlay.getInstance(); | |
322 PageManager.showPageByName(page.name); | |
323 var event = new KeyboardEvent('keydown', { | |
324 'bubbles': true, | |
325 'cancelable': true, | |
326 'key': 'Enter' | |
327 }); | |
328 assertFalse(event.defaultPrevented); | |
329 page.pageDiv.dispatchEvent(event); | |
330 assertTrue(event.defaultPrevented); | |
331 testDone(); | |
332 }); | |
333 | |
334 // Verifies that sending an empty list of indexes to move doesn't crash chrome. | |
335 TEST_F('OptionsWebUITest', 'emptySelectedIndexesDoesntCrash', function() { | |
336 chrome.send('dragDropStartupPage', [0, []]); | |
337 setTimeout(testDone); | |
338 }); | |
339 | |
340 // This test turns out to be flaky on all platforms. | |
341 // See http://crbug.com/315250. | |
342 | |
343 // An overlay's position should remain the same as it shows. | |
344 TEST_F('OptionsWebUITest', 'DISABLED_OverlayShowDoesntShift', function() { | |
345 var overlayName = 'startup'; | |
346 var overlay = $('startup-overlay'); | |
347 var frozenPages = document.getElementsByClassName('frozen'); // Gets updated. | |
348 expectEquals(0, frozenPages.length); | |
349 | |
350 document.addEventListener('transitionend', function(e) { | |
351 if (e.target != overlay) | |
352 return; | |
353 | |
354 assertFalse(overlay.classList.contains('transparent')); | |
355 expectEquals(numFrozenPages, frozenPages.length); | |
356 testDone(); | |
357 }); | |
358 | |
359 PageManager.showPageByName(overlayName); | |
360 var numFrozenPages = frozenPages.length; | |
361 expectGT(numFrozenPages, 0); | |
362 }); | |
363 | |
364 GEN('#if defined(OS_CHROMEOS)'); | |
365 // Verify that range inputs respond to touch events. Currently only Chrome OS | |
366 // uses slider options. | |
367 TEST_F('OptionsWebUITest', 'RangeInputHandlesTouchEvents', function() { | |
368 this.mockHandler.expects(once()).setIntegerPref([ | |
369 'settings.touchpad.sensitivity2', 1]); | |
370 | |
371 var touchpadRange = $('touchpad-sensitivity-range'); | |
372 var event = document.createEvent('UIEvent'); | |
373 event.initUIEvent('touchstart', true, true, window); | |
374 touchpadRange.dispatchEvent(event); | |
375 | |
376 event = document.createEvent('UIEvent'); | |
377 event.initUIEvent('touchmove', true, true, window); | |
378 touchpadRange.dispatchEvent(event); | |
379 | |
380 touchpadRange.value = 1; | |
381 | |
382 event = document.createEvent('UIEvent'); | |
383 event.initUIEvent('touchend', true, true, window); | |
384 touchpadRange.dispatchEvent(event); | |
385 | |
386 // touchcancel should also trigger the handler, since it | |
387 // changes the slider position. | |
388 this.mockHandler.expects(once()).setIntegerPref([ | |
389 'settings.touchpad.sensitivity2', 2]); | |
390 | |
391 event = document.createEvent('UIEvent'); | |
392 event.initUIEvent('touchstart', true, true, window); | |
393 touchpadRange.dispatchEvent(event); | |
394 | |
395 touchpadRange.value = 2; | |
396 | |
397 event = document.createEvent('UIEvent'); | |
398 event.initUIEvent('touchcancel', true, true, window); | |
399 touchpadRange.dispatchEvent(event); | |
400 | |
401 testDone(); | |
402 }); | |
403 GEN('#endif'); // defined(OS_CHROMEOS) | |
404 | |
405 /** | |
406 * TestFixture for OptionsPage WebUI testing including tab history and support | |
407 * for preference manipulation. If you don't need the features in the C++ | |
408 * fixture, use the simpler OptionsWebUITest (above) instead. | |
409 * @extends {testing.Test} | |
410 * @constructor | |
411 */ | |
412 function OptionsWebUIExtendedTest() {} | |
413 | |
414 OptionsWebUIExtendedTest.prototype = { | |
415 __proto__: OptionsWebUITest.prototype, | |
416 | |
417 /** @override */ | |
418 typedefCppFixture: 'OptionsBrowserTest', | |
419 | |
420 /** @override */ | |
421 setUp: function() { | |
422 OptionsWebUITest.prototype.setUp.call(this); | |
423 | |
424 // Enable when failure is resolved. | |
425 // AX_ARIA_10: http://crbug.com/559329 | |
426 this.accessibilityAuditConfig.ignoreSelectors( | |
427 'unsupportedAriaAttribute', | |
428 '#profiles-list'); | |
429 | |
430 var controlsWithoutLabelSelectors = [ | |
431 '#cookies-view-page > .content-area.cookies-list-content-area > *', | |
432 '#other-search-engine-list > .deletable-item > DIV > *', | |
433 ]; | |
434 | |
435 // Enable when failure is resolved. | |
436 // AX_TEXT_01: http://crbug.com/559330 | |
437 this.accessibilityAuditConfig.ignoreSelectors( | |
438 'controlsWithoutLabel', | |
439 controlsWithoutLabelSelectors); | |
440 | |
441 var linkWithUnclearPurposeSelectors = [ | |
442 '#sync-overview > A', | |
443 '#privacy-explanation > A', | |
444 '#languages-section > .settings-row > A', | |
445 '#cloudprint-options-mdns > .settings-row > A', | |
446 // Selectors below only affect ChromeOS tests. | |
447 '#privacy-section > DIV > DIV:nth-of-type(9) > A', | |
448 '#accessibility-learn-more', | |
449 ]; | |
450 | |
451 // Enable when failure is resolved. | |
452 // AX_TEXT_04: http://crbug.com/559326 | |
453 this.accessibilityAuditConfig.ignoreSelectors( | |
454 'linkWithUnclearPurpose', | |
455 linkWithUnclearPurposeSelectors); | |
456 | |
457 var requiredOwnedAriaRoleMissingSelectors = [ | |
458 '#default-search-engine-list', | |
459 '#other-search-engine-list', | |
460 ]; | |
461 | |
462 // Enable when failure is resolved. | |
463 // AX_ARIA_08: http://crbug.com/605689 | |
464 this.accessibilityAuditConfig.ignoreSelectors( | |
465 'requiredOwnedAriaRoleMissing', | |
466 requiredOwnedAriaRoleMissingSelectors); | |
467 }, | |
468 | |
469 testGenPreamble: function() { | |
470 // Start with no supervised users managed by this profile. | |
471 GEN(' ClearPref("' + SUPERVISED_USERS_PREF + '");'); | |
472 }, | |
473 | |
474 /** | |
475 * Asserts that two non-nested arrays are equal. The arrays must contain only | |
476 * plain data types, no nested arrays or other objects. | |
477 * @param {Array} expected An array of expected values. | |
478 * @param {Array} result An array of actual values. | |
479 * @param {boolean} doSort If true, the arrays will be sorted before being | |
480 * compared. | |
481 * @param {string} description A brief description for the array of actual | |
482 * values, to use in an error message if the arrays differ. | |
483 * @private | |
484 */ | |
485 compareArrays_: function(expected, result, doSort, description) { | |
486 var errorMessage = '\n' + description + ': ' + result + | |
487 '\nExpected: ' + expected; | |
488 assertEquals(expected.length, result.length, errorMessage); | |
489 | |
490 var expectedSorted = expected.slice(); | |
491 var resultSorted = result.slice(); | |
492 if (doSort) { | |
493 expectedSorted.sort(); | |
494 resultSorted.sort(); | |
495 } | |
496 | |
497 for (var i = 0; i < expectedSorted.length; ++i) { | |
498 assertEquals(expectedSorted[i], resultSorted[i], errorMessage); | |
499 } | |
500 }, | |
501 | |
502 /** | |
503 * Verifies that the correct pages are currently open/visible. | |
504 * @param {!Array<string>} expectedPages An array of page names expected to | |
505 * be open, with the topmost listed last. | |
506 * @param {string=} opt_expectedUrl The URL path, including hash, expected to | |
507 * be open. If undefined, the topmost (last) page name in |expectedPages| | |
508 * will be used. In either case, 'chrome://settings-frame/' will be | |
509 * prepended. | |
510 * @private | |
511 */ | |
512 verifyOpenPages_: function(expectedPages, opt_expectedUrl) { | |
513 // Check the topmost page. | |
514 expectEquals(null, PageManager.getVisibleBubble()); | |
515 var currentPage = PageManager.getTopmostVisiblePage(); | |
516 | |
517 var lastExpected = expectedPages[expectedPages.length - 1]; | |
518 expectEquals(lastExpected, currentPage.name); | |
519 // We'd like to check the title too, but we have to load the settings-frame | |
520 // instead of the outer settings page in order to have access to | |
521 // OptionsPage, and setting the title from within the settings-frame fails | |
522 // because of cross-origin access restrictions. | |
523 // TODO(pamg): Add a test fixture that loads chrome://settings and uses | |
524 // UI elements to access sub-pages, so we can test the titles and | |
525 // search-page URLs. | |
526 var expectedUrl = (typeof opt_expectedUrl == 'undefined') ? | |
527 lastExpected : opt_expectedUrl; | |
528 var fullExpectedUrl = 'chrome://settings-frame/' + expectedUrl; | |
529 expectEquals(fullExpectedUrl, window.location.href); | |
530 | |
531 // Collect open pages. | |
532 var allPageNames = Object.keys(PageManager.registeredPages).concat( | |
533 Object.keys(PageManager.registeredOverlayPages)); | |
534 var openPages = []; | |
535 for (var i = 0; i < allPageNames.length; ++i) { | |
536 var name = allPageNames[i]; | |
537 var page = PageManager.registeredPages[name] || | |
538 PageManager.registeredOverlayPages[name]; | |
539 if (page.visible) | |
540 openPages.push(page.name); | |
541 } | |
542 | |
543 this.compareArrays_(expectedPages, openPages, true, 'Open pages'); | |
544 }, | |
545 | |
546 /* | |
547 * Verifies that the correct URLs are listed in the history. Asynchronous. | |
548 * @param {!Array<string>} expectedHistory An array of URL paths expected to | |
549 * be in the tab navigation history, sorted by visit time, including the | |
550 * current page as the last entry. The base URL (chrome://settings-frame/) | |
551 * will be prepended to each. An initial 'about:blank' history entry is | |
552 * assumed and should not be included in this list. | |
553 * @param {Function=} callback A function to be called after the history has | |
554 * been verified successfully. May be undefined. | |
555 * @private | |
556 */ | |
557 verifyHistory_: function(expectedHistory, callback) { | |
558 var self = this; | |
559 OptionsWebUIExtendedTest.verifyHistoryCallback = function(results) { | |
560 // The history always starts with a blank page. | |
561 assertEquals('about:blank', results.shift()); | |
562 var fullExpectedHistory = []; | |
563 for (var i = 0; i < expectedHistory.length; ++i) { | |
564 fullExpectedHistory.push( | |
565 'chrome://settings-frame/' + expectedHistory[i]); | |
566 } | |
567 self.compareArrays_(fullExpectedHistory, results, false, 'History'); | |
568 callback(); | |
569 }; | |
570 | |
571 // The C++ fixture will call verifyHistoryCallback with the results. | |
572 chrome.send('optionsTestReportHistory'); | |
573 }, | |
574 | |
575 /** | |
576 * Overrides the page callbacks for the given PageManager overlay to verify | |
577 * that they are not called. | |
578 * @param {Object} overlay The singleton instance of the overlay. | |
579 * @private | |
580 */ | |
581 prohibitChangesToOverlay_: function(overlay) { | |
582 overlay.initializePage = | |
583 overlay.didShowPage = | |
584 overlay.didClosePage = function() { | |
585 assertTrue(false, | |
586 'Overlay was affected when changes were prohibited.'); | |
587 }; | |
588 }, | |
589 }; | |
590 | |
591 /** | |
592 * Set by verifyHistory_ to incorporate a followup callback, then called by the | |
593 * C++ fixture with the navigation history to be verified. | |
594 * @type {Function} | |
595 */ | |
596 OptionsWebUIExtendedTest.verifyHistoryCallback = null; | |
597 | |
598 // Show the search page with no query string, to fall back to the settings page. | |
599 // Test disabled because it's flaky. crbug.com/303841 | |
600 TEST_F('OptionsWebUIExtendedTest', 'DISABLED_ShowSearchPageNoQuery', | |
601 function() { | |
602 PageManager.showPageByName('search'); | |
603 this.verifyOpenPages_(['settings']); | |
604 this.verifyHistory_(['settings'], testDone); | |
605 }); | |
606 | |
607 // Manipulate the search page via the search field. | |
608 TEST_F('OptionsWebUIExtendedTest', 'ShowSearchFromField', function() { | |
609 $('search-field').onsearch({currentTarget: {value: 'query'}}); | |
610 this.verifyOpenPages_(['settings', 'search'], 'search#query'); | |
611 this.verifyHistory_(['', 'search#query'], function() { | |
612 $('search-field').onsearch({currentTarget: {value: 'query2'}}); | |
613 this.verifyOpenPages_(['settings', 'search'], 'search#query2'); | |
614 this.verifyHistory_(['', 'search#query', 'search#query2'], function() { | |
615 $('search-field').onsearch({currentTarget: {value: ''}}); | |
616 this.verifyOpenPages_(['settings'], ''); | |
617 this.verifyHistory_(['', 'search#query', 'search#query2', ''], testDone); | |
618 }.bind(this)); | |
619 }.bind(this)); | |
620 }); | |
621 | |
622 // Show a page without updating history. | |
623 TEST_F('OptionsWebUIExtendedTest', 'ShowPageNoHistory', function() { | |
624 this.verifyOpenPages_(['settings'], ''); | |
625 PageManager.showPageByName('search', true, {hash: '#query'}); | |
626 | |
627 // The settings page is also still "open" (i.e., visible), in order to show | |
628 // the search results. Furthermore, the URL hasn't been updated in the parent | |
629 // page, because we've loaded the chrome-settings frame instead of the whole | |
630 // settings page, so the cross-origin call to set the URL fails. | |
631 this.verifyOpenPages_(['settings', 'search'], 'search#query'); | |
632 var self = this; | |
633 this.verifyHistory_(['', 'search#query'], function() { | |
634 PageManager.showPageByName('settings', false); | |
635 self.verifyOpenPages_(['settings'], 'search#query'); | |
636 self.verifyHistory_(['', 'search#query'], testDone); | |
637 }); | |
638 }); | |
639 | |
640 TEST_F('OptionsWebUIExtendedTest', 'ShowPageWithHistory', function() { | |
641 PageManager.showPageByName('search', true, {hash: '#query'}); | |
642 var self = this; | |
643 this.verifyHistory_(['', 'search#query'], function() { | |
644 PageManager.showPageByName('settings', true); | |
645 self.verifyOpenPages_(['settings'], ''); | |
646 self.verifyHistory_(['', 'search#query', ''], | |
647 testDone); | |
648 }); | |
649 }); | |
650 | |
651 TEST_F('OptionsWebUIExtendedTest', 'ShowPageReplaceHistory', function() { | |
652 PageManager.showPageByName('search', true, {hash: '#query'}); | |
653 var self = this; | |
654 this.verifyHistory_(['', 'search#query'], function() { | |
655 PageManager.showPageByName('settings', true, {'replaceState': true}); | |
656 self.verifyOpenPages_(['settings'], ''); | |
657 self.verifyHistory_(['', ''], testDone); | |
658 }); | |
659 }); | |
660 | |
661 // This should be identical to ShowPageWithHisory. | |
662 TEST_F('OptionsWebUIExtendedTest', 'NavigateToPage', function() { | |
663 PageManager.showPageByName('search', true, {hash: '#query'}); | |
664 var self = this; | |
665 this.verifyHistory_(['', 'search#query'], function() { | |
666 PageManager.showPageByName('settings'); | |
667 self.verifyOpenPages_(['settings'], ''); | |
668 self.verifyHistory_(['', 'search#query', ''], testDone); | |
669 }); | |
670 }); | |
671 | |
672 // Settings overlays are much more straightforward than settings pages, opening | |
673 // normally with none of the latter's quirks in the expected history or URL. | |
674 TEST_F('OptionsWebUIExtendedTest', 'ShowOverlayNoHistory', function() { | |
675 // Open a layer-1 overlay, not updating history. | |
676 PageManager.showPageByName('languages', false); | |
677 this.verifyOpenPages_(['settings', 'languages'], ''); | |
678 | |
679 var self = this; | |
680 this.verifyHistory_([''], function() { | |
681 // Open a layer-2 overlay for which the layer-1 is a parent, not updating | |
682 // history. | |
683 PageManager.showPageByName('addLanguage', false); | |
684 self.verifyOpenPages_(['settings', 'languages', 'addLanguage'], | |
685 ''); | |
686 self.verifyHistory_([''], testDone); | |
687 }); | |
688 }); | |
689 | |
690 TEST_F('OptionsWebUIExtendedTest', 'ShowOverlayWithHistory', function() { | |
691 // Open a layer-1 overlay, updating history. | |
692 PageManager.showPageByName('languages', true); | |
693 this.verifyOpenPages_(['settings', 'languages']); | |
694 | |
695 var self = this; | |
696 this.verifyHistory_(['', 'languages'], function() { | |
697 // Open a layer-2 overlay, updating history. | |
698 PageManager.showPageByName('addLanguage', true); | |
699 self.verifyOpenPages_(['settings', 'languages', 'addLanguage']); | |
700 self.verifyHistory_(['', 'languages', 'addLanguage'], testDone); | |
701 }); | |
702 }); | |
703 | |
704 TEST_F('OptionsWebUIExtendedTest', 'ShowOverlayReplaceHistory', function() { | |
705 // Open a layer-1 overlay, updating history. | |
706 PageManager.showPageByName('languages', true); | |
707 var self = this; | |
708 this.verifyHistory_(['', 'languages'], function() { | |
709 // Open a layer-2 overlay, replacing history. | |
710 PageManager.showPageByName('addLanguage', true, {'replaceState': true}); | |
711 self.verifyOpenPages_(['settings', 'languages', 'addLanguage']); | |
712 self.verifyHistory_(['', 'addLanguage'], testDone); | |
713 }); | |
714 }); | |
715 | |
716 // Directly show an overlay further above this page, i.e. one for which the | |
717 // current page is an ancestor but not a parent. | |
718 TEST_F('OptionsWebUIExtendedTest', 'ShowOverlayFurtherAbove', function() { | |
719 // Open a layer-2 overlay directly. | |
720 PageManager.showPageByName('addLanguage', true); | |
721 this.verifyOpenPages_(['settings', 'languages', 'addLanguage']); | |
722 var self = this; | |
723 this.verifyHistory_(['', 'addLanguage'], testDone); | |
724 }); | |
725 | |
726 // Directly show a layer-2 overlay for which the layer-1 overlay is not a | |
727 // parent. | |
728 TEST_F('OptionsWebUIExtendedTest', 'ShowUnrelatedOverlay', function() { | |
729 // Open a layer-1 overlay. | |
730 PageManager.showPageByName('languages', true); | |
731 this.verifyOpenPages_(['settings', 'languages']); | |
732 | |
733 var self = this; | |
734 this.verifyHistory_(['', 'languages'], function() { | |
735 // Open an unrelated layer-2 overlay. | |
736 PageManager.showPageByName('cookies', true); | |
737 self.verifyOpenPages_(['settings', 'content', 'cookies']); | |
738 self.verifyHistory_(['', 'languages', 'cookies'], testDone); | |
739 }); | |
740 }); | |
741 | |
742 // Close an overlay. | |
743 TEST_F('OptionsWebUIExtendedTest', 'CloseOverlay', function() { | |
744 // Open a layer-1 overlay, then a layer-2 overlay on top of it. | |
745 PageManager.showPageByName('languages', true); | |
746 this.verifyOpenPages_(['settings', 'languages']); | |
747 PageManager.showPageByName('addLanguage', true); | |
748 this.verifyOpenPages_(['settings', 'languages', 'addLanguage']); | |
749 | |
750 var self = this; | |
751 this.verifyHistory_(['', 'languages', 'addLanguage'], function() { | |
752 // Close the layer-2 overlay. | |
753 PageManager.closeOverlay(); | |
754 self.verifyOpenPages_(['settings', 'languages']); | |
755 self.verifyHistory_( | |
756 ['', 'languages', 'addLanguage', 'languages'], | |
757 function() { | |
758 // Close the layer-1 overlay. | |
759 PageManager.closeOverlay(); | |
760 self.verifyOpenPages_(['settings'], ''); | |
761 self.verifyHistory_( | |
762 ['', 'languages', 'addLanguage', 'languages', ''], | |
763 function noop() {}); | |
764 waitUntilHidden([$('overlay-container-1'), $('overlay-container-2')]); | |
765 }); | |
766 }); | |
767 }); | |
768 | |
769 // Hashes are maintained separately for each page and are preserved when | |
770 // overlays close. | |
771 TEST_F('OptionsWebUIExtendedTest', 'CloseOverlayWithHashes', function() { | |
772 // Open an overlay on top of the search page. | |
773 PageManager.showPageByName('search', true, {hash: '#1'}); | |
774 this.verifyOpenPages_(['settings', 'search'], 'search#1'); | |
775 PageManager.showPageByName('languages', true, {hash: '#2'}); | |
776 this.verifyOpenPages_(['settings', 'search', 'languages'], | |
777 'languages#2'); | |
778 PageManager.showPageByName('addLanguage', true, {hash: '#3'}); | |
779 this.verifyOpenPages_(['settings', 'search', 'languages', 'addLanguage'], | |
780 'addLanguage#3'); | |
781 | |
782 this.verifyHistory_(['', 'search#1', 'languages#2', 'addLanguage#3'], | |
783 function() { | |
784 // Close the layer-2 overlay. | |
785 PageManager.closeOverlay(); | |
786 this.verifyOpenPages_(['settings', 'search', 'languages'], 'languages#2'); | |
787 this.verifyHistory_( | |
788 ['', 'search#1', 'languages#2', 'addLanguage#3', 'languages#2'], | |
789 function() { | |
790 // Close the layer-1 overlay. | |
791 PageManager.closeOverlay(); | |
792 this.verifyOpenPages_(['settings', 'search'], 'search#1'); | |
793 this.verifyHistory_( | |
794 ['', 'search#1', 'languages#2', 'addLanguage#3', 'languages#2', | |
795 'search#1'], | |
796 function noop() {}); | |
797 waitUntilHidden([$('overlay-container-1'), $('overlay-container-2')]); | |
798 }.bind(this)); | |
799 }.bind(this)); | |
800 }); | |
801 | |
802 // Test that closing an overlay that did not push history when opening does not | |
803 // again push history. | |
804 TEST_F('OptionsWebUIExtendedTest', 'CloseOverlayNoHistory', function() { | |
805 // Open the do not track confirmation prompt. | |
806 PageManager.showPageByName('doNotTrackConfirm', false); | |
807 | |
808 // Opening the prompt does not add to the history. | |
809 this.verifyHistory_([''], function() { | |
810 // Close the overlay. | |
811 PageManager.closeOverlay(); | |
812 // Still no history changes. | |
813 this.verifyHistory_([''], function noop() {}); | |
814 waitUntilHidden([$('overlay-container-1')]); | |
815 }.bind(this)); | |
816 }); | |
817 | |
818 // Make sure an overlay isn't closed (even temporarily) when another overlay is | |
819 // opened on top. | |
820 TEST_F('OptionsWebUIExtendedTest', 'OverlayAboveNoReset', function() { | |
821 // Open a layer-1 overlay. | |
822 PageManager.showPageByName('languages', true); | |
823 this.verifyOpenPages_(['settings', 'languages']); | |
824 | |
825 // Open a layer-2 overlay on top. This should not close 'languages'. | |
826 this.prohibitChangesToOverlay_(options.LanguageOptions.getInstance()); | |
827 PageManager.showPageByName('addLanguage', true); | |
828 this.verifyOpenPages_(['settings', 'languages', 'addLanguage']); | |
829 testDone(); | |
830 }); | |
831 | |
832 TEST_F('OptionsWebUIExtendedTest', 'OverlayTabNavigation', function() { | |
833 // Open a layer-1 overlay, then a layer-2 overlay on top of it. | |
834 PageManager.showPageByName('languages', true); | |
835 PageManager.showPageByName('addLanguage', true); | |
836 var self = this; | |
837 | |
838 // Go back twice, then forward twice. | |
839 self.verifyOpenPages_(['settings', 'languages', 'addLanguage']); | |
840 self.verifyHistory_(['', 'languages', 'addLanguage'], function() { | |
841 window.history.back(); | |
842 waitForPopstate(function() { | |
843 self.verifyOpenPages_(['settings', 'languages']); | |
844 self.verifyHistory_(['', 'languages'], function() { | |
845 window.history.back(); | |
846 waitForPopstate(function() { | |
847 self.verifyOpenPages_(['settings'], ''); | |
848 self.verifyHistory_([''], function() { | |
849 window.history.forward(); | |
850 waitForPopstate(function() { | |
851 self.verifyOpenPages_(['settings', 'languages']); | |
852 self.verifyHistory_(['', 'languages'], function() { | |
853 window.history.forward(); | |
854 waitForPopstate(function() { | |
855 self.verifyOpenPages_( | |
856 ['settings', 'languages', 'addLanguage']); | |
857 self.verifyHistory_( | |
858 ['', 'languages', 'addLanguage'], testDone); | |
859 }); | |
860 }); | |
861 }); | |
862 }); | |
863 }); | |
864 }); | |
865 }); | |
866 }); | |
867 }); | |
868 | |
869 // Going "back" to an overlay that's a child of the current overlay shouldn't | |
870 // close the current one. | |
871 TEST_F('OptionsWebUIExtendedTest', 'OverlayBackToChild', function() { | |
872 // Open a layer-1 overlay, then a layer-2 overlay on top of it. | |
873 PageManager.showPageByName('languages', true); | |
874 PageManager.showPageByName('addLanguage', true); | |
875 var self = this; | |
876 | |
877 self.verifyOpenPages_(['settings', 'languages', 'addLanguage']); | |
878 self.verifyHistory_(['', 'languages', 'addLanguage'], function() { | |
879 // Close the top overlay, then go back to it. | |
880 PageManager.closeOverlay(); | |
881 self.verifyOpenPages_(['settings', 'languages']); | |
882 self.verifyHistory_( | |
883 ['', 'languages', 'addLanguage', 'languages'], | |
884 function() { | |
885 // Going back to the 'addLanguage' page should not close 'languages'. | |
886 self.prohibitChangesToOverlay_(options.LanguageOptions.getInstance()); | |
887 window.history.back(); | |
888 waitForPopstate(function() { | |
889 self.verifyOpenPages_(['settings', 'languages', 'addLanguage']); | |
890 self.verifyHistory_(['', 'languages', 'addLanguage'], | |
891 testDone); | |
892 }); | |
893 }); | |
894 }); | |
895 }); | |
896 | |
897 // Going back to an unrelated overlay should close the overlay and its parent. | |
898 TEST_F('OptionsWebUIExtendedTest', 'OverlayBackToUnrelated', function() { | |
899 // Open a layer-1 overlay, then an unrelated layer-2 overlay. | |
900 PageManager.showPageByName('languages', true); | |
901 PageManager.showPageByName('cookies', true); | |
902 var self = this; | |
903 self.verifyOpenPages_(['settings', 'content', 'cookies']); | |
904 self.verifyHistory_(['', 'languages', 'cookies'], function() { | |
905 window.history.back(); | |
906 waitForPopstate(function() { | |
907 self.verifyOpenPages_(['settings', 'languages']); | |
908 testDone(); | |
909 }); | |
910 }); | |
911 }); | |
912 | |
913 // Verify history changes properly while the page is loading. | |
914 TEST_F('OptionsWebUIExtendedTest', 'HistoryUpdatedAfterLoading', function() { | |
915 var loc = location.href; | |
916 | |
917 document.documentElement.classList.add('loading'); | |
918 assertTrue(PageManager.isLoading()); | |
919 PageManager.showPageByName('searchEngines'); | |
920 expectNotEquals(loc, location.href); | |
921 | |
922 document.documentElement.classList.remove('loading'); | |
923 assertFalse(PageManager.isLoading()); | |
924 PageManager.showDefaultPage(); | |
925 expectEquals(loc, location.href); | |
926 | |
927 testDone(); | |
928 }); | |
929 | |
930 // A tip should be shown or hidden depending on whether this profile manages any | |
931 // supervised users. | |
932 TEST_F('OptionsWebUIExtendedTest', 'SupervisingUsers', function() { | |
933 // We start managing no supervised users. | |
934 assertTrue($('profiles-supervised-dashboard-tip').hidden); | |
935 | |
936 // Remove all supervised users, then add some, watching for the pref change | |
937 // notifications and UI updates in each case. Any non-empty pref dictionary | |
938 // is interpreted as having supervised users. | |
939 chrome.send('optionsTestSetPref', [SUPERVISED_USERS_PREF, {key: 'value'}]); | |
940 waitForResponse(BrowserOptions, 'updateManagesSupervisedUsers', function() { | |
941 assertFalse($('profiles-supervised-dashboard-tip').hidden); | |
942 chrome.send('optionsTestSetPref', [SUPERVISED_USERS_PREF, {}]); | |
943 waitForResponse(BrowserOptions, 'updateManagesSupervisedUsers', function() { | |
944 assertTrue($('profiles-supervised-dashboard-tip').hidden); | |
945 testDone(); | |
946 }); | |
947 }); | |
948 }); | |
949 | |
950 /** | |
951 * TestFixture that loads the options page at a bogus URL. | |
952 * @extends {OptionsWebUIExtendedTest} | |
953 * @constructor | |
954 */ | |
955 function OptionsWebUIRedirectTest() { | |
956 OptionsWebUIExtendedTest.call(this); | |
957 } | |
958 | |
959 OptionsWebUIRedirectTest.prototype = { | |
960 __proto__: OptionsWebUIExtendedTest.prototype, | |
961 | |
962 /** @override */ | |
963 browsePreload: 'chrome://settings-frame/nonexistantPage', | |
964 }; | |
965 | |
966 TEST_F('OptionsWebUIRedirectTest', 'TestURL', function() { | |
967 assertEquals('chrome://settings-frame/', document.location.href); | |
968 this.verifyHistory_([''], testDone); | |
969 }); | |
OLD | NEW |