sliderbase_test.js 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035
  1. // Copyright 2008 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. goog.provide('goog.ui.SliderBaseTest');
  15. goog.setTestOnly('goog.ui.SliderBaseTest');
  16. goog.require('goog.a11y.aria');
  17. goog.require('goog.a11y.aria.State');
  18. goog.require('goog.dom');
  19. goog.require('goog.dom.TagName');
  20. goog.require('goog.dom.classlist');
  21. goog.require('goog.events');
  22. goog.require('goog.events.EventType');
  23. goog.require('goog.events.KeyCodes');
  24. goog.require('goog.fx.Animation');
  25. goog.require('goog.math.Coordinate');
  26. goog.require('goog.style');
  27. goog.require('goog.style.bidi');
  28. goog.require('goog.testing.MockClock');
  29. goog.require('goog.testing.MockControl');
  30. goog.require('goog.testing.events');
  31. goog.require('goog.testing.jsunit');
  32. goog.require('goog.testing.mockmatchers');
  33. goog.require('goog.testing.recordFunction');
  34. goog.require('goog.ui.Component');
  35. goog.require('goog.ui.SliderBase');
  36. goog.require('goog.userAgent');
  37. var oneThumbSlider;
  38. var oneThumbSliderRtl;
  39. var oneChangeEventCount;
  40. var twoThumbSlider;
  41. var twoThumbSliderRtl;
  42. var twoChangeEventCount;
  43. var mockClock;
  44. var mockAnimation;
  45. /**
  46. * A basic class to implement the abstract goog.ui.SliderBase for testing.
  47. * @constructor
  48. * @extends {goog.ui.SliderBase}
  49. */
  50. function OneThumbSlider() {
  51. goog.ui.SliderBase.call(this, undefined /* domHelper */, function(value) {
  52. return value > 5 ? 'A big value.' : 'A small value.';
  53. });
  54. }
  55. goog.inherits(OneThumbSlider, goog.ui.SliderBase);
  56. /** @override */
  57. OneThumbSlider.prototype.createThumbs = function() {
  58. this.valueThumb = this.extentThumb = goog.dom.getElement('thumb');
  59. };
  60. /** @override */
  61. OneThumbSlider.prototype.getCssClass = function(orientation) {
  62. return goog.getCssName('test-slider', orientation);
  63. };
  64. /**
  65. * A basic class to implement the abstract goog.ui.SliderBase for testing.
  66. * @constructor
  67. * @extends {goog.ui.SliderBase}
  68. */
  69. function TwoThumbSlider() {
  70. goog.ui.SliderBase.call(this);
  71. }
  72. goog.inherits(TwoThumbSlider, goog.ui.SliderBase);
  73. /** @override */
  74. TwoThumbSlider.prototype.createThumbs = function() {
  75. this.valueThumb = goog.dom.getElement('valueThumb');
  76. this.extentThumb = goog.dom.getElement('extentThumb');
  77. this.rangeHighlight = goog.dom.getElement('rangeHighlight');
  78. };
  79. /** @override */
  80. TwoThumbSlider.prototype.getCssClass = function(orientation) {
  81. return goog.getCssName('test-slider', orientation);
  82. };
  83. /**
  84. * Basic class that implements the AnimationFactory interface for testing.
  85. * @param {!goog.fx.Animation|!Array<!goog.fx.Animation>} testAnimations The
  86. * test animations to use.
  87. * @constructor
  88. * @implements {goog.ui.SliderBase.AnimationFactory}
  89. */
  90. function AnimationFactory(testAnimations) {
  91. this.testAnimations = testAnimations;
  92. }
  93. /** @override */
  94. AnimationFactory.prototype.createAnimations = function() {
  95. return this.testAnimations;
  96. };
  97. function setUp() {
  98. var sandBox = goog.dom.getElement('sandbox');
  99. mockClock = new goog.testing.MockClock(true);
  100. var oneThumbElem = goog.dom.createDom(
  101. goog.dom.TagName.DIV, {'id': 'oneThumbSlider'},
  102. goog.dom.createDom(goog.dom.TagName.SPAN, {'id': 'thumb'}));
  103. sandBox.appendChild(oneThumbElem);
  104. oneThumbSlider = new OneThumbSlider();
  105. oneThumbSlider.decorate(oneThumbElem);
  106. oneChangeEventCount = 0;
  107. goog.events.listen(
  108. oneThumbSlider, goog.ui.Component.EventType.CHANGE,
  109. function() { oneChangeEventCount++; });
  110. var twoThumbElem = goog.dom.createDom(
  111. goog.dom.TagName.DIV, {'id': 'twoThumbSlider'},
  112. goog.dom.createDom(goog.dom.TagName.DIV, {'id': 'rangeHighlight'}),
  113. goog.dom.createDom(goog.dom.TagName.SPAN, {'id': 'valueThumb'}),
  114. goog.dom.createDom(goog.dom.TagName.SPAN, {'id': 'extentThumb'}));
  115. sandBox.appendChild(twoThumbElem);
  116. twoThumbSlider = new TwoThumbSlider();
  117. twoThumbSlider.decorate(twoThumbElem);
  118. twoChangeEventCount = 0;
  119. goog.events.listen(
  120. twoThumbSlider, goog.ui.Component.EventType.CHANGE,
  121. function() { twoChangeEventCount++; });
  122. var sandBoxRtl = goog.dom.createDom(
  123. goog.dom.TagName.DIV, {'dir': 'rtl', 'style': 'position:absolute;'});
  124. sandBox.appendChild(sandBoxRtl);
  125. var oneThumbElemRtl = goog.dom.createDom(
  126. goog.dom.TagName.DIV, {'id': 'oneThumbSliderRtl'},
  127. goog.dom.createDom(goog.dom.TagName.SPAN, {'id': 'thumbRtl'}));
  128. sandBoxRtl.appendChild(oneThumbElemRtl);
  129. oneThumbSliderRtl = new OneThumbSlider();
  130. oneThumbSliderRtl.enableFlipForRtl(true);
  131. oneThumbSliderRtl.decorate(oneThumbElemRtl);
  132. goog.events.listen(
  133. oneThumbSliderRtl, goog.ui.Component.EventType.CHANGE,
  134. function() { oneChangeEventCount++; });
  135. var twoThumbElemRtl = goog.dom.createDom(
  136. goog.dom.TagName.DIV, {'id': 'twoThumbSliderRtl'},
  137. goog.dom.createDom(goog.dom.TagName.DIV, {'id': 'rangeHighlightRtl'}),
  138. goog.dom.createDom(goog.dom.TagName.SPAN, {'id': 'valueThumbRtl'}),
  139. goog.dom.createDom(goog.dom.TagName.SPAN, {'id': 'extentThumbRtl'}));
  140. sandBoxRtl.appendChild(twoThumbElemRtl);
  141. twoThumbSliderRtl = new TwoThumbSlider();
  142. twoThumbSliderRtl.enableFlipForRtl(true);
  143. twoThumbSliderRtl.decorate(twoThumbElemRtl);
  144. twoChangeEventCount = 0;
  145. goog.events.listen(
  146. twoThumbSliderRtl, goog.ui.Component.EventType.CHANGE,
  147. function() { twoChangeEventCount++; });
  148. }
  149. function tearDown() {
  150. oneThumbSlider.dispose();
  151. twoThumbSlider.dispose();
  152. oneThumbSliderRtl.dispose();
  153. twoThumbSliderRtl.dispose();
  154. mockClock.dispose();
  155. goog.dom.removeChildren(goog.dom.getElement('sandbox'));
  156. }
  157. function testGetAndSetValue() {
  158. oneThumbSlider.setValue(30);
  159. assertEquals(30, oneThumbSlider.getValue());
  160. assertEquals(
  161. 'Setting valid value must dispatch only a single change event.', 1,
  162. oneChangeEventCount);
  163. oneThumbSlider.setValue(30);
  164. assertEquals(30, oneThumbSlider.getValue());
  165. assertEquals(
  166. 'Setting to same value must not dispatch change event.', 1,
  167. oneChangeEventCount);
  168. oneThumbSlider.setValue(-30);
  169. assertEquals(
  170. 'Setting invalid value must not change value.', 30,
  171. oneThumbSlider.getValue());
  172. assertEquals(
  173. 'Setting invalid value must not dispatch change event.', 1,
  174. oneChangeEventCount);
  175. // Value thumb can't go past extent thumb, so we must move that first to
  176. // allow setting value.
  177. twoThumbSlider.setExtent(70);
  178. twoChangeEventCount = 0;
  179. twoThumbSlider.setValue(60);
  180. assertEquals(60, twoThumbSlider.getValue());
  181. assertEquals(
  182. 'Setting valid value must dispatch only a single change event.', 1,
  183. twoChangeEventCount);
  184. twoThumbSlider.setValue(60);
  185. assertEquals(60, twoThumbSlider.getValue());
  186. assertEquals(
  187. 'Setting to same value must not dispatch change event.', 1,
  188. twoChangeEventCount);
  189. twoThumbSlider.setValue(-60);
  190. assertEquals(
  191. 'Setting invalid value must not change value.', 60,
  192. twoThumbSlider.getValue());
  193. assertEquals(
  194. 'Setting invalid value must not dispatch change event.', 1,
  195. twoChangeEventCount);
  196. }
  197. function testGetAndSetValueRtl() {
  198. var thumbElement = goog.dom.getElement('thumbRtl');
  199. assertEquals(0, goog.style.bidi.getOffsetStart(thumbElement));
  200. assertEquals('', thumbElement.style.left);
  201. assertTrue(thumbElement.style.right >= 0);
  202. oneThumbSliderRtl.setValue(30);
  203. assertEquals(30, oneThumbSliderRtl.getValue());
  204. assertEquals(
  205. 'Setting valid value must dispatch only a single change event.', 1,
  206. oneChangeEventCount);
  207. assertEquals('', thumbElement.style.left);
  208. assertTrue(thumbElement.style.right >= 0);
  209. oneThumbSliderRtl.setValue(30);
  210. assertEquals(30, oneThumbSliderRtl.getValue());
  211. assertEquals(
  212. 'Setting to same value must not dispatch change event.', 1,
  213. oneChangeEventCount);
  214. oneThumbSliderRtl.setValue(-30);
  215. assertEquals(
  216. 'Setting invalid value must not change value.', 30,
  217. oneThumbSliderRtl.getValue());
  218. assertEquals(
  219. 'Setting invalid value must not dispatch change event.', 1,
  220. oneChangeEventCount);
  221. // Value thumb can't go past extent thumb, so we must move that first to
  222. // allow setting value.
  223. var valueThumbElement = goog.dom.getElement('valueThumbRtl');
  224. var extentThumbElement = goog.dom.getElement('extentThumbRtl');
  225. assertEquals(0, goog.style.bidi.getOffsetStart(valueThumbElement));
  226. assertEquals(0, goog.style.bidi.getOffsetStart(extentThumbElement));
  227. assertEquals('', valueThumbElement.style.left);
  228. assertTrue(valueThumbElement.style.right >= 0);
  229. assertEquals('', extentThumbElement.style.left);
  230. assertTrue(extentThumbElement.style.right >= 0);
  231. twoThumbSliderRtl.setExtent(70);
  232. twoChangeEventCount = 0;
  233. twoThumbSliderRtl.setValue(60);
  234. assertEquals(60, twoThumbSliderRtl.getValue());
  235. assertEquals(
  236. 'Setting valid value must dispatch only a single change event.', 1,
  237. twoChangeEventCount);
  238. twoThumbSliderRtl.setValue(60);
  239. assertEquals(60, twoThumbSliderRtl.getValue());
  240. assertEquals(
  241. 'Setting to same value must not dispatch change event.', 1,
  242. twoChangeEventCount);
  243. assertEquals('', valueThumbElement.style.left);
  244. assertTrue(valueThumbElement.style.right >= 0);
  245. assertEquals('', extentThumbElement.style.left);
  246. assertTrue(extentThumbElement.style.right >= 0);
  247. twoThumbSliderRtl.setValue(-60);
  248. assertEquals(
  249. 'Setting invalid value must not change value.', 60,
  250. twoThumbSliderRtl.getValue());
  251. assertEquals(
  252. 'Setting invalid value must not dispatch change event.', 1,
  253. twoChangeEventCount);
  254. }
  255. function testGetAndSetExtent() {
  256. // Note(user): With a one thumb slider the API only really makes sense if you
  257. // always use setValue since there is no extent.
  258. twoThumbSlider.setExtent(7);
  259. assertEquals(7, twoThumbSlider.getExtent());
  260. assertEquals(
  261. 'Setting valid value must dispatch only a single change event.', 1,
  262. twoChangeEventCount);
  263. twoThumbSlider.setExtent(7);
  264. assertEquals(7, twoThumbSlider.getExtent());
  265. assertEquals(
  266. 'Setting to same value must not dispatch change event.', 1,
  267. twoChangeEventCount);
  268. twoThumbSlider.setExtent(-7);
  269. assertEquals(
  270. 'Setting invalid value must not change value.', 7,
  271. twoThumbSlider.getExtent());
  272. assertEquals(
  273. 'Setting invalid value must not dispatch change event.', 1,
  274. twoChangeEventCount);
  275. }
  276. function testUpdateValueExtent() {
  277. twoThumbSlider.setValueAndExtent(30, 50);
  278. assertNotNull(twoThumbSlider.getElement());
  279. assertEquals(
  280. 'Setting value results in updating aria-valuenow', '30',
  281. goog.a11y.aria.getState(
  282. twoThumbSlider.getElement(), goog.a11y.aria.State.VALUENOW));
  283. assertEquals(30, twoThumbSlider.getValue());
  284. assertEquals(50, twoThumbSlider.getExtent());
  285. }
  286. function testValueText() {
  287. oneThumbSlider.setValue(10);
  288. assertEquals(
  289. 'Setting value results in correct aria-valuetext', 'A big value.',
  290. goog.a11y.aria.getState(
  291. oneThumbSlider.getElement(), goog.a11y.aria.State.VALUETEXT));
  292. oneThumbSlider.setValue(2);
  293. assertEquals(
  294. 'Updating value results in updated aria-valuetext', 'A small value.',
  295. goog.a11y.aria.getState(
  296. oneThumbSlider.getElement(), goog.a11y.aria.State.VALUETEXT));
  297. }
  298. function testGetValueText() {
  299. oneThumbSlider.setValue(10);
  300. assertEquals(
  301. 'Getting the text value gets the correct description', 'A big value.',
  302. oneThumbSlider.getTextValue());
  303. oneThumbSlider.setValue(2);
  304. assertEquals(
  305. 'Getting the updated text value gets the correct updated description',
  306. 'A small value.', oneThumbSlider.getTextValue());
  307. }
  308. function testRangeListener() {
  309. var slider = new goog.ui.SliderBase;
  310. slider.updateUi_ = slider.updateAriaStates = function() {};
  311. slider.rangeModel.setValue(0);
  312. var f = goog.testing.recordFunction();
  313. goog.events.listen(slider, goog.ui.Component.EventType.CHANGE, f);
  314. slider.rangeModel.setValue(50);
  315. assertEquals(1, f.getCallCount());
  316. slider.exitDocument();
  317. slider.rangeModel.setValue(0);
  318. assertEquals(
  319. 'The range model listener should not have been removed so we ' +
  320. 'should have gotten a second event dispatch',
  321. 2, f.getCallCount());
  322. }
  323. /**
  324. * Verifies that rangeHighlight position and size are correct for the given
  325. * startValue and endValue. Assumes slider has default min/max values [0, 100],
  326. * width of 1020px, and thumb widths of 20px, with rangeHighlight drawn from
  327. * the centers of the thumbs.
  328. * @param {number} rangeHighlight The range highlight.
  329. * @param {number} startValue The start value.
  330. * @param {number} endValue The end value.
  331. */
  332. function assertHighlightedRange(rangeHighlight, startValue, endValue) {
  333. var rangeStr = '[' + startValue + ', ' + endValue + ']';
  334. var rangeStart = 10 + 10 * startValue;
  335. assertEquals(
  336. 'Range highlight for ' + rangeStr + ' should start at ' + rangeStart +
  337. 'px.',
  338. rangeStart, rangeHighlight.offsetLeft);
  339. var rangeSize = 10 * (endValue - startValue);
  340. assertEquals(
  341. 'Range highlight for ' + rangeStr + ' should have size ' + rangeSize +
  342. 'px.',
  343. rangeSize, rangeHighlight.offsetWidth);
  344. }
  345. function testKeyHandlingTests() {
  346. twoThumbSlider.setValue(0);
  347. twoThumbSlider.setExtent(100);
  348. assertEquals(0, twoThumbSlider.getValue());
  349. assertEquals(100, twoThumbSlider.getExtent());
  350. goog.testing.events.fireKeySequence(
  351. twoThumbSlider.getElement(), goog.events.KeyCodes.RIGHT);
  352. assertEquals(1, twoThumbSlider.getValue());
  353. assertEquals(99, twoThumbSlider.getExtent());
  354. goog.testing.events.fireKeySequence(
  355. twoThumbSlider.getElement(), goog.events.KeyCodes.RIGHT);
  356. assertEquals(2, twoThumbSlider.getValue());
  357. assertEquals(98, twoThumbSlider.getExtent());
  358. goog.testing.events.fireKeySequence(
  359. twoThumbSlider.getElement(), goog.events.KeyCodes.LEFT);
  360. assertEquals(1, twoThumbSlider.getValue());
  361. assertEquals(98, twoThumbSlider.getExtent());
  362. goog.testing.events.fireKeySequence(
  363. twoThumbSlider.getElement(), goog.events.KeyCodes.LEFT);
  364. assertEquals(0, twoThumbSlider.getValue());
  365. assertEquals(98, twoThumbSlider.getExtent());
  366. goog.testing.events.fireKeySequence(
  367. twoThumbSlider.getElement(), goog.events.KeyCodes.RIGHT,
  368. {shiftKey: true});
  369. assertEquals(10, twoThumbSlider.getValue());
  370. assertEquals(90, twoThumbSlider.getExtent());
  371. goog.testing.events.fireKeySequence(
  372. twoThumbSlider.getElement(), goog.events.KeyCodes.RIGHT,
  373. {shiftKey: true});
  374. assertEquals(20, twoThumbSlider.getValue());
  375. assertEquals(80, twoThumbSlider.getExtent());
  376. goog.testing.events.fireKeySequence(
  377. twoThumbSlider.getElement(), goog.events.KeyCodes.LEFT, {shiftKey: true});
  378. assertEquals(10, twoThumbSlider.getValue());
  379. assertEquals(80, twoThumbSlider.getExtent());
  380. goog.testing.events.fireKeySequence(
  381. twoThumbSlider.getElement(), goog.events.KeyCodes.LEFT, {shiftKey: true});
  382. assertEquals(0, twoThumbSlider.getValue());
  383. assertEquals(80, twoThumbSlider.getExtent());
  384. }
  385. function testKeyHandlingLargeStepSize() {
  386. twoThumbSlider.setValue(0);
  387. twoThumbSlider.setExtent(100);
  388. twoThumbSlider.setStep(5);
  389. assertEquals(0, twoThumbSlider.getValue());
  390. assertEquals(100, twoThumbSlider.getExtent());
  391. goog.testing.events.fireKeySequence(
  392. twoThumbSlider.getElement(), goog.events.KeyCodes.RIGHT);
  393. assertEquals(5, twoThumbSlider.getValue());
  394. assertEquals(95, twoThumbSlider.getExtent());
  395. goog.testing.events.fireKeySequence(
  396. twoThumbSlider.getElement(), goog.events.KeyCodes.RIGHT);
  397. assertEquals(10, twoThumbSlider.getValue());
  398. assertEquals(90, twoThumbSlider.getExtent());
  399. goog.testing.events.fireKeySequence(
  400. twoThumbSlider.getElement(), goog.events.KeyCodes.LEFT);
  401. assertEquals(5, twoThumbSlider.getValue());
  402. assertEquals(90, twoThumbSlider.getExtent());
  403. goog.testing.events.fireKeySequence(
  404. twoThumbSlider.getElement(), goog.events.KeyCodes.LEFT);
  405. assertEquals(0, twoThumbSlider.getValue());
  406. assertEquals(90, twoThumbSlider.getExtent());
  407. }
  408. function testKeyHandlingRtl() {
  409. twoThumbSliderRtl.setValue(0);
  410. twoThumbSliderRtl.setExtent(100);
  411. assertEquals(0, twoThumbSliderRtl.getValue());
  412. assertEquals(100, twoThumbSliderRtl.getExtent());
  413. goog.testing.events.fireKeySequence(
  414. twoThumbSliderRtl.getElement(), goog.events.KeyCodes.RIGHT);
  415. assertEquals(0, twoThumbSliderRtl.getValue());
  416. assertEquals(99, twoThumbSliderRtl.getExtent());
  417. goog.testing.events.fireKeySequence(
  418. twoThumbSliderRtl.getElement(), goog.events.KeyCodes.RIGHT);
  419. assertEquals(0, twoThumbSliderRtl.getValue());
  420. assertEquals(98, twoThumbSliderRtl.getExtent());
  421. goog.testing.events.fireKeySequence(
  422. twoThumbSliderRtl.getElement(), goog.events.KeyCodes.LEFT);
  423. assertEquals(1, twoThumbSliderRtl.getValue());
  424. assertEquals(98, twoThumbSliderRtl.getExtent());
  425. goog.testing.events.fireKeySequence(
  426. twoThumbSliderRtl.getElement(), goog.events.KeyCodes.LEFT);
  427. assertEquals(2, twoThumbSliderRtl.getValue());
  428. assertEquals(98, twoThumbSliderRtl.getExtent());
  429. goog.testing.events.fireKeySequence(
  430. twoThumbSliderRtl.getElement(), goog.events.KeyCodes.RIGHT,
  431. {shiftKey: true});
  432. assertEquals(0, twoThumbSliderRtl.getValue());
  433. assertEquals(90, twoThumbSliderRtl.getExtent());
  434. goog.testing.events.fireKeySequence(
  435. twoThumbSliderRtl.getElement(), goog.events.KeyCodes.RIGHT,
  436. {shiftKey: true});
  437. assertEquals(0, twoThumbSliderRtl.getValue());
  438. assertEquals(80, twoThumbSliderRtl.getExtent());
  439. goog.testing.events.fireKeySequence(
  440. twoThumbSliderRtl.getElement(), goog.events.KeyCodes.LEFT,
  441. {shiftKey: true});
  442. assertEquals(10, twoThumbSliderRtl.getValue());
  443. assertEquals(80, twoThumbSliderRtl.getExtent());
  444. goog.testing.events.fireKeySequence(
  445. twoThumbSliderRtl.getElement(), goog.events.KeyCodes.LEFT,
  446. {shiftKey: true});
  447. assertEquals(20, twoThumbSliderRtl.getValue());
  448. assertEquals(80, twoThumbSliderRtl.getExtent());
  449. }
  450. function testRangeHighlight() {
  451. var rangeHighlight = goog.dom.getElement('rangeHighlight');
  452. // Test [0, 100]
  453. twoThumbSlider.setValue(0);
  454. twoThumbSlider.setExtent(100);
  455. assertHighlightedRange(rangeHighlight, 0, 100);
  456. // Test [25, 75]
  457. twoThumbSlider.setValue(25);
  458. twoThumbSlider.setExtent(50);
  459. assertHighlightedRange(rangeHighlight, 25, 75);
  460. // Test [50, 50]
  461. twoThumbSlider.setValue(50);
  462. twoThumbSlider.setExtent(0);
  463. assertHighlightedRange(rangeHighlight, 50, 50);
  464. }
  465. function testRangeHighlightAnimation() {
  466. var animationDelay = 160; // Delay in ms, is a bit higher than actual delay.
  467. if (goog.userAgent.IE) {
  468. // For some reason, (probably due to how timing works), IE7 and IE8 will not
  469. // stop if we don't wait for it.
  470. animationDelay = 250;
  471. }
  472. var rangeHighlight = goog.dom.getElement('rangeHighlight');
  473. twoThumbSlider.setValue(0);
  474. twoThumbSlider.setExtent(100);
  475. // Animate right thumb, final range is [0, 75]
  476. twoThumbSlider.animatedSetValue(75);
  477. assertHighlightedRange(rangeHighlight, 0, 100);
  478. mockClock.tick(animationDelay);
  479. assertHighlightedRange(rangeHighlight, 0, 75);
  480. // Animate left thumb, final range is [25, 75]
  481. twoThumbSlider.animatedSetValue(25);
  482. assertHighlightedRange(rangeHighlight, 0, 75);
  483. mockClock.tick(animationDelay);
  484. assertHighlightedRange(rangeHighlight, 25, 75);
  485. }
  486. /**
  487. * Verifies that no error occurs and that the range highlight is sized correctly
  488. * for a zero-size slider (i.e. doesn't attempt to set a negative size). The
  489. * test tries to resize the slider from its original size to 0, then checks
  490. * that the range highlight's size is correctly set to 0.
  491. *
  492. * The size verification is needed because Webkit/Gecko outright ignore calls
  493. * to set negative sizes on an element, leaving it at its former size. IE
  494. * throws an error in the same situation.
  495. */
  496. function testRangeHighlightForZeroSizeSlider() {
  497. // Make sure range highlight spans whole slider before zeroing width.
  498. twoThumbSlider.setExtent(100);
  499. twoThumbSlider.getElement().style.width = 0;
  500. // The setVisible call is used to force a UI update.
  501. twoThumbSlider.setVisible(true);
  502. assertEquals(
  503. 'Range highlight size should be 0 when slider size is 0', 0,
  504. goog.dom.getElement('rangeHighlight').offsetWidth);
  505. }
  506. function testAnimatedSetValueAnimatesFactoryCreatedAnimations() {
  507. // Create and set the factory.
  508. var ignore = goog.testing.mockmatchers.ignoreArgument;
  509. var mockControl = new goog.testing.MockControl();
  510. var mockAnimation1 = mockControl.createLooseMock(goog.fx.Animation);
  511. var mockAnimation2 = mockControl.createLooseMock(goog.fx.Animation);
  512. var testAnimations = [mockAnimation1, mockAnimation2];
  513. oneThumbSlider.setAdditionalAnimations(new AnimationFactory(testAnimations));
  514. // Expect the animations to be played.
  515. mockAnimation1.play(false);
  516. mockAnimation2.play(false);
  517. mockAnimation1.addEventListener(ignore, ignore, ignore);
  518. mockAnimation2.addEventListener(ignore, ignore, ignore);
  519. // Animate and verify.
  520. mockControl.$replayAll();
  521. oneThumbSlider.animatedSetValue(50);
  522. mockControl.$verifyAll();
  523. mockControl.$resetAll();
  524. mockControl.$tearDown();
  525. }
  526. function testMouseWheelEventHandlerEnable() {
  527. // Mouse wheel handling should be enabled by default.
  528. assertTrue(oneThumbSlider.isHandleMouseWheel());
  529. // Test disabling the mouse wheel handler
  530. oneThumbSlider.setHandleMouseWheel(false);
  531. assertFalse(oneThumbSlider.isHandleMouseWheel());
  532. // Test that enabling again works fine.
  533. oneThumbSlider.setHandleMouseWheel(true);
  534. assertTrue(oneThumbSlider.isHandleMouseWheel());
  535. // Test that mouse wheel handling can be disabled before rendering a slider.
  536. var wheelDisabledElem = goog.dom.createDom(
  537. goog.dom.TagName.DIV, {}, goog.dom.createDom(goog.dom.TagName.SPAN));
  538. var wheelDisabledSlider = new OneThumbSlider();
  539. wheelDisabledSlider.setHandleMouseWheel(false);
  540. wheelDisabledSlider.decorate(wheelDisabledElem);
  541. assertFalse(wheelDisabledSlider.isHandleMouseWheel());
  542. }
  543. function testDisabledAndEnabledSlider() {
  544. // Check that a slider is enabled by default
  545. assertTrue(oneThumbSlider.isEnabled());
  546. var listenerCount = oneThumbSlider.getHandler().getListenerCount();
  547. // Disable the slider and check its state
  548. oneThumbSlider.setEnabled(false);
  549. assertFalse(oneThumbSlider.isEnabled());
  550. assertTrue(
  551. goog.dom.classlist.contains(
  552. oneThumbSlider.getElement(), 'goog-slider-disabled'));
  553. assertEquals(0, oneThumbSlider.getHandler().getListenerCount());
  554. // setValue should work unaffected even when the slider is disabled.
  555. oneThumbSlider.setValue(30);
  556. assertEquals(30, oneThumbSlider.getValue());
  557. assertEquals(
  558. 'Setting valid value must dispatch a change event ' +
  559. 'even when slider is disabled.',
  560. 1, oneChangeEventCount);
  561. // Test the transition from disabled to enabled
  562. oneThumbSlider.setEnabled(true);
  563. assertTrue(oneThumbSlider.isEnabled());
  564. assertFalse(
  565. goog.dom.classlist.contains(
  566. oneThumbSlider.getElement(), 'goog-slider-disabled'));
  567. assertTrue(listenerCount == oneThumbSlider.getHandler().getListenerCount());
  568. }
  569. function testBlockIncrementingWithEnableAndDisabled() {
  570. var doc = goog.dom.getOwnerDocument(oneThumbSlider.getElement());
  571. // Case when slider is not disabled between the mouse down and up events.
  572. goog.testing.events.fireMouseDownEvent(oneThumbSlider.getElement());
  573. assertEquals(
  574. 1, goog.events
  575. .getListeners(
  576. oneThumbSlider.getElement(), goog.events.EventType.MOUSEMOVE,
  577. false)
  578. .length);
  579. assertEquals(
  580. 1, goog.events.getListeners(doc, goog.events.EventType.MOUSEUP, true)
  581. .length);
  582. goog.testing.events.fireMouseUpEvent(oneThumbSlider.getElement());
  583. assertEquals(
  584. 0, goog.events
  585. .getListeners(
  586. oneThumbSlider.getElement(), goog.events.EventType.MOUSEMOVE,
  587. false)
  588. .length);
  589. assertEquals(
  590. 0, goog.events.getListeners(doc, goog.events.EventType.MOUSEUP, true)
  591. .length);
  592. // Case when the slider is disabled between the mouse down and up events.
  593. goog.testing.events.fireMouseDownEvent(oneThumbSlider.getElement());
  594. assertEquals(
  595. 1, goog.events
  596. .getListeners(
  597. oneThumbSlider.getElement(), goog.events.EventType.MOUSEMOVE,
  598. false)
  599. .length);
  600. assertEquals(
  601. 1, goog.events.getListeners(doc, goog.events.EventType.MOUSEUP, true)
  602. .length);
  603. oneThumbSlider.setEnabled(false);
  604. assertEquals(
  605. 0, goog.events
  606. .getListeners(
  607. oneThumbSlider.getElement(), goog.events.EventType.MOUSEMOVE,
  608. false)
  609. .length);
  610. assertEquals(
  611. 0, goog.events.getListeners(doc, goog.events.EventType.MOUSEUP, true)
  612. .length);
  613. assertEquals(1, oneThumbSlider.getHandler().getListenerCount());
  614. goog.testing.events.fireMouseUpEvent(oneThumbSlider.getElement());
  615. assertEquals(
  616. 0, goog.events
  617. .getListeners(
  618. oneThumbSlider.getElement(), goog.events.EventType.MOUSEMOVE,
  619. false)
  620. .length);
  621. assertEquals(
  622. 0, goog.events.getListeners(doc, goog.events.EventType.MOUSEUP, true)
  623. .length);
  624. }
  625. function testMouseClickWithMoveToPointEnabled() {
  626. var stepSize = 20;
  627. oneThumbSlider.setStep(stepSize);
  628. oneThumbSlider.setMoveToPointEnabled(true);
  629. var initialValue = oneThumbSlider.getValue();
  630. // Figure out the number of pixels per step.
  631. var numSteps = Math.round(
  632. (oneThumbSlider.getMaximum() - oneThumbSlider.getMinimum()) / stepSize);
  633. var size = goog.style.getSize(oneThumbSlider.getElement());
  634. var pixelsPerStep = Math.round(size.width / numSteps);
  635. var coords = goog.style.getClientPosition(oneThumbSlider.getElement());
  636. coords.x += pixelsPerStep / 2;
  637. // Case when value is increased
  638. goog.testing.events.fireClickSequence(
  639. oneThumbSlider.getElement(),
  640. /* opt_button */ undefined, coords);
  641. assertEquals(oneThumbSlider.getValue(), initialValue + stepSize);
  642. // Case when value is decreased
  643. goog.testing.events.fireClickSequence(
  644. oneThumbSlider.getElement(),
  645. /* opt_button */ undefined, coords);
  646. assertEquals(oneThumbSlider.getValue(), initialValue);
  647. // Case when thumb is clicked
  648. goog.testing.events.fireClickSequence(oneThumbSlider.getElement());
  649. assertEquals(oneThumbSlider.getValue(), initialValue);
  650. }
  651. function testNonIntegerStepSize() {
  652. var stepSize = 0.02;
  653. oneThumbSlider.setStep(stepSize);
  654. oneThumbSlider.setMinimum(-1);
  655. oneThumbSlider.setMaximum(1);
  656. oneThumbSlider.setValue(0.7);
  657. assertRoughlyEquals(0.7, oneThumbSlider.getValue(), 0.000001);
  658. oneThumbSlider.setValue(0.3);
  659. assertRoughlyEquals(0.3, oneThumbSlider.getValue(), 0.000001);
  660. }
  661. function testSingleThumbSliderHasZeroExtent() {
  662. var stepSize = 0.02;
  663. oneThumbSlider.setStep(stepSize);
  664. oneThumbSlider.setMinimum(-1);
  665. oneThumbSlider.setMaximum(1);
  666. oneThumbSlider.setValue(0.7);
  667. assertEquals(0, oneThumbSlider.getExtent());
  668. oneThumbSlider.setValue(0.3);
  669. assertEquals(0, oneThumbSlider.getExtent());
  670. }
  671. /**
  672. * Tests getThumbCoordinateForValue method.
  673. */
  674. function testThumbCoordinateForValueWithHorizontalSlider() {
  675. // Make sure the y-coordinate stays the same for the horizontal slider.
  676. var originalY = goog.style.getPosition(oneThumbSlider.valueThumb).y;
  677. var width = oneThumbSlider.getElement().clientWidth -
  678. oneThumbSlider.valueThumb.offsetWidth;
  679. var range = oneThumbSlider.getMaximum() - oneThumbSlider.getMinimum();
  680. // Verify coordinate for a particular value.
  681. var value = 20;
  682. var expectedX = Math.round(value / range * width);
  683. var expectedCoord = new goog.math.Coordinate(expectedX, originalY);
  684. var coord = oneThumbSlider.getThumbCoordinateForValue(value);
  685. assertObjectEquals(expectedCoord, coord);
  686. // Verify this works regardless of current position.
  687. oneThumbSlider.setValue(value / 2);
  688. coord = oneThumbSlider.getThumbCoordinateForValue(value);
  689. assertObjectEquals(expectedCoord, coord);
  690. }
  691. function testThumbCoordinateForValueWithVerticalSlider() {
  692. // Make sure the x-coordinate stays the same for the vertical slider.
  693. oneThumbSlider.setOrientation(goog.ui.SliderBase.Orientation.VERTICAL);
  694. var originalX = goog.style.getPosition(oneThumbSlider.valueThumb).x;
  695. var height = oneThumbSlider.getElement().clientHeight -
  696. oneThumbSlider.valueThumb.offsetHeight;
  697. var range = oneThumbSlider.getMaximum() - oneThumbSlider.getMinimum();
  698. // Verify coordinate for a particular value.
  699. var value = 20;
  700. var expectedY = height - Math.round(value / range * height);
  701. var expectedCoord = new goog.math.Coordinate(originalX, expectedY);
  702. var coord = oneThumbSlider.getThumbCoordinateForValue(value);
  703. assertObjectEquals(expectedCoord, coord);
  704. // Verify this works regardless of current position.
  705. oneThumbSlider.setValue(value / 2);
  706. coord = oneThumbSlider.getThumbCoordinateForValue(value);
  707. assertObjectEquals(expectedCoord, coord);
  708. }
  709. /**
  710. * Tests getValueFromMousePosition method.
  711. */
  712. function testValueFromMousePosition() {
  713. var value = 30;
  714. oneThumbSlider.setValue(value);
  715. var offset = goog.style.getPageOffset(oneThumbSlider.valueThumb);
  716. var size = goog.style.getSize(oneThumbSlider.valueThumb);
  717. offset.x += size.width / 2;
  718. offset.y += size.height / 2;
  719. var e = null;
  720. goog.events.listen(
  721. oneThumbSlider, goog.events.EventType.MOUSEMOVE,
  722. function(evt) { e = evt; });
  723. goog.testing.events.fireMouseMoveEvent(oneThumbSlider, offset);
  724. assertNotEquals(e, null);
  725. assertRoughlyEquals(
  726. value, Math.round(oneThumbSlider.getValueFromMousePosition(e)), 1);
  727. // Verify this works regardless of current position.
  728. oneThumbSlider.setValue(value / 2);
  729. assertRoughlyEquals(
  730. value, Math.round(oneThumbSlider.getValueFromMousePosition(e)), 1);
  731. }
  732. /**
  733. * Tests ignoring click event after mousedown event.
  734. */
  735. function testClickAfterMousedown() {
  736. // Get the center of the thumb at value zero.
  737. oneThumbSlider.setValue(0);
  738. var offset = goog.style.getPageOffset(oneThumbSlider.valueThumb);
  739. var size = goog.style.getSize(oneThumbSlider.valueThumb);
  740. offset.x += size.width / 2;
  741. offset.y += size.height / 2;
  742. var sliderElement = oneThumbSlider.getElement();
  743. var width = sliderElement.clientWidth - size.width;
  744. var range = oneThumbSlider.getMaximum() - oneThumbSlider.getMinimum();
  745. var offsetXAtZero = offset.x;
  746. // Temporarily control time.
  747. var theTime = goog.now();
  748. var saveGoogNow = goog.now;
  749. goog.now = function() { return theTime; };
  750. // set coordinate for a particular value.
  751. var valueOne = 10;
  752. offset.x = offsetXAtZero + Math.round(valueOne / range * width);
  753. goog.testing.events.fireMouseDownEvent(sliderElement, null, offset);
  754. assertEquals(valueOne, oneThumbSlider.getValue());
  755. // Verify a click event with another value that follows quickly is ignored.
  756. theTime += oneThumbSlider.MOUSE_DOWN_DELAY_ / 2;
  757. var valueTwo = 20;
  758. offset.x = offsetXAtZero + Math.round(valueTwo / range * width);
  759. goog.testing.events.fireClickEvent(sliderElement, null, offset);
  760. assertEquals(valueOne, oneThumbSlider.getValue());
  761. // Verify a click later in time does move the thumb.
  762. theTime += oneThumbSlider.MOUSE_DOWN_DELAY_;
  763. goog.testing.events.fireClickEvent(sliderElement, null, offset);
  764. assertEquals(valueTwo, oneThumbSlider.getValue());
  765. goog.now = saveGoogNow;
  766. }
  767. /**
  768. * Tests dragging events.
  769. */
  770. function testDragEvents() {
  771. var offset = goog.style.getPageOffset(oneThumbSlider.valueThumb);
  772. var size = goog.style.getSize(oneThumbSlider.valueThumb);
  773. offset.x += size.width / 2;
  774. offset.y += size.height / 2;
  775. var event_types = [];
  776. var handler = function(evt) {
  777. event_types.push(evt.type);
  778. };
  779. goog.events.listen(
  780. oneThumbSlider,
  781. [
  782. goog.ui.SliderBase.EventType.DRAG_START,
  783. goog.ui.SliderBase.EventType.DRAG_END,
  784. goog.ui.SliderBase.EventType.DRAG_VALUE_START,
  785. goog.ui.SliderBase.EventType.DRAG_VALUE_END,
  786. goog.ui.SliderBase.EventType.DRAG_EXTENT_START,
  787. goog.ui.SliderBase.EventType.DRAG_EXTENT_END,
  788. goog.ui.Component.EventType.CHANGE
  789. ],
  790. handler);
  791. // Since the order of the events between value and extent is not guaranteed
  792. // across browsers, we need to allow for both here and once we have
  793. // them all, make sure that they were different.
  794. function isValueOrExtentDragStart(type) {
  795. return type == goog.ui.SliderBase.EventType.DRAG_VALUE_START ||
  796. type == goog.ui.SliderBase.EventType.DRAG_EXTENT_START;
  797. }
  798. function isValueOrExtentDragEnd(type) {
  799. return type == goog.ui.SliderBase.EventType.DRAG_VALUE_END ||
  800. type == goog.ui.SliderBase.EventType.DRAG_EXTENT_END;
  801. }
  802. // Test that dragging the thumb calls all the correct events.
  803. goog.testing.events.fireMouseDownEvent(oneThumbSlider.valueThumb);
  804. offset.x += 100;
  805. goog.testing.events.fireMouseMoveEvent(oneThumbSlider.valueThumb, offset);
  806. goog.testing.events.fireMouseUpEvent(oneThumbSlider.valueThumb);
  807. assertEquals(9, event_types.length);
  808. assertEquals(goog.ui.SliderBase.EventType.DRAG_START, event_types[0]);
  809. assertTrue(isValueOrExtentDragStart(event_types[1]));
  810. assertEquals(goog.ui.SliderBase.EventType.DRAG_START, event_types[2]);
  811. assertTrue(isValueOrExtentDragStart(event_types[3]));
  812. assertEquals(goog.ui.Component.EventType.CHANGE, event_types[4]);
  813. assertEquals(goog.ui.SliderBase.EventType.DRAG_END, event_types[5]);
  814. assertTrue(isValueOrExtentDragEnd(event_types[6]));
  815. assertEquals(goog.ui.SliderBase.EventType.DRAG_END, event_types[7]);
  816. assertTrue(isValueOrExtentDragEnd(event_types[8]));
  817. assertFalse(event_types[1] == event_types[3]);
  818. assertFalse(event_types[6] == event_types[8]);
  819. // Test that clicking the thumb without moving the mouse does not cause a
  820. // CHANGE event between DRAG_START/DRAG_END.
  821. event_types = [];
  822. goog.testing.events.fireMouseDownEvent(oneThumbSlider.valueThumb);
  823. goog.testing.events.fireMouseUpEvent(oneThumbSlider.valueThumb);
  824. assertEquals(8, event_types.length);
  825. assertEquals(goog.ui.SliderBase.EventType.DRAG_START, event_types[0]);
  826. assertTrue(isValueOrExtentDragStart(event_types[1]));
  827. assertEquals(goog.ui.SliderBase.EventType.DRAG_START, event_types[2]);
  828. assertTrue(isValueOrExtentDragStart(event_types[3]));
  829. assertEquals(goog.ui.SliderBase.EventType.DRAG_END, event_types[4]);
  830. assertTrue(isValueOrExtentDragEnd(event_types[5]));
  831. assertEquals(goog.ui.SliderBase.EventType.DRAG_END, event_types[6]);
  832. assertTrue(isValueOrExtentDragEnd(event_types[7]));
  833. assertFalse(event_types[1] == event_types[3]);
  834. assertFalse(event_types[5] == event_types[7]);
  835. // Early listener removal, do not wait for tearDown, to avoid building up
  836. // arrays of events unnecessarilly in further tests.
  837. goog.events.removeAll(oneThumbSlider);
  838. }
  839. /**
  840. * Tests animationend event after click.
  841. */
  842. function testAnimationEndEventAfterClick() {
  843. var offset = goog.style.getPageOffset(oneThumbSlider.valueThumb);
  844. var size = goog.style.getSize(oneThumbSlider.valueThumb);
  845. offset.x += size.width / 2;
  846. offset.y += size.height / 2;
  847. var event_types = [];
  848. var handler = function(evt) {
  849. event_types.push(evt.type);
  850. };
  851. var animationDelay = 160; // Delay in ms, is a bit higher than actual delay.
  852. if (goog.userAgent.IE) {
  853. // For some reason, (probably due to how timing works), IE7 and IE8 will not
  854. // stop if we don't wait for it.
  855. animationDelay = 250;
  856. }
  857. oneThumbSlider.setMoveToPointEnabled(true);
  858. goog.events.listen(
  859. oneThumbSlider, goog.ui.SliderBase.EventType.ANIMATION_END, handler);
  860. function isAnimationEndType(type) {
  861. return type == goog.ui.SliderBase.EventType.ANIMATION_END;
  862. }
  863. offset.x += 100;
  864. goog.testing.events.fireClickSequence(
  865. oneThumbSlider.getElement(), /* opt_button */ undefined, offset);
  866. mockClock.tick(animationDelay);
  867. assertEquals(1, event_types.length);
  868. assertTrue(isAnimationEndType(event_types[0]));
  869. goog.events.removeAll(oneThumbSlider);
  870. }