mirror of
https://github.com/silverstripe/silverstripe-behat-extension
synced 2024-10-22 17:05:32 +02:00
Merge pull request #174 from open-sausages/pulls/4.0/selenium-upgrade
API Update to use new facebook driver extension
This commit is contained in:
commit
55e9221dcd
16
README.md
16
README.md
@ -102,6 +102,22 @@ This will start a Firefox browser by default. Other browsers and profiles can be
|
||||
|
||||
For example, if you want to start a Chrome Browser you can following the instructions provided [here](docs/chrome-behat.md).
|
||||
|
||||
### Running with stand-alone command
|
||||
|
||||
If running with `silverstripe/serve` and `chromedriver`, you can also use the following command
|
||||
which will automatically start and stop these services for individual tests.
|
||||
|
||||
vendor/bin/behat-ss @framework
|
||||
|
||||
This automates:
|
||||
- starting server
|
||||
- starting chromedriver
|
||||
- running behat
|
||||
- shutting down chromedriver
|
||||
- shutting down server
|
||||
|
||||
Make sure you set `SS_BASE_URL` to `http://localhost:8080` in `.env`
|
||||
|
||||
## Tutorials
|
||||
|
||||
* [Tutorial: Testing Form Submissions](docs/tutorial.md)
|
||||
|
20
bin/behat-ss
Executable file
20
bin/behat-ss
Executable file
@ -0,0 +1,20 @@
|
||||
#!/bin/sh
|
||||
echo "setting up /artifacts"
|
||||
mkdir -p artifacts
|
||||
|
||||
echo "starting chromedriver"
|
||||
chromedriver &> artifacts/chromedriver.log 2> artifacts/chromedriver-error.log &
|
||||
cd_pid=$!
|
||||
|
||||
echo "starting webserver"
|
||||
vendor/bin/serve &> artifacts/serve.log 2> artifacts/serve-error.log &
|
||||
ws_pid=$!
|
||||
|
||||
echo "starting behat"
|
||||
vendor/bin/behat "$@"
|
||||
|
||||
echo "killing webserver (PID: $ws_pid)"
|
||||
pkill -TERM -P $ws_pid &> /dev/null
|
||||
|
||||
echo "killing chromedriver (PID: $cd_pid)"
|
||||
kill -9 $cd_pid &> /dev/null
|
@ -26,10 +26,10 @@
|
||||
"behat/behat": "^3.2",
|
||||
"behat/mink": "^1.7",
|
||||
"behat/mink-extension": "^2.1",
|
||||
"behat/mink-selenium2-driver": "^1.3",
|
||||
"silverstripe/mink-facebook-web-driver": "^1",
|
||||
"symfony/dom-crawler": "^3",
|
||||
"silverstripe/testsession": "^2.0.0@alpha",
|
||||
"silverstripe/framework": "^4@dev",
|
||||
"silverstripe/testsession": "^2.1",
|
||||
"silverstripe/framework": "^4",
|
||||
"symfony/finder": "^3.2"
|
||||
},
|
||||
"autoload": {
|
||||
@ -47,6 +47,9 @@
|
||||
"3.x-dev": "3.1.x-dev"
|
||||
}
|
||||
},
|
||||
"bin": [
|
||||
"bin/behat-ss"
|
||||
],
|
||||
"scripts": {
|
||||
"lint": "phpcs --standard=PSR2 -n src/ tests/php/"
|
||||
},
|
||||
|
@ -8,16 +8,19 @@ use Behat\Behat\Hook\Scope\AfterScenarioScope;
|
||||
use Behat\Behat\Hook\Scope\AfterStepScope;
|
||||
use Behat\Behat\Hook\Scope\BeforeStepScope;
|
||||
use Behat\Behat\Hook\Scope\StepScope;
|
||||
use Behat\Mink\Driver\Selenium2Driver;
|
||||
use Behat\Mink\Element\NodeElement;
|
||||
use Behat\Mink\Session;
|
||||
use Behat\Testwork\Tester\Result\TestResult;
|
||||
use Exception;
|
||||
use Facebook\WebDriver\Exception\WebDriverException;
|
||||
use Facebook\WebDriver\WebDriver;
|
||||
use Facebook\WebDriver\WebDriverAlert;
|
||||
use Facebook\WebDriver\WebDriverExpectedCondition;
|
||||
use InvalidArgumentException;
|
||||
use SilverStripe\Assets\File;
|
||||
use SilverStripe\Assets\Filesystem;
|
||||
use SilverStripe\BehatExtension\Utility\StepHelper;
|
||||
use WebDriver\Exception as WebDriverException;
|
||||
use WebDriver\Session as WebDriverSession;
|
||||
use SilverStripe\MinkFacebookWebDriver\FacebookWebDriver;
|
||||
|
||||
/**
|
||||
* BasicContext
|
||||
@ -248,7 +251,7 @@ JS;
|
||||
|
||||
/**
|
||||
* Take screenshot when step fails.
|
||||
* Works only with Selenium2Driver.
|
||||
* Works only with FacebookWebDriver.
|
||||
*
|
||||
* @AfterStep
|
||||
* @param AfterStepScope $event
|
||||
@ -282,7 +285,7 @@ JS;
|
||||
try {
|
||||
// Navigate away triggered by reloading the page
|
||||
$this->getSession()->reload();
|
||||
$this->getWebDriverSession()->accept_alert();
|
||||
$this->getExpectedAlert()->accept();
|
||||
} catch (WebDriverException $e) {
|
||||
// no-op, alert might not be present
|
||||
}
|
||||
@ -316,8 +319,8 @@ JS;
|
||||
{
|
||||
// Validate driver
|
||||
$driver = $this->getSession()->getDriver();
|
||||
if (!($driver instanceof Selenium2Driver)) {
|
||||
file_put_contents('php://stdout', 'ScreenShots are only supported for Selenium2Driver: skipping');
|
||||
if (!($driver instanceof FacebookWebDriver)) {
|
||||
file_put_contents('php://stdout', 'ScreenShots are only supported for FacebookWebDriver: skipping');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -350,8 +353,8 @@ JS;
|
||||
}
|
||||
|
||||
$path = sprintf('%s/%s_%d.png', $path, basename($feature->getFile()), $step->getLine());
|
||||
$screenshot = $driver->getWebDriverSession()->screenshot();
|
||||
file_put_contents($path, base64_decode($screenshot));
|
||||
$screenshot = $driver->getScreenshot();
|
||||
file_put_contents($path, $screenshot);
|
||||
file_put_contents('php://stderr', sprintf('Saving screenshot into %s' . PHP_EOL, $path));
|
||||
}
|
||||
|
||||
@ -521,10 +524,7 @@ JS;
|
||||
*/
|
||||
public function iSeeTheDialogText($expected)
|
||||
{
|
||||
$session = $this->getSession();
|
||||
/** @var Selenium2Driver $driver */
|
||||
$driver = $session->getDriver();
|
||||
$text = $driver->getWebDriverSession()->getAlert_text();
|
||||
$text = $this->getExpectedAlert()->getText();
|
||||
assertContains($expected, $text);
|
||||
}
|
||||
|
||||
@ -534,7 +534,24 @@ JS;
|
||||
*/
|
||||
public function iTypeIntoTheDialog($data)
|
||||
{
|
||||
$this->getWebDriverSession()->postAlert_text([ 'text' => $data ]);
|
||||
$this->getExpectedAlert()
|
||||
->sendKeys($data)
|
||||
->accept();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for alert to appear, and return handle
|
||||
*
|
||||
* @return WebDriverAlert
|
||||
*/
|
||||
protected function getExpectedAlert()
|
||||
{
|
||||
$session = $this->getWebDriverSession();
|
||||
$session->wait()->until(
|
||||
WebDriverExpectedCondition::alertIsPresent(),
|
||||
"Alert is expected"
|
||||
);
|
||||
return $session->switchTo()->alert();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -542,7 +559,12 @@ JS;
|
||||
*/
|
||||
public function iConfirmTheDialog()
|
||||
{
|
||||
$this->getWebDriverSession()->accept_alert();
|
||||
$session = $this->getWebDriverSession();
|
||||
$session->wait()->until(
|
||||
WebDriverExpectedCondition::alertIsPresent(),
|
||||
"Alert is expected"
|
||||
);
|
||||
$session->switchTo()->alert()->accept();
|
||||
$this->handleAjaxTimeout();
|
||||
}
|
||||
|
||||
@ -551,23 +573,23 @@ JS;
|
||||
*/
|
||||
public function iDismissTheDialog()
|
||||
{
|
||||
$this->getWebDriverSession()->dismiss_alert();
|
||||
$this->getExpectedAlert()->dismiss();
|
||||
$this->handleAjaxTimeout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Selenium webdriver session.
|
||||
* Note: Will fail if current driver isn't Selenium2Driver
|
||||
* Note: Will fail if current driver isn't FacebookWebDriver
|
||||
*
|
||||
* @return WebDriverSession
|
||||
* @return WebDriver
|
||||
*/
|
||||
protected function getWebDriverSession()
|
||||
{
|
||||
$driver = $this->getSession()->getDriver();
|
||||
if (! $driver instanceof Selenium2Driver) {
|
||||
throw new \InvalidArgumentException("Not supported for non-selenium2 drivers");
|
||||
if (! $driver instanceof FacebookWebDriver) {
|
||||
throw new InvalidArgumentException("Only supported for FacebookWebDriver");
|
||||
}
|
||||
return $driver->getWebDriverSession();
|
||||
return $driver->getWebDriver();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -612,7 +634,7 @@ JS;
|
||||
}
|
||||
|
||||
if (!$parent) {
|
||||
throw new \InvalidArgumentException(sprintf('Input group with label "%s" cannot be found', $labelText));
|
||||
throw new InvalidArgumentException(sprintf('Input group with label "%s" cannot be found', $labelText));
|
||||
}
|
||||
|
||||
/** @var NodeElement $option */
|
||||
@ -632,7 +654,7 @@ JS;
|
||||
}
|
||||
|
||||
if (!$input) {
|
||||
throw new \InvalidArgumentException(sprintf('Input "%s" cannot be found', $value));
|
||||
throw new InvalidArgumentException(sprintf('Input "%s" cannot be found', $value));
|
||||
}
|
||||
|
||||
$this->getSession()->getDriver()->click($input->getXPath());
|
||||
@ -649,6 +671,7 @@ JS;
|
||||
{
|
||||
fwrite(STDOUT, "\033[s \033[93m[Breakpoint] Press \033[1;93m[RETURN]\033[0;93m to continue...\033[0m");
|
||||
while (fgets(STDIN, 1024) == '') {
|
||||
// noop
|
||||
}
|
||||
fwrite(STDOUT, "\033[u");
|
||||
|
||||
@ -669,7 +692,7 @@ JS;
|
||||
{
|
||||
$timestamp = strtotime($val);
|
||||
if (!$timestamp) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
"Can't resolve '%s' into a valid datetime value",
|
||||
$val
|
||||
));
|
||||
@ -691,7 +714,7 @@ JS;
|
||||
{
|
||||
$timestamp = strtotime($val);
|
||||
if (!$timestamp) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
"Can't resolve '%s' into a valid datetime value",
|
||||
$val
|
||||
));
|
||||
@ -713,7 +736,7 @@ JS;
|
||||
{
|
||||
$timestamp = strtotime($val);
|
||||
if (!$timestamp) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
"Can't resolve '%s' into a valid datetime value",
|
||||
$val
|
||||
));
|
||||
@ -1150,7 +1173,7 @@ JS;
|
||||
|
||||
$id = $el->getAttribute('id');
|
||||
if (empty($id)) {
|
||||
throw new \InvalidArgumentException('Element requires an "id" attribute');
|
||||
throw new InvalidArgumentException('Element requires an "id" attribute');
|
||||
}
|
||||
|
||||
$js = sprintf("document.getElementById('%s').scrollIntoView(true);", $id);
|
||||
@ -1173,7 +1196,7 @@ JS;
|
||||
|
||||
$id = $el->getAttribute('id');
|
||||
if (empty($id)) {
|
||||
throw new \InvalidArgumentException('Element requires an "id" attribute');
|
||||
throw new InvalidArgumentException('Element requires an "id" attribute');
|
||||
}
|
||||
|
||||
$js = sprintf("document.getElementById('%s').scrollIntoView(true);", $id);
|
||||
|
@ -109,6 +109,17 @@ class LoginContext implements Context
|
||||
$emailField->setValue($email);
|
||||
$passwordField->setValue($password);
|
||||
$submitButton->press();
|
||||
|
||||
// Wait 100 ms
|
||||
$this->getMainContext()->getSession()->wait(100);
|
||||
|
||||
// In case of login error, throw exception
|
||||
// E.g. 'Your session has expired. Please re-submit the form.'
|
||||
// This will allow @retry
|
||||
$page = $this->getMainContext()->getSession()->getPage();
|
||||
$message = $page->find('css', '.message.error');
|
||||
$error = $message ? $message->getText() : null;
|
||||
assertNull($message, 'Could not log in with user ' . $email . '. Error: "' . $error. '""');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -4,17 +4,18 @@ namespace SilverStripe\BehatExtension\Context;
|
||||
|
||||
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
|
||||
use Behat\Mink\Element\NodeElement;
|
||||
use Behat\Mink\Exception\ElementNotFoundException;
|
||||
use Behat\Mink\Exception\UnsupportedDriverActionException;
|
||||
use Behat\Mink\Selector\Xpath\Escaper;
|
||||
use Behat\MinkExtension\Context\MinkContext;
|
||||
use Behat\Mink\Driver\Selenium2Driver;
|
||||
use Behat\Mink\Exception\UnsupportedDriverActionException;
|
||||
use Behat\Mink\Exception\ElementNotFoundException;
|
||||
use InvalidArgumentException;
|
||||
use SilverStripe\BehatExtension\Utility\TestMailer;
|
||||
use SilverStripe\CMS\Model\SiteTree;
|
||||
use SilverStripe\Core\ClassInfo;
|
||||
use SilverStripe\Core\Convert;
|
||||
use SilverStripe\Core\Environment;
|
||||
use SilverStripe\Core\Resettable;
|
||||
use SilverStripe\MinkFacebookWebDriver\FacebookWebDriver;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
use SilverStripe\TestSession\TestSessionEnvironment;
|
||||
use Symfony\Component\CssSelector\Exception\SyntaxErrorException;
|
||||
@ -383,7 +384,7 @@ abstract class SilverStripeContext extends MinkContext implements SilverStripeAw
|
||||
public function canIntercept()
|
||||
{
|
||||
$driver = $this->getSession()->getDriver();
|
||||
if ($driver instanceof Selenium2Driver) {
|
||||
if ($driver instanceof FacebookWebDriver) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -411,7 +412,18 @@ abstract class SilverStripeContext extends MinkContext implements SilverStripeAw
|
||||
/** @var NodeElement $node */
|
||||
foreach ($nodes as $node) {
|
||||
if ($node->isVisible()) {
|
||||
$node->setValue($value);
|
||||
// Work around for https://github.com/FluentLenium/FluentLenium/issues/129
|
||||
// Otherwise "Element must be user-editable in order to clear it"
|
||||
$type = $node->getAttribute('type');
|
||||
$id = $node->getAttribute('id');
|
||||
if ($type === 'date' && $id) {
|
||||
$jsValue = Convert::raw2js($value);
|
||||
$this->getSession()->getDriver()->executeScript(
|
||||
"document.getElementById(\"{$id}\").value = \"{$jsValue}\";"
|
||||
);
|
||||
} else {
|
||||
$node->setValue($value);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ namespace SilverStripe\BehatExtension;
|
||||
|
||||
use Behat\MinkExtension\ServiceContainer\MinkExtension as BaseMinkExtension;
|
||||
use SilverStripe\BehatExtension\Compiler\MinkExtensionBaseUrlPass;
|
||||
use SilverStripe\MinkFacebookWebDriver\FacebookFactory;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
/**
|
||||
@ -14,6 +15,12 @@ use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
*/
|
||||
class MinkExtension extends BaseMinkExtension
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->registerDriverFactory(new FacebookFactory());
|
||||
}
|
||||
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
parent::process($container);
|
||||
|
Loading…
Reference in New Issue
Block a user