import Foundation import XCTest @testable import DocsKit final class DocumentListCommandTests: XCTestCase { @MainActor func testSelectedListConversionAndLiftSurviveReplacedNodeIDs() async throws { let replica = CRDTReplica(guid: "native-list-command-test") _ = try await replica.open(update: Data()) _ = try await replica.command(["op": .string("insert_element"), "at": .number(0), "type": .string("paragraph")]) let paste = #"{"op":"display_paste","at":0,"length":0,"blocks":[{"type":"bullet_list","children":[{"type":"list_item","children":[{"type":"paragraph","children":[{"type":"text","runs":[{"insert":"first"}]}]}]},{"type":"list_item","children":[{"type":"paragraph","children":[{"type":"text","runs":[{"insert":"second"}]}]}]},{"type":"list_item","children":[{"type":"paragraph","children":[{"type":"text","runs":[{"insert":"third"}]}]}]}]}]}"# _ = try await replica.command(JSONDecoder().decode([String: JSONValue].self, from: Data(paste.utf8))) for type in ["ordered_list", "ordered_list"] { let source = try await replica.presentation(source: 0, selection: NSRange(location: 0, length: 0)) let (generation, projection, _) = try XCTUnwrap(source) let paths = pathsToParagraphs(projection.children).filter { $0.contains { $0.type == "list_item" } } let commands = DocumentCommands.listCommands(type: type, paths: paths) _ = try await replica.command(["op": .string("batch"), "generation": .number(Double(generation)), "commands": .array(commands.map(JSONValue.object))]) await replica.retainGenerations([]) } let final = try await replica.projection() XCTAssertEqual(final.children.map(\.plainText).filter { !$0.isEmpty }, ["first", "second", "third"]) XCTAssertFalse(final.children.contains { ["bullet_list", "ordered_list"].contains($0.type) }) await replica.close() } private func pathsToParagraphs(_ nodes: [CRDTNode], ancestors: [CRDTNode] = []) -> [[CRDTNode]] { nodes.flatMap { node in let path = ancestors + [node] return node.type == "paragraph" ? [path] : pathsToParagraphs(node.children ?? [], ancestors: path) } } }