Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 24 additions & 14 deletions src/Codeception/Lib/Connector/Symfony.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,7 @@ protected function doRequest(object $request): Response
*/
public function rebootKernel(): void
{
foreach ($this->persistentServices as $name => $_) {
if ($this->container->has($name)) {
$this->persistentServices[$name] = $this->container->get($name);
}
}
$this->updatePersistentServices();

$this->persistDoctrineConnections();

Expand All @@ -68,15 +64,7 @@ public function rebootKernel(): void

$this->container = $this->resolveContainer();

foreach ($this->persistentServices as $name => $service) {
try {
$this->container->set($name, $service);
} catch (InvalidArgumentException $e) {
if (function_exists('codecept_debug')) {
codecept_debug("[Symfony] Can't set persistent service {$name}: {$e->getMessage()}");
}
}
}
$this->injectPersistentServices();

$this->getProfiler()?->enable();
}
Expand Down Expand Up @@ -116,4 +104,26 @@ private function persistDoctrineConnections(): void
}
})->call($this->kernel->getContainer());
}

private function updatePersistentServices(): void
{
foreach ($this->persistentServices as $name => $_) {
if ($this->container->has($name)) {
$this->persistentServices[$name] = $this->container->get($name);
}
}
}

private function injectPersistentServices(): void
{
foreach ($this->persistentServices as $name => $service) {
try {
$this->container->set($name, $service);
} catch (InvalidArgumentException $e) {
if (function_exists('codecept_debug')) {
codecept_debug("[Symfony] Can't set persistent service {$name}: {$e->getMessage()}");
}
}
}
}
}
11 changes: 6 additions & 5 deletions src/Codeception/Module/Symfony.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@
use Symfony\Bundle\SecurityBundle\DataCollector\SecurityDataCollector;
use Symfony\Component\BrowserKit\AbstractBrowser;
use Symfony\Component\Dotenv\Dotenv;
use Symfony\Component\Finder\Finder;
use Symfony\Component\HttpKernel\DataCollector\TimeDataCollector;
use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\HttpKernel\Profiler\Profile;
Expand All @@ -57,6 +56,7 @@
use function count;
use function extension_loaded;
use function file_exists;
use function glob;
use function implode;
use function ini_get;
use function ini_set;
Expand Down Expand Up @@ -259,6 +259,7 @@ public function _after(TestInterface $test): void

$this->cachedResponse = null;
$this->cachedProfile = null;
$this->cachedRoutes = null;

parent::_after($test);
}
Expand Down Expand Up @@ -333,8 +334,8 @@ protected function getKernelClass(): string
if (file_exists($expectedKernelPath)) {
include_once $expectedKernelPath;
} else {
foreach ((new Finder())->name('*Kernel.php')->depth('0')->in($path) as $file) {
include_once $file->getRealPath();
foreach (glob($path . DIRECTORY_SEPARATOR . '*Kernel.php') ?: [] as $file) {
include_once $file;
}
}

Expand Down Expand Up @@ -450,12 +451,12 @@ private function debugSecurityData(SecurityDataCollector $securityCollector): vo

private function debugMailerData(MessageDataCollector $messageCollector): void
{
$this->debugSection('Emails', sprintf('%d sent', count($messageCollector->getEvents()->getMessages())));
$this->debugSection('Emails', sprintf('%d sent', count($messageCollector->getEvents()->getEvents())));
}

private function debugNotifierData(NotificationDataCollector $notificationCollector): void
{
$this->debugSection('Notifications', sprintf('%d sent', count($notificationCollector->getEvents()->getMessages())));
$this->debugSection('Notifications', sprintf('%d sent', count($notificationCollector->getEvents()->getEvents())));
}

private function debugTimeData(TimeDataCollector $timeCollector): void
Expand Down
3 changes: 1 addition & 2 deletions src/Codeception/Module/Symfony/BrowserAssertionsTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
use Symfony\Component\HttpFoundation\Test\Constraint\ResponseStatusCodeSame;

use function class_exists;
use function count;
use function sprintf;

trait BrowserAssertionsTrait
Expand Down Expand Up @@ -372,7 +371,7 @@ public function submitSymfonyForm(string $name, array $fields): void
}

