forked from Rockachopa/Timmy-time-dashboard
test: remove hardcoded sleeps, add pytest-timeout (#69)
- Replace fixed time.sleep() calls with intelligent polling or WebDriverWait - Add pytest-timeout dependency and --timeout=30 to prevent hangs - Fixes test flakiness and improves test suite speed Co-authored-by: Alexander Payne <apayne@MM.local>
This commit is contained in:
committed by
GitHub
parent
bf0e388d2a
commit
51140fb7f0
@@ -4,7 +4,6 @@ RUN: SELENIUM_UI=1 pytest tests/functional/test_fast_e2e.py -v
|
||||
"""
|
||||
|
||||
import os
|
||||
import time
|
||||
|
||||
import pytest
|
||||
import httpx
|
||||
@@ -31,7 +30,7 @@ def driver():
|
||||
opts.add_argument("--disable-dev-shm-usage")
|
||||
opts.add_argument("--disable-gpu")
|
||||
opts.add_argument("--window-size=1280,900")
|
||||
|
||||
|
||||
d = webdriver.Chrome(options=opts)
|
||||
d.implicitly_wait(2) # Reduced from 5s
|
||||
yield d
|
||||
@@ -52,7 +51,7 @@ def dashboard_url():
|
||||
|
||||
class TestAllPagesLoad:
|
||||
"""Single test that checks all pages load - much faster than separate tests."""
|
||||
|
||||
|
||||
def test_all_dashboard_pages_exist(self, driver, dashboard_url):
|
||||
"""Verify all new feature pages load successfully in one browser session."""
|
||||
pages = [
|
||||
@@ -63,9 +62,9 @@ class TestAllPagesLoad:
|
||||
("/self-modify/queue", "Upgrade"),
|
||||
("/swarm/live", "Swarm"), # Live page has "Swarm" not "Live"
|
||||
]
|
||||
|
||||
|
||||
failures = []
|
||||
|
||||
|
||||
for path, expected_text in pages:
|
||||
try:
|
||||
driver.get(f"{dashboard_url}{path}")
|
||||
@@ -73,55 +72,63 @@ class TestAllPagesLoad:
|
||||
WebDriverWait(driver, 3).until(
|
||||
EC.presence_of_element_located((By.TAG_NAME, "body"))
|
||||
)
|
||||
|
||||
|
||||
# Verify page has expected content
|
||||
body_text = driver.find_element(By.TAG_NAME, "body").text
|
||||
if expected_text.lower() not in body_text.lower():
|
||||
failures.append(f"{path}: missing '{expected_text}'")
|
||||
|
||||
|
||||
except Exception as exc:
|
||||
failures.append(f"{path}: {type(exc).__name__}")
|
||||
|
||||
|
||||
if failures:
|
||||
pytest.fail(f"Pages failed to load: {', '.join(failures)}")
|
||||
|
||||
|
||||
class TestAllFeaturesWork:
|
||||
"""Combined functional tests - single browser session."""
|
||||
|
||||
|
||||
def test_event_log_and_memory_and_ledger_functional(self, driver, dashboard_url):
|
||||
"""Test Event Log, Memory, and Ledger functionality in one go."""
|
||||
|
||||
|
||||
# 1. Event Log - verify events display
|
||||
driver.get(f"{dashboard_url}/swarm/events")
|
||||
time.sleep(0.5)
|
||||
|
||||
WebDriverWait(driver, 3).until(
|
||||
EC.presence_of_element_located((By.TAG_NAME, "body"))
|
||||
)
|
||||
|
||||
# Should have header and either events or empty state
|
||||
body = driver.find_element(By.TAG_NAME, "body").text
|
||||
assert "Event" in body or "event" in body, "Event log page missing header"
|
||||
|
||||
|
||||
# Create a task via API to generate an event
|
||||
try:
|
||||
httpx.post(
|
||||
f"{dashboard_url}/swarm/tasks",
|
||||
data={"description": "E2E test task"},
|
||||
timeout=2
|
||||
timeout=2,
|
||||
)
|
||||
except Exception:
|
||||
pass # Ignore, just checking page exists
|
||||
|
||||
|
||||
# 2. Memory - verify search works
|
||||
driver.get(f"{dashboard_url}/memory?query=test")
|
||||
time.sleep(0.5)
|
||||
|
||||
WebDriverWait(driver, 3).until(
|
||||
EC.presence_of_element_located((By.TAG_NAME, "body"))
|
||||
)
|
||||
|
||||
# Should have search input
|
||||
search = driver.find_elements(By.CSS_SELECTOR, "input[type='search'], input[name='query']")
|
||||
search = driver.find_elements(
|
||||
By.CSS_SELECTOR, "input[type='search'], input[name='query']"
|
||||
)
|
||||
assert search, "Memory page missing search input"
|
||||
|
||||
|
||||
# 3. Ledger - verify balance display
|
||||
driver.get(f"{dashboard_url}/lightning/ledger")
|
||||
time.sleep(0.5)
|
||||
|
||||
WebDriverWait(driver, 3).until(
|
||||
EC.presence_of_element_located((By.TAG_NAME, "body"))
|
||||
)
|
||||
|
||||
body = driver.find_element(By.TAG_NAME, "body").text
|
||||
# Should show balance-related text
|
||||
has_balance = any(x in body.lower() for x in ["balance", "sats", "transaction"])
|
||||
@@ -130,73 +137,88 @@ class TestAllFeaturesWork:
|
||||
|
||||
class TestCascadeRouter:
|
||||
"""Cascade Router - combined checks."""
|
||||
|
||||
|
||||
def test_router_status_and_navigation(self, driver, dashboard_url):
|
||||
"""Verify router status page and nav link in one test."""
|
||||
|
||||
|
||||
# Check router status page
|
||||
driver.get(f"{dashboard_url}/router/status")
|
||||
time.sleep(0.5)
|
||||
|
||||
WebDriverWait(driver, 3).until(
|
||||
EC.presence_of_element_located((By.TAG_NAME, "body"))
|
||||
)
|
||||
|
||||
body = driver.find_element(By.TAG_NAME, "body").text
|
||||
|
||||
|
||||
# Should show providers or config message
|
||||
has_content = any(x in body.lower() for x in [
|
||||
"provider", "router", "ollama", "config", "status"
|
||||
])
|
||||
has_content = any(
|
||||
x in body.lower()
|
||||
for x in ["provider", "router", "ollama", "config", "status"]
|
||||
)
|
||||
assert has_content, "Router status page missing content"
|
||||
|
||||
|
||||
# Check nav has router link
|
||||
driver.get(dashboard_url)
|
||||
time.sleep(0.3)
|
||||
|
||||
WebDriverWait(driver, 3).until(
|
||||
EC.presence_of_element_located((By.TAG_NAME, "body"))
|
||||
)
|
||||
|
||||
nav_links = driver.find_elements(By.XPATH, "//a[contains(@href, '/router')]")
|
||||
assert nav_links, "Navigation missing router link"
|
||||
|
||||
|
||||
class TestUpgradeQueue:
|
||||
"""Upgrade Queue - combined checks."""
|
||||
|
||||
|
||||
def test_upgrade_queue_page_and_elements(self, driver, dashboard_url):
|
||||
"""Verify upgrade queue page loads with expected elements."""
|
||||
|
||||
|
||||
driver.get(f"{dashboard_url}/self-modify/queue")
|
||||
time.sleep(0.5)
|
||||
|
||||
WebDriverWait(driver, 3).until(
|
||||
EC.presence_of_element_located((By.TAG_NAME, "body"))
|
||||
)
|
||||
|
||||
body = driver.find_element(By.TAG_NAME, "body").text
|
||||
|
||||
|
||||
# Should have queue header
|
||||
assert "upgrade" in body.lower() or "queue" in body.lower(), "Missing queue header"
|
||||
|
||||
assert "upgrade" in body.lower() or "queue" in body.lower(), (
|
||||
"Missing queue header"
|
||||
)
|
||||
|
||||
# Should have pending section or empty state
|
||||
has_pending = "pending" in body.lower() or "no pending" in body.lower()
|
||||
assert has_pending, "Missing pending upgrades section"
|
||||
|
||||
|
||||
# Check for approve/reject buttons if upgrades exist
|
||||
approve_btns = driver.find_elements(By.XPATH, "//button[contains(text(), 'Approve')]")
|
||||
reject_btns = driver.find_elements(By.XPATH, "//button[contains(text(), 'Reject')]")
|
||||
|
||||
approve_btns = driver.find_elements(
|
||||
By.XPATH, "//button[contains(text(), 'Approve')]"
|
||||
)
|
||||
reject_btns = driver.find_elements(
|
||||
By.XPATH, "//button[contains(text(), 'Reject')]"
|
||||
)
|
||||
|
||||
# Either no upgrades (no buttons) or buttons exist
|
||||
# This is a soft check - page structure is valid either way
|
||||
|
||||
|
||||
class TestActivityFeed:
|
||||
"""Activity Feed - combined checks."""
|
||||
|
||||
|
||||
def test_swarm_live_page_and_activity_feed(self, driver, dashboard_url):
|
||||
"""Verify swarm live page has activity feed elements."""
|
||||
|
||||
|
||||
driver.get(f"{dashboard_url}/swarm/live")
|
||||
time.sleep(0.5)
|
||||
|
||||
WebDriverWait(driver, 3).until(
|
||||
EC.presence_of_element_located((By.TAG_NAME, "body"))
|
||||
)
|
||||
|
||||
body = driver.find_element(By.TAG_NAME, "body").text
|
||||
|
||||
|
||||
# Should have live indicator or activity section
|
||||
has_live = any(x in body.lower() for x in [
|
||||
"live", "activity", "swarm", "agents", "tasks"
|
||||
])
|
||||
has_live = any(
|
||||
x in body.lower() for x in ["live", "activity", "swarm", "agents", "tasks"]
|
||||
)
|
||||
assert has_live, "Swarm live page missing content"
|
||||
|
||||
|
||||
# Check for WebSocket connection indicator (if implemented)
|
||||
# or just basic structure
|
||||
panels = driver.find_elements(By.CSS_SELECTOR, ".card, .panel, .mc-panel")
|
||||
@@ -205,7 +227,7 @@ class TestActivityFeed:
|
||||
|
||||
class TestFastSmoke:
|
||||
"""Ultra-fast smoke tests using HTTP where possible."""
|
||||
|
||||
|
||||
def test_all_routes_respond_200(self, dashboard_url):
|
||||
"""HTTP-only test - no browser, very fast."""
|
||||
routes = [
|
||||
@@ -216,16 +238,18 @@ class TestFastSmoke:
|
||||
"/self-modify/queue",
|
||||
"/swarm/live",
|
||||
]
|
||||
|
||||
|
||||
failures = []
|
||||
|
||||
|
||||
for route in routes:
|
||||
try:
|
||||
r = httpx.get(f"{dashboard_url}{route}", timeout=3, follow_redirects=True)
|
||||
r = httpx.get(
|
||||
f"{dashboard_url}{route}", timeout=3, follow_redirects=True
|
||||
)
|
||||
if r.status_code != 200:
|
||||
failures.append(f"{route}: {r.status_code}")
|
||||
except Exception as exc:
|
||||
failures.append(f"{route}: {type(exc).__name__}")
|
||||
|
||||
|
||||
if failures:
|
||||
pytest.fail(f"Routes failed: {', '.join(failures)}")
|
||||
|
||||
Reference in New Issue
Block a user