Files
damus/damusUITests/damusUITests.swift
Daniel D’Aquino fe09f9da99 Add signup UI end to end test
Changelog-None
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2025-10-22 14:25:38 -07:00

206 lines
8.9 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// damusUITests.swift
// damusUITests
//
// Created by William Casarin on 2022-04-01.
//
import XCTest
class damusUITests: XCTestCase {
var app = XCUIApplication()
typealias AID = AppAccessibilityIdentifiers
override func setUpWithError() throws {
// Put setup code here. This method is called before the invocation of each test method in the class.
self.app = XCUIApplication()
// In UI tests it is usually best to stop immediately when a failure occurs.
continueAfterFailure = false
// In UI tests its important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
// Set app language to English
app.launchArguments += ["-AppleLanguages", "(en)"]
app.launchArguments += ["-AppleLocale", "en_US"]
// Force portrait orientation
XCUIDevice.shared.orientation = .portrait
// Optional: Reset the device's orientation before each test
addTeardownBlock {
XCUIDevice.shared.orientation = .portrait
}
app.launch()
}
override func tearDownWithError() throws {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}
/// Tests if banner edit button is clickable.
/// Note: This is able to detect if the button is obscured by an invisible overlaying object.
/// See https://github.com/damus-io/damus/issues/2636 for the kind of issue this guards against.
func testEditBannerImage() throws {
// Use XCTAssert and related functions to verify your tests produce the correct results.
try self.loginIfNotAlready()
guard app.buttons[AID.main_side_menu_button.rawValue].tapIfExists(timeout: 5) else { throw DamusUITestError.timeout_waiting_for_element }
guard app.buttons[AID.side_menu_profile_button.rawValue].tapIfExists(timeout: 5) else { throw DamusUITestError.timeout_waiting_for_element }
guard app.buttons[AID.own_profile_edit_button.rawValue].tapIfExists(timeout: 5) else { throw DamusUITestError.timeout_waiting_for_element }
guard app.buttons[AID.own_profile_banner_image_edit_button.rawValue].waitForExistence(timeout: 5) else { throw DamusUITestError.timeout_waiting_for_element }
let bannerEditButtonCoordinates = app.buttons[AID.own_profile_banner_image_edit_button.rawValue].coordinate(withNormalizedOffset: CGVector.zero).withOffset(CGVector(dx: 15, dy: 15))
bannerEditButtonCoordinates.tap()
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 {
if app.buttons[AID.sign_in_option_button.rawValue].waitForExistence(timeout: 5) {
try self.login()
}
app.buttons[AID.onboarding_interest_option_button.rawValue].firstMatch.tapIfExists(timeout: 5)
app.buttons[AID.onboarding_interest_page_next_page.rawValue].tapIfExists(timeout: 5)
app.buttons[AID.onboarding_content_settings_page_next_page.rawValue].tapIfExists(timeout: 5)
app.buttons[AID.onboarding_sheet_skip_button.rawValue].tapIfExists(timeout: 5)
app.buttons[AID.post_composer_cancel_button.rawValue].tapIfExists(timeout: 5)
}
func login() throws {
app.buttons[AID.sign_in_option_button.rawValue].tap()
guard app.secureTextFields[AID.sign_in_nsec_key_entry_field.rawValue].tapIfExists(timeout: 10) else { throw DamusUITestError.timeout_waiting_for_element }
app.typeText("nsec1vxvz8c7070d99njn0aqpcttljnzhfutt422l0r37yep7htesd0mq9p8fg2")
guard app.buttons[AID.sign_in_confirm_button.rawValue].tapIfExists(timeout: 5) else { throw DamusUITestError.timeout_waiting_for_element }
}
enum DamusUITestError: Error {
case timeout_waiting_for_element
}
}
extension XCUIElement {
@discardableResult
func tapIfExists(timeout: TimeInterval) -> Bool {
if self.waitForExistence(timeout: timeout) {
self.tap()
return true
}
return false
}
}