mirror of
https://github.com/nicklockwood/SwiftFormat.git
synced 2026-06-16 10:34:34 +00:00
Add --suite-name-format option to swiftTestingTestCaseNames rule (#2384)
Co-authored-by: calda <1811727+calda@users.noreply.github.com>
This commit is contained in:
@@ -3669,11 +3669,12 @@ set to 4.2 or above.
|
||||
|
||||
## swiftTestingTestCaseNames
|
||||
|
||||
In Swift Testing, don't prefix @Test methods with 'test', and use raw identifier test function names.
|
||||
Format Swift Testing @Test and @Suite names.
|
||||
|
||||
Option | Description
|
||||
--- | ---
|
||||
`--test-case-name-format` | Swift Testing test case name format: "preserve" or "raw-identifiers" (default)
|
||||
`--test-case-name-format` | Swift Testing test case name format: "preserve", "raw-identifiers" (default) or "standard-identifiers"
|
||||
`--suite-name-format` | Swift Testing suite name format: "preserve", "raw-identifiers" or "standard-identifiers" (default)
|
||||
|
||||
<details>
|
||||
<summary>Examples</summary>
|
||||
|
||||
@@ -1886,7 +1886,7 @@ extension Formatter {
|
||||
newMethodName = String(newMethodName.dropFirst())
|
||||
}
|
||||
|
||||
updateFunctionName(forFunctionAt: funcKeywordIndex, to: newMethodName)
|
||||
updateDeclarationName(forDeclarationAt: funcKeywordIndex, to: newMethodName)
|
||||
}
|
||||
|
||||
// Handle names like ``func `test feature`()``, ``func `Test Feature`()``
|
||||
@@ -1895,29 +1895,39 @@ extension Formatter {
|
||||
newMethodName = String(newMethodName.first!) + newMethodName.dropFirst()
|
||||
newMethodName = "`" + newMethodName
|
||||
|
||||
updateFunctionName(forFunctionAt: funcKeywordIndex, to: newMethodName)
|
||||
updateDeclarationName(forDeclarationAt: funcKeywordIndex, to: newMethodName)
|
||||
}
|
||||
}
|
||||
|
||||
/// Updates the name of the given method / function, unless that change could cause a build failure.
|
||||
func updateFunctionName(forFunctionAt funcKeywordIndex: Int, to newMethodName: String) {
|
||||
// The name of a function always immediately follows the `func` keyword
|
||||
guard let methodNameIndex = index(of: .nonSpaceOrCommentOrLinebreak, after: funcKeywordIndex),
|
||||
tokens[methodNameIndex].isIdentifier
|
||||
/// Updates the name of the given declaration (function or type), unless that change could cause a build failure.
|
||||
func updateDeclarationName(forDeclarationAt keywordIndex: Int, to newName: String) {
|
||||
// The name of a declaration always immediately follows the keyword (e.g. `func`, `struct`, etc.)
|
||||
guard let nameIndex = index(of: .nonSpaceOrCommentOrLinebreak, after: keywordIndex),
|
||||
tokens[nameIndex].isIdentifier
|
||||
else { return }
|
||||
|
||||
// For type declarations (struct, class, actor, enum), don't rename if the current
|
||||
// name is referenced elsewhere in the file, since renaming would break those references.
|
||||
if case let .keyword(keyword) = tokens[keywordIndex],
|
||||
Token.swiftTypeKeywords.contains(keyword)
|
||||
{
|
||||
let currentIdentifier = tokens[nameIndex]
|
||||
let referenceCount = tokens.filter { $0 == currentIdentifier }.count
|
||||
guard referenceCount <= 1 else { return }
|
||||
}
|
||||
|
||||
// Ensure that the new identifier is valid (e.g. starts with a letter, not a number),
|
||||
// and is unique / doesn't already exist somewhere in the file.
|
||||
let unescapedName = newMethodName.hasPrefix("`") && newMethodName.hasSuffix("`")
|
||||
? String(newMethodName.dropFirst().dropLast()) : newMethodName
|
||||
guard !newMethodName.isEmpty,
|
||||
newMethodName.first?.isLetter == true || newMethodName.first == "`",
|
||||
!tokens.contains(.identifier(newMethodName)),
|
||||
let unescapedName = newName.hasPrefix("`") && newName.hasSuffix("`")
|
||||
? String(newName.dropFirst().dropLast()) : newName
|
||||
guard !newName.isEmpty,
|
||||
newName.first?.isLetter == true || newName.first == "`",
|
||||
!tokens.contains(.identifier(newName)),
|
||||
!tokens.contains(.identifier(unescapedName)),
|
||||
!swiftKeywords.union(["Any", "Self", "self", "super", "nil", "true", "false"]).contains(unescapedName)
|
||||
else { return }
|
||||
|
||||
replaceToken(at: methodNameIndex, with: .identifier(newMethodName))
|
||||
replaceToken(at: nameIndex, with: .identifier(newName))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1466,6 +1466,13 @@ struct _Descriptors {
|
||||
keyPath: \.testCaseNameFormat
|
||||
)
|
||||
|
||||
let suiteNameFormat = OptionDescriptor(
|
||||
argumentName: "suite-name-format",
|
||||
displayName: "Suite Name Format",
|
||||
help: "Swift Testing suite name format:",
|
||||
keyPath: \.suiteNameFormat
|
||||
)
|
||||
|
||||
// MARK: - Internal
|
||||
|
||||
let fragment = OptionDescriptor(
|
||||
|
||||
@@ -187,10 +187,11 @@ public enum ClosureVoidReturn: String, CaseIterable {
|
||||
case preserve
|
||||
}
|
||||
|
||||
/// Format for Swift Testing test case names
|
||||
public enum TestCaseNameFormat: String, CaseIterable {
|
||||
/// Format for Swift Testing test case / suite names
|
||||
public enum SwiftTestingNameFormat: String, CaseIterable {
|
||||
case preserve
|
||||
case rawIdentifiers = "raw-identifiers"
|
||||
case standardIdentifiers = "standard-identifiers"
|
||||
}
|
||||
|
||||
public enum TrailingCommas: String, CaseIterable {
|
||||
@@ -884,7 +885,8 @@ public struct FormatOptions: CustomStringConvertible {
|
||||
public var redundantAsync: RedundantEffectMode
|
||||
public var allowPartialWrapping: Bool
|
||||
public var preferSynthesizedInitForInternalStructs: PreferSynthesizedInitMode
|
||||
public var testCaseNameFormat: TestCaseNameFormat
|
||||
public var testCaseNameFormat: SwiftTestingNameFormat
|
||||
public var suiteNameFormat: SwiftTestingNameFormat
|
||||
|
||||
/// Deprecated
|
||||
public var indentComments: Bool
|
||||
@@ -1030,7 +1032,8 @@ public struct FormatOptions: CustomStringConvertible {
|
||||
redundantAsync: RedundantEffectMode = .testsOnly,
|
||||
allowPartialWrapping: Bool = true,
|
||||
preferSynthesizedInitForInternalStructs: PreferSynthesizedInitMode = .never,
|
||||
testCaseNameFormat: TestCaseNameFormat = .rawIdentifiers,
|
||||
testCaseNameFormat: SwiftTestingNameFormat = .rawIdentifiers,
|
||||
suiteNameFormat: SwiftTestingNameFormat = .standardIdentifiers,
|
||||
// Doesn't really belong here, but hard to put elsewhere
|
||||
fragment: Bool = false,
|
||||
ignoreConflictMarkers: Bool = false,
|
||||
@@ -1166,6 +1169,7 @@ public struct FormatOptions: CustomStringConvertible {
|
||||
self.allowPartialWrapping = allowPartialWrapping
|
||||
self.preferSynthesizedInitForInternalStructs = preferSynthesizedInitForInternalStructs
|
||||
self.testCaseNameFormat = testCaseNameFormat
|
||||
self.suiteNameFormat = suiteNameFormat
|
||||
self.indentComments = indentComments
|
||||
self.fragment = fragment
|
||||
self.ignoreConflictMarkers = ignoreConflictMarkers
|
||||
|
||||
@@ -5,8 +5,8 @@ import Foundation
|
||||
|
||||
public extension FormatRule {
|
||||
static let swiftTestingTestCaseNames = FormatRule(
|
||||
help: "In Swift Testing, don't prefix @Test methods with 'test', and use raw identifier test function names.",
|
||||
options: ["test-case-name-format"]
|
||||
help: "Format Swift Testing @Test and @Suite names.",
|
||||
options: ["test-case-name-format", "suite-name-format"]
|
||||
) { formatter in
|
||||
guard formatter.hasImport("Testing") else { return }
|
||||
|
||||
@@ -18,12 +18,34 @@ public extension FormatRule {
|
||||
switch formatter.options.testCaseNameFormat {
|
||||
case .rawIdentifiers:
|
||||
guard formatter.options.swiftVersion >= "6.2" else { return }
|
||||
formatter.convertToRawIdentifier(forTestFunctionAt: funcKeywordIndex)
|
||||
formatter.convertToRawIdentifier(forDeclarationAt: funcKeywordIndex, macroName: "@Test", upperCamelCase: false)
|
||||
|
||||
case .standardIdentifiers:
|
||||
formatter.convertToStandardIdentifier(forDeclarationAt: funcKeywordIndex, macroName: "@Test", upperCamelCase: false)
|
||||
|
||||
case .preserve:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
let typeKeywords: [String] = ["struct", "class", "actor", "enum"]
|
||||
for typeKeyword in typeKeywords {
|
||||
formatter.forEach(.keyword(typeKeyword)) { keywordIndex, _ in
|
||||
guard formatter.modifiersForDeclaration(at: keywordIndex, contains: "@Suite") else { return }
|
||||
|
||||
switch formatter.options.suiteNameFormat {
|
||||
case .rawIdentifiers:
|
||||
guard formatter.options.swiftVersion >= "6.2" else { return }
|
||||
formatter.convertToRawIdentifier(forDeclarationAt: keywordIndex, macroName: "@Suite", upperCamelCase: true)
|
||||
|
||||
case .standardIdentifiers:
|
||||
formatter.convertToStandardIdentifier(forDeclarationAt: keywordIndex, macroName: "@Suite", upperCamelCase: true)
|
||||
|
||||
case .preserve:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
} examples: {
|
||||
"""
|
||||
```diff
|
||||
@@ -52,49 +74,70 @@ public extension FormatRule {
|
||||
}
|
||||
|
||||
extension Formatter {
|
||||
/// Converts a `@Test` function name to use a raw identifier (backtick-quoted name with spaces).
|
||||
/// If the `@Test` attribute has a display name, uses that as the function name and removes it from the attribute.
|
||||
/// Otherwise, converts the camelCase/underscore function name to a space-separated raw identifier.
|
||||
func convertToRawIdentifier(forTestFunctionAt funcKeywordIndex: Int) {
|
||||
guard let methodNameIndex = index(of: .nonSpaceOrCommentOrLinebreak, after: funcKeywordIndex),
|
||||
tokens[methodNameIndex].isIdentifier
|
||||
/// Converts a declaration name to use a raw identifier (backtick-quoted name with spaces).
|
||||
/// If the macro attribute has a display name, uses that as the name and removes it from the attribute.
|
||||
/// Otherwise, converts the camelCase/underscore name to a space-separated raw identifier.
|
||||
func convertToRawIdentifier(forDeclarationAt keywordIndex: Int, macroName: String, upperCamelCase: Bool) {
|
||||
guard let nameIndex = index(of: .nonSpaceOrCommentOrLinebreak, after: keywordIndex),
|
||||
tokens[nameIndex].isIdentifier
|
||||
else { return }
|
||||
|
||||
let methodName = tokens[methodNameIndex].string
|
||||
let name = tokens[nameIndex].string
|
||||
|
||||
// Check if the @Test attribute has a display name argument
|
||||
if var displayName = testDisplayName(forDeclarationAt: funcKeywordIndex) {
|
||||
// Remove any existing backticks from the test name, since raw identifiers cant contain backticks
|
||||
// Check if the macro attribute has a display name argument
|
||||
if var displayName = macroDisplayName(forDeclarationAt: keywordIndex, macroName: macroName) {
|
||||
// Remove any existing backticks from the name, since raw identifiers can't contain backticks
|
||||
displayName = displayName.replacingOccurrences(of: "`", with: "")
|
||||
|
||||
let newMethodName = "`\(displayName)`"
|
||||
updateFunctionName(forFunctionAt: funcKeywordIndex, to: newMethodName)
|
||||
removeTestDisplayNameString(forDeclarationAt: funcKeywordIndex)
|
||||
let newName = "`\(displayName)`"
|
||||
updateDeclarationName(forDeclarationAt: keywordIndex, to: newName)
|
||||
removeMacroDisplayNameString(forDeclarationAt: keywordIndex, macroName: macroName)
|
||||
} else {
|
||||
// Convert the method name to a raw identifier
|
||||
let baseName = methodName.camelCaseToWords()
|
||||
guard !baseName.isEmpty, baseName != methodName else { return }
|
||||
// Convert the name to a raw identifier
|
||||
let baseName = name.camelCaseToWords()
|
||||
guard !baseName.isEmpty, baseName != name else { return }
|
||||
|
||||
let newMethodName = "`\(baseName)`"
|
||||
guard tokens[methodNameIndex] != .identifier(newMethodName) else { return }
|
||||
let newName = "`\(baseName)`"
|
||||
guard tokens[nameIndex] != .identifier(newName) else { return }
|
||||
|
||||
updateFunctionName(forFunctionAt: funcKeywordIndex, to: newMethodName)
|
||||
updateDeclarationName(forDeclarationAt: keywordIndex, to: newName)
|
||||
}
|
||||
}
|
||||
|
||||
/// Extracts the display name string from a `@Test("display name")` attribute, if present.
|
||||
func testDisplayName(forDeclarationAt funcKeywordIndex: Int) -> String? {
|
||||
// Walk backwards from `func` to find `@Test`
|
||||
var testAttrIndex: Int?
|
||||
_ = modifiersForDeclaration(at: funcKeywordIndex, contains: { index, modifier in
|
||||
if modifier.hasPrefix("@Test(") || modifier.hasPrefix("@Test (") {
|
||||
testAttrIndex = index
|
||||
/// Converts a raw identifier declaration name to a standard identifier, and removes
|
||||
/// any display name string from the macro attribute.
|
||||
func convertToStandardIdentifier(forDeclarationAt keywordIndex: Int, macroName: String, upperCamelCase: Bool) {
|
||||
// Remove display name string from the macro attribute if present
|
||||
removeMacroDisplayNameString(forDeclarationAt: keywordIndex, macroName: macroName)
|
||||
|
||||
guard let nameIndex = index(of: .nonSpaceOrCommentOrLinebreak, after: keywordIndex),
|
||||
tokens[nameIndex].isIdentifier
|
||||
else { return }
|
||||
|
||||
let name = tokens[nameIndex].string
|
||||
|
||||
// Only convert raw identifiers (backtick-quoted names)
|
||||
guard name.hasPrefix("`"), name.hasSuffix("`") else { return }
|
||||
|
||||
let rawName = String(name.dropFirst().dropLast())
|
||||
let newName = rawName.wordsToIdentifier(upperCamelCase: upperCamelCase)
|
||||
guard !newName.isEmpty, newName != name else { return }
|
||||
|
||||
updateDeclarationName(forDeclarationAt: keywordIndex, to: newName)
|
||||
}
|
||||
|
||||
/// Extracts the display name string from a macro attribute like `@Test("display name")` or `@Suite("display name")`.
|
||||
func macroDisplayName(forDeclarationAt keywordIndex: Int, macroName: String) -> String? {
|
||||
var macroAttrIndex: Int?
|
||||
_ = modifiersForDeclaration(at: keywordIndex, contains: { index, modifier in
|
||||
if modifier.hasPrefix("\(macroName)(") || modifier.hasPrefix("\(macroName) (") {
|
||||
macroAttrIndex = index
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
guard let attrIndex = testAttrIndex,
|
||||
guard let attrIndex = macroAttrIndex,
|
||||
let parenStart = index(of: .startOfScope("("), after: attrIndex),
|
||||
let firstToken = index(of: .nonSpaceOrCommentOrLinebreak, after: parenStart),
|
||||
tokens[firstToken] == .startOfScope("\"")
|
||||
@@ -112,18 +155,18 @@ extension Formatter {
|
||||
return displayName.isEmpty ? nil : displayName
|
||||
}
|
||||
|
||||
/// Removes the display name from a `@Test("display name")` or `@Test("display name", ...)` attribute.
|
||||
func removeTestDisplayNameString(forDeclarationAt funcKeywordIndex: Int) {
|
||||
var testAttrIndex: Int?
|
||||
_ = modifiersForDeclaration(at: funcKeywordIndex, contains: { index, modifier in
|
||||
if modifier.hasPrefix("@Test(") || modifier.hasPrefix("@Test (") {
|
||||
testAttrIndex = index
|
||||
/// Removes the display name from a macro attribute like `@Test("display name")` or `@Suite("display name", ...)`.
|
||||
func removeMacroDisplayNameString(forDeclarationAt keywordIndex: Int, macroName: String) {
|
||||
var macroAttrIndex: Int?
|
||||
_ = modifiersForDeclaration(at: keywordIndex, contains: { index, modifier in
|
||||
if modifier.hasPrefix("\(macroName)(") || modifier.hasPrefix("\(macroName) (") {
|
||||
macroAttrIndex = index
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
guard let attrIndex = testAttrIndex,
|
||||
guard let attrIndex = macroAttrIndex,
|
||||
let parenStart = index(of: .startOfScope("("), after: attrIndex),
|
||||
let firstToken = index(of: .nonSpaceOrCommentOrLinebreak, after: parenStart),
|
||||
tokens[firstToken] == .startOfScope("\""),
|
||||
@@ -141,7 +184,7 @@ extension Formatter {
|
||||
removeTokens(in: firstToken ..< removeEnd)
|
||||
} else {
|
||||
// This is the only argument — remove the entire parentheses
|
||||
// Also remove any space between @Test and (
|
||||
// Also remove any space between the macro and (
|
||||
let removeStart = (index(of: .nonSpaceOrComment, after: attrIndex) == parenStart)
|
||||
? (attrIndex + 1) : parenStart
|
||||
removeTokens(in: removeStart ... parenEnd)
|
||||
@@ -191,4 +234,22 @@ extension String {
|
||||
|
||||
return words
|
||||
}
|
||||
|
||||
/// Converts a space-separated string to a standard identifier (camelCase or UpperCamelCase).
|
||||
/// For example: "my test case" -> "myTestCase" (lowerCamelCase) or "MyTestCase" (upperCamelCase).
|
||||
func wordsToIdentifier(upperCamelCase: Bool) -> String {
|
||||
let words = split(separator: " ").map(String.init)
|
||||
guard !words.isEmpty else { return "" }
|
||||
|
||||
var result = ""
|
||||
for (index, word) in words.enumerated() {
|
||||
guard !word.isEmpty else { continue }
|
||||
if index == 0, !upperCamelCase {
|
||||
result += word.prefix(1).lowercased() + word.dropFirst()
|
||||
} else {
|
||||
result += word.prefix(1).uppercased() + word.dropFirst()
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -527,4 +527,460 @@ final class SwiftTestingTestCaseNamesTests: XCTestCase {
|
||||
testFormatting(for: input, output, rule: .swiftTestingTestCaseNames,
|
||||
options: FormatOptions(swiftVersion: "6.2"))
|
||||
}
|
||||
|
||||
// MARK: - standard-identifiers for @Test
|
||||
|
||||
func testStandardIdentifiersConvertsRawIdentifierToLowerCamelCase() {
|
||||
let input = """
|
||||
import Testing
|
||||
|
||||
struct MyFeatureTests {
|
||||
@Test
|
||||
func `my feature has no bugs`() {
|
||||
#expect(true)
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
let output = """
|
||||
import Testing
|
||||
|
||||
struct MyFeatureTests {
|
||||
@Test
|
||||
func myFeatureHasNoBugs() {
|
||||
#expect(true)
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
testFormatting(for: input, output, rule: .swiftTestingTestCaseNames,
|
||||
options: FormatOptions(testCaseNameFormat: .standardIdentifiers, swiftVersion: "6.2"))
|
||||
}
|
||||
|
||||
func testStandardIdentifiersRemovesDisplayNameFromTest() {
|
||||
let input = """
|
||||
import Testing
|
||||
|
||||
struct MyFeatureTests {
|
||||
@Test("My feature works")
|
||||
func myFeatureWorks() {
|
||||
#expect(true)
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
let output = """
|
||||
import Testing
|
||||
|
||||
struct MyFeatureTests {
|
||||
@Test
|
||||
func myFeatureWorks() {
|
||||
#expect(true)
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
testFormatting(for: input, output, rule: .swiftTestingTestCaseNames,
|
||||
options: FormatOptions(testCaseNameFormat: .standardIdentifiers, swiftVersion: "6.2"))
|
||||
}
|
||||
|
||||
func testStandardIdentifiersRemovesDisplayNameAndConvertsRawIdentifier() {
|
||||
let input = """
|
||||
import Testing
|
||||
|
||||
struct MyFeatureTests {
|
||||
@Test("My feature works")
|
||||
func `my feature works`() {
|
||||
#expect(true)
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
let output = """
|
||||
import Testing
|
||||
|
||||
struct MyFeatureTests {
|
||||
@Test
|
||||
func myFeatureWorks() {
|
||||
#expect(true)
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
testFormatting(for: input, output, rule: .swiftTestingTestCaseNames,
|
||||
options: FormatOptions(testCaseNameFormat: .standardIdentifiers, swiftVersion: "6.2"))
|
||||
}
|
||||
|
||||
func testStandardIdentifiersRemovesDisplayNameWithArguments() {
|
||||
let input = """
|
||||
import Testing
|
||||
|
||||
struct MyFeatureTests {
|
||||
@Test("Features work as expected", arguments: [
|
||||
.foo,
|
||||
.bar,
|
||||
])
|
||||
func `features work as expected`(_ feature: Feature) {
|
||||
#expect(true)
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
let output = """
|
||||
import Testing
|
||||
|
||||
struct MyFeatureTests {
|
||||
@Test(arguments: [
|
||||
.foo,
|
||||
.bar,
|
||||
])
|
||||
func featuresWorkAsExpected(_ feature: Feature) {
|
||||
#expect(true)
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
testFormatting(for: input, output, rule: .swiftTestingTestCaseNames,
|
||||
options: FormatOptions(testCaseNameFormat: .standardIdentifiers, swiftVersion: "6.2"),
|
||||
exclude: [.unusedArguments])
|
||||
}
|
||||
|
||||
func testStandardIdentifiersPreservesStandardIdentifierFunction() {
|
||||
let input = """
|
||||
import Testing
|
||||
|
||||
struct MyFeatureTests {
|
||||
@Test
|
||||
func myFeatureWorks() {
|
||||
#expect(true)
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
testFormatting(for: input, rule: .swiftTestingTestCaseNames,
|
||||
options: FormatOptions(testCaseNameFormat: .standardIdentifiers, swiftVersion: "6.2"))
|
||||
}
|
||||
|
||||
func testStandardIdentifiersStillRemovesTestPrefix() {
|
||||
let input = """
|
||||
import Testing
|
||||
|
||||
struct MyFeatureTests {
|
||||
@Test
|
||||
func testMyFeatureWorks() {
|
||||
#expect(true)
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
let output = """
|
||||
import Testing
|
||||
|
||||
struct MyFeatureTests {
|
||||
@Test
|
||||
func myFeatureWorks() {
|
||||
#expect(true)
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
testFormatting(for: input, output, rule: .swiftTestingTestCaseNames,
|
||||
options: FormatOptions(testCaseNameFormat: .standardIdentifiers, swiftVersion: "6.2"))
|
||||
}
|
||||
|
||||
// MARK: - @Suite with standard-identifiers (default)
|
||||
|
||||
func testSuiteStandardIdentifiersRemovesDisplayName() {
|
||||
let input = """
|
||||
import Testing
|
||||
|
||||
@Suite("My Feature Tests")
|
||||
struct MyFeatureTests {
|
||||
@Test func myTest() {}
|
||||
}
|
||||
"""
|
||||
|
||||
let output = """
|
||||
import Testing
|
||||
|
||||
@Suite
|
||||
struct MyFeatureTests {
|
||||
@Test func myTest() {}
|
||||
}
|
||||
"""
|
||||
|
||||
testFormatting(for: input, output, rule: .swiftTestingTestCaseNames,
|
||||
options: FormatOptions(testCaseNameFormat: .preserve, swiftVersion: "6.2"))
|
||||
}
|
||||
|
||||
func testSuiteStandardIdentifiersConvertsRawIdentifier() {
|
||||
let input = """
|
||||
import Testing
|
||||
|
||||
@Suite
|
||||
struct `My test suite` {
|
||||
@Test func myTest() {}
|
||||
}
|
||||
"""
|
||||
|
||||
let output = """
|
||||
import Testing
|
||||
|
||||
@Suite
|
||||
struct MyTestSuite {
|
||||
@Test func myTest() {}
|
||||
}
|
||||
"""
|
||||
|
||||
testFormatting(for: input, output, rule: .swiftTestingTestCaseNames,
|
||||
options: FormatOptions(testCaseNameFormat: .preserve, swiftVersion: "6.2"))
|
||||
}
|
||||
|
||||
func testSuiteStandardIdentifiersRemovesDisplayNameAndConvertsRawIdentifier() {
|
||||
let input = """
|
||||
import Testing
|
||||
|
||||
@Suite("My test suite")
|
||||
struct `My test suite` {
|
||||
@Test func myTest() {}
|
||||
}
|
||||
"""
|
||||
|
||||
let output = """
|
||||
import Testing
|
||||
|
||||
@Suite
|
||||
struct MyTestSuite {
|
||||
@Test func myTest() {}
|
||||
}
|
||||
"""
|
||||
|
||||
testFormatting(for: input, output, rule: .swiftTestingTestCaseNames,
|
||||
options: FormatOptions(testCaseNameFormat: .preserve, swiftVersion: "6.2"))
|
||||
}
|
||||
|
||||
func testSuiteStandardIdentifiersPreservesStandardName() {
|
||||
let input = """
|
||||
import Testing
|
||||
|
||||
@Suite
|
||||
struct MyFeatureTests {
|
||||
@Test func myTest() {}
|
||||
}
|
||||
"""
|
||||
|
||||
testFormatting(for: input, rule: .swiftTestingTestCaseNames,
|
||||
options: FormatOptions(testCaseNameFormat: .preserve, swiftVersion: "6.2"))
|
||||
}
|
||||
|
||||
func testSuiteStandardIdentifiersRemovesDisplayNameWithOtherArgs() {
|
||||
let input = """
|
||||
import Testing
|
||||
|
||||
@Suite("My Feature Tests", .serialized)
|
||||
struct MyFeatureTests {
|
||||
@Test func myTest() {}
|
||||
}
|
||||
"""
|
||||
|
||||
let output = """
|
||||
import Testing
|
||||
|
||||
@Suite(.serialized)
|
||||
struct MyFeatureTests {
|
||||
@Test func myTest() {}
|
||||
}
|
||||
"""
|
||||
|
||||
testFormatting(for: input, output, rule: .swiftTestingTestCaseNames,
|
||||
options: FormatOptions(testCaseNameFormat: .preserve, swiftVersion: "6.2"))
|
||||
}
|
||||
|
||||
// MARK: - @Suite with raw-identifiers
|
||||
|
||||
func testSuiteRawIdentifiersConvertsCamelCase() {
|
||||
let input = """
|
||||
import Testing
|
||||
|
||||
@Suite
|
||||
struct MyFeatureTests {
|
||||
@Test func myTest() {}
|
||||
}
|
||||
"""
|
||||
|
||||
let output = """
|
||||
import Testing
|
||||
|
||||
@Suite
|
||||
struct `my feature tests` {
|
||||
@Test func myTest() {}
|
||||
}
|
||||
"""
|
||||
|
||||
testFormatting(for: input, output, rule: .swiftTestingTestCaseNames,
|
||||
options: FormatOptions(testCaseNameFormat: .preserve,
|
||||
suiteNameFormat: .rawIdentifiers,
|
||||
swiftVersion: "6.2"))
|
||||
}
|
||||
|
||||
func testSuiteRawIdentifiersUsesDisplayName() {
|
||||
let input = """
|
||||
import Testing
|
||||
|
||||
@Suite("My Feature Tests")
|
||||
struct MyFeatureTests {
|
||||
@Test func myTest() {}
|
||||
}
|
||||
"""
|
||||
|
||||
let output = """
|
||||
import Testing
|
||||
|
||||
@Suite
|
||||
struct `My Feature Tests` {
|
||||
@Test func myTest() {}
|
||||
}
|
||||
"""
|
||||
|
||||
testFormatting(for: input, output, rule: .swiftTestingTestCaseNames,
|
||||
options: FormatOptions(testCaseNameFormat: .preserve,
|
||||
suiteNameFormat: .rawIdentifiers,
|
||||
swiftVersion: "6.2"))
|
||||
}
|
||||
|
||||
func testSuiteRawIdentifiersFallsBackToPreserveBelowSwift6_2() {
|
||||
let input = """
|
||||
import Testing
|
||||
|
||||
@Suite
|
||||
struct MyFeatureTests {
|
||||
@Test func myTest() {}
|
||||
}
|
||||
"""
|
||||
|
||||
testFormatting(for: input, rule: .swiftTestingTestCaseNames,
|
||||
options: FormatOptions(testCaseNameFormat: .preserve,
|
||||
suiteNameFormat: .rawIdentifiers,
|
||||
swiftVersion: "6.1"))
|
||||
}
|
||||
|
||||
// MARK: - @Suite with preserve
|
||||
|
||||
func testSuitePreserveKeepsEverything() {
|
||||
let input = """
|
||||
import Testing
|
||||
|
||||
@Suite("My Feature Tests")
|
||||
struct MyFeatureTests {
|
||||
@Test func myTest() {}
|
||||
}
|
||||
"""
|
||||
|
||||
testFormatting(for: input, rule: .swiftTestingTestCaseNames,
|
||||
options: FormatOptions(testCaseNameFormat: .preserve,
|
||||
suiteNameFormat: .preserve,
|
||||
swiftVersion: "6.2"))
|
||||
}
|
||||
|
||||
// MARK: - @Suite on class/actor/enum
|
||||
|
||||
func testSuiteWorksWithClass() {
|
||||
let input = """
|
||||
import Testing
|
||||
|
||||
@Suite("My Feature Tests")
|
||||
class MyFeatureTests {
|
||||
@Test func myTest() {}
|
||||
}
|
||||
"""
|
||||
|
||||
let output = """
|
||||
import Testing
|
||||
|
||||
@Suite
|
||||
class MyFeatureTests {
|
||||
@Test func myTest() {}
|
||||
}
|
||||
"""
|
||||
|
||||
testFormatting(for: input, output, rule: .swiftTestingTestCaseNames,
|
||||
options: FormatOptions(testCaseNameFormat: .preserve, swiftVersion: "6.2"))
|
||||
}
|
||||
|
||||
func testSuiteWorksWithActor() {
|
||||
let input = """
|
||||
import Testing
|
||||
|
||||
@Suite("My Feature Tests")
|
||||
actor MyFeatureTests {
|
||||
@Test func myTest() {}
|
||||
}
|
||||
"""
|
||||
|
||||
let output = """
|
||||
import Testing
|
||||
|
||||
@Suite
|
||||
actor MyFeatureTests {
|
||||
@Test func myTest() {}
|
||||
}
|
||||
"""
|
||||
|
||||
testFormatting(for: input, output, rule: .swiftTestingTestCaseNames,
|
||||
options: FormatOptions(testCaseNameFormat: .preserve, swiftVersion: "6.2"))
|
||||
}
|
||||
|
||||
func testSuiteWorksWithEnum() {
|
||||
let input = """
|
||||
import Testing
|
||||
|
||||
@Suite("My Feature Tests")
|
||||
enum MyFeatureTests {
|
||||
@Test static func myTest() {}
|
||||
}
|
||||
"""
|
||||
|
||||
let output = """
|
||||
import Testing
|
||||
|
||||
@Suite
|
||||
enum MyFeatureTests {
|
||||
@Test static func myTest() {}
|
||||
}
|
||||
"""
|
||||
|
||||
testFormatting(for: input, output, rule: .swiftTestingTestCaseNames,
|
||||
options: FormatOptions(testCaseNameFormat: .preserve, swiftVersion: "6.2"))
|
||||
}
|
||||
|
||||
func testSuiteNameNotUpdatedWhenReferencedAsStaticMember() {
|
||||
let input = """
|
||||
import Testing
|
||||
|
||||
@Suite("My Feature Tests")
|
||||
struct `My Feature Tests` {
|
||||
@Test func myTest() {}
|
||||
}
|
||||
|
||||
func runTests() {
|
||||
`My Feature Tests`.runAll()
|
||||
}
|
||||
"""
|
||||
|
||||
let output = """
|
||||
import Testing
|
||||
|
||||
@Suite
|
||||
struct `My Feature Tests` {
|
||||
@Test func myTest() {}
|
||||
}
|
||||
|
||||
func runTests() {
|
||||
`My Feature Tests`.runAll()
|
||||
}
|
||||
"""
|
||||
|
||||
testFormatting(for: input, output, rule: .swiftTestingTestCaseNames,
|
||||
options: FormatOptions(testCaseNameFormat: .preserve, swiftVersion: "6.2"))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user