summaryrefslogtreecommitdiff
path: root/DomainDigUITests/AccessibilityScreenshotTests.swift
diff options
context:
space:
mode:
Diffstat (limited to 'DomainDigUITests/AccessibilityScreenshotTests.swift')
-rw-r--r--DomainDigUITests/AccessibilityScreenshotTests.swift105
1 files changed, 105 insertions, 0 deletions
diff --git a/DomainDigUITests/AccessibilityScreenshotTests.swift b/DomainDigUITests/AccessibilityScreenshotTests.swift
new file mode 100644
index 0000000..9cdafac
--- /dev/null
+++ b/DomainDigUITests/AccessibilityScreenshotTests.swift
@@ -0,0 +1,105 @@
+import XCTest
+
+/// Captures the Phase-6 visual-pass evidence as result-bundle attachments:
+/// the seeded dense screens in Light and Dark, and again at an accessibility
+/// text size. Driven from XCUITest (not `simctl`) for two reasons proven during
+/// this pass: the seed fixtures only populate under the test harness's launch,
+/// and `xcrun simctl ui appearance` does not propagate to a headless-booted
+/// simulator — so appearance is switched through the app's own Settings →
+/// Display picker, exercising the real code path.
+///
+/// **This is a capture utility, not a pass/fail test.** Every step is
+/// best-effort and never asserts: a control it cannot reach on a given runtime
+/// simply yields no screenshot, so adding it to the audit suite can never gate
+/// CI. Run with an explicit `-resultBundlePath` and export the attachments.
+@MainActor
+final class AccessibilityScreenshotTests: XCTestCase {
+ override func setUp() {
+ continueAfterFailure = true
+ }
+
+ /// Dashboard, Watchlist, and batch results in Light then Dark.
+ func testLightAndDarkScreens() {
+ let app = AccessibilityAuditHarness.launch(seeded: true)
+
+ for appearance in ["Light", "Dark"] {
+ guard setAppearance(app, to: appearance) else { continue }
+ captureSeededScreens(app, suffix: appearance.lowercased())
+ }
+ }
+
+ /// The same seeded screens at the largest accessibility text size, where
+ /// reflow and clipping surface. Launched fresh with the content-size arg.
+ func testAccessibilityTextSizeScreens() {
+ let app = AccessibilityAuditHarness.launch(
+ contentSizeCategory: "UICTContentSizeCategoryAccessibilityXXXL",
+ seeded: true
+ )
+ captureSeededScreens(app, suffix: "axxxl")
+ }
+
+ // MARK: Capture (best-effort)
+
+ private func captureSeededScreens(_ app: XCUIApplication, suffix: String) {
+ app.selectRootTab("Dashboard")
+ _ = app.buttons["Refresh all tracked domains"].waitForExistence(timeout: 5)
+ attach(app, name: "dashboard-\(suffix)")
+
+ app.selectRootTab("Settings")
+ let trackedDomains = app.buttons["Tracked Domains"]
+ if trackedDomains.waitForExistence(timeout: 5) {
+ trackedDomains.tap()
+ _ = element(app, labelled: "healthy.example").waitForExistence(timeout: 5)
+ attach(app, name: "watchlist-\(suffix)")
+ }
+
+ app.selectRootTab("Inspect")
+ _ = element(app, labelled: "broken.example").waitForExistence(timeout: 5)
+ attach(app, name: "batch-\(suffix)")
+ }
+
+ private func element(_ app: XCUIApplication, labelled label: String) -> XCUIElement {
+ app.descendants(matching: .any)
+ .matching(NSPredicate(format: "label == %@", label))
+ .firstMatch
+ }
+
+ private func attach(_ app: XCUIApplication, name: String) {
+ let attachment = XCTAttachment(screenshot: app.screenshot())
+ attachment.name = name
+ attachment.lifetime = .keepAlways
+ add(attachment)
+ }
+
+ // MARK: Appearance (best-effort; returns whether it switched)
+
+ /// Drives Settings → Display → Appearance to the given option. The `Picker`
+ /// renders as an inline `.menu` whose trigger is labelled
+ /// "Appearance, <current value>". Returns `false` (rather than failing) if
+ /// any step is unreachable on this runtime.
+ private func setAppearance(_ app: XCUIApplication, to option: String) -> Bool {
+ // A prior capture may have left the Settings tab on a pushed view
+ // (Tracked Domains). Re-selecting the active tab pops it back to root.
+ app.selectRootTab("Settings")
+ let display = app.buttons["Display"]
+ if !display.waitForExistence(timeout: 2) {
+ app.selectRootTab("Settings")
+ }
+ guard display.waitForExistence(timeout: 5) else { return false }
+ display.tap()
+
+ let trigger = app.buttons
+ .matching(NSPredicate(format: "label BEGINSWITH %@", "Appearance"))
+ .firstMatch
+ guard trigger.waitForExistence(timeout: 5) else { return false }
+ trigger.tap()
+
+ // The popped menu exposes System / Light / Dark as buttons (or menu
+ // items on some runtimes).
+ let asButton = app.buttons[option]
+ let choice = asButton.waitForExistence(timeout: 3) ? asButton : app.menuItems[option]
+ guard choice.waitForExistence(timeout: 3) else { return false }
+ choice.tap()
+ return true
+ }
+}