@@ -31,7 +35,8 @@
+ name="name" value="{{ candidate.name }}" maxlength="16" required autofocus
+ data-action="input->bo--modal#markDirty change->bo--modal#markDirty">
{{ include('backoffice/help/season_candidates.html.twig') }}
diff --git a/tests/Controller/Backoffice/QuizControllerTest.php b/tests/Controller/Backoffice/QuizControllerTest.php
index 04db72a..072b55e 100644
--- a/tests/Controller/Backoffice/QuizControllerTest.php
+++ b/tests/Controller/Backoffice/QuizControllerTest.php
@@ -173,6 +173,47 @@ final class QuizControllerTest extends AbstractControllerWebTestCase
$this->assertTrue($updated->active);
}
+ public function testResetCandidateProgressDeletesGivenAnswersAndClearsStarted(): void
+ {
+ $quiz = $this->getQuizByName('Quiz 1');
+ $candidate = $this->getCandidate('Tom');
+
+ $quizCandidate = new QuizCandidate($quiz, $candidate);
+ $quizCandidate->started = new DateTimeImmutable();
+
+ $this->entityManager->persist($quizCandidate);
+ $firstQuestion = $quiz->questions->first();
+ $this->assertInstanceOf(Question::class, $firstQuestion);
+ $answer = $firstQuestion->answers->first();
+ $this->assertInstanceOf(Answer::class, $answer);
+ $this->entityManager->persist(new GivenAnswer($candidate, $quiz, $answer));
+ $this->entityManager->flush();
+
+ $crawler = $this->client->request(Request::METHOD_GET, \sprintf('/backoffice/season/krtek/quiz/%s/candidates-list', $quiz->id));
+ self::assertResponseIsSuccessful();
+ $token = (string) $crawler->filter(\sprintf('form[action*="/%s/reset"] input[name="_token"]', $candidate->id))->first()->attr('value');
+
+ $this->client->request(Request::METHOD_POST, \sprintf('/backoffice/quiz/%s/candidate/%s/reset', $quiz->id, $candidate->id), [
+ '_token' => $token,
+ ]);
+
+ self::assertResponseRedirects();
+ $this->entityManager->clear();
+
+ $updated = $this->entityManager->getRepository(QuizCandidate::class)->findOneBy([
+ 'quiz' => $this->getQuizByName('Quiz 1'),
+ 'candidate' => $this->getCandidate('Tom'),
+ ]);
+ $this->assertInstanceOf(QuizCandidate::class, $updated);
+ $this->assertNotInstanceOf(\DateTimeImmutable::class, $updated->started);
+
+ $remainingAnswers = $this->entityManager->getRepository(GivenAnswer::class)->findBy([
+ 'quiz' => $quiz,
+ 'candidate' => $candidate,
+ ]);
+ $this->assertCount(0, $remainingAnswers);
+ }
+
public function testModifyCorrection(): void
{
$quiz = $this->getQuizByName('Quiz 1');
diff --git a/tests/Controller/Backoffice/SeasonControllerTest.php b/tests/Controller/Backoffice/SeasonControllerTest.php
index 6c2fabc..956cfc3 100644
--- a/tests/Controller/Backoffice/SeasonControllerTest.php
+++ b/tests/Controller/Backoffice/SeasonControllerTest.php
@@ -104,6 +104,42 @@ final class SeasonControllerTest extends AbstractControllerWebTestCase
$this->assertNotInstanceOf(Candidate::class, $this->entityManager->getRepository(Candidate::class)->find($candidateId));
}
+ public function testAddCandidates(): void
+ {
+ $this->client->request(Request::METHOD_GET, '/backoffice/season/krtek/add-candidate');
+ $form = $this->client->getCrawler()->filter('form')->form([
+ 'add_candidates_form[candidates]' => "Nora\nPiet",
+ ]);
+ $this->client->submit($form);
+
+ self::assertResponseRedirects('/backoffice/season/krtek/candidates');
+ $this->entityManager->clear();
+
+ $season = $this->entityManager->getRepository(Season::class)->findOneBy(['seasonCode' => 'krtek']);
+ $this->assertInstanceOf(Season::class, $season);
+ $names = array_map(static fn (Candidate $candidate): string => $candidate->name, $season->candidates->toArray());
+ $this->assertContains('Nora', $names);
+ $this->assertContains('Piet', $names);
+ }
+
+ public function testAddCandidatesViaTurboFrameReturnsEmptyFrame(): void
+ {
+ $this->client->xmlHttpRequest(Request::METHOD_GET, '/backoffice/season/krtek/add-candidate', server: ['HTTP_TURBO-FRAME' => 'add-candidates-modal-frame']);
+ $form = $this->client->getCrawler()->filter('form')->form([
+ 'add_candidates_form[candidates]' => 'Sanne',
+ ]);
+ $this->client->submit($form, [], ['HTTP_TURBO-FRAME' => 'add-candidates-modal-frame']);
+
+ self::assertResponseIsSuccessful();
+ $this->assertStringContainsString('', (string) $this->client->getResponse()->getContent());
+ $this->entityManager->clear();
+
+ $season = $this->entityManager->getRepository(Season::class)->findOneBy(['seasonCode' => 'krtek']);
+ $this->assertInstanceOf(Season::class, $season);
+ $names = array_map(static fn (Candidate $candidate): string => $candidate->name, $season->candidates->toArray());
+ $this->assertContains('Sanne', $names);
+ }
+
public function testRenameCandidateIsDeniedForNonOwner(): void
{
$candidate = $this->getCandidate('Tom');
diff --git a/translations/messages+intl-icu.nl.xliff b/translations/messages+intl-icu.nl.xliff
index 4733a0e..76eb9ec 100644
--- a/translations/messages+intl-icu.nl.xliff
+++ b/translations/messages+intl-icu.nl.xliff
@@ -117,6 +117,10 @@
Are you sure you want to delete this quiz?
Weet je zeker dat je deze test wilt verwijderen?
+
+ Are you sure you want to reset progress for this candidate? Their given answers for this quiz will be deleted.
+ Weet je zeker dat je de voortgang van deze kandidaat wilt resetten? De ingevulde antwoorden voor deze test worden verwijderd.
+
Assign
Toewijzen
@@ -161,6 +165,10 @@
Candidate not found
Kandidaat niet gevonden
+
+ Candidate progress reset
+ Voortgang kandidaat gereset
+
Candidate renamed
Kandidaat hernoemd
@@ -541,6 +549,10 @@
Number of dropouts:
Aantal afvallers:
+
+ One candidate per line
+ Eén kandidaat per regel
+
Open
Openen
@@ -761,6 +773,10 @@
Reset password
Wachtwoord herstellen
+
+ Reset progress
+ Voortgang resetten
+
Reset your password
Wachtwoord herstellen