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 HtmlUrlInput}.
27   *
28   * @author Ahmed Ashour
29   * @author Ronald Brill
30   * @author Anton Demydenko
31   */
32  public class HtmlUrlInputTest 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 = 'url';\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=\"url\">';\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='url' 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 = 'url';\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=\"url\">';\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='url' 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='url' id='tester' value='http://htmlunit.sourceforge.net'>\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({"https://htmlunit.org", ""})
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='url' name='tester' id='tester' value='https://htmlunit.org'>\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='url' 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='url' 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({"http://example.com",
212              "http://example.com",
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='url' pattern='.*test.*' id='e1' value='http://example.com' name='k'>\n",
219                     "", null);
220     }
221 
222     /**
223      * @throws Exception if an error occurs
224      */
225     @Test
226     @Alerts({"http://test.com",
227              "http://test.com",
228              "true",
229              "false-false-false-false-false-false-false-false-false-true-false",
230              "true",
231              "§§URL§§?k=http%3A%2F%2Ftest.com", "2"})
232     public void patternValidationValid() throws Exception {
233         validation("<input type='url' pattern='.*test.*' id='e1' value='http://test.com' name='k'>\n", "", null);
234     }
235 
236     /**
237      * @throws Exception if an error occurs
238      */
239     @Test
240     @Alerts({"",
241              "",
242              "true",
243              "false-false-false-false-false-false-false-false-false-true-false",
244              "true",
245              "§§URL§§?k=", "2"})
246     public void patternValidationEmpty() throws Exception {
247         validation("<input type='url' pattern='.*test.*' id='e1' name='k' value=''>\n", "", null);
248     }
249 
250     /**
251      * @throws Exception if an error occurs
252      */
253     @Test
254     @Alerts({" ",
255              "",
256              "true",
257              "false-false-false-false-false-false-false-false-false-true-false",
258              "true",
259              "§§URL§§?k=", "2"})
260     public void patternValidationBlank() throws Exception {
261         validation("<input type='url' pattern='.*test.*' id='e1' name='k' value=' '>\n", "", null);
262     }
263 
264     /**
265      * @throws Exception if an error occurs
266      */
267     @Test
268     @Alerts({"  \t",
269              "",
270              "true",
271              "false-false-false-false-false-false-false-false-false-true-false",
272              "true",
273              "§§URL§§?k=", "2"})
274     public void patternValidationWhitespace() throws Exception {
275         validation("<input type='url' pattern='.*test.*' id='e1' name='k' value='  \t'>\n", "", null);
276     }
277 
278     /**
279      * @throws Exception if an error occurs
280      */
281     @Test
282     @Alerts({" http://test.com ",
283              "http://test.com",
284              "true",
285              "false-false-false-false-false-false-false-false-false-true-false",
286              "true",
287              "§§URL§§?k=http%3A%2F%2Ftest.com", "2"})
288     public void patternValidationTrimInitial() throws Exception {
289         validation("<input type='url' pattern='.*test.*' id='e1' name='k' value=' http://test.com '>\n", "", null);
290     }
291 
292     /**
293      * @throws Exception if an error occurs
294      */
295     @Test
296     @Alerts({"null",
297              " http://test.com ",
298              "true",
299              "false-false-false-false-false-false-false-false-false-true-false",
300              "true",
301              "§§URL§§?k=+http%3A%2F%2Ftest.com+", "2"})
302     public void patternValidationTrimType() throws Exception {
303         validation("<input type='url' pattern='.*test.*' id='e1' name='k'>\n", "", " http://test.com ");
304     }
305 
306     /**
307      * @throws Exception if an error occurs
308      */
309     @Test
310     @Alerts({"null",
311              "http://example.com",
312              "false",
313              "false-false-false-false-false-false-false-true-false-false-false",
314              "true",
315              "§§URL§§", "1"})
316     public void minLengthValidationInvalid() throws Exception {
317         validation("<input type='url' minlength='20' id='e1' name='k'>\n", "", "http://example.com");
318     }
319 
320     /**
321      * @throws Exception if an error occurs
322      */
323     @Test
324     @Alerts({"http://example.com",
325              "http://example.com",
326              "true",
327              "false-false-false-false-false-false-false-false-false-true-false",
328              "true",
329              "§§URL§§?k=http%3A%2F%2Fexample.com", "2"})
330     public void minLengthValidationInvalidInitial() throws Exception {
331         validation("<input type='url' minlength='20' id='e1' name='k' value='http://example.com'>\n", "", null);
332     }
333 
334     /**
335      * @throws Exception if an error occurs
336      */
337     @Test
338     @Alerts({"null",
339              "",
340              "true",
341              "false-false-false-false-false-false-false-false-false-true-false",
342              "true",
343              "§§URL§§?k=", "2"})
344     public void minLengthValidationInvalidNoInitial() throws Exception {
345         validation("<input type='url' minlength='20' id='e1' name='k'>\n", "", null);
346     }
347 
348     /**
349      * @throws Exception if an error occurs
350      */
351     @Test
352     @Alerts({"null",
353              "http://example.com/test",
354              "true",
355              "false-false-false-false-false-false-false-false-false-true-false",
356              "true",
357              "§§URL§§?k=http%3A%2F%2Fexample.com%2Ftest", "2"})
358     public void minLengthValidationValid() throws Exception {
359         validation("<input type='url' minlength='20' id='e1' name='k'>\n", "", "http://example.com/test");
360     }
361 
362     /**
363      * @throws Exception if an error occurs
364      */
365     @Test
366     @Alerts({"null",
367              "http://example.com",
368              "true",
369              "false-false-false-false-false-false-false-false-false-true-false",
370              "true",
371              "§§URL§§?k=http%3A%2F%2Fexample.com", "2"})
372     public void maxLengthValidationValid() throws Exception {
373         validation("<input type='url' maxlength='20' id='e1' name='k'>\n", "", "http://example.com");
374     }
375 
376     /**
377      * @throws Exception if an error occurs
378      */
379     @Test
380     @Alerts({"null",
381              "http://example.com/t",
382              "true",
383              "false-false-false-false-false-false-false-false-false-true-false",
384              "true",
385              "§§URL§§?k=http%3A%2F%2Fexample.com%2Ft", "2"})
386     public void maxLengthValidationInvalid() throws Exception {
387         validation("<input type='url' maxlength='20' id='e1' name='k'>\n", "", "http://example.com/test");
388     }
389 
390     /**
391      * @throws Exception if an error occurs
392      */
393     @Test
394     @Alerts({"http://example.com/test",
395              "http://example.com/test",
396              "true",
397              "false-false-false-false-false-false-false-false-false-true-false",
398              "true",
399              "§§URL§§?k=http%3A%2F%2Fexample.com%2Ftest", "2"})
400     public void maxLengthValidationInvalidInitial() throws Exception {
401         validation("<input type='url' maxlength='20' "
402                     + "id='e1' name='k' value='http://example.com/test'>\n", "", null);
403     }
404 
405     /**
406      * @throws Exception if an error occurs
407      */
408     @Test
409     @Alerts({"true", "false", "true", "false", "true"})
410     public void willValidate() throws Exception {
411         final String html = DOCTYPE_HTML
412                 + "<html><head>\n"
413                 + "  <script>\n"
414                 + LOG_TITLE_FUNCTION
415                 + "    function test() {\n"
416                 + "      log(document.getElementById('o1').willValidate);\n"
417                 + "      log(document.getElementById('o2').willValidate);\n"
418                 + "      log(document.getElementById('o3').willValidate);\n"
419                 + "      log(document.getElementById('o4').willValidate);\n"
420                 + "      log(document.getElementById('o5').willValidate);\n"
421                 + "    }\n"
422                 + "  </script>\n"
423                 + "</head>\n"
424                 + "<body onload='test()'>\n"
425                 + "  <form>\n"
426                 + "    <input type='url' id='o1'>\n"
427                 + "    <input type='url' id='o2' disabled>\n"
428                 + "    <input type='url' id='o3' hidden>\n"
429                 + "    <input type='url' id='o4' readonly>\n"
430                 + "    <input type='url' id='o5' style='display: none'>\n"
431                 + "  </form>\n"
432                 + "</body></html>";
433 
434         loadPageVerifyTitle2(html);
435     }
436 
437     /**
438      * @throws Exception if an error occurs
439      */
440     @Test
441     @Alerts({"null",
442              "",
443              "true",
444              "false-false-false-false-false-false-false-false-false-true-false",
445              "true",
446              "§§URL§§?k=", "2"})
447     public void validationEmpty() throws Exception {
448         validation("<input type='url' id='e1' name='k'>\n", "", null);
449     }
450 
451     /**
452      * @throws Exception if an error occurs
453      */
454     @Test
455     @Alerts({"null",
456              "",
457              "false",
458              "false-true-false-false-false-false-false-false-false-false-false",
459              "true",
460              "§§URL§§", "1"})
461     public void validationCustomValidity() throws Exception {
462         validation("<input type='url' id='e1' name='k'>\n", "elem.setCustomValidity('Invalid');", null);
463     }
464 
465     /**
466      * @throws Exception if an error occurs
467      */
468     @Test
469     @Alerts({"null",
470              "",
471              "false",
472              "false-true-false-false-false-false-false-false-false-false-false",
473              "true",
474              "§§URL§§", "1"})
475     public void validationBlankCustomValidity() throws Exception {
476         validation("<input type='url' id='e1' name='k'>\n", "elem.setCustomValidity(' ');\n", null);
477     }
478 
479     /**
480      * @throws Exception if an error occurs
481      */
482     @Test
483     @Alerts({"null",
484              "",
485              "true",
486              "false-false-false-false-false-false-false-false-false-true-false",
487              "true",
488              "§§URL§§?k=", "2"})
489     public void validationResetCustomValidity() throws Exception {
490         validation("<input type='url' id='e1' name='k'>\n",
491                 "elem.setCustomValidity('Invalid');elem.setCustomValidity('');", null);
492     }
493 
494     /**
495      * @throws Exception if an error occurs
496      */
497     @Test
498     @Alerts({"null",
499              "",
500              "false",
501              "false-false-false-false-false-false-false-false-false-false-true",
502              "true",
503              "§§URL§§", "1"})
504     public void validationRequired() throws Exception {
505         validation("<input type='url' id='e1' name='k' required>\n", "", null);
506     }
507 
508     /**
509      * @throws Exception if an error occurs
510      */
511     @Test
512     @Alerts({"null",
513              "",
514              "true",
515              "false-false-false-false-false-false-false-false-false-true-false",
516              "true",
517              "§§URL§§?k=http%3A%2F%2Fexample.com", "2"})
518     public void validationRequiredValueSet() throws Exception {
519         validation("<input type='url' id='e1' name='k' required>\n", "elem.value='http://example.com';", null);
520     }
521 
522     /**
523      * @throws Exception if an error occurs
524      */
525     @Test
526     @Alerts({"null",
527              "",
528              "false",
529              "false-false-true-false-false-false-false-false-false-false-false",
530              "true",
531              "§§URL§§", "1"})
532     public void validationPattern() throws Exception {
533         validation("<input type='url' id='e1' name='k' pattern='.*test.*'>\n",
534                     "elem.value='http://example.com';", null);
535     }
536 
537     private void validation(final String htmlPart, final String jsPart, final String sendKeys) throws Exception {
538         final String html = DOCTYPE_HTML
539                 + "<html><head>\n"
540                 + "  <script>\n"
541                 + LOG_TITLE_FUNCTION
542                 + "    function logValidityState(s) {\n"
543                 + "      log(s.badInput"
544                         + "+ '-' + s.customError"
545                         + "+ '-' + s.patternMismatch"
546                         + "+ '-' + s.rangeOverflow"
547                         + "+ '-' + s.rangeUnderflow"
548                         + "+ '-' + s.stepMismatch"
549                         + "+ '-' + s.tooLong"
550                         + "+ '-' + s.tooShort"
551                         + " + '-' + s.typeMismatch"
552                         + " + '-' + s.valid"
553                         + " + '-' + s.valueMissing);\n"
554                 + "    }\n"
555                 + "    function test() {\n"
556                 + "      var elem = document.getElementById('e1');\n"
557                 + jsPart
558                 + "      log(elem.checkValidity());\n"
559                 + "      logValidityState(elem.validity);\n"
560                 + "      log(elem.willValidate);\n"
561                 + "    }\n"
562                 + "  </script>\n"
563                 + "</head>\n"
564                 + "<body>\n"
565                 + "  <form>\n"
566                 + htmlPart
567                 + "    <button id='myTest' type='button' onclick='test()'>Test</button>\n"
568                 + "    <button id='myButton' type='submit'>Submit</button>\n"
569                 + "  </form>\n"
570                 + "</body></html>";
571 
572         final String secondContent = DOCTYPE_HTML
573                 + "<html><head><title>second</title></head><body>\n"
574                 + "  <p>hello world</p>\n"
575                 + "</body></html>";
576 
577         getMockWebConnection().setResponse(URL_SECOND, secondContent);
578         expandExpectedAlertsVariables(URL_FIRST);
579 
580         final WebDriver driver = loadPage2(html, URL_FIRST);
581 
582         final WebElement foo = driver.findElement(By.id("e1"));
583         if (sendKeys != null) {
584             foo.sendKeys(sendKeys);
585         }
586         assertEquals(getExpectedAlerts()[0], "" + foo.getDomAttribute("value"));
587         assertEquals(getExpectedAlerts()[1], foo.getDomProperty("value"));
588 
589         driver.findElement(By.id("myTest")).click();
590         verifyTitle2(driver, getExpectedAlerts()[2], getExpectedAlerts()[3], getExpectedAlerts()[4]);
591 
592         driver.findElement(By.id("myButton")).click();
593         if (useRealBrowser()) {
594             Thread.sleep(400);
595         }
596         assertEquals(getExpectedAlerts()[5], getMockWebConnection().getLastWebRequest().getUrl());
597         assertEquals(Integer.parseInt(getExpectedAlerts()[6]), getMockWebConnection().getRequestCount());
598     }
599 }