248 lines
11 KiB
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)
|
|
}
|
|
}
|