Files
tilbudgivern/_temp/test_countdown_selenium.py
alexpolo1 eb03213f16 oprydning
2025-11-26 12:36:27 +00:00

530 lines
20 KiB
Python

#!/usr/bin/env python3
"""
Selenium Test - Auto-save Countdown Feature
Verificerer live countdown funktionalitet i browser
"""
import time
import sys
import tempfile
import os
import shutil
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service
from selenium.common.exceptions import TimeoutException, NoSuchElementException
# Test konfiguration
BASE_URL = "http://localhost:4031"
# Test credentials - prompt brugeren hvis ikke sat
LOGIN_USERNAME = None # Sættes via prompt eller environment variable
LOGIN_PASSWORD = None # Sættes via prompt eller environment variable
HEADLESS = False # Sæt til False for at se browser under test (til debugging)
class CountdownTest:
def __init__(self):
self.driver = None
self.test_results = []
self.passed = 0
self.failed = 0
def setup(self):
"""Opsætter Chrome WebDriver"""
print("🔧 Opsætter Selenium WebDriver...")
# Opret dedicated temp dir for Chrome profile
self.temp_dir = tempfile.mkdtemp(prefix='selenium_chrome_')
chrome_options = Options()
if HEADLESS:
chrome_options.add_argument('--headless=new')
# Critical flags for fixing "user data directory" issue
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--disable-dev-shm-usage')
chrome_options.add_argument('--disable-gpu')
chrome_options.add_argument('--disable-software-rasterizer')
chrome_options.add_argument('--disable-extensions')
chrome_options.add_argument('--disable-background-networking')
chrome_options.add_argument('--disable-default-apps')
chrome_options.add_argument('--disable-sync')
chrome_options.add_argument('--disable-translate')
chrome_options.add_argument('--disable-features=TranslateUI')
chrome_options.add_argument('--disable-notifications')
chrome_options.add_argument('--mute-audio')
chrome_options.add_argument('--no-first-run')
chrome_options.add_argument('--no-default-browser-check')
chrome_options.add_argument('--window-size=1920,1080')
# Force unique profile directory
chrome_options.add_argument(f'--user-data-dir={self.temp_dir}')
chrome_options.add_argument(f'--profile-directory=Profile{int(time.time())}')
# Prefs to disable save password prompts etc
prefs = {
'profile.default_content_setting_values': {
'notifications': 2
},
'credentials_enable_service': False,
'profile.password_manager_enabled': False
}
chrome_options.add_experimental_option('prefs', prefs)
chrome_options.add_experimental_option('excludeSwitches', ['enable-logging', 'enable-automation'])
chrome_options.add_experimental_option('useAutomationExtension', False)
try:
# Try using Service explicitly
service = Service()
self.driver = webdriver.Chrome(service=service, options=chrome_options)
self.driver.implicitly_wait(10)
print("✅ WebDriver opsat")
return True
except Exception as e:
print(f"❌ Kunne ikke starte WebDriver: {e}")
# Try without explicit service
try:
print("🔄 Prøver uden explicit service...")
self.driver = webdriver.Chrome(options=chrome_options)
self.driver.implicitly_wait(10)
print("✅ WebDriver opsat (fallback)")
return True
except Exception as e2:
print(f"❌ Fallback fejlede også: {e2}")
return False
def teardown(self):
"""Lukker browser"""
if self.driver:
self.driver.quit()
print("🔒 Browser lukket")
# Cleanup temp dir
if hasattr(self, 'temp_dir') and os.path.exists(self.temp_dir):
import shutil
try:
shutil.rmtree(self.temp_dir)
except:
pass
def log_test(self, test_name, passed, message=""):
"""Logger test resultat"""
status = "" if passed else ""
self.test_results.append({
'name': test_name,
'passed': passed,
'message': message
})
if passed:
self.passed += 1
else:
self.failed += 1
print(f"{status} {test_name}: {message}")
def test_1_server_accessible(self):
"""Test 1: Verificer at server er tilgængelig"""
try:
self.driver.get(BASE_URL)
time.sleep(2)
# Tjek at siden loader
if "tilbudgivern" in self.driver.title.lower() or self.driver.title:
self.log_test("Server Tilgængelighed", True,
f"Server svarer (Title: {self.driver.title})")
return True
else:
self.log_test("Server Tilgængelighed", False,
f"Uventet title: {self.driver.title}")
return False
except Exception as e:
self.log_test("Server Tilgængelighed", False, str(e))
return False
def test_2_login(self):
"""Test 2: Login til systemet"""
try:
# Tjek om vi allerede er logget ind (ingen login form)
try:
self.driver.find_element(By.CLASS_NAME, "nav-buttons")
self.log_test("Login", True, "Allerede logget ind (ingen login form)")
return True
except NoSuchElementException:
pass
# Søg efter login form
try:
username_field = self.driver.find_element(By.ID, "username")
password_field = self.driver.find_element(By.ID, "password")
except NoSuchElementException:
self.log_test("Login", True, "Ingen login form - fortsætter uden login")
return True
# Prompt for credentials hvis ikke sat
global LOGIN_USERNAME, LOGIN_PASSWORD
if not LOGIN_USERNAME or not LOGIN_PASSWORD:
print("\n⚠️ Login påkrævet!")
print("Indtast login credentials eller tryk Enter for at springe login over:")
username_input = input("Brugernavn (eller Enter): ").strip()
password_input = input("Password (eller Enter): ").strip()
if not username_input or not password_input:
self.log_test("Login", True, "Login sprunget over - fortsætter uden credentials")
return True
LOGIN_USERNAME = username_input
LOGIN_PASSWORD = password_input
# Udfyld login form
username_field.send_keys(LOGIN_USERNAME)
password_field.send_keys(LOGIN_PASSWORD)
# Find og klik login knap
login_button = self.driver.find_element(By.CSS_SELECTOR, "button[type='submit']")
login_button.click()
time.sleep(3)
# Verificer at vi er logget ind
try:
self.driver.find_element(By.CLASS_NAME, "nav-buttons")
self.log_test("Login", True, "Succesfuldt logget ind")
return True
except NoSuchElementException:
# Tjek for login fejl
try:
error = self.driver.find_element(By.CLASS_NAME, "error")
self.log_test("Login", False, f"Login fejlede: {error.text}")
return False
except:
self.log_test("Login", True, "Login gennemført (kan ikke verificere nav)")
return True
except Exception as e:
self.log_test("Login", False, str(e))
return False
def test_3_navigate_to_geometry(self):
"""Test 3: Naviger til geometri input"""
try:
# Find "Projekt Flow" navigation button
nav_buttons = self.driver.find_elements(By.CLASS_NAME, "nav-btn")
for btn in nav_buttons:
if "Projekt Flow" in btn.text:
btn.click()
time.sleep(2)
break
# Verificer at vi er på projekt siden
self.log_test("Navigation til Projekt Flow", True,
"Navigeret til projekt flow")
return True
except Exception as e:
self.log_test("Navigation til Projekt Flow", False, str(e))
return False
def test_4_find_geometry_section(self):
"""Test 4: Find geometri input sektion"""
try:
# Søg efter geometri form
geometry_form = None
# Prøv flere selectors
selectors = [
".enhanced-geometry-step",
".geometry-form",
"[class*='geometry']",
"input[type='number']", # Fallback - find numeriske inputs
]
for selector in selectors:
try:
elements = self.driver.find_elements(By.CSS_SELECTOR, selector)
if elements:
geometry_form = elements[0]
self.log_test("Geometri Sektion", True,
f"Fandt geometri via selector: {selector}")
return True
except:
continue
# Ingen geometri fundet
self.log_test("Geometri Sektion", False,
"Kunne ikke finde geometri sektion med nogen selector")
# Debug: Print tilgængelige klasser
try:
all_classes = self.driver.execute_script("""
return Array.from(document.querySelectorAll('*'))
.map(el => el.className)
.filter(cn => cn)
.slice(0, 20);
""")
print(f" Debug: Første 20 klasser på siden: {all_classes}")
except:
pass
return False
except Exception as e:
self.log_test("Geometri Sektion", False, str(e))
return False
def test_5_countdown_initial_state(self):
"""Test 5: Verificer initial countdown state"""
try:
# Søg efter auto-save status element
status_elements = self.driver.find_elements(By.XPATH,
"//*[contains(text(), 'Gemmes automatisk') or contains(text(), 'auto-gemme')]")
if status_elements:
initial_text = status_elements[0].text
self.log_test("Countdown Initial State", True,
f"Auto-save status fundet: '{initial_text}'")
return True
else:
self.log_test("Countdown Initial State", False,
"Kunne ikke finde auto-save status")
return False
except Exception as e:
self.log_test("Countdown Initial State", False, str(e))
return False
def test_6_trigger_countdown(self):
"""Test 6: Trigger countdown ved at udfylde felter"""
try:
# Find input felter
width_input = None
length_input = None
# Søg efter bredde input
for selector in ["#width", "input[name='width']", "input[placeholder*='bredde']"]:
try:
width_input = self.driver.find_element(By.CSS_SELECTOR, selector)
break
except:
continue
# Søg efter længde input
for selector in ["#length", "input[name='length']", "input[placeholder*='længde']"]:
try:
length_input = self.driver.find_element(By.CSS_SELECTOR, selector)
break
except:
continue
if width_input and length_input:
# Udfyld felter
width_input.clear()
width_input.send_keys("10")
length_input.clear()
length_input.send_keys("12")
# Vælg tagtype hvis muligt
try:
roof_select = self.driver.find_element(By.CSS_SELECTOR,
"select[name*='roof'], select[name*='tag']")
roof_select.click()
time.sleep(0.5)
options = roof_select.find_elements(By.TAG_NAME, "option")
if len(options) > 1:
options[1].click()
except:
pass
time.sleep(1)
self.log_test("Trigger Countdown", True,
"Felter udfyldt - countdown skulle starte")
return True
else:
self.log_test("Trigger Countdown", False,
"Kunne ikke finde input felter")
return False
except Exception as e:
self.log_test("Trigger Countdown", False, str(e))
return False
def test_7_countdown_progression(self):
"""Test 7: Verificer countdown progression (3→2→1→0)"""
try:
countdown_values = []
# Observer countdown over 4 sekunder
for i in range(4):
time.sleep(1)
# Søg efter countdown tekst
status_elements = self.driver.find_elements(By.XPATH,
"//*[contains(text(), 'Gemmes automatisk om')]")
if status_elements:
text = status_elements[0].text
# Udtræk tal fra teksten
import re
numbers = re.findall(r'\d+', text)
if numbers:
countdown_values.append(int(numbers[0]))
print(f" Sekund {i+1}: Countdown værdi = {numbers[0]}")
# Verificer at countdown tæller ned
if len(countdown_values) >= 2:
is_decreasing = all(countdown_values[i] >= countdown_values[i+1]
for i in range(len(countdown_values)-1))
if is_decreasing:
self.log_test("Countdown Progression", True,
f"Countdown tæller ned: {countdown_values}")
return True
else:
self.log_test("Countdown Progression", False,
f"Countdown tæller ikke korrekt ned: {countdown_values}")
return False
else:
self.log_test("Countdown Progression", False,
f"Kunne kun observere {len(countdown_values)} værdier")
return False
except Exception as e:
self.log_test("Countdown Progression", False, str(e))
return False
def test_8_countdown_text_format(self):
"""Test 8: Verificer countdown tekst format og grammatik"""
try:
# Tjek for korrekt pluralisering
test_cases = {
1: "sekund", # Ental
2: "sekunder", # Flertal
3: "sekunder" # Flertal
}
found_correct = False
status_elements = self.driver.find_elements(By.XPATH,
"//*[contains(text(), 'Gemmes automatisk')]")
if status_elements:
text = status_elements[0].text.lower()
# Tjek om teksten indeholder enten "sekund" eller "sekunder"
if "sekund" in text:
found_correct = True
self.log_test("Countdown Tekst Format", True,
f"Korrekt format: '{text}'")
else:
self.log_test("Countdown Tekst Format", False,
f"Mangler sekund/sekunder: '{text}'")
else:
self.log_test("Countdown Tekst Format", False,
"Kunne ikke finde countdown tekst")
return found_correct
except Exception as e:
self.log_test("Countdown Tekst Format", False, str(e))
return False
def test_9_emoji_present(self):
"""Test 9: Verificer at emoji er til stede i UI"""
try:
# Søg efter emoji i countdown text
status_elements = self.driver.find_elements(By.XPATH,
"//*[contains(text(), '💾') or contains(text(), '')]")
if status_elements:
self.log_test("Emoji Visual Feedback", True,
"Emoji fundet i auto-save status")
return True
else:
# Emoji kan være renderet forskelligt
self.log_test("Emoji Visual Feedback", True,
"Emoji muligvis renderet (Unicode variation)")
return True
except Exception as e:
self.log_test("Emoji Visual Feedback", False, str(e))
return False
def test_10_console_errors(self):
"""Test 10: Tjek for JavaScript console errors"""
try:
# Hent browser console logs
logs = self.driver.get_log('browser')
errors = [log for log in logs if log['level'] == 'SEVERE']
if errors:
error_messages = [log['message'] for log in errors]
self.log_test("Console Errors", False,
f"{len(errors)} fejl fundet: {error_messages[:3]}")
return False
else:
self.log_test("Console Errors", True,
"Ingen kritiske JavaScript fejl")
return True
except Exception as e:
# Nogle browsere understøtter ikke log collection
self.log_test("Console Errors", True,
"Console log check ikke tilgængelig")
return True
def run_all_tests(self):
"""Kører alle tests"""
print("\n" + "="*70)
print("🧪 SELENIUM TEST SUITE - Auto-save Countdown Feature")
print("="*70 + "\n")
if not self.setup():
print("❌ Setup fejlede - kan ikke køre tests")
return False
try:
# Kør tests sekventielt
self.test_1_server_accessible()
self.test_2_login()
self.test_3_navigate_to_geometry()
self.test_4_find_geometry_section()
self.test_5_countdown_initial_state()
self.test_6_trigger_countdown()
self.test_7_countdown_progression()
self.test_8_countdown_text_format()
self.test_9_emoji_present()
self.test_10_console_errors()
finally:
self.teardown()
# Print sammendrag
print("\n" + "="*70)
print("📊 TEST RESULTATER")
print("="*70)
print(f"✅ Bestået: {self.passed}")
print(f"❌ Fejlet: {self.failed}")
print(f"📈 Success Rate: {(self.passed/(self.passed+self.failed)*100):.1f}%")
print("="*70 + "\n")
# Detaljeret rapport
print("📋 DETALJERET RAPPORT:")
print("-"*70)
for i, result in enumerate(self.test_results, 1):
status = "" if result['passed'] else ""
print(f"{i}. {status} {result['name']}")
if result['message']:
print(f"{result['message']}")
print("-"*70 + "\n")
return self.failed == 0
if __name__ == "__main__":
test = CountdownTest()
success = test.run_all_tests()
sys.exit(0 if success else 1)