okami.mom/glamour

git clone https://git.nkp.pet/okami.mom/glamour

Ayman BagabasMerge pull request #408 from charmbracelet/v2-expcf874d7

main
6.2 KiB325 linesraw
1package glamour
2
3import (
4	"bytes"
5	"errors"
6	"fmt"
7	"io"
8	"os"
9	"regexp"
10	"strings"
11	"testing"
12
13	"charm.land/glamour/v2/styles"
14	"github.com/charmbracelet/x/exp/golden"
15)
16
17const markdown = "testdata/readme.markdown.in"
18
19func TestTermRendererWriter(t *testing.T) {
20	r, err := NewTermRenderer(
21		WithStandardStyle(styles.DarkStyle),
22	)
23	if err != nil {
24		t.Fatal(err)
25	}
26
27	in, err := os.ReadFile(markdown)
28	if err != nil {
29		t.Fatal(err)
30	}
31
32	_, err = r.Write(in)
33	if err != nil {
34		t.Fatal(err)
35	}
36	err = r.Close()
37	if err != nil {
38		t.Fatal(err)
39	}
40
41	b, err := io.ReadAll(r)
42	if err != nil {
43		t.Fatal(err)
44	}
45
46	golden.RequireEqual(t, b)
47}
48
49func TestTermRenderer(t *testing.T) {
50	r, err := NewTermRenderer(
51		WithStandardStyle("dark"),
52	)
53	if err != nil {
54		t.Fatal(err)
55	}
56
57	in, err := os.ReadFile(markdown)
58	if err != nil {
59		t.Fatal(err)
60	}
61
62	b, err := r.Render(string(in))
63	if err != nil {
64		t.Fatal(err)
65	}
66
67	golden.RequireEqual(t, []byte(b))
68}
69
70func TestWithEmoji(t *testing.T) {
71	r, err := NewTermRenderer(
72		WithEmoji(),
73	)
74	if err != nil {
75		t.Fatal(err)
76	}
77
78	b, err := r.Render(":+1:")
79	if err != nil {
80		t.Fatal(err)
81	}
82	b = strings.TrimSpace(b)
83
84	// Thumbs up unicode character
85	td := "\U0001f44d"
86
87	if td != b {
88		t.Errorf("Rendered output doesn't match!\nExpected: `\n%s`\nGot: `\n%s`\n", td, b)
89	}
90}
91
92func TestWithPreservedNewLines(t *testing.T) {
93	r, err := NewTermRenderer(
94		WithPreservedNewLines(),
95	)
96	if err != nil {
97		t.Fatal(err)
98	}
99
100	in, err := os.ReadFile("testdata/preserved_newline.in")
101	if err != nil {
102		t.Fatal(err)
103	}
104
105	b, err := r.Render(string(in))
106	if err != nil {
107		t.Fatal(err)
108	}
109
110	golden.RequireEqual(t, []byte(b))
111}
112
113func TestStyles(t *testing.T) {
114	_, err := NewTermRenderer()
115	if err != nil {
116		t.Fatal(err)
117	}
118
119	_, err = NewTermRenderer(
120		WithStandardStyle(styles.DarkStyle),
121	)
122	if err != nil {
123		t.Fatal(err)
124	}
125
126	_, err = NewTermRenderer(
127		WithEnvironmentConfig(),
128	)
129	if err != nil {
130		t.Fatal(err)
131	}
132}
133
134// TestCustomStyle checks the expected errors with custom styling. We need to
135// support built-in styles and custom style sheets.
136func TestCustomStyle(t *testing.T) {
137	md := "testdata/example.md"
138	tests := []struct {
139		name      string
140		stylePath string
141		err       error
142		expected  string
143	}{
144		{name: "style exists", stylePath: "testdata/custom.style", err: nil, expected: "testdata/custom.style"},
145		{name: "style doesn't exist", stylePath: "testdata/notfound.style", err: os.ErrNotExist, expected: styles.DarkStyle},
146		{name: "style is empty", stylePath: "", err: nil, expected: styles.DarkStyle},
147	}
148
149	for _, tc := range tests {
150		t.Run(tc.name, func(t *testing.T) {
151			t.Setenv("GLAMOUR_STYLE", tc.stylePath)
152			g, err := NewTermRenderer(
153				WithEnvironmentConfig(),
154			)
155			if !errors.Is(err, tc.err) {
156				t.Fatal(err)
157			}
158			if !errors.Is(tc.err, os.ErrNotExist) {
159				w, err := NewTermRenderer(WithStylePath(tc.expected))
160				if err != nil {
161					t.Fatal(err)
162				}
163				text, _ := os.ReadFile(md)
164				want, err := w.RenderBytes(text)
165				got, err := g.RenderBytes(text)
166				if !bytes.Equal(want, got) {
167					t.Error("Wrong style used")
168				}
169			}
170		})
171	}
172}
173
174func TestRenderHelpers(t *testing.T) {
175	in, err := os.ReadFile(markdown)
176	if err != nil {
177		t.Fatal(err)
178	}
179
180	b, err := Render(string(in), "dark")
181	if err != nil {
182		t.Error(err)
183	}
184
185	golden.RequireEqual(t, []byte(b))
186}
187
188func TestCapitalization(t *testing.T) {
189	p := true
190	style := styles.DarkStyleConfig
191	style.H1.Upper = &p
192	style.H2.Title = &p
193	style.H3.Lower = &p
194
195	r, err := NewTermRenderer(
196		WithStyles(style),
197	)
198	if err != nil {
199		t.Fatal(err)
200	}
201
202	b, err := r.Render("# everything is uppercase\n## everything is titled\n### everything is lowercase")
203	if err != nil {
204		t.Fatal(err)
205	}
206
207	golden.RequireEqual(t, []byte(b))
208}
209
210func FuzzData(f *testing.F) {
211	f.Fuzz(func(t *testing.T, data []byte) {
212		func() int {
213			_, err := RenderBytes(data, styles.DarkStyle)
214			if err != nil {
215				return 0
216			}
217			return 1
218		}()
219	})
220}
221
222func TestTableAscii(t *testing.T) {
223	markdown := strings.TrimSpace(`
224| Header A  | Header B  |
225| --------- | --------- |
226| Cell 1    | Cell 2    |
227| Cell 3    | Cell 4    |
228| Cell 5    | Cell 6    |
229`)
230
231	renderer, err := NewTermRenderer(
232		WithStyles(styles.ASCIIStyleConfig),
233		WithWordWrap(80),
234	)
235	if err != nil {
236		t.Fatal(err)
237	}
238
239	result, err := renderer.Render(markdown)
240	if err != nil {
241		t.Fatal(err)
242	}
243
244	nonAsciiRegexp := regexp.MustCompile(`[^\x00-\x7f]+`)
245	nonAsciiChars := nonAsciiRegexp.FindAllString(result, -1)
246	if len(nonAsciiChars) > 0 {
247		t.Errorf("Non-ASCII characters found in output: %v", nonAsciiChars)
248	}
249}
250
251func ExampleASCIIStyleConfig() {
252	markdown := strings.TrimSpace(`
253| Header A  | Header B  |
254| --------- | --------- |
255| Cell 1    | Cell 2    |
256| Cell 3    | Cell 4    |
257| Cell 5    | Cell 6    |
258`)
259
260	renderer, err := NewTermRenderer(
261		WithStyles(styles.ASCIIStyleConfig),
262		WithWordWrap(80),
263	)
264	if err != nil {
265		return
266	}
267
268	result, err := renderer.Render(markdown)
269	if err != nil {
270		return
271	}
272	result = strings.ReplaceAll(result, " ", ".")
273	fmt.Println(result)
274
275	// Output:
276	// ..............................................................................
277	// ...Header.A.............................|.Header.B............................
278	// ..--------------------------------------|-------------------------------------
279	// ...Cell.1...............................|.Cell.2..............................
280	// ...Cell.3...............................|.Cell.4..............................
281	// ...Cell.5...............................|.Cell.6..............................
282}
283
284func TestWithChromaFormatterDefault(t *testing.T) {
285	r, err := NewTermRenderer(
286		WithStandardStyle(styles.DarkStyle),
287	)
288	if err != nil {
289		t.Fatal(err)
290	}
291
292	in, err := os.ReadFile("testdata/TestWithChromaFormatter.md")
293	if err != nil {
294		t.Fatal(err)
295	}
296
297	b, err := r.Render(string(in))
298	if err != nil {
299		t.Fatal(err)
300	}
301
302	golden.RequireEqual(t, []byte(b))
303}
304
305func TestWithChromaFormatterCustom(t *testing.T) {
306	r, err := NewTermRenderer(
307		WithStandardStyle(styles.DarkStyle),
308		WithChromaFormatter("terminal16"),
309	)
310	if err != nil {
311		t.Fatal(err)
312	}
313
314	in, err := os.ReadFile("testdata/TestWithChromaFormatter.md")
315	if err != nil {
316		t.Fatal(err)
317	}
318
319	b, err := r.Render(string(in))
320	if err != nil {
321		t.Fatal(err)
322	}
323
324	golden.RequireEqual(t, []byte(b))
325}