aboutsummaryrefslogtreecommitdiff
path: root/internal/web/server_test.go
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-07-23 15:00:17 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-07-23 15:00:17 +0200
commitaa6c9dad764089a3f0e287b5f90937e97dde3921 (patch)
tree213f7f56bf3c5ca1db82f0eee7c482886fa9ce43 /internal/web/server_test.go
parent66a17fda33e44e9022d6724f850323f754715259 (diff)
downloadlectio-aa6c9dad764089a3f0e287b5f90937e97dde3921.tar.gz
lectio-aa6c9dad764089a3f0e287b5f90937e97dde3921.zip
web: horizontal/vertical/interlinear display modes + web_display config
Adds a display-layout option to lectio-web alongside the existing colour themes: - config: WebDisplay field (toml web_display), default "horizontal", normalized on load via the new exported config.NormalizeDisplay (unknown -> "horizontal", same lenient style as the other web_* fields). - render: extracts the citation/ToEnglishRef resolution shared by GatherVersion into an unexported resolveRef helper (GatherVersion's signature/behavior unchanged) and adds GatherVerses(version, sec, lectionary) returning raw bible.Verse structs plus a versified flag, for column/interlinear alignment. - web/render: RenderReadings gains a display parameter. "horizontal" is the original stacked layout, byte-for-byte the same code path as before. "vertical" reuses the same per-version column data in a .display-vertical/.vcol grid (the CLI compare view, browser-side). "interlinear" maps versions through render.OfflineVersions' pl->wuj substitution (pl has no verse numbers), gathers GatherVerses per version, and interleaves them by chapter:verse into .ilverse/.illine blocks (ordered union of keys, first versified version's order first). - server: adds a display <select> to the top bar wired into the existing #controls HTMX form; a resolveQuery(cfg, r) helper replaces the duplicated date/lectionary/all/versions(+now display) resolution in indexHandler and readingsHandler. - fold-in from the B2 review: indexHandler now populates indexData.Lookup via a shared renderLookup(ref, versions) helper (also used by lookupHandler), so a bookmarked "/?ref=...&v=..." link shows its lookup result instead of an empty pane. - base.css: layout-only rules for the two new modes (no colours; themes stay colour-only). go test ./..., go vet ./..., gofmt clean; go build ./cmd/lectio-web ok.
Diffstat (limited to 'internal/web/server_test.go')
-rw-r--r--internal/web/server_test.go91
1 files changed, 91 insertions, 0 deletions
diff --git a/internal/web/server_test.go b/internal/web/server_test.go
index 9e37177..b5cf78a 100644
--- a/internal/web/server_test.go
+++ b/internal/web/server_test.go
@@ -104,4 +104,95 @@ func TestServer(t *testing.T) {
t.Error("theme.css fallback served empty body")
}
})
+
+ t.Run("readings partial interlinear", func(t *testing.T) {
+ rec := httptest.NewRecorder()
+ srv.ServeHTTP(rec, httptest.NewRequest("GET", "/readings?date=2026-07-22&v=wuj&v=vul&display=interlinear", nil))
+ if rec.Code != http.StatusOK {
+ t.Fatalf("status = %d, want 200", rec.Code)
+ }
+ body := rec.Body.String()
+ if strings.Contains(body, "<html") {
+ t.Errorf("partial is not a fragment: %q", body)
+ }
+ i := strings.Index(body, `class="vnum"`)
+ if i == -1 {
+ t.Fatalf("interlinear partial missing a vnum verse label: %q", body)
+ }
+ // Bound the first ilverse block by the next vnum span (each ilverse
+ // carries exactly one), so the check covers every version's line
+ // grouped under that one key, not just the first.
+ block := body[i:]
+ if j := strings.Index(body[i+1:], `class="vnum"`); j != -1 {
+ block = body[i : i+1+j]
+ }
+ if !strings.Contains(block, "Wujek") || !strings.Contains(block, "Wulgata") {
+ t.Errorf("interlinear verse block missing both version labels grouped together: %q", block)
+ }
+ })
+
+ t.Run("readings partial vertical", func(t *testing.T) {
+ rec := httptest.NewRecorder()
+ srv.ServeHTTP(rec, httptest.NewRequest("GET", "/readings?date=2026-07-22&v=wuj&v=vul&display=vertical", nil))
+ if rec.Code != http.StatusOK {
+ t.Fatalf("status = %d, want 200", rec.Code)
+ }
+ if !strings.Contains(rec.Body.String(), "display-vertical") {
+ t.Errorf("vertical partial missing display-vertical: %q", rec.Body.String())
+ }
+ })
+
+ t.Run("readings partial interlinear substitutes pl", func(t *testing.T) {
+ rec := httptest.NewRecorder()
+ srv.ServeHTTP(rec, httptest.NewRequest("GET", "/readings?date=2026-07-22&v=pl&display=interlinear", nil))
+ if rec.Code != http.StatusOK {
+ t.Fatalf("status = %d, want 200", rec.Code)
+ }
+ body := rec.Body.String()
+ if !strings.Contains(body, "Wujek") {
+ t.Errorf("pl should be substituted with wuj in interlinear mode: %q", body)
+ }
+ if strings.Contains(body, "Polski (niedziela.pl)") {
+ t.Errorf("pl paragraph column should not appear in interlinear mode: %q", body)
+ }
+ })
+
+ t.Run("readings partial bad display falls back to horizontal", func(t *testing.T) {
+ rec := httptest.NewRecorder()
+ srv.ServeHTTP(rec, httptest.NewRequest("GET", "/readings?date=2026-07-22&v=wuj&display=bogus", nil))
+ if rec.Code != http.StatusOK {
+ t.Fatalf("status = %d, want 200", rec.Code)
+ }
+ body := rec.Body.String()
+ if strings.Contains(body, "display-vertical") || strings.Contains(body, "ilverse") {
+ t.Errorf("bad display should fall back to horizontal, got: %q", body)
+ }
+ })
+
+ t.Run("index page seeds lookup results from ?ref=", func(t *testing.T) {
+ rec := httptest.NewRecorder()
+ srv.ServeHTTP(rec, httptest.NewRequest("GET", "/?ref=J+20:1&v=wuj", nil))
+ if rec.Code != http.StatusOK {
+ t.Fatalf("status = %d, want 200", rec.Code)
+ }
+ body := rec.Body.String()
+ if !strings.Contains(body, `id="lookup-results"`) {
+ t.Fatalf("body missing lookup-results container: %q", body)
+ }
+ i := strings.Index(body, `id="lookup-results"`)
+ if !strings.Contains(body[i:], "20:1") {
+ t.Errorf("lookup-results not populated from ?ref=: %q", body[i:min(i+400, len(body))])
+ }
+ })
+
+ t.Run("index page seeds display select from cfg", func(t *testing.T) {
+ rec := httptest.NewRecorder()
+ srv.ServeHTTP(rec, httptest.NewRequest("GET", "/?date=2026-07-22&v=wuj", nil))
+ if rec.Code != http.StatusOK {
+ t.Fatalf("status = %d, want 200", rec.Code)
+ }
+ if !strings.Contains(rec.Body.String(), `name="display"`) {
+ t.Errorf("body missing display select: %q", rec.Body.String())
+ }
+ })
}