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], + ]; + } +}