diff --git a/Package.swift b/Package.swift index 6df7d63..20c5f9e 100644 --- a/Package.swift +++ b/Package.swift @@ -17,14 +17,16 @@ let package = Package( ], dependencies: [ .package(name: "swift-snapshot-testing", url: "https://github.com/pointfreeco/swift-snapshot-testing.git", .upToNextMajor(from: "1.12.0")), - .package(name: "SwiftRex", url: "https://github.com/SwiftRex/SwiftRex.git", .upToNextMajor(from: "0.8.12")) + .package(name: "SwiftRex", url: "https://github.com/SwiftRex/SwiftRex.git", .upToNextMajor(from: "0.8.12")), + .package(name: "AccessibilitySnapshot", url: "https://github.com/cashapp/AccessibilitySnapshot.git", from: "0.7.0"), ], targets: [ .target( name: "TestingExtensions", dependencies: [ .product(name: "SnapshotTesting", package: "swift-snapshot-testing"), - .product(name: "CombineRexDynamic", package: "SwiftRex") + .product(name: "CombineRexDynamic", package: "SwiftRex"), + .product(name: "AccessibilitySnapshot", package: "AccessibilitySnapshot") ] ) ] diff --git a/Sources/TestingExtensions/SnapshotTestBase.swift b/Sources/TestingExtensions/SnapshotTestBase.swift index 9a4c46e..7753d81 100644 --- a/Sources/TestingExtensions/SnapshotTestBase.swift +++ b/Sources/TestingExtensions/SnapshotTestBase.swift @@ -11,6 +11,20 @@ import Foundation import SnapshotTesting import SwiftUI import XCTest +import AccessibilitySnapshot + +extension SnapshotTestBase { + public typealias DeviceConfiguration = (name: String, device: ViewImageConfig) +} + +extension SnapshotTestBase { + /// Configuration of Accessibility snapshots. + public enum A11ySnapshotConfiguration { + case disabled + case enabled + case enabledWithDevices([DeviceConfiguration]) + } +} open class SnapshotTestBase: XCTestCase { public var allowAnimations: Bool = false @@ -20,7 +34,7 @@ open class SnapshotTestBase: XCTestCase { UIView.setAnimationsEnabled(allowAnimations) } - open var defaultDevices: [(name: String, device: ViewImageConfig)] { + open var defaultDevices: [DeviceConfiguration] { [ ("iPhone8", .iPhone8), ("iPhone13proMax", .iPhone13ProMax), @@ -28,10 +42,28 @@ open class SnapshotTestBase: XCTestCase { ("iPadPro", .iPadPro12_9(.portrait)) ] } - + + open var a11yDefaultDevices: [DeviceConfiguration] { + [ + ("iPhone13pro", .iPhone13) + ] + } + + /// Asserts snapshots on the given devices (or default) respecting it's parameters. + /// - Parameters: + /// - view: The view to be snapshotted + /// - devices: Specified devices, if not given it'll take default devices + /// - a11ySnapshotConfiguration: Accessibility snapshot configuration, if enabled it adds a accessibility snapshot + /// - style: `UIUserInterfaceStyle` to be applied + /// - imageDiffPrecision: Precision of the compared images, 1 means it matches 100%, range is between 0 and 1. + /// - file: The file this was executed from, it will be taken as the snapshot's file name. + /// - testName: The test name taken as a part of the snapshot's file name. + /// - line: The line number on which failure occurred. Defaults to the line number on which this + /// function was called. open func assertSnapshotDevices( _ view: V, - devices: [(name: String, device: ViewImageConfig)]? = nil, + devices: [DeviceConfiguration]? = nil, + a11ySnapshotConfiguration: A11ySnapshotConfiguration = .disabled, style: [UIUserInterfaceStyle] = [.unspecified], imageDiffPrecision: Float = 1.0, file: StaticString = #file, @@ -43,18 +75,19 @@ open class SnapshotTestBase: XCTestCase { let vc = UIHostingController(rootView: view) vc.overrideUserInterfaceStyle = uiStyle - let suffix: String - switch uiStyle { - case .unspecified: - suffix = "" - case .light: - suffix = "-light" - case .dark: - suffix = "-dark" - @unknown default: - fatalError("Unhandled UIUserInterfaceStyle \(uiStyle)") - } - + let suffix: String = { + switch uiStyle { + case .unspecified: + return "" + case .light: + return "-light" + case .dark: + return "-dark" + @unknown default: + fatalError("Unhandled UIUserInterfaceStyle \(uiStyle)") + } + }() + assertSnapshot( of: vc, as: .image(on: config.device, precision: imageDiffPrecision), @@ -64,6 +97,33 @@ open class SnapshotTestBase: XCTestCase { ) } } + + let a11ySnapshotDevices: [DeviceConfiguration]? = { + switch a11ySnapshotConfiguration { + case .disabled: + return nil + case .enabled: + return a11yDefaultDevices + case .enabledWithDevices(let specifiedDevices): + return specifiedDevices + } + }() + + guard let a11ySnapshotDevices else { return } + + guard UIApplication.shared != nil else { + XCTFail("Accessibility snapshots must be run from a hosting application!") + } + + a11ySnapshotDevices.forEach { config in + assertSnapshot( + of: view, + as: .accessibilityImage(showActivationPoints: .always, drawHierarchyInKeyWindow: true), + file: file, + testName: "\(testName)-\(config.name)-accessibility", + line: line + ) + } } } #endif