in output, got:\n%s", out)
}
}
func TestToHTML_CodeBlock(t *testing.T) {
md := "```go\nfmt.Println(\"hi\")\n```\n"
out, err := ToHTML(md)
if err != nil {
t.Fatalf("ToHTML returned error: %v", err)
}
if !strings.Contains(out, "") {
t.Errorf("expected in output, got:\n%s", out)
}
}
func TestToHTML_Empty(t *testing.T) {
out, err := ToHTML("")
if err != nil {
t.Fatalf("ToHTML returned error for empty input: %v", err)
}
if !strings.HasPrefix(out, "") {
t.Errorf("expected DOCTYPE even for empty input, got:\n%.80s...", out)
}
}
func TestToANSI_Smoke(t *testing.T) {
_, err := ToANSI("# Hello\n\nSome **bold** text.", "dark", 80)
if err != nil {
t.Fatalf("ToANSI returned error: %v", err)
}
}
func TestToHTML_Callout_Note(t *testing.T) {
md := "> [!note]\n> This is a note callout\n"
out, err := ToHTML(md)
if err != nil {
t.Fatalf("ToHTML returned error: %v", err)
}
// Print actual HTML for debugging
t.Logf("Actual HTML output:\n%s", out)
if !strings.Contains(out, "callout") {
t.Errorf("expected 'callout' class in output, got:\n%s", out)
}
if !strings.Contains(out, "callout-note") {
t.Errorf("expected 'callout-note' class in output, got:\n%s", out)
}
if !strings.Contains(out, "This is a note callout") {
t.Errorf("expected callout content in output, got:\n%s", out)
}
}
func TestToHTML_Callout_WithTitle(t *testing.T) {
md := "> [!warning] Custom Warning Title\n> This is a warning\n"
out, err := ToHTML(md)
if err != nil {
t.Fatalf("ToHTML returned error: %v", err)
}
if !strings.Contains(out, "callout-warning") {
t.Errorf("expected 'callout-warning' class in output, got:\n%s", out)
}
if !strings.Contains(out, "Custom Warning Title") {
t.Errorf("expected custom title in output, got:\n%s", out)
}
if !strings.Contains(out, "This is a warning") {
t.Errorf("expected callout content in output, got:\n%s", out)
}
}
func TestToHTML_Callout_MultiParagraph(t *testing.T) {
md := "> [!tip]\n> First paragraph\n> \n> Second paragraph\n"
out, err := ToHTML(md)
if err != nil {
t.Fatalf("ToHTML returned error: %v", err)
}
if !strings.Contains(out, "callout-tip") {
t.Errorf("expected 'callout-tip' class in output, got:\n%s", out)
}
if !strings.Contains(out, "First paragraph") {
t.Errorf("expected first paragraph in output, got:\n%s", out)
}
if !strings.Contains(out, "Second paragraph") {
t.Errorf("expected second paragraph in output, got:\n%s", out)
}
}
func TestToHTML_Callout_Types(t *testing.T) {
tests := []struct {
name string
callType string
wantClass string
}{
{"note", "note", "callout-note"},
{"tip", "tip", "callout-tip"},
{"important", "important", "callout-important"},
{"warning", "warning", "callout-warning"},
{"caution", "caution", "callout-caution"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
md := "> [!" + tt.callType + "]\n> Test content\n"
out, err := ToHTML(md)
if err != nil {
t.Fatalf("ToHTML returned error: %v", err)
}
if !strings.Contains(out, tt.wantClass) {
t.Errorf("expected '%s' class in output, got:\n%s", tt.wantClass, out)
}
})
}
}
func TestToHTML_Callout_NoSpaceSyntax(t *testing.T) {
// Test if >[!note] works without space after >
md := ">[!note] No Space Test\n>This tests the syntax without space\n"
out, err := ToHTML(md)
if err != nil {
t.Fatalf("ToHTML returned error: %v", err)
}
// Check if it rendered as callout or as regular blockquote
if strings.Contains(out, "callout-note") {
// Success: >[!note] (no space) DOES work as callout
if !strings.Contains(out, "No Space Test") {
t.Errorf("expected title in callout output, got:\n%s", out)
}
if !strings.Contains(out, "This tests the syntax without space") {
t.Errorf("expected content in callout output, got:\n%s", out)
}
} else {
// Failure: rendered as blockquote instead
t.Errorf(">[!note] without space did not render as callout. Use '> [!note]' (with space) instead. Got:\n%s", out)
}
}
func TestFormatCalloutsForPlainText_WithTitle(t *testing.T) {
input := "> [!tip] Good News\n> We're ahead of schedule!\n"
expected := "š” Good News\nWe're ahead of schedule!\n"
got := FormatCalloutsForPlainText(input)
if got != expected {
t.Errorf("FormatCalloutsForPlainText with title:\nwant: %q\ngot: %q", expected, got)
}
}
func TestFormatCalloutsForPlainText_NoTitle(t *testing.T) {
input := "> [!note]\n> This is a note\n"
expected := "š Note\nThis is a note\n"
got := FormatCalloutsForPlainText(input)
if got != expected {
t.Errorf("FormatCalloutsForPlainText without title:\nwant: %q\ngot: %q", expected, got)
}
}
func TestFormatCalloutsForPlainText_MultipleCallouts(t *testing.T) {
input := "> [!warning] Action Required\n> Please review by Friday.\n\n> [!note]\n> Please read\n"
expected := "ā ļø Action Required\nPlease review by Friday.\n\nš Note\nPlease read\n"
got := FormatCalloutsForPlainText(input)
if got != expected {
t.Errorf("FormatCalloutsForPlainText with multiple callouts:\nwant: %q\ngot: %q", expected, got)
}
}
func TestFormatCalloutsForPlainText_NoSpaceAfterArrow(t *testing.T) {
input := ">[!tip] Title\n>Content here\n"
got := FormatCalloutsForPlainText(input)
if !strings.Contains(got, "š” Title") {
t.Errorf("FormatCalloutsForPlainText should handle >[!type] without space:\ngot: %q", got)
}
if !strings.Contains(got, "Content here") {
t.Errorf("FormatCalloutsForPlainText should unquote content:\ngot: %q", got)
}
// Should NOT contain > markers
if strings.Contains(got, ">") {
t.Errorf("FormatCalloutsForPlainText should remove blockquote markers:\ngot: %q", got)
}
}
func TestFormatCalloutsForPlainText_AllTypes(t *testing.T) {
tests := []struct {
callType string
wantIcon string
}{
{"note", "š"},
{"tip", "š”"},
{"warning", "ā ļø"},
{"danger", "šØ"},
{"success", "ā
"},
{"info", "ā¹ļø"},
{"question", "ā"},
{"bug", "š"},
{"example", "š"},
}
for _, tt := range tests {
t.Run(tt.callType, func(t *testing.T) {
input := "> [!" + tt.callType + "] Title\n> Content\n"
got := FormatCalloutsForPlainText(input)
if !strings.Contains(got, tt.wantIcon) {
t.Errorf("expected icon %s for type %s, got: %q", tt.wantIcon, tt.callType, got)
}
})
}
}
func TestFormatCalloutsForPlainText_PreservesNonCallouts(t *testing.T) {
input := "Regular text\n\n> Regular blockquote\n> without callout\n\n> [!note] Callout\n> With content\n"
got := FormatCalloutsForPlainText(input)
// Should preserve regular text and non-callout blockquotes
if !strings.Contains(got, "Regular text") {
t.Error("should preserve regular text")
}
if !strings.Contains(got, "> Regular blockquote") {
t.Error("should preserve regular blockquotes")
}
// Should format the callout (no blockquote marker)
if !strings.Contains(got, "š Callout") {
t.Error("should format callout syntax")
}
if !strings.Contains(got, "With content") {
t.Error("should include callout content")
}
}
func TestFormatCalloutsForPlainText_MultiParagraphCallout(t *testing.T) {
input := "> [!tip] Title\n> First paragraph\n> \n> Second paragraph\n"
got := FormatCalloutsForPlainText(input)
if !strings.Contains(got, "š” Title") {
t.Errorf("should have emoji title, got: %q", got)
}
if !strings.Contains(got, "First paragraph") {
t.Errorf("should have first paragraph, got: %q", got)
}
if !strings.Contains(got, "Second paragraph") {
t.Errorf("should have second paragraph, got: %q", got)
}
// Should not have > markers
lines := strings.Split(got, "\n")
for _, line := range lines {
if strings.TrimSpace(line) != "" && strings.HasPrefix(strings.TrimSpace(line), ">") {
t.Errorf("should not have > markers in callout content, got line: %q", line)
}
}
}