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 c519f7d0e858d82c229e0a7fdce6eae949ba3573
parent 0a8c572701d849e38cb1c71cff501b9412595e77
Author: triesap <tyson@radroots.org>
Date:   Fri, 28 Aug 2026 20:22:29 +0000

test: bind Add completion to stable UI state

- observe operation start on the stable Add root or changed evidence
- wait for the form to re-enable before moving its viewport
- validate a changed terminal status or submit accessibility value
- avoid losing fast successful submissions behind disabled scrolling

Diffstat:
MRadrootsUITests/RadrootsRemoteQualificationUITests.swift | 36+++++++++++++++++++++++++++---------
1 file changed, 27 insertions(+), 9 deletions(-)

diff --git a/RadrootsUITests/RadrootsRemoteQualificationUITests.swift b/RadrootsUITests/RadrootsRemoteQualificationUITests.swift @@ -1101,7 +1101,6 @@ final class RadrootsRemoteQualificationUITests: XCTestCase { let priorStatusLabel = status.exists ? status.label : nil let priorSubmitValue = submit.value as? String submit.tap() - scrollTo(app, element: submit) guard waitForWorkToFinish( app, @@ -1122,7 +1121,18 @@ final class RadrootsRemoteQualificationUITests: XCTestCase { XCTFail("The terminal Add submission control was unavailable") return nil } - return submit.value as? String ?? "missing" + let terminalValue = submit.value as? String ?? "missing" + let statusChanged = status.exists + && !status.label.isEmpty + && status.label != priorStatusLabel + guard statusChanged || terminalValue != priorSubmitValue && terminalValue != "Working" else { + XCTFail( + "The Add submission did not expose changed terminal evidence; " + + submissionDiagnostics(app, submit: submit) + ) + return nil + } + return terminalValue } @MainActor @@ -1156,18 +1166,26 @@ final class RadrootsRemoteQualificationUITests: XCTestCase { priorStatusLabel: String?, priorSubmitValue: String? ) -> Bool { + let addRoot = app.descendants(matching: .any)["radroots.add.root"] let progress = app.descendants(matching: .any)["radroots.add.progress"] - let finished = NSPredicate { _, _ in - guard !progress.exists else { return false } + let started = NSPredicate { _, _ in + if progress.exists || addRoot.exists && !addRoot.isEnabled { + return true + } if status.exists, !status.label.isEmpty, status.label != priorStatusLabel { return true } - guard submit.exists else { return false } - let submitValue = submit.value as? String - return submitValue != priorSubmitValue && submitValue != "Working" + return submit.exists && submit.value as? String != priorSubmitValue + } + let startExpectation = XCTNSPredicateExpectation(predicate: started, object: app) + guard XCTWaiter.wait(for: [startExpectation], timeout: 10) == .completed else { + return false + } + let settled = NSPredicate { _, _ in + addRoot.exists && addRoot.isEnabled && !progress.exists } - let expectation = XCTNSPredicateExpectation(predicate: finished, object: app) - return XCTWaiter.wait(for: [expectation], timeout: 180) == .completed + let settleExpectation = XCTNSPredicateExpectation(predicate: settled, object: app) + return XCTWaiter.wait(for: [settleExpectation], timeout: 180) == .completed } @MainActor