Skip to content

Commit 63adf26

Browse files
test: harden email and password input that causes tests to flake occasionally
1 parent 56767c4 commit 63adf26

3 files changed

Lines changed: 103 additions & 27 deletions

File tree

e2eTest/FirebaseSwiftUIExample/FirebaseSwiftUIExampleUITests/FirebaseSwiftUIExampleUITests.swift

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -139,13 +139,11 @@ final class FirebaseSwiftUIExampleUITests: XCTestCase {
139139

140140
let emailField = app.textFields["email-field"]
141141
XCTAssertTrue(emailField.waitForExistence(timeout: 6), "Email field should exist")
142-
emailField.tap()
143-
emailField.typeText(email)
142+
try enterText(email, into: emailField, app: app)
144143

145144
let passwordField = app.secureTextFields["password-field"]
146145
XCTAssertTrue(passwordField.exists, "Password field should exist")
147-
passwordField.tap()
148-
passwordField.typeText(password)
146+
try enterText(password, into: passwordField, app: app)
149147

150148
let signInButton = app.buttons["sign-in-button"]
151149
XCTAssertTrue(signInButton.exists, "Sign-In button should exist")
@@ -233,20 +231,24 @@ final class FirebaseSwiftUIExampleUITests: XCTestCase {
233231
let emailField = app.textFields["email-field"]
234232

235233
XCTAssertTrue(emailField.waitForExistence(timeout: 2), "Email field should exist")
236-
try pasteIntoField(emailField, text: email, app: app)
234+
try enterText(email, into: emailField, app: app)
237235

238236
let passwordField = app.secureTextFields["password-field"]
239237
XCTAssertTrue(passwordField.exists, "Password field should exist")
240-
try pasteIntoField(passwordField, text: password, app: app)
238+
try enterText(password, into: passwordField, app: app)
241239

242240
let confirmPasswordField = app.secureTextFields["confirm-password-field"]
243241
XCTAssertTrue(confirmPasswordField.exists, "Confirm password field should exist")
244-
try pasteIntoField(confirmPasswordField, text: password, app: app)
242+
try enterText(password, into: confirmPasswordField, app: app)
245243

246244
// Create the user (sign up)
247245
let signUpButton = app
248246
.buttons["sign-in-button"] // This button changes context after switch-auth-flow
249247
XCTAssertTrue(signUpButton.exists, "Sign-Up button should exist")
248+
XCTAssertTrue(
249+
waitForElementToBecomeEnabled(signUpButton, timeout: 5),
250+
"Sign-up button should become enabled after entering credentials"
251+
)
250252
signUpButton.tap()
251253

252254
// Wait for user creation and signed-in view to appear

e2eTest/FirebaseSwiftUIExample/FirebaseSwiftUIExampleUITests/MFAEnrolmentUITests.swift

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -212,14 +212,11 @@ final class MFAEnrollmentUITests: XCTestCase {
212212
// Generate unique phone number using timestamp to avoid conflicts between tests
213213
let uniqueId = Int(Date().timeIntervalSince1970 * 1000) % 1_000_000
214214
let phoneNumberWithoutDialCode = "7\(String(format: "%09d", uniqueId))"
215-
UIPasteboard.general.string = phoneNumberWithoutDialCode
216-
phoneField.tap()
217-
phoneField.press(forDuration: 1.2)
218-
app.menuItems["Paste"].tap()
215+
try enterText(phoneNumberWithoutDialCode, into: phoneField, app: app)
219216

220217
let displayNameField = app.textFields["display-name-field"]
221218
XCTAssertTrue(displayNameField.waitForExistence(timeout: 10))
222-
try pasteIntoField(displayNameField, text: "test user", app: app)
219+
try enterText("test user", into: displayNameField, app: app)
223220

224221
let sendCodeButton = app.buttons["send-sms-button"]
225222
XCTAssertTrue(sendCodeButton.waitForExistence(timeout: 10))
@@ -406,26 +403,28 @@ final class MFAEnrollmentUITests: XCTestCase {
406403
// Fill email field
407404
let emailField = app.textFields["email-field"]
408405
XCTAssertTrue(emailField.waitForExistence(timeout: 10), "Email field should exist")
409-
// Workaround for updating SecureFields with ConnectHardwareKeyboard enabled
410-
try pasteIntoField(emailField, text: email, app: app)
406+
try enterText(email, into: emailField, app: app)
411407

412408
// Fill password field
413409
let passwordField = app.secureTextFields["password-field"]
414410
XCTAssertTrue(passwordField.exists, "Password field should exist")
415-
try pasteIntoField(passwordField, text: password, app: app)
411+
try enterText(password, into: passwordField, app: app)
416412

417413
// Create the user (sign up)
418414
let signUpButton = app
419415
.buttons["sign-in-button"] // This button changes context after switch-auth-flow
420416
XCTAssertTrue(signUpButton.exists, "Sign-up button should exist")
417+
XCTAssertTrue(
418+
waitForElementToBecomeEnabled(signUpButton, timeout: 5),
419+
"Sign-up button should become enabled after entering credentials"
420+
)
421421
signUpButton.tap()
422422

423423
let notNowButton = app.scrollViews.containing(.button, identifier: "Not Now").firstMatch
424424
if notNowButton.waitForExistence(timeout: 5) {
425425
notNowButton.tap()
426426
}
427427

428-
// Wait for signed-in state
429428
// Wait for signed-in state
430429
let signedInText = app.staticTexts["signed-in-text"]
431430
XCTAssertTrue(

e2eTest/FirebaseSwiftUIExample/FirebaseSwiftUIExampleUITests/TestUtils.swift

Lines changed: 86 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,26 +40,36 @@ func createEmail() -> String {
4040

4141
// MARK: - Text Input Helpers
4242

43-
/// Pastes text into a text field using the system paste menu
44-
/// - Parameters:
45-
/// - field: The XCUIElement representing the text field
46-
/// - text: The text to paste
47-
/// - app: The XCUIApplication instance
48-
@MainActor func pasteIntoField(_ field: XCUIElement, text: String, app: XCUIApplication) throws {
49-
UIPasteboard.general.string = text
43+
@MainActor private func waitForFieldValue(_ field: XCUIElement,
44+
expectedText: String,
45+
timeout: TimeInterval = 2) -> Bool {
46+
let deadline = Date().addingTimeInterval(timeout)
47+
48+
while Date() < deadline {
49+
if (field.value as? String) == expectedText {
50+
return true
51+
}
52+
RunLoop.current.run(until: Date().addingTimeInterval(0.1))
53+
}
54+
55+
return (field.value as? String) == expectedText
56+
}
57+
58+
@MainActor private func showPasteMenu(for field: XCUIElement,
59+
text: String,
60+
app: XCUIApplication) throws -> XCUIElement {
5061
field.tap()
5162

52-
// Give field time to become first responder
63+
// Give field time to become first responder.
5364
usleep(200_000) // 0.2 seconds
5465

55-
// Press and hold to bring up paste menu
66+
// Press and hold to bring up paste menu.
5667
field.press(forDuration: 1.5)
5768

5869
let pasteMenuItem = app.menuItems["Paste"]
5970

60-
// Wait for paste menu to appear
71+
// Fallback to double-tap if the context menu did not appear.
6172
if !pasteMenuItem.waitForExistence(timeout: 3) {
62-
// Fallback: try double tap approach
6373
field.doubleTap()
6474
usleep(300_000) // 0.3 seconds
6575

@@ -74,7 +84,72 @@ func createEmail() -> String {
7484
}
7585
}
7686

87+
return pasteMenuItem
88+
}
89+
90+
@MainActor private func typeIntoField(_ field: XCUIElement,
91+
text: String) throws {
92+
field.tap()
93+
field.typeText(text)
94+
95+
guard waitForFieldValue(field, expectedText: text) else {
96+
throw NSError(
97+
domain: "TestError",
98+
code: 2,
99+
userInfo: [
100+
NSLocalizedDescriptionKey: "Failed to type expected text into field. Text was: \(text)",
101+
]
102+
)
103+
}
104+
}
105+
106+
@MainActor private func pasteIntoSecureField(_ field: XCUIElement,
107+
text: String,
108+
app: XCUIApplication) throws {
109+
let originalValue = field.value as? String
110+
UIPasteboard.general.string = text
111+
let pasteMenuItem = try showPasteMenu(for: field, text: text, app: app)
77112
pasteMenuItem.tap()
113+
usleep(200_000) // 0.2 seconds
114+
UIPasteboard.general.string = nil
115+
116+
guard (field.value as? String) != originalValue else {
117+
throw NSError(
118+
domain: "TestError",
119+
code: 3,
120+
userInfo: [
121+
NSLocalizedDescriptionKey: "Failed to paste expected text into secure field. Text was: \(text)",
122+
]
123+
)
124+
}
125+
}
126+
127+
/// Enters text into a UI test field.
128+
/// - Parameters:
129+
/// - field: The XCUIElement representing the text field
130+
/// - text: The text to enter
131+
/// - app: The XCUIApplication instance
132+
@MainActor func enterText(_ text: String, into field: XCUIElement, app: XCUIApplication) throws {
133+
switch field.elementType {
134+
case .secureTextField:
135+
try pasteIntoSecureField(field, text: text, app: app)
136+
default:
137+
try typeIntoField(field, text: text)
138+
}
139+
}
140+
141+
@MainActor func waitForElementToBecomeEnabled(_ element: XCUIElement,
142+
timeout: TimeInterval = 5) -> Bool {
143+
let deadline = Date().addingTimeInterval(timeout)
144+
145+
while Date() < deadline {
146+
if element.isEnabled {
147+
return true
148+
}
149+
RunLoop.current.run(until: Date().addingTimeInterval(0.1))
150+
}
151+
152+
return element.isEnabled
78153
}
79154

80155
// MARK: - User Creation

0 commit comments

Comments
 (0)