By Sergey, a quality assurance manager on Murano Software’s team
If you have ever tried to upload a file under Selenium RC, you must know that it is impossible to type in the file upload text field. This caused by a JavaScript security restriction. JavaScript is not allowed to modify the value of <input type="file"> form fields. http://wiki.openqa.org suggests to work around this by running the tests in the experimental “chrome” mode for Firefox. But it is mentioned that there is no way to do this on any other browser.
I have tried to use the “chrome” mode for Firefox and the “iehta” mode for IE, but my tests turned out very unstable. Fortunately, there is another workaround.
For Firefox and IE, you can set the focus inside the file upload field by using JavaScript.
selenium.getEval("this.browserbot.getCurrentWindow().document.getElementById('FileUploadFieldId').focus();")
Then use the selenium.keyPressNative () method to type the file path, i.e. C:\Some Folder\TestFile.txt. The method simulates a user pressing and releasing a key by sending a native operating system keystroke. I created a helper method to type the file path. It handles upper case and lower case letters, back slashes, spaces and periods.
import java.awt.event.KeyEvent
protected void typeNative(String filePath) throws InterruptedException
{
for (char c : filePath.toCharArray()) {
if (c == ':') {
selenium.keyDownNative(Integer.toString(KeyEvent.VK_SHIFT));
selenium.keyPressNative(Integer.toString(KeyEvent.VK_SEMICOLON));
selenium.keyUpNative(Integer.toString(KeyEvent.VK_SHIFT));
}
else if (c == '\\') {
selenium.keyPressNative(Integer.toString(KeyEvent.VK_BACK_SLASH));
}
else if (c == '.') {
selenium.keyPressNative(Integer.toString(KeyEvent.VK_PERIOD));
}
else if (c == ' ') {
selenium.keyPressNative(Integer.toString(KeyEvent.VK_SPACE));
}
else {
KeyStroke key = KeyStroke.getKeyStroke("pressed " + Character.toUpperCase(c));
if (null != key) {
// should only have to worry about case with standard characters
if (Character.isUpperCase(c)) {
selenium.keyDownNative(Integer.toString(KeyEvent.VK_SHIFT));
}
selenium.keyPressNative(Integer.toString(key.getKeyCode()));
if (Character.isUpperCase(c)) {
selenium.keyUpNative(Integer.toString(KeyEvent.VK_SHIFT));
}
}
}
}
}
If you're using Selenium RC and Java, you can exploit java.awt.Robot, instead of using the selenium.keyPressNative() method. See the code sample below.
For Safari, there is no rendered field in the HTML page into which you can simply type keys. Safari requires that the user interact with a “File Upload” dialog box. To get the dialog, you can put selenium.click() on the file upload field. But here you will meet another unpleasant surprise. The “File Upload” dialog box will be loaded in a modal window, so the executing of your code will be held up until the modal window is closed.
To handle that, you need to invoke the type native method in the other thread one step before clicking on the file upload field and set a delay to wait until the modal window with the “File Upload” dialog box appears. See the code sample that uses java.awt.Robot.
protected void chooseFile(String element, String filePath) throws InterruptedException
{
new FileChooserThread(filePath).start();
selenium.click(element);
}
To invoke the type native method, just add new class containing the code below to your project.
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
public class FileChooserThread extends Thread
{
public FileChooserThread(String file) {
super(new FileRunner(file));
}
}
class FileRunner implements Runnable
{
private String fullName;
public FileRunner(String fileName) {
this.fullName = fileName;
}
public void run() {
try {
Thread.sleep(2000);
Robot robot = new Robot(); // input simulation class
for (char c : fullName.toCharArray()){
if (c == ':') {
robot.keyPress(KeyEvent.VK_SHIFT);
robot.keyPress(KeyEvent.VK_SEMICOLON);
robot.keyRelease(KeyEvent.VK_SHIFT);
}
else if (c == '\\') {
robot.keyPress(KeyEvent.VK_BACK_SLASH);
}
else if (c == '.') {
robot.keyPress(KeyEvent.VK_PERIOD);
}
else if (c == ' ') {
robot.keyPress(KeyEvent.VK_SPACE);
}
else {
KeyStroke key = KeyStroke.getKeyStroke("pressed " + Character.toUpperCase(c));
if (null != key) {
// should only have to worry about case with standard characters
if (Character.isUpperCase(c)) {
robot.keyPress(KeyEvent.VK_SHIFT);
}
robot.keyPress(key.getKeyCode());
robot.keyRelease(key.getKeyCode());
if (Character.isUpperCase(c)) {
robot.keyRelease(KeyEvent.VK_SHIFT);
}
}
}
}
robot.keyPress(KeyEvent.VK_ENTER);
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
}
The final remark is addressed to the focus. This workaround will work only if the system focus is into the file upload field. I had to start the selenium server with the -multiWindow parameter to set the system focus into the browser window.