Storyline: add the Prologue chapter, extend the layout for sessions
Chapters now carry an eyebrow ("Overview", "Prologue", ...) and a short
tag for the list/sidebar badge, instead of a bare running number, so
non-numbered entries read correctly.
New chapter: "Prologue - The Long Road to Brackenford", the session
outline formatted as a runnable document -- an at-a-glance metadata band
(levels, duration, locations, hook), the in-world courier notice as a
posted handout, numbered scenes, GM callouts for the "ask the players"
prompts, label/body rows for the skill-check clues and travel-day
prompts, a three-stage encounter block, dramatic beat lines, and a
before/after panel for the legend the party's own retelling becomes.
Cross-links the Hearthburrow to Enemies and the closing line to the
overview's "Five and the Five".
Tests: render every chapter partial headless (no HTTP/DB) so a broken
variable or path() fails loudly, check each registered chapter has its
template file, and assert the /storyline routes sit behind the login gate.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Tests\Controller;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||
|
||||
class StorylineControllerTest extends WebTestCase
|
||||
{
|
||||
public function testStorylineRequiresLogin(): void
|
||||
{
|
||||
$client = static::createClient();
|
||||
$client->request('GET', '/storyline');
|
||||
|
||||
self::assertResponseRedirects('/login');
|
||||
}
|
||||
|
||||
public function testUnknownChapterAlsoGoesThroughTheLoginGate(): void
|
||||
{
|
||||
$client = static::createClient();
|
||||
$client->request('GET', '/storyline/does-not-exist');
|
||||
|
||||
// Behind IS_AUTHENTICATED_FULLY, so an anonymous hit redirects rather
|
||||
// than 404s; the 404 path itself is covered in the render test below.
|
||||
self::assertResponseRedirects('/login');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace App\Tests\Storyline;
|
||||
|
||||
use App\Controller\StorylineController;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
use Twig\Environment;
|
||||
|
||||
/**
|
||||
* Renders every storyline chapter partial on its own (no HTTP, no DB) so a
|
||||
* broken variable, filter or path() call in the hand-written content fails
|
||||
* loudly instead of only at request time.
|
||||
*/
|
||||
class ChapterTemplatesTest extends KernelTestCase
|
||||
{
|
||||
/**
|
||||
* @return iterable<string, array{string, string, string}>
|
||||
*/
|
||||
public static function chapters(): iterable
|
||||
{
|
||||
yield 'overview' => [
|
||||
'storyline/chapters/overview.html.twig',
|
||||
'Sir Aldren was one of the five heroes',
|
||||
'Kyuss leaking out',
|
||||
];
|
||||
yield 'prologue' => [
|
||||
'storyline/chapters/prologue-the-long-road.html.twig',
|
||||
'The Crooked Goose',
|
||||
'Tell them the blood is no longer enough',
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('chapters')]
|
||||
public function testChapterPartialRenders(string $template, string $needle1, string $needle2): void
|
||||
{
|
||||
self::bootKernel();
|
||||
/** @var Environment $twig */
|
||||
$twig = self::getContainer()->get('twig');
|
||||
|
||||
$html = $twig->render($template);
|
||||
|
||||
self::assertStringContainsString($needle1, $html);
|
||||
self::assertStringContainsString($needle2, $html);
|
||||
}
|
||||
|
||||
public function testEveryRegisteredChapterHasATemplateFile(): void
|
||||
{
|
||||
$ref = new \ReflectionClass(StorylineController::class);
|
||||
/** @var array<string, array{template: string}> $chapters */
|
||||
$chapters = $ref->getConstant('CHAPTERS');
|
||||
|
||||
self::assertNotEmpty($chapters);
|
||||
foreach ($chapters as $slug => $chapter) {
|
||||
$path = \dirname(__DIR__, 2).'/templates/'.$chapter['template'];
|
||||
self::assertFileExists($path, sprintf('Chapter "%s" points at a missing template.', $slug));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user