"))
}
@Test
func orgCommentLinesIgnored() {
let html = render("# This is a comment\nNormal text")
#expect(!html.contains("This is a comment"))
#expect(html.contains("Normal text"))
}
@Test
func orgStrikethrough() {
let html = render("+deleted text+")
#expect(html.contains(""))
}
@Test
func orgExampleBlock() {
let html = render("#+begin_example\nhello world\n#+end_example")
// Example blocks are a bare
(org's exporter uses no inner ).
#expect(html.contains("
hello world"))
#expect(!html.contains("
"))
}
@Test
func orgTitleKeyword() {
let html = render("#+TITLE: My Document\nBody text")
#expect(html.contains("org-title"))
#expect(html.contains("My Document"))
}
@Test
func orgItalicDoesNotMatchURLPaths() {
let html = render("[[https://example.com/path/to/file]]")
#expect(!html.contains(""))
}
@Test
func orgWrappedBulletNormalizesLines() {
let html = render("- First line\n continues here")
#expect(html.contains("
First line continues here
"))
}
@Test
func orgHeaderKeywordsIgnored() {
let html = render("""
#+OPTIONS: toc:nil
#+PROPERTY: header-args :results output
Body text
""")
#expect(!html.contains("#+OPTIONS"))
#expect(!html.contains("#+PROPERTY"))
#expect(html.contains("
Body text
"))
}
@Test
func orgVerseBlock() {
let html = render("""
#+begin_verse
There is a line.
And an indented line.
#+end_verse
""")
// Verse renders as
with between lines, matching org.
#expect(html.contains(#"
"##))
// Inline footnote content sits directly in the
, not wrapped in
.
#expect(html.contains(#"
defined inline "#))
#expect(!html.contains("
defined inline"))
}
@Test
func propertyDrawerIsDropped() {
let html = render("""
* Heading
:PROPERTIES:
:CUSTOM_ID: first
:OWNER: nobody
:END:
Body text.
""")
#expect(!html.contains("org-properties"))
#expect(!html.contains("
Heading"))
#expect(html.contains("
Body text.
"))
}
@Test
func headingTagsRenderAsSpans() {
let html = render("* Write the parser :work:rust:")
#expect(html.contains(#"
Write the parser workrust
"#))
}
@Test
func headingWithoutTagsIsUnchanged() {
let html = render("* Just a heading")
#expect(html.contains("
Just a heading
"))
#expect(!html.contains("class=\"tag\""))
}
@Test
func bareUrlBecomesLink() {
let html = render("See https://example.com now.")
#expect(html.contains(#"https://example.com"#))
// Trailing sentence punctuation stays outside the link.
let end = render("End at https://example.com.")
#expect(end.contains(#"https://example.com."#))
}
@Test
func taskCheckboxesRenderAsCode() {
let html = render("""
- [ ] todo
- [X] done
- [-] partial
""")
#expect(html.contains("[ ] todo"))
#expect(html.contains("[X] done"))
#expect(html.contains("[-] partial"))
#expect(!html.contains("
"#))
}
@Test
func describedFileImageIsALink() {
// A description turns an image target into a link, not an inline image.
let html = render("[[file:diagram.png][the diagram]]")
#expect(html.contains(#"the diagram"#))
#expect(!html.contains(""#))
// The double-bracketed form works the same.
let bracketed = render("[[https://example.com/builds][[https://example.com/badge.svg]]]")
#expect(bracketed.contains(#"see diagram.png"#))
#expect(!plain.contains("Figure 1: A stylised chart"#))
// alt is the caption with markup stripped.
#expect(html.contains(#"alt="A stylised chart""#))
}
@Test
func attrHtmlPromotesImageToFigureWithoutCaption() {
let html = render("""
#+ATTR_HTML: :alt "a cat, sitting" :loading lazy
[[file:cat.jpg]]
""")
#expect(html.contains("First"))
#expect(html.contains("Figure 2: Second"))
}
@Test
func threeDeepNestedList() {
let html = render("""
- outer
- inner
- deepest
- second inner
- second outer
""")
#expect(html.contains("
outer\n
\n
inner\n
\n
deepest
"))
#expect(html.contains("
second inner
"))
#expect(html.contains("
second outer
"))
}
@Test
func nestedOrderedList() {
let html = render("""
1. first
2. second
1. point one
2. point two
3. third
""")
#expect(html.contains("
second\n\n
point one
"))
#expect(html.contains("
third
"))
}
@Test
func descriptionList() {
let html = render("""
- term one :: first definition
- /marked/ term :: holds inline markup
""")
#expect(html.contains("
"))
#expect(html.contains("
term one
"))
#expect(html.contains("
first definition
"))
#expect(html.contains("
marked term
"))
}
@Test
func multiParagraphListItem() {
let html = render("""
- an item with two paragraphs
the second paragraph
- a plain sibling
""")
#expect(html.contains("
an item with two paragraphs
\n
the second paragraph
"))
#expect(html.contains("
a plain sibling
"))
}
@Test
func exportBlockHtmlPassesThroughLatexDropped() {
let html = render("""
#+BEGIN_EXPORT html
#+END_EXPORT
#+BEGIN_EXPORT latex
\\emph{dropped}
#+END_EXPORT
""")
#expect(html.contains(""))
#expect(!html.contains("dropped"))
#expect(!html.contains("BEGIN_EXPORT"))
}
@Test
func specialBlockRendersAsNamedDiv() {
let html = render("""
#+BEGIN_NOTE
Contents are *org*, not literal.
#+END_NOTE
""")
#expect(html.contains(#"
"#))
#expect(html.contains("
Contents are org, not literal.
"))
}
@Test
func superscriptRenders() {
#expect(render("N^2 here.").contains("N2"))
#expect(render("H^{2O} molecule.").contains("H2O"))
}
@Test
func idLinkBecomesFragment() {
let html = render("See [[id:abc-123]] and [[id:x][the x]].")
#expect(html.contains(##"abc-123"##))
#expect(html.contains(##"the x"##))
}
@Test
func unknownKeywordLinesAreDropped() {
let html = render("#+FILETAGS: :sample:\n\nBody.")
#expect(!html.contains("FILETAGS"))
#expect(!html.contains("sample"))
#expect(html.contains("
Body.
"))
}
@Test
func relativeURLsResolveWithConfigurablePathSegments() {
// gitbay serves raw bytes at /raw// and the file page at /blob//, so an
// image resolves against a different segment than a link.
let options = OrgRenderOptions(
host: "gitbay.org",
owner: "krz",
repositoryName: "gitbay",
ref: "HEAD",
readmePath: "README.org",
imagePathSegment: "raw",
linkPathSegment: "blob"
)
let html = render("""
[[file:docs/logo.png]]
See [[docs/DESIGN.org][the design]].
""", options: options)
#expect(html.contains(#"the design"#))
}
}