field_ios

In-the-field app for Radroots on iOS
git clone https://radroots.dev/git/field_ios.git
Log | Files | Refs | README | LICENSE

commit 3aa823bc97725a43894095ccee5327fca6144831
parent d9b521d738d0ce7fd23a3c95a235ba047bcb0b55
Author: triesap <tyson@radroots.org>
Date:   Sat, 29 Aug 2026 02:22:34 +0000

ios: separate contrast and persona audits

- Keep the dedicated five-form accessibility lane responsible for contrast.
- Retain semantic, clipping, trait, and hit-region audits in P04 attempts.
- Remove the brittle native picker disclosure exception entirely.
- Verify the full contrast lane on the final separated source bytes.

Diffstat:
MRadrootsUITests/RadrootsRemoteQualificationUITests.swift | 47+++++++++++++++++++----------------------------
1 file changed, 19 insertions(+), 28 deletions(-)

diff --git a/RadrootsUITests/RadrootsRemoteQualificationUITests.swift b/RadrootsUITests/RadrootsRemoteQualificationUITests.swift @@ -420,9 +420,23 @@ final class RadrootsRemoteQualificationUITests: XCTestCase { ] } + private var personaSemanticAuditTypes: XCUIAccessibilityAuditType { + [ + .elementDetection, + .hitRegion, + .sufficientElementDescription, + .textClipped, + .trait, + ] + } + @MainActor - private func performLocalSocialAccessibilityAudit(_ app: XCUIApplication) throws { - try app.performAccessibilityAudit(for: accessibilityAuditTypes) { issue in + private func performLocalSocialAccessibilityAudit( + _ app: XCUIApplication, + includeContrast: Bool = true + ) throws { + let auditTypes = includeContrast ? accessibilityAuditTypes : personaSemanticAuditTypes + try app.performAccessibilityAudit(for: auditTypes) { issue in if issue.auditType == .contrast && issue.compactDescription == "Contrast nearly passed" { return true } @@ -445,9 +459,6 @@ final class RadrootsRemoteQualificationUITests: XCTestCase { { return true } - if self.isNativeAddTypePickerDisclosureIssue(issue, app: app) { - return true - } // Xcode 26 can emit text-clipping findings with no element, identifier, // label, type, or frame. Element-bound findings remain fatal. guard let element = issue.element else { return issue.auditType == .textClipped } @@ -458,28 +469,6 @@ final class RadrootsRemoteQualificationUITests: XCTestCase { } @MainActor - private func isNativeAddTypePickerDisclosureIssue( - _ issue: XCUIAccessibilityAuditIssue, - app: XCUIApplication - ) -> Bool { - guard issue.auditType == .contrast, let element = issue.element else { return false } - let picker = app.descendants(matching: .any)["radroots.add.type"] - guard picker.exists, picker.isEnabled, let selected = picker.value as? String else { - return false - } - let governedLabels = ["Update", "Photo update", "Ask", "Event", "Food availability"] - guard governedLabels.contains(selected), element.frame.intersects(picker.frame) else { - return false - } - // Xcode 26 folds the system navigation-link disclosure glyph into the - // picker's SwiftUI accessibility node. The selected value is explicitly - // primary-colored; only that native decorative glyph is exempted here. - let exactLabels = [selected, "Type", "Type, \(selected)"] - return element.identifier == "radroots.add.type" - || (element.identifier.isEmpty && exactLabels.contains(element.label)) - } - - @MainActor private func systemChromePartiallyOccludes(_ element: XCUIElement, app: XCUIApplication) -> Bool { let frame = element.frame let navigationBar = app.navigationBars.firstMatch @@ -848,7 +837,9 @@ final class RadrootsRemoteQualificationUITests: XCTestCase { assertProgressiveDisclosure(app, type: type) } if interactionProfile == "novice_accessibility_keyboard" { - try performLocalSocialAccessibilityAudit(app) + // The dedicated accessibility test owns the exact full contrast lane. + // Persona attempts retain every semantic audit plus keyboard/focus use. + try performLocalSocialAccessibilityAudit(app, includeContrast: false) } try completeOpenDraft(app, flow: attempt.flow, marker: attempt.marker) }