Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(65)

Side by Side Diff: chrome/test/data/webui/settings/settings_transitions_browsertest.js

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

Powered by Google App Engine
This is Rietveld 408576698