A bundle nested under another bundle is a member of that series (ADR-0033), so
`Site.Sequence` walks up to the nearest bundle ancestor and back down to its
members: ordered by `order` where set, then by name. Members resolve through the
language fallback, so a chapter with no Bengali variant still holds its place in
Bengali reading order instead of breaking prev/next.
One `.Sequence` field carries both shapes a theme needs. A landing page renders
`.Members` as an archive; a chapter renders `.Prev`/`.Next`, which are pointers
into `.Members` so `{{with}}` yields nothing at the ends. `Index == 0` is what
tells the two apart.
`Query` was deliberately not extended. A series ascends where `Run` descends, and
an order knob on `Query` is the config knob rule 6 bans; instead `Site.keys()`
came out so both iterate the index one way, deleting `Run`'s own dedupe map.
`draft` is not honoured: no bundle carries the field and nothing else excludes
drafts, so entry 19 adds it in both places at once. Recorded in content-model.md
rather than left implied.
state.md also corrects six inventory rows that had drifted before this change —
three LOC figures, the test total, `go.mod`, and two lines that were flatly wrong
("Dependencies: none", "goldmark is not yet imported"). The coupling gate proves
state.md changed with the code; it cannot prove the numbers are right.
117 lines
3.1 KiB
Go
117 lines
3.1 KiB
Go
package render
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
"testing/fstest"
|
|
|
|
"khosra/internal/content"
|
|
)
|
|
|
|
func TestBundleRendersMarkdownIntoTheTheme(t *testing.T) {
|
|
r, err := New(nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
b, err := content.Parse("posts/hello.md", []byte("---\ntitle: Hello\n---\n\n# Heading\n\nSome *prose*.\n"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
out, err := r.Bundle(b, "en", []string{"en"}, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got := string(out)
|
|
for _, want := range []string{
|
|
"<!doctype html>", `<html lang="en">`, "<title>Hello</title>",
|
|
"<h1>Hello</h1>", "<em>prose</em>", "<style>",
|
|
} {
|
|
if !strings.Contains(got, want) {
|
|
t.Errorf("output missing %q\n---\n%s", want, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestBundleWithoutTitleFallsBackToKey(t *testing.T) {
|
|
r, err := New(nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
b, err := content.Parse("status/note.md", []byte("just a note\n"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
out, err := r.Bundle(b, "en", []string{"en"}, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !strings.Contains(string(out), "<title>status/note</title>") {
|
|
t.Errorf("a titleless bundle must still produce a title element:\n%s", out)
|
|
}
|
|
}
|
|
|
|
func TestSiteOverridesOneBlockAndInheritsTheRest(t *testing.T) {
|
|
siteFS := fstest.MapFS{
|
|
"templates/page.html": {Data: []byte(`{{define "main"}}<section class="mine">{{.Title}}</section>{{end}}`)},
|
|
}
|
|
r, err := New(siteFS)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
b, err := content.Parse("pages/about.md", []byte("---\ntitle: About\n---\nbody\n"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
out, err := r.Bundle(b, "en", []string{"en"}, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got := string(out)
|
|
if !strings.Contains(got, `<section class="mine">About</section>`) {
|
|
t.Errorf("the site override should win:\n%s", got)
|
|
}
|
|
if !strings.Contains(got, "<!doctype html>") || !strings.Contains(got, `<link rel="canonical"`) {
|
|
t.Errorf("the document should still come from the embedded base:\n%s", got)
|
|
}
|
|
if strings.Contains(got, "<article>") {
|
|
t.Error("the embedded main should have been replaced, not appended")
|
|
}
|
|
}
|
|
|
|
func TestAListingOverrideDoesNotLeakIntoBundlePages(t *testing.T) {
|
|
siteFS := fstest.MapFS{
|
|
"templates/list.html": {Data: []byte(`{{define "main"}}LISTING ONLY{{end}}`)},
|
|
}
|
|
r, err := New(siteFS)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
b, err := content.Parse("pages/about.md", []byte("---\ntitle: About\n---\nbody\n"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
out, err := r.Bundle(b, "en", nil, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if strings.Contains(string(out), "LISTING ONLY") {
|
|
t.Error("a listing override must not reach bundle pages — that is why sets are per kind")
|
|
}
|
|
}
|
|
|
|
func TestSiteStylesheetReplacesTheReferenceOne(t *testing.T) {
|
|
siteFS := fstest.MapFS{"templates/theme.css": {Data: []byte("body{color:rebeccapurple}")}}
|
|
r, err := New(siteFS)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
b, _ := content.Parse("pages/x.md", []byte("hi\n"))
|
|
out, err := r.Bundle(b, "en", nil, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !strings.Contains(string(out), "rebeccapurple") {
|
|
t.Error("the site stylesheet should replace the reference one")
|
|
}
|
|
}
|