aboutsummaryrefslogtreecommitdiff
path: root/Tests
diff options
context:
space:
mode:
authorRuben Beltran del Rio <jj@r.bdr.sh>2026-06-15 14:35:36 +0200
committerRuben Beltran del Rio <jj@r.bdr.sh>2026-06-16 14:47:30 +0200
commit657aa83a5ca24dc35572c040df64a0abb85e8612 (patch)
treefe0b6fd425d8cad9dc6a28ada4ad8600e42b367e /Tests
Initial extraction from Norganize
Diffstat (limited to 'Tests')
-rw-r--r--Tests/NorgUITests/InlineTextTests.swift79
-rw-r--r--Tests/NorgUITests/NorgLinkURLTests.swift30
-rw-r--r--Tests/NorgUITests/NorgThemeTests.swift35
-rw-r--r--Tests/NorgUITests/OrdinalTests.swift38
-rw-r--r--Tests/NorgUITests/RenderingSmokeTests.swift51
-rw-r--r--Tests/NorgUITests/TaskStatusUITests.swift29
6 files changed, 262 insertions, 0 deletions
diff --git a/Tests/NorgUITests/InlineTextTests.swift b/Tests/NorgUITests/InlineTextTests.swift
new file mode 100644
index 0000000..c2c6537
--- /dev/null
+++ b/Tests/NorgUITests/InlineTextTests.swift
@@ -0,0 +1,79 @@
+import NorgKit
+import SwiftUI
+import Testing
+
+@testable import NorgUI
+
+@Suite struct InlineTextTests {
+ @Test func plainSpanCarriesText() {
+ let attributed = AttributedString(norg: [InlineSpan(text: "hello")])
+ #expect(String(attributed.characters) == "hello")
+ }
+
+ @Test func multipleSpansConcatenate() {
+ let attributed = AttributedString(norg: [
+ InlineSpan(text: "foo"),
+ InlineSpan(text: "bar"),
+ ])
+ #expect(String(attributed.characters) == "foobar")
+ }
+
+ @Test func verbatimUsesThemeColor() {
+ let attributed = AttributedString(norg: [InlineSpan(text: "x", styles: .verbatim)])
+ let run = attributed.runs.first
+ #expect(run?.foregroundColor == NorgTheme.default.verbatimColor)
+ }
+
+ @Test func spoilerAppliesForegroundAndBackground() {
+ let attributed = AttributedString(norg: [InlineSpan(text: "x", styles: .spoiler)])
+ let run = attributed.runs.first
+ #expect(run?.foregroundColor == NorgTheme.default.spoilerForeground)
+ #expect(run?.backgroundColor == NorgTheme.default.spoilerBackground)
+ }
+
+ @Test func superscriptUsesThemeOffset() {
+ let attributed = AttributedString(norg: [InlineSpan(text: "x", styles: .superscript)])
+ #expect(attributed.runs.first?.baselineOffset == NorgTheme.default.superscriptOffset)
+ }
+
+ @Test func subscriptUsesThemeOffset() {
+ let attributed = AttributedString(norg: [InlineSpan(text: "x", styles: .subscript)])
+ #expect(attributed.runs.first?.baselineOffset == NorgTheme.default.subscriptOffset)
+ }
+
+ @Test func externalLinkBecomesTappable() {
+ let attributed = AttributedString(norg: [
+ InlineSpan(text: "site", link: InlineLink(kind: .link, target: "https://example.com"))
+ ])
+ let run = attributed.runs.first
+ #expect(run?.link == URL(string: "https://example.com"))
+ #expect(run?.foregroundColor == NorgTheme.default.linkColor)
+ }
+
+ @Test func internalLinkIsEncodedForRouting() {
+ let attributed = AttributedString(norg: [
+ InlineSpan(text: "note", link: InlineLink(kind: .link, target: "* Some heading"))
+ ])
+ let run = attributed.runs.first
+ let decoded = run?.link.flatMap(NorgLinkURL.decode)
+ #expect(decoded == InlineLink(kind: .link, target: "* Some heading"))
+ #expect(run?.foregroundColor == NorgTheme.default.linkColor)
+ }
+
+ @Test func anchorIsEncodedForRouting() {
+ let attributed = AttributedString(norg: [
+ InlineSpan(text: "ref", link: InlineLink(kind: .anchor, target: "footnote"))
+ ])
+ let run = attributed.runs.first
+ let decoded = run?.link.flatMap(NorgLinkURL.decode)
+ #expect(decoded == InlineLink(kind: .anchor, target: "footnote"))
+ }
+
+ @Test func customThemeOverridesColors() {
+ var theme = NorgTheme.default
+ theme.verbatimColor = .orange
+ let attributed = AttributedString(
+ norg: [InlineSpan(text: "x", styles: .verbatim)], theme: theme)
+ #expect(attributed.runs.first?.foregroundColor == .orange)
+ }
+}
diff --git a/Tests/NorgUITests/NorgLinkURLTests.swift b/Tests/NorgUITests/NorgLinkURLTests.swift
new file mode 100644
index 0000000..dcd5446
--- /dev/null
+++ b/Tests/NorgUITests/NorgLinkURLTests.swift
@@ -0,0 +1,30 @@
+import Foundation
+import NorgKit
+import Testing
+
+@testable import NorgUI
+
+@Suite struct NorgLinkURLTests {
+ @Test func roundTripsLink() throws {
+ let link = InlineLink(kind: .link, target: "* My Heading")
+ let url = try #require(NorgLinkURL.encode(link))
+ #expect(NorgLinkURL.decode(url) == link)
+ }
+
+ @Test func roundTripsAnchor() throws {
+ let link = InlineLink(kind: .anchor, target: "some anchor")
+ let url = try #require(NorgLinkURL.encode(link))
+ #expect(NorgLinkURL.decode(url) == link)
+ }
+
+ @Test func roundTripsMissingTarget() throws {
+ let link = InlineLink(kind: .link, target: nil)
+ let url = try #require(NorgLinkURL.encode(link))
+ #expect(NorgLinkURL.decode(url) == link)
+ }
+
+ @Test func ignoresForeignURLs() throws {
+ let url = try #require(URL(string: "https://example.com"))
+ #expect(NorgLinkURL.decode(url) == nil)
+ }
+}
diff --git a/Tests/NorgUITests/NorgThemeTests.swift b/Tests/NorgUITests/NorgThemeTests.swift
new file mode 100644
index 0000000..a621d90
--- /dev/null
+++ b/Tests/NorgUITests/NorgThemeTests.swift
@@ -0,0 +1,35 @@
+import NorgKit
+import SwiftUI
+import Testing
+
+@testable import NorgUI
+
+@Suite struct NorgThemeTests {
+ @Test func defaultStatusTints() {
+ let theme = NorgTheme.default
+ #expect(theme.statusTint(.done) == .green)
+ #expect(theme.statusTint(.urgent) == .red)
+ #expect(theme.statusTint(.pending) == .blue)
+ #expect(theme.statusTint(.undone) == .secondary)
+ #expect(theme.statusTint(.cancelled) == .secondary)
+ #expect(theme.statusTint(.onHold) == .secondary)
+ }
+
+ @Test func defaultScalarValues() {
+ let theme = NorgTheme.default
+ #expect(theme.superscriptOffset == 5)
+ #expect(theme.subscriptOffset == -3)
+ #expect(theme.indentWidth == 18)
+ #expect(theme.linkColor == .accentColor)
+ #expect(theme.verbatimColor == .pink)
+ }
+
+ @Test func defaultStatusSymbols() {
+ let theme = NorgTheme.default
+ #expect(theme.statusSymbol(.undone) == "circle")
+ #expect(theme.statusSymbol(.done) == "checkmark.circle.fill")
+ for status in TaskStatus.allCases {
+ #expect(!theme.statusSymbol(status).isEmpty)
+ }
+ }
+}
diff --git a/Tests/NorgUITests/OrdinalTests.swift b/Tests/NorgUITests/OrdinalTests.swift
new file mode 100644
index 0000000..954477c
--- /dev/null
+++ b/Tests/NorgUITests/OrdinalTests.swift
@@ -0,0 +1,38 @@
+import NorgKit
+import Testing
+
+@testable import NorgUI
+
+@Suite struct OrdinalTests {
+ private func ordered(_ level: Int) -> NorgBlock {
+ .orderedListItem(level: level, status: nil, content: [], line: 0)
+ }
+ private func unordered(_ level: Int) -> NorgBlock {
+ .unorderedListItem(level: level, status: nil, content: [], line: 0)
+ }
+ private var paragraph: NorgBlock { .paragraph(content: [], line: 0) }
+
+ @Test func numbersConsecutiveItems() {
+ #expect(orderedOrdinals([ordered(1), ordered(1), ordered(1)]) == [1, 2, 3])
+ }
+
+ @Test func countsEachLevelIndependently() {
+ let blocks = [ordered(1), ordered(2), ordered(2), ordered(1)]
+ #expect(orderedOrdinals(blocks) == [1, 1, 2, 2])
+ }
+
+ @Test func paragraphResetsNumbering() {
+ let blocks = [ordered(1), paragraph, ordered(1)]
+ #expect(orderedOrdinals(blocks) == [1, nil, 1])
+ }
+
+ @Test func unorderedItemInterruptsNumbering() {
+ let blocks = [ordered(1), unordered(1), ordered(1)]
+ #expect(orderedOrdinals(blocks) == [1, nil, 1])
+ }
+
+ @Test func siblingOrdinalsNumberOrderedNodesOnly() {
+ let nodes = [ordered(1), unordered(1), ordered(1), paragraph].map { NorgNode(block: $0) }
+ #expect(ordinals(forSiblings: nodes) == [1, nil, 2, nil])
+ }
+}
diff --git a/Tests/NorgUITests/RenderingSmokeTests.swift b/Tests/NorgUITests/RenderingSmokeTests.swift
new file mode 100644
index 0000000..39c9eb5
--- /dev/null
+++ b/Tests/NorgUITests/RenderingSmokeTests.swift
@@ -0,0 +1,51 @@
+import NorgKit
+import Testing
+
+@testable import NorgUI
+
+@Suite struct RenderingSmokeTests {
+ @Test func rangeableBlockExposesBodyForRecursion() {
+ let doc = NorgParser.parse("$ Norg\nA structured note format.")
+ guard case .definition(_, _, let body, _) = doc.blocks.first else {
+ Issue.record("Expected a definition")
+ return
+ }
+ #expect(!body.isEmpty)
+ }
+
+ @Test func headingNestsContentAsTreeChildren() {
+ let nodes = NorgParser.parse("* Title\nbody text below").tree()
+ guard let heading = nodes.first, case .heading = heading.block else {
+ Issue.record("Expected a heading root")
+ return
+ }
+ #expect(!heading.children.isEmpty)
+ }
+
+ @Test func nestedListNestsAsTreeChildren() {
+ let nodes = NorgParser.parse("- one\n-- nested").tree()
+ guard let item = nodes.first else {
+ Issue.record("Expected a list item")
+ return
+ }
+ #expect(item.children.count == 1)
+ }
+
+ @Test func nonCodeRangedTagIsRouted() {
+ let doc = NorgParser.parse("@math\nx^2 + y^2 = z^2\n@end")
+ guard case .rangedTag(let name, _, _, _) = doc.blocks.first else {
+ Issue.record("Expected a ranged tag")
+ return
+ }
+ #expect(name == ["math"])
+ }
+
+ @Test func codeFenceBecomesCodeBlock() {
+ let doc = NorgParser.parse("@code swift\nlet x = 1\n@end")
+ guard case .codeBlock(let language, _, _) = doc.blocks.first else {
+ Issue.record("Expected a code block")
+ return
+ }
+ #expect(language == "swift")
+ }
+}
diff --git a/Tests/NorgUITests/TaskStatusUITests.swift b/Tests/NorgUITests/TaskStatusUITests.swift
new file mode 100644
index 0000000..0b5dd53
--- /dev/null
+++ b/Tests/NorgUITests/TaskStatusUITests.swift
@@ -0,0 +1,29 @@
+import Foundation
+import NorgKit
+import Testing
+
+@testable import NorgUI
+
+@Suite struct TaskStatusUITests {
+ private func resolved(_ status: TaskStatus) -> String {
+ String(localized: status.displayName)
+ }
+
+ @Test func everyStatusHasADisplayName() {
+ for status in TaskStatus.allCases {
+ #expect(!resolved(status).isEmpty)
+ }
+ }
+
+ @Test func displayNamesAreDistinct() {
+ let names = Set(TaskStatus.allCases.map(resolved))
+ #expect(names.count == TaskStatus.allCases.count)
+ }
+
+ @Test func themeStatusLabelDefaultsToDisplayName() {
+ let theme = NorgTheme.default
+ for status in TaskStatus.allCases {
+ #expect(String(localized: theme.statusLabel(status)) == resolved(status))
+ }
+ }
+}