Skip to content

A11y default snapshot handling #13

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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")
]
)
]
Expand Down
90 changes: 75 additions & 15 deletions Sources/TestingExtensions/SnapshotTestBase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -20,18 +34,36 @@ open class SnapshotTestBase: XCTestCase {
UIView.setAnimationsEnabled(allowAnimations)
}

open var defaultDevices: [(name: String, device: ViewImageConfig)] {
open var defaultDevices: [DeviceConfiguration] {
[
("iPhone8", .iPhone8),
("iPhone13proMax", .iPhone13ProMax),
("iPadMini", .iPadMini(.portrait) ),
("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<V: View>(
_ view: V,
devices: [(name: String, device: ViewImageConfig)]? = nil,
devices: [DeviceConfiguration]? = nil,
a11ySnapshotConfiguration: A11ySnapshotConfiguration = .disabled,
style: [UIUserInterfaceStyle] = [.unspecified],
imageDiffPrecision: Float = 1.0,
file: StaticString = #file,
Expand All @@ -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),
Expand All @@ -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