"""Tests para whatsapp_open_chat. whatsapp_open_chat compone cuatro primitivas CDP (cdp_eval, cdp_type_chars, cdp_press_key, cdp_click_xy) y requiere un Chrome vivo. Aqui se mockean las cuatro con monkeypatch sobre el modulo `browser.whatsapp_open_chat` (donde quedan ligados los nombres por el `from browser.X import Y`), de modo que NO hace falta Chrome. Las llamadas a cdp_eval que importan son dos: - la del ancla (querySelectorAll '#side span[title]') -> devuelve coords JSON. - la de verificacion (footer contenteditable aria-label) -> devuelve el label. El resto de cdp_eval (focus/select del input) devuelven un value inocuo. """ import json import os import sys sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) import browser.whatsapp_open_chat as woc from browser.whatsapp_open_chat import whatsapp_open_chat # --- Fakes ----------------------------------------------------------------- def _fake_cdp_eval_factory(anchor_value, composer_value): """Devuelve un fake de cdp_eval que distingue el ancla del composer. - Expresion con 'span[title]' (busqueda del ancla) -> {"value": anchor_value}. - Expresion con 'contenteditable' (composer) -> {"value": composer_value}. - Cualquier otra (focus/select del input) -> {"value": None} inocuo. """ def _fake(expr, *, port=9222, target_url_substr=""): if "span[title]" in expr: return {"ok": True, "value": anchor_value, "error": "", "target_url": ""} if "contenteditable" in expr: return {"ok": True, "value": composer_value, "error": "", "target_url": ""} return {"ok": True, "value": None, "error": "", "target_url": ""} return _fake class _Spy: """Registra los argumentos posicionales de cada llamada.""" def __init__(self, ret=None): self.calls = [] self.ret = ret if ret is not None else {"ok": True} def __call__(self, *args, **kwargs): self.calls.append((args, kwargs)) return self.ret def _patch_io(monkeypatch, *, anchor_value, composer_value, click_spy=None): """Mockea las cuatro primitivas + time.sleep en el modulo woc.""" monkeypatch.setattr(woc, "cdp_eval", _fake_cdp_eval_factory(anchor_value, composer_value)) monkeypatch.setattr(woc, "cdp_type_chars", lambda *a, **k: {"ok": True}) monkeypatch.setattr(woc, "cdp_press_key", lambda *a, **k: {"ok": True}) monkeypatch.setattr(woc, "cdp_click_xy", click_spy or (lambda *a, **k: {"ok": True})) monkeypatch.setattr(woc.time, "sleep", lambda *a, **k: None) # --- Tests ----------------------------------------------------------------- def test_golden_abre_chat_y_verifica_composer(monkeypatch): coords = json.dumps({"x": 180, "y": 240}) label = "Escribir un mensaje para el grupo NOTAS WASAP" _patch_io(monkeypatch, anchor_value=coords, composer_value=label) res = whatsapp_open_chat("NOTAS WASAP", port=9222, target_url_substr="whatsapp") assert res["opened"] is True assert res["name"] == "NOTAS WASAP" assert res["composer_label"] == label assert res["coords"] == {"x": 180, "y": 240} def test_edge_ancla_no_encontrada_opened_false(monkeypatch): # El ancla no existe: cdp_eval del span[title] devuelve value None. _patch_io(monkeypatch, anchor_value=None, composer_value="irrelevante") res = whatsapp_open_chat("Contacto Inexistente", port=9222, target_url_substr="whatsapp") assert res["opened"] is False assert res["name"] == "Contacto Inexistente" assert "no encontrado" in res["reason"] # Sin coords ni composer_label cuando no se encuentra el ancla. assert "coords" not in res assert "composer_label" not in res def test_click_usa_coords_devueltas_por_el_ancla(monkeypatch): coords = json.dumps({"x": 333, "y": 444}) label = "Escribir un mensaje para el grupo NOTAS WASAP" click_spy = _Spy(ret={"ok": True}) _patch_io(monkeypatch, anchor_value=coords, composer_value=label, click_spy=click_spy) res = whatsapp_open_chat("NOTAS WASAP", port=9222, target_url_substr="whatsapp") # Se llamo a cdp_click_xy exactamente una vez con las coords del ancla. assert len(click_spy.calls) == 1 args, kwargs = click_spy.calls[0] assert args[0] == 333 assert args[1] == 444 assert kwargs["port"] == 9222 assert kwargs["target_url_substr"] == "whatsapp" assert res["opened"] is True