Merge pull request #4985 from pamil/theme/test-factory

[Theme] Easier way to test themes
This commit is contained in:
Paweł Jędrzejewski 2016-05-10 10:50:15 +02:00
commit 95ebafef7c
16 changed files with 549 additions and 24 deletions

View file

@ -40,11 +40,7 @@ sylius_channel:
sylius_theme:
sources:
filesystem:
directories:
- "%kernel.root_dir%/themes"
- "%kernel.root_dir%/../vendor/sylius/themes"
- "%kernel.cache_dir%/_themes"
test: ~
doctrine_cache:
providers:

View file

@ -10,6 +10,7 @@ Existing configuration sources
:titlesonly:
configuration_sources/filesystem
configuration_sources/test
Creating custom configuration source
------------------------------------

View file

@ -0,0 +1,28 @@
Test configuration source
=========================
**Test** configuration source provides an interface that can be used to add, remove and access themes in test environment.
They are stored in the cache directory and if used with Behat, they are persisted across steps but not across scenarios.
Configuration reference
-----------------------
This source does not have any configuration options. To enable it, use the following configuration:
.. code-block:: yaml
sylius_theme:
sources:
test: ~
Usage
-----
In order to use tests, have a look at ``sylius.theme.test_theme_configuration_manager`` service
(implementing `TestThemeConfigurationManagerInterface`_). You can:
- add a theme: ``void add(array $configuration)``
- remove a theme: ``void remove(string $themeName)``
- remove all themes: ``void clear()``
.. _TestThemeConfigurationManagerInterface: http://api.sylius.org/Sylius/Bundle/ThemeBundle/Configuration/Test/TestThemeConfigurationManagerInterface.html

View file

@ -14,6 +14,7 @@
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="sylius.behat.context.hook.doctrine_orm.class">Sylius\Behat\Context\Hook\DoctrineORMContext</parameter>
<parameter key="sylius.behat.context.hook.test_theme.class">Sylius\Behat\Context\Hook\TestThemeContext</parameter>
</parameters>
<services>
@ -21,5 +22,10 @@
<argument type="service" id="doctrine.orm.entity_manager" container="symfony_shared" />
<tag name="sylius.behat.context" />
</service>
<service id="sylius.behat.context.hook.test_theme" class="%sylius.behat.context.hook.test_theme.class%">
<argument type="service" id="sylius.theme.test_theme_configuration_manager" container="symfony_shared" />
<tag name="sylius.behat.context" />
</service>
</services>
</container>

View file

@ -183,7 +183,7 @@
<argument type="service" id="sylius.factory.theme" container="symfony" />
<argument type="service" id="sylius.repository.channel" container="symfony" />
<argument type="service" id="sylius.manager.channel" container="symfony" />
<argument type="service" id="service_container" container="symfony" /> <!-- have to get Symfony2 parameter -->
<argument type="service" id="sylius.theme.test_theme_configuration_manager" container="symfony" />
<tag name="sylius.behat.context" />
</service>

View file

@ -6,6 +6,7 @@ default:
ui_theming:
contexts_as_services:
- sylius.behat.context.hook.doctrine_orm
- sylius.behat.context.hook.test_theme
- sylius.behat.context.transform.shared_storage
- sylius.behat.context.transform.channel

View file

@ -0,0 +1,42 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sylius\Behat\Context\Hook;
use Behat\Behat\Context\Context;
use Sylius\Bundle\ThemeBundle\Configuration\Test\TestThemeConfigurationManagerInterface;
/**
* @author Kamil Kokot <kamil.kokot@lakion.com>
*/
final class TestThemeContext implements Context
{
/**
* @var TestThemeConfigurationManagerInterface
*/
private $testThemeConfigurationManager;
/**
* @param TestThemeConfigurationManagerInterface $testThemeConfigurationManager
*/
public function __construct(TestThemeConfigurationManagerInterface $testThemeConfigurationManager)
{
$this->testThemeConfigurationManager = $testThemeConfigurationManager;
}
/**
* @BeforeScenario
*/
public function purgeTestThemes()
{
$this->testThemeConfigurationManager->clear();
}
}

View file

