fix: H1 conversion leaves code alone in markdown that starts with HTML

ConvertContentH1 reads content as markdown on every path and converts it
outside goldmark's code ranges only; a body opening with an HTML block
keeps the h1 tags of its code samples, a raw h1 outside code is converted
as in v0.3.1 (the 33-15 test pin for that case is reversed).

quick-261104-xae, review WR-01.
This commit is contained in:
Jakub Zych
2026-11-05 00:17:53 +01:00
parent 0a62090435
commit a41d537e52
4 changed files with 111 additions and 16 deletions

View File

@@ -75,12 +75,41 @@ func TestConvertContentH1(t *testing.T) {
if n != 1 || !strings.Contains(h, "<h2") || strings.Contains(h, "<h1") || !strings.Contains(h, "&lt;h1&gt;Hello&lt;/h1&gt;") || !strings.Contains(h, "<code>&lt;h1&gt;</code>") {
t.Fatalf("rendered HTML = %q n=%d", h, n)
}
// Markdown without rendered HTML: nothing changes, nothing counted.
if c, h, n := ConvertContentH1("plain <h1>x</h1> text", ""); c != "plain <h1>x</h1> text" || h != "" || n != 0 {
// A raw h1 outside code is a heading in the rendered page, so it is
// converted in the markdown source too (as v0.3.1 did), and counted
// when there is no rendered HTML.
if c, h, n := ConvertContentH1("plain <h1>x</h1> text", ""); c != "plain <h2>x</h2> text" || h != "" || n != 1 {
t.Fatalf("markdown without HTML = %q %q %d", c, h, n)
}
}
// TestConvertContentH1HTMLLead pins markdown that starts with an HTML
// block (33-REVIEW-GAPFIX WR-01): the first byte does not make it HTML,
// so the h1 samples in its code keep their tags and only a raw h1 outside
// code is converted.
func TestConvertContentH1HTMLLead(t *testing.T) {
probe := "<p class=\"lead\">Intro</p>\n\n```html\n<h1>Hello</h1>\n```"
rendered, err := FormatHTML(probe)
if err != nil {
t.Fatal(err)
}
for _, html := range []string{rendered, ""} {
c, h, n := ConvertContentH1(probe, html)
if c != probe || h != html || n != 0 {
t.Fatalf("probe with content_html %q = %q %q n=%d", html, c, h, n)
}
}
mixed := "<p class=\"lead\">Intro</p>\n\n<h1>Real</h1>\n\n```html\n<h1>Hello</h1>\n```"
want := "<p class=\"lead\">Intro</p>\n\n<h2>Real</h2>\n\n```html\n<h1>Hello</h1>\n```"
if c, h, n := ConvertContentH1(mixed, ""); c != want || h != "" || n != 1 {
t.Fatalf("raw h1 outside code = %q %q n=%d", c, h, n)
}
span := "<p>x</p>\n\nUse `<h1>` here"
if c, _, n := ConvertContentH1(span, ""); c != span || n != 0 {
t.Fatalf("code span = %q n=%d", c, n)
}
}
// TestConvertTranslationH1 pins a locale's translated attributes,
// changed in place: content and content_html converted, other attributes
// and values that are not strings left alone (a category's pillar text
@@ -102,4 +131,13 @@ func TestConvertTranslationH1(t *testing.T) {
if n := ConvertTranslationH1(tr); n != 0 || tr["content"] != md {
t.Fatalf("a markdown translation was rewritten: %v n=%d", tr, n)
}
probe := "<p class=\"lead\">Intro</p>\n\n```html\n<h1>Hello</h1>\n```"
rendered, err := FormatHTML(probe)
if err != nil {
t.Fatal(err)
}
lead := map[string]any{"content": probe, "content_html": rendered}
if n := ConvertTranslationH1(lead); n != 0 || lead["content"] != probe {
t.Fatalf("markdown with an HTML lead was rewritten: %v n=%d", lead, n)
}
}