Skip to main content

jevtest

Plain-English end-to-end tests for Android and iOS apps.
Write what a user does and what they should see. Jev works out the taps. CI replays every run exactly.

CI Coverage PyPI Python Docs MIT license

Documentation · Getting started · Steps · CI


app: build/app-debug.apk
device: { android: emulator-5554 }

tests:
  - name: Sign in
    fresh: true
    steps:
      - do: Sign in with email "${DEMO_EMAIL}" and password "${DEMO_PASSWORD}"
        expect: The home screen is showing
        see: Welcome, ${DEMO_EMAIL}
$ jevtest run login.yaml --lock frozen --out results
jevtest 0.10.0 · android · emulator-5554 · dev.jevtest.jevtest_demo · jev-1.13.0 · lockfile: frozen

▶ Sign in
  ✓ do: Sign in with email "${DEMO_EMAIL}" and password "${DEMO_PASSWORD}" (2.6s) — 3 saved steps
      → type "${DEMO_EMAIL}" into text_field 'Email'
      → type "${DEMO_PASSWORD}" into password_field 'Password'
      → tap button 'Sign in'
      ✓ expect: The home screen is showing — Jev 0.96
      ✓ see: Welcome, ${DEMO_EMAIL}
  PASS Sign in (5.1s)

1/1 passed in 5s
Jev: 1 decision, 1 from lockfile, 0 asked live in 0.0s (0% of run time), $0.0000
Results: results/20260928-161344/android/emulator-5554

Why jevtest

  • Tests read like the spec. One action, then what should be true. do: takes a plain-English goal; tap:, type:, swipe:, scroll_to: and 22 more give exact control.
  • Deterministic. Every Jev decision is recorded in a lockfile. The same screen always gets the same answer; --lock frozen replays a run exactly, with no network and no API key.
  • Nothing assumed. No default device, no guessing what a typo meant, and settings with good defaults and strict limits. The whole test file is checked before a device is touched, and every problem is reported at once with what to fix.
  • Real apps, real phones. Android emulators and phones, iOS simulators and iPhones. Native, Flutter, React Native and in-app WebViews, driven the same way. Animations stay on, and what a step changes on the device (orientation, dark mode, network, autofill, an iOS location) is put back.
  • Wait until, or fail. Each step waits until what it needs is on screen, up to its timeout, then fails saying what it waited for. No sleeps.
  • See what to name. jevtest inspect starts the app and saves each screen with every element boxed on the screenshot, the names steps can use, and a JSON dump for agents writing tests.
  • Built for scale and CI. ${SECRETS} from .env or CI, shared test libraries, whole folders in one command, several devices at once, JUnit XML, JSON reports and failure screenshots.

Install

pip install jevtest

You also need the platform tools for your apps: the Android SDK and a JDK, and/or Xcode. A TypeSafe API key gives access to Jev. See Getting started.

Run

jevtest run tests.yaml --lock record --out results     # asks Jev about new screens, records the answers
jevtest run tests/ --lock frozen --out results         # a whole folder, replayed exactly from the lockfiles
jevtest inspect app.apk --device emulator-5554 --out inspect   # see each screen as steps name it

How it works

A small agent on the device reads the accessibility tree in milliseconds. jevtest describes the screen as text and asks Jev, TypeSafe's decision model, to choose the next action and element from the options on screen. Jev never writes free text, so everything typed comes from your test file. The steps it works out for each do: are saved, and every later run repeats them exactly, without asking Jev. More.

Status

New, and tested end to end on a Flutter demo app with native and web screens: Android emulators (API 37 and API 35), a Pixel 4a (Android 13), iOS simulators (iOS 26) and an iPhone 17 (iOS 27). Also on six apps written to test it: SwiftUI, UIKit, Jetpack Compose, Android Views, React Native and a second Flutter app, on the emulators and simulators; the Android ones on the Pixel, and SwiftUI, UIKit and Flutter on the iPhone.

Built to a high bar: a clean architecture whose layer rules are checked on every commit; strict mypy and strict pyright with no Any; every ruff rule; no function above a complexity of 10; 100% line and branch coverage in random order, with Hypothesis properties on every input jevtest reads; dependencies audited and every CI action pinned to a commit. Issues and pull requests are welcome; see Contributing.

License

MIT

Metadata

Release files for jevtest 0.10.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for jevtest 0.10.0
File Size Uploaded
jevtest-0.10.0.tar.gz 155.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for jevtest 0.10.0
File Interpreter ABI Platform
jevtest-0.10.0-py3-none-any.whl Python 3 none any Details

Total release size: 328.1 kB

Release files / jevtest-0.10.0.tar.gz

Download URL jevtest-0.10.0.tar.gz
Size 155.5 kB
Tags Source
SHA-256 checksum
How to use checksums
fe2a033909ec5aa911d418ffd3ca161d3a80ab935324bc119247b62ea139a001
BLAKE2b-256 checksum
How to use checksums
6b827e26f2457302558ba45a656e3fdc982ca63bc90b9ad1c429120caaaed7dd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 1, 2026.

Transparency log

Release files / jevtest-0.10.0-py3-none-any.whl

Download URL jevtest-0.10.0-py3-none-any.whl
Size 172.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
45151d2c7b3c270de22063456699665793918993594c5a57c37448ea0c53926d
BLAKE2b-256 checksum
How to use checksums
31c93955b190a9eeb6aa18baf90293e80e90cac9323ba01a76717dd9993d5ecc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 1, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.10.0 This release

2 release files

0.9.17

2 release files

0.9.16

2 release files

0.9.15

2 release files

0.9.14

2 release files

0.9.13

2 release files

0.9.12

2 release files

0.9.11

2 release files

0.9.10

2 release files

0.9.9

2 release files

0.9.8

2 release files

0.9.7

2 release files

0.9.6

2 release files

0.9.5

2 release files

0.9.4

2 release files

0.9.3

2 release files

0.9.2

2 release files

0.9.1

2 release files

0.9.0

2 release files

0.8.0

2 release files

0.7.2

2 release files

0.7.1

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page