@ -13,14 +13,13 @@ namespace Sylius\Behat\Context\Setup;
use Behat\Behat\Context\Context;
use Doctrine\Common\Persistence\ObjectManager;
use Sylius\Bundle\ThemeBundle\Configuration\Test\TestThemeConfigurationManagerInterface;
use Sylius\Bundle\ThemeBundle\Factory\ThemeFactoryInterface;
use Sylius\Bundle\ThemeBundle\Model\ThemeInterface;
use Sylius\Bundle\ThemeBundle\Repository\ThemeRepositoryInterface;
use Sylius\Component\Channel\Repository\ChannelRepositoryInterface;
use Sylius\Component\Core\Model\ChannelInterface;
use Sylius\Component\Core\Test\Services\SharedStorageInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Zend\Hydrator\HydrationInterface;
/**
* @author Kamil Kokot <kamil.kokot@lakion.com>
@ -53,9 +52,9 @@ final class ThemeContext implements Context
private $channelManager;
/**
* @var string
* @var TestThemeConfigurationManagerInterface
*/
private $cacheDir;
private $testThemeConfigurationManager;
/**
* @param SharedStorageInterface $sharedStorage
@ -63,7 +62,7 @@ final class ThemeContext implements Context
* @param ThemeFactoryInterface $themeFactory
* @param ChannelRepositoryInterface $channelRepository
* @param ObjectManager $channelManager
* @param ContainerInterface $container
* @param TestThemeConfigurationManagerInterface $testThemeConfigurationManager
*/
public function __construct(
SharedStorageInterface $sharedStorage,
@ -71,14 +70,14 @@ final class ThemeContext implements Context
ThemeFactoryInterface $themeFactory,
ChannelRepositoryInterface $channelRepository,
ObjectManager $channelManager,
ContainerInterface $container
TestThemeConfigurationManagerInterface $testThemeConfigurationManager
) {
$this->sharedStorage = $sharedStorage;
$this->themeRepository = $themeRepository;
$this->themeFactory = $themeFactory;
$this->channelRepository = $channelRepository;
$this->channelManager = $channelManager;
$this->cacheDir = $container->getParameter('kernel.cache_dir');
$this->testThemeConfigurationManager = $testThemeConfigurationManager;
}
/**
@ -86,18 +85,11 @@ final class ThemeContext implements Context
*/
public function storeHasTheme($themeName)
{
$theme = $this->themeFactory->create($themeName, sprintf('%s/_themes/%s/', $this->cacheDir, $themeName));
$this->testThemeConfigurationManager->add([
'name' => $themeName,
]);
if (!file_exists($theme->getPath())) {
mkdir($theme->getPath(), 0777, true);
}
file_put_contents(
rtrim($theme->getPath(), '/') . '/composer.json',
sprintf('{ "name": "%s" }', $themeName)
);
$this->sharedStorage->set('theme', $theme);
$this->sharedStorage->set('theme', $this->themeRepository->findOneByName($themeName));
}
/**

View file

@ -0,0 +1,41 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sylius\Bundle\ThemeBundle\Configuration\Test;
use Sylius\Bundle\ThemeBundle\Configuration\ConfigurationProviderInterface;
/**
* @author Kamil Kokot <kamil.kokot@lakion.com>
*/
final class TestConfigurationProvider implements ConfigurationProviderInterface
{
/**
* @var TestThemeConfigurationManagerInterface
*/
private $testThemeConfigurationManager;
/**
* @param TestThemeConfigurationManagerInterface $testThemeConfigurationManager
*/
public function __construct(TestThemeConfigurationManagerInterface $testThemeConfigurationManager)
{
$this->testThemeConfigurationManager = $testThemeConfigurationManager;
}
/**
* {@inheritdoc}
*/
public function getConfigurations()
{
return $this->testThemeConfigurationManager->findAll();
}
}

View file

@ -0,0 +1,59 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sylius\Bundle\ThemeBundle\Configuration\Test;
use Sylius\Bundle\ThemeBundle\Configuration\ConfigurationSourceFactoryInterface;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Parameter;
use Symfony\Component\DependencyInjection\Reference;
/**
* @author Kamil Kokot <kamil.kokot@lakion.com>
*/
final class TestConfigurationSourceFactory implements ConfigurationSourceFactoryInterface
{
/**
* {@inheritdoc}
*/
public function buildConfiguration(ArrayNodeDefinition $node)
{
// no configuration
}
/**
* {@inheritdoc}
*/
public function initializeSource(ContainerBuilder $container, array $config)
{
$container->setDefinition(
'sylius.theme.test_theme_configuration_manager',
new Definition(TestThemeConfigurationManager::class, [
new Reference('sylius.theme.configuration.processor'),
new Parameter('kernel.cache_dir'),
])
);
return new Definition(TestConfigurationProvider::class, [
new Reference('sylius.theme.test_theme_configuration_manager'),
]);
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'test';
}
}

View file

