cli + tui to publish to leaflet (wip) & manage tasks, notes & watch/read lists 馃崈
charm
leaflet
readability
golang
1package public
2
3import (
4 "image"
5 "image/png"
6 "os"
7 "path/filepath"
8 "strings"
9 "testing"
10
11 "github.com/stormlightlabs/noteleaf/internal/shared"
12)
13
14func TestMarkdownConverter(t *testing.T) {
15 converter := NewMarkdownConverter()
16
17 t.Run("Conversion", func(t *testing.T) {
18 t.Run("converts heading to HeaderBlock", func(t *testing.T) {
19 markdown := "# Hello World"
20 blocks, err := converter.ToLeaflet(markdown)
21 shared.AssertNoError(t, err, "ToLeaflet should succeed")
22 shared.AssertEqual(t, 1, len(blocks), "should have 1 block")
23
24 header, ok := blocks[0].Block.(HeaderBlock)
25 shared.AssertTrue(t, ok, "block should be HeaderBlock")
26 shared.AssertEqual(t, TypeHeaderBlock, header.Type, "type should match")
27 shared.AssertEqual(t, 1, header.Level, "level should be 1")
28 shared.AssertEqual(t, "Hello World", header.Plaintext, "text should match")
29 })
30
31 t.Run("converts multiple heading levels", func(t *testing.T) {
32 markdown := "## Level 2\n\n### Level 3\n\n###### Level 6"
33 blocks, err := converter.ToLeaflet(markdown)
34 shared.AssertNoError(t, err, "ToLeaflet should succeed")
35 shared.AssertEqual(t, 3, len(blocks), "should have 3 blocks")
36
37 h2 := blocks[0].Block.(HeaderBlock)
38 shared.AssertEqual(t, 2, h2.Level, "first heading level")
39
40 h3 := blocks[1].Block.(HeaderBlock)
41 shared.AssertEqual(t, 3, h3.Level, "second heading level")
42
43 h6 := blocks[2].Block.(HeaderBlock)
44 shared.AssertEqual(t, 6, h6.Level, "third heading level")
45 })
46
47 t.Run("converts paragraph to TextBlock", func(t *testing.T) {
48 markdown := "This is a simple paragraph."
49 blocks, err := converter.ToLeaflet(markdown)
50 shared.AssertNoError(t, err, "ToLeaflet should succeed")
51 shared.AssertEqual(t, 1, len(blocks), "should have 1 block")
52
53 text, ok := blocks[0].Block.(TextBlock)
54 shared.AssertTrue(t, ok, "block should be TextBlock")
55 shared.AssertEqual(t, TypeTextBlock, text.Type, "type should match")
56 shared.AssertEqual(t, "This is a simple paragraph.", text.Plaintext, "text should match")
57 })
58
59 t.Run("converts code block to CodeBlock", func(t *testing.T) {
60 markdown := "```go\nfunc main() {\n}\n```"
61 blocks, err := converter.ToLeaflet(markdown)
62 shared.AssertNoError(t, err, "ToLeaflet should succeed")
63 shared.AssertEqual(t, 1, len(blocks), "should have 1 block")
64
65 code, ok := blocks[0].Block.(CodeBlock)
66 shared.AssertTrue(t, ok, "block should be CodeBlock")
67 shared.AssertEqual(t, TypeCodeBlock, code.Type, "type should match")
68 shared.AssertEqual(t, "go", code.Language, "language should match")
69 shared.AssertTrue(t, strings.Contains(code.Plaintext, "func main"), "code content should match")
70 })
71
72 t.Run("converts blockquote to BlockquoteBlock", func(t *testing.T) {
73 markdown := "> This is a quote"
74 blocks, err := converter.ToLeaflet(markdown)
75 shared.AssertNoError(t, err, "ToLeaflet should succeed")
76 shared.AssertEqual(t, 1, len(blocks), "should have 1 block")
77
78 quote, ok := blocks[0].Block.(BlockquoteBlock)
79 shared.AssertTrue(t, ok, "block should be BlockquoteBlock")
80 shared.AssertEqual(t, TypeBlockquoteBlock, quote.Type, "type should match")
81 shared.AssertTrue(t, strings.Contains(quote.Plaintext, "This is a quote"), "quote text should match")
82 })
83
84 t.Run("converts list to UnorderedListBlock", func(t *testing.T) {
85 markdown := "- Item 1\n- Item 2\n- Item 3"
86 blocks, err := converter.ToLeaflet(markdown)
87 shared.AssertNoError(t, err, "ToLeaflet should succeed")
88 shared.AssertEqual(t, 1, len(blocks), "should have 1 block")
89
90 list, ok := blocks[0].Block.(UnorderedListBlock)
91 shared.AssertTrue(t, ok, "block should be UnorderedListBlock")
92 shared.AssertEqual(t, TypeUnorderedListBlock, list.Type, "type should match")
93 shared.AssertEqual(t, 3, len(list.Children), "should have 3 items")
94
95 item1 := list.Children[0].Content.(TextBlock)
96 shared.AssertTrue(t, strings.Contains(item1.Plaintext, "Item 1"), "first item text")
97 })
98
99 t.Run("converts horizontal rule to HorizontalRuleBlock", func(t *testing.T) {
100 markdown := "---"
101 blocks, err := converter.ToLeaflet(markdown)
102 shared.AssertNoError(t, err, "ToLeaflet should succeed")
103 shared.AssertEqual(t, 1, len(blocks), "should have 1 block")
104
105 hr, ok := blocks[0].Block.(HorizontalRuleBlock)
106 shared.AssertTrue(t, ok, "block should be HorizontalRuleBlock")
107 shared.AssertEqual(t, TypeHorizontalRuleBlock, hr.Type, "type should match")
108 })
109
110 t.Run("converts mixed blocks", func(t *testing.T) {
111 markdown := `# Title
112
113This is a paragraph.
114
115## Subtitle
116
117- List item 1
118- List item 2
119
120---
121
122` + "```go\ncode\n```"
123
124 blocks, err := converter.ToLeaflet(markdown)
125
126 shared.AssertNoError(t, err, "ToLeaflet should succeed")
127 shared.AssertTrue(t, len(blocks) >= 5, "should have multiple blocks")
128 })
129 })
130
131 t.Run("Facets", func(t *testing.T) {
132 t.Run("extracts bold facet", func(t *testing.T) {
133 markdown := "This is **bold** text"
134 blocks, err := converter.ToLeaflet(markdown)
135
136 shared.AssertNoError(t, err, "ToLeaflet should succeed")
137 text := blocks[0].Block.(TextBlock)
138
139 shared.AssertTrue(t, len(text.Facets) > 0, "should have facets")
140 shared.AssertTrue(t, strings.Contains(text.Plaintext, "bold"), "text should contain 'bold'")
141 })
142
143 t.Run("extracts italic facet", func(t *testing.T) {
144 markdown := "This is *italic* text"
145 blocks, err := converter.ToLeaflet(markdown)
146
147 shared.AssertNoError(t, err, "ToLeaflet should succeed")
148 text := blocks[0].Block.(TextBlock)
149
150 shared.AssertTrue(t, len(text.Facets) > 0, "should have facets")
151 })
152
153 t.Run("extracts inline code facet", func(t *testing.T) {
154 markdown := "This is `code` text"
155 blocks, err := converter.ToLeaflet(markdown)
156
157 shared.AssertNoError(t, err, "ToLeaflet should succeed")
158 text := blocks[0].Block.(TextBlock)
159
160 shared.AssertTrue(t, len(text.Facets) > 0, "should have facets")
161 shared.AssertTrue(t, strings.Contains(text.Plaintext, "code"), "text should contain 'code'")
162 })
163
164 t.Run("extracts link facet", func(t *testing.T) {
165 markdown := "This is a [link](https://example.com)"
166 blocks, err := converter.ToLeaflet(markdown)
167
168 shared.AssertNoError(t, err, "ToLeaflet should succeed")
169 text := blocks[0].Block.(TextBlock)
170
171 shared.AssertTrue(t, len(text.Facets) > 0, "should have facets")
172 shared.AssertTrue(t, strings.Contains(text.Plaintext, "link"), "text should contain 'link'")
173
174 foundLink := false
175 for _, facet := range text.Facets {
176 for _, feature := range facet.Features {
177 if link, ok := feature.(FacetLink); ok {
178 shared.AssertEqual(t, "https://example.com", link.URI, "link URI should match")
179 foundLink = true
180 }
181 }
182 }
183 shared.AssertTrue(t, foundLink, "should have found link facet")
184 })
185
186 t.Run("extracts strikethrough facet", func(t *testing.T) {
187 markdown := "This is ~~deleted~~ text"
188 blocks, err := converter.ToLeaflet(markdown)
189
190 shared.AssertNoError(t, err, "ToLeaflet should succeed")
191 text := blocks[0].Block.(TextBlock)
192
193 shared.AssertTrue(t, len(text.Facets) > 0, "should have facets")
194 })
195
196 t.Run("extracts multiple facets", func(t *testing.T) {
197 markdown := "This has **bold** and *italic* and `code`"
198 blocks, err := converter.ToLeaflet(markdown)
199
200 shared.AssertNoError(t, err, "ToLeaflet should succeed")
201 text := blocks[0].Block.(TextBlock)
202
203 shared.AssertTrue(t, len(text.Facets) >= 3, "should have at least 3 facets")
204 })
205
206 t.Run("handles overlapping bold and italic", func(t *testing.T) {
207 markdown := "***bold and italic***"
208 blocks, err := converter.ToLeaflet(markdown)
209
210 shared.AssertNoError(t, err, "ToLeaflet should succeed")
211 text := blocks[0].Block.(TextBlock)
212
213 shared.AssertEqual(t, "bold and italic", text.Plaintext, "text should be correct")
214 shared.AssertTrue(t, len(text.Facets) > 0, "should have facets")
215
216 facet := text.Facets[0]
217 shared.AssertEqual(t, 2, len(facet.Features), "should have 2 features")
218
219 hasBold := false
220 hasItalic := false
221 for _, feature := range facet.Features {
222 switch feature.(type) {
223 case FacetBold:
224 hasBold = true
225 case FacetItalic:
226 hasItalic = true
227 }
228 }
229 shared.AssertTrue(t, hasBold, "should have bold feature")
230 shared.AssertTrue(t, hasItalic, "should have italic feature")
231 })
232
233 t.Run("handles nested bold in italic", func(t *testing.T) {
234 markdown := "*italic **and bold** still italic*"
235 blocks, err := converter.ToLeaflet(markdown)
236
237 shared.AssertNoError(t, err, "ToLeaflet should succeed")
238 text := blocks[0].Block.(TextBlock)
239
240 shared.AssertEqual(t, "italic and bold still italic", text.Plaintext, "text should be correct")
241 shared.AssertTrue(t, len(text.Facets) >= 2, "should have multiple facets")
242
243 foundOverlap := false
244 for _, facet := range text.Facets {
245 if strings.Contains(text.Plaintext[facet.Index.ByteStart:facet.Index.ByteEnd], "and bold") {
246 shared.AssertTrue(t, len(facet.Features) >= 2, "overlapping section should have multiple features")
247 foundOverlap = true
248 }
249 }
250 shared.AssertTrue(t, foundOverlap, "should find overlapping facet")
251 })
252
253 t.Run("handles link with formatting", func(t *testing.T) {
254 markdown := "[**bold link**](https://example.com)"
255 blocks, err := converter.ToLeaflet(markdown)
256
257 shared.AssertNoError(t, err, "ToLeaflet should succeed")
258 text := blocks[0].Block.(TextBlock)
259
260 shared.AssertEqual(t, "bold link", text.Plaintext, "text should be correct")
261 shared.AssertTrue(t, len(text.Facets) > 0, "should have facets")
262
263 hasLink := false
264 hasBold := false
265 for _, facet := range text.Facets {
266 for _, feature := range facet.Features {
267 switch f := feature.(type) {
268 case FacetLink:
269 hasLink = true
270 shared.AssertEqual(t, "https://example.com", f.URI, "link URI should match")
271 case FacetBold:
272 hasBold = true
273 }
274 }
275 }
276 shared.AssertTrue(t, hasLink, "should have link feature")
277 shared.AssertTrue(t, hasBold, "should have bold feature")
278 })
279
280 t.Run("handles strikethrough with bold", func(t *testing.T) {
281 markdown := "~~**deleted bold**~~"
282 blocks, err := converter.ToLeaflet(markdown)
283
284 shared.AssertNoError(t, err, "ToLeaflet should succeed")
285 text := blocks[0].Block.(TextBlock)
286
287 shared.AssertEqual(t, "deleted bold", text.Plaintext, "text should be correct")
288 shared.AssertTrue(t, len(text.Facets) > 0, "should have facets")
289
290 hasStrike := false
291 hasBold := false
292 for _, facet := range text.Facets {
293 for _, feature := range facet.Features {
294 switch feature.(type) {
295 case FacetStrikethrough:
296 hasStrike = true
297 case FacetBold:
298 hasBold = true
299 }
300 }
301 }
302 shared.AssertTrue(t, hasStrike, "should have strikethrough feature")
303 shared.AssertTrue(t, hasBold, "should have bold feature")
304 })
305
306 t.Run("handles complex nested formatting", func(t *testing.T) {
307 markdown := "*italic **bold and italic** italic*"
308 blocks, err := converter.ToLeaflet(markdown)
309
310 shared.AssertNoError(t, err, "ToLeaflet should succeed")
311 text := blocks[0].Block.(TextBlock)
312
313 foundBoldItalic := false
314 for _, facet := range text.Facets {
315 content := text.Plaintext[facet.Index.ByteStart:facet.Index.ByteEnd]
316 if strings.Contains(content, "bold and italic") {
317 shared.AssertTrue(t, len(facet.Features) >= 2, "nested section should have multiple features")
318 foundBoldItalic = true
319 }
320 }
321 shared.AssertTrue(t, foundBoldItalic, "should find nested bold and italic section")
322 })
323 })
324
325 t.Run("Round-trip Conversion", func(t *testing.T) {
326 t.Run("heading round-trip", func(t *testing.T) {
327 original := "## Hello World"
328 blocks, err := converter.ToLeaflet(original)
329 shared.AssertNoError(t, err, "ToLeaflet should succeed")
330
331 markdown, err := converter.FromLeaflet(blocks)
332 shared.AssertNoError(t, err, "FromLeaflet should succeed")
333 shared.AssertTrue(t, strings.Contains(markdown, "Hello World"), "should contain original text")
334 shared.AssertTrue(t, strings.HasPrefix(markdown, "##"), "should have heading markers")
335 })
336
337 t.Run("text round-trip", func(t *testing.T) {
338 original := "Simple paragraph"
339 blocks, err := converter.ToLeaflet(original)
340 shared.AssertNoError(t, err, "ToLeaflet should succeed")
341
342 markdown, err := converter.FromLeaflet(blocks)
343 shared.AssertNoError(t, err, "FromLeaflet should succeed")
344 shared.AssertTrue(t, strings.Contains(markdown, "Simple paragraph"), "should contain original text")
345 })
346
347 t.Run("code block round-trip", func(t *testing.T) {
348 original := "```go\nfunc test() {}\n```"
349 blocks, err := converter.ToLeaflet(original)
350 shared.AssertNoError(t, err, "ToLeaflet should succeed")
351
352 markdown, err := converter.FromLeaflet(blocks)
353 shared.AssertNoError(t, err, "FromLeaflet should succeed")
354 shared.AssertTrue(t, strings.Contains(markdown, "```"), "should have code fences")
355 shared.AssertTrue(t, strings.Contains(markdown, "func test"), "should contain code")
356 })
357
358 t.Run("list round-trip", func(t *testing.T) {
359 original := "- Item 1\n- Item 2"
360 blocks, err := converter.ToLeaflet(original)
361 shared.AssertNoError(t, err, "ToLeaflet should succeed")
362
363 markdown, err := converter.FromLeaflet(blocks)
364 shared.AssertNoError(t, err, "FromLeaflet should succeed")
365
366 shared.AssertTrue(t, strings.Contains(markdown, "Item 1"), "should contain first item")
367 shared.AssertTrue(t, strings.Contains(markdown, "Item 2"), "should contain second item")
368 shared.AssertTrue(t, strings.Contains(markdown, "-"), "should have list markers")
369 })
370 })
371
372 t.Run("Edge Cases", func(t *testing.T) {
373 t.Run("handles empty markdown", func(t *testing.T) {
374 blocks, err := converter.ToLeaflet("")
375 shared.AssertNoError(t, err, "should handle empty string")
376 shared.AssertEqual(t, 0, len(blocks), "should have no blocks")
377 })
378
379 t.Run("skips empty paragraphs", func(t *testing.T) {
380 markdown := "\n\n\n"
381 blocks, err := converter.ToLeaflet(markdown)
382 shared.AssertNoError(t, err, "should succeed")
383 shared.AssertEqual(t, 0, len(blocks), "should skip empty paragraphs")
384 })
385
386 t.Run("handles special characters", func(t *testing.T) {
387 markdown := "Text with *special* characters"
388 blocks, err := converter.ToLeaflet(markdown)
389 shared.AssertNoError(t, err, "should handle special characters")
390 shared.AssertEqual(t, 1, len(blocks), "should have 1 block")
391
392 text := blocks[0].Block.(TextBlock)
393 shared.AssertTrue(t, strings.Contains(text.Plaintext, "special"), "should preserve text")
394 shared.AssertTrue(t, strings.Contains(text.Plaintext, "characters"), "should preserve text")
395 })
396
397 t.Run("handles multiple paragraphs", func(t *testing.T) {
398 markdown := "First paragraph\n\nSecond paragraph"
399 blocks, err := converter.ToLeaflet(markdown)
400 shared.AssertNoError(t, err, "should succeed")
401 shared.AssertEqual(t, 2, len(blocks), "should have 2 blocks")
402 })
403 })
404
405 t.Run("Image Handling", func(t *testing.T) {
406 tmpDir := t.TempDir()
407 createTestImage := func(t *testing.T, name string, width, height int) string {
408 path := filepath.Join(tmpDir, name)
409
410 img := image.NewRGBA(image.Rect(0, 0, width, height))
411 f, err := os.Create(path)
412 shared.AssertNoError(t, err, "should create image file")
413 defer f.Close()
414
415 err = png.Encode(f, img)
416 shared.AssertNoError(t, err, "should encode image")
417
418 return path
419 }
420
421 t.Run("converts image without resolver (placeholder)", func(t *testing.T) {
422 markdown := ""
423 converter := NewMarkdownConverter()
424 blocks, err := converter.ToLeaflet(markdown)
425
426 shared.AssertNoError(t, err, "ToLeaflet should succeed")
427 shared.AssertTrue(t, len(blocks) >= 1, "should have at least 1 block")
428
429 var imgBlock ImageBlock
430 found := false
431 for _, block := range blocks {
432 if img, ok := block.Block.(ImageBlock); ok {
433 imgBlock = img
434 found = true
435 break
436 }
437 }
438
439 shared.AssertTrue(t, found, "should find image block")
440 shared.AssertEqual(t, TypeImageBlock, imgBlock.Type, "type should match")
441 shared.AssertEqual(t, "alt text", imgBlock.Alt, "alt text should match")
442 shared.AssertEqual(t, "bafkreiplaceholder", imgBlock.Image.Ref.Link, "should have placeholder CID")
443 })
444
445 t.Run("resolves local image with dimensions", func(t *testing.T) {
446 _ = createTestImage(t, "test.png", 800, 600)
447 markdown := ""
448
449 resolver := &LocalImageResolver{}
450 converter := NewMarkdownConverter().WithImageResolver(resolver, tmpDir)
451
452 blocks, err := converter.ToLeaflet(markdown)
453 shared.AssertNoError(t, err, "ToLeaflet should succeed")
454 shared.AssertTrue(t, len(blocks) >= 1, "should have at least 1 block")
455
456 var imgBlock ImageBlock
457 found := false
458 for _, block := range blocks {
459 if img, ok := block.Block.(ImageBlock); ok {
460 imgBlock = img
461 found = true
462 break
463 }
464 }
465
466 shared.AssertTrue(t, found, "should find image block")
467 shared.AssertEqual(t, "test image", imgBlock.Alt, "alt text should match")
468 shared.AssertEqual(t, 800, imgBlock.AspectRatio.Width, "width should match")
469 shared.AssertEqual(t, 600, imgBlock.AspectRatio.Height, "height should match")
470 shared.AssertEqual(t, "image/png", imgBlock.Image.MimeType, "mime type should match")
471 })
472
473 t.Run("handles inline images in paragraph", func(t *testing.T) {
474 _ = createTestImage(t, "inline.png", 100, 100)
475 markdown := "Some text before  and text after"
476
477 resolver := &LocalImageResolver{}
478 converter := NewMarkdownConverter().WithImageResolver(resolver, tmpDir)
479
480 blocks, err := converter.ToLeaflet(markdown)
481 shared.AssertNoError(t, err, "ToLeaflet should succeed")
482 shared.AssertTrue(t, len(blocks) >= 2, "should have multiple blocks for inline images")
483
484 textBlock1, ok := blocks[0].Block.(TextBlock)
485 shared.AssertTrue(t, ok, "first block should be text")
486 shared.AssertTrue(t, strings.Contains(textBlock1.Plaintext, "Some text before"), "should contain text before image")
487
488 imgBlock, ok := blocks[1].Block.(ImageBlock)
489 shared.AssertTrue(t, ok, "second block should be image")
490 shared.AssertEqual(t, "inline", imgBlock.Alt, "alt text should match")
491 })
492
493 t.Run("handles multiple images", func(t *testing.T) {
494 _ = createTestImage(t, "img1.png", 200, 150)
495 _ = createTestImage(t, "img2.png", 300, 200)
496 markdown := "\n\n"
497
498 resolver := &LocalImageResolver{}
499 converter := NewMarkdownConverter().WithImageResolver(resolver, tmpDir)
500
501 blocks, err := converter.ToLeaflet(markdown)
502 shared.AssertNoError(t, err, "ToLeaflet should succeed")
503
504 var imageBlocks []ImageBlock
505 for _, block := range blocks {
506 if img, ok := block.Block.(ImageBlock); ok {
507 imageBlocks = append(imageBlocks, img)
508 }
509 }
510
511 shared.AssertEqual(t, 2, len(imageBlocks), "should have 2 image blocks")
512 shared.AssertEqual(t, "first", imageBlocks[0].Alt, "first alt text")
513 shared.AssertEqual(t, 200, imageBlocks[0].AspectRatio.Width, "first width")
514 shared.AssertEqual(t, "second", imageBlocks[1].Alt, "second alt text")
515 shared.AssertEqual(t, 300, imageBlocks[1].AspectRatio.Width, "second width")
516 })
517
518 t.Run("uses custom blob uploader", func(t *testing.T) {
519 _ = createTestImage(t, "upload.png", 100, 100)
520 markdown := ""
521
522 uploadCalled := false
523 resolver := &LocalImageResolver{
524 BlobUploader: func(data []byte, mimeType string) (Blob, error) {
525 uploadCalled = true
526 shared.AssertEqual(t, "image/png", mimeType, "mime type should be png")
527 shared.AssertTrue(t, len(data) > 0, "should have data")
528
529 return Blob{
530 Type: TypeBlob,
531 Ref: CID{Link: "bafkreicustomcid"},
532 MimeType: mimeType,
533 Size: len(data),
534 }, nil
535 },
536 }
537
538 converter := NewMarkdownConverter().WithImageResolver(resolver, tmpDir)
539 blocks, err := converter.ToLeaflet(markdown)
540
541 shared.AssertNoError(t, err, "ToLeaflet should succeed")
542 shared.AssertTrue(t, uploadCalled, "upload should be called")
543
544 imgBlock, ok := blocks[0].Block.(ImageBlock)
545 shared.AssertTrue(t, ok, "block should be image")
546 shared.AssertEqual(t, "bafkreicustomcid", imgBlock.Image.Ref.Link, "should use custom CID")
547 })
548
549 t.Run("handles missing image gracefully", func(t *testing.T) {
550 markdown := ""
551 resolver := &LocalImageResolver{}
552 converter := NewMarkdownConverter().WithImageResolver(resolver, tmpDir)
553
554 _, err := converter.ToLeaflet(markdown)
555 shared.AssertError(t, err, "should error on missing image")
556 shared.AssertTrue(t, strings.Contains(err.Error(), "failed to resolve image"), "error should mention resolution failure")
557 })
558
559 t.Run("gathers images from complex document", func(t *testing.T) {
560 _ = createTestImage(t, "header.png", 100, 100)
561 _ = createTestImage(t, "body.png", 200, 200)
562 _ = createTestImage(t, "list.png", 50, 50)
563
564 markdown := `# Header
565
566
567
568Some text with  image.
569
570- List item
571- Another item with 
572`
573
574 resolver := &LocalImageResolver{}
575 converter := NewMarkdownConverter().WithImageResolver(resolver, tmpDir)
576
577 blocks, err := converter.ToLeaflet(markdown)
578 shared.AssertNoError(t, err, "ToLeaflet should succeed")
579
580 imageCount := 0
581 for _, block := range blocks {
582 if _, ok := block.Block.(ImageBlock); ok {
583 imageCount++
584 }
585 }
586 shared.AssertTrue(t, imageCount >= 2, "should find multiple images")
587 })
588
589 t.Run("preserves image dimensions accurately", func(t *testing.T) {
590 testCases := []struct {
591 name string
592 width int
593 height int
594 }{
595 {"square.png", 100, 100},
596 {"landscape.png", 1920, 1080},
597 {"portrait.png", 1080, 1920},
598 {"wide.png", 2560, 1440},
599 }
600
601 for _, tc := range testCases {
602 createTestImage(t, tc.name, tc.width, tc.height)
603 markdown := ""
604
605 resolver := &LocalImageResolver{}
606 converter := NewMarkdownConverter().WithImageResolver(resolver, tmpDir)
607
608 blocks, err := converter.ToLeaflet(markdown)
609 shared.AssertNoError(t, err, "should convert "+tc.name)
610
611 imgBlock := blocks[0].Block.(ImageBlock)
612 shared.AssertEqual(t, tc.width, imgBlock.AspectRatio.Width, tc.name+" width")
613 shared.AssertEqual(t, tc.height, imgBlock.AspectRatio.Height, tc.name+" height")
614 }
615 })
616 })
617}