Files
life-echo/app-expo/docs/testing.md
Sully fa42757916 feat: OpenTelemetry LGTM observability, dev tooling, and memoir UX fixes (#31)
* add staging ios app build script

* feat(api): add OpenTelemetry LGTM stack for local observability

Wire OTel traces, metrics, and logs through a collector to Tempo,
Prometheus, and Loki, with custom LLM instrumentation, dev compose overlay,
Grafana provisioning, env templates, and development.sh auto-start.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat: expand observability, harden dev tooling, and fix expo staging UX

Add business and LLM Prometheus metrics with Grafana dashboards, alerting,
and a metrics verification script. Wire telemetry through adapters and core
LLM paths, and document the local LGTM workflow.

Fix development.sh for macOS bash 3.2, open Grafana and eval-web in Chrome,
and repair eval-web auto-open (unbound EVAL_WEB_BROWSER_SCHEDULED). Merge
internal-eval into the main dev script with improved compose handling.

Require EXPO_PUBLIC_* at build time, improve iOS HTTP ATS for staging IPs,
show memoir empty state instead of load errors when no chapters exist, and
add jest env setup plus chapter list response normalization.

Co-authored-by: Cursor <cursoragent@cursor.com>

* chore: enable Grafana Assistant Cursor plugin

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: memoir empty state and repair withdrawn 0020_chapters_book_id stamp

Show empty memoir UI when the chapter list succeeds with no items; treat auth/404 as non-fatal. Extend alembic revision repair so local dev DBs stamped with the removed 0020_chapters_book_id migration can roll back and upgrade to 0019.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Kevin <kevin@brighteng.org>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-20 15:12:21 +08:00

192 lines
5.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# `app-expo` 测试框架规范
## 目标
测试只解决一件事:阻止高价值回归。
禁止为了覆盖率、形式感或 TDD 仪式感添加低价值测试。
## 工具链
- 测试框架:`Jest`
- 组件测试:`@testing-library/react-native`
- Expo 预设:`jest-expo`
- E2E暂不纳入默认门禁后续如需引入使用 `Maestro`
## 强制规则
- 必须优先测试高风险代码:有分支、有状态、有环境差异、有回归历史。
- 必须优先写用户可见断言,不写实现细节断言。
- 必须把测试文件放在 `tests/`,禁止放进 Expo Router 的 `app/` 目录。
- 必须让 `react-test-renderer``react` 主版本严格一致。
- 必须在新增领域逻辑时评估是否加入 `collectCoverageFrom`
- 必须保证 CI 可重复运行,测试不得依赖本地手工环境。
## 禁止规则
- 禁止为了凑覆盖率给低价值代码补测试。
- 禁止批量给 `src/components/ui/*` 生成型 wrapper 补测试。
- 禁止测试 `className`、样式细节、第三方库内部实现。
- 禁止默认使用 snapshot。
- 禁止在没有明确回归风险时盲目 TDD。
- 禁止在 CI 中自动更新 snapshot。
## 测什么
优先级从高到低:
1. 纯逻辑和桥接层
2. 关键页面的关键可见结果
3. 少量高价值主流程
当前门禁覆盖文件:
- `src/i18n/index.ts`
- `src/constants/theme-bridge.ts`
- `src/app/index.tsx`
## 不测什么
默认不测以下内容,除非出现真实回归或明确业务风险:
- `src/components/ui/*`
- 纯透传组件
- 静态文案和静态布局
- 样式实现细节
- 第三方库已经保证的行为
## RNTL 规则
- 当前使用 `@testing-library/react-native@13`
- 直接使用内置 matcher例如 `toBeOnTheScreen()`
- 不添加 `@testing-library/react-native/extend-expect`
- 不引入 `@testing-library/jest-native`,除非现有 matcher 明确不够用
## TDD 规则
- 新增业务逻辑、修 bug、有明确回归风险时优先测试先行
- 配置接入、探索性 UI 调整、低风险文案变更:不强制 TDD
- 如果代码很难测:先拆耦合,再决定是否写测试
## Snapshot 规则
默认不用 snapshot。
只有同时满足以下条件才允许:
- 组件稳定
- 共享范围广
- 风险来自整体结构而不是交互
- 评审明确同意
否则一律改写为行为断言。
## 覆盖率规则
覆盖率是门禁,不是目标。
当前阈值:
- `branches >= 80`
- `functions >= 90`
- `lines >= 90`
- `statements >= 90`
这些阈值只对 `collectCoverageFrom` 中的高价值文件生效。
新增有分支、状态变化或环境差异的领域逻辑时,评审必须回答一个问题:
`这个文件要不要进入 collectCoverageFrom`
## 本地命令
```bash
npm run test
npm run test:changed
npm run test:ci
npm run test:update
```
本地约束:
- 日常开发优先使用 `npm run test``npm run test:changed`
- 提交前必须至少运行一次 `npm run test:ci`
- 只有明确批准时才运行 `npm run test:update`
## CI 门禁
`App Expo Quality` 必须执行:
- `npm ci`
- `npm run format:check`
- `npm run lint`
- `npm run test:ci`
同仓 PR 额外要求:
- 发布 coverage sticky comment
## E2E 规则
E2E 不是默认门禁。
只有满足以下条件才引入:
- 核心用户旅程稳定
- 已明确 smoke flow
- 团队能承担维护成本
引入后也只测关键主路径,不做像素巡检,不做大面积 UI 回归脚本。
### 前置准备
安装 Maestro CLI
```bash
curl -Ls "https://get.maestro.mobile.dev" | bash
```
构建并安装到目标 iOS Simulator / Android Emulator / 真机。不同 flow 对构建方式的要求不同:
| 场景 | 构建命令 | 读取配置 |
|------|---------|---------|
| 普通开发 | `pnpm ios` / `pnpm android` | `env/development` |
| 需要登录态的 E2E | `pnpm ios:e2e` / `pnpm android:e2e` | `.env.e2e`staging backend + E2E 开关) |
正常 staging 构建继续使用 `env/staging``use-env staging`),不注入 `EXPO_PUBLIC_E2E`
### 现有 flow
| Flow 文件 | 用途 | 需要后端 | 需要 E2E 构建 |
|-----------|------|---------|--------------|
| `login-smoke.yaml` | 登录页协议拦截 smoke验证 App 启动到未登录态 | 否 | 否 |
| `login-authenticated.yaml` | 快捷登录进入已登录态 | 是 | 是 |
| `post-login-tabs-smoke.yaml` | 登录后 Tab 导航 smoke对话 → 回忆录 → 我的 → 对话) | 是 | 是 |
| `post-login-long-chat.yaml` | 登录后发送大量对话消息,为回忆录生成提供种子数据 | 是 | 是 |
### 运行命令
无后端依赖(普通构建即可):
```bash
pnpm e2e:ios
pnpm e2e:android
```
需要 E2E 构建 + 后端 mock 登录:
```bash
pnpm e2e:auth:ios
pnpm e2e:auth:android
pnpm e2e:post-login:ios
pnpm e2e:long-chat:ios
```
调试时可通过 `pnpm start:e2e` 启动 dev server 并自动加载 `.env.e2e`
### 登录后 E2E 环境要求
- 后端:`APP_ENV` 不能是 `production`,并开启 `MOCK_SMS_LOGIN_ENABLED=1`
- App通过 `pnpm ios:e2e` / `pnpm android:e2e` 构建安装,读取 `.env.e2e`
- 所有需要登录态的 flow 通过点击 `login.e2e.quickLogin.button` 进入已登录态