import os import re import json import time import base64 from pathlib import Path from dotenv import load_dotenv from playwright.sync_api import sync_playwright, TimeoutError as PlaywrightTimeout load_dotenv() from get_token import ( SOL_URL, MENU_URL, TOKEN_FILE, SCREENSHOT_DIR, RUC, USER, PASSWORD, fill_login_form, save_screenshot, save_token, check_for_token, navigate_sol_menu, click_menu_item_by_id, extract_token_from_menu, ) TEST_DIR = SCREENSHOT_DIR TEST_DIR.mkdir(exist_ok=True) USER_AGENT = ( "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) " "AppleWebKit/537.36 (KHTML, like Gecko) " "Chrome/120.0.0.0 Safari/537.36" ) BROWSER_ARGS = [ "--disable-blink-features=AutomationControlled", "--disable-popup-blocking", "--no-sandbox", "--disable-web-security", "--disable-features=IsolateOrigins,site-per-process", "--window-size=1280,800", "--start-maximized", ] def captura(page, tag, nombre): safe = f"{tag}_{nombre}" page.screenshot(path=str(TEST_DIR / f"{safe}.png")) def _salto_linea(texto, ancho=100): for i in range(0, len(texto), ancho): print(f" {texto[i:i+ancho]}") def _login_si_necesario(page, tag): page.wait_for_load_state("networkidle") has_inputs = len(page.locator("input[type='password']").all()) > 0 necesita_login = has_inputs or any( k in page.url.lower() for k in ["login", "autentica", "auth"] ) if not necesita_login: print(" No se detectó login") return True print(" Login detectado. Rellenando credenciales...") fill_login_form(page) captura(page, tag, "form_rellenado") page.keyboard.press("Enter") try: page.wait_for_load_state("networkidle", timeout=30000) except Exception as e: print(f" ⚠️ Timeout post-login: {e}") page.wait_for_timeout(3000) captura(page, tag, "post_login") print(f" URL post-login: {page.url}") return True def _buscar_token_en_pagina(page, tag): time.sleep(2) token = check_for_token(page) if token: return token token = navigate_sol_menu(page) if token: return token token = extract_token_from_menu(page) return token def _navegar_con_cookies(page, url, tag): page.goto(url, wait_until="domcontentloaded") captura(page, tag, "navegacion") print(f" URL tras navegar: {page.url}") # ─── ESTRATEGIAS ───────────────────────────────────────────── def estrategia_1_fixed_popup(context, page): tag = "e1" print("\n" + "=" * 60) print("ESTRATEGIA 1: Popup con --disable-popup-blocking + event listener") print("=" * 60) page.goto(SOL_URL, wait_until="domcontentloaded") captura(page, tag, "sol_home") popup_found = [None] got_popup = [False] def on_page(new_page): if not got_popup[0]: print(f" 📌 Página detectada: {new_page.url[:100]}") popup_found[0] = new_page got_popup[0] = True page.context().on("page", on_page) with page.expect_popup() as popup_info: page.evaluate("tramiteConsulta()") try: pup = popup_info.value pup.wait_for_load_state("domcontentloaded", timeout=20000) print(f" Popup URL: {pup.url}") captura(pup, tag, "popup") if "chrome-error" in pup.url: print(" ⚠️ Popup en chrome-error. Reintentando recarga...") try: pup.wait_for_load_state("domcontentloaded", timeout=20000) except: pass captura(pup, tag, "popup_retry") if "chrome-error" in pup.url: print(" ❌ Sigue en chrome-error tras recarga") print(f" URL final: {pup.url}") return None _login_si_necesario(pup, tag) token = _buscar_token_en_pagina(pup, tag) return token except Exception as e: print(f" ❌ Error con popup: {e}") return None def estrategia_2_direct_menu(context, page): tag = "e2" print("\n" + "=" * 60) print("ESTRATEGIA 2: Menú directo con sesión desde SOL") print("=" * 60) page.goto(SOL_URL, wait_until="domcontentloaded") captura(page, tag, "sol_home") _navegar_con_cookies(page, MENU_URL, tag) if not _login_si_necesario(page, tag): return None token = _buscar_token_en_pagina(page, tag) return token def estrategia_3_direct_login(context, page): tag = "e3" print("\n" + "=" * 60) print("ESTRATEGIA 3: Login directo al menú (sin SOL)") print("=" * 60) _navegar_con_cookies(page, MENU_URL, tag) if not _login_si_necesario(page, tag): return None token = _buscar_token_en_pagina(page, tag) return token def estrategia_4_popup_url_directo(context, page): tag = "e4" print("\n" + "=" * 60) print("ESTRATEGIA 4: Extraer URL del popup y navegar directo") print("=" * 60) page.goto(SOL_URL, wait_until="domcontentloaded") captura(page, tag, "sol_home") try: popup_url = page.evaluate("""() => { const fn = tramiteConsulta.toString(); const match = fn.match(/window\\.open\\(['"]([^'"]+)['"]/); return match ? match[1] : null; }""") if popup_url: if popup_url.startswith("/"): from urllib.parse import urljoin popup_url = urljoin(SOL_URL, popup_url) print(f" URL del popup: {popup_url}") else: print(" No se pudo extraer URL") return None except Exception as e: print(f" Error extrayendo URL: {e}") return None _navegar_con_cookies(page, popup_url, tag) if not _login_si_necesario(page, tag): return None token = _buscar_token_en_pagina(page, tag) return token # ─── CONFIGURACIONES DE LANZAMIENTO ────────────────────────── def lanzar_chrome(p, channel=None, extra_args=None): """Lanza navegador con la configuracion dada.""" args = list(BROWSER_ARGS) if extra_args: args.extend(extra_args) kwargs = { "headless": True, "args": args, } if channel: kwargs["channel"] = channel print(f" Lanzando: {channel} (headless)") else: print(f" Lanzando: chromium empaquetado (headless)") browser = p.chromium.launch(**kwargs) context = browser.new_context( viewport={"width": 1280, "height": 800}, locale="es-PE", timezone_id="America/Lima", user_agent=USER_AGENT, no_viewport=False, ignore_https_errors=True, ) page = context.new_page() page.set_default_timeout(90000) return browser, context, page def ejecutar_escenario(p, nombre_browser, channel, extra_args): """Ejecuta todas las estrategias con un navegador/configuracion.""" print(f"\n{'#'*60}") print(f"# ESCENARIO: {nombre_browser}") print(f"{'#'*60}") browser, context, page = lanzar_chrome(p, channel, extra_args) token = None estrategias = [ ("1_fixed_popup", estrategia_1_fixed_popup), ("2_direct_menu", estrategia_2_direct_menu), ("3_direct_login", estrategia_3_direct_login), ("4_popup_url_directo", estrategia_4_popup_url_directo), ] for nombre, fn in estrategias: print() try: token = fn(context, page) except Exception as e: print(f" ⚠️ Excepción en {nombre}: {e}") import traceback traceback.print_exc() token = None if token: print(f"\n✅ Token obtenido con '{nombre}' en escenario '{nombre_browser}'!") return token else: print(f"\n ❌ '{nombre}' falló") context.close() browser.close() return None def main(): print("=" * 60) print("🔐 TEST HEADLESS TOKEN - SUNAT") print("=" * 60) print(f" RUC: {RUC}") print(f" USER: {USER}") if not RUC or not USER or not PASSWORD: print("\n❌ Faltan credenciales en .env (SUNAT_RUC, SUNAT_USER, SUNAT_PASSWORD)") return escenarios = [ ("Chrome real (channel=chrome)", "chrome", None), ("Chromium empaquetado", None, None), ("Chromium + extra stealth", None, ["--disable-gpu", "--disable-accelerated-2d-canvas"]), ] with sync_playwright() as p: for nombre_browser, channel, extra_args in escenarios: token = ejecutar_escenario(p, nombre_browser, channel, extra_args) if token: save_token(token) print(f"\n✅ Token guardado en {TOKEN_FILE}") print(f" Primeros 50 chars: {token[:50]}...") # Quitar token de prueba, no guardarlo permanentemente if TOKEN_FILE.exists(): TOKEN_FILE.unlink() return print(f"\n❌ Todas las estrategias y escenarios fallaron.") print(" Revisa screenshots en:", TEST_DIR) if __name__ == "__main__": main()