1package md_test23import (4 "testing"56 "gno.land/p/moul/md/v0"7)89func TestHelpers(t *testing.T) {10 tests := []struct {11 name string12 function func() string13 expected string14 }{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 }3536 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}4546func 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 })5556 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 })6465 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}7576func TestUserLink(t *testing.T) {77 tests := []struct {78 name string79 input string80 expected string81 }{82 {"username", "moul", "[@moul](/u/moul)"},83 // "g1blah" data part is too short for a bech32 address (min 684 // 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 }9596 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}105106func 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 })115116 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}125126func TestColumns(t *testing.T) {127 tests := []struct {128 name string129 input []string130 padded bool131 expected string132 }{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 1151</gno-columns>152`,153 },154 {155 name: "one column padded",156 input: []string{"Column 1"},157 padded: true,158 expected: `<gno-columns>159Column 1160<gno-columns-sep>161162<gno-columns-sep>163164<gno-columns-sep>165166</gno-columns>167`,168 },169 {170 name: "two columns",171 input: []string{"Column 1", "Column 2"},172 padded: false,173 expected: `<gno-columns>174Column 1175<gno-columns-sep>176Column 2177</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 1186<gno-columns-sep>187Column 2188<gno-columns-sep>189190<gno-columns-sep>191192</gno-columns>193`,194 },195 {196 name: "four columns",197 input: []string{"A", "B", "C", "D"},198 padded: false,199 expected: `<gno-columns>200A201<gno-columns-sep>202B203<gno-columns-sep>204C205<gno-columns-sep>206D207</gno-columns>208`,209 },210 {211 name: "four columns padded",212 input: []string{"A", "B", "C", "D"},213 padded: true,214 expected: `<gno-columns>215A216<gno-columns-sep>217B218<gno-columns-sep>219C220<gno-columns-sep>221D222</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>2301231<gno-columns-sep>2322233<gno-columns-sep>2343235<gno-columns-sep>2364237<gno-columns-sep>2385239</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>2471248<gno-columns-sep>2492250<gno-columns-sep>2513252<gno-columns-sep>2534254<gno-columns-sep>2555256<gno-columns-sep>257258<gno-columns-sep>259260<gno-columns-sep>261262</gno-columns>263`,264 },265 }266267 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}276277func TestColumnsN(t *testing.T) {278 tests := []struct {279 name string280 content []string281 colsPerRow int282 padded bool283 expected string284 }{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>298A299<gno-columns-sep>300B301<gno-columns-sep>302C303</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>312A313<gno-columns-sep>314B315</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>324A325<gno-columns-sep>326B327</gno-columns>328<gno-columns>329C330</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>339A340<gno-columns-sep>341B342</gno-columns>343<gno-columns>344C345<gno-columns-sep>346347</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>356X357<gno-columns-sep>358359<gno-columns-sep>360361</gno-columns>362`,363 },364 }365366 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}375Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.