Add signup UI end to end test

Changelog-None
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit is contained in:
Daniel D’Aquino
2025-10-22 14:25:38 -07:00
parent 67d2b249b6
commit fe09f9da99
7 changed files with 142 additions and 0 deletions

View File

@@ -25,6 +25,23 @@ enum AppAccessibilityIdentifiers: String {
case sign_in_confirm_button case sign_in_confirm_button
// MARK: Sign Up / Create Account
// Prefix: `sign_up`
/// Button to navigate to create account view
case sign_up_option_button
/// Text field for entering name during account creation
case sign_up_name_field
/// Text field for entering bio during account creation
case sign_up_bio_field
/// Button to proceed to the next step after entering profile info
case sign_up_next_button
/// Button to save keys after account creation
case sign_up_save_keys_button
/// Button to skip saving keys
case sign_up_skip_save_keys_button
// MARK: Onboarding // MARK: Onboarding
// Prefix: `onboarding` // Prefix: `onboarding`
@@ -60,6 +77,12 @@ enum AppAccessibilityIdentifiers: String {
/// The profile option in the side menu /// The profile option in the side menu
case side_menu_profile_button case side_menu_profile_button
/// The logout button in the side menu
case side_menu_logout_button
/// The logout confirmation button in the alert dialog
case side_menu_logout_confirm_button
// MARK: Items specific to the user's own profile // MARK: Items specific to the user's own profile
// Prefix: `own_profile` // Prefix: `own_profile`

View File

@@ -55,10 +55,12 @@ struct CreateAccountView: View, KeyboardReadable {
.foregroundColor(DamusColors.neutral6) .foregroundColor(DamusColors.neutral6)
FormTextInput(NSLocalizedString("Satoshi Nakamoto", comment: "Name of Bitcoin creator(s)."), text: $account.name) FormTextInput(NSLocalizedString("Satoshi Nakamoto", comment: "Name of Bitcoin creator(s)."), text: $account.name)
.textInputAutocapitalization(.words) .textInputAutocapitalization(.words)
.accessibilityIdentifier(AppAccessibilityIdentifiers.sign_up_name_field.rawValue)
FormLabel(NSLocalizedString("Bio", comment: "Label to prompt bio entry for user to describe themself."), optional: true) FormLabel(NSLocalizedString("Bio", comment: "Label to prompt bio entry for user to describe themself."), optional: true)
.foregroundColor(DamusColors.neutral6) .foregroundColor(DamusColors.neutral6)
FormTextInput(NSLocalizedString("Absolute legend.", comment: "Example Bio"), text: $account.about) FormTextInput(NSLocalizedString("Absolute legend.", comment: "Example Bio"), text: $account.about)
.accessibilityIdentifier(AppAccessibilityIdentifiers.sign_up_bio_field.rawValue)
} }
.padding(.top, 25) .padding(.top, 25)
@@ -75,6 +77,7 @@ struct CreateAccountView: View, KeyboardReadable {
.disabled(profileUploadObserver.isLoading || account.name.isEmpty) .disabled(profileUploadObserver.isLoading || account.name.isEmpty)
.opacity(profileUploadObserver.isLoading || account.name.isEmpty ? 0.5 : 1) .opacity(profileUploadObserver.isLoading || account.name.isEmpty ? 0.5 : 1)
.padding(.top, 20) .padding(.top, 20)
.accessibilityIdentifier(AppAccessibilityIdentifiers.sign_up_next_button.rawValue)
LoginPrompt() LoginPrompt()
.padding(.top) .padding(.top)

View File

@@ -452,6 +452,7 @@ struct CreateAccountPrompt: View {
Button(NSLocalizedString("Create account", comment: "Button to navigate to create account view.")) { Button(NSLocalizedString("Create account", comment: "Button to navigate to create account view.")) {
nav.push(route: Route.CreateAccount) nav.push(route: Route.CreateAccount)
} }
.accessibilityIdentifier(AppAccessibilityIdentifiers.sign_up_option_button.rawValue)
Spacer() Spacer()
} }

View File

@@ -99,6 +99,7 @@ struct SaveKeysView: View {
} }
.buttonStyle(GradientButtonStyle()) .buttonStyle(GradientButtonStyle())
.padding(.top, 20) .padding(.top, 20)
.accessibilityIdentifier(AppAccessibilityIdentifiers.sign_up_save_keys_button.rawValue)
Button(action: { Button(action: {
Task { await complete_account_creation(account) } Task { await complete_account_creation(account) }
@@ -111,6 +112,7 @@ struct SaveKeysView: View {
} }
.buttonStyle(NeutralButtonStyle(padding: EdgeInsets(top: 15, leading: 15, bottom: 15, trailing: 15), cornerRadius: 12)) .buttonStyle(NeutralButtonStyle(padding: EdgeInsets(top: 15, leading: 15, bottom: 15, trailing: 15), cornerRadius: 12))
.padding(.top, 20) .padding(.top, 20)
.accessibilityIdentifier(AppAccessibilityIdentifiers.sign_up_skip_save_keys_button.rawValue)
} }
} }
.padding(20) .padding(20)

