field_ios

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

TeraSubmissionQualificationSupport.swift (7555B)


      1 import XCTest
      2 
      3 extension TeraRemoteQualificationUITests {
      4   @MainActor
      5   func beginNewDraft(_ action: XCUIElement) -> Bool {
      6     let ready = NSPredicate { _, _ in
      7       action.exists && action.isEnabled && action.isHittable
      8     }
      9     guard XCTWaiter.wait(for: [XCTNSPredicateExpectation(predicate: ready, object: action)], timeout: 10) == .completed else { return false }
     10     action.tap()
     11     // New may first preserve the old editor through an asynchronous durable
     12     // save. Editing before it settles intentionally cancels that replacement.
     13     return XCTWaiter.wait(for: [XCTNSPredicateExpectation(predicate: ready, object: action)], timeout: 60) == .completed
     14   }
     15 
     16   @MainActor
     17   func enterExactText(_ field: XCUIElement, value: String) {
     18     field.typeText(value)
     19     XCTAssertEqual(field.value as? String, value, "The fresh form must contain only the supplied field value")
     20   }
     21 
     22   @MainActor
     23   func openAdd(_ app: XCUIApplication) -> XCUIElement? {
     24     let add = app.tabBars.buttons["Add"]
     25     guard add.waitForExistence(timeout: 10) else { return nil }
     26     let root = app.descendants(matching: .any)["radroots.add.root"]
     27     let type = app.descendants(matching: .any)["radroots.add.type"]
     28     for _ in 0 ..< 3 {
     29       add.tap()
     30       guard root.waitForExistence(timeout: 10) else { continue }
     31       // Public disclosure precedes the picker in the lazy native Form.
     32       // Reveal its actual row before requiring existence and interaction.
     33       scrollTo(app, element: type)
     34       if type.waitForExistence(timeout: 10), type.isHittable {
     35         return type
     36       }
     37     }
     38     return nil
     39   }
     40 
     41   @MainActor
     42   func recordAccessibilityIssue(_ issue: XCUIAccessibilityAuditIssue) {
     43     let description = [
     44       "Audit type: \(issue.auditType.rawValue)",
     45       issue.compactDescription,
     46       issue.detailedDescription,
     47       issue.element?.debugDescription ?? "No associated accessibility element",
     48     ].joined(separator: "\n")
     49     let attachment = XCTAttachment(string: description)
     50     attachment.name = "Accessibility issue details"
     51     attachment.lifetime = .keepAlways
     52     add(attachment)
     53   }
     54 
     55   @MainActor
     56   func assertDraftOutboxContainsAtLeast(_ app: XCUIApplication, count: Int) {
     57     let sheet = app.descendants(matching: .any)["radroots.add.drafts.sheet"]
     58     let list = sheet.descendants(matching: .collectionView).firstMatch
     59     guard list.waitForExistence(timeout: 10) else {
     60       return XCTFail("The visible Drafts list was unavailable")
     61     }
     62 
     63     for _ in 0 ..< 8 {
     64       list.swipeDown()
     65     }
     66     var identifiers = Set<String>()
     67     let rows = app.descendants(matching: .any).matching(
     68       NSPredicate(format: "identifier BEGINSWITH 'tera.add.submission.' AND NOT identifier CONTAINS '.progress'")
     69     )
     70     for _ in 0 ..< 12 where identifiers.count < count {
     71       for index in 0 ..< rows.count {
     72         let identifier = rows.element(boundBy: index).identifier
     73         if identifier.range(of: "^tera\\.add\\.submission\\.[0-9a-f]{32}$", options: .regularExpression) != nil {
     74           identifiers.insert(identifier)
     75         }
     76       }
     77       if identifiers.count < count {
     78         list.swipeUp()
     79       }
     80     }
     81     XCTAssertGreaterThanOrEqual(
     82       identifiers.count,
     83       count,
     84       "The visible Drafts list omitted persisted outbox rows; identifiers=\(identifiers.sorted())"
     85     )
     86   }
     87 
     88   @MainActor
     89   func waitForWorkToFinish(
     90     _ app: XCUIApplication,
     91     submit: XCUIElement,
     92     status: XCUIElement,
     93     priorStatusLabel: String?,
     94     priorSubmitValue: String?
     95   ) -> Bool {
     96     let addRoot = app.descendants(matching: .any)["radroots.add.root"]
     97     let progress = app.descendants(matching: .any)["radroots.add.progress"]
     98     let submissionProgress = app.descendants(matching: .any)["tera.add.submission.progress"]
     99     let started = NSPredicate { _, _ in
    100       if progress.exists || submissionProgress.exists || addRoot.value as? String == "Working" {
    101         return true
    102       }
    103       if status.exists, !status.label.isEmpty, status.label != priorStatusLabel {
    104         return true
    105       }
    106       return submit.exists && submit.value as? String != priorSubmitValue
    107     }
    108     let startExpectation = XCTNSPredicateExpectation(predicate: started, object: app)
    109     _ = XCTWaiter.wait(for: [startExpectation], timeout: 5)
    110     let settled = NSPredicate { _, _ in
    111       addRoot.value as? String == "Ready" && !progress.exists && !submissionProgress.exists
    112     }
    113     let settleExpectation = XCTNSPredicateExpectation(predicate: settled, object: app)
    114     return XCTWaiter.wait(for: [settleExpectation], timeout: 180) == .completed
    115   }
    116 
    117   @MainActor
    118   func terminalControlShowsSettledState(_ submit: XCUIElement) -> Bool {
    119     if submit.isEnabled {
    120       return true
    121     }
    122     // Both complete and temporarily deferred work disable a new effect.
    123     // Successful qualification still requires actual policy completion below.
    124     let recognized = ["Delivery policy complete", "Publication waiting"].contains(submit.label)
    125     XCTAssertTrue(recognized, "Unexpected inactive publication control: \(submit.label)")
    126     return recognized
    127   }
    128 
    129   @MainActor
    130   func finishOriginalPublication(_ app: XCUIApplication, submit: XCUIElement) -> Bool {
    131     for _ in 0 ..< 3 {
    132       scrollTo(app, element: submit)
    133       guard submit.waitForExistence(timeout: 10) else { return false }
    134       if submit.label == "Delivery policy complete" {
    135         return true
    136       }
    137       guard submit.label == "Publication waiting" else {
    138         XCTFail("The original publication did not complete: \(submit.label)")
    139         return false
    140       }
    141       let priorValue = submit.value as? String
    142       let check = app.buttons["Check saved submission status"]
    143       TeraAccessibilityNavigation.scroll(app, to: check, up: false, test: self)
    144       guard check.waitForExistence(timeout: 10), check.isHittable else { return false }
    145       check.tap()
    146       guard waitForWorkToFinish(app, submit: submit, status: app.staticTexts["tera.add.submission.status"],
    147                                 priorStatusLabel: nil, priorSubmitValue: priorValue) else { return false }
    148       scrollTo(app, element: submit)
    149       guard submit.waitForExistence(timeout: 10) else { return false }
    150       if submit.isEnabled {
    151         guard let ready = readySubmit(app), submitAndWait(app, submit: ready) != nil else { return false }
    152       }
    153     }
    154     scrollTo(app, element: submit)
    155     let completed = submit.exists && submit.label == "Delivery policy complete"
    156     XCTAssertTrue(completed, "The original publication did not complete within three visible status/continuation attempts")
    157     return completed
    158   }
    159 
    160   @MainActor
    161   func assertSavedPhotoEditing(_ app: XCUIApplication) {
    162     guard openDrafts(app) else { return XCTFail("The saved editing inventory did not open") }
    163     let reopen = app.buttons.matching(
    164       NSPredicate(format: "identifier BEGINSWITH 'tera.add.composer.' AND label == 'Reopen'")
    165     ).firstMatch
    166     guard reopen.waitForExistence(timeout: 20) else {
    167       return XCTFail("The persisted photo editing inventory omitted its Reopen action")
    168     }
    169     scrollTo(app, element: reopen)
    170     XCTAssertTrue(waitUntilHittable(reopen, timeout: 10))
    171     reopen.tap()
    172     XCTAssertTrue(app.navigationBars["Drafts & outbox"].waitForNonExistence(timeout: 20))
    173     let description = app.descendants(matching: .any)["Describe this photo"]
    174     scrollTo(app, element: description)
    175     XCTAssertEqual(description.value as? String, "A green test image prepared for the photo update",
    176                    "Reopening persisted photo editing must retain the supplied description")
    177   }
    178 }