feat: 浏览器一期:坐标点击 + live 截图
- browser_click_at(x, y):视口坐标点击(1280x800、左上角 原点),越界与非数字拒绝;live 视图里用户在截图上的 点击落点就走这个工具。 - browser_live_shot:固定文件 browser-live.png + 状态 sidecar browser-live.json(URL/标题/时间戳),watcher 轮询一个稳定路径即可;技能要求每次动作后都调一次, 保持画面最新。 - MCP 新增 browser_click_at / browser_live_shot 两个工具; 技能补 live 视图一节;测试 9 项(含 fake mouse、越界、 live 文件与状态读回)。
This commit is contained in:
1 parent
cb6de0e3aa
commit
814fe8f7c2
4 files changed
+151
-1
No files matched your search
@@ -26,11 +26,20 @@ class _FakeLocator:
|
||||
return self._text
|
||||
|
||||
|
||||
class _FakeMouse:
|
||||
def __init__(self, page):
|
||||
self._page = page
|
||||
|
||||
def click(self, x, y):
|
||||
self._page.calls.append(("mouse_click", x, y))
|
||||
|
||||
|
||||
class _FakePage:
|
||||
def __init__(self):
|
||||
self.url = "https://example.com/"
|
||||
self.calls = []
|
||||
self.shot_path = None
|
||||
self.mouse = _FakeMouse(self)
|
||||
|
||||
def title(self):
|
||||
return "Example Domain"
|
||||
@@ -146,11 +155,43 @@ class BrowserTest(unittest.TestCase):
|
||||
self.assertTrue(self.browser.close()["closed"])
|
||||
self.assertTrue(self.browser.close()["closed"])
|
||||
|
||||
def test_click_at_and_bounds(self):
|
||||
self.browser.navigate("https://example.com")
|
||||
out = self.browser.click_at(100, 200)
|
||||
self.assertTrue(out["ok"])
|
||||
self.assertEqual(out["x"], 100)
|
||||
self.assertIn(("mouse_click", 100, 200), self.page.calls)
|
||||
with self.assertRaises(BrowserError):
|
||||
self.browser.click_at(2000, 10)
|
||||
with self.assertRaises(BrowserError):
|
||||
self.browser.click_at("left", 10)
|
||||
|
||||
def test_live_shot_writes_fixed_file_and_status(self):
|
||||
self.browser.navigate("https://example.com")
|
||||
out = self.browser.live_shot()
|
||||
self.assertEqual(out["image"], "browser-live.png")
|
||||
self.assertTrue(out["bytes"] > 0)
|
||||
self.assertTrue(os.path.exists(out["path"]))
|
||||
with open(os.path.join(self._tmp.name,
|
||||
"browser-live.json")) as fh:
|
||||
status = json.load(fh)
|
||||
self.assertEqual(status["image"], "browser-live.png")
|
||||
self.assertEqual(status["url"], "https://example.com")
|
||||
|
||||
|
||||
class FakeMCPBrowser:
|
||||
def navigate(self, url):
|
||||
return {"url": url, "title": "T", "ok": True}
|
||||
|
||||
def click_at(self, x, y):
|
||||
return {"url": "https://x/", "title": "T", "ok": True,
|
||||
"x": x, "y": y}
|
||||
|
||||
def live_shot(self, name=None):
|
||||
return {"image": name or "browser-live.png",
|
||||
"path": "/w/browser-live.png", "bytes": 456,
|
||||
"url": "https://x/", "title": "T", "timestamp": 1}
|
||||
|
||||
def snapshot(self):
|
||||
return {"url": "https://x/", "title": "T", "text": "hi",
|
||||
"truncated": False}
|
||||
@@ -168,7 +209,8 @@ class MCPTest(unittest.TestCase):
|
||||
for name in ("browser_navigate", "browser_snapshot",
|
||||
"browser_click", "browser_fill",
|
||||
"browser_evaluate", "browser_screenshot",
|
||||
"browser_close"):
|
||||
"browser_close", "browser_click_at",
|
||||
"browser_live_shot"):
|
||||
self.assertIn(name, names)
|
||||
|
||||
def test_tools_call_dispatches(self):
|
||||
|
||||
Reference in new issue
Block a user