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 java.net.URL;
18  import java.util.Arrays;
19  import java.util.Collections;
20  import java.util.List;
21  
22  import org.htmlunit.MockWebConnection;
23  import org.htmlunit.WebDriverTestCase;
24  import org.htmlunit.junit.BrowserRunner;
25  import org.htmlunit.junit.annotation.Alerts;
26  import org.htmlunit.util.NameValuePair;
27  import org.junit.Test;
28  import org.junit.runner.RunWith;
29  import org.openqa.selenium.By;
30  import org.openqa.selenium.WebDriver;
31  import org.openqa.selenium.WebElement;
32  
33  /**
34   * Tests for {@link HtmlSubmitInput}.
35   *
36   * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
37   * @author Marc Guillemot
38   * @author Ahmed Ashour
39   * @author Ronald Brill
40   * @author Frank Danek
41   */
42  @RunWith(BrowserRunner.class)
43  public class HtmlSubmitInputTest extends WebDriverTestCase {
44  
45      /**
46       * @throws Exception if the test fails
47       */
48      @Test
49      public void submit() throws Exception {
50          final String html = DOCTYPE_HTML
51              + "<html><head><title>foo</title></head><body>\n"
52              + "<form id='form1' method='post'>\n"
53              + "<input type='submit' name='aButton' value='foo'/>\n"
54              + "<input type='suBMit' name='button' value='foo'/>\n"
55              + "<input type='submit' name='anotherButton' value='foo'/>\n"
56              + "</form></body></html>";
57  
58          final WebDriver driver = loadPage2(html);
59  
60          final WebElement button = driver.findElement(By.name("button"));
61          button.click();
62  
63          assertTitle(driver, "foo");
64  
65          assertEquals(Collections.singletonList(new NameValuePair("button", "foo")),
66              getMockWebConnection().getLastParameters());
67      }
68  
69      /**
70       * @throws Exception if the test fails
71       */
72      @Test
73      @Alerts({"foo", "bar"})
74      public void click_onClick() throws Exception {
75          final String html = DOCTYPE_HTML
76              + "<html><head>"
77              + "<script>\n"
78              + LOG_TITLE_FUNCTION
79              + "</script>\n"
80              + "</head><body>\n"
81              + "<form id='form1' onSubmit='log(\"bar\"); return false;'>\n"
82              + "  <input type='submit' name='button' value='foo' onClick='log(\"foo\")'/>\n"
83              + "</form></body></html>";
84  
85          final WebDriver webDriver = loadPage2(html);
86  
87          final WebElement button = webDriver.findElement(By.name("button"));
88          button.click();
89  
90          verifyTitle2(webDriver, getExpectedAlerts());
91      }
92  
93      /**
94       * @throws Exception if the test fails
95       */
96      @Test
97      public void click_onClick_JavascriptReturnsTrue() throws Exception {
98          final String html = DOCTYPE_HTML
99              + "<html><head></head><body>\n"
100             + "<form name='form1' method='get' action='foo.html'>\n"
101             + "<input name='button' type='submit' value='PushMe' id='button1'"
102             + "onclick='return true'/></form>\n"
103             + "</body></html>";
104         final String secondHtml = DOCTYPE_HTML + "<html><head><title>Second</title></head><body></body></html>";
105 
106         getMockWebConnection().setResponse(new URL(URL_FIRST, "foo.html"), secondHtml);
107 
108         final WebDriver driver = loadPage2(html);
109 
110         final WebElement button = driver.findElement(By.id("button1"));
111         button.click();
112 
113         assertTitle(driver, "Second");
114     }
115 
116     /**
117      * @throws Exception if the test fails
118      */
119     @Test
120     @Alerts("1")
121     public void outsideForm() throws Exception {
122         final String html = DOCTYPE_HTML
123             + "<html><head>\n"
124             + "<script>\n"
125             + LOG_TITLE_FUNCTION
126             + "</script>\n"
127             + "</head>\n"
128             + "<body>\n"
129             + "<input id='myInput' type='submit' onclick='log(1)'>\n"
130             + "</body></html>";
131 
132         final WebDriver webDriver = loadPage2(html);
133         final WebElement input = webDriver.findElement(By.id("myInput"));
134         input.click();
135 
136         verifyTitle2(webDriver, getExpectedAlerts());
137     }
138 
139     /**
140      * @throws Exception if the test fails
141      */
142     @Test
143     @Alerts("1")
144     public void onclickDisables() throws Exception {
145         final String html = DOCTYPE_HTML
146             + "<html>\n"
147             + "<head>\n"
148             + "  <script type='text/javascript'>\n"
149             + "    function submitForm() {\n"
150             + "      document.deliveryChannelForm.submitBtn.disabled = true;\n"
151             + "    }\n"
152             + "  </script>\n"
153             + "</head>\n"
154             + "<body>\n"
155             + "  <form action='test' name='deliveryChannelForm'>\n"
156             + "    <input name='submitBtn' type='submit' value='Save' title='Save' onclick='submitForm();'>\n"
157             + "  </form>\n"
158             + "</body>\n"
159             + "</html>";
160 
161         getMockWebConnection().setDefaultResponse("");
162         final WebDriver webDriver = loadPage2(html);
163         final WebElement input = webDriver.findElement(By.name("submitBtn"));
164         input.click();
165 
166         assertEquals(Integer.parseInt(getExpectedAlerts()[0]), getMockWebConnection().getRequestCount());
167     }
168 
169     /**
170      * @throws Exception if the test fails
171      */
172     @Test
173     @Alerts("2")
174     public void doubleSubmission() throws Exception {
175         final String html = DOCTYPE_HTML
176             + "<html>\n"
177             + "<head>\n"
178             + "  <script type='text/javascript'>\n"
179             + "    function submitForm() {\n"
180             + "      document.deliveryChannelForm.submitBtn.disabled = true;\n"
181             + "      document.deliveryChannelForm.submit();\n"
182             + "    }\n"
183             + "  </script>\n"
184             + "</head>\n"
185             + "<body>\n"
186             + "  <form action='test' name='deliveryChannelForm'>\n"
187             + "    <input name='submitBtn' type='submit' value='Save' title='Save' onclick='submitForm();'>\n"
188             + "  </form>\n"
189             + "</body>\n"
190             + "</html>";
191 
192         getMockWebConnection().setDefaultResponse("");
193         final WebDriver webDriver = loadPage2(html);
194         final WebElement input = webDriver.findElement(By.name("submitBtn"));
195         input.click();
196 
197         assertEquals(Integer.parseInt(getExpectedAlerts()[0]), getMockWebConnection().getRequestCount());
198     }
199 
200     /**
201      * @throws Exception if the test fails
202      */
203     @Test
204     public void doubleSubmissionWithRedirect() throws Exception {
205         final String html = DOCTYPE_HTML
206             + "<html><body>\n"
207             + "<script>\n"
208             + "function submitForm(btn) {\n"
209             + "  btn.form.submit();\n"
210             + "  btn.disabled = true;\n"
211             + "}\n"
212             + "</script>\n"
213             + "<form method='post' action='test'>\n"
214             + "  <input name='text' type='text'>\n"
215             + "  <input name='btn' type='submit' onclick='submitForm(this);'>\n"
216             + "</form>\n"
217             + "</body></html>";
218 
219         final MockWebConnection mockWebConnection = getMockWebConnection();
220 
221         final List<NameValuePair> redirectHeaders = Arrays.asList(new NameValuePair("Location", "/nextPage"));
222         mockWebConnection.setResponse(new URL(URL_FIRST, "test"), "", 302, "Found", null, redirectHeaders);
223 
224         mockWebConnection.setResponse(new URL(URL_FIRST, "nextPage"),
225                 DOCTYPE_HTML + "<html><head><title>next page</title></head></html>");
226 
227         final WebDriver driver = loadPage2(html);
228         final WebElement input = driver.findElement(By.name("btn"));
229         input.click();
230 
231         assertTitle(driver, "next page");
232         assertEquals(3, mockWebConnection.getRequestCount());
233     }
234 
235     /**
236      * @throws Exception if the test fails
237      */
238     @Test
239     @Alerts({"-", "-", "-"})
240     public void defaultValues() throws Exception {
241         final String html = DOCTYPE_HTML
242             + "<html><head>\n"
243             + "<script>\n"
244             + LOG_TITLE_FUNCTION
245             + "  function test() {\n"
246             + "    var input = document.getElementById('submit1');\n"
247             + "    log(input.value + '-' + input.defaultValue);\n"
248 
249             + "    input = document.createElement('input');\n"
250             + "    input.type = 'submit';\n"
251             + "    log(input.value + '-' + input.defaultValue);\n"
252 
253             + "    var builder = document.createElement('div');\n"
254             + "    builder.innerHTML = '<input type=\"submit\">';\n"
255             + "    input = builder.firstChild;\n"
256             + "    log(input.value + '-' + input.defaultValue);\n"
257             + "  }\n"
258             + "</script>\n"
259             + "</head><body onload='test()'>\n"
260             + "<form>\n"
261             + "  <input type='submit' id='submit1'>\n"
262             + "</form>\n"
263             + "</body></html>";
264 
265         loadPageVerifyTitle2(html);
266     }
267 
268     /**
269      * @throws Exception if the test fails
270      */
271     @Test
272     @Alerts({"-", "-", "-"})
273     public void defaultValuesAfterClone() throws Exception {
274         final String html = DOCTYPE_HTML
275             + "<html><head>\n"
276             + "<script>\n"
277             + LOG_TITLE_FUNCTION
278             + "  function test() {\n"
279             + "    var input = document.getElementById('submit1');\n"
280             + "    input = input.cloneNode(false);\n"
281             + "    log(input.value + '-' + input.defaultValue);\n"
282 
283             + "    input = document.createElement('input');\n"
284             + "    input.type = 'submit';\n"
285             + "    input = input.cloneNode(false);\n"
286             + "    log(input.value + '-' + input.defaultValue);\n"
287 
288             + "    var builder = document.createElement('div');\n"
289             + "    builder.innerHTML = '<input type=\"submit\">';\n"
290             + "    input = builder.firstChild;\n"
291             + "    input = input.cloneNode(false);\n"
292             + "    log(input.value + '-' + input.defaultValue);\n"
293             + "  }\n"
294             + "</script>\n"
295             + "</head><body onload='test()'>\n"
296             + "<form>\n"
297             + "  <input type='submit' id='submit1'>\n"
298             + "</form>\n"
299             + "</body></html>";
300 
301         loadPageVerifyTitle2(html);
302     }
303 
304     /**
305      * @throws Exception if the test fails
306      */
307     @Test
308     @Alerts({"initial-initial", "initial-initial", "newValue-newValue", "newValue-newValue",
309                 "newDefault-newDefault", "newDefault-newDefault"})
310     public void resetByClick() throws Exception {
311         final String html = DOCTYPE_HTML
312             + "<html><head>\n"
313             + "<script>\n"
314             + LOG_TITLE_FUNCTION
315             + "  function test() {\n"
316             + "    var submit = document.getElementById('testId');\n"
317             + "    log(submit.value + '-' + submit.defaultValue);\n"
318 
319             + "    document.getElementById('testReset').click;\n"
320             + "    log(submit.value + '-' + submit.defaultValue);\n"
321 
322             + "    submit.value = 'newValue';\n"
323             + "    log(submit.value + '-' + submit.defaultValue);\n"
324 
325             + "    document.getElementById('testReset').click;\n"
326             + "    log(submit.value + '-' + submit.defaultValue);\n"
327 
328             + "    submit.defaultValue = 'newDefault';\n"
329             + "    log(submit.value + '-' + submit.defaultValue);\n"
330 
331             + "    document.forms[0].reset;\n"
332             + "    log(submit.value + '-' + submit.defaultValue);\n"
333             + "  }\n"
334             + "</script>\n"
335             + "</head><body onload='test()'>\n"
336             + "<form>\n"
337             + "  <input type='submit' id='testId' value='initial'>\n"
338             + "  <input type='reset' id='testReset'>\n"
339             + "</form>\n"
340             + "</body></html>";
341 
342         loadPageVerifyTitle2(html);
343     }
344 
345     /**
346      * @throws Exception if the test fails
347      */
348     @Test
349     @Alerts({"initial-initial", "initial-initial", "newValue-newValue", "newValue-newValue",
350                 "newDefault-newDefault", "newDefault-newDefault"})
351     public void resetByJS() throws Exception {
352         final String html = DOCTYPE_HTML
353             + "<html><head>\n"
354             + "<script>\n"
355             + LOG_TITLE_FUNCTION
356             + "  function test() {\n"
357             + "    var submit = document.getElementById('testId');\n"
358             + "    log(submit.value + '-' + submit.defaultValue);\n"
359 
360             + "    document.forms[0].reset;\n"
361             + "    log(submit.value + '-' + submit.defaultValue);\n"
362 
363             + "    submit.value = 'newValue';\n"
364             + "    log(submit.value + '-' + submit.defaultValue);\n"
365 
366             + "    document.forms[0].reset;\n"
367             + "    log(submit.value + '-' + submit.defaultValue);\n"
368 
369             + "    submit.defaultValue = 'newDefault';\n"
370             + "    log(submit.value + '-' + submit.defaultValue);\n"
371 
372             + "    document.forms[0].reset;\n"
373             + "    log(submit.value + '-' + submit.defaultValue);\n"
374             + "  }\n"
375             + "</script>\n"
376             + "</head><body onload='test()'>\n"
377             + "<form>\n"
378             + "  <input type='submit' id='testId' value='initial'>\n"
379             + "</form>\n"
380             + "</body></html>";
381 
382         loadPageVerifyTitle2(html);
383     }
384 
385     /**
386      * @throws Exception if the test fails
387      */
388     @Test
389     @Alerts({"initial-initial", "default-default", "newValue-newValue", "newdefault-newdefault"})
390     public void defaultValue() throws Exception {
391         final String html = DOCTYPE_HTML
392             + "<html><head>\n"
393             + "<script>\n"
394             + LOG_TITLE_FUNCTION
395             + "  function test() {\n"
396             + "    var submit = document.getElementById('testId');\n"
397             + "    log(submit.value + '-' + submit.defaultValue);\n"
398 
399             + "    submit.defaultValue = 'default';\n"
400             + "    log(submit.value + '-' + submit.defaultValue);\n"
401 
402             + "    submit.value = 'newValue';\n"
403             + "    log(submit.value + '-' + submit.defaultValue);\n"
404             + "    submit.defaultValue = 'newdefault';\n"
405             + "    log(submit.value + '-' + submit.defaultValue);\n"
406             + "  }\n"
407             + "</script>\n"
408             + "</head><body onload='test()'>\n"
409             + "<form>\n"
410             + "  <input type='submit' id='testId' value='initial'>\n"
411             + "</form>\n"
412             + "</body></html>";
413 
414         loadPageVerifyTitle2(html);
415     }
416 
417     /**
418      * @throws Exception if the test fails
419      */
420     @Test
421     @Alerts("--")
422     public void minMaxStep() throws Exception {
423         final String html = DOCTYPE_HTML
424             + "<html>\n"
425             + "<head>\n"
426             + "<script>\n"
427             + LOG_TITLE_FUNCTION
428             + "  function test() {\n"
429             + "    var input = document.getElementById('tester');\n"
430             + "    log(input.min + '-' + input.max + '-' + input.step);\n"
431             + "  }\n"
432             + "</script>\n"
433             + "</head>\n"
434             + "<body onload='test()'>\n"
435             + "<form>\n"
436             + "  <input type='submit' id='tester'>\n"
437             + "</form>\n"
438             + "</body>\n"
439             + "</html>";
440 
441         loadPageVerifyTitle2(html);
442     }
443 
444     /**
445      * @throws Exception if an error occurs
446      */
447     @Test
448     @Alerts({"true", "false", "true", "false", "true"})
449     public void willValidate() throws Exception {
450         final String html = DOCTYPE_HTML
451                 + "<html><head>\n"
452                 + "  <script>\n"
453                 + LOG_TITLE_FUNCTION
454                 + "    function test() {\n"
455                 + "      log(document.getElementById('o1').willValidate);\n"
456                 + "      log(document.getElementById('o2').willValidate);\n"
457                 + "      log(document.getElementById('o3').willValidate);\n"
458                 + "      log(document.getElementById('o4').willValidate);\n"
459                 + "      log(document.getElementById('o5').willValidate);\n"
460                 + "    }\n"
461                 + "  </script>\n"
462                 + "</head>\n"
463                 + "<body onload='test()'>\n"
464                 + "  <form>\n"
465                 + "    <input type='submit' id='o1'>\n"
466                 + "    <input type='submit' id='o2' disabled>\n"
467                 + "    <input type='submit' id='o3' hidden>\n"
468                 + "    <input type='submit' id='o4' readonly>\n"
469                 + "    <input type='submit' id='o5' style='display: none'>\n"
470                 + "  </form>\n"
471                 + "</body></html>";
472 
473         loadPageVerifyTitle2(html);
474     }
475 
476     /**
477      * @throws Exception if an error occurs
478      */
479     @Test
480     @Alerts({"true",
481              "false-false-false-false-false-false-false-false-false-true-false",
482              "true"})
483     public void validationEmpty() throws Exception {
484         validation("<input type='submit' id='e1'>\n", "");
485     }
486 
487     /**
488      * @throws Exception if an error occurs
489      */
490     @Test
491     @Alerts({"false",
492              "false-true-false-false-false-false-false-false-false-false-false",
493              "true"})
494     public void validationCustomValidity() throws Exception {
495         validation("<input type='submit' id='e1'>\n", "elem.setCustomValidity('Invalid');");
496     }
497 
498     /**
499      * @throws Exception if an error occurs
500      */
501     @Test
502     @Alerts({"false",
503              "false-true-false-false-false-false-false-false-false-false-false",
504              "true"})
505     public void validationBlankCustomValidity() throws Exception {
506         validation("<input type='submit' id='e1'>\n", "elem.setCustomValidity(' ');\n");
507     }
508 
509     /**
510      * @throws Exception if an error occurs
511      */
512     @Test
513     @Alerts({"true",
514              "false-false-false-false-false-false-false-false-false-true-false",
515              "true"})
516     public void validationResetCustomValidity() throws Exception {
517         validation("<input type='submit' id='e1'>\n",
518                 "elem.setCustomValidity('Invalid');elem.setCustomValidity('');");
519     }
520 
521     /**
522      * @throws Exception if an error occurs
523      */
524     @Test
525     @Alerts({"true",
526              "false-false-false-false-false-false-false-false-false-true-false",
527              "true"})
528     public void validationRequired() throws Exception {
529         validation("<input type='submit' id='e1' required>\n", "");
530     }
531 
532     private void validation(final String htmlPart, final String jsPart) throws Exception {
533         final String html = DOCTYPE_HTML
534                 + "<html><head>\n"
535                 + "  <script>\n"
536                 + LOG_TITLE_FUNCTION
537                 + "    function logValidityState(s) {\n"
538                 + "      log(s.badInput"
539                         + "+ '-' + s.customError"
540                         + "+ '-' + s.patternMismatch"
541                         + "+ '-' + s.rangeOverflow"
542                         + "+ '-' + s.rangeUnderflow"
543                         + "+ '-' + s.stepMismatch"
544                         + "+ '-' + s.tooLong"
545                         + "+ '-' + s.tooShort"
546                         + " + '-' + s.typeMismatch"
547                         + " + '-' + s.valid"
548                         + " + '-' + s.valueMissing);\n"
549                 + "    }\n"
550                 + "    function test() {\n"
551                 + "      var elem = document.getElementById('e1');\n"
552                 + jsPart
553                 + "      log(elem.checkValidity());\n"
554                 + "      logValidityState(elem.validity);\n"
555                 + "      log(elem.willValidate);\n"
556                 + "    }\n"
557                 + "  </script>\n"
558                 + "</head>\n"
559                 + "<body onload='test()'>\n"
560                 + "  <form>\n"
561                 + htmlPart
562                 + "  </form>\n"
563                 + "</body></html>";
564 
565         loadPageVerifyTitle2(html);
566     }
567 }