@@ -32,7 +32,7 @@ import Foundation
32
32
/// - column: The column where the assertion occurs. The default is the line column you call
33
33
/// this function.
34
34
public func assertInlineSnapshot< Value> (
35
- of value: @autoclosure ( ) throws -> Value ,
35
+ of value: @autoclosure ( ) throws -> Value ? ,
36
36
as snapshotting: Snapshotting < Value , String > ,
37
37
message: @autoclosure ( ) -> String = " " ,
38
38
record isRecording: Bool = isRecording,
@@ -46,41 +46,44 @@ import Foundation
46
46
) {
47
47
let _: Void = installTestObserver
48
48
do {
49
- var actual : String !
49
+ var actual : String ?
50
50
let expectation = XCTestExpectation ( )
51
- try snapshotting. snapshot ( value ( ) ) . run {
52
- actual = $0
53
- expectation. fulfill ( )
54
- }
55
- switch XCTWaiter . wait ( for: [ expectation] , timeout: timeout) {
56
- case . completed:
57
- break
58
- case . timedOut:
59
- XCTFail (
60
- """
61
- Exceeded timeout of \( timeout) seconds waiting for snapshot.
62
-
63
- This can happen when an asynchronously loaded value (like a network response) has not \
64
- loaded. If a timeout is unavoidable, consider setting the " timeout " parameter of
65
- " assertInlineSnapshot " to a higher value.
66
- """ ,
67
- file: file,
68
- line: line
69
- )
70
- return
71
- case . incorrectOrder, . interrupted, . invertedFulfillment:
72
- XCTFail ( " Couldn't snapshot value " , file: file, line: line)
73
- return
74
- @unknown default :
75
- XCTFail ( " Couldn't snapshot value " , file: file, line: line)
76
- return
51
+ if let value = try value ( ) {
52
+ snapshotting. snapshot ( value) . run {
53
+ actual = $0
54
+ expectation. fulfill ( )
55
+ }
56
+ switch XCTWaiter . wait ( for: [ expectation] , timeout: timeout) {
57
+ case . completed:
58
+ break
59
+ case . timedOut:
60
+ XCTFail (
61
+ """
62
+ Exceeded timeout of \( timeout) seconds waiting for snapshot.
63
+
64
+ This can happen when an asynchronously loaded value (like a network response) has not \
65
+ loaded. If a timeout is unavoidable, consider setting the " timeout " parameter of
66
+ " assertInlineSnapshot " to a higher value.
67
+ """ ,
68
+ file: file,
69
+ line: line
70
+ )
71
+ return
72
+ case . incorrectOrder, . interrupted, . invertedFulfillment:
73
+ XCTFail ( " Couldn't snapshot value " , file: file, line: line)
74
+ return
75
+ @unknown default :
76
+ XCTFail ( " Couldn't snapshot value " , file: file, line: line)
77
+ return
78
+ }
77
79
}
78
- guard !isRecording, let expected = expected ? ( )
80
+ let expected = expected ? ( )
81
+ guard !isRecording, let expected
79
82
else {
80
83
// NB: Write snapshot state before calling `XCTFail` in case `continueAfterFailure = false`
81
84
inlineSnapshotState [ File ( path: file) , default: [ ] ] . append (
82
85
InlineSnapshot (
83
- expected: expected ? ( ) ,
86
+ expected: expected,
84
87
actual: actual,
85
88
wasRecording: isRecording,
86
89
syntaxDescriptor: syntaxDescriptor,
@@ -100,9 +103,7 @@ import Foundation
100
103
Automatically recorded a new snapshot for " \( syntaxDescriptor. trailingClosureLabel) " .
101
104
"""
102
105
}
103
- if let expected = expected ? ( ) ,
104
- let difference = snapshotting. diffing. diff ( expected, actual) ? . 0
105
- {
106
+ if let difference = snapshotting. diffing. diff ( expected ?? " " , actual ?? " " ) ? . 0 {
106
107
failure += " Difference: … \n \n \( difference. indenting ( by: 2 ) ) "
107
108
}
108
109
XCTFail (
@@ -116,7 +117,7 @@ import Foundation
116
117
)
117
118
return
118
119
}
119
- guard let difference = snapshotting. diffing. diff ( expected, actual) ? . 0
120
+ guard let difference = snapshotting. diffing. diff ( expected, actual ?? " " ) ? . 0
120
121
else { return }
121
122
122
123
let message = message ( )
@@ -304,7 +305,7 @@ public struct InlineSnapshotSyntaxDescriptor: Hashable {
304
305
305
306
private struct InlineSnapshot : Hashable {
306
307
var expected : String ?
307
- var actual : String
308
+ var actual : String ?
308
309
var wasRecording : Bool
309
310
var syntaxDescriptor : InlineSnapshotSyntaxDescriptor
310
311
var function : String
@@ -388,8 +389,8 @@ public struct InlineSnapshotSyntaxDescriptor: Hashable {
388
389
self . wasRecording = snapshots. first? . wasRecording ?? isRecording
389
390
self . indent = String (
390
391
sourceLocationConverter. sourceLines
391
- . first ( where : { $0. first? . isWhitespace == true && $0 != " \n " } ) ?
392
- . prefix ( while : { $0. isWhitespace } )
392
+ . first { $0. first? . isWhitespace == true && $0. contains { !$0 . isWhitespace } } ?
393
+ . prefix { $0. isWhitespace }
393
394
?? " "
394
395
)
395
396
self . snapshots = snapshots
@@ -421,40 +422,42 @@ public struct InlineSnapshotSyntaxDescriptor: Hashable {
421
422
. prefix ( while: { $0 == " " || $0 == " \t " } )
422
423
)
423
424
let delimiter = String (
424
- repeating: " # " , count: snapshot. actual. hashCount ( isMultiline: true )
425
+ repeating: " # " , count: ( snapshot. actual ?? " " ) . hashCount ( isMultiline: true )
425
426
)
426
427
let leadingIndent = leadingTrivia + self . indent
427
428
let snapshotLabel = TokenSyntax (
428
429
stringLiteral: snapshot. syntaxDescriptor. trailingClosureLabel
429
430
)
430
- let snapshotClosure = ClosureExprSyntax (
431
- leftBrace: . leftBraceToken( trailingTrivia: . newline) ,
432
- statements: CodeBlockItemListSyntax {
433
- StringLiteralExprSyntax (
434
- leadingTrivia: Trivia ( stringLiteral: leadingIndent) ,
435
- openingPounds: . rawStringPoundDelimiter( delimiter) ,
436
- openingQuote: . multilineStringQuoteToken( trailingTrivia: . newline) ,
437
- segments: [
438
- . stringSegment(
439
- StringSegmentSyntax (
440
- content: . stringSegment(
441
- snapshot. actual
442
- . replacingOccurrences ( of: " \r " , with: #"\ \#( delimiter) r"# )
443
- . indenting ( with: leadingIndent)
431
+ let snapshotClosure = snapshot. actual. map { actual in
432
+ ClosureExprSyntax (
433
+ leftBrace: . leftBraceToken( trailingTrivia: . newline) ,
434
+ statements: CodeBlockItemListSyntax {
435
+ StringLiteralExprSyntax (
436
+ leadingTrivia: Trivia ( stringLiteral: leadingIndent) ,
437
+ openingPounds: . rawStringPoundDelimiter( delimiter) ,
438
+ openingQuote: . multilineStringQuoteToken( trailingTrivia: . newline) ,
439
+ segments: [
440
+ . stringSegment(
441
+ StringSegmentSyntax (
442
+ content: . stringSegment(
443
+ actual
444
+ . replacingOccurrences ( of: " \r " , with: #"\ \#( delimiter) r"# )
445
+ . indenting ( with: leadingIndent)
446
+ )
444
447
)
445
448
)
446
- )
447
- ] ,
448
- closingQuote: . multilineStringQuoteToken(
449
- leadingTrivia: . newline + Trivia( stringLiteral: leadingIndent)
450
- ) ,
451
- closingPounds: . rawStringPoundDelimiter( delimiter)
449
+ ] ,
450
+ closingQuote: . multilineStringQuoteToken(
451
+ leadingTrivia: . newline + Trivia( stringLiteral: leadingIndent)
452
+ ) ,
453
+ closingPounds: . rawStringPoundDelimiter( delimiter)
454
+ )
455
+ } ,
456
+ rightBrace: . rightBraceToken(
457
+ leadingTrivia: . newline + Trivia( stringLiteral: leadingTrivia)
452
458
)
453
- } ,
454
- rightBrace: . rightBraceToken(
455
- leadingTrivia: . newline + Trivia( stringLiteral: leadingTrivia)
456
459
)
457
- )
460
+ }
458
461
459
462
let arguments = functionCallExpr. arguments
460
463
let firstTrailingClosureOffset =
@@ -475,23 +478,41 @@ public struct InlineSnapshotSyntaxDescriptor: Hashable {
475
478
switch centeredTrailingClosureOffset {
476
479
case ..< 0 :
477
480
let index = arguments. index ( arguments. startIndex, offsetBy: trailingClosureOffset)
478
- functionCallExpr. arguments [ index] . label = snapshotLabel
479
- functionCallExpr. arguments [ index] . expression = ExprSyntax ( snapshotClosure)
481
+ if let snapshotClosure {
482
+ functionCallExpr. arguments [ index] . label = snapshotLabel
483
+ functionCallExpr. arguments [ index] . expression = ExprSyntax ( snapshotClosure)
484
+ } else {
485
+ functionCallExpr. arguments. remove ( at: index)
486
+ }
480
487
481
488
case 0 :
482
489
if snapshot. wasRecording || functionCallExpr. trailingClosure == nil {
483
490
functionCallExpr. rightParen? . trailingTrivia = . space
484
- functionCallExpr. trailingClosure = snapshotClosure
491
+ if let snapshotClosure {
492
+ functionCallExpr. trailingClosure = snapshotClosure // FIXME: ?? multipleTrailingClosures.removeFirst()
493
+ } else if !functionCallExpr. additionalTrailingClosures. isEmpty {
494
+ let additionalTrailingClosure = functionCallExpr. additionalTrailingClosures. remove (
495
+ at: functionCallExpr. additionalTrailingClosures. startIndex
496
+ )
497
+ functionCallExpr. trailingClosure = additionalTrailingClosure. closure
498
+ } else {
499
+ functionCallExpr. rightParen? . trailingTrivia = " "
500
+ functionCallExpr. trailingClosure = nil
501
+ }
485
502
} else {
486
503
fatalError ( )
487
504
}
488
505
489
506
case 1 ... :
490
- var newElement : MultipleTrailingClosureElementSyntax {
491
- MultipleTrailingClosureElementSyntax (
492
- label: snapshotLabel,
493
- closure: snapshotClosure. with ( \. leadingTrivia, snapshotClosure. leadingTrivia + . space)
494
- )
507
+ var newElement : MultipleTrailingClosureElementSyntax ? {
508
+ snapshotClosure. map { snapshotClosure in
509
+ MultipleTrailingClosureElementSyntax (
510
+ label: snapshotLabel,
511
+ closure: snapshotClosure. with (
512
+ \. leadingTrivia, snapshotClosure. leadingTrivia + . space
513
+ )
514
+ )
515
+ }
495
516
}
496
517
497
518
if !functionCallExpr. additionalTrailingClosures. isEmpty,
@@ -510,16 +531,22 @@ public struct InlineSnapshotSyntaxDescriptor: Hashable {
510
531
functionCallExpr. additionalTrailingClosures [ index] . label. text
511
532
) {
512
533
if snapshot. wasRecording {
513
- functionCallExpr. additionalTrailingClosures [ index] . label = snapshotLabel
514
- functionCallExpr. additionalTrailingClosures [ index] . closure = snapshotClosure
534
+ if let snapshotClosure {
535
+ functionCallExpr. additionalTrailingClosures [ index] . label = snapshotLabel
536
+ functionCallExpr. additionalTrailingClosures [ index] . closure = snapshotClosure
537
+ } else {
538
+ functionCallExpr. additionalTrailingClosures. remove ( at: index)
539
+ }
515
540
}
516
- } else {
541
+ } else if let newElement,
542
+ snapshot. wasRecording || index == functionCallExpr. additionalTrailingClosures. endIndex
543
+ {
517
544
functionCallExpr. additionalTrailingClosures. insert (
518
545
newElement. with ( \. trailingTrivia, . space) ,
519
546
at: index
520
547
)
521
548
}
522
- } else if centeredTrailingClosureOffset >= 1 {
549
+ } else if centeredTrailingClosureOffset >= 1 , let newElement {
523
550
if let index = functionCallExpr. additionalTrailingClosures. index (
524
551
functionCallExpr. additionalTrailingClosures. endIndex,
525
552
offsetBy: - 1 ,
0 commit comments