Files
SwiftFormat/Tests/Rules/WrapMultilineFunctionChainsTests.swift

341 lines
9.4 KiB
Swift

//
// WrapMultilineFunctionChainsTests.swift
// SwiftFormat
//
// Created by Eric Horacek on 2/20/25.
// Copyright © 2025 Nick Lockwood. All rights reserved.
//
import XCTest
@testable import SwiftFormat
final class WrapMultilineFunctionChainsTests: XCTestCase {
func testWrapIfExpressionAssignment() {
let input = """
let evenSquaresSum = [20, 17, 35, 4]
.filter { $0 % 2 == 0 }.map { $0 * $0 }
.reduce(0, +)
"""
let output = """
let evenSquaresSum = [20, 17, 35, 4]
.filter { $0 % 2 == 0 }
.map { $0 * $0 }
.reduce(0, +)
"""
testFormatting(for: input, [output], rules: [.wrapMultilineFunctionChains, .indent])
}
func testWrapMultipleFunctionCalls() {
let input = """
let result = array
.first?.map { $0 * 2 }.filter { $0 > 10 }
.reduce(0, +)
"""
let output = """
let result = array
.first?
.map { $0 * 2 }
.filter { $0 > 10 }
.reduce(0, +)
"""
testFormatting(for: input, [output], rules: [.wrapMultilineFunctionChains, .indent])
}
func testWrapSingleFunctionCall() {
let input = """
let result = array.map { $0 * 2 }
"""
testFormatting(for: input, [], rules: [.wrapMultilineFunctionChains, .indent])
}
func testWrapNestedFunctionCallsWithTrailingClosures() {
let input = """
let result = array
.map { $0.filter { $1 > 10 } }.flatMap { $0 }
"""
let output = """
let result = array
.map { $0.filter { $1 > 10 } }
.flatMap { $0 }
"""
testFormatting(for: input, [output], rules: [.wrapMultilineFunctionChains, .indent])
}
func testWrapNestedFunctionCallsWithClosureParameters() {
let input = """
let result = array
.map { $0.reduce(0, +) }.flatMap { $0 }
"""
let output = """
let result = array
.map { $0.reduce(0, +) }
.flatMap { $0 }
"""
testFormatting(for: input, [output], rules: [.wrapMultilineFunctionChains, .indent])
}
func testWrapFunctionCallsWithComments() {
let input = """
let result = array
.map { $0 * 2 } // multiply by 2
.filter { $0 > 10 } // filter greater than 10
.reduce(0, +) // sum up
"""
testFormatting(for: input, [], rules: [.wrapMultilineFunctionChains, .indent])
}
func testWrapFunctionCallsWithPropertyAccess() {
let input = """
let result = array
.first?.property.map { $0 * 2 }.filter { $0 > 10 }
.reduce(0, +)
"""
let output = """
let result = array
.first?
.property
.map { $0 * 2 }
.filter { $0 > 10 }
.reduce(0, +)
"""
testFormatting(for: input, [output], rules: [.wrapMultilineFunctionChains, .indent])
}
func testWrapFunctionCallsWithMultiplePropertyAccesses() {
let input = """
let result = array
.first?.property.anotherProperty.map { $0 * 2 }.filter { $0 > 10 }
.reduce(0, +)
"""
let output = """
let result = array
.first?
.property
.anotherProperty
.map { $0 * 2 }
.filter { $0 > 10 }
.reduce(0, +)
"""
testFormatting(for: input, [output], rules: [.wrapMultilineFunctionChains, .indent])
}
func testWrapFunctionCallsWithChainsOnEndOfClosures() {
let input = """
let result = array
.map { item in
item.property
}.filter { $0 > 10 }
.reduce(0, +)
"""
let output = """
let result = array
.map { item in
item.property
}
.filter { $0 > 10 }
.reduce(0, +)
"""
testFormatting(for: input, [output], rules: [.wrapMultilineFunctionChains, .indent])
}
func testWrapFunctionCallsWithChainsOnEndOfParameters() {
let input = """
let result = array
.function(
arg1: item.property,
arg2: item.property
).reduce(0, +)
"""
let output = """
let result = array
.function(
arg1: item.property,
arg2: item.property
)
.reduce(0, +)
"""
testFormatting(for: input, [output], rules: [.wrapMultilineFunctionChains, .indent])
}
func testMultilineOperatorStatementsNotWrapped() {
let input = """
formatter.currentIndentForLine(at: conditionBeginIndex)
.count < indent.count + formatter.options.indent.count
"""
testFormatting(for: input, rules: [.wrapMultilineFunctionChains, .indent])
}
func testNamespacedAccessNotWrapped() {
let input = """
Namespace.NestedNamespace.property
.map { $0 * 2 }
"""
testFormatting(for: input, rules: [.wrapMultilineFunctionChains, .indent])
}
func testNestedNamespacedAccessNotWrapped() {
let input = """
Namespace.NestedNamespace.DeeplyNestedNamespace.property
.function()
"""
testFormatting(for: input, rules: [.wrapMultilineFunctionChains, .indent])
}
func testMultilineTypesNotWrapped() {
let input = """
func tableCellSelection(for selection: Selection?) -> Selection
.TableSelection.CellSelection?
{
selection.tableCellSelection
}
"""
testFormatting(for: input, rules: [.wrapMultilineFunctionChains, .indent])
}
func testSingleFunctionChainNotWrapped() {
let input = """
let result = array.map { $0 * 2 }
"""
testFormatting(for: input, [], rules: [.wrapMultilineFunctionChains, .indent])
}
func testMultipleFunctionChainsOnOneLineNotWrapped() {
let input = """
let result = array.map { $0 * 2 }.filter { $0 > 10 }.reduce(0, +)
"""
testFormatting(for: input, [], rules: [.wrapMultilineFunctionChains, .indent])
}
func testFunctionChainWithOptionalChaining() {
let input = """
let result = array?
.map { $0 * 2 }
.filter { $0 > 10 }
.reduce(0, +)
"""
testFormatting(for: input, [], rules: [.wrapMultilineFunctionChains, .indent])
}
func testConsecutiveDeclarations() {
let input = """
let sequence = [42].async
let sequence = [43].async
"""
testFormatting(for: input, [], rules: [.wrapMultilineFunctionChains, .indent])
}
func testConsecutiveStatements() {
let input = """
let encoded = try JSONEncoder().encode(container)
let decoded = try JSONDecoder().decode(Container.self, from: encoded)
"""
testFormatting(for: input, [], rules: [.wrapMultilineFunctionChains, .indent])
}
func testAdjacentChainsChainsShouldNotWrap() {
let input = """
value.property.map { $0 * 2 }
value.property.map { $0 * 2 }
"""
testFormatting(for: input, [], rules: [.wrapMultilineFunctionChains, .indent])
}
func testAdjacentChainsInViewBuildersWithDifferentWraps() {
let input = """
Text("S")
.padding(10)
Color.blue.frame(maxWidth: 1, maxHeight: .infinity).fixedSize()
"""
testFormatting(for: input, [], rules: [.wrapMultilineFunctionChains, .indent])
}
func testNonFunctionChains() {
let input = """
let adjusted: CGPoint = .init(
x: rect.origin.x + insets.left,
y: rect.origin.y + insets.top
)
"""
testFormatting(for: input, [], rules: [.wrapMultilineFunctionChains, .indent])
}
func testAdjacentChainsInConditions() {
let input = """
let hexSanitized = hexString
.trimmingCharacters(in: .whitespacesAndNewlines)
.replacingOccurrences(of: "#", with: "")
var hex: UInt64 = 0
guard Scanner(string: hexSanitized).scanHexInt64(&hex) else {
return nil
}
"""
testFormatting(for: input, [], rules: [.wrapMultilineFunctionChains, .indent])
}
func testAdjacentChainsInIfConditionWhereStatement() {
let input = """
let xcTestCaseInstanceMethods = Set(["expectation"])
.union(options.additionalXCTestSymbols)
for index in tokens.indices where tokens[index].isIdentifier {
let identifier = tokens[index].string
}
"""
testFormatting(for: input, [], rules: [.wrapMultilineFunctionChains, .indent])
}
func testTrailingClosureNotWrapped() {
let input = """
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [weak self] in
self?.hideKeyboard.send()
}
"""
testFormatting(for: input, [], rules: [.wrapMultilineFunctionChains, .indent])
}
func testParenthesisInSingleLineDoNoWrap() {
let input = """
MainActor.assumeIsolated {
let value = try await someFunction()
}
(invoke() as Value).method()
"""
testFormatting(for: input, [], rules: [.wrapMultilineFunctionChains, .indent], exclude: [.hoistTry])
}
}