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