Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 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 /** @fileoverview Suite of tests for Settings navigational transitions. */ | |
| 6 | |
| 7 GEN_INCLUDE(['settings_page_browsertest.js']); | |
| 8 | |
| 9 /** | |
| 10 * @constructor | |
| 11 * @extends {SettingsPageBrowserTest} | |
| 12 */ | |
| 13 function SettingsTransitionsBrowserTest() {} | |
| 14 | |
| 15 /** | |
| 16 * @typedef {{ | |
| 17 * duration: Number, | |
| 18 * hasStarted: function(): bool, | |
| 19 * testIntermediateState: function(), | |
| 20 * testAfter: function() | |
|
tommycli
2016/08/01 22:56:10
Was this supposed to be testEndState?
michaelpg
2016/08/04 00:56:48
Done.
| |
| 21 * }} | |
| 22 */ | |
| 23 var TransitionTest; | |
| 24 | |
| 25 /** @const {number} Default animation timing for neon-animated-pages. */ | |
| 26 var kNeonAnimationsDuration = 500; | |
| 27 | |
| 28 SettingsTransitionsBrowserTest.prototype = { | |
| 29 __proto__: SettingsPageBrowserTest.prototype, | |
| 30 | |
| 31 /** @override */ | |
| 32 browsePreload: 'chrome://md-settings/', | |
| 33 | |
| 34 /** | |
| 35 * Tests a transition by waiting for it to start, running tests during its | |
| 36 * execution, and running tests after its duration is reached, all using | |
| 37 * callbacks -- no access to underlying animations. | |
| 38 * @param {!TransitionTest} transitionTest | |
| 39 * @return {!Promise} Resolved once the tests have finished. | |
| 40 */ | |
| 41 testTransition: function(transitionTest) { | |
| 42 // We approximate the time when the transition started. | |
| 43 var startedNoEarlierThan = window.performance.now(); | |
| 44 var startedNoLaterThan; | |
| 45 return new Promise(function(resolve, reject) { | |
| 46 // Figure out when the transition has started with a rAF loop. Even if we | |
| 47 // required access to underlying animation(s), there's no good way to | |
| 48 // force a transition to start or know when it starts without spinning. | |
| 49 (function checkIfStarted() { | |
| 50 if (transitionTest.hasStarted()) { | |
| 51 startedNoLaterThan = window.performance.now(); | |
| 52 resolve(); | |
| 53 } else { | |
| 54 // Update the latest known time the animation wasn't running, then | |
| 55 // check again. | |
| 56 startedNoEarlierThan = window.performance.now(); | |
| 57 requestAnimationFrame(checkIfStarted); | |
| 58 } | |
| 59 })(); | |
|
tommycli
2016/08/01 22:56:10
Wow that block above is exotic.
michaelpg
2016/08/04 00:56:48
Well the recursive function to check for a conditi
| |
| 60 }).then(function() { | |
| 61 // Hopefully, the transition hasn't finished yet. | |
| 62 var minDuration = window.performance.now() - startedNoLaterThan; | |
| 63 var maxDuration = window.performance.now() - startedNoEarlierThan; | |
| 64 assertLE(minDuration, transitionTest.duration); | |
| 65 if (maxDuration < transitionTest.duration) { | |
| 66 transitionTest.testIntermediateState(); | |
| 67 } else { | |
| 68 // We ran quite late. Don't run testIntermediateState(). | |
| 69 console.warn('testIntermediateState() was not called because the ' + | |
| 70 'transition may have already ended.'); | |
| 71 } | |
| 72 // Wait until the transition should have completed. | |
| 73 var maxTimeToEnd = transitionTest.duration - minDuration; | |
| 74 var BUFFER = 10; | |
|
tommycli
2016/08/01 22:56:10
Is this leftover?
michaelpg
2016/08/04 00:56:48
I'm now seeing occasional flakes with any kind of
| |
| 75 return new Promise(function(resolve, reject) { | |
| 76 setTimeout(function() { | |
| 77 transitionTest.testEndState(); | |
| 78 resolve(); | |
| 79 }, maxTimeToEnd + BUFFER); | |
| 80 }); | |
| 81 }); | |
| 82 }, | |
| 83 | |
| 84 /** | |
| 85 * Checks whether the subpage header (back button and title) is visible. | |
| 86 * @param {!SettingsSubpageElement} subpage | |
| 87 * @return {boolean} | |
| 88 */ | |
| 89 checkSubpageHeaderVisible: function(subpage) { | |
| 90 var backButton = subpage.$$('[icon="settings:arrow-back"]'); | |
| 91 assertNotEquals(null, backButton); | |
| 92 assertGT(backButton.clientHeight, 0); | |
| 93 var title = subpage.$$('h2'); | |
| 94 assertNotEquals(null, title); | |
| 95 assertGT(title.clientHeight, 0); | |
| 96 assertGT(title.innerText.trim().length, 0); | |
| 97 }, | |
| 98 | |
| 99 /** | |
| 100 * @param {!HTMLElement} newNode The node to fade to. | |
| 101 * @param {!HTMLElement} oldNode The node to fade from. | |
| 102 * @return {!TransitionTest} | |
| 103 */ | |
| 104 getFadeTransitionTest: function(newNode, oldNode) { | |
| 105 var newNodeStyle = getComputedStyle(newNode); | |
| 106 var oldNodeStyle = getComputedStyle(oldNode); | |
| 107 return { | |
| 108 duration: kNeonAnimationsDuration, | |
| 109 | |
| 110 hasStarted: function() { | |
| 111 return newNodeStyle.opacity != '0' && | |
| 112 oldNodeStyle.opacity != '1'; | |
| 113 }, | |
| 114 | |
| 115 testIntermediateState: function() { | |
| 116 assertGT(oldNode.clientHeight, 0); | |
| 117 assertGT(newNode.clientHeight, 0); | |
| 118 | |
| 119 var oldNodeOpacity = parseFloat(oldNodeStyle.opacity); | |
| 120 assertLT(oldNodeOpacity, 1); | |
| 121 assertGT(oldNodeOpacity, 0); | |
| 122 var newNodeOpacity = parseFloat(newNodeStyle.opacity); | |
| 123 assertLT(newNodeOpacity, 1); | |
| 124 assertGT(newNodeOpacity, 0); | |
| 125 }, | |
| 126 | |
| 127 testEndState: function() { | |
| 128 assertEquals('none', oldNodeStyle.display); | |
| 129 assertEquals('1', newNodeStyle.opacity); | |
| 130 }, | |
| 131 }; | |
| 132 }, | |
| 133 | |
| 134 /** | |
| 135 * Tests the transition to open a subpage from the main animatable. | |
| 136 * @param {!SettingsSectionElement} section | |
| 137 * @param {string} subpageId | |
| 138 * @param {!settings.Route} subpageRoute | |
| 139 * @return {!Promise<!SettingsSubpageElement>} Resolved with the opened | |
| 140 * subpage once the transition completes. | |
| 141 */ | |
| 142 testOpenSubpage: function(section, subpageId, subpageRoute) { | |
| 143 var page = section.domHost; | |
| 144 | |
| 145 var sectionStyle = getComputedStyle(section); | |
| 146 var cardStyle = getComputedStyle(section.$.card); | |
| 147 | |
| 148 // Some sanity checks of starting conditions. | |
| 149 var sectionHeightStart = section.clientHeight; | |
| 150 var cardHeightStart = section.$.card.clientHeight; | |
| 151 assertGT(cardHeightStart, 10); | |
| 152 assertGT(sectionHeightStart, cardHeightStart); | |
| 153 | |
| 154 var mainAnimatable = this.getSectionMainAnimatable(section); | |
| 155 assertNotEquals(null, mainAnimatable); | |
| 156 assertGT(mainAnimatable.clientHeight, 0); | |
| 157 var mainAnimatableStyle = getComputedStyle(mainAnimatable); | |
| 158 | |
| 159 this.verifySubpagesHidden(section); | |
| 160 | |
| 161 var otherSections = this.getSections(section.domHost); | |
| 162 otherSections.splice(otherSections.indexOf(section), 1); | |
| 163 assertGT(otherSections.length, 0); | |
| 164 | |
| 165 for (var otherSection of otherSections) | |
| 166 assertGT(otherSection.clientHeight, 0); | |
| 167 | |
| 168 // Play the transition. | |
| 169 settings.navigateTo(subpageRoute); | |
| 170 assertEquals(subpageRoute, page.currentRoute); | |
| 171 | |
| 172 // The section and the card should not have changed height yet. | |
| 173 assertEquals(section.clientHeight, sectionHeightStart); | |
| 174 assertEquals(section.$.card.clientHeight, cardHeightStart); | |
| 175 | |
| 176 // The main animatable is still fully visible. | |
| 177 assertEquals('1', mainAnimatableStyle.opacity); | |
| 178 | |
| 179 // The subpage should be stamped but not faded in yet. | |
| 180 var subpages = this.getSectionSubpages(section); | |
| 181 assertTrue(subpages.has(subpageId)); | |
| 182 var subpage = subpages.get(subpageId); | |
| 183 this.checkSubpageHeaderVisible(subpage); | |
| 184 var subpageStyle = getComputedStyle(subpage); | |
| 185 assertEquals('0', subpageStyle.opacity); | |
| 186 | |
| 187 var container = page.offsetParent; | |
| 188 | |
| 189 // Test stages of the section/card expand transition. | |
| 190 var expandTransitionTest = { | |
| 191 duration: EXPAND_DURATION, | |
| 192 | |
| 193 hasStarted: function() { | |
| 194 // Transition starts by expanding card. | |
| 195 return section.$.card.clientHeight > cardHeightStart; | |
| 196 }, | |
| 197 | |
| 198 testIntermediateState: function() { | |
| 199 // The card must be fixed in the viewport while expanding. | |
| 200 assertEquals('fixed', cardStyle.position); | |
| 201 | |
| 202 // The card is offset to be below the toolbar. | |
| 203 assertGT(section.$.card.offsetTop, | |
| 204 container.getBoundingClientRect().top); | |
| 205 | |
| 206 // All sections retain their dimensions while the expanding section's | |
| 207 // card expands. | |
| 208 assertEquals(sectionHeightStart, section.clientHeight); | |
| 209 for (var otherSection of otherSections) | |
| 210 assertGT(otherSection.clientHeight, 0); | |
| 211 }, | |
| 212 | |
| 213 testEndState: function() { | |
| 214 // Sanity check. | |
| 215 assertGT(section.clientHeight, sectionHeightStart); | |
| 216 | |
| 217 // Transition ends with the expanded section spanning the | |
| 218 // container height at a minimum. | |
| 219 assertEquals(sectionStyle.minHeight, '100%'); | |
| 220 assertGE(section.clientHeight, container.clientHeight); | |
| 221 | |
| 222 // All other sections are hidden. | |
| 223 for (var otherSection of otherSections) | |
| 224 assertEquals(0, otherSection.clientHeight); | |
| 225 }, | |
| 226 }; | |
| 227 | |
| 228 // Test stages of the animatable/subpage fade transition. | |
| 229 var fadeTransitionTest = | |
| 230 this.getFadeTransitionTest(subpage, mainAnimatable); | |
| 231 | |
| 232 return Promise.all([ | |
| 233 this.testTransition(expandTransitionTest), | |
| 234 this.testTransition(fadeTransitionTest), | |
| 235 ]).then(function() { | |
| 236 return subpage; | |
| 237 }); | |
| 238 }, | |
| 239 | |
| 240 /** | |
| 241 * Tests the transition to close a subpage and return to the main animatable. | |
| 242 * @return {!Promise} Resolved once the transition | |
| 243 * completes. | |
| 244 */ | |
| 245 testCloseSubpage: function(section, subpage) { | |
| 246 var page = section.domHost; | |
| 247 var container = page.offsetParent; | |
| 248 | |
| 249 var sectionStyle = getComputedStyle(section); | |
| 250 var cardStyle = getComputedStyle(section.$.card); | |
| 251 var subpageStyle = getComputedStyle(subpage); | |
| 252 | |
| 253 var sectionHeightStart = section.clientHeight; | |
| 254 var cardHeightStart = section.$.card.clientHeight; | |
| 255 | |
| 256 var mainAnimatable = this.getSectionMainAnimatable(section); | |
| 257 assertTrue(!!mainAnimatable); | |
| 258 assertEquals(mainAnimatable.clientHeight, 0); | |
| 259 var mainAnimatableStyle = getComputedStyle(mainAnimatable); | |
| 260 | |
| 261 var otherSections = this.getSections(section.domHost); | |
| 262 otherSections.splice(otherSections.indexOf(section), 1); | |
| 263 assertGT(otherSections.length, 0); | |
| 264 | |
| 265 for (var otherSection of otherSections) | |
| 266 assertEquals(otherSection.clientHeight, 0); | |
| 267 | |
| 268 // Find the 2nd-highest route (the section itself). | |
| 269 var route = page.currentRoute; | |
| 270 while (route.parent.parent) | |
| 271 route = route.parent; | |
| 272 | |
| 273 // Play the transition. | |
| 274 settings.navigateTo(route); | |
| 275 assertEquals(route, page.currentRoute); | |
| 276 | |
| 277 // Depending on the browser height, the card may have been scrollable. | |
| 278 if (container.clientHeight == cardHeightStart) { | |
| 279 // The card was not scrollable, so the height should not have changed. | |
| 280 assertEquals(cardHeightStart, section.$.card.clientHeight); | |
| 281 } else { | |
| 282 // The card was scrollable (larger than the container), so it should have | |
| 283 // been resized to the container height. | |
| 284 assertEquals(section.$.card.clientHeight, container.clientHeight); | |
| 285 } | |
| 286 | |
| 287 // The subpage is still fully visible. | |
| 288 assertEquals('1', subpageStyle.opacity); | |
| 289 this.checkSubpageHeaderVisible(subpage); | |
| 290 | |
| 291 // Test stages of the section/card collapse transition. | |
| 292 var collapseTransitionTest = { | |
| 293 duration: EXPAND_DURATION, | |
| 294 | |
| 295 hasStarted: function() { | |
| 296 // Transition starts by collapsing card. | |
| 297 return section.$.card.clientHeight < cardHeightStart; | |
| 298 }, | |
| 299 | |
| 300 testIntermediateState: function() { | |
| 301 // The card must be fixed in the viewport while collapsing. | |
| 302 assertEquals('fixed', cardStyle.position); | |
| 303 | |
| 304 // All sections are now visible. | |
| 305 for (var otherSection of otherSections) | |
| 306 assertGT(otherSection.clientHeight, 0); | |
| 307 | |
| 308 // TODO(michaelpg): The card should be offset to be below the toolbar, | |
| 309 // but scroll position breaks on closing the subpage: crbug.com/537359. | |
| 310 }, | |
| 311 | |
| 312 testEndState: function() { | |
| 313 // Sanity check. | |
| 314 assertLT(section.clientHeight, sectionHeightStart); | |
| 315 assertEquals('0px', sectionStyle.minHeight); | |
| 316 }, | |
| 317 }; | |
| 318 | |
| 319 // Test stages of the animatable/subpage fade transition. | |
| 320 var fadeTransitionTest = | |
| 321 this.getFadeTransitionTest(mainAnimatable, subpage); | |
| 322 | |
| 323 return Promise.all([ | |
| 324 this.testTransition(collapseTransitionTest), | |
| 325 this.testTransition(fadeTransitionTest), | |
| 326 ]).then(function() { | |
| 327 return subpage; | |
| 328 }); | |
| 329 }, | |
| 330 }; | |
| 331 | |
| 332 TEST_F('SettingsTransitionsBrowserTest', 'Subpages', function() { | |
| 333 var self = this; | |
| 334 | |
| 335 suite('Subpages', function() { | |
| 336 suiteSetup(function() { | |
| 337 var settingsUI = document.querySelector('* /deep/ settings-ui'); | |
| 338 MockInteractions.tap(settingsUI.$.close); | |
| 339 }); | |
| 340 | |
| 341 test('open and close a sub-page', function() { | |
| 342 var page = self.getPage('basic'); | |
| 343 assertEquals(settings.Route.BASIC, page.currentRoute); | |
| 344 | |
| 345 var section = self.getSection(page, 'search'); | |
| 346 | |
| 347 // Make the section's main page 100px shorter than the page container to | |
| 348 // test a normal expand animation. | |
| 349 var container = page.offsetParent; | |
| 350 assertGT(container.clientHeight, 200, | |
| 351 'Browser window too short to test WebUI!'); | |
| 352 var mainAnimatable = self.getSectionMainAnimatable(section); | |
| 353 mainAnimatable.style.height = container.clientHeight - 100 + 'px'; | |
| 354 section.scrollIntoView(); | |
| 355 | |
| 356 return self.testOpenSubpage( | |
| 357 section, 'search-engines', settings.Route.SEARCH_ENGINES | |
| 358 ).then(function(subpage) { | |
| 359 // Change the subpage's height to test scrolling. | |
| 360 subpage.style.height = '100px'; | |
| 361 // Everything is taller than the subpage, since the section's min-height | |
| 362 // is 100% of the container. | |
| 363 assertGT(section.$.card.clientHeight, subpage.clientHeight); | |
| 364 assertEquals(section.$.card.clientHeight, section.clientHeight); | |
| 365 // The container shouldn't scroll vertically. Check the offsetHeight in | |
| 366 // case the container has a horizontal scrollbar (small window). | |
| 367 // TODO(michaelpg): Why is this necessary? | |
| 368 assertEquals(container.offsetHeight, section.clientHeight); | |
| 369 assertEquals(container.offsetHeight, container.scrollHeight); | |
| 370 | |
| 371 // Make sure the section and its card stretch with the content. | |
| 372 subpage.style.height = '10000px'; | |
| 373 assertEquals(subpage.clientHeight, section.clientHeight); | |
| 374 assertEquals(subpage.clientHeight, section.$.card.clientHeight); | |
| 375 | |
| 376 // The card is now larger than the container, so the container should | |
| 377 // scroll. | |
| 378 assertGT(section.$.card.clientHeight, container.clientHeight); | |
| 379 assertGT(container.scrollHeight, container.clientHeight); | |
| 380 assertEquals(10000, container.scrollHeight); | |
| 381 | |
| 382 subpage.style.height = ''; | |
| 383 return subpage; | |
| 384 }).then(function(subpage) { | |
| 385 // Close the subpage. | |
| 386 return self.testCloseSubpage(section, subpage); | |
| 387 }); | |
| 388 }); | |
| 389 }); | |
| 390 | |
| 391 mocha.run(); | |
| 392 }); | |
| OLD | NEW |