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