Files
TUIkit/Tests/TUIkitTests/ModalPresentationModifierTests.swift
T
phranck ac5e63eb84 Feat: Add ButtonRole, horizontal Alert buttons, ESC dismiss, arrow key navigation
- Add ButtonRole (.cancel, .destructive) with SwiftUI-conformant API
- Alert renders buttons horizontally, sorted by role (cancel left)
- Alert max width capped at 60 characters for readability
- ESC key dismisses alerts via AlertPresentationModifier handler
- FocusManager: Left/Right arrows navigate within sections (like Up/Down)
- Move completed plans to done/ directory
- Update example pages with minor adjustments
2026-02-08 23:00:59 +01:00

173 lines
5.1 KiB
Swift

// 🖥️ TUIKit — Terminal UI Kit for Swift
// ModalPresentationModifierTests.swift
//
// Created by LAYERED.work
// License: MIT dimming, centering, and arbitrary content.
//
import Testing
@testable import TUIkit
@MainActor
@Suite("ModalPresentationModifier Tests")
struct ModalPresentationModifierTests {
/// Helper to create a RenderContext with default test settings.
private func testContext() -> RenderContext {
RenderContext(
availableWidth: 80,
availableHeight: 24,
tuiContext: TUIContext()
)
}
/// Helper to render a view to a FrameBuffer.
private func render<V: View>(_ view: V) -> FrameBuffer {
renderToBuffer(view, context: testContext())
}
@Test("Modal not presented shows only base content")
func notPresentedShowsBase() {
let isPresented = Binding.constant(false)
let view = Text("Base Content")
.modal(isPresented: isPresented) {
Text("Modal Content")
}
let buffer = render(view)
let content = buffer.lines.joined(separator: "\n").stripped
#expect(content.contains("Base Content"))
#expect(!content.contains("Modal Content"))
}
@Test("Modal presented shows dimmed base with modal overlay")
func presentedShowsModal() {
let isPresented = Binding.constant(true)
let view = VStack {
Text("Base Content")
Text("More base text")
}
.modal(isPresented: isPresented) {
Text("Modal Content")
}
let buffer = render(view)
// Modal content should be present
let stripped = buffer.lines.joined(separator: "\n").stripped
#expect(stripped.contains("Modal Content"))
// Should have ANSI codes (from dimmed base and compositing)
let rawContent = buffer.lines.joined(separator: "\n")
#expect(rawContent.contains("\u{1B}[")) // Contains ANSI codes
}
@Test("Modal accepts arbitrary view content")
func arbitraryContent() {
let isPresented = Binding.constant(true)
let view = Text("Base")
.modal(isPresented: isPresented) {
VStack {
Text("Line 1")
Text("Line 2")
Text("Line 3")
}
}
let buffer = render(view)
let content = buffer.lines.joined(separator: "\n").stripped
#expect(content.contains("Line 1"))
#expect(content.contains("Line 2"))
#expect(content.contains("Line 3"))
}
@Test("Modal works with Dialog view")
func modalWithDialog() {
let isPresented = Binding.constant(true)
let view = Text("Base")
.modal(isPresented: isPresented) {
Dialog(title: "Settings") {
Text("Option 1")
Text("Option 2")
}
}
let buffer = render(view)
let content = buffer.lines.joined(separator: "\n").stripped
#expect(content.contains("Settings"))
#expect(content.contains("Option 1"))
#expect(content.contains("Option 2"))
}
@Test("Modal works with Alert view")
func modalWithAlert() {
let isPresented = Binding.constant(true)
let view = Text("Base")
.modal(isPresented: isPresented) {
Alert(title: "Warning", message: "Sure?") {
Button("Yes") { }
}
}
let buffer = render(view)
let content = buffer.lines.joined(separator: "\n").stripped
#expect(content.contains("Warning"))
#expect(content.contains("Sure?"))
#expect(content.contains("Yes"))
}
@Test("Toggle isPresented switches between states")
func togglePresentation() {
@State var showModal = false
let view1 = Text("Content")
.modal(isPresented: $showModal) {
Text("Modal")
}
let buffer1 = render(view1)
let content1 = buffer1.lines.joined(separator: "\n").stripped
// Initially not shown
#expect(!content1.contains("Modal"))
// Toggle to show
showModal = true
let view2 = Text("Content")
.modal(isPresented: $showModal) {
Text("Modal")
}
let buffer2 = render(view2)
let content2 = buffer2.lines.joined(separator: "\n").stripped
// Now shown
#expect(content2.contains("Modal"))
}
@Test("Modal centers content over base")
func centersContent() {
let isPresented = Binding.constant(true)
let view = VStack {
Text("Wide base content that spans multiple characters")
Text("Another line of wide content here")
}
.modal(isPresented: isPresented) {
Text("Small")
}
let buffer = render(view)
// Modal should be rendered (non-empty, contains both base and modal)
#expect(!buffer.isEmpty)
let content = buffer.lines.joined(separator: "\n").stripped
#expect(content.contains("Wide base content"))
#expect(content.contains("Small"))
}
}