diff --git a/registry.json b/registry.json index b6462e4a7..f9fcaabc6 100644 --- a/registry.json +++ b/registry.json @@ -152,8 +152,8 @@ "category": "web", "contributors": [ { - "name": "CLI-Anything-Team", - "url": "https://github.com/HKUDS/CLI-Anything" + "name": "shinpei710", + "url": "https://github.com/shinpei710" } ] }, diff --git a/web-yu-pri/agent-harness/cli_anything/web_yu_pri/tests/TEST.md b/web-yu-pri/agent-harness/cli_anything/web_yu_pri/tests/TEST.md index c93e51ed5..a709bb2e1 100644 --- a/web-yu-pri/agent-harness/cli_anything/web_yu_pri/tests/TEST.md +++ b/web-yu-pri/agent-harness/cli_anything/web_yu_pri/tests/TEST.md @@ -2,7 +2,7 @@ ## Test Inventory Plan -- `test_core.py`: 12 unit/CLI smoke tests planned. +- `test_core.py`: 15 unit/CLI smoke tests planned. - `test_full_e2e.py`: 2 E2E tests planned. ## Unit Test Plan @@ -57,7 +57,7 @@ python -m pytest cli_anything/web_yu_pri/tests -v Result: ```text -collected 14 items +collected 17 items cli_anything/web_yu_pri/tests/test_core.py::test_parse_item_mapping_aliases PASSED cli_anything/web_yu_pri/tests/test_core.py::test_parse_item_rejects_bad_country PASSED @@ -70,11 +70,14 @@ cli_anything/web_yu_pri/tests/test_core.py::test_selector_report_contains_conten cli_anything/web_yu_pri/tests/test_core.py::test_build_dry_run_has_safety_metadata PASSED cli_anything/web_yu_pri/tests/test_core.py::test_cli_help PASSED cli_anything/web_yu_pri/tests/test_core.py::test_cli_plan_json PASSED +cli_anything/web_yu_pri/tests/test_core.py::test_cli_plan_missing_file_json PASSED cli_anything/web_yu_pri/tests/test_core.py::test_cli_contents_fill_dry_run_json PASSED +cli_anything/web_yu_pri/tests/test_core.py::test_cli_contents_fill_missing_file_json PASSED +cli_anything/web_yu_pri/tests/test_core.py::test_cli_open_login_json_defaults_to_no_wait PASSED cli_anything/web_yu_pri/tests/test_full_e2e.py::test_dry_run_cli_workflow PASSED cli_anything/web_yu_pri/tests/test_full_e2e.py::test_live_status_against_contents_page SKIPPED -13 passed, 1 skipped +16 passed, 1 skipped ``` Additional checks: diff --git a/web-yu-pri/agent-harness/cli_anything/web_yu_pri/tests/test_core.py b/web-yu-pri/agent-harness/cli_anything/web_yu_pri/tests/test_core.py index c06380d28..bc7c11934 100644 --- a/web-yu-pri/agent-harness/cli_anything/web_yu_pri/tests/test_core.py +++ b/web-yu-pri/agent-harness/cli_anything/web_yu_pri/tests/test_core.py @@ -1,6 +1,7 @@ """Core tests for cli-anything-web-yu-pri.""" import json +from unittest.mock import patch from click.testing import CliRunner import pytest @@ -115,6 +116,16 @@ def test_cli_plan_json(tmp_path): assert data["computed_total"] == 100 +def test_cli_plan_missing_file_json(tmp_path): + missing = tmp_path / "missing.json" + result = CliRunner().invoke(cli, ["--json", "plan", str(missing)]) + assert result.exit_code == 1 + assert "Usage:" not in result.output + data = json.loads(result.output) + assert data["type"] == "FileNotFoundError" + assert "items file not found" in data["error"] + + def test_cli_contents_fill_dry_run_json(tmp_path): path = tmp_path / "items.json" path.write_text(json.dumps([{"description": "A", "value": 100}]), encoding="utf-8") @@ -123,3 +134,25 @@ def test_cli_contents_fill_dry_run_json(tmp_path): data = json.loads(result.output) assert data["dry_run"] is True assert data["plan"]["declared_total"] == 100 + + +def test_cli_contents_fill_missing_file_json(tmp_path): + missing = tmp_path / "missing.json" + result = CliRunner().invoke(cli, ["--json", "contents", "fill", str(missing), "--dry-run"]) + assert result.exit_code == 1 + assert "Usage:" not in result.output + data = json.loads(result.output) + assert data["type"] == "FileNotFoundError" + assert "items file not found" in data["error"] + + +def test_cli_open_login_json_defaults_to_no_wait(): + fake_info = {"url": "https://mgr.post.japanpost.jp/C30P01Action.do", "title": "Web Yu-pri"} + with patch("cli_anything.web_yu_pri.web_yu_pri_cli.open_login", return_value=fake_info) as open_mock, \ + patch("cli_anything.web_yu_pri.web_yu_pri_cli._interactive_session") as interactive_mock: + result = CliRunner().invoke(cli, ["--json", "open-login"]) + + assert result.exit_code == 0 + assert json.loads(result.output) == fake_info + open_mock.assert_called_once() + interactive_mock.assert_not_called() diff --git a/web-yu-pri/agent-harness/cli_anything/web_yu_pri/web_yu_pri_cli.py b/web-yu-pri/agent-harness/cli_anything/web_yu_pri/web_yu_pri_cli.py index 64f6cd361..8d19764e8 100644 --- a/web-yu-pri/agent-harness/cli_anything/web_yu_pri/web_yu_pri_cli.py +++ b/web-yu-pri/agent-harness/cli_anything/web_yu_pri/web_yu_pri_cli.py @@ -6,6 +6,7 @@ from __future__ import annotations import json import shlex from contextlib import contextmanager +from pathlib import Path from typing import Any import click @@ -79,7 +80,10 @@ def _load_plan( total_value: int | None, value_mode: str, ) -> tuple[list[Any], dict[str, Any]]: - items = load_items(items_file, default_country=default_country) + path = Path(items_file).expanduser() + if not path.is_file(): + raise FileNotFoundError(f"items file not found: {items_file}") + items = load_items(path, default_country=default_country) plan = build_contents_plan(items, total_value=total_value, value_mode=value_mode) return items, plan @@ -126,7 +130,7 @@ def selectors_cmd() -> None: @cli.command("plan") -@click.argument("items_file", type=click.Path(exists=True, dir_okay=False, path_type=str)) +@click.argument("items_file", type=str) @click.option("--country-default", default=None, help="Default ISO country code for rows without one.") @click.option("--total-value", type=int, default=None, help="Override the declared total value in yen.") @click.option( @@ -152,15 +156,15 @@ def plan_cmd(items_file: str, country_default: str | None, total_value: int | No @click.option("--browser-channel", default=None, help="Playwright channel, for example msedge or chrome.") @click.option( "--wait/--no-wait", - default=True, - show_default=True, - help="Keep the browser open until Enter is pressed.", + default=None, + help="Keep the browser open until Enter is pressed. Defaults to on for human output and off for --json.", ) -def open_login_cmd(url: str, headless: bool, browser_channel: str | None, wait: bool) -> None: +def open_login_cmd(url: str, headless: bool, browser_channel: str | None, wait: bool | None) -> None: """Open the Web Yu-pri login/start page in the persistent profile.""" try: - if wait: + should_wait = (not _json_output) if wait is None else wait + if should_wait: with _interactive_session(url, headless=headless, browser_channel=browser_channel) as info: emit(info, "Opened Web Yu-pri login page.") if not _json_output: @@ -234,7 +238,7 @@ def contents() -> None: @contents.command("fill") -@click.argument("items_file", type=click.Path(exists=True, dir_okay=False, path_type=str)) +@click.argument("items_file", type=str) @click.option("--url", default=CONTENTS_URL, show_default=True, help="Contents form URL.") @click.option("--country-default", default=None, help="Default ISO country code for rows without one.") @click.option("--total-value", type=int, default=None, help="Override the declared total value in yen.")