diff --git a/FlipcashUITests/Smoke/DisplayNameSmokeTests.swift b/FlipcashUITests/Smoke/DisplayNameSmokeTests.swift index ac59c7bf3..9b17c9816 100644 --- a/FlipcashUITests/Smoke/DisplayNameSmokeTests.swift +++ b/FlipcashUITests/Smoke/DisplayNameSmokeTests.swift @@ -69,12 +69,37 @@ final class DisplayNameSmokeTests: BaseUITestCase { let seeded = (field.value as? String) ?? "" XCTAssertFalse(seeded.isEmpty, "Expected the editor to be seeded with the current name") field.typeText(String(repeating: XCUIKeyboardKey.delete.rawValue, count: seeded.count)) - XCTAssertFalse(save.isEnabled, "Save must stay disabled while the name is empty") + + // Wait for the disabled state rather than sampling it: the emptied field + // reaches the button on SwiftUI's next render, which under load lands + // after the typing settles. The field is already empty by then — the + // instantaneous read just catches the state the button is leaving. + let saveDisabled = XCTNSPredicateExpectation( + predicate: NSPredicate(format: "isEnabled == false"), + object: save + ) + XCTAssertEqual( + XCTWaiter().wait(for: [saveDisabled], timeout: 5), .completed, + "Save must stay disabled while the name is empty. Field: [\((field.value as? String) ?? "")]" + ) field.typeText("Renamed \(Int.random(in: 1_000...9_999))") XCTAssertTrue(save.isEnabled, "Save must enable once the name is valid and changed") save.tap() + // Replacing a name that is already set is confirmed first; only the + // first name set during onboarding saves straight through. The editor + // stays up behind the dialog until it is answered. + let confirmDialog = app.otherElements["Change Display Name?"] + XCTAssertTrue( + confirmDialog.waitForExistence(timeout: 30), + "Expected the rename confirmation dialog. On screen: [\(visibleText())]" + ) + waitUntilHittableAndTap( + confirmDialog.buttons["Change Display Name"], + "Expected the dialog's Change Display Name action to be tappable" + ) + // `ProfileNameScreen(completion: .back)` pops itself only once // `SetDisplayName` returns, so landing back on My Account is proof the // new name was accepted and moderated — a rejection keeps the editor up