GUÍA DE LOCALIZADORES DINÁMICOS - HAKALAB FRAMEWORK
====================================================

CONTENIDO
=========
1. Introducción
2. Conceptos Básicos
3. Sintaxis de Placeholders
4. Ejemplos Prácticos
5. Casos de Uso Avanzados
6. Manejo de Caracteres Especiales
7. Mejores Prácticas
8. Solución de Problemas


1. INTRODUCCIÓN
===============

Los localizadores dinámicos permiten crear selectores reutilizables que se adaptan a valores que cambian en tiempo de ejecución. En lugar de crear múltiples localizadores para cada variación, defines un localizador con placeholders que se reemplazan dinámicamente.

¿Por qué usar localizadores dinámicos?

- Reutilización: Un localizador para múltiples valores
- Mantenibilidad: Cambios centralizados en el JSON
- Flexibilidad: Valores dinámicos desde features o variables
- Escalabilidad: Menos código duplicado

Ejemplo Comparativo

Sin localizadores dinámicos (Mucho código):
{
  "resultado_hola": "//p[text()='Hola']",
  "resultado_adios": "//p[text()='Adios']",
  "resultado_buenos_dias": "//p[text()='Buenos días']",
  "resultado_buenas_noches": "//p[text()='Buenas noches']"
}

Con localizadores dinámicos (Un solo localizador):
{
  "resultado_por_texto": "//p[text()='<valor>']"
}


2. CONCEPTOS BÁSICOS
====================

Estructura de un Localizador Dinámico

Un localizador dinámico es un selector que contiene placeholders que se reemplazan en tiempo de ejecución:

Localizador: //p[text()='<valor>']
             └─────────────────┘
             Placeholder que será reemplazado

Cómo Funciona

1. Definir en JSON: Crear un localizador con placeholder
2. Usar en Feature: Pasar el valor dinámico en el step
3. Reemplazar: El framework reemplaza el placeholder con el valor
4. Ejecutar: Se usa el localizador final para encontrar el elemento


3. SINTAXIS DE PLACEHOLDERS
===========================

Formatos Soportados

El framework soporta múltiples formatos de placeholder para mayor flexibilidad:

<valor>           # Formato 1: Ángulos con "valor"
<value>           # Formato 2: Ángulos con "value" (inglés)
<placeholder>     # Formato 3: Ángulos con "placeholder"
{valor}           # Formato 4: Llaves con "valor"
{value}           # Formato 5: Llaves con "value"
{placeholder}     # Formato 6: Llaves con "placeholder"

Ejemplos de Placeholders

{
  "resultado_por_texto": "//p[text()='<valor>']",
  "boton_por_nombre": "//button[contains(text(), '<value>')]",
  "input_por_placeholder": "//input[@placeholder='{placeholder}']",
  "elemento_por_id": "//div[@id='{valor}']",
  "link_por_href": "//a[@href='<value>']"
}


4. EJEMPLOS PRÁCTICOS
=====================

Ejemplo 1: Búsqueda de Resultados por Texto

JSON (json_poms/SEARCH.json):
{
  "resultado_por_texto": "//div[@class='resultado'][contains(text(), '<valor>')]"
}

Feature:
Scenario: Buscar múltiples resultados
  Given navego a "https://example.com/search"
  When busco "Python"
  Then hago click en "$.SEARCH.resultado_por_texto=Python"
  
  When busco "JavaScript"
  Then hago click en "$.SEARCH.resultado_por_texto=JavaScript"
  
  When busco "Ruby"
  Then hago click en "$.SEARCH.resultado_por_texto=Ruby"

Step (ejemplo):
@when('hago click en "{selector}"')
def click_elemento(context, selector):
    element = _get_element(context, selector)
    element.click()


Ejemplo 2: Validación de Mensajes

JSON (json_poms/MESSAGES.json):
{
  "mensaje_por_texto": "//div[@class='message'][text()='<valor>']",
  "alerta_por_tipo": "//div[@class='alert alert-{valor}']"
}

