forked from michalochman/SilverStripeExtension
/
ModuleSuiteLocator.php
193 lines (176 loc) · 5.94 KB
/
ModuleSuiteLocator.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
<?php
namespace SilverStripe\BehatExtension\Controllers;
use Behat\Testwork\Cli\Controller;
use Behat\Testwork\Suite\Cli\SuiteController;
use Behat\Testwork\Suite\ServiceContainer\SuiteExtension;
use Behat\Testwork\Suite\SuiteRegistry;
use Exception;
use InvalidArgumentException;
use RuntimeException;
use SilverStripe\Core\Manifest\Module;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Yaml\Parser;
/**
* Locates test suite configuration based on module name.
*
* Replaces:
* @see SuiteController for similar core behat controller
*/
class ModuleSuiteLocator implements Controller
{
use ModuleCommandTrait;
/**
* @var Container
*/
protected $container;
/**
* @var SuiteRegistry
*/
protected $registry;
/**
* Cache of configured suites
*
* @see SuiteExtension Which registers these
* @var array
*/
private $suiteConfigurations = array();
/**
* Init suite locator
*
* @param ContainerInterface $container
* @param SuiteRegistry $registry
*/
public function __construct(
ContainerInterface $container,
SuiteRegistry $registry
) {
$this->container = $container;
$this->registry = $registry;
$this->suiteConfigurations = $container->getParameter('suite.configurations');
}
/**
* Configures command to be able to process it later.
*
* @param Command $command
*/
public function configure(Command $command)
{
$command->addArgument(
'module',
InputArgument::OPTIONAL,
"Specific module suite to load. "
. "Must be in @modulename format. Supports @vendor/name syntax for vendor installed modules. "
. "Ensure that a modulename/behat.yml exists containing a behat suite of the same name."
);
$command->addOption(
'--suite',
'-s',
InputOption::VALUE_REQUIRED,
'Only execute a specific suite.'
);
}
/**
* Processes data from container and console input.
*
* @param InputInterface $input
* @param OutputInterface $output
* @throws RuntimeException
* @return null
*/
public function execute(InputInterface $input, OutputInterface $output)
{
// Register all suites if no arguments given
if (!$input->getArgument('module') && !$input->getOption('suite')) {
foreach ($this->suiteConfigurations as $name => $config) {
$this->registry->registerSuiteConfiguration(
$name,
$config['type'],
$config['settings']
);
}
return null;
}
// Don't register config if init
if ($input->getOption('init')) {
return;
}
// Get module either via @module or --suite module
if ($input->getArgument('module')) {
// Get suite from module
$moduleName = $input->getArgument('module');
$module = $this->getModule($moduleName);
// Suite name always omits vendor
$suiteName = $module->getShortName();
} else {
// Get module from suite
$suiteName = $input->getOption('suite');
$module = $this->getModule($suiteName, false);
}
// Load registered suite
if (isset($this->suiteConfigurations[$suiteName])) {
$config = $this->suiteConfigurations[$suiteName];
} elseif ($module) {
// Suite doesn't exist, so load dynamically from nested `behat.yml`
$config = $this->loadSuiteConfiguration($suiteName, $module);
} else {
throw new InvalidArgumentException("Could not find suite config {$suiteName}");
}
// Register config
$this->registry->registerSuiteConfiguration(
$suiteName,
$config['type'],
$config['settings']
);
return null;
}
/**
* Get behat.yml configured for this module
*
* @param Module $module
* @return string Path to config
*/
protected function findModuleConfig(Module $module)
{
$pathSuffix = $this->container->getParameter('silverstripe_extension.context.features_path');
$path = $module->getPath();
// Find all candidate paths
foreach ([ "{$path}/", "{$path}/{$pathSuffix}"] as $parent) {
foreach ([$parent.'behat.yml', $parent.'.behat.yml'] as $candidate) {
if (file_exists($candidate ?? '')) {
return $candidate;
}
}
}
throw new InvalidArgumentException("No behat.yml found for module " . $module->getName());
}
/**
* Load configuration dynamically from yml
*
* @param string $suite Suite name
* @param Module $module
* @return array
* @throws Exception
*/
protected function loadSuiteConfiguration($suite, Module $module)
{
$path = $this->findModuleConfig($module);
$yamlParser = new Parser();
$config = $yamlParser->parse(file_get_contents($path ?? ''));
if (empty($config['default']['suites'][$suite])) {
throw new Exception("Path {$path} does not contain default.suites.{$suite} config");
}
$suiteConfig = $config['default']['suites'][$suite];
// Resolve variables
$resolvedConfig = $this->container->getParameterBag()->resolveValue($suiteConfig);
return [
'type' => null, // @todo figure out what this is for
'settings' => $resolvedConfig,
];
}
}