Playwright测试失败AI诊断工具

QA Autopilot是一款pytest插件,通过AI实时诊断Playwright测试失败原因,支持多LLM提供商(OpenAI、Anthropic、Ollama、DeepSeek),默认符合GDPR规范且开源。

  • by julienmerconsulting
  • Python
  • 本地部署
  • 1 星标
  • 更新于
在 GitHub 查看 julienmerconsulting/qa-autopilot

使用说明

🚀 QA自动驾驶仪——剧作家测试失败的人工智能诊断

pytest插件——Playwright测试失败的实时AI诊断

多LLM(OpenAI·Anthropic·Ollama·DeepSeek)·默认GDPR·开源


安装 • 快速开始 • 记分卡 • 运作原理 • 配置


🎯 的问题

剧作家测试不及格。错误消息显示:

TimeoutError: Page.click: Timeout 5000ms exceeded.
waiting for locator("a[href='/international/']")

选择器正确。元素存在。 那么,为什么它不起作用呢?

因为饼干横幅覆盖了一切。或者元素位于iframe内部。或者DOM是通过AJAX重新加载的。或者按钮是 disabled。或者你用过 click() 而不是 dblclick().

QA Autopilot告诉你真正的原因。


📊 记分卡

一套搜索结果 7个陷阱测试 专门设计用于欺骗诊断工具:

测试 陷阱 人工智能诊断 类别 信心
🫣 Cookie覆盖 横幅覆盖的元素 ✅ Cookie横幅阻止点击 element_obscured 🟢 95%
🖼️ 不可见的iframe iframe内的元素,在主框架中搜索 ✅ 缺少iframe上下文 iframe_context 🟢 95%
👻 陈旧的AJAX DOM重新加载前捕获的定位器 ✅ AJAX后的过时引用 stale_reference 🟢 95%
↪️ 无声重定向 URL重定向301/302 ✅ 测试通过(检测到陷阱) -- ✅
🚫 禁用按钮 元素可见但已禁用 ✅ 检测到禁用属性 element_disabled 🟢 95%
🔤 Unicode正则表达式 Zinedine 对比 Zinédine ✅ 正则表达式重音不匹配 encoding_mismatch 🟢 95%
🫣 双击 同意管理器拦截点击 ✅ 检测到重叠 element_obscured 🟢 95%

在95%的置信度下,有6/6个正确诊断 --第7次测试通过(无需诊断)。


⚠️ 局限性

[!小心] 测试200多条线路: 发送到AI的上下文被有意截断。 E2E测试应该保持简短——一个场景,一个责任,不超过50行。 除此之外,这是一个设计问题,而不是诊断问题。 在查找失败原因之前,请重新构建测试。

📦 安装

QA Autopilot是一个原生Python模块,可以直接在PyPI上使用:

pip install qa-autopilot

就是这样。没有配置,没有服务器,没有帐户。一条线。

自动 .env 加载:

pip install qa-autopilot[dotenv]

或来源:

git clone https://github.com/julienmerconsulting/qa-autopilot.git
cd qa-autopilot
pip install -e .

先决条件

playwright install chromium

.env 配置

创建 .env 项目根目录下的文件:

# OpenAI (default)
OPENAI_API_KEY=sk-...

# Or DeepSeek
BASE_URL=https://api.deepseek.com
API_KEY=sk-...
QA_MODEL=deepseek-chat

# Or local Ollama (zero cost)
BASE_URL=http://localhost:11434/v1
API_KEY=ollama
QA_MODEL=llama3

⚡ 快速开始

pytest模式(推荐)

在pytest命令中添加一个标志:

pytest tests/ --qa-autopilot -v

就是这样。每次失败的测试都会得到自动的人工智能诊断。

使用HTML报告

pytest tests/ --qa-autopilot --html=qa-reports/report.html --self-contained-html -v

单机模式

python -m qa_autopilot tests/test_checkout.py
python -m qa_autopilot tests/test_login.py::test_auth
python -m qa_autopilot tests/ -k "checkout" --headed

