field_ios

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

TeraAccessibilityUITestSurface.swift (5969B)


      1 #if DEBUG
      2   import RadrootsKit
      3   import SwiftUI
      4 
      5   /// Isolated presentation qualification: real stores and SQLite, no credentials
      6   /// or external relays. The photo fixture cannot confirm durability or upload bytes.
      7   struct TeraAccessibilityUITestSurface: View {
      8     @StateObject private var fixture = TeraAccessibilityFixture()
      9     @Environment(\.accessibilityReduceMotion) private var reduceMotion
     10     @Environment(\.dynamicTypeSize) private var textSize
     11     private let scenario = ProcessInfo.processInfo.environment["TERA_IOS_UI_TEST_ACCESSIBILITY"]
     12 
     13     var body: some View {
     14       Group {
     15         if scenario == "failure" || scenario == "identity" {
     16           RuntimeStatusView(phase: scenario == "identity" ? .identityRequired : .failed(.local(
     17             operation: "fixture", code: "ios.fixture.unavailable", safeMessage: "Local state is unavailable."
     18           )), retry: { fixture.retried = true }, createIdentity: {}, importIdentity: { _ in },
     19                             unlockIdentity: {}, recoverIdentity: {}, applyConfigurationReconfiguration: {})
     20           .overlay(alignment: .bottom) {
     21             if fixture.retried {
     22               Text("Local state checked").accessibilityIdentifier("tera.test.accessibility.retried")
     23             }
     24           }
     25         } else if let stores = fixture.stores, let snapshot = fixture.snapshot {
     26           TeraRootShell(snapshot: snapshot, stores: stores)
     27         } else {
     28           Text(fixture.failure ?? "Preparing isolated accessibility fixture")
     29         }
     30       }
     31       .accessibilityElement(children: .contain)
     32       .accessibilityIdentifier("tera.test.accessibility.root")
     33       .accessibilityValue(Text(verbatim: "text=\(textSize); reduceMotion=\(reduceMotion)"))
     34       .task { await fixture.start() }
     35       .overlay {
     36         if scenario == "save-error", !fixture.hasPreparedSaveError {
     37           Button("Prepare unconfirmed Save") {
     38             Task { await fixture.prepareSaveError() }
     39           }
     40           .buttonStyle(.borderedProminent)
     41         }
     42       }
     43     }
     44   }
     45 
     46   @MainActor
     47   private final class TeraAccessibilityFixture: ObservableObject {
     48     @Published var stores: TeraProductStores?
     49     @Published var snapshot: TeraRuntimeSnapshot?
     50     @Published var failure: String?
     51     @Published var retried = false
     52     @Published var hasPreparedSaveError = false
     53     private let client = TeraRuntimeClient.production()
     54 
     55     func start() async {
     56       guard stores == nil, failure == nil else { return }
     57       do {
     58         let root = FileManager.default.temporaryDirectory.appendingPathComponent("tera-accessibility-" + UUID().uuidString)
     59         try FileManager.default.createDirectory(at: root, withIntermediateDirectories: true)
     60         let publicKey = "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
     61         let roots = try RadrootsAppleFileRoots(appIdentifier: "test.tera.accessibility", dataRoot: root,
     62                                                cacheRoot: root, temporaryRoot: root)
     63         let mobileStore = try RadrootsAppleMobileStore.prepare(roots: roots, publicKeyHex: publicKey,
     64                                                                protectedDataAvailability: .available)
     65         let configuration = TeraRuntimeLaunchConfiguration(
     66           applicationSupportDirectory: mobileStore.applicationSupportDirectory.path,
     67           publicKeyHex: publicKey,
     68           sourceGenerationHex: String(repeating: "04", count: 32), sourceGenerationCreatedAtUnixMilliseconds: 1_800_000_000_000,
     69           protectedData: .available, networkProfile: .simulator, writableRelays: ["ws://127.0.0.1:19999"], blossom: nil,
     70           app: .init(bundleIdentifier: "test.tera.accessibility", version: "1", buildNumber: "1", buildSHA: nil),
     71           signerGeneration: "accessibility-fixture", signer: TeraAccessibilitySigner(), adoptBootstrapSettings: false
     72         )
     73         let snapshot = try await client.start(configuration: configuration)
     74         let stores = TeraProductStores(runtimeClient: client, addMedia: TeraAccessibilityMedia())
     75         stores.configure(snapshot: snapshot)
     76         await stores.resume()
     77         if let label = ProcessInfo.processInfo.environment["TERA_IOS_UI_TEST_FORM"],
     78           let type = TeraAddCommandType.allCases.first(where: { $0.label == label })
     79         {
     80           stores.add.selectType(type)
     81         }
     82         self.snapshot = snapshot
     83         self.stores = stores
     84       } catch {
     85         if case let TeraRuntimeClientError.startup(report) = error {
     86           failure = "Accessibility fixture failed to start: " + report.code
     87         } else {
     88           failure = "Accessibility fixture failed to start."
     89         }
     90       }
     91     }
     92 
     93     func prepareSaveError() async {
     94       guard let stores, !hasPreparedSaveError else { return }
     95       await stores.add.importPhotos()
     96       await stores.add.save()
     97       hasPreparedSaveError = true
     98     }
     99   }
    100 
    101   private struct TeraAccessibilitySigner: TeraRuntimeSigner {
    102     func availability() async -> TeraRuntimeSignerAvailability {
    103       .unavailable
    104     }
    105 
    106     func sign(_: TeraRuntimeSigningRequest) async -> TeraRuntimeSigningOutcome {
    107       .unavailable
    108     }
    109   }
    110 
    111   private struct TeraAccessibilityMedia: TeraAddMediaHandling {
    112     func support() async throws -> TeraAddMediaSupport {
    113       .init(library: true, camera: false)
    114     }
    115 
    116     func importImages(limit: Int) async throws -> [TeraPreparedMedia] {
    117       guard limit > 0 else { return [] }
    118       return [.init(opaqueReference: "media:" + String(repeating: "0", count: 64), remoteURL: nil,
    119                     sha256: String(repeating: "0", count: 64), mediaType: "image/png", byteSize: 4,
    120                     width: 2, height: 2, alt: "", preparedAtUnixSeconds: 1_800_000_000)]
    121     }
    122 
    123     func captureImage() async throws -> TeraPreparedMedia {
    124       throw TeraComposerAcknowledgment.unconfirmed
    125     }
    126 
    127     func open(_: [TeraPreparedMedia]) async throws -> TeraOpenedMedia {
    128       throw TeraComposerAcknowledgment.unconfirmed
    129     }
    130   }
    131 #endif