RideKit
Docs/Reference/End-to-end run — 2026-09-07

End-to-end run — 2026-09-07

Everything that ships was exercised against one local stack in a single overnight run: the three apps on two Android emulators side by side, the operator admin and the fleet owner's web console through Playwright, the sales site and the operator's marketing-site template, plus every unit and integration suite. Bugs found on the way were fixed in the same sitting and are listed below with the commit that carries them.

The stack under test

Piece How it ran
Backend NestJS build (node dist/main.js) on :3000, Postgres 17 + PostGIS, Redis, dev OTP 000000, e2e seed (src/database/seeds/e2e-seed.ts)
Admin next dev on :3200 against the backend above
Rider + partner apps release APKs on a headless Pixel-class emulator (emulator-5554), GPS fixed to Mumbai, API at 10.0.2.2:3000
Driver app same, on a second emulator (emulator-5556)
Sales site / site-template next build + the test suites, then a crawl of every route

Results

Mobile — two emulators, one real ride (mobile/scripts/e2e-side-by-side.sh)

Step Result Proof in the database
Driver signs in and goes online driver_profiles.availability = online
Rider signs in, searches "Bandra Kurla Complex", books a Bike a new rides row, requested
Driver gets the offer, accepts, arrives, starts, completes ride completed with this driver; driver credit + commission postings for the ride
Driver rates the rider, rider rates the driver two ride_ratings rows
Rider screen tour (16 screens, ends in Hindi) screenshots rider-20rider-35
Driver screen tour (14 screens, ends in Hindi) ✅ (stand-alone re-run after the menu fix) screenshots driver-20driver-33
Partner tour (15 screens) screenshots partner-01partner-15
Partner registers, assigns, frees and retires a car the plate is gone from live vehicles
A new number applies to run a fleet an unverified organizations row with applied_at

Twelve runs were needed to get here; every one of the earlier failures is in the table below, and the fix for each is in the tree. The last two runs were slowed by something outside the code: the Mac's idle-sleep timer (set to one minute) put the whole stack to sleep several times, and caffeinate did not hold it awake with the lid closed. A step that shows an hour in the log was asleep, not slow.

Admin — Playwright (admin/e2e)

18 spec files, 67 tests, run as the operator (and, for the partner panel, as the fleet owner) against the seeded stack:

