popupbase_test.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  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.PopupBaseTest');
  15. goog.setTestOnly('goog.ui.PopupBaseTest');
  16. goog.require('goog.dom');
  17. goog.require('goog.dom.TagName');
  18. goog.require('goog.events');
  19. goog.require('goog.events.EventTarget');
  20. goog.require('goog.events.EventType');
  21. goog.require('goog.events.KeyCodes');
  22. goog.require('goog.fx.Transition');
  23. goog.require('goog.fx.css3');
  24. goog.require('goog.testing.MockClock');
  25. goog.require('goog.testing.events');
  26. goog.require('goog.testing.events.Event');
  27. goog.require('goog.testing.jsunit');
  28. goog.require('goog.ui.PopupBase');
  29. var targetDiv;
  30. var popupDiv;
  31. var partnerDiv;
  32. var clock;
  33. var popup;
  34. function setUpPage() {
  35. targetDiv = goog.dom.getElement('targetDiv');
  36. popupDiv = goog.dom.getElement('popupDiv');
  37. partnerDiv = goog.dom.getElement('partnerDiv');
  38. }
  39. function setUp() {
  40. popup = new goog.ui.PopupBase(popupDiv);
  41. clock = new goog.testing.MockClock(true);
  42. }
  43. function tearDown() {
  44. popup.dispose();
  45. clock.uninstall();
  46. document.body.setAttribute('dir', 'ltr');
  47. }
  48. function testSetVisible() {
  49. popup.setVisible(true);
  50. assertEquals('visible', popupDiv.style.visibility);
  51. assertEquals('', popupDiv.style.display);
  52. popup.setVisible(false);
  53. assertEquals('hidden', popupDiv.style.visibility);
  54. assertEquals('none', popupDiv.style.display);
  55. }
  56. function testEscapeDismissal() {
  57. popup.setHideOnEscape(true);
  58. assertTrue(
  59. 'Sanity check that getHideOnEscape is true when set to true.',
  60. popup.getHideOnEscape());
  61. popup.setVisible(true);
  62. assertFalse(
  63. 'Escape key should be cancelled',
  64. goog.testing.events.fireKeySequence(targetDiv, goog.events.KeyCodes.ESC));
  65. assertFalse(popup.isVisible());
  66. }
  67. function testEscapeDismissalCanBeDisabled() {
  68. popup.setHideOnEscape(false);
  69. popup.setVisible(true);
  70. assertTrue(
  71. 'Escape key should be cancelled',
  72. goog.testing.events.fireKeySequence(targetDiv, goog.events.KeyCodes.ESC));
  73. assertTrue(popup.isVisible());
  74. }
  75. function testEscapeDismissalIsDisabledByDefault() {
  76. assertFalse(popup.getHideOnEscape());
  77. }
  78. function testEscapeDismissalDoesNotRecognizeOtherKeys() {
  79. popup.setHideOnEscape(true);
  80. popup.setVisible(true);
  81. var eventsPropagated = 0;
  82. goog.events.listenOnce(
  83. goog.dom.getElement('commonAncestor'),
  84. [
  85. goog.events.EventType.KEYDOWN, goog.events.EventType.KEYUP,
  86. goog.events.EventType.KEYPRESS
  87. ],
  88. function() { ++eventsPropagated; });
  89. assertTrue('Popup should remain visible', popup.isVisible());
  90. assertTrue(
  91. 'The key event default action should not be prevented',
  92. goog.testing.events.fireKeySequence(targetDiv, goog.events.KeyCodes.A));
  93. assertEquals(
  94. 'Keydown, keyup, and keypress should have all propagated', 3,
  95. eventsPropagated);
  96. }
  97. function testEscapeDismissalCanBeCancelledByBeforeHideEvent() {
  98. popup.setHideOnEscape(true);
  99. popup.setVisible(true);
  100. var eventsPropagated = 0;
  101. goog.events.listenOnce(
  102. goog.dom.getElement('commonAncestor'), goog.events.EventType.KEYDOWN,
  103. function() { ++eventsPropagated; });
  104. // Make a listener so that we stop hiding with an event handler.
  105. goog.events.listenOnce(
  106. popup, goog.ui.PopupBase.EventType.BEFORE_HIDE,
  107. function(e) { e.preventDefault(); });
  108. assertEquals('The hide should have been cancelled', true, popup.isVisible());
  109. assertTrue(
  110. 'The key event default action should not be prevented',
  111. goog.testing.events.fireKeySequence(targetDiv, goog.events.KeyCodes.ESC));
  112. assertEquals('Keydown should have all propagated', 1, eventsPropagated);
  113. }
  114. function testEscapeDismissalProvidesKeyTargetAsTargetForHideEvents() {
  115. popup.setHideOnEscape(true);
  116. popup.setVisible(true);
  117. var calls = 0;
  118. goog.events.listenOnce(
  119. popup,
  120. [
  121. goog.ui.PopupBase.EventType.BEFORE_HIDE,
  122. goog.ui.PopupBase.EventType.HIDE
  123. ],
  124. function(e) {
  125. calls++;
  126. assertEquals(
  127. 'The key target should be the hide event target', 'targetDiv',
  128. e.target.id);
  129. });
  130. goog.testing.events.fireKeySequence(targetDiv, goog.events.KeyCodes.ESC);
  131. }
  132. function testAutoHide() {
  133. popup.setAutoHide(true);
  134. popup.setVisible(true);
  135. clock.tick(1000); // avoid bouncing
  136. goog.testing.events.fireClickSequence(targetDiv);
  137. assertFalse(popup.isVisible());
  138. }
  139. function testAutoHideCanBeDisabled() {
  140. popup.setAutoHide(false);
  141. popup.setVisible(true);
  142. clock.tick(1000); // avoid bouncing
  143. goog.testing.events.fireClickSequence(targetDiv);
  144. assertTrue(
  145. 'Should not be hidden if auto hide is disabled', popup.isVisible());
  146. }
  147. function testAutoHideEnabledByDefault() {
  148. assertTrue(popup.getAutoHide());
  149. }
  150. function testAutoHideWithPartners() {
  151. popup.setAutoHide(true);
  152. popup.setVisible(true);
  153. popup.addAutoHidePartner(targetDiv);
  154. popup.addAutoHidePartner(partnerDiv);
  155. clock.tick(1000); // avoid bouncing
  156. goog.testing.events.fireClickSequence(targetDiv);
  157. assertTrue(popup.isVisible());
  158. goog.testing.events.fireClickSequence(partnerDiv);
  159. assertTrue(popup.isVisible());
  160. popup.removeAutoHidePartner(partnerDiv);
  161. goog.testing.events.fireClickSequence(partnerDiv);
  162. assertFalse(popup.isVisible());
  163. }
  164. function testCanAddElementDuringBeforeShow() {
  165. popup.setElement(null);
  166. goog.events.listenOnce(
  167. popup, goog.ui.PopupBase.EventType.BEFORE_SHOW,
  168. function() { popup.setElement(popupDiv); });
  169. popup.setVisible(true);
  170. assertTrue('Popup should be shown', popup.isVisible());
  171. }
  172. function testShowWithNoElementThrowsException() {
  173. popup.setElement(null);
  174. var e = assertThrows(function() { popup.setVisible(true); });
  175. assertEquals(
  176. 'Caller must call setElement before trying to show the popup', e.message);
  177. }
  178. function testShowEventFiredWithNoTransition() {
  179. var showHandlerCalled = false;
  180. goog.events.listen(popup, goog.ui.PopupBase.EventType.SHOW, function() {
  181. showHandlerCalled = true;
  182. });
  183. popup.setVisible(true);
  184. assertTrue(showHandlerCalled);
  185. }
  186. function testHideEventFiredWithNoTransition() {
  187. var hideHandlerCalled = false;
  188. goog.events.listen(popup, goog.ui.PopupBase.EventType.HIDE, function() {
  189. hideHandlerCalled = true;
  190. });
  191. popup.setVisible(true);
  192. popup.setVisible(false);
  193. assertTrue(hideHandlerCalled);
  194. }
  195. function testOnShowTransition() {
  196. var mockTransition = new MockTransition();
  197. var showHandlerCalled = false;
  198. goog.events.listen(popup, goog.ui.PopupBase.EventType.SHOW, function() {
  199. showHandlerCalled = true;
  200. });
  201. popup.setTransition(mockTransition);
  202. popup.setVisible(true);
  203. assertTrue(mockTransition.wasPlayed);
  204. assertFalse(showHandlerCalled);
  205. mockTransition.dispatchEvent(goog.fx.Transition.EventType.END);
  206. assertTrue(showHandlerCalled);
  207. }
  208. function testOnHideTransition() {
  209. var mockTransition = new MockTransition();
  210. var hideHandlerCalled = false;
  211. goog.events.listen(popup, goog.ui.PopupBase.EventType.HIDE, function() {
  212. hideHandlerCalled = true;
  213. });
  214. popup.setTransition(undefined, mockTransition);
  215. popup.setVisible(true);
  216. assertFalse(mockTransition.wasPlayed);
  217. popup.setVisible(false);
  218. assertTrue(mockTransition.wasPlayed);
  219. assertFalse(hideHandlerCalled);
  220. mockTransition.dispatchEvent(goog.fx.Transition.EventType.END);
  221. assertTrue(hideHandlerCalled);
  222. }
  223. function testSetVisibleWorksCorrectlyWithTransitions() {
  224. popup.setTransition(
  225. goog.fx.css3.fadeIn(popup.getElement(), 1),
  226. goog.fx.css3.fadeOut(popup.getElement(), 1));
  227. // Consecutive calls to setVisible works without needing to wait for
  228. // transition to finish.
  229. popup.setVisible(true);
  230. assertTrue(popup.isVisible());
  231. popup.setVisible(false);
  232. assertFalse(popup.isVisible());
  233. clock.tick(1100);
  234. // Calling setVisible(true) immediately changed the state to visible.
  235. popup.setVisible(true);
  236. assertTrue(popup.isVisible());
  237. clock.tick(1100);
  238. // Consecutive calls to setVisible, in opposite order.
  239. popup.setVisible(false);
  240. popup.setVisible(true);
  241. assertTrue(popup.isVisible());
  242. clock.tick(1100);
  243. // Calling setVisible(false) immediately changed the state to not visible.
  244. popup.setVisible(false);
  245. assertFalse(popup.isVisible());
  246. clock.tick(1100);
  247. }
  248. function testWasRecentlyVisibleWorksCorrectlyWithTransitions() {
  249. popup.setTransition(
  250. goog.fx.css3.fadeIn(popup.getElement(), 1),
  251. goog.fx.css3.fadeOut(popup.getElement(), 1));
  252. popup.setVisible(true);
  253. clock.tick(1100);
  254. popup.setVisible(false);
  255. assertTrue(popup.isOrWasRecentlyVisible());
  256. clock.tick(goog.ui.PopupBase.DEBOUNCE_DELAY_MS);
  257. assertFalse(popup.isOrWasRecentlyVisible());
  258. }
  259. function testMoveOffscreenRTL() {
  260. document.body.setAttribute('dir', 'rtl');
  261. popup.reposition = function() {
  262. this.element_.style.left = '100px';
  263. this.element_.style.top = '100px';
  264. };
  265. popup.setType(goog.ui.PopupBase.Type.MOVE_OFFSCREEN);
  266. popup.setElement(goog.dom.getElement('moveOffscreenPopupDiv'));
  267. originalScrollWidth = goog.dom.getDocumentScrollElement().scrollWidth;
  268. popup.setVisible(true);
  269. popup.setVisible(false);
  270. assertFalse(
  271. 'Moving a popup offscreen should not cause scrollbars',
  272. goog.dom.getDocumentScrollElement().scrollWidth != originalScrollWidth);
  273. }
  274. function testOnDocumentBlurDisabledCrossIframeDismissalWithoutDelay() {
  275. popup.setEnableCrossIframeDismissal(false);
  276. popup.setVisible(true);
  277. var e = new goog.testing.events.Event(goog.events.EventType.BLUR, document);
  278. goog.testing.events.fireBrowserEvent(e);
  279. assertTrue('Popup should remain visible', popup.isVisible());
  280. }
  281. function testOnDocumentBlurDisabledCrossIframeDismissalWithDelay() {
  282. popup.setEnableCrossIframeDismissal(false);
  283. popup.setVisible(true);
  284. var e = new goog.testing.events.Event(goog.events.EventType.BLUR, document);
  285. clock.tick(goog.ui.PopupBase.DEBOUNCE_DELAY_MS);
  286. goog.testing.events.fireBrowserEvent(e);
  287. assertTrue('Popup should remain visible', popup.isVisible());
  288. }
  289. function testOnDocumentBlurActiveElementInsidePopupWithoutDelay() {
  290. popup.setVisible(true);
  291. var elementInsidePopup = goog.dom.createDom(goog.dom.TagName.DIV);
  292. goog.dom.append(popupDiv, elementInsidePopup);
  293. elementInsidePopup.setAttribute('tabIndex', 0);
  294. elementInsidePopup.focus();
  295. var e = new goog.testing.events.Event(goog.events.EventType.BLUR, document);
  296. goog.testing.events.fireBrowserEvent(e);
  297. assertTrue('Popup should remain visible', popup.isVisible());
  298. }
  299. function testOnDocumentBlurActiveElementInsidePopupWithDelay() {
  300. popup.setVisible(true);
  301. var elementInsidePopup = goog.dom.createDom(goog.dom.TagName.DIV);
  302. goog.dom.append(popupDiv, elementInsidePopup);
  303. elementInsidePopup.setAttribute('tabIndex', 0);
  304. elementInsidePopup.focus();
  305. var e = new goog.testing.events.Event(goog.events.EventType.BLUR, document);
  306. clock.tick(goog.ui.PopupBase.DEBOUNCE_DELAY_MS);
  307. goog.testing.events.fireBrowserEvent(e);
  308. assertTrue('Popup should remain visible', popup.isVisible());
  309. }
  310. function testOnDocumentBlurActiveElementIsBodyWithoutDelay() {
  311. popup.setVisible(true);
  312. var bodyElement =
  313. goog.dom.getDomHelper().getElementsByTagNameAndClass('body')[0];
  314. bodyElement.setAttribute('tabIndex', 0);
  315. bodyElement.focus();
  316. var e = new goog.testing.events.Event(goog.events.EventType.BLUR, document);
  317. goog.testing.events.fireBrowserEvent(e);
  318. assertTrue('Popup should remain visible', popup.isVisible());
  319. }
  320. function testOnDocumentBlurActiveElementIsBodyWithDelay() {
  321. popup.setVisible(true);
  322. var bodyElement =
  323. goog.dom.getDomHelper().getElementsByTagNameAndClass('body')[0];
  324. bodyElement.setAttribute('tabIndex', 0);
  325. bodyElement.focus();
  326. var e = new goog.testing.events.Event(goog.events.EventType.BLUR, document);
  327. clock.tick(goog.ui.PopupBase.DEBOUNCE_DELAY_MS);
  328. goog.testing.events.fireBrowserEvent(e);
  329. assertTrue('Popup should remain visible', popup.isVisible());
  330. }
  331. function testOnDocumentBlurEventTargetNotDocumentWithoutDelay() {
  332. popup.setVisible(true);
  333. var e = new goog.testing.events.Event(goog.events.EventType.BLUR, targetDiv);
  334. goog.testing.events.fireBrowserEvent(e);
  335. assertTrue('Popup should remain visible', popup.isVisible());
  336. }
  337. function testOnDocumentBlurEventTargetNotDocumentWithDelay() {
  338. popup.setVisible(true);
  339. var e = new goog.testing.events.Event(goog.events.EventType.BLUR, targetDiv);
  340. clock.tick(goog.ui.PopupBase.DEBOUNCE_DELAY_MS);
  341. goog.testing.events.fireBrowserEvent(e);
  342. assertTrue('Popup should remain visible', popup.isVisible());
  343. }
  344. function testOnDocumentBlurShouldDebounceWithoutDelay() {
  345. popup.setVisible(true);
  346. var commonAncestor = goog.dom.getElement('commonAncestor');
  347. var focusDiv = goog.dom.createDom(goog.dom.TagName.DIV, 'tabIndex');
  348. focusDiv.setAttribute('tabIndex', 0);
  349. goog.dom.appendChild(commonAncestor, focusDiv);
  350. focusDiv.focus();
  351. var e = new goog.testing.events.Event(goog.events.EventType.BLUR, document);
  352. goog.testing.events.fireBrowserEvent(e);
  353. assertTrue('Popup should be visible', popup.isVisible());
  354. goog.dom.removeNode(focusDiv);
  355. }
  356. function testOnDocumentBlurShouldNotDebounceWithDelay() {
  357. popup.setVisible(true);
  358. clock.tick(goog.ui.PopupBase.DEBOUNCE_DELAY_MS);
  359. var commonAncestor = goog.dom.getElement('commonAncestor');
  360. var focusDiv = goog.dom.createDom(goog.dom.TagName.DIV, 'tabIndex');
  361. focusDiv.setAttribute('tabIndex', 0);
  362. goog.dom.appendChild(commonAncestor, focusDiv);
  363. focusDiv.focus();
  364. var e = new goog.testing.events.Event(goog.events.EventType.BLUR, document);
  365. goog.testing.events.fireBrowserEvent(e);
  366. assertFalse('Popup should be invisible', popup.isVisible());
  367. goog.dom.removeNode(focusDiv);
  368. }
  369. function testOnDocumentBlurShouldNotHideBubbleWithoutDelay() {
  370. popup.setVisible(true);
  371. var commonAncestor = goog.dom.getElement('commonAncestor');
  372. var focusDiv = goog.dom.createDom(goog.dom.TagName.DIV, 'tabIndex');
  373. focusDiv.setAttribute('tabIndex', 0);
  374. goog.dom.appendChild(commonAncestor, focusDiv);
  375. focusDiv.focus();
  376. var e = new goog.testing.events.Event(goog.events.EventType.BLUR, document);
  377. goog.testing.events.fireBrowserEvent(e);
  378. assertTrue('Popup should be visible', popup.isVisible());
  379. goog.dom.removeNode(focusDiv);
  380. }
  381. function testOnDocumentBlurShouldHideBubbleWithDelay() {
  382. popup.setVisible(true);
  383. clock.tick(goog.ui.PopupBase.DEBOUNCE_DELAY_MS);
  384. var commonAncestor = goog.dom.getElement('commonAncestor');
  385. var focusDiv = goog.dom.createDom(goog.dom.TagName.DIV, 'tabIndex');
  386. focusDiv.setAttribute('tabIndex', 0);
  387. goog.dom.appendChild(commonAncestor, focusDiv);
  388. focusDiv.focus();
  389. var e = new goog.testing.events.Event(goog.events.EventType.BLUR, document);
  390. goog.testing.events.fireBrowserEvent(e);
  391. assertFalse('Popup should be invisible', popup.isVisible());
  392. goog.dom.removeNode(focusDiv);
  393. }
  394. /**
  395. * @implements {goog.fx.Transition}
  396. * @extends {goog.events.EventTarget}
  397. * @constructor
  398. */
  399. var MockTransition = function() {
  400. MockTransition.base(this, 'constructor');
  401. this.wasPlayed = false;
  402. };
  403. goog.inherits(MockTransition, goog.events.EventTarget);
  404. MockTransition.prototype.play = function() {
  405. this.wasPlayed = true;
  406. };
  407. MockTransition.prototype.stop = goog.nullFunction;
  408. // TODO(gboyer): Write better unit tests for click and cross-iframe dismissal.