#if canImport(UIKit) import XCTest import UIKit @testable import RichTextEditorUIKit import RichTextEditorCore @available(iOS 13.0, *) final class DetailsBoxInsertTests: XCTestCase { func test_insertDetailsBlock_onEmptyParagraph_replacesIt_expanded_caretInTitle() { let v = DocumentCanvasView() v.setBlocks([.paragraph(ParagraphBlock(id: BlockID("p")))], width: 320) v.frame = CGRect(x: 0, y: 0, width: 320, height: 600); v.layoutIfNeeded() v.setSelectionForTesting(anchor: v.boxes[0].textStart, head: v.boxes[0].textStart) v.insertDetailsBlock() let blocks = v.currentBlocks() guard blocks.count == 1, case .details(let d) = blocks[0] else { return XCTFail("expected one details block") } XCTAssertTrue(d.expanded) XCTAssertEqual(d.title, []) XCTAssertEqual(d.children.count, 1) // one empty body paragraph guard case .paragraph(let bp) = d.children[0] else { return XCTFail() } XCTAssertTrue(bp.text.isEmpty) let box = v.boxes.first { $0 is DetailsBox } as! DetailsBox XCTAssertEqual(v.head, box.leafRegions().first?.globalStart) // caret at the title start } func test_insertDetailsBlock_recomputesBody_leafRegionsOrdered_navigationDoesNotCrash() { let v = DocumentCanvasView() v.setBlocks([.paragraph(ParagraphBlock(id: BlockID("p")))], width: 320) v.frame = CGRect(x: 0, y: 0, width: 320, height: 600); v.layoutIfNeeded() v.setSelectionForTesting(anchor: v.boxes[0].textStart, head: v.boxes[0].textStart) v.insertDetailsBlock() // The body child's nodeStart must be assigned (DetailsBox.recompute ran via recomputeSpans), so // leaf regions come out in ascending globalStart order — else nextTextPosition builds an inverted // `pos..