krz/domain-dig
an ios app for DNS & SSL analysis
clone: git clone https://gitbay.org/krz/domain-dig.git
v5.0.3: DomainDigUITests/AccessibilityAuditTests.swift · raw
1import XCTest
2
3/// One accessibility audit per primary screen, plus a Dynamic Type sweep.
4///
5/// See `AccessibilityAuditHarness` for why these report rather than fail by
6/// default, and how to make them enforcing.
7@MainActor
8final class AccessibilityAuditTests: XCTestCase {
9 override func setUp() {
10 // Keep going after a failure so an enforced audit still collects and
11 // attaches every finding. With this off, XCTest aborts at the first
12 // reported issue and the burndown list is lost precisely when a category
13 // is being enforced.
14 continueAfterFailure = true
15 }
16
17 // MARK: Per-screen audits
18
19 func testInspectScreen() throws {
20 try auditRootTab("Inspect")
21 }
22
23 func testDashboardScreen() throws {
24 try auditRootTab("Dashboard")
25 }
26
27 func testAuditScreen() throws {
28 try auditRootTab("Audit")
29 }
30
31 func testHistoryScreen() throws {
32 try auditRootTab("History")
33 }
34
35 func testSettingsScreen() throws {
36 try auditRootTab("Settings")
37 }
38
39 func testTrackedDomainsScreen() throws {
40 let app = AccessibilityAuditHarness.launch()
41 app.selectRootTab("Settings")
42
43 let trackedDomains = app.buttons["Tracked Domains"]
44 XCTAssertTrue(
45 trackedDomains.waitForExistence(timeout: 5),
46 "Settings no longer offers a Tracked Domains row"
47 )
48 trackedDomains.tap()
49
50 let audited = try AccessibilityAuditHarness.audit(app, screen: "tracked-domains", test: self)
51 try XCTSkipUnless(audited, "Tracked Domains audit did not complete in time")
52 }
53
54 // MARK: Dynamic Type
55
56 /// Re-audits every root screen at the largest accessibility content size.
57 ///
58 /// This is where clipped text and fixed-height containers surface — the
59 /// `.accessibility5`-class failures that the fixed geometry in
60 /// `AppDensityMetrics` is expected to produce until phase 3 of #21 lands.
61 ///
62 /// Every screen is attempted even if an earlier one times out, so one slow
63 /// screen cannot silently drop the rest; the skip is reported at the end.
64 func testAllScreensAtLargestAccessibilitySize() throws {
65 let app = AccessibilityAuditHarness.launch(
66 contentSizeCategory: "UICTContentSizeCategoryAccessibilityXXXL"
67 )
68
69 var unaudited: [String] = []
70
71 for tab in ["Inspect", "Dashboard", "Audit", "History", "Settings"] {
72 app.selectRootTab(tab)
73 let screen = "\(tab.lowercased())-accessibilityXXXL"
74 if try !AccessibilityAuditHarness.audit(app, screen: screen, test: self) {
75 unaudited.append(tab)
76 }
77 }
78
79 try XCTSkipUnless(
80 unaudited.isEmpty,
81 "Audit did not complete in time for: \(unaudited.joined(separator: ", "))"
82 )
83 }
84
85 // MARK: Seeded audits — dense rows that never render on an empty simulator
86
87 /// Dashboard with a populated portfolio: summary tiles, quick filters,
88 /// activity/attention/expiry rows, and the grouped portfolio list.
89 func testSeededDashboard() throws {
90 let app = AccessibilityAuditHarness.launch(seeded: true)
91 app.selectRootTab("Dashboard")
92 let audited = try AccessibilityAuditHarness.audit(app, screen: "seeded-dashboard", test: self, reportOnly: true)
93 try XCTSkipUnless(audited, "Audit did not complete in time for seeded Dashboard")
94 }
95
96 /// The watchlist's dense rows (up to nine text elements each).
97 func testSeededTrackedDomains() throws {
98 let app = AccessibilityAuditHarness.launch(seeded: true)
99 app.selectRootTab("Settings")
100 let trackedDomains = app.buttons["Tracked Domains"]
101 XCTAssertTrue(trackedDomains.waitForExistence(timeout: 5))
102 trackedDomains.tap()
103 let audited = try AccessibilityAuditHarness.audit(app, screen: "seeded-tracked-domains", test: self, reportOnly: true)
104 try XCTSkipUnless(audited, "Audit did not complete in time for seeded Tracked Domains")
105 }
106
107 /// Batch result rows on the Inspect tab, including a failed lookup.
108 func testSeededBatchResults() throws {
109 let app = AccessibilityAuditHarness.launch(seeded: true)
110 app.selectRootTab("Inspect")
111 let audited = try AccessibilityAuditHarness.audit(app, screen: "seeded-batch", test: self, reportOnly: true)
112 try XCTSkipUnless(audited, "Audit did not complete in time for seeded batch results")
113 }
114
115 /// The seeded screens at an **intermediate** accessibility size.
116 ///
117 /// The default/`AccessibilityXXXL` pair brackets the range but skips the
118 /// middle band, and two production layout bugs lived exactly there — a
119 /// bordered button letter-wrapping vertically at a merely-large size, which
120 /// neither endpoint reproduced. `AccessibilityL` samples that band across the
121 /// dense seeded screens so a regression in it cannot slip between the two
122 /// existing test points. Reports rather than gates, matching the other
123 /// seeded audits.
124 func testSeededScreensAtIntermediateAccessibilitySize() throws {
125 let app = AccessibilityAuditHarness.launch(
126 contentSizeCategory: "UICTContentSizeCategoryAccessibilityL",
127 seeded: true
128 )
129
130 var unaudited: [String] = []
131
132 app.selectRootTab("Dashboard")
133 if try !AccessibilityAuditHarness.audit(app, screen: "seeded-dashboard-accessibilityL", test: self, reportOnly: true) {
134 unaudited.append("Dashboard")
135 }
136
137 app.selectRootTab("Inspect")
138 if try !AccessibilityAuditHarness.audit(app, screen: "seeded-batch-accessibilityL", test: self, reportOnly: true) {
139 unaudited.append("Inspect batch")
140 }
141
142 app.selectRootTab("Settings")
143 let trackedDomains = app.buttons["Tracked Domains"]
144 if trackedDomains.waitForExistence(timeout: 5) {
145 trackedDomains.tap()
146 if try !AccessibilityAuditHarness.audit(app, screen: "seeded-tracked-domains-accessibilityL", test: self, reportOnly: true) {
147 unaudited.append("Tracked Domains")
148 }
149 }
150
151 try XCTSkipUnless(
152 unaudited.isEmpty,
153 "Audit did not complete in time for: \(unaudited.joined(separator: ", "))"
154 )
155 }
156
157 /// The seeded screens again at the largest accessibility size — the case the
158 /// deferred ViewThatFits work exists for.
159 func testSeededScreensAtLargestAccessibilitySize() throws {
160 let app = AccessibilityAuditHarness.launch(
161 contentSizeCategory: "UICTContentSizeCategoryAccessibilityXXXL",
162 seeded: true
163 )
164
165 var unaudited: [String] = []
166
167 app.selectRootTab("Dashboard")
168 if try !AccessibilityAuditHarness.audit(app, screen: "seeded-dashboard-accessibilityXXXL", test: self, reportOnly: true) {
169 unaudited.append("Dashboard")
170 }
171
172 app.selectRootTab("Inspect")
173 if try !AccessibilityAuditHarness.audit(app, screen: "seeded-batch-accessibilityXXXL", test: self, reportOnly: true) {
174 unaudited.append("Inspect batch")
175 }
176
177 app.selectRootTab("Settings")
178 let trackedDomains = app.buttons["Tracked Domains"]
179 if trackedDomains.waitForExistence(timeout: 5) {
180 trackedDomains.tap()
181 if try !AccessibilityAuditHarness.audit(app, screen: "seeded-tracked-domains-accessibilityXXXL", test: self, reportOnly: true) {
182 unaudited.append("Tracked Domains")
183 }
184 }
185
186 try XCTSkipUnless(
187 unaudited.isEmpty,
188 "Audit did not complete in time for: \(unaudited.joined(separator: ", "))"
189 )
190 }
191
192 // MARK: Helpers
193
194 private func auditRootTab(_ tab: String) throws {
195 let app = AccessibilityAuditHarness.launch()
196 app.selectRootTab(tab)
197 let audited = try AccessibilityAuditHarness.audit(app, screen: tab.lowercased(), test: self)
198 try XCTSkipUnless(audited, "Audit did not complete in time for \(tab)")
199 }
200}