import Foundation import Testing @testable import OrgSwift private func render(_ source: String, options: OrgRenderOptions = .init()) -> String { OrgRenderer.renderToHTML(source, options: options) } struct OrgRendererTests { @Test func orgDeepHeading() { let html = render("**** Level 4 Heading") #expect(html.contains("

")) } @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(#"

    "#)) #expect(html.contains("There is a line.
    ")) #expect(html.contains("And an indented line.")) #expect(!html.contains("#+begin_verse")) } @Test func orgNamedBlockRendersCaption() { let html = render(""" #+CAPTION: Build output #+NAME: build-log #+begin_example hello world #+end_example """) #expect(html.contains(#"

    "#)) #expect(html.contains("
    Build output
    ")) #expect(!html.contains("#+CAPTION")) #expect(!html.contains("#+NAME")) } @Test func orgLinkedImageRenders() { let html = render("[[https://example.com][[https://img.cleberg.net/apps/hutch/screenshots/ipad/01_patch.jpg]]]") #expect(html.contains(#""#)) #expect(html.contains(#""#)) } @Test func orgLinkedRelativeImageRenders() { // In hutch this passed an explicit imageURLResolver closure; here the same // resolution is expressed through OrgRenderOptions (owner/repositoryName/host). let html = render( "[[https://example.com/docs][[./images/badge.svg]]]", options: OrgRenderOptions(owner: "~ccleberg", repositoryName: "Hutch") ) #expect(html.contains(#""#)) #expect(html.contains(#""#)) } @Test func orgNestedBulletListRendersNestedMarkup() { let html = render(""" * Lists ** Unordered - First bullet - Second bullet - Third bullet with /italic/ and *bold* - Bullet with wrapped continuation line - Bullet with nested list - Nested child one - Nested child two - Bullet with inline code ~let x = 1~ """) #expect(html.contains("

    ")) #expect(html.contains("

    Body text.

    ")) } @Test func headingTagsRenderAsSpans() { let html = render("* Write the parser :work:rust:") #expect(html.contains(#"

    Write the parser work rust

    "#)) } @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
        1. point one
        2. ")) #expect(html.contains("
        3. third
        4. ")) } @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("
        5. an item with two paragraphs

          \n

          the second paragraph

        6. ")) #expect(html.contains("
        7. a plain sibling
        8. ")) } @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"#)) } }