#if os(macOS) import AppKit import SwiftUI import XCTest @testable import DocsKit @MainActor private final class RenderState { var pending = false var allowed: UInt64 = 1 var selection = NSRange(location: 0, length: 0) var acknowledgements: [UInt64] = [] var commands: [[String: JSONValue]] = [] func candidate(_ content: NSAttributedString, generation: UInt64) -> NativeDocumentTextView { NativeDocumentTextView(content: content, generation: generation, didRender: { self.acknowledgements.append($0) }, compositionChanged: { _ in }, isEditable: true, paged: false, pendingInput: pending, canRender: { !self.pending && self.allowed == $0 }, currentSelection: { self.selection }, selection: Binding(get: { self.selection }, set: { self.selection = $0 }), edit: { self.pending = true; self.commands.append($0); return true }) } } final class NativeRenderTests: XCTestCase { @MainActor func testDelayedCandidateCannotOverwriteAcceptedNativeTyping() { let state = RenderState() let initial = state.candidate(NSAttributedString(string: "a😀b\n"), generation: 1) let coordinator = initial.makeCoordinator() let text = DocsTextView(frame: CGRect(x: 0, y: 0, width: 500, height: 700)) text.delegate = coordinator coordinator.applyRender(initial, to: text) state.allowed = 2 let delayed = state.candidate(NSAttributedString(string: "older server view\n"), generation: 2) XCTAssertFalse(delayed.pendingInput) text.insertText("X", replacementRange: NSRange(location: 4, length: 0)) XCTAssertTrue(state.pending) XCTAssertEqual(text.string, "a😀bX\n") XCTAssertEqual(state.commands.last?["generation"], .number(1)) coordinator.applyRender(delayed, to: text) XCTAssertEqual(text.string, "a😀bX\n") XCTAssertEqual(state.acknowledgements, [1]) state.pending = false; state.allowed = 3 coordinator.applyRender(delayed, to: text) XCTAssertEqual(text.string, "a😀bX\n") state.selection = NSRange(location: 6, length: 0) let current = state.candidate(NSAttributedString(string: "a😀bX!\n"), generation: 3) coordinator.applyRender(current, to: text) XCTAssertEqual(text.string, "a😀bX!\n") XCTAssertEqual(text.selectedRange(), NSRange(location: 6, length: 0)) XCTAssertEqual(state.acknowledgements, [1, 3]) } @MainActor func testMarkedTextSurvivesPeerPresentationAndCommitsOnce() { let state = RenderState() let initial = state.candidate(NSAttributedString(string: "a😀b\n"), generation: 1) let coordinator = initial.makeCoordinator() let text = DocsTextView(frame: CGRect(x: 0, y: 0, width: 500, height: 700)) text.delegate = coordinator text.compositionCommitted = { coordinator.commitComposition(before: $0, after: $1) } coordinator.applyRender(initial, to: text) text.setMarkedText("に", selectedRange: NSRange(location: 1, length: 0), replacementRange: NSRange(location: 1, length: 2)) XCTAssertTrue(text.hasMarkedText()) XCTAssertTrue(state.commands.isEmpty) state.allowed = 2 coordinator.applyRender(state.candidate(NSAttributedString(string: "peer update\n"), generation: 2), to: text) XCTAssertEqual(text.string, "aにb\n") XCTAssertEqual(state.acknowledgements, [1]) text.unmarkText() XCTAssertEqual(state.commands.count, 1) XCTAssertEqual(state.commands.first?["generation"], .number(1)) XCTAssertEqual(state.commands.first?["at"], .number(1)) XCTAssertEqual(state.commands.first?["length"], .number(2)) XCTAssertEqual(state.commands.first?["text"], .string("に")) } @MainActor func testAttributeOnlyRefreshPreservesNativeCaret() { let state = RenderState() let initial = state.candidate(NSAttributedString(string: "abcdef\n"), generation: 1) let coordinator = initial.makeCoordinator() let text = DocsTextView(frame: CGRect(x: 0, y: 0, width: 500, height: 700)) coordinator.applyRender(initial, to: text) text.setSelectedRange(NSRange(location: 4, length: 0)) state.selection = NSRange(location: 0, length: 0) let changed = NSAttributedString(string: "abcdef\n", attributes: [.foregroundColor: NSColor.red]) coordinator.applyRender(state.candidate(changed, generation: 1), to: text) XCTAssertEqual(text.selectedRange(), NSRange(location: 4, length: 0)) XCTAssertEqual(text.textStorage?.attribute(.foregroundColor, at: 0, effectiveRange: nil) as? NSColor, .red) } } #endif