|
|
@@ -59,6 +59,54 @@ final class TwigViewTest extends TestCase
|
|
|
self::assertStringContainsString('/assets/js/vendor/htmx.min.js', $html);
|
|
|
}
|
|
|
|
|
|
+ public function testHomeRendersDeletedSprintFlashWhenSet(): void
|
|
|
+ {
|
|
|
+ // R01-N26: the green "Sprint X was deleted" chip is now driven by
|
|
|
+ // the `deletedSprintName` view variable (sourced from a one-shot
|
|
|
+ // session flash, not from `?deleted=`). Pin that the chip appears
|
|
|
+ // for any non-empty string and that the name is HTML-escaped.
|
|
|
+ $html = $this->view->render('home', [
|
|
|
+ 'title' => 'Sprint Planner',
|
|
|
+ 'csrfToken' => 'tok',
|
|
|
+ 'currentUser' => $this->makeUser(true),
|
|
|
+ 'schemaVersion' => 3,
|
|
|
+ 'dbPath' => '/tmp/x',
|
|
|
+ 'appEnv' => 'production',
|
|
|
+ 'oidcConfigured' => false,
|
|
|
+ 'localAdminEnabled' => true,
|
|
|
+ 'authError' => false,
|
|
|
+ 'deletedSprintName' => 'Sprint <Alpha>',
|
|
|
+ 'sprintRows' => [],
|
|
|
+ ]);
|
|
|
+
|
|
|
+ self::assertStringContainsString('was deleted', $html);
|
|
|
+ self::assertStringContainsString('Sprint <Alpha>', $html);
|
|
|
+ self::assertStringNotContainsString(
|
|
|
+ '<b>Sprint <Alpha></b>',
|
|
|
+ $html,
|
|
|
+ 'sprint name must be Twig-autoescaped — never rendered raw',
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ public function testHomeOmitsDeletedSprintFlashWhenEmpty(): void
|
|
|
+ {
|
|
|
+ $html = $this->view->render('home', [
|
|
|
+ 'title' => 'Sprint Planner',
|
|
|
+ 'csrfToken' => 'tok',
|
|
|
+ 'currentUser' => $this->makeUser(true),
|
|
|
+ 'schemaVersion' => 3,
|
|
|
+ 'dbPath' => '/tmp/x',
|
|
|
+ 'appEnv' => 'production',
|
|
|
+ 'oidcConfigured' => false,
|
|
|
+ 'localAdminEnabled' => true,
|
|
|
+ 'authError' => false,
|
|
|
+ 'deletedSprintName' => '',
|
|
|
+ 'sprintRows' => [],
|
|
|
+ ]);
|
|
|
+
|
|
|
+ self::assertStringNotContainsString('was deleted', $html);
|
|
|
+ }
|
|
|
+
|
|
|
public function testHomeForAnonymousUserHidesRuntimePanel(): void
|
|
|
{
|
|
|
$html = $this->view->render('home', [
|