krz/domain-dig
an ios app for DNS & SSL analysis
clone: git clone https://gitbay.org/krz/domain-dig.git
v4.9.0: 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 again at the largest accessibility size — the case the
116 /// deferred ViewThatFits work exists for.
117 func testSeededScreensAtLargestAccessibilitySize() throws {
118 let app = AccessibilityAuditHarness.launch(
119 contentSizeCategory: "UICTContentSizeCategoryAccessibilityXXXL",
120 seeded: true
121 )
122
123 var unaudited: [String] = []
124
125 app.selectRootTab("Dashboard")
126 if try !AccessibilityAuditHarness.audit(app, screen: "seeded-dashboard-accessibilityXXXL", test: self, reportOnly: true) {
127 unaudited.append("Dashboard")
128 }
129
130 app.selectRootTab("Inspect")
131 if try !AccessibilityAuditHarness.audit(app, screen: "seeded-batch-accessibilityXXXL", test: self, reportOnly: true) {
132 unaudited.append("Inspect batch")
133 }
134
135 app.selectRootTab("Settings")
136 let trackedDomains = app.buttons["Tracked Domains"]
137 if trackedDomains.waitForExistence(timeout: 5) {
138 trackedDomains.tap()
139 if try !AccessibilityAuditHarness.audit(app, screen: "seeded-tracked-domains-accessibilityXXXL", test: self, reportOnly: true) {
140 unaudited.append("Tracked Domains")
141 }
142 }
143
144 try XCTSkipUnless(
145 unaudited.isEmpty,
146 "Audit did not complete in time for: \(unaudited.joined(separator: ", "))"
147 )
148 }
149
150 // MARK: Helpers
151
152 private func auditRootTab(_ tab: String) throws {
153 let app = AccessibilityAuditHarness.launch()
154 app.selectRootTab(tab)
155 let audited = try AccessibilityAuditHarness.audit(app, screen: tab.lowercased(), test: self)
156 try XCTSkipUnless(audited, "Audit did not complete in time for \(tab)")
157 }
158}