aboutsummaryrefslogtreecommitdiff
path: root/Tests
diff options
context:
space:
mode:
Diffstat (limited to 'Tests')
-rw-r--r--Tests/NorgKeyboardToolbarTests/EditorSnippet+PresentationTests.swift13
-rw-r--r--Tests/NorgKeyboardToolbarTests/EditorSnippetTests.swift150
-rw-r--r--Tests/NorgKeyboardToolbarTests/LineScannerTests.swift45
3 files changed, 208 insertions, 0 deletions
diff --git a/Tests/NorgKeyboardToolbarTests/EditorSnippet+PresentationTests.swift b/Tests/NorgKeyboardToolbarTests/EditorSnippet+PresentationTests.swift
new file mode 100644
index 0000000..e31df1b
--- /dev/null
+++ b/Tests/NorgKeyboardToolbarTests/EditorSnippet+PresentationTests.swift
@@ -0,0 +1,13 @@
+import Testing
+
+@testable import NorgKeyboardToolbar
+
+struct EditorSnippetPresentationTests {
+
+ @Test func everySnippetHasALabelAndSymbol() {
+ for snippet in EditorSnippet.allCases {
+ #expect(!snippet.label.isEmpty)
+ #expect(!snippet.systemImage.isEmpty)
+ }
+ }
+}
diff --git a/Tests/NorgKeyboardToolbarTests/EditorSnippetTests.swift b/Tests/NorgKeyboardToolbarTests/EditorSnippetTests.swift
new file mode 100644
index 0000000..00a9332
--- /dev/null
+++ b/Tests/NorgKeyboardToolbarTests/EditorSnippetTests.swift
@@ -0,0 +1,150 @@
+import Testing
+
+@testable import NorgKeyboardToolbar
+
+struct EditorSnippetTests {
+
+ @Test func idMatchesRawValueAndIsUnique() {
+ for snippet in EditorSnippet.allCases {
+ #expect(snippet.id == snippet.rawValue)
+ }
+ #expect(Set(EditorSnippet.allCases.map(\.id)).count == EditorSnippet.allCases.count)
+ }
+
+ @Test func insertsIntoEmptyDocumentWithoutLeadingNewline() {
+ let result = EditorSnippet.heading.apply(to: "", cursor: 0)
+ #expect(result.text == "* ")
+ #expect(result.cursor == 2)
+ }
+
+ @Test func insertsOnNewLineBeneathCurrentLine() {
+ let result = EditorSnippet.task.apply(to: "first line", cursor: 3)
+ #expect(result.text == "first line\n- ( ) ")
+ // Cursor sits at the end of the inserted task marker, ready to type.
+ #expect(result.cursor == result.text.count)
+ }
+
+ @Test func insertsBetweenLinesNotSplittingThem() {
+ let result = EditorSnippet.task.apply(to: "a\nb", cursor: 0)
+ #expect(result.text == "a\n- ( ) \nb")
+ }
+
+ @Test func delimiterHasNoTrailingSpace() {
+ let weak = EditorSnippet.weakReverse.apply(to: "x", cursor: 1)
+ #expect(weak.text == "x\n---")
+ let strong = EditorSnippet.strongReverse.apply(to: "x", cursor: 1)
+ #expect(strong.text == "x\n===")
+ }
+
+ @Test func codeBlockPlacesCursorIndentedBetweenMarkers() {
+ let result = EditorSnippet.code.apply(to: "note", cursor: 4)
+ #expect(result.text == "note\n@code\n\t\n@end")
+ // Cursor lands right after the tab on the middle line.
+ let index = result.text.index(result.text.startIndex, offsetBy: result.cursor)
+ #expect(result.text[result.text.startIndex..<index].hasSuffix("@code\n\t"))
+ #expect(String(result.text[index...]) == "\n@end")
+ }
+
+ @Test func clampsCursorBeyondBounds() {
+ let result = EditorSnippet.heading.apply(to: "abc", cursor: 999)
+ #expect(result.text == "abc\n* ")
+ }
+
+ // MARK: - Heading promotion (the `*` button's special behaviour)
+
+ @Test func headingPromotesEmptyMarker() {
+ // "* |" -> "** |"
+ let result = EditorSnippet.heading.apply(to: "* ", cursor: 2)
+ #expect(result.text == "** ")
+ #expect(result.cursor == 3)
+ }
+
+ @Test func headingPromotesWithTrailingContent() {
+ // "* |Hello" -> "** |Hello"
+ let result = EditorSnippet.heading.apply(to: "* Hello", cursor: 2)
+ #expect(result.text == "** Hello")
+ #expect(result.cursor == 3)
+ }
+
+ @Test func headingPromotesBareStars() {
+ // "***|" -> "****|"
+ let result = EditorSnippet.heading.apply(to: "***", cursor: 3)
+ #expect(result.text == "****")
+ #expect(result.cursor == 4)
+ }
+
+ @Test func headingInsertsNewLineWhenCursorAfterContent() {
+ // "* Hello|" -> new heading line
+ let result = EditorSnippet.heading.apply(to: "* Hello", cursor: 7)
+ #expect(result.text == "* Hello\n* ")
+ }
+
+ @Test func headingInsertsNewLineWhenMarkerNotAtLineStart() {
+ // "Hello *|" -> new heading line, not promotion
+ let result = EditorSnippet.heading.apply(to: "Hello *", cursor: 7)
+ #expect(result.text == "Hello *\n* ")
+ }
+
+ @Test func headingPromotesIndentedMarker() {
+ let result = EditorSnippet.heading.apply(to: " ** ", cursor: 5)
+ #expect(result.text == " *** ")
+ }
+
+ @Test func headingPromotionKeepsCursorWhenInsertingAfterIt() {
+ // "*|*" -> "**|*" : the new star lands after the cursor, so the offset
+ // is unchanged.
+ let result = EditorSnippet.heading.apply(to: "**", cursor: 1)
+ #expect(result.text == "***")
+ #expect(result.cursor == 1)
+ }
+
+ // MARK: - Task button heading awareness
+
+ @Test func taskAddsMarkerToBareHeading() {
+ // "* |" -> "* ( ) |"
+ let result = EditorSnippet.task.apply(to: "* ", cursor: 2)
+ #expect(result.text == "* ( ) ")
+ #expect(result.cursor == 6)
+ }
+
+ @Test func taskAddsMarkerToHeadingPreservingLevelAndContent() {
+ // "** |Hello" -> "** ( ) |Hello"
+ let result = EditorSnippet.task.apply(to: "** Hello", cursor: 3)
+ #expect(result.text == "** ( ) Hello")
+ #expect(result.cursor == 7)
+ }
+
+ @Test func taskInsertsNewLineWhenHeadingHasContent() {
+ // "** Hello|" -> "** Hello\n- ( ) "
+ let result = EditorSnippet.task.apply(to: "** Hello", cursor: 8)
+ #expect(result.text == "** Hello\n- ( ) ")
+ #expect(result.cursor == result.text.count)
+ }
+
+ @Test func taskAddsMarkerToBareOrderedListItem() {
+ // "~ |" -> "~ ( ) |"
+ let result = EditorSnippet.task.apply(to: "~ ", cursor: 2)
+ #expect(result.text == "~ ( ) ")
+ #expect(result.cursor == 6)
+ }
+
+ @Test func taskAddsMarkerToOrderedListPreservingLevelAndContent() {
+ // "~~ |Hello" -> "~~ ( ) |Hello"
+ let result = EditorSnippet.task.apply(to: "~~ Hello", cursor: 3)
+ #expect(result.text == "~~ ( ) Hello")
+ #expect(result.cursor == 7)
+ }
+
+ @Test func taskInsertsNewLineWhenOrderedListHasContent() {
+ // "~ Hello|" -> "~ Hello\n- ( ) "
+ let result = EditorSnippet.task.apply(to: "~ Hello", cursor: 7)
+ #expect(result.text == "~ Hello\n- ( ) ")
+ #expect(result.cursor == result.text.count)
+ }
+
+ @Test func headingIgnoresOrderedListMarker() {
+ // The `*` button only promotes `*` headings, not `~` lists.
+ let result = EditorSnippet.heading.apply(to: "~ ", cursor: 2)
+ #expect(result.text == "~ \n* ")
+ }
+}
diff --git a/Tests/NorgKeyboardToolbarTests/LineScannerTests.swift b/Tests/NorgKeyboardToolbarTests/LineScannerTests.swift
new file mode 100644
index 0000000..17b9cb0
--- /dev/null
+++ b/Tests/NorgKeyboardToolbarTests/LineScannerTests.swift
@@ -0,0 +1,45 @@
+import Testing
+
+@testable import NorgKeyboardToolbar
+
+struct LineScannerTests {
+
+ @Test func lineStartFindsBeginningOfCurrentLine() {
+ let chars = Array("first\nsecond")
+ // Cursor inside "second" (offset 9 -> the 'o').
+ #expect(LineScanner.lineStart(in: chars, at: 9) == 6)
+ }
+
+ @Test func lineStartAtBufferStartIsZero() {
+ let chars = Array("hello")
+ #expect(LineScanner.lineStart(in: chars, at: 3) == 0)
+ }
+
+ @Test func lineStartJustAfterNewlineIsTheNewLine() {
+ let chars = Array("a\nb")
+ // Offset 2 sits at 'b', whose line starts right after the newline.
+ #expect(LineScanner.lineStart(in: chars, at: 2) == 2)
+ }
+
+ @Test func lineEndFindsNextNewline() {
+ let chars = Array("first\nsecond")
+ #expect(LineScanner.lineEnd(in: chars, at: 2) == 5)
+ }
+
+ @Test func lineEndAtLastLineIsBufferCount() {
+ let chars = Array("first\nsecond")
+ #expect(LineScanner.lineEnd(in: chars, at: 8) == chars.count)
+ }
+
+ @Test func lineEndOnNewlineStaysPut() {
+ let chars = Array("a\nb")
+ // Offset 1 is the newline itself.
+ #expect(LineScanner.lineEnd(in: chars, at: 1) == 1)
+ }
+
+ @Test func handlesEmptyBuffer() {
+ let chars: [Character] = []
+ #expect(LineScanner.lineStart(in: chars, at: 0) == 0)
+ #expect(LineScanner.lineEnd(in: chars, at: 0) == 0)
+ }
+}