Skip to content

Commit 4bd4943

Browse files
authored
Add tests for external routing files (#1036)
| Q | A | --------------- | ----- | Bug fix? | no | New feature? | no | BC breaks? | no | Deprecations? | no | Related tickets | partially #1029 | License | MIT
2 parents a9f6c61 + 8dc64d4 commit 4bd4943

File tree

11 files changed

+368
-5
lines changed

11 files changed

+368
-5
lines changed

tests/Application/config/packages/doctrine.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ doctrine:
1919
type: attribute
2020
dir: '%kernel.project_dir%/src/BoardGameBlog/Domain'
2121
prefix: 'App\BoardGameBlog\Domain'
22+
Conference:
23+
is_bundle: false
24+
type: attribute
25+
dir: '%kernel.project_dir%/src/Conference/Entity'
26+
prefix: 'App\Conference\Entity'
2227
Subscription:
2328
is_bundle: false
2429
type: attribute

tests/Application/config/services.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ services:
9595
App\BoardGameBlog\:
9696
resource: '../src/BoardGameBlog'
9797

98+
App\Conference\:
99+
resource: '../src/Conference'
100+
98101
App\Subscription\:
99102
resource: '../src/Subscription'
100103

tests/Application/config/sylius/resources.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
sylius_resource:
22
mapping:
3+
imports:
4+
- '%kernel.project_dir%/config/sylius/resources/imports'
35
paths:
46
- '%kernel.project_dir%/src/BoardGameBlog/Infrastructure/Sylius/Resource'
57
- '%kernel.project_dir%/src/Subscription/Entity'
@@ -52,6 +54,10 @@ sylius_resource:
5254
model: App\Entity\PullRequest
5355
form: App\Form\Type\PullRequestType
5456

57+
app.speaker:
58+
classes:
59+
model: App\Conference\Entity\Speaker
60+
5561
app.zone:
5662
classes:
5763
model: App\Entity\Zone\Zone
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Sylius package.
5+
*
6+
* (c) Sylius Sp. z o.o.
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
use App\Conference\Entity\Speaker;
15+
use App\Conference\Form\SpeakerType;
16+
use App\Conference\Grid\SpeakerGrid;
17+
use Sylius\Resource\Metadata\Create;
18+
use Sylius\Resource\Metadata\Delete;
19+
use Sylius\Resource\Metadata\Index;
20+
use Sylius\Resource\Metadata\Operations;
21+
use Sylius\Resource\Metadata\ResourceMetadata;
22+
use Sylius\Resource\Metadata\Update;
23+
24+
return (new ResourceMetadata())
25+
->withRoutePrefix('/admin')
26+
->withClass(Speaker::class)
27+
->withSection('admin')
28+
->withTemplatesDir('crud')
29+
->withFormType(SpeakerType::class)
30+
->withOperations(new Operations([
31+
new Create(),
32+
new Update(),
33+
new Delete(),
34+
new Index(grid: SpeakerGrid::class),
35+
]))
36+
;
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Sylius package.
5+
*
6+
* (c) Sylius Sp. z o.o.
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace App\Conference\Entity;
15+
16+
use Doctrine\ORM\Mapping as ORM;
17+
use Sylius\Resource\Model\ResourceInterface;
18+
use Symfony\Component\Validator\Constraints\NotBlank;
19+
20+
#[ORM\Entity]
21+
class Speaker implements ResourceInterface
22+
{
23+
public function __construct(
24+
#[ORM\Id]
25+
#[ORM\Column(type: 'integer', unique: true)]
26+
#[ORM\GeneratedValue(strategy: 'AUTO')]
27+
public ?int $id = null,
28+
#[NotBlank]
29+
#[ORM\Column]
30+
public ?string $firstName = null,
31+
#[NotBlank]
32+
#[ORM\Column]
33+
public ?string $lastName = null,
34+
) {
35+
}
36+
37+
public function getId(): ?int
38+
{
39+
return $this->id;
40+
}
41+
42+
public function getFullName(): ?string
43+
{
44+
return $this->firstName . ' ' . $this->lastName;
45+
}
46+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Sylius package.
5+
*
6+
* (c) Sylius Sp. z o.o.
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace App\Conference\Factory;
15+
16+
use App\Conference\Entity\Speaker;
17+
use Zenstruck\Foundry\Persistence\PersistentProxyObjectFactory;
18+
19+
/**
20+
* @extends PersistentProxyObjectFactory<Speaker>
21+
*/
22+
final class SpeakerFactory extends PersistentProxyObjectFactory
23+
{
24+
public static function class(): string
25+
{
26+
return Speaker::class;
27+
}
28+
29+
public function withFirstName(string $firstName): self
30+
{
31+
return $this->with(['firstName' => $firstName]);
32+
}
33+
34+
public function withLastName(string $lastName): self
35+
{
36+
return $this->with(['lastName' => $lastName]);
37+
}
38+
39+
protected function defaults(): array
40+
{
41+
return [
42+
'firstName' => self::faker()->firstName(),
43+
'lastName' => self::faker()->lastName(),
44+
];
45+
}
46+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Sylius package.
5+
*
6+
* (c) Sylius Sp. z o.o.
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace App\Conference\Form;
15+
16+
use App\Conference\Entity\Speaker;
17+
use Symfony\Component\Form\AbstractType;
18+
use Symfony\Component\Form\FormBuilderInterface;
19+
use Symfony\Component\OptionsResolver\OptionsResolver;
20+
21+
final class SpeakerType extends AbstractType
22+
{
23+
public function buildForm(FormBuilderInterface $builder, array $options)
24+
{
25+
$builder
26+
->add('firstName')
27+
->add('lastName')
28+
;
29+
}
30+
31+
public function configureOptions(OptionsResolver $resolver)
32+
{
33+
$resolver->setDefaults([
34+
'data_class' => Speaker::class,
35+
]);
36+
}
37+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Sylius package.
5+
*
6+
* (c) Sylius Sp. z o.o.
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace App\Conference\Grid;
15+
16+
use App\Conference\Entity\Speaker;
17+
use Sylius\Bundle\GridBundle\Builder\Action\CreateAction;
18+
use Sylius\Bundle\GridBundle\Builder\Action\DeleteAction;
19+
use Sylius\Bundle\GridBundle\Builder\Action\UpdateAction;
20+
use Sylius\Bundle\GridBundle\Builder\ActionGroup\ItemActionGroup;
21+
use Sylius\Bundle\GridBundle\Builder\ActionGroup\MainActionGroup;
22+
use Sylius\Bundle\GridBundle\Builder\Field\StringField;
23+
use Sylius\Bundle\GridBundle\Builder\GridBuilderInterface;
24+
use Sylius\Bundle\GridBundle\Grid\AbstractGrid;
25+
use Sylius\Bundle\GridBundle\Grid\ResourceAwareGridInterface;
26+
27+
final class SpeakerGrid extends AbstractGrid implements ResourceAwareGridInterface
28+
{
29+
public static function getName(): string
30+
{
31+
return 'app_speaker';
32+
}
33+
34+
public function buildGrid(GridBuilderInterface $gridBuilder): void
35+
{
36+
$gridBuilder
37+
->orderBy('fullName', 'asc')
38+
->setLimits([10, 25, 50])
39+
->addField(
40+
StringField::create('fullName')
41+
->setLabel('Name')
42+
->setSortable(true, 'firstName'),
43+
)
44+
->addActionGroup(
45+
MainActionGroup::create(
46+
CreateAction::create(),
47+
),
48+
)
49+
->addActionGroup(
50+
ItemActionGroup::create(
51+
UpdateAction::create(),
52+
DeleteAction::create(),
53+
),
54+
)
55+
;
56+
}
57+
58+
public function getResourceClass(): string
59+
{
60+
return Speaker::class;
61+
}
62+
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Sylius package.
5+
*
6+
* (c) Sylius Sp. z o.o.
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace App\Tests\Controller;
15+
16+
use ApiTestCase\ApiTestCase;
17+
use App\Conference\Entity\Speaker;
18+
use App\Conference\Factory\SpeakerFactory;
19+
use Coduo\PHPMatcher\Backtrace\VoidBacktrace;
20+
use Coduo\PHPMatcher\Matcher;
21+
use Doctrine\ORM\EntityManagerInterface;
22+
use Symfony\Component\HttpFoundation\Response;
23+
use Zenstruck\Foundry\Test\Factories;
24+
use Zenstruck\Foundry\Test\ResetDatabase;
25+
26+
final class SpeakerUiTest extends ApiTestCase
27+
{
28+
use Factories;
29+
use ResetDatabase;
30+
31+
/** @test */
32+
public function it_allows_browsing_speakers(): void
33+
{
34+
SpeakerFactory::new()
35+
->withFirstName('Francis')
36+
->withLastName('Hilaire')
37+
->create()
38+
;
39+
40+
SpeakerFactory::new()
41+
->withFirstName('Gregor')
42+
->withLastName('Šink')
43+
->create()
44+
;
45+
46+
$this->client->request('GET', '/admin/speakers');
47+
$response = $this->client->getResponse();
48+
49+
$this->assertResponseCode($response, Response::HTTP_OK);
50+
$content = $response->getContent();
51+
52+
$this->assertStringContainsString('<td>Francis Hilaire</td>', $content);
53+
$this->assertStringContainsString('<td>Gregor Šink</td>', $content);
54+
}
55+
56+
/** @test */
57+
public function it_allows_accessing_speaker_creation_page(): void
58+
{
59+
$this->client->request('GET', '/admin/speakers/new');
60+
61+
$this->assertResponseCode($this->client->getResponse(), Response::HTTP_OK);
62+
}
63+
64+
/** @test */
65+
public function it_allows_creating_a_speaker(): void
66+
{
67+
$this->client->request('GET', '/admin/speakers/new');
68+
$this->client->submitForm('Create', [
69+
'speaker[firstName]' => 'Francis',
70+
'speaker[lastName]' => 'Hilaire',
71+
]);
72+
73+
$this->assertResponseRedirects(null, expectedCode: Response::HTTP_FOUND);
74+
75+
/** @var Speaker|null $speaker */
76+
$speaker = static::getContainer()->get(EntityManagerInterface::class)->getRepository(Speaker::class)->findOneBy(['firstName' => 'Francis']);
77+
78+
$this->assertNotNull($speaker);
79+
$this->assertSame('Francis Hilaire', $speaker->getFullName());
80+
}
81+
82+
/** @test */
83+
public function it_allows_updating_a_speaker(): void
84+
{
85+
$speaker = SpeakerFactory::createOne();
86+
87+
$this->client->request('GET', '/admin/speakers/' . $speaker->getId() . '/edit');
88+
$this->client->submitForm('Save changes', [
89+
'speaker[firstName]' => 'Francis',
90+
'speaker[lastName]' => 'Hilaire',
91+
]);
92+
93+
$this->assertResponseRedirects(null, expectedCode: Response::HTTP_FOUND);
94+
95+
$speaker->_refresh();
96+
$this->assertSame('Francis Hilaire', $speaker->getFullName());
97+
}
98+
99+
/** @test */
100+
public function it_allows_deleting_a_speaker(): void
101+
{
102+
SpeakerFactory::createOne();
103+
104+
$this->client->request('GET', '/admin/speakers');
105+
$this->client->submitForm('Delete');
106+
107+
$this->assertResponseRedirects(null, expectedCode: Response::HTTP_FOUND);
108+
109+
/** @var Speaker[] $speakers */
110+
$speakers = static::getContainer()->get(EntityManagerInterface::class)->getRepository(Speaker::class)->findAll();
111+
112+
$this->assertEmpty($speakers);
113+
}
114+
115+
protected function buildMatcher(): Matcher
116+
{
117+
return $this->matcherFactory->createMatcher(new VoidBacktrace());
118+
}
119+
}

0 commit comments

Comments
 (0)