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