Skip to content

Commit cced398

Browse files
committed
cambia metodo select_first_available_time_resilient
1 parent 376ed34 commit cced398

File tree

1 file changed

+86
-240
lines changed

1 file changed

+86
-240
lines changed

pages/fake_cinema/cinema_home_page.py

Lines changed: 86 additions & 240 deletions
Original file line numberDiff line numberDiff line change
@@ -368,289 +368,135 @@ def select_first_available_time(self):
368368

369369
def select_first_available_time_resilient(self, max_attempts=3):
370370
"""
371-
Versión con debugging extremo para diagnosticar problemas en CI/CD.
372-
Captura estado completo de la página en cada intento.
371+
Versión mejorada que maneja el caso cuando no hay horarios disponibles.
372+
Si no encuentra horarios, intenta con otras fechas automáticamente.
373373
374374
:param max_attempts: Número de intentos (default: 3)
375375
:return: El texto de la hora seleccionada
376376
"""
377377
import os
378378
import time
379379

380-
# Timeout más largo en CI/CD
381380
timeout = 30 if os.getenv('CI') else 20
382381

383-
logger.debug("=" * 60)
384-
logger.debug("🔍 INICIO DEBUG - select_first_available_time_resilient")
385-
logger.debug(f"Ambiente: {'CI/CD' if os.getenv('CI') else 'LOCAL'}")
386-
logger.debug(f"Timeout configurado: {timeout}s")
387-
logger.debug("=" * 60)
388-
389382
for attempt in range(max_attempts):
390383
try:
391-
logger.debug(f"\n{'=' * 60}")
392-
logger.debug(f"[RETRY] 🔄 Intento {attempt + 1}/{max_attempts}")
393-
logger.debug(f"{'=' * 60}")
394-
395-
# ========== PASO 1: Verificar URL actual ==========
396-
current_url = self.driver.current_url
397-
logger.debug(f"📍 URL actual: {current_url}")
384+
logger.debug(f"[RETRY] Intento {attempt + 1}/{max_attempts} de seleccionar horario")
398385

399-
# ========== PASO 2: Esperar que la página termine de cargar ==========
400-
logger.debug("⏳ Esperando document.readyState = 'complete'...")
386+
# Esperar que la página termine de cargar
401387
WebDriverWait(self.driver, 10).until(
402388
lambda d: d.execute_script('return document.readyState') == 'complete'
403389
)
404-
logger.debug("✅ document.readyState = 'complete'")
405390

