From 755289333d437d51ade402cafbda1be42e882169 Mon Sep 17 00:00:00 2001 From: Kristofer Karlsson Date: Thu, 7 Dec 2023 14:07:56 +0100 Subject: [PATCH 1/2] Add support for PHP 8.3 --- composer.json | 2 +- composer.lock | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index df3a857..d100c8b 100644 --- a/composer.json +++ b/composer.json @@ -35,7 +35,7 @@ } }, "require": { - "php": "~8.0.0 || ~8.1.0 || ~8.2.0", + "php": "~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0", "laminas/laminas-escaper": "^2.5", "league/plates": "^3.3", "mezzio/mezzio-helpers": "^5.2", diff --git a/composer.lock b/composer.lock index 3855d73..b3f74ac 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "fbf1eb99ce1514d330011868ab6e35cc", + "content-hash": "89ec15a832334231dcb08fc028e107b6", "packages": [ { "name": "fig/http-message-util", @@ -4752,11 +4752,11 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "~8.0.0 || ~8.1.0 || ~8.2.0" + "php": "~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0" }, "platform-dev": [], "platform-overrides": { "php": "8.0.99" }, - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" } From 48dab0d0cf27c5338b26a96c05eae8292cbbb473 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Thu, 7 Dec 2023 20:36:11 +0100 Subject: [PATCH 2/2] Rewrote test suite to get rid of `phpspec/prophecy-phpunit` dependency this allows for a clean PHP 8.3 upgrade path. --- composer.json | 1 - composer.lock | 121 +------ psalm-baseline.xml | 322 +++--------------- test/ConfigProviderTest.php | 2 +- test/ExceptionTest.php | 2 +- .../Extension/EscaperExtensionFactoryTest.php | 65 ++-- test/Extension/EscaperExtensionTest.php | 67 ++-- test/Extension/UrlExtensionFactoryTest.php | 80 ++--- test/Extension/UrlExtensionTest.php | 76 +++-- test/PlatesEngineFactoryTest.php | 172 +++------- test/PlatesRendererFactoryTest.php | 61 ++-- test/PlatesRendererTest.php | 5 +- test/TestAsset/DummyPsrContainer.php | 32 ++ 13 files changed, 301 insertions(+), 705 deletions(-) create mode 100644 test/TestAsset/DummyPsrContainer.php diff --git a/composer.json b/composer.json index d100c8b..6cc4386 100644 --- a/composer.json +++ b/composer.json @@ -45,7 +45,6 @@ }, "require-dev": { "laminas/laminas-coding-standard": "~2.5.0", - "phpspec/prophecy-phpunit": "^2.0.1", "phpunit/phpunit": "^9.5.28", "psalm/plugin-phpunit": "0.18.4", "vimeo/psalm": "^5.4" diff --git a/composer.lock b/composer.lock index b3f74ac..9d9318f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "89ec15a832334231dcb08fc028e107b6", + "content-hash": "f0594e3741c7b21246be19d007cd4fee", "packages": [ { "name": "fig/http-message-util", @@ -2043,125 +2043,6 @@ }, "time": "2022-10-14T12:47:21+00:00" }, - { - "name": "phpspec/prophecy", - "version": "v1.16.0", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "be8cac52a0827776ff9ccda8c381ac5b71aeb359" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/be8cac52a0827776ff9ccda8c381ac5b71aeb359", - "reference": "be8cac52a0827776ff9ccda8c381ac5b71aeb359", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.2", - "php": "^7.2 || 8.0.* || 8.1.* || 8.2.*", - "phpdocumentor/reflection-docblock": "^5.2", - "sebastian/comparator": "^3.0 || ^4.0", - "sebastian/recursion-context": "^3.0 || ^4.0" - }, - "require-dev": { - "phpspec/phpspec": "^6.0 || ^7.0", - "phpunit/phpunit": "^8.0 || ^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Prophecy\\": "src/Prophecy" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - }, - { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" - } - ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", - "keywords": [ - "Double", - "Dummy", - "fake", - "mock", - "spy", - "stub" - ], - "support": { - "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/v1.16.0" - }, - "time": "2022-11-29T15:06:56+00:00" - }, - { - "name": "phpspec/prophecy-phpunit", - "version": "v2.0.1", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy-phpunit.git", - "reference": "2d7a9df55f257d2cba9b1d0c0963a54960657177" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy-phpunit/zipball/2d7a9df55f257d2cba9b1d0c0963a54960657177", - "reference": "2d7a9df55f257d2cba9b1d0c0963a54960657177", - "shasum": "" - }, - "require": { - "php": "^7.3 || ^8", - "phpspec/prophecy": "^1.3", - "phpunit/phpunit": "^9.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "psr-4": { - "Prophecy\\PhpUnit\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Christophe Coevoet", - "email": "stof@notk.org" - } - ], - "description": "Integrating the Prophecy mocking library in PHPUnit test cases", - "homepage": "http://phpspec.net", - "keywords": [ - "phpunit", - "prophecy" - ], - "support": { - "issues": "https://github.com/phpspec/prophecy-phpunit/issues", - "source": "https://github.com/phpspec/prophecy-phpunit/tree/v2.0.1" - }, - "time": "2020-07-09T08:33:42+00:00" - }, { "name": "phpstan/phpdoc-parser", "version": "1.5.1", diff --git a/psalm-baseline.xml b/psalm-baseline.xml index c2c6ba3..259673c 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1,26 +1,33 @@ - + - + $config['encoding'] ?? null - + $config['encoding'] $config['plates'] - + $config $config - + + $container->has(ServerUrlHelper::class) + ? $container->get(ServerUrlHelper::class) + : $container->get(ServerUrlHelper::class) + $container->has(UrlHelper::class) + ? $container->get(UrlHelper::class) + : $container->get(UrlHelper::class) + - + new $extension() - + $config['extension'] $container->get(Extension\EscaperExtension::class) $container->get(Extension\UrlExtension::class) @@ -29,10 +36,10 @@ $path $platesConfig - + $config['extensions'] - + $config $extension $mezzioConfig @@ -42,333 +49,82 @@ - + Folder[] - + $r->getValue($folders) - + assertIsArray - + Generator - + strrpos(ExceptionInterface::class, '\\') - - $this->container->reveal() - $this->container->reveal() - $this->container->reveal() - $this->container->reveal() - - + $escaper - - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - - - get - get - get - has - has - has - has - reveal - reveal - reveal - reveal - - - - - $this->container->reveal() - $this->container->reveal() - $this->container->reveal() - - - $serverUrlHelper - $urlHelper - - - shouldNotBeCalled - shouldNotBeCalled - shouldNotBeCalled - shouldNotBeCalled - shouldNotBeCalled - shouldNotBeCalled - shouldNotBeCalled - shouldNotBeCalled - shouldNotBeCalled - shouldNotBeCalled - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - - - get - get - get - get - get - get - get - get - get - get - has - has - has - has - has - has - has - has - has - reveal - reveal - reveal - reveal - reveal - - + generateServerUrl generateUrl generateUrl getRouteResult getRouteResult - - array + array - - willReturn - willReturn - willReturn - willReturn - willReturn - - - generate - generate - generate - getRouteResult - getRouteResult - - - - $helper - $this->container->reveal() - $this->container->reveal() - $this->container->reveal() - $this->container->reveal() - $this->container->reveal() - $this->container->reveal() - $this->container->reveal() - $this->container->reveal() - $this->container->reveal() - $this->container->reveal() - $this->container->reveal() - TestExtension::class - \ZendTest\Expressive\Plates\stdClass::class - - - $helper - - - array - array - - - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - - - get - get - get - get - get - get - get - get - get - get - get - has - has - has - has - has - has - has - has - has - has - has - has - has - has - has - has - has - has - has - has - reveal - reveal - reveal - reveal - reveal - reveal - reveal - reveal - reveal - reveal - reveal - - - TestExtension - \ZendTest\Expressive\Plates\stdClass - - + + function (int $_errno, string $_errstr): void { + $this->errorCaught = true; + } + + $this->errorCaught - + $this->errorCaught - - $this->container->reveal() - $this->container->reveal() - $this->container->reveal() - $this->container->reveal() - $this->container->reveal() - - + Engine - - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - willReturn - - + $r->getValue($plates) - + $namespace ?: null - - get - get - get - get - get - get - has - has - has - has - has - has - has - has - has - has - has - reveal - reveal - reveal - reveal - reveal - - + assertIsArray - + $params - + array array - + null - + assertIsArray assertIsArray assertIsArray diff --git a/test/ConfigProviderTest.php b/test/ConfigProviderTest.php index e11bbbd..758093b 100644 --- a/test/ConfigProviderTest.php +++ b/test/ConfigProviderTest.php @@ -7,7 +7,7 @@ use Mezzio\Plates\ConfigProvider; use PHPUnit\Framework\TestCase; -class ConfigProviderTest extends TestCase +final class ConfigProviderTest extends TestCase { private ConfigProvider $provider; diff --git a/test/ExceptionTest.php b/test/ExceptionTest.php index f405cf3..57754f7 100644 --- a/test/ExceptionTest.php +++ b/test/ExceptionTest.php @@ -15,7 +15,7 @@ use function strrpos; use function substr; -class ExceptionTest extends TestCase +final class ExceptionTest extends TestCase { public function testExceptionInterfaceExtendsTemplateExceptionInterface(): void { diff --git a/test/Extension/EscaperExtensionFactoryTest.php b/test/Extension/EscaperExtensionFactoryTest.php index 88e7bc8..98c1d9f 100644 --- a/test/Extension/EscaperExtensionFactoryTest.php +++ b/test/Extension/EscaperExtensionFactoryTest.php @@ -8,58 +8,65 @@ use Laminas\Escaper\Exception\InvalidArgumentException; use Mezzio\Plates\Extension\EscaperExtension; use Mezzio\Plates\Extension\EscaperExtensionFactory; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; -use Prophecy\Prophecy\ProphecyInterface; use Psr\Container\ContainerInterface; use ReflectionClass; -class EscaperExtensionFactoryTest extends TestCase +final class EscaperExtensionFactoryTest extends TestCase { - use ProphecyTrait; - - /** @var ContainerInterface|ProphecyInterface */ - private $container; + /** @var ContainerInterface&MockObject */ + private ContainerInterface $container; public function setUp(): void { - $this->container = $this->prophesize(ContainerInterface::class); + $this->container = $this->createMock(ContainerInterface::class); } public function testFactoryWithoutConfig(): void { - $this->container->has('config')->willReturn(false); + $this->container->method('has') + ->with('config') + ->willReturn(false); $factory = new EscaperExtensionFactory(); - $extension = $factory($this->container->reveal()); + $extension = $factory($this->container); $this->assertInstanceOf(EscaperExtension::class, $extension); } public function testFactoryWithEmptyConfig(): void { - $this->container->has('config')->willReturn(true); - $this->container->get('config')->willReturn([]); + $this->container->method('has') + ->with('config') + ->willReturn(true); + $this->container->method('get') + ->with('config') + ->willReturn([]); $factory = new EscaperExtensionFactory(); - $extension = $factory($this->container->reveal()); + $extension = $factory($this->container); $this->assertInstanceOf(EscaperExtension::class, $extension); } public function testFactoryWithInvalidEncodingSetIn(): void { - $this->container->has('config')->willReturn(true); - $this->container->get('config')->willReturn([ - 'plates' => [ - 'encoding' => '', - ], - ]); + $this->container->method('has') + ->with('config') + ->willReturn(true); + $this->container->method('get') + ->with('config') + ->willReturn([ + 'plates' => [ + 'encoding' => '', + ], + ]); $factory = new EscaperExtensionFactory(); $this->expectException(InvalidArgumentException::class); - $factory($this->container->reveal()); + $factory($this->container); } /** @@ -67,15 +74,19 @@ public function testFactoryWithInvalidEncodingSetIn(): void */ public function testFactoryWithValidEncodingSetIn(): void { - $this->container->has('config')->willReturn(true); - $this->container->get('config')->willReturn([ - 'plates' => [ - 'encoding' => 'iso-8859-1', - ], - ]); + $this->container->method('has') + ->with('config') + ->willReturn(true); + $this->container->method('get') + ->with('config') + ->willReturn([ + 'plates' => [ + 'encoding' => 'iso-8859-1', + ], + ]); $factory = new EscaperExtensionFactory(); - $extension = $factory($this->container->reveal()); + $extension = $factory($this->container); $this->assertInstanceOf(EscaperExtension::class, $extension); diff --git a/test/Extension/EscaperExtensionTest.php b/test/Extension/EscaperExtensionTest.php index 98f200f..379e313 100644 --- a/test/Extension/EscaperExtensionTest.php +++ b/test/Extension/EscaperExtensionTest.php @@ -8,46 +8,43 @@ use League\Plates\Engine; use Mezzio\Plates\Extension\EscaperExtension; use PHPUnit\Framework\TestCase; -use Prophecy\Argument; -use Prophecy\PhpUnit\ProphecyTrait; -use function is_array; - -class EscaperExtensionTest extends TestCase +final class EscaperExtensionTest extends TestCase { - use ProphecyTrait; - public function testRegistersEscaperFunctionsWithEngine(): void { $extension = new EscaperExtension(); - $engine = $this->prophesize(Engine::class); - $engine - ->registerFunction('escapeHtml', Argument::that( - static fn($argument) => is_array($argument) && - $argument[0] instanceof Escaper && $argument[1] === 'escapeHtml' - ))->shouldBeCalled(); - $engine - ->registerFunction('escapeHtmlAttr', Argument::that( - static fn($argument) => is_array($argument) && - $argument[0] instanceof Escaper && $argument[1] === 'escapeHtmlAttr' - ))->shouldBeCalled(); - $engine - ->registerFunction('escapeJs', Argument::that( - static fn($argument) => is_array($argument) && - $argument[0] instanceof Escaper && $argument[1] === 'escapeJs' - ))->shouldBeCalled(); - $engine - ->registerFunction('escapeCss', Argument::that( - static fn($argument) => is_array($argument) && - $argument[0] instanceof Escaper && $argument[1] === 'escapeCss' - ))->shouldBeCalled(); - $engine - ->registerFunction('escapeUrl', Argument::that( - static fn($argument) => is_array($argument) && - $argument[0] instanceof Escaper && $argument[1] === 'escapeUrl' - ))->shouldBeCalled(); - - $extension->register($engine->reveal()); + $engine = $this->createMock(Engine::class); + $engine->expects(self::exactly(5)) + ->method('registerFunction') + ->with( + self::logicalOr( + 'escapeHtml', + 'escapeHtmlAttr', + 'escapeJs', + 'escapeCss', + 'escapeUrl', + ), + self::callback(function (array $callback): bool { + self::assertArrayHasKey(0, $callback); + self::assertArrayHasKey(1, $callback); + self::assertInstanceOf(Escaper::class, $callback[0]); + self::assertContains( + $callback[1], + [ + 'escapeHtml', + 'escapeHtmlAttr', + 'escapeJs', + 'escapeCss', + 'escapeUrl', + ] + ); + + return true; + }) + ); + + $extension->register($engine); } } diff --git a/test/Extension/UrlExtensionFactoryTest.php b/test/Extension/UrlExtensionFactoryTest.php index 8595607..c10822e 100644 --- a/test/Extension/UrlExtensionFactoryTest.php +++ b/test/Extension/UrlExtensionFactoryTest.php @@ -10,43 +10,46 @@ use Mezzio\Plates\Exception\MissingHelperException; use Mezzio\Plates\Extension\UrlExtension; use Mezzio\Plates\Extension\UrlExtensionFactory; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; -use Prophecy\Prophecy\ProphecyInterface; use Psr\Container\ContainerInterface; -class UrlExtensionFactoryTest extends TestCase +final class UrlExtensionFactoryTest extends TestCase { - use ProphecyTrait; + /** @var ContainerInterface&MockObject */ + private ContainerInterface $container; - /** @var ContainerInterface|ProphecyInterface */ - private $container; + /** @var UrlHelper&MockObject */ + private UrlHelper $urlHelper; - /** @var UrlHelper|ProphecyInterface */ - private $urlHelper; - - /** @var ServerUrlHelper|ProphecyInterface */ - private $serverUrlHelper; + /** @var ServerUrlHelper&MockObject */ + private ServerUrlHelper $serverUrlHelper; public function setUp(): void { - $this->container = $this->prophesize(ContainerInterface::class); - $this->urlHelper = $this->prophesize(UrlHelper::class); - $this->serverUrlHelper = $this->prophesize(ServerUrlHelper::class); + $this->container = $this->createMock(ContainerInterface::class); + $this->urlHelper = $this->createMock(UrlHelper::class); + $this->serverUrlHelper = $this->createMock(ServerUrlHelper::class); } public function testFactoryReturnsUrlExtensionInstanceWhenHelpersArePresent(): void { - $urlHelper = $this->urlHelper->reveal(); - $serverUrlHelper = $this->serverUrlHelper->reveal(); - - $this->container->has(UrlHelper::class)->willReturn(true); - $this->container->get(UrlHelper::class)->willReturn($urlHelper); - $this->container->has(ServerUrlHelper::class)->willReturn(true); - $this->container->get(ServerUrlHelper::class)->willReturn($serverUrlHelper); + $urlHelper = $this->urlHelper; + $serverUrlHelper = $this->serverUrlHelper; + + $this->container->method('has') + ->willReturnMap([ + [UrlHelper::class, true], + [ServerUrlHelper::class, true], + ]); + $this->container->method('get') + ->willReturnMap([ + [UrlHelper::class, $urlHelper], + [ServerUrlHelper::class, $serverUrlHelper], + ]); $factory = new UrlExtensionFactory(); - $extension = $factory($this->container->reveal()); + $extension = $factory($this->container); $this->assertInstanceOf(UrlExtension::class, $extension); $engine = $this->createMock(Engine::class); @@ -61,36 +64,35 @@ public function testFactoryReturnsUrlExtensionInstanceWhenHelpersArePresent(): v public function testFactoryRaisesExceptionIfUrlHelperIsMissing(): void { - $this->container->has(UrlHelper::class)->willReturn(false); - $this->container->has(UrlHelper::class)->willReturn(false); - $this->container->get(UrlHelper::class)->shouldNotBeCalled(); - $this->container->get(UrlHelper::class)->shouldNotBeCalled(); - $this->container->has(ServerUrlHelper::class)->shouldNotBeCalled(); - $this->container->has(ServerUrlHelper::class)->shouldNotBeCalled(); - $this->container->get(ServerUrlHelper::class)->shouldNotBeCalled(); - $this->container->get(ServerUrlHelper::class)->shouldNotBeCalled(); + $this->container->method('has') + ->willReturnMap([ + [UrlHelper::class, false], + [ServerUrlHelper::class, false], + ]); + $this->container->expects(self::never()) + ->method('get'); $factory = new UrlExtensionFactory(); $this->expectException(MissingHelperException::class); $this->expectExceptionMessage(UrlHelper::class); - $factory($this->container->reveal()); + $factory($this->container); } public function testFactoryRaisesExceptionIfServerUrlHelperIsMissing(): void { - $this->container->has(UrlHelper::class)->willReturn(true); - $this->container->get(UrlHelper::class)->shouldNotBeCalled(); - $this->container->get(UrlHelper::class)->shouldNotBeCalled(); - $this->container->has(ServerUrlHelper::class)->willReturn(false); - $this->container->has(ServerUrlHelper::class)->willReturn(false); - $this->container->get(ServerUrlHelper::class)->shouldNotBeCalled(); - $this->container->get(ServerUrlHelper::class)->shouldNotBeCalled(); + $this->container->method('has') + ->willReturnMap([ + [UrlHelper::class, true], + [ServerUrlHelper::class, false], + ]); + $this->container->expects(self::never()) + ->method('get'); $factory = new UrlExtensionFactory(); $this->expectException(MissingHelperException::class); $this->expectExceptionMessage(ServerUrlHelper::class); - $factory($this->container->reveal()); + $factory($this->container); } } diff --git a/test/Extension/UrlExtensionTest.php b/test/Extension/UrlExtensionTest.php index 4617898..bbf38d3 100644 --- a/test/Extension/UrlExtensionTest.php +++ b/test/Extension/UrlExtensionTest.php @@ -9,47 +9,42 @@ use Mezzio\Helper\UrlHelper; use Mezzio\Plates\Extension\UrlExtension; use Mezzio\Router\RouteResult; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; -use Prophecy\Prophecy\ProphecyInterface; -class UrlExtensionTest extends TestCase +final class UrlExtensionTest extends TestCase { - use ProphecyTrait; + /** @var UrlHelper&MockObject */ + private UrlHelper $urlHelper; - /** @var UrlHelper|ProphecyInterface */ - private $urlHelper; - - /** @var ServerUrlHelper|ProphecyInterface */ - private $serverUrlHelper; + /** @var ServerUrlHelper&MockObject */ + private ServerUrlHelper $serverUrlHelper; private UrlExtension $extension; public function setUp(): void { - $this->urlHelper = $this->prophesize(UrlHelper::class); - $this->serverUrlHelper = $this->prophesize(ServerUrlHelper::class); + $this->urlHelper = $this->createMock(UrlHelper::class); + $this->serverUrlHelper = $this->createMock(ServerUrlHelper::class); $this->extension = new UrlExtension( - $this->urlHelper->reveal(), - $this->serverUrlHelper->reveal() + $this->urlHelper, + $this->serverUrlHelper ); } public function testRegistersUrlFunctionWithEngine(): void { - $engine = $this->prophesize(Engine::class); - $engine - ->registerFunction('url', $this->urlHelper) - ->shouldBeCalled(); - $engine - ->registerFunction('serverurl', $this->serverUrlHelper) - ->shouldBeCalled(); + $engine = $this->createMock(Engine::class); $engine - ->registerFunction('route', [$this->urlHelper, 'getRouteResult']) - ->shouldBeCalled(); - - $this->extension->register($engine->reveal()); + ->expects(self::exactly(3)) + ->method('registerFunction') + ->with( + self::logicalOr('url', 'serverurl', 'route'), + self::logicalOr($this->urlHelper, $this->serverUrlHelper, [$this->urlHelper, 'getRouteResult']), + ); + + $this->extension->register($engine); } /** @return array}> */ @@ -70,19 +65,24 @@ public function urlHelperParams(): array */ public function testGenerateUrlProxiesToUrlHelper($route, array $params): void { - $this->urlHelper->generate($route, $params, [], null, [])->willReturn('/success'); + $this->urlHelper->method('generate') + ->with($route, $params, [], null, []) + ->willReturn('/success'); + $this->assertEquals('/success', $this->extension->generateUrl($route, $params)); } public function testUrlHelperAcceptsQueryParametersFragmentAndOptions(): void { - $this->urlHelper->generate( - 'resource', - ['id' => 'sha1'], - ['foo' => 'bar'], - 'fragment', - ['reuse_result_params' => true] - )->willReturn('PATH'); + $this->urlHelper->method('generate') + ->with( + 'resource', + ['id' => 'sha1'], + ['foo' => 'bar'], + 'fragment', + ['reuse_result_params' => true] + ) + ->willReturn('PATH'); $this->assertEquals( 'PATH', @@ -111,21 +111,25 @@ public function serverUrlHelperParams(): array */ public function testGenerateServerUrlProxiesToServerUrlHelper($path): void { - $this->serverUrlHelper->generate($path)->willReturn('/success'); + $this->serverUrlHelper->method('generate') + ->with($path) + ->willReturn('/success'); $this->assertEquals('/success', $this->extension->generateServerUrl($path)); } public function testGetRouteResultReturnsRouteResultWhenPopulated(): void { - $result = $this->prophesize(RouteResult::class); - $this->urlHelper->getRouteResult()->willReturn($result->reveal()); + $result = $this->createMock(RouteResult::class); + $this->urlHelper->method('getRouteResult') + ->willReturn($result); $this->assertInstanceOf(RouteResult::class, $this->extension->getRouteResult()); } public function testGetRouteResultReturnsNullWhenRouteResultNotPopulatedInUrlHelper(): void { - $this->urlHelper->getRouteResult()->willReturn(null); + $this->urlHelper->method('getRouteResult') + ->willReturn(null); $this->assertNull($this->extension->getRouteResult()); } diff --git a/test/PlatesEngineFactoryTest.php b/test/PlatesEngineFactoryTest.php index ef05c34..4717b71 100644 --- a/test/PlatesEngineFactoryTest.php +++ b/test/PlatesEngineFactoryTest.php @@ -11,74 +11,42 @@ use Mezzio\Helper\UrlHelper; use Mezzio\Plates\Exception\InvalidExtensionException; use Mezzio\Plates\Extension\EscaperExtension; -use Mezzio\Plates\Extension\UrlExtension; use Mezzio\Plates\PlatesEngineFactory; +use MezzioTest\Plates\TestAsset\DummyPsrContainer; use PHPUnit\Framework\TestCase; -use Prophecy\Argument; -use Prophecy\PhpUnit\ProphecyTrait; -use Prophecy\Prophecy\ProphecyInterface; -use Psr\Container\ContainerInterface; use stdClass; -use ZendTest\Expressive\Plates\TestAsset\TestExtension; -use function is_string; use function restore_error_handler; use function set_error_handler; use const E_USER_WARNING; -class PlatesEngineFactoryTest extends TestCase +final class PlatesEngineFactoryTest extends TestCase { - use ProphecyTrait; - - /** @var ContainerInterface|ProphecyInterface */ - private $container; + private DummyPsrContainer $container; public function setUp(): void { TestAsset\TestExtension::$engine = null; - $this->container = $this->prophesize(ContainerInterface::class); - - $this->container->has(UrlHelper::class)->willReturn(true); - $this->container->get(UrlHelper::class)->willReturn( - $this->prophesize(UrlHelper::class)->reveal() - ); - $this->container->has(ServerUrlHelper::class)->willReturn(true); - $this->container->get(ServerUrlHelper::class)->willReturn( - $this->prophesize(ServerUrlHelper::class)->reveal() - ); + $this->container = new DummyPsrContainer(); - $this->container->has(UrlExtension::class)->willReturn(false); - - $this->container->has('Zend\Expressive\Plates\Extension\UrlExtension')->willReturn(false); - $this->container->has(EscaperExtension::class)->willReturn(false); - $this->container->has('Zend\Expressive\Plates\Extension\EscaperExtension')->willReturn(false); + $this->container->services[UrlHelper::class] = $this->createStub(UrlHelper::class); + $this->container->services[ServerUrlHelper::class] = $this->createStub(ServerUrlHelper::class); } - public function testFactoryReturnsPlatesEngine(): PlatesEngine + public function testUrlExtensionIsRegisteredByDefault(): void { - $this->container->has('config')->willReturn(false); - $factory = new PlatesEngineFactory(); - $engine = $factory($this->container->reveal()); - $this->assertInstanceOf(PlatesEngine::class, $engine); - return $engine; - } + $engine = (new PlatesEngineFactory())($this->container); - /** - * @depends testFactoryReturnsPlatesEngine - */ - public function testUrlExtensionIsRegisteredByDefault(PlatesEngine $engine): void - { $this->assertTrue($engine->doesFunctionExist('url')); $this->assertTrue($engine->doesFunctionExist('serverurl')); } - /** - * @depends testFactoryReturnsPlatesEngine - */ - public function testEscaperExtensionIsRegisteredByDefault(PlatesEngine $engine): void + public function testEscaperExtensionIsRegisteredByDefault(): void { + $engine = (new PlatesEngineFactory())($this->container); + $this->assertTrue($engine->doesFunctionExist('escapeHtml')); $this->assertTrue($engine->doesFunctionExist('escapeHtmlAttr')); $this->assertTrue($engine->doesFunctionExist('escapeJs')); @@ -91,15 +59,10 @@ public function testEscaperExtensionIsRegisteredByDefault(PlatesEngine $engine): */ public function testEscaperExtensionIsRegisteredFromContainer(): void { - $escaperExtension = new EscaperExtension(); - - $this->container->has(EscaperExtension::class)->willReturn(true); - $this->container->has('config')->willReturn(false); - - $this->container->get(EscaperExtension::class)->willReturn($escaperExtension); + $this->container->services[EscaperExtension::class] = new EscaperExtension(); $factory = new PlatesEngineFactory(); - $engine = $factory($this->container->reveal()); + $engine = $factory($this->container); $this->assertTrue($engine->doesFunctionExist('escapeHtml')); $this->assertTrue($engine->doesFunctionExist('escapeHtmlAttr')); @@ -110,32 +73,29 @@ public function testEscaperExtensionIsRegisteredFromContainer(): void public function testFactoryCanRegisterConfiguredExtensions(): void { - $extensionOne = $this->prophesize(ExtensionInterface::class); - $extensionOne->register(Argument::type(PlatesEngine::class))->shouldBeCalled(); - - $extensionTwo = $this->prophesize(ExtensionInterface::class); - $extensionTwo->register(Argument::type(PlatesEngine::class))->shouldBeCalled(); - $this->container->has('ExtensionTwo')->willReturn(true); - $this->container->get('ExtensionTwo')->willReturn($extensionTwo->reveal()); - - $this->container->has(TestAsset\TestExtension::class)->willReturn(false); - - $this->container->has(TestExtension::class)->willReturn(false); - - $config = [ + $extensionOne = $this->createMock(ExtensionInterface::class); + $extensionOne->expects(self::atLeastOnce()) + ->method('register') + ->with(self::isInstanceOf(PlatesEngine::class)); + + $extensionTwo = $this->createMock(ExtensionInterface::class); + $extensionTwo->expects(self::atLeastOnce()) + ->method('register') + ->with(self::isInstanceOf(PlatesEngine::class)); + + $this->container->services['ExtensionTwo'] = $extensionTwo; + $this->container->services['config'] = [ 'plates' => [ 'extensions' => [ - $extensionOne->reveal(), + $extensionOne, 'ExtensionTwo', TestAsset\TestExtension::class, ], ], ]; - $this->container->has('config')->willReturn(true); - $this->container->get('config')->willReturn($config); $factory = new PlatesEngineFactory(); - $engine = $factory($this->container->reveal()); + $engine = $factory($this->container); $this->assertInstanceOf(PlatesEngine::class, $engine); // Test that the TestExtension was registered. The other two extensions @@ -143,84 +103,66 @@ public function testFactoryCanRegisterConfiguredExtensions(): void $this->assertSame($engine, TestAsset\TestExtension::$engine); } - public function invalidExtensions(): array + /** @return non-empty-array */ + public static function invalidExtensions(): array { return [ 'non-class-string' => ['not-a-class'], ]; } - /** - * @dataProvider invalidExtensions - * @param mixed $extension - */ - public function testFactoryRaisesExceptionForInvalidExtensions($extension): void + /** @dataProvider invalidExtensions */ + public function testFactoryRaisesExceptionForInvalidExtensions(string $extension): void { - $config = [ + $this->container->services['config'] = [ 'plates' => [ 'extensions' => [ $extension, ], ], ]; - $this->container->has('config')->willReturn(true); - $this->container->get('config')->willReturn($config); - - if (is_string($extension)) { - $this->container->has($extension)->willReturn(false); - } $factory = new PlatesEngineFactory(); $this->expectException(InvalidExtensionException::class); - $factory($this->container->reveal()); + $factory($this->container); } public function testFactoryRaisesExceptionWhenAttemptingToInjectAnInvalidExtensionService(): void { - $config = [ + $this->container->services['FooExtension'] = new stdClass(); + $this->container->services['config'] = [ 'plates' => [ 'extensions' => [ 'FooExtension', ], ], ]; - $this->container->has('config')->willReturn(true); - $this->container->get('config')->willReturn($config); - - $this->container->has('FooExtension')->willReturn(true); - $this->container->get('FooExtension')->willReturn(new stdClass()); $factory = new PlatesEngineFactory(); $this->expectException(InvalidExtensionException::class); $this->expectExceptionMessage('ExtensionInterface'); - $factory($this->container->reveal()); + $factory($this->container); } public function testFactoryRaisesExceptionWhenNonServiceClassIsAnInvalidExtension(): void { - $config = [ + $this->container->services['config'] = [ 'plates' => [ 'extensions' => [ stdClass::class, ], ], ]; - $this->container->has('config')->willReturn(true); - $this->container->get('config')->willReturn($config); - - $this->container->has(stdClass::class)->willReturn(false); - - $this->container->has(\ZendTest\Expressive\Plates\stdClass::class)->willReturn(false); $factory = new PlatesEngineFactory(); $this->expectException(InvalidExtensionException::class); $this->expectExceptionMessage('ExtensionInterface'); - $factory($this->container->reveal()); + $factory($this->container); } public function testExceptionIsRaisedIfMultiplePathsSpecifyDefaultNamespace(): void { - $config = [ + $this->container->services['config'] = [ 'templates' => [ 'paths' => [ 0 => __DIR__ . '/TestAsset/bar', @@ -228,22 +170,21 @@ public function testExceptionIsRaisedIfMultiplePathsSpecifyDefaultNamespace(): v ], ], ]; - $this->container->has('config')->willReturn(true); - $this->container->get('config')->willReturn($config); + $factory = new PlatesEngineFactory(); // phpcs:ignore WebimpressCodingStandard.NamingConventions.ValidVariableName.NotCamelCaps set_error_handler(function (int $_errno, string $_errstr): void { $this->errorCaught = true; }, E_USER_WARNING); - $factory($this->container->reveal()); + $factory($this->container); restore_error_handler(); $this->assertTrue($this->errorCaught, 'Did not detect duplicate path for default namespace'); } public function testExceptionIsRaisedIfMultiplePathsInSameNamespace(): void { - $config = [ + $this->container->services['config'] = [ 'templates' => [ 'paths' => [ 'bar' => [ @@ -253,34 +194,32 @@ public function testExceptionIsRaisedIfMultiplePathsInSameNamespace(): void ], ], ]; - $this->container->has('config')->willReturn(true); - $this->container->get('config')->willReturn($config); + $factory = new PlatesEngineFactory(); $this->expectException(LogicException::class); $this->expectExceptionMessage('already being used'); - $factory($this->container->reveal()); + $factory($this->container); } public function testSetExtensionByTemplatesConfig(): void { - $config = [ + $this->container->services['config'] = [ 'templates' => [ 'extension' => 'html.twig', ], ]; - $this->container->has('config')->willReturn(true); - $this->container->get('config')->willReturn($config); + $factory = new PlatesEngineFactory(); - $engine = $factory($this->container->reveal()); + $engine = $factory($this->container); $this->assertSame('html.twig', $engine->getFileExtension()); } public function testOverrideExtensionByPlatesConfig(): void { - $config = [ + $this->container->services['config'] = [ 'templates' => [ 'extension' => 'html.twig', ], @@ -288,16 +227,16 @@ public function testOverrideExtensionByPlatesConfig(): void 'extension' => 'plates.php', ], ]; - $this->container->has('config')->willReturn(true); - $this->container->get('config')->willReturn($config); + $factory = new PlatesEngineFactory(); - $engine = $factory($this->container->reveal()); + $engine = $factory($this->container); $this->assertSame('plates.php', $engine->getFileExtension()); } - public function provideHelpersToUnregister(): array + /** @return non-empty-array}> */ + public static function provideHelpersToUnregister(): array { return [ 'url-only' => [[UrlHelper::class]], @@ -308,17 +247,16 @@ public function provideHelpersToUnregister(): array /** * @dataProvider provideHelpersToUnregister - * @param array $helpers + * @param non-empty-list $helpers */ public function testUrlExtensionIsNotLoadedIfHelpersAreNotRegistered(array $helpers): void { - $this->container->has('config')->willReturn(false); foreach ($helpers as $helper) { - $this->container->has($helper)->willReturn(false); + unset($this->container->services[$helper]); } $factory = new PlatesEngineFactory(); - $engine = $factory($this->container->reveal()); + $engine = $factory($this->container); $this->assertFalse($engine->doesFunctionExist('url')); $this->assertFalse($engine->doesFunctionExist('serverurl')); diff --git a/test/PlatesRendererFactoryTest.php b/test/PlatesRendererFactoryTest.php index 9a6a438..8030fca 100644 --- a/test/PlatesRendererFactoryTest.php +++ b/test/PlatesRendererFactoryTest.php @@ -8,49 +8,33 @@ use League\Plates\Engine as PlatesEngine; use Mezzio\Helper\ServerUrlHelper; use Mezzio\Helper\UrlHelper; -use Mezzio\Plates\Extension\EscaperExtension; -use Mezzio\Plates\Extension\UrlExtension; use Mezzio\Plates\PlatesEngineFactory; use Mezzio\Plates\PlatesRenderer; use Mezzio\Plates\PlatesRendererFactory; use Mezzio\Template\TemplatePath; +use MezzioTest\Plates\TestAsset\DummyPsrContainer; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; -use Prophecy\Prophecy\ProphecyInterface; -use Psr\Container\ContainerInterface; use ReflectionClass; use ReflectionProperty; use function sprintf; -class PlatesRendererFactoryTest extends TestCase +final class PlatesRendererFactoryTest extends TestCase { - use ProphecyTrait; - - /** @var ContainerInterface|ProphecyInterface */ - private $container; - - private bool $errorCaught = false; + private DummyPsrContainer $container; public function setUp(): void { - $this->errorCaught = false; - $this->container = $this->prophesize(ContainerInterface::class); + $this->container = new DummyPsrContainer(); } public function configureEngineService(): void { - $this->container->has(UrlExtension::class)->willReturn(false); - $this->container->has('Zend\Expressive\Plates\Extension\UrlExtension')->willReturn(false); - $this->container->has(EscaperExtension::class)->willReturn(false); - $this->container->has('Zend\Expressive\Plates\Extension\EscaperExtension')->willReturn(false); - $this->container->has(UrlHelper::class)->willReturn(true); - $this->container->has(ServerUrlHelper::class)->willReturn(true); - $this->container->get(UrlHelper::class)->willReturn($this->prophesize(UrlHelper::class)->reveal()); - $this->container->get(ServerUrlHelper::class)->willReturn($this->prophesize(ServerUrlHelper::class)->reveal()); - $engineFactory = new PlatesEngineFactory(); - $this->container->get(PlatesEngine::class)->willReturn($engineFactory($this->container->reveal())); + + $this->container->services[UrlHelper::class] = $this->createMock(UrlHelper::class); + $this->container->services[ServerUrlHelper::class] = $this->createMock(ServerUrlHelper::class); + $this->container->services[PlatesEngine::class] = $engineFactory($this->container); } public function fetchPlatesEngine(PlatesRenderer $plates): Engine @@ -129,10 +113,9 @@ public function assertPathNamespaceContains( public function testCallingFactoryWithNoConfigReturnsPlatesInstance(): PlatesRenderer { - $this->container->has('config')->willReturn(false); $this->configureEngineService(); $factory = new PlatesRendererFactory(); - $plates = $factory($this->container->reveal()); + $plates = $factory($this->container); $this->assertInstanceOf(PlatesRenderer::class, $plates); return $plates; } @@ -149,25 +132,24 @@ public function testUnconfiguredPlatesInstanceContainsNoPaths(PlatesRenderer $pl public function testConfiguresTemplateSuffix(): void { - $config = [ + $this->container->services['config'] = [ 'templates' => [ 'extension' => 'html', ], ]; - $this->container->has('config')->willReturn(true); - $this->container->get('config')->willReturn($config); + $this->configureEngineService(); $factory = new PlatesRendererFactory(); - $plates = $factory($this->container->reveal()); + $plates = $factory($this->container); $engine = $this->fetchPlatesEngine($plates); - $this->assertEquals($config['templates']['extension'], $engine->getFileExtension()); + $this->assertEquals('html', $engine->getFileExtension()); } public function testConfiguresPaths(): void { - $config = [ + $this->container->services['config'] = [ 'templates' => [ 'paths' => [ 'foo' => __DIR__ . '/TestAsset/bar', @@ -176,11 +158,10 @@ public function testConfiguresPaths(): void ], ], ]; - $this->container->has('config')->willReturn(true); - $this->container->get('config')->willReturn($config); + $this->configureEngineService(); $factory = new PlatesRendererFactory(); - $plates = $factory($this->container->reveal()); + $plates = $factory($this->container); $paths = $plates->getPaths(); $this->assertPathsHasNamespace('foo', $paths); @@ -198,19 +179,17 @@ public function testConfiguresPaths(): void public function testWillPullPlatesEngineFromContainerIfPresent(): void { - $engine = $this->prophesize(PlatesEngine::class); - $this->container->has(PlatesEngine::class)->willReturn(true); - $this->container->get(PlatesEngine::class)->willReturn($engine->reveal()); + $engine = $this->createMock(PlatesEngine::class); - $this->container->has('config')->willReturn(false); + $this->container->services[PlatesEngine::class] = $engine; $factory = new PlatesRendererFactory(); - $renderer = $factory($this->container->reveal()); + $renderer = $factory($this->container); $class = new ReflectionClass($renderer); $property = $class->getProperty('template'); $property->setAccessible(true); $template = $property->getValue($renderer); - $this->assertSame($engine->reveal(), $template); + $this->assertSame($engine, $template); } } diff --git a/test/PlatesRendererTest.php b/test/PlatesRendererTest.php index fcbfe0d..2a46334 100644 --- a/test/PlatesRendererTest.php +++ b/test/PlatesRendererTest.php @@ -10,7 +10,6 @@ use Mezzio\Template\Exception; use Mezzio\Template\TemplatePath; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; use function array_shift; use function file_get_contents; @@ -24,10 +23,8 @@ use const E_NOTICE; use const E_USER_WARNING; -class PlatesRendererTest extends TestCase +final class PlatesRendererTest extends TestCase { - use ProphecyTrait; - private Engine $platesEngine; private bool $error; diff --git a/test/TestAsset/DummyPsrContainer.php b/test/TestAsset/DummyPsrContainer.php new file mode 100644 index 0000000..a09ec59 --- /dev/null +++ b/test/TestAsset/DummyPsrContainer.php @@ -0,0 +1,32 @@ +services[$id] + ?? (static function () { + throw new class extends RuntimeException implements NotFoundExceptionInterface { + }; + })(); + } + + /** @inheritDoc */ + public function has($id): bool + { + return array_key_exists($id, $this->services); + } +}