diff --git a/.gitignore b/.gitignore index cb7f3e4ba..c8aeadf8d 100644 --- a/.gitignore +++ b/.gitignore @@ -82,5 +82,6 @@ compose.override.yaml ###> phpunit/phpunit ### /phpunit.xml /.phpunit.result.cache +/.phpunit.cache ###< phpunit/phpunit ### diff --git a/composer.json b/composer.json index 42aa83d4d..359d3efa1 100644 --- a/composer.json +++ b/composer.json @@ -98,7 +98,7 @@ "bobdenotter/configuration-notices": "^1.2", "bobdenotter/weatherwidget": "^1.1", "bolt/newswidget": "^1.3", - "dama/doctrine-test-bundle": "^6.6.0", + "dama/doctrine-test-bundle": "^6.0", "nyholm/psr7": "^1.4", "ondram/ci-detector": "^4.1", "php-http/curl-client": "^2.2", @@ -108,7 +108,7 @@ "phpstan/phpstan": "^1.2.0", "phpstan/phpstan-doctrine": "^1.0", "phpstan/phpstan-symfony": "^1.0.1", - "phpunit/phpunit": "^8.5", + "phpunit/phpunit": "^9.6", "se/selenium-server-standalone": "^3.141", "symfony/browser-kit": "^5.4", "symfony/css-selector": "^5.4", @@ -123,7 +123,8 @@ "composer/package-versions-deprecated": true, "drupol/composer-packages": true, "symfony/flex": true, - "php-http/discovery": true + "php-http/discovery": true, + "dealerdirect/phpcodesniffer-composer-installer": false } }, "extra": { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 7f403a2c4..d83a45b17 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,21 +1,19 @@ - + failOnWarning="true" + bootstrap="phpunit.bootstrap.php"> - + - @@ -26,15 +24,12 @@ - tests/php/ - - + - diff --git a/symfony.lock b/symfony.lock index fd3656993..0aca9af59 100644 --- a/symfony.lock +++ b/symfony.lock @@ -389,9 +389,6 @@ "phpunit/php-timer": { "version": "2.1.2" }, - "phpunit/php-token-stream": { - "version": "3.1.1" - }, "phpunit/phpunit": { "version": "4.7", "recipe": { @@ -475,9 +472,6 @@ "sebastian/recursion-context": { "version": "3.0.0" }, - "sebastian/resource-operations": { - "version": "2.0.1" - }, "sebastian/type": { "version": "1.1.4" }, diff --git a/tests/php/Controller/Backend/ContentEditControllerTest.bak b/tests/php/Controller/Backend/ContentEditControllerTest.bak index b7f0d64b8..ecbd10af3 100644 --- a/tests/php/Controller/Backend/ContentEditControllerTest.bak +++ b/tests/php/Controller/Backend/ContentEditControllerTest.bak @@ -51,8 +51,8 @@ X-Robots-Tag: noindex public function testCreateNewComplexNestedContent(): void { - // (2) use self::$container to access the service container - $container = self::$container; + // (2) use self::getContainer() to access the service container + $container = self::getContainer(); $admin = $this->getEm()->getRepository(User::class)->findOneByUsername('admin'); $this->client->loginUser($admin); diff --git a/tests/php/Menu/FrontendMenuBuilderTest.php b/tests/php/Menu/FrontendMenuBuilderTest.php index a612b79ae..0a2033894 100644 --- a/tests/php/Menu/FrontendMenuBuilderTest.php +++ b/tests/php/Menu/FrontendMenuBuilderTest.php @@ -4,6 +4,7 @@ namespace Bolt\Tests\Menu; +use RuntimeException; use Bolt\Collection\DeepCollection; use Bolt\Menu\FrontendMenuBuilder; use Bolt\Tests\DbAwareTestCase; @@ -31,7 +32,7 @@ protected function setUp(): void { parent::setUp(); - $this->menuBuilder = self::$container->get(FrontendMenuBuilder::class); + $this->menuBuilder = self::getContainer()->get(FrontendMenuBuilder::class); // Setup mocks for testing the localized menu $this->twig = $this->createMock(Environment::class); @@ -42,9 +43,21 @@ protected function setUp(): void $this->request->attributes = $this->createMock(ParameterBag::class); $this->request->attributes + ->expects($matcher = $this->atMost(2)) ->method('get') - ->withConsecutive(['_route'], ['_route_params']) - ->willReturn('homepage_locale', []); + ->willReturnCallback(function (string $route) use ($matcher) { + if ($matcher->getInvocationCount() === 1) { + $this->assertSame('_route', $route); + return 'homepage_locale'; + } + + if ($matcher->getInvocationCount() === 2) { + $this->assertSame('_route_params', $route); + return []; + } + + throw new RuntimeException('Unexpected call'); + }); $this->app = $this->createMock(AppVariable::class); $this->app->method('getRequest')->willReturn($this->request); $this->twig->method('getGlobals')->willReturn(['app' => $this->app]); @@ -52,7 +65,7 @@ protected function setUp(): void public function testNonExistingMenu(): void { - $this->expectException(\RuntimeException::class); + $this->expectException(RuntimeException::class); $this->menuBuilder->buildMenu($this->twig, 'foo'); } diff --git a/tests/php/Twig/ContentExtensionTestCase.php b/tests/php/Twig/ContentExtensionTestCase.php index f51081fc2..3c7fe5550 100644 --- a/tests/php/Twig/ContentExtensionTestCase.php +++ b/tests/php/Twig/ContentExtensionTestCase.php @@ -16,6 +16,7 @@ use Doctrine\Common\Collections\ArrayCollection; use PHPUnit\Framework\MockObject\MockObject; use Twig\Environment; +use RuntimeException; class ContentExtensionTestCase extends DbAwareTestCase { @@ -35,7 +36,7 @@ protected function setUp(): void { parent::setUp(); - $this->extension = self::$container->get(ContentExtension::class); + $this->extension = self::getContainer()->get(ContentExtension::class); $this->content = $this->createMock(Content::class); $this->definition = $this->createMock(ContentType::class); $this->content->method('getDefinition') @@ -45,20 +46,50 @@ protected function setUp(): void public function testTitle(): void { - $this->definition->method('has') - ->withConsecutive(['title_format']) - ->willReturn(true); - $this->definition->method('get') - ->withConsecutive(['title_format']) - ->willReturn('{number}: {title}'); + $this->definition + ->expects($matcher = $this->exactly(1)) + ->method('has') + ->willReturnCallback(function (...$parameters) use ($matcher) { + if ($matcher->getInvocationCount() === 1) { + $this->assertSame('title_format', $parameters[0]); + } + return true; + }); + $this->definition + ->expects($matcher = $this->exactly(1)) + ->method('get') + ->willReturnCallback(function (...$parameters) use ($matcher) { + if ($matcher->getInvocationCount() === 1) { + $this->assertSame('title_format', $parameters[0]); + } + return '{number}: {title}'; + }); $this->content->method('getId') ->willReturn(1); - $this->content->method('hasField') - ->withConsecutive(['number'], ['title']) - ->willReturnOnConsecutiveCalls(false, true); - $this->content->method('getField') - ->withConsecutive(['title']) - ->willReturn($this->field); + $this->content + ->expects($matcher = $this->exactly(2)) + ->method('hasField') + ->willReturnCallback(function (...$parameters) use ($matcher) { + if ($matcher->getInvocationCount() === 1) { + $this->assertSame('number', $parameters[0]); + return false; + } + if ($matcher->getInvocationCount() === 2) { + $this->assertSame('title', $parameters[0]); + return true; + } + + throw new RuntimeException('Unexpected call'); + }); + $this->content + ->expects($matcher = $this->exactly(1)) + ->method('getField') + ->willReturnCallback(function (...$parameters) use ($matcher) { + if ($matcher->getInvocationCount() === 1) { + $this->assertSame('title', $parameters[0]); + } + return $this->field; + }); $this->field->method('isTranslatable') ->willReturn(false); $this->field->method('__toString') @@ -69,17 +100,41 @@ public function testTitle(): void public function testTitleFields(): void { - $this->definition->method('has') - ->withConsecutive(['title_format']) - ->willReturn(true); - $this->definition->method('get') - ->withConsecutive(['title_format']) - ->willReturn('{number}: {title}'); + $this->definition + ->expects($matcher = $this->exactly(1)) + ->method('has') + ->willReturnCallback(function (...$parameters) use ($matcher) { + if ($matcher->getInvocationCount() === 1) { + $this->assertSame('title_format', $parameters[0]); + } + return true; + }); + $this->definition + ->expects($matcher = $this->exactly(1)) + ->method('get') + ->willReturnCallback(function (...$parameters) use ($matcher) { + if ($matcher->getInvocationCount() === 1) { + $this->assertSame('title_format', $parameters[0]); + } + return '{number}: {title}'; + }); $this->content->method('getId') ->willReturn(1); - $this->content->method('hasField') - ->withConsecutive(['number'], ['title']) - ->willReturnOnConsecutiveCalls(false, true); + $this->content + ->expects($matcher = $this->exactly(2)) + ->method('hasField') + ->willReturnCallback(function (...$parameters) use ($matcher) { + if ($matcher->getInvocationCount() === 1) { + $this->assertSame('number', $parameters[0]); + return false; + } + if ($matcher->getInvocationCount() === 2) { + $this->assertSame('title', $parameters[0]); + return true; + } + + throw new RuntimeException('Unexpected call'); + }); $this->assertSame(['number', 'title'], $this->extension->getTitleFieldsNames($this->content)); } @@ -96,9 +151,15 @@ public function testContentImage(): void $this->assertNull($this->extension->getImage($this->content)); - $imagefield->method('get') - ->withConsecutive(['filename']) - ->willReturn('example.jpg'); + $imagefield + ->expects($matcher = $this->exactly(1)) + ->method('get') + ->willReturnCallback(function (...$parameters) use ($matcher) { + if ($matcher->getInvocationCount() === 1) { + $this->assertSame('filename', $parameters[0]); + } + return 'example.jpg'; + }); $this->assertSame($imagefield, $this->extension->getImage($this->content)); } @@ -107,9 +168,14 @@ public function testContentImageWithImagelist(): void $field1 = $this->createMock(Field::class); $field2 = $this->createMock(Field::class); $image1 = $this->createMock(ImageField::class); - $image1->method('get') - ->withConsecutive(['filename']) - ->willReturn('testimage.jpg'); + $image1->expects($matcher = $this->exactly(1)) + ->method('get') + ->willReturnCallback(function (...$parameters) use ($matcher) { + if ($matcher->getInvocationCount() === 1) { + $this->assertSame('filename', $parameters[0]); + } + return 'testimage.jpg'; + }); $image2 = $this->createMock(ImageField::class); $imagelist = $this->createMock(ImagelistField::class); $field3 = $this->createMock(Field::class); @@ -128,24 +194,66 @@ public function testExcerptOnString(): void public function testExceptFromFormatShort(): void { - $this->definition->method('get') - ->withConsecutive(['excerpt_format']) - ->willReturn('{subheading}: {body}'); - - $this->content->method('hasField') - ->withConsecutive(['subheading'], ['body']) - ->willReturnOnConsecutiveCalls(true, true); + $this->definition + ->expects($matcher = $this->exactly(1)) + ->method('get') + ->willReturnCallback(function (...$parameters) use ($matcher) { + if ($matcher->getInvocationCount() === 1) { + $this->assertSame('excerpt_format', $parameters[0]); + } + return '{subheading}: {body}'; + }); + + $this->content + ->expects($matcher = $this->exactly(2)) + ->method('hasField') + ->willReturnCallback(function (...$parameters) use ($matcher) { + if ($matcher->getInvocationCount() === 1) { + $this->assertSame('subheading', $parameters[0]); + return true; + } + if ($matcher->getInvocationCount() === 2) { + $this->assertSame('body', $parameters[0]); + return true; + } + + throw new RuntimeException('Unexpected call'); + }); $field1 = $this->createMock(Field::class); $field2 = $this->createMock(Field::class); $field1->method('__toString')->willReturn("In this week's news"); $field2->method('__toString')->willReturn('Bolt 4 is pretty awesome.'); - $this->content->method('getField') - ->withConsecutive(['subheading'], ['body']) - ->willReturnOnConsecutiveCalls($field1, $field2); - $this->definition->method('has') - ->withConsecutive(['excerpt_format'], ['subheading'], ['body']) - ->willReturn(true); + $this->content + ->expects($matcher = $this->exactly(2)) + ->method('getField') + ->willReturnCallback(function (...$parameters) use ($matcher, $field1, $field2) { + if ($matcher->getInvocationCount() === 1) { + $this->assertSame('subheading', $parameters[0]); + return $field1; + } + if ($matcher->getInvocationCount() === 2) { + $this->assertSame('body', $parameters[0]); + return $field2; + } + + throw new RuntimeException('Unexpected call'); + }); + $this->definition + ->expects($matcher = $this->exactly(3)) + ->method('has') + ->willReturnCallback(function (...$parameters) use ($matcher) { + if ($matcher->getInvocationCount() === 1) { + $this->assertSame('excerpt_format', $parameters[0]); + } + if ($matcher->getInvocationCount() === 2) { + $this->assertSame('subheading', $parameters[0]); + } + if ($matcher->getInvocationCount() === 3) { + $this->assertSame('body', $parameters[0]); + } + return true; + }); $this->content->method('getId') ->willReturn(1); @@ -154,24 +262,66 @@ public function testExceptFromFormatShort(): void public function testExceptFromFormatFull(): void { - $this->definition->method('get') - ->withConsecutive(['excerpt_format']) - ->willReturn('{subheading}: {body}'); - - $this->content->method('hasField') - ->withConsecutive(['subheading'], ['body']) - ->willReturnOnConsecutiveCalls(true, true); + $this->definition + ->expects($matcher = $this->exactly(1)) + ->method('get') + ->willReturnCallback(function (...$parameters) use ($matcher) { + if ($matcher->getInvocationCount() === 1) { + $this->assertSame('excerpt_format', $parameters[0]); + } + return '{subheading}: {body}'; + }); + + $this->content + ->expects($matcher = $this->exactly(2)) + ->method('hasField') + ->willReturnCallback(function (...$parameters) use ($matcher) { + if ($matcher->getInvocationCount() === 1) { + $this->assertSame('subheading', $parameters[0]); + return true; + } + if ($matcher->getInvocationCount() === 2) { + $this->assertSame('body', $parameters[0]); + return true; + } + + throw new RuntimeException('Unexpected call'); + }); $field1 = $this->createMock(Field::class); $field2 = $this->createMock(Field::class); $field1->method('__toString')->willReturn("In this week's news"); $field2->method('__toString')->willReturn('Bolt 4 is pretty awesome.'); - $this->content->method('getField') - ->withConsecutive(['subheading'], ['body']) - ->willReturnOnConsecutiveCalls($field1, $field2); - $this->definition->method('has') - ->withConsecutive(['excerpt_format'], ['subheading'], ['body']) - ->willReturn(true); + $this->content + ->expects($matcher = $this->exactly(2)) + ->method('getField') + ->willReturnCallback(function (...$parameters) use ($matcher, $field1, $field2) { + if ($matcher->getInvocationCount() === 1) { + $this->assertSame('subheading', $parameters[0]); + return $field1; + } + if ($matcher->getInvocationCount() === 2) { + $this->assertSame('body', $parameters[0]); + return $field2; + } + + throw new RuntimeException('Unexpected call'); + }); + $this->definition + ->expects($matcher = $this->exactly(3)) + ->method('has') + ->willReturnCallback(function (...$parameters) use ($matcher) { + if ($matcher->getInvocationCount() === 1) { + $this->assertSame('excerpt_format', $parameters[0]); + } + if ($matcher->getInvocationCount() === 2) { + $this->assertSame('subheading', $parameters[0]); + } + if ($matcher->getInvocationCount() === 3) { + $this->assertSame('body', $parameters[0]); + } + return true; + }); $this->content->method('getId') ->willReturn(1); diff --git a/tests/php/Twig/FieldExtensionTestCase.php b/tests/php/Twig/FieldExtensionTestCase.php index 7092bebcc..fbe751634 100644 --- a/tests/php/Twig/FieldExtensionTestCase.php +++ b/tests/php/Twig/FieldExtensionTestCase.php @@ -24,7 +24,7 @@ protected function setUp(): void { parent::setUp(); - $this->extension = self::$container->get(FieldExtension::class); + $this->extension = self::getContainer()->get(FieldExtension::class); $this->field = $this->createMock(Field::class); $this->fieldType = $this->createMock(FieldType::class); @@ -34,18 +34,30 @@ protected function setUp(): void public function testFieldLabel(): void { - $this->fieldType->method('get') - ->withConsecutive(['label']) - ->wilLReturn('Test field'); + $this->fieldType + ->expects($matcher = $this->exactly(1)) + ->method('get') + ->willReturnCallback(function (...$parameters) use ($matcher) { + if ($matcher->getInvocationCount() === 1) { + $this->assertSame('label', $parameters[0]); + } + return 'Test field'; + }); $this->assertSame('Test field', $this->extension->getLabel($this->field)); } public function testFieldType(): void { - $this->fieldType->method('get') - ->withConsecutive(['type']) - ->willReturn('embed'); + $this->fieldType + ->expects($matcher = $this->exactly(1)) + ->method('get') + ->willReturnCallback(function (...$parameters) use ($matcher) { + if ($matcher->getInvocationCount() === 1) { + $this->assertSame('type', $parameters[0]); + } + return 'embed'; + }); $this->assertSame('embed', $this->extension->getType($this->field)); } diff --git a/tests/php/Twig/TextExtensionTestCase.php b/tests/php/Twig/TextExtensionTestCase.php index 486ade7cf..738bbd0ab 100644 --- a/tests/php/Twig/TextExtensionTestCase.php +++ b/tests/php/Twig/TextExtensionTestCase.php @@ -16,7 +16,7 @@ protected function setUp(): void { parent::setUp(); - $this->extension = self::$container->get(TextExtension::class); + $this->extension = self::getContainer()->get(TextExtension::class); } public function testPlainText(): void diff --git a/tests/php/Twig/TokenParserTestCase.php b/tests/php/Twig/TokenParserTestCase.php index 58b848ace..f84a8d61d 100644 --- a/tests/php/Twig/TokenParserTestCase.php +++ b/tests/php/Twig/TokenParserTestCase.php @@ -4,6 +4,8 @@ namespace Bolt\Tests\Twig; +use PHPUnit\Framework\TestCase; +use ReflectionProperty; use Twig\Environment; use Twig\Loader\LoaderInterface; use Twig\Node\Node; @@ -17,7 +19,7 @@ * @author Gawain Lynch * @author Bob den Otter */ -abstract class TokenParserTestCase extends \PHPUnit\Framework\TestCase +abstract class TokenParserTestCase extends TestCase { protected function getParser(TokenStream $tokenStream, AbstractTokenParser $testParser): Parser { @@ -26,7 +28,7 @@ protected function getParser(TokenStream $tokenStream, AbstractTokenParser $test $parser = new Parser($env); $parser->setParent(new Node()); - $p = new \ReflectionProperty($parser, 'stream'); + $p = new ReflectionProperty($parser, 'stream'); $p->setAccessible(true); $p->setValue($parser, $tokenStream); diff --git a/tests/php/Utils/StrTest.php b/tests/php/Utils/StrTest.php index 2c2de5bd2..8118ecd38 100644 --- a/tests/php/Utils/StrTest.php +++ b/tests/php/Utils/StrTest.php @@ -4,9 +4,10 @@ namespace Bolt\Tests\Utils; +use PHPUnit\Framework\TestCase; use Bolt\Common\Str; -class StrTest extends \PHPUnit\Framework\TestCase +class StrTest extends TestCase { public function testSlug(): void { diff --git a/tests/php/Widget/Injector/RequestZoneTest.php b/tests/php/Widget/Injector/RequestZoneTest.php index 4135a27b2..3b3635b57 100644 --- a/tests/php/Widget/Injector/RequestZoneTest.php +++ b/tests/php/Widget/Injector/RequestZoneTest.php @@ -4,6 +4,7 @@ namespace Bolt\Tests\Widget\Injector; +use ReflectionClass; use Bolt\Widget\Injector\RequestZone; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; @@ -13,7 +14,7 @@ class RequestZoneTest extends TestCase { public function providerZone() { - $o = new \ReflectionClass(RequestZone::class); + $o = new ReflectionClass(RequestZone::class); $constants = (new Collection(array_keys($o->getConstants()))) ->filter(function ($v) { return mb_strpos($v, 'NOWHERE') === false;