diff --git a/CHANGELOG.md b/CHANGELOG.md
index 13291cf..18e6922 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -22,6 +22,9 @@ All notable changes to this project will be documented in this file, in reverse
fixes some grammatical issues in the questions presented by the installer.
- [#45](https://github.com/zendframework/zend-expressive-skeleton/pull/45)
fixes how JS and CSS assets are added to zend-view templates.
+- [#48](https://github.com/zendframework/zend-expressive-skeleton/pull/48)
+ adds unit tests for the `OptionalPackages` class (which provides the Composer
+ installer scripts).
## 1.0.0rc4 - 2015-12-09
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index ab84148..ef9ff75 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -48,11 +48,19 @@ To run tests:
- Install dependencies via composer:
```console
- $ curl -sS https://getcomposer.org/installer | php --
- $ ./composer.phar install
+ $ composer install
```
- If you don't have `curl` installed, you can also download `composer.phar` from https://getcomposer.org/
+ **NOTE:** If you are wanting to test the installer itself, add the
+ `--no-scripts` flag to the `composer install` command.
+
+ If you don't have `curl` installed, you can also download `composer.phar` from
+ https://getcomposer.org/:
+
+ ```console
+ $ curl -sS https://getcomposer.org/installer | php --
+ $ ln -s composer.phar composer
+ ```
- Run the tests using the "test" command shipped in the `composer.json`:
diff --git a/README.md b/README.md
index 23de4e8..4b60d37 100644
--- a/README.md
+++ b/README.md
@@ -50,3 +50,21 @@ $ composer serve
```
You can then browse to http://localhost:8080.
+
+## Skeleton Development
+
+This section applies only if you cloned this repo with `git clone`, not when you installed expressive with
+`composer create-project ...`.
+
+If you want to run tests against the installer, you need to clone this repo and setup all dependencies with composer.
+Make sure you **prevent composer running scripts** with `--no-scripts`, otherwise it will remove the installer and
+all tests.
+
+```bash
+$ composer install --no-scripts
+$ composer test
+```
+
+Please note that the installer tests remove installed config files and templates before and after running the tests.
+
+Before contributing read [the contributing guide](CONTRIBUTING.md).
diff --git a/composer.json b/composer.json
index 3d49be9..93f9d41 100644
--- a/composer.json
+++ b/composer.json
@@ -6,7 +6,7 @@
"authors": [
{
"name": "Geert Eltink",
- "homepage": "https://xtreamwayz.github.io/"
+ "homepage": "https://xtreamwayz.com/"
}
],
"extra": {
@@ -23,9 +23,20 @@
"zendframework/zend-stdlib": "~2.7"
},
"require-dev": {
- "composer/composer": ">=1.0.0-alpha10",
"phpunit/phpunit": "^4.8",
- "squizlabs/php_codesniffer": "^2.3"
+ "squizlabs/php_codesniffer": "^2.3",
+ "filp/whoops": "^1.1",
+ "composer/composer": ">=1.0.0-alpha11",
+ "zendframework/zend-expressive-aurarouter": "^1.0",
+ "zendframework/zend-expressive-fastroute": "^1.0",
+ "zendframework/zend-expressive-zendrouter": "^1.0",
+ "zendframework/zend-expressive-platesrenderer": "^1.0",
+ "zendframework/zend-expressive-twigrenderer": "^1.0",
+ "zendframework/zend-expressive-zendviewrenderer": "^1.0",
+ "zendframework/zend-servicemanager": "^2.5",
+ "ocramius/proxy-manager": "^1.0",
+ "aura/di": "3.0.*@beta",
+ "mouf/pimple-interop": "^1.0"
},
"autoload": {
"psr-4": {
@@ -35,7 +46,8 @@
},
"autoload-dev": {
"psr-4": {
- "AppTest\\": "test/AppTest/"
+ "AppTest\\": "test/AppTest/",
+ "ExpressiveInstallerTest\\": "test/ExpressiveInstallerTest/"
}
},
"scripts": {
diff --git a/src/ExpressiveInstaller/OptionalPackages.php b/src/ExpressiveInstaller/OptionalPackages.php
index b7da1e3..4cdd023 100644
--- a/src/ExpressiveInstaller/OptionalPackages.php
+++ b/src/ExpressiveInstaller/OptionalPackages.php
@@ -162,8 +162,19 @@ public static function install(Event $event)
// House keeping
$io->write("Remove installer");
- // Remove composer source
+ // Remove test dependencies
unset(self::$composerDefinition['require-dev']['composer/composer']);
+ unset(self::$composerDefinition['require-dev']['zendframework/zend-expressive-aurarouter']);
+ unset(self::$composerDefinition['require-dev']['zendframework/zend-expressive-fastroute']);
+ unset(self::$composerDefinition['require-dev']['zendframework/zend-expressive-zendrouter']);
+ unset(self::$composerDefinition['require-dev']['zendframework/zend-expressive-platesrenderer']);
+ unset(self::$composerDefinition['require-dev']['zendframework/zend-expressive-twigrenderer']);
+ unset(self::$composerDefinition['require-dev']['zendframework/zend-expressive-zendviewrenderer']);
+ unset(self::$composerDefinition['require-dev']['zendframework/zend-servicemanager']);
+ unset(self::$composerDefinition['require-dev']['ocramius/proxy-manager']);
+ unset(self::$composerDefinition['require-dev']['aura/di']);
+ unset(self::$composerDefinition['require-dev']['mouf/pimple-interop']);
+ unset(self::$composerDefinition['autoload-dev']['psr-4']['ExpressiveInstallerTest\\']);
// Remove installer data
unset(self::$composerDefinition['extra']['optional-packages']);
@@ -189,7 +200,7 @@ public static function install(Event $event)
self::removeDefaultMiddleware($io, $projectRoot);
}
- self::cleanUp($io);
+ self::cleanUp($io, $projectRoot);
}
/**
@@ -199,11 +210,13 @@ public static function install(Event $event)
* this one) and assets (including configuration and templates).
*
* @param IOInterface $io
+ * @param string $projectRoot
*/
- private static function cleanUp(IOInterface $io)
+ private static function cleanUp(IOInterface $io, $projectRoot)
{
- $io->write("Removing Expressive installer classes and configuration");
+ $io->write("Removing Expressive installer classes, configuration, and tests");
self::recursiveRmdir(__DIR__);
+ self::recursiveRmdir($projectRoot . '/test/ExpressiveInstallerTest');
}
/**
@@ -288,7 +301,7 @@ private static function askQuestion(Composer $composer, IOInterface $io, $questi
* @param $packageName
* @param $packageVersion
*/
- private static function addPackage(IOInterface $io, $packageName, $packageVersion)
+ public static function addPackage(IOInterface $io, $packageName, $packageVersion)
{
$io->write(sprintf(
" - Adding package %s (%s)",
@@ -340,7 +353,7 @@ private static function addPackage(IOInterface $io, $packageName, $packageVersio
* @param string $target Destination.
* @param bool $force whether or not to copy over an existing file.
*/
- private static function copyFile(IOInterface $io, $projectRoot, $source, $target, $force = false)
+ public static function copyFile(IOInterface $io, $projectRoot, $source, $target, $force = false)
{
// Copy file
if ($force === false && is_file($projectRoot . $target)) {
diff --git a/test/ExpressiveInstallerTest/ContainersTest.php b/test/ExpressiveInstallerTest/ContainersTest.php
new file mode 100644
index 0000000..fe0e0a2
--- /dev/null
+++ b/test/ExpressiveInstallerTest/ContainersTest.php
@@ -0,0 +1,77 @@
+setAccessible(true);
+ $config = $r->getValue();
+
+ // Install packages
+ $this->installPackage(
+ $config['questions']['container']['options'][$containerOption],
+ $copyFilesKey
+ );
+ $this->installPackage(
+ $config['questions']['router']['options'][$routerOption],
+ $copyFilesKey
+ );
+
+ // Test container
+ $container = $this->getContainer();
+ $this->assertInstanceOf(ContainerInterface::class, $container);
+ $this->assertInstanceOf($expectedContainer, $container);
+ $this->assertTrue($container->has(Expressive\Helper\UrlHelper::class));
+ $this->assertTrue($container->has(Expressive\Helper\ServerUrlHelper::class));
+ $this->assertTrue($container->has(Expressive\Application::class));
+ $this->assertTrue($container->has(Expressive\Router\RouterInterface::class));
+
+ // Test home page
+ $response = $this->getAppResponse();
+ $this->assertEquals($expectedResponseStatusCode, $response->getStatusCode());
+ }
+
+ public function containerProvider()
+ {
+ // $containerOption, $routerOption, $copyFilesKey, $expectedResponseStatusCode, $expectedContainer
+ return [
+ 'aura-minimal' => [1, 2, 'minimal-files', 404, AuraContainer::class],
+ 'aura-full' => [1, 2, 'copy-files', 200, AuraContainer::class],
+ 'pimple-minimal' => [2, 2, 'minimal-files', 404, PimpleInteropContainer::class],
+ 'pimple-full' => [2, 2, 'copy-files', 200, PimpleInteropContainer::class],
+ 'zend-sm-minimal' => [3, 2, 'minimal-files', 404, ZendServiceManagerContainer::class],
+ 'zend-sm-full' => [3, 2, 'copy-files', 200, ZendServiceManagerContainer::class],
+ ];
+ }
+}
diff --git a/test/ExpressiveInstallerTest/InstallerTestCase.php b/test/ExpressiveInstallerTest/InstallerTestCase.php
new file mode 100644
index 0000000..2d3eda8
--- /dev/null
+++ b/test/ExpressiveInstallerTest/InstallerTestCase.php
@@ -0,0 +1,122 @@
+response = null;
+
+ $this->cleanup();
+
+ $this->io = $this->prophesize('Composer\IO\IOInterface');
+
+ $composerDefinition = new ReflectionProperty(OptionalPackages::class, 'composerDefinition');
+ $composerDefinition->setAccessible(true);
+
+ // Get composer.json
+ $composerFile = Factory::getComposerFile();
+ $json = new JsonFile($composerFile);
+ $composerDefinition->setValue($json->read());
+
+ $this->projectRoot = realpath(dirname($composerFile));
+
+ $config = new ReflectionProperty(OptionalPackages::class, 'config');
+ $config->setAccessible(true);
+ $config->setValue(require 'src/ExpressiveInstaller/config.php');
+ }
+
+ public function tearDown()
+ {
+ parent::tearDown();
+
+ $this->cleanup();
+ }
+
+ public function installPackage($config, $copyFilesKey)
+ {
+ /* TODO: First we need to set $composerDefinition, $composerRequires, $composerDevRequires and $stabilityFlags;
+ // Add packages to install
+ if (isset($config['packages'])) {
+ foreach ($config['packages'] as $packageName) {
+ OptionalPackages::addPackage($this->io, $packageName, $this->config['packages'][$packageName]);
+ }
+ }*/
+
+ // Copy files
+ if (isset($config[$copyFilesKey])) {
+ foreach ($config[$copyFilesKey] as $source => $target) {
+ OptionalPackages::copyFile($this->io->reveal(), $this->projectRoot, $source, $target);
+ }
+ }
+ }
+
+ public function cleanup()
+ {
+ foreach ($this->teardownFiles as $file) {
+ if (is_file($this->projectRoot.$file)) {
+ unlink($this->projectRoot.$file);
+ }
+ }
+ }
+
+ public function getContainer()
+ {
+ if (!$this->container) {
+ /** @var ContainerInterface $container */
+ $this->container = require 'config/container.php';
+ }
+
+ return $this->container;
+ }
+
+ public function getAppResponse($path = '/')
+ {
+ $container = $this->getContainer();
+
+ /** @var Application $app */
+ $app = $container->get('Zend\Expressive\Application');
+ $request = new ServerRequest([], [], 'https://example.com'.$path, 'GET');
+
+ /** @var ResponseInterface $response */
+ $response = $app($request, new Response());
+
+ return $response;
+ }
+}
diff --git a/test/ExpressiveInstallerTest/RoutersTest.php b/test/ExpressiveInstallerTest/RoutersTest.php
new file mode 100644
index 0000000..b42446c
--- /dev/null
+++ b/test/ExpressiveInstallerTest/RoutersTest.php
@@ -0,0 +1,94 @@
+ 'home',
+ 'path' => '/',
+ 'middleware' => HomePageAction::class,
+ 'allowed_methods' => ['GET'],
+ ],
+ [
+ 'name' => 'api.ping',
+ 'path' => '/api/ping',
+ 'middleware' => PingAction::class,
+ 'allowed_methods' => ['GET'],
+ ],
+ ];
+
+ /**
+ * @dataProvider routerProvider
+ */
+ public function testRouter(
+ $containerOption,
+ $routerOption,
+ $copyFilesKey,
+ $expectedResponseStatusCode,
+ $expectedRoutes,
+ $expectedRouter
+ ) {
+ $r = new ReflectionProperty(OptionalPackages::class, 'config');
+ $r->setAccessible(true);
+ $config = $r->getValue();
+
+ // Install packages
+ $this->installPackage(
+ $config['questions']['container']['options'][$containerOption],
+ $copyFilesKey
+ );
+ $this->installPackage(
+ $config['questions']['router']['options'][$routerOption],
+ $copyFilesKey
+ );
+
+ // Test container
+ $container = $this->getContainer();
+ $this->assertTrue($container->has(Router\RouterInterface::class));
+
+ // Test config
+ $config = $container->get('config');
+ $this->assertEquals(
+ $expectedRouter,
+ $config['dependencies']['invokables'][Router\RouterInterface::class]
+ );
+ $this->assertEquals($expectedRoutes, $config['routes']);
+
+ // Test home page
+ $response = $this->getAppResponse();
+ $this->assertEquals($expectedResponseStatusCode, $response->getStatusCode());
+ }
+
+ public function routerProvider()
+ {
+ // $containerOption, $routerOption, $copyFilesKey, $expectedResponseStatusCode, $expectedRoutes, $expectedRouter
+ return [
+ 'aura-minimal' => [3, 1, 'minimal-files', 404, [], Router\AuraRouter::class],
+ 'aura-full' => [3, 1, 'copy-files', 200, $this->expectedRoutes, Router\AuraRouter::class],
+ 'fastroute-minimal' => [3, 2, 'minimal-files', 404, [], Router\FastRouteRouter::class],
+ 'fastroute-full' => [3, 2, 'copy-files', 200, $this->expectedRoutes, Router\FastRouteRouter::class],
+ 'zend-router-minimal' => [3, 3, 'minimal-files', 404, [], Router\ZendRouter::class],
+ 'zend-router-full' => [3, 3, 'copy-files', 200, $this->expectedRoutes, Router\ZendRouter::class],
+ ];
+ }
+}
diff --git a/test/ExpressiveInstallerTest/TemplateRenderersTest.php b/test/ExpressiveInstallerTest/TemplateRenderersTest.php
new file mode 100644
index 0000000..d95f078
--- /dev/null
+++ b/test/ExpressiveInstallerTest/TemplateRenderersTest.php
@@ -0,0 +1,100 @@
+setAccessible(true);
+ $config = $r->getValue();
+
+ // Install packages
+ $this->installPackage(
+ $config['questions']['container']['options'][$containerOption],
+ $copyFilesKey
+ );
+ $this->installPackage(
+ $config['questions']['router']['options'][$routerOption],
+ $copyFilesKey
+ );
+ $this->installPackage(
+ $config['questions']['template-engine']['options'][$templateRendererOption],
+ $copyFilesKey
+ );
+
+ // Test container
+ $container = $this->getContainer();
+ $this->assertTrue($container->has(Expressive\Application::class));
+ $this->assertTrue($container->has('Zend\Expressive\FinalHandler'));
+
+ // Test config
+ $config = $container->get('config');
+ $this->assertEquals(
+ Expressive\Container\TemplatedErrorHandlerFactory::class,
+ $config['dependencies']['factories']['Zend\Expressive\FinalHandler']
+ );
+
+ // Test template renderer
+ $templateRenderer = $container->get(Expressive\Template\TemplateRendererInterface::class);
+ $this->assertInstanceOf(Expressive\Template\TemplateRendererInterface::class, $templateRenderer);
+ $this->assertInstanceOf($expectedTemplateRenderer, $templateRenderer);
+
+ if ($copyFilesKey == 'copy-files') {
+ // Test home page for full install only, otherwise you get invalid template name errors
+ $response = $this->getAppResponse();
+ $this->assertEquals($expectedResponseStatusCode, $response->getStatusCode());
+ }
+ }
+
+ public function templateRendererProvider()
+ {
+ // $containerOption, $routerOption, $templateRendererOption, $copyFilesKey, $expectedResponseStatusCode,
+ // $expectedTemplateRenderer
+ return [
+ // Full tests first so all the template paths are created before the minimal tests start
+ 'plates-full' => [3, 2, 1, 'copy-files', 200, Expressive\Plates\PlatesRenderer::class],
+ 'twig-full' => [3, 2, 2, 'copy-files', 200, Expressive\Twig\TwigRenderer::class],
+ 'zend-view-full' => [3, 2, 3, 'copy-files', 200, Expressive\ZendView\ZendViewRenderer::class],
+ // Minimal tests must be after the full tests !!!
+ 'plates-minimal' => [3, 2, 1, 'minimal-files', 404, Expressive\Plates\PlatesRenderer::class],
+ 'twig-minimal' => [3, 2, 2, 'minimal-files', 404, Expressive\Twig\TwigRenderer::class],
+ 'zend-view-minimal' => [3, 2, 3, 'minimal-files', 404, Expressive\ZendView\ZendViewRenderer::class],
+ ];
+ }
+}