codeflash-agent/packages/blackbox/tests/e2e/test_session_detail.py
Kevin Turcios 0ad5e60523
Add blackbox package: session flight recorder with HTMX dashboard (#39)
* feat(blackbox): add package with models, CLI, and HTMX dashboard

* test(blackbox): add comprehensive test coverage for dashboard

* feat(blackbox): cache session scanning via watcher invalidation

* docs(blackbox): add README and use fastapi[standard] for dev server

* refactor(blackbox): extract presentation logic into formatter classes

* refactor(blackbox): extract classify_error helpers

* feat(blackbox): wire analytics into session detail view

Show token usage, tool breakdowns, and session stats in a
collapsible panel when viewing a session.

* feat(blackbox): add codeflash plugin detection

Detect codeflash agent names, skills, and commands in transcripts.
Surface language, optimization domain, and capability badges in
the analytics panel.

* refactor(blackbox): remove underscore prefixes from internal functions

* chore: add ty python-version to root pyproject.toml

* chore(blackbox): fix lint errors in test files

* style(blackbox): apply ruff formatting to analytics

* feat(blackbox): add Playwright E2E tests for dashboard

Refactor app.py to expose create_app() factory accepting a projects_dir
override, enabling tests to run against fixture data instead of the real
~/.claude/projects/ directory. Routes now read projects_dir from
app.state instead of the module-level constant.

Add 26 Playwright tests across 5 files covering dashboard loading,
session list, session detail with filters and analytics, sidebar
collapse/localStorage persistence, and SSE log streaming. All tests
pass on chromium, firefox, and webkit (78 total).

CI gets a new e2e-blackbox job with a browser matrix strategy running
all three engines in parallel, conditional on blackbox path changes,
with trace upload on failure.

* fix(ci): sync only blackbox package in e2e job

* fix(ci): exclude e2e tests from unit test job

The test job doesn't install Playwright browsers, so e2e tests error
when pytest collects them. Ignore tests/e2e/ directories in the test
job — those are handled by the dedicated e2e-blackbox job.
2026-04-28 19:58:43 -05:00

77 lines
3.6 KiB
Python

"""Tests for session detail view, filters, and analytics."""
from __future__ import annotations
import re
from typing import TYPE_CHECKING
import pytest
from playwright.sync_api import expect
from tests.e2e.conftest import PROJECT_A_DIR, SESSION_A_ID
if TYPE_CHECKING:
from playwright.sync_api import Page
pytestmark = pytest.mark.e2e
class TestSessionDetail:
"""Clicking a session loads the detail view."""
def test_clicking_session_loads_detail(self, dashboard: Page) -> None:
"""Session detail header appears after clicking a session."""
dashboard.get_by_text("work/myapp").first.click()
expect(dashboard.locator("#session-detail h2")).to_be_visible(timeout=10_000)
def test_detail_shows_project_name(self, dashboard: Page) -> None:
"""Session detail header shows the project name."""
dashboard.get_by_text("work/myapp").first.click()
expect(dashboard.locator("#session-detail h2")).to_contain_text("work/myapp")
def test_detail_shows_session_id_prefix(self, dashboard: Page) -> None:
"""Session detail header shows the 8-char session ID prefix."""
dashboard.get_by_text("work/myapp").first.click()
detail = dashboard.locator("#session-detail")
expect(detail.get_by_text("sess-aaa")).to_be_visible(timeout=10_000)
def test_filter_buttons_visible(self, dashboard: Page) -> None:
"""Filter buttons (Compact, All, etc.) are visible after loading a session."""
dashboard.get_by_text("work/myapp").first.click()
detail = dashboard.locator("#session-detail")
expect(detail.get_by_role("button", name="Compact")).to_be_visible(timeout=10_000)
expect(detail.get_by_role("button", name="All")).to_be_visible()
def test_filter_button_active_state(self, dashboard: Page) -> None:
"""The default filter (compact) has the active accent styling."""
dashboard.get_by_text("work/myapp").first.click()
compact_btn = dashboard.locator("#session-detail button", has_text="Compact").first
expect(compact_btn).to_be_visible(timeout=10_000)
expect(compact_btn).to_have_class(re.compile(r"text-accent-400"))
def test_switching_filter_changes_active_button(self, dashboard: Page) -> None:
"""Clicking 'All' makes it active and removes active from 'Compact'."""
dashboard.get_by_text("work/myapp").first.click()
all_btn = dashboard.locator("#session-detail button", has_text="All").first
expect(all_btn).to_be_visible(timeout=10_000)
all_btn.click()
expect(all_btn).to_have_class(re.compile(r"text-accent-400"))
def test_analytics_panel_exists(self, dashboard: Page) -> None:
"""Analytics details element is present for sessions with metadata."""
dashboard.get_by_text("work/myapp").first.click()
analytics = dashboard.locator("#session-detail details")
expect(analytics).to_be_visible(timeout=10_000)
def test_analytics_panel_expands(self, dashboard: Page) -> None:
"""Clicking the analytics summary expands the panel to show token counts."""
dashboard.get_by_text("work/myapp").first.click()
summary = dashboard.locator("#session-detail details summary")
expect(summary).to_be_visible(timeout=10_000)
summary.click()
expect(dashboard.get_by_text("tokens", exact=True)).to_be_visible()
def test_session_not_found(self, page: Page, base_url: str) -> None:
"""Navigating to a non-existent session shows an error."""
page.goto(f"{base_url}/?session={PROJECT_A_DIR}/{SESSION_A_ID.replace('aaaa', 'zzzz')}")
expect(page.get_by_text("Session not found")).to_be_visible(timeout=10_000)