End-to-end tests run with e2e and its mobile engine @e2e-dev/mobile, which drives iOS simulators and Android emulators through agent-device.
e2e.config.ts targets (ios, android), permissions, agent executor
e2e/
├── agent/ step executor that runs agent steps on a Claude subscription
├── scripts/ CI helpers (account secret → env vars)
├── support/
│ ├── flows.ts shared UI flows: launch, login, logout, rooms, messages
│ ├── api.ts REST helpers for test data (users, rooms, teams, messages)
│ ├── data.ts server URLs, seeded channels, account getters
│ ├── random.ts random users and names
│ └── <area>.ts helpers used by one test area
└── tests/<area>/**/*.e2e.ts
.e2e/cache/ committed replay cache for agent steps
- Tests point at https://mobile.qa.rocket.chat (
e2e/support/data.ts). - Prefer random data created through
e2e/support/api.tsover new seeds indata.ts. CallafterEach(deleteCreatedUsers)in tests that create users. - Credentials come from environment variables:
E2E_ADMIN_USER,E2E_ADMIN_PASSWORD: a user with the needed permissions on the serverE2E_SAML_USERNAME,E2E_SAML_PASSWORD,E2E_CAS_USERNAME,E2E_CAS_PASSWORD: SAML and CAS login tests only
- To build those from an account file:
node e2e/scripts/account-env.mjs e2e_account.js > .env.e2e, thenset -a; . ./.env.e2e; set +a.
agent.act, agent.assert, agent.waitFor and agent.extract run on Claude through the Claude Agent SDK, so they use your Claude Code login (or CLAUDE_CODE_OAUTH_TOKEN in CI). E2E_CLAUDE_MODEL picks the model (default sonnet).
A passing agent.act step is recorded in .e2e/cache and replays without a model call. Commit new or changed entries with the test that produced them. When CI runs a step on the model because its recording is missing, it uploads what it recorded and comments on the PR with the command that copies those entries into .e2e/cache. Judgments (assert, waitFor, extract) always call the model, so prefer locators and expect where they can express the check.
- Build a release app: an iOS simulator
.appor an Android.apk. Debug builds load JS from Metro. - Boot a simulator or emulator.
- Run:
E2E_IOS_APP_PATH=/path/to/Rocket.Chat.app pnpm exec e2e run --target ios
E2E_ANDROID_APP_PATH=/path/to/app-release.apk pnpm exec e2e run --target android
pnpm exec e2e run --target ios server-history # one file
pnpm exec e2e run --target android --tag test-5 # one CI shardOptional variables: E2E_IOS_DEVICE (simulator name or UDID, default iPhone 17 Pro) and E2E_ANDROID_DEVICE (serial, default emulator-5554).
Results land in .e2e/: report.json, and per failure an accessibility tree (failure/screen.txt) and screenshot under artifacts/.
On Android emulators with Google Play services, disable autofill first, or the password manager can take over the login form: adb shell settings put secure autofill_service null.
- Tag each test with its CI shard:
test('...', { tags: ['test-5'] }, ...). Platform-only tests addplatforms: ['ios']orplatforms: ['android']. - Start every test from
launchApporloginWithDeepLink. On iOS they reinstall the app, since clearing app state keeps the app group container. - Prefer
getByTestIdand exactgetByText. A locator that matches more than one node fails; narrow it, or use.first()/.last(). swipe({ direction })uses scroll semantics: the finger moves opposite todirection.- Android alert buttons are uppercase; match their names with a case-insensitive regex.
