diff --git a/Sources/Rules/NoGuardInTests.swift b/Sources/Rules/NoGuardInTests.swift index 933e274c..fa8a4a81 100644 --- a/Sources/Rules/NoGuardInTests.swift +++ b/Sources/Rules/NoGuardInTests.swift @@ -75,6 +75,17 @@ public extension FormatRule { guard isValidElseBlock else { continue } + // Preserve the assertion message (if any) + let assertionMessage: [Token] = { + guard let startIndex = formatter.index(of: .startOfScope("("), after: elseBraceIndex), + let endIndex = formatter.endOfScope(at: startIndex), + formatter.index(after: startIndex, where: { $0.isStringDelimiter }) != nil + else { + return [] + } + return [.delimiter(","), .space(" ")] + formatter.tokens[startIndex + 1 ..< endIndex] + }() + // Check for variable shadowing let scopeStart = bodyRange.lowerBound let searchRange = scopeStart ..< guardIndex @@ -174,6 +185,7 @@ public extension FormatRule { .startOfScope("("), ]) replacementStatements.append(contentsOf: expressionTokens) + replacementStatements.append(contentsOf: assertionMessage) replacementStatements.append(.endOfScope(")")) addedTryStatement = true @@ -183,6 +195,7 @@ public extension FormatRule { replacementStatements.append(.identifier(assertFunctionName)) replacementStatements.append(.startOfScope("(")) replacementStatements.append(contentsOf: conditionTokens) + replacementStatements.append(contentsOf: assertionMessage) replacementStatements.append(.endOfScope(")")) case .patternMatching: diff --git a/Tests/Rules/NoGuardInTestsTests.swift b/Tests/Rules/NoGuardInTestsTests.swift index 949a21bb..84339bfd 100644 --- a/Tests/Rules/NoGuardInTestsTests.swift +++ b/Tests/Rules/NoGuardInTestsTests.swift @@ -56,7 +56,7 @@ final class NoGuardInTestsTests: XCTestCase { class TestCase: XCTestCase { func test_something() throws { - let value = try XCTUnwrap(optionalValue) + let value = try XCTUnwrap(optionalValue, "Expected value to be non-nil") } } """ @@ -264,6 +264,78 @@ final class NoGuardInTestsTests: XCTestCase { testFormatting(for: input, output, rule: .noGuardInTests, exclude: [.blankLinesAfterGuardStatements]) } + func testPreserveFailMessage() { + let input = """ + import XCTest + + class TestCase: XCTestCase { + func test_something() { + guard let value1 = optionalValue1 else { + XCTFail("Failed") + return + } + guard optionalValue2 != nil else { + XCTFail("Value was nil") + return + } + } + } + """ + let output = """ + import XCTest + + class TestCase: XCTestCase { + func test_something() throws { + let value1 = try XCTUnwrap(optionalValue1, "Failed") + XCTAssert(optionalValue2 != nil, "Value was nil") + } + } + """ + testFormatting(for: input, output, rule: .noGuardInTests, exclude: [.blankLinesAfterGuardStatements]) + } + + func testPreserveFailMessageWithInterpolations() { + let input = """ + import XCTest + + class TestCase: XCTestCase { + func test_something() { + guard optionalValue2 == nil else { + XCTFail("Value was \\(String(describing: optionalValue2))") + return + } + } + } + """ + let output = """ + import XCTest + + class TestCase: XCTestCase { + func test_something() { + XCTAssert(optionalValue2 == nil, "Value was \\(String(describing: optionalValue2))") + } + } + """ + testFormatting(for: input, output, rule: .noGuardInTests, exclude: [.blankLinesAfterGuardStatements]) + } + + func testNoMangleNontrivialGuardBody() { + let input = """ + import XCTest + + class TestCase: XCTestCase { + func test_something() { + guard optionalValue2 == nil else { + let value = optionalValue2 ?? "" + XCTFail("Value was \\(value)") + return + } + } + } + """ + testFormatting(for: input, rule: .noGuardInTests, exclude: [.blankLinesAfterGuardStatements]) + } + func testReplaceGuardWithMultipleConditionsXCTest() { let input = """ import XCTest @@ -819,7 +891,7 @@ final class NoGuardInTestsTests: XCTestCase { struct SomeTests { @Test func something() throws { - let value = try #require(optionalValue) + let value = try #require(optionalValue, "Expected value to be non-nil") } } """