Files
honeyDueKMP/iosApp/CaseraUITests/Suite0_OnboardingTests.swift

248 lines
11 KiB
Swift

import XCTest
/// Onboarding flow tests
///
/// SETUP REQUIREMENTS:
/// This test suite requires the app to be UNINSTALLED before running.
/// Add a Pre-action script to the CaseraUITests scheme (Edit Scheme Test Pre-actions):
/// /usr/bin/xcrun simctl uninstall booted com.tt.casera.CaseraDev
/// exit 0
///
/// There is ONE fresh-install test that runs the complete onboarding flow.
/// Additional tests for returning users (login screen) can run without fresh install.
final class Suite0_OnboardingTests: BaseUITestCase {
let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
override func setUpWithError() throws {
try super.setUpWithError()
sleep(2)
}
override func tearDownWithError() throws {
app.terminate()
try super.tearDownWithError()
}
private func typeText(_ text: String, into field: XCUIElement) {
field.waitForExistenceOrFail(timeout: 10)
for _ in 0..<3 {
if !field.isHittable {
app.swipeUp()
}
field.forceTap()
if !field.hasKeyboardFocus {
field.coordinate(withNormalizedOffset: CGVector(dx: 0.8, dy: 0.5)).tap()
}
if !field.hasKeyboardFocus {
continue
}
app.typeText(text)
if let value = field.value as? String {
if value.contains(text) || value.count >= text.count {
return
}
}
}
XCTFail("Unable to enter text into \(field)")
}
private func dismissStrongPasswordSuggestionIfPresent() {
let chooseOwnPassword = app.buttons["Choose My Own Password"]
if chooseOwnPassword.waitForExistence(timeout: 1) {
chooseOwnPassword.tap()
return
}
let notNow = app.buttons["Not Now"]
if notNow.exists && notNow.isHittable {
notNow.tap()
}
}
private func focusField(_ field: XCUIElement, name: String) {
field.waitForExistenceOrFail(timeout: 10)
for _ in 0..<4 {
if field.hasKeyboardFocus { return }
field.forceTap()
if field.hasKeyboardFocus { return }
field.coordinate(withNormalizedOffset: CGVector(dx: 0.9, dy: 0.5)).tap()
if field.hasKeyboardFocus { return }
}
XCTFail("Failed to focus \(name) field")
}
func test_onboarding() {
app.activate()
sleep(2)
let springboardApp = XCUIApplication(bundleIdentifier: "com.apple.springboard")
let allowButton = springboardApp.buttons["Allow"].firstMatch
if allowButton.waitForExistence(timeout: 2) {
allowButton.tap()
}
let welcome = OnboardingWelcomeScreen(app: app)
welcome.waitForLoad()
welcome.tapStartFresh()
let valuePropsTitle = app.descendants(matching: .any).matching(identifier: AccessibilityIdentifiers.Onboarding.valuePropsTitle).firstMatch
if valuePropsTitle.waitForExistence(timeout: 5) {
let valueProps = OnboardingValuePropsScreen(app: app)
valueProps.tapContinue()
}
let nameResidenceTitle = app.descendants(matching: .any).matching(identifier: AccessibilityIdentifiers.Onboarding.nameResidenceTitle).firstMatch
if nameResidenceTitle.waitForExistence(timeout: 5) {
let residenceField = app.textFields[AccessibilityIdentifiers.Onboarding.residenceNameField]
residenceField.waitUntilHittable(timeout: 8).tap()
residenceField.typeText("xcuitest")
app.descendants(matching: .any).matching(identifier: AccessibilityIdentifiers.Onboarding.nameResidenceContinueButton).firstMatch.waitUntilHittable(timeout: 8).tap()
}
let emailExpandButton = app.buttons[AccessibilityIdentifiers.Onboarding.emailSignUpExpandButton].firstMatch
if emailExpandButton.waitForExistence(timeout: 10) && emailExpandButton.isHittable {
emailExpandButton.tap()
}
let unique = Int(Date().timeIntervalSince1970)
let onboardingUsername = "xcuitest\(unique)"
let onboardingEmail = "xcuitest_\(unique)@treymail.com"
let usernameField = app.textFields[AccessibilityIdentifiers.Onboarding.usernameField].firstMatch
focusField(usernameField, name: "username")
usernameField.typeText(onboardingUsername)
XCTAssertTrue((usernameField.value as? String)?.contains(onboardingUsername) == true, "Username should be populated")
let emailField = app.textFields[AccessibilityIdentifiers.Onboarding.emailField].firstMatch
emailField.waitForExistenceOrFail(timeout: 10)
var didEnterEmail = false
for _ in 0..<5 {
app.swipeUp()
emailField.forceTap()
if emailField.hasKeyboardFocus {
emailField.typeText(onboardingEmail)
didEnterEmail = true
break
}
}
XCTAssertTrue(didEnterEmail, "Email field must become focused for typing")
let strongPassword = "TestPass123!"
let passwordField = app.secureTextFields[AccessibilityIdentifiers.Onboarding.passwordField].firstMatch
dismissStrongPasswordSuggestionIfPresent()
focusField(passwordField, name: "password")
passwordField.typeText(strongPassword)
XCTAssertFalse((passwordField.value as? String)?.isEmpty ?? true, "Password should be populated")
let confirmPasswordField = app.secureTextFields[AccessibilityIdentifiers.Onboarding.confirmPasswordField].firstMatch
dismissStrongPasswordSuggestionIfPresent()
if !confirmPasswordField.hasKeyboardFocus {
app.swipeUp()
focusField(confirmPasswordField, name: "confirm password")
}
confirmPasswordField.typeText(strongPassword)
let createAccountButtonByID = app.buttons[AccessibilityIdentifiers.Onboarding.createAccountButton]
let createAccountButtonByLabel = app.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Create Account'")).firstMatch
let createAccountButton = createAccountButtonByID.exists ? createAccountButtonByID : createAccountButtonByLabel
createAccountButton.waitForExistenceOrFail(timeout: 10)
if !createAccountButton.isHittable {
app.swipeUp()
sleep(1)
}
if !createAccountButton.isEnabled {
// Retry confirm-password input once when validation hasn't propagated.
let confirmPasswordField = app.secureTextFields[AccessibilityIdentifiers.Onboarding.confirmPasswordField].firstMatch
if confirmPasswordField.waitForExistence(timeout: 3) {
focusField(confirmPasswordField, name: "confirm password retry")
confirmPasswordField.typeText(strongPassword)
}
sleep(1)
}
XCTAssertTrue(createAccountButton.isEnabled, "Create account button should be enabled after valid form entry")
createAccountButton.forceTap()
let verifyCodeField = app.textFields[AccessibilityIdentifiers.Onboarding.verificationCodeField]
verifyCodeField.waitForExistenceOrFail(timeout: 12)
verifyCodeField.forceTap()
app.typeText("123456")
let verifyButtonByID = app.buttons[AccessibilityIdentifiers.Onboarding.verifyButton]
let verifyButtonByLabel = app.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Verify'")).firstMatch
let verifyButton = verifyButtonByID.exists ? verifyButtonByID : verifyButtonByLabel
verifyButton.waitForExistenceOrFail(timeout: 10)
if !verifyButton.isHittable {
app.swipeUp()
sleep(1)
}
verifyButton.forceTap()
let addPopular = app.buttons[AccessibilityIdentifiers.Onboarding.addPopularTasksButton].firstMatch
if addPopular.waitForExistence(timeout: 10) {
addPopular.tap()
} else {
app.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Add Most Popular'")).firstMatch.tap()
}
let addTasksContinue = app.buttons[AccessibilityIdentifiers.Onboarding.addTasksContinueButton].firstMatch
if addTasksContinue.waitForExistence(timeout: 10) {
addTasksContinue.tap()
} else {
app.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Tasks & Continue'")).firstMatch.tap()
}
let continueWithFree = app.buttons[AccessibilityIdentifiers.Onboarding.continueWithFreeButton].firstMatch
if continueWithFree.waitForExistence(timeout: 10) {
continueWithFree.tap()
} else {
app.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Continue with Free'")).firstMatch.tap()
}
let residencesHeader = app.staticTexts.containing(NSPredicate(format: "label CONTAINS[c] 'Your Properties' OR label CONTAINS[c] 'My Properties' OR label CONTAINS[c] 'Residences'")).firstMatch
XCTAssertTrue(residencesHeader.waitForExistence(timeout: 5), "Residences list screen must be visible")
let xcuitestResidence = app.staticTexts["xcuitest"].waitForExistence(timeout: 10)
XCTAssertTrue(xcuitestResidence, "Residence should appear in list")
app/*@START_MENU_TOKEN@*/.images["checkmark.circle.fill"]/*[[".buttons[\"checkmark.circle.fill\"].images",".buttons",".images[\"selected\"]",".images[\"checkmark.circle.fill\"]"],[[[-1,3],[-1,2],[-1,1,1],[-1,0]],[[-1,3],[-1,2]]],[0]]@END_MENU_TOKEN@*/.firstMatch.tap()
let taskOne = app.staticTexts.containing(NSPredicate(format: "label CONTAINS[c] %@", "HVAC")).firstMatch
XCTAssertTrue(taskOne.waitForExistence(timeout: 10), "HVAC task should appear in list")
let taskTwo = app.staticTexts.containing(NSPredicate(format: "label CONTAINS[c] %@", "Leaks")).firstMatch
XCTAssertTrue(taskTwo.waitForExistence(timeout: 10), "Leaks task should appear in list")
let taskThree = app.staticTexts.containing(NSPredicate(format: "label CONTAINS[c] %@", "Coils")).firstMatch
XCTAssertTrue(taskThree.waitForExistence(timeout: 10), "Coils task should appear in list")
// Try profile tab logout
let profileTab = app.tabBars.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Profile'")).firstMatch
if profileTab.exists && profileTab.isHittable {
profileTab.tap()
let logoutButton = app.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Logout' OR label CONTAINS[c] 'Log Out'")).firstMatch
if logoutButton.waitForExistence(timeout: 3) && logoutButton.isHittable {
logoutButton.tap()
// Handle confirmation alert
let alertLogout = app.alerts.buttons["Log Out"]
if alertLogout.waitForExistence(timeout: 2) {
alertLogout.tap()
}
}
}
// Try verification screen logout
let verifyLogout = app.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Logout'")).firstMatch
if verifyLogout.exists && verifyLogout.isHittable {
verifyLogout.tap()
}
// Wait for login screen
_ = app.textFields[AccessibilityIdentifiers.Authentication.usernameField].waitForExistence(timeout: 8)
}
}