from pathlib import Path import playwright.sync_api as playwright_sync import auth_login from auth_login import BrowserRuntime, resolve_selection_chrome_profile REPO_ROOT = Path(__file__).resolve().parents[2] def _is_relative_to(path: Path, root: Path) -> bool: try: path.resolve().relative_to(root.resolve()) except ValueError: return False return True def test_default_chrome_profile_is_outside_repo_selection_subdir(monkeypatch): monkeypatch.delenv("RPA_SELECTION_CHROME_PROFILE", raising=False) monkeypatch.delenv("RPA_CHROME_PROFILE", raising=False) profile = resolve_selection_chrome_profile(repo_root=REPO_ROOT) assert profile == (REPO_ROOT.parent / "rpa-chrome-profile" / "selection").resolve() assert not _is_relative_to(profile, REPO_ROOT) def test_env_path_inside_repo_is_rejected(monkeypatch): inside = REPO_ROOT / "rpa_selection" / "data" / "chrome-profile" monkeypatch.delenv("RPA_CHROME_PROFILE", raising=False) profile = resolve_selection_chrome_profile(requested=str(inside), repo_root=REPO_ROOT) assert profile == (REPO_ROOT.parent / "rpa-chrome-profile" / "selection").resolve() assert not _is_relative_to(profile, REPO_ROOT) def test_env_path_outside_repo_is_kept(tmp_path): outside = tmp_path / "custom-chrome" outside.mkdir() profile = resolve_selection_chrome_profile(requested=str(outside), repo_root=REPO_ROOT) assert profile == outside.resolve() def test_rpa_chrome_profile_uses_selection_subdir(monkeypatch, tmp_path): base = tmp_path / "work-profile" monkeypatch.delenv("RPA_SELECTION_CHROME_PROFILE", raising=False) monkeypatch.setenv("RPA_CHROME_PROFILE", str(base)) profile = resolve_selection_chrome_profile(repo_root=REPO_ROOT) assert profile == (base / "selection").resolve() class _FakePage: url = "about:blank" def bring_to_front(self) -> None: return None class _FakeContext: def __init__(self) -> None: self.pages = [_FakePage()] self.closed = False self.new_page_calls = 0 def new_page(self) -> _FakePage: self.new_page_calls += 1 page = _FakePage() self.pages.append(page) return page def close(self) -> None: self.closed = True class _FakeChromium: def __init__(self) -> None: self.persistent_calls: list[tuple[str, dict]] = [] self.launch_calls: list[dict] = [] self.context = _FakeContext() def launch_persistent_context(self, user_data_dir: str, **kwargs): self.persistent_calls.append((user_data_dir, kwargs)) return self.context def launch(self, **kwargs): self.launch_calls.append(kwargs) raise AssertionError("选品登录必须复用用户目录,禁止 launch 空 Chrome") class _FakePlaywright: def __init__(self, chromium: _FakeChromium) -> None: self.chromium = chromium def start(self) -> "_FakePlaywright": return self def stop(self) -> None: return None def test_browser_runtime_reuses_persistent_profile(monkeypatch, tmp_path): profile = tmp_path / "selection-profile" monkeypatch.setenv("RPA_SELECTION_CHROME_PROFILE", str(profile)) chromium = _FakeChromium() fake = _FakePlaywright(chromium) monkeypatch.setattr(playwright_sync, "sync_playwright", lambda: fake) runtime = BrowserRuntime(keep=False) ctx1, page1 = runtime.new_page() ctx2, page2 = runtime.new_page() assert chromium.launch_calls == [] assert len(chromium.persistent_calls) == 1 user_data_dir, kwargs = chromium.persistent_calls[0] assert user_data_dir == str(profile.resolve()) assert kwargs.get("channel") == "chrome" assert "--disable-blink-features=AutomationControlled" in kwargs.get("args", []) assert ctx1 is ctx2 is chromium.context assert page1 is not page2 assert chromium.context.new_page_calls == 1 runtime.close() assert chromium.context.closed assert auth_login.resolve_selection_chrome_profile() == profile.resolve()