feat: delete the selected note
This commit is contained in:
@@ -1495,3 +1495,82 @@ func notesFormattingPreservesAttachmentsMarkdownSelectionAndUndo() throws {
|
||||
#expect(saved.contents.string == before)
|
||||
#expect(saved.contents.attribute(.attachment, at: saved.contents.length - 1, effectiveRange: nil) is NSTextAttachment)
|
||||
}
|
||||
|
||||
@Test @MainActor
|
||||
func notesDeletionRequiresConfirmationPreservesFailuresAndReplacesTheLastNote() async throws {
|
||||
_ = NSApplication.shared
|
||||
let suite = "NotesDeletion.\(UUID().uuidString)"
|
||||
let defaults = try #require(UserDefaults(suiteName: suite))
|
||||
let directory = FileManager.default.temporaryDirectory.appendingPathComponent(suite)
|
||||
let backup = directory.appendingPathExtension("backup")
|
||||
defer {
|
||||
defaults.removePersistentDomain(forName: suite)
|
||||
try? FileManager.default.removeItem(at: directory)
|
||||
try? FileManager.default.removeItem(at: backup)
|
||||
}
|
||||
let store = try NotesStore(directory: directory)
|
||||
let first = try store.create()
|
||||
let second = try store.create()
|
||||
try store.update(id: first, contents: NSAttributedString(string: "Delete this note"))
|
||||
try store.update(id: second, contents: NSAttributedString(string: "Keep this note"))
|
||||
defaults.set(first.uuidString, forKey: "selectedNote")
|
||||
let controller = NotesController(defaults: defaults, directory: directory)
|
||||
let window = NSWindow(contentRect: NSRect(x: 0, y: 0, width: 570, height: 404),
|
||||
styleMask: [.titled], backing: .buffered, defer: false)
|
||||
window.contentViewController = controller
|
||||
defer { _ = controller.close(); window.orderOut(nil) }
|
||||
window.orderFront(nil)
|
||||
func descendants(_ view: NSView) -> [NSView] { [view] + view.subviews.flatMap(descendants) }
|
||||
let editor = try #require(descendants(controller.view).compactMap { $0 as? NotesTextView }.first)
|
||||
func delete(confirm: Bool) async throws {
|
||||
controller.showActions()
|
||||
let search = try #require(descendants(controller.view).compactMap { $0 as? NSSearchField }.first)
|
||||
search.stringValue = "delete"
|
||||
search.delegate?.controlTextDidChange?(Notification(name: NSControl.textDidChangeNotification, object: search))
|
||||
#expect(controller.handle(#selector(NSResponder.insertNewline(_:))))
|
||||
let sheet = try #require(window.attachedSheet)
|
||||
let content = try #require(sheet.contentView)
|
||||
let button = try #require(descendants(content).compactMap { $0 as? NSButton }
|
||||
.first { $0.title == (confirm ? "Delete Note" : "Cancel") })
|
||||
button.performClick(nil)
|
||||
// Scheduling deadline only; deletion does not depend on wall-clock time.
|
||||
for _ in 0..<1_000 {
|
||||
if window.attachedSheet == nil, window.firstResponder === editor { return }
|
||||
try await Task.sleep(for: .milliseconds(1))
|
||||
}
|
||||
try #require(window.attachedSheet == nil && window.firstResponder === editor)
|
||||
}
|
||||
try await delete(confirm: false)
|
||||
#expect(defaults.string(forKey: "selectedNote") == first.uuidString)
|
||||
#expect(editor.markdownString == "Delete this note")
|
||||
#expect(Set(try NotesStore(directory: directory).notes.map(\.id)) == Set([first, second]))
|
||||
|
||||
// A failed save must not let confirmation discard an unsaved buffer.
|
||||
try FileManager.default.moveItem(at: directory, to: backup)
|
||||
try Data("Blocked storage".utf8).write(to: directory)
|
||||
editor.insertText("Unsaved changes", replacementRange: NSRange(location: 0, length: editor.string.utf16.count))
|
||||
try await delete(confirm: true)
|
||||
#expect(defaults.string(forKey: "selectedNote") == first.uuidString)
|
||||
#expect(editor.markdownString == "Unsaved changes")
|
||||
#expect(!controller.save())
|
||||
try FileManager.default.removeItem(at: directory)
|
||||
try FileManager.default.moveItem(at: backup, to: directory)
|
||||
#expect(try NotesStore(directory: directory).notes.first { $0.id == first }?.contents.string == "Delete this note")
|
||||
|
||||
try await delete(confirm: true)
|
||||
#expect(try NotesStore(directory: directory).notes.map(\.id) == [second])
|
||||
#expect(defaults.string(forKey: "selectedNote") == second.uuidString)
|
||||
#expect(editor.markdownString == "Keep this note")
|
||||
#expect(editor.undoManager?.canUndo != true)
|
||||
try await delete(confirm: true)
|
||||
let replacement = try #require(NotesStore(directory: directory).notes.first)
|
||||
#expect(replacement.id != first && replacement.id != second)
|
||||
#expect(replacement.contents.string.isEmpty)
|
||||
#expect(defaults.string(forKey: "selectedNote") == replacement.id.uuidString)
|
||||
#expect(editor.markdownString.isEmpty)
|
||||
#expect(editor.isEditable)
|
||||
#expect(editor.undoManager?.canUndo != true)
|
||||
let reopened = NotesController(defaults: defaults, directory: directory)
|
||||
#expect(descendants(reopened.view).compactMap { $0 as? NotesTextView }.first?.markdownString == "")
|
||||
#expect(defaults.string(forKey: "selectedNote") == replacement.id.uuidString)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user