Merge pull request #123 from open-sausages/pulls/4.0/psr2

Convert to PSR-2 standard
This commit is contained in:
Hamish Friedlander 2016-08-10 09:45:01 +07:00 committed by GitHub
commit ce3c0bb6c7
20 changed files with 1998 additions and 1768 deletions

View File

@ -3,11 +3,24 @@ language: php
sudo: false sudo: false
php: php:
- 5.5
- 5.6 - 5.6
script: env:
- vendor/bin/phpunit tests matrix:
- PHPUNIT_TEST=1
- PHPCS_TEST=1
matrix:
include:
- php: 5.5
env: PHPUNIT_TEST=1
before_script: before_script:
- composer install --dev --prefer-dist - composer install --dev --prefer-dist
- pyrus install pear/PHP_CodeSniffer
- phpenv rehash
script:
- "if [ \"$PHPUNIT_TEST\" = \"1\" ]; then vendor/bin/phpunit tests; fi"
- "if [ \"$PHPCS_TEST\" = \"1\" ]; then phpcs --standard=PSR2 -n src/ tests/; fi"

View File

@ -9,8 +9,7 @@
* with this source code in the file LICENSE. * with this source code in the file LICENSE.
*/ */
spl_autoload_register(function($class) spl_autoload_register(function ($class) {
{
if (false !== strpos($class, 'SilverStripe\\BehatExtension')) { if (false !== strpos($class, 'SilverStripe\\BehatExtension')) {
require_once(__DIR__ . '/src/' . str_replace('\\', '/', $class) . '.php'); require_once(__DIR__ . '/src/' . str_replace('\\', '/', $class) . '.php');
return true; return true;

View File

@ -2,8 +2,8 @@
namespace SilverStripe\BehatExtension\Compiler; namespace SilverStripe\BehatExtension\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder, use Symfony\Component\DependencyInjection\ContainerBuilder;
Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
/** /**
* Loads SilverStripe core. Required to initialize autoloading. * Loads SilverStripe core. Required to initialize autoloading.
@ -22,7 +22,7 @@ class CoreInitializationPass implements CompilerPassInterface
$_GET['flush'] = 1; $_GET['flush'] = 1;
require_once $frameworkPath . '/core/Core.php'; require_once $frameworkPath . '/core/Core.php';
if(class_exists('TestRunner')) { if (class_exists('TestRunner')) {
// 3.x compat // 3.x compat
\TestRunner::use_test_manifest(); \TestRunner::use_test_manifest();
} else { } else {

View File

@ -2,8 +2,8 @@
namespace SilverStripe\BehatExtension\Compiler; namespace SilverStripe\BehatExtension\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder, use Symfony\Component\DependencyInjection\ContainerBuilder;
Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
/** /**
* Behat\SilverStripe container compilation pass. * Behat\SilverStripe container compilation pass.
@ -24,22 +24,23 @@ class MinkExtensionBaseUrlPass implements CompilerPassInterface
$frameworkPath = $container->getParameter('behat.silverstripe_extension.framework_path'); $frameworkPath = $container->getParameter('behat.silverstripe_extension.framework_path');
global $_FILE_TO_URL_MAPPING; global $_FILE_TO_URL_MAPPING;
if($container->getParameter('behat.mink.base_url')) { if ($container->getParameter('behat.mink.base_url')) {
// If base_url is already defined, also set it in the SilverStripe mapping // If base_url is already defined, also set it in the SilverStripe mapping
$_FILE_TO_URL_MAPPING[dirname($frameworkPath)] = $container->getParameter('behat.mink.base_url'); $_FILE_TO_URL_MAPPING[dirname($frameworkPath)] = $container->getParameter('behat.mink.base_url');
} else if($envPath = $this->findEnvironmentConfigFile($frameworkPath)) { } elseif ($envPath = $this->findEnvironmentConfigFile($frameworkPath)) {
// Otherwise try to retrieve it from _ss_environment // Otherwise try to retrieve it from _ss_environment
include_once $envPath; include_once $envPath;
if( if (isset($_FILE_TO_URL_MAPPING)
isset($_FILE_TO_URL_MAPPING)
&& !($container->hasParameter('behat.mink.base_url') && $container->getParameter('behat.mink.base_url')) && !($container->hasParameter('behat.mink.base_url') && $container->getParameter('behat.mink.base_url'))
) { ) {
$baseUrl = $this->findBaseUrlFromMapping(dirname($frameworkPath), $_FILE_TO_URL_MAPPING); $baseUrl = $this->findBaseUrlFromMapping(dirname($frameworkPath), $_FILE_TO_URL_MAPPING);
if($baseUrl) $container->setParameter('behat.mink.base_url', $baseUrl); if ($baseUrl) {
$container->setParameter('behat.mink.base_url', $baseUrl);
}
} }
} }
if(!$container->getParameter('behat.mink.base_url')) { if (!$container->getParameter('behat.mink.base_url')) {
throw new \InvalidArgumentException( throw new \InvalidArgumentException(
'"base_url" not configured. Please specify it in your behat.yml configuration, ' . '"base_url" not configured. Please specify it in your behat.yml configuration, ' .
'or in your _ss_environment.php configuration through $_FILE_TO_URL_MAPPING' 'or in your _ss_environment.php configuration through $_FILE_TO_URL_MAPPING'
@ -60,7 +61,8 @@ class MinkExtensionBaseUrlPass implements CompilerPassInterface
* @param String Absolute start path to search upwards from * @param String Absolute start path to search upwards from
* @return Boolean Absolute path to environment file * @return Boolean Absolute path to environment file
*/ */
protected function findEnvironmentConfigFile($path) { protected function findEnvironmentConfigFile($path)
{
$envPath = null; $envPath = null;
$envFile = '_ss_environment.php'; //define the name of the environment file $envFile = '_ss_environment.php'; //define the name of the environment file
$path = '.'; //define the dir to start scanning from (have to add the trailing slash) $path = '.'; //define the dir to start scanning from (have to add the trailing slash)
@ -87,12 +89,13 @@ class MinkExtensionBaseUrlPass implements CompilerPassInterface
* @param Array Map of paths to host names * @param Array Map of paths to host names
* @return String URL * @return String URL
*/ */
protected function findBaseUrlFromMapping($path, $mapping) { protected function findBaseUrlFromMapping($path, $mapping)
{
$fullPath = $path; $fullPath = $path;
$url = null; $url = null;
while($path && $path != "/" && !preg_match('/^[A-Z]:\\\\$/', $path)) { while ($path && $path != "/" && !preg_match('/^[A-Z]:\\\\$/', $path)) {
if(isset($mapping[$path])) { if (isset($mapping[$path])) {
$url = $mapping[$path] . str_replace(DIRECTORY_SEPARATOR, '/', substr($fullPath,strlen($path))); $url = $mapping[$path] . str_replace(DIRECTORY_SEPARATOR, '/', substr($fullPath, strlen($path)));
break; break;
} else { } else {
$path = dirname($path); // traverse up $path = dirname($path); // traverse up

View File

@ -2,12 +2,12 @@
namespace SilverStripe\BehatExtension\Console\Processor; namespace SilverStripe\BehatExtension\Console\Processor;
use Symfony\Component\DependencyInjection\ContainerInterface, use Symfony\Component\DependencyInjection\ContainerInterface;
Symfony\Component\Console\Command\Command, use Symfony\Component\Console\Command\Command;
Symfony\Component\Console\Input\InputArgument, use Symfony\Component\Console\Input\InputArgument;
Symfony\Component\Console\Input\InputInterface, use Symfony\Component\Console\Input\InputInterface;
Symfony\Component\Console\Output\OutputInterface, use Symfony\Component\Console\Output\OutputInterface;
Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputOption;
use Behat\Behat\Console\Processor\InitProcessor as BaseProcessor; use Behat\Behat\Console\Processor\InitProcessor as BaseProcessor;
@ -33,7 +33,10 @@ class InitProcessor extends BaseProcessor
{ {
parent::configure($command); parent::configure($command);
$command->addOption('--namespace', null, InputOption::VALUE_OPTIONAL, $command->addOption(
'--namespace',
null,
InputOption::VALUE_OPTIONAL,
"Optional namespace for FeatureContext, defaults to <foldername>\\Test\\Behaviour.\n" "Optional namespace for FeatureContext, defaults to <foldername>\\Test\\Behaviour.\n"
); );
} }
@ -68,7 +71,7 @@ class InitProcessor extends BaseProcessor
unset($_GET['flush']); unset($_GET['flush']);
$featuresPath = $input->getArgument('features'); $featuresPath = $input->getArgument('features');
if(!$featuresPath) { if (!$featuresPath) {
throw new \InvalidArgumentException('Please specify a module name (e.g. "@mymodule")'); throw new \InvalidArgumentException('Please specify a module name (e.g. "@mymodule")');
} }
@ -93,14 +96,14 @@ class InitProcessor extends BaseProcessor
} }
// TODO Retrieve from module definition once that's implemented // TODO Retrieve from module definition once that's implemented
if($input->getOption('namespace')) { if ($input->getOption('namespace')) {
$namespace = $input->getOption('namespace'); $namespace = $input->getOption('namespace');
} else { } else {
$namespace = ucfirst($currentModuleName); $namespace = ucfirst($currentModuleName);
} }
$namespace .= '\\' . $this->container->getParameter('behat.silverstripe_extension.context.namespace_suffix'); $namespace .= '\\' . $this->container->getParameter('behat.silverstripe_extension.context.namespace_suffix');
$featuresPath = rtrim($currentModulePath.DIRECTORY_SEPARATOR.$pathSuffix,DIRECTORY_SEPARATOR); $featuresPath = rtrim($currentModulePath.DIRECTORY_SEPARATOR.$pathSuffix, DIRECTORY_SEPARATOR);
$basePath = $this->container->getParameter('behat.paths.base').DIRECTORY_SEPARATOR; $basePath = $this->container->getParameter('behat.paths.base').DIRECTORY_SEPARATOR;
$bootstrapPath = $featuresPath.DIRECTORY_SEPARATOR.'bootstrap'; $bootstrapPath = $featuresPath.DIRECTORY_SEPARATOR.'bootstrap';
$contextPath = $bootstrapPath.DIRECTORY_SEPARATOR.'Context'; $contextPath = $bootstrapPath.DIRECTORY_SEPARATOR.'Context';
@ -140,7 +143,7 @@ class InitProcessor extends BaseProcessor
*/ */
protected function getFeatureContextSkelet() protected function getFeatureContextSkelet()
{ {
return <<<'PHP' return <<<'PHP'
<?php <?php
namespace %NAMESPACE%; namespace %NAMESPACE%;

View File

@ -2,11 +2,11 @@
namespace SilverStripe\BehatExtension\Console\Processor; namespace SilverStripe\BehatExtension\Console\Processor;
use Symfony\Component\DependencyInjection\ContainerInterface, use Symfony\Component\DependencyInjection\ContainerInterface;
Symfony\Component\Console\Command\Command, use Symfony\Component\Console\Command\Command;
Symfony\Component\Console\Input\InputArgument, use Symfony\Component\Console\Input\InputArgument;
Symfony\Component\Console\Input\InputInterface, use Symfony\Component\Console\Input\InputInterface;
Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use Behat\Behat\Console\Processor\LocatorProcessor as BaseProcessor; use Behat\Behat\Console\Processor\LocatorProcessor as BaseProcessor;
@ -34,7 +34,9 @@ class LocatorProcessor extends BaseProcessor
*/ */
public function configure(Command $command) public function configure(Command $command)
{ {
$command->addArgument('features', InputArgument::OPTIONAL, $command->addArgument(
'features',
InputArgument::OPTIONAL,
"Feature(s) to run. Could be:". "Feature(s) to run. Could be:".
"\n- a dir (<comment>src/to/module/Features/</comment>), " . "\n- a dir (<comment>src/to/module/Features/</comment>), " .
"\n- a feature (<comment>src/to/module/Features/*.feature</comment>), " . "\n- a feature (<comment>src/to/module/Features/*.feature</comment>), " .
@ -91,7 +93,7 @@ class LocatorProcessor extends BaseProcessor
$featuresPath = $currentModulePath.DIRECTORY_SEPARATOR.$pathSuffix.DIRECTORY_SEPARATOR.$featuresPath; $featuresPath = $currentModulePath.DIRECTORY_SEPARATOR.$pathSuffix.DIRECTORY_SEPARATOR.$featuresPath;
} }
if($input->getOption('namespace')) { if ($input->getOption('namespace')) {
$namespace = $input->getOption('namespace'); $namespace = $input->getOption('namespace');
} else { } else {
$namespace = ucfirst($currentModuleName); $namespace = ucfirst($currentModuleName);

View File

@ -2,15 +2,15 @@
namespace SilverStripe\BehatExtension\Context; namespace SilverStripe\BehatExtension\Context;
use Behat\Behat\Context\BehatContext, use Behat\Behat\Context\BehatContext;
Behat\Behat\Context\Step, use Behat\Behat\Context\Step;
Behat\Behat\Event\StepEvent, use Behat\Behat\Event\StepEvent;
Behat\Behat\Event\ScenarioEvent; use Behat\Behat\Event\ScenarioEvent;
use Behat\Mink\Driver\Selenium2Driver; use Behat\Mink\Driver\Selenium2Driver;
use Behat\Gherkin\Node\PyStringNode, use Behat\Gherkin\Node\PyStringNode;
Behat\Gherkin\Node\TableNode; use Behat\Gherkin\Node\TableNode;
// PHPUnit // PHPUnit
require_once BASE_PATH . '/vendor/phpunit/phpunit/src/Framework/Assert/Functions.php'; require_once BASE_PATH . '/vendor/phpunit/phpunit/src/Framework/Assert/Functions.php';
@ -51,7 +51,8 @@ class BasicContext extends BehatContext
* *
* @param array $parameters context parameters (set them up through behat.yml) * @param array $parameters context parameters (set them up through behat.yml)
*/ */
public function __construct(array $parameters) { public function __construct(array $parameters)
{
// Initialize your context here // Initialize your context here
$this->context = $parameters; $this->context = $parameters;
} }
@ -61,7 +62,8 @@ class BasicContext extends BehatContext
* *
* @return \Behat\Mink\Session * @return \Behat\Mink\Session
*/ */
public function getSession($name = null) { public function getSession($name = null)
{
return $this->getMainContext()->getSession($name); return $this->getMainContext()->getSession($name);
} }
@ -71,8 +73,9 @@ class BasicContext extends BehatContext
* Excluding scenarios with @modal tag is required, * Excluding scenarios with @modal tag is required,
* because modal dialogs stop any JS interaction * because modal dialogs stop any JS interaction
*/ */
public function appendErrorHandlerBeforeStep(StepEvent $event) { public function appendErrorHandlerBeforeStep(StepEvent $event)
try{ {
try {
$javascript = <<<JS $javascript = <<<JS
window.onerror = function(message, file, line, column, error) { window.onerror = function(message, file, line, column, error) {
var body = document.getElementsByTagName('body')[0]; var body = document.getElementsByTagName('body')[0];
@ -91,7 +94,7 @@ if ('undefined' !== typeof window.jQuery) {
JS; JS;
$this->getSession()->executeScript($javascript); $this->getSession()->executeScript($javascript);
}catch(\WebDriver\Exception $e){ } catch (\WebDriver\Exception $e) {
$this->logException($e); $this->logException($e);
} }
} }
@ -102,8 +105,9 @@ JS;
* Excluding scenarios with @modal tag is required, * Excluding scenarios with @modal tag is required,
* because modal dialogs stop any JS interaction * because modal dialogs stop any JS interaction
*/ */
public function readErrorHandlerAfterStep(StepEvent $event) { public function readErrorHandlerAfterStep(StepEvent $event)
try{ {
try {
$page = $this->getSession()->getPage(); $page = $this->getSession()->getPage();
$jserrors = $page->find('xpath', '//body[@data-jserrors]'); $jserrors = $page->find('xpath', '//body[@data-jserrors]');
@ -121,7 +125,7 @@ if ('undefined' !== typeof window.jQuery) {
JS; JS;
$this->getSession()->executeScript($javascript); $this->getSession()->executeScript($javascript);
}catch(\WebDriver\Exception $e){ } catch (\WebDriver\Exception $e) {
$this->logException($e); $this->logException($e);
} }
} }
@ -133,8 +137,9 @@ JS;
* *
* @BeforeStep * @BeforeStep
*/ */
public function handleAjaxBeforeStep(StepEvent $event) { public function handleAjaxBeforeStep(StepEvent $event)
try{ {
try {
$ajaxEnabledSteps = $this->getMainContext()->getAjaxSteps(); $ajaxEnabledSteps = $this->getMainContext()->getAjaxSteps();
$ajaxEnabledSteps = implode('|', array_filter($ajaxEnabledSteps)); $ajaxEnabledSteps = implode('|', array_filter($ajaxEnabledSteps));
@ -167,7 +172,7 @@ if ('undefined' !== typeof window.jQuery && 'undefined' !== typeof window.jQuery
JS; JS;
$this->getSession()->wait(500); // give browser a chance to process and render response $this->getSession()->wait(500); // give browser a chance to process and render response
$this->getSession()->executeScript($javascript); $this->getSession()->executeScript($javascript);
}catch(\WebDriver\Exception $e){ } catch (\WebDriver\Exception $e) {
$this->logException($e); $this->logException($e);
} }
} }
@ -180,8 +185,9 @@ JS;
* *
* @AfterStep ~@modal * @AfterStep ~@modal
*/ */
public function handleAjaxAfterStep(StepEvent $event) { public function handleAjaxAfterStep(StepEvent $event)
try{ {
try {
$ajaxEnabledSteps = $this->getMainContext()->getAjaxSteps(); $ajaxEnabledSteps = $this->getMainContext()->getAjaxSteps();
$ajaxEnabledSteps = implode('|', array_filter($ajaxEnabledSteps)); $ajaxEnabledSteps = implode('|', array_filter($ajaxEnabledSteps));
@ -199,16 +205,18 @@ window.jQuery(document).off('ajaxSuccess.ss.test.behaviour');
} }
JS; JS;
$this->getSession()->executeScript($javascript); $this->getSession()->executeScript($javascript);
}catch(\WebDriver\Exception $e){ } catch (\WebDriver\Exception $e) {
$this->logException($e); $this->logException($e);
} }
} }
public function handleAjaxTimeout() { public function handleAjaxTimeout()
{
$timeoutMs = $this->getMainContext()->getAjaxTimeout(); $timeoutMs = $this->getMainContext()->getAjaxTimeout();
// Wait for an ajax request to complete, but only for a maximum of 5 seconds to avoid deadlocks // Wait for an ajax request to complete, but only for a maximum of 5 seconds to avoid deadlocks
$this->getSession()->wait($timeoutMs, $this->getSession()->wait(
$timeoutMs,
"(typeof window.__ajaxStatus !== 'undefined' ? window.__ajaxStatus() : 'no ajax') !== 'waiting'" "(typeof window.__ajaxStatus !== 'undefined' ? window.__ajaxStatus() : 'no ajax') !== 'waiting'"
); );
@ -222,11 +230,12 @@ JS;
* *
* @AfterStep * @AfterStep
*/ */
public function takeScreenshotAfterFailedStep(StepEvent $event) { public function takeScreenshotAfterFailedStep(StepEvent $event)
{
if (4 === $event->getResult()) { if (4 === $event->getResult()) {
try{ try {
$this->takeScreenshot($event); $this->takeScreenshot($event);
}catch(\WebDriver\Exception $e){ } catch (\WebDriver\Exception $e) {
$this->logException($e); $this->logException($e);
} }
} }
@ -237,22 +246,23 @@ JS;
* *
* @AfterScenario * @AfterScenario
*/ */
public function closeModalDialog(ScenarioEvent $event) { public function closeModalDialog(ScenarioEvent $event)
try{ {
try {
// Only for failed tests on CMS page // Only for failed tests on CMS page
if (4 === $event->getResult()) { if (4 === $event->getResult()) {
$cmsElement = $this->getSession()->getPage()->find('css', '.cms'); $cmsElement = $this->getSession()->getPage()->find('css', '.cms');
if($cmsElement) { if ($cmsElement) {
try { try {
// Navigate away triggered by reloading the page // Navigate away triggered by reloading the page
$this->getSession()->reload(); $this->getSession()->reload();
$this->getSession()->getDriver()->getWebDriverSession()->accept_alert(); $this->getSession()->getDriver()->getWebDriverSession()->accept_alert();
} catch(\WebDriver\Exception $e) { } catch (\WebDriver\Exception $e) {
// no-op, alert might not be present // no-op, alert might not be present
} }
} }
} }
}catch(\WebDriver\Exception $e){ } catch (\WebDriver\Exception $e) {
$this->logException($e); $this->logException($e);
} }
} }
@ -262,14 +272,16 @@ JS;
* *
* @AfterScenario @assets * @AfterScenario @assets
*/ */
public function cleanAssetsAfterScenario(ScenarioEvent $event) { public function cleanAssetsAfterScenario(ScenarioEvent $event)
foreach(\File::get() as $file) { {
foreach (\File::get() as $file) {
$file->delete(); $file->delete();
} }
\Filesystem::removeFolder(ASSETS_PATH, true); \Filesystem::removeFolder(ASSETS_PATH, true);
} }
public function takeScreenshot(StepEvent $event) { public function takeScreenshot(StepEvent $event)
{
$driver = $this->getSession()->getDriver(); $driver = $this->getSession()->getDriver();
// quit silently when unsupported // quit silently when unsupported
if (!($driver instanceof Selenium2Driver)) { if (!($driver instanceof Selenium2Driver)) {
@ -282,7 +294,9 @@ JS;
$screenshotPath = null; $screenshotPath = null;
$path = $this->getMainContext()->getScreenshotPath(); $path = $this->getMainContext()->getScreenshotPath();
if(!$path) return; // quit silently when path is not set if (!$path) {
return;
} // quit silently when path is not set
\Filesystem::makeFolder($path); \Filesystem::makeFolder($path);
$path = realpath($path); $path = realpath($path);
@ -310,7 +324,8 @@ JS;
/** /**
* @Then /^I should be redirected to "([^"]+)"/ * @Then /^I should be redirected to "([^"]+)"/
*/ */
public function stepIShouldBeRedirectedTo($url) { public function stepIShouldBeRedirectedTo($url)
{
if ($this->getMainContext()->canIntercept()) { if ($this->getMainContext()->canIntercept()) {
$client = $this->getSession()->getDriver()->getClient(); $client = $this->getSession()->getDriver()->getClient();
$client->followRedirects(true); $client->followRedirects(true);
@ -325,7 +340,8 @@ JS;
/** /**
* @Given /^the page can't be found/ * @Given /^the page can't be found/
*/ */
public function stepPageCantBeFound() { public function stepPageCantBeFound()
{
$page = $this->getSession()->getPage(); $page = $this->getSession()->getPage();
assertTrue( assertTrue(
// Content from ErrorPage default record // Content from ErrorPage default record
@ -338,19 +354,23 @@ JS;
/** /**
* @Given /^I wait (?:for )?([\d\.]+) second(?:s?)$/ * @Given /^I wait (?:for )?([\d\.]+) second(?:s?)$/
*/ */
public function stepIWaitFor($secs) { public function stepIWaitFor($secs)
{
$this->getSession()->wait((float)$secs*1000); $this->getSession()->wait((float)$secs*1000);
} }
/** /**
* @Given /^I press the "([^"]*)" button$/ * @Given /^I press the "([^"]*)" button$/
*/ */
public function stepIPressTheButton($button) { public function stepIPressTheButton($button)
{
$page = $this->getSession()->getPage(); $page = $this->getSession()->getPage();
$els = $page->findAll('named', array('link_or_button', "'$button'")); $els = $page->findAll('named', array('link_or_button', "'$button'"));
$matchedEl = null; $matchedEl = null;
foreach($els as $el) { foreach ($els as $el) {
if($el->isVisible()) $matchedEl = $el; if ($el->isVisible()) {
$matchedEl = $el;
}
} }
assertNotNull($matchedEl, sprintf('%s button not found', $button)); assertNotNull($matchedEl, sprintf('%s button not found', $button));
$matchedEl->click(); $matchedEl->click();
@ -363,7 +383,8 @@ JS;
* *
* @Given /^I (?:press|follow) the "([^"]*)" (?:button|link), confirming the dialog$/ * @Given /^I (?:press|follow) the "([^"]*)" (?:button|link), confirming the dialog$/
*/ */
public function stepIPressTheButtonConfirmingTheDialog($button) { public function stepIPressTheButtonConfirmingTheDialog($button)
{
$this->stepIPressTheButton($button); $this->stepIPressTheButton($button);
$this->iConfirmTheDialog(); $this->iConfirmTheDialog();
} }
@ -374,7 +395,8 @@ JS;
* *
* @Given /^I (?:press|follow) the "([^"]*)" (?:button|link), dismissing the dialog$/ * @Given /^I (?:press|follow) the "([^"]*)" (?:button|link), dismissing the dialog$/
*/ */
public function stepIPressTheButtonDismissingTheDialog($button) { public function stepIPressTheButtonDismissingTheDialog($button)
{
$this->stepIPressTheButton($button); $this->stepIPressTheButton($button);
$this->iDismissTheDialog(); $this->iDismissTheDialog();
} }
@ -382,7 +404,8 @@ JS;
/** /**
* @Given /^I (click|double click) "([^"]*)" in the "([^"]*)" element$/ * @Given /^I (click|double click) "([^"]*)" in the "([^"]*)" element$/
*/ */
public function iClickInTheElement($clickType, $text, $selector) { public function iClickInTheElement($clickType, $text, $selector)
{
$clickTypeMap = array( $clickTypeMap = array(
"double click" => "doubleclick", "double click" => "doubleclick",
"click" => "click" "click" => "click"
@ -402,7 +425,8 @@ JS;
* *
* @Given /^I (click|double click) "([^"]*)" in the "([^"]*)" element, confirming the dialog$/ * @Given /^I (click|double click) "([^"]*)" in the "([^"]*)" element, confirming the dialog$/
*/ */
public function iClickInTheElementConfirmingTheDialog($clickType, $text, $selector) { public function iClickInTheElementConfirmingTheDialog($clickType, $text, $selector)
{
$this->iClickInTheElement($clickType, $text, $selector); $this->iClickInTheElement($clickType, $text, $selector);
$this->iConfirmTheDialog(); $this->iConfirmTheDialog();
} }
@ -412,7 +436,8 @@ JS;
* *
* @Given /^I (click|double click) "([^"]*)" in the "([^"]*)" element, dismissing the dialog$/ * @Given /^I (click|double click) "([^"]*)" in the "([^"]*)" element, dismissing the dialog$/
*/ */
public function iClickInTheElementDismissingTheDialog($clickType, $text, $selector) { public function iClickInTheElementDismissingTheDialog($clickType, $text, $selector)
{
$this->iClickInTheElement($clickType, $text, $selector); $this->iClickInTheElement($clickType, $text, $selector);
$this->iDismissTheDialog(); $this->iDismissTheDialog();
} }
@ -420,7 +445,8 @@ JS;
/** /**
* @Given /^I type "([^"]*)" into the dialog$/ * @Given /^I type "([^"]*)" into the dialog$/
*/ */
public function iTypeIntoTheDialog($data) { public function iTypeIntoTheDialog($data)
{
$data = array( $data = array(
'text' => $data, 'text' => $data,
); );
@ -430,7 +456,8 @@ JS;
/** /**
* @Given /^I confirm the dialog$/ * @Given /^I confirm the dialog$/
*/ */
public function iConfirmTheDialog() { public function iConfirmTheDialog()
{
$this->getSession()->getDriver()->getWebDriverSession()->accept_alert(); $this->getSession()->getDriver()->getWebDriverSession()->accept_alert();
$this->handleAjaxTimeout(); $this->handleAjaxTimeout();
} }
@ -438,7 +465,8 @@ JS;
/** /**
* @Given /^I dismiss the dialog$/ * @Given /^I dismiss the dialog$/
*/ */
public function iDismissTheDialog() { public function iDismissTheDialog()
{
$this->getSession()->getDriver()->getWebDriverSession()->dismiss_alert(); $this->getSession()->getDriver()->getWebDriverSession()->dismiss_alert();
$this->handleAjaxTimeout(); $this->handleAjaxTimeout();
} }
@ -446,7 +474,8 @@ JS;
/** /**
* @Given /^(?:|I )attach the file "(?P<path>[^"]*)" to "(?P<field>(?:[^"]|\\")*)" with HTML5$/ * @Given /^(?:|I )attach the file "(?P<path>[^"]*)" to "(?P<field>(?:[^"]|\\")*)" with HTML5$/
*/ */
public function iAttachTheFileTo($field, $path) { public function iAttachTheFileTo($field, $path)
{
// Remove wrapped button styling to make input field accessible to Selenium // Remove wrapped button styling to make input field accessible to Selenium
$js = <<<JS $js = <<<JS
var input = jQuery('[name="$field"]'); var input = jQuery('[name="$field"]');
@ -466,30 +495,39 @@ JS;
* *
* @Given /^I select "([^"]*)" from "([^"]*)" input group$/ * @Given /^I select "([^"]*)" from "([^"]*)" input group$/
*/ */
public function iSelectFromInputGroup($value, $labelText) { public function iSelectFromInputGroup($value, $labelText)
{
$page = $this->getSession()->getPage(); $page = $this->getSession()->getPage();
$parent = null; $parent = null;
foreach($page->findAll('css', 'label') as $label) { foreach ($page->findAll('css', 'label') as $label) {
if($label->getText() == $labelText) { if ($label->getText() == $labelText) {
$parent = $label->getParent(); $parent = $label->getParent();
} }
} }
if(!$parent) throw new \InvalidArgumentException(sprintf('Input group with label "%s" cannot be found', $labelText)); if (!$parent) {
throw new \InvalidArgumentException(sprintf('Input group with label "%s" cannot be found', $labelText));
}
foreach($parent->findAll('css', 'label') as $option) { foreach ($parent->findAll('css', 'label') as $option) {
if($option->getText() == $value) { if ($option->getText() == $value) {
$input = null; $input = null;
// First, look for inputs referenced by the "for" element on this label // First, look for inputs referenced by the "for" element on this label
$for = $option->getAttribute('for'); $for = $option->getAttribute('for');
if ($for) $input = $parent->findById($for); if ($for) {
$input = $parent->findById($for);
}
// Otherwise look for inputs _inside_ the label // Otherwise look for inputs _inside_ the label
if (!$input) $input = $option->find('css', 'input'); if (!$input) {
$input = $option->find('css', 'input');
}
if(!$input) throw new \InvalidArgumentException(sprintf('Input "%s" cannot be found', $value)); if (!$input) {
throw new \InvalidArgumentException(sprintf('Input "%s" cannot be found', $value));
}
$this->getSession()->getDriver()->click($input->getXPath()); $this->getSession()->getDriver()->click($input->getXPath());
} }
@ -501,9 +539,11 @@ JS;
* *
* @Then /^(?:|I )put a breakpoint$/ * @Then /^(?:|I )put a breakpoint$/
*/ */
public function iPutABreakpoint() { public function iPutABreakpoint()
{
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) == '') {
}
fwrite(STDOUT, "\033[u"); fwrite(STDOUT, "\033[u");
return; return;
@ -516,9 +556,10 @@ JS;
* *
* @Transform /^(?:(the|a)) time of (?<val>.*)$/ * @Transform /^(?:(the|a)) time of (?<val>.*)$/
*/ */
public function castRelativeToAbsoluteTime($prefix, $val) { public function castRelativeToAbsoluteTime($prefix, $val)
{
$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
@ -534,9 +575,10 @@ JS;
* *
* @Transform /^(?:(the|a)) datetime of (?<val>.*)$/ * @Transform /^(?:(the|a)) datetime of (?<val>.*)$/
*/ */
public function castRelativeToAbsoluteDatetime($prefix, $val) { public function castRelativeToAbsoluteDatetime($prefix, $val)
{
$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
@ -552,9 +594,10 @@ JS;
* *
* @Transform /^(?:(the|a)) date of (?<val>.*)$/ * @Transform /^(?:(the|a)) date of (?<val>.*)$/
*/ */
public function castRelativeToAbsoluteDate($prefix, $val) { public function castRelativeToAbsoluteDate($prefix, $val)
{
$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
@ -563,27 +606,33 @@ JS;
return date($this->dateFormat, $timestamp); return date($this->dateFormat, $timestamp);
} }
public function getDateFormat() { public function getDateFormat()
{
return $this->dateFormat; return $this->dateFormat;
} }
public function setDateFormat($format) { public function setDateFormat($format)
{
$this->dateFormat = $format; $this->dateFormat = $format;
} }
public function getTimeFormat() { public function getTimeFormat()
{
return $this->timeFormat; return $this->timeFormat;
} }
public function setTimeFormat($format) { public function setTimeFormat($format)
{
$this->timeFormat = $format; $this->timeFormat = $format;
} }
public function getDatetimeFormat() { public function getDatetimeFormat()
{
return $this->datetimeFormat; return $this->datetimeFormat;
} }
public function setDatetimeFormat($format) { public function setDatetimeFormat($format)
{
$this->datetimeFormat = $format; $this->datetimeFormat = $format;
} }
@ -595,9 +644,10 @@ JS;
* @Then /^the "(?P<name>(?:[^"]|\\")*)" (?P<type>(?:(field|button))) should (?P<negate>(?:(not |)))be disabled/ * @Then /^the "(?P<name>(?:[^"]|\\")*)" (?P<type>(?:(field|button))) should (?P<negate>(?:(not |)))be disabled/
* @Then /^the (?P<type>(?:(field|button))) "(?P<name>(?:[^"]|\\")*)" should (?P<negate>(?:(not |)))be disabled/ * @Then /^the (?P<type>(?:(field|button))) "(?P<name>(?:[^"]|\\")*)" should (?P<negate>(?:(not |)))be disabled/
*/ */
public function stepFieldShouldBeDisabled($name, $type, $negate) { public function stepFieldShouldBeDisabled($name, $type, $negate)
{
$page = $this->getSession()->getPage(); $page = $this->getSession()->getPage();
if($type == 'field') { if ($type == 'field') {
$element = $page->findField($name); $element = $page->findField($name);
} else { } else {
$element = $page->find('named', array( $element = $page->find('named', array(
@ -608,7 +658,7 @@ JS;
assertNotNull($element, sprintf("Element '%s' not found", $name)); assertNotNull($element, sprintf("Element '%s' not found", $name));
$disabledAttribute = $element->getAttribute('disabled'); $disabledAttribute = $element->getAttribute('disabled');
if(trim($negate)) { if (trim($negate)) {
assertNull($disabledAttribute, sprintf("Failed asserting element '%s' is not disabled", $name)); assertNull($disabledAttribute, sprintf("Failed asserting element '%s' is not disabled", $name));
} else { } else {
assertNotNull($disabledAttribute, sprintf("Failed asserting element '%s' is disabled", $name)); assertNotNull($disabledAttribute, sprintf("Failed asserting element '%s' is disabled", $name));
@ -623,7 +673,8 @@ JS;
* @Then /^the "(?P<field>(?:[^"]|\\")*)" field should be enabled/ * @Then /^the "(?P<field>(?:[^"]|\\")*)" field should be enabled/
* @Then /^the field "(?P<field>(?:[^"]|\\")*)" should be enabled/ * @Then /^the field "(?P<field>(?:[^"]|\\")*)" should be enabled/
*/ */
public function stepFieldShouldBeEnabled($field) { public function stepFieldShouldBeEnabled($field)
{
$page = $this->getSession()->getPage(); $page = $this->getSession()->getPage();
$fieldElement = $page->findField($field); $fieldElement = $page->findField($field);
assertNotNull($fieldElement, sprintf("Field '%s' not found", $field)); assertNotNull($fieldElement, sprintf("Field '%s' not found", $field));
@ -642,7 +693,8 @@ JS;
* *
* @Given /^I (?:follow|click) "(?P<link>[^"]*)" in the "(?P<region>[^"]*)" region$/ * @Given /^I (?:follow|click) "(?P<link>[^"]*)" in the "(?P<region>[^"]*)" region$/
*/ */
public function iFollowInTheRegion($link, $region) { public function iFollowInTheRegion($link, $region)
{
$context = $this->getMainContext(); $context = $this->getMainContext();
$regionObj = $context->getRegionObj($region); $regionObj = $context->getRegionObj($region);
assertNotNull($regionObj); assertNotNull($regionObj);
@ -663,7 +715,8 @@ JS;
* *
* @Given /^I fill in "(?P<field>[^"]*)" with "(?P<value>[^"]*)" in the "(?P<region>[^"]*)" region$/ * @Given /^I fill in "(?P<field>[^"]*)" with "(?P<value>[^"]*)" in the "(?P<region>[^"]*)" region$/
*/ */
public function iFillinTheRegion($field, $value, $region){ public function iFillinTheRegion($field, $value, $region)
{
$context = $this->getMainContext(); $context = $this->getMainContext();
$regionObj = $context->getRegionObj($region); $regionObj = $context->getRegionObj($region);
assertNotNull($regionObj, "Region Object is null"); assertNotNull($regionObj, "Region Object is null");
@ -688,7 +741,8 @@ JS;
* *
* @Given /^I should (?P<negate>(?:(not |)))see "(?P<text>[^"]*)" in the "(?P<region>[^"]*)" region$/ * @Given /^I should (?P<negate>(?:(not |)))see "(?P<text>[^"]*)" in the "(?P<region>[^"]*)" region$/
*/ */
public function iSeeTextInRegion($negate, $text, $region) { public function iSeeTextInRegion($negate, $text, $region)
{
$context = $this->getMainContext(); $context = $this->getMainContext();
$regionObj = $context->getRegionObj($region); $regionObj = $context->getRegionObj($region);
assertNotNull($regionObj); assertNotNull($regionObj);
@ -697,7 +751,7 @@ JS;
$actual = preg_replace('/\s+/u', ' ', $actual); $actual = preg_replace('/\s+/u', ' ', $actual);
$regex = '/'.preg_quote($text, '/').'/ui'; $regex = '/'.preg_quote($text, '/').'/ui';
if(trim($negate)) { if (trim($negate)) {
if (preg_match($regex, $actual)) { if (preg_match($regex, $actual)) {
$message = sprintf( $message = sprintf(
'The text "%s" was found in the text of the "%s" region on the page %s.', 'The text "%s" was found in the text of the "%s" region on the page %s.',
@ -720,7 +774,6 @@ JS;
throw new \Exception($message); throw new \Exception($message);
} }
} }
} }
/** /**
@ -728,7 +781,8 @@ JS;
* *
* @Given /^I select the "([^"]*)" radio button$/ * @Given /^I select the "([^"]*)" radio button$/
*/ */
public function iSelectTheRadioButton($radioLabel) { public function iSelectTheRadioButton($radioLabel)
{
$session = $this->getSession(); $session = $this->getSession();
$radioButton = $session->getPage()->find('named', array( $radioButton = $session->getPage()->find('named', array(
'radio', $this->getSession()->getSelectorsHandler()->xpathLiteral($radioLabel) 'radio', $this->getSession()->getSelectorsHandler()->xpathLiteral($radioLabel)
@ -740,7 +794,8 @@ JS;
/** /**
* @Then /^the "([^"]*)" table should contain "([^"]*)"$/ * @Then /^the "([^"]*)" table should contain "([^"]*)"$/
*/ */
public function theTableShouldContain($selector, $text) { public function theTableShouldContain($selector, $text)
{
$table = $this->getTable($selector); $table = $this->getTable($selector);
$element = $table->find('named', array('content', "'$text'")); $element = $table->find('named', array('content', "'$text'"));
@ -750,7 +805,8 @@ JS;
/** /**
* @Then /^the "([^"]*)" table should not contain "([^"]*)"$/ * @Then /^the "([^"]*)" table should not contain "([^"]*)"$/
*/ */
public function theTableShouldNotContain($selector, $text) { public function theTableShouldNotContain($selector, $text)
{
$table = $this->getTable($selector); $table = $this->getTable($selector);
$element = $table->find('named', array('content', "'$text'")); $element = $table->find('named', array('content', "'$text'"));
@ -760,7 +816,8 @@ JS;
/** /**
* @Given /^I click on "([^"]*)" in the "([^"]*)" table$/ * @Given /^I click on "([^"]*)" in the "([^"]*)" table$/
*/ */
public function iClickOnInTheTable($text, $selector) { public function iClickOnInTheTable($text, $selector)
{
$table = $this->getTable($selector); $table = $this->getTable($selector);
$element = $table->find('xpath', sprintf('//*[count(*)=0 and contains(.,"%s")]', $text)); $element = $table->find('xpath', sprintf('//*[count(*)=0 and contains(.,"%s")]', $text));
@ -778,13 +835,15 @@ JS;
* *
* @return Behat\Mink\Element\NodeElement * @return Behat\Mink\Element\NodeElement
*/ */
protected function getTable($selector) { protected function getTable($selector)
{
$selector = $this->getSession()->getSelectorsHandler()->xpathLiteral($selector); $selector = $this->getSession()->getSelectorsHandler()->xpathLiteral($selector);
$page = $this->getSession()->getPage(); $page = $this->getSession()->getPage();
$candidates = $page->findAll( $candidates = $page->findAll(
'xpath', 'xpath',
$this->getSession()->getSelectorsHandler()->selectorToXpath( $this->getSession()->getSelectorsHandler()->selectorToXpath(
"xpath", ".//table[(./@id = $selector or contains(./@title, $selector))]" "xpath",
".//table[(./@id = $selector or contains(./@title, $selector))]"
) )
); );
@ -802,8 +861,8 @@ JS;
assertTrue((bool)$candidates, 'Could not find any table elements'); assertTrue((bool)$candidates, 'Could not find any table elements');
$table = null; $table = null;
foreach($candidates as $candidate) { foreach ($candidates as $candidate) {
if(!$table && $candidate->isVisible()) { if (!$table && $candidate->isVisible()) {
$table = $candidate; $table = $candidate;
} }
} }
@ -818,7 +877,8 @@ JS;
* Assumptions: the two texts appear in their conjunct parent element once * Assumptions: the two texts appear in their conjunct parent element once
* @Then /^I should see the text "(?P<textBefore>(?:[^"]|\\")*)" (before|after) the text "(?P<textAfter>(?:[^"]|\\")*)" in the "(?P<element>[^"]*)" element$/ * @Then /^I should see the text "(?P<textBefore>(?:[^"]|\\")*)" (before|after) the text "(?P<textAfter>(?:[^"]|\\")*)" in the "(?P<element>[^"]*)" element$/
*/ */
public function theTextBeforeAfter($textBefore, $order, $textAfter, $element) { public function theTextBeforeAfter($textBefore, $order, $textAfter, $element)
{
$ele = $this->getSession()->getPage()->find('css', $element); $ele = $this->getSession()->getPage()->find('css', $element);
assertNotNull($ele, sprintf('%s not found', $element)); assertNotNull($ele, sprintf('%s not found', $element));
@ -829,7 +889,7 @@ JS;
/// Use strpos to get the position of the first occurrence of the two texts (case-sensitive) /// Use strpos to get the position of the first occurrence of the two texts (case-sensitive)
// and compare them with the given order (before or after) // and compare them with the given order (before or after)
if($order === 'before') { if ($order === 'before') {
assertTrue(strpos($text, $textBefore) < strpos($text, $textAfter)); assertTrue(strpos($text, $textBefore) < strpos($text, $textAfter));
} else { } else {
assertTrue(strpos($text, $textBefore) > strpos($text, $textAfter)); assertTrue(strpos($text, $textBefore) > strpos($text, $textAfter));
@ -843,13 +903,14 @@ JS;
* *
* @Given /^I wait for (\d+) seconds until I see the "([^"]*)" element$/ * @Given /^I wait for (\d+) seconds until I see the "([^"]*)" element$/
**/ **/
public function iWaitXUntilISee($wait, $selector) { public function iWaitXUntilISee($wait, $selector)
{
$page = $this->getSession()->getPage(); $page = $this->getSession()->getPage();
$this->spin(function($page) use ($page, $selector){ $this->spin(function ($page) use ($page, $selector) {
$element = $page->find('css', $selector); $element = $page->find('css', $selector);
if(empty($element)) { if (empty($element)) {
return false; return false;
} else { } else {
return $element->isVisible(); return $element->isVisible();
@ -865,13 +926,14 @@ JS;
* *
* @Given /^I wait until I see the "([^"]*)" element$/ * @Given /^I wait until I see the "([^"]*)" element$/
*/ */
public function iWaitUntilISee($selector) { public function iWaitUntilISee($selector)
{
$page = $this->getSession()->getPage(); $page = $this->getSession()->getPage();
$this->spin(function($page) use ($page, $selector){ $this->spin(function ($page) use ($page, $selector) {
$element = $page->find('css', $selector); $element = $page->find('css', $selector);
if(empty($element)){ if (empty($element)) {
return false; return false;
} else{ } else {
return ($element->isVisible()); return ($element->isVisible());
} }
}); });
@ -885,16 +947,17 @@ JS;
* *
* @Given /^I wait until I see the text "([^"]*)"$/ * @Given /^I wait until I see the text "([^"]*)"$/
*/ */
public function iWaitUntilISeeText($text){ public function iWaitUntilISeeText($text)
{
$page = $this->getSession()->getPage(); $page = $this->getSession()->getPage();
$session = $this->getSession(); $session = $this->getSession();
$this->spin(function($page) use ($page, $session, $text) { $this->spin(function ($page) use ($page, $session, $text) {
$element = $page->find( $element = $page->find(
'xpath', 'xpath',
$session->getSelectorsHandler()->selectorToXpath("xpath", ".//*[contains(text(), '$text')]") $session->getSelectorsHandler()->selectorToXpath("xpath", ".//*[contains(text(), '$text')]")
); );
if(empty($element)) { if (empty($element)) {
return false; return false;
} else { } else {
return ($element->isVisible()); return ($element->isVisible());
@ -905,7 +968,8 @@ JS;
/** /**
* @Given /^I scroll to the bottom$/ * @Given /^I scroll to the bottom$/
*/ */
public function iScrollToBottom() { public function iScrollToBottom()
{
$javascript = 'window.scrollTo(0, Math.max(document.documentElement.scrollHeight, document.body.scrollHeight, document.documentElement.clientHeight));'; $javascript = 'window.scrollTo(0, Math.max(document.documentElement.scrollHeight, document.body.scrollHeight, document.documentElement.clientHeight));';
$this->getSession()->executeScript($javascript); $this->getSession()->executeScript($javascript);
} }
@ -913,7 +977,8 @@ JS;
/** /**
* @Given /^I scroll to the top$/ * @Given /^I scroll to the top$/
*/ */
public function iScrollToTop() { public function iScrollToTop()
{
$this->getSession()->executeScript('window.scrollTo(0,0);'); $this->getSession()->executeScript('window.scrollTo(0,0);');
} }
@ -926,13 +991,14 @@ JS;
* *
* @Given /^I scroll to the "([^"]*)" (field|link|button)$/ * @Given /^I scroll to the "([^"]*)" (field|link|button)$/
*/ */
public function iScrollToField($locator, $type) { public function iScrollToField($locator, $type)
{
$page = $this->getSession()->getPage(); $page = $this->getSession()->getPage();
$el = $page->find('named', array($type, "'$locator'")); $el = $page->find('named', array($type, "'$locator'"));
assertNotNull($el, sprintf('%s element not found', $locator)); assertNotNull($el, sprintf('%s element not found', $locator));
$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');
} }
@ -948,12 +1014,13 @@ JS;
* *
* @Given /^I scroll to the "(?P<locator>(?:[^"]|\\")*)" element$/ * @Given /^I scroll to the "(?P<locator>(?:[^"]|\\")*)" element$/
*/ */
public function iScrollToElement($locator) { public function iScrollToElement($locator)
{
$el = $this->getSession()->getPage()->find('css', $locator); $el = $this->getSession()->getPage()->find('css', $locator);
assertNotNull($el, sprintf('The element "%s" is not found', $locator)); assertNotNull($el, sprintf('The element "%s" is not found', $locator));
$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');
} }
@ -971,10 +1038,11 @@ JS;
* @return bool Returns true if the lambda returns successfully * @return bool Returns true if the lambda returns successfully
* @throws \Exception Thrown if the wait threshold is exceeded without the lambda successfully returning * @throws \Exception Thrown if the wait threshold is exceeded without the lambda successfully returning
*/ */
public function spin($lambda, $wait = 60) { public function spin($lambda, $wait = 60)
{
for ($i = 0; $i < $wait; $i++) { for ($i = 0; $i < $wait; $i++) {
try { try {
if($lambda($this)) { if ($lambda($this)) {
return true; return true;
} }
} catch (\Exception $e) { } catch (\Exception $e) {
@ -998,8 +1066,8 @@ JS;
/** /**
* We have to catch exceptions and log somehow else otherwise behat falls over * We have to catch exceptions and log somehow else otherwise behat falls over
*/ */
protected function logException($e){ protected function logException($e)
{
file_put_contents('php://stderr', 'Exception caught: '.$e); file_put_contents('php://stderr', 'Exception caught: '.$e);
} }
} }

View File

@ -2,15 +2,15 @@
namespace SilverStripe\BehatExtension\Context; namespace SilverStripe\BehatExtension\Context;
use Behat\Behat\Context\ClosuredContextInterface, use Behat\Behat\Context\ClosuredContextInterface;
Behat\Behat\Context\TranslatedContextInterface, use Behat\Behat\Context\TranslatedContextInterface;
Behat\Behat\Context\BehatContext, use Behat\Behat\Context\BehatContext;
Behat\Behat\Context\Step, use Behat\Behat\Context\Step;
Behat\Behat\Event\FeatureEvent, use Behat\Behat\Event\FeatureEvent;
Behat\Behat\Event\ScenarioEvent, use Behat\Behat\Event\ScenarioEvent;
Behat\Behat\Exception\PendingException; use Behat\Behat\Exception\PendingException;
use Behat\Gherkin\Node\PyStringNode, use Behat\Gherkin\Node\PyStringNode;
Behat\Gherkin\Node\TableNode; use Behat\Gherkin\Node\TableNode;
use Symfony\Component\DomCrawler\Crawler; use Symfony\Component\DomCrawler\Crawler;
// PHPUnit // PHPUnit
@ -59,7 +59,7 @@ class EmailContext extends BehatContext
// to ensure its available both in CLI execution and the tested browser session // to ensure its available both in CLI execution and the tested browser session
$this->mailer = new \SilverStripe\BehatExtension\Utility\TestMailer(); $this->mailer = new \SilverStripe\BehatExtension\Utility\TestMailer();
\Email::set_mailer($this->mailer); \Email::set_mailer($this->mailer);
\Config::inst()->update("Email","send_all_emails_to", null); \Config::inst()->update("Email", "send_all_emails_to", null);
} }
/** /**
@ -70,7 +70,7 @@ class EmailContext extends BehatContext
$to = ($direction == 'to') ? $email : null; $to = ($direction == 'to') ? $email : null;
$from = ($direction == 'from') ? $email : null; $from = ($direction == 'from') ? $email : null;
$match = $this->mailer->findEmail($to, $from); $match = $this->mailer->findEmail($to, $from);
if(trim($negate)) { if (trim($negate)) {
assertNull($match); assertNull($match);
} else { } else {
assertNotNull($match); assertNotNull($match);
@ -87,8 +87,10 @@ class EmailContext extends BehatContext
$from = ($direction == 'from') ? $email : null; $from = ($direction == 'from') ? $email : null;
$match = $this->mailer->findEmail($to, $from, $subject); $match = $this->mailer->findEmail($to, $from, $subject);
$allMails = $this->mailer->findEmails($to, $from); $allMails = $this->mailer->findEmails($to, $from);
$allTitles = $allMails ? '"' . implode('","', array_map(function($email) {return $email->Subject;}, $allMails)) . '"' : null; $allTitles = $allMails ? '"' . implode('","', array_map(function ($email) {
if(trim($negate)) { return $email->Subject;
}, $allMails)) . '"' : null;
if (trim($negate)) {
assertNull($match); assertNull($match);
} else { } else {
$msg = sprintf( $msg = sprintf(
@ -97,10 +99,10 @@ class EmailContext extends BehatContext
$email, $email,
$subject $subject
); );
if($allTitles) { if ($allTitles) {
$msg .= ' Existing emails: ' . $allTitles; $msg .= ' Existing emails: ' . $allTitles;
} }
assertNotNull($match,$msg); assertNotNull($match, $msg);
} }
$this->lastMatchedEmail = $match; $this->lastMatchedEmail = $match;
} }
@ -114,19 +116,19 @@ class EmailContext extends BehatContext
*/ */
public function thereTheEmailContains($negate, $content) public function thereTheEmailContains($negate, $content)
{ {
if(!$this->lastMatchedEmail) { if (!$this->lastMatchedEmail) {
throw new \LogicException('No matched email found from previous step'); throw new \LogicException('No matched email found from previous step');
} }
$email = $this->lastMatchedEmail; $email = $this->lastMatchedEmail;
$emailContent = null; $emailContent = null;
if($email->Content) { if ($email->Content) {
$emailContent = $email->Content; $emailContent = $email->Content;
} else { } else {
$emailContent = $email->PlainContent; $emailContent = $email->PlainContent;
} }
if(trim($negate)) { if (trim($negate)) {
assertNotContains($content, $emailContent); assertNotContains($content, $emailContent);
} else { } else {
assertContains($content, $emailContent); assertContains($content, $emailContent);
@ -143,7 +145,7 @@ class EmailContext extends BehatContext
*/ */
public function thereTheEmailContainsPlainText($content) public function thereTheEmailContainsPlainText($content)
{ {
if(!$this->lastMatchedEmail) { if (!$this->lastMatchedEmail) {
throw new \LogicException('No matched email found from previous step'); throw new \LogicException('No matched email found from previous step');
} }
@ -200,7 +202,7 @@ class EmailContext extends BehatContext
*/ */
public function iGoToInTheEmail($linkSelector) public function iGoToInTheEmail($linkSelector)
{ {
if(!$this->lastMatchedEmail) { if (!$this->lastMatchedEmail) {
throw new \LogicException('No matched email found from previous step'); throw new \LogicException('No matched email found from previous step');
} }
@ -230,14 +232,15 @@ class EmailContext extends BehatContext
* Assumes an email has been identified by a previous step. * Assumes an email has been identified by a previous step.
* @Then /^the email should (not |)contain the following data:$/ * @Then /^the email should (not |)contain the following data:$/
*/ */
public function theEmailContainFollowingData($negate, TableNode $table) { public function theEmailContainFollowingData($negate, TableNode $table)
if(!$this->lastMatchedEmail) { {
if (!$this->lastMatchedEmail) {
throw new \LogicException('No matched email found from previous step'); throw new \LogicException('No matched email found from previous step');
} }
$email = $this->lastMatchedEmail; $email = $this->lastMatchedEmail;
$emailContent = null; $emailContent = null;
if($email->Content) { if ($email->Content) {
$emailContent = $email->Content; $emailContent = $email->Content;
} else { } else {
$emailContent = $email->PlainContent; $emailContent = $email->PlainContent;
@ -248,12 +251,12 @@ class EmailContext extends BehatContext
$rows = $table->getRows(); $rows = $table->getRows();
// For "should not contain" // For "should not contain"
if(trim($negate)) { if (trim($negate)) {
foreach($rows as $row) { foreach ($rows as $row) {
assertNotContains($row[0], $emailContent); assertNotContains($row[0], $emailContent);
} }
} else { } else {
foreach($rows as $row) { foreach ($rows as $row) {
assertContains($row[0], $emailContent); assertContains($row[0], $emailContent);
} }
} }
@ -265,14 +268,14 @@ class EmailContext extends BehatContext
public function thereIsAnEmailTitled($negate, $subject) public function thereIsAnEmailTitled($negate, $subject)
{ {
$match = $this->mailer->findEmail(null, null, $subject); $match = $this->mailer->findEmail(null, null, $subject);
if(trim($negate)) { if (trim($negate)) {
assertNull($match); assertNull($match);
} else { } else {
$msg = sprintf( $msg = sprintf(
'Could not find email titled "%s".', 'Could not find email titled "%s".',
$subject $subject
); );
assertNotNull($match,$msg); assertNotNull($match, $msg);
} }
$this->lastMatchedEmail = $match; $this->lastMatchedEmail = $match;
} }
@ -282,12 +285,12 @@ class EmailContext extends BehatContext
*/ */
public function theEmailSentFrom($negate, $from) public function theEmailSentFrom($negate, $from)
{ {
if(!$this->lastMatchedEmail) { if (!$this->lastMatchedEmail) {
throw new \LogicException('No matched email found from previous step'); throw new \LogicException('No matched email found from previous step');
} }
$match = $this->lastMatchedEmail; $match = $this->lastMatchedEmail;
if(trim($negate)) { if (trim($negate)) {
assertNotContains($from, $match->From); assertNotContains($from, $match->From);
} else { } else {
assertContains($from, $match->From); assertContains($from, $match->From);
@ -299,12 +302,12 @@ class EmailContext extends BehatContext
*/ */
public function theEmailSentTo($negate, $to) public function theEmailSentTo($negate, $to)
{ {
if(!$this->lastMatchedEmail) { if (!$this->lastMatchedEmail) {
throw new \LogicException('No matched email found from previous step'); throw new \LogicException('No matched email found from previous step');
} }
$match = $this->lastMatchedEmail; $match = $this->lastMatchedEmail;
if(trim($negate)) { if (trim($negate)) {
assertNotContains($to, $match->To); assertNotContains($to, $match->To);
} else { } else {
assertContains($to, $match->To); assertContains($to, $match->To);

View File

@ -2,18 +2,16 @@
namespace SilverStripe\BehatExtension\Context; namespace SilverStripe\BehatExtension\Context;
use Behat\Behat\Context\BehatContext, use Behat\Behat\Context\BehatContext;
Behat\Behat\Event\ScenarioEvent, use Behat\Behat\Event\ScenarioEvent;
Behat\Gherkin\Node\PyStringNode, use Behat\Gherkin\Node\PyStringNode;
Behat\Gherkin\Node\TableNode, use Behat\Gherkin\Node\TableNode;
SilverStripe\Filesystem\Storage\AssetStore; use SilverStripe\Filesystem\Storage\AssetStore;
use SilverStripe\ORM\DB; use SilverStripe\ORM\DB;
use SilverStripe\ORM\DataObject; use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\Versioning\Versioned; use SilverStripe\ORM\Versioning\Versioned;
use SilverStripe\Security\Permission; use SilverStripe\Security\Permission;
// PHPUnit // PHPUnit
require_once BASE_PATH . '/vendor/phpunit/phpunit/src/Framework/Assert/Functions.php'; require_once BASE_PATH . '/vendor/phpunit/phpunit/src/Framework/Assert/Functions.php';
@ -46,19 +44,22 @@ class FixtureContext extends BehatContext
*/ */
protected $createdAssets = array(); protected $createdAssets = array();
public function __construct(array $parameters) { public function __construct(array $parameters)
{
$this->context = $parameters; $this->context = $parameters;
} }
public function getSession($name = null) { public function getSession($name = null)
{
return $this->getMainContext()->getSession($name); return $this->getMainContext()->getSession($name);
} }
/** /**
* @return \FixtureFactory * @return \FixtureFactory
*/ */
public function getFixtureFactory() { public function getFixtureFactory()
if(!$this->fixtureFactory) { {
if (!$this->fixtureFactory) {
$this->fixtureFactory = \Injector::inst()->create('FixtureFactory', 'FixtureContextFactory'); $this->fixtureFactory = \Injector::inst()->create('FixtureFactory', 'FixtureContextFactory');
} }
return $this->fixtureFactory; return $this->fixtureFactory;
@ -67,28 +68,32 @@ class FixtureContext extends BehatContext
/** /**
* @param \FixtureFactory $factory * @param \FixtureFactory $factory
*/ */
public function setFixtureFactory(\FixtureFactory $factory) { public function setFixtureFactory(\FixtureFactory $factory)
{
$this->fixtureFactory = $factory; $this->fixtureFactory = $factory;
} }
/** /**
* @param String * @param String
*/ */
public function setFilesPath($path) { public function setFilesPath($path)
{
$this->filesPath = $path; $this->filesPath = $path;
} }
/** /**
* @return String * @return String
*/ */
public function getFilesPath() { public function getFilesPath()
{
return $this->filesPath; return $this->filesPath;
} }
/** /**
* @BeforeScenario @database-defaults * @BeforeScenario @database-defaults
*/ */
public function beforeDatabaseDefaults(ScenarioEvent $event) { public function beforeDatabaseDefaults(ScenarioEvent $event)
{
\SapphireTest::empty_temp_db(); \SapphireTest::empty_temp_db();
DB::get_conn()->quiet(); DB::get_conn()->quiet();
$dataClasses = \ClassInfo::subclassesFor('SilverStripe\\ORM\\DataObject'); $dataClasses = \ClassInfo::subclassesFor('SilverStripe\\ORM\\DataObject');
@ -101,14 +106,16 @@ class FixtureContext extends BehatContext
/** /**
* @AfterScenario * @AfterScenario
*/ */
public function afterResetDatabase(ScenarioEvent $event) { public function afterResetDatabase(ScenarioEvent $event)
{
\SapphireTest::empty_temp_db(); \SapphireTest::empty_temp_db();
} }
/** /**
* @AfterScenario * @AfterScenario
*/ */
public function afterResetAssets(ScenarioEvent $event) { public function afterResetAssets(ScenarioEvent $event)
{
$store = $this->getAssetStore(); $store = $this->getAssetStore();
if (is_array($this->createdAssets)) { if (is_array($this->createdAssets)) {
foreach ($this->createdAssets as $asset) { foreach ($this->createdAssets as $asset) {
@ -122,7 +129,8 @@ class FixtureContext extends BehatContext
* *
* @Given /^(?:(an|a|the) )"(?<type>[^"]+)" "(?<id>[^"]+)"$/ * @Given /^(?:(an|a|the) )"(?<type>[^"]+)" "(?<id>[^"]+)"$/
*/ */
public function stepCreateRecord($type, $id) { public function stepCreateRecord($type, $id)
{
$class = $this->convertTypeToClass($type); $class = $this->convertTypeToClass($type);
$fields = $this->prepareFixture($class, $id); $fields = $this->prepareFixture($class, $id);
$this->fixtureFactory->createObject($class, $id, $fields); $this->fixtureFactory->createObject($class, $id, $fields);
@ -133,16 +141,17 @@ class FixtureContext extends BehatContext
* *
* @Given /^(?:(an|a|the) )"(?<type>[^"]+)" "(?<id>[^"]+)" has (?:(an|a|the) )"(?<field>.*)" "(?<value>.*)"$/ * @Given /^(?:(an|a|the) )"(?<type>[^"]+)" "(?<id>[^"]+)" has (?:(an|a|the) )"(?<field>.*)" "(?<value>.*)"$/
*/ */
public function stepCreateRecordHasField($type, $id, $field, $value) { public function stepCreateRecordHasField($type, $id, $field, $value)
{
$class = $this->convertTypeToClass($type); $class = $this->convertTypeToClass($type);
$fields = $this->convertFields( $fields = $this->convertFields(
$class, $class,
array($field => $value) array($field => $value)
); );
// We should check if this fixture object already exists - if it does, we update it. If not, we create it // We should check if this fixture object already exists - if it does, we update it. If not, we create it
if($existingFixture = $this->fixtureFactory->get($class, $id)) { if ($existingFixture = $this->fixtureFactory->get($class, $id)) {
// Merge existing data with new data, and create new object to replace existing object // Merge existing data with new data, and create new object to replace existing object
foreach($fields as $k => $v) { foreach ($fields as $k => $v) {
$existingFixture->$k = $v; $existingFixture->$k = $v;
} }
$existingFixture->write(); $existingFixture->write();
@ -157,7 +166,8 @@ class FixtureContext extends BehatContext
* *
* @Given /^(?:(an|a|the) )"(?<type>[^"]+)" "(?<id>[^"]+)" (?:(with|has)) (?<data>".*)$/ * @Given /^(?:(an|a|the) )"(?<type>[^"]+)" "(?<id>[^"]+)" (?:(with|has)) (?<data>".*)$/
*/ */
public function stepCreateRecordWithData($type, $id, $data) { public function stepCreateRecordWithData($type, $id, $data)
{
$class = $this->convertTypeToClass($type); $class = $this->convertTypeToClass($type);
preg_match_all( preg_match_all(
'/"(?<key>[^"]+)"\s*=\s*"(?<value>[^"]+)"/', '/"(?<key>[^"]+)"\s*=\s*"(?<value>[^"]+)"/',
@ -170,9 +180,9 @@ class FixtureContext extends BehatContext
); );
$fields = $this->prepareFixture($class, $id, $fields); $fields = $this->prepareFixture($class, $id, $fields);
// We should check if this fixture object already exists - if it does, we update it. If not, we create it // We should check if this fixture object already exists - if it does, we update it. If not, we create it
if($existingFixture = $this->fixtureFactory->get($class, $id)) { if ($existingFixture = $this->fixtureFactory->get($class, $id)) {
// Merge existing data with new data, and create new object to replace existing object // Merge existing data with new data, and create new object to replace existing object
foreach($fields as $k => $v) { foreach ($fields as $k => $v) {
$existingFixture->$k = $v; $existingFixture->$k = $v;
} }
$existingFixture->write(); $existingFixture->write();
@ -189,16 +199,17 @@ class FixtureContext extends BehatContext
* *
* @Given /^(?:(an|a|the) )"(?<type>[^"]+)" "(?<id>[^"]+)" has the following data$/ * @Given /^(?:(an|a|the) )"(?<type>[^"]+)" "(?<id>[^"]+)" has the following data$/
*/ */
public function stepCreateRecordWithTable($type, $id, $null, TableNode $fieldsTable) { public function stepCreateRecordWithTable($type, $id, $null, TableNode $fieldsTable)
{
$class = $this->convertTypeToClass($type); $class = $this->convertTypeToClass($type);
// TODO Support more than one record // TODO Support more than one record
$fields = $this->convertFields($class, $fieldsTable->getRowsHash()); $fields = $this->convertFields($class, $fieldsTable->getRowsHash());
$fields = $this->prepareFixture($class, $id, $fields); $fields = $this->prepareFixture($class, $id, $fields);
// We should check if this fixture object already exists - if it does, we update it. If not, we create it // We should check if this fixture object already exists - if it does, we update it. If not, we create it
if($existingFixture = $this->fixtureFactory->get($class, $id)) { if ($existingFixture = $this->fixtureFactory->get($class, $id)) {
// Merge existing data with new data, and create new object to replace existing object // Merge existing data with new data, and create new object to replace existing object
foreach($fields as $k => $v) { foreach ($fields as $k => $v) {
$existingFixture->$k = $v; $existingFixture->$k = $v;
} }
$existingFixture->write(); $existingFixture->write();
@ -213,27 +224,30 @@ class FixtureContext extends BehatContext
* *
* @Given /^(?:(an|a|the) )"(?<type>[^"]+)" "(?<id>[^"]+)" is a (?<relation>[^\s]*) of (?:(an|a|the) )"(?<relationType>[^"]+)" "(?<relationId>[^"]+)"/ * @Given /^(?:(an|a|the) )"(?<type>[^"]+)" "(?<id>[^"]+)" is a (?<relation>[^\s]*) of (?:(an|a|the) )"(?<relationType>[^"]+)" "(?<relationId>[^"]+)"/
*/ */
public function stepUpdateRecordRelation($type, $id, $relation, $relationType, $relationId) { public function stepUpdateRecordRelation($type, $id, $relation, $relationType, $relationId)
{
$class = $this->convertTypeToClass($type); $class = $this->convertTypeToClass($type);
$relationClass = $this->convertTypeToClass($relationType); $relationClass = $this->convertTypeToClass($relationType);
$relationObj = $this->fixtureFactory->get($relationClass, $relationId); $relationObj = $this->fixtureFactory->get($relationClass, $relationId);
if(!$relationObj) $relationObj = $this->fixtureFactory->createObject($relationClass, $relationId); if (!$relationObj) {
$relationObj = $this->fixtureFactory->createObject($relationClass, $relationId);
}
$data = array(); $data = array();
if($relation == 'child') { if ($relation == 'child') {
$data['ParentID'] = $relationObj->ID; $data['ParentID'] = $relationObj->ID;
} }
$obj = $this->fixtureFactory->get($class, $id); $obj = $this->fixtureFactory->get($class, $id);
if($obj) { if ($obj) {
$obj->update($data); $obj->update($data);
$obj->write(); $obj->write();
} else { } else {
$obj = $this->fixtureFactory->createObject($class, $id, $data); $obj = $this->fixtureFactory->createObject($class, $id, $data);
} }
switch($relation) { switch ($relation) {
case 'parent': case 'parent':
$relationObj->ParentID = $obj->ID; $relationObj->ParentID = $obj->ID;
$relationObj->write(); $relationObj->write();
@ -243,7 +257,8 @@ class FixtureContext extends BehatContext
break; break;
default: default:
throw new \InvalidArgumentException(sprintf( throw new \InvalidArgumentException(sprintf(
'Invalid relation "%s"', $relation 'Invalid relation "%s"',
$relation
)); ));
} }
} }
@ -255,7 +270,8 @@ class FixtureContext extends BehatContext
* @example I assign the "TaxonomyTerm" "For customers" to the "Page" "Page1" * @example I assign the "TaxonomyTerm" "For customers" to the "Page" "Page1"
* @Given /^I assign (?:(an|a|the) )"(?<type>[^"]+)" "(?<value>[^"]+)" to (?:(an|a|the) )"(?<relationType>[^"]+)" "(?<relationId>[^"]+)"$/ * @Given /^I assign (?:(an|a|the) )"(?<type>[^"]+)" "(?<value>[^"]+)" to (?:(an|a|the) )"(?<relationType>[^"]+)" "(?<relationId>[^"]+)"$/
*/ */
public function stepIAssignObjToObj($type, $value, $relationType, $relationId) { public function stepIAssignObjToObj($type, $value, $relationType, $relationId)
{
self::stepIAssignObjToObjInTheRelation($type, $value, $relationType, $relationId, null); self::stepIAssignObjToObjInTheRelation($type, $value, $relationType, $relationId, null);
} }
@ -267,47 +283,62 @@ class FixtureContext extends BehatContext
* @example I assign the "TaxonomyTerm" "For customers" to the "Page" "Page1" in the "Terms" relation * @example I assign the "TaxonomyTerm" "For customers" to the "Page" "Page1" in the "Terms" relation
* @Given /^I assign (?:(an|a|the) )"(?<type>[^"]+)" "(?<value>[^"]+)" to (?:(an|a|the) )"(?<relationType>[^"]+)" "(?<relationId>[^"]+)" in the "(?<relationName>[^"]+)" relation$/ * @Given /^I assign (?:(an|a|the) )"(?<type>[^"]+)" "(?<value>[^"]+)" to (?:(an|a|the) )"(?<relationType>[^"]+)" "(?<relationId>[^"]+)" in the "(?<relationName>[^"]+)" relation$/
*/ */
public function stepIAssignObjToObjInTheRelation($type, $value, $relationType, $relationId, $relationName) { public function stepIAssignObjToObjInTheRelation($type, $value, $relationType, $relationId, $relationName)
{
$class = $this->convertTypeToClass($type); $class = $this->convertTypeToClass($type);
$relationClass = $this->convertTypeToClass($relationType); $relationClass = $this->convertTypeToClass($relationType);
// Check if this fixture object already exists - if not, we create it // Check if this fixture object already exists - if not, we create it
$relationObj = $this->fixtureFactory->get($relationClass, $relationId); $relationObj = $this->fixtureFactory->get($relationClass, $relationId);
if(!$relationObj) $relationObj = $this->fixtureFactory->createObject($relationClass, $relationId); if (!$relationObj) {
$relationObj = $this->fixtureFactory->createObject($relationClass, $relationId);
}
// Check if there is relationship defined in many_many (includes belongs_many_many) // Check if there is relationship defined in many_many (includes belongs_many_many)
$manyField = null; $manyField = null;
$oneField = null; $oneField = null;
if ($relationObj->many_many()) { if ($relationObj->many_many()) {
$manyField = array_search($class, $relationObj->many_many()); $manyField = array_search($class, $relationObj->many_many());
if($manyField && strlen($relationName) > 0) $manyField = $relationName; if ($manyField && strlen($relationName) > 0) {
$manyField = $relationName;
} }
if(empty($manyField) && $relationObj->has_many()) { }
if (empty($manyField) && $relationObj->has_many()) {
$manyField = array_search($class, $relationObj->has_many()); $manyField = array_search($class, $relationObj->has_many());
if($manyField && strlen($relationName) > 0) $manyField = $relationName; if ($manyField && strlen($relationName) > 0) {
$manyField = $relationName;
} }
if(empty($manyField) && $relationObj->has_one()) { }
if (empty($manyField) && $relationObj->has_one()) {
$oneField = array_search($class, $relationObj->has_one()); $oneField = array_search($class, $relationObj->has_one());
if($oneField && strlen($relationName) > 0) $oneField = $relationName; if ($oneField && strlen($relationName) > 0) {
$oneField = $relationName;
} }
if(empty($manyField) && empty($oneField)) { }
if (empty($manyField) && empty($oneField)) {
throw new \Exception("'$relationClass' has no relationship (has_one, has_many and many_many) with '$class'!"); throw new \Exception("'$relationClass' has no relationship (has_one, has_many and many_many) with '$class'!");
} }
// Get the searchable field to check if the fixture object already exists // Get the searchable field to check if the fixture object already exists
$temObj = new $class; $temObj = new $class;
if(isset($temObj->Name)) $field = "Name"; if (isset($temObj->Name)) {
else if(isset($temObj->Title)) $field = "Title"; $field = "Name";
else $field = "ID"; } elseif (isset($temObj->Title)) {
$field = "Title";
} else {
$field = "ID";
}
// Check if the fixture object exists - if not, we create it // Check if the fixture object exists - if not, we create it
$obj = DataObject::get($class)->filter($field, $value)->first(); $obj = DataObject::get($class)->filter($field, $value)->first();
if(!$obj) $obj = $this->fixtureFactory->createObject($class, $value); if (!$obj) {
$obj = $this->fixtureFactory->createObject($class, $value);
}
// If has_many or many_many, add this fixture object to the relation object // If has_many or many_many, add this fixture object to the relation object
// If has_one, set value to the joint field with this fixture object's ID // If has_one, set value to the joint field with this fixture object's ID
if($manyField) { if ($manyField) {
$relationObj->$manyField()->add($obj); $relationObj->$manyField()->add($obj);
} else if($oneField) { } elseif ($oneField) {
// E.g. $has_one = array('PanelOffer' => 'Offer'); // E.g. $has_one = array('PanelOffer' => 'Offer');
// then the join field is PanelOfferID. This is the common rule in the CMS // then the join field is PanelOfferID. This is the common rule in the CMS
$relationObj->{$oneField . 'ID'} = $obj->ID; $relationObj->{$oneField . 'ID'} = $obj->ID;
@ -321,11 +352,12 @@ class FixtureContext extends BehatContext
* *
* @Given /^(?:(an|a|the) )"(?<type>[^"]+)" "(?<id>[^"]+)" is (?<state>[^"]*)$/ * @Given /^(?:(an|a|the) )"(?<type>[^"]+)" "(?<id>[^"]+)" is (?<state>[^"]*)$/
*/ */
public function stepUpdateRecordState($type, $id, $state) { public function stepUpdateRecordState($type, $id, $state)
{
$class = $this->convertTypeToClass($type); $class = $this->convertTypeToClass($type);
/** @var DataObject|Versioned $obj */ /** @var DataObject|Versioned $obj */
$obj = $this->fixtureFactory->get($class, $id); $obj = $this->fixtureFactory->get($class, $id);
if(!$obj) { if (!$obj) {
throw new \InvalidArgumentException(sprintf( throw new \InvalidArgumentException(sprintf(
'Can not find record "%s" with identifier "%s"', 'Can not find record "%s" with identifier "%s"',
$type, $type,
@ -333,7 +365,7 @@ class FixtureContext extends BehatContext
)); ));
} }
switch($state) { switch ($state) {
case 'published': case 'published':
$obj->copyVersionToStage('Stage', 'Live'); $obj->copyVersionToStage('Stage', 'Live');
break; break;
@ -350,7 +382,8 @@ class FixtureContext extends BehatContext
break; break;
default: default:
throw new \InvalidArgumentException(sprintf( throw new \InvalidArgumentException(sprintf(
'Invalid state: "%s"', $state 'Invalid state: "%s"',
$state
)); ));
} }
} }
@ -366,7 +399,8 @@ class FixtureContext extends BehatContext
* *
* @Given /^there are the following ([^\s]*) records$/ * @Given /^there are the following ([^\s]*) records$/
*/ */
public function stepThereAreTheFollowingRecords($dataObject, PyStringNode $string) { public function stepThereAreTheFollowingRecords($dataObject, PyStringNode $string)
{
$yaml = array_merge(array($dataObject . ':'), $string->getLines()); $yaml = array_merge(array($dataObject . ':'), $string->getLines());
$yaml = implode("\n ", $yaml); $yaml = implode("\n ", $yaml);
@ -381,9 +415,12 @@ class FixtureContext extends BehatContext
* *
* @Given /^(?:(an|a|the) )"member" "(?<id>[^"]+)" belonging to "(?<groupId>[^"]+)"$/ * @Given /^(?:(an|a|the) )"member" "(?<id>[^"]+)" belonging to "(?<groupId>[^"]+)"$/
*/ */
public function stepCreateMemberWithGroup($id, $groupId) { public function stepCreateMemberWithGroup($id, $groupId)
{
$group = $this->fixtureFactory->get('SilverStripe\\Security\\Group', $groupId); $group = $this->fixtureFactory->get('SilverStripe\\Security\\Group', $groupId);
if(!$group) $group = $this->fixtureFactory->createObject('SilverStripe\\Security\\Group', $groupId); if (!$group) {
$group = $this->fixtureFactory->createObject('SilverStripe\\Security\\Group', $groupId);
}
$member = $this->fixtureFactory->createObject('SilverStripe\\Security\\Member', $id); $member = $this->fixtureFactory->createObject('SilverStripe\\Security\\Member', $id);
$member->Groups()->add($group); $member->Groups()->add($group);
@ -394,7 +431,8 @@ class FixtureContext extends BehatContext
* *
* @Given /^(?:(an|a|the) )"member" "(?<id>[^"]+)" belonging to "(?<groupId>[^"]+)" with (?<data>.*)$/ * @Given /^(?:(an|a|the) )"member" "(?<id>[^"]+)" belonging to "(?<groupId>[^"]+)" with (?<data>.*)$/
*/ */
public function stepCreateMemberWithGroupAndData($id, $groupId, $data) { public function stepCreateMemberWithGroupAndData($id, $groupId, $data)
{
$class = 'SilverStripe\\Security\\Member'; $class = 'SilverStripe\\Security\\Member';
preg_match_all( preg_match_all(
'/"(?<key>[^"]+)"\s*=\s*"(?<value>[^"]+)"/', '/"(?<key>[^"]+)"\s*=\s*"(?<value>[^"]+)"/',
@ -407,7 +445,9 @@ class FixtureContext extends BehatContext
); );
$group = $this->fixtureFactory->get('SilverStripe\\Security\\Group', $groupId); $group = $this->fixtureFactory->get('SilverStripe\\Security\\Group', $groupId);
if(!$group) $group = $this->fixtureFactory->createObject('SilverStripe\\Security\\Group', $groupId); if (!$group) {
$group = $this->fixtureFactory->createObject('SilverStripe\\Security\\Group', $groupId);
}
$member = $this->fixtureFactory->createObject($class, $id, $fields); $member = $this->fixtureFactory->createObject($class, $id, $fields);
$member->Groups()->add($group); $member->Groups()->add($group);
@ -418,29 +458,32 @@ class FixtureContext extends BehatContext
* *
* @Given /^(?:(an|a|the) )"group" "(?<id>[^"]+)" (?:(with|has)) permissions (?<permissionStr>.*)$/ * @Given /^(?:(an|a|the) )"group" "(?<id>[^"]+)" (?:(with|has)) permissions (?<permissionStr>.*)$/
*/ */
public function stepCreateGroupWithPermissions($id, $permissionStr) { public function stepCreateGroupWithPermissions($id, $permissionStr)
{
// Convert natural language permissions to codes // Convert natural language permissions to codes
preg_match_all('/"([^"]+)"/', $permissionStr, $matches); preg_match_all('/"([^"]+)"/', $permissionStr, $matches);
$permissions = $matches[1]; $permissions = $matches[1];
$codes = Permission::get_codes(false); $codes = Permission::get_codes(false);
$group = $this->fixtureFactory->get('SilverStripe\\Security\\Group', $id); $group = $this->fixtureFactory->get('SilverStripe\\Security\\Group', $id);
if(!$group) $group = $this->fixtureFactory->createObject('SilverStripe\\Security\\Group', $id); if (!$group) {
$group = $this->fixtureFactory->createObject('SilverStripe\\Security\\Group', $id);
}
foreach($permissions as $permission) { foreach ($permissions as $permission) {
$found = false; $found = false;
foreach($codes as $code => $details) { foreach ($codes as $code => $details) {
if( if ($permission == $code
$permission == $code
|| $permission == $details['name'] || $permission == $details['name']
) { ) {
Permission::grant($group->ID, $code); Permission::grant($group->ID, $code);
$found = true; $found = true;
} }
} }
if(!$found) { if (!$found) {
throw new \InvalidArgumentException(sprintf( throw new \InvalidArgumentException(sprintf(
'No permission found for "%s"', $permission 'No permission found for "%s"',
$permission
)); ));
} }
} }
@ -453,16 +496,17 @@ class FixtureContext extends BehatContext
* *
* @Given /^I go to (?:(an|a|the) )"(?<type>[^"]+)" "(?<id>[^"]+)"/ * @Given /^I go to (?:(an|a|the) )"(?<type>[^"]+)" "(?<id>[^"]+)"/
*/ */
public function stepGoToNamedRecord($type, $id) { public function stepGoToNamedRecord($type, $id)
{
$class = $this->convertTypeToClass($type); $class = $this->convertTypeToClass($type);
$record = $this->fixtureFactory->get($class, $id); $record = $this->fixtureFactory->get($class, $id);
if(!$record) { if (!$record) {
throw new \InvalidArgumentException(sprintf( throw new \InvalidArgumentException(sprintf(
'Cannot resolve reference "%s", no matching fixture found', 'Cannot resolve reference "%s", no matching fixture found',
$id $id
)); ));
} }
if(!$record->hasMethod('RelativeLink')) { if (!$record->hasMethod('RelativeLink')) {
throw new \InvalidArgumentException('URL for record cannot be determined, missing RelativeLink() method'); throw new \InvalidArgumentException('URL for record cannot be determined, missing RelativeLink() method');
} }
@ -476,7 +520,8 @@ class FixtureContext extends BehatContext
* *
* @Then /^there should be a (?<type>(file|folder) )"(?<path>[^"]*)"/ * @Then /^there should be a (?<type>(file|folder) )"(?<path>[^"]*)"/
*/ */
public function stepThereShouldBeAFileOrFolder($type, $path) { public function stepThereShouldBeAFileOrFolder($type, $path)
{
assertFileExists($this->joinPaths(BASE_PATH, $path)); assertFileExists($this->joinPaths(BASE_PATH, $path));
} }
@ -487,7 +532,8 @@ class FixtureContext extends BehatContext
* *
* @Then /^there should be a filename "(?<filename>[^"]*)" with hash "(?<hash>[a-fA-Z0-9]+)"/ * @Then /^there should be a filename "(?<filename>[^"]*)" with hash "(?<hash>[a-fA-Z0-9]+)"/
*/ */
public function stepThereShouldBeAFileWithTuple($filename, $hash) { public function stepThereShouldBeAFileWithTuple($filename, $hash)
{
$exists = $this->getAssetStore()->exists($filename, $hash); $exists = $this->getAssetStore()->exists($filename, $hash);
assertTrue((bool)$exists, "A file exists with filename $filename and hash $hash"); assertTrue((bool)$exists, "A file exists with filename $filename and hash $hash");
} }
@ -498,11 +544,12 @@ class FixtureContext extends BehatContext
* *
* @Transform /^([^"]+)$/ * @Transform /^([^"]+)$/
*/ */
public function lookupFixtureReference($string) { public function lookupFixtureReference($string)
if(preg_match('/^=>/', $string)) { {
if (preg_match('/^=>/', $string)) {
list($className, $identifier) = explode('.', preg_replace('/^=>/', '', $string), 2); list($className, $identifier) = explode('.', preg_replace('/^=>/', '', $string), 2);
$id = $this->fixtureFactory->getId($className, $identifier); $id = $this->fixtureFactory->getId($className, $identifier);
if(!$id) { if (!$id) {
throw new \InvalidArgumentException(sprintf( throw new \InvalidArgumentException(sprintf(
'Cannot resolve reference "%s", no matching fixture found', 'Cannot resolve reference "%s", no matching fixture found',
$string $string
@ -517,11 +564,12 @@ class FixtureContext extends BehatContext
/** /**
* @Given /^(?:(an|a|the) )"(?<type>[^"]*)" "(?<id>[^"]*)" was (?<mod>(created|last edited)) "(?<time>[^"]*)"$/ * @Given /^(?:(an|a|the) )"(?<type>[^"]*)" "(?<id>[^"]*)" was (?<mod>(created|last edited)) "(?<time>[^"]*)"$/
*/ */
public function aRecordWasLastEditedRelative($type, $id, $mod, $time) { public function aRecordWasLastEditedRelative($type, $id, $mod, $time)
{
$class = $this->convertTypeToClass($type); $class = $this->convertTypeToClass($type);
$fields = $this->prepareFixture($class, $id); $fields = $this->prepareFixture($class, $id);
$record = $this->fixtureFactory->createObject($class, $id, $fields); $record = $this->fixtureFactory->createObject($class, $id, $fields);
$date = date("Y-m-d H:i:s",strtotime($time)); $date = date("Y-m-d H:i:s", strtotime($time));
$table = $record->baseTable(); $table = $record->baseTable();
$field = ($mod == 'created') ? 'Created' : 'LastEdited'; $field = ($mod == 'created') ? 'Created' : 'LastEdited';
DB::prepared_query( DB::prepared_query(
@ -529,7 +577,7 @@ class FixtureContext extends BehatContext
[$date, $record->ID] [$date, $record->ID]
); );
// Support for Versioned extension, by checking for a "Live" stage // Support for Versioned extension, by checking for a "Live" stage
if(DB::get_schema()->hasTable($table . '_Live')) { if (DB::get_schema()->hasTable($table . '_Live')) {
DB::prepared_query( DB::prepared_query(
"UPDATE \"{$table}_Live\" SET \"{$field}\" = ? WHERE \"ID\" = ?", "UPDATE \"{$table}_Live\" SET \"{$field}\" = ? WHERE \"ID\" = ?",
[$date, $record->ID] [$date, $record->ID]
@ -545,26 +593,30 @@ class FixtureContext extends BehatContext
* @param array $data * @param array $data
* @return array Prepared $data with additional injected fields * @return array Prepared $data with additional injected fields
*/ */
protected function prepareFixture($class, $identifier, $data = array()) { protected function prepareFixture($class, $identifier, $data = array())
if($class == 'File' || is_subclass_of($class, 'File')) { {
if ($class == 'File' || is_subclass_of($class, 'File')) {
$data = $this->prepareAsset($class, $identifier, $data); $data = $this->prepareAsset($class, $identifier, $data);
} }
return $data; return $data;
} }
protected function prepareAsset($class, $identifier, $data = null) { protected function prepareAsset($class, $identifier, $data = null)
if(!$data) $data = array(); {
if (!$data) {
$data = array();
}
$relativeTargetPath = (isset($data['Filename'])) ? $data['Filename'] : $identifier; $relativeTargetPath = (isset($data['Filename'])) ? $data['Filename'] : $identifier;
$relativeTargetPath = preg_replace('/^' . ASSETS_DIR . '\/?/', '', $relativeTargetPath); $relativeTargetPath = preg_replace('/^' . ASSETS_DIR . '\/?/', '', $relativeTargetPath);
$sourcePath = $this->joinPaths($this->getFilesPath(), basename($relativeTargetPath)); $sourcePath = $this->joinPaths($this->getFilesPath(), basename($relativeTargetPath));
// Create file or folder on filesystem // Create file or folder on filesystem
if($class == 'Folder' || is_subclass_of($class, 'Folder')) { if ($class == 'Folder' || is_subclass_of($class, 'Folder')) {
$parent = \Folder::find_or_make($relativeTargetPath); $parent = \Folder::find_or_make($relativeTargetPath);
$data['ID'] = $parent->ID; $data['ID'] = $parent->ID;
} else { } else {
$parent = \Folder::find_or_make(dirname($relativeTargetPath)); $parent = \Folder::find_or_make(dirname($relativeTargetPath));
if(!file_exists($sourcePath)) { if (!file_exists($sourcePath)) {
throw new \InvalidArgumentException(sprintf( throw new \InvalidArgumentException(sprintf(
'Source file for "%s" cannot be found in "%s"', 'Source file for "%s" cannot be found in "%s"',
$relativeTargetPath, $relativeTargetPath,
@ -588,12 +640,12 @@ class FixtureContext extends BehatContext
$data['FileHash'] = $asset['Hash']; $data['FileHash'] = $asset['Hash'];
$data['FileVariant'] = $asset['Variant']; $data['FileVariant'] = $asset['Variant'];
} }
if(!isset($data['Name'])) { if (!isset($data['Name'])) {
$data['Name'] = basename($relativeTargetPath); $data['Name'] = basename($relativeTargetPath);
} }
// Save assets // Save assets
if(isset($data['FileFilename'])) { if (isset($data['FileFilename'])) {
$this->createdAssets[] = $data; $this->createdAssets[] = $data;
} }
@ -604,7 +656,8 @@ class FixtureContext extends BehatContext
* *
* @return AssetStore * @return AssetStore
*/ */
protected function getAssetStore() { protected function getAssetStore()
{
return singleton('AssetStore'); return singleton('AssetStore');
} }
@ -616,18 +669,19 @@ class FixtureContext extends BehatContext
* @param String * @param String
* @return String Class name * @return String Class name
*/ */
protected function convertTypeToClass($type) { protected function convertTypeToClass($type)
{
$type = trim($type); $type = trim($type);
// Try direct mapping // Try direct mapping
$class = str_replace(' ', '', ucwords($type)); $class = str_replace(' ', '', ucwords($type));
if(class_exists($class) && is_subclass_of($class, 'SilverStripe\\ORM\\DataObject')) { if (class_exists($class) && is_subclass_of($class, 'SilverStripe\\ORM\\DataObject')) {
return \ClassInfo::class_name($class); return \ClassInfo::class_name($class);
} }
// Fall back to singular names // Fall back to singular names
foreach(array_values(\ClassInfo::subclassesFor('SilverStripe\\ORM\\DataObject')) as $candidate) { foreach (array_values(\ClassInfo::subclassesFor('SilverStripe\\ORM\\DataObject')) as $candidate) {
if(strcasecmp(singleton($candidate)->singular_name(), $type) === 0) { if (strcasecmp(singleton($candidate)->singular_name(), $type) === 0) {
return $candidate; return $candidate;
} }
} }
@ -646,25 +700,31 @@ class FixtureContext extends BehatContext
* @param array $fields Map of field names or aliases to their values. * @param array $fields Map of field names or aliases to their values.
* @return array Map of actual object properties to their values. * @return array Map of actual object properties to their values.
*/ */
protected function convertFields($class, $fields) { protected function convertFields($class, $fields)
{
$labels = singleton($class)->fieldLabels(); $labels = singleton($class)->fieldLabels();
foreach($fields as $fieldName => $fieldVal) { foreach ($fields as $fieldName => $fieldVal) {
if($fieldLabelKey = array_search($fieldName, $labels)) { if ($fieldLabelKey = array_search($fieldName, $labels)) {
unset($fields[$fieldName]); unset($fields[$fieldName]);
$fields[$labels[$fieldLabelKey]] = $fieldVal; $fields[$labels[$fieldLabelKey]] = $fieldVal;
} }
} }
return $fields; return $fields;
} }
protected function joinPaths() { protected function joinPaths()
{
$args = func_get_args(); $args = func_get_args();
$paths = array(); $paths = array();
foreach($args as $arg) $paths = array_merge($paths, (array)$arg); foreach ($args as $arg) {
foreach($paths as &$path) $path = trim($path, '/'); $paths = array_merge($paths, (array)$arg);
if (substr($args[0], 0, 1) == '/') $paths[0] = '/' . $paths[0]; }
foreach ($paths as &$path) {
$path = trim($path, '/');
}
if (substr($args[0], 0, 1) == '/') {
$paths[0] = '/' . $paths[0];
}
return join('/', $paths); return join('/', $paths);
} }
} }

View File

@ -2,8 +2,8 @@
namespace SilverStripe\BehatExtension\Context\Initializer; namespace SilverStripe\BehatExtension\Context\Initializer;
use Behat\Behat\Context\Initializer\InitializerInterface, use Behat\Behat\Context\Initializer\InitializerInterface;
Behat\Behat\Context\ContextInterface; use Behat\Behat\Context\ContextInterface;
use SilverStripe\BehatExtension\Context\SilverStripeAwareContextInterface; use SilverStripe\BehatExtension\Context\SilverStripeAwareContextInterface;
@ -84,7 +84,7 @@ class SilverStripeAwareInitializer implements InitializerInterface
public function __destruct() public function __destruct()
{ {
// Add condition here as register_shutdown_function() also calls this in __construct() // Add condition here as register_shutdown_function() also calls this in __construct()
if($this->testSessionEnvironment) { if ($this->testSessionEnvironment) {
file_put_contents('php://stdout', "Killing test session environment..."); file_put_contents('php://stdout', "Killing test session environment...");
$this->testSessionEnvironment->endTestSession(); $this->testSessionEnvironment->endTestSession();
$this->testSessionEnvironment = null; $this->testSessionEnvironment = null;
@ -122,7 +122,9 @@ class SilverStripeAwareInitializer implements InitializerInterface
public function setAjaxSteps($ajaxSteps) public function setAjaxSteps($ajaxSteps)
{ {
if($ajaxSteps) $this->ajaxSteps = $ajaxSteps; if ($ajaxSteps) {
$this->ajaxSteps = $ajaxSteps;
}
} }
public function getAjaxSteps() public function getAjaxSteps()
@ -170,11 +172,13 @@ class SilverStripeAwareInitializer implements InitializerInterface
return $this->screenshotPath; return $this->screenshotPath;
} }
public function getRegionMap(){ public function getRegionMap()
{
return $this->regionMap; return $this->regionMap;
} }
public function setRegionMap($regionMap) { public function setRegionMap($regionMap)
{
$this->regionMap = $regionMap; $this->regionMap = $regionMap;
} }

View File

@ -8,8 +8,6 @@ use SilverStripe\ORM\DataObject;
use SilverStripe\Security\Group; use SilverStripe\Security\Group;
use SilverStripe\Security\Member; use SilverStripe\Security\Member;
// PHPUnit // PHPUnit
require_once BASE_PATH . '/vendor/phpunit/phpunit/src/Framework/Assert/Functions.php'; require_once BASE_PATH . '/vendor/phpunit/phpunit/src/Framework/Assert/Functions.php';
@ -70,7 +68,7 @@ class LoginContext extends BehatContext
* *
* @Given /^I am logged in with "([^"]*)" permissions$/ * @Given /^I am logged in with "([^"]*)" permissions$/
*/ */
function iAmLoggedInWithPermissions($permCode) public function iAmLoggedInWithPermissions($permCode)
{ {
if (!isset($this->cache_generatedMembers[$permCode])) { if (!isset($this->cache_generatedMembers[$permCode])) {
$group = Group::get()->filter('Title', "$permCode group")->first(); $group = Group::get()->filter('Title', "$permCode group")->first();
@ -132,8 +130,8 @@ class LoginContext extends BehatContext
// Try to find visible forms again on login page. // Try to find visible forms again on login page.
$visibleForm = null; $visibleForm = null;
foreach($forms as $form) { foreach ($forms as $form) {
if($form->isVisible() && $form->find('css', '[name=Email]')) { if ($form->isVisible() && $form->find('css', '[name=Email]')) {
$visibleForm = $form; $visibleForm = $form;
} }
} }

View File

@ -2,16 +2,16 @@
namespace SilverStripe\BehatExtension\Context; namespace SilverStripe\BehatExtension\Context;
use Behat\Behat\Context\Step, use Behat\Behat\Context\Step;
Behat\Behat\Event\FeatureEvent, use Behat\Behat\Event\FeatureEvent;
Behat\Behat\Event\ScenarioEvent, use Behat\Behat\Event\ScenarioEvent;
Behat\Behat\Event\SuiteEvent; use Behat\Behat\Event\SuiteEvent;
use Behat\Gherkin\Node\PyStringNode; use Behat\Gherkin\Node\PyStringNode;
use Behat\MinkExtension\Context\MinkContext; use Behat\MinkExtension\Context\MinkContext;
use Behat\Mink\Driver\GoutteDriver, use Behat\Mink\Driver\GoutteDriver;
Behat\Mink\Driver\Selenium2Driver, use Behat\Mink\Driver\Selenium2Driver;
Behat\Mink\Exception\UnsupportedDriverActionException, use Behat\Mink\Exception\UnsupportedDriverActionException;
Behat\Mink\Exception\ElementNotFoundException; use Behat\Mink\Exception\ElementNotFoundException;
use SilverStripe\BehatExtension\Context\SilverStripeAwareContextInterface; use SilverStripe\BehatExtension\Context\SilverStripeAwareContextInterface;
@ -72,61 +72,77 @@ class SilverStripeContext extends MinkContext implements SilverStripeAwareContex
* *
* @param array $parameters context parameters (set them up through behat.yml) * @param array $parameters context parameters (set them up through behat.yml)
*/ */
public function __construct(array $parameters) { public function __construct(array $parameters)
{
// Initialize your context here // Initialize your context here
$this->context = $parameters; $this->context = $parameters;
$this->testSessionEnvironment = new \TestSessionEnvironment(); $this->testSessionEnvironment = new \TestSessionEnvironment();
} }
public function setDatabase($databaseName) { public function setDatabase($databaseName)
{
$this->databaseName = $databaseName; $this->databaseName = $databaseName;
} }
public function setAjaxSteps($ajaxSteps) { public function setAjaxSteps($ajaxSteps)
if($ajaxSteps) $this->ajaxSteps = $ajaxSteps; {
if ($ajaxSteps) {
$this->ajaxSteps = $ajaxSteps;
}
} }
public function getAjaxSteps() { public function getAjaxSteps()
{
return $this->ajaxSteps; return $this->ajaxSteps;
} }
public function setAjaxTimeout($ajaxTimeout) { public function setAjaxTimeout($ajaxTimeout)
{
$this->ajaxTimeout = $ajaxTimeout; $this->ajaxTimeout = $ajaxTimeout;
} }
public function getAjaxTimeout() { public function getAjaxTimeout()
{
return $this->ajaxTimeout; return $this->ajaxTimeout;
} }
public function setAdminUrl($adminUrl) { public function setAdminUrl($adminUrl)
{
$this->adminUrl = $adminUrl; $this->adminUrl = $adminUrl;
} }
public function getAdminUrl() { public function getAdminUrl()
{
return $this->adminUrl; return $this->adminUrl;
} }
public function setLoginUrl($loginUrl) { public function setLoginUrl($loginUrl)
{
$this->loginUrl = $loginUrl; $this->loginUrl = $loginUrl;
} }
public function getLoginUrl() { public function getLoginUrl()
{
return $this->loginUrl; return $this->loginUrl;
} }
public function setScreenshotPath($screenshotPath) { public function setScreenshotPath($screenshotPath)
{
$this->screenshotPath = $screenshotPath; $this->screenshotPath = $screenshotPath;
} }
public function getScreenshotPath() { public function getScreenshotPath()
{
return $this->screenshotPath; return $this->screenshotPath;
} }
public function getRegionMap(){ public function getRegionMap()
{
return $this->regionMap; return $this->regionMap;
} }
public function setRegionMap($regionMap){ public function setRegionMap($regionMap)
{
$this->regionMap = $regionMap; $this->regionMap = $regionMap;
} }
@ -138,7 +154,8 @@ class SilverStripeContext extends MinkContext implements SilverStripeAwareContex
* @param String $region Region name or CSS selector * @param String $region Region name or CSS selector
* @return MinkElement|null * @return MinkElement|null
*/ */
public function getRegionObj($region) { public function getRegionObj($region)
{
// Try to find regions directly by CSS selector. // Try to find regions directly by CSS selector.
try { try {
$regionObj = $this->getSession()->getPage()->find( $regionObj = $this->getSession()->getPage()->find(
@ -146,35 +163,35 @@ class SilverStripeContext extends MinkContext implements SilverStripeAwareContex
// Escape CSS selector // Escape CSS selector
(false !== strpos($region, "'")) ? str_replace("'", "\'", $region) : $region (false !== strpos($region, "'")) ? str_replace("'", "\'", $region) : $region
); );
if($regionObj) { if ($regionObj) {
return $regionObj; return $regionObj;
} }
} catch(\Symfony\Component\CssSelector\Exception\SyntaxErrorException $e) { } catch (\Symfony\Component\CssSelector\Exception\SyntaxErrorException $e) {
// fall through to next case // fall through to next case
} }
// Fall back to region identified by data-title. // Fall back to region identified by data-title.
// Only apply if no double quotes exist in search string, // Only apply if no double quotes exist in search string,
// which would break the CSS selector. // which would break the CSS selector.
if(false === strpos($region, '"')) { if (false === strpos($region, '"')) {
$regionObj = $this->getSession()->getPage()->find( $regionObj = $this->getSession()->getPage()->find(
'css', 'css',
'[data-title="' . $region . '"]' '[data-title="' . $region . '"]'
); );
if($regionObj) { if ($regionObj) {
return $regionObj; return $regionObj;
} }
} }
// Look for named region // Look for named region
if(!$this->regionMap) { if (!$this->regionMap) {
throw new \LogicException("Cannot find 'region_map' in the behat.yml"); throw new \LogicException("Cannot find 'region_map' in the behat.yml");
} }
if(!array_key_exists($region, $this->regionMap)) { if (!array_key_exists($region, $this->regionMap)) {
throw new \LogicException("Cannot find the specified region in the behat.yml"); throw new \LogicException("Cannot find the specified region in the behat.yml");
} }
$regionObj = $this->getSession()->getPage()->find('css', $region); $regionObj = $this->getSession()->getPage()->find('css', $region);
if(!$regionObj) { if (!$regionObj) {
throw new ElementNotFoundException("Cannot find the specified region on the page"); throw new ElementNotFoundException("Cannot find the specified region on the page");
} }
@ -184,7 +201,8 @@ class SilverStripeContext extends MinkContext implements SilverStripeAwareContex
/** /**
* @BeforeScenario * @BeforeScenario
*/ */
public function before(ScenarioEvent $event) { public function before(ScenarioEvent $event)
{
if (!isset($this->databaseName)) { if (!isset($this->databaseName)) {
throw new \LogicException( throw new \LogicException(
'Context\'s $databaseName has to be set when implementing SilverStripeAwareContextInterface.' 'Context\'s $databaseName has to be set when implementing SilverStripeAwareContextInterface.'
@ -195,22 +213,22 @@ class SilverStripeContext extends MinkContext implements SilverStripeAwareContex
$this->testSessionEnvironment->startTestSession($state); $this->testSessionEnvironment->startTestSession($state);
// Optionally import database // Optionally import database
if(!empty($state['importDatabasePath'])) { if (!empty($state['importDatabasePath'])) {
$this->testSessionEnvironment->importDatabase( $this->testSessionEnvironment->importDatabase(
$state['importDatabasePath'], $state['importDatabasePath'],
!empty($state['requireDefaultRecords']) ? $state['requireDefaultRecords'] : false !empty($state['requireDefaultRecords']) ? $state['requireDefaultRecords'] : false
); );
} else if(!empty($state['requireDefaultRecords']) && $state['requireDefaultRecords']) { } elseif (!empty($state['requireDefaultRecords']) && $state['requireDefaultRecords']) {
$this->testSessionEnvironment->requireDefaultRecords(); $this->testSessionEnvironment->requireDefaultRecords();
} }
// Fixtures // Fixtures
$fixtureFile = (!empty($state['fixture'])) ? $state['fixture'] : null; $fixtureFile = (!empty($state['fixture'])) ? $state['fixture'] : null;
if($fixtureFile) { if ($fixtureFile) {
$this->testSessionEnvironment->loadFixtureIntoDb($fixtureFile); $this->testSessionEnvironment->loadFixtureIntoDb($fixtureFile);
} }
if($screenSize = getenv('BEHAT_SCREEN_SIZE')) { if ($screenSize = getenv('BEHAT_SCREEN_SIZE')) {
list($screenWidth, $screenHeight) = explode('x', $screenSize); list($screenWidth, $screenHeight) = explode('x', $screenSize);
$this->getSession()->resizeWindow((int)$screenWidth, (int)$screenHeight); $this->getSession()->resizeWindow((int)$screenWidth, (int)$screenHeight);
} else { } else {
@ -224,7 +242,8 @@ class SilverStripeContext extends MinkContext implements SilverStripeAwareContex
* *
* @return array * @return array
*/ */
public function getTestSessionState() { public function getTestSessionState()
{
$extraParams = array(); $extraParams = array();
parse_str(getenv('TESTSESSION_PARAMS'), $extraParams); parse_str(getenv('TESTSESSION_PARAMS'), $extraParams);
return array_merge( return array_merge(
@ -242,7 +261,8 @@ class SilverStripeContext extends MinkContext implements SilverStripeAwareContex
* @param $url * @param $url
* @return array|mixed Parsed URL * @return array|mixed Parsed URL
*/ */
public function parseUrl($url) { public function parseUrl($url)
{
$url = parse_url($url); $url = parse_url($url);
$url['vars'] = array(); $url['vars'] = array();
if (!isset($url['fragment'])) { if (!isset($url['fragment'])) {
@ -263,7 +283,8 @@ class SilverStripeContext extends MinkContext implements SilverStripeAwareContex
* @param $url string URL to compare to current URL * @param $url string URL to compare to current URL
* @return boolean Returns true if the current URL is close enough to the given URL, false otherwise. * @return boolean Returns true if the current URL is close enough to the given URL, false otherwise.
*/ */
public function isCurrentUrlSimilarTo($url) { public function isCurrentUrlSimilarTo($url)
{
$current = $this->parseUrl($this->getSession()->getCurrentUrl()); $current = $this->parseUrl($this->getSession()->getCurrentUrl());
$test = $this->parseUrl($url); $test = $this->parseUrl($url);
@ -291,7 +312,8 @@ class SilverStripeContext extends MinkContext implements SilverStripeAwareContex
* *
* @return string * @return string
*/ */
public function getBaseUrl() { public function getBaseUrl()
{
return $this->getMinkParameter('base_url') ?: ''; return $this->getMinkParameter('base_url') ?: '';
} }
@ -304,13 +326,14 @@ class SilverStripeContext extends MinkContext implements SilverStripeAwareContex
* @return string * @return string
* @throws \InvalidArgumentException * @throws \InvalidArgumentException
*/ */
public function joinUrlParts() { public function joinUrlParts()
{
if (0 === func_num_args()) { if (0 === func_num_args()) {
throw new \InvalidArgumentException('Need at least one argument'); throw new \InvalidArgumentException('Need at least one argument');
} }
$parts = func_get_args(); $parts = func_get_args();
$trimSlashes = function(&$part) { $trimSlashes = function (&$part) {
$part = trim($part, '/'); $part = trim($part, '/');
}; };
array_walk($parts, $trimSlashes); array_walk($parts, $trimSlashes);
@ -318,12 +341,12 @@ class SilverStripeContext extends MinkContext implements SilverStripeAwareContex
return implode('/', $parts); return implode('/', $parts);
} }
public function canIntercept() { public function canIntercept()
{
$driver = $this->getSession()->getDriver(); $driver = $this->getSession()->getDriver();
if ($driver instanceof GoutteDriver) { if ($driver instanceof GoutteDriver) {
return true; return true;
} } else {
else {
if ($driver instanceof Selenium2Driver) { if ($driver instanceof Selenium2Driver) {
return false; return false;
} }
@ -336,7 +359,8 @@ class SilverStripeContext extends MinkContext implements SilverStripeAwareContex
/** /**
* @Given /^(.*) without redirection$/ * @Given /^(.*) without redirection$/
*/ */
public function theRedirectionsAreIntercepted($step) { public function theRedirectionsAreIntercepted($step)
{
if ($this->canIntercept()) { if ($this->canIntercept()) {
$this->getSession()->getDriver()->getClient()->followRedirects(false); $this->getSession()->getDriver()->getClient()->followRedirects(false);
} }
@ -348,39 +372,51 @@ class SilverStripeContext extends MinkContext implements SilverStripeAwareContex
* Fills in form field with specified id|name|label|value. * Fills in form field with specified id|name|label|value.
* Overwritten to select the first *visible* element, see https://github.com/Behat/Mink/issues/311 * Overwritten to select the first *visible* element, see https://github.com/Behat/Mink/issues/311
*/ */
public function fillField($field, $value) { public function fillField($field, $value)
{
$value = $this->fixStepArgument($value); $value = $this->fixStepArgument($value);
$fields = $this->getSession()->getPage()->findAll('named', array( $fields = $this->getSession()->getPage()->findAll('named', array(
'field', $this->getSession()->getSelectorsHandler()->xpathLiteral($field) 'field', $this->getSession()->getSelectorsHandler()->xpathLiteral($field)
)); ));
if($fields) foreach($fields as $f) { if ($fields) {
if($f->isVisible()) { foreach ($fields as $f) {
if ($f->isVisible()) {
$f->setValue($value); $f->setValue($value);
return; return;
} }
} }
}
throw new ElementNotFoundException( throw new ElementNotFoundException(
$this->getSession(), 'form field', 'id|name|label|value', $field $this->getSession(),
'form field',
'id|name|label|value',
$field
); );
} }
/** /**
* Overwritten to click the first *visable* link the DOM. * Overwritten to click the first *visable* link the DOM.
*/ */
public function clickLink($link) { public function clickLink($link)
{
$link = $this->fixStepArgument($link); $link = $this->fixStepArgument($link);
$links = $this->getSession()->getPage()->findAll('named', array( $links = $this->getSession()->getPage()->findAll('named', array(
'link', $this->getSession()->getSelectorsHandler()->xpathLiteral($link) 'link', $this->getSession()->getSelectorsHandler()->xpathLiteral($link)
)); ));
if($links) foreach($links as $l) { if ($links) {
if($l->isVisible()) { foreach ($links as $l) {
if ($l->isVisible()) {
$l->click(); $l->click();
return; return;
} }
} }
}
throw new ElementNotFoundException( throw new ElementNotFoundException(
$this->getSession(), 'link', 'id|name|label|value', $link $this->getSession(),
'link',
'id|name|label|value',
$link
); );
} }
@ -392,15 +428,16 @@ class SilverStripeContext extends MinkContext implements SilverStripeAwareContex
* *
* @Given /^the current date is "([^"]*)"$/ * @Given /^the current date is "([^"]*)"$/
*/ */
public function givenTheCurrentDateIs($date) { public function givenTheCurrentDateIs($date)
{
$newDatetime = \DateTime::createFromFormat('Y-m-d', $date); $newDatetime = \DateTime::createFromFormat('Y-m-d', $date);
if(!$newDatetime) { if (!$newDatetime) {
throw new InvalidArgumentException(sprintf('Invalid date format: %s (requires "Y-m-d")', $date)); throw new InvalidArgumentException(sprintf('Invalid date format: %s (requires "Y-m-d")', $date));
} }
$state = $this->testSessionEnvironment->getState(); $state = $this->testSessionEnvironment->getState();
$oldDatetime = \DateTime::createFromFormat('Y-m-d H:i:s', isset($state->datetime) ? $state->datetime : null); $oldDatetime = \DateTime::createFromFormat('Y-m-d H:i:s', isset($state->datetime) ? $state->datetime : null);
if($oldDatetime) { if ($oldDatetime) {
$newDatetime->setTime($oldDatetime->format('H'), $oldDatetime->format('i'), $oldDatetime->format('s')); $newDatetime->setTime($oldDatetime->format('H'), $oldDatetime->format('i'), $oldDatetime->format('s'));
} }
$state->datetime = $newDatetime->format('Y-m-d H:i:s'); $state->datetime = $newDatetime->format('Y-m-d H:i:s');
@ -415,15 +452,16 @@ class SilverStripeContext extends MinkContext implements SilverStripeAwareContex
* *
* @Given /^the current time is "([^"]*)"$/ * @Given /^the current time is "([^"]*)"$/
*/ */
public function givenTheCurrentTimeIs($time) { public function givenTheCurrentTimeIs($time)
{
$newDatetime = \DateTime::createFromFormat('H:i:s', $date); $newDatetime = \DateTime::createFromFormat('H:i:s', $date);
if(!$newDatetime) { if (!$newDatetime) {
throw new InvalidArgumentException(sprintf('Invalid date format: %s (requires "H:i:s")', $date)); throw new InvalidArgumentException(sprintf('Invalid date format: %s (requires "H:i:s")', $date));
} }
$state = $this->testSessionEnvironment->getState(); $state = $this->testSessionEnvironment->getState();
$oldDatetime = \DateTime::createFromFormat('Y-m-d H:i:s', isset($state->datetime) ? $state->datetime : null); $oldDatetime = \DateTime::createFromFormat('Y-m-d H:i:s', isset($state->datetime) ? $state->datetime : null);
if($oldDatetime) { if ($oldDatetime) {
$newDatetime->setDate($oldDatetime->format('Y'), $oldDatetime->format('m'), $oldDatetime->format('d')); $newDatetime->setDate($oldDatetime->format('Y'), $oldDatetime->format('m'), $oldDatetime->format('d'));
} }
$state->datetime = $newDatetime->format('Y-m-d H:i:s'); $state->datetime = $newDatetime->format('Y-m-d H:i:s');
@ -435,7 +473,8 @@ class SilverStripeContext extends MinkContext implements SilverStripeAwareContex
* *
* @override /^(?:|I )select "(?P<option>(?:[^"]|\\")*)" from "(?P<select>(?:[^"]|\\")*)"$/ * @override /^(?:|I )select "(?P<option>(?:[^"]|\\")*)" from "(?P<select>(?:[^"]|\\")*)"$/
*/ */
public function selectOption($select, $option) { public function selectOption($select, $option)
{
// Find field // Find field
$field = $this $field = $this
->getSession() ->getSession()
@ -443,7 +482,7 @@ class SilverStripeContext extends MinkContext implements SilverStripeAwareContex
->findField($this->fixStepArgument($select)); ->findField($this->fixStepArgument($select));
// If field is visible then select it as per normal // If field is visible then select it as per normal
if($field && $field->isVisible()) { if ($field && $field->isVisible()) {
parent::selectOption($select, $option); parent::selectOption($select, $option);
} else { } else {
$this->selectOptionWithJavascript($select, $option); $this->selectOptionWithJavascript($select, $option);
@ -457,7 +496,8 @@ class SilverStripeContext extends MinkContext implements SilverStripeAwareContex
* *
* @When /^(?:|I )select "(?P<option>(?:[^"]|\\")*)" from "(?P<select>(?:[^"]|\\")*)" with javascript$/ * @When /^(?:|I )select "(?P<option>(?:[^"]|\\")*)" from "(?P<select>(?:[^"]|\\")*)" with javascript$/
*/ */
public function selectOptionWithJavascript($select, $option) { public function selectOptionWithJavascript($select, $option)
{
$select = $this->fixStepArgument($select); $select = $this->fixStepArgument($select);
$option = $this->fixStepArgument($option); $option = $this->fixStepArgument($option);
$page = $this->getSession()->getPage(); $page = $this->getSession()->getPage();
@ -479,8 +519,10 @@ class SilverStripeContext extends MinkContext implements SilverStripeAwareContex
// Merge new option in with old handling both multiselect and single select // Merge new option in with old handling both multiselect and single select
$value = $field->getValue(); $value = $field->getValue();
$newValue = $opt->getAttribute('value'); $newValue = $opt->getAttribute('value');
if(is_array($value)) { if (is_array($value)) {
if(!in_array($newValue, $value)) $value[] = $newValue; if (!in_array($newValue, $value)) {
$value[] = $newValue;
}
} else { } else {
$value = $newValue; $value = $newValue;
} }
@ -499,5 +541,4 @@ class SilverStripeContext extends MinkContext implements SilverStripeAwareContex
EOS; EOS;
$this->getSession()->getDriver()->executeScript($script); $this->getSession()->getDriver()->executeScript($script);
} }
} }

View File

@ -2,10 +2,10 @@
namespace SilverStripe\BehatExtension; namespace SilverStripe\BehatExtension;
use Symfony\Component\Config\FileLocator, use Symfony\Component\Config\FileLocator;
Symfony\Component\DependencyInjection\ContainerBuilder, use Symfony\Component\DependencyInjection\ContainerBuilder;
Symfony\Component\DependencyInjection\Loader\YamlFileLoader, use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Behat\Behat\Extension\ExtensionInterface; use Behat\Behat\Extension\ExtensionInterface;
@ -41,7 +41,8 @@ class Extension implements ExtensionInterface
$loader->load('silverstripe.yml'); $loader->load('silverstripe.yml');
$behatBasePath = $container->getParameter('behat.paths.base'); $behatBasePath = $container->getParameter('behat.paths.base');
$config['framework_path'] = realpath(sprintf('%s%s%s', $config['framework_path'] = realpath(sprintf(
'%s%s%s',
rtrim($behatBasePath, DIRECTORY_SEPARATOR), rtrim($behatBasePath, DIRECTORY_SEPARATOR),
DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR,
ltrim($config['framework_path'], DIRECTORY_SEPARATOR) ltrim($config['framework_path'], DIRECTORY_SEPARATOR)
@ -78,7 +79,7 @@ class Extension implements ExtensionInterface
* *
* @param ArrayNodeDefinition $builder * @param ArrayNodeDefinition $builder
*/ */
function getConfig(ArrayNodeDefinition $builder) public function getConfig(ArrayNodeDefinition $builder)
{ {
$builder-> $builder->
children()-> children()->

View File

@ -16,5 +16,4 @@ class MinkExtension extends \Behat\MinkExtension\Extension
parent::getCompilerPasses() parent::getCompilerPasses()
); );
} }
} }

View File

@ -7,14 +7,16 @@ namespace SilverStripe\BehatExtension\Utility;
* but saves emails in {@link TestSessionEnvironment} * but saves emails in {@link TestSessionEnvironment}
* to share the state between PHP calls (CLI vs. browser). * to share the state between PHP calls (CLI vs. browser).
*/ */
class TestMailer extends \Mailer { class TestMailer extends \Mailer
{
/** /**
* @var TestSessionEnvironment * @var TestSessionEnvironment
*/ */
protected $testSessionEnvironment; protected $testSessionEnvironment;
public function __construct() { public function __construct()
{
$this->testSessionEnvironment = \Injector::inst()->get('TestSessionEnvironment'); $this->testSessionEnvironment = \Injector::inst()->get('TestSessionEnvironment');
} }
@ -22,7 +24,8 @@ class TestMailer extends \Mailer {
* Send a plain-text email. * Send a plain-text email.
* TestMailer will merely record that the email was asked to be sent, without sending anything. * TestMailer will merely record that the email was asked to be sent, without sending anything.
*/ */
public function sendPlain($to, $from, $subject, $plainContent, $attachedFiles = false, $customHeaders = false) { public function sendPlain($to, $from, $subject, $plainContent, $attachedFiles = false, $customHeaders = false)
{
$this->saveEmail(array( $this->saveEmail(array(
'Type' => 'plain', 'Type' => 'plain',
'To' => $to, 'To' => $to,
@ -41,8 +44,16 @@ class TestMailer extends \Mailer {
* Send a multi-part HTML email * Send a multi-part HTML email
* TestMailer will merely record that the email was asked to be sent, without sending anything. * TestMailer will merely record that the email was asked to be sent, without sending anything.
*/ */
public function sendHTML($to, $from, $subject, $htmlContent, $attachedFiles = false, $customHeaders = false, public function sendHTML(
$plainContent = false, $inlineImages = false) { $to,
$from,
$subject,
$htmlContent,
$attachedFiles = false,
$customHeaders = false,
$plainContent = false,
$inlineImages = false
) {
$this->saveEmail(array( $this->saveEmail(array(
'Type' => 'html', 'Type' => 'html',
@ -61,9 +72,12 @@ class TestMailer extends \Mailer {
/** /**
* Clear the log of emails sent * Clear the log of emails sent
*/ */
public function clearEmails() { public function clearEmails()
{
$state = $this->testSessionEnvironment->getState(); $state = $this->testSessionEnvironment->getState();
if(isset($state->emails)) unset($state->emails); if (isset($state->emails)) {
unset($state->emails);
}
$this->testSessionEnvironment->applyState($state); $this->testSessionEnvironment->applyState($state);
} }
@ -78,7 +92,8 @@ class TestMailer extends \Mailer {
* @return array Contains the keys: 'type', 'to', 'from', 'subject', 'content', 'plainContent', 'attachedFiles', * @return array Contains the keys: 'type', 'to', 'from', 'subject', 'content', 'plainContent', 'attachedFiles',
* 'customHeaders', 'htmlContent', 'inlineImages' * 'customHeaders', 'htmlContent', 'inlineImages'
*/ */
public function findEmail($to = null, $from = null, $subject = null, $content = null) { public function findEmail($to = null, $from = null, $subject = null, $content = null)
{
$matches = $this->findEmails($to, $from, $subject, $content); $matches = $this->findEmails($to, $from, $subject, $content);
//got the count of matches emails //got the count of matches emails
$emailCount = count($matches); $emailCount = count($matches);
@ -97,34 +112,46 @@ class TestMailer extends \Mailer {
* @return array Contains the keys: 'type', 'to', 'from', 'subject', 'content', 'plainContent', 'attachedFiles', * @return array Contains the keys: 'type', 'to', 'from', 'subject', 'content', 'plainContent', 'attachedFiles',
* 'customHeaders', 'htmlContent', 'inlineImages' * 'customHeaders', 'htmlContent', 'inlineImages'
*/ */
public function findEmails($to = null, $from = null, $subject = null, $content = null) { public function findEmails($to = null, $from = null, $subject = null, $content = null)
{
$matches = array(); $matches = array();
$args = func_get_args(); $args = func_get_args();
$state = $this->testSessionEnvironment->getState(); $state = $this->testSessionEnvironment->getState();
$emails = isset($state->emails) ? $state->emails : array(); $emails = isset($state->emails) ? $state->emails : array();
foreach($emails as $email) { foreach ($emails as $email) {
$matched = true; $matched = true;
foreach(array('To','From','Subject','Content') as $i => $field) { foreach (array('To', 'From', 'Subject', 'Content') as $i => $field) {
if(!isset($email->$field)) continue; if (!isset($email->$field)) {
continue;
}
$value = (isset($args[$i])) ? $args[$i] : null; $value = (isset($args[$i])) ? $args[$i] : null;
if($value) { if ($value) {
if($value[0] == '/') $matched = preg_match($value, $email->$field); if ($value[0] == '/') {
else $matched = ($value == $email->$field); $matched = preg_match($value, $email->$field);
if(!$matched) break; } else {
$matched = ($value == $email->$field);
}
if (!$matched) {
break;
} }
} }
if($matched) $matches[] = $email; }
if ($matched) {
$matches[] = $email;
}
} }
return $matches; return $matches;
} }
protected function saveEmail($data) { protected function saveEmail($data)
{
$state = $this->testSessionEnvironment->getState(); $state = $this->testSessionEnvironment->getState();
if(!isset($state->emails)) $state->emails = array(); if (!isset($state->emails)) {
$state->emails = array();
}
$state->emails[] = array_filter($data); $state->emails[] = array_filter($data);
$this->testSessionEnvironment->applyState($state); $this->testSessionEnvironment->applyState($state);
} }
} }

View File

@ -1,10 +1,11 @@
<?php <?php
namespace SilverStripe\BehatExtension\Tests; namespace SilverStripe\BehatExtension\Tests;
use SilverStripe\BehatExtension\Context\SilverStripeContext, use SilverStripe\BehatExtension\Context\SilverStripeContext;
Behat\Mink\Mink; use Behat\Mink\Mink;
class SilverStripeContextTest extends \PHPUnit_Framework_TestCase { class SilverStripeContextTest extends \PHPUnit_Framework_TestCase
{
protected $backupGlobals = false; protected $backupGlobals = false;
@ -12,7 +13,8 @@ class SilverStripeContextTest extends \PHPUnit_Framework_TestCase {
* @expectedException \LogicException * @expectedException \LogicException
* @expectedExceptionMessage Cannot find 'region_map' in the behat.yml * @expectedExceptionMessage Cannot find 'region_map' in the behat.yml
*/ */
public function testGetRegionObjThrowsExceptionOnUnknownSelector() { public function testGetRegionObjThrowsExceptionOnUnknownSelector()
{
$context = $this->getContextMock(); $context = $this->getContextMock();
$context->getRegionObj('.unknown'); $context->getRegionObj('.unknown');
} }
@ -21,13 +23,15 @@ class SilverStripeContextTest extends \PHPUnit_Framework_TestCase {
* @expectedException \LogicException * @expectedException \LogicException
* @expectedExceptionMessage Cannot find the specified region in the behat.yml * @expectedExceptionMessage Cannot find the specified region in the behat.yml
*/ */
public function testGetRegionObjThrowsExceptionOnUnknownRegion() { public function testGetRegionObjThrowsExceptionOnUnknownRegion()
{
$context = $this->getContextMock(); $context = $this->getContextMock();
$context->setRegionMap(array('MyRegion' => '.my-region')); $context->setRegionMap(array('MyRegion' => '.my-region'));
$context->getRegionObj('.unknown'); $context->getRegionObj('.unknown');
} }
public function testGetRegionObjFindsBySelector() { public function testGetRegionObjFindsBySelector()
{
$context = $this->getContextMock(); $context = $this->getContextMock();
$context->getSession()->getPage() $context->getSession()->getPage()
->expects($this->any()) ->expects($this->any())
@ -37,13 +41,14 @@ class SilverStripeContextTest extends \PHPUnit_Framework_TestCase {
$this->assertNotNull($obj); $this->assertNotNull($obj);
} }
public function testGetRegionObjFindsByRegion() { public function testGetRegionObjFindsByRegion()
{
$context = $this->getContextMock(); $context = $this->getContextMock();
$el = $this->getElementMock(); $el = $this->getElementMock();
$context->getSession()->getPage() $context->getSession()->getPage()
->expects($this->any()) ->expects($this->any())
->method('find') ->method('find')
->will($this->returnCallback(function($type, $selector) use ($el) { ->will($this->returnCallback(function ($type, $selector) use ($el) {
return ($selector == '.my-region') ? $el : null; return ($selector == '.my-region') ? $el : null;
})); }));
$context->setRegionMap(array('MyRegion' => '.my-asdf')); $context->setRegionMap(array('MyRegion' => '.my-asdf'));
@ -51,7 +56,8 @@ class SilverStripeContextTest extends \PHPUnit_Framework_TestCase {
$this->assertNotNull($obj); $this->assertNotNull($obj);
} }
protected function getContextMock() { protected function getContextMock()
{
$pageMock = $this->getMockBuilder('Behat\Mink\Element\DocumentElement') $pageMock = $this->getMockBuilder('Behat\Mink\Element\DocumentElement')
->disableOriginalConstructor() ->disableOriginalConstructor()
->setMethods(array('find')) ->setMethods(array('find'))
@ -75,7 +81,8 @@ class SilverStripeContextTest extends \PHPUnit_Framework_TestCase {
return $context; return $context;
} }
protected function getElementMock() { protected function getElementMock()
{
return $this->getMockBuilder('Behat\Mink\Element\Element') return $this->getMockBuilder('Behat\Mink\Element\Element')
->disableOriginalConstructor() ->disableOriginalConstructor()
->getMock(); ->getMock();

View File

@ -1,5 +1,7 @@
<?php <?php
$frameworkPath = __DIR__ . '/../framework'; $frameworkPath = __DIR__ . '/../framework';
$frameworkDir = basename($frameworkPath); $frameworkDir = basename($frameworkPath);
if(!defined('BASE_PATH')) define('BASE_PATH', dirname($frameworkPath)); if (!defined('BASE_PATH')) {
define('BASE_PATH', dirname($frameworkPath));
}
require_once $frameworkPath . '/core/Core.php'; require_once $frameworkPath . '/core/Core.php';