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.selenium;
16  
17  import org.htmlunit.junit.annotation.Alerts;
18  import org.htmlunit.junit.annotation.HtmlUnitNYI;
19  import org.junit.jupiter.api.Test;
20  import org.openqa.selenium.By;
21  import org.openqa.selenium.Keys;
22  import org.openqa.selenium.WebDriver;
23  import org.openqa.selenium.WebElement;
24  
25  /**
26   * Modified from
27   * <a href="https://github.com/SeleniumHQ/selenium/blob/master/java/client/test/org/openqa/selenium/TypingTest.java">
28   * TypingTest.java</a>.
29   *
30   * @author Ahmed Ashour
31   * @author Ronald Brill
32   */
33  public class TypingTest extends SeleniumTest {
34  
35      /**
36       * @throws Exception if an error occurs
37       */
38      @Test
39      public void shouldBeAbleToUseArrowKeys() throws Exception {
40          final WebDriver driver = getWebDriver("/javascriptPage.html");
41  
42          final WebElement keyReporter = driver.findElement(By.id("keyReporter"));
43          keyReporter.sendKeys("tet", Keys.ARROW_LEFT, "s");
44  
45          assertEquals("test", keyReporter.getAttribute("value"));
46          assertNull(keyReporter.getDomAttribute("value"));
47          assertEquals("test", keyReporter.getDomProperty("value"));
48      }
49  
50      /**
51       * A test.
52       */
53      @Test
54      @Alerts({"down: 40 up: 40", "down: 38 up: 38", "down: 37 up: 37", "down: 39 up: 39"})
55      @HtmlUnitNYI(FF = {"down: 40 press: 40 up: 40", "down: 38 press: 38 up: 38",
56                         "down: 37 press: 37 up: 37", "down: 39 press: 39 up: 39"},
57              FF_ESR = {"down: 40 press: 40 up: 40", "down: 38 press: 38 up: 38",
58                        "down: 37 press: 37 up: 37", "down: 39 press: 39 up: 39"})
59      public void shouldReportKeyCodeOfArrowKeys() {
60          final WebDriver driver = getWebDriver("/javascriptPage.html");
61  
62          final WebElement result = driver.findElement(By.id("result"));
63          final WebElement element = driver.findElement(By.id("keyReporter"));
64  
65          element.sendKeys(Keys.ARROW_DOWN);
66          assertEquals(getExpectedAlerts()[0], result.getText().trim());
67  
68          element.sendKeys(Keys.ARROW_UP);
69          assertEquals(getExpectedAlerts()[1], result.getText().trim());
70  
71          element.sendKeys(Keys.ARROW_LEFT);
72          assertEquals(getExpectedAlerts()[2], result.getText().trim());
73  
74          element.sendKeys(Keys.ARROW_RIGHT);
75          assertEquals(getExpectedAlerts()[3], result.getText().trim());
76  
77          // And leave no rubbish/printable keys in the "keyReporter"
78          assertEquals("", element.getAttribute("value"));
79          assertNull(element.getDomAttribute("value"));
80          assertEquals("", element.getDomProperty("value"));
81      }
82  
83      /**
84       * A test.
85       */
86      @Test
87      public void shouldReportKeyCodeOfArrowKeysUpDownEvents() {
88          final WebDriver driver = getWebDriver("/javascriptPage.html");
89  
90          final WebElement result = driver.findElement(By.id("result"));
91          final WebElement element = driver.findElement(By.id("keyReporter"));
92  
93          element.sendKeys(Keys.ARROW_DOWN);
94          assertTrue(result.getText().trim().contains("down: 40"));
95          assertTrue(result.getText().trim().contains("up: 40"));
96  
97          element.sendKeys(Keys.ARROW_UP);
98          assertTrue(result.getText().trim().contains("down: 38"));
99          assertTrue(result.getText().trim().contains("up: 38"));
100 
101         element.sendKeys(Keys.ARROW_LEFT);
102         assertTrue(result.getText().trim().contains("down: 37"));
103         assertTrue(result.getText().trim().contains("up: 37"));
104 
105         element.sendKeys(Keys.ARROW_RIGHT);
106         assertTrue(result.getText().trim().contains("down: 39"));
107         assertTrue(result.getText().trim().contains("up: 39"));
108 
109         // And leave no rubbish/printable keys in the "keyReporter"
110         assertEquals("", element.getAttribute("value"));
111         assertNull(element.getDomAttribute("value"));
112         assertEquals("", element.getDomProperty("value"));
113     }
114 
115     /**
116      * A test.
117      */
118     @Test
119     public void numericShiftKeys() {
120         final WebDriver driver = getWebDriver("/javascriptPage.html");
121 
122         final WebElement result = driver.findElement(By.id("result"));
123         final WebElement element = driver.findElement(By.id("keyReporter"));
124 
125         final String numericShiftsEtc = "~!@#$%^&*()_+{}:\"<>?|END~";
126         element.sendKeys(numericShiftsEtc);
127 
128         assertEquals(numericShiftsEtc, element.getAttribute("value"));
129         assertNull(element.getDomAttribute("value"));
130         assertEquals(numericShiftsEtc, element.getDomProperty("value"));
131 
132         assertTrue(result.getText(), result.getText().trim().contains(" up: 16"));
133     }
134 
135     /**
136      * A test.
137      */
138     @Test
139     public void uppercaseAlphaKeys() {
140         final WebDriver driver = getWebDriver("/javascriptPage.html");
141 
142         final WebElement result = driver.findElement(By.id("result"));
143         final WebElement element = driver.findElement(By.id("keyReporter"));
144 
145         final String upperAlphas = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
146         element.sendKeys(upperAlphas);
147 
148         assertEquals(upperAlphas, element.getAttribute("value"));
149         assertNull(element.getDomAttribute("value"));
150         assertEquals(upperAlphas, element.getDomProperty("value"));
151 
152         assertTrue(result.getText(), result.getText().trim().contains(" up: 16"));
153     }
154 
155     /**
156      * A test.
157      */
158     @Test
159     public void allPrintableKeys() {
160         final WebDriver driver = getWebDriver("/javascriptPage.html");
161 
162         final WebElement result = driver.findElement(By.id("result"));
163         final WebElement element = driver.findElement(By.id("keyReporter"));
164 
165         final String allPrintable =
166                 "!\"#$%&'()*+,-./0123456789:;<=>?@ ABCDEFGHIJKLMNO"
167                 + "PQRSTUVWXYZ [\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
168         element.sendKeys(allPrintable);
169 
170         assertEquals(allPrintable, element.getAttribute("value"));
171         assertNull(element.getDomAttribute("value"));
172         assertEquals(allPrintable, element.getDomProperty("value"));
173 
174         assertTrue(result.getText(), result.getText().trim().contains(" up: 16"));
175     }
176 
177     /**
178      * A test.
179      */
180     @Test
181     public void testArrowKeysAndPageUpAndDown() {
182         final WebDriver driver = getWebDriver("/javascriptPage.html");
183 
184         final WebElement element = driver.findElement(By.id("keyReporter"));
185 
186         element.sendKeys("a" + Keys.LEFT + "b" + Keys.RIGHT
187                 + Keys.UP + Keys.DOWN + Keys.PAGE_UP + Keys.PAGE_DOWN + "1");
188 
189         assertEquals("ba1", element.getAttribute("value"));
190         assertNull(element.getDomAttribute("value"));
191         assertEquals("ba1", element.getDomProperty("value"));
192     }
193 
194     /**
195      * A test.
196      */
197     @Test
198     public void homeAndEndAndPageUpAndPageDownKeys() {
199         final WebDriver driver = getWebDriver("/javascriptPage.html");
200 
201         final WebElement element = driver.findElement(By.id("keyReporter"));
202 
203         element.sendKeys("abc" + Keys.HOME + "0" + Keys.LEFT + Keys.RIGHT
204                 + Keys.PAGE_UP + Keys.PAGE_DOWN + Keys.END + "1" + Keys.HOME
205                 + "0" + Keys.PAGE_UP + Keys.END + "111" + Keys.HOME + "00");
206 
207         assertEquals("0000abc1111", element.getAttribute("value"));
208         assertNull(element.getDomAttribute("value"));
209         assertEquals("0000abc1111", element.getDomProperty("value"));
210     }
211 
212     /**
213      * A test.
214      */
215     @Test
216     public void deleteAndBackspaceKeys() {
217         final WebDriver driver = getWebDriver("/javascriptPage.html");
218 
219         final WebElement element = driver.findElement(By.id("keyReporter"));
220 
221         element.sendKeys("abcdefghi");
222         assertEquals("abcdefghi", element.getAttribute("value"));
223         assertNull(element.getDomAttribute("value"));
224         assertEquals("abcdefghi", element.getDomProperty("value"));
225 
226         element.sendKeys(Keys.LEFT, Keys.LEFT, Keys.DELETE);
227         assertEquals("abcdefgi", element.getAttribute("value"));
228         assertNull(element.getDomAttribute("value"));
229         assertEquals("abcdefgi", element.getDomProperty("value"));
230 
231         element.sendKeys(Keys.LEFT, Keys.LEFT, Keys.BACK_SPACE);
232         assertEquals("abcdfgi", element.getAttribute("value"));
233         assertNull(element.getDomAttribute("value"));
234         assertEquals("abcdfgi", element.getDomProperty("value"));
235     }
236 
237     /**
238      * A test.
239      */
240     @Test
241     public void specialSpaceKeys() {
242         final WebDriver driver = getWebDriver("/javascriptPage.html");
243 
244         final WebElement element = driver.findElement(By.id("keyReporter"));
245 
246         element.sendKeys("abcd" + Keys.SPACE + "fgh" + Keys.SPACE + "ij");
247         assertEquals("abcd fgh ij", element.getAttribute("value"));
248         assertNull(element.getDomAttribute("value"));
249         assertEquals("abcd fgh ij", element.getDomProperty("value"));
250     }
251 
252     /**
253      * A test.
254      */
255     @Test
256     public void numberpadKeys() {
257         final WebDriver driver = getWebDriver("/javascriptPage.html");
258 
259         final WebElement element = driver.findElement(By.id("keyReporter"));
260 
261         element.sendKeys("abcd" + Keys.MULTIPLY + Keys.SUBTRACT + Keys.ADD
262                 + Keys.DECIMAL + Keys.SEPARATOR + Keys.NUMPAD0 + Keys.NUMPAD9
263                 + Keys.ADD + Keys.SEMICOLON + Keys.EQUALS + Keys.DIVIDE
264                 + Keys.NUMPAD3 + "abcd");
265         assertEquals("abcd*-+.,09+;=/3abcd", element.getAttribute("value"));
266         assertNull(element.getDomAttribute("value"));
267         assertEquals("abcd*-+.,09+;=/3abcd", element.getDomProperty("value"));
268     }
269 
270     /**
271      * A test.
272      */
273     @Test
274     public void shiftSelectionDeletes() {
275         final WebDriver driver = getWebDriver("/javascriptPage.html");
276 
277         final WebElement element = driver.findElement(By.id("keyReporter"));
278 
279         element.sendKeys("abcd efgh");
280         assertEquals("abcd efgh", element.getAttribute("value"));
281         assertNull(element.getDomAttribute("value"));
282         assertEquals("abcd efgh", element.getDomProperty("value"));
283 
284         element.sendKeys(Keys.SHIFT, Keys.LEFT, Keys.LEFT, Keys.LEFT);
285         element.sendKeys(Keys.DELETE);
286         assertEquals("abcd e", element.getAttribute("value"));
287         assertNull(element.getDomAttribute("value"));
288         assertEquals("abcd e", element.getDomProperty("value"));
289     }
290 
291     /**
292      * A test.
293      */
294     @Test
295     public void chordControlHomeShiftEndDelete() {
296         final WebDriver driver = getWebDriver("/javascriptPage.html");
297 
298         final WebElement result = driver.findElement(By.id("result"));
299         final WebElement element = driver.findElement(By.id("keyReporter"));
300 
301         element.sendKeys("!\"#$%&'()*+,-./0123456789:;<=>?@ ABCDEFG");
302 
303         element.sendKeys(Keys.HOME);
304         element.sendKeys("" + Keys.SHIFT + Keys.END);
305         assertTrue(result.getText(), result.getText().contains(" up: 16"));
306 
307         element.sendKeys(Keys.DELETE);
308         assertEquals("", element.getAttribute("value"));
309         assertNull(element.getDomAttribute("value"));
310         assertEquals("", element.getDomProperty("value"));
311     }
312 
313     /**
314      * A test.
315      */
316     @Test
317     public void chordReveseShiftHomeSelectionDeletes() {
318         final WebDriver driver = getWebDriver("/javascriptPage.html");
319 
320         final WebElement result = driver.findElement(By.id("result"));
321         final WebElement element = driver.findElement(By.id("keyReporter"));
322 
323         element.sendKeys("done" + Keys.HOME);
324         assertEquals("done", element.getAttribute("value"));
325         assertNull(element.getDomAttribute("value"));
326         assertEquals("done", element.getDomProperty("value"));
327 
328         element.sendKeys("" + Keys.SHIFT + "ALL " + Keys.HOME);
329         assertEquals("ALL done", element.getAttribute("value"));
330         assertNull(element.getDomAttribute("value"));
331         assertEquals("ALL done", element.getDomProperty("value"));
332 
333         element.sendKeys(Keys.DELETE);
334         assertEquals("done", element.getAttribute("value"));
335         assertNull(element.getDomAttribute("value"));
336         assertEquals("done", element.getDomProperty("value"));
337 
338         element.sendKeys("" + Keys.END + Keys.SHIFT + Keys.HOME);
339         assertEquals("done", element.getAttribute("value"));
340         assertNull(element.getDomAttribute("value"));
341         assertEquals("done", element.getDomProperty("value"));
342         // Note: trailing SHIFT up here
343         assertTrue(result.getText(), result.getText().trim().contains(" up: 16"));
344 
345         element.sendKeys("" + Keys.DELETE);
346         assertEquals("", element.getAttribute("value"));
347         assertNull(element.getDomAttribute("value"));
348         assertEquals("", element.getDomProperty("value"));
349     }
350 
351     /**
352      * A test.
353      */
354     @Test
355     public void generateKeyPressEventEvenWhenElementPreventsDefault() {
356         final WebDriver driver = getWebDriver("/javascriptPage.html");
357 
358         final WebElement silent = driver.findElement(By.name("suppress"));
359         final WebElement result = driver.findElement(By.id("result"));
360 
361         silent.sendKeys("s");
362         assertEquals("", result.getText().trim());
363     }
364 
365     /**
366      * A test.
367      */
368     @Test
369     public void nonPrintableCharactersShouldWorkWithContentEditableOrDesignModeSet() {
370         final WebDriver driver = getWebDriver("/rich_text.html");
371 
372         driver.switchTo().frame("editFrame");
373         final WebElement element = driver.switchTo().activeElement();
374         element.sendKeys("Dishy", Keys.BACK_SPACE, Keys.LEFT, Keys.LEFT);
375         element.sendKeys(Keys.LEFT, Keys.LEFT, "F", Keys.DELETE, Keys.END, "ee!");
376 
377         assertEquals("Fishee!", element.getText());
378     }
379 
380     /**
381      * A test.
382      */
383     @Test
384     @Alerts({"keydown (target) keyup (target) keyup (body)",
385              "keydown (target) a pressed; removing keyup (body)"})
386     public void canSafelyTypeOnElementThatIsRemovedFromTheDomOnKeyPress() {
387         final WebDriver driver = getWebDriver("/key_tests/remove_on_keypress.html");
388 
389         final WebElement input = driver.findElement(By.id("target"));
390         final WebElement log = driver.findElement(By.id("log"));
391 
392         assertEquals("", log.getAttribute("value"));
393         assertNull(log.getDomAttribute("value"));
394         assertEquals("", log.getDomProperty("value"));
395 
396         input.sendKeys("b");
397         assertEquals(getExpectedAlerts()[0], getValueText(log).replace('\n', ' '));
398         assertNull(getValueDomAttributeText(log));
399         assertEquals(getExpectedAlerts()[0], getValueDomPropertyText(log).replace('\n', ' '));
400         log.clear();
401 
402         input.sendKeys("a");
403 
404         assertEquals(getExpectedAlerts()[1], getValueText(log).replace('\n', ' '));
405         assertNull(getValueDomAttributeText(log));
406         assertEquals(getExpectedAlerts()[1], getValueDomPropertyText(log).replace('\n', ' '));
407     }
408 
409     private static String getValueText(final WebElement el) {
410         // Standardize on \n and strip any trailing whitespace.
411         return el.getAttribute("value").replace("\r\n", "\n").trim();
412     }
413 
414     private static String getValueDomAttributeText(final WebElement el) {
415         final String attrib = el.getDomAttribute("value");
416         if (attrib == null) {
417             return attrib;
418         }
419 
420         // Standardize on \n and strip any trailing whitespace.
421         return attrib.replace("\r\n", "\n").trim();
422     }
423 
424     private static String getValueDomPropertyText(final WebElement el) {
425         // Standardize on \n and strip any trailing whitespace.
426         return el.getDomProperty("value").replace("\r\n", "\n").trim();
427     }
428 
429     /**
430      * If the first typed character is prevented by preventing it's
431      * KeyPress-Event, the remaining string should still be appended and NOT
432      * prepended.
433      *
434      * @throws Exception if an error occurs
435      */
436     @Test
437     public void typePreventedCharacterFirst() throws Exception {
438         final String html = DOCTYPE_HTML
439             + "<html><head>\n"
440             + "<script>\n"
441             + "  function stopEnterKey(evt) {\n"
442             + "    var evt = evt || window.event;\n"
443             + "    if (evt && evt.keyCode === 13)\n"
444             + "    {\n"
445             + "      evt.preventDefault()\n"
446             + "    }\n"
447             + "  }\n"
448             + "  window.document.onkeypress = stopEnterKey;\n"
449             + "</script></head>\n"
450             + "<body>\n"
451             + "  <input id='myInput' type='text' value='Hello'>\n"
452             + "</body></html>";
453 
454         final WebDriver driver = loadPage2(html);
455         final WebElement input = driver.findElement(By.id("myInput"));
456         input.sendKeys("World");
457 
458         assertEquals("'World' should be appended.", "HelloWorld", input.getAttribute("value"));
459         assertEquals("'World' should not be appended.", "Hello", input.getDomAttribute("value"));
460         assertEquals("'World' should be appended.", "HelloWorld", input.getDomProperty("value"));
461     }
462 }