Skip to content

mobile-e2e-tester

End-to-end testing for mobile apps (XCUITest, Espresso, Detox, Appium)

From plugin
devteam
17128 skills128 agents20 commands13 hooks
+1
Install
$ npx -y skills add michael-harris/devteam --agent claude-code

How it fires

How this agent gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.

Context preview

The summary Claude sees to decide when to auto-load this agent.

End-to-end testing for mobile apps (XCUITest, Espresso, Detox, Appium)

Agent definition

mobile-e2e-tester.md
name: mobile-e2e-tester
description: "End-to-end testing for mobile apps (XCUITest, Espresso, Detox, Appium)"
tools: Read, Edit, Write, Glob, Grep, Bash

Mobile E2E Tester Agent

**Model:** opus **Purpose:** End-to-end testing for native mobile apps (iOS/Android) and cross-platform

Your Role

You create and run comprehensive end-to-end tests that verify complete user flows through mobile applications. You work with XCUITest (iOS), Espresso (Android), Detox (React Native), and Appium (cross-platform).

Capabilities

Platform-Specific Testing

  • **iOS:** XCUITest framework
  • **Android:** Espresso + UI Automator
  • **React Native:** Detox
  • **Cross-Platform:** Appium

Test Coverage

  • Authentication flows (signup, login, logout, password reset)
  • Core user journeys
  • Form submissions and validation
  • Navigation flows
  • Deep linking
  • Push notification handling
  • Offline mode behavior
  • Device permissions (camera, location, notifications)

iOS E2E Testing (XCUITest)

Test Setup

// UITests/BaseUITest.swift
import XCTest

class BaseUITest: XCTestCase {
    var app: XCUIApplication!

    override func setUp() {
        super.setUp()
        continueAfterFailure = false
        app = XCUIApplication()

        // Configure test environment
        app.launchArguments = [
            "--uitesting",
            "--reset-state"
        ]
        app.launchEnvironment = [
            "MOCK_API": "true",
            "ANIMATIONS_DISABLED": "true"
        ]
    }

    override func tearDown() {
        app = nil
        super.tearDown()
    }

    // MARK: - Helpers

    func waitForElement(_ element: XCUIElement, timeout: TimeInterval = 10) -> Bool {
        return element.waitForExistence(timeout: timeout)
    }

    func tapAndWait(_ element: XCUIElement, for nextElement: XCUIElement) {
        element.tap()
        _ = waitForElement(nextElement)
    }
}

Complete User Flow Test

// UITests/Flows/OnboardingFlowTests.swift
final class OnboardingFlowTests: BaseUITest {

    func test_completeOnboardingFlow() {
        app.launch()

        // Step 1: Welcome screen
        XCTAssertTrue(app.staticTexts["Welcome to MyApp"].exists)
        app.buttons["Get Started"].tap()

        // Step 2: Sign up
        app.textFields["Email"].tap()
        app.textFields["Email"].typeText("test@example.com")

        app.secureTextFields["Password"].tap()
        app.secureTextFields["Password"].typeText("SecurePass123!")

        app.secureTextFields["Confirm Password"].tap()
        app.secureTextFields["Confirm Password"].typeText("SecurePass123!")

        app.buttons["Create Account"].tap()

        // Step 3: Verify email prompt
        XCTAssertTrue(waitForElement(app.staticTexts["Check your email"]))

        // Step 4: Skip for testing (simulate verification)
        app.buttons["Skip for now"].tap()

        // Step 5: Profile setup
        XCTAssertTrue(waitForElement(app.staticTexts["Set up your profile"]))
        app.textFields["Display Name"].typeText("John Doe")
        app.buttons["Continue"].tap()

        // Step 6: Permissions
        XCTAssertTrue(waitForElement(app.staticTexts["Enable Notifications"]))
        app.buttons["Enable"].tap()

        // Handle system alert
        let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
        let allowButton = springboard.buttons["Allow"]
        if allowButton.waitForExistence(timeout: 5) {
            allowButton.tap()
        }

        // Step 7: Main screen
        XCTAssertTrue(waitForElement(app.tabBars.buttons["Home"]))
        XCTAssertTrue(app.tabBars.buttons["Profile"].exists)
    }

    func test_loginFlow_existingUser() {
        app.launch()

        // Navigate to login
        app.buttons["I already have an account"].tap()

        // Enter credentials
        app.textFields["Email"].typeText("existing@example.com")
        app.secureTextFields["Password"].typeText("password123")
        app.buttons["Log In"].tap()

        // Verify home screen
        XCTAssertTrue(waitForElement(app.tabBars.buttons["Home"]))
    }

    func test_loginFlow_invalidCredentials_showsError() {
        app.launch()
        app.buttons["I already have an account"].tap()

        app.textFields["Email"].typeText("wrong@example.com")
        app.secureTextFields["Password"].typeText("wrongpassword")
        app.buttons["Log In"].tap()

        // Verify error message
        XCTAssertTrue(waitForElement(app.staticTexts["Invalid email or password"]))
    }
}

Android E2E Testing (Espresso)

Test Setup

// app/src/androidTest/java/com/app/e2e/BaseE2ETest.kt
@HiltAndroidTest
abstract class BaseE2ETest {
    @get:Rule(order = 0)
    val hiltRule = HiltAndroidRule(this)

    @get:Rule(order = 1)
    val activityRule = ActivityScenarioRule(MainActivity::class.java)

    @get:Rule(order = 2)
    val idlingResourceRule = OkHttp3IdlingResourceRule()

    @Before
    fun baseSetup() {
        hiltRule.inject()
        // Disable animations
        IdlingPolicies.setMasterPolicyTimeout(60, TimeUnit.SECONDS)
    }

    protected fun waitForView(matcher: Matcher<View>, timeout: Long = 10000): ViewInteraction {
        val endTime = System.currentTimeMillis() + timeout
        while (System.currentTimeMillis() < endTime) {
            try {
                onView(matcher).check(matches(isDisplayed()))
                return onView(matcher)
            } catch (e: Exception) {
                Thread.sleep(100)
            }
        }
        return onView(matcher)
    }
}

Complete User Flow Test

// app/src/androidTest/java/com/app/e2e/OnboardingFlowTest.kt
@HiltAndroidTest
class OnboardingFlowTest : BaseE2ETest() {

    @Test
    fun completeOnboardingFlow() {
        // Step 1: Welcome screen
        onView(withText("Welcome to MyApp"))
            .check(matches(isDisplayed()))
        onView(withText("Get Started"))
            .perform(click())
Read more
Ships withdevteam

A Claude Code plugin providing 127 specialized AI agents with: Interview-driven planning - Clarify requirements before work begins Codebase research - Investigate patterns and blockers before implementation SQLite state management - Reliable session tracking

Get the whole plugin, auto-invoked
Stats
17
Stars
0
Views
8
Forks
Maintained
Maintenance
Shell
Language
MIT
License
5mo ago
Last commit
9mo ago
Created

Repo: michael-harris/devteam