fix: make navigation no-focus by default
- Change nav open and open-wait to avoid activating newly created tabs unless --focus is explicitly requested. - Send background=true for default opens so older or remote extensions also avoid stealing focus even if they ignore the new focus flag. - Remove the redundant --bg flag from navigation and search CLI commands now that no-focus/background behavior is the default. - Thread focus support through the sync SDK, async SDK, tab helpers, and workflow decorators. - Update README and demo usage to document the new default and --focus opt-in. - Bump package and extension metadata to 0.12.3. - Add regression coverage for CLI help, wire payloads, and extension behavior.
This commit is contained in:
@@ -138,9 +138,9 @@ Important: profile aliases are browser-instance aliases, not window aliases. Win
|
|||||||
### Navigation (`nav`)
|
### Navigation (`nav`)
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
# Open a URL
|
# Open a URL (no focus stealing by default)
|
||||||
browser-cli nav open https://example.com
|
browser-cli nav open https://example.com
|
||||||
browser-cli nav open https://example.com --bg # background, no focus
|
browser-cli nav open https://example.com --focus # bring opened tab/window forward
|
||||||
browser-cli nav open https://example.com --window work # into a named window
|
browser-cli nav open https://example.com --window work # into a named window
|
||||||
browser-cli nav open https://example.com --group research # into a tab group (name or ID)
|
browser-cli nav open https://example.com --group research # into a tab group (name or ID)
|
||||||
|
|
||||||
@@ -163,7 +163,7 @@ Each search command opens the search results in your browser using the same flag
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
browser-cli search google openai api
|
browser-cli search google openai api
|
||||||
browser-cli search brave rust iterators --bg
|
browser-cli search brave rust iterators
|
||||||
browser-cli search ddg tab groups --window work
|
browser-cli search ddg tab groups --window work
|
||||||
browser-cli search youtube browser automation
|
browser-cli search youtube browser automation
|
||||||
browser-cli search yt lo fi
|
browser-cli search yt lo fi
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ class AsyncDecoratorsNS(WorkflowDecoratorsMixin):
|
|||||||
wait: bool = False,
|
wait: bool = False,
|
||||||
timeout: float = 30.0,
|
timeout: float = 30.0,
|
||||||
background: bool = False,
|
background: bool = False,
|
||||||
|
focus: bool = False,
|
||||||
window: str | None = None,
|
window: str | None = None,
|
||||||
group: str | None = None,
|
group: str | None = None,
|
||||||
close: bool = False,
|
close: bool = False,
|
||||||
@@ -95,6 +96,7 @@ class AsyncDecoratorsNS(WorkflowDecoratorsMixin):
|
|||||||
wait=wait,
|
wait=wait,
|
||||||
timeout=timeout,
|
timeout=timeout,
|
||||||
background=background,
|
background=background,
|
||||||
|
focus=focus,
|
||||||
window=window,
|
window=window,
|
||||||
group=group,
|
group=group,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -10,13 +10,13 @@ def nav_group():
|
|||||||
|
|
||||||
@nav_group.command("open")
|
@nav_group.command("open")
|
||||||
@click.argument("url")
|
@click.argument("url")
|
||||||
@click.option("--bg", is_flag=True, help="Open in background (no focus)")
|
@click.option("--focus", is_flag=True, help="Bring the opened tab/window to the front")
|
||||||
@click.option("--window", "window_name", default=None, help="Open in named window")
|
@click.option("--window", "window_name", default=None, help="Open in named window")
|
||||||
@click.option("--group", "group_name", default=None, help="Open directly into a tab group (name or ID)")
|
@click.option("--group", "group_name", default=None, help="Open directly into a tab group (name or ID)")
|
||||||
@handle_errors
|
@handle_errors
|
||||||
def cmd_open(url, bg, window_name, group_name):
|
def cmd_open(url, focus, window_name, group_name):
|
||||||
"""Open URL in a new tab."""
|
"""Open URL in a new tab without stealing focus by default."""
|
||||||
client_from_ctx().nav.open(url, background=bg, window=window_name, group=group_name)
|
client_from_ctx().nav.open(url, focus=focus, window=window_name, group=group_name)
|
||||||
suffix = ""
|
suffix = ""
|
||||||
if group_name:
|
if group_name:
|
||||||
suffix = f" in group '{group_name}'"
|
suffix = f" in group '{group_name}'"
|
||||||
@@ -70,13 +70,13 @@ def cmd_focus(pattern):
|
|||||||
@nav_group.command("open-wait")
|
@nav_group.command("open-wait")
|
||||||
@click.argument("url")
|
@click.argument("url")
|
||||||
@click.option("--timeout", type=float, default=30.0, show_default=True, help="Max seconds to wait for load")
|
@click.option("--timeout", type=float, default=30.0, show_default=True, help="Max seconds to wait for load")
|
||||||
@click.option("--bg", is_flag=True, help="Open in background (no focus)")
|
@click.option("--focus", is_flag=True, help="Bring the opened tab/window to the front")
|
||||||
@click.option("--window", "window_name", default=None, help="Open in named window")
|
@click.option("--window", "window_name", default=None, help="Open in named window")
|
||||||
@click.option("--group", "group_name", default=None, help="Open in tab group")
|
@click.option("--group", "group_name", default=None, help="Open in tab group")
|
||||||
@handle_errors
|
@handle_errors
|
||||||
def cmd_open_wait(url, timeout, bg, window_name, group_name):
|
def cmd_open_wait(url, timeout, focus, window_name, group_name):
|
||||||
"""Open URL in a new tab and wait until fully loaded."""
|
"""Open URL in a new tab and wait until fully loaded."""
|
||||||
tab = client_from_ctx().nav.open_wait(url, timeout=timeout, background=bg, window=window_name, group=group_name)
|
tab = client_from_ctx().nav.open_wait(url, timeout=timeout, focus=focus, window=window_name, group=group_name)
|
||||||
console.print(f"[green]Loaded:[/green] {url}" + (f" — {tab.title}" if tab.title else ""))
|
console.print(f"[green]Loaded:[/green] {url}" + (f" — {tab.title}" if tab.title else ""))
|
||||||
|
|
||||||
@nav_group.command("wait")
|
@nav_group.command("wait")
|
||||||
|
|||||||
@@ -63,13 +63,12 @@ def search_group():
|
|||||||
def _build_command(engine_key: str, help_text: str) -> click.Command:
|
def _build_command(engine_key: str, help_text: str) -> click.Command:
|
||||||
@click.command(engine_key, help=help_text)
|
@click.command(engine_key, help=help_text)
|
||||||
@click.argument("query", nargs=-1, required=True)
|
@click.argument("query", nargs=-1, required=True)
|
||||||
@click.option("--bg", is_flag=True, help="Open in background (no focus)")
|
|
||||||
@click.option("--window", "window", default=None, help="Open in named window")
|
@click.option("--window", "window", default=None, help="Open in named window")
|
||||||
@click.option("--group", "group", default=None, help="Open in tab group (name or ID)")
|
@click.option("--group", "group", default=None, help="Open in tab group (name or ID)")
|
||||||
@handle_errors
|
@handle_errors
|
||||||
def _cmd(query, bg, window, group):
|
def _cmd(query, window, group):
|
||||||
terms = " ".join(query)
|
terms = " ".join(query)
|
||||||
client_from_ctx().nav.search(engine_key, terms, background=bg, window=window, group=group)
|
client_from_ctx().nav.search(engine_key, terms, window=window, group=group)
|
||||||
suffix = f" in group '{group}'" if group else (f" in window '{window}'" if window else "")
|
suffix = f" in group '{group}'" if group else (f" in window '{window}'" if window else "")
|
||||||
display = _DISPLAY_NAMES.get(engine_key, engine_key.capitalize())
|
display = _DISPLAY_NAMES.get(engine_key, engine_key.capitalize())
|
||||||
console.print(f"[green]Searching[/green] [cyan]{display}[/cyan]: {terms}{suffix}")
|
console.print(f"[green]Searching[/green] [cyan]{display}[/cyan]: {terms}{suffix}")
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ from __future__ import annotations
|
|||||||
from browser_cli.models import Tab
|
from browser_cli.models import Tab
|
||||||
from browser_cli.sdk.base import Namespace, sdk_command
|
from browser_cli.sdk.base import Namespace, sdk_command
|
||||||
|
|
||||||
def _open_args(self, url, *, background=False, window=None, group=None):
|
def _open_args(self, url, *, background=False, focus=False, window=None, group=None):
|
||||||
return {"url": url, "background": background, "window": window, "group": group}
|
return {"url": url, "background": background or not focus, "focus": focus, "window": window, "group": group}
|
||||||
|
|
||||||
def _tab_args(self, tab_id=None):
|
def _tab_args(self, tab_id=None):
|
||||||
return {"tabId": tab_id}
|
return {"tabId": tab_id}
|
||||||
@@ -14,8 +14,16 @@ class NavigationNS(Namespace):
|
|||||||
"""Open URLs, navigate history, and focus tabs."""
|
"""Open URLs, navigate history, and focus tabs."""
|
||||||
|
|
||||||
@sdk_command("navigate.open", _open_args)
|
@sdk_command("navigate.open", _open_args)
|
||||||
def open(self, url: str, *, background: bool = False, window: str | None = None, group: str | None = None) -> None:
|
def open(
|
||||||
"""Open *url* in a new tab."""
|
self,
|
||||||
|
url: str,
|
||||||
|
*,
|
||||||
|
background: bool = False,
|
||||||
|
focus: bool = False,
|
||||||
|
window: str | None = None,
|
||||||
|
group: str | None = None,
|
||||||
|
) -> None:
|
||||||
|
"""Open *url* in a new tab without stealing OS focus by default."""
|
||||||
|
|
||||||
def open_wait(
|
def open_wait(
|
||||||
self,
|
self,
|
||||||
@@ -23,6 +31,7 @@ class NavigationNS(Namespace):
|
|||||||
*,
|
*,
|
||||||
timeout: float = 30.0,
|
timeout: float = 30.0,
|
||||||
background: bool = False,
|
background: bool = False,
|
||||||
|
focus: bool = False,
|
||||||
window: str | None = None,
|
window: str | None = None,
|
||||||
group: str | None = None,
|
group: str | None = None,
|
||||||
) -> Tab:
|
) -> Tab:
|
||||||
@@ -30,7 +39,7 @@ class NavigationNS(Namespace):
|
|||||||
return self.require_tab(
|
return self.require_tab(
|
||||||
self.command("navigate.open_wait", {
|
self.command("navigate.open_wait", {
|
||||||
"url": url, "timeout": int(timeout * 1000),
|
"url": url, "timeout": int(timeout * 1000),
|
||||||
"background": background, "window": window, "group": group,
|
"background": background or not focus, "focus": focus, "window": window, "group": group,
|
||||||
}),
|
}),
|
||||||
"navigate.open_wait returned unexpected data",
|
"navigate.open_wait returned unexpected data",
|
||||||
)
|
)
|
||||||
@@ -61,7 +70,7 @@ class NavigationNS(Namespace):
|
|||||||
|
|
||||||
def search(
|
def search(
|
||||||
self, engine: str, query: str, *,
|
self, engine: str, query: str, *,
|
||||||
background: bool = False, window: str | None = None, group: str | None = None,
|
background: bool = False, focus: bool = False, window: str | None = None, group: str | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Open a search query in the given engine (e.g. 'google', 'youtube', 'ddg')."""
|
"""Open a search query in the given engine (e.g. 'google', 'youtube', 'ddg')."""
|
||||||
from urllib.parse import quote_plus
|
from urllib.parse import quote_plus
|
||||||
@@ -70,4 +79,4 @@ class NavigationNS(Namespace):
|
|||||||
if template is None:
|
if template is None:
|
||||||
raise ValueError(f"Unknown search engine '{engine}'. Available: {', '.join(ENGINES)}")
|
raise ValueError(f"Unknown search engine '{engine}'. Available: {', '.join(ENGINES)}")
|
||||||
url = template.format(query=quote_plus(query))
|
url = template.format(query=quote_plus(query))
|
||||||
self.command("navigate.open", {"url": url, "background": background, "window": window, "group": group})
|
self.command("navigate.open", {"url": url, "background": background or not focus, "focus": focus, "window": window, "group": group})
|
||||||
|
|||||||
@@ -24,17 +24,19 @@ class TabsNS(Namespace):
|
|||||||
wait: bool = False,
|
wait: bool = False,
|
||||||
timeout: float = 30.0,
|
timeout: float = 30.0,
|
||||||
background: bool = False,
|
background: bool = False,
|
||||||
|
focus: bool = False,
|
||||||
window: str | None = None,
|
window: str | None = None,
|
||||||
group: str | None = None,
|
group: str | None = None,
|
||||||
) -> Tab:
|
) -> Tab:
|
||||||
"""Open *url* in a new tab and return a bound :class:`Tab`.
|
"""Open *url* in a new tab and return a bound :class:`Tab`.
|
||||||
|
|
||||||
Set ``wait=True`` to block until the page reaches ``readyState=complete``.
|
Set ``wait=True`` to block until the page reaches ``readyState=complete``.
|
||||||
|
Pass ``focus=True`` to explicitly bring the created tab/window forward.
|
||||||
"""
|
"""
|
||||||
if wait:
|
if wait:
|
||||||
return self._c.nav.open_wait(url, timeout=timeout, background=background, window=window, group=group)
|
return self._c.nav.open_wait(url, timeout=timeout, background=background, focus=focus, window=window, group=group)
|
||||||
return self.require_tab(
|
return self.require_tab(
|
||||||
self.command("navigate.open", {"url": url, "background": background, "window": window, "group": group}),
|
self.command("navigate.open", {"url": url, "background": background or not focus, "focus": focus, "window": window, "group": group}),
|
||||||
"navigate.open returned unexpected data",
|
"navigate.open returned unexpected data",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ class WorkflowDecoratorsMixin:
|
|||||||
wait: bool = False,
|
wait: bool = False,
|
||||||
timeout: float = 30.0,
|
timeout: float = 30.0,
|
||||||
background: bool = False,
|
background: bool = False,
|
||||||
|
focus: bool = False,
|
||||||
window: str | None = None,
|
window: str | None = None,
|
||||||
group: str | None = None,
|
group: str | None = None,
|
||||||
close: bool = False,
|
close: bool = False,
|
||||||
@@ -97,6 +98,7 @@ class WorkflowDecoratorsMixin:
|
|||||||
wait=wait,
|
wait=wait,
|
||||||
timeout=timeout,
|
timeout=timeout,
|
||||||
background=background,
|
background=background,
|
||||||
|
focus=focus,
|
||||||
window=window,
|
window=window,
|
||||||
group=group,
|
group=group,
|
||||||
)
|
)
|
||||||
|
|||||||
+2
-2
@@ -43,8 +43,8 @@ pause
|
|||||||
header "3/8 · Create 'research' group and open URLs into it"
|
header "3/8 · Create 'research' group and open URLs into it"
|
||||||
$CLI groups create research
|
$CLI groups create research
|
||||||
echo ""
|
echo ""
|
||||||
$CLI nav open https://example.com --group research --bg
|
$CLI nav open https://example.com --group research
|
||||||
$CLI nav open https://wikipedia.org --group research --bg
|
$CLI nav open https://wikipedia.org --group research
|
||||||
echo ""
|
echo ""
|
||||||
echo " Tabs are now open inside the 'research' group in your browser."
|
echo " Tabs are now open inside the 'research' group in your browser."
|
||||||
pause
|
pause
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"manifest_version": 3,
|
"manifest_version": 3,
|
||||||
"name": "browser-cli",
|
"name": "browser-cli",
|
||||||
"version": "0.12.2",
|
"version": "0.12.3",
|
||||||
"description": "Control your browser from the terminal or Python SDK",
|
"description": "Control your browser from the terminal or Python SDK",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"tabs",
|
"tabs",
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export class NavigationCommands extends CommandGroup {
|
|||||||
"navigate.open_wait": (a: NavOpenWaitArgs) => this.navOpenWait(a),
|
"navigate.open_wait": (a: NavOpenWaitArgs) => this.navOpenWait(a),
|
||||||
};
|
};
|
||||||
|
|
||||||
private async navOpen({ url, background, window: windowName, windowId: explicitWindowId, group: groupNameOrId }: NavOpenArgs) {
|
private async navOpen({ url, background, focus, window: windowName, windowId: explicitWindowId, group: groupNameOrId }: NavOpenArgs) {
|
||||||
let windowId: number | undefined;
|
let windowId: number | undefined;
|
||||||
if (explicitWindowId != null) {
|
if (explicitWindowId != null) {
|
||||||
windowId = explicitWindowId;
|
windowId = explicitWindowId;
|
||||||
@@ -26,7 +26,7 @@ export class NavigationCommands extends CommandGroup {
|
|||||||
const entry = Object.entries(aliases).find(([, v]) => v === windowName);
|
const entry = Object.entries(aliases).find(([, v]) => v === windowName);
|
||||||
if (entry) windowId = parseInt(entry[0]);
|
if (entry) windowId = parseInt(entry[0]);
|
||||||
}
|
}
|
||||||
const tab = await chrome.tabs.create({ url, active: !background, windowId });
|
const tab = await chrome.tabs.create({ url, active: Boolean(focus) && !background, windowId });
|
||||||
if (groupNameOrId != null) {
|
if (groupNameOrId != null) {
|
||||||
let groupId;
|
let groupId;
|
||||||
try {
|
try {
|
||||||
@@ -115,8 +115,8 @@ export class NavigationCommands extends CommandGroup {
|
|||||||
throw new Error(`Tab ${tab.id} did not reach status '${readyState}' within ${timeout}ms`);
|
throw new Error(`Tab ${tab.id} did not reach status '${readyState}' within ${timeout}ms`);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async navOpenWait({ url, timeout = 30000, background, window: windowName, group }: NavOpenWaitArgs = {}) {
|
private async navOpenWait({ url, timeout = 30000, background, focus, window: windowName, group }: NavOpenWaitArgs = {}) {
|
||||||
const opened = await this.navOpen({ url, background, window: windowName, group });
|
const opened = await this.navOpen({ url, background, focus, window: windowName, group });
|
||||||
return await this.navWait({ tabId: opened.id, timeout });
|
return await this.navWait({ tabId: opened.id, timeout });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import type { Job } from './jobs';
|
|||||||
export interface NavOpenArgs {
|
export interface NavOpenArgs {
|
||||||
url?: string;
|
url?: string;
|
||||||
background?: boolean;
|
background?: boolean;
|
||||||
|
focus?: boolean;
|
||||||
window?: string;
|
window?: string;
|
||||||
windowId?: number;
|
windowId?: number;
|
||||||
group?: string | number;
|
group?: string | number;
|
||||||
@@ -18,6 +19,7 @@ export interface NavOpenWaitArgs {
|
|||||||
url?: string;
|
url?: string;
|
||||||
timeout?: number;
|
timeout?: number;
|
||||||
background?: boolean;
|
background?: boolean;
|
||||||
|
focus?: boolean;
|
||||||
window?: string;
|
window?: string;
|
||||||
group?: string | number;
|
group?: string | number;
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "browser-cli"
|
name = "browser-cli"
|
||||||
version = "0.12.2"
|
version = "0.12.3"
|
||||||
description = "Control your real running browser from the terminal or Python SDK"
|
description = "Control your real running browser from the terminal or Python SDK"
|
||||||
requires-python = ">=3.10"
|
requires-python = ">=3.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
|||||||
+9
-5
@@ -160,7 +160,7 @@ class TestNavigation:
|
|||||||
b.nav.open("https://example.com")
|
b.nav.open("https://example.com")
|
||||||
mock_send.assert_called_once_with(
|
mock_send.assert_called_once_with(
|
||||||
"navigate.open",
|
"navigate.open",
|
||||||
{"url": "https://example.com", "background": False, "window": None, "group": None},
|
{"url": "https://example.com", "background": True, "focus": False, "window": None, "group": None},
|
||||||
profile=None, remote=None, key=None,
|
profile=None, remote=None, key=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -173,6 +173,10 @@ class TestNavigation:
|
|||||||
b.nav.open("https://x.com", group="Work")
|
b.nav.open("https://x.com", group="Work")
|
||||||
assert mock_send.call_args[0][1]["group"] == "Work"
|
assert mock_send.call_args[0][1]["group"] == "Work"
|
||||||
|
|
||||||
|
def test_open_focus_is_explicit(self, b, mock_send):
|
||||||
|
b.nav.open("https://example.com", focus=True)
|
||||||
|
assert mock_send.call_args[0][1]["focus"] is True
|
||||||
|
|
||||||
def test_tabs_open_returns_bound_tab(self, b, mock_send):
|
def test_tabs_open_returns_bound_tab(self, b, mock_send):
|
||||||
mock_send.return_value = {"id": 123, "url": "https://example.com"}
|
mock_send.return_value = {"id": 123, "url": "https://example.com"}
|
||||||
|
|
||||||
@@ -183,7 +187,7 @@ class TestNavigation:
|
|||||||
assert tab._browser is b
|
assert tab._browser is b
|
||||||
mock_send.assert_called_once_with(
|
mock_send.assert_called_once_with(
|
||||||
"navigate.open",
|
"navigate.open",
|
||||||
{"url": "https://example.com", "background": True, "window": None, "group": None},
|
{"url": "https://example.com", "background": True, "focus": False, "window": None, "group": None},
|
||||||
profile=None,
|
profile=None,
|
||||||
remote=None,
|
remote=None,
|
||||||
key=None,
|
key=None,
|
||||||
@@ -197,7 +201,7 @@ class TestNavigation:
|
|||||||
assert tab.id == 10
|
assert tab.id == 10
|
||||||
mock_send.assert_called_once_with(
|
mock_send.assert_called_once_with(
|
||||||
"navigate.open_wait",
|
"navigate.open_wait",
|
||||||
{"url": "https://example.com", "timeout": 1500, "background": False, "window": None, "group": None},
|
{"url": "https://example.com", "timeout": 1500, "background": True, "focus": False, "window": None, "group": None},
|
||||||
profile=None,
|
profile=None,
|
||||||
remote=None,
|
remote=None,
|
||||||
key=None,
|
key=None,
|
||||||
@@ -1031,7 +1035,7 @@ class TestSDKDecorators:
|
|||||||
assert mock_send.mock_calls == [
|
assert mock_send.mock_calls == [
|
||||||
call(
|
call(
|
||||||
"navigate.open_wait",
|
"navigate.open_wait",
|
||||||
{"url": "https://example.com", "timeout": 1500, "background": False, "window": None, "group": None},
|
{"url": "https://example.com", "timeout": 1500, "background": True, "focus": False, "window": None, "group": None},
|
||||||
profile=None,
|
profile=None,
|
||||||
remote=None,
|
remote=None,
|
||||||
key=None,
|
key=None,
|
||||||
@@ -1190,7 +1194,7 @@ class TestAsyncBrowserCLI:
|
|||||||
assert mock_send_async.mock_calls == [
|
assert mock_send_async.mock_calls == [
|
||||||
call(
|
call(
|
||||||
"navigate.open",
|
"navigate.open",
|
||||||
{"url": "https://example.com", "background": False, "window": None, "group": None},
|
{"url": "https://example.com", "background": True, "focus": False, "window": None, "group": None},
|
||||||
profile=None,
|
profile=None,
|
||||||
remote=None,
|
remote=None,
|
||||||
key=None,
|
key=None,
|
||||||
|
|||||||
@@ -346,14 +346,43 @@ def test_cli_perf_profile_ultra():
|
|||||||
from browser_cli.commands.navigate import nav_group
|
from browser_cli.commands.navigate import nav_group
|
||||||
|
|
||||||
def test_cli_nav_open():
|
def test_cli_nav_open():
|
||||||
result = _run(nav_group, ["open", "https://example.com"], {"id": 42, "url": "https://example.com"})
|
with patch("browser_cli.send_command", return_value={"id": 42, "url": "https://example.com"}) as send_command:
|
||||||
assert result.exit_code == 0
|
result = CliRunner().invoke(nav_group, ["open", "https://example.com"])
|
||||||
assert "Opened" in result.output
|
|
||||||
|
|
||||||
def test_cli_nav_open_bg():
|
|
||||||
result = _run(nav_group, ["open", "https://example.com", "--bg"], {"id": 42})
|
|
||||||
assert result.exit_code == 0
|
assert result.exit_code == 0
|
||||||
assert "Opened" in result.output
|
assert "Opened" in result.output
|
||||||
|
send_command.assert_called_once_with(
|
||||||
|
"navigate.open",
|
||||||
|
{"url": "https://example.com", "background": True, "focus": False, "window": None, "group": None},
|
||||||
|
profile=None,
|
||||||
|
remote=None,
|
||||||
|
key=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_cli_nav_open_has_no_bg_option():
|
||||||
|
result = CliRunner().invoke(nav_group, ["open", "--help"])
|
||||||
|
|
||||||
|
assert result.exit_code == 0
|
||||||
|
assert "--bg" not in result.output
|
||||||
|
|
||||||
|
def test_cli_nav_open_wait_has_no_bg_option():
|
||||||
|
result = CliRunner().invoke(nav_group, ["open-wait", "--help"])
|
||||||
|
|
||||||
|
assert result.exit_code == 0
|
||||||
|
assert "--bg" not in result.output
|
||||||
|
|
||||||
|
def test_cli_nav_open_focus_is_explicit():
|
||||||
|
with patch("browser_cli.send_command", return_value={"id": 42}) as send_command:
|
||||||
|
result = CliRunner().invoke(nav_group, ["open", "https://example.com", "--focus"])
|
||||||
|
|
||||||
|
assert result.exit_code == 0
|
||||||
|
send_command.assert_called_once_with(
|
||||||
|
"navigate.open",
|
||||||
|
{"url": "https://example.com", "background": False, "focus": True, "window": None, "group": None},
|
||||||
|
profile=None,
|
||||||
|
remote=None,
|
||||||
|
key=None,
|
||||||
|
)
|
||||||
|
|
||||||
def test_cli_nav_open_with_group():
|
def test_cli_nav_open_with_group():
|
||||||
result = _run(nav_group, ["open", "https://example.com", "--group", "work"], {"id": 42})
|
result = _run(nav_group, ["open", "https://example.com", "--group", "work"], {"id": 42})
|
||||||
@@ -412,6 +441,18 @@ def test_cli_nav_wait():
|
|||||||
assert result.exit_code == 0
|
assert result.exit_code == 0
|
||||||
assert "Ready" in result.output
|
assert "Ready" in result.output
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# search commands
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
from browser_cli.commands.search import search_group
|
||||||
|
|
||||||
|
def test_cli_search_has_no_bg_option():
|
||||||
|
result = CliRunner().invoke(search_group, ["google", "--help"])
|
||||||
|
|
||||||
|
assert result.exit_code == 0
|
||||||
|
assert "--bg" not in result.output
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# navigate commands — with tab_id argument
|
# navigate commands — with tab_id argument
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -111,10 +111,12 @@ def test_large_extension_operations_yield_between_batches():
|
|||||||
assert "perf.set_profile" in perf
|
assert "perf.set_profile" in perf
|
||||||
assert "__background" in connection
|
assert "__background" in connection
|
||||||
|
|
||||||
def test_tab_activation_and_merge_do_not_steal_audible_video_window():
|
def test_tab_activation_open_and_merge_do_not_steal_audible_video_window():
|
||||||
tabs = (ROOT / "extension" / "src" / "commands" / "tabs.ts").read_text()
|
tabs = (ROOT / "extension" / "src" / "commands" / "tabs.ts").read_text()
|
||||||
|
navigation = (ROOT / "extension" / "src" / "commands" / "navigation.ts").read_text()
|
||||||
|
|
||||||
assert "await chrome.windows.update(tab.windowId, { focused: true });" not in tabs
|
assert "await chrome.windows.update(tab.windowId, { focused: true });" not in tabs
|
||||||
|
assert "active: Boolean(focus) && !background" in navigation
|
||||||
assert "windowHasAudibleTabs" in tabs
|
assert "windowHasAudibleTabs" in tabs
|
||||||
assert "!this.windowHasAudibleTabs(w)" in tabs
|
assert "!this.windowHasAudibleTabs(w)" in tabs
|
||||||
assert "skippedAudibleWindows" in tabs
|
assert "skippedAudibleWindows" in tabs
|
||||||
|
|||||||
Reference in New Issue
Block a user