View Javadoc
1   /*
2    * Copyright (c) 2002-2025 Gargoyle Software Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * https://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   */
15  package org.htmlunit.javascript.host.event;
16  
17  import org.htmlunit.WebDriverTestCase;
18  import org.htmlunit.junit.BrowserRunner;
19  import org.htmlunit.junit.annotation.Alerts;
20  import org.htmlunit.junit.annotation.HtmlUnitNYI;
21  import org.junit.Test;
22  import org.junit.runner.RunWith;
23  import org.openqa.selenium.By;
24  import org.openqa.selenium.WebDriver;
25  import org.openqa.selenium.WebElement;
26  
27  /**
28   * Tests for mouse events support.
29   *
30   * @author Marc Guillemot
31   * @author Ahmed Ashour
32   * @author Frank Danek
33   * @author Ronald Brill
34   */
35  @RunWith(BrowserRunner.class)
36  public class MouseEventTest extends WebDriverTestCase {
37  
38      private static final String DUMP_EVENT_FUNCTION = "  function dump(event) {\n"
39              + "    log(event);\n"
40              + "    log(event.type);\n"
41              + "    log(event.bubbles);\n"
42              + "    log(event.cancelable);\n"
43              + "    log(event.composed);\n"
44  
45              + "    log(event.view == window);\n"
46              + "    log(event.screenX);\n"
47              + "    log(event.screenY);\n"
48              + "    log(event.clientX);\n"
49              + "    log(event.clientY);\n"
50              + "    log(event.ctrlKey);\n"
51              + "    log(event.altKey);\n"
52              + "    log(event.shiftKey);\n"
53              + "    log(event.metaKey);\n"
54              + "    log(event.button);\n"
55              + "    log(event.buttons);\n"
56              + "    log(event.which);\n"
57              + "  }\n";
58  
59      /**
60       * @throws Exception if the test fails
61       */
62      @Test
63      @Alerts({"[object MouseEvent]", "click", "false", "false", "false", "false",
64               "0", "0", "0", "0", "false", "false", "false", "false", "0", "0", "1"})
65      public void create_ctor() throws Exception {
66          final String html = DOCTYPE_HTML
67              + "<html><head><script>\n"
68              + LOG_TITLE_FUNCTION
69              + "  function test() {\n"
70              + "    try {\n"
71              + "      var event = new MouseEvent('click');\n"
72              + "      dump(event);\n"
73              + "    } catch(e) { logEx(e) }\n"
74              + "  }\n"
75              + DUMP_EVENT_FUNCTION
76              + "</script></head><body onload='test()'>\n"
77              + "</body></html>";
78  
79          loadPageVerifyTitle2(html);
80      }
81  
82      /**
83       * @throws Exception if the test fails
84       */
85      @Test
86      @Alerts("TypeError")
87      @HtmlUnitNYI(CHROME = {"[object MouseEvent]", "undefined", "false", "false", "false", "false",
88                             "0", "0", "0", "0", "false", "false", "false", "false", "0", "0", "1"},
89                  EDGE = {"[object MouseEvent]", "undefined", "false", "false", "false", "false",
90                          "0", "0", "0", "0", "false", "false", "false", "false", "0", "0", "1"},
91                  FF = {"[object MouseEvent]", "undefined", "false", "false", "false", "false",
92                        "0", "0", "0", "0", "false", "false", "false", "false", "0", "0", "1"},
93                  FF_ESR = {"[object MouseEvent]", "undefined", "false", "false", "false", "false",
94                            "0", "0", "0", "0", "false", "false", "false", "false", "0", "0", "1"})
95      public void create_ctorWithoutType() throws Exception {
96          final String html = DOCTYPE_HTML
97              + "<html><head><script>\n"
98              + LOG_TITLE_FUNCTION
99              + "  function test() {\n"
100             + "    try {\n"
101             + "      var event = new MouseEvent();\n"
102             + "      dump(event);\n"
103             + "    } catch(e) { logEx(e) }\n"
104             + "  }\n"
105             + DUMP_EVENT_FUNCTION
106             + "</script></head><body onload='test()'>\n"
107             + "</body></html>";
108 
109         loadPageVerifyTitle2(html);
110     }
111 
112     /**
113      * @throws Exception if the test fails
114      */
115     @Test
116     @Alerts({"[object MouseEvent]", "42", "false", "false", "false", "false",
117              "0", "0", "0", "0", "false", "false", "false", "false", "0", "0", "1"})
118     public void create_ctorNumericType() throws Exception {
119         final String html = DOCTYPE_HTML
120             + "<html><head><script>\n"
121             + LOG_TITLE_FUNCTION
122             + "  function test() {\n"
123             + "    try {\n"
124             + "      var event = new MouseEvent(42);\n"
125             + "      dump(event);\n"
126             + "    } catch(e) { logEx(e) }\n"
127             + "  }\n"
128             + DUMP_EVENT_FUNCTION
129             + "</script></head><body onload='test()'>\n"
130             + "</body></html>";
131 
132         loadPageVerifyTitle2(html);
133     }
134 
135     /**
136      * @throws Exception if the test fails
137      */
138     @Test
139     @Alerts({"[object MouseEvent]", "null", "false", "false", "false", "false",
140              "0", "0", "0", "0", "false", "false", "false", "false", "0", "0", "1"})
141     public void create_ctorNullType() throws Exception {
142         final String html = DOCTYPE_HTML
143             + "<html><head><script>\n"
144             + LOG_TITLE_FUNCTION
145             + "  function test() {\n"
146             + "    try {\n"
147             + "      var event = new MouseEvent(null);\n"
148             + "      dump(event);\n"
149             + "    } catch(e) { logEx(e) }\n"
150             + "  }\n"
151             + DUMP_EVENT_FUNCTION
152             + "</script></head><body onload='test()'>\n"
153             + "</body></html>";
154 
155         loadPageVerifyTitle2(html);
156     }
157 
158     /**
159      * @throws Exception if the test fails
160      */
161     @Test
162     @Alerts("ReferenceError")
163     public void create_ctorUnknownType() throws Exception {
164         final String html = DOCTYPE_HTML
165             + "<html><head><script>\n"
166             + LOG_TITLE_FUNCTION
167             + "  function test() {\n"
168             + "    try {\n"
169             + "      var event = new MouseEvent(unknown);\n"
170             + "      dump(event);\n"
171             + "    } catch(e) { logEx(e) }\n"
172             + "  }\n"
173             + DUMP_EVENT_FUNCTION
174             + "</script></head><body onload='test()'>\n"
175             + "</body></html>";
176 
177         loadPageVerifyTitle2(html);
178     }
179 
180     /**
181      * @throws Exception if the test fails
182      */
183     @Test
184     @Alerts({"[object MouseEvent]", "HtmlUnitEvent", "false", "false", "false", "false",
185              "0", "0", "0", "0", "false", "false", "false", "false", "0", "0", "1"})
186     public void create_ctorArbitraryType() throws Exception {
187         final String html = DOCTYPE_HTML
188             + "<html><head><script>\n"
189             + LOG_TITLE_FUNCTION
190             + "  function test() {\n"
191             + "    try {\n"
192             + "      var event = new MouseEvent('HtmlUnitEvent');\n"
193             + "      dump(event);\n"
194             + "    } catch(e) { logEx(e) }\n"
195             + "  }\n"
196             + DUMP_EVENT_FUNCTION
197             + "</script></head><body onload='test()'>\n"
198             + "</body></html>";
199 
200         loadPageVerifyTitle2(html);
201     }
202 
203     /**
204      * @throws Exception if the test fails
205      */
206     @Test
207     @Alerts({"[object MouseEvent]", "click", "false", "false", "false", "false",
208              "7", "0", "13", "-15", "true", "true", "true", "true", "2", "4", "3"})
209     public void create_ctorAllDetails() throws Exception {
210         final String html = DOCTYPE_HTML
211             + "<html><head><script>\n"
212             + LOG_TITLE_FUNCTION
213             + "  function test() {\n"
214             + "    try {\n"
215             + "      var event = new MouseEvent('click', {\n"
216             + "        'screenX': 7,\n"
217             + "        'screenY': 0.4,\n"
218             + "        'clientX': 13.007,\n"
219             + "        'clientY': -15,\n"
220 
221             + "        'ctrlKey': true,\n"
222             + "        'shiftKey': 'true',\n"
223             + "        'altKey': 1,\n"
224             + "        'metaKey': {},\n"
225 
226             + "        'button': 2,\n"
227             + "        'buttons': 4\n"
228             + "      });\n"
229             + "      dump(event);\n"
230             + "    } catch(e) { logEx(e) }\n"
231             + "  }\n"
232             + DUMP_EVENT_FUNCTION
233             + "</script></head><body onload='test()'>\n"
234             + "</body></html>";
235 
236         loadPageVerifyTitle2(html);
237     }
238 
239     /**
240      * @throws Exception if the test fails
241      */
242     @Test
243     @Alerts({"DOM2: [object MouseEvent]", "DOM3: [object MouseEvent]"})
244     public void createEvent() throws Exception {
245         final String html = DOCTYPE_HTML
246             + "<html><head><script>\n"
247             + LOG_TITLE_FUNCTION
248             + "  function test() {\n"
249             + "    try {\n"
250             + "      log('DOM2: ' + document.createEvent('MouseEvents'));\n"
251             + "    } catch(e) {log('DOM2: exception')}\n"
252             + "    try {\n"
253             + "      log('DOM3: ' + document.createEvent('MouseEvent'));\n"
254             + "    } catch(e) {log('DOM3: exception')}\n"
255             + "  }\n"
256             + "</script></head><body onload='test()'>\n"
257             + "</body></html>";
258         loadPageVerifyTitle2(html);
259     }
260 
261     /**
262      * @throws Exception if an error occurs
263      */
264     @Test
265     @Alerts({"click", "true", "true", "true", "1", "2", "3", "4", "true", "true", "true", "true"})
266     public void initMouseEvent() throws Exception {
267         final String html = DOCTYPE_HTML
268             + "<html><body><script>\n"
269             + LOG_TITLE_FUNCTION
270             + "  var e = document.createEvent('MouseEvents');\n"
271             + "  e.initMouseEvent('click', true, true, window, 0, 1, 2, 3, 4, true, true, true, true, 0, null);\n"
272             + "  log(e.type);\n"
273             + "  log(e.bubbles);\n"
274             + "  log(e.cancelable);\n"
275             + "  log(e.view == window);\n"
276             + "  log(e.screenX);\n"
277             + "  log(e.screenY);\n"
278             + "  log(e.clientX);\n"
279             + "  log(e.clientY);\n"
280             + "  log(e.ctrlKey);\n"
281             + "  log(e.altKey);\n"
282             + "  log(e.shiftKey);\n"
283             + "  log(e.metaKey);\n"
284             + "</script></body></html>";
285 
286         loadPageVerifyTitle2(html);
287     }
288 
289     /**
290      * @throws Exception if the test fails
291      */
292     @Test
293     @Alerts({"1", "1"})
294     public void dispatchEvent() throws Exception {
295         final String html = DOCTYPE_HTML
296             + "<html><head><script>\n"
297             + LOG_TITLE_FUNCTION
298             + "  var clickCount = 0;\n"
299             + "  var dblClickCount = 0;\n"
300             + "  function test() {\n"
301             + "    var div = document.getElementById('myDiv');\n"
302             + "    div.onclick = clickHandler;\n"
303             + "    div.ondblclick = dblClickHandler;\n"
304             + "    if (div.fireEvent) {\n"
305             + "      var clickEvent = (evt = document.createEventObject(), evt.type = 'click', evt);\n"
306             + "      div.fireEvent('onclick', clickEvent);\n"
307             + "      var dblclickEvent = (evt_0 = document.createEventObject(), evt_0.type = 'dblclick', evt_0);\n"
308             + "      div.fireEvent('ondblclick', dblclickEvent);\n"
309             + "    }\n"
310             + "    else {\n"
311             + "      var clickEvent = $createMouseEvent(document, 'click', true, true, 0, 0, 0, 0, 0,"
312             + " false, false, false, false, 1, null);\n"
313             + "      div.dispatchEvent(clickEvent);\n"
314             + "      var dblclickEvent = $createMouseEvent(document, 'dblclick', true, true, 0, 0, 0, 0, 0,"
315             + " false, false, false, false, 1, null);\n"
316             + "      div.dispatchEvent(dblclickEvent);\n"
317             + "    }\n"
318             + "    log(clickCount);\n"
319             + "    log(dblClickCount);\n"
320             + "  }\n"
321             + "  function clickHandler() {\n"
322             + "    clickCount++;\n"
323             + "  }\n"
324             + "  function dblClickHandler() {\n"
325             + "    dblClickCount++;\n"
326             + "  }\n"
327             + "  function $createMouseEvent(doc, type, canBubble, cancelable, detail, screenX, screenY, clientX,"
328             + " clientY, ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget) {\n"
329             + "    button == 1?(button = 0):button == 4?(button = 1):(button = 2);\n"
330             + "    var evt = doc.createEvent('MouseEvents');\n"
331             + "    evt.initMouseEvent(type, canBubble, cancelable, null, detail, screenX, screenY, clientX,"
332             + " clientY, ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget);\n"
333             + "    return evt;\n"
334             + "  }\n"
335             + "</script></head><body onload='test()'>\n"
336             + "  <div id='myDiv'></div>\n"
337             + "</body></html>";
338 
339         loadPageVerifyTitle2(html);
340     }
341 
342     /**
343      * @throws Exception if an error occurs
344      */
345     @Test
346     @Alerts("0")
347     public void button_onclick() throws Exception {
348         final String html = DOCTYPE_HTML
349             + "<html><body>\n"
350             + "<p id='clicker'>Click me!</p>\n"
351             + "<script>\n"
352             + LOG_TITLE_FUNCTION
353             + "  function handler(event) {\n"
354             + "    log(event.button);\n"
355             + "  }\n"
356             + "  var p = document.getElementById('clicker');\n"
357             + "  if (p.addEventListener ) {\n"
358             + "    p.addEventListener('click', handler, false);\n"
359             + "  } else if (p.attachEvent) {\n"
360             + "    p.attachEvent('onclick', handler);\n"
361             + "  }\n"
362             + "</script></body></html>";
363 
364         final WebDriver driver = loadPage2(html);
365         driver.findElement(By.id("clicker")).click();
366 
367         verifyTitle2(driver, getExpectedAlerts());
368     }
369 
370     /**
371      * @throws Exception if an error occurs
372      */
373     @Test
374     @Alerts("0")
375     public void button_onmousedown() throws Exception {
376         final String html = DOCTYPE_HTML
377             + "<html><body>\n"
378             + "<p id='clicker'>Click me!</p>\n"
379             + "<script>\n"
380             + LOG_TITLE_FUNCTION
381             + "  function handler(event) {\n"
382             + "    log(event.button);\n"
383             + "  }\n"
384             + "  var p = document.getElementById('clicker');\n"
385             + "  if (p.addEventListener ) {\n"
386             + "    p.addEventListener('mousedown', handler, false);\n"
387             + "  } else if (p.attachEvent) {\n"
388             + "    p.attachEvent('onmousedown', handler);\n"
389             + "  }\n"
390             + "</script></body></html>";
391 
392         final WebDriver driver = loadPage2(html);
393         driver.findElement(By.id("clicker")).click();
394 
395         verifyTitle2(driver, getExpectedAlerts());
396     }
397 
398     /**
399      * @throws Exception if the test fails
400      */
401     @Test
402     @Alerts("Click on DIV(id=div1): true, true, false, false")
403     public void eventCoordinates_div() throws Exception {
404         eventCoordinates("div1");
405     }
406 
407     /**
408      * @throws Exception if the test fails
409      */
410     @Test
411     @Alerts("Click on SPAN(id=span1): true, true, true, false")
412     public void eventCoordinates_spanInsideDiv() throws Exception {
413         eventCoordinates("span1");
414     }
415 
416     /**
417      * @throws Exception if the test fails
418      */
419     @Test
420     @Alerts("Click on SPAN(id=span2): true, false, false, true")
421     public void eventCoordinates_span() throws Exception {
422         eventCoordinates("span2");
423     }
424 
425     /**
426      * @throws Exception if the test fails
427      */
428     @Test
429     @Alerts("Click on TEXTAREA(id=myTextarea): true, false, false, false")
430     public void eventCoordinates_textarea() throws Exception {
431         eventCoordinates("myTextarea");
432     }
433 
434     private void eventCoordinates(final String id) throws Exception {
435         final String html = getFileContent("event_coordinates.html");
436 
437         final String[] expected = getExpectedAlerts();
438 
439         setExpectedAlerts();
440         final WebDriver driver = loadPageVerifyTitle2(html);
441 
442         final WebElement textarea = driver.findElement(By.id("myTextarea"));
443         assertEquals("", textarea.getText());
444 
445         driver.findElement(By.id(id)).click();
446         assertEquals(expected[0], textarea.getDomProperty("value").trim());
447     }
448 
449     /**
450      * @throws Exception if an error occurs
451      */
452     @Test
453     @Alerts({"0", "0"})
454     public void pageX() throws Exception {
455         final String html = DOCTYPE_HTML
456             + "<html><body><script>\n"
457             + LOG_TITLE_FUNCTION
458             + "  var e = document.createEvent('MouseEvents');\n"
459             + "  log(e.pageX);\n"
460             + "  log(e.pageY);\n"
461             + "</script></body></html>";
462 
463         loadPageVerifyTitle2(html);
464     }
465 }