3

Set-Up

I'm using Python + Selenium to upload an image to the back-end of a system.

The upload button to which I send_keys() has the following HTML,

<div id="uploadifive-FileNameUpload" class="uploadifive-button" style="height:
18px; line-height: 18px; overflow: hidden; position: relative; text-align: center;
width: 50px;">
  Upload
  <input id="FileNameUpload" name="FileNameUpload" data-editor="#FileName" 
  data-url="http://also-inc.com/upload/uploadfile" data-path="~/UserFiles/Products/Images/" 
  data-maxsize="10240" data-extensions="*.jpg;*.jpeg;*.png;*.gif;*.bmp;" 
  data-thumbnailwidth="128" data-thumbnailheight="128" 
  data-thumbnailpath="/UserFiles/Products/Images/Preview/" data-uniquename="True" 
  data-preview="/Content/uploadify/noimage.jpg" data-isnew="true" 
  data-auth="D2C14774E29BBB87D2F34719884CFC5C6370502B067D5FC55D0C40A5EE6B1646ED4C77C9C0180D607052FF52653BA981732417A24C3F7547903649C4D64491C184E1C60D7756608784B4B3E806417E77750D87BABD9CDDCB6294EA62DE884EC7B3A4416558405874ED1C0259CD4430990BA83FC0" 
  data-session="f1txsiyxglqb3ma1dr45awrf" class="file-uploader hide-input" style="display: none;" 
  type="file">
  <input style="font-size: 18px; opacity: 0; position: absolute; right: -3px; top: -3px; z-index: 999;" type="file">
</div>

Note that the button has two input with type="file".


Code Used

upload_button = el_id('uploadifive-FileNameUpload').find_element_by_xpath('input[2]')
upload_button.send_keys(path_to_my_image)  

where el_id() = browser.find_element_by_id() and path_to_my_image ends with .jpeg.


Problem

The code used successfully uploads the image via the second input. The image, however, is saved without the .jpeg extension. Thus the image appears broken in the back-end; i.e. image_name instead of image_name.jpeg.

I think the image is saved without the extension because the second input does not allow for it.


Tries

  1. adding an attribute to second input

The first input has the following attribute data-extensions="*.jpg;*.jpeg;*.png;*.gif;*.bmp;". I added this attribute to the second input,

upload_button = el_id('uploadifive-FileNameUpload').find_element_by_xpath('input[2]')
browser.execute_script("arguments[0].setAttribute('data-extensions','*.jpg;*.jpeg;*.png;*.gif;*.bmp;')", upload_button)
upload_button = el_id('uploadifive-FileNameUpload').find_element_by_xpath('input[2]')
upload_button.send_keys(path_to_my_image)  

This added the attribute, but the uploaded image was still saved without .jpeg extension.

  1. changing class and style attribute of first input

The first input has class="file-uploader hide-input" and style="display: none;", which I set to class="file-uploader" and style="display: block;" via,

upload_button = el_id('uploadifive-FileNameUpload').find_element_by_xpath('input[1]')
browser.execute_script("arguments[0].setAttribute('class','file-uploader hide-input')", upload_button)
upload_button = el_id('uploadifive-FileNameUpload').find_element_by_xpath('input[1]')
browser.execute_script("arguments[0].setAttribute('style','display: block')", upload_button)
upload_button = el_id('uploadifive-FileNameUpload').find_element_by_xpath('input[1]')
upload_button.send_keys(path_to_my_image)  

but then the image doesn't upload. The upload button changes to a native OS file picker 'Select file' button.

How do I solve this?


Sidenote

I don't understand why this button has two input. I'm a beginner, so forgive me if I'm wrong, but intuitively I think a button just needs 1 input?

LucSpan
  • 1,831
  • 6
  • 31
  • 66
  • You say the file uploads successfully—just without the extension. Where exactly did you see the file? Is the rest of the name correct? That is, is it only the extension that is missing? Are there any other differences? E.g., is it in the same folder as other files uploaded manually (i.e., not with Selenium)? – Ian Lesperance Apr 03 '18 at 11:57
  • Probably a dumb question, but are you sure that path_to_my_image contains the file extension? – Arnon Axelrod Apr 04 '18 at 19:57

2 Answers2

0

