diff options
Diffstat (limited to 'Tests')
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) + } +} |