Skip to content

Commit 937da2a

Browse files
Merge pull request #1334 from firebase/fix-ci
2 parents 77c9436 + 6bdb82f commit 937da2a

5 files changed

Lines changed: 200 additions & 140 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,4 @@ Pods/
2929
Podfile.lock
3030
*.xcworkspace/
3131
samples/**/GoogleService-Info.plist
32+
e2eTest/FirebaseSwiftUIExample/FirebaseSwiftUIExampleUITests.xcresult/

e2eTest/FirebaseSwiftUIExample/FirebaseSwiftUIExampleUITests/FirebaseSwiftUIExampleUITests.swift

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -233,22 +233,15 @@ final class FirebaseSwiftUIExampleUITests: XCTestCase {
233233
let emailField = app.textFields["email-field"]
234234

235235
XCTAssertTrue(emailField.waitForExistence(timeout: 2), "Email field should exist")
236-
// Workaround for updating SecureFields with ConnectHardwareKeyboard enabled
237-
UIPasteboard.general.string = email
238-
emailField.press(forDuration: 1.2)
239-
app.menuItems["Paste"].tap()
236+
try pasteIntoField(emailField, text: email, app: app)
240237

241238
let passwordField = app.secureTextFields["password-field"]
242239
XCTAssertTrue(passwordField.exists, "Password field should exist")
243-
UIPasteboard.general.string = password
244-
passwordField.press(forDuration: 1.2)
245-
app.menuItems["Paste"].tap()
240+
try pasteIntoField(passwordField, text: password, app: app)
246241

247242
let confirmPasswordField = app.secureTextFields["confirm-password-field"]
248243
XCTAssertTrue(confirmPasswordField.exists, "Confirm password field should exist")
249-
UIPasteboard.general.string = password
250-
confirmPasswordField.press(forDuration: 1.2)
251-
app.menuItems["Paste"].tap()
244+
try pasteIntoField(confirmPasswordField, text: password, app: app)
252245

253246
// Create the user (sign up)
254247
let signUpButton = app

e2eTest/FirebaseSwiftUIExample/FirebaseSwiftUIExampleUITests/MFAEnrolmentUITests.swift

Lines changed: 64 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -56,23 +56,7 @@ final class MFAEnrollmentUITests: XCTestCase {
5656
// Sign in first to access MFA management
5757
try signInToApp(app: app, email: email)
5858

59-
// Check MFA management button exists
60-
let mfaManagementButton = app.buttons["mfa-management-button"]
61-
XCTAssertTrue(
62-
mfaManagementButton.waitForExistence(timeout: 5),
63-
"MFA management button should exist"
64-
)
65-
XCTAssertTrue(mfaManagementButton.isEnabled, "MFA management button should be enabled")
66-
67-
// Tap the button
68-
mfaManagementButton.tap()
69-
70-
// Verify we navigated to MFA management view
71-
let managementTitle = app.staticTexts["Two-Factor Authentication"]
72-
XCTAssertTrue(
73-
managementTitle.waitForExistence(timeout: 5),
74-
"Should navigate to MFA management view"
75-
)
59+
try navigateToMFAManagement(app: app)
7660
}
7761

7862
@MainActor
@@ -87,16 +71,16 @@ final class MFAEnrollmentUITests: XCTestCase {
8771

8872
// Sign in and navigate to MFA management
8973
try signInToApp(app: app, email: email)
90-
app.buttons["mfa-management-button"].tap()
74+
try navigateToMFAManagement(app: app)
9175

9276
// Tap setup MFA button (for users with no enrolled factors)
9377
let setupButton = app.buttons["setup-mfa-button"]
94-
if setupButton.waitForExistence(timeout: 3) {
78+
if setupButton.waitForExistence(timeout: 10) {
9579
setupButton.tap()
9680
} else {
9781
// If factors are already enrolled, tap add another method
9882
let addMethodButton = app.buttons["add-mfa-method-button"]
99-
XCTAssertTrue(addMethodButton.waitForExistence(timeout: 3), "Add method button should exist")
83+
XCTAssertTrue(addMethodButton.waitForExistence(timeout: 10), "Add method button should exist")
10084
addMethodButton.tap()
10185
}
10286

@@ -448,27 +432,46 @@ final class MFAEnrollmentUITests: XCTestCase {
448432
signedInText.waitForExistence(timeout: 30),
449433
"SignedInView should be visible after login"
450434
)
435+
dismissAlert(app: app)
451436
XCTAssertTrue(signedInText.exists, "SignedInView should be visible after login")
452437
}
453438

439+
@MainActor
440+
private func navigateToMFAManagement(app: XCUIApplication) throws {
441+
dismissAlert(app: app)
442+
443+
let mfaManagementButton = app.buttons["mfa-management-button"]
444+
XCTAssertTrue(
445+
mfaManagementButton.waitForExistence(timeout: 10),
446+
"MFA management button should exist"
447+
)
448+
XCTAssertTrue(mfaManagementButton.isEnabled, "MFA management button should be enabled")
449+
mfaManagementButton.tap()
450+
451+
let managementTitle = app.staticTexts["Two-Factor Authentication"]
452+
XCTAssertTrue(
453+
managementTitle.waitForExistence(timeout: 10),
454+
"Should navigate to MFA management view"
455+
)
456+
}
457+
454458
@MainActor
455459
private func navigateToMFAEnrollment(app: XCUIApplication) throws {
456-
// Navigate to MFA management
457-
app.buttons["mfa-management-button"].tap()
460+
try navigateToMFAManagement(app: app)
458461

459462
// Navigate to MFA enrollment
460463
let setupButton = app.buttons["setup-mfa-button"]
461-
if setupButton.waitForExistence(timeout: 3) {
464+
if setupButton.waitForExistence(timeout: 10) {
462465
setupButton.tap()
463466
} else {
464467
let addMethodButton = app.buttons["add-mfa-method-button"]
465-
XCTAssertTrue(addMethodButton.waitForExistence(timeout: 3), "Add method button should exist")
468+
XCTAssertTrue(addMethodButton.waitForExistence(timeout: 10), "Add method button should exist")
466469
addMethodButton.tap()
467470
}
468471

469472
// Verify we're in MFA enrollment view
470473
let enrollmentTitle = app.staticTexts["Set Up Two-Factor Authentication"]
471-
XCTAssertTrue(enrollmentTitle.waitForExistence(timeout: 5), "Should be in MFA enrollment view")
474+
XCTAssertTrue(enrollmentTitle.waitForExistence(timeout: 10), "Should be in MFA enrollment view")
472475
}
473476
}
474477

@@ -486,49 +489,51 @@ struct VerificationCode: Codable {
486489
/// - Returns: The verification code as a String
487490
/// - Throws: Error if unable to retrieve codes
488491
private func getLastSmsCode(specificPhone: String? = nil) async throws -> String {
489-
let getSmsCodesUrl =
490-
"http://127.0.0.1:9099/emulator/v1/projects/flutterfire-e2e-tests/verificationCodes"
492+
do {
493+
for projectID in authEmulatorCandidateProjectIDs() {
494+
let getSmsCodesUrl =
495+
"http://127.0.0.1:9099/emulator/v1/projects/\(projectID)/verificationCodes"
491496

492-
guard let url = URL(string: getSmsCodesUrl) else {
493-
throw NSError(
494-
domain: "getLastSmsCode",
495-
code: -1,
496-
userInfo: [NSLocalizedDescriptionKey: "Failed to create URL for SMS codes endpoint"]
497-
)
498-
}
497+
guard let url = URL(string: getSmsCodesUrl) else {
498+
continue
499+
}
499500

500-
do {
501-
let (data, _) = try await URLSession.shared.data(from: url)
501+
guard let (data, _) = try? await URLSession.shared.data(from: url) else {
502+
continue
503+
}
502504

503-
let decoder = JSONDecoder()
504-
let codesResponse = try decoder.decode(VerificationCodesResponse.self, from: data)
505+
let decoder = JSONDecoder()
506+
guard let codesResponse = try? decoder.decode(VerificationCodesResponse.self, from: data) else {
507+
continue
508+
}
505509

506-
guard let codes = codesResponse.verificationCodes, !codes.isEmpty else {
507-
throw NSError(
508-
domain: "getLastSmsCode",
509-
code: -1,
510-
userInfo: [NSLocalizedDescriptionKey: "No SMS verification codes found in emulator"]
511-
)
512-
}
510+
guard let codes = codesResponse.verificationCodes, !codes.isEmpty else {
511+
continue
512+
}
513513

514-
if let specificPhone = specificPhone {
515-
// Search backwards through codes for the specific phone number
516-
for code in codes.reversed() {
517-
if code.phoneNumber == specificPhone {
518-
return code.code
514+
if let specificPhone = specificPhone {
515+
// Search backwards through codes for the specific phone number
516+
for code in codes.reversed() {
517+
if code.phoneNumber == specificPhone {
518+
return code.code
519+
}
519520
}
521+
} else if let lastCode = codes.last {
522+
return lastCode.code
520523
}
521-
throw NSError(
522-
domain: "getLastSmsCode",
523-
code: -1,
524-
userInfo: [
525-
NSLocalizedDescriptionKey: "No SMS verification code found for phone number: \(specificPhone)",
526-
]
527-
)
524+
}
525+
526+
let description = if let specificPhone {
527+
"No SMS verification code found for phone number: \(specificPhone)"
528528
} else {
529-
// Return the last code in the array
530-
return codes.last!.code
529+
"No SMS verification codes found in emulator"
531530
}
531+
532+
throw NSError(
533+
domain: "getLastSmsCode",
534+
code: -1,
535+
userInfo: [NSLocalizedDescriptionKey: description]
536+
)
532537
} catch let error as DecodingError {
533538
throw NSError(
534539
domain: "getLastSmsCode",

e2eTest/FirebaseSwiftUIExample/FirebaseSwiftUIExampleUITests/MFAResolutionUITests.swift

Lines changed: 49 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -266,63 +266,64 @@ final class MFAResolutionUITests: XCTestCase {
266266
@MainActor
267267
private func getSMSVerificationCode(for phoneNumber: String,
268268
codeType: String = "enrollment") async -> String? {
269-
let emulatorUrl =
270-
"http://127.0.0.1:9099/emulator/v1/projects/flutterfire-e2e-tests/verificationCodes"
271-
272-
guard let url = URL(string: emulatorUrl) else {
273-
return nil
274-
}
275-
276269
do {
277-
let (data, _) = try await URLSession.shared.data(from: url)
270+
for projectID in authEmulatorCandidateProjectIDs() {
271+
let emulatorUrl =
272+
"http://127.0.0.1:9099/emulator/v1/projects/\(projectID)/verificationCodes"
278273

279-
guard let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
280-
let codes = json["verificationCodes"] as? [[String: Any]] else {
281-
print("❌ Failed to parse verification codes")
282-
return nil
283-
}
284-
285-
// Filter codes by phone number and type, then get the most recent one
286-
let matchingCodes = codes.filter { codeInfo in
287-
guard let phone = codeInfo["phoneNumber"] as? String else {
288-
print("❌ Code missing phoneNumber field")
289-
return false
274+
guard let url = URL(string: emulatorUrl) else {
275+
continue
290276
}
291277

292-
// The key difference between enrollment and verification codes:
293-
// - Enrollment codes have full phone numbers (e.g., "+15551234567")
294-
// - Verification codes have masked phone numbers (e.g., "+*******4567")
295-
let isMasked = phone.contains("*")
296-
297-
// Match phone number
298-
let phoneMatches: Bool
299-
if isMasked {
300-
// Extract last 4 digits from both numbers
301-
let last4OfResponse = String(phone.suffix(4))
302-
let last4OfTarget = String(phoneNumber.suffix(4))
303-
phoneMatches = last4OfResponse == last4OfTarget
304-
} else {
305-
// Full phone number match
306-
phoneMatches = phone == phoneNumber
307-
}
278+
let (data, _) = try await URLSession.shared.data(from: url)
308279

309-
guard phoneMatches else {
310-
return false
280+
guard let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
281+
let codes = json["verificationCodes"] as? [[String: Any]] else {
282+
continue
311283
}
312284

313-
if codeType == "enrollment" {
314-
// Enrollment codes have unmasked phone numbers
315-
return !isMasked
316-
} else { // "verification"
317-
// Verification codes have masked phone numbers
318-
return isMasked
285+
// Filter codes by phone number and type, then get the most recent one
286+
let matchingCodes = codes.filter { codeInfo in
287+
guard let phone = codeInfo["phoneNumber"] as? String else {
288+
print("❌ Code missing phoneNumber field")
289+
return false
290+
}
291+
292+
// The key difference between enrollment and verification codes:
293+
// - Enrollment codes have full phone numbers (e.g., "+15551234567")
294+
// - Verification codes have masked phone numbers (e.g., "+*******4567")
295+
let isMasked = phone.contains("*")
296+
297+
// Match phone number
298+
let phoneMatches: Bool
299+
if isMasked {
300+
// Extract last 4 digits from both numbers
301+
let last4OfResponse = String(phone.suffix(4))
302+
let last4OfTarget = String(phoneNumber.suffix(4))
303+
phoneMatches = last4OfResponse == last4OfTarget
304+
} else {
305+
// Full phone number match
306+
phoneMatches = phone == phoneNumber
307+
}
308+
309+
guard phoneMatches else {
310+
return false
311+
}
312+
313+
if codeType == "enrollment" {
314+
// Enrollment codes have unmasked phone numbers
315+
return !isMasked
316+
} else { // "verification"
317+
// Verification codes have masked phone numbers
318+
return isMasked
319+
}
319320
}
320-
}
321321

322-
// Get the last matching code (most recent)
323-
if let lastCode = matchingCodes.last,
324-
let code = lastCode["code"] as? String {
325-
return code
322+
// Get the last matching code (most recent)
323+
if let lastCode = matchingCodes.last,
324+
let code = lastCode["code"] as? String {
325+
return code
326+
}
326327
}
327328

328329
print("❌ No matching code found")

0 commit comments

Comments
 (0)