diff --git a/Elternbeirat.Web.Tests/FooterYearTests.cs b/Elternbeirat.Web.Tests/FooterYearTests.cs new file mode 100644 index 0000000..803852b --- /dev/null +++ b/Elternbeirat.Web.Tests/FooterYearTests.cs @@ -0,0 +1,27 @@ +using Elternbeirat.Web.Features.Layout; + +namespace Elternbeirat.Web.Tests; + +/// +/// Unit tests for the footer's copyright year. Pure logic, no PocketBase needed. +/// +public class FooterYearTests +{ + [Fact] + public void New_year_in_Berlin_wins_over_the_UTC_year() + { + // 23:30 UTC on New Year's Eve is already 00:30 in Berlin (winter time, +1), + // so the footer must show the new year although the server clock says the old. + var now = new DateTimeOffset(2026, 12, 31, 23, 30, 0, TimeSpan.Zero); + + FooterYear.For(now).ShouldBe(2027); + } + + [Fact] + public void Ordinary_day_keeps_its_year() + { + var now = new DateTimeOffset(2026, 10, 1, 12, 0, 0, TimeSpan.Zero); + + FooterYear.For(now).ShouldBe(2026); + } +} diff --git a/Elternbeirat.Web.Tests/NavBuilderTests.cs b/Elternbeirat.Web.Tests/NavBuilderTests.cs index f12c2e1..b433b9a 100644 --- a/Elternbeirat.Web.Tests/NavBuilderTests.cs +++ b/Elternbeirat.Web.Tests/NavBuilderTests.cs @@ -43,6 +43,15 @@ public class NavBuilderTests footer.Select(link => link.Title).ShouldBe(["Impressum", "Datenschutz"]); } + [Fact] + public void No_pages_give_empty_menus() + { + // PocketBase down or nothing published yet: both menus are empty lists, not + // null, so the layout can simply skip a column instead of crashing. + NavBuilder.Header([]).ShouldBeEmpty(); + NavBuilder.Footer([]).ShouldBeEmpty(); + } + [Fact] public void Home_page_maps_to_root_every_other_slug_to_its_path() { diff --git a/Elternbeirat.Web.Tests/RouteSmokeTests.cs b/Elternbeirat.Web.Tests/RouteSmokeTests.cs index 32079dc..ca9ba7c 100644 --- a/Elternbeirat.Web.Tests/RouteSmokeTests.cs +++ b/Elternbeirat.Web.Tests/RouteSmokeTests.cs @@ -115,6 +115,30 @@ public sealed partial class RouteSmokeTests : IDisposable html.ShouldNotContain("href=\"/draft\""); // public=false, filtered out } + [Fact] + public async Task Footer_links_every_public_header_and_footer_page_in_order() + { + // The footer repeats the header pages as "Schnellzugriff" and then lists the + // footer pages, each column in its order field. Only the