Add signup UI end to end test
Changelog-None Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit is contained in:
@@ -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`
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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: {
|
||||||
|
|||||||
@@ -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.")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
Reference in New Issue
Block a user