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.BuggyWebDriver;
21  import org.htmlunit.junit.annotation.HtmlUnitNYI;
22  import org.junit.Test;
23  import org.junit.runner.RunWith;
24  import org.openqa.selenium.By;
25  import org.openqa.selenium.InvalidElementStateException;
26  import org.openqa.selenium.WebDriver;
27  import org.openqa.selenium.WebElement;
28  import org.openqa.selenium.htmlunit.HtmlUnitDriver;
29  
30  /**
31   * Tests for {@link HtmlTimeInput}.
32   *
33   * @author Ronald Brill
34   * @author Anton Demydenko
35   */
36  @RunWith(BrowserRunner.class)
37  public class HtmlTimeInputTest extends WebDriverTestCase {
38  
39      /**
40       * @throws Exception if the test fails
41       */
42      @Test
43      @Alerts({"--null", "--null", "--null"})
44      public void defaultValues() throws Exception {
45          final String html = DOCTYPE_HTML
46              + "<html><head>\n"
47              + "<script>\n"
48              + LOG_TITLE_FUNCTION
49              + "  function test() {\n"
50              + "    var input = document.getElementById('text1');\n"
51              + "    log(input.value + '-' + input.defaultValue + '-' + input.getAttribute('value'));\n"
52  
53              + "    try {\n"
54              + "      input = document.createElement('input');\n"
55              + "      input.type = 'time';\n"
56              + "      log(input.value + '-' + input.defaultValue + '-' + input.getAttribute('value'));\n"
57              + "    } catch(e)  { logEx(e); }\n"
58  
59              + "    var builder = document.createElement('div');\n"
60              + "    builder.innerHTML = '<input type=\"time\">';\n"
61              + "    input = builder.firstChild;\n"
62              + "    log(input.value + '-' + input.defaultValue + '-' + input.getAttribute('value'));\n"
63              + "  }\n"
64              + "</script>\n"
65              + "</head><body onload='test()'>\n"
66              + "<form>\n"
67              + "  <input type='time' id='text1'>\n"
68              + "</form>\n"
69              + "</body></html>";
70  
71          loadPageVerifyTitle2(html);
72      }
73  
74      /**
75       * @throws Exception if the test fails
76       */
77      @Test
78      @Alerts({"--null", "--null", "--null"})
79      public void defaultValuesAfterClone() throws Exception {
80          final String html = DOCTYPE_HTML
81              + "<html><head>\n"
82              + "<script>\n"
83              + LOG_TITLE_FUNCTION
84              + "  function test() {\n"
85              + "    var input = document.getElementById('text1');\n"
86              + "    input = input.cloneNode(false);\n"
87              + "    log(input.value + '-' + input.defaultValue + '-' + input.getAttribute('value'));\n"
88  
89              + "    try {\n"
90              + "      input = document.createElement('input');\n"
91              + "      input.type = 'time';\n"
92              + "      input = input.cloneNode(false);\n"
93              + "      log(input.value + '-' + input.defaultValue + '-' + input.getAttribute('value'));\n"
94              + "    } catch(e)  { logEx(e); }\n"
95  
96              + "    var builder = document.createElement('div');\n"
97              + "    builder.innerHTML = '<input type=\"time\">';\n"
98              + "    input = builder.firstChild;\n"
99              + "    input = input.cloneNode(false);\n"
100             + "    log(input.value + '-' + input.defaultValue + '-' + input.getAttribute('value'));\n"
101             + "  }\n"
102             + "</script>\n"
103             + "</head><body onload='test()'>\n"
104             + "<form>\n"
105             + "  <input type='time' id='text1'>\n"
106             + "</form>\n"
107             + "</body></html>";
108 
109         loadPageVerifyTitle2(html);
110     }
111 
112     /**
113      * @throws Exception if the test fails
114      */
115     @Test
116     @Alerts(DEFAULT = {"", "20:04"},
117             FF = {"08:04", "20:04"},
118             FF_ESR = {"08:04", "20:04"})
119     @BuggyWebDriver(FF = {"08:04", ""},
120             FF_ESR = {"08:04", ""})
121     @HtmlUnitNYI(CHROME = {"08:04", "20:04"},
122             EDGE = {"08:04", "20:04"})
123     public void type() throws Exception {
124         final String htmlContent = DOCTYPE_HTML
125             + "<html><head></head><body>\n"
126             + "<form id='form1'>\n"
127             + "  <input type='time' id='foo'>\n"
128             + "</form></body></html>";
129 
130         final WebDriver driver = loadPage2(htmlContent);
131 
132         final WebElement input = driver.findElement(By.id("foo"));
133 
134         input.sendKeys("0804");
135         assertNull(input.getDomAttribute("value"));
136         assertEquals(getExpectedAlerts()[0], input.getDomProperty("value"));
137 
138         input.sendKeys("PM");
139         assertNull(input.getDomAttribute("value"));
140         assertEquals(getExpectedAlerts()[1], input.getDomProperty("value"));
141     }
142 
143     /**
144      * @throws Exception if the test fails
145      */
146     @Test
147     @Alerts("ex: ")
148     public void typeWhileDisabled() throws Exception {
149         final String html = DOCTYPE_HTML + "<html><body><input type='time' id='p' disabled='disabled'/></body></html>";
150         final WebDriver driver = loadPage2(html);
151         final WebElement p = driver.findElement(By.id("p"));
152         try {
153             p.sendKeys("804PM");
154             assertEquals(getExpectedAlerts()[0], "no ex: " + p.getDomProperty("value"));
155             return;
156         }
157         catch (final InvalidElementStateException e) {
158             // as expected
159         }
160         assertEquals(getExpectedAlerts()[0], "ex: " + p.getDomProperty("value"));
161     }
162 
163     /**
164      * @throws Exception if the test fails
165      */
166     @Test
167     @Alerts({"null", "null"})
168     public void typeDoesNotChangeValueAttribute() throws Exception {
169         final String html = DOCTYPE_HTML
170                 + "<html>\n"
171                 + "<head></head>\n"
172                 + "<body>\n"
173                 + "  <input type='time' id='t'/>\n"
174                 + "  <button id='check' onclick='alert(document.getElementById(\"t\").getAttribute(\"value\"));'>"
175                         + "DoIt</button>\n"
176                 + "</body></html>";
177 
178         final WebDriver driver = loadPage2(html);
179         final WebElement t = driver.findElement(By.id("t"));
180 
181         final WebElement check = driver.findElement(By.id("check"));
182         check.click();
183         verifyAlerts(driver, getExpectedAlerts()[0]);
184 
185         t.sendKeys("0804PM");
186         check.click();
187         verifyAlerts(driver, getExpectedAlerts()[1]);
188     }
189 
190     /**
191      * @throws Exception if the test fails
192      */
193     @Test
194     @Alerts({"20:04", "20:04"})
195     public void typeDoesNotChangeValueAttributeWithInitialValue() throws Exception {
196         final String html = DOCTYPE_HTML
197                 + "<html>\n"
198                 + "<head></head>\n"
199                 + "<body>\n"
200                 + "  <input type='time' id='t' value='20:04'/>\n"
201                 + "  <button id='check' onclick='alert(document.getElementById(\"t\").getAttribute(\"value\"));'>"
202                         + "DoIt</button>\n"
203                 + "</body></html>";
204 
205         final WebDriver driver = loadPage2(html);
206         final WebElement t = driver.findElement(By.id("t"));
207 
208         final WebElement check = driver.findElement(By.id("check"));
209         check.click();
210         verifyAlerts(driver, getExpectedAlerts()[0]);
211 
212         t.sendKeys("0905AM");
213         check.click();
214         verifyAlerts(driver, getExpectedAlerts()[1]);
215     }
216 
217     /**
218      * Verifies getVisibleText().
219      * @throws Exception if the test fails
220      */
221     @Test
222     @Alerts("")
223     public void getVisibleText() throws Exception {
224         final String htmlContent = DOCTYPE_HTML
225             + "<html>\n"
226             + "<head></head>\n"
227             + "<body>\n"
228             + "<form id='form1'>\n"
229             + "  <input type='time' name='tester' id='tester' value='11:55' min='09:00' max='18:00'>\n"
230             + "</form>\n"
231             + "</body></html>";
232 
233         final WebDriver driver = loadPage2(htmlContent);
234         final String text = driver.findElement(By.id("tester")).getText();
235         assertEquals(getExpectedAlerts()[0], text);
236 
237         if (driver instanceof HtmlUnitDriver) {
238             final HtmlPage page = (HtmlPage) getEnclosedPage();
239             assertEquals(getExpectedAlerts()[0], page.getBody().getVisibleText());
240         }
241     }
242 
243     /**
244      * Verifies clear().
245      * @throws Exception if the test fails
246      */
247     @Test
248     @Alerts({"11:55", ""})
249     public void clearInput() throws Exception {
250         final String htmlContent = DOCTYPE_HTML
251                 + "<html>\n"
252                 + "<head></head>\n"
253                 + "<body>\n"
254                 + "<form id='form1'>\n"
255                 + "  <input type='time' name='tester' id='tester' value='11:55'>\n"
256                 + "</form>\n"
257                 + "</body></html>";
258 
259         final WebDriver driver = loadPage2(htmlContent);
260         final WebElement element = driver.findElement(By.id("tester"));
261 
262         assertEquals(getExpectedAlerts()[0], element.getDomAttribute("value"));
263         assertEquals(getExpectedAlerts()[0], element.getDomProperty("value"));
264 
265         element.clear();
266         assertEquals(getExpectedAlerts()[0], element.getDomAttribute("value"));
267         assertEquals(getExpectedAlerts()[1], element.getDomProperty("value"));
268     }
269 
270     /**
271      * @throws Exception if the test fails
272      */
273     @Test
274     @Alerts("--")
275     public void minMaxStep() throws Exception {
276         final String html = DOCTYPE_HTML
277             + "<html>\n"
278             + "<head>\n"
279             + "<script>\n"
280             + LOG_TITLE_FUNCTION
281             + "  function test() {\n"
282             + "    var input = document.getElementById('tester');\n"
283             + "    log(input.min + '-' + input.max + '-' + input.step);\n"
284             + "  }\n"
285             + "</script>\n"
286             + "</head>\n"
287             + "<body onload='test()'>\n"
288             + "<form>\n"
289             + "  <input type='time' id='tester'>\n"
290             + "</form>\n"
291             + "</body>\n"
292             + "</html>";
293 
294         loadPageVerifyTitle2(html);
295     }
296 
297     /**
298      * @throws Exception if an error occurs
299      */
300     @Test
301     @Alerts("true-false-true-true-true-true")
302     public void minValidation() throws Exception {
303         final String html = DOCTYPE_HTML
304             + "<html>\n"
305             + "<head>\n"
306             + "<script>\n"
307             + LOG_TITLE_FUNCTION
308             + "  function test() {\n"
309             + "    log(document.getElementById('id1').checkValidity() + '-'\n"
310             + "         + document.getElementById('id2').checkValidity() + '-'\n"
311             + "         + document.getElementById('id3').checkValidity() + '-'\n"
312             + "         + document.getElementById('id4').checkValidity() + '-'\n"
313             + "         + document.getElementById('id5').checkValidity() + '-'\n"
314             + "         + document.getElementById('id6').checkValidity());\n"
315             + "  }\n"
316             + "</script>\n"
317             + "</head>\n"
318             + "<body onload='test()'>\n"
319             + "  <input type='time' id='id1' min='09:00'>\n"
320             + "  <input type='time' id='id2' min='09:00' value='08:00'>\n"
321             + "  <input type='time' id='id3' min='09:00' value='09:00'>\n"
322             + "  <input type='time' id='id4' min='09:00' value='10:00'>\n"
323             + "  <input type='time' id='id5' value='09:00'>\n"
324             + "  <input type='time' id='id6' min='foo' value='09:00'>\n"
325             + "</body>\n"
326             + "</html>";
327 
328         loadPageVerifyTitle2(html);
329     }
330 
331     /**
332      * @throws Exception if an error occurs
333      */
334     @Test
335     @Alerts("true-true-true-false-true-true")
336     public void maxValidation() throws Exception {
337         final String html = DOCTYPE_HTML
338             + "<html>\n"
339             + "<head>\n"
340             + "<script>\n"
341             + LOG_TITLE_FUNCTION
342             + "  function test() {\n"
343             + "    log(document.getElementById('id1').checkValidity() + '-'\n"
344             + "         + document.getElementById('id2').checkValidity() + '-'\n"
345             + "         + document.getElementById('id3').checkValidity() + '-'\n"
346             + "         + document.getElementById('id4').checkValidity() + '-'\n"
347             + "         + document.getElementById('id5').checkValidity() + '-'\n"
348             + "         + document.getElementById('id6').checkValidity());\n"
349             + "  }\n"
350             + "</script>\n"
351             + "</head>\n"
352             + "<body onload='test()'>\n"
353             + "  <input type='time' id='id1' max='09:00'>\n"
354             + "  <input type='time' id='id2' max='09:00' value='08:00'>\n"
355             + "  <input type='time' id='id3' max='09:00' value='09:00'>\n"
356             + "  <input type='time' id='id4' max='09:00' value='10:00'>\n"
357             + "  <input type='time' id='id5' value='09:00'>\n"
358             + "  <input type='time' id='id6' max='foo' value='09:00'>\n"
359             + "</body>\n"
360             + "</html>";
361 
362         loadPageVerifyTitle2(html);
363     }
364 
365     /**
366      * @throws Exception if an error occurs
367      */
368     @Test
369     @Alerts({"true", "false", "true", "false", "true"})
370     public void willValidate() throws Exception {
371         final String html = DOCTYPE_HTML
372                 + "<html><head>\n"
373                 + "  <script>\n"
374                 + LOG_TITLE_FUNCTION
375                 + "    function test() {\n"
376                 + "      log(document.getElementById('o1').willValidate);\n"
377                 + "      log(document.getElementById('o2').willValidate);\n"
378                 + "      log(document.getElementById('o3').willValidate);\n"
379                 + "      log(document.getElementById('o4').willValidate);\n"
380                 + "      log(document.getElementById('o5').willValidate);\n"
381                 + "    }\n"
382                 + "  </script>\n"
383                 + "</head>\n"
384                 + "<body onload='test()'>\n"
385                 + "  <form>\n"
386                 + "    <input type='time' id='o1'>\n"
387                 + "    <input type='time' id='o2' disabled>\n"
388                 + "    <input type='time' id='o3' hidden>\n"
389                 + "    <input type='time' id='o4' readonly>\n"
390                 + "    <input type='time' id='o5' style='display: none'>\n"
391                 + "  </form>\n"
392                 + "</body></html>";
393 
394         loadPageVerifyTitle2(html);
395     }
396 
397     /**
398      * @throws Exception if an error occurs
399      */
400     @Test
401     @Alerts({"true",
402              "false-false-false-false-false-false-false-false-false-true-false",
403              "true"})
404     public void validationEmpty() throws Exception {
405         validation("<input type='time' id='e1'>\n", "");
406     }
407 
408     /**
409      * @throws Exception if an error occurs
410      */
411     @Test
412     @Alerts({"false",
413              "false-true-false-false-false-false-false-false-false-false-false",
414              "true"})
415     public void validationCustomValidity() throws Exception {
416         validation("<input type='time' id='e1'>\n", "elem.setCustomValidity('Invalid');");
417     }
418 
419     /**
420      * @throws Exception if an error occurs
421      */
422     @Test
423     @Alerts({"false",
424              "false-true-false-false-false-false-false-false-false-false-false",
425              "true"})
426     public void validationBlankCustomValidity() throws Exception {
427         validation("<input type='time' id='e1'>\n", "elem.setCustomValidity(' ');\n");
428     }
429 
430     /**
431      * @throws Exception if an error occurs
432      */
433     @Test
434     @Alerts({"true",
435              "false-false-false-false-false-false-false-false-false-true-false",
436              "true"})
437     public void validationResetCustomValidity() throws Exception {
438         validation("<input type='time' id='e1'>\n",
439                 "elem.setCustomValidity('Invalid');elem.setCustomValidity('');");
440     }
441 
442     /**
443      * @throws Exception if an error occurs
444      */
445     @Test
446     @Alerts({"false",
447              "false-false-false-false-false-false-false-false-false-false-true",
448              "true"})
449     public void validationRequired() throws Exception {
450         validation("<input type='time' id='e1' required>\n", "");
451     }
452 
453     /**
454      * @throws Exception if an error occurs
455      */
456     @Test
457     @Alerts({"true",
458              "false-false-false-false-false-false-false-false-false-true-false",
459              "true"})
460     public void validationRequiredValueSet() throws Exception {
461         validation("<input type='time' id='e1' required>\n", "elem.value='10:00';");
462     }
463 
464     private void validation(final String htmlPart, final String jsPart) throws Exception {
465         final String html = DOCTYPE_HTML
466                 + "<html><head>\n"
467                 + "  <script>\n"
468                 + LOG_TITLE_FUNCTION
469                 + "    function logValidityState(s) {\n"
470                 + "      log(s.badInput"
471                         + "+ '-' + s.customError"
472                         + "+ '-' + s.patternMismatch"
473                         + "+ '-' + s.rangeOverflow"
474                         + "+ '-' + s.rangeUnderflow"
475                         + "+ '-' + s.stepMismatch"
476                         + "+ '-' + s.tooLong"
477                         + "+ '-' + s.tooShort"
478                         + " + '-' + s.typeMismatch"
479                         + " + '-' + s.valid"
480                         + " + '-' + s.valueMissing);\n"
481                 + "    }\n"
482                 + "    function test() {\n"
483                 + "      var elem = document.getElementById('e1');\n"
484                 + jsPart
485                 + "      log(elem.checkValidity());\n"
486                 + "      logValidityState(elem.validity);\n"
487                 + "      log(elem.willValidate);\n"
488                 + "    }\n"
489                 + "  </script>\n"
490                 + "</head>\n"
491                 + "<body onload='test()'>\n"
492                 + "  <form>\n"
493                 + htmlPart
494                 + "  </form>\n"
495                 + "</body></html>";
496 
497         loadPageVerifyTitle2(html);
498     }
499 }