krz/domain-dig

an ios app for DNS & SSL analysis

clone: git clone https://gitbay.org/krz/domain-dig.git

v5.0.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 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}