import AppKit import ConjureKit import Testing @testable import Conjure @Test @MainActor func headingsFollowVisibleParagraphEditingAndRoundTrip() 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) func reset(_ source: String) { editor.setMarkdownContents(NSAttributedString(string: source)) MarkdownHighlighting.apply(to: editor) editor.refreshTasks() editor.breakUndoCoalescing() editor.undoManager?.removeAllActions() let title = (editor.string as NSString).range(of: "Title") editor.setSelectedRange(NSRange(location: title.location, length: 0)) } for (before, after) in [ ("## Title", "## Title"), ("\n## Title", "## Title"), ("Text\n## Title", "TextTitle"), ("# Text\n## Title", "# TextTitle"), ("Text\n\n## Title", "Text\n## Title") ] { reset(before) editor.undoManager?.beginUndoGrouping() editor.deleteBackward(nil) editor.undoManager?.endUndoGrouping() #expect(editor.markdownString == after) if before != after { editor.undoManager?.undo() #expect(editor.markdownString == before) editor.undoManager?.redo() #expect(editor.markdownString == after) } } for level in 1...6 { reset("### Title") editor.undoManager?.beginUndoGrouping() for character in String(repeating: "#", count: level) + " " { editor.insertText(String(character), replacementRange: editor.selectedRange()) } editor.undoManager?.endUndoGrouping() let expected = String(repeating: "#", count: level) + " Title" #expect(editor.markdownString == expected) let title = (editor.string as NSString).range(of: "Title") #expect(editor.selectedRange().location == title.location) let sizes: [CGFloat] = [26, 23, 21, 19, 17, 15] #expect((editor.textStorage?.attribute(.font, at: title.location, effectiveRange: nil) as? NSFont)?.pointSize == sizes[level - 1]) editor.undoManager?.undo() #expect(editor.markdownString == "### Title") editor.undoManager?.redo() #expect(editor.markdownString == expected) #expect(controller.save()) let reloaded = try #require(NotesStore(directory: directory).notes.first) #expect(reloaded.contents.string == expected) } reset("```text\n## Title\n```") editor.insertText("# ", replacementRange: editor.selectedRange()) #expect(editor.markdownString == "```text\n## # Title\n```") }