mirror of
https://github.com/silverstripe/silverstripe-behat-extension
synced 2024-10-22 17:05:32 +02:00
NEW LocatorProcessor and ClassGuesser to manage modules
Important for running behat tests per-module, while stil keeping parameters like "base_url" configurable without modifying behat.yml files in the modules own source code.
This commit is contained in:
parent
046350fcce
commit
feba280470
60
README.md
60
README.md
@ -57,10 +57,7 @@ And get the latest Selenium2 server (requires Java):
|
||||
As a convention, SilverStripe Behat tests live in a `tests/behat` subfolder
|
||||
of your module. You can create it with the following command:
|
||||
|
||||
cd mymodule
|
||||
mkdir -p tests/behat
|
||||
cd tests/behat
|
||||
../../../vendor/bin/behat --init
|
||||
mkdir -p mymodule/tests/behat/features/bootstrap/MyModule/Test/Behaviour
|
||||
|
||||
### FeatureContext
|
||||
|
||||
@ -69,10 +66,10 @@ here as well. The only major difference is the base class from which
|
||||
to extend your own `FeatureContext`: It should be `SilverStripeContext`
|
||||
rather than `BehatContext`.
|
||||
|
||||
Example: FeatureContext.php
|
||||
Example: mymodule/tests/behat/features/bootstrap/MyModule/Test/Behaviour/FeatureContext.php
|
||||
|
||||
<?php
|
||||
namespace SilverStripe\MyModule\Test\Behaviour;
|
||||
namespace MyModule\Test\Behaviour;
|
||||
|
||||
use SilverStripe\BehatExtension\Context\SilverStripeContext,
|
||||
SilverStripe\BehatExtension\Context\BasicContext,
|
||||
@ -94,7 +91,7 @@ Example: FeatureContext.php
|
||||
|
||||
### behat.yml
|
||||
|
||||
Create a `mymodule/tests/behat/behat.yml` file, and add the template below.
|
||||
Create a `behat.yml` file in the project root, and add the template below.
|
||||
|
||||
TODO: Move to auto-loaded configuration
|
||||
|
||||
@ -134,23 +131,6 @@ of a failed step. It defaults to whatever is returned by PHP's `sys_get_temp_dir
|
||||
Screenshot names within that directory consist of feature file filename and line
|
||||
number that failed.
|
||||
|
||||
### Additional profiles
|
||||
|
||||
By default, `MinkExtension` is using `FirefoxDriver`.
|
||||
Let's say you want to user `ChromeDriver` too.
|
||||
|
||||
You can either override the `selenium2` setting in default profile or add another
|
||||
profile that can be run using `bin/behat --profile=PROFILE_NAME`, where `PROFILE_NAME`
|
||||
could be `chrome`.
|
||||
|
||||
chrome:
|
||||
extensions:
|
||||
Behat\MinkExtension\Extension:
|
||||
selenium2:
|
||||
capabilities:
|
||||
browserName: chrome
|
||||
version: ANY
|
||||
|
||||
## Usage
|
||||
|
||||
### Starting the selenium server
|
||||
@ -171,14 +151,11 @@ You will have Behat binary located in `bin` directory in your project root (or w
|
||||
By default, Behat will use Selenium2 driver.
|
||||
Selenium will also try to use chrome browser. Refer to `behat.yml` for details.
|
||||
|
||||
# This will run all feature tests located in `features` directory
|
||||
vendor/bin/behat --config mymodule/tests/behat/behat.yml
|
||||
# Run all "mymodule" tests
|
||||
vendor/bin/behat @mymodule
|
||||
|
||||
# This will run all feature tests using chrome profile
|
||||
vendor/behat --config mymodule/tests/behat/behat.yml --profile=chrome
|
||||
|
||||
# This will run a specific feature test
|
||||
vendor/behat --config mymodule/tests/behat/behat.yml mymodule/tests/behat/features/my-steps.feature
|
||||
# Run a specific feature test
|
||||
vendor/behat @mymodule/my-steps.feature
|
||||
|
||||
### Available Step Definitions
|
||||
|
||||
@ -186,7 +163,7 @@ The extension comes with several `BehatContext` subclasses come with some extra
|
||||
Some of them are just helpful in general website testing, other's are specific to SilverStripe.
|
||||
To find out all available steps (and the files they are defined in), run the following:
|
||||
|
||||
vendor/bin/behat --config mymodule/tests/behat/behat.yml --definitions=i
|
||||
vendor/bin/behat @mymodule --definitions=i
|
||||
|
||||
Note: There are more specific step definitions in the SilverStripe `framework` module
|
||||
for interacting with the CMS interfaces (see `framework/tests/behat/features/bootstrap`).
|
||||
@ -243,6 +220,25 @@ The module runner empties the database before each scenario tagged with
|
||||
`@database-defaults` and populates it with default records (usually a set of
|
||||
default pages).
|
||||
|
||||
## Howto
|
||||
|
||||
### Additional profiles
|
||||
|
||||
By default, `MinkExtension` is using `FirefoxDriver`.
|
||||
Let's say you want to use `ChromeDriver` too.
|
||||
|
||||
You can either override the `selenium2` setting in default profile or add another
|
||||
profile that can be run using `bin/behat --profile=PROFILE_NAME`, where `PROFILE_NAME`
|
||||
could be `chrome`.
|
||||
|
||||
chrome:
|
||||
extensions:
|
||||
Behat\MinkExtension\Extension:
|
||||
selenium2:
|
||||
capabilities:
|
||||
browserName: chrome
|
||||
version: ANY
|
||||
|
||||
## FAQ
|
||||
|
||||
### Why does the module need to know about the framework path on the filesystem?
|
||||
|
@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\BehatExtension\Console\Processor;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface,
|
||||
Symfony\Component\Console\Command\Command,
|
||||
Symfony\Component\Console\Input\InputArgument,
|
||||
Symfony\Component\Console\Input\InputInterface,
|
||||
Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
use Behat\Behat\Console\Processor\LocatorProcessor as BaseProcessor;
|
||||
|
||||
/**
|
||||
* Path locator processor.
|
||||
*/
|
||||
class LocatorProcessor extends BaseProcessor
|
||||
{
|
||||
private $container;
|
||||
|
||||
/**
|
||||
* Constructs processor.
|
||||
*
|
||||
* @param ContainerInterface $container Container instance
|
||||
*/
|
||||
public function __construct(ContainerInterface $container)
|
||||
{
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures command to be able to process it later.
|
||||
*
|
||||
* @param Command $command
|
||||
*/
|
||||
public function configure(Command $command)
|
||||
{
|
||||
$command->addArgument('features', InputArgument::OPTIONAL,
|
||||
"Feature(s) to run. Could be:".
|
||||
"\n- a dir (<comment>src/to/module/Features/</comment>), " .
|
||||
"\n- a feature (<comment>src/to/module/Features/*.feature</comment>), " .
|
||||
"\n- a scenario at specific line (<comment>src/to/module/Features/*.feature:10</comment>). " .
|
||||
"\n- Also, you can use short module notation (<comment>@moduleName/*.feature:10</comment>)"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes data from container and console input.
|
||||
*
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function process(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
// Bootstrap SS so we can use module listing
|
||||
$frameworkPath = $this->container->getParameter('behat.silverstripe_extension.framework_path');
|
||||
$_GET['flush'] = 1;
|
||||
require_once $frameworkPath . '/core/Core.php';
|
||||
unset($_GET['flush']);
|
||||
|
||||
$featuresPath = $input->getArgument('features');
|
||||
$pathSuffix = $this->container->getParameter('behat.silverstripe_extension.context.path_suffix');
|
||||
|
||||
$currentModuleName = null;
|
||||
$modules = \SS_ClassLoader::instance()->getManifest()->getModules();
|
||||
|
||||
// get module specified in behat.yml
|
||||
$currentModuleName = $this->container->getParameter('behat.silverstripe_extension.module');
|
||||
|
||||
// get module from short notation if path starts from @
|
||||
if ($featuresPath && preg_match('/^\@([^\/\\\\]+)(.*)$/', $featuresPath, $matches)) {
|
||||
$currentModuleName = $matches[1];
|
||||
// TODO Replace with proper module loader once AJShort's changes are merged into core
|
||||
$currentModulePath = $modules[$currentModuleName];
|
||||
$featuresPath = str_replace(
|
||||
'@'.$currentModuleName,
|
||||
$currentModulePath.DIRECTORY_SEPARATOR.$pathSuffix,
|
||||
$featuresPath
|
||||
);
|
||||
// get module from provided features path
|
||||
} elseif (!$currentModuleName && $featuresPath) {
|
||||
$path = realpath(preg_replace('/\.feature\:.*$/', '.feature', $featuresPath));
|
||||
foreach ($modules as $moduleName => $modulePath) {
|
||||
if (false !== strpos($path, realpath($modulePath))) {
|
||||
$currentModuleName = $moduleName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// if module is configured for profile and feature provided
|
||||
} elseif ($currentModuleName && $featuresPath) {
|
||||
$currentModulePath = $modules[$currentModuleName];
|
||||
$featuresPath = $currentModulePath.DIRECTORY_SEPARATOR.$pathSuffix.DIRECTORY_SEPARATOR.$featuresPath;
|
||||
}
|
||||
|
||||
if ($currentModuleName) {
|
||||
$this->container
|
||||
->get('behat.silverstripe_extension.context.class_guesser')
|
||||
->setModuleNamespace(ucfirst($currentModuleName));
|
||||
}
|
||||
|
||||
if (!$featuresPath) {
|
||||
$featuresPath = $this->container->getParameter('behat.paths.features');
|
||||
}
|
||||
|
||||
$this->container
|
||||
->get('behat.console.command')
|
||||
->setFeaturesPaths($featuresPath ? array($featuresPath) : array());
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\BehatExtension\Context\ClassGuesser;
|
||||
|
||||
use Behat\Behat\Context\ClassGuesser\ClassGuesserInterface;
|
||||
|
||||
/**
|
||||
* Module context class guesser.
|
||||
* Provides module context class if found.
|
||||
*/
|
||||
class ModuleContextClassGuesser implements ClassGuesserInterface
|
||||
{
|
||||
private $classSuffix;
|
||||
private $namespace;
|
||||
|
||||
/**
|
||||
* Initializes guesser.
|
||||
*
|
||||
* @param string $classSuffix
|
||||
*/
|
||||
public function __construct($classSuffix = 'Test\\Behaviour\\FeatureContext')
|
||||
{
|
||||
$this->classSuffix = $classSuffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets bundle namespace to use for guessing.
|
||||
*
|
||||
* @param string $namespace
|
||||
*/
|
||||
public function setModuleNamespace($namespace)
|
||||
{
|
||||
$this->namespace = $namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to guess context classname.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function guess()
|
||||
{
|
||||
// Try fully qualified namespace
|
||||
if (class_exists($class = $this->namespace.'\\'.$this->classSuffix)) {
|
||||
return $class;
|
||||
}
|
||||
// Fall back to namespace with SilverStripe prefix
|
||||
// TODO Remove once core has namespace capabilities for modules
|
||||
if (class_exists($class = 'SilverStripe\\'.$this->namespace.'\\'.$this->classSuffix)) {
|
||||
return $class;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,11 +1,16 @@
|
||||
parameters:
|
||||
behat.silverstripe_extension.context.initializer.class: SilverStripe\BehatExtension\Context\Initializer\SilverStripeAwareInitializer
|
||||
behat.silverstripe_extension.framework_path: ~
|
||||
behat.silverstripe_extension.context.class_guesser.class: SilverStripe\BehatExtension\Context\ClassGuesser\ModuleContextClassGuesser
|
||||
behat.console.processor.locator.class: SilverStripe\BehatExtension\Console\Processor\LocatorProcessor
|
||||
behat.silverstripe_extension.context.class_suffix: Test\Behaviour\FeatureContext
|
||||
behat.silverstripe_extension.framework_path: framework
|
||||
behat.silverstripe_extension.ajax_steps: ~
|
||||
behat.silverstripe_extension.ajax_timeout: ~
|
||||
behat.silverstripe_extension.admin_url: ~
|
||||
behat.silverstripe_extension.login_url: ~
|
||||
behat.silverstripe_extension.screenshot_path: ~
|
||||
behat.silverstripe_extension.module:
|
||||
behat.silverstripe_extension.context.path_suffix: tests/behat/features/
|
||||
services:
|
||||
behat.silverstripe_extension.context.initializer:
|
||||
class: %behat.silverstripe_extension.context.initializer.class%
|
||||
@ -20,3 +25,9 @@ services:
|
||||
- [setScreenshotPath, [%behat.silverstripe_extension.screenshot_path%]]
|
||||
tags:
|
||||
- { name: behat.context.initializer }
|
||||
behat.silverstripe_extension.context.class_guesser:
|
||||
class: %behat.silverstripe_extension.context.class_guesser.class%
|
||||
arguments:
|
||||
- %behat.silverstripe_extension.context.class_suffix%
|
||||
tags:
|
||||
- { name: behat.context.class_guesser, priority: 10 }
|
Loading…
Reference in New Issue
Block a user