pastehandler_test.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  1. // Copyright 2009 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.events.PasteHandlerTest');
  15. goog.setTestOnly('goog.events.PasteHandlerTest');
  16. goog.require('goog.dom');
  17. goog.require('goog.events');
  18. goog.require('goog.events.BrowserEvent');
  19. goog.require('goog.events.EventTarget');
  20. goog.require('goog.events.EventType');
  21. goog.require('goog.events.KeyCodes');
  22. goog.require('goog.events.PasteHandler');
  23. goog.require('goog.testing.MockClock');
  24. goog.require('goog.testing.jsunit');
  25. goog.require('goog.userAgent');
  26. function setUp() {
  27. textarea = new goog.events.EventTarget();
  28. textarea.value = '';
  29. clock = new goog.testing.MockClock(true);
  30. handler = new goog.events.PasteHandler(textarea);
  31. pasted = false;
  32. goog.events.listen(
  33. handler, goog.events.PasteHandler.EventType.PASTE,
  34. function() { pasted = true; });
  35. }
  36. function tearDown() {
  37. textarea.dispose();
  38. handler.dispose();
  39. clock.dispose();
  40. }
  41. function newBrowserEvent(type) {
  42. if (goog.isString(type)) {
  43. return new goog.events.BrowserEvent({type: type});
  44. } else {
  45. return new goog.events.BrowserEvent(type);
  46. }
  47. }
  48. function testDispatchingPasteEventSupportedByAFewBrowsersWork() {
  49. if (!goog.events.PasteHandler.SUPPORTS_NATIVE_PASTE_EVENT) {
  50. return;
  51. }
  52. var handlerThatSupportsPasteEvents = new goog.events.PasteHandler(textarea);
  53. // user clicks on the textarea and give it focus
  54. goog.events.listen(
  55. handlerThatSupportsPasteEvents, goog.events.PasteHandler.EventType.PASTE,
  56. function() { pasted = true; });
  57. textarea.dispatchEvent(newBrowserEvent('paste'));
  58. assertTrue(pasted);
  59. }
  60. function testJustTypingDoesntFirePasteEvent() {
  61. if (goog.events.PasteHandler.SUPPORTS_NATIVE_PASTE_EVENT) {
  62. return;
  63. }
  64. // user clicks on the textarea and give it focus
  65. textarea.dispatchEvent(newBrowserEvent(goog.events.EventType.FOCUS));
  66. assertFalse(pasted);
  67. // user starts typing
  68. textarea.dispatchEvent(newBrowserEvent({
  69. type: goog.events.EventType.KEYDOWN,
  70. keyCode: goog.events.KeyCodes.A
  71. }));
  72. textarea.value = 'a';
  73. assertFalse(pasted);
  74. // still typing
  75. textarea.dispatchEvent(
  76. {type: goog.events.EventType.KEYDOWN, keyCode: goog.events.KeyCodes.B});
  77. textarea.value = 'ab';
  78. assertFalse(pasted);
  79. // ends typing
  80. textarea.dispatchEvent(
  81. {type: goog.events.EventType.KEYDOWN, keyCode: goog.events.KeyCodes.C});
  82. textarea.value = 'abc';
  83. assertFalse(pasted);
  84. }
  85. function testStartsOnInitialState() {
  86. assertTrue(handler.getState() == goog.events.PasteHandler.State.INIT);
  87. assertFalse(pasted);
  88. }
  89. function testBlurOnInit() {
  90. if (goog.events.PasteHandler.SUPPORTS_NATIVE_PASTE_EVENT) {
  91. return;
  92. }
  93. textarea.dispatchEvent(goog.events.EventType.BLUR);
  94. assertTrue(handler.getState() == goog.events.PasteHandler.State.INIT);
  95. assertFalse(pasted);
  96. }
  97. function testFocusOnInit() {
  98. if (goog.events.PasteHandler.SUPPORTS_NATIVE_PASTE_EVENT) {
  99. return;
  100. }
  101. textarea.dispatchEvent(goog.events.EventType.FOCUS);
  102. assertTrue(handler.getState() == goog.events.PasteHandler.State.FOCUSED);
  103. assertFalse(pasted);
  104. }
  105. function testInputOnFocus() {
  106. if (goog.events.PasteHandler.SUPPORTS_NATIVE_PASTE_EVENT) {
  107. return;
  108. }
  109. // user clicks on the textarea
  110. textarea.dispatchEvent(newBrowserEvent(goog.events.EventType.FOCUS));
  111. clock.tick(
  112. goog.events.PasteHandler.MANDATORY_MS_BETWEEN_INPUT_EVENTS_TIE_BREAKER +
  113. 1);
  114. // and right click -> paste a text!
  115. textarea.dispatchEvent(newBrowserEvent('input'));
  116. assertTrue(handler.getState() == goog.events.PasteHandler.State.FOCUSED);
  117. // make sure we detected it
  118. assertTrue(pasted);
  119. }
  120. function testKeyPressOnFocus() {
  121. if (goog.events.PasteHandler.SUPPORTS_NATIVE_PASTE_EVENT) {
  122. return;
  123. }
  124. // user clicks on the textarea
  125. textarea.dispatchEvent(newBrowserEvent(goog.events.EventType.FOCUS));
  126. // starts typing something
  127. textarea.dispatchEvent(newBrowserEvent({
  128. type: goog.events.EventType.KEYDOWN,
  129. keyCode: goog.events.KeyCodes.A
  130. }));
  131. assertTrue(handler.getState() == goog.events.PasteHandler.State.TYPING);
  132. assertFalse(pasted);
  133. // and then presses ctrl+v
  134. textarea.dispatchEvent(newBrowserEvent({
  135. type: goog.events.EventType.KEYDOWN,
  136. keyCode: goog.events.KeyCodes.V,
  137. ctrlKey: true
  138. }));
  139. assertTrue(handler.getState() == goog.events.PasteHandler.State.TYPING);
  140. // makes sure we detected it
  141. assertTrue(pasted);
  142. }
  143. function testMouseOverOnInit() {
  144. if (goog.events.PasteHandler.SUPPORTS_NATIVE_PASTE_EVENT) {
  145. return;
  146. }
  147. // user has something on the events
  148. textarea.value = 'pasted string';
  149. // and right click -> paste it on the textarea, WITHOUT giving focus
  150. textarea.dispatchEvent(newBrowserEvent(goog.events.EventType.MOUSEOVER));
  151. assertTrue(handler.getState() == goog.events.PasteHandler.State.INIT);
  152. // makes sure we detect it
  153. assertTrue(pasted);
  154. pasted = false;
  155. // user normaly mouseovers the textarea, with no text change
  156. textarea.dispatchEvent(goog.events.EventType.MOUSEOVER);
  157. assertTrue(handler.getState() == goog.events.PasteHandler.State.INIT);
  158. // text area value doesn't change
  159. assertFalse(pasted);
  160. }
  161. function testMouseOverAfterTyping() {
  162. if (goog.events.PasteHandler.SUPPORTS_NATIVE_PASTE_EVENT) {
  163. return;
  164. }
  165. textarea.dispatchEvent(goog.events.EventType.FOCUS);
  166. assertFalse(pasted);
  167. textarea.dispatchEvent(
  168. {type: goog.events.EventType.KEYDOWN, keyCode: goog.events.KeyCodes.A});
  169. assertFalse(pasted);
  170. textarea.value = 'a';
  171. textarea.dispatchEvent('input');
  172. assertFalse(pasted);
  173. assertEquals('a', handler.oldValue_);
  174. textarea.dispatchEvent(goog.events.EventType.MOUSEOVER);
  175. assertFalse(pasted);
  176. }
  177. function testTypingAndThenRightClickPaste() {
  178. if (goog.events.PasteHandler.SUPPORTS_NATIVE_PASTE_EVENT) {
  179. return;
  180. }
  181. textarea.dispatchEvent(goog.events.EventType.FOCUS);
  182. textarea.dispatchEvent(
  183. {type: goog.events.EventType.KEYDOWN, keyCode: goog.events.KeyCodes.A});
  184. assertFalse(pasted);
  185. textarea.value = 'a';
  186. clock.tick(
  187. goog.events.PasteHandler.MANDATORY_MS_BETWEEN_INPUT_EVENTS_TIE_BREAKER +
  188. 1);
  189. textarea.dispatchEvent('input');
  190. assertFalse(pasted);
  191. assertEquals('a', handler.oldValue_);
  192. textarea.value = 'ab';
  193. clock.tick(
  194. goog.events.PasteHandler.MANDATORY_MS_BETWEEN_INPUT_EVENTS_TIE_BREAKER +
  195. 1);
  196. textarea.dispatchEvent(newBrowserEvent('input'));
  197. assertTrue(pasted);
  198. }
  199. function testTypingReallyFastDispatchesTwoInputEventsBeforeTheKeyDownEvent() {
  200. if (goog.events.PasteHandler.SUPPORTS_NATIVE_PASTE_EVENT) {
  201. return;
  202. }
  203. textarea.dispatchEvent(goog.events.EventType.FOCUS);
  204. // keydown and input events seems to be fired indepently: even though input
  205. // should happen after the key event, it doesn't if the user types fast
  206. // enough. FF2 + linux doesn't fire keydown events for every key pressed when
  207. // you type fast enough. if one of the keydown events gets swallowed, two
  208. // input events are fired consecutively. notice that there is a similar
  209. // scenario, that actually does produce a valid paste action.
  210. // {@see testRightClickRightClickAlsoDispatchesTwoConsecutiveInputEvents}
  211. textarea.dispatchEvent(
  212. {type: goog.events.EventType.KEYDOWN, keyCode: goog.events.KeyCodes.A});
  213. assertFalse(pasted);
  214. textarea.value = 'a';
  215. clock.tick(
  216. goog.events.PasteHandler.MANDATORY_MS_BETWEEN_INPUT_EVENTS_TIE_BREAKER -
  217. 1);
  218. textarea.dispatchEvent('input');
  219. assertFalse(pasted);
  220. // second key down events gets fired on a different order
  221. textarea.value = 'ab';
  222. clock.tick(
  223. goog.events.PasteHandler.MANDATORY_MS_BETWEEN_INPUT_EVENTS_TIE_BREAKER -
  224. 1);
  225. textarea.dispatchEvent('input');
  226. assertFalse(pasted);
  227. }
  228. function testRightClickRightClickAlsoDispatchesTwoConsecutiveInputEvents() {
  229. if (goog.events.PasteHandler.SUPPORTS_NATIVE_PASTE_EVENT) {
  230. return;
  231. }
  232. textarea.dispatchEvent(goog.events.EventType.FOCUS);
  233. // there is also another case that two consecutive INPUT events are fired,
  234. // but in a valid paste action: if the user edit -> paste -> edit -> paste,
  235. // it is a valid paste action.
  236. textarea.value = 'a';
  237. clock.tick(
  238. goog.events.PasteHandler.MANDATORY_MS_BETWEEN_INPUT_EVENTS_TIE_BREAKER +
  239. 1);
  240. textarea.dispatchEvent(newBrowserEvent('input'));
  241. assertTrue(pasted);
  242. // second key down events gets fired on a different order
  243. textarea.value = 'ab';
  244. clock.tick(
  245. goog.events.PasteHandler.MANDATORY_MS_BETWEEN_INPUT_EVENTS_TIE_BREAKER +
  246. 1);
  247. textarea.dispatchEvent(newBrowserEvent('input'));
  248. assertTrue(pasted);
  249. }
  250. function testMiddleClickWithoutFocusTriggersPasteEvent() {
  251. if (goog.events.PasteHandler.SUPPORTS_NATIVE_PASTE_EVENT) {
  252. return;
  253. }
  254. // if the textarea is NOT selected, and then we use the middle button,
  255. // FF2+linux pastes what was last highlighted, causing a paste action.
  256. textarea.dispatchEvent(goog.events.EventType.FOCUS);
  257. textarea.dispatchEvent(newBrowserEvent('input'));
  258. assertTrue(pasted);
  259. }
  260. function testMacRightClickPasteRequiresCtrlBecauseItHasOneButton() {
  261. if (!goog.userAgent.OPERA || !goog.userAgent.MAC) {
  262. return;
  263. }
  264. var handler = new goog.events.PasteHandler(textarea);
  265. // user clicks on the textarea and give it focus
  266. goog.events.listen(
  267. handler, goog.events.PasteHandler.EventType.PASTE,
  268. function() { pasted = true; });
  269. textarea.dispatchEvent(goog.events.EventType.FOCUS);
  270. assertFalse(pasted);
  271. textarea.dispatchEvent({type: goog.events.EventType.KEYDOWN, keyCode: 0});
  272. assertFalse(pasted);
  273. clock.tick(
  274. goog.events.PasteHandler.MANDATORY_MS_BETWEEN_INPUT_EVENTS_TIE_BREAKER +
  275. 1);
  276. textarea.dispatchEvent(newBrowserEvent('input'));
  277. assertTrue(pasted);
  278. }
  279. function testOperaMacFiresKeyCode17WhenAppleKeyPressedButDoesNotFireKeyDown() {
  280. if (!goog.userAgent.OPERA || !goog.userAgent.MAC) {
  281. return;
  282. }
  283. var handler = new goog.events.PasteHandler(textarea);
  284. // user clicks on the textarea and give it focus
  285. goog.events.listen(
  286. handler, goog.events.PasteHandler.EventType.PASTE,
  287. function() { pasted = true; });
  288. textarea.dispatchEvent(goog.events.EventType.FOCUS);
  289. assertFalse(pasted);
  290. // apple key is pressed, generating a keydown event
  291. textarea.dispatchEvent({type: goog.events.EventType.KEYDOWN, keyCode: 17});
  292. assertFalse(pasted);
  293. clock.tick(
  294. goog.events.PasteHandler.MANDATORY_MS_BETWEEN_INPUT_EVENTS_TIE_BREAKER +
  295. 1);
  296. // and then text is added magically without any extra keydown events.
  297. textarea.dispatchEvent(newBrowserEvent('input'));
  298. assertTrue(pasted);
  299. }
  300. function testScriptingDoesntTriggerPasteEvents() {
  301. var handlerUsedToListenForScriptingChanges =
  302. new goog.events.PasteHandler(textarea);
  303. pasted = false;
  304. // user clicks on the textarea and give it focus
  305. goog.events.listen(
  306. handlerUsedToListenForScriptingChanges,
  307. goog.events.PasteHandler.EventType.PASTE, function() { pasted = true; });
  308. goog.dom.getElement('foo').value = 'dear paste handler,';
  309. assertFalse(pasted);
  310. goog.dom.getElement('foo').value = 'please dont misunderstand script changes';
  311. assertFalse(pasted);
  312. goog.dom.getElement('foo').value = 'with user generated paste events';
  313. assertFalse(pasted);
  314. goog.dom.getElement('foo').value = 'thanks!';
  315. assertFalse(pasted);
  316. }
  317. function testAfterPaste() {
  318. if (!goog.events.PasteHandler.SUPPORTS_NATIVE_PASTE_EVENT) {
  319. return;
  320. }
  321. var handlerThatSupportsPasteEvents = new goog.events.PasteHandler(textarea);
  322. pasted = false;
  323. goog.events.listen(
  324. handlerThatSupportsPasteEvents, goog.events.PasteHandler.EventType.PASTE,
  325. function() { pasted = true; });
  326. var afterPasteFired = false;
  327. goog.events.listen(
  328. handlerThatSupportsPasteEvents,
  329. goog.events.PasteHandler.EventType.AFTER_PASTE,
  330. function() { afterPasteFired = true; });
  331. // Initial paste event comes before AFTER_PASTE has fired.
  332. textarea.dispatchEvent(newBrowserEvent('paste'));
  333. assertTrue(pasted);
  334. assertFalse(afterPasteFired);
  335. // Once text is pasted, it takes a bit to detect it, at which point
  336. // AFTER_PASTE is fired.
  337. clock.tick(goog.events.PasteHandler.PASTE_POLLING_PERIOD_MS_);
  338. textarea.value = 'text';
  339. clock.tick(goog.events.PasteHandler.PASTE_POLLING_PERIOD_MS_);
  340. assertTrue(afterPasteFired);
  341. }
  342. function testAfterPasteNotFiredIfDelayTooLong() {
  343. if (!goog.events.PasteHandler.SUPPORTS_NATIVE_PASTE_EVENT) {
  344. return;
  345. }
  346. var handlerThatSupportsPasteEvents = new goog.events.PasteHandler(textarea);
  347. pasted = false;
  348. goog.events.listen(
  349. handlerThatSupportsPasteEvents, goog.events.PasteHandler.EventType.PASTE,
  350. function() { pasted = true; });
  351. var afterPasteFired = false;
  352. goog.events.listen(
  353. handlerThatSupportsPasteEvents,
  354. goog.events.PasteHandler.EventType.AFTER_PASTE,
  355. function() { afterPasteFired = true; });
  356. // Initial paste event comes before AFTER_PASTE has fired.
  357. textarea.dispatchEvent(newBrowserEvent('paste'));
  358. assertTrue(pasted);
  359. assertFalse(afterPasteFired);
  360. // If the new text doesn't show up in time, we never fire AFTER_PASTE.
  361. clock.tick(goog.events.PasteHandler.PASTE_POLLING_TIMEOUT_MS_);
  362. textarea.value = 'text';
  363. clock.tick(goog.events.PasteHandler.PASTE_POLLING_PERIOD_MS_);
  364. assertFalse(afterPasteFired);
  365. }