feat(UX-001): add full-width two-pane dashboard layout

This commit is contained in:
Bu5hm4nn
2026-03-25 23:19:09 +01:00
parent 960e1e9215
commit a60c5fb1f2
10 changed files with 473 additions and 212 deletions

View File

@@ -9,6 +9,21 @@ ARTIFACTS = Path("tests/artifacts")
ARTIFACTS.mkdir(parents=True, exist_ok=True)
def assert_two_pane_layout(page, left_testid: str, right_testid: str) -> None:
left = page.locator(f'[data-testid="{left_testid}"]:visible').first
right = page.locator(f'[data-testid="{right_testid}"]:visible').first
expect(left).to_be_visible(timeout=15000)
expect(right).to_be_visible(timeout=15000)
left_box = left.bounding_box()
right_box = right.bounding_box()
assert left_box is not None
assert right_box is not None
assert left_box["x"] < right_box["x"]
assert abs(left_box["y"] - right_box["y"]) < 120
width_ratio = right_box["width"] / left_box["width"]
assert 1.6 <= width_ratio <= 2.4
def test_homepage_and_options_page_render() -> None:
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
@@ -29,6 +44,7 @@ def test_homepage_and_options_page_render() -> None:
expect(page.locator("text=Overview").first).to_be_visible(timeout=10000)
expect(page.locator("text=Live quote source:").first).to_be_visible(timeout=15000)
expect(page.locator("text=Alert Status").first).to_be_visible(timeout=15000)
assert_two_pane_layout(page, "overview-left-pane", "overview-right-pane")
page.screenshot(path=str(ARTIFACTS / "overview.png"), full_page=True)
page.goto(BASE_URL, wait_until="domcontentloaded", timeout=30000)
@@ -39,54 +55,34 @@ def test_homepage_and_options_page_render() -> None:
expect(page.locator("text=Backtests").first).to_be_visible(timeout=15000)
expect(page.locator("text=Scenario Form").first).to_be_visible(timeout=15000)
expect(page.locator("text=Scenario Summary").first).to_be_visible(timeout=15000)
expect(page.locator("text=Daily Results").first).to_be_visible(timeout=15000)
assert_two_pane_layout(page, "backtests-left-pane", "backtests-right-pane")
backtests_text = page.locator("body").inner_text(timeout=15000)
assert "Auto-derived entry spot: $100.00" in backtests_text
assert "Historical scenario starts undercollateralized:" in backtests_text
assert "loan_amount must be less than initial collateral value" not in backtests_text
assert "RuntimeError" not in backtests_text
assert "Server error" not in backtests_text
assert "Traceback" not in backtests_text
page.screenshot(path=str(ARTIFACTS / "backtests.png"), full_page=True)
page.get_by_label("Template").click()
page.get_by_text("Protective Put 95%", exact=True).click()
page.get_by_role("button", name="Run backtest").click()
expect(page.locator("text=Scenario Summary").first).to_be_visible(timeout=15000)
expect(page.locator("text=Template: Protective Put 95%").first).to_be_visible(timeout=15000)
rerun_text = page.locator("body").inner_text(timeout=15000)
assert "Margin call days hedged" in rerun_text
assert "RuntimeError" not in rerun_text
assert "Server error" not in rerun_text
page.goto(f"{workspace_url}/event-comparison", wait_until="networkidle", timeout=30000)
expect(page.locator("text=Event Comparison").first).to_be_visible(timeout=15000)
expect(page.locator("text=Comparison Form").first).to_be_visible(timeout=15000)
expect(page.locator("text=Ranked Results").first).to_be_visible(timeout=15000)
expect(page.locator("text=Scenario Metadata").first).to_be_visible(timeout=15000)
expect(page.locator("text=Portfolio Value Paths").first).to_be_visible(timeout=15000)
expect(page.locator("text=Scenario Summary").first).to_be_visible(timeout=15000)
assert_two_pane_layout(page, "event-comparison-left-pane", "event-comparison-right-pane")
event_text = page.locator("body").inner_text(timeout=15000)
assert "GLD January 2024 Selloff" in event_text
assert "Protective Put ATM" in event_text
assert "Baseline series shows the unhedged collateral value path" in event_text
assert "Hedged margin call days" in event_text
assert "Templates compared" in event_text and "4" in event_text
assert "Historical scenario starts undercollateralized:" in event_text
assert "loan_amount must be less than initial collateral value" not in event_text
assert "RuntimeError" not in event_text
assert "Server error" not in event_text
assert "Traceback" not in event_text
page.get_by_label("Event preset").click()
page.get_by_text("GLD January 2024 Drawdown", exact=True).click()
page.get_by_role("button", name="Run comparison").click()
expect(page.locator("text=GLD January 2024 Drawdown").first).to_be_visible(timeout=15000)
rerun_event_text = page.locator("body").inner_text(timeout=15000)
assert "Laddered Puts 33/33/33 ATM + 95% + 90%" in rerun_event_text
assert "Templates compared" in rerun_event_text and "3" in rerun_event_text
assert "RuntimeError" not in rerun_event_text
assert "Server error" not in rerun_event_text
page.screenshot(path=str(ARTIFACTS / "event-comparison.png"), full_page=True)
page.goto(f"{BASE_URL}/options", wait_until="domcontentloaded", timeout=30000)
expect(page.locator("text=Options Chain").first).to_be_visible(timeout=15000)
expect(page.locator("text=Filters").first).to_be_visible(timeout=15000)
assert_two_pane_layout(page, "options-left-pane", "options-right-pane")
body_text = page.locator("body").inner_text(timeout=15000)
assert "Server error" not in body_text
assert "RuntimeError" not in body_text
@@ -97,6 +93,7 @@ def test_homepage_and_options_page_render() -> None:
expect(page.locator("text=Settings").first).to_be_visible(timeout=15000)
expect(page.locator("text=Collateral entry basis").first).to_be_visible(timeout=15000)
expect(page.locator("text=Entry price ($/oz)").first).to_be_visible(timeout=15000)
assert_two_pane_layout(page, "settings-left-pane", "settings-right-pane")
page.get_by_label("Collateral entry basis").click()
page.get_by_text("Gold weight + entry price", exact=True).click()
@@ -122,18 +119,56 @@ def test_homepage_and_options_page_render() -> None:
expect(page.get_by_label("Underlying units")).to_have_value("2200")
expect(page.get_by_label("Loan amount")).to_have_value("222000")
expect(page.get_by_label("Margin call LTV")).to_have_value("0.8")
assert_two_pane_layout(page, "backtests-left-pane", "backtests-right-pane")
backtests_workspace_text = page.locator("body").inner_text(timeout=15000)
assert "Scenario Summary" in backtests_workspace_text
assert "Scenario Results" in backtests_workspace_text
assert "$220,000" in backtests_workspace_text
assert "Historical scenario starts undercollateralized:" in backtests_workspace_text
page.get_by_label("Underlying units").fill("3000")
page.get_by_label("Template").click()
page.get_by_text("Protective Put 95%", exact=True).click()
page.get_by_role("button", name="Run backtest").click()
expect(page.locator("text=Daily Results").first).to_be_visible(timeout=15000)
expect(page.locator("text=Scenario Results").first).to_be_visible(timeout=15000)
expect(page.locator("text=Template: Protective Put 95%").first).to_be_visible(timeout=15000)
rerun_text = page.locator("body").inner_text(timeout=15000)
assert "Margin call days hedged" in rerun_text
assert "Historical scenario starts undercollateralized:" not in rerun_text
assert "RuntimeError" not in rerun_text
assert "Server error" not in rerun_text
page.goto(f"{workspace_url}/event-comparison", wait_until="networkidle", timeout=30000)
expect(page.get_by_label("Underlying units")).to_have_value("2200")
expect(page.get_by_label("Loan amount")).to_have_value("222000")
expect(page.get_by_label("Margin call LTV")).to_have_value("0.8")
assert_two_pane_layout(page, "event-comparison-left-pane", "event-comparison-right-pane")
event_workspace_text = page.locator("body").inner_text(timeout=15000)
assert "$222,000" in event_workspace_text
assert "2,200" in event_workspace_text
assert "80.0%" in event_workspace_text
assert "Historical scenario starts undercollateralized:" in event_workspace_text
page.get_by_label("Underlying units").fill("3000")
page.get_by_role("button", name="Run comparison").click()
expect(page.locator("text=Ranked Results").first).to_be_visible(timeout=15000)
expect(page.locator("text=Scenario Results").first).to_be_visible(timeout=15000)
expect(page.locator("text=Portfolio Value Paths").first).to_be_visible(timeout=15000)
rerun_event_text = page.locator("body").inner_text(timeout=15000)
assert "Baseline series shows the unhedged collateral value path" in rerun_event_text
assert "Templates compared" in rerun_event_text and "4" in rerun_event_text
assert "Historical scenario starts undercollateralized:" not in rerun_event_text
page.get_by_label("Event preset").click()
page.get_by_text("GLD January 2024 Drawdown", exact=True).click()
page.get_by_role("button", name="Run comparison").click()
expect(page.locator("text=GLD January 2024 Drawdown").first).to_be_visible(timeout=15000)
rerun_event_text = page.locator("body").inner_text(timeout=15000)
assert "Laddered Puts 33/33/33 ATM + 95% + 90%" in rerun_event_text
assert "Templates compared" in rerun_event_text and "3" in rerun_event_text
assert "RuntimeError" not in rerun_event_text
assert "Server error" not in rerun_event_text
page.goto(workspace_url, wait_until="domcontentloaded", timeout=30000)
overview_text = page.locator("body").inner_text(timeout=15000)
@@ -181,6 +216,7 @@ def test_homepage_and_options_page_render() -> None:
expect(page.locator("text=Hedge Analysis").first).to_be_visible(timeout=15000)
expect(page.locator("text=Strategy selector").first).to_be_visible(timeout=15000)
expect(page.locator("text=Strategy Controls").first).to_be_visible(timeout=15000)
assert_two_pane_layout(page, "hedge-left-pane", "hedge-right-pane")
hedge_text = page.locator("body").inner_text(timeout=15000)
assert "Scenario Summary" in hedge_text
assert "RuntimeError" not in hedge_text