@@ -152,6 +152,51 @@ extension Event {
152
152
}
153
153
}
154
154
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
+
155
200
extension Event . AdvancedConsoleOutputRecorder {
156
201
/// Record an event and its context.
157
202
///
@@ -417,7 +462,7 @@ extension Event.AdvancedConsoleOutputRecorder {
417
462
418
463
// Add spacing between top-level modules with vertical line continuation
419
464
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
421
466
}
422
467
}
423
468
}
@@ -522,15 +567,15 @@ extension Event.AdvancedConsoleOutputRecorder {
522
567
// Single root module: no tree prefix, flush left
523
568
treePrefix = " "
524
569
} else if isFirstRoot {
525
- // Multiple roots: first root uses "┌─"
526
- treePrefix = " ┌─ "
570
+ // Multiple roots: first root uses first branch character
571
+ treePrefix = _treeFirstBranch
527
572
} else {
528
573
// Multiple roots: other roots use standard tree characters
529
- treePrefix = isLast ? " ╰─ " : " ├─ "
574
+ treePrefix = isLast ? _treeLastBranch : _treeBranch
530
575
}
531
576
} else {
532
577
// Nested suites: use standard tree characters
533
- treePrefix = isLast ? " ╰─ " : " ├─ "
578
+ treePrefix = isLast ? _treeLastBranch : _treeBranch
534
579
}
535
580
536
581
let suiteName = node. displayName ?? node. name
@@ -548,7 +593,7 @@ extension Event.AdvancedConsoleOutputRecorder {
548
593
}
549
594
} else {
550
595
// Nested case: continue vertical line unless this is the last node
551
- childPrefix = prefix + ( isLast ? " " : " │ " )
596
+ childPrefix = prefix + ( isLast ? " " : " \( _treeVertical ) " )
552
597
}
553
598
554
599
for (childIndex, childID) in node. children. enumerated ( ) {
@@ -567,10 +612,10 @@ extension Event.AdvancedConsoleOutputRecorder {
567
612
if prefix. isEmpty {
568
613
if isSingleRoot {
569
614
// Single root case: use 3 spaces + vertical line
570
- spacingPrefix = " │ "
615
+ spacingPrefix = " \( _treeVertical ) "
571
616
} else {
572
617
// Multiple roots case: use 3 spaces + vertical line
573
- spacingPrefix = " │ "
618
+ spacingPrefix = " \( _treeVertical ) "
574
619
}
575
620
} else {
576
621
// Nested case: use the child prefix
@@ -583,7 +628,7 @@ extension Event.AdvancedConsoleOutputRecorder {
583
628
}
584
629
} else {
585
630
// Test case line
586
- let treePrefix = isLast ? " ╰─ " : " ├─ "
631
+ let treePrefix = isLast ? _treeLastBranch : _treeBranch
587
632
let statusIcon = _getStatusIcon ( for: context. testData [ node. testID] ? . result ?? . passed)
588
633
let testName = node. displayName ?? node. name
589
634
@@ -601,18 +646,18 @@ extension Event.AdvancedConsoleOutputRecorder {
601
646
602
647
// Render issues for failed tests
603
648
if let issues = context. testData [ node. testID] ? . issues, !issues. isEmpty {
604
- let issuePrefix = prefix + ( isLast ? " " : " │ " )
649
+ let issuePrefix = prefix + ( isLast ? " " : " \( _treeVertical ) " )
605
650
for (issueIndex, issue) in issues. enumerated ( ) {
606
651
let isLastIssue = issueIndex == issues. count - 1
607
- let issueTreePrefix = isLastIssue ? " ╰─ " : " ├─ "
652
+ let issueTreePrefix = isLastIssue ? _treeLastBranch : _treeBranch
608
653
let issueIcon = _getStatusIcon ( for: . failed)
609
654
let issueDescription = issue. _error? . description ?? " Test failure "
610
655
611
656
output += " \( issuePrefix) \( issueTreePrefix) \( issueIcon) \( issueDescription) \n "
612
657
613
658
// Add source location
614
659
if let sourceLocation = issue. sourceLocation {
615
- let locationPrefix = issuePrefix + ( isLastIssue ? " " : " │ " )
660
+ let locationPrefix = issuePrefix + ( isLastIssue ? " " : " \( _treeVertical ) " )
616
661
output += " \( locationPrefix) At \( sourceLocation. fileName) : \( sourceLocation. line) : \( sourceLocation. column) \n "
617
662
}
618
663
}
0 commit comments