# pwauto

基于 Playwright + pytest 的高效 UI 自动化测试框架。

## 核心特性

- **全局自动登录**：整个测试会话期间自动静默登录一次（提取 `users.json` 中配置的第一个用户），并在内存与硬盘（`state.json`）中持久化凭证，后续所有用例极速复用登录态。如登录失败则触发全局熔断。
- **自动失败录像 (Trace)**：引入智能 Trace 录制器，用例失败时会自动保存排错全家桶（DOM快照、网络请求、控制台输出、录像）至 `reports/traces/`，并**自动无缝挂载**到 Allure 报告中。
- **按需分配测试夹具**：
  - `page` 夹具：自带全局登录状态，开箱即用，适合绝大多数业务测试。
  - `guest_page` 夹具：纯净的游客状态，专门用于测试“登录”、“注册”等未授权页面。

## 项目结构

```
pwauto/
├── pwauto/                     # 框架核心（pip install -e . 安装后全局可用）
│   ├── core/
│   │   └── base.py             # BasePage 基类，封装通用页面操作
│   └── utils/
│       ├── config.py           # 多环境配置加载（基于 .env 文件）
│       ├── data_loader.py      # JSON 测试数据加载器
│       ├── feishu.py           # 飞书 Webhook 通知
│       ├── logger.py           # 日志配置（loguru）
│       └── paths.py            # 项目路径常量，自动创建目录机制
├── pages/                      # Page Object 层（业务页面）
│   ├── login_page.py
│   └── index_page.py
├── tests/                      # 测试用例
│   ├── conftest.py             # pytest 全局配置、fixtures、hooks
│   └── test_login.py
├── data/                       # 测试数据（JSON）
│   └── users.json
├── logs/                       # 运行日志（自动生成，已 gitignore）
├── reports/                    # 各种报告和产物文件夹（已 gitignore）
│   ├── allure-results/         # Allure 原始数据（自动生成）
│   └── traces/                 # Playwright 失败追踪录像文件（自动生成）
├── .env.prod                   # 环境配置文件（已 gitignore，需手动创建）
├── .gitignore
└── pyproject.toml              # 项目依赖 & pytest 配置
```

## 框架分层

```
测试用例 (tests/)
    ↓ 调用
页面对象 (pages/)
    ↓ 继承
框架基类 (pwauto/core/base.py)
    ↓ 使用
工具层 (pwauto/utils/)
```

- **pwauto/core** — 框架基类，封装了 `navigate`、`click`、`fill`、`get_text` 等通用操作，自带日志和超时控制
- **pwauto/utils** — 工具集：环境配置、日志、数据加载、飞书通知
- **pages/** — Page Object 层，每个页面一个类，定位器和业务行为分离
- **tests/** — 测试用例，通过 pytest 参数化驱动，数据来自 `data/` 目录

## 环境准备

### 1. 安装依赖

```bash
python -m venv venv
source venv/bin/activate
pip install -e .
playwright install chromium
```

### 2. 创建环境配置文件

根据目标环境创建 `.env.{env}` 文件（如 `.env.dev`、`.env.test`、`.env.prod`），格式：

```ini
BASE_URL=https://your-app-url.com
HEADLESS=false
TIMEOUT=10000

# 飞书通知（可选）
FEISHU_ENABLED=true
FEISHU_WEBHOOK=https://open.feishu.cn/open-apis/bot/v2/hook/your-webhook-id
```

| 变量 | 说明 | 默认值 |
|------|------|--------|
| `BASE_URL` | 被测系统地址（必填） | — |
| `HEADLESS` | 是否无头模式运行浏览器 | `false` |
| `TIMEOUT` | 全局操作超时时间（ms） | `10000` |
| `FEISHU_ENABLED` | 飞书通知总开关 | `false` |
| `FEISHU_WEBHOOK` | 飞书自定义机器人 Webhook 地址 | — |

## 运行测试

```bash
# 指定环境运行全部用例（框架会自动先完成全局登录）
pytest --env prod

# 运行指定的测试文件
pytest --env prod tests/test_file.py
```

## 查看报告

### Allure 报告与录像

报告路径集中管理在了 `reports/` 目录下，用例失败时，Trace 录像会自动挂载到报告中，直接在网页里查看：

```bash
allure serve reports/allure-results
```

**本地单独查看 Trace 文件**

除了通过 Allure 报告查看外，也可以直接在终端使用 Playwright CLI 本地打开 `reports/traces/` 目录下的 `.zip` 格式的 Trace 文件：

```bash
playwright show-trace reports/traces/your_trace_file.zip
```

### 飞书通知

当 `FEISHU_ENABLED=true` 且配置了 `FEISHU_WEBHOOK` 时，每次测试 session 结束后会自动发送测试结果到飞书群：

```
自动化测试报告 ✅ 全部通过
─────────────────────────
环境: prod  |  耗时: 23.5s
通过: 10  |  失败: 0  |  总数: 10
时间: 2026-02-19 14:30:00
```

## 新增页面 & 用例指南

### 1. 添加一个新页面

在 `pages/` 下创建文件，继承 `BasePage`：

```python
from pwauto.core.base import BasePage

class YourPage(BasePage):
    BTN_SUBMIT = 'role=button[name="提交"]'

    def open(self):
        self.navigate("/your-path")

    def submit(self):
        self.click(self.BTN_SUBMIT, name="提交按钮")
```

### 2. 添加测试用例 (区分登录状态)

框架提供了两个非常方便的 Fixture 给不同场景使用。

**普通业务测试（使用 `page` 夹具）**
此时页面已经**自带全局登录态**，并且内置了失败录像录制器。

```python
class TestYourFeature:
    def test_something(self, page):
        your_page = YourPage(page)
        your_page.open() # 直接进入业务页，无需登录
        your_page.submit()
        assert ...
```

**鉴权/登录本身测试（使用 `guest_page` 夹具）**
此时页面是**未登录的干干净净的游客态**。

```python
class TestLoginFeature:
    def test_login_fail(self, guest_page):
        login_page = LoginPage(guest_page)
        login_page.open()
        login_page.do_login("wrong_user", "wrong_pwd")
        assert "错误" in login_page.get_error_tips()
```

### 3. 添加测试数据 (数据驱动)

如果需要数据驱动，在 `data/` 下创建 JSON 文件：

```json
[
    {
        "case_name": "正常登录-管理员",
        "username": "admin",
        "password": "admin123"
    }
]
```

在测试文件中加载数据，配合 `pytest.mark.parametrize` 使用：

```python
import pytest
from pwauto.utils.data_loader import load_json_data

USERS = load_json_data("users.json")

@pytest.mark.parametrize("user_info", USERS, ids=[u["case_name"] for u in USERS])
class TestAuth:
    def test_login_process(self, guest_page, user_info):
        login_page = LoginPage(guest_page)
        login_page.open()
        login_page.do_login(user_info["username"], user_info["password"])
        assert ...
```


## 技术栈

| 组件 | 用途 |
|------|------|
| [Playwright](https://playwright.dev/python/) | 浏览器自动化与 Trace 追踪 |
| [pytest](https://docs.pytest.org/) | 测试框架 |
| [Allure](https://allurereport.org/) | 测试报告 |
| [loguru](https://github.com/Delgan/loguru) | 日志 |
| [python-dotenv](https://github.com/theskumar/python-dotenv) | 环境配置 |