173 lines
5.9 KiB
Go
173 lines
5.9 KiB
Go
package main
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/charmbracelet/lipgloss"
|
|
"github.com/charmbracelet/x/ansi"
|
|
"github.com/muesli/termenv"
|
|
)
|
|
|
|
func TestCommentMarkdownDistinguishesQuoteAndReply(t *testing.T) {
|
|
lines := renderCommentMarkdown("> This is the quoted review.\n\nThis is my reply.", 60)
|
|
rendered := strings.Join(lines, "\n")
|
|
plain := ansi.Strip(rendered)
|
|
if !strings.Contains(plain, "This is the quoted review.") ||
|
|
!strings.Contains(plain, "This is my reply.") {
|
|
t.Fatalf("comment content was lost:\n%q", rendered)
|
|
}
|
|
if !strings.Contains(plain, "│") && !strings.Contains(plain, "┃") &&
|
|
!strings.Contains(plain, ">") {
|
|
t.Fatalf("quote has no visible marker:\n%q", rendered)
|
|
}
|
|
}
|
|
|
|
func TestCommentMarkdownCacheReturnsIndependentLines(t *testing.T) {
|
|
commentMarkdownLines.clear()
|
|
t.Cleanup(commentMarkdownLines.clear)
|
|
const body = "> Cached quote\n\n```go\nprintln(\"cached\")\n```"
|
|
|
|
first := renderCommentMarkdown(body, 60)
|
|
if len(first) == 0 {
|
|
t.Fatal("cached Markdown rendered no lines")
|
|
}
|
|
first[0] = "mutated by caller"
|
|
second := renderCommentMarkdown(body, 60)
|
|
if second[0] == first[0] {
|
|
t.Fatal("caller mutation changed cached Markdown lines")
|
|
}
|
|
|
|
key := markdownLineCacheKey{markdown: normalizeGitHubAlerts(body), width: 60}
|
|
cached, ok := commentMarkdownLines.get(key)
|
|
if !ok || len(cached) == 0 {
|
|
t.Fatal("rendered Markdown was not cached")
|
|
}
|
|
}
|
|
|
|
func TestCommentMarkdownStylesInlineCode(t *testing.T) {
|
|
defer applyTheme("dark")
|
|
if err := applyTheme("dark"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
rendered := strings.Join(renderCommentMarkdown(
|
|
"Use `list_comparison` for this value.", 60,
|
|
), "\n")
|
|
plain := ansi.Strip(rendered)
|
|
if !strings.Contains(plain, "Use list_comparison for this value.") {
|
|
t.Fatalf("inline code was lost: %q", rendered)
|
|
}
|
|
if strings.Contains(plain, "Use list_comparison for") {
|
|
t.Fatalf("inline code added surrounding spaces: %q", plain)
|
|
}
|
|
if !strings.Contains(rendered, "48;2;44;48;69m") {
|
|
t.Fatalf("inline code has no distinct background: %q", rendered)
|
|
}
|
|
}
|
|
|
|
func TestCommentMarkdownUsesActiveThemePalette(t *testing.T) {
|
|
defer applyTheme("dark")
|
|
if err := applyTheme("catppuccin-mocha"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
rendered := strings.Join(renderCommentMarkdown(
|
|
"## Heading\n\nUse `value` and [link](https://example.com).\n\n```python\nif ready:\n return 1\n```",
|
|
80,
|
|
), "\n")
|
|
for _, sequence := range []string{
|
|
"38;2;203;166;247", // Catppuccin mauve heading/link.
|
|
"38;2;166;227;161", // Catppuccin green inline code/string.
|
|
} {
|
|
if !strings.Contains(rendered, sequence) {
|
|
t.Fatalf("Markdown did not use active palette color %q:\n%q", sequence, rendered)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestWrappedQuoteKeepsRailOnEveryLine(t *testing.T) {
|
|
const width = 28
|
|
lines := renderCommentMarkdown(
|
|
"> This quoted reply is deliberately long enough to wrap across several terminal lines.",
|
|
width,
|
|
)
|
|
if len(lines) < 2 {
|
|
t.Fatalf("quote did not wrap: %#v", lines)
|
|
}
|
|
for i, line := range lines {
|
|
plain := ansi.Strip(line)
|
|
if strings.TrimSpace(plain) != "" && !strings.HasPrefix(plain, "│ ") {
|
|
t.Fatalf("wrapped quote line %d lost its rail: %q", i, plain)
|
|
}
|
|
if ansi.StringWidth(line) > width {
|
|
t.Fatalf("wrapped quote line %d is too wide: %q", i, plain)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCommentMarkdownRendersGFMStructures(t *testing.T) {
|
|
body := "## Result\n\n- [x] done\n- [ ] pending\n\n~~old~~ **new** :+1:\n\n| Name | State |\n| --- | --- |\n| check | good |"
|
|
plain := ansi.Strip(strings.Join(renderCommentMarkdown(body, 70), "\n"))
|
|
for _, wanted := range []string{"Result", "done", "pending", "old", "new", "👍", "Name", "State", "check", "good"} {
|
|
if !strings.Contains(plain, wanted) {
|
|
t.Fatalf("rendered Markdown lost %q:\n%s", wanted, plain)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCommentMarkdownRendersGitHubAlert(t *testing.T) {
|
|
plain := ansi.Strip(strings.Join(renderCommentMarkdown(
|
|
"> [!WARNING]\n> This can delete data.", 60,
|
|
), "\n"))
|
|
if !strings.Contains(plain, "Warning") || !strings.Contains(plain, "This can delete data.") {
|
|
t.Fatalf("GitHub alert was not rendered structurally:\n%s", plain)
|
|
}
|
|
}
|
|
|
|
func TestCommentMarkdownStaysWithinRequestedWidth(t *testing.T) {
|
|
const width = 34
|
|
body := "> A long quoted reply containing `inline_code` that needs wrapping.\n\n" +
|
|
"- A similarly long list item that also needs wrapping."
|
|
for i, line := range renderCommentMarkdown(body, width) {
|
|
if got := ansi.StringWidth(line); got > width {
|
|
t.Fatalf("line %d width = %d, want at most %d: %q", i, got, width, line)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCommentMarkdownHighlightsContributorMentions(t *testing.T) {
|
|
defer applyTheme("dark")
|
|
if err := applyTheme("dark"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
previousProfile := lipgloss.ColorProfile()
|
|
lipgloss.SetColorProfile(termenv.TrueColor)
|
|
t.Cleanup(func() { lipgloss.SetColorProfile(previousProfile) })
|
|
rendered := strings.Join(renderCommentMarkdown(
|
|
"Ask @pablu and @other-contributor, then continue with **important text**.", 80,
|
|
), "\n")
|
|
for _, login := range []string{"pablu", "other-contributor"} {
|
|
mention := "@" + login
|
|
if !strings.Contains(rendered, authorStyle(login).Render(mention)) {
|
|
t.Fatalf("%s does not use its deterministic author style:\n%q", mention, rendered)
|
|
}
|
|
}
|
|
if plain := strings.TrimSpace(ansi.Strip(rendered)); plain !=
|
|
"Ask @pablu and @other-contributor, then continue with important text." {
|
|
t.Fatalf("mention highlighting changed rendered text: %q", plain)
|
|
}
|
|
}
|
|
|
|
func TestCommentMarkdownDoesNotHighlightEmailAddresses(t *testing.T) {
|
|
defer applyTheme("dark")
|
|
if err := applyTheme("dark"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
previousProfile := lipgloss.ColorProfile()
|
|
lipgloss.SetColorProfile(termenv.TrueColor)
|
|
t.Cleanup(func() { lipgloss.SetColorProfile(previousProfile) })
|
|
rendered := highlightRenderedMentions("Email dev@example.com, then ask @example.")
|
|
if count := strings.Count(rendered, authorStyle("example").Render("@example")); count != 1 {
|
|
t.Fatalf("highlighted @example count = %d, want 1: %q", count, rendered)
|
|
}
|
|
}
|