As per your code attempts seems we can't do much with the second <input> tag. But you can simply remove the attribute style="display: none;" from the first <input> tag and try to invoke send_keys() method as follows :

element = driver.find_element_by_xpath("//input[@id='FileNameUpload' and @name='FileNameUpload']")
driver.execute_script("arguments[0].removeAttribute('style')", element)
driver.find_element_by_xpath("//input[@id='FileNameUpload' and @name='FileNameUpload']").send_keys("path_to_my_image")

Note : path_to_my_image must be the absolute path of the image file ending with either .jpg, .jpeg, .png, .gif or .bmp extension and must be less then 10240 bytes.

undetected Selenium
  • 183,867
  • 41
  • 278
  • 352
  • Thanks! I've tried your code, but I get: `ElementNotInteractableException: Element is not reachable by keyboard` – LucSpan Apr 02 '18 at 18:23
  • @LucSpan For debugging purpose induce some hardcore wait. If that works I will provide the WebdriverWait to wrap up the code. Perhaps `not reachable by keyboard` stems out of mismatch of the binaries you are using interms of _WebDriver_ variant and _WebClient_ variant. – undetected Selenium Apr 02 '18 at 18:30
  • hardcore wait = `time.sleep()`? – LucSpan Apr 02 '18 at 18:31
  • I've tried 1) `time.sleep(10)` between your first and second line, 2) `time.sleep(10)` between your second and third line, but both give again the not reachable by keyboard error. – LucSpan Apr 02 '18 at 18:35
  • How about your binary versions? – undetected Selenium Apr 02 '18 at 18:37
  • I'm afraid I don't know what you mean with 'your binary versions'. I also don't know what you mean with WebDriver and WebClient variants. – LucSpan Apr 02 '18 at 18:42
  • I am asking about _Selenium_ version, _ChromeDriver/GeckoDriver/IEDriverServer_ version and _Chrome/Firefox/IE_ Browser version information and if they are compatible? – undetected Selenium Apr 02 '18 at 18:45
  • My bad :) Selenium 3.4.3, GeckoDriver 0.19.0, Firefox 59.0.2 (64-bits). – LucSpan Apr 02 '18 at 18:52
  • Can you bump up _Selenium_ to v**3.11.0**, _GeckoDriver_ to v**0.20.0** and re-execute your tests? – undetected Selenium Apr 02 '18 at 19:01
0

In <input> tags the text is in the value attribute. Setting it with JavaScript should work

upload_button = el_id('FileNameUpload')
browser.execute_script("arguments[0].value = 'arguments[1]';", upload_button, path_to_my_image)
Guy
  • 46,488
  • 10
  • 44
  • 88
  • Thank you for thinking with me. I ran your code, but got a `JavascriptException: SecurityError: The operation is insecure.` Found this answer: https://stackoverflow.com/questions/13348766/securityerror-the-operation-is-insecure-window-history-pushstate. Not sure how to proceed though. – LucSpan Apr 03 '18 at 09:51
  • @LucSpan just for the fun of it try to use `send_keys` on the hidden field `upload_button = el_id('FileNameUpload') upload_button.send_keys(path_to_my_image)` (without any javascript) – Guy Apr 03 '18 at 09:59
  • Already tried that before, gives me an `ElementNotInteractableException: Element is not reachable by keyboard`. See original question. – LucSpan Apr 03 '18 at 10:01
  • @LucSpan I see. You can also try another way to make it visible `execute_script("document.getElementById('FileNameUpload'‌​).style.visibility='‌​visible'")` – Guy Apr 03 '18 at 10:10
  • This gives an `JavascriptException: SyntaxError: illegal character` – LucSpan Apr 03 '18 at 10:15
  • @LucSpan try this one `execute_script("document.getElementById('FileNameUpload').style.visibility='‌​visible'")` – Guy Apr 03 '18 at 10:18
  • Same. Thank you for your help, I'm going to ask the developer to change the button. – LucSpan Apr 03 '18 at 11:06
  • You can't set the value of a `file` field using JavaScript, regardless of its visibility. As the exception clearly shows, it's a security issue. (If you could set the value with JavaScript, you could trick the browser into uploading any file on the user's system.) – Ian Lesperance Apr 03 '18 at 11:53