Files
auto_agent/tests/web_page/smoke_test.py
T

135 lines
5.1 KiB
Python

"""web_page 工具冒烟测试(fixture 本地页面,覆盖本次改动核心能力)。
运行: python Agent/tests/web_page/smoke_test.py
依赖: 无浏览器时自动分离启动(browser_control 逻辑)
"""
import http.server
import json
import sys
import threading
from pathlib import Path
HERE = Path(__file__).resolve().parent
sys.path.insert(0, str(HERE.parents[2] / "Agent" / "src"))
from agent.tools.web_page import WebPage as WebPageTool # noqa: E402
PORT = 8766
PASS = 0
FAIL = 0
def check(name: str, cond: bool, detail: str = "") -> None:
global PASS, FAIL
if cond:
PASS += 1
print(f"[PASS] {name}")
else:
FAIL += 1
print(f"[FAIL] {name} {detail}")
class NoCacheHandler(http.server.SimpleHTTPRequestHandler):
def end_headers(self):
self.send_header("Cache-Control", "no-store")
super().end_headers()
def log_message(self, *a):
pass
def serve() -> None:
handler = lambda *a, **kw: NoCacheHandler(*a, directory=str(HERE / "fixture"), **kw)
http.server.ThreadingHTTPServer(("127.0.0.1", PORT), handler).serve_forever()
def main() -> None:
threading.Thread(target=serve, daemon=True).start()
tool = WebPageTool()
def run(actions: list[dict]) -> str:
return tool._run(actions)[0]
# 1. 打开 fixture 页面
msg = run([{"action": "goto", "url": f"http://127.0.0.1:{PORT}/index.html"}])
check("goto fixture", "已打开" in msg, msg)
# 2. 登录区元素
msg = run([{"action": "snapshot"}])
check("快照含账户名", '"账户名"' in msg, msg[:200])
check("快照含密码", '"密码"' in msg)
check("快照含登录按钮", 'button "登录"' in msg)
# 3. 打开弹窗 → 层级断言
msg = run([
{"action": "click", "selector": "text=\"打开弹窗\""},
{"action": "wait", "wait_seconds": 0.3},
{"action": "snapshot"},
])
check("弹窗标注 [弹窗]", "[弹窗]" in msg, msg[:300])
check("弹窗含 Close", 'button "Close"' in msg)
check("弹窗含确认按钮", 'button "确认"' in msg)
# 4. 展开下拉 → 选项进快照 + 纯文本标题标注
msg = run([
{"action": "click", "selector": "text=\"请选择产品\"", "note": "展开下拉"},
{"action": "wait", "wait_seconds": 0.3},
{"action": "snapshot"},
])
check("纯文本容器标 [文本]", "[文本]" in msg, msg[:300])
check("下拉选项进快照", "意外险" in msg and "060003旅游意外伤害保险" in msg, msg[:400])
# 5. text= 精确匹配点击下拉选项
msg = run([{"action": "click", "selector": "text=\"意外险\"", "note": "选项"}])
check("text= 点选项成功", "已点击" in msg, msg)
msg = run([{"action": "eval_js", "js": "document.querySelector('.ant-select-selection').textContent"}])
check("下拉选中意外险", "意外险" in msg, msg)
# 6. date 动作:2005-07-09(年份面板需翻两个十年;先关闭弹窗避免遮挡)
msg = run([
{"action": "click", "selector": "text=\"Close\"", "note": "关弹窗"},
{"action": "wait", "wait_seconds": 0.3},
{"action": "date", "selector": "input.ant-calendar-picker-input", "value": "2005-07-09"},
])
check("date 动作成功", "已选择日期: 2005-07-09" in msg, msg)
msg = run([{"action": "eval_js", "js": "document.querySelector('.ant-calendar-picker-input').value"}])
check("date 写入输入框", "2005-07-09" in msg, msg)
# 7. shadow DOM 穿透
msg = run([{"action": "snapshot", "max_elements": 100}])
check("shadow DOM 按钮可见", "影子按钮" in msg, msg[:300])
# 8. text= 精确匹配失败 → 自动子串匹配报告真实候选
# (打开弹窗使下拉选项可见:text=旅游 精确匹配不到,子串候选含 060003旅游意外伤害保险)
msg = run([
{"action": "click", "selector": "text=\"打开弹窗\""},
{"action": "wait", "wait_seconds": 0.3},
{"action": "click", "selector": "text=旅游"},
])
check("text= 失败给出候选报告", "候选" in msg and "060003旅游" in msg, msg[:300])
# 9. 树节点:switcher 标 [展开],title 正常显示且可点击选中
msg = run([{"action": "snapshot", "max_elements": 200}])
check("树 switcher 标 [展开]", "[展开]" in msg, msg[:400])
check("树节点标题可见", "北京分公司车险管理部" in msg, msg[:400])
msg = run([
{"action": "click", "selector": "text=\"Close\"", "note": "关弹窗"},
{"action": "wait", "wait_seconds": 0.3},
{"action": "click", "selector": "text=\"北京分公司车险管理部\"", "note": "树节点标题"},
])
check("树节点标题可点击", "已点击" in msg, msg)
msg = run([{"action": "eval_js", "js": "document.getElementById('org-selected').textContent"}])
check("树节点点击选中生效", "已选: 北京分公司车险管理部" in msg, msg)
print()
if FAIL:
print(f"✗ {FAIL} 项失败 / {PASS} 项通过")
sys.exit(1)
print(f"全部通过 ✓({PASS} 项断言)")
if __name__ == "__main__":
main()