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) } }