# PRD 需求文档 - 用例间登录态重置

> **文档类型**: PRD 需求文档
> **创建日期**: 2026-07-14
> **作者**: czj
> **优先级**: P0
> **状态**: 待开发

---

## 一、需求背景

### 1.1 问题描述

执行登录模块用例时，多个用例共享同一个执行器实例和浏览器上下文。当上一个用例登录成功进入主页后，下一个需要验证登录页面的用例（如"空账号密码验证"）会因为当前已在主页而**找不到登录表单元素**，导致测试失败。

### 1.2 问题场景示例

```
用例1：登录-完整登录流程 → 登录成功，进入主页（_is_logged_in = True）
用例2：登录-空账号密码验证 → 需要验证登录页面的错误提示
                            → 但当前已在主页，找不到 input[placeholder*="手机号"]
                            → ❌ 测试失败
```

### 1.3 根本原因

- 执行器实例在整批用例执行期间被**复用**（保持登录态）
- 用例之间**没有隔离**，前一个用例的副作用（登录态）会影响后一个用例
- 登录模块的用例都需要从**登录页面**开始，但缺乏重置机制

### 1.4 影响范围

- **页面**: 登录模块全部 5 个用例
- **核心问题**: 用例不独立、不可重复执行
- **用户预期**: 每个登录用例都应从登录页开始，无论上一个用例结果如何

---

## 二、需求目标

### 2.1 核心目标

每个登录模块用例执行前，都**重置到登录页面初始状态**，保证用例独立性和可重复性。

### 2.2 设计原则

- **用例配置控制**：通过用例 config 中的 `reset_login` 字段控制是否重置
- **灵活可控**：只有标记了 `reset_login: true` 的用例才重置，不影响其他模块
- **可靠性**：重置后必须确保回到登录页面且登录表单可用

---

## 三、技术方案

### 3.1 退出登录方式（已验证）

通过实测，被测系统（https://192.168.5.44）的可靠退出登录方式：

```python
# 1. 清除 cookies
context.clear_cookies()
# 2. 清除 localStorage / sessionStorage
page.evaluate('() => { localStorage.clear(); sessionStorage.clear(); }')
# 3. 重新访问登录页（用 domcontentloaded 避免主页 networkidle 超时）
page.goto(TARGET_URL, wait_until='domcontentloaded')
```

**验证结果**：
- 重置前：URL = `https://192.168.5.44/#/platform?platform=%2Fplatform%2F%23%2FHome`（主页）
- 重置后：URL = `https://192.168.5.44/#/platform?platform=%2Fplatform%2F%23%2Flogin`（登录页）
- 重置后登录表单元素存在 ✓

### 3.2 改动点

| 改动项 | 文件 | 说明 |
|--------|------|------|
| 新增 reset_login_state() 方法 | `backend/app/executors/playwright_executor.py` | 清除登录态并回到登录页 |
| execute_case 中检查 reset_login | `backend/app/executors/playwright_executor.py` | 执行步骤前判断是否重置 |
| 用例配置添加 reset_login | `backend/scripts/create_login_cases_full.py` | 登录模块用例标记 |
| 数据库已有用例更新 | 批量更新脚本 | 已存在的登录用例补上配置 |

### 3.3 配置字段定义

用例 `config` 中新增字段：

```json
{
  "timeout": 60000,
  "screenshot": true,
  "auto_login": false,
  "reset_login": true
}
```

| 字段 | 类型 | 默认 | 说明 |
|------|------|------|------|
| `reset_login` | bool | false | true 表示该用例执行前重置登录态 |

### 3.4 执行流程

```
execute_case(case):
    1. 合并用例 config
    2. 如果 config.reset_login == True:
         调用 reset_login_state()
         （清除 cookies + storage + 访问登录页 + _is_logged_in=False）
    3. 执行用例步骤
```

---

## 四、reset_login_state() 方法设计

```python
def reset_login_state(self) -> None:
    """
    重置登录态：清除 cookies + storage，重新访问登录页

    用于用例间隔离，保证每个需要登录页的用例都从初始状态开始。
    """
    try:
        logger.info("重置登录态...")
        # 清除 cookies
        self._context.clear_cookies()
        # 清除 localStorage / sessionStorage
        try:
            self._page.evaluate('() => { try { localStorage.clear(); sessionStorage.clear(); } catch(e) {} }')
        except Exception:
            pass
        # 重新访问登录页（用 domcontentloaded 避免 networkidle 超时）
        self._page.goto(self.LOGIN_CONFIG["base_url"], wait_until="domcontentloaded")
        self._page.wait_for_timeout(2000)
        # 重置登录状态标志
        self._is_logged_in = False
        logger.info("登录态已重置，回到登录页")
    except Exception as e:
        logger.error(f"重置登录态失败: {e}")
        # 重置失败不阻断，继续执行
```

---

## 五、验收标准

| 测试项 | 预期结果 |
|--------|----------|
| 单独执行登录-空账号密码验证 | ✅ 通过（从登录页开始） |
| 单独执行登录-完整登录流程 | ✅ 通过（登录成功） |
| 批量执行登录模块 5 个用例 | ✅ 全部通过（每个用例独立） |
| 先执行登录成功用例，再执行空账号验证 | ✅ 空账号验证仍通过 |
| 重置后登录表单可用 | ✅ input[placeholder*="手机号"] 存在 |
| 非登录模块用例不受影响 | ✅ reset_login=false 不重置 |

---

## 六、相关文档

- `backend/app/executors/playwright_executor.py` - 执行引擎核心
- `backend/scripts/create_login_cases_full.py` - 用例创建脚本
- `_PRD_需求文档_执行中心展开优化.md` - 上一轮修复（自动登录逻辑）

---

*本文档由 Claude Code 生成，遵循项目 PRD 文档规范。*
