diff --git a/iosApp/MyCribUITests/RegistrationTests.swift b/iosApp/MyCribUITests/RegistrationTests.swift new file mode 100644 index 0000000..b33146a --- /dev/null +++ b/iosApp/MyCribUITests/RegistrationTests.swift @@ -0,0 +1,600 @@ +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/MyCrib/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/MyCrib/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 + } +} diff --git a/iosApp/MyCribUITests/Scripts/cleanup_test_users.sh b/iosApp/MyCribUITests/Scripts/cleanup_test_users.sh new file mode 100755 index 0000000..1da99bc --- /dev/null +++ b/iosApp/MyCribUITests/Scripts/cleanup_test_users.sh @@ -0,0 +1,36 @@ +#!/bin/bash +# Script to clean up test users from Django database +# Usage: ./cleanup_test_users.sh [email] +# If no email provided, cleans up all users with email starting with 'test_' + +EMAIL="$1" + +cd /Users/treyt/Desktop/code/MyCrib/myCribAPI + +if [ -n "$EMAIL" ]; then + FILTER="email='$EMAIL'" +else + FILTER="email__startswith='test_'" +fi + +# Try docker exec first (if running in Docker) +if docker ps --format '{{.Names}}' | grep -q 'mycrib-web\|myCrib-web'; then + CONTAINER_NAME=$(docker ps --format '{{.Names}}' | grep -E 'mycrib-web|myCrib-web' | head -1) + docker exec "$CONTAINER_NAME" python manage.py shell -c " +from django.contrib.auth import get_user_model +User = get_user_model() +deleted = User.objects.filter($FILTER).delete() +print(f'Deleted: {deleted}') +" 2>/dev/null +else + # Fallback to local Python + export DJANGO_SETTINGS_MODULE=myCrib.settings + python manage.py shell -c " +from django.contrib.auth import get_user_model +User = get_user_model() +deleted = User.objects.filter($FILTER).delete() +print(f'Deleted: {deleted}') +" 2>/dev/null +fi + +echo "Test users cleanup complete" diff --git a/iosApp/MyCribUITests/Scripts/get_verification_code.sh b/iosApp/MyCribUITests/Scripts/get_verification_code.sh new file mode 100755 index 0000000..1781e71 --- /dev/null +++ b/iosApp/MyCribUITests/Scripts/get_verification_code.sh @@ -0,0 +1,57 @@ +#!/bin/bash +# Script to fetch verification code from Django database +# Usage: ./get_verification_code.sh +# Output: Writes the verification code to /tmp/mycrib_verification_code_.txt + +EMAIL="$1" + +if [ -z "$EMAIL" ]; then + echo "Usage: $0 " + exit 1 +fi + +# Sanitize email for filename +SANITIZED_EMAIL=$(echo "$EMAIL" | sed 's/@/_at_/g' | sed 's/\./_dot_/g') +OUTPUT_FILE="/tmp/mycrib_verification_code_${SANITIZED_EMAIL}.txt" + +cd /Users/treyt/Desktop/code/MyCrib/myCribAPI + +# Try docker exec first (if running in Docker) +if docker ps --format '{{.Names}}' | grep -q 'mycrib-web\|myCrib-web'; then + CONTAINER_NAME=$(docker ps --format '{{.Names}}' | grep -E 'mycrib-web|myCrib-web' | head -1) + CODE=$(docker exec "$CONTAINER_NAME" python manage.py shell -c " +from user.models import ConfirmationCode +from django.contrib.auth import get_user_model +User = get_user_model() +try: + user = User.objects.get(email='$EMAIL') + code = ConfirmationCode.objects.filter(user=user, is_used=False).latest('created_at') + print(code.code) +except Exception as e: + print('ERROR:', e) +" 2>/dev/null) +else + # Fallback to local Python + export DJANGO_SETTINGS_MODULE=myCrib.settings + CODE=$(python manage.py shell -c " +from user.models import ConfirmationCode +from django.contrib.auth import get_user_model +User = get_user_model() +try: + user = User.objects.get(email='$EMAIL') + code = ConfirmationCode.objects.filter(user=user, is_used=False).latest('created_at') + print(code.code) +except Exception as e: + print('ERROR:', e) +" 2>/dev/null) +fi + +# Check if we got a valid 6-digit code +if [[ "$CODE" =~ ^[0-9]{6}$ ]]; then + echo "$CODE" > "$OUTPUT_FILE" + echo "Verification code saved to $OUTPUT_FILE: $CODE" + exit 0 +else + echo "Failed to get verification code: $CODE" + exit 1 +fi diff --git a/iosApp/iosApp/Info.plist b/iosApp/iosApp/Info.plist index bb16c76..e672846 100644 --- a/iosApp/iosApp/Info.plist +++ b/iosApp/iosApp/Info.plist @@ -17,6 +17,24 @@ ITSAppUsesNonExemptEncryption + NSAppTransportSecurity + + NSAllowsLocalNetworking + + NSExceptionDomains + + localhost + + NSExceptionAllowsInsecureHTTPLoads + + + 127.0.0.1 + + NSExceptionAllowsInsecureHTTPLoads + + + + UIBackgroundModes remote-notification diff --git a/iosApp/iosApp/Register/RegisterView.swift b/iosApp/iosApp/Register/RegisterView.swift index dac5597..4b79eb0 100644 --- a/iosApp/iosApp/Register/RegisterView.swift +++ b/iosApp/iosApp/Register/RegisterView.swift @@ -135,8 +135,11 @@ struct RegisterView: View { .fullScreenCover(isPresented: $viewModel.isRegistered) { VerifyEmailView( onVerifySuccess: { - dismiss() + // User has verified their email - mark as authenticated + // This will update RootView to show the main app + AuthenticationManager.shared.login() showVerifyEmail = false + dismiss() }, onLogout: { // Logout and return to login screen diff --git a/iosApp/iosApp/Residence/ResidencesListView.swift b/iosApp/iosApp/Residence/ResidencesListView.swift index d84a6b7..2af9bc4 100644 --- a/iosApp/iosApp/Residence/ResidencesListView.swift +++ b/iosApp/iosApp/Residence/ResidencesListView.swift @@ -106,7 +106,11 @@ struct ResidencesListView: View { .interactiveDismissDisabled() } .onChange(of: authManager.isAuthenticated) { isAuth in - if !isAuth { + if isAuth { + // User just logged in or registered - load their residences + viewModel.loadMyResidences() + } else { + // User logged out - clear data viewModel.myResidences = nil } } diff --git a/iosApp/iosApp/VerifyEmail/VerifyEmailView.swift b/iosApp/iosApp/VerifyEmail/VerifyEmailView.swift index 09e935f..9734786 100644 --- a/iosApp/iosApp/VerifyEmail/VerifyEmailView.swift +++ b/iosApp/iosApp/VerifyEmail/VerifyEmailView.swift @@ -66,6 +66,7 @@ struct VerifyEmailView: View { .frame(height: 60) .padding(.horizontal) .focused($isFocused) + .accessibilityIdentifier(AccessibilityIdentifiers.Authentication.verificationCodeField) .onChange(of: viewModel.code) { _, newValue in // Limit to 6 digits if newValue.count > 6 {