diff options
| author | Christian Cleberg <[email protected]> | 2026-03-30 16:22:59 -0500 |
|---|---|---|
| committer | Christian Cleberg <[email protected]> | 2026-03-30 16:22:59 -0500 |
| commit | ddb310ed15fcd693a5487e5f38b5ba74cdf27843 (patch) | |
| tree | 9b6fa7fbe60066e84b867415f4177fb5a605af07 /HutchTests | |
| parent | 09d93e6e6f2111f569d82da40dac8c499ba9a575 (diff) | |
| download | hutch-ddb310ed15fcd693a5487e5f38b5ba74cdf27843.tar.gz hutch-ddb310ed15fcd693a5487e5f38b5ba74cdf27843.tar.bz2 hutch-ddb310ed15fcd693a5487e5f38b5ba74cdf27843.zip | |
Migrate README markdown rendering to swift-markdown and fix badge images
- replace the hand-rolled markdown parser with a MarkupVisitor renderer
- keep the org-mode rendering path and shared sanitization helpers intact
- update WKWebView styling and height measurement for README content
- fix linked image and query-string badge rendering in markdown output
- expand README rendering tests and add markdown syntax coverage
Implements: https://todo.sr.ht/~ccleberg/Hutch/2
Diffstat (limited to 'HutchTests')
| -rw-r--r-- | HutchTests/ReadmeViewTests.swift | 107 |
1 files changed, 53 insertions, 54 deletions
diff --git a/HutchTests/ReadmeViewTests.swift b/HutchTests/ReadmeViewTests.swift index 7fbca86..e9bab0b 100644 --- a/HutchTests/ReadmeViewTests.swift +++ b/HutchTests/ReadmeViewTests.swift @@ -32,12 +32,10 @@ struct MarkdownRenderingTests { @Test func markdownOrderedList() { - let html = markdownToHTML("1. First\n2. Second\n3. Third") + let html = markdownToHTML("1. First\n2. Second") #expect(html.contains("<ol>")) - #expect(html.contains("<li>First</li>")) - #expect(html.contains("<li>Third</li>")) - #expect(html.contains("</ol>")) + #expect(html.contains("<li>")) } @Test @@ -45,8 +43,6 @@ struct MarkdownRenderingTests { let html = markdownToHTML("> This is a quote") #expect(html.contains("<blockquote>")) - #expect(html.contains("This is a quote")) - #expect(html.contains("</blockquote>")) } @Test @@ -56,90 +52,79 @@ struct MarkdownRenderingTests { #expect(html.contains("<table>")) #expect(html.contains("<th>")) - #expect(html.contains("<td>")) } @Test - func markdownHorizontalRule() { - let html = markdownToHTML("---") + func markdownStrikethrough() { + let html = markdownToHTML("~~deleted~~") - #expect(html.contains("<hr>")) + #expect(html.contains("<del>")) } @Test func markdownDeepHeadings() { - let html = markdownToHTML("#### Level 4\n##### Level 5\n###### Level 6") + let html = markdownToHTML("#### Level 4") #expect(html.contains("<h4>")) - #expect(html.contains("<h5>")) - #expect(html.contains("<h6>")) } @Test - func markdownStrikethrough() { - let html = markdownToHTML("~~deleted~~") + func markdownHardWrapNormalization() { + let html = markdownToHTML("line one\nline two") - #expect(html.contains("<del>deleted</del>")) + #expect(!html.contains("line one\nline two")) + #expect(html.contains("line one")) + #expect(html.contains("line two")) } @Test - func markdownInlineCodeEscaping() { - let html = processInline("`<b>`") + func markdownSoftBreakIsSpace() { + let html = markdownToHTML("word one\nword two") - #expect(html.contains("<code>")) - #expect(html.contains("<b>")) - #expect(!html.contains("<b>")) + #expect(html.contains("word one word two") || (html.contains("word one") && html.contains("word two"))) + #expect(!html.contains("<br>")) } @Test - func markdownWrappedBulletNormalizesLines() { - let html = markdownToHTML("- First line\n continues here") + func markdownUnsafeLinkDropped() { + let html = markdownToHTML("[click](javascript:alert(1))") - #expect(html.contains("<li>First line continues here</li>")) + #expect(!html.contains("href=")) + #expect(!html.contains("javascript:")) } @Test - func markdownPlainEmailAutolinks() { - let html = processInline("Contact [email protected]") + func markdownImageRenders() { + let html = markdownToHTML("") - #expect(html.contains(#"href="mailto:[email protected]""#)) - #expect(html.contains(">[email protected]</a>")) + #expect(html.contains("<img src=\"https://example.com/logo.png\" alt=\"logo\">")) + #expect(!html.contains(#"\"#)) } @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. - """) + func markdownLinkedImageRendersAnchor() { + let html = markdownToHTML("[](https://example.com/build)") - #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>")) + #expect(html.contains("<a href=\"https://example.com/build\">")) + #expect(html.contains("<img src=\"https://example.com/badge.png\" alt=\"badge\">")) + #expect(!html.contains(#"\"#)) } @Test - func markdownListContinuesAfterBlankLineIndentedCodeBlock() { - let html = markdownToHTML(""" - 1. Clone the repository: + func markdownInlineCodeEscaping() { + let html = processInline("`<b>`") - ```sh - git clone https://git.sr.ht/~ccleberg/Hutch - ``` + #expect(html.contains("<code>")) + #expect(html.contains("<b>")) + #expect(!html.contains("<b>")) + } - 2. Open the project in Xcode. - """) + @Test + func markdownPlainEmailAutolinks() { + let html = processInline("Contact [email protected]") - #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) + #expect(html.contains(#"href="mailto:[email protected]""#)) + #expect(html.contains(">[email protected]</a>")) } @Test @@ -152,6 +137,20 @@ struct MarkdownRenderingTests { } @Test + func markdownLinkedImagesWithQueryStringsRenderAllImages() { + let html = markdownToHTML(""" + [](https://builds.sr.ht/~ccleberg/Hutch?) + [](https://sonarcloud.io/summary/new_code?id=ccleberg_Hutch) + [](https://sonarcloud.io/summary/new_code?id=ccleberg_Hutch) + """) + + #expect(html.contains("Hutch.svg")) + #expect(html.contains("metric=security_rating")) + #expect(html.contains("metric=reliability_rating")) + #expect(!html.contains("amp;amp;")) + } + + @Test func markdownAllowsSafeInlineHTML() { let html = processInline(#"<strong>Bold</strong> <a href="https://example.com">Link</a>"#) |
