Skip to content

Commit f740f2a

Browse files
authored
Use traits for methods that belong to the public API (#94)
1 parent 776a689 commit f740f2a

12 files changed

+1271
-1131
lines changed

Diff for: src/Codeception/Module/Symfony.php

+25-1,131
Large diffs are not rendered by default.
+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Codeception\Module\Symfony;
6+
7+
use Codeception\Lib\Connector\Symfony as SymfonyConnector;
8+
use Symfony\Component\HttpFoundation\Response;
9+
use function sprintf;
10+
11+
trait BrowserAssertionsTrait
12+
{
13+
/**
14+
* Reboot client's kernel.
15+
* Can be used to manually reboot kernel when 'rebootable_client' => false
16+
*
17+
* ``` php
18+
* <?php
19+
*
20+
* // Perform some requests
21+
*
22+
* $I->rebootClientKernel();
23+
*
24+
* // Perform other requests
25+
*
26+
* ```
27+
*
28+
*/
29+
public function rebootClientKernel(): void
30+
{
31+
if ($this->client instanceof SymfonyConnector) {
32+
$this->client->rebootKernel();
33+
}
34+
}
35+
36+
/**
37+
* Goes to a page and check that it can be accessed.
38+
*
39+
* ```php
40+
* <?php
41+
* $I->seePageIsAvailable('/dashboard');
42+
* ```
43+
*
44+
* @param string $url
45+
*/
46+
public function seePageIsAvailable(string $url): void
47+
{
48+
$this->amOnPage($url);
49+
$this->seeResponseCodeIsSuccessful();
50+
$this->seeInCurrentUrl($url);
51+
}
52+
53+
/**
54+
* Goes to a page and check that it redirects to another.
55+
*
56+
* ```php
57+
* <?php
58+
* $I->seePageRedirectsTo('/admin', '/login');
59+
* ```
60+
*
61+
* @param string $page
62+
* @param string $redirectsTo
63+
*/
64+
public function seePageRedirectsTo(string $page, string $redirectsTo): void
65+
{
66+
$this->client->followRedirects(false);
67+
$this->amOnPage($page);
68+
/** @var Response $response */
69+
$response = $this->client->getResponse();
70+
$this->assertTrue(
71+
$response->isRedirection()
72+
);
73+
$this->client->followRedirect();
74+
$this->seeInCurrentUrl($redirectsTo);
75+
}
76+
77+
/**
78+
* Submit a form specifying the form name only once.
79+
*
80+
* Use this function instead of $I->submitForm() to avoid repeating the form name in the field selectors.
81+
* If you customized the names of the field selectors use $I->submitForm() for full control.
82+
*
83+
* ```php
84+
* <?php
85+
* $I->submitSymfonyForm('login_form', [
86+
* '[email]' => '[email protected]',
87+
* '[password]' => 'secretForest'
88+
* ]);
89+
* ```
90+
*
91+
* @param string $name
92+
* @param string[] $fields
93+
*/
94+
public function submitSymfonyForm(string $name, array $fields): void
95+
{
96+
$selector = sprintf('form[name=%s]', $name);
97+
98+
$params = [];
99+
foreach ($fields as $key => $value) {
100+
$fixedKey = sprintf('%s%s', $name, $key);
101+
$params[$fixedKey] = $value;
102+
}
103+
$button = sprintf('%s_submit', $name);
104+
105+
$this->submitForm($selector, $params, $button);
106+
}
107+
}
+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Codeception\Module\Symfony;
6+
7+
use Symfony\Bundle\FrameworkBundle\Console\Application;
8+
use Symfony\Component\Console\Tester\CommandTester;
9+
10+
trait ConsoleAssertionsTrait
11+
{
12+
/**
13+
* Run Symfony console command, grab response and return as string.
14+
* Recommended to use for integration or functional testing.
15+
*
16+
* ``` php
17+
* <?php
18+
* $result = $I->runSymfonyConsoleCommand('hello:world', ['arg' => 'argValue', 'opt1' => 'optValue'], ['input']);
19+
* ```
20+
*
21+
* @param string $command The console command to execute
22+
* @param array $parameters Parameters (arguments and options) to pass to the command
23+
* @param array $consoleInputs Console inputs (e.g. used for interactive questions)
24+
* @param int $expectedExitCode The expected exit code of the command
25+
*
26+
* @return string Returns the console output of the command
27+
*/
28+
public function runSymfonyConsoleCommand(string $command, array $parameters = [], array $consoleInputs = [], int $expectedExitCode = 0): string
29+
{
30+
$kernel = $this->grabService('kernel');
31+
$application = new Application($kernel);
32+
$consoleCommand = $application->find($command);
33+
$commandTester = new CommandTester($consoleCommand);
34+
$commandTester->setInputs($consoleInputs);
35+
36+
$parameters = ['command' => $command] + $parameters;
37+
$exitCode = $commandTester->execute($parameters);
38+
$output = $commandTester->getDisplay();
39+
40+
$this->assertEquals(
41+
$expectedExitCode,
42+
$exitCode,
43+
'Command did not exit with code '.$expectedExitCode
44+
.' but with '.$exitCode.': '.$output
45+
);
46+
47+
return $output;
48+
}
49+
}
+129
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Codeception\Module\Symfony;
6+
use function class_exists;
7+
use function get_class;
8+
use function interface_exists;
9+
use function is_object;
10+
use function is_string;
11+
use function is_subclass_of;
12+
use function json_encode;
13+
use function sprintf;
14+
15+
trait DoctrineAssertionsTrait
16+
{
17+
/**
18+
* Retrieves number of records from database
19+
* 'id' is the default search parameter.
20+
*
21+
* ```php
22+
* <?php
23+
* $I->grabNumRecords('User::class', ['name' => 'davert']);
24+
* ```
25+
*
26+
* @param string $entityClass The entity class
27+
* @param array $criteria Optional query criteria
28+
* @return int
29+
*/
30+
public function grabNumRecords(string $entityClass, array $criteria = []): int
31+
{
32+
$em = $this->_getEntityManager();
33+
$repository = $em->getRepository($entityClass);
34+
35+
if (empty($criteria)) {
36+
return (int)$repository->createQueryBuilder('a')
37+
->select('count(a.id)')
38+
->getQuery()
39+
->getSingleScalarResult();
40+
}
41+
return $repository->count($criteria);
42+
}
43+
44+
/**
45+
* Grab a Doctrine entity repository.
46+
* Works with objects, entities, repositories, and repository interfaces.
47+
*
48+
* ```php
49+
* <?php
50+
* $I->grabRepository($user);
51+
* $I->grabRepository(User::class);
52+
* $I->grabRepository(UserRepository::class);
53+
* $I->grabRepository(UserRepositoryInterface::class);
54+
* ```
55+
*
56+
* @param object|string $mixed
57+
* @return \Doctrine\ORM\EntityRepository|null
58+
*/
59+
public function grabRepository($mixed)
60+
{
61+
$entityRepoClass = '\Doctrine\ORM\EntityRepository';
62+
$isNotARepo = function () use ($mixed): void {
63+
$this->fail(
64+
sprintf("'%s' is not an entity repository", $mixed)
65+
);
66+
};
67+
$getRepo = function () use ($mixed, $entityRepoClass, $isNotARepo) {
68+
if (!$repo = $this->grabService($mixed)) return null;
69+
if (!$repo instanceof $entityRepoClass) {
70+
$isNotARepo();
71+
return null;
72+
}
73+
return $repo;
74+
};
75+
76+
if (is_object($mixed)) {
77+
$mixed = get_class($mixed);
78+
}
79+
80+
if (interface_exists($mixed)) {
81+
return $getRepo();
82+
}
83+
84+
if (!is_string($mixed) || !class_exists($mixed) ) {
85+
$isNotARepo();
86+
return null;
87+
}
88+
89+
if (is_subclass_of($mixed, $entityRepoClass)){
90+
return $getRepo();
91+
}
92+
93+
$em = $this->_getEntityManager();
94+
if ($em->getMetadataFactory()->isTransient($mixed)) {
95+
$isNotARepo();
96+
return null;
97+
}
98+
99+
return $em->getRepository($mixed);
100+
}
101+
102+
/**
103+
* Checks that number of given records were found in database.
104+
* 'id' is the default search parameter.
105+
*
106+
* ```php
107+
* <?php
108+
* $I->seeNumRecords(1, User::class, ['name' => 'davert']);
109+
* $I->seeNumRecords(80, User::class);
110+
* ```
111+
*
112+
* @param int $expectedNum Expected number of records
113+
* @param string $className A doctrine entity
114+
* @param array $criteria Optional query criteria
115+
*/
116+
public function seeNumRecords(int $expectedNum, string $className, array $criteria = []): void
117+
{
118+
$currentNum = $this->grabNumRecords($className, $criteria);
119+
120+
$this->assertEquals(
121+
$expectedNum,
122+
$currentNum,
123+
sprintf(
124+
'The number of found %s (%d) does not match expected number %d with %s',
125+
$className, $currentNum, $expectedNum, json_encode($criteria)
126+
)
127+
);
128+
}
129+
}
+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Codeception\Module\Symfony;
6+
7+
use Symfony\Component\HttpKernel\DataCollector\EventDataCollector;
8+
use Symfony\Component\VarDumper\Cloner\Data;
9+
use function get_class;
10+
use function is_array;
11+
use function is_object;
12+
use function strpos;
13+
14+
trait EventsAssertionsTrait
15+
{
16+
/**
17+
* Make sure events did not fire during the test.
18+
*
19+
* ``` php
20+
* <?php
21+
* $I->dontSeeEventTriggered('App\MyEvent');
22+
* $I->dontSeeEventTriggered(new App\Events\MyEvent());
23+
* $I->dontSeeEventTriggered(['App\MyEvent', 'App\MyOtherEvent']);
24+
* ```
25+
* @param string|object|string[] $expected
26+
*/
27+
public function dontSeeEventTriggered($expected): void
28+
{
29+
/** @var EventDataCollector $eventCollector */
30+
$eventCollector = $this->grabCollector('events', __FUNCTION__);
31+
32+
/** @var Data $data */
33+
$data = $eventCollector->getNotCalledListeners();
34+
35+
$actual = $data->getValue(true);
36+
$expected = is_array($expected) ? $expected : [$expected];
37+
38+
foreach ($expected as $expectedEvent) {
39+
$notTriggered = false;
40+
$expectedEvent = is_object($expectedEvent) ? get_class($expectedEvent) : $expectedEvent;
41+
42+
foreach ($actual as $actualEvent) {
43+
if (strpos($actualEvent['pretty'], $expectedEvent) === 0) {
44+
$notTriggered = true;
45+
}
46+
}
47+
$this->assertTrue($notTriggered, "The '$expectedEvent' event triggered");
48+
}
49+
}
50+
51+
/**
52+
* Make sure events fired during the test.
53+
*
54+
* ``` php
55+
* <?php
56+
* $I->seeEventTriggered('App\MyEvent');
57+
* $I->seeEventTriggered(new App\Events\MyEvent());
58+
* $I->seeEventTriggered(['App\MyEvent', 'App\MyOtherEvent']);
59+
* ```
60+
* @param string|object|string[] $expected
61+
*/
62+
public function seeEventTriggered($expected): void
63+
{
64+
/** @var EventDataCollector $eventCollector */
65+
$eventCollector = $this->grabCollector('events', __FUNCTION__);
66+
67+
/** @var Data $data */
68+
$data = $eventCollector->getCalledListeners();
69+
70+
if ($data->count() === 0) {
71+
$this->fail('No event was triggered');
72+
}
73+
74+
$actual = $data->getValue(true);
75+
$expected = is_array($expected) ? $expected : [$expected];
76+
77+
foreach ($expected as $expectedEvent) {
78+
$triggered = false;
79+
$expectedEvent = is_object($expectedEvent) ? get_class($expectedEvent) : $expectedEvent;
80+
81+
foreach ($actual as $actualEvent) {
82+
if (strpos($actualEvent['pretty'], $expectedEvent) === 0) {
83+
$triggered = true;
84+
}
85+
}
86+
$this->assertTrue($triggered, "The '$expectedEvent' event did not trigger");
87+
}
88+
}
89+
}

0 commit comments

Comments
 (0)