| 1 | import Foundation |
| 2 | import XCTest |
| 3 | #if os(macOS) |
| 4 | import AppKit |
| 5 | #endif |
| 6 | @testable import DocsKit |
| 7 | |
| 8 | final class DocumentRenderReconciliationTests: XCTestCase { |
| 9 | @MainActor func testAcknowledgedOptimisticTextUpdatesMappingWithoutReplacingCharacters() { |
| 10 | #if os(macOS) |
| 11 | let storage = NSTextStorage(string: "Hello 😀\n", attributes: [.docsNode: "old", .docsOffset: 0, .font: NSFont.systemFont(ofSize: 12)]) |
| 12 | let observer = StorageObserver() |
| 13 | storage.delegate = observer |
| 14 | let next = NSAttributedString(string: storage.string, attributes: [.docsNode: "published", .docsOffset: 7, .font: NSFont.systemFont(ofSize: 12)]) |
| 15 | DocumentRenderReconciliation.apply(next, to: storage) |
| 16 | XCTAssertEqual(storage.string, next.string) |
| 17 | XCTAssertEqual(storage.attribute(.docsNode, at: 0, effectiveRange: nil) as? String, "published") |
| 18 | XCTAssertEqual(storage.attribute(.docsOffset, at: 0, effectiveRange: nil) as? Int, 7) |
| 19 | XCTAssertFalse(observer.editedCharacters) |
| 20 | XCTAssertTrue(observer.editedAttributes) |
| 21 | #endif |
| 22 | } |
| 23 | @MainActor func testRemoteReplacementPreservesUnicodeAndAllAttributes() { |
| 24 | let storage = NSMutableAttributedString(string: "A😀old Z", attributes: [.docsNode: "old"]) |
| 25 | let next = NSMutableAttributedString(string: "A😁日本語 Z", attributes: [.docsNode: "new"]) |
| 26 | next.addAttribute(.docsOffset, value: 12, range: NSRange(location: 3, length: 3)) |
| 27 | DocumentRenderReconciliation.apply(next, to: storage) |
| 28 | XCTAssertTrue(storage.isEqual(to: next)) |
| 29 | } |
| 30 | #if os(macOS) |
| 31 | @MainActor func testMacLogicalPaperWidthDoesNotReflowAtNarrowZoomedViewport() { |
| 32 | let narrow = DocsPageGeometry(viewportWidth: 350, paged: true) |
| 33 | let wide = DocsPageGeometry(viewportWidth: 1200, paged: true) |
| 34 | XCTAssertEqual(narrow.textWidth, 468) |
| 35 | XCTAssertEqual(wide.textWidth, narrow.textWidth) |
| 36 | XCTAssertEqual(narrow.paperWidth, 612) |
| 37 | } |
| 38 | #endif |
| 39 | } |
| 40 | #if os(macOS) |
| 41 | @MainActor private final class StorageObserver: NSObject, @preconcurrency NSTextStorageDelegate { |
| 42 | var editedCharacters = false |
| 43 | var editedAttributes = false |
| 44 | func textStorage(_ textStorage: NSTextStorage, didProcessEditing editedMask: NSTextStorageEditActions, range editedRange: NSRange, changeInLength delta: Int) { |
| 45 | editedCharacters = editedCharacters || editedMask.contains(.editedCharacters) |
| 46 | editedAttributes = editedAttributes || editedMask.contains(.editedAttributes) |
| 47 | } |
| 48 | } |
| 49 | #endif |
| 50 | |