@ -0,0 +1,170 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sylius\Bundle\ThemeBundle\Configuration\Test;
use Sylius\Bundle\ThemeBundle\Configuration\ConfigurationProcessorInterface;
use Symfony\Component\Filesystem\Filesystem;
/**
* @author Kamil Kokot <kamil.kokot@lakion.com>
*/
final class TestThemeConfigurationManager implements TestThemeConfigurationManagerInterface
{
/**
* @var ConfigurationProcessorInterface
*/
private $configurationProcessor;
/**
* @var Filesystem
*/
private $filesystem;
/**
* @var string
*/
private $configurationsFile;
/**
* @param ConfigurationProcessorInterface $configurationProcessor
* @param string $cacheDir
*/
public function __construct(ConfigurationProcessorInterface $configurationProcessor, $cacheDir)
{
$this->configurationProcessor = $configurationProcessor;
$this->filesystem = new Filesystem();
$this->configurationsFile = rtrim($cacheDir, '/') . '/_test_themes/data.serialized';
}
/**
* {@inheritdoc}
*/
public function findAll()
{
$this->initializeIfNeeded();
return $this->load();
}
/**
* {@inheritdoc}
*/
public function add(array $configuration)
{
$this->initializeIfNeeded();
$configuration = $this->configurationProcessor->process([$configuration]);
$configuration['path'] = $this->getThemeDirectory($configuration['name']);
$this->initializeTheme($configuration['name']);
$configurations = $this->load();
$configurations[] = $configuration;
$this->save($configurations);
}
/**
* {@inheritdoc}
*/
public function remove($themeName)
{
$this->initializeIfNeeded();
$this->clearTheme($themeName);
$configurations = $this->load();
$configurations = array_filter($configurations, function ($configuration) use ($themeName) {
return isset($configuration['name']) && $configuration['name'] !== $themeName;
});
$this->save($configurations);
}
/**
* {@inheritdoc}
*/
public function clear()
{
$configurationsDirectory = dirname($this->configurationsFile);
if ($this->filesystem->exists($configurationsDirectory)) {
$this->filesystem->remove($configurationsDirectory);
}
}
/**
* @return array
*/
private function load()
{
return unserialize(file_get_contents($this->configurationsFile));
}
/**
* @param array $configurations
*/
private function save(array $configurations)
{
file_put_contents($this->configurationsFile, serialize($configurations));
}
private function initializeIfNeeded()
{
if ($this->filesystem->exists($this->configurationsFile)) {
return;
}
$this->initialize();
}
private function initialize()
{
$configurationsDirectory = dirname($this->configurationsFile);
$this->filesystem->mkdir($configurationsDirectory);
$this->save([]);
}
/**
* @param string $themeName
*/
private function initializeTheme($themeName)
{
$themeDirectory = $this->getThemeDirectory($themeName);
$this->filesystem->mkdir($themeDirectory);
}
/**
* @param string $themeName
*/
private function clearTheme($themeName)
{
$themeDirectory = $this->getThemeDirectory($themeName);
if (!$this->filesystem->exists($themeDirectory)) {
return;
}
$this->filesystem->remove($themeDirectory);
}
/**
* @param string $themeName
*
* @return string
*/
private function getThemeDirectory($themeName)
{
return rtrim(dirname($this->configurationsFile), '/') . '/' . $themeName;
}
}

View file

@ -0,0 +1,38 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sylius\Bundle\ThemeBundle\Configuration\Test;
/**
* @author Kamil Kokot <kamil.kokot@lakion.com>
*/
interface TestThemeConfigurationManagerInterface
{
/**
* @return array
*/
public function findAll();
/**
* @param array $configuration
*/
public function add(array $configuration);
/**
* @param string $themeName
*/
public function remove($themeName);
/**
* Clear currently used configurations storage.
*/
public function clear();
}

View file

@ -128,5 +128,9 @@ final class SyliusThemeExtension extends Extension implements PrependExtensionIn
$compositeConfigurationProvider = $container->getDefinition('sylius.theme.configuration.provider');
$compositeConfigurationProvider->replaceArgument(0, $configurationProviders);
foreach ($this->configurationSourceFactories as $configurationSourceFactory) {
$container->addObjectResource($configurationSourceFactory);
}
}
}

View file

@ -12,6 +12,7 @@
namespace Sylius\Bundle\ThemeBundle;
use Sylius\Bundle\ThemeBundle\Configuration\Filesystem\FilesystemConfigurationSourceFactory;
use Sylius\Bundle\ThemeBundle\Configuration\Test\TestConfigurationSourceFactory;
use Sylius\Bundle\ThemeBundle\DependencyInjection\SyliusThemeExtension;
use Sylius\Bundle\ThemeBundle\Translation\DependencyInjection\Compiler\TranslatorAliasingPass;
use Sylius\Bundle\ThemeBundle\Translation\DependencyInjection\Compiler\TranslatorFallbackLocalesPass;
@ -33,6 +34,7 @@ class SyliusThemeBundle extends Bundle
/** @var SyliusThemeExtension $themeExtension */
$themeExtension = $container->getExtension('sylius_theme');
$themeExtension->addConfigurationSourceFactory(new FilesystemConfigurationSourceFactory());
$themeExtension->addConfigurationSourceFactory(new TestConfigurationSourceFactory());
$container->addCompilerPass(new TranslatorAliasingPass());
$container->addCompilerPass(new TranslatorFallbackLocalesPass());

