mirror of
https://github.com/runbear-io/beardrive.git
synced 2026-08-25 08:08:08 +02:00
A leading YAML frontmatter block used to render as goldmark's thematic-break soup (hr + stray text). It now renders as a compact table: keys in the author's order (yaml.Node, not a map), flat lists comma-joined, nested values as compact YAML in <code>, everything HTML-escaped. Anything that isn't a well-formed YAML mapping — mid-doc fences, unclosed fences, list-shaped or invalid YAML — falls through and renders exactly as before; empty frontmatter is simply hidden. Applies everywhere the server renders markdown: the hub viewer and public share pages, each with theme-matched styling. Matters most for OKF/gbrain-style frontmattered knowledge bases. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P5cxPQdSGJnjXCYY9GeWXt
88 lines
2.5 KiB
Go
88 lines
2.5 KiB
Go
package webapp
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// A leading YAML frontmatter block renders as a key/value table (author
|
|
// key order, escaped values) instead of goldmark's thematic-break soup;
|
|
// everything that isn't a clean frontmatter mapping renders exactly as
|
|
// before.
|
|
func TestRenderMarkdownFrontmatter(t *testing.T) {
|
|
src := `---
|
|
title: Q3 findings
|
|
tags: [churn, revenue]
|
|
owner: snow@runbear.io
|
|
meta:
|
|
reviewed: true
|
|
---
|
|
|
|
# Body
|
|
|
|
Hello.`
|
|
out, err := RenderMarkdown([]byte(src))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, want := range []string{
|
|
`<table class="frontmatter">`,
|
|
`<th scope="row">title</th><td>Q3 findings</td>`,
|
|
`<td>churn, revenue</td>`, // flat lists comma-join
|
|
`owner`, `snow@runbear.io`,
|
|
`<code>reviewed: true</code>`, // nested values as compact YAML
|
|
`<h1 id="body">Body</h1>`, // the body still renders
|
|
} {
|
|
if !strings.Contains(out, want) {
|
|
t.Errorf("missing %q in:\n%s", want, out)
|
|
}
|
|
}
|
|
if strings.Contains(out, "<hr") {
|
|
t.Errorf("frontmatter fences leaked as thematic breaks:\n%s", out)
|
|
}
|
|
// Key order preserved: title row precedes owner row.
|
|
if strings.Index(out, ">title<") > strings.Index(out, ">owner<") {
|
|
t.Errorf("frontmatter keys reordered:\n%s", out)
|
|
}
|
|
}
|
|
|
|
func TestRenderMarkdownFrontmatterEscapes(t *testing.T) {
|
|
out, err := RenderMarkdown([]byte("---\nnote: <script>alert(1)</script>\n---\nx"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if strings.Contains(out, "<script>") {
|
|
t.Fatalf("frontmatter value not escaped:\n%s", out)
|
|
}
|
|
if !strings.Contains(out, "<script>") {
|
|
t.Fatalf("escaped value missing:\n%s", out)
|
|
}
|
|
}
|
|
|
|
func TestRenderMarkdownFrontmatterFallthrough(t *testing.T) {
|
|
cases := map[string]struct {
|
|
src string
|
|
wantTable bool
|
|
want string
|
|
}{
|
|
"no frontmatter": {"# Hi\n\ntext", false, "<h1"},
|
|
"mid-doc fences": {"para\n\n---\n\nmore", false, "<hr"},
|
|
"unclosed fence": {"---\ntitle: x\n\nbody", false, ""},
|
|
"non-mapping yaml": {"---\n- just\n- a list\n---\nbody", false, ""},
|
|
"invalid yaml": {"---\n: : :\n---\nbody", false, ""},
|
|
"empty frontmatter hidden": {"---\n---\nbody", false, "<p>body</p>"},
|
|
}
|
|
for name, c := range cases {
|
|
out, err := RenderMarkdown([]byte(c.src))
|
|
if err != nil {
|
|
t.Fatalf("%s: %v", name, err)
|
|
}
|
|
if got := strings.Contains(out, `class="frontmatter"`); got != c.wantTable {
|
|
t.Errorf("%s: table presence = %v, want %v\n%s", name, got, c.wantTable, out)
|
|
}
|
|
if c.want != "" && !strings.Contains(out, c.want) {
|
|
t.Errorf("%s: missing %q in:\n%s", name, c.want, out)
|
|
}
|
|
}
|
|
}
|