Remediate all P0-S priority findings from cross-platform architecture audit: - Harden token storage with EncryptedSharedPreferences (Android) and Keychain (iOS) - Add SSL pinning and certificate validation to API clients - Fix subscription cache race conditions and add thread-safe access - Add input validation for document uploads and file type restrictions - Refactor DocumentApi to use proper multipart upload flow - Add rate limiting awareness and retry logic to API layer - Harden subscription tier enforcement in SubscriptionHelper - Add biometric prompt for sensitive actions (Login, Onboarding) - Fix notification permission handling and device registration - Add UI test infrastructure (page objects, fixtures, smoke tests) - Add CI workflow for mobile builds Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
87 lines
2.8 KiB
Swift
87 lines
2.8 KiB
Swift
import XCTest
|
|
|
|
/// Page object for the login screen.
|
|
///
|
|
/// Uses accessibility identifiers from `AccessibilityIdentifiers.Authentication`
|
|
/// to locate elements. Provides typed actions for login flow interactions.
|
|
class LoginScreen: BaseScreen {
|
|
|
|
// MARK: - Elements
|
|
|
|
var emailField: XCUIElement {
|
|
app.textFields[AccessibilityIdentifiers.Authentication.usernameField]
|
|
}
|
|
|
|
var passwordField: XCUIElement {
|
|
// Password field may be a SecureTextField or regular TextField depending on visibility toggle
|
|
let secure = app.secureTextFields[AccessibilityIdentifiers.Authentication.passwordField]
|
|
if secure.exists { return secure }
|
|
return app.textFields[AccessibilityIdentifiers.Authentication.passwordField]
|
|
}
|
|
|
|
var loginButton: XCUIElement {
|
|
app.buttons[AccessibilityIdentifiers.Authentication.loginButton]
|
|
}
|
|
|
|
var appleSignInButton: XCUIElement {
|
|
app.buttons[AccessibilityIdentifiers.Authentication.appleSignInButton]
|
|
}
|
|
|
|
var signUpButton: XCUIElement {
|
|
app.buttons[AccessibilityIdentifiers.Authentication.signUpButton]
|
|
}
|
|
|
|
var forgotPasswordButton: XCUIElement {
|
|
app.buttons[AccessibilityIdentifiers.Authentication.forgotPasswordButton]
|
|
}
|
|
|
|
var passwordVisibilityToggle: XCUIElement {
|
|
app.buttons[AccessibilityIdentifiers.Authentication.passwordVisibilityToggle]
|
|
}
|
|
|
|
var welcomeText: XCUIElement {
|
|
app.staticTexts["Welcome Back"]
|
|
}
|
|
|
|
override var isDisplayed: Bool {
|
|
emailField.waitForExistence(timeout: timeout)
|
|
}
|
|
|
|
// MARK: - Actions
|
|
|
|
/// Logs in with the provided credentials and returns a MainTabScreen.
|
|
/// Waits for the email field to appear before typing.
|
|
@discardableResult
|
|
func login(email: String, password: String) -> MainTabScreen {
|
|
waitForElement(emailField).tap()
|
|
emailField.typeText(email)
|
|
|
|
let pwField = passwordField
|
|
pwField.tap()
|
|
pwField.typeText(password)
|
|
|
|
loginButton.tap()
|
|
return MainTabScreen(app: app)
|
|
}
|
|
|
|
/// Taps the sign up / register link and returns a RegisterScreen.
|
|
@discardableResult
|
|
func tapSignUp() -> RegisterScreen {
|
|
waitForElement(signUpButton).tap()
|
|
return RegisterScreen(app: app)
|
|
}
|
|
|
|
/// Taps the forgot password link.
|
|
func tapForgotPassword() {
|
|
waitForElement(forgotPasswordButton).tap()
|
|
}
|
|
|
|
/// Toggles password visibility and returns whether the password is now visible.
|
|
@discardableResult
|
|
func togglePasswordVisibility() -> Bool {
|
|
waitForElement(passwordVisibilityToggle).tap()
|
|
// If a regular text field with the password identifier exists, password is visible
|
|
return app.textFields[AccessibilityIdentifiers.Authentication.passwordField].exists
|
|
}
|
|
}
|