Feature:
Scenario: Validar diferentes mensajes
  Given navego a "https://example.com"
  Then debería ver "$.MESSAGES.mensaje_por_texto=Operación exitosa"
  And debería ver "$.MESSAGES.mensaje_por_texto=Datos guardados"
  And debería ver "$.MESSAGES.alerta_por_tipo=success"
  And debería ver "$.MESSAGES.alerta_por_tipo=warning"


Ejemplo 3: Interacción con Tablas

JSON (json_poms/TABLES.json):
{
  "celda_por_contenido": "//table//td[text()='<valor>']",
  "fila_por_nombre": "//tr[td[text()='<valor>']]",
  "boton_en_fila": "//tr[td[text()='<valor>']]//button[@class='edit']"
}

Feature:
Scenario: Editar registros en tabla
  Given navego a "https://example.com/usuarios"
  When hago click en "$.TABLES.boton_en_fila=Juan Pérez"
  And escribo "Juan Carlos" en "input[name='nombre']"
  And hago click en "button[type='submit']"
  Then debería ver "$.TABLES.celda_por_contenido=Juan Carlos"


Ejemplo 4: Navegación por Menús

JSON (json_poms/MENU.json):
{
  "opcion_menu": "//nav//a[text()='<valor>']",
  "submenu": "//nav//li[contains(text(), '<valor>')]//ul//li"
}

Feature:
Scenario: Navegar por menú dinámico
  Given navego a "https://example.com"
  When hago click en "$.MENU.opcion_menu=Productos"
  And hago click en "$.MENU.opcion_menu=Servicios"
  And hago click en "$.MENU.opcion_menu=Contacto"
  Then debería estar en "https://example.com/contacto"


5. CASOS DE USO AVANZADOS
==========================

Caso 1: Múltiples Placeholders en un Localizador

{
  "celda_tabla": "//table[@id='<tabla>']//tr[td[text()='<fila>']]//td[text()='<columna>']"
}

Uso:
When hago click en "$.TABLES.celda_tabla=usuarios|Juan|Email"

Nota: Actualmente se soporta un placeholder por localizador. Para múltiples, considera usar variables.


Caso 2: Combinación con Variables

Scenario: Usar variables con localizadores dinámicos
  Given creo variable "nombre_usuario" con valor "Juan Pérez"
  When hago click en "$.TABLES.boton_en_fila=${nombre_usuario}"
  Then debería ver "Registro editado"


Caso 3: Localizadores Condicionales

{
  "elemento_por_estado": "//div[@data-status='<valor>']",
  "boton_por_accion": "//button[@data-action='<valor>']"
}

Feature:
Scenario: Acciones condicionales
  Given navego a "https://example.com/dashboard"
  When hago click en "$.ELEMENTS.boton_por_accion=editar"
  And espero 2 segundos
  When hago click en "$.ELEMENTS.boton_por_accion=guardar"
  Then debería ver "$.ELEMENTS.elemento_por_estado=success"


6. MANEJO DE CARACTERES ESPECIALES
===================================

Caracteres Especiales en XPath

El framework maneja automáticamente caracteres especiales en valores dinámicos:

