Go 130 lines
package guide
import (
"flag"
"os"
"path/filepath"
"regexp"
"strings"
"testing"
)
var updateGolden = flag.Bool("update", false, "rewrite the rendered golden file")
var reANSI = regexp.MustCompile("\x1b\\[[0-9;]*m")
func stripANSI(s string) string { return reANSI.ReplaceAllString(s, "") }
// TestRender_Golden locks the full rendered manual (no colour) so any
// drift in the renderer is visible in review. Re-run with -update to
// refresh after an intentional change.
func TestRender_Golden(t *testing.T) {
got := Render(false)
golden := filepath.Join("testdata", "usage.rendered.golden")
if *updateGolden {
if err := os.WriteFile(golden, []byte(got), 0o644); err != nil {
t.Fatalf("write golden: %v", err)
}
}
want, err := os.ReadFile(golden)
if err != nil {
t.Fatalf("read golden: %v (run with -update to create it)", err)
}
if got != string(want) {
t.Errorf("Render(false) drifted from testdata/usage.rendered.golden — re-run with -update if intended")
}
}
// TestRender_ColourStripsToPlain is the colour invariant: Render only
// adds ANSI escapes on top of the plain layout, so stripping them from
// Render(true) must yield Render(false).
func TestRender_ColourStripsToPlain(t *testing.T) {
coloured := Render(true)
if !strings.Contains(coloured, "\x1b[") {
t.Fatal("Render(true) emitted no ANSI escapes")
}
if stripANSI(coloured) != Render(false) {
t.Error("stripANSI(Render(true)) != Render(false) — colour changed the layout")
}
}
func TestRender_Heading(t *testing.T) {
got := renderManual("## Builtin workflows", false)
lines := strings.Split(got, "\n")
if lines[0] != "Builtin workflows" {
t.Errorf("heading text = %q, want stripped of ##", lines[0])
}
if lines[1] != strings.Repeat("─", len("Builtin workflows")) {
t.Errorf("heading rule = %q, want ─ sized to the title", lines[1])
}
}
func TestRender_TableBox(t *testing.T) {
src := "| A | Bee |\n| - | --- |\n| 1 | two |"
got := renderManual(src, false)
want := strings.Join([]string{
"┌───┬─────┐",
"│ A │ Bee │",
"├───┼─────┤",
"│ 1 │ two │",
"└───┴─────┘",
}, "\n")
if got != want {
t.Errorf("table render mismatch:\n got:\n%s\nwant:\n%s", got, want)
}
}
func TestRender_TableEscapedPipe(t *testing.T) {
src := "| Key | Note |\n| --- | --- |\n| `a\\|b` | one cell |"
got := renderManual(src, false)
if !strings.Contains(got, "a|b") {
t.Errorf("escaped pipe not preserved as a literal cell value:\n%s", got)
}
// Three rows of three columns would mean the escape leaked a split;
// the body row must stay two columns (one ┼ junction in the rule).
for line := range strings.SplitSeq(got, "\n") {
if strings.HasPrefix(line, "├") && strings.Count(line, "┼") != 1 {
t.Errorf("expected a 2-column body, got rule %q", line)
}
}
}
func TestRender_InlineCodeAndBold(t *testing.T) {
plain := renderManual("run `eeco go` and read the **brief**.", false)
if plain != "run eeco go and read the brief." {
t.Errorf("plain inline = %q", plain)
}
coloured := renderManual("run `eeco go` and read the **brief**.", true)
if !strings.Contains(coloured, ansiFaint+"eeco go"+ansiReset) {
t.Errorf("code span not faint: %q", coloured)
}
if !strings.Contains(coloured, ansiBold+"brief"+ansiReset) {
t.Errorf("bold span not bold: %q", coloured)
}
}
func TestRender_FenceVerbatim(t *testing.T) {
src := "```\n**not bold** `not code`\n```"
got := renderManual(src, false)
if !strings.Contains(got, "**not bold** `not code`") {
t.Errorf("fenced body should stay literal, got %q", got)
}
if !strings.HasPrefix(got, " ") {
t.Errorf("fenced body should be indented, got %q", got)
}
}
func TestRender_Bullet(t *testing.T) {
got := renderManual("- a point", false)
if got != "• a point" {
t.Errorf("bullet render = %q, want • glyph", got)
}
}
func TestRender_UnknownPassThrough(t *testing.T) {
got := renderManual("just some prose with no markup", false)
if got != "just some prose with no markup" {
t.Errorf("plain prose changed: %q", got)
}
}