406-
# ========== PASO 3: Verificar estado de fecha seleccionada ==========
407-
try:
408-
selected_date = self.driver.execute_script("""
409-
const selected = document.querySelector('button[class*="selected"], button[aria-pressed="true"]');
410-
return selected ? selected.innerText : 'No date selected';
411-
""")
412-
logger.debug(f"📅 Fecha seleccionada: {selected_date}")
413-
except Exception as e:
414-
logger.warning(f"⚠️ No se pudo verificar fecha seleccionada: {e}")
415-
416-
# ========== PASO 4: Buscar contenedor de horarios ==========
417-
logger.debug("🔍 Buscando contenedor de horarios en el DOM...")
418-
try:
419-
time_container_info = self.driver.execute_script("""
420-
const selectors = [
421-
'[class*="time"]',
422-
'[class*="schedule"]',
423-
'[class*="hour"]',
424-
'section',
425-
'[data-times]',
426-
'[data-schedule]'
427-
];
428-
429-
for (let selector of selectors) {
430-
const container = document.querySelector(selector);
431-
if (container) {
432-
return {
433-
selector: selector,
434-
found: true,
435-
innerHTML_length: container.innerHTML.length,
436-
innerHTML_preview: container.innerHTML.substring(0, 300),
437-
visible: container.offsetParent !== null
438-
};
439-
}
440-
}
441-
return { found: false };
442-
""")
443-
444-
if time_container_info.get('found'):
445-
logger.debug(f"✅ Contenedor encontrado con selector: {time_container_info['selector']}")
446-
logger.debug(f" - Visible: {time_container_info['visible']}")
447-
logger.debug(f" - Longitud HTML: {time_container_info['innerHTML_length']} chars")
448-
logger.debug(f" - Preview: {time_container_info['innerHTML_preview'][:150]}...")
449-
else:
450-
logger.warning("⚠️ NO se encontró contenedor de horarios en el DOM")
451-
except Exception as e:
452-
logger.error(f"❌ Error al buscar contenedor: {e}")
453-
454-
# ========== PASO 5: Verificar loaders activos ==========
455-
logger.debug("🔍 Verificando loaders activos...")
456-
try:
457-
loaders = self.driver.execute_script("""
458-
const loadingSelectors = [
459-
'.loading', '.spinner', '.loader',
460-
'[class*="load"]', '[class*="spinner"]',
461-
'[data-loading]', '[data-loading="true"]'
462-
];
463-
464-
let activeLoaders = [];
465-
for (let selector of loadingSelectors) {
466-
const elements = document.querySelectorAll(selector);
467-
if (elements.length > 0) {
468-
activeLoaders.push({
469-
selector: selector,
470-
count: elements.length
471-
});
472-
}
473-
}
474-
return activeLoaders;
475-
""")
476-
477-
if loaders and len(loaders) > 0:
478-
logger.warning(f"⚠️ Se encontraron {len(loaders)} tipos de loaders activos:")
479-
for loader in loaders:
480-
logger.warning(f" - {loader['selector']}: {loader['count']} elementos")
481-
else:
482-
logger.debug("✅ No hay loaders activos")
483-
except Exception as e:
484-
logger.error(f"❌ Error al verificar loaders: {e}")
485-
486-
# ========== PASO 6: Intentar eliminar loaders ==========
391+
# Scroll y espera
392+
self.driver.execute_script("window.scrollTo(0, 500);")
393+
time.sleep(2)
394+
395+
# Intentar eliminar loaders
487396
try:
488-
logger.debug("🧹 Intentando eliminar loaders...")
489397
WebDriverWait(self.driver, 5).until_not(
490398
EC.presence_of_element_located(
491399
(By.CSS_SELECTOR, ".loading, .spinner, [class*='load'], [class*='spinner']"))
492400
)
493-
logger.debug("✅ Loaders eliminados o no presentes")
494401
except:
495-
logger.debug("⏭️ Timeout esperando eliminación de loaders (continuando...)")
496-
497-
# ========== PASO 7: Scroll al área de horarios ==========
498-
logger.debug("📜 Ejecutando scroll...")
499-
self.driver.execute_script("window.scrollTo(0, 500);")
500-
time.sleep(2)
501-
logger.debug("✅ Scroll ejecutado")
402+
pass
502403

