-
Notifications
You must be signed in to change notification settings - Fork 490
Expand file tree
/
Copy pathTestUtils.swift
More file actions
151 lines (126 loc) · 5.43 KB
/
TestUtils.swift
File metadata and controls
151 lines (126 loc) · 5.43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
import Foundation
import XCTest
// MARK: - Email Generation
func createEmail() -> String {
let before = UUID().uuidString.prefix(8)
let after = UUID().uuidString.prefix(6)
return "\(before)@\(after).com"
}
// MARK: - App Configuration
/// Creates and configures an XCUIApplication with default test launch arguments
@MainActor func createTestApp(mfaEnabled: Bool = false) -> XCUIApplication {
let app = XCUIApplication()
app.launchArguments.append("--test-view-enabled")
if mfaEnabled {
app.launchArguments.append("--mfa-enabled")
}
return app
}
// MARK: - Alert Handling
@MainActor func dismissAlert(app: XCUIApplication) {
if app.scrollViews.otherElements.buttons["Not Now"].waitForExistence(timeout: 2) {
app.scrollViews.otherElements.buttons["Not Now"].tap()
}
}
// MARK: - User Creation
/// Helper to create a test user in the emulator via REST API (avoids keychain issues)
@MainActor func createTestUser(email: String, password: String = "123456",
verifyEmail: Bool = false) async throws {
// Use Firebase Auth emulator REST API directly to avoid keychain access issues in UI tests
let signUpUrl =
"http://127.0.0.1:9099/identitytoolkit.googleapis.com/v1/accounts:signUp?key=fake-api-key"
guard let url = URL(string: signUpUrl) else {
throw NSError(domain: "TestError", code: 1,
userInfo: [NSLocalizedDescriptionKey: "Invalid emulator URL"])
}
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let body: [String: Any] = [
"email": email,
"password": password,
"returnSecureToken": true,
]
request.httpBody = try JSONSerialization.data(withJSONObject: body)
let (data, response) = try await URLSession.shared.data(for: request)
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
let errorBody = String(data: data, encoding: .utf8) ?? "Unknown error"
throw NSError(domain: "TestError", code: 2,
userInfo: [NSLocalizedDescriptionKey: "Failed to create user: \(errorBody)"])
}
// If email verification is requested, verify the email
if verifyEmail {
// Parse the response to get the idToken
if let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
let idToken = json["idToken"] as? String {
try await verifyEmailInEmulator(email: email, idToken: idToken)
}
}
}
// MARK: - Email Verification
/// Verifies an email address in the emulator using the OOB code mechanism
@MainActor func verifyEmailInEmulator(email: String,
idToken: String,
projectID: String = "flutterfire-e2e-tests",
emulatorHost: String = "127.0.0.1:9099") async throws {
let base = "http://\(emulatorHost)"
// Step 1: Trigger email verification (creates OOB code in emulator)
var sendReq = URLRequest(
url: URL(
string: "\(base)/identitytoolkit.googleapis.com/v1/accounts:sendOobCode?key=fake-api-key"
)!
)
sendReq.httpMethod = "POST"
sendReq.setValue("application/json", forHTTPHeaderField: "Content-Type")
sendReq.httpBody = try JSONSerialization.data(withJSONObject: [
"requestType": "VERIFY_EMAIL",
"idToken": idToken,
])
let (_, sendResp) = try await URLSession.shared.data(for: sendReq)
guard let http = sendResp as? HTTPURLResponse, http.statusCode == 200 else {
throw NSError(domain: "EmulatorError", code: 1,
userInfo: [NSLocalizedDescriptionKey: "Failed to send verification email"])
}
// Step 2: Fetch OOB codes from emulator
let oobURL = URL(string: "\(base)/emulator/v1/projects/\(projectID)/oobCodes")!
let (oobData, oobResp) = try await URLSession.shared.data(from: oobURL)
guard (oobResp as? HTTPURLResponse)?.statusCode == 200 else {
throw NSError(domain: "EmulatorError", code: 2,
userInfo: [NSLocalizedDescriptionKey: "Failed to fetch OOB codes"])
}
struct OobEnvelope: Decodable { let oobCodes: [OobItem] }
struct OobItem: Decodable {
let oobCode: String
let email: String
let requestType: String
let creationTime: String?
}
let envelope = try JSONDecoder().decode(OobEnvelope.self, from: oobData)
// Step 3: Find most recent VERIFY_EMAIL code for this email
let iso = ISO8601DateFormatter()
let codeItem = envelope.oobCodes
.filter {
$0.email.caseInsensitiveCompare(email) == .orderedSame && $0.requestType == "VERIFY_EMAIL"
}
.sorted {
let d0 = $0.creationTime.flatMap { iso.date(from: $0) } ?? .distantPast
let d1 = $1.creationTime.flatMap { iso.date(from: $0) } ?? .distantPast
return d0 > d1
}
.first
guard let oobCode = codeItem?.oobCode else {
throw NSError(domain: "EmulatorError", code: 3,
userInfo: [
NSLocalizedDescriptionKey: "No VERIFY_EMAIL OOB code found for \(email)",
])
}
// Step 4: Apply the OOB code (simulate clicking verification link)
let verifyURL =
URL(string: "\(base)/emulator/action?mode=verifyEmail&oobCode=\(oobCode)&apiKey=fake-api-key")!
let (_, verifyResp) = try await URLSession.shared.data(from: verifyURL)
guard (verifyResp as? HTTPURLResponse)?.statusCode == 200 else {
throw NSError(domain: "EmulatorError", code: 4,
userInfo: [NSLocalizedDescriptionKey: "Failed to apply OOB code"])
}
}