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