Skip to content

Commit 1d90f22

Browse files
committed
docs(skill): add mobile self-test workflow
1 parent f03c614 commit 1d90f22

File tree

1 file changed

+314
-0
lines changed

1 file changed

+314
-0
lines changed
Lines changed: 314 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,314 @@
1+
---
2+
name: mobile-self-test
3+
description: Self-test a mobile feature change or bug fix after implementation in `apps/mobile`. Use this whenever the user asks to verify a mobile change, run simulator acceptance, smoke-test a mobile PR, or provide screenshot proof for a mobile fix. This skill decides between prod vs local API mode, starts the local follow-server when needed, builds a release app, uses Maestro only to bootstrap registration for non-auth work, then switches to screenshot-driven visual validation and returns screenshot evidence.
4+
disable-model-invocation: true
5+
allowed-tools: Bash, Read, Write, Edit, Glob, Grep
6+
---
7+
8+
# Mobile Self Test
9+
10+
Validate a mobile change after implementation.
11+
12+
This skill extends `../mobile-e2e/SKILL.md`. Read that skill first for the baseline doctor checks, iOS simulator boot rules, Java/Android SDK setup, and Maestro artifact conventions. Then apply the extra rules in this skill.
13+
14+
## Files that matter
15+
16+
- Reference skill: `../mobile-e2e/SKILL.md`
17+
- Runner: `apps/mobile/e2e/run-maestro.sh`
18+
- iOS register flow: `apps/mobile/e2e/flows/ios/register.yaml`
19+
- Android register flow: `apps/mobile/e2e/flows/android/register.yaml`
20+
- Shared auth flows: `apps/mobile/e2e/flows/shared/*.yaml`
21+
- Expo config: `apps/mobile/app.config.ts`
22+
- Build profiles: `apps/mobile/eas.json`
23+
- Mobile artifacts: `apps/mobile/e2e/artifacts/`
24+
- Local server repo: `/Users/diygod/Code/Projects/follow-server`
25+
26+
## Default assumptions
27+
28+
- Prefer **iOS simulator** unless the user explicitly asks for Android or the change is Android-specific.
29+
- Default to **prod API mode** when the user did not specify a mode.
30+
- Default to **local API mode** when the task also involves local server changes, backend debugging, or modified files in `/Users/diygod/Code/Projects/follow-server`.
31+
- Keep `EXPO_PUBLIC_E2E_LANGUAGE=en` unless the user explicitly wants another language. The existing Maestro flows assume English UI.
32+
33+
## Decide API mode first
34+
35+
Use this decision order:
36+
37+
1. If the user explicitly asks for `prod` or `local`, obey that.
38+
2. Otherwise, if the task depends on local backend changes or local server behavior, use `local`.
39+
3. Otherwise, use `prod`.
40+
41+
Map the chosen mode into the release build:
42+
43+
- `prod` mode: `EXPO_PUBLIC_E2E_ENV_PROFILE=prod`
44+
- `local` mode: `EXPO_PUBLIC_E2E_ENV_PROFILE=local`
45+
46+
Do not silently reuse a build from the other mode. Rebuild the release app when switching between `prod` and `local`.
47+
48+
## Always do first
49+
50+
From repo root:
51+
52+
```bash
53+
cd apps/mobile
54+
pnpm run e2e:doctor
55+
pnpm run typecheck
56+
```
57+
58+
If these fail, stop and report the blocker before attempting simulator work.
59+
60+
## Local server mode
61+
62+
`local` mode requires the local server to be available at `http://localhost:3000`.
63+
64+
Before starting anything, check whether it is already running. Do not start a duplicate server.
65+
66+
```bash
67+
FOLLOW_SERVER_LOG=/tmp/follow-server-dev-core.log
68+
69+
if pgrep -af "pnpm dev:core" >/dev/null 2>&1 || lsof -nP -iTCP:3000 -sTCP:LISTEN >/dev/null 2>&1; then
70+
echo "follow-server already running"
71+
else
72+
(
73+
cd /Users/diygod/Code/Projects/follow-server
74+
nohup pnpm dev:core >"$FOLLOW_SERVER_LOG" 2>&1 &
75+
)
76+
fi
77+
78+
for _ in $(seq 1 60); do
79+
nc -z 127.0.0.1 3000 >/dev/null 2>&1 && break
80+
sleep 2
81+
done
82+
83+
nc -z 127.0.0.1 3000 >/dev/null 2>&1
84+
```
85+
86+
If the task depends on other local surfaces such as `http://localhost:2233`, call that out explicitly instead of pretending the mobile test fully covers it.
87+
88+
## Release build profiles for self-test
89+
90+
Use release-style builds so the test matches user-facing behavior.
91+
92+
- iOS simulator builds: `PROFILE=e2e-ios-simulator`
93+
- Android emulator builds: `PROFILE=e2e-android`
94+
95+
Always pair those with the chosen API mode and language:
96+
97+
```bash
98+
export EXPO_PUBLIC_E2E_ENV_PROFILE=<prod-or-local>
99+
export EXPO_PUBLIC_E2E_LANGUAGE=en
100+
```
101+
102+
## iOS workflow
103+
104+
Reuse the simulator selection and boot process from `../mobile-e2e/SKILL.md`.
105+
106+
### Build release simulator app
107+
108+
```bash
109+
cd apps/mobile/ios
110+
pod install
111+
112+
PROFILE=e2e-ios-simulator \
113+
EXPO_PUBLIC_E2E_ENV_PROFILE=<prod-or-local> \
114+
EXPO_PUBLIC_E2E_LANGUAGE=en \
115+
xcodebuild -workspace Folo.xcworkspace \
116+
-scheme Folo \
117+
-configuration Release \
118+
-sdk iphonesimulator \
119+
-destination 'id=<IOS_UDID>' \
120+
clean build
121+
```
122+
123+
Expected output pattern:
124+
125+
```bash
126+
~/Library/Developer/Xcode/DerivedData/.../Build/Products/Release-iphonesimulator/Folo.app
127+
```
128+
129+
### Install app on simulator
130+
131+
```bash
132+
xcrun simctl install <IOS_UDID> <PATH_TO_Folo.app>
133+
xcrun simctl launch <IOS_UDID> is.follow
134+
```
135+
136+
## Android workflow
137+
138+
Reuse the Java and Android SDK setup from `../mobile-e2e/SKILL.md`.
139+
140+
If no emulator is booted yet, start one before installing the APK.
141+
142+
```bash
143+
emulator -list-avds
144+
emulator @<AVD_NAME>
145+
adb wait-for-device
146+
```
147+
148+
If `apps/mobile/android` does not exist locally, generate it first.
149+
150+
```bash
151+
cd apps/mobile
152+
pnpm expo prebuild android
153+
```
154+
155+
### Build release APK
156+
157+
```bash
158+
cd apps/mobile/android
159+
160+
PROFILE=e2e-android \
161+
EXPO_PUBLIC_E2E_ENV_PROFILE=<prod-or-local> \
162+
EXPO_PUBLIC_E2E_LANGUAGE=en \
163+
./gradlew clean app:assembleRelease --console=plain
164+
```
165+
166+
Expected APK path:
167+
168+
```bash
169+
apps/mobile/android/app/build/outputs/apk/release/app-release.apk
170+
```
171+
172+
### Install app on emulator
173+
174+
```bash
175+
adb -s <ANDROID_DEVICE_ID> install -r apps/mobile/android/app/build/outputs/apk/release/app-release.apk
176+
adb -s <ANDROID_DEVICE_ID> shell monkey -p is.follow -c android.intent.category.LAUNCHER 1
177+
```
178+
179+
## Choose the auth strategy
180+
181+
This is the core difference from `mobile-e2e`.
182+
183+
### A. Change is **not** related to login or registration
184+
185+
Use the existing automated **registration** flow first to bootstrap a clean logged-in account, then do the real verification visually.
186+
187+
Examples:
188+
189+
- timeline behavior
190+
- subscription management
191+
- onboarding content after auth
192+
- settings pages unrelated to sign-in state
193+
- player, reader, share, discover, profile editing
194+
195+
Generate a unique test account before running the flow:
196+
197+
```bash
198+
export E2E_PASSWORD='Password123!'
199+
export E2E_EMAIL="folo-self-test-$(date +%Y%m%d%H%M%S)@example.com"
200+
```
201+
202+
#### iOS registration bootstrap
203+
204+
```bash
205+
cd apps/mobile
206+
maestro test --format junit --platform ios --device <IOS_UDID> \
207+
--debug-output e2e/artifacts/ios/register-bootstrap \
208+
-e E2E_EMAIL="$E2E_EMAIL" \
209+
-e E2E_PASSWORD="$E2E_PASSWORD" \
210+
e2e/flows/ios/register.yaml
211+
```
212+
213+
#### Android registration bootstrap
214+
215+
```bash
216+
cd apps/mobile
217+
maestro test --format junit --platform android --device <ANDROID_DEVICE_ID> \
218+
--debug-output e2e/artifacts/android/register-bootstrap \
219+
-e E2E_EMAIL="$E2E_EMAIL" \
220+
-e E2E_PASSWORD="$E2E_PASSWORD" \
221+
e2e/flows/android/register.yaml
222+
```
223+
224+
After registration succeeds, continue with screenshot-driven visual testing.
225+
226+
### B. Change **is** related to login, registration, logout, session handling, auth validation, or onboarding gates
227+
228+
Do **not** rely on the existing Maestro auth flows for the actual verification. Use a fully visual/manual run instead so the changed UX itself is what gets tested.
229+
230+
Examples:
231+
232+
- register screen changes
233+
- login screen changes
234+
- credential validation changes
235+
- auth toggle changes
236+
- logout behavior
237+
- auth/session restoration
238+
- onboarding shown or hidden based on auth state
239+
240+
For auth-related work:
241+
242+
- create the test account manually through the UI if needed
243+
- use screenshots after every critical step
244+
- verify success and error states visually
245+
- keep a clean record of the exact screen sequence shown to the user
246+
247+
## Screenshot-driven visual testing
248+
249+
Once the app is in the right state, drive the rest of the validation with the visual toolchain available in the current environment. Screenshots are the source of truth for acceptance.
250+
251+
Create a timestamped artifact folder first:
252+
253+
```bash
254+
REPO_ROOT="$(git rev-parse --show-toplevel)"
255+
ARTIFACT_DIR="$REPO_ROOT/apps/mobile/e2e/artifacts/manual/$(date +%Y%m%d-%H%M%S)-<platform>-<prod-or-local>"
256+
mkdir -p "$ARTIFACT_DIR"
257+
```
258+
259+
Capture screenshots after each meaningful checkpoint.
260+
261+
### iOS screenshot command
262+
263+
```bash
264+
xcrun simctl io <IOS_UDID> screenshot "$ARTIFACT_DIR/<name>.png"
265+
```
266+
267+
### Android screenshot command
268+
269+
```bash
270+
adb -s <ANDROID_DEVICE_ID> exec-out screencap -p > "$ARTIFACT_DIR/<name>.png"
271+
```
272+
273+
Minimum screenshot set for a complete self-test:
274+
275+
1. entry screen before the changed flow
276+
2. the changed screen or interaction in progress
277+
3. the final success state or the reproduced bug state
278+
279+
Add more screenshots when the flow has multiple important states.
280+
281+
Do not report success without screenshot evidence.
282+
283+
## What to validate visually
284+
285+
Use the screenshots to confirm at least these points when relevant:
286+
287+
- the correct screen is reached
288+
- the changed control, copy, or layout is visible
289+
- loading, empty, error, and success states look correct
290+
- the operation completes without obvious regressions or blocking dialogs
291+
- the app is talking to the intended environment (`prod` or `local`)
292+
293+
If the UI or behavior is ambiguous, capture another screenshot instead of guessing.
294+
295+
## Final user-facing output
296+
297+
The final response must include:
298+
299+
- API mode used and why it was chosen
300+
- platform and simulator/emulator used
301+
- whether the local server was reused or started, plus log path if started
302+
- build command used
303+
- whether auth bootstrap was automated or fully visual
304+
- concise step-by-step result summary
305+
- pass/fail conclusion
306+
- screenshot evidence with absolute file paths
307+
308+
If the client supports local image rendering, attach the key screenshots as images in the final message. Otherwise, list the absolute paths clearly so the user can open them.
309+
310+
## Failure handling
311+
312+
- If doctor, typecheck, build, install, or server startup fails, stop and report the exact failing command.
313+
- If `local` mode cannot reach the local server, do not silently fall back to `prod`.
314+
- If the visual flow cannot be completed because the environment lacks the required interaction tooling, report that limitation clearly and still return the screenshots you captured.

0 commit comments

Comments
 (0)