-
Notifications
You must be signed in to change notification settings - Fork 163
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Issue #187: Add support for uploading files to remote Selenium instance #252
Changes from 4 commits
8ff8c99
de3acde
c6fded0
f519ea6
3bff87e
0a18331
2064e99
506a2a0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
#!/usr/bin/env sh | ||
set -e | ||
|
||
echo ' Downloading selenium' | ||
docker pull selenium/standalone-firefox:2.53.1 | ||
echo ' Running selenium' | ||
docker run -d -p 4444:4444 --network=host selenium/standalone-firefox:2.53.1 |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,6 +13,7 @@ | |
use Behat\Mink\Exception\DriverException; | ||
use Behat\Mink\Selector\Xpath\Escaper; | ||
use WebDriver\Element; | ||
use WebDriver\Exception\InvalidRequest; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The Scrutinizer CI says that this class import isn't used anymore: https://scrutinizer-ci.com/g/minkphp/MinkSelenium2Driver/inspections/89c102e8-c656-4124-b660-6129c7ec2f16/patches?status=new |
||
use WebDriver\Exception\NoSuchElement; | ||
use WebDriver\Exception\UnknownError; | ||
use WebDriver\Exception; | ||
|
@@ -794,7 +795,18 @@ public function attachFile($xpath, $path) | |
$element = $this->findElement($xpath); | ||
$this->ensureInputType($element, $xpath, 'file', 'attach a file on'); | ||
|
||
$element->postValue(array('value' => array($path))); | ||
// Upload the file to Selenium and use the remote path. This will | ||
// ensure that Selenium always has access to the file, even if it runs | ||
// as a remote instance. | ||
try { | ||
$remote_path = $this->uploadFile($path); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please use camelCase variables names |
||
} | ||
catch (InvalidRequest $e) { | ||
// File could not be uploaded to remote instance. Use the local path. | ||
$remote_path = $path; | ||
} | ||
|
||
$element->postValue(array('value' => array($remote_path))); | ||
} | ||
|
||
/** | ||
|
@@ -1120,4 +1132,52 @@ private function trigger($xpath, $event, $options = '{}') | |
$script = 'Syn.trigger("' . $event . '", ' . $options . ', {{ELEMENT}})'; | ||
$this->withSyn()->executeJsOnXpath($xpath, $script); | ||
} | ||
|
||
/** | ||
* Uploads a file to the Selenium instance. | ||
* | ||
* Note that uploading files is not part of the official WebDriver | ||
* specification, but it is supported by Selenium. | ||
* @see https://github.com/SeleniumHQ/selenium/blob/master/py/selenium/webdriver/remote/webelement.py#L533 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please move |
||
* | ||
* @param string $path The path to the file to upload. | ||
* | ||
* @return string The remote path. | ||
* | ||
* @throws DriverException When the file is not found. | ||
* @throws InvalidRequest When the driver does not support file uploads. | ||
*/ | ||
public function uploadFile($path) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The opening P.S. |
||
if (!is_file($path)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There wasn't such check before. Is it even needed? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not strictly needed :) |
||
throw new DriverException('Could not upload file, the file: ' . $path . '. was not found.'); | ||
} | ||
|
||
// Selenium only accepts uploads that are compressed as a Zip archive. | ||
$temp_filename = tempnam('', 'WebDriverZip'); | ||
|
||
$archive = new \ZipArchive(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also it seems, that you always are using To preserve BC I think, that:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This method is not replacing the uploading that was done before. That part is still handled by Do you think it would be maybe a good idea to rename this method to When doing this transfer the file always needs to be zipped, it never works otherwise. If you try to transfer an unzipped file to Selenium you get this error:
Note that it is not possible to figure out if Selenium runs locally or remotely. A remote instance might be forwarded to a local port, appearing exactly the same to us as a local instance. So the only thing we can do is to attempt a transfer. When this succeeds we get a remote path back which we can use. When anything goes wrong we catch this and continue as normal with the original file path. I've restructured it a bit now so that it works as you suggest, any error throws an exception which will be caught and the file will be attached in the old way. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Then when There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes that is what is going to happen now. If |
||
$archive->open($temp_filename, \ZipArchive::CREATE); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you need to check the return value here, as ZipArchive uses the return value to handle errors instead of throwing exceptions |
||
$archive->addFile($path, basename($path)); | ||
$archive->close(); | ||
|
||
try { | ||
$remote_path = $this->getWebDriverSession()->file(array('file' => base64_encode(file_get_contents($temp_filename)))); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use |
||
} | ||
catch (InvalidRequest $e) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is Maybe we should catch and rethrow all exceptions? |
||
// Catch the error so we can still clean up the temporary archive. | ||
} | ||
|
||
unlink($temp_filename); | ||
|
||
// If the file upload failed, then probably Selenium was not used but | ||
// another web driver such as PhantomJS. | ||
// @todo Support other drivers when (if) they get remote file transfer | ||
// capability. | ||
if (empty($remote_path)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Better to replace with this: if (isset($e)) {
throw $e;
} The above version:
|
||
throw new InvalidRequest(); | ||
} | ||
|
||
return $remote_path; | ||
} | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This uses Docker on Travis CI? I wonder how slow that might me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's quite comparable it seems, the build takes about 30 seconds longer. It looks like this time is needed to download the docker image.
I think this is the most cost effective way of testing a remote instance of Selenium. If we want to try a "real" remote then we probably have to go with a hosted service like Browserstack.