md_test.gno
8.07 Kb · 374 lines
1package md_test
2
3import (
4 "testing"
5
6 "gno.land/p/moul/md"
7)
8
9func TestHelpers(t *testing.T) {
10 tests := []struct {
11 name string
12 function func() string
13 expected string
14 }{
15 {"Bold", func() string { return md.Bold("foo") }, "**foo**"},
16 {"Italic", func() string { return md.Italic("foo") }, "*foo*"},
17 {"Strikethrough", func() string { return md.Strikethrough("foo") }, "~~foo~~"},
18 {"H1", func() string { return md.H1("foo") }, "# foo\n"},
19 {"HorizontalRule", md.HorizontalRule, "---\n"},
20 {"InlineCode", func() string { return md.InlineCode("foo") }, "`foo`"},
21 {"CodeBlock", func() string { return md.CodeBlock("foo") }, "```\nfoo\n```\n"},
22 {"LanguageCodeBlock", func() string { return md.LanguageCodeBlock("go", "foo") }, "```go\nfoo\n```\n"},
23 {"Link", func() string { return md.Link("foo", "http://example.com") }, "[foo](http://example.com)"},
24 {"UserLink", func() string { return md.UserLink("moul") }, "[@moul](/u/moul)"},
25 {"Image", func() string { return md.Image("foo", "http://example.com") }, ""},
26 {"InlineImageWithLink", func() string {
27 return md.InlineImageWithLink("alt", "http://img.example.com/x.png", "http://link.example.com")
28 }, "[](http://link.example.com)"},
29 {"FootnoteDefinition", func() string { return md.FootnoteDefinition("foo", "bar") }, "[^foo]:\n bar\n"},
30 {"LinkReferenceDefinition", func() string {
31 return md.LinkReferenceDefinition("foo", "http://example.com", "")
32 }, "\n\n[foo]: http://example.com\n\n"},
33 {"Paragraph", func() string { return md.Paragraph("foo") }, "foo\n\n"},
34 }
35
36 for _, tt := range tests {
37 t.Run(tt.name, func(t *testing.T) {
38 result := tt.function()
39 if result != tt.expected {
40 t.Errorf("%s() = %q, want %q", tt.name, result, tt.expected)
41 }
42 })
43 }
44}
45
46func TestLists(t *testing.T) {
47 t.Run("BulletList", func(t *testing.T) {
48 items := []string{"foo", "bar"}
49 expected := "- foo\n- bar\n"
50 result := md.BulletList(items)
51 if result != expected {
52 t.Errorf("BulletList(%q) = %q, want %q", items, result, expected)
53 }
54 })
55
56 t.Run("OrderedList", func(t *testing.T) {
57 items := []string{"foo", "bar"}
58 expected := "1. foo\n2. bar\n"
59 result := md.OrderedList(items)
60 if result != expected {
61 t.Errorf("OrderedList(%q) = %q, want %q", items, result, expected)
62 }
63 })
64
65 t.Run("TodoList", func(t *testing.T) {
66 items := []string{"foo", "bar\nmore bar"}
67 done := []bool{true, false}
68 expected := "- [x] foo\n- [ ] bar\n more bar\n"
69 result := md.TodoList(items, done)
70 if result != expected {
71 t.Errorf("TodoList(%q, %q) = %q, want %q", items, done, result, expected)
72 }
73 })
74}
75
76func TestUserLink(t *testing.T) {
77 tests := []struct {
78 name string
79 input string
80 expected string
81 }{
82 {"username", "moul", "[@moul](/u/moul)"},
83 // "g1blah" data part is too short for a bech32 address (min 6
84 // chars after "1"); UserLink validates as a username instead.
85 {"short g1-prefixed string treated as username", "g1blah", "[@g1blah](/u/g1blah)"},
86 // "user_name" is a valid username (charset includes _ and -);
87 // the validator returns it verbatim, so no markdown escape of _.
88 {"username with underscore", "user_name", "[@user_name](/u/user_name)"},
89 // "g1abc123" has a 6-char data part — valid bech32 shape.
90 {"address with 6-char data", "g1abc123", "[g1abc123](/u/g1abc123)"},
91 // invalid identifier (spaces, uppercase, etc.) → empty.
92 {"rejected: spaces", "hello world", ""},
93 {"rejected: uppercase first char", "Alice", ""},
94 }
95
96 for _, tt := range tests {
97 t.Run(tt.name, func(t *testing.T) {
98 result := md.UserLink(tt.input)
99 if result != tt.expected {
100 t.Errorf("UserLink(%q) = %q, want %q", tt.input, result, tt.expected)
101 }
102 })
103 }
104}
105
106func TestNested(t *testing.T) {
107 t.Run("Nested Single Level", func(t *testing.T) {
108 content := "- foo\n- bar"
109 expected := " - foo\n - bar"
110 result := md.Nested(content, " ")
111 if result != expected {
112 t.Errorf("Nested(%q) = %q, want %q", content, result, expected)
113 }
114 })
115
116 t.Run("Nested Double Level", func(t *testing.T) {
117 content := " - foo\n - bar"
118 expected := " - foo\n - bar"
119 result := md.Nested(content, " ")
120 if result != expected {
121 t.Errorf("Nested(%q) = %q, want %q", content, result, expected)
122 }
123 })
124}
125
126func TestColumns(t *testing.T) {
127 tests := []struct {
128 name string
129 input []string
130 padded bool
131 expected string
132 }{
133 {
134 name: "no columns",
135 input: []string{},
136 padded: false,
137 expected: "",
138 },
139 {
140 name: "no columns padded",
141 input: []string{},
142 padded: true,
143 expected: "",
144 },
145 {
146 name: "one column",
147 input: []string{"Column 1"},
148 padded: false,
149 expected: `<gno-columns>
150Column 1
151</gno-columns>
152`,
153 },
154 {
155 name: "one column padded",
156 input: []string{"Column 1"},
157 padded: true,
158 expected: `<gno-columns>
159Column 1
160<gno-columns-sep>
161
162<gno-columns-sep>
163
164<gno-columns-sep>
165
166</gno-columns>
167`,
168 },
169 {
170 name: "two columns",
171 input: []string{"Column 1", "Column 2"},
172 padded: false,
173 expected: `<gno-columns>
174Column 1
175<gno-columns-sep>
176Column 2
177</gno-columns>
178`,
179 },
180 {
181 name: "two columns padded",
182 input: []string{"Column 1", "Column 2"},
183 padded: true,
184 expected: `<gno-columns>
185Column 1
186<gno-columns-sep>
187Column 2
188<gno-columns-sep>
189
190<gno-columns-sep>
191
192</gno-columns>
193`,
194 },
195 {
196 name: "four columns",
197 input: []string{"A", "B", "C", "D"},
198 padded: false,
199 expected: `<gno-columns>
200A
201<gno-columns-sep>
202B
203<gno-columns-sep>
204C
205<gno-columns-sep>
206D
207</gno-columns>
208`,
209 },
210 {
211 name: "four columns padded",
212 input: []string{"A", "B", "C", "D"},
213 padded: true,
214 expected: `<gno-columns>
215A
216<gno-columns-sep>
217B
218<gno-columns-sep>
219C
220<gno-columns-sep>
221D
222</gno-columns>
223`,
224 },
225 {
226 name: "more than four columns",
227 input: []string{"1", "2", "3", "4", "5"},
228 padded: false,
229 expected: `<gno-columns>
2301
231<gno-columns-sep>
2322
233<gno-columns-sep>
2343
235<gno-columns-sep>
2364
237<gno-columns-sep>
2385
239</gno-columns>
240`,
241 },
242 {
243 name: "more than four columns padded",
244 input: []string{"1", "2", "3", "4", "5"},
245 padded: true,
246 expected: `<gno-columns>
2471
248<gno-columns-sep>
2492
250<gno-columns-sep>
2513
252<gno-columns-sep>
2534
254<gno-columns-sep>
2555
256<gno-columns-sep>
257
258<gno-columns-sep>
259
260<gno-columns-sep>
261
262</gno-columns>
263`,
264 },
265 }
266
267 for _, tt := range tests {
268 t.Run(tt.name, func(t *testing.T) {
269 result := md.Columns(tt.input, tt.padded)
270 if result != tt.expected {
271 t.Errorf("Columns(%v, %v) =\n%q\nwant:\n%q", tt.input, tt.padded, result, tt.expected)
272 }
273 })
274 }
275}
276
277func TestColumnsN(t *testing.T) {
278 tests := []struct {
279 name string
280 content []string
281 colsPerRow int
282 padded bool
283 expected string
284 }{
285 {
286 name: "empty input",
287 content: []string{},
288 colsPerRow: 2,
289 padded: false,
290 expected: "",
291 },
292 {
293 name: "colsPerRow <= 0",
294 content: []string{"A", "B", "C"},
295 colsPerRow: 0,
296 padded: false,
297 expected: `<gno-columns>
298A
299<gno-columns-sep>
300B
301<gno-columns-sep>
302C
303</gno-columns>
304`,
305 },
306 {
307 name: "exact full row, no padding",
308 content: []string{"A", "B"},
309 colsPerRow: 2,
310 padded: false,
311 expected: `<gno-columns>
312A
313<gno-columns-sep>
314B
315</gno-columns>
316`,
317 },
318 {
319 name: "partial last row, no padding",
320 content: []string{"A", "B", "C"},
321 colsPerRow: 2,
322 padded: false,
323 expected: `<gno-columns>
324A
325<gno-columns-sep>
326B
327</gno-columns>
328<gno-columns>
329C
330</gno-columns>
331`,
332 },
333 {
334 name: "partial last row, with padding",
335 content: []string{"A", "B", "C"},
336 colsPerRow: 2,
337 padded: true,
338 expected: `<gno-columns>
339A
340<gno-columns-sep>
341B
342</gno-columns>
343<gno-columns>
344C
345<gno-columns-sep>
346
347</gno-columns>
348`,
349 },
350 {
351 name: "padded with more empty cells",
352 content: []string{"X"},
353 colsPerRow: 3,
354 padded: true,
355 expected: `<gno-columns>
356X
357<gno-columns-sep>
358
359<gno-columns-sep>
360
361</gno-columns>
362`,
363 },
364 }
365
366 for _, tt := range tests {
367 t.Run(tt.name, func(t *testing.T) {
368 result := md.ColumnsN(tt.content, tt.colsPerRow, tt.padded)
369 if result != tt.expected {
370 t.Errorf("ColumnsN(%v, %d, %v) =\n%q\nwant:\n%q", tt.content, tt.colsPerRow, tt.padded, result, tt.expected)
371 }
372 })
373 }
374}