From 09d93e6e6f2111f569d82da40dac8c499ba9a575 Mon Sep 17 00:00:00 2001 From: Christian Cleberg Date: Mon, 30 Mar 2026 13:10:54 -0500 Subject: partial extended support for markdown and org-mode --- HutchTests/ReadmeViewTests.swift | 211 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 211 insertions(+) (limited to 'HutchTests/ReadmeViewTests.swift') diff --git a/HutchTests/ReadmeViewTests.swift b/HutchTests/ReadmeViewTests.swift index 9c3c019..7fbca86 100644 --- a/HutchTests/ReadmeViewTests.swift +++ b/HutchTests/ReadmeViewTests.swift @@ -27,3 +27,214 @@ struct ReadmeViewTests { #expect(sanitizedReadmeLinkURLString("#readme") == "#readme") } } + +struct MarkdownRenderingTests { + + @Test + func markdownOrderedList() { + let html = markdownToHTML("1. First\n2. Second\n3. Third") + + #expect(html.contains("
    ")) + #expect(html.contains("
  1. First
  2. ")) + #expect(html.contains("
  3. Third
  4. ")) + #expect(html.contains("
")) + } + + @Test + func markdownBlockquote() { + let html = markdownToHTML("> This is a quote") + + #expect(html.contains("
")) + #expect(html.contains("This is a quote")) + #expect(html.contains("
")) + } + + @Test + func markdownTable() { + let input = "| A | B |\n|---|---|\n| 1 | 2 |" + let html = markdownToHTML(input) + + #expect(html.contains("")) + #expect(html.contains("
")) + #expect(html.contains("")) + } + + @Test + func markdownHorizontalRule() { + let html = markdownToHTML("---") + + #expect(html.contains("
")) + } + + @Test + func markdownDeepHeadings() { + let html = markdownToHTML("#### Level 4\n##### Level 5\n###### Level 6") + + #expect(html.contains("

")) + #expect(html.contains("

")) + #expect(html.contains("
")) + } + + @Test + func markdownStrikethrough() { + let html = markdownToHTML("~~deleted~~") + + #expect(html.contains("deleted")) + } + + @Test + func markdownInlineCodeEscaping() { + let html = processInline("``") + + #expect(html.contains("")) + #expect(html.contains("<b>")) + #expect(!html.contains("")) + } + + @Test + func markdownWrappedBulletNormalizesLines() { + let html = markdownToHTML("- First line\n continues here") + + #expect(html.contains("
  • First line continues here
  • ")) + } + + @Test + func markdownPlainEmailAutolinks() { + let html = processInline("Contact hello@cleberg.net") + + #expect(html.contains(#"href="mailto:hello@cleberg.net""#)) + #expect(html.contains(">hello@cleberg.net")) + } + + @Test + func markdownListContinuesAfterCodeBlock() { + let html = markdownToHTML(""" + 1. Clone the repository: + ```sh + git clone https://git.sr.ht/~ccleberg/Hutch + ``` + 2. Open the project in Xcode. + """) + + #expect(html.contains("
      ")) + #expect(html.contains("
      "))
      +        #expect(html.contains("
    1. Clone the repository:

      ")) + #expect(html.contains("
    2. Open the project in Xcode.
    3. ")) + #expect(html.contains("
    ")) + } + + @Test + func markdownListContinuesAfterBlankLineIndentedCodeBlock() { + let html = markdownToHTML(""" + 1. Clone the repository: + + ```sh + git clone https://git.sr.ht/~ccleberg/Hutch + ``` + + 2. Open the project in Xcode. + """) + + #expect(html.contains("
  • Clone the repository:

    ")) + #expect(html.contains("
    git clone https://git.sr.ht/~ccleberg/Hutch
    ")) + #expect(html.contains("
  • Open the project in Xcode.
  • ")) + #expect(!html.contains("
      \n
    1. Open the project in Xcode.
    2. \n
    \n
      ")) + #expect(html.firstRange(of: "

      Clone the repository:

      ")!.lowerBound < html.firstRange(of: "
      git clone https://git.sr.ht/~ccleberg/Hutch
      ")!.lowerBound) + } + + @Test + func markdownImageQueryStringPreservesAmpersands() { + let html = processInline("![badge](https://sonarcloud.io/api/project_badges/measure?project=ccleberg_Hutch&metric=security_rating)") + + #expect(html.contains("metric=security_rating")) + #expect(!html.contains("amp;metric")) + #expect(html.contains("Bold Link"#) + + #expect(html.contains("Bold")) + #expect(html.contains(#"Link"#)) + } + + @Test + func markdownProtectedTokensDoNotLeak() { + let html = processInline(#"Back to top `yoshi [ARG] `"#) + + #expect(!html.contains("ZZPROTECTED")) + #expect(html.contains(#"Back to top"#)) + #expect(html.contains("yoshi [ARG] <FILE>")) + } +} + +struct OrgRenderingTests { + + @Test + func orgDeepHeading() { + let html = orgToHTML("**** Level 4 Heading") + + #expect(html.contains("

      ")) + } + + @Test + func orgCommentLinesIgnored() { + let html = orgToHTML("# This is a comment\nNormal text") + + #expect(!html.contains("This is a comment")) + #expect(html.contains("Normal text")) + } + + @Test + func orgStrikethrough() { + let html = orgToHTML("+deleted text+") + + #expect(html.contains("")) + } + + @Test + func orgExampleBlock() { + let html = orgToHTML("#+begin_example\nhello world\n#+end_example") + + #expect(html.contains("
      "))
      +        #expect(html.contains("hello world"))
      +    }
      +
      +    @Test
      +    func orgTitleKeyword() {
      +        let html = orgToHTML("#+TITLE: My Document\nBody text")
      +
      +        #expect(html.contains("org-title"))
      +        #expect(html.contains("My Document"))
      +    }
      +
      +    @Test
      +    func orgItalicDoesNotMatchURLPaths() {
      +        let html = orgToHTML("[[https://example.com/path/to/file]]")
      +
      +        #expect(!html.contains(""))
      +    }
      +
      +    @Test
      +    func orgWrappedBulletNormalizesLines() {
      +        let html = orgToHTML("- First line\n  continues here")
      +
      +        #expect(html.contains("
    1. First line continues here
    2. ")) + } +} + +struct RepositoryAssetURLTests { + + @Test + func repositoryAssetURLPercentEncodesImagePaths() { + let url = resolveRepositoryAssetURL( + "images/My Logo.png", + owner: "~ccleberg", + repositoryName: "Hutch", + readmePath: "README.md" + ) + + #expect(url == "https://git.sr.ht/~ccleberg/Hutch/blob/HEAD/images/My%20Logo.png") + } +} -- cgit v1.2.3