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 org.htmlunit.WebDriverTestCase;
18  import org.htmlunit.junit.annotation.Alerts;
19  import org.htmlunit.junit.annotation.HtmlUnitNYI;
20  import org.junit.jupiter.api.Test;
21  import org.openqa.selenium.By;
22  import org.openqa.selenium.WebDriver;
23  import org.openqa.selenium.WebElement;
24  import org.openqa.selenium.htmlunit.HtmlUnitDriver;
25  
26  /**
27   * Tests for {@link HtmlEmailInput}.
28   *
29   * @author Ahmed Ashour
30   * @author Ronald Brill
31   * @author Anton Demydenko
32   */
33  public class HtmlEmailInputTest extends WebDriverTestCase {
34  
35      /**
36       * @throws Exception if the test fails
37       */
38      @Test
39      @Alerts({"--null", "--null", "--null"})
40      public void defaultValues() throws Exception {
41          final String html = DOCTYPE_HTML
42              + "<html><head>\n"
43              + "<script>\n"
44              + LOG_TITLE_FUNCTION
45              + "  function test() {\n"
46              + "    var input = document.getElementById('text1');\n"
47              + "    log(input.value + '-' + input.defaultValue + '-' + input.getAttribute('value'));\n"
48  
49              + "    try {\n"
50              + "      input = document.createElement('input');\n"
51              + "      input.type = 'email';\n"
52              + "      log(input.value + '-' + input.defaultValue + '-' + input.getAttribute('value'));\n"
53              + "    } catch(e)  { logEx(e); }\n"
54  
55              + "    var builder = document.createElement('div');\n"
56              + "    builder.innerHTML = '<input type=\"email\">';\n"
57              + "    input = builder.firstChild;\n"
58              + "    log(input.value + '-' + input.defaultValue + '-' + input.getAttribute('value'));\n"
59              + "  }\n"
60              + "</script>\n"
61              + "</head><body onload='test()'>\n"
62              + "<form>\n"
63              + "  <input type='email' id='text1'>\n"
64              + "</form>\n"
65              + "</body></html>";
66  
67          loadPageVerifyTitle2(html);
68      }
69  
70      /**
71       * @throws Exception if the test fails
72       */
73      @Test
74      @Alerts({"--null", "--null", "--null"})
75      public void defaultValuesAfterClone() throws Exception {
76          final String html = DOCTYPE_HTML
77              + "<html><head>\n"
78              + "<script>\n"
79              + LOG_TITLE_FUNCTION
80              + "  function test() {\n"
81              + "    var input = document.getElementById('text1');\n"
82              + "    input = input.cloneNode(false);\n"
83              + "    log(input.value + '-' + input.defaultValue + '-' + input.getAttribute('value'));\n"
84  
85              + "    try {\n"
86              + "      input = document.createElement('input');\n"
87              + "      input.type = 'email';\n"
88              + "      input = input.cloneNode(false);\n"
89              + "      log(input.value + '-' + input.defaultValue + '-' + input.getAttribute('value'));\n"
90              + "    } catch(e)  { logEx(e); }\n"
91  
92              + "    var builder = document.createElement('div');\n"
93              + "    builder.innerHTML = '<input type=\"email\">';\n"
94              + "    input = builder.firstChild;\n"
95              + "    input = input.cloneNode(false);\n"
96              + "    log(input.value + '-' + input.defaultValue + '-' + input.getAttribute('value'));\n"
97              + "  }\n"
98              + "</script>\n"
99              + "</head><body onload='test()'>\n"
100             + "<form>\n"
101             + "  <input type='email' id='text1'>\n"
102             + "</form>\n"
103             + "</body></html>";
104 
105         loadPageVerifyTitle2(html);
106     }
107 
108     /**
109      * Verifies getVisibleText().
110      * @throws Exception if the test fails
111      */
112     @Test
113     @Alerts("")
114     public void getVisibleText() throws Exception {
115         final String htmlContent = DOCTYPE_HTML
116             + "<html>\n"
117             + "<head></head>\n"
118             + "<body>\n"
119             + "<form id='form1'>\n"
120             + "  <input type='email' name='tester' id='tester' value='dave@aol.com' >\n"
121             + "</form>\n"
122             + "</body></html>";
123 
124         final WebDriver driver = loadPage2(htmlContent);
125         final String text = driver.findElement(By.id("tester")).getText();
126         assertEquals(getExpectedAlerts()[0], text);
127 
128         if (driver instanceof HtmlUnitDriver) {
129             final HtmlPage page = (HtmlPage) getEnclosedPage();
130             assertEquals(getExpectedAlerts()[0], page.getBody().getVisibleText());
131         }
132     }
133 
134     /**
135      * Verifies clear().
136      * @throws Exception if the test fails
137      */
138     @Test
139     @Alerts({"dave@aol.com", ""})
140     public void clearInput() throws Exception {
141         final String htmlContent = DOCTYPE_HTML
142                 + "<html>\n"
143                 + "<head></head>\n"
144                 + "<body>\n"
145                 + "<form id='form1'>\n"
146                 + "  <input type='email' name='tester' id='tester' value='dave@aol.com'>\n"
147                 + "</form>\n"
148                 + "</body></html>";
149 
150         final WebDriver driver = loadPage2(htmlContent);
151         final WebElement element = driver.findElement(By.id("tester"));
152 
153         assertEquals(getExpectedAlerts()[0], element.getDomAttribute("value"));
154         assertEquals(getExpectedAlerts()[0], element.getDomProperty("value"));
155 
156         element.clear();
157         assertEquals(getExpectedAlerts()[0], element.getDomAttribute("value"));
158         assertEquals(getExpectedAlerts()[1], element.getDomProperty("value"));
159     }
160 
161     /**
162      * @throws Exception if the test fails
163      */
164     @Test
165     @Alerts("hello")
166     public void typing() throws Exception {
167         final String htmlContent = DOCTYPE_HTML
168             + "<html><head><title>foo</title></head><body>\n"
169             + "<form id='form1'>\n"
170             + "  <input type='email' id='foo'>\n"
171             + "</form></body></html>";
172 
173         final WebDriver driver = loadPage2(htmlContent);
174 
175         final WebElement input = driver.findElement(By.id("foo"));
176         input.sendKeys("hello");
177         assertEquals(getExpectedAlerts()[0], input.getDomProperty("value"));
178         assertNull(input.getDomAttribute("value"));
179     }
180 
181     /**
182      * @throws Exception if the test fails
183      */
184     @Test
185     @Alerts("--")
186     public void minMaxStep() throws Exception {
187         final String html = DOCTYPE_HTML
188             + "<html>\n"
189             + "<head>\n"
190             + "<script>\n"
191             + LOG_TITLE_FUNCTION
192             + "  function test() {\n"
193             + "    var input = document.getElementById('tester');\n"
194             + "    log(input.min + '-' + input.max + '-' + input.step);\n"
195             + "  }\n"
196             + "</script>\n"
197             + "</head>\n"
198             + "<body onload='test()'>\n"
199             + "<form>\n"
200             + "  <input type='email' id='tester'>\n"
201             + "</form>\n"
202             + "</body>\n"
203             + "</html>";
204 
205         loadPageVerifyTitle2(html);
206     }
207 
208     /**
209      * @throws Exception if an error occurs
210      */
211     @Test
212     @Alerts({"abc@eemail.com",
213              "abc@eemail.com",
214              "false",
215              "false-false-true-false-false-false-false-false-false-false-false",
216              "true",
217              "§§URL§§", "1"})
218     public void patternValidationInvalid() throws Exception {
219         validation("<input type='email' pattern='.+@email.com' id='e1' name='k' value='abc@eemail.com'>\n",
220                     "", null);
221     }
222 
223     /**
224      * @throws Exception if an error occurs
225      */
226     @Test
227     @Alerts({"abc@email.com",
228              "abc@email.com",
229              "true",
230              "false-false-false-false-false-false-false-false-false-true-false",
231              "true",
232              "§§URL§§?k=abc%40email.com", "2"})
233     public void patternValidationValid() throws Exception {
234         validation("<input type='email' pattern='.+@email.com' "
235                 + "id='e1' name='k' value='abc@email.com'>\n", "", null);
236     }
237 
238     /**
239      * @throws Exception if an error occurs
240      */
241     @Test
242     @Alerts({"",
243              "",
244              "true",
245              "false-false-false-false-false-false-false-false-false-true-false",
246              "true",
247              "§§URL§§?k=", "2"})
248     public void patternValidationEmpty() throws Exception {
249         validation("<input type='email' pattern='.+@email.com' id='e1' name='k' value=''>\n", "", null);
250     }
251 
252     /**
253      * @throws Exception if an error occurs
254      */
255     @Test
256     @Alerts({" ",
257              "",
258              "true",
259              "false-false-false-false-false-false-false-false-false-true-false",
260              "true",
261              "§§URL§§?k=", "2"})
262     public void patternValidationBlank() throws Exception {
263         validation("<input type='email' pattern='.+@email.com' id='e1' name='k' value=' '>\n", "", null);
264     }
265 
266     /**
267      * @throws Exception if an error occurs
268      */
269     @Test
270     @Alerts({"  \t",
271              "",
272              "true",
273              "false-false-false-false-false-false-false-false-false-true-false",
274              "true",
275              "§§URL§§?k=", "2"})
276     public void patternValidationWhitespace() throws Exception {
277         validation("<input type='email' pattern='.+@email.com' id='e1' name='k' value='  \t'>\n", "", null);
278     }
279 
280     /**
281      * @throws Exception if an error occurs
282      */
283     @Test
284     @Alerts({" abc@email.com ",
285              "abc@email.com",
286              "true",
287              "false-false-false-false-false-false-false-false-false-true-false",
288              "true",
289              "§§URL§§?k=abc%40email.com", "2"})
290     public void patternValidationTrimInitial() throws Exception {
291         validation("<input type='email' pattern='.+@email.com' id='e1' name='k' value=' abc@email.com '>\n", "", null);
292     }
293 
294     /**
295      * @throws Exception if an error occurs
296      */
297     @Test
298     @Alerts(DEFAULT = {"null",
299                        "abc@email.com",
300                        "true",
301                        "false-false-false-false-false-false-false-false-false-true-false",
302                        "true",
303                        "§§URL§§?k=abc%40email.com", "2"},
304             FF = {"null",
305                   "abc@email.com",
306                   "false",
307                   "false-false-false-false-false-false-false-false-true-false-false",
308                   "true",
309                   "§§URL§§", "1"},
310             FF_ESR = {"null",
311                       "abc@email.com",
312                       "false",
313                       "false-false-false-false-false-false-false-false-true-false-false",
314                       "true",
315                       "§§URL§§", "1"})
316     @HtmlUnitNYI(FF = {"null",
317                        "abc@email.com",
318                        "true",
319                        "false-false-false-false-false-false-false-false-false-true-false",
320                        "true",
321                        "§§URL§§?k=abc%40email.com", "2"},
322                  FF_ESR = {"null",
323                            "abc@email.com",
324                            "true",
325                            "false-false-false-false-false-false-false-false-false-true-false",
326                            "true",
327                            "§§URL§§?k=abc%40email.com", "2"})
328     public void patternValidationTrimType() throws Exception {
329         validation("<input type='email' pattern='.+@email.com' id='e1' name='k'>\n", "", " abc@email.com");
330     }
331 
332     /**
333      * @throws Exception if an error occurs
334      */
335     @Test
336     @Alerts({"null",
337              "a@b.com",
338              "false",
339              "false-false-false-false-false-false-false-true-false-false-false",
340              "true",
341              "§§URL§§", "1"})
342     public void minLengthValidationInvalid() throws Exception {
343         validation("<input type='email' minlength='10' id='e1' name='k'>\n", "", "a@b.com");
344     }
345 
346     /**
347      * @throws Exception if an error occurs
348      */
349     @Test
350     @Alerts({"a@b.com",
351              "a@b.com",
352              "true",
353              "false-false-false-false-false-false-false-false-false-true-false",
354              "true",
355              "§§URL§§?k=a%40b.com", "2"})
356     public void minLengthValidationInvalidInitial() throws Exception {
357         validation("<input type='email' minlength='10' id='e1' name='k' value='a@b.com'>\n", "", null);
358     }
359 
360     /**
361      * @throws Exception if an error occurs
362      */
363     @Test
364     @Alerts({"null",
365              "",
366              "true",
367              "false-false-false-false-false-false-false-false-false-true-false",
368              "true",
369              "§§URL§§?k=", "2"})
370     public void minLengthValidationInvalidNoInitial() throws Exception {
371         validation("<input type='email' minlength='10' id='e1' name='k'>\n", "", null);
372     }
373 
374     /**
375      * @throws Exception if an error occurs
376      */
377     @Test
378     @Alerts({"null",
379              "a@b.com",
380              "true",
381              "false-false-false-false-false-false-false-false-false-true-false",
382              "true",
383              "§§URL§§?k=a%40b.com", "2"})
384     public void minLengthValidationValid() throws Exception {
385         validation("<input type='email' minlength='5' id='e1' name='k'>\n", "", "a@b.com");
386     }
387 
388     /**
389      * @throws Exception if an error occurs
390      */
391     @Test
392     @Alerts({"null",
393              "a@b.c",
394              "true",
395              "false-false-false-false-false-false-false-false-false-true-false",
396              "true",
397              "§§URL§§?k=a%40b.c", "2"})
398     public void maxLengthValidationValid() throws Exception {
399         validation("<input type='email' maxlength='5' id='e1' name='k'>\n", "", "a@b.com");
400     }
401 
402     /**
403      * @throws Exception if an error occurs
404      */
405     @Test
406     @Alerts({"null",
407              "a@ema",
408              "true",
409              "false-false-false-false-false-false-false-false-false-true-false",
410              "true",
411              "§§URL§§?k=a%40ema", "2"})
412     public void maxLengthValidationInvalid() throws Exception {
413         validation("<input type='email' maxlength='5' id='e1' name='k'>\n", "", "a@email.com");
414     }
415 
416     /**
417      * @throws Exception if an error occurs
418      */
419     @Test
420     @Alerts({"a@email.com",
421              "a@email.com",
422              "true",
423              "false-false-false-false-false-false-false-false-false-true-false",
424              "true",
425              "§§URL§§?k=a%40email.com", "2"})
426     public void maxLengthValidationInvalidInitial() throws Exception {
427         validation("<input type='email' maxlength='5' id='e1' name='k' value='a@email.com'>\n", "", null);
428     }
429 
430     /**
431      * @throws Exception if an error occurs
432      */
433     @Test
434     @Alerts({"true", "false", "true", "false", "true"})
435     public void willValidate() throws Exception {
436         final String html = DOCTYPE_HTML
437                 + "<html><head>\n"
438                 + "  <script>\n"
439                 + LOG_TITLE_FUNCTION
440                 + "    function test() {\n"
441                 + "      log(document.getElementById('o1').willValidate);\n"
442                 + "      log(document.getElementById('o2').willValidate);\n"
443                 + "      log(document.getElementById('o3').willValidate);\n"
444                 + "      log(document.getElementById('o4').willValidate);\n"
445                 + "      log(document.getElementById('o5').willValidate);\n"
446                 + "    }\n"
447                 + "  </script>\n"
448                 + "</head>\n"
449                 + "<body onload='test()'>\n"
450                 + "  <form>\n"
451                 + "    <input type='email' id='o1'>\n"
452                 + "    <input type='email' id='o2' disabled>\n"
453                 + "    <input type='email' id='o3' hidden>\n"
454                 + "    <input type='email' id='o4' readonly>\n"
455                 + "    <input type='email' id='o5' style='display: none'>\n"
456                 + "  </form>\n"
457                 + "</body></html>";
458 
459         loadPageVerifyTitle2(html);
460     }
461 
462     /**
463      * @throws Exception if an error occurs
464      */
465     @Test
466     @Alerts({"null",
467              "",
468              "true",
469              "false-false-false-false-false-false-false-false-false-true-false",
470              "true",
471              "§§URL§§?k=", "2"})
472     public void validationEmpty() throws Exception {
473         validation("<input type='email' id='e1' name='k'>\n", "", null);
474     }
475 
476     /**
477      * @throws Exception if an error occurs
478      */
479     @Test
480     @Alerts({"null",
481              "",
482              "false",
483              "false-true-false-false-false-false-false-false-false-false-false",
484              "true",
485              "§§URL§§", "1"})
486     public void validationCustomValidity() throws Exception {
487         validation("<input type='email' id='e1' name='k'>\n", "elem.setCustomValidity('Invalid');", null);
488     }
489 
490     /**
491      * @throws Exception if an error occurs
492      */
493     @Test
494     @Alerts({"null",
495              "",
496              "false",
497              "false-true-false-false-false-false-false-false-false-false-false",
498              "true",
499              "§§URL§§", "1"})
500     public void validationBlankCustomValidity() throws Exception {
501         validation("<input type='email' id='e1' name='k'>\n", "elem.setCustomValidity(' ');\n", null);
502     }
503 
504     /**
505      * @throws Exception if an error occurs
506      */
507     @Test
508     @Alerts({"null",
509              "",
510              "true",
511              "false-false-false-false-false-false-false-false-false-true-false",
512              "true",
513              "§§URL§§?k=", "2"})
514     public void validationResetCustomValidity() throws Exception {
515         validation("<input type='email' id='e1' name='k'>\n",
516                 "elem.setCustomValidity('Invalid');elem.setCustomValidity('');", null);
517     }
518 
519     /**
520      * @throws Exception if an error occurs
521      */
522     @Test
523     @Alerts({"null",
524              "",
525              "false",
526              "false-false-false-false-false-false-false-false-false-false-true",
527              "true",
528              "§§URL§§", "1"})
529     public void validationRequired() throws Exception {
530         validation("<input type='email' id='e1' name='k' required>\n", "", null);
531     }
532 
533     /**
534      * @throws Exception if an error occurs
535      */
536     @Test
537     @Alerts({"null",
538              "",
539              "true",
540              "false-false-false-false-false-false-false-false-false-true-false",
541              "true",
542              "§§URL§§?k=test%40abc.edu", "2"})
543     public void validationRequiredValueSet() throws Exception {
544         validation("<input type='email' id='e1' name='k' required>\n", "elem.value='test@abc.edu';", null);
545     }
546 
547     /**
548      * @throws Exception if an error occurs
549      */
550     @Test
551     @Alerts({"null",
552              "",
553              "false",
554              "false-false-true-false-false-false-false-false-false-false-false",
555              "true",
556              "§§URL§§", "1"})
557     public void validationPattern() throws Exception {
558         validation("<input type='email' id='e1' name='k' pattern='.+@email.com'>\n",
559                     "elem.value='abc@eemail.com';", null);
560     }
561 
562     private void validation(final String htmlPart, final String jsPart, final String sendKeys) throws Exception {
563         final String html = DOCTYPE_HTML
564                 + "<html><head>\n"
565                 + "  <script>\n"
566                 + LOG_TITLE_FUNCTION
567                 + "    function logValidityState(s) {\n"
568                 + "      log(s.badInput"
569                         + "+ '-' + s.customError"
570                         + "+ '-' + s.patternMismatch"
571                         + "+ '-' + s.rangeOverflow"
572                         + "+ '-' + s.rangeUnderflow"
573                         + "+ '-' + s.stepMismatch"
574                         + "+ '-' + s.tooLong"
575                         + "+ '-' + s.tooShort"
576                         + " + '-' + s.typeMismatch"
577                         + " + '-' + s.valid"
578                         + " + '-' + s.valueMissing);\n"
579                 + "    }\n"
580                 + "    function test() {\n"
581                 + "      var elem = document.getElementById('e1');\n"
582                 + jsPart
583                 + "      log(elem.checkValidity());\n"
584                 + "      logValidityState(elem.validity);\n"
585                 + "      log(elem.willValidate);\n"
586                 + "    }\n"
587                 + "  </script>\n"
588                 + "</head>\n"
589                 + "<body>\n"
590                 + "  <form>\n"
591                 + htmlPart
592                 + "    <button id='myTest' type='button' onclick='test()'>Test</button>\n"
593                 + "    <button id='myButton' type='submit'>Submit</button>\n"
594                 + "  </form>\n"
595                 + "</body></html>";
596 
597         final String secondContent = DOCTYPE_HTML
598                 + "<html><head><title>second</title></head><body>\n"
599                 + "  <p>hello world</p>\n"
600                 + "</body></html>";
601 
602         getMockWebConnection().setResponse(URL_SECOND, secondContent);
603         expandExpectedAlertsVariables(URL_FIRST);
604 
605         final WebDriver driver = loadPage2(html, URL_FIRST);
606 
607         final WebElement foo = driver.findElement(By.id("e1"));
608         if (sendKeys != null) {
609             foo.sendKeys(sendKeys);
610         }
611         assertEquals(getExpectedAlerts()[0], "" + foo.getDomAttribute("value"));
612         assertEquals(getExpectedAlerts()[1], foo.getDomProperty("value"));
613 
614         driver.findElement(By.id("myTest")).click();
615         verifyTitle2(driver, getExpectedAlerts()[2], getExpectedAlerts()[3], getExpectedAlerts()[4]);
616 
617         driver.findElement(By.id("myButton")).click();
618         if (useRealBrowser()) {
619             Thread.sleep(400);
620         }
621         assertEquals(getExpectedAlerts()[5], getMockWebConnection().getLastWebRequest().getUrl());
622         assertEquals(Integer.parseInt(getExpectedAlerts()[6]), getMockWebConnection().getRequestCount());
623     }
624 }