From 51f57d309a8f213137192ad34bc0128e2aabe050 Mon Sep 17 00:00:00 2001 From: Keith Solomon Date: Sun, 26 Jul 2026 00:01:00 -0500 Subject: [PATCH] feat: render the bundled-scenarios list on the home page --- src/Http/Handlers/GetHomePage.php | 36 +++++++++++++++++++++++++++ src/Views/home.php | 15 +++++++++-- tests/Integration/GetHomePageTest.php | 33 ++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 2 deletions(-) diff --git a/src/Http/Handlers/GetHomePage.php b/src/Http/Handlers/GetHomePage.php index 32275ef..62f708a 100644 --- a/src/Http/Handlers/GetHomePage.php +++ b/src/Http/Handlers/GetHomePage.php @@ -9,10 +9,15 @@ use BattleForge\Http\Response; final class GetHomePage { + public function __construct(private readonly string $scenariosDir = '') + { + } + /** @param array $params */ public function handle(Request $request, array $params): Response { $csrf = $request->cookies['__csrf'] ?? ''; + $bundled = $this->loadBundledScenarios(); ob_start(); require_once __DIR__ . '/../../Views/layout.php'; @@ -21,4 +26,35 @@ final class GetHomePage return Response::html(200, $body); } + + /** @return list */ + private function loadBundledScenarios(): array + { + if ($this->scenariosDir === '' || !is_file($this->scenariosDir . '/manifest.json')) { + return []; + } + $decoded = json_decode((string) file_get_contents($this->scenariosDir . '/manifest.json'), true); + if (!is_array($decoded)) { + return []; + } + $entries = []; + foreach ($decoded as $id => $entry) { + if (!is_string($id) || !preg_match('/^[a-z0-9-]+$/', $id) || !is_array($entry)) { + continue; + } + if (!isset($entry['name'], $entry['summary'])) { + continue; + } + if (!is_file($this->scenariosDir . '/' . $id . '.json')) { + continue; + } + $entries[] = [ + 'id' => $id, + 'name' => (string) $entry['name'], + 'summary' => (string) $entry['summary'], + ]; + } + usort($entries, static fn (array $a, array $b): int => strcmp($a['id'], $b['id'])); + return $entries; + } } diff --git a/src/Views/home.php b/src/Views/home.php index 81143f2..428aceb 100644 --- a/src/Views/home.php +++ b/src/Views/home.php @@ -5,15 +5,26 @@ declare(strict_types=1); use BattleForge\Http\Escape; /** @var string $csrf Provided by the front controller. */ +/** @var list $bundled Provided by GetHomePage. */ ?> Escape::html($v); $a = static fn (string $v): string => Escape::attr($v); ?>

BattleForge

New scenario

+

Play bundled

+
    + +
  • + +
  • + +

Recent scenarios

No saved scenarios yet.

headers['Content-Security-Policy']); self::assertStringContainsString('', $response->body); } + + public function testItRendersTheBundledScenariosListFromTheManifest(): void + { + $tmpDir = sys_get_temp_dir() . '/bf-home-bundled-' . bin2hex(random_bytes(4)); + mkdir($tmpDir, 0700, true); + file_put_contents( + $tmpDir . '/manifest.json', + json_encode([ + 'skirmish' => ['name' => 'Skirmish', 'summary' => 'Open ground.'], + 'hold-the-line' => ['name' => 'Hold the Line', 'summary' => 'Defend.'], + ], JSON_THROW_ON_ERROR), + ); + foreach (['skirmish.json', 'hold-the-line.json'] as $file) { + file_put_contents($tmpDir . '/' . $file, '{}'); + } + + $handler = new GetHomePage($tmpDir); + $request = new Request([], [], [], [], [], '', 'GET', '/', 'text/html', ''); + $response = $handler->handle($request, []); + + self::assertSame(200, $response->status); + self::assertStringContainsString('id="bf-bundled"', $response->body); + self::assertStringContainsString('data-bf-bundle="skirmish"', $response->body); + self::assertStringContainsString('data-bf-bundle="hold-the-line"', $response->body); + self::assertStringContainsString('Open ground.', $response->body); + self::assertStringContainsString('Defend.', $response->body); + + unlink($tmpDir . '/manifest.json'); + foreach (['skirmish.json', 'hold-the-line.json'] as $file) { + unlink($tmpDir . '/' . $file); + } + rmdir($tmpDir); + } }