callout_test.go
package display
import (
"bytes"
"strings"
"testing"
"github.com/yuin/goldmark/parser"
)
// renderMD runs source through the real markdown pipeline (the same package
// global used to serve pages), so these tests exercise the wired-in extension.
func renderMD(t *testing.T, source string) string {
t.Helper()
var buf bytes.Buffer
ctx := parser.NewContext(parser.WithIDs(newSlugIDs()))
if err := markdown.Convert([]byte(source), &buf, parser.WithContext(ctx)); err != nil {
t.Fatalf("convert: %v", err)
}
return buf.String()
}
func TestCalloutBareType(t *testing.T) {
got := renderMD(t, ":::note\nheads up\n:::\n")
want := "<div class=\"callout callout-note\">\n<p>heads up</p>\n</div>\n"
if got != want {
t.Errorf("got:\n%s\nwant:\n%s", got, want)
}
}
func TestCalloutTypeAndTitle(t *testing.T) {
got := renderMD(t, ":::warning Be careful\nbody here\n:::\n")
want := "<div class=\"callout callout-warning\">\n" +
"<p class=\"callout-title\">Be careful</p>\n" +
"<p>body here</p>\n" +
"</div>\n"
if got != want {
t.Errorf("got:\n%s\nwant:\n%s", got, want)
}
}
func TestCalloutMultiParagraphAndMarkdownBody(t *testing.T) {
got := renderMD(t, ":::tip Handy\nfirst *para* here\n\nsecond para\n\n- one\n- two\n:::\n")
for _, want := range []string{
"<div class=\"callout callout-tip\">\n",
"<p class=\"callout-title\">Handy</p>\n",
"<em>para</em>",
"<p>second para</p>",
"<ul>\n<li>one</li>\n<li>two</li>\n</ul>",
} {
if !strings.Contains(got, want) {
t.Errorf("missing %q in:\n%s", want, got)
}
}
}
func TestCalloutQuadColonCloser(t *testing.T) {
// Opener and closer may both be three-OR-MORE colons.
got := renderMD(t, "::::info\nboxed\n::::\n")
want := "<div class=\"callout callout-info\">\n<p>boxed</p>\n</div>\n"
if got != want {
t.Errorf("got:\n%s\nwant:\n%s", got, want)
}
}
func TestCalloutSurroundingProse(t *testing.T) {
got := renderMD(t, "before text\n\n:::note\ninside\n:::\n\nafter text\n")
for _, want := range []string{
"<p>before text</p>",
"<div class=\"callout callout-note\">",
"<p>inside</p>",
"<p>after text</p>",
} {
if !strings.Contains(got, want) {
t.Errorf("missing %q in:\n%s", want, got)
}
}
}
func TestCalloutUnterminatedRunsToEnd(t *testing.T) {
// An opener with no closer should still render, running to end of document.
got := renderMD(t, ":::warning No closer\nstill inside\nmore body\n")
for _, want := range []string{
"<div class=\"callout callout-warning\">",
"<p class=\"callout-title\">No closer</p>",
"still inside",
"</div>\n",
} {
if !strings.Contains(got, want) {
t.Errorf("missing %q in:\n%s", want, got)
}
}
}
func TestCalloutLoneOpenerFallsBackToParagraph(t *testing.T) {
// A lone ":::" is a closer token, not an opener: with no open callout it is
// just ordinary paragraph text, never a callout div.
got := renderMD(t, ":::\n")
if strings.Contains(got, "callout") {
t.Errorf("lone ::: became a callout; got:\n%s", got)
}
if !strings.Contains(got, "<p>:::</p>") {
t.Errorf("lone ::: should be a plain paragraph; got:\n%s", got)
}
}
func TestCalloutTypeIsEscaped(t *testing.T) {
// The type only appears as a class suffix and must be HTML-escaped, so an
// author string cannot inject structure into our chrome.
got := renderMD(t, ":::a\"><script>\nx\n:::\n")
if strings.Contains(got, "<script>") {
t.Errorf("type escaped incorrectly; got:\n%s", got)
}
}
func TestCalloutTypeNormalized(t *testing.T) {
// warn folds onto the canonical warning type that style.css styles.
got := renderMD(t, ":::warn Heads up\nx\n:::\n")
if !strings.Contains(got, "callout-warning") {
t.Errorf("warn not normalized to warning; got:\n%s", got)
}
}
func TestCalloutUnknownTypePassesThrough(t *testing.T) {
// An unknown type is left as-is on the class, for the neutral fallback box.
got := renderMD(t, ":::obscure\nx\n:::\n")
if !strings.Contains(got, "callout callout-obscure") {
t.Errorf("unknown type not preserved; got:\n%s", got)
}
}
func TestCalloutNoNesting(t *testing.T) {
// An opener inside a callout is left as plain content, not a nested box.
got := renderMD(t, ":::note\n:::warning\ninner\n:::\n")
if strings.Count(got, "callout callout-") != 1 {
t.Errorf("expected exactly one callout div; got:\n%s", got)
}
}
display/callout_test.go · 4.2 KiB · 1f54fea43d