M1: CRUD-Slice 'Mahlzeit' Schritt 2 – UI (Controller, Templates, Routen, Feature-Tests)
This commit is contained in:
@@ -0,0 +1,201 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Functional\UI\Essen;
|
||||
|
||||
use App\Data\Essen\Entity\MahlzeitEntity;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Tools\SchemaTool;
|
||||
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||
use Symfony\Component\BrowserKit\Cookie;
|
||||
|
||||
/**
|
||||
* M1 Schritt 2: CRUD-Slice "Mahlzeit" über die Web-UI (eingeloggte Session).
|
||||
*
|
||||
* `createClient()` darf pro Test nur einmal den Kernel booten, daher wird
|
||||
* Client + Schema + Login je Test per Helper vorbereitet (Pattern wie SmokeTest).
|
||||
* Test-DB ist file-basiert (var/test.db), s. doctrine.yaml when@test.
|
||||
*/
|
||||
final class MahlzeitControllerTest extends WebTestCase
|
||||
{
|
||||
private const AUTH_KEYS = [
|
||||
'auth_sub' => 'test-sub',
|
||||
'auth_name' => 'Jens',
|
||||
'auth_email' => 'jens@ljh31.de',
|
||||
];
|
||||
|
||||
private KernelBrowser $client;
|
||||
|
||||
public function testIndexRedirectsToLoginWithoutSession(): void
|
||||
{
|
||||
$client = $this->vorbereiten(false);
|
||||
$client->request('GET', '/mahlzeiten');
|
||||
|
||||
self::assertResponseStatusCodeSame(302);
|
||||
self::assertResponseRedirects('/login?redirect=/mahlzeiten');
|
||||
}
|
||||
|
||||
public function testIndexRendersEmptyListForAuthedUser(): void
|
||||
{
|
||||
$client = $this->vorbereiten();
|
||||
$client->request('GET', '/mahlzeiten');
|
||||
|
||||
self::assertResponseIsSuccessful();
|
||||
self::assertSelectorTextContains('h1', 'Mahlzeiten');
|
||||
self::assertSelectorTextContains('p', 'Noch keine Mahlzeiten');
|
||||
}
|
||||
|
||||
public function testNeuRendersEmptyForm(): void
|
||||
{
|
||||
$client = $this->vorbereiten();
|
||||
$client->request('GET', '/mahlzeiten/neu');
|
||||
|
||||
self::assertResponseIsSuccessful();
|
||||
self::assertSelectorExists('input[name="name"]');
|
||||
self::assertSelectorExists('textarea[name="notiz"]');
|
||||
self::assertSelectorExists('input[name="wochentage[]"][value="mo"]');
|
||||
}
|
||||
|
||||
public function testCreateRedirectsAndPersists(): void
|
||||
{
|
||||
$client = $this->vorbereiten();
|
||||
$client->request('POST', '/mahlzeiten', [
|
||||
'name' => 'Bolognese',
|
||||
'notiz' => 'mit Basilikum',
|
||||
'wochentage' => ['mo', 'mi'],
|
||||
]);
|
||||
|
||||
self::assertResponseRedirects('/mahlzeiten');
|
||||
|
||||
$entity = $this->em()->getRepository(MahlzeitEntity::class)->findOneBy(['name' => 'Bolognese']);
|
||||
self::assertNotNull($entity);
|
||||
self::assertSame('mit Basilikum', $entity->getNotiz());
|
||||
self::assertSame('mo,mi', $entity->getWochentage());
|
||||
self::assertSame('geplant', $entity->getStatus());
|
||||
}
|
||||
|
||||
public function testCreateWithEmptyNameRendersFormWithError(): void
|
||||
{
|
||||
$client = $this->vorbereiten();
|
||||
$client->request('POST', '/mahlzeiten', [
|
||||
'name' => ' ',
|
||||
'notiz' => 'x',
|
||||
'wochentage' => ['di'],
|
||||
]);
|
||||
|
||||
self::assertResponseIsSuccessful();
|
||||
self::assertSelectorTextContains('div', 'Name ist ein Pflichtfeld');
|
||||
|
||||
self::assertCount(0, $this->em()->getRepository(MahlzeitEntity::class)->findAll());
|
||||
}
|
||||
|
||||
public function testUpdatePersistsChanges(): void
|
||||
{
|
||||
$client = $this->vorbereiten();
|
||||
$id = $this->anlegen($client, 'Curry', '', []);
|
||||
|
||||
$client->request('POST', sprintf('/mahlzeiten/%d', $id), [
|
||||
'name' => 'Currywurst',
|
||||
'notiz' => 'scharf',
|
||||
'wochentage' => ['do'],
|
||||
]);
|
||||
|
||||
self::assertResponseRedirects('/mahlzeiten');
|
||||
|
||||
$this->em()->clear();
|
||||
$entity = $this->em()->getRepository(MahlzeitEntity::class)->find($id);
|
||||
self::assertSame('Currywurst', $entity->getName());
|
||||
self::assertSame('scharf', $entity->getNotiz());
|
||||
self::assertSame('do', $entity->getWochentage());
|
||||
}
|
||||
|
||||
public function testDeleteRemovesMeal(): void
|
||||
{
|
||||
$client = $this->vorbereiten();
|
||||
$id = $this->anlegen($client, 'Pizza', '', []);
|
||||
|
||||
$client->request('POST', sprintf('/mahlzeiten/%d/loeschen', $id));
|
||||
|
||||
self::assertResponseRedirects('/mahlzeiten');
|
||||
|
||||
$this->em()->clear();
|
||||
self::assertNull($this->em()->getRepository(MahlzeitEntity::class)->find($id));
|
||||
}
|
||||
|
||||
public function testBearbeitenUnknownIdReturns404(): void
|
||||
{
|
||||
$client = $this->vorbereiten();
|
||||
$client->request('GET', '/mahlzeiten/99999/bearbeiten');
|
||||
|
||||
self::assertResponseStatusCodeSame(404);
|
||||
}
|
||||
|
||||
public function testIndexShowsPersistedMealWithActions(): void
|
||||
{
|
||||
$client = $this->vorbereiten();
|
||||
$this->anlegen($client, 'Lasagne', 'Samstag', ['sa']);
|
||||
|
||||
$client->request('GET', '/mahlzeiten');
|
||||
|
||||
self::assertResponseIsSuccessful();
|
||||
self::assertSelectorTextContains('strong', 'Lasagne');
|
||||
self::assertSelectorTextContains('span.tag', 'Sa');
|
||||
self::assertSelectorExists('a[href$="/bearbeiten"]');
|
||||
self::assertSelectorExists('form[action$="/loeschen"]');
|
||||
}
|
||||
|
||||
/**
|
||||
* Client + sauberes Schema (+ optional eingeloggte Session) bereitstellen.
|
||||
*/
|
||||
private function vorbereiten(bool $auth = true): KernelBrowser
|
||||
{
|
||||
$client = self::createClient();
|
||||
$client->followRedirects(false);
|
||||
$this->client = $client;
|
||||
|
||||
$em = $this->em();
|
||||
$schemaTool = new SchemaTool($em);
|
||||
$schemaTool->dropSchema($em->getMetadataFactory()->getAllMetadata());
|
||||
$schemaTool->createSchema($em->getMetadataFactory()->getAllMetadata());
|
||||
|
||||
if ($auth) {
|
||||
$session = self::getContainer()->get('session.factory')->createSession();
|
||||
$session->start();
|
||||
foreach (self::AUTH_KEYS as $k => $v) {
|
||||
$session->set($k, $v);
|
||||
}
|
||||
$session->save();
|
||||
$client->getCookieJar()->set(new Cookie($session->getName(), $session->getId()));
|
||||
}
|
||||
|
||||
return $client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Legt eine Mahlzeit über die UI an und liefert die neue DB-ID.
|
||||
*/
|
||||
private function anlegen(KernelBrowser $client, string $name, string $notiz, array $wochentage): int
|
||||
{
|
||||
$client->request('POST', '/mahlzeiten', [
|
||||
'name' => $name,
|
||||
'notiz' => $notiz,
|
||||
'wochentage' => $wochentage,
|
||||
]);
|
||||
self::assertResponseRedirects('/mahlzeiten');
|
||||
|
||||
$em = $this->em();
|
||||
$em->clear();
|
||||
|
||||
$entity = $em->getRepository(MahlzeitEntity::class)->findOneBy(['name' => $name]);
|
||||
self::assertNotNull($entity);
|
||||
|
||||
return (int) $entity->getId();
|
||||
}
|
||||
|
||||
private function em(): EntityManagerInterface
|
||||
{
|
||||
return self::getContainer()->get('doctrine')->getManager();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user