| 1 | import Foundation |
| 2 | import XCTest |
| 3 | @testable import DocsKit |
| 4 | |
| 5 | final class CRDTGenerationTests: XCTestCase { |
| 6 | @MainActor func testDelayedPresentationDoesNotReplaceSourceOfQueuedTyping() async throws { |
| 7 | let replica = CRDTReplica(guid: "native-generation-test") |
| 8 | _ = try await replica.open(update: Data()) |
| 9 | _ = try await replica.command(["op": .string("insert_element"), "at": .number(0), "type": .string("paragraph")]) |
| 10 | let first = try await replica.presentation(source: 0, selection: NSRange(location: 0, length: 0)) |
| 11 | let source = try XCTUnwrap(first?.0) |
| 12 | var published = source |
| 13 | let characters = Array("Native text".unicodeScalars) |
| 14 | for (index, scalar) in characters.enumerated() { |
| 15 | _ = try await replica.command(["op": .string("display_edit"), "generation": .number(Double(source)), "at": .number(Double(index)), "length": .number(0), "text": .string(String(scalar))]) |
| 16 | await replica.retainGenerations([source, published]) |
| 17 | let candidate = try await replica.presentation(source: source, selection: NSRange(location: index + 1, length: 0)) |
| 18 | published = try XCTUnwrap(candidate?.0) |
| 19 | // Deliberately withhold render acknowledgement: the next callback |
| 20 | // still belongs to the original displayed source, not this candidate. |
| 21 | } |
| 22 | let final = try await replica.projection() |
| 23 | XCTAssertEqual(final.children.map(\.plainText).joined(), "Native text") |
| 24 | XCTAssertTrue(final.canUndo) |
| 25 | _ = try await replica.command(["op": .string("display_edit"), "generation": .number(Double(published)), "at": .number(11), "length": .number(0), "text": .string(" bold"), "marks": .object(["strong": .object([:])])]) |
| 26 | let formatted = try await replica.projection() |
| 27 | XCTAssertEqual(formatted.children.map(\.plainText).joined(), "Native text bold") |
| 28 | XCTAssertEqual(formatted.children[0].children?.last?.runs?.last?.marks?["strong"], .object([:])) |
| 29 | await replica.close() |
| 30 | } |
| 31 | } |
| 32 | |