Run Result
Full suite, two workers in parallel 62 passed, 5 failed
The five failures re-run one at a time 4 of them pass untouched — they had tripped over each other in parallel (a language switch persisted for the same admin user while another spec read English; the fleet application toggled verified under another spec's feet)
The remaining two one real bug (#21, the Add-vehicle modal) and one stale assertion (#22, the Markets scope) — both fixed, both green on re-run

Everything in admin/e2e is green serially. Making the suite safe to run in parallel (a per-worker admin user, a per-spec fleet) is a follow-up; CI runs it with the config's default workers, so it belongs on the list below.

Unit and integration suites

Suite Result
Backend Jest (against the real Postgres + Redis) 103 suites, 1424 passed, 0 failed
Admin Vitest 21 files, 396 passed
Partner app Jest (stores, formatting, catalog parity) 56 passed
Sales site Vitest 32 passed
site-template content tests 10 passed
Backend tsc, admin tsc clean

Sales site and site-template

  • site: 32 vitest tests green; next build clean; every one of the 24 routes answers 200 in a crawl; the API routes refuse what they should.
  • site-template: 10 content tests green after the defaults fix; next build clean.

Bugs found and fixed on the way

# Where What was wrong Fixed in
1 Admin → Partners The list never returned isVerified, the settlement terms or the driver/vehicle counts, so every fleet rendered as unverified with "0 drivers · 0 vehicles". 97faa01
2 Admin shell A fleet owner's token could open the operator console by URL — an RBAC-empty shell where every call 403s. The dashboard layout now routes anyone whose landing page is not /dashboard there. 97faa01
3 Admin lists On drivers, riders, trips, parcels and vehicles the filter → URL sync (router.replace) could cancel a row's Open navigation that had just started; found by the Playwright specs clicking Open. Now history.replaceState, which the router never sees. 97faa01
4 Partner app Every fleet owner was told they were not one whenever the fleet check failed for any reason; a 403 now leads to the application form and keeps the session, any other failure says what it is. 403a742
5 Backend RBAC Nothing had ever granted the fleet_owner role, so the partner surfaces answered 403 to the very people they were built for. Granted on application, operator creation and ownership change; existing owners backfilled by migration. 95812ca
6 Admin e2e Four specs were written against the pre-redesign login and shell (OTP field, sign-out, language menu, confirm dialog) and had been failing quietly. d841713
7 site-template Tests still carried a copy of the section defaults that had moved into the backend, and the colour fallback was a different teal from everything else. b1b54ce
8 Driver flow Google Play services' one-time "Location Accuracy" dialog sat over the map on a fresh image and swallowed the first tap on Go online; the Maestro flows now dismiss it (optionally, so a real device without it is unaffected). this run
9 Mobile flows Under load (two emulators, Playwright and next dev at once) a cold start takes close to a minute; the first-screen waits were 45 s and the rider flow timed out with the welcome screen half a second away. Raised to 120 s and the mobile run now goes after the browser run, not beside it. this run
10 e2e seed Re-running the partner specs left "E2E Fleet …" organisations behind, and the OTP throttle from the previous run blocked the next sign-in. The seed now purges the smoke fleets and resets the throttle for every seeded phone. 97faa01
11 e2e seed / dispatch The demo driver's active vehicle was an "Auto Rickshaw" — a type with no fare in the market — so the rider's booking (a Bike, the first option on the confirm screen) was never offered to them and sat in requested. Dispatch was right; the data was not. The seed now pins the demo driver's active vehicle to the market's first priced type and hands any other driver left on that car one of their own back. this run
12 Rider flow Google's "Location Accuracy" consent lands on top of the rider's home the moment it asks for a position, so the flow waited for Where to behind a dialog. It now accepts the dialog first (optionally), as the driver flow does. this run
13 Mobile script Four sign-ins per run against a per-phone OTP limit of five an hour meant the second run after a fix was refused with 429. The script now resets the throttle for its four test phones before it starts, the way the seed does. this run
14 e2e seed / service area Two service zones drawn by hand for an analytics demo (Bengaluru, Indore) were still active in the dev database. Once any service zone exists the platform stops serving everywhere, so every booking at the market centre (Mumbai) was refused with "Point is outside the service area" — correct enforcement, stale data. The seed now adds an "E2E service area" around the market centre when the zones drawn leave it out, and removes it on clean; the hand-drawn zones are untouched. this run
15 Mobile script Dispatch offers a ride to one driver at a time with a 15 s window and marks it no_drivers when the list is exhausted — there is no second round. The script ran the driver's accept flow after the rider's booking flow had finished, so the offer had come and gone a minute before the driver app was even watching. The driver flow now starts first, in the background, and waits up to five minutes for the offer the rider's booking produces. A headless emulator dropping off adb mid-flow (seen twice) is retried once. this run
16 Emulators Booted headless with -gpu swiftshader_indirect, the driver emulator burned four to five host cores rendering the live map in software, and the other emulator kept dropping off adb ("device offline") at every app launch. Rebooting both with -gpu host (Metal on Apple silicon) halved that; the flows' launch retries stopped firing. this run
17 Rider flow The rating flow restarted the app to reach the rating screen; a restart lands on home once the ride is complete (see the observation below), so it now brings the app forward without restarting it and the tracking screen hands over to rating as designed. this run
18 Screen tours Account-menu rows below the fold (Help, Settings, Legal …) were tapped without scrolling; both tours now scroll each row into view first. this run
19 Partner tour Maestro's text: selector is a regex, and the seeded address "Fleet pickup 1 (E2E)" has parentheses in it — the group matched nothing. Escaped. The application flow's fleet is now removed before each run, like the Playwright smoke fleets. this run
20 Fleet console (backend) A fleet could never register a plate that had belonged to a retired car: the clash check read every vehicle ever, while the database's unique index is partial on deleted_at IS NULL (cars change hands; retire-and-re-add is normal). Found by the partner vehicle flow on its second run. Both the create and the update check now look at live vehicles only. this run
21 Admin fleet console The Add-vehicle modal read the vehicle-type catalogue once, when it opened; a click that beat the catalogue request left the select showing the first type while the form held none, and Add stayed disabled for good. Caught by the Playwright partner-panel spec on a fast run. The form now takes the first type when the catalogue arrives. this run
22 Admin e2e The Markets page opens on the installation's own country group; the spec counted the whole catalogue against that chip. It now switches to "All countries" first. this run

Not a bug in the product, but worth knowing: port 3000 was held by a runaway next-server from an unrelated project (~/website/apps/web, 185 % CPU); it was killed so the backend could bind. The supervisor scripts used for the run restart the backend and admin if they die.

What the mobile run covers

mobile/scripts/e2e-side-by-side.sh drives both emulators in the order a real ride happens, and asks the database after each step whether it really happened:

  1. Driver goes online (driver/e2e/10-go-online.yaml) → driver_profiles.availability = online.
  2. Rider books (rider/e2e/10-ride-request.yaml) — sign in, home, search "Bandra Kurla Complex", confirm, dispatch searching → a rides row in requested|accepted|arrived.
  3. Driver accepts, arrives, completes, rates (driver/e2e/11-accept-and-complete.yaml) → ride completed with this driver, driver credit + commission postings in the ledger, one ride_ratings row.
  4. Rider rates (rider/e2e/11-ride-rate.yaml) → two ratings on the ride.
  5. Rider screen tour (rider/e2e/12-screens-tour.yaml) — parcel, rental, intercity, menu, history, wallet, places, referral, scheduled, safety, help, settings, parcels, profile, language → Hindi.
  6. Driver screen tour (driver/e2e/12-screens-tour.yaml) — menu, earnings, history, wallet, quests, heatmap, destination, profile, vehicles, documents, settings, help, legal, Hindi.
  7. Partner tour (partner/e2e/01-login-tour.yaml) — welcome, home, drivers, driver detail + terms, vehicles, vehicle detail, trips, trip detail, earnings, wallet + withdraw sheet, account, Hindi, legal.
  8. Partner manages a car (partner/e2e/02-manage-vehicle.yaml) — add "MH 02 E2E 7777", assign Vinod Yadav, free, retire → the plate is gone from vehicles.
  9. Partner applies (partner/e2e/03-apply.yaml) — a fresh account applies → an unverified organisation with applied_at.

Screenshots of every screen are in docs/media/e2e-2026-09-07/ and the step log is RESULTS.md in the same folder.

What the admin run covers

admin/e2e — 18 specs. Beyond the feature specs (auth, dashboard, dispatch, drivers, trips, parcels, markets, organizations, pricing, promos, safety, settings, i18n, website), two are new for the partner work and one is a net:

  • partners.spec.ts — the operator's list flags the application, verify is a round trip through the API and reversible, the operator sees a fleet's drivers / cars / trips / earnings, and creating a fleet for a new owner lets that owner sign in to it.
  • partner-panel.spec.ts — the owner lands on their own fleet (never the operator console), the tables match the API, adding a car and assigning a driver round-trips and is undone, and a driver is found by phone, shown masked, and cannot be added twice.
  • screens.spec.ts — opens all 34 operator screens and fails on any console error, unhandled rejection, failed API call or empty page.

Observations worth a product decision

  • Rating after a restart. If the rider closes the app between the driver completing the trip and the rating screen, the next open lands on home and nothing asks for the rating; the trip can only be rated from its history entry. Most ride apps put a "rate your last trip" card on the next open. Not a defect — the ride settles either way — but a gap in the loop. (The rating flow keeps the app alive between steps for exactly this reason.)

Follow-ups (engineering, not urgent)

  • Parallel-safe admin e2e — the i18n and partners specs share one admin user and one seeded fleet; give each worker its own, or pin the suite to one worker in CI.
  • "Rate your last trip" on next open — see the observation above.
  • Emulator sleep — a long unattended run needs the Mac's idle sleep off (pmset) or the lid open; caffeinate alone was not enough.

Open items for the morning (owner decisions, not defects)

  • Sales site copyTODO(owner) markers are still visible on /demo, /faq and /legal/*: the legal entity name, what support covers, and the Expo / store links. The crawl passes; the words are yours to choose.
  • Pricing — the plans on the sales site are placeholders until the numbers are decided.
  • Selling on other platforms — to discuss; nothing in the code assumes one channel.
source: docs/testing/END-TO-END-2026-09-07.md (ships identically in the product zip)