import XCTest @testable import SaplingEditor final class EditorScrollStabilityTests: XCTestCase { func testRenderMetricsCountScrollRestorationDuringDirtyEdit() { var instrumentation = EditorInstrumentationSnapshot() instrumentation.recordRenderPass(EditorRenderPassMetric( reason: .sourceChange, durationMilliseconds: 0.4, characterCount: 1_200, lineCount: 80, dirtyLineCount: 3, activeLineIndex: 40, isFullRender: false, restoredScrollPosition: true )) XCTAssertEqual(instrumentation.renderPassCount, 1) XCTAssertEqual(instrumentation.fullRenderCount, 0) XCTAssertEqual(instrumentation.totalDirtyLineCount, 3) XCTAssertEqual(instrumentation.lastDirtyLineCount, 3) XCTAssertEqual(instrumentation.scrollRestorationCount, 1) } func testRapidNavigationSchedulesOnlyOldAndNewActiveLines() { let source = (1...500).map { "Line \($0)" }.joined(separator: "\n") let firstJump = EditorDirtyLineInvalidator.plan( previousText: source, currentText: source, previousActiveLineIndex: 10, currentActiveLineIndex: 250 ) let secondJump = EditorDirtyLineInvalidator.plan( previousText: source, currentText: source, previousActiveLineIndex: 250, currentActiveLineIndex: 490 ) XCTAssertEqual(firstJump.dirtyLineIndexes, [10, 250]) XCTAssertEqual(secondJump.dirtyLineIndexes, [250, 490]) XCTAssertFalse(firstJump.isFullRender) XCTAssertFalse(secondJump.isFullRender) } func testEditingWhileScrolledDoesNotRequireFullRender() { let previous = (1...200).map { "Line \($0)" }.joined(separator: "\n") var lines = previous.components(separatedBy: "\n") lines[120] = "Line 121 edited while viewport is away from the top" let current = lines.joined(separator: "\n") let plan = EditorDirtyLineInvalidator.plan( previousText: previous, currentText: current, previousActiveLineIndex: 120, currentActiveLineIndex: 120 ) var instrumentation = EditorInstrumentationSnapshot() instrumentation.recordRenderPass(EditorRenderPassMetric( reason: plan.reason, durationMilliseconds: 0.5, characterCount: current.utf16.count, lineCount: 200, dirtyLineCount: plan.dirtyLineCount, activeLineIndex: 120, isFullRender: plan.isFullRender, restoredScrollPosition: true )) XCTAssertEqual(plan.reason, .sourceChange) XCTAssertFalse(plan.isFullRender) XCTAssertEqual(plan.dirtyLineIndexes, [119, 120, 121]) XCTAssertEqual(instrumentation.scrollRestorationCount, 1) } }