summaryrefslogtreecommitdiff
path: root/HutchTests/ReadmeViewTests.swift
diff options
context:
space:
mode:
authorChristian Cleberg <[email protected]>2026-03-30 13:10:54 -0500
committerChristian Cleberg <[email protected]>2026-03-30 13:10:54 -0500
commit09d93e6e6f2111f569d82da40dac8c499ba9a575 (patch)
tree0a4ff3d25ac8bc23d181c40351ae057f92fff45e /HutchTests/ReadmeViewTests.swift
parent0ca0f7656a077ed1e71f63c27015c667dff7466e (diff)
downloadhutch-09d93e6e6f2111f569d82da40dac8c499ba9a575.tar.gz
hutch-09d93e6e6f2111f569d82da40dac8c499ba9a575.tar.bz2
hutch-09d93e6e6f2111f569d82da40dac8c499ba9a575.zip
partial extended support for markdown and org-mode
Diffstat (limited to 'HutchTests/ReadmeViewTests.swift')
-rw-r--r--HutchTests/ReadmeViewTests.swift211
1 files changed, 211 insertions, 0 deletions
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("<ol>"))
+ #expect(html.contains("<li>First</li>"))
+ #expect(html.contains("<li>Third</li>"))
+ #expect(html.contains("</ol>"))
+ }
+
+ @Test
+ func markdownBlockquote() {
+ let html = markdownToHTML("> This is a quote")
+
+ #expect(html.contains("<blockquote>"))
+ #expect(html.contains("This is a quote"))
+ #expect(html.contains("</blockquote>"))
+ }
+
+ @Test
+ func markdownTable() {
+ let input = "| A | B |\n|---|---|\n| 1 | 2 |"
+ let html = markdownToHTML(input)
+
+ #expect(html.contains("<table>"))
+ #expect(html.contains("<th>"))
+ #expect(html.contains("<td>"))
+ }
+
+ @Test
+ func markdownHorizontalRule() {
+ let html = markdownToHTML("---")
+
+ #expect(html.contains("<hr>"))
+ }
+
+ @Test
+ func markdownDeepHeadings() {
+ let html = markdownToHTML("#### Level 4\n##### Level 5\n###### Level 6")
+
+ #expect(html.contains("<h4>"))
+ #expect(html.contains("<h5>"))
+ #expect(html.contains("<h6>"))
+ }
+
+ @Test
+ func markdownStrikethrough() {
+ let html = markdownToHTML("~~deleted~~")
+
+ #expect(html.contains("<del>deleted</del>"))
+ }
+
+ @Test
+ func markdownInlineCodeEscaping() {
+ let html = processInline("`<b>`")
+
+ #expect(html.contains("<code>"))
+ #expect(html.contains("&lt;b&gt;"))
+ #expect(!html.contains("<b>"))
+ }
+
+ @Test
+ func markdownWrappedBulletNormalizesLines() {
+ let html = markdownToHTML("- First line\n continues here")
+
+ #expect(html.contains("<li>First line continues here</li>"))
+ }
+
+ @Test
+ func markdownPlainEmailAutolinks() {
+ let html = processInline("Contact [email protected]")
+
+ #expect(html.contains(#"href="mailto:[email protected]""#))
+ #expect(html.contains(">[email protected]</a>"))
+ }
+
+ @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("<ol>"))
+ #expect(html.contains("<pre><code>"))
+ #expect(html.contains("<li><p>Clone the repository:</p>"))
+ #expect(html.contains("<li>Open the project in Xcode.</li>"))
+ #expect(html.contains("</ol>"))
+ }
+
+ @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("<li><p>Clone the repository:</p>"))
+ #expect(html.contains("<pre><code>git clone https://git.sr.ht/~ccleberg/Hutch</code></pre>"))
+ #expect(html.contains("<li>Open the project in Xcode.</li>"))
+ #expect(!html.contains("<ol>\n<li>Open the project in Xcode.</li>\n</ol>\n<ol>"))
+ #expect(html.firstRange(of: "<p>Clone the repository:</p>")!.lowerBound < html.firstRange(of: "<pre><code>git clone https://git.sr.ht/~ccleberg/Hutch</code></pre>")!.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("<img"))
+ }
+
+ @Test
+ func markdownAllowsSafeInlineHTML() {
+ let html = processInline(#"<strong>Bold</strong> <a href="https://example.com">Link</a>"#)
+
+ #expect(html.contains("<strong>Bold</strong>"))
+ #expect(html.contains(#"<a href="https://example.com">Link</a>"#))
+ }
+
+ @Test
+ func markdownProtectedTokensDoNotLeak() {
+ let html = processInline(#"<a href="https://example.com">Back to top</a> `yoshi [ARG] <FILE>`"#)
+
+ #expect(!html.contains("ZZPROTECTED"))
+ #expect(html.contains(#"<a href="https://example.com">Back to top</a>"#))
+ #expect(html.contains("<code>yoshi [ARG] &lt;FILE&gt;</code>"))
+ }
+}
+
+struct OrgRenderingTests {
+
+ @Test
+ func orgDeepHeading() {
+ let html = orgToHTML("**** Level 4 Heading")
+
+ #expect(html.contains("<h4>"))
+ }
+
+ @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("<del>"))
+ }
+
+ @Test
+ func orgExampleBlock() {
+ let html = orgToHTML("#+begin_example\nhello world\n#+end_example")
+
+ #expect(html.contains("<pre><code>"))
+ #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("<em>"))
+ }
+
+ @Test
+ func orgWrappedBulletNormalizesLines() {
+ let html = orgToHTML("- First line\n continues here")
+
+ #expect(html.contains("<li>First line continues here</li>"))
+ }
+}
+
+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")
+ }
+}