itsulu-blog-publisher/e2e/conftest.py
Nicholas Riegel acfa1d93d7 feat: establish Phase 3 E2E testing infrastructure with Playwright
Created comprehensive E2E test suite for ITSulu Blog Publisher using Playwright
and Runboat. Includes:

PHASE3_ROADMAP.md:
- Goals for E2E coverage (10-30 scenarios)
- Performance benchmark targets (latency, tokens, queries, throughput)
- Implementation plan with layer-by-layer breakdown
- Success criteria and SLO targets
- Runboat integration details for CI/CD

e2e/ directory structure:
- conftest.py: Runboat polling, auth fixtures, page fixture
- requirements.txt: pytest, playwright, requests
- test_generation.py: On-demand generation workflows (5 tests)
- test_scheduling.py: Schedule slot configuration and execution (6 tests)
- test_error_recovery.py: Error handling and email notifications (8 tests)

Total: 19 E2E test scenarios covering:
- On-demand post generation with auto-publish
- Scheduled generation with topic queue
- Error recovery and retry mechanism
- Email notifications with correct content
- Social media copy generation
- Concurrent post generation
- Progress feedback during API calls

Tests use:
- Playwright sync API with 30s timeout (Odoo JS rendering)
- Runboat polling with 180s timeout (instance cold-start)
- Session-scoped auth to avoid repeated 30s logins
- Data-test-id selectors where available, fallback to get_by_*
- Proper wait_for_load_state() for async operations

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-05-30 00:50:43 -04:00

118 lines
3.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Playwright E2E test configuration for ITSulu Blog Publisher.
Handles Runboat instance polling, authentication, and fixture setup.
"""
import os
import time
import requests
import pytest
BASE_URL = os.environ.get('ODOO_BASE_URL', 'http://localhost:8069')
RUNBOAT_TIMEOUT = 180 # seconds
def wait_for_odoo(url, timeout=RUNBOAT_TIMEOUT):
"""
Poll Odoo login page until it responds (Runboat cold-start handling).
Runboat instances take 3060s to boot; this ensures we don't timeout.
"""
deadline = time.time() + timeout
last_error = None
while time.time() < deadline:
try:
response = requests.get(f"{url}/web/login", timeout=5)
if response.status_code == 200:
return
except Exception as e:
last_error = e
pass
time.sleep(2)
raise TimeoutError(
f"Odoo at {BASE_URL} did not respond after {timeout}s. "
f"Last error: {last_error}"
)
@pytest.fixture(scope='session')
def browser_context_args(browser_context_args):
"""
Poll Runboat instance and set base URL for all tests.
Runs once per test session before any browser is created.
"""
print(f"\n📡 Waiting for Odoo at {BASE_URL}...")
wait_for_odoo(BASE_URL)
print("✅ Odoo is ready")
return {**browser_context_args, 'base_url': BASE_URL}
@pytest.fixture(scope='session')
def auth_state(browser, browser_context_args):
"""
Log in as admin and save authentication state.
Reused across all tests to avoid repeated login (30s+ per test).
"""
print("\n🔐 Authenticating as admin...")
ctx = browser.new_context(**browser_context_args)
page = ctx.new_page()
page.set_default_timeout(30_000)
try:
page.goto('/web/login')
page.get_by_label('Email').fill('admin')
page.get_by_label('Password').fill('admin')
page.get_by_role('button', name='Log in').click()
page.wait_for_url('**/odoo/**', timeout=30_000)
state = ctx.storage_state()
print("✅ Authentication successful")
ctx.close()
return state
except Exception as e:
ctx.close()
raise RuntimeError(f"Login failed: {e}")
@pytest.fixture
def page(browser, browser_context_args, auth_state):
"""
Authenticated Playwright page fixture.
Each test gets a fresh context with saved auth state.
"""
ctx = browser.new_context(**browser_context_args, storage_state=auth_state)
pg = ctx.new_page()
pg.set_default_timeout(30_000) # Odoo JS rendering is slow
yield pg
ctx.close()
@pytest.fixture
def admin_user(page):
"""
Helper to get admin user record (via API call within page context).
Useful for setting up test data that requires user relationships.
"""
# In a real Runboat instance, we'd fetch this via the web services
# For now, return a simple dict with admin info
return {
'id': 2, # Odoo default admin user ID
'name': 'Administrator',
'email': 'admin@example.com',
'login': 'admin',
}
@pytest.fixture(scope='session')
def blog_name():
"""Pre-configured test blog name (must exist in template DB)."""
return 'ITSulu Insights'
@pytest.fixture(scope='session')
def notification_email():
"""Email address for testing notifications."""
return 'test@itsulu.com'