View File

@@ -44,6 +44,7 @@ struct SetupView: View {
.frame(minWidth: 300, maxWidth: .infinity, maxHeight: 12, alignment: .center) .frame(minWidth: 300, maxWidth: .infinity, maxHeight: 12, alignment: .center)
} }
.buttonStyle(GradientButtonStyle()) .buttonStyle(GradientButtonStyle())
.accessibilityIdentifier(AppAccessibilityIdentifiers.sign_up_option_button.rawValue)
.padding(.horizontal) .padding(.horizontal)
Button(action: { Button(action: {

View File

@@ -87,6 +87,7 @@ struct SideMenuView: View {
}, label: { }, label: {
navLabel(title: NSLocalizedString("Logout", comment: "Sidebar menu label to sign out of the account."), img: "logout") navLabel(title: NSLocalizedString("Logout", comment: "Sidebar menu label to sign out of the account."), img: "logout")
}) })
.accessibilityIdentifier(AppAccessibilityIdentifiers.side_menu_logout_button.rawValue)
} }
} }
@@ -211,6 +212,7 @@ struct SideMenuView: View {
Button(NSLocalizedString("Logout", comment: "Button for logging out the user."), role: .destructive) { Button(NSLocalizedString("Logout", comment: "Button for logging out the user."), role: .destructive) {
logout(damus_state) logout(damus_state)
} }
.accessibilityIdentifier(AppAccessibilityIdentifiers.side_menu_logout_confirm_button.rawValue)
} message: { } message: {
Text("Make sure your nsec account key is saved before you logout or you will lose access to this account", comment: "Reminder message in alert to get customer to verify that their private security account key is saved saved before logging out.") Text("Make sure your nsec account key is saved before you logout or you will lose access to this account", comment: "Reminder message in alert to get customer to verify that their private security account key is saved saved before logging out.")
} }

View File