直接导入模式

from qa_autopilot import QAInterceptor

# Inside your test
interceptor = QAInterceptor(page)
interceptor.start()

# ... your test ...

# On failure
diagnosis = interceptor.diagnose(error_message, "test_file.py")
print(diagnosis["root_cause"])
print(diagnosis["category"])

🔍 运作原理

┌─────────────────────────────────────────────────────┐
│                YOUR PLAYWRIGHT TEST                  │
│                                                      │
│   page.goto("https://example.com")                  │
│   page.click("#submit")           ← FAIL            │
│   expect(page).to_have_url(...)                     │
└──────────────────────┬──────────────────────────────┘
                       │
         ┌─────────────▼─────────────┐
         │    QA AUTOPILOT HOOK      │
         │   (listens in parallel)   │
         └─────────────┬─────────────┘
                       │
    ┌──────────────────┼──────────────────┐
    ▼                  ▼                  ▼
┌────────┐      ┌──────────┐      ┌───────────┐
│  DOM   │      │ NETWORK  │      │ CONSOLE   │
│Listener│      │ Capture  │      │ Capture   │
│  (JS)  │      │ req/res  │      │ err/warn  │
└───┬────┘      └────┬─────┘      └─────┬─────┘
    │                │                   │
    └────────────────┼───────────────────┘
                     │
         ┌───────────▼───────────┐
         │   CONTEXT BUNDLE      │
         │  code + error + DOM   │
         │  + network + console  │
         │  + screenshot (opt)   │
         └───────────┬───────────┘
                     │
         ┌───────────▼───────────┐
         │    ONE PROMPT → AI    │
         │   (12 categories)     │
         │   diagnosis + fix     │
         └───────────┬───────────┘
                     │
    ┌────────────────┼────────────────┐
    ▼                ▼                ▼
┌────────┐    ┌───────────┐    ┌──────────┐
│Terminal│    │   JSON    │    │  Jira    │
│ Output │    │  Report   │    │ (if bug) │
└────────┘    └───────────┘    └──────────┘

5级管道

  1. 透明挂钩 --通过本地事件插入剧作家页面
  2. 并行捕获 --DOM(注入JS监听器)、网络、控制台、截图
  3. 故障检测 --pytest钩子拦截 FAILED 状态
  4. 捆绑+提示 --所有上下文都在一次人工智能通话中消失
  5. 诊断 --根本原因+类别+具体修复+JSON报告

🏷️ 12个诊断类别

图标 类别 描述
🎯 wrong_selector 选择器损坏、缺失或太宽
⏭️ missing_step 缺少步骤(Cookie、goto、下拉菜单)
⏱️ timing 比赛状态,元素尚未准备就绪
🫣 element_obscured 覆盖层/模态/横幅覆盖的元素
🚫 element_disabled 元素已找到但已禁用
🔀 wrong_action 错误的方法(点击vs dblclick,填充vs类型)
🖼️ iframe_context 在错误的帧中搜索元素
🔤 encoding_mismatch Unicode/重音/正则表达式问题
👻 stale_reference DOM更改后定位器失效
📊 test_data 预期值错误的断言
🐛 app_bug 应用程序错误(不是测试)→ 生成Jira票证
🌐 network 失败的网络请求(4xx/5xx)

⚙️ 配置

环境变量

变量 默认值 描述
OPENAI_API_KEY (如果没有API_KEY,则为必填项) OpenAI API密钥
API_KEY (可选) 替代提供商的密钥(DeepSeek、Ollama…)
BASE_URL None (原生OpenAI) LLM提供者基本URL
QA_MODEL gpt-4.1-mini 要使用的AI模型
QA_SCREENSHOT 0 1 在提示中包含屏幕截图
QA_REPORT_DIR qa-reports/ 报告目录
QA_REDACT_INPUTS 1 自动编辑敏感字段(密码、信用卡、令牌、IBAN…)。 0 禁用(不推荐)。

pytest参数

