/
HaveValidDynamicTypeSnapshot.swift
135 lines (114 loc) · 5.54 KB
/
HaveValidDynamicTypeSnapshot.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import Nimble
import UIKit
public func allContentSizeCategories() -> [UIContentSizeCategory] {
return [
.extraSmall, .small, .medium,
.large, .extraLarge, .extraExtraLarge,
.extraExtraExtraLarge, .accessibilityMedium,
.accessibilityLarge, .accessibilityExtraLarge,
.accessibilityExtraExtraLarge, .accessibilityExtraExtraExtraLarge
]
}
func shortCategoryName(_ category: UIContentSizeCategory) -> String {
return category.rawValue.replacingOccurrences(of: "UICTContentSizeCategory", with: "")
}
func combinePredicates<T>(_ predicates: [Nimble.Predicate<T>],
deferred: (() -> Void)? = nil) -> Nimble.Predicate<T> {
return Predicate { actualExpression in
defer {
deferred?()
}
let result = PredicateResult(status: .fail, message: .fail(""))
return try predicates.reduce(result) { _, matcher -> PredicateResult in
let result = try matcher.satisfies(actualExpression)
return PredicateResult(status: PredicateStatus(bool: result.status == .matches),
message: result.message)
}
}
}
public func haveValidDynamicTypeSnapshot<T: Snapshotable>(named name: String? = nil,
identifier: String? = nil,
usesDrawRect: Bool = false,
pixelTolerance: CGFloat? = nil,
tolerance: CGFloat? = nil,
sizes: [UIContentSizeCategory] = allContentSizeCategories(),
isDeviceAgnostic: Bool = false) -> Nimble.Predicate<T> {
let mock = NBSMockedApplication()
let predicates: [Nimble.Predicate<T>] = sizes.map { category in
let sanitizedName = sanitizedTestName(name)
let nameWithCategory = "\(sanitizedName)_\(shortCategoryName(category))"
return Nimble.Predicate { actualExpression in
mock.mockPreferredContentSizeCategory(category)
updateTraitCollection(on: actualExpression)
let predicate: Nimble.Predicate<T>
if isDeviceAgnostic {
predicate = haveValidDeviceAgnosticSnapshot(named: nameWithCategory, identifier: identifier,
usesDrawRect: usesDrawRect, pixelTolerance: pixelTolerance,
tolerance: tolerance)
} else {
predicate = haveValidSnapshot(named: nameWithCategory,
identifier: identifier,
usesDrawRect: usesDrawRect,
pixelTolerance: pixelTolerance,
tolerance: tolerance)
}
return try predicate.satisfies(actualExpression)
}
}
return combinePredicates(predicates) {
mock.stopMockingPreferredContentSizeCategory()
}
}
public func recordDynamicTypeSnapshot<T: Snapshotable>(named name: String? = nil,
identifier: String? = nil,
usesDrawRect: Bool = false,
sizes: [UIContentSizeCategory] = allContentSizeCategories(),
isDeviceAgnostic: Bool = false) -> Nimble.Predicate<T> {
let mock = NBSMockedApplication()
let predicates: [Nimble.Predicate<T>] = sizes.map { category in
let sanitizedName = sanitizedTestName(name)
let nameWithCategory = "\(sanitizedName)_\(shortCategoryName(category))"
return Nimble.Predicate { actualExpression in
mock.mockPreferredContentSizeCategory(category)
updateTraitCollection(on: actualExpression)
let predicate: Nimble.Predicate<T>
if isDeviceAgnostic {
predicate = recordDeviceAgnosticSnapshot(named: nameWithCategory,
identifier: identifier,
usesDrawRect: usesDrawRect)
} else {
predicate = recordSnapshot(named: nameWithCategory, identifier: identifier, usesDrawRect: usesDrawRect)
}
return try predicate.satisfies(actualExpression)
}
}
return combinePredicates(predicates) {
mock.stopMockingPreferredContentSizeCategory()
}
}
private func updateTraitCollection<T: Snapshotable>(on expression: Expression<T>) {
// swiftlint:disable:next force_try force_unwrapping
let instance = try! expression.evaluate()!
updateTraitCollection(on: instance)
}
private func updateTraitCollection(on element: Snapshotable) {
if let environment = element as? UITraitEnvironment {
if let vc = environment as? UIViewController {
vc.beginAppearanceTransition(true, animated: false)
vc.endAppearanceTransition()
}
environment.traitCollectionDidChange(nil)
if let view = environment as? UIView {
view.subviews.forEach(updateTraitCollection(on:))
} else if let vc = environment as? UIViewController {
#if swift(>=4.2)
vc.children.forEach(updateTraitCollection(on:))
#else
vc.childViewControllers.forEach(updateTraitCollection(on:))
#endif
if vc.isViewLoaded {
updateTraitCollection(on: vc.view)
}
}
}
}