fix(chv): 快照改走 page.aria_snapshot()
Playwright 1.64.1 已移除 page.accessibility,真实快照一路报 AttributeError(P2 假实现用的旧 API,单测一直没抓到;2026-11-08 构建 #8 驱动 live 抓到)。新版 aria_snapshot() 返回字符串, 超 64KB 截断;旧版 accessibility.snapshot()(dict)只作回落。 驱动的 a11y 断言接受 str 或 dict。
This commit is contained in:
1 parent
7361b0654f
commit
0bfb00ab1b
3 files changed
+72
-6
No files matched your search
@@ -24,9 +24,11 @@
|
|||||||
返回 changed=false(整帧相同跳过;变化区裁剪是阶段 4 的事,
|
返回 changed=false(整帧相同跳过;变化区裁剪是阶段 4 的事,
|
||||||
这里不冒充已做)。
|
这里不冒充已做)。
|
||||||
|
|
||||||
对齐 Muse P2 的不变口径:子代理只向单元返回页面无障碍树快照
|
对齐 Muse P2 的不变口径:子代理只向单元返回页面无障碍快照,
|
||||||
(Playwright accessibility.snapshot),不见原始 DOM,不能在页面
|
不见原始 DOM,不能在页面上下文跑 JS,不暴露 evaluate/content,
|
||||||
上下文跑 JS,不暴露 evaluate/content,不开 DevTools。
|
不开 DevTools。快照 API:当前 Playwright(1.64.1)已移除
|
||||||
|
page.accessibility,改用 page.aria_snapshot()(返回 YAML 式
|
||||||
|
字符串);旧版 accessibility.snapshot()(返回 dict)只作回落。
|
||||||
|
|
||||||
操作(Unix socket /run/chv/browser.sock,JSON 行):
|
操作(Unix socket /run/chv/browser.sock,JSON 行):
|
||||||
open {url, session?} -> 导航并返回 {title, url, a11y, truncated}
|
open {url, session?} -> 导航并返回 {title, url, a11y, truncated}
|
||||||
@@ -342,10 +344,25 @@ class BrowserAgent:
|
|||||||
try:
|
try:
|
||||||
title = page.title()
|
title = page.title()
|
||||||
url = page.url
|
url = page.url
|
||||||
tree = page.accessibility.snapshot()
|
# 当前 Playwright(1.64.1):page.accessibility 已移除,
|
||||||
|
# 用 page.aria_snapshot()(字符串);旧版 dict 树只作回落。
|
||||||
|
if hasattr(page, "aria_snapshot"):
|
||||||
|
snap = page.aria_snapshot()
|
||||||
|
if isinstance(snap, dict):
|
||||||
|
tree, truncated = truncate_a11y(snap)
|
||||||
|
else:
|
||||||
|
text = snap or ""
|
||||||
|
truncated = len(text) > MAX_A11Y_CHARS
|
||||||
|
tree = text[:MAX_A11Y_CHARS] if truncated else text
|
||||||
|
elif hasattr(page, "accessibility"):
|
||||||
|
tree = page.accessibility.snapshot()
|
||||||
|
tree, truncated = truncate_a11y(tree or {})
|
||||||
|
else:
|
||||||
|
return {"ok": False,
|
||||||
|
"error": "浏览器不支持无障碍快照(无 "
|
||||||
|
"aria_snapshot/accessibility)"}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return {"ok": False, "error": "取快照失败:%s" % e}
|
return {"ok": False, "error": "取快照失败:%s" % e}
|
||||||
tree, truncated = truncate_a11y(tree or {})
|
|
||||||
return {"ok": True, "title": title, "url": url,
|
return {"ok": True, "title": title, "url": url,
|
||||||
"a11y": tree, "truncated": truncated,
|
"a11y": tree, "truncated": truncated,
|
||||||
"session": session}
|
"session": session}
|
||||||
|
|||||||
@@ -554,7 +554,8 @@ class BrowserTest(unittest.TestCase):
|
|||||||
self.assertTrue(resp["ok"])
|
self.assertTrue(resp["ok"])
|
||||||
self.assertEqual(resp["title"], "Example Domain")
|
self.assertEqual(resp["title"], "Example Domain")
|
||||||
self.assertEqual(resp["url"], "https://example.com/")
|
self.assertEqual(resp["url"], "https://example.com/")
|
||||||
self.assertEqual(resp["a11y"]["role"], "WebArea")
|
self.assertIsInstance(resp["a11y"], str)
|
||||||
|
self.assertIn("Example Domain", resp["a11y"])
|
||||||
snap = agent.snapshot()
|
snap = agent.snapshot()
|
||||||
self.assertTrue(snap["ok"])
|
self.assertTrue(snap["ok"])
|
||||||
shot = agent.screenshot()
|
shot = agent.screenshot()
|
||||||
@@ -590,6 +591,11 @@ class FakePage:
|
|||||||
return {"role": "WebArea", "name": "Example Domain",
|
return {"role": "WebArea", "name": "Example Domain",
|
||||||
"children": [{"role": "heading", "name": "Example"}]}
|
"children": [{"role": "heading", "name": "Example"}]}
|
||||||
|
|
||||||
|
def aria_snapshot(self):
|
||||||
|
# 当前 Playwright(1.64.1):快照是字符串,不是 dict 树。
|
||||||
|
return ("- heading \"Example Domain\" [level=1]\n"
|
||||||
|
"- paragraph \"Example\"")
|
||||||
|
|
||||||
def screenshot(self, path=None, type=None, quality=None):
|
def screenshot(self, path=None, type=None, quality=None):
|
||||||
data = b"\xff\xd8\xff\xe0FAKEJPEG"
|
data = b"\xff\xd8\xff\xe0FAKEJPEG"
|
||||||
if path:
|
if path:
|
||||||
|
|||||||
@@ -55,6 +55,9 @@ class FakePage:
|
|||||||
return {"role": "WebArea", "name": self._title,
|
return {"role": "WebArea", "name": self._title,
|
||||||
"children": [{"role": "heading", "name": self._title}]}
|
"children": [{"role": "heading", "name": self._title}]}
|
||||||
|
|
||||||
|
def aria_snapshot(self):
|
||||||
|
return "- heading \"%s\" [level=1]" % self._title
|
||||||
|
|
||||||
def screenshot(self, path=None, type=None, quality=None):
|
def screenshot(self, path=None, type=None, quality=None):
|
||||||
assert type == "jpeg", type
|
assert type == "jpeg", type
|
||||||
assert quality == 70, quality
|
assert quality == 70, quality
|
||||||
@@ -201,6 +204,46 @@ class MultiSessionTest(unittest.TestCase):
|
|||||||
{"A", "B"})
|
{"A", "B"})
|
||||||
|
|
||||||
|
|
||||||
|
class SnapshotApiTest(unittest.TestCase):
|
||||||
|
def test_snapshot_returns_aria_string(self):
|
||||||
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
|
agent, _c, _h = _agent([FakePage("Example Domain")], tmp)
|
||||||
|
agent.open("https://example.com")
|
||||||
|
snap = agent.snapshot()
|
||||||
|
self.assertTrue(snap["ok"])
|
||||||
|
self.assertIsInstance(snap["a11y"], str)
|
||||||
|
self.assertIn("Example Domain", snap["a11y"])
|
||||||
|
self.assertFalse(snap["truncated"])
|
||||||
|
|
||||||
|
def test_snapshot_truncates_long_aria_string(self):
|
||||||
|
class LongPage(FakePage):
|
||||||
|
def aria_snapshot(self):
|
||||||
|
return "x" * (BROWSER.MAX_A11Y_CHARS + 100)
|
||||||
|
|
||||||
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
|
agent, _c, _h = _agent([LongPage()], tmp)
|
||||||
|
agent.open("https://example.com")
|
||||||
|
snap = agent.snapshot()
|
||||||
|
self.assertTrue(snap["truncated"])
|
||||||
|
self.assertLessEqual(len(snap["a11y"]),
|
||||||
|
BROWSER.MAX_A11Y_CHARS)
|
||||||
|
|
||||||
|
def test_snapshot_falls_back_to_legacy_accessibility(self):
|
||||||
|
# 旧版页只有 accessibility.snapshot()(dict 树)时回落。
|
||||||
|
class LegacyPage(FakePage):
|
||||||
|
@property
|
||||||
|
def aria_snapshot(self):
|
||||||
|
raise AttributeError("no aria_snapshot")
|
||||||
|
|
||||||
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
|
agent, _c, _h = _agent([LegacyPage("Legacy")], tmp)
|
||||||
|
agent.open("https://example.com")
|
||||||
|
snap = agent.snapshot()
|
||||||
|
self.assertTrue(snap["ok"])
|
||||||
|
self.assertIsInstance(snap["a11y"], dict)
|
||||||
|
self.assertEqual(snap["a11y"]["role"], "WebArea")
|
||||||
|
|
||||||
|
|
||||||
class ScreenshotTest(unittest.TestCase):
|
class ScreenshotTest(unittest.TestCase):
|
||||||
def test_jpeg_q70_width_1280(self):
|
def test_jpeg_q70_width_1280(self):
|
||||||
with tempfile.TemporaryDirectory() as tmp:
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
|
|||||||
Reference in new issue
Block a user