Refactor our environment handler to decorate the original one
This commit is contained in:
@@ -13,21 +13,18 @@ declare(strict_types=1);
|
||||
|
||||
namespace FriendsOfBehat\SymfonyExtension\Context\Environment\Handler;
|
||||
|
||||
use Behat\Behat\Context\Argument\ArgumentResolverFactory;
|
||||
use Behat\Behat\Context\Argument\NullFactory;
|
||||
use Behat\Behat\Context\Argument\SuiteScopedResolverFactory;
|
||||
use Behat\Behat\Context\Argument\SuiteScopedResolverFactoryAdapter;
|
||||
use Behat\Behat\Context\Context;
|
||||
use Behat\Behat\Context\ContextClass\ClassResolver;
|
||||
use Behat\Behat\Context\ContextFactory;
|
||||
use Behat\Behat\Context\Environment\ContextEnvironment;
|
||||
use Behat\Behat\Context\Environment\InitializedContextEnvironment;
|
||||
use Behat\Testwork\Environment\Environment;
|
||||
use Behat\Testwork\Environment\Exception\EnvironmentIsolationException;
|
||||
use Behat\Testwork\Environment\Handler\EnvironmentHandler;
|
||||
use Behat\Testwork\Suite\Exception\SuiteConfigurationException;
|
||||
use Behat\Testwork\Suite\GenericSuite;
|
||||
use Behat\Testwork\Suite\Suite;
|
||||
use FriendsOfBehat\SymfonyExtension\Bundle\FriendsOfBehatSymfonyExtensionBundle;
|
||||
use FriendsOfBehat\SymfonyExtension\Context\Environment\InitialisedContextServiceEnvironment;
|
||||
use FriendsOfBehat\SymfonyExtension\Context\Environment\UninitialisedContextServiceEnvironment;
|
||||
use FriendsOfBehat\SymfonyExtension\Context\Environment\InitialisedSymfonyExtensionEnvironment;
|
||||
use FriendsOfBehat\SymfonyExtension\Context\Environment\UninitialisedSymfonyExtensionEnvironment;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
|
||||
@@ -36,29 +33,13 @@ final class ContextServiceEnvironmentHandler implements EnvironmentHandler
|
||||
/** @var KernelInterface */
|
||||
private $symfonyKernel;
|
||||
|
||||
/** @var ClassResolver[] */
|
||||
private $classResolvers = [];
|
||||
/** @var EnvironmentHandler */
|
||||
private $decoratedEnvironmentHandler;
|
||||
|
||||
/** @var ContextFactory */
|
||||
private $contextFactory;
|
||||
|
||||
/** @var ArgumentResolverFactory */
|
||||
private $resolverFactory;
|
||||
|
||||
/**
|
||||
* @param ArgumentResolverFactory|SuiteScopedResolverFactory $resolverFactory
|
||||
*/
|
||||
public function __construct(KernelInterface $symfonyKernel, ContextFactory $factory, $resolverFactory = null)
|
||||
public function __construct(KernelInterface $symfonyKernel, EnvironmentHandler $decoratedEnvironmentHandler)
|
||||
{
|
||||
$this->symfonyKernel = $symfonyKernel;
|
||||
|
||||
$this->contextFactory = $factory;
|
||||
|
||||
if ($resolverFactory && !$resolverFactory instanceof ArgumentResolverFactory) {
|
||||
$resolverFactory = new SuiteScopedResolverFactoryAdapter($resolverFactory);
|
||||
}
|
||||
|
||||
$this->resolverFactory = $resolverFactory ?: new NullFactory();
|
||||
$this->decoratedEnvironmentHandler = $decoratedEnvironmentHandler;
|
||||
}
|
||||
|
||||
public function supportsSuite(Suite $suite): bool
|
||||
@@ -68,21 +49,34 @@ final class ContextServiceEnvironmentHandler implements EnvironmentHandler
|
||||
|
||||
public function buildEnvironment(Suite $suite): Environment
|
||||
{
|
||||
$environment = new UninitialisedContextServiceEnvironment($suite);
|
||||
foreach ($this->getSuiteContextsServices($suite) as [$contextId, $contextArguments]) {
|
||||
$environment->registerContextService($contextId, $this->getContextClass($contextId), $contextArguments);
|
||||
$symfonyContexts = [];
|
||||
|
||||
foreach ($this->getSuiteContextsServices($suite) as $serviceId) {
|
||||
if (!$this->getContainer()->has($serviceId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$symfonyContexts[$serviceId] = get_class($this->getContainer()->get($serviceId));
|
||||
}
|
||||
|
||||
return $environment;
|
||||
$delegatedSuite = $this->cloneSuiteWithoutContexts($suite, array_keys($symfonyContexts));
|
||||
|
||||
$delegatedEnvironment = $this->decoratedEnvironmentHandler->buildEnvironment($delegatedSuite);
|
||||
|
||||
if (!$delegatedEnvironment instanceof ContextEnvironment) {
|
||||
throw new \Exception();
|
||||
}
|
||||
|
||||
return new UninitialisedSymfonyExtensionEnvironment($suite, $symfonyContexts, $delegatedEnvironment);
|
||||
}
|
||||
|
||||
public function supportsEnvironmentAndSubject(Environment $environment, $testSubject = null): bool
|
||||
{
|
||||
return $environment instanceof UninitialisedContextServiceEnvironment;
|
||||
return $environment instanceof UninitialisedSymfonyExtensionEnvironment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param UninitialisedContextServiceEnvironment $uninitializedEnvironment
|
||||
* @param UninitialisedSymfonyExtensionEnvironment $uninitializedEnvironment
|
||||
*
|
||||
* @throws EnvironmentIsolationException
|
||||
*/
|
||||
@@ -90,54 +84,78 @@ final class ContextServiceEnvironmentHandler implements EnvironmentHandler
|
||||
{
|
||||
$this->assertEnvironmentCanBeIsolated($uninitializedEnvironment, $testSubject);
|
||||
|
||||
$environment = new InitialisedContextServiceEnvironment($uninitializedEnvironment->getSuite());
|
||||
$resolvers = $this->resolverFactory->createArgumentResolvers($environment);
|
||||
$environment = new InitialisedSymfonyExtensionEnvironment($uninitializedEnvironment->getSuite());
|
||||
|
||||
foreach ($uninitializedEnvironment->getContextServicesWithArguments() as $contextId => $arguments) {
|
||||
foreach ($uninitializedEnvironment->getServices() as $serviceId) {
|
||||
/** @var Context $context */
|
||||
$context = $this->getContext($contextId, $arguments, $resolvers);
|
||||
$context = $this->getContainer()->get($serviceId);
|
||||
|
||||
$environment->registerContext($context);
|
||||
}
|
||||
|
||||
$delegatedEnvironment = $this->decoratedEnvironmentHandler->isolateEnvironment($uninitializedEnvironment->getDelegatedEnvironment());
|
||||
|
||||
if (!$delegatedEnvironment instanceof InitializedContextEnvironment) {
|
||||
throw new \Exception();
|
||||
}
|
||||
|
||||
foreach ($delegatedEnvironment->getContexts() as $context) {
|
||||
$environment->registerContext($context);
|
||||
}
|
||||
|
||||
return $environment;
|
||||
}
|
||||
|
||||
public function registerClassResolver(ClassResolver $classResolver): void
|
||||
{
|
||||
$this->classResolvers[] = $classResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array[]
|
||||
* @return string[]
|
||||
*
|
||||
* @throws SuiteConfigurationException If "contexts" setting is not an array
|
||||
*/
|
||||
private function getSuiteContextsServices(Suite $suite): array
|
||||
{
|
||||
$contextsServices = $suite->getSetting('contexts');
|
||||
$contexts = $suite->getSetting('contexts');
|
||||
|
||||
if (!is_array($contextsServices)) {
|
||||
if (!is_array($contexts)) {
|
||||
throw new SuiteConfigurationException(sprintf(
|
||||
'"contexts" setting of the "%s" suite is expected to be an array, %s given.',
|
||||
$suite->getName(),
|
||||
gettype($contextsServices)
|
||||
gettype($contexts)
|
||||
), $suite->getName());
|
||||
}
|
||||
|
||||
return array_map(
|
||||
function ($context): array {
|
||||
$class = $context;
|
||||
$arguments = [];
|
||||
return array_map([$this, 'normaliseContext'], $contexts);
|
||||
}
|
||||
|
||||
if (is_array($context)) {
|
||||
$class = current(array_keys($context));
|
||||
$arguments = $context[$class];
|
||||
}
|
||||
private function cloneSuiteWithoutContexts(Suite $suite, array $contextsToRemove): Suite
|
||||
{
|
||||
$contexts = $suite->getSetting('contexts');
|
||||
|
||||
return [$class, $arguments];
|
||||
},
|
||||
$contextsServices
|
||||
);
|
||||
if (!is_array($contexts)) {
|
||||
throw new SuiteConfigurationException(sprintf(
|
||||
'"contexts" setting of the "%s" suite is expected to be an array, %s given.',
|
||||
$suite->getName(),
|
||||
gettype($contexts)
|
||||
), $suite->getName());
|
||||
}
|
||||
|
||||
$contexts = array_filter($contexts, function ($context) use ($contextsToRemove): bool {
|
||||
return !in_array($this->normaliseContext($context), $contextsToRemove, true);
|
||||
});
|
||||
|
||||
return new GenericSuite($suite->getName(), array_merge($suite->getSettings(), ['contexts' => $contexts]));
|
||||
}
|
||||
|
||||
private function normaliseContext($context): string
|
||||
{
|
||||
if (is_array($context)) {
|
||||
return current(array_keys($context));
|
||||
}
|
||||
|
||||
if (is_string($context)) {
|
||||
return $context;
|
||||
}
|
||||
|
||||
throw new \Exception();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -154,60 +172,6 @@ final class ContextServiceEnvironmentHandler implements EnvironmentHandler
|
||||
}
|
||||
}
|
||||
|
||||
private function resolveContextId(string $contextId): string
|
||||
{
|
||||
foreach ($this->classResolvers as $resolver) {
|
||||
if ($resolver->supportsClass($contextId)) {
|
||||
return $resolver->resolveClass($contextId);
|
||||
}
|
||||
}
|
||||
|
||||
return $contextId;
|
||||
}
|
||||
|
||||
private function getContextClass(string $contextId): string
|
||||
{
|
||||
$contextId = $this->resolveContextId($contextId);
|
||||
|
||||
if ($this->getContainer()->has($contextId)) {
|
||||
return get_class($this->getContainer()->get($contextId));
|
||||
}
|
||||
|
||||
$class = '\\' . ltrim($contextId, '\\');
|
||||
|
||||
if (class_exists($class)) {
|
||||
return $class;
|
||||
}
|
||||
|
||||
throw new \DomainException(sprintf('There is no service or class "%s".', $contextId));
|
||||
}
|
||||
|
||||
private function getContext(string $contextId, array $arguments = [], array $resolvers = []): Context
|
||||
{
|
||||
$contextId = $this->resolveContextId($contextId);
|
||||
|
||||
$class = '\\' . ltrim($contextId, '\\');
|
||||
|
||||
if ($this->getContainer()->has($contextId)) {
|
||||
$context = $this->getContainer()->get($contextId);
|
||||
} elseif (class_exists($class)) {
|
||||
$context = $this->contextFactory->createContext($class, $arguments, $resolvers);
|
||||
} else {
|
||||
throw new \DomainException(sprintf('There is no service or class "%s".', $contextId));
|
||||
}
|
||||
|
||||
if (!$context instanceof Context) {
|
||||
throw new \DomainException(sprintf(
|
||||
'Context "%s" referenced as "%s" needs to implement "%s".',
|
||||
get_class($context),
|
||||
$contextId,
|
||||
Context::class
|
||||
));
|
||||
}
|
||||
|
||||
return $context;
|
||||
}
|
||||
|
||||
private function getContainer(): ContainerInterface
|
||||
{
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user