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.annotation.Alerts;
25  import org.htmlunit.util.NameValuePair;
26  import org.junit.jupiter.api.Test;
27  import org.openqa.selenium.By;
28  import org.openqa.selenium.WebDriver;
29  import org.openqa.selenium.WebElement;
30  
31  /**
32   * Tests for {@link HtmlSubmitInput}.
33   *
34   * @author Mike Bowler
35   * @author Marc Guillemot
36   * @author Ahmed Ashour
37   * @author Ronald Brill
38   * @author Frank Danek
39   */
40  public class HtmlSubmitInputTest extends WebDriverTestCase {
41  
42      /**
43       * @throws Exception if the test fails
44       */
45      @Test
46      public void submit() throws Exception {
47          final String html = DOCTYPE_HTML
48              + "<html><head><title>foo</title></head><body>\n"
49              + "<form id='form1' method='post'>\n"
50              + "<input type='submit' name='aButton' value='foo'/>\n"
51              + "<input type='suBMit' name='button' value='foo'/>\n"
52              + "<input type='submit' name='anotherButton' value='foo'/>\n"
53              + "</form></body></html>";
54  
55          final WebDriver driver = loadPage2(html);
56  
57          final WebElement button = driver.findElement(By.name("button"));
58          button.click();
59  
60          assertTitle(driver, "foo");
61  
62          if (useRealBrowser()) {
63              Thread.sleep(200);
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         if (useRealBrowser()) {
198             Thread.sleep(200);
199         }
200         assertEquals(Integer.parseInt(getExpectedAlerts()[0]), getMockWebConnection().getRequestCount());
201     }
202 
203     /**
204      * @throws Exception if the test fails
205      */
206     @Test
207     public void doubleSubmissionWithRedirect() throws Exception {
208         final String html = DOCTYPE_HTML
209             + "<html><body>\n"
210             + "<script>\n"
211             + "function submitForm(btn) {\n"
212             + "  btn.form.submit();\n"
213             + "  btn.disabled = true;\n"
214             + "}\n"
215             + "</script>\n"
216             + "<form method='post' action='test'>\n"
217             + "  <input name='text' type='text'>\n"
218             + "  <input name='btn' type='submit' onclick='submitForm(this);'>\n"
219             + "</form>\n"
220             + "</body></html>";
221 
222         final MockWebConnection mockWebConnection = getMockWebConnection();
223 
224         final List<NameValuePair> redirectHeaders = Arrays.asList(new NameValuePair("Location", "/nextPage"));
225         mockWebConnection.setResponse(new URL(URL_FIRST, "test"), "", 302, "Found", null, redirectHeaders);
226 
227         mockWebConnection.setResponse(new URL(URL_FIRST, "nextPage"),
228                 DOCTYPE_HTML + "<html><head><title>next page</title></head></html>");
229 
230         final WebDriver driver = loadPage2(html);
231         final WebElement input = driver.findElement(By.name("btn"));
232         input.click();
233 
234         assertTitle(driver, "next page");
235         assertEquals(3, mockWebConnection.getRequestCount());
236     }
237 
238     /**
239      * @throws Exception if the test fails
240      */
241     @Test
242     @Alerts({"-", "-", "-"})
243     public void defaultValues() throws Exception {
244         final String html = DOCTYPE_HTML
245             + "<html><head>\n"
246             + "<script>\n"
247             + LOG_TITLE_FUNCTION
248             + "  function test() {\n"
249             + "    var input = document.getElementById('submit1');\n"
250             + "    log(input.value + '-' + input.defaultValue);\n"
251 
252             + "    input = document.createElement('input');\n"
253             + "    input.type = 'submit';\n"
254             + "    log(input.value + '-' + input.defaultValue);\n"
255 
256             + "    var builder = document.createElement('div');\n"
257             + "    builder.innerHTML = '<input type=\"submit\">';\n"
258             + "    input = builder.firstChild;\n"
259             + "    log(input.value + '-' + input.defaultValue);\n"
260             + "  }\n"
261             + "</script>\n"
262             + "</head><body onload='test()'>\n"
263             + "<form>\n"
264             + "  <input type='submit' id='submit1'>\n"
265             + "</form>\n"
266             + "</body></html>";
267 
268         loadPageVerifyTitle2(html);
269     }
270 
271     /**
272      * @throws Exception if the test fails
273      */
274     @Test
275     @Alerts({"-", "-", "-"})
276     public void defaultValuesAfterClone() throws Exception {
277         final String html = DOCTYPE_HTML
278             + "<html><head>\n"
279             + "<script>\n"
280             + LOG_TITLE_FUNCTION
281             + "  function test() {\n"
282             + "    var input = document.getElementById('submit1');\n"
283             + "    input = input.cloneNode(false);\n"
284             + "    log(input.value + '-' + input.defaultValue);\n"
285 
286             + "    input = document.createElement('input');\n"
287             + "    input.type = 'submit';\n"
288             + "    input = input.cloneNode(false);\n"
289             + "    log(input.value + '-' + input.defaultValue);\n"
290 
291             + "    var builder = document.createElement('div');\n"
292             + "    builder.innerHTML = '<input type=\"submit\">';\n"
293             + "    input = builder.firstChild;\n"
294             + "    input = input.cloneNode(false);\n"
295             + "    log(input.value + '-' + input.defaultValue);\n"
296             + "  }\n"
297             + "</script>\n"
298             + "</head><body onload='test()'>\n"
299             + "<form>\n"
300             + "  <input type='submit' id='submit1'>\n"
301             + "</form>\n"
302             + "</body></html>";
303 
304         loadPageVerifyTitle2(html);
305     }
306 
307     /**
308      * @throws Exception if the test fails
309      */
310     @Test
311     @Alerts({"initial-initial", "initial-initial", "newValue-newValue", "newValue-newValue",
312                 "newDefault-newDefault", "newDefault-newDefault"})
313     public void resetByClick() throws Exception {
314         final String html = DOCTYPE_HTML
315             + "<html><head>\n"
316             + "<script>\n"
317             + LOG_TITLE_FUNCTION
318             + "  function test() {\n"
319             + "    var submit = document.getElementById('testId');\n"
320             + "    log(submit.value + '-' + submit.defaultValue);\n"
321 
322             + "    document.getElementById('testReset').click;\n"
323             + "    log(submit.value + '-' + submit.defaultValue);\n"
324 
325             + "    submit.value = 'newValue';\n"
326             + "    log(submit.value + '-' + submit.defaultValue);\n"
327 
328             + "    document.getElementById('testReset').click;\n"
329             + "    log(submit.value + '-' + submit.defaultValue);\n"
330 
331             + "    submit.defaultValue = 'newDefault';\n"
332             + "    log(submit.value + '-' + submit.defaultValue);\n"
333 
334             + "    document.forms[0].reset;\n"
335             + "    log(submit.value + '-' + submit.defaultValue);\n"
336             + "  }\n"
337             + "</script>\n"
338             + "</head><body onload='test()'>\n"
339             + "<form>\n"
340             + "  <input type='submit' id='testId' value='initial'>\n"
341             + "  <input type='reset' id='testReset'>\n"
342             + "</form>\n"
343             + "</body></html>";
344 
345         loadPageVerifyTitle2(html);
346     }
347 
348     /**
349      * @throws Exception if the test fails
350      */
351     @Test
352     @Alerts({"initial-initial", "initial-initial", "newValue-newValue", "newValue-newValue",
353                 "newDefault-newDefault", "newDefault-newDefault"})
354     public void resetByJS() throws Exception {
355         final String html = DOCTYPE_HTML
356             + "<html><head>\n"
357             + "<script>\n"
358             + LOG_TITLE_FUNCTION
359             + "  function test() {\n"
360             + "    var submit = document.getElementById('testId');\n"
361             + "    log(submit.value + '-' + submit.defaultValue);\n"
362 
363             + "    document.forms[0].reset;\n"
364             + "    log(submit.value + '-' + submit.defaultValue);\n"
365 
366             + "    submit.value = 'newValue';\n"
367             + "    log(submit.value + '-' + submit.defaultValue);\n"
368 
369             + "    document.forms[0].reset;\n"
370             + "    log(submit.value + '-' + submit.defaultValue);\n"
371 
372             + "    submit.defaultValue = 'newDefault';\n"
373             + "    log(submit.value + '-' + submit.defaultValue);\n"
374 
375             + "    document.forms[0].reset;\n"
376             + "    log(submit.value + '-' + submit.defaultValue);\n"
377             + "  }\n"
378             + "</script>\n"
379             + "</head><body onload='test()'>\n"
380             + "<form>\n"
381             + "  <input type='submit' id='testId' value='initial'>\n"
382             + "</form>\n"
383             + "</body></html>";
384 
385         loadPageVerifyTitle2(html);
386     }
387 
388     /**
389      * @throws Exception if the test fails
390      */
391     @Test
392     @Alerts({"initial-initial", "default-default", "newValue-newValue", "newdefault-newdefault"})
393     public void defaultValue() throws Exception {
394         final String html = DOCTYPE_HTML
395             + "<html><head>\n"
396             + "<script>\n"
397             + LOG_TITLE_FUNCTION
398             + "  function test() {\n"
399             + "    var submit = document.getElementById('testId');\n"
400             + "    log(submit.value + '-' + submit.defaultValue);\n"
401 
402             + "    submit.defaultValue = 'default';\n"
403             + "    log(submit.value + '-' + submit.defaultValue);\n"
404 
405             + "    submit.value = 'newValue';\n"
406             + "    log(submit.value + '-' + submit.defaultValue);\n"
407             + "    submit.defaultValue = 'newdefault';\n"
408             + "    log(submit.value + '-' + submit.defaultValue);\n"
409             + "  }\n"
410             + "</script>\n"
411             + "</head><body onload='test()'>\n"
412             + "<form>\n"
413             + "  <input type='submit' id='testId' value='initial'>\n"
414             + "</form>\n"
415             + "</body></html>";
416 
417         loadPageVerifyTitle2(html);
418     }
419 
420     /**
421      * @throws Exception if the test fails
422      */
423     @Test
424     @Alerts("--")
425     public void minMaxStep() throws Exception {
426         final String html = DOCTYPE_HTML
427             + "<html>\n"
428             + "<head>\n"
429             + "<script>\n"
430             + LOG_TITLE_FUNCTION
431             + "  function test() {\n"
432             + "    var input = document.getElementById('tester');\n"
433             + "    log(input.min + '-' + input.max + '-' + input.step);\n"
434             + "  }\n"
435             + "</script>\n"
436             + "</head>\n"
437             + "<body onload='test()'>\n"
438             + "<form>\n"
439             + "  <input type='submit' id='tester'>\n"
440             + "</form>\n"
441             + "</body>\n"
442             + "</html>";
443 
444         loadPageVerifyTitle2(html);
445     }
446 
447     /**
448      * @throws Exception if an error occurs
449      */
450     @Test
451     @Alerts({"true", "false", "true", "false", "true"})
452     public void willValidate() throws Exception {
453         final String html = DOCTYPE_HTML
454                 + "<html><head>\n"
455                 + "  <script>\n"
456                 + LOG_TITLE_FUNCTION
457                 + "    function test() {\n"
458                 + "      log(document.getElementById('o1').willValidate);\n"
459                 + "      log(document.getElementById('o2').willValidate);\n"
460                 + "      log(document.getElementById('o3').willValidate);\n"
461                 + "      log(document.getElementById('o4').willValidate);\n"
462                 + "      log(document.getElementById('o5').willValidate);\n"
463                 + "    }\n"
464                 + "  </script>\n"
465                 + "</head>\n"
466                 + "<body onload='test()'>\n"
467                 + "  <form>\n"
468                 + "    <input type='submit' id='o1'>\n"
469                 + "    <input type='submit' id='o2' disabled>\n"
470                 + "    <input type='submit' id='o3' hidden>\n"
471                 + "    <input type='submit' id='o4' readonly>\n"
472                 + "    <input type='submit' id='o5' style='display: none'>\n"
473                 + "  </form>\n"
474                 + "</body></html>";
475 
476         loadPageVerifyTitle2(html);
477     }
478 
479     /**
480      * @throws Exception if an error occurs
481      */
482     @Test
483     @Alerts({"true",
484              "false-false-false-false-false-false-false-false-false-true-false",
485              "true"})
486     public void validationEmpty() throws Exception {
487         validation("<input type='submit' id='e1'>\n", "");
488     }
489 
490     /**
491      * @throws Exception if an error occurs
492      */
493     @Test
494     @Alerts({"false",
495              "false-true-false-false-false-false-false-false-false-false-false",
496              "true"})
497     public void validationCustomValidity() throws Exception {
498         validation("<input type='submit' id='e1'>\n", "elem.setCustomValidity('Invalid');");
499     }
500 
501     /**
502      * @throws Exception if an error occurs
503      */
504     @Test
505     @Alerts({"false",
506              "false-true-false-false-false-false-false-false-false-false-false",
507              "true"})
508     public void validationBlankCustomValidity() throws Exception {
509         validation("<input type='submit' id='e1'>\n", "elem.setCustomValidity(' ');\n");
510     }
511 
512     /**
513      * @throws Exception if an error occurs
514      */
515     @Test
516     @Alerts({"true",
517              "false-false-false-false-false-false-false-false-false-true-false",
518              "true"})
519     public void validationResetCustomValidity() throws Exception {
520         validation("<input type='submit' id='e1'>\n",
521                 "elem.setCustomValidity('Invalid');elem.setCustomValidity('');");
522     }
523 
524     /**
525      * @throws Exception if an error occurs
526      */
527     @Test
528     @Alerts({"true",
529              "false-false-false-false-false-false-false-false-false-true-false",
530              "true"})
531     public void validationRequired() throws Exception {
532         validation("<input type='submit' id='e1' required>\n", "");
533     }
534 
535     private void validation(final String htmlPart, final String jsPart) throws Exception {
536         final String html = DOCTYPE_HTML
537                 + "<html><head>\n"
538                 + "  <script>\n"
539                 + LOG_TITLE_FUNCTION
540                 + "    function logValidityState(s) {\n"
541                 + "      log(s.badInput"
542                         + "+ '-' + s.customError"
543                         + "+ '-' + s.patternMismatch"
544                         + "+ '-' + s.rangeOverflow"
545                         + "+ '-' + s.rangeUnderflow"
546                         + "+ '-' + s.stepMismatch"
547                         + "+ '-' + s.tooLong"
548                         + "+ '-' + s.tooShort"
549                         + " + '-' + s.typeMismatch"
550                         + " + '-' + s.valid"
551                         + " + '-' + s.valueMissing);\n"
552                 + "    }\n"
553                 + "    function test() {\n"
554                 + "      var elem = document.getElementById('e1');\n"
555                 + jsPart
556                 + "      log(elem.checkValidity());\n"
557                 + "      logValidityState(elem.validity);\n"
558                 + "      log(elem.willValidate);\n"
559                 + "    }\n"
560                 + "  </script>\n"
561                 + "</head>\n"
562                 + "<body onload='test()'>\n"
563                 + "  <form>\n"
564                 + htmlPart
565                 + "  </form>\n"
566                 + "</body></html>";
567 
568         loadPageVerifyTitle2(html);
569     }
570 }