Skip to content

Commit e0099f6

Browse files
committed
feat: Add ASCII fallback for Unicode box-drawing characters
1 parent 1e133ab commit e0099f6

File tree

2 files changed

+60
-14
lines changed

2 files changed

+60
-14
lines changed

Sources/Testing/Events/Recorder/Event.AdvancedConsoleOutputRecorder.swift

Lines changed: 57 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,51 @@ extension Event {
152152
}
153153
}
154154

155+
// MARK: - ASCII Fallback Support
156+
157+
extension Event.AdvancedConsoleOutputRecorder {
158+
/// Get the appropriate tree drawing character with ASCII fallback.
159+
///
160+
/// - Parameters:
161+
/// - unicode: The Unicode box-drawing character to use.
162+
/// - ascii: The ASCII fallback character(s) to use.
163+
///
164+
/// - Returns: The appropriate character based on terminal capabilities.
165+
private func _treeCharacter(unicode: String, ascii: String) -> String {
166+
// Use ASCII fallback on Windows or when ANSI escape codes are disabled
167+
// This follows the same pattern as Event.Symbol
168+
#if os(Windows)
169+
return ascii
170+
#else
171+
if options.base.useANSIEscapeCodes {
172+
return unicode
173+
} else {
174+
return ascii
175+
}
176+
#endif
177+
}
178+
179+
/// Get the tree branch character (├─).
180+
private var _treeBranch: String {
181+
_treeCharacter(unicode: "├─ ", ascii: "|- ")
182+
}
183+
184+
/// Get the tree last branch character (╰─).
185+
private var _treeLastBranch: String {
186+
_treeCharacter(unicode: "╰─ ", ascii: "`- ")
187+
}
188+
189+
/// Get the tree first branch character (┌─).
190+
private var _treeFirstBranch: String {
191+
_treeCharacter(unicode: "┌─ ", ascii: ".- ")
192+
}
193+
194+
/// Get the tree vertical line character (│).
195+
private var _treeVertical: String {
196+
_treeCharacter(unicode: "", ascii: "|")
197+
}
198+
}
199+
155200
extension Event.AdvancedConsoleOutputRecorder {
156201
/// Record an event and its context.
157202
///
@@ -417,7 +462,7 @@ extension Event.AdvancedConsoleOutputRecorder {
417462

418463
// Add spacing between top-level modules with vertical line continuation
419464
if index < context.rootNodes.count - 1 {
420-
output += "\n" // Add vertical line continuation between modules
465+
output += "\(_treeVertical)\n" // Add vertical line continuation between modules
421466
}
422467
}
423468
}
@@ -522,15 +567,15 @@ extension Event.AdvancedConsoleOutputRecorder {
522567
// Single root module: no tree prefix, flush left
523568
treePrefix = ""
524569
} else if isFirstRoot {
525-
// Multiple roots: first root uses "┌─"
526-
treePrefix = "┌─ "
570+
// Multiple roots: first root uses first branch character
571+
treePrefix = _treeFirstBranch
527572
} else {
528573
// Multiple roots: other roots use standard tree characters
529-
treePrefix = isLast ? "╰─ " : "├─ "
574+
treePrefix = isLast ? _treeLastBranch : _treeBranch
530575
}
531576
} else {
532577
// Nested suites: use standard tree characters
533-
treePrefix = isLast ? "╰─ " : "├─ "
578+
treePrefix = isLast ? _treeLastBranch : _treeBranch
534579
}
535580

536581
let suiteName = node.displayName ?? node.name
@@ -548,7 +593,7 @@ extension Event.AdvancedConsoleOutputRecorder {
548593
}
549594
} else {
550595
// Nested case: continue vertical line unless this is the last node
551-
childPrefix = prefix + (isLast ? " " : " ")
596+
childPrefix = prefix + (isLast ? " " : "\(_treeVertical) ")
552597
}
553598

554599
for (childIndex, childID) in node.children.enumerated() {
@@ -567,10 +612,10 @@ extension Event.AdvancedConsoleOutputRecorder {
567612
if prefix.isEmpty {
568613
if isSingleRoot {
569614
// Single root case: use 3 spaces + vertical line
570-
spacingPrefix = " "
615+
spacingPrefix = " \(_treeVertical)"
571616
} else {
572617
// Multiple roots case: use 3 spaces + vertical line
573-
spacingPrefix = " "
618+
spacingPrefix = " \(_treeVertical)"
574619
}
575620
} else {
576621
// Nested case: use the child prefix
@@ -583,7 +628,7 @@ extension Event.AdvancedConsoleOutputRecorder {
583628
}
584629
} else {
585630
// Test case line
586-
let treePrefix = isLast ? "╰─ " : "├─ "
631+
let treePrefix = isLast ? _treeLastBranch : _treeBranch
587632
let statusIcon = _getStatusIcon(for: context.testData[node.testID]?.result ?? .passed)
588633
let testName = node.displayName ?? node.name
589634

@@ -601,18 +646,18 @@ extension Event.AdvancedConsoleOutputRecorder {
601646

602647
// Render issues for failed tests
603648
if let issues = context.testData[node.testID]?.issues, !issues.isEmpty {
604-
let issuePrefix = prefix + (isLast ? " " : " ")
649+
let issuePrefix = prefix + (isLast ? " " : "\(_treeVertical) ")
605650
for (issueIndex, issue) in issues.enumerated() {
606651
let isLastIssue = issueIndex == issues.count - 1
607-
let issueTreePrefix = isLastIssue ? "╰─ " : "├─ "
652+
let issueTreePrefix = isLastIssue ? _treeLastBranch : _treeBranch
608653
let issueIcon = _getStatusIcon(for: .failed)
609654
let issueDescription = issue._error?.description ?? "Test failure"
610655

611656
output += "\(issuePrefix)\(issueTreePrefix)\(issueIcon) \(issueDescription)\n"
612657

613658
// Add source location
614659
if let sourceLocation = issue.sourceLocation {
615-
let locationPrefix = issuePrefix + (isLastIssue ? " " : " ")
660+
let locationPrefix = issuePrefix + (isLastIssue ? " " : "\(_treeVertical) ")
616661
output += "\(locationPrefix)At \(sourceLocation.fileName):\(sourceLocation.line):\(sourceLocation.column)\n"
617662
}
618663
}

Tests/TestingTests/AdvancedConsoleOutputRecorderTests.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,9 @@ struct AdvancedConsoleOutputRecorderTests {
8686
#expect(buffer.contains("HIERARCHICAL TEST RESULTS"))
8787
#expect(buffer.contains("completed"))
8888

89-
// Should contain tree structure characters
90-
#expect(buffer.contains("├─") || buffer.contains("╰─") || buffer.contains("┌─"))
89+
// Should contain tree structure characters (Unicode or ASCII fallback)
90+
#expect(buffer.contains("├─") || buffer.contains("╰─") || buffer.contains("┌─") ||
91+
buffer.contains("|-") || buffer.contains("`-") || buffer.contains(".-"))
9192
}
9293

9394
@Test("Failed test details are properly formatted")

0 commit comments

Comments
 (0)