Comillas Simples (')

JSON:
{
  "elemento": "//p[text()='<valor>']"
}

Uso:
When hago click en "$.SEARCH.elemento=O'Brien"
Se convierte a: //p[text()="O'Brien"]


Comillas Dobles (")

JSON:
{
  "elemento": "//p[text()='<valor>']"
}

Uso:
When hago click en "$.SEARCH.elemento=Dijo \"Hola\""
Se convierte a: //p[text()='Dijo "Hola"']


Comillas Simples y Dobles

JSON:
{
  "elemento": "//p[text()='<valor>']"
}

Uso:
When hago click en "$.SEARCH.elemento=O'Brien dijo \"Hola\""
Se convierte a: //p[text()=concat('O', "'", 'Brien dijo "Hola"')]


Caracteres Especiales en CSS

JSON:
{
  "elemento_css": "div[data-name='<valor>']"
}

Uso:
When hago click en "$.SEARCH.elemento_css=test@example.com"
Funciona correctamente con caracteres especiales


7. MEJORES PRÁCTICAS
====================

1. Nomenclatura Clara

Bien: Nombre descriptivo
{
  "resultado_por_texto": "//div[@class='resultado'][text()='<valor>']"
}

Mal: Nombre genérico
{
  "elemento": "//div[text()='<valor>']"
}


2. Documentar Placeholders

{
  "resultado_por_texto": "//div[@class='resultado'][text()='<valor>']",
  "_comentario": "Busca un resultado por su texto. Placeholder: <valor> = texto del resultado"
}


3. Usar Selectores Específicos

Bien: Selector específico
{
  "boton_por_nombre": "//button[@class='action-button'][text()='<valor>']"
}

Mal: Selector muy genérico
{
  "boton": "//button[text()='<valor>']"
}


4. Validar Valores en Features

Scenario: Validar entrada antes de usar
  Given creo variable "nombre_usuario" con valor "Juan Pérez"
  When el variable "nombre_usuario" no esté vacía
  Then hago click en "$.TABLES.boton_en_fila=${nombre_usuario}"


5. Usar Constantes para Valores Comunes

Scenario: Usar valores predefinidos
  Given creo variable "estado_activo" con valor "Activo"
  And creo variable "estado_inactivo" con valor "Inactivo"
  When hago click en "$.ELEMENTS.elemento_por_estado=${estado_activo}"
  Then debería ver "$.ELEMENTS.elemento_por_estado=${estado_inactivo}"


8. SOLUCIÓN DE PROBLEMAS
=========================

Error: "Placeholder no reemplazado"

Síntoma: El placeholder <valor> aparece en el XPath final

Causa: El valor dinámico no se pasó correctamente

Solución:
Incorrecto - Sin =:
When hago click en "$.SEARCH.resultado_por_texto"

Correcto - Con =:
When hago click en "$.SEARCH.resultado_por_texto=Python"


Error: "Elemento no encontrado"

Síntoma: El elemento no se encuentra aunque el placeholder se reemplazó

Causa: El valor dinámico no coincide exactamente con el texto en la página

Solución:
Verificar el valor exacto en la página:
When tomo screenshot "debug_elemento"

Usar contains() en lugar de text()=:
JSON: "//div[contains(text(), '<valor>')]"
When hago click en "$.SEARCH.resultado_por_texto=Python"


Error: "Caracteres especiales causan error"

Síntoma: Error al usar comillas o caracteres especiales

Causa: El framework no escapó correctamente los caracteres

Solución: El framework maneja esto automáticamente, pero si persiste:
Given creo variable "valor_especial" con valor "O'Brien"
When hago click en "$.SEARCH.elemento=${valor_especial}"


Error: "Múltiples elementos encontrados"

Síntoma: El localizador es demasiado genérico

Solución: Hacer el selector más específico

Muy genérico:
{
  "elemento": "//div[text()='<valor>']"
}

Más específico:
{
  "elemento": "//div[@class='resultado'][text()='<valor>']"
}


REFERENCIA RÁPIDA
=================

Sintaxis Básica

Identificador: $.ARCHIVO.elemento=valor_dinamico
               └─────────────────┘ └──────────────┘
               Localizador JSON   Valor dinámico


Formatos de Placeholder

Formato          Ejemplo                          Uso
<valor>          //p[text()='<valor>']            Recomendado
<value>          //p[text()='<value>']            Alternativa inglés
<placeholder>    //p[text()='<placeholder>']      Descriptivo
{valor}          //p[text()='{valor}']            Alternativa
{value}          //p[text()='{value}']            Alternativa
{placeholder}    //p[text()='{placeholder}']      Alternativa


Ejemplos de Uso

Básico:
When hago click en "$.SEARCH.resultado_por_texto=Python"

Con variables:
When hago click en "$.SEARCH.resultado_por_texto=${mi_variable}"

Con espacios:
When hago click en "$.SEARCH.resultado_por_texto=Python Programming"

Con caracteres especiales:
When hago click en "$.SEARCH.resultado_por_texto=O'Brien"


CONCLUSIÓN
==========

Los localizadores dinámicos son una herramienta poderosa para crear tests más mantenibles y reutilizables. Úsalos cuando:

- Necesites el mismo localizador con diferentes valores
- Quieras reducir duplicación en tu JSON
- Busques mayor flexibilidad en tus tests
- Necesites valores que cambien en tiempo de ejecución

¡Ahora puedes crear tests más eficientes y mantenibles!
