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