mirror of
https://gitlab.com/itsulu-odoo/itsulu-blog-publisher.git
synced 2026-05-30 23:41:23 +00:00
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>
118 lines
3.3 KiB
Python
118 lines
3.3 KiB
Python
"""
|
||
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 30–60s 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'
|