import Foundation import Testing @testable import Hutch struct ReadmeViewTests { @Test func sanitizedReadmeLinkURLStringRejectsUnexpectedSchemes() { #expect(sanitizedReadmeLinkURLString("javascript:alert(1)") == nil) #expect(sanitizedReadmeLinkURLString("file:///tmp/readme") == nil) #expect(sanitizedReadmeLinkURLString("data:text/html;base64,SGVsbG8=") == nil) } @Test func processInlineDropsUnsafeMarkdownLinks() { let rendered = processInline("[click me](javascript:alert)") #expect(rendered == "click me") #expect(!rendered.contains("href=")) #expect(!rendered.contains("javascript:")) } @Test func sanitizedReadmeLinkURLStringAllowsExpectedDestinations() { #expect(sanitizedReadmeLinkURLString("https://example.com/docs?q=1") == "https://example.com/docs?q=1") #expect(sanitizedReadmeLinkURLString("mailto:test@example.com") == "mailto:test@example.com") #expect(sanitizedReadmeLinkURLString("#readme") == "#readme") } } struct MarkdownRenderingTests { @Test func markdownOrderedList() { let html = markdownToHTML("1. First\n2. Second") #expect(html.contains("
    ")) #expect(html.contains("
  1. ")) } @Test func markdownBlockquote() { let html = markdownToHTML("> 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("
    ")) } @Test func markdownTableAlignment() { let input = "| Left | Center | Right |\n|:-----|:------:|------:|\n| a | b | c |" let html = markdownToHTML(input) #expect(html.contains("text-align: left;")) #expect(html.contains("text-align: center;")) #expect(html.contains("text-align: right;")) } @Test func markdownStrikethrough() { let html = markdownToHTML("~~deleted~~") #expect(html.contains("")) } @Test func markdownDeepHeadings() { let html = markdownToHTML("#### Level 4") #expect(html.contains("

    ")) } @Test func markdownHardWrapNormalization() { let html = markdownToHTML("line one\nline two") #expect(!html.contains("line one\nline two")) #expect(html.contains("line one")) #expect(html.contains("line two")) } @Test func markdownSoftBreakIsSpace() { let html = markdownToHTML("word one\nword two") #expect(html.contains("word one word two") || (html.contains("word one") && html.contains("word two"))) #expect(!html.contains("
    ")) } @Test func markdownUnsafeLinkDropped() { let html = markdownToHTML("[click](javascript:alert(1))") #expect(!html.contains("href=")) #expect(!html.contains("javascript:")) } @Test func markdownImageRenders() { let html = markdownToHTML("![logo](https://example.com/logo.png)") #expect(html.contains("\"logo\"")) #expect(!html.contains(#"\"#)) } @Test func markdownLinkedImageRendersAnchor() { let html = markdownToHTML("[![badge](https://example.com/badge.png)](https://example.com/build)") #expect(html.contains("")) #expect(html.contains("\"badge\"")) #expect(!html.contains(#"\"#)) } @Test func markdownInlineCodeEscaping() { let html = processInline("``") #expect(html.contains("")) #expect(html.contains("<b>")) #expect(!html.contains("")) } @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 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("
  2. First line continues here
  3. ")) } @Test func orgHeaderKeywordsIgnored() { let html = orgToHTML(""" #+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 = orgToHTML(""" #+begin_verse There is a line. And an indented line. #+end_verse """) #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 = orgToHTML(""" #+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 = orgToHTML("[[https://example.com][[https://img.cleberg.net/apps/hutch/screenshots/ipad/01_patch.jpg]]]") #expect(html.contains(#""#)) #expect(html.contains(#""#)) } @Test func orgLinkedRelativeImageRenders() { let html = orgToHTML( "[[https://example.com/docs][[./images/badge.svg]]]", imageURLResolver: { source in source == "./images/badge.svg" ? "https://git.sr.ht/~ccleberg/Hutch/blob/HEAD/images/badge.svg" : nil } ) #expect(html.contains(#""#)) #expect(html.contains(#""#)) } @Test func orgNestedBulletListRendersNestedMarkup() { let html = orgToHTML(""" * 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("
    • Bullet with nested list\n
        ")) #expect(html.contains("
      • Nested child one
      • ")) #expect(html.contains("
      • Nested child two
      • ")) #expect(html.contains("
      • Bullet with wrapped continuation line
      • ")) } @Test func orgTableAlignmentRendersStyles() { let html = orgToHTML(""" | Left | Center | Right | |:-----+:-----:+------:| | a | b | c | | 1 | 2 | 3 | """) #expect(html.contains("text-align: left;")) #expect(html.contains("text-align: center;")) #expect(html.contains("text-align: right;")) } } 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") } }