eletrotupi / tcc/ frontend/e2e/README.md9cd93e6ab48e317d51b0bdf9066dc5852cda738f
4.3 KB | 113 lines Raw
1
# Mobile E2E (Maestro on a physical Android device)
2

3
Smoke coverage for the core journey: **login → register a mood → logout**. Runs
4
on a real Android device over `adb` (no emulator), against a dev build of the
5
app talking to the normal local API
6

7
## What you need
8

9
1. **API up, with the seeded user.**: the app points at whichever API
10
   `EXPO_PUBLIC_API_BASE_URL` names
11

12
   ```sh
13
   docker compose up -d db valkey api
14
   cd api && npm run seed        # idempotent; creates e2e@example.com / e2e-password
15
   ```
16

17
2. **Metro serving the dev build**, with the device-reachable API URL baked in
18
   (`EXPO_PUBLIC_*` is inlined at bundle time, so Metro must be started *after*
19
   `frontend/.env.local` is set: see `npm run test:e2e:env`)
20

21
3. **The dev build installed** (`com.eletrotupi.orbit`) and the device visible in
22
   `adb devices`
23

24
4. **Maestro** on `PATH` (`https://get.maestro.mobile.dev`)
25

26
## Running
27

28
```sh
29
cd frontend
30
npm run test:e2e          # adb prep + the tagged smoke flows
31
npm run test:e2e:env      # print the EXPO_PUBLIC_* values Metro needs
32
```
33

34
`scripts/e2e.sh` is POSIX `sh`, so it runs unchanged on Debian/Ubuntu and on
35
Alpine (busybox ash). It turns the device's animation scales off for the run and
36
restores the previous values on exit — that setting is persisted on the device,
37
so it is never left behind.
38

39
A single flow, while iterating (a few minutes faster than the whole suite):
40

41
```sh
42
cd frontend
43
npm run test:e2e:env      # prints HOST_IP, the one machine-specific value
44

45
maestro test e2e/mood.yaml \
46
  --env HOST_IP=<lan-ip> --env PORT=8081 \
47
  --env E2E_EMAIL=e2e@example.com --env E2E_PASSWORD=e2e-password
48
```
49

50
Three flows take roughly 8 minutes: every flow clears app state, so each one
51
pays for a cold bundle load and the dev-client startup dance
52

53
## How the flows are structured
54

55
Every flow is **self-contained and order-independent**: Maestro runs them in an
56
arbitrary order, so none may assume another ran first. Each starts with:
57

58
```yaml
59
- runFlow:
60
    file: helpers/_login.yaml
61
```
62

63
and `helpers/_login.yaml` does the whole reset-and-authenticate: `clearState`
64
=> deep-link the dev build => log in => dismiss the notification prompt. That
65
means `clearState` is what guarantees a logged-out start, which has three
66
consequences worth knowing:
67

68
- it resets **runtime permissions**, so the notification dialog shows up again
69
  and must be allowed (see copy matches below);
70
- it resets **expo-dev-client**, so its first-run overlays appear ("Continue",
71
  then the dev-menu panel's "Close");
72
- it discards the downloaded JS bundle, hence the cold start
73

74
The app is intentionally **not** launched with `exp://`: Expo Go owns that
75
scheme, and driving Expo Go while asserting on `com.eletrotupi.orbit` will fail
76
with "element not found" everywhere. The dev build owns
77
`orbit://expo-development-client/?url=...`, kinda hacky but I don't care
78

79
## testID convention
80

81
`id`s are English even though the UI is pt-BR. Flows only touch screens that
82
pass an explicit `testID`
83

84
Auto-derived fallbacks exist when a screen passes nothing: `Button` →
85
`button-<slug(title)>`, `Input`/`TextArea` → `input-<slug(label)>`,
86
`MoodSelector` → `mood-<id>`. The slug util lives in `Button.tsx` / `Input.tsx`
87
(NFD strip, lowercase, kebab-style)
88

89
Every flow file: **including subflows** — must declare `appId:
90
com.eletrotupi.orbit` in its config section. Maestro 2.x requires it per file;
91
the workspace `.maestro/config.yaml` cannot supply it
92

93
## The unavoidable copy matches
94

95
A handful of strings cannot be reached by `testID` because they belong to the
96
OS or to dev tooling. These are deliberate, and the only ones a flow may assert
97
on:
98

99
| Where | Selector | Why |
100
| --- | --- | --- |
101
| History card annotation | `"e2e smoke register"` | the one *data* assertion; proves write-through |
102
| Notification permission | `com.android.permissioncontroller:id/permission_allow_button` | AOSP system dialog |
103
| expo-dev-client onboarding | `^Continue$` | dev tooling, not app UI |
104
| expo-dev-client dev menu | `^Close$` (content-desc) | dev tooling, not app UI |
105

106
Everything else is greeted via `testID`
107

108
## Known gotchas
109

110
- **The formSheet opens at its half detent.** `entry/mood-components` uses
111
  `sheetInitialDetentIndex: 1` of `[0.25, 0.5, 1]`, so only the first rows of
112
  the component list are mounted
113
- The soft keyboard covers `button-login`; the flow calls `hideKeyboard` first