Permalink
Browse files

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.
  • Loading branch information...
1 parent 046350f commit feba280470b1427ef5afdb32e333ada907783b8a @chillu chillu committed Nov 14, 2012
View
@@ -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,22 +151,19 @@ 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
-
- # This will run all feature tests using chrome profile
- vendor/behat --config mymodule/tests/behat/behat.yml --profile=chrome
+ # Run all "mymodule" tests
+ vendor/bin/behat @mymodule
- # 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
The extension comes with several `BehatContext` subclasses come with some extra step defintions.
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 }

0 comments on commit feba280

Please sign in to comment.