I usually put file upload waaaay down the list of things to automate because it is generally a pain in the butt. Not only do you have to manage the files to upload, but you have to negotiate the actual interact with the input boxes. Now, if you are using a flash uploader, just move along. (And then stop using a flash uploader and get a pure HTML implementation.)

I’ve been thinking about how to manage file uploads for awhile in Saunter but finally got around to implementing it as a first step towards something else that is in the pipeline.

Se-RC

I’ve always used something similar as Dealing With File Downloads With Selenium which drops out of your Se script into AutoIT3. It seems though that there is a little known call in the Se-RC API called attach_file which not only will fill in an input field with a path for you but will copy the file from the script machine to the browser machine for you. Why have I not known about this before?!!

One ‘quirk’ of this call is that the Se Server will use HTTP to grab the file that it is going to use in the input box, this means that you have to have the file hosted somewhere via HTTP. In a Saunter world, the files to be uploaded sit in directories under support/files so you want to use the support directory as your web root.

I had thought originally of building in a tiny web server to do this hosting, but that is again crossing out of the framework side of things and into the infrastructure side which is outside of scope. Somewhere in your network will need to have these files hosted and in most places there are enough spare web servers kicking around to handle this, or if you can quickly run one on your local machine.

<pre lang="php">php -S localhost:7000
<pre lang="python">python -m SimpleHTTPServer 7000

Once you have determined the port that you are going to run on, its just a matter of using it in your script.

PHP

<pre lang="php">$GLOBALS['settings']['YourCompany'] = array(
  "file_server_base" => "http://a.flyingmonkey.ca:7000",
);
<pre lang="php">function upload($image, $storetime="30 Minutes", $obscure_filename="basic", $accept_rules="Yes") {
  $path_to_image = "{$GLOBALS['settings']['YourCompany']['file_server_base']}/files/$image";

  $this->upload = $path_to_image;
  $this->storetime = "label=$storetime";
  $this->obscure_filename = "label=$obscure_filename";
  $this->accept_rules = "label=$accept_rules";

  self::$selenium->click($this->locators['button']);

  $p = new Preview();
  $p->wait_until_loaded();
  return $p;
}

Python

<pre lang="python">[YourCompany]
file_server_base: http://a.flyingmonkey.ca:7000
<pre lang="python">def upload(self, image, storetime="30 Minutes", obscure_filename="basic", accept_rules="Yes"):
    path_to_image = "%s/files/%s" % (self.config.get('YourCompany', 'file_server_base'), image)

    self.selenium.attach_file(locators['upload'], path_to_image)

    self.storetime = "label=%s" % storetime
    self.obscure_filename = "label=%s" % obscure_filename
    self.accept_rules = "label=%s" % accept_rules

    self.selenium.click(locators['button'])

    p = Preview().wait_until_loaded()
    return p

Sauce Labs

If you are running your scripts through Sauce Labs, they are not going to be able to get these files if they are hosted behind your firewall. Either you can host them on a publicly accessible cloud instance or you can can run Sauce Connect which punches a secure tunnel between their VM and your network. One thing it doesn’t handle though is your internal DNS which is why you need to hack the hosts file on the machine running Sauce Connect. In the above example I used a.flyingmonkey.ca but you can use whatever you want. Once you have that little hack in place the tunnel knows how to find your files.

WebDriver

The Remote WebDriver API (which is what Saunter uses) handles both the upload of a file from a local path on the script machine to the execution machine and set the path to the remote file on an input element transparently through send_keys. Not only does this let us not learn another corner of an API, but it means we don’t need to host things via a web server somewhere.

PHP

Note: You need to be using at least PHPWebDriver-1.8.0 which comes with SaunterPHP-1.0.12 in order for this to work.

<pre lang="php">$GLOBALS['settings']['YourCompany'] = array(
  "file_server_base" => "http://a.flyingmonkey.ca:7000",
);
<pre lang="php">function upload($image, $storetime="30 Minutes", $obscure_filename="basic", $accept_rules="Yes") {
  $path_to_image = "{$GLOBALS['settings']['saunter.base']}/support/files/$image";

  $this->upload = $path_to_image;
  $this->storetime = $storetime;
  $this->obscure_filename = $obscure_filename;
  $this->accept_rules = $accept_rules;

  $b = call_user_func_array(array(self::$session, "element"), $this->locators['button']);
  $b->click();

  $p = new Preview(self::$session);
  $p->wait_until_loaded();
  return $p;
}

Python

<pre lang="python">[YourCompany]
file_server_base: http://a.flyingmonkey.ca:7000
<pre lang="python">def upload(self, image, storetime="30 Minutes", obscure_filename="basic", accept_rules="Yes"):
    path_to_image = os.path.join(self.config.get('Saunter', 'base'), 'support', 'files', image)

    u = self.driver.find_element_by_locator(locators['upload'])
    u.send_keys(path_to_image)

    self.storetime.selected = "text=%s" % storetime
    self.obscure_filename.selected = "text=%s" % obscure_filename
    self.accept_rules.selected = "text=%s" % accept_rules

    button = self.driver.find_element_by_locator(locators['button'])
    button.click()

    p = Preview(self.driver).wait_until_loaded()
    return p

All these examples are available in Github; PHP, Python