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