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.
144 lines
6.0 KiB
Go
144 lines
6.0 KiB
Go
package classes
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// TestConvertH1 pins ConvertH1's byte rule: only h1 tag names change.
|
|
func TestConvertH1(t *testing.T) {
|
|
cases := []struct {
|
|
in, want string
|
|
n int
|
|
}{
|
|
{"<h1>A</h1><p>x</p>", "<h2>A</h2><p>x</p>", 1},
|
|
{`<h1 class="x" id='y'>T</h1>`, `<h2 class="x" id='y'>T</h2>`, 1},
|
|
{"<H1>Up</H1>", "<h2>Up</h2>", 1},
|
|
{"<h1>a</h1>\n<h1>b</h1>", "<h2>a</h2>\n<h2>b</h2>", 2},
|
|
{"<p>no heading & text</p>", "<p>no heading & text</p>", 0},
|
|
{"<!-- <h1>c</h1> --><p>x</p>", "<!-- <h1>c</h1> --><p>x</p>", 0},
|
|
{"<script>var s = '<h1>';</script>", "<script>var s = '<h1>';</script>", 0},
|
|
{"<h10>x</h10><h1>y</h1>", "<h10>x</h10><h2>y</h2>", 1},
|
|
{"# Markdown <h1>raw</h1> a < b", "# Markdown <h2>raw</h2> a < b", 1},
|
|
{"<p>unterminated <h1>x", "<p>unterminated <h2>x", 1},
|
|
{"text <", "text <", 0},
|
|
{"", "", 0},
|
|
}
|
|
for _, c := range cases {
|
|
got, n := ConvertH1(c.in)
|
|
if got != c.want || n != c.n {
|
|
t.Errorf("ConvertH1(%q) = %q, %d; want %q, %d", c.in, got, n, c.want, c.n)
|
|
}
|
|
}
|
|
// L1 WR-03: input without a match comes back identical, and an h1
|
|
// followed by an unfinished tag keeps the tail.
|
|
for _, in := range []string{"p a <b", "x<y", "<p>a <b class=\"c", "a < b"} {
|
|
if out, n := ConvertH1(in); out != in || n != 0 {
|
|
t.Errorf("ConvertH1(%q) = %q %d", in, out, n)
|
|
}
|
|
}
|
|
if out, n := ConvertH1("<h1>x</h1><p>a <b"); out != "<h2>x</h2><p>a <b" || n != 1 {
|
|
t.Errorf("unfinished tail = %q %d", out, n)
|
|
}
|
|
}
|
|
|
|
// TestConvertContentH1 pins the body pair: content_html's count when its
|
|
// HTML had H1s, content's otherwise (a markdown body's H1 is only in its
|
|
// rendered HTML), both converted.
|
|
func TestConvertContentH1(t *testing.T) {
|
|
c, h, n := ConvertContentH1("# Title\n\ntext", "<h1>Title</h1><p>text</p>")
|
|
if c != "# Title\n\ntext" || h != "<h2>Title</h2><p>text</p>" || n != 1 {
|
|
t.Fatalf("markdown body = %q %q %d", c, h, n)
|
|
}
|
|
c, h, n = ConvertContentH1("<h1>a</h1><h1>b</h1>", "")
|
|
if c != "<h2>a</h2><h2>b</h2>" || h != "" || n != 2 {
|
|
t.Fatalf("HTML body without rendered HTML = %q %q %d", c, h, n)
|
|
}
|
|
if _, _, n := ConvertContentH1("<p>x</p>", "<p>x</p>"); n != 0 {
|
|
t.Fatal("no H1, nothing converted")
|
|
}
|
|
if CountH1(`<h1>a</h1><div><h1 class="x">b</h1></div><h2>c</h2>`) != 2 || CountH1("") != 0 {
|
|
t.Fatal("CountH1 is wrong")
|
|
}
|
|
// L1 CR-02: markdown with a fenced html sample and an inline code h1 is
|
|
// kept byte for byte; its rendered HTML has the real heading converted
|
|
// and the code still escaped.
|
|
md := "# Title\n\nExample:\n\n```html\n<h1>Hello</h1>\n```\n\nInline `<h1>` tag."
|
|
rendered, err := FormatHTML(md)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
c, h, n = ConvertContentH1(md, rendered)
|
|
if c != md {
|
|
t.Fatalf("the markdown source was rewritten:\n%s", c)
|
|
}
|
|
if n != 1 || !strings.Contains(h, "<h2") || strings.Contains(h, "<h1") || !strings.Contains(h, "<h1>Hello</h1>") || !strings.Contains(h, "<code><h1></code>") {
|
|
t.Fatalf("rendered HTML = %q n=%d", h, n)
|
|
}
|
|
// 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
|
|
// takes the same path as a post body).
|
|
func TestConvertTranslationH1(t *testing.T) {
|
|
attrs := map[string]any{"title": "<h1>not content</h1>", "content": "<h1>Pillar</h1><p>x</p>", "content_html": 5}
|
|
if n := ConvertTranslationH1(attrs); n != 1 || attrs["content"] != "<h2>Pillar</h2><p>x</p>" || attrs["title"] != "<h1>not content</h1>" || attrs["content_html"] != 5 {
|
|
t.Fatalf("attrs = %v n=%d", attrs, n)
|
|
}
|
|
both := map[string]any{"content": "# T", "content_html": "<h1>T</h1>"}
|
|
if n := ConvertTranslationH1(both); n != 1 || both["content_html"] != "<h2>T</h2>" || both["content"] != "# T" {
|
|
t.Fatalf("both = %v n=%d", both, n)
|
|
}
|
|
if ConvertTranslationH1(nil) != 0 || ConvertTranslationH1(map[string]any{"title": "x"}) != 0 {
|
|
t.Fatal("nothing to convert")
|
|
}
|
|
md := "Sample:\n\n```html\n<h1>Hi</h1>\n```\n"
|
|
tr := map[string]any{"content": md, "content_html": "<pre><code><h1>Hi</h1></code></pre>"}
|
|
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)
|
|
}
|
|
}
|