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.html;
16  
17  import static org.junit.jupiter.api.Assertions.fail;
18  
19  import java.awt.GraphicsEnvironment;
20  import java.util.Arrays;
21  import java.util.Collections;
22  
23  import org.apache.commons.lang3.ArrayUtils;
24  import org.htmlunit.WebDriverTestCase;
25  import org.htmlunit.junit.annotation.Alerts;
26  import org.htmlunit.util.MimeType;
27  import org.junit.jupiter.api.Assertions;
28  import org.junit.jupiter.api.Assumptions;
29  import org.junit.jupiter.api.Test;
30  import org.openqa.selenium.By;
31  import org.openqa.selenium.InvalidElementStateException;
32  import org.openqa.selenium.Keys;
33  import org.openqa.selenium.WebDriver;
34  import org.openqa.selenium.WebElement;
35  import org.openqa.selenium.htmlunit.HtmlUnitDriver;
36  
37  /**
38   * Tests for {@link HtmlTextInput}.
39   *
40   * @author Ronald Brill
41   * @author Anton Demydenko
42   */
43  public class HtmlTextInputTest extends WebDriverTestCase {
44  
45      private static boolean SKIP_ = false;
46  
47      static {
48          if (GraphicsEnvironment.isHeadless()) {
49              // skip the tests in headless mode
50              SKIP_ = true;
51          }
52      }
53  
54      /**
55       * Verifies getVisibleText().
56       * @throws Exception if the test fails
57       */
58      @Test
59      @Alerts("")
60      public void getVisibleText() throws Exception {
61          final String htmlContent = DOCTYPE_HTML
62              + "<html>\n"
63              + "<head></head>\n"
64              + "<body>\n"
65              + "<form id='form1'>\n"
66              + "  <input type='text' name='tester' id='tester' value='bla'>\n"
67              + "</form>\n"
68              + "</body></html>";
69  
70          final WebDriver driver = loadPage2(htmlContent);
71          final String text = driver.findElement(By.id("tester")).getText();
72          assertEquals(getExpectedAlerts()[0], text);
73  
74          if (driver instanceof HtmlUnitDriver) {
75              final HtmlPage page = (HtmlPage) getEnclosedPage();
76              assertEquals(getExpectedAlerts()[0], page.getBody().getVisibleText());
77          }
78      }
79  
80      /**
81       * @throws Exception if the test fails
82       */
83      @Test
84      public void type() throws Exception {
85          final String html = DOCTYPE_HTML
86                  + "<html><head></head><body><input type='text' id='t'/></body></html>";
87          final WebDriver driver = loadPage2(html);
88          final WebElement t = driver.findElement(By.id("t"));
89  
90          t.sendKeys("abc");
91          assertNull(t.getDomAttribute("value"));
92          assertEquals("abc", t.getDomProperty("value"));
93  
94          t.sendKeys(Keys.BACK_SPACE);
95          assertNull(t.getDomAttribute("value"));
96          assertEquals("ab", t.getDomProperty("value"));
97  
98          t.sendKeys(Keys.BACK_SPACE);
99          assertNull(t.getDomAttribute("value"));
100         assertEquals("a", t.getDomProperty("value"));
101 
102         t.sendKeys(Keys.BACK_SPACE);
103         assertNull(t.getDomAttribute("value"));
104         assertEquals("", t.getDomProperty("value"));
105 
106         t.sendKeys(Keys.BACK_SPACE);
107         assertNull(t.getDomAttribute("value"));
108         assertEquals("", t.getDomProperty("value"));
109     }
110 
111     /**
112      * @throws Exception if the test fails
113      */
114     @Test
115     public void typeWhileDisabled() throws Exception {
116         final String html = DOCTYPE_HTML
117                 + "<html><body><input type='text' id='p' disabled='disabled'/></body></html>";
118         final WebDriver driver = loadPage2(html);
119         final WebElement p = driver.findElement(By.id("p"));
120         try {
121             p.sendKeys("abc");
122             fail();
123         }
124         catch (final InvalidElementStateException e) {
125             // as expected
126         }
127 
128         assertNull(p.getDomAttribute("value"));
129         assertEquals("", p.getDomProperty("value"));
130     }
131 
132     /**
133      * @throws Exception if the test fails
134      */
135     @Test
136     @Alerts({"null", "null"})
137     public void typeDoesNotChangeValueAttribute() throws Exception {
138         final String html = DOCTYPE_HTML
139                 + "<html>\n"
140                 + "<head>\n"
141                 + "<script>\n"
142                 + LOG_TITLE_FUNCTION
143                 + "</script>"
144                 + "</head>\n"
145                 + "<body>\n"
146                 + "  <input type='text' id='t'/>\n"
147                 + "  <button id='check' onclick='log(document.getElementById(\"t\").getAttribute(\"value\"));'>"
148                         + "DoIt</button>\n"
149                 + "</body></html>";
150 
151         final WebDriver driver = loadPage2(html);
152         final WebElement t = driver.findElement(By.id("t"));
153 
154         final WebElement check = driver.findElement(By.id("check"));
155         check.click();
156         verifyTitle2(driver, getExpectedAlerts()[0]);
157 
158         t.sendKeys("abc");
159         check.click();
160         verifyTitle2(driver, getExpectedAlerts());
161     }
162 
163     /**
164      * @throws Exception if the test fails
165      */
166     @Test
167     @Alerts({"HtmlUnit", "HtmlUnit"})
168     public void typeDoesNotChangeValueAttributeWithInitialValue() throws Exception {
169         final String html = DOCTYPE_HTML
170                 + "<html>\n"
171                 + "<head>\n"
172                 + "<script>\n"
173                 + LOG_TITLE_FUNCTION
174                 + "</script>"
175                 + "</head>\n"
176                 + "<body>\n"
177                 + "  <input type='text' id='t' value='HtmlUnit'/>\n"
178                 + "  <button id='check' onclick='log(document.getElementById(\"t\").getAttribute(\"value\"));'>"
179                         + "DoIt</button>\n"
180                 + "</body></html>";
181 
182         final WebDriver driver = loadPage2(html);
183         final WebElement t = driver.findElement(By.id("t"));
184 
185         final WebElement check = driver.findElement(By.id("check"));
186         check.click();
187         verifyTitle2(driver, getExpectedAlerts()[0]);
188 
189         t.sendKeys("abc");
190         check.click();
191         verifyTitle2(driver, getExpectedAlerts());
192     }
193 
194     /**
195      * @throws Exception if an error occurs
196      */
197     @Test
198     public void preventDefault_OnKeyDown() throws Exception {
199         final String html = DOCTYPE_HTML
200             + "<html><head><script>\n"
201             + "  function handler(e) {\n"
202             + "    if (e && e.target.value.length > 2)\n"
203             + "      e.preventDefault();\n"
204             + "    else if (!e && window.event.srcElement.value.length > 2)\n"
205             + "      return false;\n"
206             + "  }\n"
207             + "  function init() {\n"
208             + "    document.getElementById('p').onkeydown = handler;\n"
209             + "  }\n"
210             + "</script></head>\n"
211             + "<body onload='init()'>\n"
212             + "<input type='text' id='p'></input>\n"
213             + "</body></html>";
214 
215         final WebDriver driver = loadPage2(html);
216         final WebElement p = driver.findElement(By.id("p"));
217 
218         p.sendKeys("abcd");
219         assertNull(p.getDomAttribute("value"));
220         assertEquals("abc", p.getDomProperty("value"));
221     }
222 
223     /**
224      * @throws Exception if an error occurs
225      */
226     @Test
227     public void preventDefault_OnKeyPress() throws Exception {
228         final String html = DOCTYPE_HTML
229             + "<html><head><script>\n"
230             + "  function handler(e) {\n"
231             + "    if (e && e.target.value.length > 2)\n"
232             + "      e.preventDefault();\n"
233             + "    else if (!e && window.event.srcElement.value.length > 2)\n"
234             + "      return false;\n"
235             + "  }\n"
236             + "  function init() {\n"
237             + "    document.getElementById('p').onkeypress = handler;\n"
238             + "  }\n"
239             + "</script></head>\n"
240             + "<body onload='init()'>\n"
241             + "<input type='text' id='p'></input>\n"
242             + "</body></html>";
243 
244         final WebDriver driver = loadPage2(html);
245         final WebElement p = driver.findElement(By.id("p"));
246 
247         p.sendKeys("abcd");
248         assertNull(p.getDomAttribute("value"));
249         assertEquals("abc", p.getDomProperty("value"));
250     }
251 
252     /**
253      * @throws Exception if an error occurs
254      */
255     @Test
256     public void typeOnChange() throws Exception {
257         final String html = DOCTYPE_HTML
258             + "<html><head>\n"
259             + "<script>\n"
260             + LOG_TEXTAREA_FUNCTION
261             + "</script>"
262             + "</head>\n"
263             + "<body>\n"
264             + "<input type='text' id='p' value='Hello world'"
265                 + " onChange='log(\"foo\");log(event.type);'"
266                 + " onBlur='log(\"boo\");log(event.type);'>\n"
267             + "<button id='b'>some button</button>\n"
268             + LOG_TEXTAREA
269             + "</body></html>";
270 
271         final WebDriver driver = loadPage2(html);
272         final WebElement p = driver.findElement(By.id("p"));
273         p.sendKeys("HtmlUnit");
274 
275         verifyTextArea2(driver);
276 
277         // trigger lost focus
278         driver.findElement(By.id("b")).click();
279         final String[] expectedAlerts1 = {"foo", "change", "boo", "blur"};
280         verifyTextArea2(driver, expectedAlerts1);
281 
282         // set only the focus but change nothing
283         p.click();
284         verifyTextArea2(driver, expectedAlerts1);
285 
286         // trigger lost focus
287         driver.findElement(By.id("b")).click();
288         final String[] expectedAlerts2 = {"boo", "blur"};
289         verifyTextArea2(driver, ArrayUtils.addAll(expectedAlerts1, expectedAlerts2));
290     }
291 
292     /**
293      * @throws Exception if an error occurs
294      */
295     @Test
296     public void setValueOnChange() throws Exception {
297         final String html = DOCTYPE_HTML
298               + "<html>\n"
299               + "<head></head>\n"
300               + "<body>\n"
301               + "  <input type='text' id='t' value='Hello world'"
302                     + " onChange='log(\"foo\");log(event.type);'>\n"
303               + "  <button id='b'>some button</button>\n"
304               + "  <button id='set' onclick='document.getElementById(\"t\").value=\"HtmlUnit\"'>setValue</button>\n"
305               + "</body></html>";
306 
307         final WebDriver driver = loadPage2(html);
308         driver.findElement(By.id("set")).click();
309 
310         assertEquals(Collections.emptyList(), getCollectedAlerts(driver));
311 
312         // trigger lost focus
313         driver.findElement(By.id("b")).click();
314         assertEquals(Collections.emptyList(), getCollectedAlerts(driver));
315     }
316 
317     /**
318      * @throws Exception if an error occurs
319      */
320     @Test
321     public void setDefaultValueOnChange() throws Exception {
322         final String html = DOCTYPE_HTML
323               + "<html>\n"
324               + "<head></head>\n"
325               + "<body>\n"
326               + "  <input type='text' id='t' value='Hello world'"
327                     + " onChange='log(\"foo\");log(event.type);'>\n"
328               + "  <button id='b'>some button</button>\n"
329               + "  <button id='set' onclick='document.getElementById(\"t\").defaultValue=\"HtmlUnit\"'>"
330                       + "setValue</button>\n"
331               + "</body></html>";
332 
333         final WebDriver driver = loadPage2(html);
334         driver.findElement(By.id("set")).click();
335 
336         assertEquals(Collections.emptyList(), getCollectedAlerts(driver));
337 
338         // trigger lost focus
339         driver.findElement(By.id("b")).click();
340         assertEquals(Collections.emptyList(), getCollectedAlerts(driver));
341     }
342 
343     /**
344      * @throws Exception if the test fails
345      */
346     @Test
347     @Alerts({"--null", "--null", "--null"})
348     public void defaultValues() throws Exception {
349         final String html = DOCTYPE_HTML
350             + "<html><head>\n"
351             + "<script>\n"
352             + LOG_TITLE_FUNCTION
353             + "  function test() {\n"
354             + "    var input = document.getElementById('text1');\n"
355             + "    log(input.value + '-' + input.defaultValue + '-' + input.getAttribute('value'));\n"
356 
357             + "    input = document.createElement('input');\n"
358             + "    input.type = 'text';\n"
359             + "    log(input.value + '-' + input.defaultValue + '-' + input.getAttribute('value'));\n"
360 
361             + "    var builder = document.createElement('div');\n"
362             + "    builder.innerHTML = '<input type=\"text\">';\n"
363             + "    input = builder.firstChild;\n"
364             + "    log(input.value + '-' + input.defaultValue + '-' + input.getAttribute('value'));\n"
365             + "  }\n"
366             + "</script>\n"
367             + "</head><body onload='test()'>\n"
368             + "<form>\n"
369             + "  <input type='text' id='text1'>\n"
370             + "</form>\n"
371             + "</body></html>";
372 
373         loadPageVerifyTitle2(html);
374     }
375 
376     /**
377      * @throws Exception if the test fails
378      */
379     @Test
380     @Alerts({"--null", "--null", "--null"})
381     public void defaultValuesAfterClone() throws Exception {
382         final String html = DOCTYPE_HTML
383             + "<html><head>\n"
384             + "<script>\n"
385             + LOG_TITLE_FUNCTION
386             + "  function test() {\n"
387             + "    var input = document.getElementById('text1');\n"
388             + "    input = input.cloneNode(false);\n"
389             + "    log(input.value + '-' + input.defaultValue + '-' + input.getAttribute('value'));\n"
390 
391             + "    input = document.createElement('input');\n"
392             + "    input.type = 'text';\n"
393             + "    input = input.cloneNode(false);\n"
394             + "    log(input.value + '-' + input.defaultValue + '-' + input.getAttribute('value'));\n"
395 
396             + "    var builder = document.createElement('div');\n"
397             + "    builder.innerHTML = '<input type=\"text\">';\n"
398             + "    input = builder.firstChild;\n"
399             + "    input = input.cloneNode(false);\n"
400             + "    log(input.value + '-' + input.defaultValue + '-' + input.getAttribute('value'));\n"
401             + "  }\n"
402             + "</script>\n"
403             + "</head><body onload='test()'>\n"
404             + "<form>\n"
405             + "  <input type='text' id='text1'>\n"
406             + "</form>\n"
407             + "</body></html>";
408 
409         loadPageVerifyTitle2(html);
410     }
411 
412     /**
413      * @throws Exception if the test fails
414      */
415     @Test
416     @Alerts({"initial-initial-initial", "initial-initial-initial",
417                 "newValue-initial-initial", "newValue-initial-initial",
418                 "newValue-newDefault-newDefault", "newValue-newDefault-newDefault"})
419     public void resetByClick() throws Exception {
420         final String html = DOCTYPE_HTML
421             + "<html><head>\n"
422             + "<script>\n"
423             + LOG_TITLE_FUNCTION
424             + "  function test() {\n"
425             + "    var text = document.getElementById('testId');\n"
426             + "    log(text.value + '-' + text.defaultValue + '-' + text.getAttribute('value'));\n"
427 
428             + "    document.getElementById('testReset').click;\n"
429             + "    log(text.value + '-' + text.defaultValue + '-' + text.getAttribute('value'));\n"
430 
431             + "    text.value = 'newValue';\n"
432             + "    log(text.value + '-' + text.defaultValue + '-' + text.getAttribute('value'));\n"
433 
434             + "    document.getElementById('testReset').click;\n"
435             + "    log(text.value + '-' + text.defaultValue + '-' + text.getAttribute('value'));\n"
436 
437             + "    text.defaultValue = 'newDefault';\n"
438             + "    log(text.value + '-' + text.defaultValue + '-' + text.getAttribute('value'));\n"
439 
440             + "    document.forms[0].reset;\n"
441             + "    log(text.value + '-' + text.defaultValue + '-' + text.getAttribute('value'));\n"
442             + "  }\n"
443             + "</script>\n"
444             + "</head><body onload='test()'>\n"
445             + "<form>\n"
446             + "  <input type='text' id='testId' value='initial'>\n"
447             + "  <input type='reset' id='testReset'>\n"
448             + "</form>\n"
449             + "</body></html>";
450 
451         loadPageVerifyTitle2(html);
452     }
453 
454     /**
455      * @throws Exception if the test fails
456      */
457     @Test
458     @Alerts({"initial-initial-initial", "initial-initial-initial",
459                 "newValue-initial-initial", "newValue-initial-initial",
460                 "newValue-newDefault-newDefault", "newValue-newDefault-newDefault"})
461     public void resetByJS() throws Exception {
462         final String html = DOCTYPE_HTML
463             + "<html><head>\n"
464             + "<script>\n"
465             + LOG_TITLE_FUNCTION
466             + "  function test() {\n"
467             + "    var text = document.getElementById('testId');\n"
468             + "    log(text.value + '-' + text.defaultValue + '-' + text.getAttribute('value'));\n"
469 
470             + "    document.forms[0].reset;\n"
471             + "    log(text.value + '-' + text.defaultValue + '-' + text.getAttribute('value'));\n"
472 
473             + "    text.value = 'newValue';\n"
474             + "    log(text.value + '-' + text.defaultValue + '-' + text.getAttribute('value'));\n"
475 
476             + "    document.forms[0].reset;\n"
477             + "    log(text.value + '-' + text.defaultValue + '-' + text.getAttribute('value'));\n"
478 
479             + "    text.defaultValue = 'newDefault';\n"
480             + "    log(text.value + '-' + text.defaultValue + '-' + text.getAttribute('value'));\n"
481 
482             + "    document.forms[0].reset;\n"
483             + "    log(text.value + '-' + text.defaultValue + '-' + text.getAttribute('value'));\n"
484             + "  }\n"
485             + "</script>\n"
486             + "</head><body onload='test()'>\n"
487             + "<form>\n"
488             + "  <input type='text' id='testId' value='initial'>\n"
489             + "</form>\n"
490             + "</body></html>";
491 
492         loadPageVerifyTitle2(html);
493     }
494 
495     /**
496      * @throws Exception if the test fails
497      */
498     @Test
499     @Alerts({"initial-initial-initial", "default-default-default",
500                 "newValue-default-default", "newValue-attribValue-attribValue",
501                 "newValue-newDefault-newDefault"})
502     public void value() throws Exception {
503         final String html = DOCTYPE_HTML
504             + "<html><head>\n"
505             + "<script>\n"
506             + LOG_TITLE_FUNCTION
507             + "  function test() {\n"
508             + "    var text = document.getElementById('testId');\n"
509             + "    log(text.value + '-' + text.defaultValue + '-' + text.getAttribute('value'));\n"
510 
511             + "    text.defaultValue = 'default';\n"
512             + "    log(text.value + '-' + text.defaultValue + '-' + text.getAttribute('value'));\n"
513 
514             + "    text.value = 'newValue';\n"
515             + "    log(text.value + '-' + text.defaultValue + '-' + text.getAttribute('value'));\n"
516 
517             + "    text.setAttribute('value', 'attribValue');\n"
518             + "    log(text.value + '-' + text.defaultValue + '-' + text.getAttribute('value'));\n"
519 
520             + "    text.defaultValue = 'newDefault';\n"
521             + "    log(text.value + '-' + text.defaultValue + '-' + text.getAttribute('value'));\n"
522             + "  }\n"
523             + "</script>\n"
524             + "</head><body onload='test()'>\n"
525             + "<form>\n"
526             + "  <input type='text' id='testId' value='initial'>\n"
527             + "</form>\n"
528             + "</body></html>";
529 
530         loadPageVerifyTitle2(html);
531     }
532 
533     /**
534      * @throws Exception if the test fails
535      */
536     @Test
537     @Alerts(DEFAULT = "textLength not available",
538             FF = "7",
539             FF_ESR = "7")
540     public void textLength() throws Exception {
541         final String html = DOCTYPE_HTML
542             + "<html><head>\n"
543             + "<script>\n"
544             + LOG_TITLE_FUNCTION
545             + "  function test() {\n"
546             + "    var text = document.getElementById('testId');\n"
547             + "    if(text.textLength) {\n"
548             + "      log(text.textLength);\n"
549             + "    } else {\n"
550             + "      log('textLength not available');\n"
551             + "    }\n"
552             + "  }\n"
553             + "</script>\n"
554             + "</head><body onload='test()'>\n"
555             + "<form>\n"
556             + "  <input type='text' id='testId' value='initial'>\n"
557             + "</form>\n"
558             + "</body></html>";
559 
560         loadPageVerifyTitle2(html);
561     }
562 
563     /**
564      * @throws Exception if an error occurs
565      */
566     @Test
567     @Alerts("0")
568     public void selection() throws Exception {
569         final String html = DOCTYPE_HTML
570             + "<html><head><script>\n"
571             + LOG_TITLE_FUNCTION
572             + "  function test() {\n"
573             + "    log(getSelection(document.getElementById('text1')).length);\n"
574             + "  }\n"
575             + "  function getSelection(element) {\n"
576             + "    return element.value.substring(element.selectionStart, element.selectionEnd);\n"
577             + "  }\n"
578             + "</script></head>\n"
579             + "<body onload='test()'>\n"
580             + "  <input type='text' id='text1'/>\n"
581             + "</body></html>";
582         loadPageVerifyTitle2(html);
583     }
584 
585     /**
586      * @throws Exception if test fails
587      */
588     @Test
589     @Alerts({"0,0", "11,11", "3,11", "3,10"})
590     public void selection2_1() throws Exception {
591         selection2(3, 10);
592     }
593 
594     /**
595      * @throws Exception if test fails
596      */
597     @Test
598     @Alerts({"0,0", "11,11", "11,11", "11,11"})
599     public void selection2_2() throws Exception {
600         selection2(-3, 15);
601     }
602 
603     /**
604      * @throws Exception if test fails
605      */
606     @Test
607     @Alerts({"0,0", "11,11", "10,11", "5,5"})
608     public void selection2_3() throws Exception {
609         selection2(10, 5);
610     }
611 
612     private void selection2(final int selectionStart, final int selectionEnd) throws Exception {
613         final String html = DOCTYPE_HTML
614             + "<html>\n"
615             + "<body>\n"
616             + "<input id='myTextInput' value='Bonjour' type='text'>\n"
617             + "<script>\n"
618             + LOG_TITLE_FUNCTION
619             + "  var input = document.getElementById('myTextInput');\n"
620             + "  log(input.selectionStart + ',' + input.selectionEnd);\n"
621             + "  input.value = 'Hello there';\n"
622             + "  log(input.selectionStart + ',' + input.selectionEnd);\n"
623             + "  input.selectionStart = " + selectionStart + ";\n"
624             + "  log(input.selectionStart + ',' + input.selectionEnd);\n"
625             + "  input.selectionEnd = " + selectionEnd + ";\n"
626             + "  log(input.selectionStart + ',' + input.selectionEnd);\n"
627             + "</script>\n"
628             + "</body>\n"
629             + "</html>";
630         loadPageVerifyTitle2(html);
631     }
632 
633     /**
634      * @throws Exception if test fails
635      */
636     @Test
637     @Alerts({"0,0", "4,5", "10,10", "4,4", "1,1"})
638     public void selectionOnUpdate() throws Exception {
639         final String html = DOCTYPE_HTML
640             + "<html>\n"
641             + "<body>\n"
642             + "<input id='myTextInput' value='Hello' type='text'>\n"
643             + "<script>\n"
644             + LOG_TITLE_FUNCTION
645             + "  var input = document.getElementById('myTextInput');\n"
646             + "  log(input.selectionStart + ',' + input.selectionEnd);\n"
647 
648             + "  input.selectionStart = 4;\n"
649             + "  input.selectionEnd = 5;\n"
650             + "  log(input.selectionStart + ',' + input.selectionEnd);\n"
651             + "  input.value = 'abcdefghif';\n"
652             + "  log(input.selectionStart + ',' + input.selectionEnd);\n"
653 
654             + "  input.value = 'abcd';\n"
655             + "  log(input.selectionStart + ',' + input.selectionEnd);\n"
656 
657             + "  input.selectionStart = 0;\n"
658             + "  input.selectionEnd = 4;\n"
659 
660             + "  input.value = 'a';\n"
661             + "  log(input.selectionStart + ',' + input.selectionEnd);\n"
662             + "</script>\n"
663             + "</body>\n"
664             + "</html>";
665         loadPageVerifyTitle2(html);
666     }
667 
668     /**
669      * @throws Exception if the test fails
670      */
671     @Test
672     public void submitOnEnter() throws Exception {
673         final String html = DOCTYPE_HTML
674             + "<html>\n"
675             + "<body>\n"
676             + "  <form action='result.html'>\n"
677             + "    <input id='t' value='hello'/>\n"
678             + "  </form>\n"
679             + "</body>\n"
680             + "</html>";
681         getMockWebConnection().setDefaultResponse("Error: not found", 404, "Not Found", MimeType.TEXT_HTML);
682 
683         final WebDriver driver = loadPage2(html);
684         final WebElement field = driver.findElement(By.id("t"));
685 
686         field.sendKeys("\n");
687         if (useRealBrowser()) {
688             Thread.sleep(400);
689         }
690         assertEquals(2, getMockWebConnection().getRequestCount());
691     }
692 
693     /**
694      * @throws Exception if the test fails
695      */
696     @Test
697     public void sendKeysEnterWithoutForm() throws Exception {
698         final String html = DOCTYPE_HTML
699             + "<html>\n"
700             + "<body>\n"
701             + "  <input id='t' value='hello'>\n"
702             + "</body>\n"
703             + "</html>";
704 
705         final WebDriver driver = loadPage2(html);
706         driver.findElement(By.id("t")).sendKeys("\n");
707 
708         assertEquals(1, getMockWebConnection().getRequestCount());
709     }
710 
711     /**
712      * @throws Exception if the test fails
713      */
714     @Test
715     public void submitWithoutForm() throws Exception {
716         final String html = DOCTYPE_HTML
717             + "<html>\n"
718             + "<body>\n"
719             + "  <input id='t' value='hello'>\n"
720             + "</body>\n"
721             + "</html>";
722 
723         final WebDriver driver = loadPage2(html);
724         Assertions.assertThrows(UnsupportedOperationException.class, () -> driver.findElement(By.id("t")).submit());
725 
726         assertEquals(1, getMockWebConnection().getRequestCount());
727     }
728 
729     /**
730      * @throws Exception if the test fails
731      */
732     @Test
733     @Alerts("--")
734     public void minMaxStep() throws Exception {
735         final String html = DOCTYPE_HTML
736             + "<html>\n"
737             + "<head>\n"
738             + "<script>\n"
739             + LOG_TITLE_FUNCTION
740             + "  function test() {\n"
741             + "    var input = document.getElementById('tester');\n"
742             + "    log(input.min + '-' + input.max + '-' + input.step);\n"
743             + "  }\n"
744             + "</script>\n"
745             + "</head>\n"
746             + "<body onload='test()'>\n"
747             + "<form>\n"
748             + "  <input type='text' id='tester'>\n"
749             + "</form>\n"
750             + "</body>\n"
751             + "</html>";
752 
753         loadPageVerifyTitle2(html);
754     }
755 
756     /**
757      * @throws Exception if an error occurs
758      */
759     @Test
760     @Alerts({"0987654321!",
761              "0987654321!",
762              "false",
763              "false-false-true-false-false-false-false-false-false-false-false",
764              "true",
765              "§§URL§§", "1"})
766     public void patternValidationInvalid() throws Exception {
767         validation("<input type='text' pattern='[0-9a-zA-Z]{10,40}' id='e1' name='k' value='0987654321!'>\n",
768                     "", null);
769     }
770 
771     /**
772      * @throws Exception if an error occurs
773      */
774     @Test
775     @Alerts({"68746d6c756e69742072756c657a21",
776              "68746d6c756e69742072756c657a21",
777              "true",
778              "false-false-false-false-false-false-false-false-false-true-false",
779              "true",
780              "§§URL§§?k=68746d6c756e69742072756c657a21", "2"})
781     public void patternValidationValid() throws Exception {
782         validation("<input type='text' pattern='[0-9a-zA-Z]{10,40}' "
783                 + "id='e1' name='k' value='68746d6c756e69742072756c657a21'>\n", "", null);
784     }
785 
786     /**
787      * @throws Exception if an error occurs
788      */
789     @Test
790     @Alerts({"",
791              "",
792              "true",
793              "false-false-false-false-false-false-false-false-false-true-false",
794              "true",
795              "§§URL§§?k=", "2"})
796     public void patternValidationEmpty() throws Exception {
797         validation("<input type='text' pattern='[0-9a-zA-Z]{10,40}' id='e1' name='k' value=''>\n", "", null);
798     }
799 
800     /**
801      * @throws Exception if an error occurs
802      */
803     @Test
804     @Alerts({" ",
805              " ",
806              "false",
807              "false-false-true-false-false-false-false-false-false-false-false",
808              "true",
809              "§§URL§§", "1"})
810     public void patternValidationBlank() throws Exception {
811         validation("<input type='text' pattern='[0-9a-zA-Z]{10,40}' id='e1' name='k' value=' '>\n", "", null);
812     }
813 
814     /**
815      * @throws Exception if an error occurs
816      */
817     @Test
818     @Alerts({"  \t",
819              "  \t",
820              "false",
821              "false-false-true-false-false-false-false-false-false-false-false",
822              "true",
823              "§§URL§§", "1"})
824     public void patternValidationWhitespace() throws Exception {
825         validation("<input type='text' pattern='[0-9a-zA-Z]{10,40}' id='e1' name='k' value='  \t'>\n", "", null);
826     }
827 
828     /**
829      * @throws Exception if an error occurs
830      */
831     @Test
832     @Alerts({" 210 ",
833              " 210 ",
834              "true",
835              "false-false-false-false-false-false-false-false-false-true-false",
836              "true",
837              "§§URL§§?k=+210+", "2"})
838     public void patternValidationTrimInitial() throws Exception {
839         validation("<input type='text' pattern='[ 012]{3,10}' id='e1' name='k' value=' 210 '>\n", "", null);
840     }
841 
842     /**
843      * @throws Exception if an error occurs
844      */
845     @Test
846     @Alerts({"null",
847              " 210 ",
848              "true",
849              "false-false-false-false-false-false-false-false-false-true-false",
850              "true",
851              "§§URL§§?k=+210+", "2"})
852     public void patternValidationTrimType() throws Exception {
853         validation("<input type='text' pattern='[ 012]{3,10}' id='e1' name='k'>\n", "", " 210 ");
854     }
855 
856     /**
857      * @throws Exception if an error occurs
858      */
859     @Test
860     @Alerts({"null",
861              "abcd",
862              "false",
863              "false-false-false-false-false-false-false-true-false-false-false",
864              "true",
865              "§§URL§§", "1"})
866     public void minLengthValidationInvalid() throws Exception {
867         validation("<input type='text' minlength='5' id='e1' name='k'>\n", "", "abcd");
868     }
869 
870     /**
871      * @throws Exception if an error occurs
872      */
873     @Test
874     @Alerts({"ab",
875              "ab",
876              "true",
877              "false-false-false-false-false-false-false-false-false-true-false",
878              "true",
879              "§§URL§§?k=ab", "2"})
880     public void minLengthValidationInvalidInitial() throws Exception {
881         validation("<input type='text' minlength='5' id='e1' name='k' value='ab'>\n", "", null);
882     }
883 
884     /**
885      * @throws Exception if an error occurs
886      */
887     @Test
888     @Alerts({"null",
889              "",
890              "true",
891              "false-false-false-false-false-false-false-false-false-true-false",
892              "true",
893              "§§URL§§?k=", "2"})
894     public void minLengthValidationInvalidNoInitial() throws Exception {
895         validation("<input type='text' minlength='5' id='e1' name='k'>\n", "", null);
896     }
897 
898     /**
899      * @throws Exception if an error occurs
900      */
901     @Test
902     @Alerts({"null",
903              "abcdefghi",
904              "true",
905              "false-false-false-false-false-false-false-false-false-true-false",
906              "true",
907              "§§URL§§?k=abcdefghi", "2"})
908     public void minLengthValidationValid() throws Exception {
909         validation("<input type='text' minlength='5' id='e1' name='k'>\n", "", "abcdefghi");
910     }
911 
912     /**
913      * @throws Exception if an error occurs
914      */
915     @Test
916     @Alerts({"null",
917              "abcd",
918              "true",
919              "false-false-false-false-false-false-false-false-false-true-false",
920              "true",
921              "§§URL§§?k=abcd", "2"})
922     public void maxLengthValidationValid() throws Exception {
923         validation("<input type='text' maxlength='5' id='e1' name='k'>\n", "", "abcd");
924     }
925 
926     /**
927      * @throws Exception if an error occurs
928      */
929     @Test
930     @Alerts({"null",
931              "abcde",
932              "true",
933              "false-false-false-false-false-false-false-false-false-true-false",
934              "true",
935              "§§URL§§?k=abcde", "2"})
936     public void maxLengthValidationInvalid() throws Exception {
937         validation("<input type='text' maxlength='5' id='e1' name='k'>\n", "", "abcdefghi");
938     }
939 
940     /**
941      * @throws Exception if an error occurs
942      */
943     @Test
944     @Alerts({"abcdefghi",
945              "abcdefghi",
946              "true",
947              "false-false-false-false-false-false-false-false-false-true-false",
948              "true",
949              "§§URL§§?k=abcdefghi", "2"})
950     public void maxLengthValidationInvalidInitial() throws Exception {
951         validation("<input type='text' maxlength='5' id='e1' value='abcdefghi' name='k'>\n", "", null);
952     }
953 
954     /**
955      * @throws Exception if an error occurs
956      */
957     @Test
958     @Alerts({"true", "false", "true", "false", "true"})
959     public void willValidate() throws Exception {
960         final String html = DOCTYPE_HTML
961                 + "<html><head>\n"
962                 + "  <script>\n"
963                 + LOG_TITLE_FUNCTION
964                 + "    function test() {\n"
965                 + "      log(document.getElementById('o1').willValidate);\n"
966                 + "      log(document.getElementById('o2').willValidate);\n"
967                 + "      log(document.getElementById('o3').willValidate);\n"
968                 + "      log(document.getElementById('o4').willValidate);\n"
969                 + "      log(document.getElementById('o5').willValidate);\n"
970                 + "    }\n"
971                 + "  </script>\n"
972                 + "</head>\n"
973                 + "<body onload='test()'>\n"
974                 + "  <form>\n"
975                 + "    <input type='text' id='o1'>\n"
976                 + "    <input type='text' id='o2' disabled>\n"
977                 + "    <input type='text' id='o3' hidden>\n"
978                 + "    <input type='text' id='o4' readonly>\n"
979                 + "    <input type='text' id='o5' style='display: none'>\n"
980                 + "  </form>\n"
981                 + "</body></html>";
982 
983         loadPageVerifyTitle2(html);
984     }
985 
986     /**
987      * @throws Exception if an error occurs
988      */
989     @Test
990     @Alerts({"null",
991              "",
992              "true",
993              "false-false-false-false-false-false-false-false-false-true-false",
994              "true",
995              "§§URL§§?k=", "2"})
996     public void validationEmpty() throws Exception {
997         validation("<input type='text' id='e1' name='k'>\n", "", null);
998     }
999 
1000     /**
1001      * @throws Exception if an error occurs
1002      */
1003     @Test
1004     @Alerts({"null",
1005              "",
1006              "false",
1007              "false-true-false-false-false-false-false-false-false-false-false",
1008              "true",
1009              "§§URL§§", "1"})
1010     public void validationCustomValidity() throws Exception {
1011         validation("<input type='text' id='e1' name='k'>\n", "elem.setCustomValidity('Invalid');", null);
1012     }
1013 
1014     /**
1015      * @throws Exception if an error occurs
1016      */
1017     @Test
1018     @Alerts({"null",
1019              "",
1020              "false",
1021              "false-true-false-false-false-false-false-false-false-false-false",
1022              "true",
1023              "§§URL§§", "1"})
1024     public void validationBlankCustomValidity() throws Exception {
1025         validation("<input type='text' id='e1' name='k'>\n", "elem.setCustomValidity(' ');\n", null);
1026     }
1027 
1028     /**
1029      * @throws Exception if an error occurs
1030      */
1031     @Test
1032     @Alerts({"null",
1033              "",
1034              "true",
1035              "false-false-false-false-false-false-false-false-false-true-false",
1036              "true",
1037              "§§URL§§?k=", "2"})
1038     public void validationResetCustomValidity() throws Exception {
1039         validation("<input type='text' id='e1' name='k'>\n",
1040                 "elem.setCustomValidity('Invalid');elem.setCustomValidity('');", null);
1041     }
1042 
1043     /**
1044      * @throws Exception if an error occurs
1045      */
1046     @Test
1047     @Alerts({"null",
1048              "",
1049              "false",
1050              "false-false-false-false-false-false-false-false-false-false-true",
1051              "true",
1052              "§§URL§§", "1"})
1053     public void validationRequired() throws Exception {
1054         validation("<input type='text' id='e1' name='k' required>\n", "", null);
1055     }
1056 
1057     /**
1058      * @throws Exception if an error occurs
1059      */
1060     @Test
1061     @Alerts({"null",
1062              "",
1063              "true",
1064              "false-false-false-false-false-false-false-false-false-true-false",
1065              "true",
1066              "§§URL§§?k=victoria", "2"})
1067     public void validationRequiredValueSet() throws Exception {
1068         validation("<input type='text' id='e1' name='k' required>\n", "elem.value='victoria';", null);
1069     }
1070 
1071     /**
1072      * @throws Exception if an error occurs
1073      */
1074     @Test
1075     @Alerts({"null",
1076              "",
1077              "false",
1078              "false-false-true-false-false-false-false-false-false-false-false",
1079              "true",
1080              "§§URL§§", "1"})
1081     public void validationPattern() throws Exception {
1082         validation("<input type='text' id='e1' name='k' pattern='abc'>\n", "elem.value='one';", null);
1083     }
1084 
1085     private void validation(final String htmlPart, final String jsPart, final String sendKeys) throws Exception {
1086         final String html = DOCTYPE_HTML
1087                 + "<html><head>\n"
1088                 + "  <script>\n"
1089                 + LOG_TITLE_FUNCTION
1090                 + "    function logValidityState(s) {\n"
1091                 + "      log(s.badInput"
1092                         + "+ '-' + s.customError"
1093                         + "+ '-' + s.patternMismatch"
1094                         + "+ '-' + s.rangeOverflow"
1095                         + "+ '-' + s.rangeUnderflow"
1096                         + "+ '-' + s.stepMismatch"
1097                         + "+ '-' + s.tooLong"
1098                         + "+ '-' + s.tooShort"
1099                         + " + '-' + s.typeMismatch"
1100                         + " + '-' + s.valid"
1101                         + " + '-' + s.valueMissing);\n"
1102                 + "    }\n"
1103                 + "    function test() {\n"
1104                 + "      var elem = document.getElementById('e1');\n"
1105                 + jsPart
1106                 + "      log(elem.checkValidity());\n"
1107                 + "      logValidityState(elem.validity);\n"
1108                 + "      log(elem.willValidate);\n"
1109                 + "    }\n"
1110                 + "  </script>\n"
1111                 + "</head>\n"
1112                 + "<body>\n"
1113                 + "  <form>\n"
1114                 + htmlPart
1115                 + "    <button id='myTest' type='button' onclick='test()'>Test</button>\n"
1116                 + "    <button id='myButton' type='submit'>Submit</button>\n"
1117                 + "  </form>\n"
1118                 + "</body></html>";
1119 
1120         final String secondContent = DOCTYPE_HTML
1121                 + "<html><head><title>second</title></head><body>\n"
1122                 + "  <p>hello world</p>\n"
1123                 + "</body></html>";
1124 
1125         getMockWebConnection().setResponse(URL_SECOND, secondContent);
1126         expandExpectedAlertsVariables(URL_FIRST);
1127 
1128         final WebDriver driver = loadPage2(html, URL_FIRST);
1129 
1130         final WebElement foo = driver.findElement(By.id("e1"));
1131         if (sendKeys != null) {
1132             foo.sendKeys(sendKeys);
1133         }
1134         assertEquals(getExpectedAlerts()[0], "" + foo.getDomAttribute("value"));
1135         assertEquals(getExpectedAlerts()[1], foo.getDomProperty("value"));
1136 
1137         driver.findElement(By.id("myTest")).click();
1138         verifyTitle2(driver, getExpectedAlerts()[2], getExpectedAlerts()[3], getExpectedAlerts()[4]);
1139 
1140         driver.findElement(By.id("myButton")).click();
1141         if (useRealBrowser()) {
1142             Thread.sleep(400);
1143         }
1144         assertEquals(getExpectedAlerts()[5], getMockWebConnection().getLastWebRequest().getUrl());
1145         assertEquals(Integer.parseInt(getExpectedAlerts()[6]), getMockWebConnection().getRequestCount());
1146     }
1147 
1148     /**
1149      * @throws Exception if an error occurs
1150      */
1151     @Test
1152     @Alerts({"abcx", "", "abcx", "abcx"})
1153     public void clipboard() throws Exception {
1154         Assumptions.assumeFalse(SKIP_);
1155 
1156         final String html = DOCTYPE_HTML
1157                 + "<html><head>\n"
1158                 + "  <script>\n"
1159                 + LOG_TITLE_FUNCTION
1160                 + "    function test() {\n"
1161                 + "      log(document.getElementById('i1').value);\n"
1162                 + "      log(document.getElementById('i2').value);\n"
1163                 + "    }\n"
1164                 + "  </script>\n"
1165                 + "</head>\n"
1166                 + "<body>\n"
1167                 + "  <form>\n"
1168                 + "    <input type='text' id='i1' value='abcx'>\n"
1169                 + "    <input type='text' id='i2'>\n"
1170                 + "  </form>\n"
1171                 + "  <button id='check' onclick='test()'>Test</button>\n"
1172                 + "</body></html>";
1173 
1174         final WebDriver driver = loadPage2(html);
1175         driver.findElement(By.id("check")).click();
1176         verifyTitle2(driver, Arrays.copyOfRange(getExpectedAlerts(), 0, 2));
1177 
1178         driver.findElement(By.id("i1")).sendKeys(Keys.CONTROL + "a");
1179         driver.findElement(By.id("i1")).sendKeys(Keys.CONTROL + "c");
1180 
1181         driver.findElement(By.id("i2")).sendKeys(Keys.CONTROL + "v");
1182         driver.findElement(By.id("check")).click();
1183         verifyTitle2(driver, getExpectedAlerts());
1184     }
1185 }