Files
vault-dash/tests/test_playwright_server.py
Bu5hm4nn d835544e58 fix: correct backtest job result serialization and add Playwright test fixtures
- Fix BacktestPageRunResult serialization in jobs.py to correctly access
  nested fields from scenario and run_result objects
- Add test_backtest_job.py with comprehensive tests for job execution
- Add conftest_playwright.py with ServerManager that starts FastAPI server
  for Playwright tests using uvicorn
- Add test_playwright_server.py with E2E tests using the server fixture

The job serialization bug was causing backtest results to fail silently
because it was trying to access non-existent fields on BacktestPageRunResult.
2026-04-04 18:27:34 +02:00

134 lines
5.1 KiB
Python

"""Playwright tests for end-to-end validation of the vault dashboard.
This test file verifies that:
1. The application starts and serves pages correctly
2. Workspace creation and navigation work
3. Backtest page loads and executes scenarios
4. The full user flow works without runtime errors
"""
from __future__ import annotations
import sys
from pathlib import Path
from playwright.sync_api import expect
# Import the server fixture from conftest_playwright
sys.path.insert(0, str(Path(__file__).parent))
ARTIFACTS = Path("tests/artifacts")
ARTIFACTS.mkdir(parents=True, exist_ok=True)
def assert_no_horizontal_overflow(page) -> None:
"""Verify the page doesn't have horizontal scrollbar."""
scroll_width = page.evaluate("document.documentElement.scrollWidth")
viewport_width = page.evaluate("window.innerWidth")
assert scroll_width <= viewport_width + 1
def test_server_starts_and_serves_homepage(server_url: str) -> None:
"""Verify the server starts and serves the homepage."""
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page(viewport={"width": 1440, "height": 1000})
page.goto(server_url, wait_until="domcontentloaded", timeout=30000)
expect(page).to_have_title("NiceGUI")
expect(page.locator("text=Create a private workspace URL").first).to_be_visible(timeout=10000)
browser.close()
def test_workspace_creation_and_navigation(server_url: str) -> None:
"""Test creating a workspace and navigating between pages."""
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page(viewport={"width": 1440, "height": 1000})
# Create workspace
page.goto(server_url, wait_until="domcontentloaded", timeout=30000)
expect(page.locator("text=Create a private workspace URL").first).to_be_visible(timeout=10000)
page.get_by_role("button", name="Get started").click()
page.wait_for_url(f"{server_url}/*", timeout=15000)
workspace_url = page.url
workspace_id = workspace_url.removeprefix(f"{server_url}/")
assert workspace_id, "Should have workspace ID in URL"
# Verify overview page loads
expect(page.locator("text=Vault Dashboard").first).to_be_visible(timeout=10000)
expect(page.locator("text=Overview").first).to_be_visible(timeout=10000)
browser.close()
def test_backtest_with_synthetic_data(server_url: str) -> None:
"""E2E test: Backtest scenario execution with synthetic fixture data.
This test verifies:
1. Synthetic data source can be selected
2. Fixture-supported dates (2024-01-02 to 2024-01-08) work
3. Run button triggers execution and results display
"""
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page(viewport={"width": 1440, "height": 1000})
# Create workspace
page.goto(server_url, wait_until="domcontentloaded", timeout=30000)
expect(page.locator("text=Create a private workspace URL").first).to_be_visible(timeout=10000)
page.get_by_role("button", name="Get started").click()
page.wait_for_url(f"{server_url}/*", timeout=15000)
workspace_url = page.url
# Navigate to backtests
page.goto(f"{workspace_url}/backtests", wait_until="domcontentloaded", timeout=30000)
expect(page.locator("text=Scenario Configuration")).to_be_visible(timeout=10000)
# Select Synthetic data source
data_source_select = page.locator("[data-testid=data-source-select]")
expect(data_source_select).to_be_visible(timeout=5000)
data_source_select.click()
page.get_by_text("Synthetic", exact=True).click()
page.wait_for_timeout(500)
# Fill in fixture-supported dates
start_date_input = page.get_by_label("Start date")
end_date_input = page.get_by_label("End date")
start_date_input.fill("2024-01-02")
end_date_input.fill("2024-01-08")
start_date_input.press("Tab")
page.wait_for_timeout(1000)
# Fill scenario parameters
page.get_by_label("Underlying units").fill("1000")
page.get_by_label("Loan amount").fill("68000")
page.get_by_label("Margin call LTV").fill("0.75")
# Run backtest
run_button = page.get_by_role("button", name="Run backtest")
expect(run_button).to_be_enabled(timeout=5000)
run_button.click()
# Wait for results
scenario_results = page.locator("text=Scenario Results")
expect(scenario_results).to_be_visible(timeout=30000)
# Verify results
result_text = page.locator("body").inner_text(timeout=10000)
assert "Start value" in result_text
assert "RuntimeError" not in result_text
assert "Traceback" not in result_text
page.screenshot(path=str(ARTIFACTS / "backtest_synthetic_results.png"), full_page=True)
browser.close()