# 02 · 测试与验收计划

> 唯一判据：**现有 H5 零改动运行**，且行为与《契约规范》§8/§9/§11 完全一致。

## 1. 测试层次

| 层次 | 对象 | 手段 | 何时 |
|---|---|---|---|
| **单元测试** | 纯逻辑：`MessageCodec`、`HandlerRegistry`、`VersionResolver`、`ConfigManager` 解析、各 DTO 序列化 | ArkTS 单测 + `FakeWebController` | 随开发，CI 强制 |
| **桥协议测试** | `BridgeController` 全路径 | FakeWebController 模拟 H5 收发 | M1 |
| **集成测试** | 容器 + 启动 + 资源 + 配置 | 真机/模拟器 | M2 起 |
| **契约一致性测试** | 全 44 入站 + 全出站 handler | 真机 + 测试 H5 探针 | M3 起每周回归 |
| **端到端（E2E）** | 真实大厅 H5 + 子游戏 + 活动页 | 真机手测 + 录屏 | M3/M4 |
| **性能测试** | 冷启动、子游戏切换、内存 | DevEco Profiler / hilog 打点 | M5 |
| **安全测试** | 加固清单 | 人工审查 + 工具 | M5 |

## 2. 关键测试资产

### 2.1 最小回显 H5（M1 必备）
一个独立测试页，仅用桥 API：
- `registerHandler('echo', (d,cb)=>cb(d))`、`callHandler('getTime')`、`callHandler('echo','hi')`。
- 验证：H5→原生→回执、原生→H5→回执、启动前积压消息补发、`yy://` 拦截（V1）。

### 2.2 契约探针 H5（M3 回归）
一个覆盖**全部 44 入站 handler**的探针页：逐个 `callHandler` 并记录回传，与《契约规范》期望值比对，输出"通过/差异"报告。出站 handler 用 `registerHandler` 全量挂载、记录原生推送。
> 此页是"H5 零改动"的自动化守门员，纳入每周回归。

## 3. 两个阻塞性验证（最高优先，决定技术路线）

| 验证 | 用例 | 通过判据 | 不通过 → 动作 |
|---|---|---|---|
| **V1 `yy://` 拦截**（M1, T-M1-07） | 最小 H5 设 `iframe.src='yy://__queue__'`，原生 `onLoadIntercept` 取 URL | 能拿到 `yy://` URL 并拦截、`_fetchQueue` 回执经 `yy://return/` 到达 | 切 `onInterceptRequest`+`WebSchemeHandler`；或改桥 JS 走 `javaScriptProxy.postMessage`（[00 §5](./00_总体规划.md)） |
| **V2 `file://` 跨域**（M2, T-M2-08） | 现有大厅 `index.html` 以 `file://` 加载，请求本地 js/css/图片/`app_data.js` | 资源全部正常加载、H5 正常运行 | 启用自定义协议/`onInterceptRequest` 接管本地资源（框架 §7.3） |

> 这两项必须在对应里程碑**作为 Exit Gate 强制通过**。

## 4. 契约一致性核对清单（H5 零改动判据）

### 4.1 桥协议（§2）
- [ ] `window.WebViewJavascriptBridge` 注入后 API 与事件 `WebViewJavascriptBridgeReady` 一致
- [ ] `registerHandler`/`callHandler` 双向、回执、`callbackId`/`responseId` 路由正确
- [ ] `_fetchQueue` 经 `yy://return/` 回传（非 runJavaScript 返回值）
- [ ] 启动前积压消息在注桥后补发

### 4.2 入站 44 handler（§8）—— 逐条核对名称/参数/同步返回
- [ ] 账号社交：`accreditlogin`、`friendsSharetypeUrlToptitleDescript`、`getphoto`
- [ ] 支付：`paybrowser`、`getGameplay`（**本期桩，验"不报错不卡死"**）
- [ ] 设备信息：`getTime`、`getphonestate`、`getbattery`、`getwifiLevel`、`getnetwork`、`getcompareCode`、`getphoneInfo`、`getmarketname`、`getothername`、`getOther`
- [ ] 交互：`orientation`、`vibrator`、`repeatvibrator`、`canclevibrator`、`gameCopytext`、`gamepastetext`、`notification`(空)
- [ ] 摇一摇：`startshake`、`SwitchShake`、`stopshake`
- [ ] 定位：`startlocation`、`getlocationinfo`
- [ ] 音频：`prepareaudio`、`mediaTypeAudio`、`srcIsloop`、`voicePlaying`
- [ ] 扫码/相机/浏览器/网页：`opensaoma`、`opencamera`、`browser`、`OpenurlTitleData`、`openApplyDownloadpath`
- [ ] 子游戏/房间：`SwitchOverGameData`、`getGameinstall`、`createRoom`、`exitRoom`、`getVideoinfo`、`DragViewvideoIsshow`（**房间本期桩**）
- [ ] 退出/返回：`finsh`、`backgameData`

### 4.3 出站 handler（§9）—— 逐条核对名称/data 结构/时机
- [ ] `appservice`(1/2)、`setPostUrl`、`getWebdata`、`getphoto`、`sharelogin`、`sharesuccess`、`shakeEnd`、`getBattery`、`getwifiLevel`、`getnetwork`、`getaudiourl`(+`filepath`)、`gameui_play_voice`、`gameui_stop_voice`、`getphoneinfo`、`getVideoinfo`(桩不推)、`PayuserPaytypePaystate`(桩不推)、`getlocationinfo`、`phonestate`、`getsaomaData`、`getcameraaAddress`
- [ ] 名称陷阱：`backgameData-`（带连字符）≠ 入站 `backgameData`

### 4.4 数据结构（§12）
- [ ] `sharetypeBean`/`MaplocationInfo`/`phoneInfoBean`/`savephotoURLBean`/`videoinfobean`/`Othervideoinfo`/支付/`sharelogin`/`sharesuccess` 字段名同名同义

### 4.5 启动/资源/容器（§3–§7、§11）
- [ ] 配置键齐全、远程配置分层覆盖、`showmessage` 阻断
- [ ] 资源路径、version.xml 版本比较、zip 下载解压
- [ ] `app_data.js` 全局变量、**加载前注入**
- [ ] 入口 URL（`weburl` 空/非空）、`?Launchtype=` 参数
- [ ] 通用网页容器 `settings` 6 法 + 3 直调 + 101 回传

## 5. 里程碑验收 Gate

| 里程碑 | Gate |
|---|---|
| M0 | 工程编译/签名/CI 通过；`contracts` 全量类型对照契约零缺漏 |
| **M1** | 回显 H5 双向通；**V1 通过**；桥单测 ≥80% |
| **M2** | 真机加载大厅 H5、进子游戏；**V2 通过**；app_data.js 时序正确 |
| M3 | §4.2/§4.3/§4.4 清单全过；**桩能力不报错不卡死** |
| M4 | §11.3 通用容器一致；活动页/收银台/客服打开回传正常 |
| M5 | 切换 <300ms、冷启动 <2s（基线）；安全清单（附录 B）全过；可观测可用 |
| **本期总验收** | 现有大厅 + 全部子游戏 + 网页页面**零改动运行**；§4 全清单通过 |

## 6. 缺陷分级与回归

- **P0**：H5 无法运行/桥不通/崩溃 → 当日修复，阻塞合入。
- **P1**：某 handler 行为不符契约 → 当迭代修复。
- **P2**：性能/体验 → M5 收口。
- 每次合入触发"契约探针 H5"回归，防止已通过的 handler 退化。