$node = $this->getClient()->getCrawler()->filter($selector);
$this->assertGreaterThan(0, count($node), sprintf('Form "%s" not found.', $selector));
$this->assertGreaterThan(0, $node->count(), sprintf('Form "%s" not found.', $selector));
$form = $node->form();
$this->getClient()->submit($form, $params);
}
Expand Down
5 changes: 4 additions & 1 deletion src/Codeception/Module/Symfony/CacheTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ trait CacheTrait
{
private ?object $cachedResponse = null;
private ?Profile $cachedProfile = null;
/** @var array<string, string>|null */
protected ?array $cachedRoutes = null;

/** @var array<string, mixed> */
protected array $state = [];
Expand Down Expand Up @@ -53,7 +55,8 @@ protected function getInternalDomains(): array

protected function clearRouterCache(): void
{
unset($this->state['internalDomains'], $this->state['cachedRoutes']);
unset($this->state['internalDomains']);
$this->cachedRoutes = null;
}

/**
Expand Down
17 changes: 11 additions & 6 deletions src/Codeception/Module/Symfony/DoctrineAssertionsTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ trait DoctrineAssertionsTrait
* $I->grabNumRecords(User::class, ['status' => 'active']);
* ```
*
* @param class-string<object> $entityClass Fully-qualified entity class name
* @template T of object
* @param class-string<T> $entityClass Fully-qualified entity class name
* @param array<string, mixed> $criteria Optional query criteria
*/
public function grabNumRecords(string $entityClass, array $criteria = []): int
Expand All @@ -44,18 +45,20 @@ public function grabNumRecords(string $entityClass, array $criteria = []): int
* $I->grabRepository(UserRepositoryInterface::class); // interface
* ```
*
* @param object|class-string $mixed
* @return EntityRepository<object>
* @template T of object
* @param object|class-string<T> $entityOrClass
* @return ($entityOrClass is class-string<T> ? EntityRepository<T> : EntityRepository<object>)
*/
public function grabRepository(object|string $mixed): EntityRepository
public function grabRepository(object|string $entityOrClass): EntityRepository
{
$id = is_object($mixed) ? $mixed::class : $mixed;
$id = is_object($entityOrClass) ? $entityOrClass::class : $entityOrClass;

if (interface_exists($id) || is_subclass_of($id, EntityRepository::class)) {
$repo = $this->grabService($id);
if (!($repo instanceof EntityRepository && $repo instanceof $id)) {
Assert::fail(sprintf("'%s' is not an entity repository", $id));
}
/** @var EntityRepository<T>|EntityRepository<object> $repo */
return $repo;
}

Expand All @@ -64,6 +67,7 @@ public function grabRepository(object|string $mixed): EntityRepository
Assert::fail(sprintf("'%s' is not a managed Doctrine entity", $id));
}

/** @var EntityRepository<T>|EntityRepository<object> */
return $em->getRepository($id);
}

Expand All @@ -77,8 +81,9 @@ public function grabRepository(object|string $mixed): EntityRepository
* $I->seeNumRecords(80, User::class);
* ```
*
* @template T of object
* @param int $expectedNum Expected count
* @param class-string<object> $className Entity class
* @param class-string<T> $className Entity class
* @param array<string, mixed> $criteria Optional criteria
*/
public function seeNumRecords(int $expectedNum, string $className, array $criteria = []): void
Expand Down
4 changes: 2 additions & 2 deletions src/Codeception/Module/Symfony/DomCrawlerAssertionsTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -170,10 +170,10 @@ private function assertCheckboxState(string $fieldName, bool $checked, string $m
$this->assertThatCrawler($constraint, $message);
}

private function assertInputValue(string $fieldName, string $value, bool $same, string $message): void
private function assertInputValue(string $fieldName, string $expectedValue, bool $same, string $message): void
{
$this->assertThatCrawler(new CrawlerSelectorExists("input[name=\"$fieldName\"]"), $message);
$constraint = new CrawlerSelectorAttributeValueSame("input[name=\"$fieldName\"]", 'value', $value);
$constraint = new CrawlerSelectorAttributeValueSame("input[name=\"$fieldName\"]", 'value', $expectedValue);
if (!$same) {
$constraint = new LogicalNot($constraint);
}
Expand Down
70 changes: 41 additions & 29 deletions src/Codeception/Module/Symfony/FormAssertionsTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
use Symfony\Component\Form\Extension\DataCollector\FormDataCollector;
use Symfony\Component\VarDumper\Cloner\Data;

use function count;
use function implode;
use function is_array;
use function is_int;
Expand All @@ -26,18 +25,18 @@ trait FormAssertionsTrait
* $I->assertFormValue('#loginForm', 'username', 'john_doe');
* ```
*/
public function assertFormValue(string $formSelector, string $fieldName, string $value, string $message = ''): void
public function assertFormValue(string $formSelector, string $fieldName, string $expectedValue, string $message = ''): void
{
$node = $this->getClient()->getCrawler()->filter($formSelector);
$this->assertGreaterThan(0, count($node), sprintf('Form "%s" not found.', $formSelector));
$this->assertGreaterThan(0, $node->count(), sprintf('Form "%s" not found.', $formSelector));

$values = $node->form()->getValues();
$this->assertArrayHasKey(
$fieldName,
$values,
$message ?: sprintf('Field "%s" not found in form "%s".', $fieldName, $formSelector)
);
$this->assertSame($value, $values[$fieldName]);
$this->assertSame($expectedValue, $values[$fieldName]);
}

/**
Expand All @@ -51,7 +50,7 @@ public function assertFormValue(string $formSelector, string $fieldName, string
public function assertNoFormValue(string $formSelector, string $fieldName, string $message = ''): void
{
$node = $this->getClient()->getCrawler()->filter($formSelector);
$this->assertGreaterThan(0, count($node), sprintf('Form "%s" not found.', $formSelector));
$this->assertGreaterThan(0, $node->count(), sprintf('Form "%s" not found.', $formSelector));

$values = $node->form()->getValues();
$this->assertArrayNotHasKey(
Expand Down Expand Up @@ -86,19 +85,11 @@ public function dontSeeFormErrors(): void
*/
public function seeFormErrorMessage(string $field, ?string $message = null): void
{
$errors = $this->getErrorsForField($field);

if ($errors === []) {
Assert::fail("No form error message for field '{$field}'.");
}
$collector = $this->grabFormCollector(__FUNCTION__);
/** @var array<string, mixed> $formsData */
$formsData = $this->getRawCollectorData($collector)['forms'] ?? [];

if ($message !== null) {
$this->assertStringContainsString(
$message,
implode("\n", $errors),
sprintf("There is an error message for the field '%s', but it does not match the expected message.", $field)
);
}
$this->assertFormErrorMessage($field, $message, $formsData);
}

/**
Expand Down Expand Up @@ -140,8 +131,34 @@ public function seeFormErrorMessage(string $field, ?string $message = null): voi
*/
public function seeFormErrorMessages(array $expectedErrors): void
{
$collector = $this->grabFormCollector(__FUNCTION__);
/** @var array<string, mixed> $formsData */
$formsData = $this->getRawCollectorData($collector)['forms'] ?? [];

foreach ($expectedErrors as $field => $msg) {
is_int($field) ? $this->seeFormErrorMessage((string) $msg) : $this->seeFormErrorMessage($field, $msg);
if (is_int($field)) {
$this->assertFormErrorMessage((string) $msg, null, $formsData);
} else {
$this->assertFormErrorMessage($field, $msg, $formsData);
}
}
}

/** @param array<string, mixed> $formsData */
private function assertFormErrorMessage(string $field, ?string $message, array $formsData): void
{
$errors = $this->getErrorsForField($field, $formsData);

if ($errors === []) {
Assert::fail("No form error message for field '{$field}'.");
}

if ($message !== null) {
$this->assertStringContainsString(
$message,
implode("\n", $errors),
sprintf("There is an error message for the field '%s', but it does not match the expected message.", $field)
);
}
}

Expand Down Expand Up @@ -172,16 +189,11 @@ private function getFormErrorsCount(string $function): int
}

/**
* @param array<string, mixed> $formsData
* @return list<string>
*/
private function getErrorsForField(string $field): array
private function getErrorsForField(string $field, array $formsData): array
{
$collector = $this->grabFormCollector('seeFormErrorMessage');
$formsData = $this->getRawCollectorData($collector)['forms'] ?? [];
if (!is_array($formsData)) {
return [];
}

$errorsForField = [];
$fieldFound = false;

Expand Down Expand Up @@ -215,11 +227,11 @@ private function getErrorsForField(string $field): array
/** @return array<string, mixed> */
private function getRawCollectorData(FormDataCollector $collector): array
{
$data = $collector->getData();
if ($data instanceof Data) {
$data = $data->getValue(true);
$collectorData = $collector->getData();
if ($collectorData instanceof Data) {
$collectorData = $collectorData->getValue(true);
}
/** @var array<string, mixed> */
return is_array($data) ? $data : [];
return is_array($collectorData) ? $collectorData : [];
}
}
10 changes: 5 additions & 5 deletions src/Codeception/Module/Symfony/HttpClientAssertionsTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -145,13 +145,13 @@ private function getHttpClientTraces(string $httpClientId, string $function): ar
return $clientData['traces'];
}

private function extractValue(mixed $value): mixed
private function extractValue(mixed $traceData): mixed
{
return match (true) {
$value instanceof Data => $value->getValue(true),
is_object($value) && method_exists($value, 'getValue') => $value->getValue(true),
$value instanceof Stringable => (string) $value,
default => $value,
$traceData instanceof Data => $traceData->getValue(true),
is_object($traceData) && method_exists($traceData, 'getValue') => $traceData->getValue(true),
$traceData instanceof Stringable => (string) $traceData,
default => $traceData,
};
}

Expand Down
13 changes: 8 additions & 5 deletions src/Codeception/Module/Symfony/RouterAssertionsTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,13 @@ private function getCurrentRouteMatch(string $routeName): array

private function findRouteByActionOrFail(string $action): string
{
if (isset($this->cachedRoutes[$action])) {
return $this->cachedRoutes[$action];
}

foreach ($this->getCachedRoutes() as $ctrl => $name) {
if (str_ends_with($ctrl, $action)) {
return $name;
return $this->cachedRoutes[$action] = $name;
}
}

Expand All @@ -130,9 +134,8 @@ private function findRouteByActionOrFail(string $action): string
/** @return array<string, string> */
private function getCachedRoutes(): array
{
if (isset($this->state['cachedRoutes'])) {
/** @var array<string, string> */
return $this->state['cachedRoutes'];
if ($this->cachedRoutes !== null) {
return $this->cachedRoutes;
}

$routes = [];
Expand All @@ -143,7 +146,7 @@ private function getCachedRoutes(): array
}
}

return $this->state['cachedRoutes'] = $routes;
return $this->cachedRoutes = $routes;
}

private function assertRouteExists(string $routeName): void
Expand Down
Loading