diff options
Diffstat (limited to 'Tests/NorgEditorTests/NorgEditorTests.swift')
| -rw-r--r-- | Tests/NorgEditorTests/NorgEditorTests.swift | 100 |
1 files changed, 100 insertions, 0 deletions
diff --git a/Tests/NorgEditorTests/NorgEditorTests.swift b/Tests/NorgEditorTests/NorgEditorTests.swift new file mode 100644 index 0000000..6a6caf7 --- /dev/null +++ b/Tests/NorgEditorTests/NorgEditorTests.swift @@ -0,0 +1,100 @@ +import Foundation +import NorgKit +import Testing + +@testable import NorgEditor + +@Test func plainTextYieldsNoHighlights() { + #expect(NorgHighlighter.highlights(in: "just some prose").isEmpty) + #expect(NorgHighlighter.highlights(in: "").isEmpty) +} + +@Test func headingMarkerIsHighlighted() { + let highlights = NorgHighlighter.highlights(in: "** Title") + let heading = highlights.first + #expect(heading?.kind == .heading(level: 2)) + // The marker run `**` spans the first two UTF-16 units; the title is plain. + #expect(heading?.range == NSRange(location: 0, length: 2)) +} + +@Test func taskStatusIsHighlightedWithMarkerAndStatus() { + let highlights = NorgHighlighter.highlights(in: "- (x) buy milk") + #expect(highlights.contains { $0.kind == .unorderedList(level: 1) }) + + let status = highlights.first { highlight in + if case .taskStatus = highlight.kind { return true } + return false + } + #expect(status?.kind == .taskStatus(.done)) + // `(x)` — open paren, status char, close paren. + #expect(status?.range == NSRange(location: 2, length: 3)) +} + +@Test func inlineVerbatimReportsDelimitersAndStyledText() { + let highlights = NorgHighlighter.highlights(in: "a `code` b") + let styled = highlights.first { highlight in + if case .styledText(let style) = highlight.kind { return style.contains(.verbatim) } + return false + } + #expect(styled?.range == NSRange(location: 3, length: 4)) // "code" + #expect(highlights.contains { $0.kind == .modifierDelimiter(.verbatim) }) +} + +@Test func rangesAreValidForMultibyteSource() { + // An emoji is two UTF-16 units; the bold run must land on the right span. + let source = "🌟 *bold*" + let highlights = NorgHighlighter.highlights(in: source) + let nsSource = source as NSString + let styled = highlights.first { highlight in + if case .styledText(let style) = highlight.kind { return style.contains(.bold) } + return false + } + let range = styled?.range + #expect(range.map { nsSource.substring(with: $0) } == "bold") +} + +@Test func linkTargetIsHighlighted() { + let highlights = NorgHighlighter.highlights(in: "see {https://example.com}") + #expect(highlights.contains { $0.kind == .linkTarget }) + #expect(highlights.contains { $0.kind == .linkDelimiter }) +} + +// MARK: - Auto-indentation (Return key) + +/// Indentation at the end of `source` (where Return would land). +private func indent(after source: String) -> String { + NorgAutoIndent.indentation(for: source, newlineAt: (source as NSString).length) +} + +@Test func indentsUnderTheOpenHeading() { + // `*** ` is three markers plus a space, so the text starts at column 4. + #expect(indent(after: "*** Headline") == " ") + #expect(indent(after: "* Top") == " ") +} + +@Test func plainTextWithoutAHeadingHasNoIndent() { + #expect(indent(after: "just a paragraph") == "") + #expect(indent(after: "") == "") +} + +@Test func indentPersistsAcrossContentLines() { + // A paragraph under the heading keeps the heading's indent on the next line. + #expect(indent(after: "* A\nsome prose") == " ") +} + +@Test func indentFollowsTheDeepestOpenHeading() { + #expect(indent(after: "* A\n** B\n*** C") == " ") +} + +@Test func strongDelimiterClosesAllHeadings() { + #expect(indent(after: "* A\n** B\n===\nmore") == "") +} + +@Test func weakDelimiterReturnsToTheParentHeading() { + // `---` closes `** B`, leaving `* A` open → align under its text (column 2). + #expect(indent(after: "* A\n** B\n---\nmore") == " ") +} + +@Test func horizontalRuleLeavesHeadingScopeIntact() { + #expect(indent(after: "* A\n___\nmore") == " ") +} |