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.io.File;
18  import java.util.regex.Matcher;
19  import java.util.regex.Pattern;
20  
21  import org.apache.commons.lang3.StringUtils;
22  import org.htmlunit.HttpHeader;
23  import org.htmlunit.WebDriverTestCase;
24  import org.htmlunit.WebRequest;
25  import org.htmlunit.junit.BrowserRunner;
26  import org.htmlunit.junit.annotation.Alerts;
27  import org.htmlunit.junit.annotation.Retry;
28  import org.htmlunit.util.MimeType;
29  import org.junit.Test;
30  import org.junit.runner.RunWith;
31  import org.openqa.selenium.By;
32  import org.openqa.selenium.WebDriver;
33  import org.openqa.selenium.WebElement;
34  
35  /**
36   * Tests for {@link HtmlFileInput}.
37   *
38   * @author Marc Guillemot
39   * @author Ahmed Ashour
40   * @author Ronald Brill
41   * @author Frank Danek
42   */
43  @RunWith(BrowserRunner.class)
44  public class HtmlFileInputTest extends WebDriverTestCase {
45  
46      /**
47       * @throws Exception if an error occurs
48       */
49      @Test
50      public void contentTypeHeader() throws Exception {
51          final String htmlContent = DOCTYPE_HTML
52                  + "<html>\n"
53                  + "<body>\n"
54                  + "<form action='upload2' method='post' enctype='multipart/form-data'>\n"
55                  + "Name: <input name='myInput' type='file'><br>\n"
56                  + "Name 2 (should stay empty): <input name='myInput2' type='file'><br>\n"
57                  + "<input type='submit' value='Upload' id='mySubmit'>\n"
58                  + "</form>\n"
59                  + "</body></html>\n";
60          getMockWebConnection().setDefaultResponse("Error: not found", 404, "Not Found", MimeType.TEXT_HTML);
61  
62          final WebDriver driver = loadPage2(htmlContent);
63          String path = getClass().getClassLoader().getResource("realm.properties").toExternalForm();
64          path = path.substring(path.indexOf('/') + 1).replace('/', '\\');
65  
66          driver.findElement(By.name("myInput")).sendKeys(path);
67          driver.findElement(By.id("mySubmit")).click();
68  
69          final WebRequest request = getMockWebConnection().getLastWebRequest();
70          final String contentType = request.getAdditionalHeaders().get(HttpHeader.CONTENT_TYPE);
71          assertTrue(StringUtils.isNotBlank(contentType));
72          assertFalse(StringUtils.containsIgnoreCase(contentType, "charset"));
73      }
74  
75      /**
76       * @throws Exception if an error occurs
77       */
78      @Test
79      @Retry
80      @Alerts("Content-Disposition: form-data; name=\"myInput\"; filename=\"\"")
81      public void empty() throws Exception {
82          final String htmlContent = DOCTYPE_HTML
83                  + "<html>\n"
84                  + "<body>\n"
85                  + "<form action='upload2' method='post' enctype='multipart/form-data'>\n"
86                  + "Name: <input name='myInput' type='file'><br>\n"
87                  + "Name 2 (should stay empty): <input name='myInput2' type='file'><br>\n"
88                  + "<input type='submit' value='Upload' id='mySubmit'>\n"
89                  + "</form>\n"
90                  + "</body></html>\n";
91          getMockWebConnection().setDefaultResponse("Error: not found", 404, "Not Found", MimeType.TEXT_HTML);
92  
93          final WebDriver driver = loadPage2(htmlContent);
94          driver.findElement(By.id("mySubmit")).click();
95  
96          final String pageSource = getMockWebConnection().getLastWebRequest().getRequestBody();
97          final Matcher matcher = Pattern.compile(getExpectedAlerts()[0]).matcher(pageSource);
98          assertTrue(pageSource, matcher.find());
99      }
100 
101     /**
102      * @throws Exception if an error occurs
103      */
104     @Test
105     @Alerts("Content-Disposition: form-data; name=\"myInput\"; filename=\"realm.properties\"")
106     // since 2.28
107     // there is an option for IE, for local and trusted sites IE includes the file path
108     // because we do not support any IE specific setting we do not send the filename as
109     // done by the other browsers
110     public void realFile() throws Exception {
111         final String htmlContent = DOCTYPE_HTML
112                 + "<html>\n"
113                 + "<body>\n"
114                 + "<form action='upload2' method='post' enctype='multipart/form-data'>\n"
115                 + "Name: <input name='myInput' type='file'><br>\n"
116                 + "Name 2 (should stay empty): <input name='myInput2' type='file'><br>\n"
117                 + "<input type='submit' value='Upload' id='mySubmit'>\n"
118                 + "</form>\n"
119                 + "</body></html>\n";
120         getMockWebConnection().setDefaultResponse("Error: not found", 404, "Not Found", MimeType.TEXT_HTML);
121 
122         final WebDriver driver = loadPage2(htmlContent);
123 
124         String path = getClass().getClassLoader().getResource("realm.properties").toExternalForm();
125         path = path.substring(path.indexOf('/') + 1).replace('/', '\\');
126 
127         driver.findElement(By.name("myInput")).sendKeys(path);
128         driver.findElement(By.id("mySubmit")).click();
129 
130         final String pageSource = getMockWebConnection().getLastWebRequest().getRequestBody();
131         final Matcher matcher = Pattern.compile(getExpectedAlerts()[0]).matcher(pageSource);
132         assertTrue(pageSource, matcher.find());
133     }
134 
135     /**
136      * @throws Exception if an error occurs
137      */
138     @Test
139     @Retry
140     public void chunked() throws Exception {
141         final String htmlContent = DOCTYPE_HTML
142                 + "<html>\n"
143                 + "<body>\n"
144                 + "<form action='upload2' method='post' enctype='multipart/form-data'>\n"
145                 + "Name: <input name='myInput' type='file'><br>\n"
146                 + "Name 2 (should stay empty): <input name='myInput2' type='file'><br>\n"
147                 + "<input type='submit' value='Upload' id='mySubmit'>\n"
148                 + "</form>\n"
149                 + "</body></html>\n";
150         getMockWebConnection().setDefaultResponse("Error: not found", 404, "Not Found", MimeType.TEXT_HTML);
151 
152         final WebDriver driver = loadPage2(htmlContent);
153 
154         driver.findElement(By.id("mySubmit")).click();
155 
156         final String pageSource = getMockWebConnection().getLastWebRequest().getRequestBody();
157         assertFalse(pageSource.contains("chunked"));
158     }
159 
160     /**
161      * Verifies that getText() returns an empty string.
162      * @throws Exception if the test fails
163      */
164     @Test
165     public void getText() throws Exception {
166         final String htmlContent = DOCTYPE_HTML
167             + "<html><head><title>foo</title></head><body>\n"
168             + "<form id='form1'>\n"
169             + "  <input type='file' name='foo' id='foo' value='bla'>\n"
170             + "</form></body></html>";
171 
172         final WebDriver driver = loadPage2(htmlContent);
173 
174         final WebElement input = driver.findElement(By.id("foo"));
175         assertEquals("", input.getText());
176     }
177 
178     /**
179      * @throws Exception if an error occurs
180      */
181     @Test
182     @Alerts({"InvalidStateError/DOMException", "-Hello world-Hello world-0", "-Hello world-Hello world-0"})
183     public void setValueOnChange() throws Exception {
184         final String html = DOCTYPE_HTML
185               + "<html>\n"
186               + "<head>\n"
187               + "<script>\n"
188               + LOG_TITLE_FUNCTION
189               + "  function test() {\n"
190               + "    var input = document.getElementById('f');\n"
191               + "    try{\n"
192               + "      input.value = 'HtmlUnit';\n"
193               + "    } catch(e) { logEx(e); }\n"
194               + "    log(input.value + '-' + input.defaultValue "
195                           + "+ '-' + input.getAttribute('value') "
196                           + "+ '-' + input.files.length);\n"
197 
198               + "    try{\n"
199               + "      input.value = '';\n"
200               + "    } catch(e) { logEx(e); }\n"
201               + "    log(input.value + '-' + input.defaultValue "
202                           + "+ '-' + input.getAttribute('value') "
203                           + "+ '-' + input.files.length);\n"
204               + "  }\n"
205               + "</script>\n"
206               + "<body>\n"
207               + "  <input type='file' id='f' value='Hello world' onChange='log(\"foo\");log(event.type);'>\n"
208               + "  <button id='b'>some button</button>\n"
209               + "  <button id='set' onclick='test()'>setValue</button>\n"
210               + "</body></html>";
211 
212         final WebDriver driver = loadPage2(html);
213         driver.findElement(By.id("set")).click();
214 
215         verifyTitle2(driver, getExpectedAlerts());
216 
217         // trigger lost focus
218         driver.findElement(By.id("b")).click();
219         verifyTitle2(driver, getExpectedAlerts());
220     }
221 
222     /**
223      * @throws Exception if an error occurs
224      */
225     @Test
226     @Alerts({"-HtmlUnit-HtmlUnit-0", "---0"})
227     public void setDefaultValueOnChange() throws Exception {
228         final String html = DOCTYPE_HTML
229               + "<html>\n"
230               + "<head>\n"
231               + "<script>\n"
232               + LOG_TITLE_FUNCTION
233               + "  function test() {\n"
234               + "    var input = document.getElementById('f');\n"
235               + "    try{\n"
236               + "      input.defaultValue = 'HtmlUnit';\n"
237               + "    } catch(e) { logEx(e); }\n"
238               + "    log(input.value + '-' + input.defaultValue "
239                           + "+ '-' + input.getAttribute('value') "
240                           + "+ '-' + input.files.length);\n"
241 
242               + "    try{\n"
243               + "      input.defaultValue = '';\n"
244               + "    } catch(e) { logEx(e); }\n"
245               + "    log(input.value + '-' + input.defaultValue "
246                           + "+ '-' + input.getAttribute('value') "
247                           + "+ '-' + input.files.length);\n"
248               + "  }\n"
249               + "</script>\n"
250               + "</head>\n"
251               + "<body>\n"
252               + "  <input type='file' id='f' value='Hello world' onChange='log(\"foo\");log(event.type);'>\n"
253               + "  <button id='b'>some button</button>\n"
254               + "  <button id='set' onclick='test()'>setValue</button>\n"
255               + "</body></html>";
256 
257         final WebDriver driver = loadPage2(html);
258         driver.findElement(By.id("set")).click();
259 
260         verifyTitle2(driver, getExpectedAlerts());
261 
262         // trigger lost focus
263         driver.findElement(By.id("b")).click();
264         verifyTitle2(driver, getExpectedAlerts());
265     }
266 
267     /**
268      * @throws Exception if the test fails
269      */
270     @Test
271     @Alerts({"--null", "--null", "--null"})
272     public void defaultValues() throws Exception {
273         final String html = DOCTYPE_HTML
274             + "<html><head>\n"
275             + "<script>\n"
276             + LOG_TITLE_FUNCTION
277             + "  function test() {\n"
278             + "    var input = document.getElementById('file1');\n"
279             + "    log(input.value + '-' + input.defaultValue + '-' + input.getAttribute('value'));\n"
280 
281             + "    input = document.createElement('input');\n"
282             + "    input.type = 'file';\n"
283             + "    log(input.value + '-' + input.defaultValue + '-' + input.getAttribute('value'));\n"
284 
285             + "    var builder = document.createElement('div');\n"
286             + "    builder.innerHTML = '<input type=\"file\">';\n"
287             + "    input = builder.firstChild;\n"
288             + "    log(input.value + '-' + input.defaultValue + '-' + input.getAttribute('value'));\n"
289             + "  }\n"
290             + "</script>\n"
291             + "</head><body onload='test()'>\n"
292             + "<form>\n"
293             + "  <input type='file' id='file1'>\n"
294             + "</form>\n"
295             + "</body></html>";
296 
297         loadPageVerifyTitle2(html);
298     }
299 
300     /**
301      * @throws Exception if the test fails
302      */
303     @Test
304     @Alerts({"--null", "--null", "--null"})
305     public void defaultValuesAfterClone() throws Exception {
306         final String html = DOCTYPE_HTML
307             + "<html><head>\n"
308             + "<script>\n"
309             + LOG_TITLE_FUNCTION
310             + "  function test() {\n"
311             + "    var input = document.getElementById('file1');\n"
312             + "    input = input.cloneNode(false);\n"
313             + "    log(input.value + '-' + input.defaultValue + '-' + input.getAttribute('value'));\n"
314 
315             + "    input = document.createElement('input');\n"
316             + "    input.type = 'file';\n"
317             + "    input = input.cloneNode(false);\n"
318             + "    log(input.value + '-' + input.defaultValue + '-' + input.getAttribute('value'));\n"
319 
320             + "    var builder = document.createElement('div');\n"
321             + "    builder.innerHTML = '<input type=\"file\">';\n"
322             + "    input = builder.firstChild;\n"
323             + "    input = input.cloneNode(false);\n"
324             + "    log(input.value + '-' + input.defaultValue + '-' + input.getAttribute('value'));\n"
325             + "  }\n"
326             + "</script>\n"
327             + "</head><body onload='test()'>\n"
328             + "<form>\n"
329             + "  <input type='file' id='file1'>\n"
330             + "</form>\n"
331             + "</body></html>";
332 
333         loadPageVerifyTitle2(html);
334     }
335 
336     /**
337      * @throws Exception if the test fails
338      */
339     @Test
340     @Alerts({"-initial-initial", "-initial-initial",
341              "InvalidStateError/DOMException", "-initial-initial", "-initial-initial",
342              "-newDefault-newDefault", "-newDefault-newDefault"})
343     public void resetByClick() throws Exception {
344         final String html = DOCTYPE_HTML
345             + "<html><head>\n"
346             + "<script>\n"
347             + LOG_TITLE_FUNCTION
348             + "  function test() {\n"
349             + "    var file = document.getElementById('testId');\n"
350             + "    log(file.value + '-' + file.defaultValue + '-' + file.getAttribute('value'));\n"
351 
352             + "    document.getElementById('testReset').click;\n"
353             + "    log(file.value + '-' + file.defaultValue + '-' + file.getAttribute('value'));\n"
354 
355             + "    try{\n"
356             + "      file.value = 'newValue';\n"
357             + "    } catch(e) { logEx(e); }\n"
358             + "    log(file.value + '-' + file.defaultValue + '-' + file.getAttribute('value'));\n"
359 
360             + "    document.getElementById('testReset').click;\n"
361             + "    log(file.value + '-' + file.defaultValue + '-' + file.getAttribute('value'));\n"
362 
363             + "    file.defaultValue = 'newDefault';\n"
364             + "    log(file.value + '-' + file.defaultValue + '-' + file.getAttribute('value'));\n"
365 
366             + "    document.forms[0].reset;\n"
367             + "    log(file.value + '-' + file.defaultValue + '-' + file.getAttribute('value'));\n"
368             + "  }\n"
369             + "</script>\n"
370             + "</head><body onload='test()'>\n"
371             + "<form>\n"
372             + "  <input type='file' id='testId' value='initial'>\n"
373             + "  <input type='reset' id='testReset'>\n"
374             + "</form>\n"
375             + "</body></html>";
376 
377         loadPageVerifyTitle2(html);
378     }
379 
380     /**
381      * @throws Exception if the test fails
382      */
383     @Test
384     @Alerts({"-initial-initial", "-initial-initial",
385              "InvalidStateError/DOMException", "-initial-initial", "-initial-initial",
386              "-newDefault-newDefault", "-newDefault-newDefault"})
387     public void resetByJS() throws Exception {
388         final String html = DOCTYPE_HTML
389             + "<html><head>\n"
390             + "<script>\n"
391             + LOG_TITLE_FUNCTION
392             + "  function test() {\n"
393             + "    var file = document.getElementById('testId');\n"
394             + "    log(file.value + '-' + file.defaultValue + '-' + file.getAttribute('value'));\n"
395 
396             + "    document.forms[0].reset;\n"
397             + "    log(file.value + '-' + file.defaultValue + '-' + file.getAttribute('value'));\n"
398 
399             + "    try{\n"
400             + "      file.value = 'newValue';\n"
401             + "    } catch(e) { logEx(e); }\n"
402             + "    log(file.value + '-' + file.defaultValue + '-' + file.getAttribute('value'));\n"
403 
404             + "    document.forms[0].reset;\n"
405             + "    log(file.value + '-' + file.defaultValue + '-' + file.getAttribute('value'));\n"
406 
407             + "    file.defaultValue = 'newDefault';\n"
408             + "    log(file.value + '-' + file.defaultValue + '-' + file.getAttribute('value'));\n"
409 
410             + "    document.forms[0].reset;\n"
411             + "    log(file.value + '-' + file.defaultValue + '-' + file.getAttribute('value'));\n"
412             + "  }\n"
413             + "</script>\n"
414             + "</head><body onload='test()'>\n"
415             + "<form>\n"
416             + "  <input type='file' id='testId' value='initial'>\n"
417             + "</form>\n"
418             + "</body></html>";
419 
420         loadPageVerifyTitle2(html);
421     }
422 
423     /**
424      * @throws Exception if the test fails
425      */
426     @Test
427     @Alerts({"-initial-initial", "-default-default",
428              "InvalidStateError/DOMException", "-default-default",
429              "-attribValue-attribValue", "-newDefault-newDefault"})
430     public void value() throws Exception {
431         final String html = DOCTYPE_HTML
432             + "<html><head>\n"
433             + "<script>\n"
434             + LOG_TITLE_FUNCTION
435             + "  function test() {\n"
436             + "    var file = document.getElementById('testId');\n"
437             + "    log(file.value + '-' + file.defaultValue + '-' + file.getAttribute('value'));\n"
438 
439             + "    file.defaultValue = 'default';\n"
440             + "    log(file.value + '-' + file.defaultValue + '-' + file.getAttribute('value'));\n"
441 
442             + "    try{\n"
443             + "      file.value = 'newValue';\n"
444             + "    } catch(e) { logEx(e); }\n"
445             + "    log(file.value + '-' + file.defaultValue + '-' + file.getAttribute('value'));\n"
446 
447             + "    file.setAttribute('value', 'attribValue');\n"
448             + "    log(file.value + '-' + file.defaultValue + '-' + file.getAttribute('value'));\n"
449 
450             + "    file.defaultValue = 'newDefault';\n"
451             + "    log(file.value + '-' + file.defaultValue + '-' + file.getAttribute('value'));\n"
452             + "  }\n"
453             + "</script>\n"
454             + "</head><body onload='test()'>\n"
455             + "<form>\n"
456             + "  <input type='file' id='testId' value='initial'>\n"
457             + "</form>\n"
458             + "</body></html>";
459 
460         loadPageVerifyTitle2(html);
461     }
462 
463     /**
464      * @throws Exception if the test fails
465      */
466     @Test
467     @Alerts("textLength not available")
468     public void textLength() throws Exception {
469         final String html = DOCTYPE_HTML
470             + "<html><head>\n"
471             + "<script>\n"
472             + LOG_TITLE_FUNCTION
473             + "  function test() {\n"
474             + "    var text = document.getElementById('testId');\n"
475             + "    if(text.textLength) {\n"
476             + "      log(text.textLength);\n"
477             + "    } else {\n"
478             + "      log('textLength not available');\n"
479             + "    }\n"
480             + "  }\n"
481             + "</script>\n"
482             + "</head><body onload='test()'>\n"
483             + "<form>\n"
484             + "  <input type='file' id='testId' value='initial'>\n"
485             + "</form>\n"
486             + "</body></html>";
487 
488         loadPageVerifyTitle2(html);
489     }
490 
491     /**
492      * @throws Exception if an error occurs
493      */
494     @Test
495     @Alerts({"null", "null", "0"})
496     public void selection() throws Exception {
497         final String html = DOCTYPE_HTML
498             + "<html><head><script>\n"
499             + LOG_TITLE_FUNCTION
500             + "  function test() {\n"
501             + "    var s = getSelection(document.getElementById('text1'));\n"
502             + "    if (s != undefined) {\n"
503             + "      log(s.length);\n"
504             + "    }\n"
505             + "  }\n"
506             + "  function getSelection(element) {\n"
507             + "    try {\n"
508             + "      log(element.selectionStart);\n"
509             + "    } catch(e) { log('ex start'); }\n"
510             + "    try {\n"
511             + "      log(element.selectionEnd);\n"
512             + "    } catch(e) { log('ex end'); }\n"
513             + "    try {\n"
514             + "      return element.value.substring(element.selectionStart, element.selectionEnd);\n"
515             + "    } catch(e) { logEx(e); }\n"
516             + "  }\n"
517             + "</script></head>\n"
518             + "<body onload='test()'>\n"
519             + "  <input type='file' id='text1'/>\n"
520             + "</body></html>";
521         loadPageVerifyTitle2(html);
522     }
523 
524     /**
525      * @throws Exception if test fails
526      */
527     @Test
528     @Alerts({"null,null", "InvalidStateError/DOMException", "null,null",
529              "InvalidStateError/DOMException", "null,null",
530              "InvalidStateError/DOMException", "null,null"})
531     public void selection2_1() throws Exception {
532         selection2(3, 10);
533     }
534 
535     /**
536      * @throws Exception if test fails
537      */
538     @Test
539     @Alerts({"null,null", "InvalidStateError/DOMException", "null,null",
540              "InvalidStateError/DOMException", "null,null",
541              "InvalidStateError/DOMException", "null,null"})
542     public void selection2_2() throws Exception {
543         selection2(-3, 15);
544     }
545 
546     /**
547      * @throws Exception if test fails
548      */
549     @Test
550     @Alerts({"null,null", "InvalidStateError/DOMException", "null,null",
551              "InvalidStateError/DOMException", "null,null",
552              "InvalidStateError/DOMException", "null,null"})
553     public void selection2_3() throws Exception {
554         selection2(10, 5);
555     }
556 
557     private void selection2(final int selectionStart, final int selectionEnd) throws Exception {
558         final String html = DOCTYPE_HTML
559             + "<html>\n"
560             + "<body>\n"
561             + "<input id='myTextInput' value='Bonjour' type='file'>\n"
562             + "<script>\n"
563             + LOG_TITLE_FUNCTION
564             + "  var input = document.getElementById('myTextInput');\n"
565 
566             + "  try {\n"
567             + "    log(input.selectionStart + ',' + input.selectionEnd);\n"
568             + "  } catch(e) { logEx(e); }\n"
569 
570             + "  try{\n"
571             + "    input.value = '12345678900';\n"
572             + "  } catch(e) { logEx(e); }\n"
573             + "  try {\n"
574             + "    log(input.selectionStart + ',' + input.selectionEnd);\n"
575             + "  } catch(e) { logEx(e); }\n"
576 
577             + "  try {\n"
578             + "    input.selectionStart = " + selectionStart + ";\n"
579             + "  } catch(e) { logEx(e); }\n"
580             + "  try {\n"
581             + "    log(input.selectionStart + ',' + input.selectionEnd);\n"
582             + "  } catch(e) { logEx(e); }\n"
583 
584             + "  try {\n"
585             + "    input.selectionEnd = " + selectionEnd + ";\n"
586             + "  } catch(e) { logEx(e); }\n"
587             + "  try {\n"
588             + "    log(input.selectionStart + ',' + input.selectionEnd);\n"
589             + "  } catch(e) { logEx(e); }\n"
590             + "</script>\n"
591             + "</body>\n"
592             + "</html>";
593 
594         loadPageVerifyTitle2(html);
595     }
596 
597     /**
598      * @throws Exception if test fails
599      */
600     @Test
601     @Alerts({"null,null", "InvalidStateError/DOMException"})
602     public void selectionOnUpdate() throws Exception {
603         final String html = DOCTYPE_HTML
604             + "<html>\n"
605             + "<body>\n"
606             + "<input id='myTextInput' value='Hello' type='file'>\n"
607             + "<script>\n"
608             + LOG_TITLE_FUNCTION
609             + "  var input = document.getElementById('myTextInput');\n"
610 
611             + "  try {\n"
612             + "    log(input.selectionStart + ',' + input.selectionEnd);\n"
613 
614             + "    input.selectionStart = 4;\n"
615             + "    input.selectionEnd = 5;\n"
616             + "    log(input.selectionStart + ',' + input.selectionEnd);\n"
617             + "    input.value = 'abcdefghif';\n"
618             + "    log(input.selectionStart + ',' + input.selectionEnd);\n"
619 
620             + "    input.value = 'abcd';\n"
621             + "    log(input.selectionStart + ',' + input.selectionEnd);\n"
622 
623             + "    input.selectionStart = 0;\n"
624             + "    input.selectionEnd = 4;\n"
625 
626             + "    input.value = 'a';\n"
627             + "    log(input.selectionStart + ',' + input.selectionEnd);\n"
628             + "  } catch(e) { logEx(e); }\n"
629             + "</script>\n"
630             + "</body>\n"
631             + "</html>";
632 
633         loadPageVerifyTitle2(html);
634     }
635 
636     /**
637      * @throws Exception if the test fails
638      */
639     @Test
640     @Alerts({"changed2", "changed"})
641     public void firingOnchange() throws Exception {
642         final String html = DOCTYPE_HTML
643             + "<html><body>\n"
644             + "<script>\n"
645             + LOG_TITLE_FUNCTION
646             + "</script>\n"
647             + "<form onchange='log(\"changed\")'>\n"
648             + "  <input type='file' id='file1' onchange='log(\"changed2\")' "
649                 + "onkeydown='log(\"onkeydown2\")' "
650                 + "onkeypress='log(\"onkeypress2\")' "
651                 + "onkeyup='log(\"onkeyup2\")'>\n"
652             + "</form>\n"
653             + "</body></html>";
654 
655         final WebDriver driver = loadPage2(html);
656         final File tmpFile = File.createTempFile("htmlunit-test", ".txt");
657         driver.findElement(By.id("file1")).sendKeys(tmpFile.getAbsolutePath());
658         assertTrue(tmpFile.delete());
659 
660         verifyTitle2(driver, getExpectedAlerts());
661     }
662 
663     /**
664      * @throws Exception if the test fails
665      */
666     @Test
667     @Alerts({"true", "true"})
668     public void nonZeroWidthHeight() throws Exception {
669         final String html = DOCTYPE_HTML
670                 + "<html><head>\n"
671                 + "<script>\n"
672                 + LOG_TITLE_FUNCTION
673                 + "  function test() {\n"
674                 + "    var file = document.getElementById('testId');\n"
675                 + "    log(file.clientWidth > 2);\n"
676                 + "    log(file.clientHeight > 2);\n"
677                 + "  }\n"
678                 + "</script>\n"
679                 + "</head><body onload='test()'>\n"
680                 + "<form>\n"
681                 + "  <input type='file' id='testId'>\n"
682                 + "</form>\n"
683                 + "</body></html>";
684 
685         loadPageVerifyTitle2(html);
686     }
687 
688     /**
689      * @throws Exception if an error occurs
690      */
691     @Test
692     @Alerts("C:\\fakepath\\pom.xml--null")
693     // since 2.28
694     // there is an option for IE, for local and trusted sites IE includes the file path
695     // because we do not support any IE specific setting we do not send the filename as
696     // done by the other browsers
697     public void value2() throws Exception {
698         final String html = DOCTYPE_HTML
699               + "<html>\n"
700               + "<head>\n"
701               + "<script>\n"
702               + LOG_TITLE_FUNCTION
703               + "  function test() {\n"
704               + "    var input = document.getElementById('f');\n"
705               + "    log(input.value + '-' + input.defaultValue + '-' + input.getAttribute('value'));\n"
706               + "  }\n"
707               + "</script>\n"
708               + "</head>\n"
709               + "<body>\n"
710               + "  <input type='file' id='f'>\n"
711               + "  <button id='clickMe' onclick='test()'>Click Me</button>\n"
712               + "</body></html>";
713 
714         final String absolutePath = new File("pom.xml").getAbsolutePath();
715 
716         final WebDriver driver = loadPage2(html);
717         driver.findElement(By.id("f")).sendKeys(absolutePath);
718         driver.findElement(By.id("clickMe")).click();
719 
720         verifyTitle2(driver, getExpectedAlerts());
721     }
722 
723     /**
724      * @throws Exception if an error occurs
725      */
726     @Test
727     @Alerts({"--null", "--"})
728     public void setAttribute() throws Exception {
729         final String html = DOCTYPE_HTML
730               + "<html>\n"
731               + "<head>\n"
732               + "<script>\n"
733               + LOG_TITLE_FUNCTION
734               + "  function test() {\n"
735               + "    var input = document.createElement('input');\n"
736               + "    log(input.value + '-' + input.defaultValue + '-' + input.getAttribute('value'));\n"
737               + "    input.setAttribute('value', '');\n"
738               + "    log(input.value + '-' + input.defaultValue + '-' + input.getAttribute('value'));\n"
739               + "  }\n"
740               + "</script>\n"
741               + "</head>\n"
742               + "<body onload='test()'>\n"
743               + "</body></html>";
744 
745         loadPageVerifyTitle2(html);
746     }
747 
748     /**
749      * @throws Exception if an error occurs
750      */
751     @Test
752     @Alerts({"foo", "change"})
753     public void onchange() throws Exception {
754         final String html = DOCTYPE_HTML
755               + "<html>\n"
756               + "<head>\n"
757               + "<script>\n"
758               + LOG_TITLE_FUNCTION
759               + "</script>\n"
760               + "</head>\n"
761               + "<body>\n"
762               + "  <input type='file' id='f' value='Hello world' onChange='log(\"foo\");log(event.type);'>\n"
763               + "  <button id='clickMe' onclick='test()'>Click Me</button>\n"
764               + "</body></html>";
765 
766         final String absolutePath = new File("pom.xml").getAbsolutePath();
767 
768         final WebDriver driver = loadPage2(html);
769         driver.findElement(By.id("f")).sendKeys(absolutePath);
770 
771         verifyTitle2(driver, getExpectedAlerts());
772     }
773 
774     /**
775      * @throws Exception if an error occurs
776      */
777     @Test
778     @Alerts("C:\\fakepath\\pom.xml")
779     public void getAttribute() throws Exception {
780         final String html = DOCTYPE_HTML
781               + "<html><body>\n"
782               + "  <input type='file' id='f'>\n"
783               + "</body></html>";
784 
785         final String absolutePath = new File("pom.xml").getAbsolutePath();
786 
787         final WebDriver driver = loadPage2(html);
788         final WebElement e = driver.findElement(By.id("f"));
789         e.sendKeys(absolutePath);
790         assertNull(e.getDomAttribute("value"));
791         assertEquals(getExpectedAlerts()[0], e.getDomProperty("value"));
792     }
793 
794     /**
795      * @throws Exception if the test fails
796      */
797     @Test
798     @Alerts("--")
799     public void minMaxStep() throws Exception {
800         final String html = DOCTYPE_HTML
801             + "<html>\n"
802             + "<head>\n"
803             + "<script>\n"
804             + LOG_TITLE_FUNCTION
805             + "  function test() {\n"
806             + "    var input = document.getElementById('tester');\n"
807             + "    log(input.min + '-' + input.max + '-' + input.step);\n"
808             + "  }\n"
809             + "</script>\n"
810             + "</head>\n"
811             + "<body onload='test()'>\n"
812             + "<form>\n"
813             + "  <input type='file' id='tester'>\n"
814             + "</form>\n"
815             + "</body>\n"
816             + "</html>";
817 
818         loadPageVerifyTitle2(html);
819     }
820 
821     /**
822      * @throws Exception if an error occurs
823      */
824     @Test
825     @Alerts({"true", "false", "true", "false", "true"})
826     public void willValidate() throws Exception {
827         final String html = DOCTYPE_HTML
828                 + "<html><head>\n"
829                 + "  <script>\n"
830                 + LOG_TITLE_FUNCTION
831                 + "    function test() {\n"
832                 + "      log(document.getElementById('o1').willValidate);\n"
833                 + "      log(document.getElementById('o2').willValidate);\n"
834                 + "      log(document.getElementById('o3').willValidate);\n"
835                 + "      log(document.getElementById('o4').willValidate);\n"
836                 + "      log(document.getElementById('o5').willValidate);\n"
837                 + "    }\n"
838                 + "  </script>\n"
839                 + "</head>\n"
840                 + "<body onload='test()'>\n"
841                 + "  <form>\n"
842                 + "    <input type='file' id='o1'>\n"
843                 + "    <input type='file' id='o2' disabled>\n"
844                 + "    <input type='file' id='o3' hidden>\n"
845                 + "    <input type='file' id='o4' readonly>\n"
846                 + "    <input type='file' id='o5' style='display: none'>\n"
847                 + "  </form>\n"
848                 + "</body></html>";
849 
850         loadPageVerifyTitle2(html);
851     }
852 
853     /**
854      * @throws Exception if an error occurs
855      */
856     @Test
857     @Alerts({"true",
858              "false-false-false-false-false-false-false-false-false-true-false",
859              "true"})
860     public void validationEmpty() throws Exception {
861         validation("<input type='file' id='e1'>\n", "");
862     }
863 
864     /**
865      * @throws Exception if an error occurs
866      */
867     @Test
868     @Alerts({"false",
869              "false-true-false-false-false-false-false-false-false-false-false",
870              "true"})
871     public void validationCustomValidity() throws Exception {
872         validation("<input type='file' id='e1'>\n", "elem.setCustomValidity('Invalid');");
873     }
874 
875     /**
876      * @throws Exception if an error occurs
877      */
878     @Test
879     @Alerts({"false",
880              "false-true-false-false-false-false-false-false-false-false-false",
881              "true"})
882     public void validationBlankCustomValidity() throws Exception {
883         validation("<input type='file' id='e1'>\n", "elem.setCustomValidity(' ');\n");
884     }
885 
886     /**
887      * @throws Exception if an error occurs
888      */
889     @Test
890     @Alerts({"true",
891              "false-false-false-false-false-false-false-false-false-true-false",
892              "true"})
893     public void validationResetCustomValidity() throws Exception {
894         validation("<input type='file' id='e1'>\n",
895                 "elem.setCustomValidity('Invalid');elem.setCustomValidity('');");
896     }
897 
898     /**
899      * @throws Exception if an error occurs
900      */
901     @Test
902     @Alerts({"false",
903              "false-false-false-false-false-false-false-false-false-false-true",
904              "true"})
905     public void validationRequired() throws Exception {
906         validation("<input type='file' id='e1' required>\n", "");
907     }
908 
909     private void validation(final String htmlPart, final String jsPart) throws Exception {
910         final String html = DOCTYPE_HTML
911                 + "<html><head>\n"
912                 + "  <script>\n"
913                 + LOG_TITLE_FUNCTION
914                 + "    function logValidityState(s) {\n"
915                 + "      log(s.badInput"
916                         + "+ '-' + s.customError"
917                         + "+ '-' + s.patternMismatch"
918                         + "+ '-' + s.rangeOverflow"
919                         + "+ '-' + s.rangeUnderflow"
920                         + "+ '-' + s.stepMismatch"
921                         + "+ '-' + s.tooLong"
922                         + "+ '-' + s.tooShort"
923                         + " + '-' + s.typeMismatch"
924                         + " + '-' + s.valid"
925                         + " + '-' + s.valueMissing);\n"
926                 + "    }\n"
927                 + "    function test() {\n"
928                 + "      var elem = document.getElementById('e1');\n"
929                 + jsPart
930                 + "      log(elem.checkValidity());\n"
931                 + "      logValidityState(elem.validity);\n"
932                 + "      log(elem.willValidate);\n"
933                 + "    }\n"
934                 + "  </script>\n"
935                 + "</head>\n"
936                 + "<body onload='test()'>\n"
937                 + "  <form>\n"
938                 + htmlPart
939                 + "  </form>\n"
940                 + "</body></html>";
941 
942         loadPageVerifyTitle2(html);
943     }
944 
945     /**
946      * @throws Exception if an error occurs
947      */
948     @Test
949     @Alerts("0")
950     public void clear() throws Exception {
951         final String html = DOCTYPE_HTML
952                 + "<html><head>\n"
953                 + "  <script>\n"
954                 + LOG_TITLE_FUNCTION
955                 + "    function test() {\n"
956                 + "      var f =  document.createElement('input');\n"
957                 + "      f.type='file';\n"
958                 + "      f.id='fileId';\n"
959                 + "      document.body.appendChild(f);"
960 
961                 + "      f.value='';\n"
962                 + "      log(f.files.length);\n"
963                 + "    }\n"
964                 + "  </script>\n"
965                 + "</head>\n"
966                 + "<body onload='test()'>\n"
967                 + "</body></html>";
968 
969         loadPageVerifyTitle2(html);
970     }
971 
972     /**
973      * @throws Exception if an error occurs
974      */
975     @Test
976     @Alerts({"C:\\fakepath\\pom.xml-Hello world-Hello world",
977              "<input type=\"file\" id=\"f\" value=\"Hello world\" multiple=\"\">"})
978     public void valueFakepath() throws Exception {
979         final String html = DOCTYPE_HTML
980               + "<html>\n"
981               + "<head>\n"
982               + "<script>\n"
983               + LOG_TITLE_FUNCTION
984               + "  function test() {\n"
985               + "    var input = document.getElementById('f');\n"
986               + "    log(input.value + '-' + input.defaultValue + '-' + input.getAttribute('value'));\n"
987               + "    log(input.outerHTML);\n"
988               + "  }\n"
989               + "</script></head>\n"
990               + "<body>\n"
991               + "  <input type='file' id='f' value='Hello world' multiple>\n"
992               + "  <button id='clickMe' onclick='test()'>Click Me</button>\n"
993               + "</body></html>";
994 
995         final File pom = new File("pom.xml");
996 
997         final WebDriver driver = loadPage2(html);
998         driver.findElement(By.id("f")).sendKeys(pom.getAbsolutePath());
999         driver.findElement(By.id("clickMe")).click();
1000         verifyTitle2(driver, getExpectedAlerts());
1001     }
1002 
1003     /**
1004      * @throws Exception if an error occurs
1005      */
1006     @Test
1007     @Alerts({"C:\\fakepath\\index.html-Hello world-Hello world",
1008              "<input type=\"file\" id=\"f\" value=\"Hello world\" multiple=\"\" webkitdirectory=\"\">"})
1009     public void valueWebkitdirectory() throws Exception {
1010         final String html = DOCTYPE_HTML
1011               + "<html>\n"
1012               + "<head>\n"
1013               + "<script>\n"
1014               + LOG_TITLE_FUNCTION
1015               + "  function test() {\n"
1016               + "    var input = document.getElementById('f');\n"
1017               + "    log(input.value + '-' + input.defaultValue + '-' + input.getAttribute('value'));\n"
1018               + "    log(input.outerHTML);\n"
1019               + "  }\n"
1020               + "</script></head>\n"
1021               + "<body>\n"
1022               + "  <input type='file' id='f' value='Hello world' multiple webkitdirectory>\n"
1023               + "  <button id='clickMe' onclick='test()'>Click Me</button>\n"
1024               + "</body></html>";
1025 
1026         final File dir = new File("src/test/resources/pjl-comp-filter");
1027         assertTrue(dir.exists());
1028 
1029         final WebDriver driver = loadPage2(html);
1030         driver.findElement(By.id("f")).sendKeys(dir.getAbsolutePath());
1031         driver.findElement(By.id("clickMe")).click();
1032         verifyTitle2(driver, getExpectedAlerts());
1033     }
1034 }