503-
# ========== PASO 8: Contar botones de horario ANTES del wait ==========
504-
logger.debug("🔍 Contando botones de horario ANTES del WebDriverWait...")
505-
try:
506-
button_count = self.driver.execute_script("""
507-
const buttons = document.querySelectorAll('button');
508-
const timeButtons = Array.from(buttons).filter(btn => {
509-
const text = btn.innerText || btn.textContent;
510-
return text.includes(':') && (text.includes('AM') || text.includes('PM'));
511-
});
512-
513-
return {
514-
total_buttons: buttons.length,
515-
time_buttons: timeButtons.length,
516-
sample_buttons: timeButtons.slice(0, 3).map(b => ({
517-
text: b.innerText,
518-
visible: b.offsetParent !== null,
519-
enabled: !b.disabled,
520-
classes: b.className
521-
}))
522-
};
523-
""")
524-
525-
logger.debug(f"📊 Botones en página:")
526-
logger.debug(f" - Total de botones: {button_count['total_buttons']}")
527-
logger.debug(f" - Botones de horario: {button_count['time_buttons']}")
528-
529-
if button_count['time_buttons'] > 0:
530-
logger.debug(f" - Muestra de botones encontrados:")
531-
for i, btn in enumerate(button_count['sample_buttons'], 1):
532-
logger.debug(
533-
f" {i}. '{btn['text']}' - Visible: {btn['visible']}, Enabled: {btn['enabled']}")
534-
else:
535-
logger.warning("⚠️ NO se encontraron botones de horario en el DOM aún")
536-
537-
except Exception as e:
538-
logger.error(f"❌ Error al contar botones: {e}")
539-
540-
# ========== PASO 9: WebDriverWait con logging ==========
541-
logger.debug(f"⏳ Iniciando WebDriverWait ({timeout}s) para botones de horario...")
542-
start_wait = time.time()
543-
544-
time_buttons = WebDriverWait(self.driver, timeout).until(
545-
EC.presence_of_all_elements_located((
546-
By.XPATH,
547-
"//button[contains(text(), ':') and (contains(text(), 'AM') or contains(text(), 'PM'))]"
548-
))
404+
# NUEVO: Verificar si hay botones de horario ANTES del wait largo
405+
time_buttons = self.driver.find_elements(
406+
By.XPATH,
407+
"//button[contains(text(), ':') and (contains(text(), 'AM') or contains(text(), 'PM'))]"
549408
)
550409

551-
elapsed_wait = time.time() - start_wait
552-
logger.debug(f"✅ WebDriverWait completado en {elapsed_wait:.2f}s")
410+
# Si NO hay botones, intentar con otra fecha
411+
if len(time_buttons) == 0:
412+
logger.warning(f"⚠️ No hay horarios para la fecha actual, probando otra fecha...")
553413

554-
# ========== PASO 10: Validar botones encontrados ==========
555-
if not time_buttons:
556-
logger.error("❌ time_buttons está vacío después del wait")
557-
raise Exception("No se encontraron horarios disponibles (lista vacía)")
414+
# Obtener todas las fechas disponibles
415+
date_buttons = self.driver.find_elements(
416+
By.XPATH,
417+
"//button[contains(@class, 'date') or @data-date or contains(text(), '/')]"
418+
)
558419

559-
logger.debug(f"✅ Se encontraron {len(time_buttons)} botones de horario")
420+
if len(date_buttons) == 0:
421+
# Si no hay botones de fecha, buscar por cualquier botón clickeable
422+
date_buttons = self.driver.find_elements(
423+
By.XPATH,
424+
"//section//button[not(contains(text(), ':'))]"
425+
)
560426

561-
# ========== PASO 11: Intentar click en primer botón ==========
562-
for idx, button in enumerate(time_buttons):
563-
try:
564-
is_displayed = button.is_displayed()
565-
is_enabled = button.is_enabled()
427+
logger.debug(f" Fechas disponibles: {len(date_buttons)}")
566428

567-
logger.debug(f"🔍 Botón {idx + 1}: displayed={is_displayed}, enabled={is_enabled}")
429+
# Probar con las siguientes 3 fechas
430+
for idx, date_btn in enumerate(date_buttons[1:4], 1):
431+
try:
432+
logger.debug(f" Probando fecha #{idx}...")
568433

569-
if is_displayed and is_enabled:
570-
# Scroll al elemento
571-
self.driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", button)
434+
# Scroll al botón de fecha
435+
self.driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", date_btn)
572436
time.sleep(0.5)
573437

574-
button_text = button.text.strip()
575-
logger.debug(f"🎯 Intentando click en: '{button_text}'")
576-
577-
# Click con JavaScript como fallback
438+
# Click en la fecha
578439
try:
579-
button.click()
580-
logger.debug("✅ Click con .click() exitoso")
440+
date_btn.click()
581441
except:
582-
self.driver.execute_script("arguments[0].click();", button)
583-
logger.debug("✅ Click con JavaScript exitoso")
442+
self.driver.execute_script("arguments[0].click();", date_btn)
443+
444+
time.sleep(3) # Esperar que carguen los horarios
445+
446+
# Verificar si ahora hay horarios
447+
time_buttons = self.driver.find_elements(
448+
By.XPATH,
449+
"//button[contains(text(), ':') and (contains(text(), 'AM') or contains(text(), 'PM'))]"
450+
)
451+
452+
if len(time_buttons) > 0:
453+
logger.info(f"✅ Fecha #{idx} tiene {len(time_buttons)} horarios disponibles")
454+
break
455+
else:
456+
logger.debug(f" Fecha #{idx} sin horarios, siguiente...")
457+
458+
except Exception as e:
459+
logger.debug(f" Error con fecha #{idx}: {e}")
460+
continue
461+
462+
# Si TODAVÍA no hay botones después de probar fechas, usar el wait original
463+
if len(time_buttons) == 0:
464+
logger.debug("Esperando horarios con WebDriverWait...")
465+
time_buttons = WebDriverWait(self.driver, timeout).until(
466+
EC.presence_of_all_elements_located((
467+
By.XPATH,
468+
"//button[contains(text(), ':') and (contains(text(), 'AM') or contains(text(), 'PM'))]"
469+
))
470+
)
584471

585-
logger.info(f"✅ Horario '{button_text}' seleccionado en intento {attempt + 1}")
586-
logger.debug("=" * 60)
587-
logger.debug("🎉 FIN DEBUG - Selección exitosa")
588-
logger.debug("=" * 60)
589-
return button_text
472+
# Validar que hay botones
473+
if not time_buttons:
474+
raise Exception("No se encontraron horarios disponibles.")
590475

591-
except Exception as e:
592-
logger.warning(f"⚠️ Error al procesar botón {idx + 1}: {type(e).__name__} - {str(e)}")
593-
continue
476+
# Seleccionar primer botón habilitado
477+
for button in time_buttons:
478+
if button.is_displayed() and button.is_enabled():
479+
self.driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", button)
480+
time.sleep(0.5)
594481

595-
raise Exception("No se encontró ningún botón de hora habilitado.")
482+
button_text = button.text.strip()
596483

597-
except Exception as e:
598-
error_type = type(e).__name__
599-
error_msg = str(e)
484+
try:
485+
button.click()
486+
except:
487+
self.driver.execute_script("arguments[0].click();", button)
600488

601-
logger.warning(f"\n{'=' * 60}")
602-
logger.warning(f"⚠️ INTENTO {attempt + 1} FALLÓ")
603-
logger.warning(f"{'=' * 60}")
604-
logger.warning(f"Error: {error_type}")
605-
logger.warning(f"Mensaje: {error_msg[:200]}")
489+
logger.info(f"✅ Horario '{button_text}' seleccionado en intento {attempt + 1}")
490+
return button_text
606491

607-
# ========== CAPTURA ADICIONAL EN FALLO ==========
492+
raise Exception("No se encontró ningún botón de hora habilitado.")
493+
494+
except Exception as e:
495+
logger.warning(f"⚠️ Intento {attempt + 1} falló: {type(e).__name__}")
608496
if attempt < max_attempts - 1:
609-
try:
610-
# Capturar screenshot path (solo en CI)
611-
if os.getenv('CI'):
612-
logger.debug("📸 Screenshot disponible en artifacts del workflow")
613-
614-
# Verificar mensajes de error en la página
615-
logger.debug("🔍 Buscando mensajes de error en la página...")
616-
error_elements = self.driver.execute_script("""
617-
const errorTexts = [];
618-
const errorSelectors = ['[class*="error"]', '[class*="alert"]', '[role="alert"]'];
619-
620-
for (let selector of errorSelectors) {
621-
const elements = document.querySelectorAll(selector);
622-
elements.forEach(el => {
623-
if (el.innerText.trim()) {
624-
errorTexts.push(el.innerText.trim().substring(0, 100));
625-
}
626-
});
627-
}
628-
return errorTexts;
629-
""")
630-
631-
if error_elements:
632-
logger.warning(f"⚠️ Mensajes de error en página:")
633-
for msg in error_elements[:3]:
634-
logger.warning(f" - {msg}")
635-
636-
# Capturar HTML del body para análisis
637-
body_html = self.driver.execute_script("""
638-
return document.body.innerHTML.substring(0, 500);
639-
""")
640-
logger.debug(f"📄 HTML Body preview: {body_html[:200]}...")
641-
642-
except Exception as debug_error:
643-
logger.error(f"❌ Error en captura de debug: {debug_error}")
644-
645-
logger.debug(f"⏳ Esperando 3s antes del reintento...")
646497
time.sleep(3)
647498
else:
648-
logger.error("\n" + "=" * 60)
649-
logger.error("❌ TODOS LOS INTENTOS FALLARON")
650-
logger.error("=" * 60)
651-
logger.error(f"Error final: {error_type}")
652-
logger.error(f"Mensaje: {error_msg}")
653-
logger.error("=" * 60)
499+
logger.error("❌ Todos los intentos fallaron")
654500
raise
655501

656502
def is_seat_grid_displayed(self):

0 commit comments

Comments
 (0)