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
|
As a convention, SilverStripe Behat tests live in a `tests/behat` subfolder
|
||||||
of your module. You can create it with the following command:
|
of your module. You can create it with the following command:
|
||||||
|
|
||||||
cd mymodule
|
mkdir -p mymodule/tests/behat/features/bootstrap/MyModule/Test/Behaviour
|
||||||
mkdir -p tests/behat
|
|
||||||
cd tests/behat
|
|
||||||
../../../vendor/bin/behat --init
|
|
||||||
|
|
||||||
### FeatureContext
|
### 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`
|
to extend your own `FeatureContext`: It should be `SilverStripeContext`
|
||||||
rather than `BehatContext`.
|
rather than `BehatContext`.
|
||||||
|
|
||||||
Example: FeatureContext.php
|
Example: mymodule/tests/behat/features/bootstrap/MyModule/Test/Behaviour/FeatureContext.php
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
namespace SilverStripe\MyModule\Test\Behaviour;
|
namespace MyModule\Test\Behaviour;
|
||||||
|
|
||||||
use SilverStripe\BehatExtension\Context\SilverStripeContext,
|
use SilverStripe\BehatExtension\Context\SilverStripeContext,
|
||||||
SilverStripe\BehatExtension\Context\BasicContext,
|
SilverStripe\BehatExtension\Context\BasicContext,
|
||||||
@ -94,7 +91,7 @@ Example: FeatureContext.php
|
|||||||
|
|
||||||
### behat.yml
|
### 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
|
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
|
Screenshot names within that directory consist of feature file filename and line
|
||||||
number that failed.
|
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
|
## Usage
|
||||||
|
|
||||||
### Starting the selenium server
|
### 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.
|
By default, Behat will use Selenium2 driver.
|
||||||
Selenium will also try to use chrome browser. Refer to `behat.yml` for details.
|
Selenium will also try to use chrome browser. Refer to `behat.yml` for details.
|
||||||
|
|
||||||
# This will run all feature tests located in `features` directory
|
# Run all "mymodule" tests
|
||||||
vendor/bin/behat --config mymodule/tests/behat/behat.yml
|
vendor/bin/behat @mymodule
|
||||||
|
|
||||||
# This will run all feature tests using chrome profile
|
# Run a specific feature test
|
||||||
vendor/behat --config mymodule/tests/behat/behat.yml --profile=chrome
|
vendor/behat @mymodule/my-steps.feature
|
||||||
|
|
||||||
# This will run a specific feature test
|
|
||||||
vendor/behat --config mymodule/tests/behat/behat.yml mymodule/tests/behat/features/my-steps.feature
|
|
||||||
|
|
||||||
### Available Step Definitions
|
### 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.
|
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:
|
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
|
Note: There are more specific step definitions in the SilverStripe `framework` module
|
||||||
for interacting with the CMS interfaces (see `framework/tests/behat/features/bootstrap`).
|
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
|
`@database-defaults` and populates it with default records (usually a set of
|
||||||
default pages).
|
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
|
## FAQ
|
||||||
|
|
||||||
### Why does the module need to know about the framework path on the filesystem?
|
### 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:
|
parameters:
|
||||||
behat.silverstripe_extension.context.initializer.class: SilverStripe\BehatExtension\Context\Initializer\SilverStripeAwareInitializer
|
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_steps: ~
|
||||||
behat.silverstripe_extension.ajax_timeout: ~
|
behat.silverstripe_extension.ajax_timeout: ~
|
||||||
behat.silverstripe_extension.admin_url: ~
|
behat.silverstripe_extension.admin_url: ~
|
||||||
behat.silverstripe_extension.login_url: ~
|
behat.silverstripe_extension.login_url: ~
|
||||||
behat.silverstripe_extension.screenshot_path: ~
|
behat.silverstripe_extension.screenshot_path: ~
|
||||||
|
behat.silverstripe_extension.module:
|
||||||
|
behat.silverstripe_extension.context.path_suffix: tests/behat/features/
|
||||||
services:
|
services:
|
||||||
behat.silverstripe_extension.context.initializer:
|
behat.silverstripe_extension.context.initializer:
|
||||||
class: %behat.silverstripe_extension.context.initializer.class%
|
class: %behat.silverstripe_extension.context.initializer.class%
|
||||||
@ -20,3 +25,9 @@ services:
|
|||||||
- [setScreenshotPath, [%behat.silverstripe_extension.screenshot_path%]]
|
- [setScreenshotPath, [%behat.silverstripe_extension.screenshot_path%]]
|
||||||
tags:
|
tags:
|
||||||
- { name: behat.context.initializer }
|
- { 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