feat(tabs): reuse an open tab instead of duplicating a page
- Add browser_cli/url_match.py with a canonical URL key for tab matching - Canonicalize scheme, host, default ports, www., trailing slash, query order - Support reuse modes none, exact, similar and origin via normalize_mode() - Pick the closest match when several tabs qualify, preferring identical URLs - Skip the reload when the reused tab already shows the requested page - Expose nav.reusable_tab() so callers can look up a tab without side effects - Accept reuse/reuse_domain/reuse_title in tabs.open(), not just nav.open() - Add --reuse-exact to nav open and nav open-wait; --reuse is now fuzzy - Default MCP tabs_open to reuse="similar" and return a reused flag - Tell MCP agents in the server instructions to reuse before opening - Document the reuse modes in README for MCP, CLI and Python API - Cover url_match, SDK reuse paths and MCP reuse behaviour with new tests - Bump package and extension version to 0.16.9 - Refresh uv.lock for the updated dependency set
This commit is contained in:
@@ -34,21 +34,60 @@ def test_session_export_cli_prints_json():
|
||||
assert result.exit_code == 0
|
||||
assert '"name": "work"' in result.output
|
||||
|
||||
def test_nav_open_reuse_navigates_existing_tab_instead_of_opening_new():
|
||||
calls = []
|
||||
def _reuse_sender(calls, tab_url="https://example.com"):
|
||||
tab = {"id": 7, "windowId": 1, "active": False, "muted": False, "title": "Example", "url": tab_url}
|
||||
|
||||
def sender(command, args=None, **kwargs):
|
||||
calls.append((command, args))
|
||||
if command == "tabs.list":
|
||||
return [{"id": 7, "windowId": 1, "active": False, "muted": False, "title": "Example", "url": "https://example.com"}]
|
||||
return [tab]
|
||||
if command in {"tabs.status", "navigate.open"}:
|
||||
return dict(tab)
|
||||
return {}
|
||||
return sender
|
||||
|
||||
BrowserCLI(browser="testing", _command_sender=sender).nav.open("https://example.com", reuse=True)
|
||||
def test_nav_open_reuse_navigates_existing_tab_instead_of_opening_new():
|
||||
calls = []
|
||||
|
||||
BrowserCLI(browser="testing", _command_sender=_reuse_sender(calls, "https://example.com/app?a=1")).nav.open(
|
||||
"https://example.com/app?a=2", reuse=True
|
||||
)
|
||||
assert calls == [
|
||||
("tabs.list", {}),
|
||||
("navigate.to", {"tabId": 7, "url": "https://example.com"}),
|
||||
("navigate.to", {"tabId": 7, "url": "https://example.com/app?a=2"}),
|
||||
]
|
||||
|
||||
def test_nav_open_reuse_matches_nearly_identical_urls():
|
||||
calls = []
|
||||
|
||||
BrowserCLI(browser="testing", _command_sender=_reuse_sender(calls, "https://example.com/app?a=1&b=2")).nav.open(
|
||||
"https://www.example.com/app/?b=2&a=1#top", reuse=True
|
||||
)
|
||||
assert calls == [("tabs.list", {})] # same page already open → no reload, no new tab
|
||||
|
||||
def test_nav_open_reuse_exact_opens_a_new_tab_for_a_different_url():
|
||||
calls = []
|
||||
|
||||
BrowserCLI(browser="testing", _command_sender=_reuse_sender(calls)).nav.open(
|
||||
"https://example.com/", reuse="exact"
|
||||
)
|
||||
assert [command for command, _ in calls] == ["tabs.list", "navigate.open"]
|
||||
|
||||
def test_tabs_open_reuse_returns_the_existing_tab():
|
||||
calls = []
|
||||
|
||||
tab = BrowserCLI(browser="testing", _command_sender=_reuse_sender(calls)).tabs.open(
|
||||
"https://example.com/deep/page", reuse="origin"
|
||||
)
|
||||
assert tab.id == 7
|
||||
assert [command for command, _ in calls] == ["tabs.list", "navigate.to", "tabs.status"]
|
||||
|
||||
def test_tabs_open_without_reuse_always_creates_a_tab():
|
||||
calls = []
|
||||
|
||||
BrowserCLI(browser="testing", _command_sender=_reuse_sender(calls)).tabs.open("https://example.com")
|
||||
assert [command for command, _ in calls] == ["navigate.open"]
|
||||
|
||||
def _tree_sender(tabs, groups):
|
||||
def sender(command, args=None, **kwargs):
|
||||
if command == "tabs.list":
|
||||
|
||||
Reference in New Issue
Block a user