Files
conjure/Tests/ConjureKitTests/NotesHeadingTests.swift
T

105 lines
5.9 KiB
Swift

import AppKit
import ConjureKit
import Testing
@testable import Conjure
@Test @MainActor
func headingShortcutsConsumeSyntaxAndPersistAllSixLevels() throws {
_ = NSApplication.shared
let suite = "NotesHeadings.\(UUID().uuidString)"
let defaults = try #require(UserDefaults(suiteName: suite))
let directory = FileManager.default.temporaryDirectory.appendingPathComponent(suite)
defer {
defaults.removePersistentDomain(forName: suite)
try? FileManager.default.removeItem(at: directory)
}
let controller = NotesController(defaults: defaults, directory: directory)
let window = NSWindow(contentRect: NSRect(x: 0, y: 0, width: 600, height: 500),
styleMask: .borderless, backing: .buffered, defer: false)
window.contentViewController = controller
defer { _ = controller.close(); window.orderOut(nil) }
func descendants(_ view: NSView) -> [NSView] { [view] + view.subviews.flatMap(descendants) }
let editor = try #require(descendants(controller.view).compactMap { $0 as? NotesTextView }.first)
window.makeFirstResponder(editor)
for level in 1...6 {
editor.setDocumentContents(NSAttributedString(string: "Title"))
editor.setSelectedRange(NSRange(location: 0, length: 0))
editor.beginInsertionSession()
for character in String(repeating: "#", count: level) + " " {
editor.insertText(String(character), replacementRange: editor.selectedRange())
}
editor.endInsertionSession()
#expect(editor.string == "Title")
#expect(editor.selectedRange() == NSRange(location: 0, length: 0))
#expect(editor.documentContents.attribute(NoteAttributes.block, at: 0, effectiveRange: nil) as? String == "heading")
#expect(editor.documentContents.attribute(NoteAttributes.headingLevel, at: 0, effectiveRange: nil) as? Int == level)
editor.undoManager?.undo()
#expect(editor.string == "Title")
#expect(editor.documentContents.attribute(NoteAttributes.block, at: 0, effectiveRange: nil) == nil)
editor.undoManager?.redo()
#expect(editor.documentContents.attribute(NoteAttributes.headingLevel, at: 0, effectiveRange: nil) as? Int == level)
#expect(controller.save())
let reloaded = try #require(NotesStore(directory: directory).notes.first)
#expect(reloaded.contents.string == "Title")
#expect(reloaded.contents.attribute(NoteAttributes.headingLevel, at: 0, effectiveRange: nil) as? Int == level)
}
}
@Test @MainActor
func headingReturnSplitsVisibleParagraphAndUndoRestoresItsSemantics() throws {
_ = NSApplication.shared
let editor = NotesTextView(frame: NSRect(x: 0, y: 0, width: 600, height: 500))
editor.allowsUndo = true
let window = NSWindow(contentRect: editor.frame, styleMask: .borderless, backing: .buffered, defer: false)
window.contentView = editor
window.makeFirstResponder(editor)
defer { window.orderOut(nil) }
for position in [2, 5] {
editor.setDocumentContents(NoteDocument.importMarkdown(NSAttributedString(string: "## Title")))
editor.setSelectedRange(NSRange(location: position, length: 0))
editor.undoManager?.removeAllActions()
editor.insertNewline(nil)
#expect(editor.string == (position == 2 ? "Ti\ntle" : "Title\n"))
#expect(editor.selectedRange() == NSRange(location: position + 1, length: 0))
#expect(editor.documentContents.attribute(NoteAttributes.headingLevel, at: 0, effectiveRange: nil) as? Int == 2)
editor.insertText("body", replacementRange: editor.selectedRange())
#expect(editor.documentContents.attribute(NoteAttributes.block, at: position + 1, effectiveRange: nil) == nil)
editor.undoManager?.undo()
editor.undoManager?.undo()
#expect(editor.string == "Title")
#expect(editor.documentContents.attribute(NoteAttributes.headingLevel, at: 0, effectiveRange: nil) as? Int == 2)
}
editor.setDocumentContents(NoteDocument.importMarkdown(NSAttributedString(string: "```text\n## Title\n```")))
editor.setSelectedRange(NSRange(location: 0, length: 0))
for character in "# " { editor.insertText(String(character), replacementRange: editor.selectedRange()) }
#expect(editor.string == "# ## Title")
#expect(editor.documentContents.attribute(NoteAttributes.block, at: 0, effectiveRange: nil) as? String == "code")
#expect(editor.documentContents.attribute(NoteAttributes.headingLevel, at: 0, effectiveRange: nil) == nil)
}
@Test @MainActor
func headingCommandsKeepCaretOnTheVisibleBodyLine() throws {
_ = NSApplication.shared
let editor = NotesTextView(frame: NSRect(x: 0, y: 0, width: 600, height: 500))
let window = NSWindow(contentRect: editor.frame, styleMask: .borderless, backing: .buffered, defer: false)
window.contentView = editor
window.makeFirstResponder(editor)
defer { window.orderOut(nil) }
for source in ["\n# Title", "Text\n\n## Title", "Text\n\n\n###### Title"] {
editor.setDocumentContents(NoteDocument.importMarkdown(NSAttributedString(string: source)))
let before = editor.string
editor.setSelectedRange(NSRange(location: (editor.string as NSString).range(of: "Title").location, length: 0))
for command in [#selector(NSResponder.deleteBackward(_:)), #selector(NSResponder.insertNewline(_:))] {
editor.doCommand(by: command)
let title = (editor.string as NSString).range(of: "Title")
#expect(editor.selectedRange() == NSRange(location: title.location, length: 0))
let caretRect = editor.firstRect(forCharacterRange: editor.selectedRange(), actualRange: nil)
let titleRect = editor.firstRect(forCharacterRange: NSRange(location: title.location, length: 1), actualRange: nil)
try #require(titleRect.height > 0)
#expect(caretRect.origin == titleRect.origin)
#expect(caretRect.height == titleRect.height)
}
#expect(editor.string == before)
}
}