pytest tests/ --qa-autopilot          # Enable AI diagnosis
pytest tests/ --qa-autopilot --headed # With visible browser
pytest tests/ --qa-autopilot -k "login" # Filter by keyword

📁 间引结构

qa-reports/
├── summary_20260223_014751.json          # Consolidated run report
├── diag_test_broken_20260223_014713.json # Individual diagnosis
├── diag_test_broken_20260223_014659.json
├── jira_test_broken_20260223_014659.md   # Jira ticket (if app_bug)
└── report.html                           # pytest HTML report

合并报表示例

[
  {
    "test": "test_element_covered_by_overlay[chromium]",
    "category": "element_obscured",
    "confidence": 0.95,
    "root_cause": "The target element is covered by the cookie banner",
    "suggested_fix": "Close the cookie banner before clicking"
  }
]

🔒 安全与GDPR

QA自动驾驶仪 自动地 在任何LLM调用之前编辑敏感数据。这种保护是 默认启用 (QA_REDACT_INPUTS=1)并在 两条战线:

1.浏览器端编校(DOM监听器)

在敏感字段中键入的值被拦截 浏览器内部,在 saveEntry() JS监听器的功能,并替换为 [REDACTED] 之前 任何存储。真正的价值永远不会离开浏览器。

检测标准 示例
HTML type password, email, tel
name/id 包含 password, passwd, pwd, secret, token, cvv, card, ssn, auth, pin, api_key, credit, iban, bic, swift, client_secret
placeholder / aria-label 包含 相同的模式
autocomplete current-password, new-password, cc-* (信用卡)

2.源代码编辑(LLM调用前)

测试 .py 文件也会被扫描,硬编码的凭据也会被编辑:

  • page.fill("#password", "...") / .type() / .press_sequentially() / .input_value() 关于敏感选择器
  • Python变量: password = "...", token = "...", api_key = "...", client_secret = "...", access_token = "..."等等。
  • os.environ["PASSWORD"] = "..." (直接分配)

当应用编辑时,qa autopilot会打印一条警告:

⚠️  Hardcoded credentials detected in test_login.py, redacted before LLM call.
    Best practice: use os.environ or pytest fixtures for secrets.

LLM看到了什么?

CAPTURED DOM ACTIONS (3 total)
  1. INPUT ✅ #username = 'john.doe@example.com'
  2. INPUT ✅ #password = [REDACTED — sensitive field]
  3. CLICK ✅ button[type="submit"] (text: 'Login')

TEST CODE
def test_login(page):
    page.fill("#username", "john.doe@example.com")
    page.fill("#password", "[REDACTED]")
    page.click("button[type='submit']")

LLM在提示中明确获悉 [REDACTED] 做 不 表示空白或损坏的字段,但受GDPR保护。在不知道实际值的情况下进行诊断。

如何验证编辑是否有效?

# Run a test that types a password
pytest tests/test_login.py --qa-autopilot

# Check the JSON report — you should see [REDACTED] everywhere
grep -i "password\|REDACTED" qa-reports/diag_*.json

对于超级偏执狂:用拦截传出流量 mitmproxy 并验证请求 api.openai.com 永远不要包含你真正的敏感价值。

发送到LLM的数据

数据 已发送 可修改
测试源代码(最多3000个字符) ✅ ✅ 默认情况下 (fill/assign/env上的正则表达式)
剧作家错误消息 ✅ ❌
元素选择器 ✅ ❌
输入值(DOM监听器) ✅ ✅ 默认情况下 (6个标准级联)
页面URL ✅ ❌
控制台错误 ✅ ❌
4xx/5xx请求体(截断) ✅ ❌
截图 仅当 QA_SCREENSHOT=1 无

禁用编辑(不建议)

对于极少数情况,“敏感”字段的内容是合法有用的(对包含 auth 但实际上与认证无关):

QA_REDACT_INPUTS=0 pytest tests/ --qa-autopilot

