1// Copyright 2014 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/**
6 * @suppress {checkTypes}
7 *
8 * @fileoverview
9 * Browser test for the scenario below:
10 * 1. Resize the client window to various sizes and verify the existence of
11 *    horizontal and/or vertical scroll-bars.
12 * 2. TODO(jamiewalch): Connect to a host and toggle various combinations of
13 *    scale and resize; repeat test 1.
14 * 3. TODO(jamiewalch): Disconnect; repeat test 1.
15 */
16
17'use strict';
18
19/** @constructor */
20browserTest.Scrollbars = function() {
21  this.scroller_ = document.getElementById('scroller');
22  this.SCROLLBAR_WIDTH_ = 16;
23  this.BORDER_WIDTH_ = 1;
24
25  // The top border is already accounted for by getBoundingClientRect, but
26  // the bottom border is not.
27  var marker = document.getElementById('bottom-marker');
28  this.CONTENT_HEIGHT_ =
29      marker.getBoundingClientRect().top + this.BORDER_WIDTH_;
30
31  // The width of the content is computed from the width of a <section> (690px)
32  // plus the margin of the "inset" class (20px). There's no easy way to get
33  // that without hard-coding it. In fact, this is a bit simplistic because
34  // the horizontal space required by the header depends on the length of the
35  // product name.
36  this.CONTENT_WIDTH_ = 690 + 20 + 2 * this.BORDER_WIDTH_;
37
38};
39
40
41browserTest.Scrollbars.prototype.run = function(data) {
42  if (!base.isAppsV2()) {
43    browserTest.fail(
44        'Scroll-bar testing requires resizing the app window, which can ' +
45        'only be done programmatically in apps v2.');
46  }
47
48  // Verify that scrollbars are added/removed correctly on the home screen.
49  this.verifyHomeScreenScrollbars_()
50      .then(browserTest.pass, browserTest.fail);
51};
52
53
54/**
55 * Verify the test cases for the home-screen.
56 */
57browserTest.Scrollbars.prototype.verifyHomeScreenScrollbars_ = function() {
58  // Note that, due to crbug.com/240772, if the window already has
59  // scroll-bars, they will not be removed if the window size is
60  // increased by less than the scroll-bar width. We work around that
61  // when connected to a host because we know how big the content is
62  // (in fact, testing this work-around is the main motivation for
63  // writing this test), but it's not worth it for the home screen,
64  // so make the window large not to require scrollbars before each test.
65  var tooWide = this.CONTENT_WIDTH_ + 100;
66  var tooTall = this.CONTENT_HEIGHT_ + 100;
67  var removeScrollbars = this.resize_.bind(this, tooWide, tooTall);
68
69  // Verify there are no scroll-bars if the window is as big as it needs
70  // to be.
71  return removeScrollbars()
72  .then(this.resizeAndVerifyScroll_(
73        this.CONTENT_WIDTH_,
74        this.CONTENT_HEIGHT_,
75        false, false))
76
77  // Verify there is a vertical scroll-bar if the window is shorter than it
78  // needs to be.
79  .then(removeScrollbars)
80  .then(this.resizeAndVerifyScroll_.bind(
81        this,
82        this.CONTENT_WIDTH_ + this.SCROLLBAR_WIDTH_,
83        this.CONTENT_HEIGHT_ - 1,
84        false, true))
85
86  // Verify there is a horizontal scroll-bar if the window is narrow than it
87  // needs to be.
88  .then(removeScrollbars)
89  .then(this.resizeAndVerifyScroll_.bind(
90        this,
91        this.CONTENT_WIDTH_ - 1,
92        this.CONTENT_HEIGHT_ + this.SCROLLBAR_WIDTH_,
93        true, false))
94
95  // Verify there are both horizontal and vertical scroll-bars, even if one
96  // is only needed as a result of the space occupied by the other.
97  .then(removeScrollbars)
98  .then(this.resizeAndVerifyScroll_.bind(
99        this,
100        this.CONTENT_WIDTH_,
101        this.CONTENT_HEIGHT_ - 1,
102        true, true))
103  .then(removeScrollbars)
104  .then(this.resizeAndVerifyScroll_.bind(
105        this,
106        this.CONTENT_WIDTH_ - 1,
107        this.CONTENT_HEIGHT_,
108        true, true))
109
110  // Verify there are both horizontal and vertical scroll-bars, if both are
111  // required independently.
112  .then(removeScrollbars)
113  .then(this.resizeAndVerifyScroll_.bind(
114        this,
115        this.CONTENT_WIDTH_ - 1,
116        this.CONTENT_HEIGHT_ - 1,
117        true, true));
118};
119
120
121/**
122 * Returns whether or not horizontal and vertical scroll-bars are expected
123 * and visible. To do this, it performs a hit-test close to the right and
124 * bottom edges of the scroller <div>; since the content of that <div> fills
125 * it completely, the hit-test will return the content unless there is a
126 * scroll-bar visible on the corresponding edge, in which case it will return
127 * the scroller <div> itself.
128 *
129 * @private
130 */
131browserTest.Scrollbars.prototype.getScrollbarState_ = function() {
132  var rect = this.scroller_.getBoundingClientRect();
133  var rightElement = document.elementFromPoint(
134      rect.right - 1, (rect.top + rect.bottom) / 2);
135  var bottomElement = document.elementFromPoint(
136      (rect.left + rect.right) / 2, rect.bottom - 1);
137  return {
138    horizontal: bottomElement === this.scroller_,
139    vertical: rightElement === this.scroller_
140  };
141};
142
143
144/**
145 * Returns a promise that resolves if the scroll-bar state is as expected, or
146 * rejects otherwise.
147 *
148 * @private
149 */
150browserTest.Scrollbars.prototype.verifyScrollbarState_ =
151    function(horizontalExpected, verticalExpected) {
152  var scrollbarState = this.getScrollbarState_();
153  if (scrollbarState.horizontal && !horizontalExpected) {
154    return Promise.reject(new Error(
155        'Horizontal scrollbar present but not expected.'));
156  } else if (!scrollbarState.horizontal && horizontalExpected) {
157    return Promise.reject(new Error(
158        'Horizontal scrollbar expected but not present.'));
159  } else if (scrollbarState.vertical && !verticalExpected) {
160    return Promise.reject(new Error(
161        'Vertical scrollbar present but not expected.'));
162  } else if (!scrollbarState.vertical && verticalExpected) {
163    return Promise.reject(new Error(
164        'Vertical scrollbar expected but not present.'));
165  }
166  return Promise.resolve();
167};
168
169
170/**
171 * @private
172 * @return {Promise} A promise that will be fulfilled when the window has
173 *     been resized and it's safe to test scroll-bar visibility.
174 */
175browserTest.Scrollbars.prototype.resize_ = function(width, height) {
176  var win = chrome.app.window.current();
177  win.resizeTo(width, height);
178  // Chrome takes a while to update the scroll-bars, so don't resolve
179  // immediately. Waiting for the onBoundsChanged event would be cleaner,
180  // but isn't reliable.
181  return base.Promise.sleep(500);
182};
183
184
185/**
186 * @private
187 * @return {Promise} A promise that will be fulfilled when the window has
188 *     been resized and it's safe to test scroll-bar visibility.
189 */
190browserTest.Scrollbars.prototype.resizeAndVerifyScroll_ =
191    function(width, height, horizontalExpected, verticalExpected) {
192  return this.resize_(width, height).then(
193      this.verifyScrollbarState_.bind(
194          this, horizontalExpected, verticalExpected));
195};
196