Files
honeyDueKMP/iosApp/CaseraUITests/RegistrationTests.swift
Trey t c6eef720ed Rebrand from MyCrib to Casera
- Rename Kotlin package from com.example.mycrib to com.example.casera
- Update Android app name, namespace, and application ID
- Update iOS bundle identifiers and project settings
- Rename iOS directories (MyCribTests -> CaseraTests, etc.)
- Update deep link schemes from mycrib:// to casera://
- Update app group identifiers
- Update subscription product IDs
- Update all UI strings and branding

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-28 21:10:38 -06:00

601 lines
26 KiB
Swift

import XCTest
#if os(macOS)
import Foundation
#endif
/// Comprehensive registration flow tests
/// Tests the complete registration and email verification flow
///
/// NOTE: Tests that require database access (fetchVerificationCode, cleanupTestUser)
/// use shell scripts that run on the host machine. These tests are designed to run
/// in the iOS Simulator with the backend running locally.
final class RegistrationTests: XCTestCase {
var app: XCUIApplication!
// Test user credentials - using timestamp to ensure unique users
private var testUsername: String {
return "testuser_\(Int(Date().timeIntervalSince1970))"
}
private var testEmail: String {
return "test_\(Int(Date().timeIntervalSince1970))@example.com"
}
private let testPassword = "TestPass123!"
override func setUpWithError() throws {
continueAfterFailure = false
app = XCUIApplication()
app.launch()
ensureLoggedOut()
}
override func tearDownWithError() throws {
// Clean up: logout if logged in
ensureLoggedOut()
app = nil
}
// MARK: - Helper Methods
private func ensureLoggedOut() {
UITestHelpers.ensureLoggedOut(app: app)
}
private func navigateToRegistration() {
let signUpButton = app.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Sign Up'")).firstMatch
XCTAssertTrue(signUpButton.waitForExistence(timeout: 5), "Sign Up button should exist on login screen")
signUpButton.tap()
sleep(1)
}
/// Dismisses the iOS Strong Password suggestion overlay if it appears
private func dismissStrongPasswordSuggestion() {
// Look for "Choose My Own Password" or similar button in the password suggestion
let chooseOwnPassword = app.buttons["Choose My Own Password"]
if chooseOwnPassword.waitForExistence(timeout: 2) {
chooseOwnPassword.tap()
sleep(1)
return
}
// Try alternate labels
let notNowButton = app.buttons["Not Now"]
if notNowButton.exists {
notNowButton.tap()
sleep(1)
return
}
// Try tapping outside the suggestion to dismiss it
let strongPasswordText = app.staticTexts.containing(NSPredicate(format: "label CONTAINS[c] 'Strong Password'")).firstMatch
if strongPasswordText.exists {
// Tap somewhere else to dismiss
app.tap()
sleep(1)
}
}
/// Fixed test verification code - Django uses this code for emails starting with "test_" in DEBUG mode
/// This matches ConfirmationCode.TEST_VERIFICATION_CODE in the Django backend
private let testVerificationCode = "123456"
/// Note: cleanupTestUser should be called from command line after tests complete
/// Run: cd /Users/treyt/Desktop/code/Casera/myCribAPI && python manage.py shell -c "from django.contrib.auth import get_user_model; User = get_user_model(); User.objects.filter(email__startswith='test_').delete()"
private func cleanupTestUser(email: String) {
print("Cleanup test user: \(email)")
print("Run manually if needed: cd /Users/treyt/Desktop/code/Casera/myCribAPI && python manage.py shell -c \"from django.contrib.auth import get_user_model; User = get_user_model(); User.objects.filter(email='\(email)').delete()\"")
}
// MARK: - Registration Form Tests
func testRegistrationScreenElements() {
// Given: User is on login screen
navigateToRegistration()
// Then: Registration form should have all expected elements
let usernameField = app.textFields[AccessibilityIdentifiers.Authentication.registerUsernameField]
let emailField = app.textFields[AccessibilityIdentifiers.Authentication.registerEmailField]
let passwordField = app.secureTextFields[AccessibilityIdentifiers.Authentication.registerPasswordField]
let confirmPasswordField = app.secureTextFields[AccessibilityIdentifiers.Authentication.registerConfirmPasswordField]
let createAccountButton = app.buttons[AccessibilityIdentifiers.Authentication.registerButton]
let cancelButton = app.buttons[AccessibilityIdentifiers.Authentication.registerCancelButton]
XCTAssertTrue(usernameField.waitForExistence(timeout: 5), "Username field should exist")
XCTAssertTrue(emailField.exists, "Email field should exist")
XCTAssertTrue(passwordField.exists, "Password field should exist")
XCTAssertTrue(confirmPasswordField.exists, "Confirm password field should exist")
XCTAssertTrue(createAccountButton.exists, "Create Account button should exist")
XCTAssertTrue(cancelButton.exists, "Cancel button should exist")
}
func testRegistrationWithEmptyFields() {
// Given: User is on registration screen
navigateToRegistration()
// When: User taps Create Account without filling fields
let createAccountButton = app.buttons[AccessibilityIdentifiers.Authentication.registerButton]
XCTAssertTrue(createAccountButton.waitForExistence(timeout: 5))
createAccountButton.tap()
// Then: Error message should appear
sleep(2)
let errorExists = app.staticTexts.containing(NSPredicate(format: "label CONTAINS[c] 'required' OR label CONTAINS[c] 'error' OR label CONTAINS[c] 'invalid'")).firstMatch.waitForExistence(timeout: 3)
XCTAssertTrue(errorExists, "Error message should appear for empty fields")
}
func testRegistrationWithInvalidEmail() {
// Given: User is on registration screen
navigateToRegistration()
// When: User fills form with invalid email
let usernameField = app.textFields[AccessibilityIdentifiers.Authentication.registerUsernameField]
let emailField = app.textFields[AccessibilityIdentifiers.Authentication.registerEmailField]
let passwordField = app.secureTextFields[AccessibilityIdentifiers.Authentication.registerPasswordField]
let confirmPasswordField = app.secureTextFields[AccessibilityIdentifiers.Authentication.registerConfirmPasswordField]
XCTAssertTrue(usernameField.waitForExistence(timeout: 5))
usernameField.tap()
usernameField.typeText("testuser")
emailField.tap()
emailField.typeText("invalid-email") // Invalid email format
passwordField.tap()
passwordField.typeText(testPassword)
confirmPasswordField.tap()
confirmPasswordField.typeText(testPassword)
let createAccountButton = app.buttons[AccessibilityIdentifiers.Authentication.registerButton]
createAccountButton.tap()
// Then: Error message for invalid email should appear
sleep(2)
let errorExists = app.staticTexts.containing(NSPredicate(format: "label CONTAINS[c] 'email' OR label CONTAINS[c] 'invalid'")).firstMatch.waitForExistence(timeout: 3)
XCTAssertTrue(errorExists, "Error message should appear for invalid email")
}
func testRegistrationWithMismatchedPasswords() {
// Given: User is on registration screen
navigateToRegistration()
// When: User fills form with mismatched passwords
let usernameField = app.textFields[AccessibilityIdentifiers.Authentication.registerUsernameField]
let emailField = app.textFields[AccessibilityIdentifiers.Authentication.registerEmailField]
let passwordField = app.secureTextFields[AccessibilityIdentifiers.Authentication.registerPasswordField]
let confirmPasswordField = app.secureTextFields[AccessibilityIdentifiers.Authentication.registerConfirmPasswordField]
XCTAssertTrue(usernameField.waitForExistence(timeout: 5))
usernameField.tap()
usernameField.typeText("testuser")
emailField.tap()
emailField.typeText("test@example.com")
passwordField.tap()
passwordField.typeText("Password123!")
confirmPasswordField.tap()
confirmPasswordField.typeText("DifferentPassword123!") // Mismatched password
let createAccountButton = app.buttons[AccessibilityIdentifiers.Authentication.registerButton]
createAccountButton.tap()
// Then: Error message for mismatched passwords should appear
sleep(2)
let errorExists = app.staticTexts.containing(NSPredicate(format: "label CONTAINS[c] 'match' OR label CONTAINS[c] 'password'")).firstMatch.waitForExistence(timeout: 3)
XCTAssertTrue(errorExists, "Error message should appear for mismatched passwords")
}
func testRegistrationWithWeakPassword() {
// Given: User is on registration screen
navigateToRegistration()
// When: User fills form with weak password
let usernameField = app.textFields[AccessibilityIdentifiers.Authentication.registerUsernameField]
let emailField = app.textFields[AccessibilityIdentifiers.Authentication.registerEmailField]
let passwordField = app.secureTextFields[AccessibilityIdentifiers.Authentication.registerPasswordField]
let confirmPasswordField = app.secureTextFields[AccessibilityIdentifiers.Authentication.registerConfirmPasswordField]
XCTAssertTrue(usernameField.waitForExistence(timeout: 5))
usernameField.tap()
usernameField.typeText("testuser")
emailField.tap()
emailField.typeText("test@example.com")
passwordField.tap()
passwordField.typeText("weak") // Too short/weak password
confirmPasswordField.tap()
confirmPasswordField.typeText("weak")
let createAccountButton = app.buttons[AccessibilityIdentifiers.Authentication.registerButton]
createAccountButton.tap()
// Then: Error message for weak password should appear
sleep(2)
let errorExists = app.staticTexts.containing(NSPredicate(format: "label CONTAINS[c] 'password' OR label CONTAINS[c] 'character' OR label CONTAINS[c] 'strong'")).firstMatch.waitForExistence(timeout: 3)
XCTAssertTrue(errorExists, "Error message should appear for weak password")
}
func testCancelRegistration() {
// Given: User is on registration screen
navigateToRegistration()
// When: User taps Cancel button
let cancelButton = app.buttons[AccessibilityIdentifiers.Authentication.registerCancelButton]
XCTAssertTrue(cancelButton.waitForExistence(timeout: 5))
cancelButton.tap()
// Then: Should return to login screen
sleep(1)
let welcomeText = app.staticTexts["Welcome Back"]
XCTAssertTrue(welcomeText.waitForExistence(timeout: 5), "Should return to login screen")
}
// MARK: - Full Registration Flow Tests
func testSuccessfulRegistrationAndVerification() {
// Use unique credentials for this test
let username = testUsername
let email = testEmail
// Given: User is on registration screen
navigateToRegistration()
// When: User fills in valid registration details
let usernameField = app.textFields[AccessibilityIdentifiers.Authentication.registerUsernameField]
let emailField = app.textFields[AccessibilityIdentifiers.Authentication.registerEmailField]
let passwordField = app.secureTextFields[AccessibilityIdentifiers.Authentication.registerPasswordField]
let confirmPasswordField = app.secureTextFields[AccessibilityIdentifiers.Authentication.registerConfirmPasswordField]
XCTAssertTrue(usernameField.waitForExistence(timeout: 5))
usernameField.tap()
usernameField.typeText(username)
emailField.tap()
emailField.typeText(email)
// Handle strong password suggestion by dismissing it
passwordField.tap()
sleep(1)
// Dismiss the strong password suggestion if it appears
dismissStrongPasswordSuggestion()
// Now type the password
passwordField.tap()
passwordField.typeText(testPassword)
confirmPasswordField.tap()
sleep(1)
dismissStrongPasswordSuggestion()
confirmPasswordField.tap()
confirmPasswordField.typeText(testPassword)
// Submit registration
let createAccountButton = app.buttons[AccessibilityIdentifiers.Authentication.registerButton]
createAccountButton.tap()
// Wait for verification screen to appear
let verifyEmailTitle = app.staticTexts["Verify Your Email"]
XCTAssertTrue(verifyEmailTitle.waitForExistence(timeout: 10), "Should navigate to email verification screen")
// Use the fixed test verification code
// Django uses "123456" for emails starting with "test_" when DEBUG=True
let verificationCode = testVerificationCode
print("Using test verification code: \(verificationCode)")
// Enter the verification code
let codeField = app.textFields[AccessibilityIdentifiers.Authentication.verificationCodeField]
XCTAssertTrue(codeField.waitForExistence(timeout: 5), "Verification code field should exist")
codeField.tap()
codeField.typeText(verificationCode)
// Submit verification
let verifyButton = app.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Verify'")).firstMatch
XCTAssertTrue(verifyButton.exists, "Verify button should exist")
verifyButton.tap()
// Then: Should navigate to main app screen (home/residences tab)
let residencesTab = app.tabBars.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Residences'")).firstMatch
XCTAssertTrue(residencesTab.waitForExistence(timeout: 10), "Should navigate to main app after successful verification")
// Verify we're on the home screen by checking for residences-related content
let homeScreenVisible = residencesTab.isSelected || app.navigationBars.containing(NSPredicate(format: "identifier CONTAINS[c] 'Residences' OR identifier CONTAINS[c] 'Home' OR identifier CONTAINS[c] 'Properties'")).firstMatch.exists
XCTAssertTrue(homeScreenVisible || residencesTab.exists, "Should be on home/residences screen after registration")
// Navigate to Profile tab and log out
let profileTab = app.tabBars.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Profile'")).firstMatch
XCTAssertTrue(profileTab.waitForExistence(timeout: 5), "Profile tab should exist")
profileTab.tap()
sleep(1)
// Tap logout button
let logoutButton = app.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Logout' OR label CONTAINS[c] 'Log Out' OR label CONTAINS[c] 'Sign Out'")).firstMatch
XCTAssertTrue(logoutButton.waitForExistence(timeout: 5), "Logout button should exist on profile screen")
logoutButton.tap()
sleep(1)
// Confirm logout in alert if present
let alertLogoutButton = app.alerts.buttons["Log Out"]
if alertLogoutButton.waitForExistence(timeout: 3) {
alertLogoutButton.tap()
sleep(1)
}
// Verify we're back on login screen
let welcomeText = app.staticTexts["Welcome Back"]
XCTAssertTrue(welcomeText.waitForExistence(timeout: 5), "Should return to login screen after logout")
// Cleanup test user
cleanupTestUser(email: email)
}
func testRegistrationWithInvalidVerificationCode() {
// Use unique credentials for this test
let username = testUsername
let email = testEmail
// Given: User is on registration screen
navigateToRegistration()
// Register a new user
let usernameField = app.textFields[AccessibilityIdentifiers.Authentication.registerUsernameField]
let emailField = app.textFields[AccessibilityIdentifiers.Authentication.registerEmailField]
let passwordField = app.secureTextFields[AccessibilityIdentifiers.Authentication.registerPasswordField]
let confirmPasswordField = app.secureTextFields[AccessibilityIdentifiers.Authentication.registerConfirmPasswordField]
XCTAssertTrue(usernameField.waitForExistence(timeout: 5))
usernameField.tap()
usernameField.typeText(username)
emailField.tap()
emailField.typeText(email)
passwordField.tap()
sleep(1)
dismissStrongPasswordSuggestion()
passwordField.tap()
passwordField.typeText(testPassword)
confirmPasswordField.tap()
sleep(1)
dismissStrongPasswordSuggestion()
confirmPasswordField.tap()
confirmPasswordField.typeText(testPassword)
let createAccountButton = app.buttons[AccessibilityIdentifiers.Authentication.registerButton]
createAccountButton.tap()
// Wait for verification screen
let verifyEmailTitle = app.staticTexts["Verify Your Email"]
XCTAssertTrue(verifyEmailTitle.waitForExistence(timeout: 10), "Should navigate to email verification screen")
// Enter an invalid verification code
let codeField = app.textFields[AccessibilityIdentifiers.Authentication.verificationCodeField]
XCTAssertTrue(codeField.waitForExistence(timeout: 5))
codeField.tap()
codeField.typeText("000000") // Invalid code
// Submit verification
let verifyButton = app.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Verify'")).firstMatch
verifyButton.tap()
// Then: Error message should appear
sleep(3)
let errorExists = app.staticTexts.containing(NSPredicate(format: "label CONTAINS[c] 'invalid' OR label CONTAINS[c] 'error' OR label CONTAINS[c] 'incorrect'")).firstMatch.waitForExistence(timeout: 5)
XCTAssertTrue(errorExists, "Error message should appear for invalid verification code")
// Cleanup: Logout and delete test user
let logoutButton = app.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Logout'")).firstMatch
if logoutButton.exists {
logoutButton.tap()
sleep(2)
}
cleanupTestUser(email: email)
}
func testLogoutFromVerificationScreen() {
// Use unique credentials for this test
let username = testUsername
let email = testEmail
// Given: User is on registration screen
navigateToRegistration()
// Register a new user
let usernameField = app.textFields[AccessibilityIdentifiers.Authentication.registerUsernameField]
let emailField = app.textFields[AccessibilityIdentifiers.Authentication.registerEmailField]
let passwordField = app.secureTextFields[AccessibilityIdentifiers.Authentication.registerPasswordField]
let confirmPasswordField = app.secureTextFields[AccessibilityIdentifiers.Authentication.registerConfirmPasswordField]
XCTAssertTrue(usernameField.waitForExistence(timeout: 5))
usernameField.tap()
usernameField.typeText(username)
emailField.tap()
emailField.typeText(email)
passwordField.tap()
sleep(1)
dismissStrongPasswordSuggestion()
passwordField.tap()
passwordField.typeText(testPassword)
confirmPasswordField.tap()
sleep(1)
dismissStrongPasswordSuggestion()
confirmPasswordField.tap()
confirmPasswordField.typeText(testPassword)
let createAccountButton = app.buttons[AccessibilityIdentifiers.Authentication.registerButton]
createAccountButton.tap()
// Wait for verification screen
let verifyEmailTitle = app.staticTexts["Verify Your Email"]
XCTAssertTrue(verifyEmailTitle.waitForExistence(timeout: 10), "Should navigate to email verification screen")
// When: User taps Logout button
let logoutButton = app.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Logout'")).firstMatch
XCTAssertTrue(logoutButton.waitForExistence(timeout: 5), "Logout button should exist on verification screen")
logoutButton.tap()
// Then: Should return to login screen
sleep(2)
let welcomeText = app.staticTexts["Welcome Back"]
XCTAssertTrue(welcomeText.waitForExistence(timeout: 5), "Should return to login screen after logout")
// Cleanup
cleanupTestUser(email: email)
}
func testVerificationCodeFieldValidation() {
// Use unique credentials for this test
let username = testUsername
let email = testEmail
// Given: User is on registration screen and registers
navigateToRegistration()
let usernameField = app.textFields[AccessibilityIdentifiers.Authentication.registerUsernameField]
let emailField = app.textFields[AccessibilityIdentifiers.Authentication.registerEmailField]
let passwordField = app.secureTextFields[AccessibilityIdentifiers.Authentication.registerPasswordField]
let confirmPasswordField = app.secureTextFields[AccessibilityIdentifiers.Authentication.registerConfirmPasswordField]
XCTAssertTrue(usernameField.waitForExistence(timeout: 5))
usernameField.tap()
usernameField.typeText(username)
emailField.tap()
emailField.typeText(email)
passwordField.tap()
sleep(1)
dismissStrongPasswordSuggestion()
passwordField.tap()
passwordField.typeText(testPassword)
confirmPasswordField.tap()
sleep(1)
dismissStrongPasswordSuggestion()
confirmPasswordField.tap()
confirmPasswordField.typeText(testPassword)
let createAccountButton = app.buttons[AccessibilityIdentifiers.Authentication.registerButton]
createAccountButton.tap()
// Wait for verification screen
let verifyEmailTitle = app.staticTexts["Verify Your Email"]
XCTAssertTrue(verifyEmailTitle.waitForExistence(timeout: 10))
// When: User tries to verify with incomplete code
let codeField = app.textFields[AccessibilityIdentifiers.Authentication.verificationCodeField]
XCTAssertTrue(codeField.waitForExistence(timeout: 5))
codeField.tap()
codeField.typeText("123") // Only 3 digits
// Then: Verify button should be disabled or show error
let verifyButton = app.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Verify'")).firstMatch
XCTAssertTrue(verifyButton.exists)
// The button might be disabled or tapping it shows an error
// Check that verification doesn't proceed with incomplete code
verifyButton.tap()
sleep(1)
// Should still be on verification screen
XCTAssertTrue(verifyEmailTitle.exists, "Should still be on verification screen with incomplete code")
// Cleanup
let logoutButton = app.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Logout'")).firstMatch
if logoutButton.exists {
logoutButton.tap()
sleep(2)
}
cleanupTestUser(email: email)
}
func testRegistrationWithExistingUsername() {
// This test requires an existing user in the database
// First, we need to ensure testuser exists
// Given: User is on registration screen
navigateToRegistration()
// When: User tries to register with existing username
let usernameField = app.textFields[AccessibilityIdentifiers.Authentication.registerUsernameField]
let emailField = app.textFields[AccessibilityIdentifiers.Authentication.registerEmailField]
let passwordField = app.secureTextFields[AccessibilityIdentifiers.Authentication.registerPasswordField]
let confirmPasswordField = app.secureTextFields[AccessibilityIdentifiers.Authentication.registerConfirmPasswordField]
XCTAssertTrue(usernameField.waitForExistence(timeout: 5))
usernameField.tap()
usernameField.typeText("testuser") // Assuming this user already exists
emailField.tap()
emailField.typeText("newemail_\(Int(Date().timeIntervalSince1970))@example.com")
passwordField.tap()
passwordField.typeText(testPassword)
confirmPasswordField.tap()
confirmPasswordField.typeText(testPassword)
let createAccountButton = app.buttons[AccessibilityIdentifiers.Authentication.registerButton]
createAccountButton.tap()
// Then: Error message for existing username should appear
sleep(3)
let errorExists = app.staticTexts.containing(NSPredicate(format: "label CONTAINS[c] 'exists' OR label CONTAINS[c] 'already' OR label CONTAINS[c] 'taken'")).firstMatch.waitForExistence(timeout: 5)
XCTAssertTrue(errorExists, "Error message should appear for existing username")
}
func testKeyboardNavigationDuringRegistration() {
// Given: User is on registration screen
navigateToRegistration()
// When: User navigates through fields using keyboard
let usernameField = app.textFields[AccessibilityIdentifiers.Authentication.registerUsernameField]
let emailField = app.textFields[AccessibilityIdentifiers.Authentication.registerEmailField]
let passwordField = app.secureTextFields[AccessibilityIdentifiers.Authentication.registerPasswordField]
XCTAssertTrue(usernameField.waitForExistence(timeout: 5))
// Start with username field
usernameField.tap()
XCTAssertTrue(usernameField.hasKeyboardFocus, "Username field should have keyboard focus")
usernameField.typeText("testuser\n") // Press return to move to next field
sleep(1)
// Email field should now be focused (or at least exist)
XCTAssertTrue(emailField.exists, "Email field should exist")
emailField.tap()
emailField.typeText("test@example.com\n")
sleep(1)
// Password field should now be accessible
XCTAssertTrue(passwordField.exists, "Password field should exist")
}
}
// MARK: - XCUIElement Extension for keyboard focus
extension XCUIElement {
var hasKeyboardFocus: Bool {
return (value(forKey: "hasKeyboardFocus") as? Bool) ?? false
}
}