diff --git a/lib/symfony/framework-bundle/Test/BrowserKitAssertionsTrait.php b/lib/symfony/framework-bundle/Test/BrowserKitAssertionsTrait.php new file mode 100644 index 000000000..a6d4fed33 --- /dev/null +++ b/lib/symfony/framework-bundle/Test/BrowserKitAssertionsTrait.php @@ -0,0 +1,197 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Test; + +use PHPUnit\Framework\Constraint\Constraint; +use PHPUnit\Framework\Constraint\LogicalAnd; +use PHPUnit\Framework\Constraint\LogicalNot; +use PHPUnit\Framework\ExpectationFailedException; +use Symfony\Component\BrowserKit\AbstractBrowser; +use Symfony\Component\BrowserKit\Test\Constraint as BrowserKitConstraint; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\Test\Constraint as ResponseConstraint; + +/** + * Ideas borrowed from Laravel Dusk's assertions. + * + * @see https://laravel.com/docs/5.7/dusk#available-assertions + */ +trait BrowserKitAssertionsTrait +{ + public static function assertResponseIsSuccessful(string $message = ''): void + { + self::assertThatForResponse(new ResponseConstraint\ResponseIsSuccessful(), $message); + } + + public static function assertResponseStatusCodeSame(int $expectedCode, string $message = ''): void + { + self::assertThatForResponse(new ResponseConstraint\ResponseStatusCodeSame($expectedCode), $message); + } + + public static function assertResponseFormatSame(?string $expectedFormat, string $message = ''): void + { + self::assertThatForResponse(new ResponseConstraint\ResponseFormatSame(self::getRequest(), $expectedFormat), $message); + } + + public static function assertResponseRedirects(string $expectedLocation = null, int $expectedCode = null, string $message = ''): void + { + $constraint = new ResponseConstraint\ResponseIsRedirected(); + if ($expectedLocation) { + if (class_exists(ResponseConstraint\ResponseHeaderLocationSame::class)) { + $locationConstraint = new ResponseConstraint\ResponseHeaderLocationSame(self::getRequest(), $expectedLocation); + } else { + $locationConstraint = new ResponseConstraint\ResponseHeaderSame('Location', $expectedLocation); + } + + $constraint = LogicalAnd::fromConstraints($constraint, $locationConstraint); + } + if ($expectedCode) { + $constraint = LogicalAnd::fromConstraints($constraint, new ResponseConstraint\ResponseStatusCodeSame($expectedCode)); + } + + self::assertThatForResponse($constraint, $message); + } + + public static function assertResponseHasHeader(string $headerName, string $message = ''): void + { + self::assertThatForResponse(new ResponseConstraint\ResponseHasHeader($headerName), $message); + } + + public static function assertResponseNotHasHeader(string $headerName, string $message = ''): void + { + self::assertThatForResponse(new LogicalNot(new ResponseConstraint\ResponseHasHeader($headerName)), $message); + } + + public static function assertResponseHeaderSame(string $headerName, string $expectedValue, string $message = ''): void + { + self::assertThatForResponse(new ResponseConstraint\ResponseHeaderSame($headerName, $expectedValue), $message); + } + + public static function assertResponseHeaderNotSame(string $headerName, string $expectedValue, string $message = ''): void + { + self::assertThatForResponse(new LogicalNot(new ResponseConstraint\ResponseHeaderSame($headerName, $expectedValue)), $message); + } + + public static function assertResponseHasCookie(string $name, string $path = '/', string $domain = null, string $message = ''): void + { + self::assertThatForResponse(new ResponseConstraint\ResponseHasCookie($name, $path, $domain), $message); + } + + public static function assertResponseNotHasCookie(string $name, string $path = '/', string $domain = null, string $message = ''): void + { + self::assertThatForResponse(new LogicalNot(new ResponseConstraint\ResponseHasCookie($name, $path, $domain)), $message); + } + + public static function assertResponseCookieValueSame(string $name, string $expectedValue, string $path = '/', string $domain = null, string $message = ''): void + { + self::assertThatForResponse(LogicalAnd::fromConstraints( + new ResponseConstraint\ResponseHasCookie($name, $path, $domain), + new ResponseConstraint\ResponseCookieValueSame($name, $expectedValue, $path, $domain) + ), $message); + } + + public static function assertResponseIsUnprocessable(string $message = ''): void + { + self::assertThatForResponse(new ResponseConstraint\ResponseIsUnprocessable(), $message); + } + + public static function assertBrowserHasCookie(string $name, string $path = '/', string $domain = null, string $message = ''): void + { + self::assertThatForClient(new BrowserKitConstraint\BrowserHasCookie($name, $path, $domain), $message); + } + + public static function assertBrowserNotHasCookie(string $name, string $path = '/', string $domain = null, string $message = ''): void + { + self::assertThatForClient(new LogicalNot(new BrowserKitConstraint\BrowserHasCookie($name, $path, $domain)), $message); + } + + public static function assertBrowserCookieValueSame(string $name, string $expectedValue, bool $raw = false, string $path = '/', string $domain = null, string $message = ''): void + { + self::assertThatForClient(LogicalAnd::fromConstraints( + new BrowserKitConstraint\BrowserHasCookie($name, $path, $domain), + new BrowserKitConstraint\BrowserCookieValueSame($name, $expectedValue, $raw, $path, $domain) + ), $message); + } + + public static function assertRequestAttributeValueSame(string $name, string $expectedValue, string $message = ''): void + { + self::assertThat(self::getRequest(), new ResponseConstraint\RequestAttributeValueSame($name, $expectedValue), $message); + } + + public static function assertRouteSame(string $expectedRoute, array $parameters = [], string $message = ''): void + { + $constraint = new ResponseConstraint\RequestAttributeValueSame('_route', $expectedRoute); + $constraints = []; + foreach ($parameters as $key => $value) { + $constraints[] = new ResponseConstraint\RequestAttributeValueSame($key, $value); + } + if ($constraints) { + $constraint = LogicalAnd::fromConstraints($constraint, ...$constraints); + } + + self::assertThat(self::getRequest(), $constraint, $message); + } + + public static function assertThatForResponse(Constraint $constraint, string $message = ''): void + { + try { + self::assertThat(self::getResponse(), $constraint, $message); + } catch (ExpectationFailedException $exception) { + if (($serverExceptionMessage = self::getResponse()->headers->get('X-Debug-Exception')) + && ($serverExceptionFile = self::getResponse()->headers->get('X-Debug-Exception-File'))) { + $serverExceptionFile = explode(':', $serverExceptionFile); + $exception->__construct($exception->getMessage(), $exception->getComparisonFailure(), new \ErrorException(rawurldecode($serverExceptionMessage), 0, 1, rawurldecode($serverExceptionFile[0]), $serverExceptionFile[1]), $exception->getPrevious()); + } + + throw $exception; + } + } + + public static function assertThatForClient(Constraint $constraint, string $message = ''): void + { + self::assertThat(self::getClient(), $constraint, $message); + } + + protected static function getClient(AbstractBrowser $newClient = null): ?AbstractBrowser + { + static $client; + + if (0 < \func_num_args()) { + return $client = $newClient; + } + + if (!$client instanceof AbstractBrowser) { + static::fail(sprintf('A client must be set to make assertions on it. Did you forget to call "%s::createClient()"?', __CLASS__)); + } + + return $client; + } + + private static function getResponse(): Response + { + if (!$response = self::getClient()->getResponse()) { + static::fail('A client must have an HTTP Response to make assertions. Did you forget to make an HTTP request?'); + } + + return $response; + } + + private static function getRequest(): Request + { + if (!$request = self::getClient()->getRequest()) { + static::fail('A client must have an HTTP Request to make assertions. Did you forget to make an HTTP request?'); + } + + return $request; + } +} diff --git a/lib/symfony/framework-bundle/Test/DomCrawlerAssertionsTrait.php b/lib/symfony/framework-bundle/Test/DomCrawlerAssertionsTrait.php new file mode 100644 index 000000000..a16709461 --- /dev/null +++ b/lib/symfony/framework-bundle/Test/DomCrawlerAssertionsTrait.php @@ -0,0 +1,151 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Test; + +use PHPUnit\Framework\Constraint\LogicalAnd; +use PHPUnit\Framework\Constraint\LogicalNot; +use Symfony\Component\DomCrawler\Crawler; +use Symfony\Component\DomCrawler\Test\Constraint as DomCrawlerConstraint; +use Symfony\Component\DomCrawler\Test\Constraint\CrawlerSelectorExists; + +/** + * Ideas borrowed from Laravel Dusk's assertions. + * + * @see https://laravel.com/docs/5.7/dusk#available-assertions + */ +trait DomCrawlerAssertionsTrait +{ + public static function assertSelectorExists(string $selector, string $message = ''): void + { + self::assertThat(self::getCrawler(), new DomCrawlerConstraint\CrawlerSelectorExists($selector), $message); + } + + public static function assertSelectorNotExists(string $selector, string $message = ''): void + { + self::assertThat(self::getCrawler(), new LogicalNot(new DomCrawlerConstraint\CrawlerSelectorExists($selector)), $message); + } + + public static function assertSelectorCount(int $expectedCount, string $selector, string $message = ''): void + { + self::assertThat(self::getCrawler(), new DomCrawlerConstraint\CrawlerSelectorCount($expectedCount, $selector), $message); + } + + public static function assertSelectorTextContains(string $selector, string $text, string $message = ''): void + { + self::assertThat(self::getCrawler(), LogicalAnd::fromConstraints( + new DomCrawlerConstraint\CrawlerSelectorExists($selector), + new DomCrawlerConstraint\CrawlerSelectorTextContains($selector, $text) + ), $message); + } + + public static function assertAnySelectorTextContains(string $selector, string $text, string $message = ''): void + { + self::assertThat(self::getCrawler(), LogicalAnd::fromConstraints( + new DomCrawlerConstraint\CrawlerSelectorExists($selector), + new DomCrawlerConstraint\CrawlerAnySelectorTextContains($selector, $text) + ), $message); + } + + public static function assertSelectorTextSame(string $selector, string $text, string $message = ''): void + { + self::assertThat(self::getCrawler(), LogicalAnd::fromConstraints( + new DomCrawlerConstraint\CrawlerSelectorExists($selector), + new DomCrawlerConstraint\CrawlerSelectorTextSame($selector, $text) + ), $message); + } + + public static function assertAnySelectorTextSame(string $selector, string $text, string $message = ''): void + { + self::assertThat(self::getCrawler(), LogicalAnd::fromConstraints( + new DomCrawlerConstraint\CrawlerSelectorExists($selector), + new DomCrawlerConstraint\CrawlerAnySelectorTextSame($selector, $text) + ), $message); + } + + public static function assertSelectorTextNotContains(string $selector, string $text, string $message = ''): void + { + self::assertThat(self::getCrawler(), LogicalAnd::fromConstraints( + new DomCrawlerConstraint\CrawlerSelectorExists($selector), + new LogicalNot(new DomCrawlerConstraint\CrawlerSelectorTextContains($selector, $text)) + ), $message); + } + + public static function assertAnySelectorTextNotContains(string $selector, string $text, string $message = ''): void + { + self::assertThat(self::getCrawler(), LogicalAnd::fromConstraints( + new DomCrawlerConstraint\CrawlerSelectorExists($selector), + new LogicalNot(new DomCrawlerConstraint\CrawlerAnySelectorTextContains($selector, $text)) + ), $message); + } + + public static function assertPageTitleSame(string $expectedTitle, string $message = ''): void + { + self::assertSelectorTextSame('title', $expectedTitle, $message); + } + + public static function assertPageTitleContains(string $expectedTitle, string $message = ''): void + { + self::assertSelectorTextContains('title', $expectedTitle, $message); + } + + public static function assertInputValueSame(string $fieldName, string $expectedValue, string $message = ''): void + { + self::assertThat(self::getCrawler(), LogicalAnd::fromConstraints( + new DomCrawlerConstraint\CrawlerSelectorExists("input[name=\"$fieldName\"]"), + new DomCrawlerConstraint\CrawlerSelectorAttributeValueSame("input[name=\"$fieldName\"]", 'value', $expectedValue) + ), $message); + } + + public static function assertInputValueNotSame(string $fieldName, string $expectedValue, string $message = ''): void + { + self::assertThat(self::getCrawler(), LogicalAnd::fromConstraints( + new DomCrawlerConstraint\CrawlerSelectorExists("input[name=\"$fieldName\"]"), + new LogicalNot(new DomCrawlerConstraint\CrawlerSelectorAttributeValueSame("input[name=\"$fieldName\"]", 'value', $expectedValue)) + ), $message); + } + + public static function assertCheckboxChecked(string $fieldName, string $message = ''): void + { + self::assertThat(self::getCrawler(), new CrawlerSelectorExists("input[name=\"$fieldName\"]:checked"), $message); + } + + public static function assertCheckboxNotChecked(string $fieldName, string $message = ''): void + { + self::assertThat(self::getCrawler(), new LogicalNot(new CrawlerSelectorExists("input[name=\"$fieldName\"]:checked")), $message); + } + + public static function assertFormValue(string $formSelector, string $fieldName, string $value, string $message = ''): void + { + $node = self::getCrawler()->filter($formSelector); + self::assertNotEmpty($node, sprintf('Form "%s" not found.', $formSelector)); + $values = $node->form()->getValues(); + self::assertArrayHasKey($fieldName, $values, $message ?: sprintf('Field "%s" not found in form "%s".', $fieldName, $formSelector)); + self::assertSame($value, $values[$fieldName]); + } + + public static function assertNoFormValue(string $formSelector, string $fieldName, string $message = ''): void + { + $node = self::getCrawler()->filter($formSelector); + self::assertNotEmpty($node, sprintf('Form "%s" not found.', $formSelector)); + $values = $node->form()->getValues(); + self::assertArrayNotHasKey($fieldName, $values, $message ?: sprintf('Field "%s" has a value in form "%s".', $fieldName, $formSelector)); + } + + private static function getCrawler(): Crawler + { + if (!$crawler = self::getClient()->getCrawler()) { + static::fail('A client must have a crawler to make assertions. Did you forget to make an HTTP request?'); + } + + return $crawler; + } +} diff --git a/lib/symfony/framework-bundle/Test/HttpClientAssertionsTrait.php b/lib/symfony/framework-bundle/Test/HttpClientAssertionsTrait.php new file mode 100644 index 000000000..bed835fa1 --- /dev/null +++ b/lib/symfony/framework-bundle/Test/HttpClientAssertionsTrait.php @@ -0,0 +1,134 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Test; + +use Symfony\Bundle\FrameworkBundle\KernelBrowser; +use Symfony\Component\HttpClient\DataCollector\HttpClientDataCollector; + +/* + * @author Mathieu Santostefano + */ + +trait HttpClientAssertionsTrait +{ + public static function assertHttpClientRequest(string $expectedUrl, string $expectedMethod = 'GET', string|array $expectedBody = null, array $expectedHeaders = [], string $httpClientId = 'http_client'): void + { + /** @var KernelBrowser $client */ + $client = static::getClient(); + + if (!($profile = $client->getProfile())) { + static::fail('The Profiler must be enabled for the current request. Please ensure to call "$client->enableProfiler()" before making the request.'); + } + + /** @var HttpClientDataCollector $httpClientDataCollector */ + $httpClientDataCollector = $profile->getCollector('http_client'); + $expectedRequestHasBeenFound = false; + + if (!\array_key_exists($httpClientId, $httpClientDataCollector->getClients())) { + static::fail(sprintf('HttpClient "%s" is not registered.', $httpClientId)); + } + + foreach ($httpClientDataCollector->getClients()[$httpClientId]['traces'] as $trace) { + if (($expectedUrl !== $trace['info']['url'] && $expectedUrl !== $trace['url']) + || $expectedMethod !== $trace['method'] + ) { + continue; + } + + if (null !== $expectedBody) { + $actualBody = null; + + if (null !== $trace['options']['body'] && null === $trace['options']['json']) { + $actualBody = \is_string($trace['options']['body']) ? $trace['options']['body'] : $trace['options']['body']->getValue(true); + } + + if (null === $trace['options']['body'] && null !== $trace['options']['json']) { + $actualBody = $trace['options']['json']->getValue(true); + } + + if (!$actualBody) { + continue; + } + + if ($expectedBody === $actualBody) { + $expectedRequestHasBeenFound = true; + + if (!$expectedHeaders) { + break; + } + } + } + + if ($expectedHeaders) { + $actualHeaders = $trace['options']['headers'] ?? []; + + foreach ($actualHeaders as $headerKey => $actualHeader) { + if (\array_key_exists($headerKey, $expectedHeaders) + && $expectedHeaders[$headerKey] === $actualHeader->getValue(true) + ) { + $expectedRequestHasBeenFound = true; + break 2; + } + } + } + + $expectedRequestHasBeenFound = true; + break; + } + + self::assertTrue($expectedRequestHasBeenFound, 'The expected request has not been called: "'.$expectedMethod.'" - "'.$expectedUrl.'"'); + } + + public function assertNotHttpClientRequest(string $unexpectedUrl, string $expectedMethod = 'GET', string $httpClientId = 'http_client'): void + { + /** @var KernelBrowser $client */ + $client = static::getClient(); + + if (!$profile = $client->getProfile()) { + static::fail('The Profiler must be enabled for the current request. Please ensure to call "$client->enableProfiler()" before making the request.'); + } + + /** @var HttpClientDataCollector $httpClientDataCollector */ + $httpClientDataCollector = $profile->getCollector('http_client'); + $unexpectedUrlHasBeenFound = false; + + if (!\array_key_exists($httpClientId, $httpClientDataCollector->getClients())) { + static::fail(sprintf('HttpClient "%s" is not registered.', $httpClientId)); + } + + foreach ($httpClientDataCollector->getClients()[$httpClientId]['traces'] as $trace) { + if (($unexpectedUrl === $trace['info']['url'] || $unexpectedUrl === $trace['url']) + && $expectedMethod === $trace['method'] + ) { + $unexpectedUrlHasBeenFound = true; + break; + } + } + + self::assertFalse($unexpectedUrlHasBeenFound, sprintf('Unexpected URL called: "%s" - "%s"', $expectedMethod, $unexpectedUrl)); + } + + public static function assertHttpClientRequestCount(int $count, string $httpClientId = 'http_client'): void + { + /** @var KernelBrowser $client */ + $client = static::getClient(); + + if (!($profile = $client->getProfile())) { + static::fail('The Profiler must be enabled for the current request. Please ensure to call "$client->enableProfiler()" before making the request.'); + } + + /** @var HttpClientDataCollector $httpClientDataCollector */ + $httpClientDataCollector = $profile->getCollector('http_client'); + + self::assertCount($count, $httpClientDataCollector->getClients()[$httpClientId]['traces']); + } +} diff --git a/lib/symfony/framework-bundle/Test/KernelTestCase.php b/lib/symfony/framework-bundle/Test/KernelTestCase.php new file mode 100644 index 000000000..8d27b757f --- /dev/null +++ b/lib/symfony/framework-bundle/Test/KernelTestCase.php @@ -0,0 +1,137 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Test; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; +use Symfony\Component\HttpKernel\KernelInterface; +use Symfony\Contracts\Service\ResetInterface; + +/** + * KernelTestCase is the base class for tests needing a Kernel. + * + * @author Fabien Potencier + */ +abstract class KernelTestCase extends TestCase +{ + use MailerAssertionsTrait; + use NotificationAssertionsTrait; + + protected static $class; + + /** + * @var KernelInterface + */ + protected static $kernel; + + protected static $booted = false; + + protected function tearDown(): void + { + static::ensureKernelShutdown(); + static::$class = null; + static::$kernel = null; + static::$booted = false; + } + + /** + * @throws \RuntimeException + * @throws \LogicException + */ + protected static function getKernelClass(): string + { + if (!isset($_SERVER['KERNEL_CLASS']) && !isset($_ENV['KERNEL_CLASS'])) { + throw new \LogicException(sprintf('You must set the KERNEL_CLASS environment variable to the fully-qualified class name of your Kernel in phpunit.xml / phpunit.xml.dist or override the "%1$s::createKernel()" or "%1$s::getKernelClass()" method.', static::class)); + } + + if (!class_exists($class = $_ENV['KERNEL_CLASS'] ?? $_SERVER['KERNEL_CLASS'])) { + throw new \RuntimeException(sprintf('Class "%s" doesn\'t exist or cannot be autoloaded. Check that the KERNEL_CLASS value in phpunit.xml matches the fully-qualified class name of your Kernel or override the "%s::createKernel()" method.', $class, static::class)); + } + + return $class; + } + + /** + * Boots the Kernel for this test. + */ + protected static function bootKernel(array $options = []): KernelInterface + { + static::ensureKernelShutdown(); + + $kernel = static::createKernel($options); + $kernel->boot(); + static::$kernel = $kernel; + static::$booted = true; + + return static::$kernel; + } + + /** + * Provides a dedicated test container with access to both public and private + * services. The container will not include private services that have been + * inlined or removed. Private services will be removed when they are not + * used by other services. + * + * Using this method is the best way to get a container from your test code. + * + * @return Container + */ + protected static function getContainer(): ContainerInterface + { + if (!static::$booted) { + static::bootKernel(); + } + + try { + return self::$kernel->getContainer()->get('test.service_container'); + } catch (ServiceNotFoundException $e) { + throw new \LogicException('Could not find service "test.service_container". Try updating the "framework.test" config to "true".', 0, $e); + } + } + + /** + * Creates a Kernel. + * + * Available options: + * + * * environment + * * debug + */ + protected static function createKernel(array $options = []): KernelInterface + { + static::$class ??= static::getKernelClass(); + + $env = $options['environment'] ?? $_ENV['APP_ENV'] ?? $_SERVER['APP_ENV'] ?? 'test'; + $debug = $options['debug'] ?? $_ENV['APP_DEBUG'] ?? $_SERVER['APP_DEBUG'] ?? true; + + return new static::$class($env, $debug); + } + + /** + * Shuts the kernel down if it was used in the test - called by the tearDown method by default. + */ + protected static function ensureKernelShutdown() + { + if (null !== static::$kernel) { + static::$kernel->boot(); + $container = static::$kernel->getContainer(); + static::$kernel->shutdown(); + static::$booted = false; + + if ($container instanceof ResetInterface) { + $container->reset(); + } + } + } +} diff --git a/lib/symfony/framework-bundle/Test/MailerAssertionsTrait.php b/lib/symfony/framework-bundle/Test/MailerAssertionsTrait.php new file mode 100644 index 000000000..83643421e --- /dev/null +++ b/lib/symfony/framework-bundle/Test/MailerAssertionsTrait.php @@ -0,0 +1,138 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Test; + +use PHPUnit\Framework\Constraint\LogicalNot; +use Symfony\Component\Mailer\Event\MessageEvent; +use Symfony\Component\Mailer\Event\MessageEvents; +use Symfony\Component\Mailer\Test\Constraint as MailerConstraint; +use Symfony\Component\Mime\RawMessage; +use Symfony\Component\Mime\Test\Constraint as MimeConstraint; + +trait MailerAssertionsTrait +{ + public static function assertEmailCount(int $count, string $transport = null, string $message = ''): void + { + self::assertThat(self::getMessageMailerEvents(), new MailerConstraint\EmailCount($count, $transport), $message); + } + + public static function assertQueuedEmailCount(int $count, string $transport = null, string $message = ''): void + { + self::assertThat(self::getMessageMailerEvents(), new MailerConstraint\EmailCount($count, $transport, true), $message); + } + + public static function assertEmailIsQueued(MessageEvent $event, string $message = ''): void + { + self::assertThat($event, new MailerConstraint\EmailIsQueued(), $message); + } + + public static function assertEmailIsNotQueued(MessageEvent $event, string $message = ''): void + { + self::assertThat($event, new LogicalNot(new MailerConstraint\EmailIsQueued()), $message); + } + + public static function assertEmailAttachmentCount(RawMessage $email, int $count, string $message = ''): void + { + self::assertThat($email, new MimeConstraint\EmailAttachmentCount($count), $message); + } + + public static function assertEmailTextBodyContains(RawMessage $email, string $text, string $message = ''): void + { + self::assertThat($email, new MimeConstraint\EmailTextBodyContains($text), $message); + } + + public static function assertEmailTextBodyNotContains(RawMessage $email, string $text, string $message = ''): void + { + self::assertThat($email, new LogicalNot(new MimeConstraint\EmailTextBodyContains($text)), $message); + } + + public static function assertEmailHtmlBodyContains(RawMessage $email, string $text, string $message = ''): void + { + self::assertThat($email, new MimeConstraint\EmailHtmlBodyContains($text), $message); + } + + public static function assertEmailHtmlBodyNotContains(RawMessage $email, string $text, string $message = ''): void + { + self::assertThat($email, new LogicalNot(new MimeConstraint\EmailHtmlBodyContains($text)), $message); + } + + public static function assertEmailHasHeader(RawMessage $email, string $headerName, string $message = ''): void + { + self::assertThat($email, new MimeConstraint\EmailHasHeader($headerName), $message); + } + + public static function assertEmailNotHasHeader(RawMessage $email, string $headerName, string $message = ''): void + { + self::assertThat($email, new LogicalNot(new MimeConstraint\EmailHasHeader($headerName)), $message); + } + + public static function assertEmailHeaderSame(RawMessage $email, string $headerName, string $expectedValue, string $message = ''): void + { + self::assertThat($email, new MimeConstraint\EmailHeaderSame($headerName, $expectedValue), $message); + } + + public static function assertEmailHeaderNotSame(RawMessage $email, string $headerName, string $expectedValue, string $message = ''): void + { + self::assertThat($email, new LogicalNot(new MimeConstraint\EmailHeaderSame($headerName, $expectedValue)), $message); + } + + public static function assertEmailAddressContains(RawMessage $email, string $headerName, string $expectedValue, string $message = ''): void + { + self::assertThat($email, new MimeConstraint\EmailAddressContains($headerName, $expectedValue), $message); + } + + public static function assertEmailSubjectContains(RawMessage $email, string $expectedValue, string $message = ''): void + { + self::assertThat($email, new MimeConstraint\EmailSubjectContains($expectedValue), $message); + } + + public static function assertEmailSubjectNotContains(RawMessage $email, string $expectedValue, string $message = ''): void + { + self::assertThat($email, new LogicalNot(new MimeConstraint\EmailSubjectContains($expectedValue)), $message); + } + + /** + * @return MessageEvent[] + */ + public static function getMailerEvents(string $transport = null): array + { + return self::getMessageMailerEvents()->getEvents($transport); + } + + public static function getMailerEvent(int $index = 0, string $transport = null): ?MessageEvent + { + return self::getMailerEvents($transport)[$index] ?? null; + } + + /** + * @return RawMessage[] + */ + public static function getMailerMessages(string $transport = null): array + { + return self::getMessageMailerEvents()->getMessages($transport); + } + + public static function getMailerMessage(int $index = 0, string $transport = null): ?RawMessage + { + return self::getMailerMessages($transport)[$index] ?? null; + } + + private static function getMessageMailerEvents(): MessageEvents + { + $container = static::getContainer(); + if ($container->has('mailer.message_logger_listener')) { + return $container->get('mailer.message_logger_listener')->getEvents(); + } + + static::fail('A client must have Mailer enabled to make email assertions. Did you forget to require symfony/mailer?'); + } +} diff --git a/lib/symfony/framework-bundle/Test/NotificationAssertionsTrait.php b/lib/symfony/framework-bundle/Test/NotificationAssertionsTrait.php new file mode 100644 index 000000000..53d24cb12 --- /dev/null +++ b/lib/symfony/framework-bundle/Test/NotificationAssertionsTrait.php @@ -0,0 +1,100 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Test; + +use PHPUnit\Framework\Constraint\LogicalNot; +use Symfony\Component\Notifier\Event\MessageEvent; +use Symfony\Component\Notifier\Event\NotificationEvents; +use Symfony\Component\Notifier\Message\MessageInterface; +use Symfony\Component\Notifier\Test\Constraint as NotifierConstraint; + +/* + * @author Smaïne Milianni + */ +trait NotificationAssertionsTrait +{ + public static function assertNotificationCount(int $count, string $transportName = null, string $message = ''): void + { + self::assertThat(self::getNotificationEvents(), new NotifierConstraint\NotificationCount($count, $transportName), $message); + } + + public static function assertQueuedNotificationCount(int $count, string $transportName = null, string $message = ''): void + { + self::assertThat(self::getNotificationEvents(), new NotifierConstraint\NotificationCount($count, $transportName, true), $message); + } + + public static function assertNotificationIsQueued(MessageEvent $event, string $message = ''): void + { + self::assertThat($event, new NotifierConstraint\NotificationIsQueued(), $message); + } + + public static function assertNotificationIsNotQueued(MessageEvent $event, string $message = ''): void + { + self::assertThat($event, new LogicalNot(new NotifierConstraint\NotificationIsQueued()), $message); + } + + public static function assertNotificationSubjectContains(MessageInterface $notification, string $text, string $message = ''): void + { + self::assertThat($notification, new NotifierConstraint\NotificationSubjectContains($text), $message); + } + + public static function assertNotificationSubjectNotContains(MessageInterface $notification, string $text, string $message = ''): void + { + self::assertThat($notification, new LogicalNot(new NotifierConstraint\NotificationSubjectContains($text)), $message); + } + + public static function assertNotificationTransportIsEqual(MessageInterface $notification, string $transportName = null, string $message = ''): void + { + self::assertThat($notification, new NotifierConstraint\NotificationTransportIsEqual($transportName), $message); + } + + public static function assertNotificationTransportIsNotEqual(MessageInterface $notification, string $transportName = null, string $message = ''): void + { + self::assertThat($notification, new LogicalNot(new NotifierConstraint\NotificationTransportIsEqual($transportName)), $message); + } + + /** + * @return MessageEvent[] + */ + public static function getNotifierEvents(string $transportName = null): array + { + return self::getNotificationEvents()->getEvents($transportName); + } + + public static function getNotifierEvent(int $index = 0, string $transportName = null): ?MessageEvent + { + return self::getNotifierEvents($transportName)[$index] ?? null; + } + + /** + * @return MessageInterface[] + */ + public static function getNotifierMessages(string $transportName = null): array + { + return self::getNotificationEvents()->getMessages($transportName); + } + + public static function getNotifierMessage(int $index = 0, string $transportName = null): ?MessageInterface + { + return self::getNotifierMessages($transportName)[$index] ?? null; + } + + public static function getNotificationEvents(): NotificationEvents + { + $container = static::getContainer(); + if ($container->has('notifier.notification_logger_listener')) { + return $container->get('notifier.notification_logger_listener')->getEvents(); + } + + static::fail('A client must have Notifier enabled to make notifications assertions. Did you forget to require symfony/notifier?'); + } +} diff --git a/lib/symfony/framework-bundle/Test/TestBrowserToken.php b/lib/symfony/framework-bundle/Test/TestBrowserToken.php new file mode 100644 index 000000000..8bf365eb0 --- /dev/null +++ b/lib/symfony/framework-bundle/Test/TestBrowserToken.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Test; + +use Symfony\Component\Security\Core\Authentication\Token\AbstractToken; +use Symfony\Component\Security\Core\User\UserInterface; + +/** + * A very limited token that is used to login in tests using the KernelBrowser. + * + * @author Wouter de Jong + */ +class TestBrowserToken extends AbstractToken +{ + private string $firewallName; + + public function __construct(array $roles = [], UserInterface $user = null, string $firewallName = 'main') + { + parent::__construct($roles); + + if (null !== $user) { + $this->setUser($user); + } + + $this->firewallName = $firewallName; + } + + public function getFirewallName(): string + { + return $this->firewallName; + } + + public function getCredentials(): mixed + { + return null; + } + + public function __serialize(): array + { + return [$this->firewallName, parent::__serialize()]; + } + + public function __unserialize(array $data): void + { + [$this->firewallName, $parentData] = $data; + + parent::__unserialize($parentData); + } +} diff --git a/lib/symfony/framework-bundle/Test/TestContainer.php b/lib/symfony/framework-bundle/Test/TestContainer.php new file mode 100644 index 000000000..e1e7a8592 --- /dev/null +++ b/lib/symfony/framework-bundle/Test/TestContainer.php @@ -0,0 +1,126 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Test; + +use Psr\Container\ContainerInterface; +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; +use Symfony\Component\HttpKernel\KernelInterface; + +/** + * A special container used in tests. This gives access to both public and + * private services. The container will not include private services that have + * been inlined or removed. Private services will be removed when they are not + * used by other services. + * + * @author Nicolas Grekas + * + * @internal + */ +class TestContainer extends Container +{ + public function __construct( + private KernelInterface $kernel, + private string $privateServicesLocatorId, + private array $renamedIds = [], + ) { + } + + public function compile(): void + { + $this->getPublicContainer()->compile(); + } + + public function isCompiled(): bool + { + return $this->getPublicContainer()->isCompiled(); + } + + public function getParameterBag(): ParameterBagInterface + { + return $this->getPublicContainer()->getParameterBag(); + } + + public function getParameter(string $name): array|bool|string|int|float|\UnitEnum|null + { + return $this->getPublicContainer()->getParameter($name); + } + + public function hasParameter(string $name): bool + { + return $this->getPublicContainer()->hasParameter($name); + } + + public function setParameter(string $name, mixed $value): void + { + $this->getPublicContainer()->setParameter($name, $value); + } + + public function set(string $id, mixed $service): void + { + $container = $this->getPublicContainer(); + $renamedId = $this->renamedIds[$id] ?? $id; + + try { + $container->set($renamedId, $service); + } catch (InvalidArgumentException $e) { + if (!str_starts_with($e->getMessage(), "The \"$renamedId\" service is private")) { + throw $e; + } + if (isset($container->privates[$renamedId])) { + throw new InvalidArgumentException(sprintf('The "%s" service is already initialized, you cannot replace it.', $id)); + } + $container->privates[$renamedId] = $service; + } + } + + public function has(string $id): bool + { + return $this->getPublicContainer()->has($id) || $this->getPrivateContainer()->has($id); + } + + public function get(string $id, int $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE): ?object + { + return $this->getPrivateContainer()->has($id) ? $this->getPrivateContainer()->get($id) : $this->getPublicContainer()->get($id, $invalidBehavior); + } + + public function initialized(string $id): bool + { + return $this->getPublicContainer()->initialized($id); + } + + public function reset(): void + { + // ignore the call + } + + public function getServiceIds(): array + { + return $this->getPublicContainer()->getServiceIds(); + } + + public function getRemovedIds(): array + { + return $this->getPublicContainer()->getRemovedIds(); + } + + private function getPublicContainer(): Container + { + return $this->kernel->getContainer() ?? throw new \LogicException('Cannot access the container on a non-booted kernel. Did you forget to boot it?'); + } + + private function getPrivateContainer(): ContainerInterface + { + return $this->getPublicContainer()->get($this->privateServicesLocatorId); + } +} diff --git a/lib/symfony/framework-bundle/Test/WebTestAssertionsTrait.php b/lib/symfony/framework-bundle/Test/WebTestAssertionsTrait.php new file mode 100644 index 000000000..aebd4577b --- /dev/null +++ b/lib/symfony/framework-bundle/Test/WebTestAssertionsTrait.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Test; + +trait WebTestAssertionsTrait +{ + use BrowserKitAssertionsTrait; + use DomCrawlerAssertionsTrait; + use HttpClientAssertionsTrait; +} diff --git a/lib/symfony/framework-bundle/Test/WebTestCase.php b/lib/symfony/framework-bundle/Test/WebTestCase.php new file mode 100644 index 000000000..de31d4ba9 --- /dev/null +++ b/lib/symfony/framework-bundle/Test/WebTestCase.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Test; + +use Symfony\Bundle\FrameworkBundle\KernelBrowser; +use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; + +/** + * WebTestCase is the base class for functional tests. + * + * @author Fabien Potencier + */ +abstract class WebTestCase extends KernelTestCase +{ + use WebTestAssertionsTrait; + + protected function tearDown(): void + { + parent::tearDown(); + self::getClient(null); + } + + /** + * Creates a KernelBrowser. + * + * @param array $options An array of options to pass to the createKernel method + * @param array $server An array of server parameters + */ + protected static function createClient(array $options = [], array $server = []): KernelBrowser + { + if (static::$booted) { + throw new \LogicException(sprintf('Booting the kernel before calling "%s()" is not supported, the kernel should only be booted once.', __METHOD__)); + } + + $kernel = static::bootKernel($options); + + try { + $client = $kernel->getContainer()->get('test.client'); + } catch (ServiceNotFoundException) { + if (class_exists(KernelBrowser::class)) { + throw new \LogicException('You cannot create the client used in functional tests if the "framework.test" config is not set to true.'); + } + throw new \LogicException('You cannot create the client used in functional tests if the BrowserKit component is not available. Try running "composer require symfony/browser-kit".'); + } + + $client->setServerParameters($server); + + return self::getClient($client); + } +} diff --git a/symfony/.env.test b/symfony/.env.test new file mode 100644 index 000000000..348df8d11 --- /dev/null +++ b/symfony/.env.test @@ -0,0 +1 @@ +KERNEL_CLASS=Combodo\iTop\Kernel \ No newline at end of file diff --git a/tests/php-unit-tests/composer.json b/tests/php-unit-tests/composer.json index 263eba2f1..259fd7f02 100644 --- a/tests/php-unit-tests/composer.json +++ b/tests/php-unit-tests/composer.json @@ -7,7 +7,8 @@ "psr-4": { "Combodo\\iTop\\Test\\UnitTest\\": "src/BaseTestCase/", "Combodo\\iTop\\Test\\UnitTest\\Hook\\": "src/Hook/", - "Combodo\\iTop\\Test\\UnitTest\\Service\\": "src/Service/" + "Combodo\\iTop\\Test\\UnitTest\\Service\\": "src/Service/", + "Symfony\\Bundle\\FrameworkBundle\\Test\\": "../../lib/symfony/framework-bundle/Test/" } } } diff --git a/tests/php-unit-tests/phpunit.xml.dist b/tests/php-unit-tests/phpunit.xml.dist index 5bab63d01..df01a6e6f 100644 --- a/tests/php-unit-tests/phpunit.xml.dist +++ b/tests/php-unit-tests/phpunit.xml.dist @@ -30,6 +30,7 @@ + diff --git a/tests/php-unit-tests/unitary-tests/sources/FormSDK/Symfony/SymfonyBridgeTest.php b/tests/php-unit-tests/unitary-tests/sources/FormSDK/Symfony/SymfonyBridgeTest.php new file mode 100644 index 000000000..46c3eec66 --- /dev/null +++ b/tests/php-unit-tests/unitary-tests/sources/FormSDK/Symfony/SymfonyBridgeTest.php @@ -0,0 +1,126 @@ +getContainer()->get(SymfonyBridge::class); + + $oFormFieldDescription = new FormFieldDescription($sName, $oType, $aOptions); + $aSymfonyTypeDescription = $oSymfonyBridge->ToSymfonyFormType($oFormFieldDescription); + + $this->assertIsArray($aSymfonyTypeDescription); + } + + /** + * Helper than can be called in the context of a data provider + * + * @since 3.0.4 3.1.1 3.2.0 N°6658 method creation + */ + public static function GetAppRoot() + { + if (defined('APPROOT')) { + return APPROOT; + } + + $sAppRootPath = static::GetFirstDirUpContainingFile(__DIR__, 'approot.inc.php'); + + return $sAppRootPath . '/'; + } + + private static function GetFirstDirUpContainingFile(string $sSearchPath, string $sFileToFindGlobPattern): ?string + { + for ($iDepth = 0; $iDepth < 8; $iDepth++) { + $aGlobFiles = glob($sSearchPath . '/' . $sFileToFindGlobPattern); + if (is_array($aGlobFiles) && (count($aGlobFiles) > 0)) { + return $sSearchPath . '/'; + } + $iOffsetSep = strrpos($sSearchPath, '/'); + if ($iOffsetSep === false) { + $iOffsetSep = strrpos($sSearchPath, '\\'); + if ($iOffsetSep === false) { + // Do not throw an exception here as PHPUnit will not show it clearly when determing the list of test to perform + return 'Could not find the approot file in ' . $sSearchPath; + } + } + $sSearchPath = substr($sSearchPath, 0, $iOffsetSep); + } + return null; + } + + protected function RequireOnceUnitTestFile(string $sFileRelPath): void + { + $aStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); + $sCallerDirAbsPath = dirname($aStack[0]['file']); + + require_once $sCallerDirAbsPath . DIRECTORY_SEPARATOR . $sFileRelPath; + } + + protected function RequireOnceItopFile(string $sFileRelPath): void + { + require_once $this->GetAppRoot() . $sFileRelPath; + } + + public function ToSymfonyFormTypeProvider() + { + $this->RequireOnceItopFile( 'sources/FormSDK/Field/FormFieldTypeEnumeration.php'); + + return [ + 'Test 1' => [ + 'language', + FormFieldTypeEnumeration::SELECT, + [ + 'label' => 'Ma langue', + 'choices' => [ + 'French' => 'FR FR', + 'English' => 'EN EN', + ] + ], + [ + 'language', + ChoiceType::class, + [ + 'label' => 'Ma langue', + 'French' => 'FR FR', + 'English' => 'EN EN', + ] + ] + ] + ]; + } + + + + public function testCreateLayoutTypes(): void + { + + } + + + + +}