import XCTest @testable import SaplingEditor #if os(macOS) import AppKit final class MarkdownTextStylerRenderingTests: XCTestCase { func testRenderedHeadingHidesMarkerWhileActiveHeadingKeepsSourceVisible() { let source = "Intro\n# Heading" let renderedStorage = styledStorage(source: source, activeLineIndex: 0) let activeStorage = styledStorage(source: source, activeLineIndex: 1) let headingMarker = (source as NSString).range(of: "#") let headingText = (source as NSString).range(of: "Heading") XCTAssertTrue(isHidden(renderedStorage, at: headingMarker.location)) XCTAssertGreaterThan(font(in: renderedStorage, at: headingText.location).pointSize, 20) XCTAssertFalse(isHidden(activeStorage, at: headingMarker.location)) } func testRenderedInlineMarkdownHidesDelimitersOnly() { let source = "Intro\nThis has **bold**, *italic*, and `code`." let storage = styledStorage(source: source, activeLineIndex: 0) let nsSource = source as NSString XCTAssertTrue(isHidden(storage, at: nsSource.range(of: "**").location)) XCTAssertFalse(isHidden(storage, at: nsSource.range(of: "bold").location)) XCTAssertTrue(isHidden(storage, at: nsSource.range(of: "*italic*").location)) XCTAssertFalse(isHidden(storage, at: nsSource.range(of: "italic").location)) XCTAssertTrue(isHidden(storage, at: nsSource.range(of: "`").location)) XCTAssertNotNil(storage.attribute(.backgroundColor, at: nsSource.range(of: "code").location, effectiveRange: nil)) } func testRenderedTaskListHidesListMarkerAndShowsTaskState() { let source = "Intro\n* [x] Done\n* [ ] Todo" let storage = styledStorage(source: source, activeLineIndex: 0) let nsSource = source as NSString let checkedMarker = nsSource.range(of: "* [x]") let uncheckedMarker = nsSource.range(of: "* [ ]") let doneRange = nsSource.range(of: "Done") XCTAssertTrue(isHidden(storage, at: checkedMarker.location)) XCTAssertFalse(isHidden(storage, at: checkedMarker.location + 2)) XCTAssertTrue(isHidden(storage, at: uncheckedMarker.location)) XCTAssertFalse(isHidden(storage, at: uncheckedMarker.location + 2)) XCTAssertNotNil(storage.attribute(.strikethroughStyle, at: doneRange.location, effectiveRange: nil)) } func testRenderedCodeBlockHidesFencesAndStylesCodeContent() { let source = "Intro\n```swift\nlet value = 42\n```" let storage = styledStorage(source: source, activeLineIndex: 0) let nsSource = source as NSString let openingFence = nsSource.range(of: "```swift") let language = nsSource.range(of: "swift") let code = nsSource.range(of: "let value = 42") let closingFence = nsSource.range(of: "```", options: .backwards) XCTAssertTrue(isHidden(storage, at: openingFence.location)) XCTAssertFalse(isHidden(storage, at: language.location)) XCTAssertNotNil(storage.attribute(.backgroundColor, at: code.location, effectiveRange: nil)) XCTAssertEqual(font(in: storage, at: code.location).fontDescriptor.symbolicTraits.contains(.monoSpace), true) XCTAssertTrue(isHidden(storage, at: closingFence.location)) } func testDirtyCodeBlockRenderingResolvesBlockContext() { let source = "Intro\n```swift\nlet value = 42\n```" let storage = NSTextStorage(string: source) let lineIndex = DocumentLineIndex(source: source) let codeLine = 2 MarkdownTextStyler.apply( to: storage, lineIndex: lineIndex, invalidationPlan: EditorDirtyLineInvalidationPlan( reason: .activeLineChange, isFullRender: false, dirtyLineIndexes: [codeLine] ), activeLineIndex: 0, backgroundColor: .textBackgroundColor, activeLineBackgroundColor: .controlAccentColor.withAlphaComponent(0.10), textColor: .labelColor, secondaryTextColor: .secondaryLabelColor, accentColor: .controlAccentColor ) let code = (source as NSString).range(of: "let value = 42") XCTAssertNotNil(storage.attribute(.backgroundColor, at: code.location, effectiveRange: nil)) XCTAssertEqual(font(in: storage, at: code.location).fontDescriptor.symbolicTraits.contains(.monoSpace), true) } private func styledStorage(source: String, activeLineIndex: Int) -> NSTextStorage { let storage = NSTextStorage(string: source) let lineIndex = DocumentLineIndex(source: source) MarkdownTextStyler.apply( to: storage, lineIndex: lineIndex, invalidationPlan: EditorDirtyLineInvalidator.plan( previousText: nil, currentLineIndex: lineIndex, edit: nil, previousActiveLineIndex: nil, currentActiveLineIndex: activeLineIndex ), activeLineIndex: activeLineIndex, backgroundColor: .textBackgroundColor, activeLineBackgroundColor: .controlAccentColor.withAlphaComponent(0.10), textColor: .labelColor, secondaryTextColor: .secondaryLabelColor, accentColor: .controlAccentColor ) return storage } private func isHidden(_ storage: NSTextStorage, at location: Int) -> Bool { let color = storage.attribute(.foregroundColor, at: location, effectiveRange: nil) as? NSColor return color?.alphaComponent == 0 && font(in: storage, at: location).pointSize < 1 } private func font(in storage: NSTextStorage, at location: Int) -> NSFont { guard let font = storage.attribute(.font, at: location, effectiveRange: nil) as? NSFont else { return NSFont.systemFont(ofSize: 0) } return font } } #endif