-
Notifications
You must be signed in to change notification settings - Fork 99
/
HaveValidDynamicTypeSnapshot.swift
135 lines (114 loc) · 5.51 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.Matcher<T>],
deferred: (() -> Void)? = nil) -> Nimble.Matcher<T> {
return Matcher { actualExpression in
defer {
deferred?()
}
let result = MatcherResult(status: .fail, message: .fail(""))
return try predicates.reduce(result) { _, matcher -> MatcherResult in
let result = try matcher.satisfies(actualExpression)
return MatcherResult(status: MatcherStatus(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.Matcher<T> {
let mock = NBSMockedApplication()
let predicates: [Nimble.Matcher<T>] = sizes.map { category in
let sanitizedName = sanitizedTestName(name)
let nameWithCategory = "\(sanitizedName)_\(shortCategoryName(category))"
return Nimble.Matcher { actualExpression in
mock.mockPreferredContentSizeCategory(category)
updateTraitCollection(on: actualExpression)
let predicate: Nimble.Matcher<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.Matcher<T> {
let mock = NBSMockedApplication()
let predicates: [Nimble.Matcher<T>] = sizes.map { category in
let sanitizedName = sanitizedTestName(name)
let nameWithCategory = "\(sanitizedName)_\(shortCategoryName(category))"
return Nimble.Matcher { actualExpression in
mock.mockPreferredContentSizeCategory(category)
updateTraitCollection(on: actualExpression)
let predicate: Nimble.Matcher<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)
}
}
}
}