mirror of
https://github.com/MarijnDoeve/TijdVoorDeTest.git
synced 2026-07-11 12:28:23 +02:00
2fd15ba8fa
* test: expand coverage, dedupe data-driven tests, extract shared WebTestCase base - Add #[CoversClass] to Base64Test and FilenameSanitizerTest - Merge near-duplicate test methods into #[DataProvider] cases across ResetPasswordControllerTest, SettingsControllerTest, ClaimSeasonCommandTest, FilenameSanitizerTest, Base64Test, and SeasonRepositoryTest - Add integration tests for previously untested controllers: public QuizController (quiz-taking flow), LoginController, RegistrationController, EliminationController, and PrepareEliminationController - Add unit tests for Elimination and BankQuestion entity logic - Extract shared WebTestCase setup/helpers (client, entityManager, login, entity lookups, CSRF token scraping) into AbstractControllerWebTestCase, removing duplicated boilerplate from all 14 WebTestCase files * test: address PR review feedback - Scope AbstractControllerWebTestCase::getCandidate/getQuizByName by season code (both Candidate and Quiz are only unique per season, not system-wide) and add a CandidateRepository regression test guarding against same-named candidates in different seasons - Add missing entityManager->clear() before verifying DB state after a POST in PrepareEliminationControllerTest and QuestionBankControllerTest - Add non-owner denial tests for BackofficeController::exportQuiz and QuizQuestionController::edit/reorder, which had IsGranted checks with no test coverage * ci: publish PHPUnit coverage to GitHub code coverage - Generate a Cobertura report alongside the existing JUnit report and upload it with actions/upload-code-coverage so coverage shows up on PRs and the default branch via GitHub's code coverage feature - Add a step to copy both reports out of the php container before publishing them, since var/ is a Docker volume (see the Dockerfile's VOLUME /app/var/) and isn't bind-mounted to the runner — this also fixes the existing JUnit report publishing step, which was silently looking at a path that never had contets on the runner * ci: fix coverage report paths and tolerate Code Quality not yet enabled - Write PHPUnit's JUnit and Cobertura reports to reports/ instead of var/, since var/ is a Docker volume (Dockerfile's VOLUME /app/var/) that isn't bind-mounted to the runner - reports written there never reached the host, which is also why the prior junit.xml publish step had nothing to read. reports/ is a plain path under the project's bind mount, so no extra copy-out step is needed - Set fail-on-error: false on the coverage upload step: it 404s until "Code Quality" is turned on for the repo under Settings > Code security > Code quality, a one-time manual step
103 lines
4.0 KiB
PHP
103 lines
4.0 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Tvdt\Tests\Controller;
|
|
|
|
use Doctrine\ORM\EntityManagerInterface;
|
|
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
|
|
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
|
use Symfony\Component\DomCrawler\Crawler;
|
|
use Symfony\Component\HttpFoundation\Request;
|
|
use Tvdt\Entity\Candidate;
|
|
use Tvdt\Entity\Quiz;
|
|
use Tvdt\Entity\Season;
|
|
use Tvdt\Entity\User;
|
|
|
|
abstract class AbstractControllerWebTestCase extends WebTestCase
|
|
{
|
|
protected KernelBrowser $client;
|
|
|
|
protected EntityManagerInterface $entityManager;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
$this->client = self::createClient();
|
|
$this->entityManager = self::getContainer()->get(EntityManagerInterface::class);
|
|
}
|
|
|
|
protected function getUserByEmail(string $email): User
|
|
{
|
|
$user = $this->entityManager->getRepository(User::class)->findOneBy(['email' => $email]);
|
|
$this->assertInstanceOf(User::class, $user);
|
|
|
|
return $user;
|
|
}
|
|
|
|
protected function loginAs(string $email): void
|
|
{
|
|
$this->client->loginUser($this->getUserByEmail($email));
|
|
}
|
|
|
|
/** Quiz names are only unique per season (see Quiz's UniqueConstraint), so this is scoped by season code. */
|
|
protected function getQuizByName(string $name, string $seasonCode = 'krtek'): Quiz
|
|
{
|
|
$quiz = $this->entityManager->getRepository(Quiz::class)->findOneBy(['name' => $name, 'season' => $this->getSeasonByCode($seasonCode)]);
|
|
$this->assertInstanceOf(Quiz::class, $quiz);
|
|
|
|
return $quiz;
|
|
}
|
|
|
|
/** Candidate names are only unique per season (see Candidate's UniqueConstraint), so this is scoped by season code. */
|
|
protected function getCandidate(string $name, string $seasonCode = 'krtek'): Candidate
|
|
{
|
|
$candidate = $this->entityManager->getRepository(Candidate::class)->findOneBy(['name' => $name, 'season' => $this->getSeasonByCode($seasonCode)]);
|
|
$this->assertInstanceOf(Candidate::class, $candidate);
|
|
|
|
return $candidate;
|
|
}
|
|
|
|
protected function getSeasonByCode(string $seasonCode): Season
|
|
{
|
|
$season = $this->entityManager->getRepository(Season::class)->findOneBy(['seasonCode' => $seasonCode]);
|
|
$this->assertInstanceOf(Season::class, $season);
|
|
|
|
return $season;
|
|
}
|
|
|
|
/** GETs $url and extracts the CSRF token from a form whose action contains $formActionContains. */
|
|
protected function getCsrfTokenFromPage(string $url, string $formActionContains, string $tokenFieldName = '_token'): string
|
|
{
|
|
$crawler = $this->client->request(Request::METHOD_GET, $url);
|
|
self::assertResponseIsSuccessful();
|
|
|
|
return $this->getCsrfTokenFromCrawler($crawler, $formActionContains, $tokenFieldName);
|
|
}
|
|
|
|
/** Extracts the CSRF token from a form on the page already loaded in the client. */
|
|
protected function getCsrfTokenFromCurrentPage(string $formActionContains, string $tokenFieldName = '_token'): string
|
|
{
|
|
return $this->getCsrfTokenFromCrawler($this->client->getCrawler(), $formActionContains, $tokenFieldName);
|
|
}
|
|
|
|
/** GETs $url and extracts the CSRF token input, regardless of which form it belongs to. */
|
|
protected function getTokenFromPage(string $url, string $tokenFieldName = '_token'): string
|
|
{
|
|
$crawler = $this->client->request(Request::METHOD_GET, $url);
|
|
self::assertResponseIsSuccessful();
|
|
|
|
$input = $crawler->filter(\sprintf('input[name="%s"]', $tokenFieldName));
|
|
$this->assertGreaterThan(0, $input->count(), \sprintf('No input named "%s" found on the page', $tokenFieldName));
|
|
|
|
return (string) $input->first()->attr('value');
|
|
}
|
|
|
|
private function getCsrfTokenFromCrawler(Crawler $crawler, string $formActionContains, string $tokenFieldName): string
|
|
{
|
|
$input = $crawler->filter(\sprintf('form[action*="%s"] input[name="%s"]', $formActionContains, $tokenFieldName));
|
|
$this->assertGreaterThan(0, $input->count(), \sprintf('No form found with action containing "%s"', $formActionContains));
|
|
|
|
return (string) $input->first()->attr('value');
|
|
}
|
|
}
|