⚠️ 仅适用于虚构的测试数据。 如有疑问,请保持启用编辑。Redaction不是硬编码凭据的借口:它不能捕捉到每一个奇异的情况(具有发明名称、连接值等的变量)。黄金法则依然存在: 永远不要硬编码秘密.

已知限制——记录的剩余风险

浏览器端侦听器捕获 在实字段中键入的运行时值 无论测试如何处理它们(类型/名称/占位符/aria标签/自动补全)。源端正则表达式捕获了最常见的模式(page.fill("#password", ...), PASSWORD = "...", get_by_label("Password").fill(...), get_by_placeholder("IBAN").fill(...)).然而,有两种特定的模式 测试源中的硬编码凭据 目前尚未被捕获:

模式 示例 状态
来自剧作家codegen的嵌套引用 page.fill("[name='cvv']", "123") 跟踪为 xfail 在 tests/test_redaction.py
页面对象模型——声明为类属性的定位器, .fill() 呼叫其他地方 PASSWORD_INPUT = "#password" ... page.fill(PASSWORD_INPUT, secret) 跟踪为 xfail 在 tests/test_redaction.py

在这两种情况下 在浏览器中键入的运行时值仍然受到保护 通过浏览器监听器。泄漏仅影响 源代码中硬编码的值 (无论如何,这都是不好的做法——使用 os.environ 或pytest夹具)。这 xfail 测试使剩余风险 可见和跟踪 因此,任何未来的修复都将得到验证。


🏗️ 建筑

qa-autopilot/
├── qa_autopilot/
│   ├── __init__.py          # Public exports
│   ├── core.py              # QAInterceptor + capture
│   ├── prompt.py            # Prompt v2 (12 categories)
│   ├── diagnose.py          # AI call + retry + JSON mode
│   ├── reporter.py          # JSON + Jira markdown reports
│   ├── listener.js          # DOM listener (browser injection)
│   └── plugin.py            # pytest hooks
├── tests/
│   ├── test_traps.py        # Trap test suite
│   └── conftest.py
├── examples/
│   └── standalone.py        # Direct usage example
├── pyproject.toml
├── LICENSE
└── README.md

注: 当前版本是一个整体 qa_autopilot.py 文件(约1250行)。 上面的结构是v2的目标。


🆚 为什么没有其他选择?

QA自动驾驶仪 剧作家MCP(23K行) SaaS(Testim、Mabl…)
代码行数 ~1250 23000+ 已关闭
安装 pip install MCP服务器+配置 帐户+许可证
配置 1个标志 32个MCP工具 仪表板+集成
价格 免费+OpenAI密钥 免费 200-500美元/月/用户
诊断 12个类别,95% 基本 可变
厂商锁定 无 MCP协议 总计

🛠️ DOM监听器——6层级联

注入浏览器的JavaScript监听器使用6级选择器级联,从最稳定到最不稳定:

层级 策略 示例
1 data-testid / id / name [data-testid="submit-btn"]
2 aria-label / placeholder / title [aria-label="Close"]
3 href (链接) a[href="/checkout"]
4 具有稳定属性的父对象 [data-testid="form"] button
5 相关标签(输入) //label[contains(text(),"Email")]//input
6 短CSS+ nth-of-type button.primary:nth-of-type(2)

每个选择器在DOM中都经过唯一性验证。包括Shadow DOM支持。


🤝 贡献者

贡献者 贡献
朱利安·梅尔 原作者
@ 施瓦巴 多提供商法学硕士支持(DeepSeek、Ollama)+中文翻译

欢迎投稿——问题、错误报告、拉取请求。


标签: pytest插件·剧作家·剧作家python·ai测试·llm·openai·anthropic·deepseek·ollama·mistral·groq·自愈测试·根本原因分析·测试调试·qa自动化·gdpr·rgpd·数据保护·多llm·开源qa


📄 许可证

麻省理工学院——随心所欲。


由...创建 朱利安·梅尔 --JMer咨询

QA架构师·20多年经验·Katalon欧洲顶级合作伙伴