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