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.. "** |" 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* ") } // MARK: - Task button supports every detached modifier @Test func taskAddsMarkerToBareUnorderedListItem() { // "- |" -> "- ( ) |" let result = EditorSnippet.task.apply(to: "- ", cursor: 2) #expect(result.text == "- ( ) ") #expect(result.cursor == 6) } @Test func taskAddsMarkerToEveryDetachedModifier() { // Quotes, definitions, footnotes and the like all carry tasks per the // Norg spec, matching NorgKit's task scanner. for marker in [">", "$", "^", ":"] { let result = EditorSnippet.task.apply(to: "\(marker) ", cursor: 2) #expect(result.text == "\(marker) ( ) ") #expect(result.cursor == 6) } } @Test func taskAddsMarkerToNestedDetachedModifierPreservingContent() { // ">> |Quote" -> ">> ( ) |Quote" let result = EditorSnippet.task.apply(to: ">> Quote", cursor: 3) #expect(result.text == ">> ( ) Quote") #expect(result.cursor == 7) } @Test func taskIgnoresWeakDelimiterMadeOfDashes() { // "---|" is a delimiter, not a list item — start a new task line instead. let result = EditorSnippet.task.apply(to: "---", cursor: 3) #expect(result.text == "---\n- ( ) ") } @Test func taskInsertsNewLineWhenMarkerHasNoSeparatingSpace() { // A bare marker with no trailing space isn't a detached modifier yet. let result = EditorSnippet.task.apply(to: ">", cursor: 1) #expect(result.text == ">\n- ( ) ") } }