Community examples aren't actively verified by the e2e team so exercise extra caution when running.
The tests in specs/ are an ordinary e2e project. e2e.config.ts and the files under specs/ run with npx e2e run when the environment names three things: a device, a build of the :e2e app, and a Clerk development instance.
The CLI in bin/ and src/ sits on top of that project. It makes those three things for a run, and it adds what a pull request needs. The tests never import the CLI. test/seam.test.ts fails when a file under specs/ or e2e.config.ts names a path outside specs/.
To prove a change, use the CLI. .claude/skills/verify-clerk-android/SKILL.md has the steps. Run a test by hand when you want e2e alone, for example to work on a test with e2e's own flags.
Run a test by hand
You need Node 24.8 or newer on 24, the Android SDK, JDK 21, and a running emulator, and the publishable key and secret key of a Clerk development instance.
cd e2e-tests
npm ci
(cd .. && ./gradlew :e2e:assembleDebug)
adb devices
export CLERK_E2E_DEVICE=<the serial of a running emulator, from the list above, such as emulator-5554>
export CLERK_E2E_APP_PATH=../e2e/build/outputs/apk/debug/e2e-debug.apk
export CLERK_PUBLISHABLE_KEY=pk_test_...
export CLERK_SECRET_KEY=sk_test_...
npx e2e run specs/features/sign-in-email-code/complete.e2e.ts--video on, --retries, --grep, and --output are e2e's own flags. e2e writes its report and its screenshots to .e2e/, which git ignores.
| Setting | Needed | What it is |
|---|---|---|
CLERK_E2E_DEVICE | yes | The serial of a running emulator, as adb devices prints it, such as emulator-5554. Only an emulator-<port> serial is accepted. The name of an AVD, a host:port address, and the serial of a phone are refused. |
CLERK_E2E_APP_PATH | no | The debug APK of :e2e. The first launch of a run installs it. Leave it unset when the app is already on the emulator. |
CLERK_PUBLISHABLE_KEY | yes | The pk_test_ key of a development instance. |
CLERK_SECRET_KEY | yes | The sk_test_ key of the same instance. The tests create users and sign-in tickets with it. A pk_live_ or sk_live_ key is refused. |
The instance
The tests expect the settings in src/core/instances/base.json of @clerk/internal-mobile-e2e, which the CLI puts on the application it creates. On an instance with those settings, every test file that has no settings file beside it passes by hand.
An instance left at Clerk's defaults passes only part of the suite. The tests that sign in with a ticket, or sign up with an email address and a password, pass. The tests that need a phone number, an email code at sign-in, organizations, or a second account on the device fail, because a new instance has none of those turned on.
Every run creates users with addresses like maya.7c1e+clerk_test@example.com and never deletes them. The address is a first name and the last four characters of the run id, here r20261002-141210-7c1e. No two tests of a run on one machine get the same name. A development instance holds 100 users. Use an instance that you can empty.
Test files that need the CLI
These test files have a .settings.json file beside them, and pass only on an instance with those settings:
specs/features/session-tasks/choose-organization.e2e.tsspecs/features/session-tasks/complete-setup-mfa.e2e.tsspecs/features/session-tasks/setup-mfa.e2e.tsspecs/features/session-tasks/sign-up-setup-mfa.e2e.ts
The CLI's run puts the instance on the declared settings before the file starts, and e2e alone does not. Run them through the CLI, for example e2e-tests/bin/control-clerk-android run session-tasks from the repo root.
What a run by hand does not do
- It creates no Clerk application, changes no instance settings, and replaces no instance that is filling up.
- It leases and locks no device. Run one test process per device at a time.
- It does not rebuild the app when the sources change.
- It records no video of the whole run.
--video onrecords one per attempt. - It keeps no sealed run directory, scans nothing for secrets, redacts no log, and posts nothing to a pull request.
- It leaves the
agent-devicedaemon that e2e started running.npx agent-device daemon stopstops it.
Layout
e2e.config.tsandspecs/are the e2e project.specs/features/has the tests,specs/fixtures.tshas thehostfixture they use,specs/app.tsnames the test app, and@clerk/internal-mobile-e2e/specs/support/is the tests' support code. Itsinputs.tsis the only file that reads the settings above.bin/andsrc/are the CLI. Under the CLI the test process gets the same settings, with one difference: in place of the secret key it gets the address and token of a stand-in that the CLI runs on this machine. The stand-in forwards the three Backend API calls the tests make, for this run's test users only, so the test process never holds the key.test/has the unit tests of both.npm testruns them, with no device, key, or network.- The CLI core and the test support code are the package
@clerk/internal-mobile-e2e, which clerk-ios and clerk/javascript also use.package.jsonpins it to a commit of clerk/internal-mobile-e2e, and.npmrclets npm install it from git.