chore: auto-commit (13 archivos)
- .gitignore - CAPABILITIES_TODO.md - demo_e2e/results/prueba_1_quotes.json - demo_e2e/results/prueba_2_perceive.json - demo_e2e/results/prueba_3_search.json - demo_e2e/results/prueba_4_login_session.json - demo_e2e/results/prueba_5_books.json - demo_e2e/results/prueba_6_session_storage.json - demo_e2e/results/prueba_7_find_honesto.json - demo_e2e/results/prueba_8_verificacion.json - ... Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,11 @@ analysis/*/
|
||||
vaults/*
|
||||
!vaults/.gitkeep
|
||||
|
||||
# Reports: reportes de trabajo (artefacto local). Como los vaults, no suben a
|
||||
# Gitea; solo el marcador .gitkeep se versiona. Convención: .claude/rules/reports.md
|
||||
reports/*
|
||||
!reports/.gitkeep
|
||||
|
||||
# Entornos, temporales y estado local
|
||||
.venv/
|
||||
__pycache__/
|
||||
|
||||
+51
-1
@@ -2,7 +2,7 @@
|
||||
title: Capacidades de navegador (CDP) + construcción del MCP full-CDP
|
||||
artefacto: project · projects/web_scraping
|
||||
created: 06/06/2026 00:00
|
||||
updated: 06/06/2026 09:00
|
||||
updated: 06/06/2026 10:00
|
||||
status: in_progress
|
||||
related_issues: []
|
||||
related_flows: []
|
||||
@@ -548,6 +548,56 @@ Storage granular desglosado (cookie/localStorage/sessionStorage × get/set/delet
|
||||
nosotros lo hacemos con 6), video/tracing/highlight (9 tools de debug visual de testing), `generate_locator`,
|
||||
`run_code_unsafe` (= nuestro `page_eval_js`). Replicar esto sube el contador a ~45 sin añadir poder.
|
||||
|
||||
### H. Política de velocidad de interacción (human / fast / instant) — REFACTOR, no flag
|
||||
|
||||
**No es un flag, es un refactor del primitivo + breaking en el teclado.** Hoy el camino de acción es
|
||||
human-only sin escape: `CdpClickXYHuman` siempre llama `CdpMoveMouseHuman` (25 puntos con sleeps), la
|
||||
micro-pausa press/release (30-90ms) está hardcoded, `CdpTypeText` no tiene opts (delay 10ms/char hardcoded),
|
||||
y `MouseHumanOpts` no tiene campo de modo (poner Steps=1 sigue moviendo y pausando). La política de 3 modos
|
||||
exige tocar el primitivo, no pasar opts distintos.
|
||||
|
||||
**El eje que separa los 3 (clave): "¿dispara eventos de ratón reales?"** — `fast` SÍ (mousemove/down/up,
|
||||
movimiento reducido), `instant` NO (usa `element.click()` JS). Hay SPAs con listeners en hover/mousedown que
|
||||
se rompen con instant pero funcionan con fast — eso justifica tener 3 y no 2.
|
||||
|
||||
| Parámetro | human (default) | fast | instant |
|
||||
|---|---|---|---|
|
||||
| Trayectoria ratón | Bézier ~25 pts | recta ~5 pts | sin movimiento |
|
||||
| Duración move | 350-800ms | ~40-80ms | 0 |
|
||||
| Jitter | 2px | 0-1px | 0 |
|
||||
| Pausa press/release | 30-90ms | 5-15ms | 0 |
|
||||
| Eventos ratón reales | sí | sí | no (`element.click()` JS) |
|
||||
| Typing delay/char | 10-40ms var | 1-3ms | 0 (set value + `input` event) |
|
||||
| Dispara hover/mousedown handlers | sí | sí | no |
|
||||
| Sigilo anti-bot | alto | bajo | nulo |
|
||||
| Caso de uso | agente vs detección | scraping masivo propio | tests + **fallback sin-bbox** |
|
||||
|
||||
**`instant` no es "solo tests": es el fallback obligatorio sin geometría.** `refBoxCenter` falla con error
|
||||
duro si el nodo no tiene box model (display:contents, área 0 con handler). El camino instant
|
||||
(`backendNodeId` → `DOM.resolveNode` → `Runtime.callFunctionOn` `.click()`) es el degradado natural cuando
|
||||
la vía bbox no aplica. No añadir un 4º modo "paranoid" — tres y basta.
|
||||
|
||||
**Arquitectura (sin meter estado de sesión en las funciones stateless):**
|
||||
- [x] **H1** ✅ (06/06) `Mode` (string `human`/`fast`/`instant`) en `MouseHumanOpts`. Branch en
|
||||
`CdpMoveMouseHuman` (instant=no-op, fast=reducido) y `CdpClickXYHuman` (pausa derivada del modo, no
|
||||
hardcoded). Toca el **primitivo compartido** → re-test de click_ref/human/xy. (medio)
|
||||
- [x] **H2** ✅ (06/06) `MouseProfileForMode(mode)` → `MouseHumanOpts` — **una sola fuente de verdad** del mapeo
|
||||
modo→números, consumida por MCP + runner YAML + CLI (no duplicar). (pequeño)
|
||||
- [ ] **H3** Teclado con config: `CdpTypeText` gana variante con delay/modo + instant via `set value` +
|
||||
`input` event. **BREAKING** → blast radius `script_navegador` (uses_functions) + MCP `type_ref`;
|
||||
**migrar consumidores en el mismo PR**. Es el corte que más cuidado pide. (medio)
|
||||
- [x] **H4** ✅ (06/06) `instant` fallback en `CdpClickRef` (`element.click()` JS) cuando no hay bbox o Mode=instant.
|
||||
- [~] **H5** parcial (06/06): override per-acción `mode` en tools MCP `dom_click_ref`/`dom_hover_ref` ✅
|
||||
(validado: human ~1212ms > fast ~590ms > instant ~499ms). PENDIENTE: estado de sesión
|
||||
`browser_set_mode` + `mode:` en YAML + `--mode` CLI.
|
||||
- [ ] **H6** Precedencia: **override per-acción > modo de sesión > default human**. Mantener el override
|
||||
per-acción (instant puntual sobre un elemento sin bbox aunque la sesión sea human).
|
||||
- [ ] **H7** e2e por modo: instant rompe sitio-con-hover, human/fast no — es la prueba que valida el eje.
|
||||
|
||||
**Los waits NO entran aquí** (`wait_idle` quietMs/timeout, `wait_load`) — son *correctness*, ortogonales al
|
||||
sigilo. Pero documentar: en scraping masivo el throughput lo matan TANTO la humanización COMO los waits;
|
||||
`mode: fast` sin ajustar waits no da toda la velocidad.
|
||||
|
||||
### Telemetría
|
||||
|
||||
`call_monitor` ya existe — cada tool del MCP encaja sin trabajo extra.
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
"port": 9333,
|
||||
"url": "https://quotes.toscrape.com"
|
||||
},
|
||||
"ms": 113,
|
||||
"ms": 303,
|
||||
"is_error": false,
|
||||
"response": "navigated to https://quotes.toscrape.com"
|
||||
},
|
||||
@@ -50,7 +50,7 @@
|
||||
"port": 9333,
|
||||
"timeout_ms": 12000
|
||||
},
|
||||
"ms": 235,
|
||||
"ms": 434,
|
||||
"is_error": false,
|
||||
"response": "page loaded"
|
||||
},
|
||||
@@ -60,7 +60,7 @@
|
||||
"port": 9333,
|
||||
"expression": "[...document.querySelectorAll('.quote')].map(q=>({text:q.querySelector('.text').innerText,author:q.querySelector('.author').innerText,tags:[...q.querySelectorAll('.tag')].map(t=>t.innerText)}))"
|
||||
},
|
||||
"ms": 6,
|
||||
"ms": 5,
|
||||
"is_error": false,
|
||||
"response": "[{\"author\":\"Albert Einstein\",\"tags\":[\"change\",\"deep-thoughts\",\"thinking\",\"world\"],\"text\":\"“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”\"},{\"author\":\"J.K. Rowling\",\"tags\":[\"abilities\",\"choices\"],\"text\":\"“It is our choices, Harry, that show what we truly are, far more than our abilities.”\"},{\"author\":\"Albert Einstein\",\"tags\":[\"inspirational\",\"life\",\"live\",\"miracle\",\"miracles\"],\"text\":\"“There are only two ways to live your life. One is as though nothing is a miracle. The other is as though everything is a miracle.”\"},{\"author\":\"Jane Austen\",\"tags\":[\"aliteracy\",\"books\",\"classic\",\"humor\"],\"text\":\"“The person, be it gentleman or lady, who has not pleasure in a good novel, must be intolerably stupid.”\"},{\"author\":\"Marilyn Monroe\",\"tags\":[\"be-yourself\",\"inspirational\"],\"text\":\"“Imperfection is beauty, madness is genius and it's better to be absolutely ridiculous than absolutely boring.”\"},{\"author\":\"Albert Einstein\",\"tags\":[\"adulthood\",\"success\",\"value\"],\"text\":\"“Try not to become a man of success. Rather become a man of value.”\"},{\"author\":\"André Gide\",\"tags\":[\"life\",\"love\"],\"text\":\"“It is better to be hated for what you are than to be loved for what you are not.”\"},{\"author\":\"Thomas A. Edison\",\"tags\":[\"edison\",\"failure\",\"inspirational\",\"paraphrased\"],\"text\":\"“I have not failed. I've just found 10,000 ways that won't work.”\"},{\"author\":\"Eleanor Roosevelt\",\"tags\":[\"misattributed-eleanor-roosevelt\"],\"text\":\"“A woman is like a tea bag; you never know how strong it is until it's in hot water.”\"},{\"author\":\"Steve Martin\",\"tags\":[\"humor\",\"obvious\",\"simile\"],\"text\":\"“A day without sunshine is like, you know, night.”\"}]"
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"has_refs": true,
|
||||
"has_link": true,
|
||||
"outline_len": 4017,
|
||||
"outline_preview": "RootWebArea \"The Internet\"\ngeneric\n generic\n separator\n generic\n StaticText \"Powered by \"\n InlineTextBox \"Powered by \"\n link \"Elemental Selenium\" #ref=169\n StaticText \"Elemental Selenium\"\n InlineTextBox \"Elemental \"\n InlineTextBox \"Selenium\"\nlink \"Fork me on GitHub\" #ref=74\n image \"Fork me on GitHub\"\ngeneric\n heading \"Welcome to the-internet\"\n StaticText \"Welcome to the-internet\"\n InlineTextBox \"Welcome to the-internet\"\n heading \"Available Examples\"\n StaticText \"Available Examples\"\n InlineTextBox \"Available Examples\"\n list\n listitem\n link \"A/B Testing\" #ref=81\n StaticText \"A/B Testing\"\n InlineTextBox \"A/B Testing\"\n listitem\n link \"Add/Remove Elements\" #ref=83\n StaticText \"Add/Remove Elements\"\n InlineTextBox \"Add/Remove Elements\"\n listitem\n link \"Basic Auth\" #ref=84\n StaticText \"Basic Auth\"\n InlineTextBox \"Basic Auth\"\n StaticText \" (user and pass: admin)\"\n InlineTextBox \" (user and pass: admin)\"\n listitem\n link \"Broken Images\" #ref=86\n StaticText \"Broken Images\"\n InlineTextBox \"Broken Images\"\n listitem\n link \"Challenging DOM\" #ref=88\n StaticText \"Challenging DOM\"\n InlineTextBox \"Challenging DOM\"\n listitem\n link \"Checkboxes\" #ref=90\n StaticText \"Checkboxes\"\n InlineTextBox \"Checkboxes\"\n listitem\n link \"Context Menu\" #ref=92\n StaticTex",
|
||||
"outline_preview": "RootWebArea \"The Internet\"\ngeneric\n generic\n separator\n generic\n StaticText \"Powered by \"\n InlineTextBox \"Powered by \"\n link \"Elemental Selenium\" #ref=169\n StaticText \"Elemental Selenium\"\n InlineTextBox \"Elemental \"\n InlineTextBox \"Selenium\"\nlink \"Fork me on GitHub\" #ref=71\n image \"Fork me on GitHub\"\ngeneric\n heading \"Welcome to the-internet\"\n StaticText \"Welcome to the-internet\"\n InlineTextBox \"Welcome to the-internet\"\n heading \"Available Examples\"\n StaticText \"Available Examples\"\n InlineTextBox \"Available Examples\"\n list\n listitem\n link \"A/B Testing\" #ref=78\n StaticText \"A/B Testing\"\n InlineTextBox \"A/B Testing\"\n listitem\n link \"Add/Remove Elements\" #ref=80\n StaticText \"Add/Remove Elements\"\n InlineTextBox \"Add/Remove Elements\"\n listitem\n link \"Basic Auth\" #ref=82\n StaticText \"Basic Auth\"\n InlineTextBox \"Basic Auth\"\n StaticText \" (user and pass: admin)\"\n InlineTextBox \" (user and pass: admin)\"\n listitem\n link \"Broken Images\" #ref=84\n StaticText \"Broken Images\"\n InlineTextBox \"Broken Images\"\n listitem\n link \"Challenging DOM\" #ref=86\n StaticText \"Challenging DOM\"\n InlineTextBox \"Challenging DOM\"\n listitem\n link \"Checkboxes\" #ref=88\n StaticText \"Checkboxes\"\n InlineTextBox \"Checkboxes\"\n listitem\n link \"Context Menu\" #ref=90\n StaticTex",
|
||||
"steps": [
|
||||
{
|
||||
"tool": "tab_navigate",
|
||||
@@ -12,7 +12,7 @@
|
||||
"port": 9333,
|
||||
"url": "https://the-internet.herokuapp.com"
|
||||
},
|
||||
"ms": 404,
|
||||
"ms": 378,
|
||||
"is_error": false,
|
||||
"response": "navigated to https://the-internet.herokuapp.com"
|
||||
},
|
||||
@@ -22,7 +22,7 @@
|
||||
"port": 9333,
|
||||
"timeout_ms": 12000
|
||||
},
|
||||
"ms": 241,
|
||||
"ms": 829,
|
||||
"is_error": false,
|
||||
"response": "page loaded"
|
||||
},
|
||||
@@ -32,9 +32,9 @@
|
||||
"port": 9333,
|
||||
"max_chars": 4000
|
||||
},
|
||||
"ms": 103,
|
||||
"ms": 104,
|
||||
"is_error": false,
|
||||
"response": "RootWebArea \"The Internet\"\ngeneric\n generic\n separator\n generic\n StaticText \"Powered by \"\n InlineTextBox \"Powered by \"\n link \"Elemental Selenium\" #ref=169\n StaticText \"Elemental Selenium\"\n InlineTextBox \"Elemental \"\n InlineTextBox \"Selenium\"\nlink \"Fork me on GitHub\" #ref=74\n image \"Fork me on GitHub\"\ngeneric\n heading \"Welcome to the-internet\"\n StaticText \"Welcome to the-internet\"\n InlineTextBox \"Welcome to the-internet\"\n heading \"Available Examples\"\n StaticText \"Available Examples\"\n InlineTextBox \"Available Examples\"\n list\n listitem\n link \"A/B Testing\" #ref=81\n StaticText \"A/B Testing\"\n InlineTextBox \"A/B Testing\"\n listitem\n link \"Add/Remove Elements\" #ref=83\n StaticText \"Add/Remove Elements\"\n InlineTextBox \"Add/Remove Elements\"\n listitem\n link \"Basic Auth\" #ref=84\n StaticText \"Basic Auth\"\n InlineTextBox \"Basic Auth\"\n StaticText \" (user and pass: admin)\"\n InlineTextBox \" (user and pass: admin)\"\n listitem\n link \"Broken Images\" #ref=86\n StaticText \"Broken Images\"\n InlineTextBox \"Broken Images\"\n listitem\n link \"Challenging DOM\" #ref=88\n StaticText \"Challenging DOM\"\n InlineTextBox \"Challenging DOM\"\n listitem\n link \"Checkboxes\" #ref=90\n StaticText \"Checkboxes\"\n InlineTextBox \"Checkboxes\"\n listitem\n link \"Context Menu\" #ref=92\n StaticText \"Context Menu\"\n InlineTextBox \"Context Menu\"\n listitem\n link \"Digest Authentication\" #ref=94\n StaticText \"Digest Authentication\"\n InlineTextBox \"Digest Authentication\"\n StaticText \" (user and pass: admin)\"\n InlineTextBox \" (user and pass: admin)\"\n listitem\n link \"Disappearing Elements\" #ref=96\n StaticText \"Disappearing Elements\"\n InlineTextBox \"Disappearing Elements\"\n listitem\n link \"Drag and Drop\" #ref=97\n "
|
||||
"response": "RootWebArea \"The Internet\"\ngeneric\n generic\n separator\n generic\n StaticText \"Powered by \"\n InlineTextBox \"Powered by \"\n link \"Elemental Selenium\" #ref=169\n StaticText \"Elemental Selenium\"\n InlineTextBox \"Elemental \"\n InlineTextBox \"Selenium\"\nlink \"Fork me on GitHub\" #ref=71\n image \"Fork me on GitHub\"\ngeneric\n heading \"Welcome to the-internet\"\n StaticText \"Welcome to the-internet\"\n InlineTextBox \"Welcome to the-internet\"\n heading \"Available Examples\"\n StaticText \"Available Examples\"\n InlineTextBox \"Available Examples\"\n list\n listitem\n link \"A/B Testing\" #ref=78\n StaticText \"A/B Testing\"\n InlineTextBox \"A/B Testing\"\n listitem\n link \"Add/Remove Elements\" #ref=80\n StaticText \"Add/Remove Elements\"\n InlineTextBox \"Add/Remove Elements\"\n listitem\n link \"Basic Auth\" #ref=82\n StaticText \"Basic Auth\"\n InlineTextBox \"Basic Auth\"\n StaticText \" (user and pass: admin)\"\n InlineTextBox \" (user and pass: admin)\"\n listitem\n link \"Broken Images\" #ref=84\n StaticText \"Broken Images\"\n InlineTextBox \"Broken Images\"\n listitem\n link \"Challenging DOM\" #ref=86\n StaticText \"Challenging DOM\"\n InlineTextBox \"Challenging DOM\"\n listitem\n link \"Checkboxes\" #ref=88\n StaticText \"Checkboxes\"\n InlineTextBox \"Checkboxes\"\n listitem\n link \"Context Menu\" #ref=90\n StaticText \"Context Menu\"\n InlineTextBox \"Context Menu\"\n listitem\n link \"Digest Authentication\" #ref=92\n StaticText \"Digest Authentication\"\n InlineTextBox \"Digest Authentication\"\n StaticText \" (user and pass: admin)\"\n InlineTextBox \" (user and pass: admin)\"\n listitem\n link \"Disappearing Elements\" #ref=94\n StaticText \"Disappearing Elements\"\n InlineTextBox \"Disappearing Elements\"\n listitem\n link \"Drag and Drop\" #ref=96\n "
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
"args": {
|
||||
"port": 9333
|
||||
},
|
||||
"ms": 7,
|
||||
"ms": 1,
|
||||
"is_error": false,
|
||||
"response": "cookies cleared"
|
||||
},
|
||||
@@ -18,7 +18,7 @@
|
||||
"port": 9333,
|
||||
"url": "https://the-internet.herokuapp.com/login"
|
||||
},
|
||||
"ms": 94,
|
||||
"ms": 93,
|
||||
"is_error": false,
|
||||
"response": "navigated to https://the-internet.herokuapp.com/login"
|
||||
},
|
||||
@@ -28,7 +28,7 @@
|
||||
"port": 9333,
|
||||
"timeout_ms": 12000
|
||||
},
|
||||
"ms": 233,
|
||||
"ms": 243,
|
||||
"is_error": false,
|
||||
"response": "page loaded"
|
||||
},
|
||||
@@ -38,7 +38,7 @@
|
||||
"port": 9333,
|
||||
"selector": "#username"
|
||||
},
|
||||
"ms": 17,
|
||||
"ms": 23,
|
||||
"is_error": false,
|
||||
"response": "clicked #username"
|
||||
},
|
||||
@@ -48,7 +48,7 @@
|
||||
"port": 9333,
|
||||
"text": "tomsmith"
|
||||
},
|
||||
"ms": 107,
|
||||
"ms": 114,
|
||||
"is_error": false,
|
||||
"response": "typed text"
|
||||
},
|
||||
@@ -58,7 +58,7 @@
|
||||
"port": 9333,
|
||||
"selector": "#password"
|
||||
},
|
||||
"ms": 6,
|
||||
"ms": 8,
|
||||
"is_error": false,
|
||||
"response": "clicked #password"
|
||||
},
|
||||
@@ -68,7 +68,7 @@
|
||||
"port": 9333,
|
||||
"text": "SuperSecretPassword!"
|
||||
},
|
||||
"ms": 265,
|
||||
"ms": 272,
|
||||
"is_error": false,
|
||||
"response": "typed text"
|
||||
},
|
||||
@@ -78,7 +78,7 @@
|
||||
"port": 9333,
|
||||
"key": "Enter"
|
||||
},
|
||||
"ms": 5,
|
||||
"ms": 6,
|
||||
"is_error": false,
|
||||
"response": "pressed Enter"
|
||||
},
|
||||
@@ -88,7 +88,7 @@
|
||||
"port": 9333,
|
||||
"timeout_ms": 12000
|
||||
},
|
||||
"ms": 2,
|
||||
"ms": 1,
|
||||
"is_error": false,
|
||||
"response": "page loaded"
|
||||
},
|
||||
@@ -99,7 +99,7 @@
|
||||
"selector": "#flash",
|
||||
"timeout_ms": 8000
|
||||
},
|
||||
"ms": 208,
|
||||
"ms": 420,
|
||||
"is_error": false,
|
||||
"response": "element appeared: #flash"
|
||||
},
|
||||
@@ -110,7 +110,7 @@
|
||||
"selector": "#flash",
|
||||
"max_bytes": 200
|
||||
},
|
||||
"ms": 0,
|
||||
"ms": 2,
|
||||
"is_error": false,
|
||||
"response": " You logged into a secure area!\n×"
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
"args": {
|
||||
"port": 9333
|
||||
},
|
||||
"ms": 1,
|
||||
"ms": 5,
|
||||
"is_error": false,
|
||||
"response": "cookies cleared"
|
||||
},
|
||||
@@ -23,7 +23,7 @@
|
||||
"port": 9333,
|
||||
"url": "https://the-internet.herokuapp.com/login"
|
||||
},
|
||||
"ms": 86,
|
||||
"ms": 98,
|
||||
"is_error": false,
|
||||
"response": "navigated to https://the-internet.herokuapp.com/login"
|
||||
},
|
||||
@@ -33,7 +33,7 @@
|
||||
"port": 9333,
|
||||
"timeout_ms": 12000
|
||||
},
|
||||
"ms": 222,
|
||||
"ms": 230,
|
||||
"is_error": false,
|
||||
"response": "page loaded"
|
||||
},
|
||||
@@ -43,7 +43,7 @@
|
||||
"port": 9333,
|
||||
"selector": "#username"
|
||||
},
|
||||
"ms": 13,
|
||||
"ms": 12,
|
||||
"is_error": false,
|
||||
"response": "clicked #username"
|
||||
},
|
||||
@@ -53,7 +53,7 @@
|
||||
"port": 9333,
|
||||
"text": "tomsmith"
|
||||
},
|
||||
"ms": 112,
|
||||
"ms": 105,
|
||||
"is_error": false,
|
||||
"response": "typed text"
|
||||
},
|
||||
@@ -63,7 +63,7 @@
|
||||
"port": 9333,
|
||||
"selector": "#password"
|
||||
},
|
||||
"ms": 5,
|
||||
"ms": 10,
|
||||
"is_error": false,
|
||||
"response": "clicked #password"
|
||||
},
|
||||
@@ -73,7 +73,7 @@
|
||||
"port": 9333,
|
||||
"text": "SuperSecretPassword!"
|
||||
},
|
||||
"ms": 271,
|
||||
"ms": 272,
|
||||
"is_error": false,
|
||||
"response": "typed text"
|
||||
},
|
||||
@@ -83,7 +83,7 @@
|
||||
"port": 9333,
|
||||
"selector": "button[type=submit]"
|
||||
},
|
||||
"ms": 9,
|
||||
"ms": 7,
|
||||
"is_error": false,
|
||||
"response": "clicked button[type=submit]"
|
||||
},
|
||||
@@ -93,7 +93,7 @@
|
||||
"port": 9333,
|
||||
"timeout_ms": 12000
|
||||
},
|
||||
"ms": 2,
|
||||
"ms": 1,
|
||||
"is_error": false,
|
||||
"response": "page loaded"
|
||||
},
|
||||
@@ -104,7 +104,7 @@
|
||||
"selector": "#flash",
|
||||
"timeout_ms": 8000
|
||||
},
|
||||
"ms": 431,
|
||||
"ms": 432,
|
||||
"is_error": false,
|
||||
"response": "element appeared: #flash"
|
||||
},
|
||||
@@ -115,7 +115,7 @@
|
||||
"selector": "#flash",
|
||||
"max_bytes": 300
|
||||
},
|
||||
"ms": 1,
|
||||
"ms": 2,
|
||||
"is_error": false,
|
||||
"response": " You logged into a secure area!\n×"
|
||||
},
|
||||
@@ -125,7 +125,7 @@
|
||||
"port": 9333,
|
||||
"path": "/tmp/demo_session.json"
|
||||
},
|
||||
"ms": 4,
|
||||
"ms": 5,
|
||||
"is_error": false,
|
||||
"response": "storage state saved to /tmp/demo_session.json"
|
||||
},
|
||||
@@ -134,7 +134,7 @@
|
||||
"args": {
|
||||
"port": 9333
|
||||
},
|
||||
"ms": 1,
|
||||
"ms": 2,
|
||||
"is_error": false,
|
||||
"response": "cookies cleared"
|
||||
},
|
||||
@@ -144,7 +144,7 @@
|
||||
"port": 9333,
|
||||
"url": "https://the-internet.herokuapp.com/secure"
|
||||
},
|
||||
"ms": 183,
|
||||
"ms": 185,
|
||||
"is_error": false,
|
||||
"response": "navigated to https://the-internet.herokuapp.com/secure"
|
||||
},
|
||||
@@ -154,7 +154,7 @@
|
||||
"port": 9333,
|
||||
"timeout_ms": 12000
|
||||
},
|
||||
"ms": 231,
|
||||
"ms": 228,
|
||||
"is_error": false,
|
||||
"response": "page loaded"
|
||||
},
|
||||
@@ -175,7 +175,7 @@
|
||||
"port": 9333,
|
||||
"url": "https://the-internet.herokuapp.com"
|
||||
},
|
||||
"ms": 102,
|
||||
"ms": 95,
|
||||
"is_error": false,
|
||||
"response": "navigated to https://the-internet.herokuapp.com"
|
||||
},
|
||||
@@ -185,7 +185,7 @@
|
||||
"port": 9333,
|
||||
"timeout_ms": 12000
|
||||
},
|
||||
"ms": 238,
|
||||
"ms": 254,
|
||||
"is_error": false,
|
||||
"response": "page loaded"
|
||||
},
|
||||
@@ -195,7 +195,7 @@
|
||||
"port": 9333,
|
||||
"path": "/tmp/demo_session.json"
|
||||
},
|
||||
"ms": 7,
|
||||
"ms": 6,
|
||||
"is_error": false,
|
||||
"response": "storage state loaded from /tmp/demo_session.json"
|
||||
},
|
||||
@@ -205,7 +205,7 @@
|
||||
"port": 9333,
|
||||
"url": "https://the-internet.herokuapp.com/secure"
|
||||
},
|
||||
"ms": 98,
|
||||
"ms": 105,
|
||||
"is_error": false,
|
||||
"response": "navigated to https://the-internet.herokuapp.com/secure"
|
||||
},
|
||||
@@ -225,7 +225,7 @@
|
||||
"port": 9333,
|
||||
"expression": "JSON.stringify({path:location.pathname,secure:document.body.innerText.includes('Secure Area')})"
|
||||
},
|
||||
"ms": 3,
|
||||
"ms": 2,
|
||||
"is_error": false,
|
||||
"response": "{\"path\":\"/secure\",\"secure\":true}"
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
"port": 9333,
|
||||
"url": "https://books.toscrape.com/catalogue/page-1.html"
|
||||
},
|
||||
"ms": 26,
|
||||
"ms": 190,
|
||||
"is_error": false,
|
||||
"response": "navigated to https://books.toscrape.com/catalogue/page-1.html"
|
||||
},
|
||||
@@ -37,7 +37,7 @@
|
||||
"port": 9333,
|
||||
"timeout_ms": 12000
|
||||
},
|
||||
"ms": 211,
|
||||
"ms": 872,
|
||||
"is_error": false,
|
||||
"response": "page loaded"
|
||||
},
|
||||
@@ -47,7 +47,7 @@
|
||||
"port": 9333,
|
||||
"expression": "[...document.querySelectorAll('.product_pod')].map(b=>({title:b.querySelector('h3 a').getAttribute('title'),price:b.querySelector('.price_color').innerText,stock:b.querySelector('.availability').innerText.trim()}))"
|
||||
},
|
||||
"ms": 5,
|
||||
"ms": 4,
|
||||
"is_error": false,
|
||||
"response": "[{\"price\":\"£51.77\",\"stock\":\"In stock\",\"title\":\"A Light in the Attic\"},{\"price\":\"£53.74\",\"stock\":\"In stock\",\"title\":\"Tipping the Velvet\"},{\"price\":\"£50.10\",\"stock\":\"In stock\",\"title\":\"Soumission\"},{\"price\":\"£47.82\",\"stock\":\"In stock\",\"title\":\"Sharp Objects\"},{\"price\":\"£54.23\",\"stock\":\"In stock\",\"title\":\"Sapiens: A Brief History of Humankind\"},{\"price\":\"£22.65\",\"stock\":\"In stock\",\"title\":\"The Requiem Red\"},{\"price\":\"£33.34\",\"stock\":\"In stock\",\"title\":\"The Dirty Little Secrets of Getting Your Dream Job\"},{\"price\":\"£17.93\",\"stock\":\"In stock\",\"title\":\"The Coming Woman: A Novel Based on the Life of the Infamous Feminist, Victoria Woodhull\"},{\"price\":\"£22.60\",\"stock\":\"In stock\",\"title\":\"The Boys in the Boat: Nine Americans and Their Epic Quest for Gold at the 1936 Berlin Olympics\"},{\"price\":\"£52.15\",\"stock\":\"In stock\",\"title\":\"The Black Maria\"},{\"price\":\"£13.99\",\"stock\":\"In stock\",\"title\":\"Starving Hearts (Triangular Trade Trilogy, #1)\"},{\"price\":\"£20.66\",\"stock\":\"In stock\",\"title\":\"Shakespeare's Sonnets\"},{\"price\":\"£17.46\",\"stock\":\"In stock\",\"title\":\"Set Me Free\"},{\"price\":\"£52.29\",\"stock\":\"In stock\",\"title\":\"Scott Pilgrim's Precious Little Life (Scott Pilgrim #1)\"},{\"price\":\"£35.02\",\"stock\":\"In stock\",\"title\":\"Rip it Up and Start Again\"},{\"price\":\"£57.25\",\"stock\":\"In stock\",\"title\":\"Our Band Could Be Your Life: Scenes from the American Indie Underground, 1981-1991\"},{\"price\":\"£23.88\",\"stock\":\"In stock\",\"title\":\"Olio\"},{\"price\":\"£37.59\",\"stock\":\"In stock\",\"title\":\"Mesaerion: The Best Science Fiction Stories 1800-1849\"},{\"price\":\"£51.33\",\"stock\":\"In stock\",\"title\":\"Libertarianism for Beginners\"},{\"price\":\"£45.17\",\"stock\":\"In stock\",\"title\":\"It's Only the Himalayas\"}]"
|
||||
},
|
||||
@@ -57,7 +57,7 @@
|
||||
"port": 9333,
|
||||
"url": "https://books.toscrape.com/catalogue/page-2.html"
|
||||
},
|
||||
"ms": 22,
|
||||
"ms": 108,
|
||||
"is_error": false,
|
||||
"response": "navigated to https://books.toscrape.com/catalogue/page-2.html"
|
||||
},
|
||||
@@ -67,7 +67,7 @@
|
||||
"port": 9333,
|
||||
"timeout_ms": 12000
|
||||
},
|
||||
"ms": 216,
|
||||
"ms": 643,
|
||||
"is_error": false,
|
||||
"response": "page loaded"
|
||||
},
|
||||
@@ -87,7 +87,7 @@
|
||||
"port": 9333,
|
||||
"url": "https://books.toscrape.com/catalogue/page-3.html"
|
||||
},
|
||||
"ms": 18,
|
||||
"ms": 106,
|
||||
"is_error": false,
|
||||
"response": "navigated to https://books.toscrape.com/catalogue/page-3.html"
|
||||
},
|
||||
@@ -97,7 +97,7 @@
|
||||
"port": 9333,
|
||||
"timeout_ms": 12000
|
||||
},
|
||||
"ms": 222,
|
||||
"ms": 420,
|
||||
"is_error": false,
|
||||
"response": "page loaded"
|
||||
},
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
"port": 9333,
|
||||
"url": "https://the-internet.herokuapp.com"
|
||||
},
|
||||
"ms": 112,
|
||||
"ms": 106,
|
||||
"is_error": false,
|
||||
"response": "navigated to https://the-internet.herokuapp.com"
|
||||
},
|
||||
@@ -21,7 +21,7 @@
|
||||
"port": 9333,
|
||||
"timeout_ms": 12000
|
||||
},
|
||||
"ms": 239,
|
||||
"ms": 229,
|
||||
"is_error": false,
|
||||
"response": "page loaded"
|
||||
},
|
||||
@@ -31,7 +31,7 @@
|
||||
"port": 9333,
|
||||
"expression": "window.sessionStorage.setItem('demo_k','demo_v'); 'set'"
|
||||
},
|
||||
"ms": 4,
|
||||
"ms": 3,
|
||||
"is_error": false,
|
||||
"response": "set"
|
||||
},
|
||||
@@ -41,7 +41,7 @@
|
||||
"port": 9333,
|
||||
"path": "/tmp/demo_ss.json"
|
||||
},
|
||||
"ms": 5,
|
||||
"ms": 6,
|
||||
"is_error": false,
|
||||
"response": "storage state saved to /tmp/demo_ss.json"
|
||||
},
|
||||
@@ -61,7 +61,7 @@
|
||||
"port": 9333,
|
||||
"expression": "String(window.sessionStorage.getItem('demo_k'))"
|
||||
},
|
||||
"ms": 1,
|
||||
"ms": 2,
|
||||
"is_error": false,
|
||||
"response": "null"
|
||||
},
|
||||
@@ -71,7 +71,7 @@
|
||||
"port": 9333,
|
||||
"path": "/tmp/demo_ss.json"
|
||||
},
|
||||
"ms": 4,
|
||||
"ms": 3,
|
||||
"is_error": false,
|
||||
"response": "storage state loaded from /tmp/demo_ss.json"
|
||||
},
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"port": 9333,
|
||||
"timeout_ms": 12000
|
||||
},
|
||||
"ms": 228,
|
||||
"ms": 215,
|
||||
"is_error": false,
|
||||
"response": "page loaded"
|
||||
},
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"port": 9333,
|
||||
"url": "https://quotes.toscrape.com"
|
||||
},
|
||||
"ms": 103,
|
||||
"ms": 105,
|
||||
"is_error": false,
|
||||
"response": "navigated to https://quotes.toscrape.com"
|
||||
},
|
||||
@@ -20,7 +20,7 @@
|
||||
"port": 9333,
|
||||
"timeout_ms": 12000
|
||||
},
|
||||
"ms": 223,
|
||||
"ms": 226,
|
||||
"is_error": false,
|
||||
"response": "page loaded"
|
||||
},
|
||||
@@ -30,7 +30,7 @@
|
||||
"port": 9333,
|
||||
"expression": "var b=document.createElement('button');b.id='hidden_btn';b.textContent='x';b.style.display='none';document.body.appendChild(b);'injected'"
|
||||
},
|
||||
"ms": 3,
|
||||
"ms": 1,
|
||||
"is_error": false,
|
||||
"response": "injected"
|
||||
},
|
||||
@@ -40,7 +40,7 @@
|
||||
"port": 9333,
|
||||
"selector": "#hidden_btn"
|
||||
},
|
||||
"ms": 2,
|
||||
"ms": 3,
|
||||
"is_error": true,
|
||||
"response": "cdp click: elemento \"#hidden_btn\" existe pero no es visible/clickable (display:none, oculto, opacity 0 o tamaño 0)"
|
||||
},
|
||||
@@ -50,7 +50,7 @@
|
||||
"port": 9333,
|
||||
"expression": "if(document.activeElement){document.activeElement.blur();} document.body.focus(); 'blurred'"
|
||||
},
|
||||
"ms": 1,
|
||||
"ms": 2,
|
||||
"is_error": false,
|
||||
"response": "blurred"
|
||||
},
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"port": 9333,
|
||||
"url": "https://the-internet.herokuapp.com/login"
|
||||
},
|
||||
"ms": 109,
|
||||
"ms": 110,
|
||||
"is_error": false,
|
||||
"response": "navigated to https://the-internet.herokuapp.com/login"
|
||||
},
|
||||
@@ -36,7 +36,7 @@
|
||||
"port": 9333,
|
||||
"timeout_ms": 12000
|
||||
},
|
||||
"ms": 244,
|
||||
"ms": 220,
|
||||
"is_error": false,
|
||||
"response": "page loaded"
|
||||
},
|
||||
@@ -46,7 +46,7 @@
|
||||
"port": 9333,
|
||||
"max_chars": 6000
|
||||
},
|
||||
"ms": 107,
|
||||
"ms": 106,
|
||||
"is_error": false,
|
||||
"response": "RootWebArea \"The Internet\"\ngeneric\n generic\n separator\n generic\n StaticText \"Powered by \"\n InlineTextBox \"Powered by \"\n link \"Elemental Selenium\" #ref=51\n StaticText \"Elemental Selenium\"\n InlineTextBox \"Elemental \"\n InlineTextBox \"Selenium\"\nlink \"Fork me on GitHub\" #ref=31\n image \"Fork me on GitHub\"\ngeneric\nheading \"Login Page\"\n StaticText \"Login Page\"\n InlineTextBox \"Login Page\"\nheading \"This is where you can log into the secure area. Enter tomsmith for the username and SuperSecretPassword! for the password. If the information is wrong you should see error messages.\"\n StaticText \"This is where you can log into the secure area. Enter \"\n InlineTextBox \"This is where you can log into the secure area. Enter \"\n emphasis\n StaticText \"tomsmith\"\n InlineTextBox \"tomsmith\"\n StaticText \" for the username and \"\n InlineTextBox \" for the \"\n InlineTextBox \"username and \"\n emphasis\n StaticText \"SuperSecretPassword!\"\n InlineTextBox \"SuperSecretPassword!\"\n StaticText \" for the password. If the information is wrong you should see error messages.\"\n InlineTextBox \" for the password. If the \"\n InlineTextBox \"information is wrong you should see error messages.\"\ngeneric\n button \" Login\" #ref=7\n generic\n StaticText \" Login\"\n InlineTextBox \" Login\"\ngeneric\n LabelText\n StaticText \"Username\"\n InlineTextBox \"Username\"\n textbox \"Username\" #ref=5\ngeneric\n LabelText\n StaticText \"Password\"\n InlineTextBox \"Password\"\n textbox \"Password\" #ref=6\nStaticText \"\"\n InlineTextBox \"\"\n"
|
||||
},
|
||||
@@ -57,7 +57,7 @@
|
||||
"ref": 5,
|
||||
"text": "tomsmith"
|
||||
},
|
||||
"ms": 618,
|
||||
"ms": 601,
|
||||
"is_error": false,
|
||||
"response": "typed into ref 5\n\nRootWebArea \"The Internet\"\ngeneric\n generic\n separator\n generic\n StaticText \"Powered by \"\n InlineTextBox \"Powered by \"\n link \"Elemental Selenium\" #ref=51\n StaticText \"Elemental Selenium\"\n InlineTextBox \"Elemental \"\n InlineTextBox \"Selenium\"\nlink \"Fork me on GitHub\" #ref=31\n image \"Fork me on GitHub\"\ngeneric\nheading \"Login Page\"\n StaticText \"Login Page\"\n InlineTextBox \"Login Page\"\nheading \"This is where you can log into the secure area. Enter tomsmith for the username and SuperSecretPassword! for the password. If the information is wrong you should see error messages.\"\n StaticText \"This is where you can log into the secure area. Enter \"\n InlineTextBox \"This is where you can log into the secure area. Enter \"\n emphasis\n StaticText \"tomsmith\"\n InlineTextBox \"tomsmith\"\n StaticText \" for the username and \"\n InlineTextBox \" for the \"\n InlineTextBox \"username and \"\n emphasis\n StaticText \"SuperSecretPassword!\"\n InlineTextBox \"SuperSecretPassword!\"\n StaticText \" for the password. If the information is wrong you should see error messages.\"\n InlineTextBox \" for the password. If the \"\n InlineTextBox \"information is wrong you should see error messages.\"\ngeneric\n button \" Login\" #ref=7\n generic\n StaticText \" Login\"\n InlineTextBox \" Login\"\ngeneric\n LabelText\n StaticText \"Username\"\n InlineTextBox \"Username\"\n textbox \"Username\" = 'tomsmith' #ref=5\n generic\n StaticText \"tomsmith\"\n InlineTextBox \"tomsmith\"\ngeneric\n LabelText\n StaticText \"Password\"\n InlineTextBox \"Password\"\n textbox \"Password\" #ref=6\nStaticText \"\"\n InlineTextBox \"\"\n"
|
||||
},
|
||||
@@ -78,7 +78,7 @@
|
||||
"port": 9333,
|
||||
"ref": 7
|
||||
},
|
||||
"ms": 1562,
|
||||
"ms": 1162,
|
||||
"is_error": false,
|
||||
"response": "clicked ref 7\n\nRootWebArea \"The Internet\"\ngeneric\n generic\n separator\n generic\n StaticText \"Powered by \"\n InlineTextBox \"Powered by \"\n link \"Elemental Selenium\" #ref=103\n StaticText \"Elemental Selenium\"\n InlineTextBox \"Elemental \"\n InlineTextBox \"Selenium\"\ngeneric\n StaticText \" You logged into a secure area!\"\n InlineTextBox \" You logged into a secure area!\"\n link \"×\" #ref=89\n StaticText \"×\"\n InlineTextBox \"×\"\nlink \"Fork me on GitHub\" #ref=91\n image \"Fork me on GitHub\"\ngeneric\nheading \"Secure Area\"\n StaticText \"Secure Area\"\n InlineTextBox \"Secure Area\"\nheading \"Welcome to the Secure Area. When you are done click logout below.\"\n StaticText \"Welcome to the Secure Area. When you are done click logout below.\"\n InlineTextBox \"Welcome to the Secure Area. When you are done click logout below.\"\nlink \"Logout\" #ref=97\n StaticText \"Logout\"\n InlineTextBox \"Logout\"\nStaticText \"\"\n InlineTextBox \"\"\n"
|
||||
},
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
"""E2E de las features nuevas (no parte de los 9 del demo principal):
|
||||
|
||||
- dom_find_ref_by_text (P0-3): encontrar por texto y clicar por #ref en the-internet.
|
||||
- handle_dialog + DialogLog (P1-4): auto-aceptar un confirm() y verificar Count/LastMessage
|
||||
reportado por browser_disconnect.
|
||||
|
||||
Requiere Chrome CDP en 9333 ya corriendo + binario browser_mcp compilado.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
|
||||
from mcp_client import MCPClient
|
||||
|
||||
ROOT = "/home/enmanuel/fn_registry"
|
||||
EXE = os.path.join(ROOT, "projects/web_scraping/apps/browser_mcp/browser_mcp")
|
||||
PORT = 9333
|
||||
|
||||
|
||||
def main():
|
||||
c = MCPClient(EXE, env={"FN_REGISTRY_ROOT": ROOT}, cwd=ROOT,
|
||||
stderr_path="/tmp/fn_newfeat_stderr.log")
|
||||
c.initialize()
|
||||
results = {}
|
||||
|
||||
# --- P0-3: find_ref_by_text + click_ref ---
|
||||
c.call("browser_connect", {"port": PORT})
|
||||
c.call("tab_navigate", {"port": PORT, "url": "https://the-internet.herokuapp.com/"})
|
||||
c.call("page_wait_load", {"port": PORT, "timeout_ms": 15000})
|
||||
find_txt, find_err = c.call("dom_find_ref_by_text",
|
||||
{"port": PORT, "text": "Form Authentication"})
|
||||
ref = None
|
||||
if not find_err and "ref=" in find_txt:
|
||||
ref = int(find_txt.split("ref=")[1].split()[0])
|
||||
after = ""
|
||||
if ref:
|
||||
after, _ = c.call("dom_click_ref", {"port": PORT, "ref": ref})
|
||||
landed = ("Login Page" in after) or ("Username" in after) or ("/login" in after.lower())
|
||||
results["find_ref_by_text"] = {
|
||||
"find_response": find_txt, "ref": ref,
|
||||
"landed_on_login": landed,
|
||||
"verdict": "PASS" if (ref and landed) else "FAIL",
|
||||
}
|
||||
|
||||
# --- P1-4: handle_dialog + DialogLog ---
|
||||
c.call("tab_navigate",
|
||||
{"port": PORT, "url": "https://the-internet.herokuapp.com/javascript_alerts"})
|
||||
c.call("page_wait_load", {"port": PORT, "timeout_ms": 15000})
|
||||
c.call("handle_dialog", {"port": PORT, "accept": True})
|
||||
# El botón "Click for JS Confirm" dispara confirm("I am a JS Confirm").
|
||||
c.call("dom_click_text", {"port": PORT, "text": "Click for JS Confirm"})
|
||||
result_text, _ = c.call("page_get_text", {"port": PORT, "selector": "#result"})
|
||||
# disconnect reporta el DialogLog (count + último mensaje).
|
||||
disc_msg, _ = c.call("browser_disconnect", {"port": PORT})
|
||||
accepted = "You clicked: Ok" in result_text
|
||||
log_reported = ("dialogs auto-handled: 1" in disc_msg) and ("I am a JS Confirm" in disc_msg)
|
||||
results["dialog_log"] = {
|
||||
"result_text": result_text.strip(),
|
||||
"disconnect_msg": disc_msg,
|
||||
"confirm_accepted": accepted,
|
||||
"log_reported": log_reported,
|
||||
"verdict": "PASS" if (accepted and log_reported) else "FAIL",
|
||||
}
|
||||
|
||||
c.close()
|
||||
print(json.dumps(results, ensure_ascii=False, indent=2))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user