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).
|
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
|
## Tutorials
|
||||||
|
|
||||||
* [Tutorial: Testing Form Submissions](docs/tutorial.md)
|
* [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/behat": "^3.2",
|
||||||
"behat/mink": "^1.7",
|
"behat/mink": "^1.7",
|
||||||
"behat/mink-extension": "^2.1",
|
"behat/mink-extension": "^2.1",
|
||||||
"behat/mink-selenium2-driver": "^1.3",
|
"silverstripe/mink-facebook-web-driver": "^1",
|
||||||
"symfony/dom-crawler": "^3",
|
"symfony/dom-crawler": "^3",
|
||||||
"silverstripe/testsession": "^2.0.0@alpha",
|
"silverstripe/testsession": "^2.1",
|
||||||
"silverstripe/framework": "^4@dev",
|
"silverstripe/framework": "^4",
|
||||||
"symfony/finder": "^3.2"
|
"symfony/finder": "^3.2"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
@ -47,6 +47,9 @@
|
|||||||
"3.x-dev": "3.1.x-dev"
|
"3.x-dev": "3.1.x-dev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"bin": [
|
||||||
|
"bin/behat-ss"
|
||||||
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "phpcs --standard=PSR2 -n src/ tests/php/"
|
"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\AfterStepScope;
|
||||||
use Behat\Behat\Hook\Scope\BeforeStepScope;
|
use Behat\Behat\Hook\Scope\BeforeStepScope;
|
||||||
use Behat\Behat\Hook\Scope\StepScope;
|
use Behat\Behat\Hook\Scope\StepScope;
|
||||||
use Behat\Mink\Driver\Selenium2Driver;
|
|
||||||
use Behat\Mink\Element\NodeElement;
|
use Behat\Mink\Element\NodeElement;
|
||||||
use Behat\Mink\Session;
|
use Behat\Mink\Session;
|
||||||
use Behat\Testwork\Tester\Result\TestResult;
|
use Behat\Testwork\Tester\Result\TestResult;
|
||||||
use Exception;
|
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\File;
|
||||||
use SilverStripe\Assets\Filesystem;
|
use SilverStripe\Assets\Filesystem;
|
||||||
use SilverStripe\BehatExtension\Utility\StepHelper;
|
use SilverStripe\BehatExtension\Utility\StepHelper;
|
||||||
use WebDriver\Exception as WebDriverException;
|
use SilverStripe\MinkFacebookWebDriver\FacebookWebDriver;
|
||||||
use WebDriver\Session as WebDriverSession;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BasicContext
|
* BasicContext
|
||||||
@ -248,7 +251,7 @@ JS;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Take screenshot when step fails.
|
* Take screenshot when step fails.
|
||||||
* Works only with Selenium2Driver.
|
* Works only with FacebookWebDriver.
|
||||||
*
|
*
|
||||||
* @AfterStep
|
* @AfterStep
|
||||||
* @param AfterStepScope $event
|
* @param AfterStepScope $event
|
||||||
@ -282,7 +285,7 @@ JS;
|
|||||||
try {
|
try {
|
||||||
// Navigate away triggered by reloading the page
|
// Navigate away triggered by reloading the page
|
||||||
$this->getSession()->reload();
|
$this->getSession()->reload();
|
||||||
$this->getWebDriverSession()->accept_alert();
|
$this->getExpectedAlert()->accept();
|
||||||
} catch (WebDriverException $e) {
|
} catch (WebDriverException $e) {
|
||||||
// no-op, alert might not be present
|
// no-op, alert might not be present
|
||||||
}
|
}
|
||||||
@ -316,8 +319,8 @@ JS;
|
|||||||
{
|
{
|
||||||
// Validate driver
|
// Validate driver
|
||||||
$driver = $this->getSession()->getDriver();
|
$driver = $this->getSession()->getDriver();
|
||||||
if (!($driver instanceof Selenium2Driver)) {
|
if (!($driver instanceof FacebookWebDriver)) {
|
||||||
file_put_contents('php://stdout', 'ScreenShots are only supported for Selenium2Driver: skipping');
|
file_put_contents('php://stdout', 'ScreenShots are only supported for FacebookWebDriver: skipping');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -350,8 +353,8 @@ JS;
|
|||||||
}
|
}
|
||||||
|
|
||||||
$path = sprintf('%s/%s_%d.png', $path, basename($feature->getFile()), $step->getLine());
|
$path = sprintf('%s/%s_%d.png', $path, basename($feature->getFile()), $step->getLine());
|
||||||
$screenshot = $driver->getWebDriverSession()->screenshot();
|
$screenshot = $driver->getScreenshot();
|
||||||
file_put_contents($path, base64_decode($screenshot));
|
file_put_contents($path, $screenshot);
|
||||||
file_put_contents('php://stderr', sprintf('Saving screenshot into %s' . PHP_EOL, $path));
|
file_put_contents('php://stderr', sprintf('Saving screenshot into %s' . PHP_EOL, $path));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -521,10 +524,7 @@ JS;
|
|||||||
*/
|
*/
|
||||||
public function iSeeTheDialogText($expected)
|
public function iSeeTheDialogText($expected)
|
||||||
{
|
{
|
||||||
$session = $this->getSession();
|
$text = $this->getExpectedAlert()->getText();
|
||||||
/** @var Selenium2Driver $driver */
|
|
||||||
$driver = $session->getDriver();
|
|
||||||
$text = $driver->getWebDriverSession()->getAlert_text();
|
|
||||||
assertContains($expected, $text);
|
assertContains($expected, $text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -534,7 +534,24 @@ JS;
|
|||||||
*/
|
*/
|
||||||
public function iTypeIntoTheDialog($data)
|
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()
|
public function iConfirmTheDialog()
|
||||||
{
|
{
|
||||||
$this->getWebDriverSession()->accept_alert();
|
$session = $this->getWebDriverSession();
|
||||||
|
$session->wait()->until(
|
||||||
|
WebDriverExpectedCondition::alertIsPresent(),
|
||||||
|
"Alert is expected"
|
||||||
|
);
|
||||||
|
$session->switchTo()->alert()->accept();
|
||||||
$this->handleAjaxTimeout();
|
$this->handleAjaxTimeout();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -551,23 +573,23 @@ JS;
|
|||||||
*/
|
*/
|
||||||
public function iDismissTheDialog()
|
public function iDismissTheDialog()
|
||||||
{
|
{
|
||||||
$this->getWebDriverSession()->dismiss_alert();
|
$this->getExpectedAlert()->dismiss();
|
||||||
$this->handleAjaxTimeout();
|
$this->handleAjaxTimeout();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get Selenium webdriver session.
|
* 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()
|
protected function getWebDriverSession()
|
||||||
{
|
{
|
||||||
$driver = $this->getSession()->getDriver();
|
$driver = $this->getSession()->getDriver();
|
||||||
if (! $driver instanceof Selenium2Driver) {
|
if (! $driver instanceof FacebookWebDriver) {
|
||||||
throw new \InvalidArgumentException("Not supported for non-selenium2 drivers");
|
throw new InvalidArgumentException("Only supported for FacebookWebDriver");
|
||||||
}
|
}
|
||||||
return $driver->getWebDriverSession();
|
return $driver->getWebDriver();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -612,7 +634,7 @@ JS;
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!$parent) {
|
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 */
|
/** @var NodeElement $option */
|
||||||
@ -632,7 +654,7 @@ JS;
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!$input) {
|
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());
|
$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");
|
fwrite(STDOUT, "\033[s \033[93m[Breakpoint] Press \033[1;93m[RETURN]\033[0;93m to continue...\033[0m");
|
||||||
while (fgets(STDIN, 1024) == '') {
|
while (fgets(STDIN, 1024) == '') {
|
||||||
|
// noop
|
||||||
}
|
}
|
||||||
fwrite(STDOUT, "\033[u");
|
fwrite(STDOUT, "\033[u");
|
||||||
|
|
||||||
@ -669,7 +692,7 @@ JS;
|
|||||||
{
|
{
|
||||||
$timestamp = strtotime($val);
|
$timestamp = strtotime($val);
|
||||||
if (!$timestamp) {
|
if (!$timestamp) {
|
||||||
throw new \InvalidArgumentException(sprintf(
|
throw new InvalidArgumentException(sprintf(
|
||||||
"Can't resolve '%s' into a valid datetime value",
|
"Can't resolve '%s' into a valid datetime value",
|
||||||
$val
|
$val
|
||||||
));
|
));
|
||||||
@ -691,7 +714,7 @@ JS;
|
|||||||
{
|
{
|
||||||
$timestamp = strtotime($val);
|
$timestamp = strtotime($val);
|
||||||
if (!$timestamp) {
|
if (!$timestamp) {
|
||||||
throw new \InvalidArgumentException(sprintf(
|
throw new InvalidArgumentException(sprintf(
|
||||||
"Can't resolve '%s' into a valid datetime value",
|
"Can't resolve '%s' into a valid datetime value",
|
||||||
$val
|
$val
|
||||||
));
|
));
|
||||||
@ -713,7 +736,7 @@ JS;
|
|||||||
{
|
{
|
||||||
$timestamp = strtotime($val);
|
$timestamp = strtotime($val);
|
||||||
if (!$timestamp) {
|
if (!$timestamp) {
|
||||||
throw new \InvalidArgumentException(sprintf(
|
throw new InvalidArgumentException(sprintf(
|
||||||
"Can't resolve '%s' into a valid datetime value",
|
"Can't resolve '%s' into a valid datetime value",
|
||||||
$val
|
$val
|
||||||
));
|
));
|
||||||
@ -1150,7 +1173,7 @@ JS;
|
|||||||
|
|
||||||
$id = $el->getAttribute('id');
|
$id = $el->getAttribute('id');
|
||||||
if (empty($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);
|
$js = sprintf("document.getElementById('%s').scrollIntoView(true);", $id);
|
||||||
@ -1173,7 +1196,7 @@ JS;
|
|||||||
|
|
||||||
$id = $el->getAttribute('id');
|
$id = $el->getAttribute('id');
|
||||||
if (empty($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);
|
$js = sprintf("document.getElementById('%s').scrollIntoView(true);", $id);
|
||||||
|
@ -109,6 +109,17 @@ class LoginContext implements Context
|
|||||||
$emailField->setValue($email);
|
$emailField->setValue($email);
|
||||||
$passwordField->setValue($password);
|
$passwordField->setValue($password);
|
||||||
$submitButton->press();
|
$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\Behat\Hook\Scope\BeforeScenarioScope;
|
||||||
use Behat\Mink\Element\NodeElement;
|
use Behat\Mink\Element\NodeElement;
|
||||||
|
use Behat\Mink\Exception\ElementNotFoundException;
|
||||||
|
use Behat\Mink\Exception\UnsupportedDriverActionException;
|
||||||
use Behat\Mink\Selector\Xpath\Escaper;
|
use Behat\Mink\Selector\Xpath\Escaper;
|
||||||
use Behat\MinkExtension\Context\MinkContext;
|
use Behat\MinkExtension\Context\MinkContext;
|
||||||
use Behat\Mink\Driver\Selenium2Driver;
|
|
||||||
use Behat\Mink\Exception\UnsupportedDriverActionException;
|
|
||||||
use Behat\Mink\Exception\ElementNotFoundException;
|
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use SilverStripe\BehatExtension\Utility\TestMailer;
|
use SilverStripe\BehatExtension\Utility\TestMailer;
|
||||||
use SilverStripe\CMS\Model\SiteTree;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
use SilverStripe\Core\ClassInfo;
|
use SilverStripe\Core\ClassInfo;
|
||||||
|
use SilverStripe\Core\Convert;
|
||||||
use SilverStripe\Core\Environment;
|
use SilverStripe\Core\Environment;
|
||||||
use SilverStripe\Core\Resettable;
|
use SilverStripe\Core\Resettable;
|
||||||
|
use SilverStripe\MinkFacebookWebDriver\FacebookWebDriver;
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
use SilverStripe\TestSession\TestSessionEnvironment;
|
use SilverStripe\TestSession\TestSessionEnvironment;
|
||||||
use Symfony\Component\CssSelector\Exception\SyntaxErrorException;
|
use Symfony\Component\CssSelector\Exception\SyntaxErrorException;
|
||||||
@ -383,7 +384,7 @@ abstract class SilverStripeContext extends MinkContext implements SilverStripeAw
|
|||||||
public function canIntercept()
|
public function canIntercept()
|
||||||
{
|
{
|
||||||
$driver = $this->getSession()->getDriver();
|
$driver = $this->getSession()->getDriver();
|
||||||
if ($driver instanceof Selenium2Driver) {
|
if ($driver instanceof FacebookWebDriver) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -411,7 +412,18 @@ abstract class SilverStripeContext extends MinkContext implements SilverStripeAw
|
|||||||
/** @var NodeElement $node */
|
/** @var NodeElement $node */
|
||||||
foreach ($nodes as $node) {
|
foreach ($nodes as $node) {
|
||||||
if ($node->isVisible()) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ namespace SilverStripe\BehatExtension;
|
|||||||
|
|
||||||
use Behat\MinkExtension\ServiceContainer\MinkExtension as BaseMinkExtension;
|
use Behat\MinkExtension\ServiceContainer\MinkExtension as BaseMinkExtension;
|
||||||
use SilverStripe\BehatExtension\Compiler\MinkExtensionBaseUrlPass;
|
use SilverStripe\BehatExtension\Compiler\MinkExtensionBaseUrlPass;
|
||||||
|
use SilverStripe\MinkFacebookWebDriver\FacebookFactory;
|
||||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -14,6 +15,12 @@ use Symfony\Component\DependencyInjection\ContainerBuilder;
|
|||||||
*/
|
*/
|
||||||
class MinkExtension extends BaseMinkExtension
|
class MinkExtension extends BaseMinkExtension
|
||||||
{
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
$this->registerDriverFactory(new FacebookFactory());
|
||||||
|
}
|
||||||
|
|
||||||
public function process(ContainerBuilder $container)
|
public function process(ContainerBuilder $container)
|
||||||
{
|
{
|
||||||
parent::process($container);
|
parent::process($container);
|
||||||
|
Loading…
Reference in New Issue
Block a user