draglistgroup_test.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  1. // Copyright 2011 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.fx.DragListGroupTest');
  15. goog.setTestOnly('goog.fx.DragListGroupTest');
  16. goog.require('goog.array');
  17. goog.require('goog.dom');
  18. goog.require('goog.dom.TagName');
  19. goog.require('goog.dom.classlist');
  20. goog.require('goog.events');
  21. goog.require('goog.events.BrowserEvent');
  22. goog.require('goog.events.BrowserFeature');
  23. goog.require('goog.events.Event');
  24. goog.require('goog.events.EventType');
  25. goog.require('goog.fx.DragEvent');
  26. goog.require('goog.fx.DragListDirection');
  27. goog.require('goog.fx.DragListGroup');
  28. goog.require('goog.fx.Dragger');
  29. goog.require('goog.math.Coordinate');
  30. goog.require('goog.object');
  31. goog.require('goog.testing.events');
  32. goog.require('goog.testing.jsunit');
  33. /** @type {goog.fx.DragListGroup} */
  34. var dlg;
  35. /** @type {goog.dom.Element} */
  36. var list;
  37. /** @type {goog.events.BrowserEvent} */
  38. var event;
  39. /**
  40. * The number of event listeners registered by the DragListGroup after the
  41. * init() call.
  42. * @type {number}
  43. */
  44. var initialListenerCount;
  45. /**
  46. * Type of events fired by the DragListGroup.
  47. * @type {!Array<string>}
  48. */
  49. var firedEventTypes;
  50. function setUp() {
  51. var sandbox = goog.dom.getElement('sandbox');
  52. list = goog.dom.createDom(goog.dom.TagName.DIV, {'id': 'horiz_div'});
  53. list.appendChild(
  54. goog.dom.createDom(
  55. goog.dom.TagName.DIV, null, goog.dom.createTextNode('1')));
  56. list.appendChild(
  57. goog.dom.createDom(
  58. goog.dom.TagName.DIV, null, goog.dom.createTextNode('2')));
  59. list.appendChild(
  60. goog.dom.createDom(
  61. goog.dom.TagName.DIV, null, goog.dom.createTextNode('3')));
  62. sandbox.appendChild(list);
  63. dlg = new goog.fx.DragListGroup();
  64. dlg.setDragItemHoverClass('opacity_40', 'cursor_move');
  65. dlg.setDragItemHandleHoverClass('opacity_40', 'cursor_pointer');
  66. dlg.setCurrDragItemClass('blue_bg', 'opacity_40');
  67. dlg.setDraggerElClass('cursor_move', 'blue_bg');
  68. dlg.addDragList(list, goog.fx.DragListDirection.RIGHT);
  69. dlg.init();
  70. initialListenerCount = goog.object.getCount(dlg.eventHandler_.keys_);
  71. event = new goog.events.BrowserEvent();
  72. event.currentTarget =
  73. goog.dom.getElementsByTagName(goog.dom.TagName.DIV, list)[0];
  74. firedEventTypes = [];
  75. goog.events.listen(
  76. dlg, goog.object.getValues(goog.fx.DragListGroup.EventType),
  77. function(e) { firedEventTypes.push(e.type); });
  78. }
  79. function tearDown() {
  80. dlg.dispose();
  81. goog.dom.removeChildren(goog.dom.getElement('sandbox'));
  82. }
  83. /**
  84. * Test the initial assumptions.
  85. *
  86. * Verify that the setter methods work properly, i.e., the CSS classes are
  87. * stored in the private arrays after init() but are not added yet to target.
  88. * (Since initially, we are not yet hovering over any list, in particular,
  89. * over this target.)
  90. */
  91. function testSettersAfterInit() {
  92. assertTrue(
  93. goog.array.equals(
  94. dlg.dragItemHoverClasses_, ['opacity_40', 'cursor_move']));
  95. assertTrue(
  96. goog.array.equals(
  97. dlg.dragItemHandleHoverClasses_, ['opacity_40', 'cursor_pointer']));
  98. assertTrue(
  99. goog.array.equals(dlg.currDragItemClasses_, ['blue_bg', 'opacity_40']));
  100. assertFalse(
  101. 'Should have no cursor_move class after init',
  102. goog.dom.classlist.contains(event.currentTarget, 'cursor_move'));
  103. assertFalse(
  104. 'Should have no cursor_pointer class after init',
  105. goog.dom.classlist.contains(event.currentTarget, 'cursor_pointer'));
  106. assertFalse(
  107. 'Should have no opacity_40 class after init',
  108. goog.dom.classlist.contains(event.currentTarget, 'opacity_40'));
  109. assertFalse(
  110. 'Should not have blue_bg class after init',
  111. goog.dom.classlist.contains(event.currentTarget, 'blue_bg'));
  112. }
  113. /**
  114. * Test the effect of hovering over a list.
  115. *
  116. * Check that after the MOUSEOVER browser event these classes are added to
  117. * the current target of the event.
  118. */
  119. function testAddDragItemHoverClasses() {
  120. dlg.handleDragItemMouseover_(event);
  121. assertTrue(
  122. 'Should have cursor_move class after MOUSEOVER',
  123. goog.dom.classlist.contains(event.currentTarget, 'cursor_move'));
  124. assertTrue(
  125. 'Should have opacity_40 class after MOUSEOVER',
  126. goog.dom.classlist.contains(event.currentTarget, 'opacity_40'));
  127. assertFalse(
  128. 'Should not have cursor_pointer class after MOUSEOVER',
  129. goog.dom.classlist.contains(event.currentTarget, 'cursor_pointer'));
  130. assertFalse(
  131. 'Should not have blue_bg class after MOUSEOVER',
  132. goog.dom.classlist.contains(event.currentTarget, 'blue_bg'));
  133. }
  134. function testAddDragItemHandleHoverClasses() {
  135. dlg.handleDragItemHandleMouseover_(event);
  136. assertFalse(
  137. 'Should not have cursor_move class after MOUSEOVER',
  138. goog.dom.classlist.contains(event.currentTarget, 'cursor_move'));
  139. assertTrue(
  140. 'Should have opacity_40 class after MOUSEOVER',
  141. goog.dom.classlist.contains(event.currentTarget, 'opacity_40'));
  142. assertTrue(
  143. 'Should have cursor_pointer class after MOUSEOVER',
  144. goog.dom.classlist.contains(event.currentTarget, 'cursor_pointer'));
  145. assertFalse(
  146. 'Should not have blue_bg class after MOUSEOVER',
  147. goog.dom.classlist.contains(event.currentTarget, 'blue_bg'));
  148. }
  149. /**
  150. * Test the effect of stopping hovering over a list.
  151. *
  152. * Check that after the MOUSEOUT browser event all CSS classes are removed
  153. * from the target (as we are no longer hovering over the it).
  154. */
  155. function testRemoveDragItemHoverClasses() {
  156. dlg.handleDragItemMouseover_(event);
  157. dlg.handleDragItemMouseout_(event);
  158. assertFalse(
  159. 'Should have no cursor_move class after MOUSEOUT',
  160. goog.dom.classlist.contains(event.currentTarget, 'cursor_move'));
  161. assertFalse(
  162. 'Should have no cursor_pointer class after MOUSEOUT',
  163. goog.dom.classlist.contains(event.currentTarget, 'cursor_pointer'));
  164. assertFalse(
  165. 'Should have no opacity_40 class after MOUSEOUT',
  166. goog.dom.classlist.contains(event.currentTarget, 'opacity_40'));
  167. assertFalse(
  168. 'Should have no blue_bg class after MOUSEOUT',
  169. goog.dom.classlist.contains(event.currentTarget, 'blue_bg'));
  170. }
  171. function testRemoveDragItemHandleHoverClasses() {
  172. dlg.handleDragItemHandleMouseover_(event);
  173. dlg.handleDragItemHandleMouseout_(event);
  174. assertFalse(
  175. 'Should have no cursor_move class after MOUSEOUT',
  176. goog.dom.classlist.contains(event.currentTarget, 'cursor_move'));
  177. assertFalse(
  178. 'Should have no cursor_pointer class after MOUSEOUT',
  179. goog.dom.classlist.contains(event.currentTarget, 'cursor_pointer'));
  180. assertFalse(
  181. 'Should have no opacity_40 class after MOUSEOUT',
  182. goog.dom.classlist.contains(event.currentTarget, 'opacity_40'));
  183. assertFalse(
  184. 'Should have no blue_bg class after MOUSEOUT',
  185. goog.dom.classlist.contains(event.currentTarget, 'blue_bg'));
  186. }
  187. /**
  188. * Test the effect of dragging an item. (DRAGSTART event.)
  189. *
  190. * Check that after the MOUSEDOWN browser event is handled by the
  191. * handlePotentialDragStart_() method the currDragItem has the CSS classes
  192. * set by the setter method.
  193. */
  194. function testAddCurrentDragItemClasses() {
  195. var be = new goog.events.BrowserEvent({
  196. type: goog.events.EventType.MOUSEDOWN,
  197. button: goog.events.BrowserFeature.HAS_W3C_BUTTON ? 0 : 1
  198. });
  199. event.event_ = be;
  200. dlg.handlePotentialDragStart_(event);
  201. assertFalse(
  202. 'Should have no cursor_move class after MOUSEDOWN',
  203. goog.dom.classlist.contains(dlg.currDragItem_, 'cursor_move'));
  204. assertFalse(
  205. 'Should have no cursor_pointer class after MOUSEDOWN',
  206. goog.dom.classlist.contains(dlg.currDragItem_, 'cursor_pointer'));
  207. assertTrue(
  208. 'Should have opacity_40 class after MOUSEDOWN',
  209. goog.dom.classlist.contains(dlg.currDragItem_, 'opacity_40'));
  210. assertTrue(
  211. 'Should have blue_bg class after MOUSEDOWN',
  212. goog.dom.classlist.contains(dlg.currDragItem_, 'blue_bg'));
  213. }
  214. /**
  215. * Test the effect of dragging an item. (DRAGEND event.)
  216. *
  217. * Check that after the MOUSEUP browser event handled by the handleDragEnd_()
  218. * method the currDragItem has no CSS classes set in the dispatched event.
  219. */
  220. function testRemoveCurrentDragItemClasses() {
  221. var be = new goog.events.BrowserEvent({
  222. type: goog.events.EventType.MOUSEDOWN,
  223. button: goog.events.BrowserFeature.HAS_W3C_BUTTON ? 0 : 1
  224. });
  225. event.event_ = be;
  226. dlg.handlePotentialDragStart_(event);
  227. // Need to catch the dispatched event because the temporary fields
  228. // including dlg.currentDragItem_ are cleared after the dragging has ended.
  229. var currDragItem = goog.dom.createDom(
  230. goog.dom.TagName.DIV, ['cursor_move', 'blue_bg'],
  231. goog.dom.createTextNode('4'));
  232. goog.events.listen(dlg, goog.fx.DragListGroup.EventType.DRAGEND, function(e) {
  233. currDragItem = dlg.currDragItem_;
  234. });
  235. var dragger = new goog.fx.Dragger(event.currentTarget);
  236. be.type = goog.events.EventType.MOUSEUP;
  237. be.clientX = 1;
  238. be.clientY = 2;
  239. var dragEvent = new goog.fx.DragEvent(
  240. goog.fx.Dragger.EventType.END, dragger, be.clientX, be.clientY, be);
  241. dlg.handleDragEnd_(dragEvent); // this method dispatches the DRAGEND event
  242. dragger.dispose();
  243. assertFalse(
  244. 'Should have no cursor_move class after MOUSEUP',
  245. goog.dom.classlist.contains(currDragItem, 'cursor_move'));
  246. assertFalse(
  247. 'Should have no cursor_pointer class after MOUSEUP',
  248. goog.dom.classlist.contains(currDragItem, 'cursor_pointer'));
  249. assertFalse(
  250. 'Should have no opacity_40 class after MOUSEUP',
  251. goog.dom.classlist.contains(currDragItem, 'opacity_40'));
  252. assertFalse(
  253. 'Should have no blue_bg class after MOUSEUP',
  254. goog.dom.classlist.contains(currDragItem, 'blue_bg'));
  255. }
  256. /**
  257. * Asserts that the DragListGroup is in idle state.
  258. * @param {!goog.fx.DragListGroup} dlg The DragListGroup to examine.
  259. */
  260. function assertIdle(dlg) {
  261. assertFalse(dlg.isDragging());
  262. assertNull('dragger element has been cleaned up', dlg.draggerEl_);
  263. assertNull('dragger has been cleaned up', dlg.dragger_);
  264. assertEquals(
  265. 'the additional event listeners have been removed', initialListenerCount,
  266. goog.object.getCount(dlg.eventHandler_.keys_));
  267. }
  268. function testFiredEvents() {
  269. goog.testing.events.fireClickSequence(list.firstChild);
  270. assertArrayEquals(
  271. 'event types in case of zero distance dragging',
  272. [
  273. goog.fx.DragListGroup.EventType.DRAGGERCREATED.toString(),
  274. goog.fx.DragListGroup.EventType.BEFOREDRAGSTART.toString(),
  275. goog.fx.DragListGroup.EventType.DRAGSTART.toString(),
  276. goog.fx.DragListGroup.EventType.BEFOREDRAGEND.toString(),
  277. goog.fx.DragListGroup.EventType.DRAGGERREMOVED.toString(),
  278. goog.fx.DragListGroup.EventType.DRAGEND.toString()
  279. ],
  280. firedEventTypes);
  281. assertIdle(dlg);
  282. }
  283. function testFiredEventsWithHysteresis() {
  284. dlg.setHysteresis(2);
  285. goog.testing.events.fireClickSequence(list.firstChild);
  286. assertArrayEquals(
  287. 'only clone events are fired on click if hysteresis is enabled',
  288. [
  289. goog.fx.DragListGroup.EventType.DRAGGERCREATED.toString(),
  290. goog.fx.DragListGroup.EventType.DRAGGERREMOVED.toString()
  291. ],
  292. firedEventTypes);
  293. firedEventTypes.length = 0;
  294. assertIdle(dlg);
  295. goog.testing.events.fireMouseDownEvent(
  296. list.firstChild, null, new goog.math.Coordinate(0, 0));
  297. goog.testing.events.fireMouseMoveEvent(
  298. list.firstChild, new goog.math.Coordinate(1, 0));
  299. assertArrayEquals(
  300. 'only potential-start event is fired on click if hysteresis is enabled',
  301. [goog.fx.DragListGroup.EventType.DRAGGERCREATED.toString()],
  302. firedEventTypes);
  303. firedEventTypes.length = 0;
  304. goog.testing.events.fireMouseMoveEvent(
  305. list.firstChild, new goog.math.Coordinate(3, 0));
  306. assertArrayEquals(
  307. 'start+move events are fired over hysteresis distance',
  308. [
  309. goog.fx.DragListGroup.EventType.BEFOREDRAGSTART.toString(),
  310. goog.fx.DragListGroup.EventType.DRAGSTART.toString(),
  311. goog.fx.DragListGroup.EventType.BEFOREDRAGMOVE.toString(),
  312. goog.fx.DragListGroup.EventType.DRAGMOVE.toString()
  313. ],
  314. firedEventTypes);
  315. assertTrue(dlg.isDragging());
  316. firedEventTypes.length = 0;
  317. goog.testing.events.fireMouseUpEvent(
  318. list.firstChild, null, new goog.math.Coordinate(3, 0));
  319. assertArrayEquals(
  320. 'end events are fired on mouseup',
  321. [
  322. goog.fx.DragListGroup.EventType.BEFOREDRAGEND.toString(),
  323. goog.fx.DragListGroup.EventType.DRAGGERREMOVED.toString(),
  324. goog.fx.DragListGroup.EventType.DRAGEND.toString()
  325. ],
  326. firedEventTypes);
  327. assertIdle(dlg);
  328. }
  329. function testPreventDefaultBeforeDragStart() {
  330. goog.events.listen(
  331. dlg, goog.fx.DragListGroup.EventType.BEFOREDRAGSTART,
  332. goog.events.Event.preventDefault);
  333. goog.testing.events.fireMouseDownEvent(list.firstChild);
  334. assertArrayEquals(
  335. 'event types if dragging is prevented',
  336. [
  337. goog.fx.DragListGroup.EventType.DRAGGERCREATED.toString(),
  338. goog.fx.DragListGroup.EventType.BEFOREDRAGSTART.toString(),
  339. goog.fx.DragListGroup.EventType.DRAGGERREMOVED.toString()
  340. ],
  341. firedEventTypes);
  342. assertIdle(dlg);
  343. }
  344. function testPreventDefaultBeforeDragStartWithHysteresis() {
  345. dlg.setHysteresis(5);
  346. goog.events.listen(
  347. dlg, goog.fx.DragListGroup.EventType.BEFOREDRAGSTART,
  348. goog.events.Event.preventDefault);
  349. goog.testing.events.fireMouseDownEvent(
  350. list.firstChild, null, new goog.math.Coordinate(0, 0));
  351. goog.testing.events.fireMouseMoveEvent(
  352. list.firstChild, new goog.math.Coordinate(10, 0));
  353. assertArrayEquals(
  354. 'event types if dragging is prevented',
  355. [
  356. goog.fx.DragListGroup.EventType.DRAGGERCREATED.toString(),
  357. goog.fx.DragListGroup.EventType.BEFOREDRAGSTART.toString(),
  358. goog.fx.DragListGroup.EventType.DRAGGERREMOVED.toString()
  359. ],
  360. firedEventTypes);
  361. assertIdle(dlg);
  362. }
  363. function testRightClick() {
  364. goog.testing.events.fireMouseDownEvent(
  365. list.firstChild, goog.events.BrowserEvent.MouseButton.RIGHT);
  366. goog.testing.events.fireMouseUpEvent(
  367. list.firstChild, goog.events.BrowserEvent.MouseButton.RIGHT);
  368. assertArrayEquals(
  369. 'only clone events are fired on right click',
  370. [
  371. goog.fx.DragListGroup.EventType.DRAGGERCREATED.toString(),
  372. goog.fx.DragListGroup.EventType.DRAGGERREMOVED.toString()
  373. ],
  374. firedEventTypes);
  375. assertIdle(dlg);
  376. }
  377. /**
  378. * Tests that a new item can be added to a drag list after the control has
  379. * been initialized.
  380. */
  381. function testAddItemToDragList() {
  382. var item = goog.dom.createDom(
  383. goog.dom.TagName.DIV, null, goog.dom.createTextNode('newItem'));
  384. dlg.addItemToDragList(list, item);
  385. assertEquals(item, list.lastChild);
  386. assertEquals(4, goog.dom.getChildren(list).length);
  387. goog.events.listen(
  388. dlg, goog.fx.DragListGroup.EventType.BEFOREDRAGSTART,
  389. goog.events.Event.preventDefault);
  390. goog.testing.events.fireMouseDownEvent(item);
  391. assertArrayEquals(
  392. 'Should fire beforedragstart event when clicked',
  393. [
  394. goog.fx.DragListGroup.EventType.DRAGGERCREATED.toString(),
  395. goog.fx.DragListGroup.EventType.BEFOREDRAGSTART.toString(),
  396. goog.fx.DragListGroup.EventType.DRAGGERREMOVED.toString()
  397. ],
  398. firedEventTypes);
  399. }
  400. /**
  401. * Tests that a new item added to a drag list after the control has been
  402. * initialized is inserted at the correct position.
  403. */
  404. function testInsertItemInDragList() {
  405. var item = goog.dom.createDom(
  406. goog.dom.TagName.DIV, null, goog.dom.createTextNode('newItem'));
  407. dlg.addItemToDragList(list, item, 0);
  408. assertEquals(item, list.firstChild);
  409. assertEquals(4, goog.dom.getChildren(list).length);
  410. goog.events.listen(
  411. dlg, goog.fx.DragListGroup.EventType.BEFOREDRAGSTART,
  412. goog.events.Event.preventDefault);
  413. goog.testing.events.fireMouseDownEvent(item);
  414. assertArrayEquals(
  415. 'Should fire beforedragstart event when clicked',
  416. [
  417. goog.fx.DragListGroup.EventType.DRAGGERCREATED.toString(),
  418. goog.fx.DragListGroup.EventType.BEFOREDRAGSTART.toString(),
  419. goog.fx.DragListGroup.EventType.DRAGGERREMOVED.toString()
  420. ],
  421. firedEventTypes);
  422. }