Files
khosra/internal/render/render_test.go
T
bdeshi 1f7ca09313 add shortcodes as the first internal/ext feature
A call is `{{< name key="value" >}}` alone on a line, parsed by a goldmark block
parser into an AST node and rendered by executing a theme template of that name
(ADR-0036). `figure` ships; `include` and `gallery` need the including bundle's
directory, which the parser does not carry yet, so they wait.

The layering did the design work here. internal/render may not import
internal/ext, so render.New takes a callback that receives a Partial and returns
Markdown extensions, and cmd/khosra/wire.go holds the only list of enabled
features. Empty that list and the engine still builds and serves — which is the
property extensions.md says the contract should have.

Raw HTML stays disabled. An author's text reaches a page only as arguments that
html/template escapes in context, which the real binary shows: a hostile alt
becomes &lt;script&gt; and src="javascript:…" becomes #ZgotmplZ. Getting
contextual escaping from the standard library rather than writing it is the whole
reason a fragment renders this instead of the feature.

parseSet became variadic so the fragment set reuses it rather than growing a
second copy of the overlay logic; `Partial` takes map[string]string after the
advisory correctly flagged `any` as generality nothing had asked for.
2026-08-01 02:23:35 +06:00

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, 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, 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, nil)
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, nil)
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, nil)
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")
}
}