331 lines
14 KiB
Python
331 lines
14 KiB
Python
"""
|
|
DOM Inspector - World-Class Debug Tool
|
|
|
|
Purpose: Inspect actual DOM structure at runtime to identify correct locators
|
|
Usage: Run this script to quickly identify element structures without running full tests
|
|
|
|
This saves time by:
|
|
1. Inspecting DOM directly without full test execution
|
|
2. Testing multiple locator strategies quickly
|
|
3. Identifying correct selectors before updating code
|
|
4. Verifying element presence and visibility
|
|
"""
|
|
import sys
|
|
import os
|
|
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
|
|
|
from selenium import webdriver
|
|
from selenium.webdriver.chrome.service import Service
|
|
from selenium.webdriver.chrome.options import Options
|
|
from selenium.webdriver.common.by import By
|
|
from selenium.webdriver.support.ui import WebDriverWait
|
|
from selenium.webdriver.support import expected_conditions as EC
|
|
from pages.login_page import LoginPage
|
|
from pages.mandatory_reset_page import MandatoryResetPage
|
|
from config.config import TEST_USERNAME, TEST_PASSWORD, BASE_URL
|
|
import time
|
|
import json
|
|
|
|
|
|
class DOMInspector:
|
|
"""World-Class DOM Inspector for Quick Debugging"""
|
|
|
|
def __init__(self, driver):
|
|
self.driver = driver
|
|
|
|
def find_elements_by_strategies(self, element_name, strategies):
|
|
"""
|
|
Try multiple locator strategies and report results
|
|
|
|
Args:
|
|
element_name: Name of element for logging
|
|
strategies: List of (strategy_name, locator) tuples
|
|
"""
|
|
print(f"\n{'='*80}")
|
|
print(f"🔍 INSPECTING: {element_name}")
|
|
print(f"{'='*80}")
|
|
|
|
results = []
|
|
for strategy_name, locator in strategies:
|
|
try:
|
|
if isinstance(locator, tuple):
|
|
by_type, selector = locator
|
|
else:
|
|
by_type, selector = locator[0], locator[1]
|
|
|
|
print(f"\n📋 Strategy: {strategy_name}")
|
|
print(f" Locator: {by_type} = '{selector}'")
|
|
|
|
# Try to find element
|
|
elements = self.driver.find_elements(by_type, selector)
|
|
|
|
if elements:
|
|
print(f" ✅ Found {len(elements)} element(s)")
|
|
|
|
for idx, elem in enumerate(elements):
|
|
try:
|
|
is_displayed = elem.is_displayed()
|
|
is_enabled = elem.is_enabled() if hasattr(elem, 'is_enabled') else 'N/A'
|
|
tag_name = elem.tag_name
|
|
text = elem.text[:100] if elem.text else 'No text'
|
|
|
|
# Get attributes
|
|
attrs = {}
|
|
try:
|
|
attrs['id'] = elem.get_attribute('id')
|
|
attrs['class'] = elem.get_attribute('class')
|
|
attrs['data-testid'] = elem.get_attribute('data-testid')
|
|
attrs['name'] = elem.get_attribute('name')
|
|
attrs['type'] = elem.get_attribute('type')
|
|
except:
|
|
pass
|
|
|
|
print(f" Element {idx + 1}:")
|
|
print(f" - Tag: {tag_name}")
|
|
print(f" - Displayed: {is_displayed}")
|
|
print(f" - Enabled: {is_enabled}")
|
|
print(f" - Text: {text}")
|
|
print(f" - Attributes: {json.dumps({k: v for k, v in attrs.items() if v}, indent=8)}")
|
|
|
|
# Get outer HTML snippet
|
|
try:
|
|
outer_html = elem.get_attribute('outerHTML')
|
|
if outer_html:
|
|
snippet = outer_html[:200] + '...' if len(outer_html) > 200 else outer_html
|
|
print(f" - HTML: {snippet}")
|
|
except:
|
|
pass
|
|
|
|
results.append({
|
|
'strategy': strategy_name,
|
|
'locator': selector,
|
|
'found': True,
|
|
'displayed': is_displayed,
|
|
'element': elem,
|
|
'attributes': attrs
|
|
})
|
|
except Exception as e:
|
|
print(f" ⚠️ Error inspecting element {idx + 1}: {e}")
|
|
else:
|
|
print(f" ❌ No elements found")
|
|
results.append({
|
|
'strategy': strategy_name,
|
|
'locator': selector,
|
|
'found': False
|
|
})
|
|
except Exception as e:
|
|
print(f" ❌ Error with strategy: {e}")
|
|
results.append({
|
|
'strategy': strategy_name,
|
|
'locator': selector,
|
|
'found': False,
|
|
'error': str(e)
|
|
})
|
|
|
|
return results
|
|
|
|
def inspect_modal_structure(self):
|
|
"""Inspect password reset modal structure"""
|
|
print(f"\n{'='*80}")
|
|
print("🔍 INSPECTING: Password Reset Modal Structure")
|
|
print(f"{'='*80}")
|
|
|
|
# Check for modal overlay
|
|
print("\n📋 Checking for modal overlay...")
|
|
overlay_selectors = [
|
|
("CSS - Exact", (By.CSS_SELECTOR, "div.fixed.inset-0.bg-black\\/60.z-\\[99999\\]")),
|
|
("CSS - Partial", (By.CSS_SELECTOR, "div[class*='fixed'][class*='inset-0'][class*='bg-black'][class*='z-[99999]']")),
|
|
("CSS - Generic", (By.CSS_SELECTOR, "div.fixed.inset-0.bg-black")),
|
|
("XPath - Fixed inset", (By.XPATH, "//div[contains(@class, 'fixed') and contains(@class, 'inset-0')]")),
|
|
]
|
|
|
|
overlay_results = self.find_elements_by_strategies("Modal Overlay", overlay_selectors)
|
|
|
|
# Check for modal content
|
|
print("\n📋 Checking for modal content...")
|
|
content_selectors = [
|
|
("Text - Welcome", (By.XPATH, "//*[contains(text(), 'Welcome to Cognitive Prism')]")),
|
|
("Text - Reset Password", (By.XPATH, "//*[contains(text(), 'Password Reset Required')]")),
|
|
("Text - Secure Account", (By.XPATH, "//*[contains(text(), 'Secure Your Account')]")),
|
|
]
|
|
|
|
content_results = self.find_elements_by_strategies("Modal Content", content_selectors)
|
|
|
|
return overlay_results, content_results
|
|
|
|
def inspect_continue_button(self):
|
|
"""Inspect Continue button structure"""
|
|
print(f"\n{'='*80}")
|
|
print("🔍 INSPECTING: Continue Button")
|
|
print(f"{'='*80}")
|
|
|
|
strategies = [
|
|
("data-testid", (By.CSS_SELECTOR, "[data-testid='mandatory_reset__continue_button']")),
|
|
("XPath - Span text", (By.XPATH, "//span[contains(text(), 'Continue to Reset Password')]")),
|
|
("XPath - Button with span", (By.XPATH, "//button[.//span[contains(text(), 'Continue')]]")),
|
|
("XPath - Button contains Continue", (By.XPATH, "//button[contains(., 'Continue')]")),
|
|
("XPath - Button with Reset", (By.XPATH, "//button[contains(., 'Reset Password')]")),
|
|
("XPath - Any button in modal", (By.XPATH, "//div[contains(@class, 'fixed')]//button")),
|
|
("XPath - Button with arrow icon", (By.XPATH, "//button[.//*[local-name()='svg']]")),
|
|
]
|
|
|
|
return self.find_elements_by_strategies("Continue Button", strategies)
|
|
|
|
def inspect_form_fields(self):
|
|
"""Inspect password reset form fields"""
|
|
print(f"\n{'='*80}")
|
|
print("🔍 INSPECTING: Password Reset Form Fields")
|
|
print(f"{'='*80}")
|
|
|
|
strategies = [
|
|
("Form container", (By.CSS_SELECTOR, "[data-testid='mandatory_reset__form']")),
|
|
("Current password input", (By.CSS_SELECTOR, "[data-testid='mandatory_reset__current_password_input']")),
|
|
("New password input", (By.CSS_SELECTOR, "[data-testid='mandatory_reset__new_password_input']")),
|
|
("Confirm password input", (By.CSS_SELECTOR, "[data-testid='mandatory_reset__confirm_password_input']")),
|
|
("Submit button", (By.CSS_SELECTOR, "[data-testid='mandatory_reset__submit_button']")),
|
|
("XPath - Input by name", (By.XPATH, "//input[@name='currentPassword' or @name='newPassword' or @name='confirmPassword']")),
|
|
("XPath - Input type password", (By.XPATH, "//input[@type='password']")),
|
|
]
|
|
|
|
return self.find_elements_by_strategies("Form Fields", strategies)
|
|
|
|
def get_page_source_snippet(self, search_text, context_lines=5):
|
|
"""Get snippet of page source around specific text"""
|
|
try:
|
|
page_source = self.driver.page_source
|
|
if search_text.lower() in page_source.lower():
|
|
index = page_source.lower().index(search_text.lower())
|
|
start = max(0, index - 500)
|
|
end = min(len(page_source), index + 500)
|
|
snippet = page_source[start:end]
|
|
return snippet
|
|
return None
|
|
except:
|
|
return None
|
|
|
|
def inspect_page_source(self):
|
|
"""Inspect page source for key indicators"""
|
|
print(f"\n{'='*80}")
|
|
print("🔍 INSPECTING: Page Source Indicators")
|
|
print(f"{'='*80}")
|
|
|
|
indicators = [
|
|
"Welcome to Cognitive Prism",
|
|
"Password Reset Required",
|
|
"Continue to Reset Password",
|
|
"mandatory_reset",
|
|
"data-testid",
|
|
]
|
|
|
|
page_source = self.driver.page_source.lower()
|
|
|
|
for indicator in indicators:
|
|
found = indicator.lower() in page_source
|
|
status = "✅" if found else "❌"
|
|
print(f"{status} '{indicator}': {'Found' if found else 'Not found'}")
|
|
|
|
if found:
|
|
snippet = self.get_page_source_snippet(indicator)
|
|
if snippet:
|
|
print(f" Context: {snippet[:300]}...")
|
|
|
|
|
|
def debug_password_reset_modal():
|
|
"""Main debug function for password reset modal"""
|
|
print("=" * 80)
|
|
print("🔍 DOM INSPECTOR - Password Reset Modal Debug")
|
|
print("=" * 80)
|
|
print("\n🎯 Purpose: Inspect actual DOM structure to identify correct locators")
|
|
print("💡 This saves time by avoiding full test execution\n")
|
|
|
|
# Setup Chrome driver
|
|
chrome_options = Options()
|
|
chrome_options.add_argument("--start-maximized")
|
|
chrome_options.add_argument("--disable-blink-features=AutomationControlled")
|
|
chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])
|
|
chrome_options.add_experimental_option('useAutomationExtension', False)
|
|
|
|
driver = webdriver.Chrome(options=chrome_options)
|
|
inspector = DOMInspector(driver)
|
|
|
|
try:
|
|
# Step 1: Login
|
|
print(f"\n{'='*80}")
|
|
print("STEP 1: LOGIN")
|
|
print(f"{'='*80}")
|
|
login_page = LoginPage(driver)
|
|
login_page.login(identifier=TEST_USERNAME, password=TEST_PASSWORD)
|
|
print(f"✅ Login completed")
|
|
print(f" Current URL: {driver.current_url}")
|
|
time.sleep(2)
|
|
|
|
# Step 2: Inspect modal structure
|
|
print(f"\n{'='*80}")
|
|
print("STEP 2: INSPECT MODAL STRUCTURE")
|
|
print(f"{'='*80}")
|
|
overlay_results, content_results = inspector.inspect_modal_structure()
|
|
|
|
# Step 3: Inspect Continue button
|
|
print(f"\n{'='*80}")
|
|
print("STEP 3: INSPECT CONTINUE BUTTON")
|
|
print(f"{'='*80}")
|
|
continue_results = inspector.inspect_continue_button()
|
|
|
|
# Step 4: Inspect form fields (if form is visible)
|
|
print(f"\n{'='*80}")
|
|
print("STEP 4: INSPECT FORM FIELDS")
|
|
print(f"{'='*80}")
|
|
form_results = inspector.inspect_form_fields()
|
|
|
|
# Step 5: Inspect page source
|
|
inspector.inspect_page_source()
|
|
|
|
# Step 6: Summary and recommendations
|
|
print(f"\n{'='*80}")
|
|
print("📊 SUMMARY & RECOMMENDATIONS")
|
|
print(f"{'='*80}")
|
|
|
|
# Find working strategies
|
|
working_overlay = [r for r in overlay_results if r.get('found') and r.get('displayed')]
|
|
working_continue = [r for r in continue_results if r.get('found') and r.get('displayed')]
|
|
working_form = [r for r in form_results if r.get('found') and r.get('displayed')]
|
|
|
|
print(f"\n✅ Working Strategies Found:")
|
|
if working_overlay:
|
|
print(f" Modal Overlay: {working_overlay[0]['strategy']} - {working_overlay[0]['locator']}")
|
|
if working_continue:
|
|
print(f" Continue Button: {working_continue[0]['strategy']} - {working_continue[0]['locator']}")
|
|
if working_form:
|
|
print(f" Form Fields: {working_form[0]['strategy']} - {working_form[0]['locator']}")
|
|
|
|
if not working_continue:
|
|
print(f"\n⚠️ Continue Button Not Found!")
|
|
print(f" Recommendations:")
|
|
print(f" 1. Check if button is inside a shadow DOM")
|
|
print(f" 2. Check if button has animation delays")
|
|
print(f" 3. Try JavaScript-based element finding")
|
|
print(f" 4. Check if button is inside a specific container")
|
|
|
|
print(f"\n⏸️ Browser will stay open for 30 seconds for manual inspection...")
|
|
print(f" You can inspect the DOM manually in browser DevTools")
|
|
time.sleep(30)
|
|
|
|
except Exception as e:
|
|
print(f"\n❌ ERROR: {e}")
|
|
import traceback
|
|
traceback.print_exc()
|
|
print(f"\n⏸️ Browser will stay open for 30 seconds for manual inspection...")
|
|
time.sleep(30)
|
|
finally:
|
|
print(f"\n{'='*80}")
|
|
print("CLEANUP")
|
|
print(f"{'='*80}")
|
|
driver.quit()
|
|
print("✅ Driver closed")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
debug_password_reset_modal()
|
|
|
|
|