AtlatestRepositorysigil-org
1
(import (sigil test)2
(sigil org)3
(sigil sxml)4
(sigil string))6
;; ============================================================7
;; Metadata8
;; ============================================================10
(test-group "metadata"11
(test "title metadata"12
(let ((doc (org->sxml "#+TITLE: My Document\n\nContent")))13
(assert-equal 'org-document (sxml-tag doc))14
(assert-equal "My Document" (sxml-attr-ref doc 'title))))16
(test "multiple metadata"17
(let ((doc (org->sxml "#+TITLE: Test\n#+AUTHOR: Alice\n\nContent")))18
(assert-equal "Test" (sxml-attr-ref doc 'title))19
(assert-equal "Alice" (sxml-attr-ref doc 'author))))21
(test "no metadata"22
(let ((doc (org->sxml "* Heading\n\nText")))23
(assert-equal 'org-document (sxml-tag doc))24
(assert-equal '() (sxml-attributes doc)))))26
;; ============================================================27
;; Headlines28
;; ============================================================30
(test-group "headlines"31
(test "simple headline"32
(let* ((doc (org->sxml "* Hello"))33
(hl (car (sxml-content doc))))34
(assert-equal 'headline (sxml-tag hl))35
(assert-equal 1 (sxml-attr-ref hl 'level))))37
(test "nested headlines"38
(let* ((doc (org->sxml "* Top\n** Sub"))39
(top (car (sxml-content doc))))40
(assert-equal 'headline (sxml-tag top))41
(assert-equal 1 (sxml-attr-ref top 'level))42
;; Sub-headline should be nested43
(let ((children (filter (lambda (x)44
(and (pair? x) (eq? (car x) 'headline)))45
(cdr top))))46
(assert-true (> (length children) 0)))))48
(test "headline with TODO"49
(let* ((doc (org->sxml "* TODO Fix the bug"))50
(hl (car (sxml-content doc))))51
(assert-equal "TODO" (sxml-attr-ref hl 'todo))))53
(test "headline with tags"54
(let* ((doc (org->sxml "* My heading :tag1:tag2:"))55
(hl (car (sxml-content doc))))56
(assert-equal "tag1 tag2" (sxml-attr-ref hl 'tags))))58
(test "headline with priority"59
(let* ((doc (org->sxml "* [#A] Important task"))60
(hl (car (sxml-content doc))))61
(assert-equal "A" (sxml-attr-ref hl 'priority)))))63
;; ============================================================64
;; Property Drawers65
;; ============================================================67
(test-group "property drawers"68
(test "property drawer"69
(let* ((doc (org->sxml "* Heading\n:PROPERTIES:\n:CUSTOM_ID: my-id\n:END:\n\nText"))70
(hl (car (sxml-content doc)))71
(drawer (let find ((elems (cdr hl)))72
(cond73
((null? elems) #f)74
((and (pair? (car elems))75
(eq? (caar elems) 'property-drawer))76
(car elems))77
(else (find (cdr elems)))))))78
(assert-true (pair? drawer)))))80
;; ============================================================81
;; Source Blocks82
;; ============================================================84
(test-group "source blocks"85
(test "source block with language"86
(let* ((doc (org->sxml "#+BEGIN_SRC scheme\n(define x 42)\n#+END_SRC"))87
(block (car (sxml-content doc))))88
(assert-equal 'src-block (sxml-tag block))89
(assert-equal "scheme" (sxml-attr-ref block 'language))))91
(test "source block preserves content"92
(let* ((doc (org->sxml "#+BEGIN_SRC python\nprint(\"hello\")\nprint(\"world\")\n#+END_SRC"))93
(block (car (sxml-content doc)))94
(text (car (sxml-content block))))95
(assert-true (string-contains? text "hello"))96
(assert-true (string-contains? text "world")))))98
;; ============================================================99
;; Other Blocks100
;; ============================================================102
(test-group "other blocks"103
(test "quote block"104
(let* ((doc (org->sxml "#+BEGIN_QUOTE\nSome quote\n#+END_QUOTE"))105
(block (car (sxml-content doc))))106
(assert-equal 'quote-block (sxml-tag block))))108
(test "example block"109
(let* ((doc (org->sxml "#+BEGIN_EXAMPLE\nSome example\n#+END_EXAMPLE"))110
(block (car (sxml-content doc))))111
(assert-equal 'example-block (sxml-tag block)))))113
;; ============================================================114
;; Inline Markup115
;; ============================================================117
(test-group "inline markup"118
(test "bold text"119
(let* ((doc (org->sxml "This is *bold* text"))120
(para (car (sxml-content doc)))121
(has-bold (let find ((elems (cdr para)))122
(cond123
((null? elems) #f)124
((and (pair? (car elems))125
(eq? (caar elems) 'bold))126
#t)127
(else (find (cdr elems)))))))128
(assert-true has-bold)))130
(test "italic text"131
(let* ((doc (org->sxml "This is /italic/ text"))132
(para (car (sxml-content doc)))133
(has-italic (let find ((elems (cdr para)))134
(cond135
((null? elems) #f)136
((and (pair? (car elems))137
(eq? (caar elems) 'italic))138
#t)139
(else (find (cdr elems)))))))140
(assert-true has-italic)))142
(test "code text"143
(let* ((doc (org->sxml "Use ~my-function~ here"))144
(para (car (sxml-content doc)))145
(has-code (let find ((elems (cdr para)))146
(cond147
((null? elems) #f)148
((and (pair? (car elems))149
(eq? (caar elems) 'code))150
#t)151
(else (find (cdr elems)))))))152
(assert-true has-code)))154
(test "verbatim text"155
(let* ((doc (org->sxml "The value is =42= here"))156
(para (car (sxml-content doc)))157
(has-verb (let find ((elems (cdr para)))158
(cond159
((null? elems) #f)160
((and (pair? (car elems))161
(eq? (caar elems) 'verbatim))162
#t)163
(else (find (cdr elems)))))))164
(assert-true has-verb))))166
;; ============================================================167
;; Links168
;; ============================================================170
(test-group "links"171
(test "link with description"172
(let* ((doc (org->sxml "Visit [[https://example.com][Example]]"))173
(para (car (sxml-content doc)))174
(link (let find ((elems (cdr para)))175
(cond176
((null? elems) #f)177
((and (pair? (car elems))178
(eq? (caar elems) 'a))179
(car elems))180
(else (find (cdr elems)))))))181
(assert-true (pair? link))182
(assert-equal "https://example.com" (sxml-attr-ref link 'href))))184
(test "link without description"185
(let* ((doc (org->sxml "See [[https://example.com]]"))186
(para (car (sxml-content doc)))187
(link (let find ((elems (cdr para)))188
(cond189
((null? elems) #f)190
((and (pair? (car elems))191
(eq? (caar elems) 'a))192
(car elems))193
(else (find (cdr elems)))))))194
(assert-true (pair? link)))))196
;; ============================================================197
;; Lists198
;; ============================================================200
(test-group "lists"201
(test "unordered list"202
(let* ((doc (org->sxml "- item one\n- item two\n- item three"))203
(list-elem (car (sxml-content doc))))204
(assert-equal 'unordered-list (sxml-tag list-elem))))206
(test "ordered list"207
(let* ((doc (org->sxml "1. first\n2. second\n3. third"))208
(list-elem (car (sxml-content doc))))209
(assert-equal 'ordered-list (sxml-tag list-elem))))211
(test "list with checkbox"212
(let* ((doc (org->sxml "- [ ] unchecked\n- [x] checked"))213
(list-elem (car (sxml-content doc)))214
(items (sxml-content list-elem)))215
(assert-equal 2 (length items)))))217
;; ============================================================218
;; Paragraphs and Rules219
;; ============================================================221
(test-group "paragraphs"222
(test "simple paragraph"223
(let* ((doc (org->sxml "Hello world"))224
(para (car (sxml-content doc))))225
(assert-equal 'paragraph (sxml-tag para))))227
(test "multiple paragraphs"228
(let* ((doc (org->sxml "First paragraph.\n\nSecond paragraph."))229
(content (sxml-content doc)))230
(assert-equal 2 (length content)))))232
(test-group "horizontal rules"233
(test "horizontal rule"234
(let* ((doc (org->sxml "Above\n\n-----\n\nBelow"))235
(content (sxml-content doc))236
(has-hr (let find ((elems content))237
(cond238
((null? elems) #f)239
((and (pair? (car elems))240
(eq? (caar elems) 'hr))241
#t)242
(else (find (cdr elems)))))))243
(assert-true has-hr))))245
;; ============================================================246
;; Timestamps247
;; ============================================================249
(test-group "timestamps"250
(test "active timestamp"251
(let* ((doc (org->sxml "Scheduled for <2026-02-20 Fri>"))252
(para (car (sxml-content doc)))253
(has-ts (let find ((elems (cdr para)))254
(cond255
((null? elems) #f)256
((and (pair? (car elems))257
(eq? (caar elems) 'timestamp))258
#t)259
(else (find (cdr elems)))))))260
(assert-true has-ts)))262
(test "inactive timestamp"263
(let* ((doc (org->sxml "Created [2026-02-20 Fri]"))264
(para (car (sxml-content doc)))265
(ts (let find ((elems (cdr para)))266
(cond267
((null? elems) #f)268
((and (pair? (car elems))269
(eq? (caar elems) 'timestamp))270
(car elems))271
(else (find (cdr elems)))))))272
(assert-true (pair? ts))273
(assert-equal "inactive" (sxml-attr-ref ts 'type)))))275
;; ============================================================276
;; Comments277
;; ============================================================279
(test-group "comments"280
(test "comment lines are skipped"281
(let* ((doc (org->sxml "# This is a comment\nVisible text"))282
(content (sxml-content doc)))283
;; Should only have the paragraph, not the comment284
(assert-equal 1 (length content))285
(assert-equal 'paragraph (sxml-tag (car content))))))287
;; ============================================================288
;; Tables289
;; ============================================================291
(test-group "tables"292
(test "simple table"293
(let* ((doc (org->sxml "| A | B |\n| 1 | 2 |"))294
(content (sxml-content doc))295
(tbl (car content)))296
(assert-equal 'table (sxml-tag tbl))297
;; Two rows, first is header (th), second is data (td)298
(let ((rows (sxml-content tbl)))299
(assert-equal 2 (length rows))300
(assert-equal 'tr (sxml-tag (car rows)))301
(assert-equal 'th (sxml-tag (car (sxml-content (car rows)))))302
(assert-equal 'td (sxml-tag (car (sxml-content (cadr rows))))))))304
(test "table with separator row"305
(let* ((doc (org->sxml "| Name | Value |\n|------+-------|\n| foo | 42 |"))306
(content (sxml-content doc))307
(tbl (car content)))308
(assert-equal 'table (sxml-tag tbl))309
;; Separator row is skipped, so 2 data rows310
(let ((rows (sxml-content tbl)))311
(assert-equal 2 (length rows))312
;; First row is header313
(assert-equal 'th (sxml-tag (car (sxml-content (car rows))))))))315
(test "table with inline markup in cells"316
(let* ((doc (org->sxml "| Key | Desc |\n|-----+------|\n| ~C-x~ | =save= |"))317
(content (sxml-content doc))318
(tbl (car content))319
(rows (sxml-content tbl))320
(data-row (cadr rows))321
(cells (sxml-content data-row)))322
;; First cell should contain code element323
(let ((cell1-content (sxml-content (car cells))))324
(assert-true (pair? cell1-content))325
(assert-equal 'code (sxml-tag (car cell1-content))))))327
(test "single row table"328
(let* ((doc (org->sxml "| only | row |"))329
(content (sxml-content doc))330
(tbl (car content))331
(rows (sxml-content tbl)))332
;; Single row should just be a tr333
(assert-equal 1 (length rows))334
(assert-equal 'tr (sxml-tag (car rows))))))336
;; ============================================================337
;; Integration338
;; ============================================================340
(test-group "integration"341
(test "full document"342
(let ((doc (org->sxml343
(string-append344
"#+TITLE: My Document\n"345
"#+AUTHOR: Test User\n"346
"\n"347
"* Introduction\n"348
"\n"349
"This is a *bold* introduction with a [[https://example.com][link]].\n"350
"\n"351
"** Details\n"352
"\n"353
"- Item one\n"354
"- Item two\n"355
"\n"356
"#+BEGIN_SRC scheme\n"357
"(define x 42)\n"358
"#+END_SRC\n"))))359
(assert-equal 'org-document (sxml-tag doc))360
(assert-equal "My Document" (sxml-attr-ref doc 'title))361
(assert-equal "Test User" (sxml-attr-ref doc 'author)))))363
(run-tests)