@@ -57,6 +57,116 @@ class damusUITests: XCTestCase {
guard app.buttons[AID.own_profile_banner_image_edit_from_url.rawValue].waitForExistence(timeout: 5) else { throw DamusUITestError.timeout_waiting_for_element } guard app.buttons[AID.own_profile_banner_image_edit_from_url.rawValue].waitForExistence(timeout: 5) else { throw DamusUITestError.timeout_waiting_for_element }
} }
/// Tests the sign up flow to ensure users can successfully create a new account.
/// This test verifies:
/// 1. The "Create account" button is accessible
/// 2. Users can enter their name and bio
/// 3. The "Next" button becomes enabled after entering required information
/// 4. Users reach the save keys screen
/// 5. Users can skip saving keys and complete onboarding
func testSignUpFlow() throws {
try logoutIfNotAlready()
// Verify we're on the initial screen with sign up option
guard app.buttons[AID.sign_up_option_button.rawValue].waitForExistence(timeout: 5) else {
throw DamusUITestError.timeout_waiting_for_element
}
// Tap the create account button
app.buttons[AID.sign_up_option_button.rawValue].tap()
// Wait for the create account screen to appear
guard app.textFields[AID.sign_up_name_field.rawValue].waitForExistence(timeout: 5) else {
throw DamusUITestError.timeout_waiting_for_element
}
// Enter name (required field)
let nameField = app.textFields[AID.sign_up_name_field.rawValue]
nameField.tap()
nameField.typeText("Test User")
// Enter bio (optional field)
let bioField = app.textFields[AID.sign_up_bio_field.rawValue]
bioField.tap()
bioField.typeText("This is a test bio")
// Verify the Next button is present and enabled
let nextButton = app.buttons[AID.sign_up_next_button.rawValue]
guard nextButton.waitForExistence(timeout: 5) else {
throw DamusUITestError.timeout_waiting_for_element
}
// Tap Next to proceed to save keys screen
nextButton.tap()
// Verify we reached the save keys screen by checking for the save button
guard app.buttons[AID.sign_up_save_keys_button.rawValue].waitForExistence(timeout: 10) else {
throw DamusUITestError.timeout_waiting_for_element
}
// Verify both save options are present
XCTAssertTrue(app.buttons[AID.sign_up_skip_save_keys_button.rawValue].exists,
"Skip save keys button should be visible")
// Tap "Not now" to skip saving keys and continue to onboarding
app.buttons[AID.sign_up_skip_save_keys_button.rawValue].tap()
// Go through onboarding flow (similar to loginIfNotAlready)
// Select an interest if the interests page appears
app.buttons[AID.onboarding_interest_option_button.rawValue].firstMatch.tapIfExists(timeout: 5)
app.buttons[AID.onboarding_interest_page_next_page.rawValue].tapIfExists(timeout: 5)
// Continue through content settings page
app.buttons[AID.onboarding_content_settings_page_next_page.rawValue].tapIfExists(timeout: 5)
// Skip any remaining onboarding sheets
app.buttons[AID.onboarding_sheet_skip_button.rawValue].tapIfExists(timeout: 5)
// Cancel post composer if it appears
app.buttons[AID.post_composer_cancel_button.rawValue].tapIfExists(timeout: 5)
// Verify we've reached the main app interface by checking for the side menu button
guard app.buttons[AID.main_side_menu_button.rawValue].waitForExistence(timeout: 10) else {
throw DamusUITestError.timeout_waiting_for_element
}
}
func logoutIfNotAlready() throws {
// First, check if user is already logged in and logout if needed
if app.buttons[AID.main_side_menu_button.rawValue].waitForExistence(timeout: 5) {
// User is already logged in, need to logout first
try logout()
}
}
func logout() throws {
app.buttons[AID.main_side_menu_button.rawValue].tap()
guard app.buttons[AID.side_menu_logout_button.rawValue].waitForExistence(timeout: 5) else {
throw DamusUITestError.timeout_waiting_for_element
}
app.buttons[AID.side_menu_logout_button.rawValue].tap()
// Handle logout confirmation dialog (system alert)
// Wait for the alert to appear
let alert = app.alerts.firstMatch
guard alert.waitForExistence(timeout: 5) else {
throw DamusUITestError.timeout_waiting_for_element
}
// Tap the confirm button in the alert
let confirmButton = alert.buttons[AID.side_menu_logout_confirm_button.rawValue].firstMatch
guard confirmButton.waitForExistence(timeout: 5) else {
throw DamusUITestError.timeout_waiting_for_element
}
confirmButton.tap()
// Wait a moment for logout to complete
sleep(2)
}
func loginIfNotAlready() throws { func loginIfNotAlready() throws {
if app.buttons[AID.sign_in_option_button.rawValue].waitForExistence(timeout: 5) { if app.buttons[AID.sign_in_option_button.rawValue].waitForExistence(timeout: 5) {
try self.login() try self.login()