View file

@ -0,0 +1,52 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace spec\Sylius\Bundle\ThemeBundle\Configuration\Test;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Sylius\Bundle\ThemeBundle\Configuration\ConfigurationProviderInterface;
use Sylius\Bundle\ThemeBundle\Configuration\Test\TestThemeConfigurationManagerInterface;
use Sylius\Bundle\ThemeBundle\Configuration\Test\TestConfigurationProvider;
/**
* @mixin TestConfigurationProvider
*
* @author Kamil Kokot <kamil.kokot@lakion.com>
*/
final class TestConfigurationProviderSpec extends ObjectBehavior
{
function let(TestThemeConfigurationManagerInterface $testThemeConfigurationManager)
{
$this->beConstructedWith($testThemeConfigurationManager);
}
function it_is_initializable()
{
$this->shouldHaveType('Sylius\Bundle\ThemeBundle\Configuration\Test\TestConfigurationProvider');
}
function it_implements_configuration_provider_interface()
{
$this->shouldImplement(ConfigurationProviderInterface::class);
}
function it_provides_configuration_based_on_test_configuration_manager(TestThemeConfigurationManagerInterface $testThemeConfigurationManager)
{
$testThemeConfigurationManager->findAll()->willReturn([
['name' => 'theme/name'],
]);
$this->getConfigurations()->shouldReturn([
['name' => 'theme/name'],
]);
}
}

View file

@ -0,0 +1,93 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace spec\Sylius\Bundle\ThemeBundle\Configuration\Test;
use org\bovigo\vfs\vfsStreamWrapper as VfsStreamWrapper;
use org\bovigo\vfs\vfsStreamDirectory as VfsStreamDirectory;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Sylius\Bundle\ThemeBundle\Configuration\ConfigurationProcessorInterface;
use Sylius\Bundle\ThemeBundle\Configuration\Test\TestThemeConfigurationManager;
use Sylius\Bundle\ThemeBundle\Configuration\Test\TestThemeConfigurationManagerInterface;
/**
* @mixin TestThemeConfigurationManager
*
* @author Kamil Kokot <kamil.kokot@lakion.com>
*/
final class TestThemeConfigurationManagerSpec extends ObjectBehavior
{
function let(ConfigurationProcessorInterface $configurationProcessor)
{
VfsStreamWrapper::register();
VfsStreamWrapper::setRoot(new VfsStreamDirectory(''));
$this->beConstructedWith($configurationProcessor, 'vfs://cache/');
}
function letGo()
{
VfsStreamWrapper::unregister();
}
function it_is_initializable()
{
$this->shouldHaveType('Sylius\Bundle\ThemeBundle\Configuration\Test\TestThemeConfigurationManager');
}
function it_implements_test_configuration_manager_interface()
{
$this->shouldImplement(TestThemeConfigurationManagerInterface::class);
}
function it_finds_all_saved_configurations()
{
$this->findAll()->shouldReturn([]);
}
function it_stores_theme_configuration(ConfigurationProcessorInterface $configurationProcessor)
{
$configurationProcessor->process([['name' => 'theme/name']])->willReturn(['name' => 'theme/name']);
$this->add(['name' => 'theme/name']);
$this->findAll()->shouldHaveCount(1);
}
function its_theme_configurations_can_be_removed(ConfigurationProcessorInterface $configurationProcessor)
{
$configurationProcessor->process([['name' => 'theme/name']])->willReturn(['name' => 'theme/name']);
$this->add(['name' => 'theme/name']);
$this->remove('theme/name');
$this->findAll()->shouldReturn([]);
}
function it_clears_all_theme_configurations(ConfigurationProcessorInterface $configurationProcessor)
{
$configurationProcessor->process([['name' => 'theme/name1']])->willReturn(['name' => 'theme/name1']);
$configurationProcessor->process([['name' => 'theme/name2']])->willReturn(['name' => 'theme/name2']);
$this->add(['name' => 'theme/name1']);
$this->add(['name' => 'theme/name2']);
$this->clear();
$this->findAll()->shouldReturn([]);
}
function it_does_not_throw_any_exception_if_clearing_unexisting_storage()
{
$this->clear();
}
}