- Changed the default port for the development server from 8000 to 8765 in the documentation. - Updated testing instructions to reflect the new server port. - Improved comments and structure in various files for better clarity and maintainability. - Refactored some logging statements for consistency and readability. - Added new configuration options for browser host management, including session isolation settings. - Enhanced type safety and modularity in several components, ensuring better integration and performance.
174 lines
5.0 KiB
Python
174 lines
5.0 KiB
Python
"""Pytest configuration and shared fixtures for all tests."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import TYPE_CHECKING
|
|
from unittest.mock import AsyncMock, MagicMock
|
|
|
|
import pytest
|
|
|
|
if TYPE_CHECKING:
|
|
from guide.app.core.config import AppSettings, BrowserHostConfig
|
|
from guide.app.models.personas.models import DemoPersona
|
|
from guide.app.models.personas.store import PersonaStore
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_browser_hosts() -> dict[str, BrowserHostConfig]:
|
|
"""Provide mock browser host configurations for testing."""
|
|
from guide.app.core.config import BrowserHostConfig, HostKind
|
|
|
|
return {
|
|
"demo-extension": BrowserHostConfig(
|
|
id="demo-extension",
|
|
kind=HostKind.EXTENSION,
|
|
port=17373,
|
|
),
|
|
"support-extension": BrowserHostConfig(
|
|
id="support-extension",
|
|
kind=HostKind.EXTENSION,
|
|
port=17374,
|
|
),
|
|
"browserless-cdp": BrowserHostConfig(
|
|
id="browserless-cdp",
|
|
kind=HostKind.CDP,
|
|
host="browserless.lab",
|
|
port=80,
|
|
cdp_url="ws://browserless.lab:80/",
|
|
browser="chromium",
|
|
),
|
|
"headless-local": BrowserHostConfig(
|
|
id="headless-local",
|
|
kind=HostKind.HEADLESS,
|
|
browser="chromium",
|
|
),
|
|
}
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_personas() -> dict[str, DemoPersona]:
|
|
"""Provide mock persona configurations for testing."""
|
|
from guide.app.models.personas.models import (
|
|
DemoPersona,
|
|
LoginMethod,
|
|
PersonaRole,
|
|
)
|
|
|
|
return {
|
|
"buyer": DemoPersona(
|
|
id="buyer",
|
|
role=PersonaRole.BUYER,
|
|
email="travis@raindrop.com",
|
|
login_method=LoginMethod.MFA_EMAIL,
|
|
browser_host_id="demo-extension",
|
|
),
|
|
"analyst": DemoPersona(
|
|
id="analyst",
|
|
role=PersonaRole.ANALYST,
|
|
email="rd.daya@sidepiece.rip",
|
|
login_method=LoginMethod.MFA_EMAIL,
|
|
browser_host_id="support-extension",
|
|
),
|
|
"supplier": DemoPersona(
|
|
id="supplier",
|
|
role=PersonaRole.SUPPLIER,
|
|
email="supplier.demo@example.com",
|
|
login_method=LoginMethod.MFA_EMAIL,
|
|
browser_host_id="headless-local",
|
|
),
|
|
}
|
|
|
|
|
|
@pytest.fixture
|
|
def app_settings(
|
|
mock_browser_hosts: dict[str, BrowserHostConfig],
|
|
mock_personas: dict[str, DemoPersona],
|
|
) -> AppSettings:
|
|
"""Provide application settings with mock configuration."""
|
|
from guide.app.core.config import AppSettings
|
|
|
|
return AppSettings(
|
|
raindrop_base_url="https://app.raindrop.com",
|
|
raindrop_graphql_url="https://app.raindrop.com/graphql",
|
|
default_browser_host_id="browserless-cdp",
|
|
browser_hosts=mock_browser_hosts,
|
|
personas=mock_personas,
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def persona_store(app_settings: AppSettings) -> PersonaStore:
|
|
"""Provide PersonaStore instance with mock settings."""
|
|
from guide.app.models.personas.store import PersonaStore
|
|
|
|
return PersonaStore(app_settings)
|
|
|
|
|
|
@pytest.fixture
|
|
def action_registry(persona_store: PersonaStore) -> object:
|
|
"""Provide ActionRegistry instance with DI context.
|
|
|
|
Note: Returns object type since ActionRegistry doesn't have public type exports.
|
|
"""
|
|
from guide.app.actions.registry import ActionRegistry
|
|
|
|
registry = ActionRegistry(
|
|
di_context={
|
|
"persona_store": persona_store,
|
|
"login_url": "https://app.raindrop.com",
|
|
}
|
|
)
|
|
return registry
|
|
|
|
|
|
@pytest.fixture
|
|
def action_context(persona_store: PersonaStore) -> object:
|
|
"""Provide ActionContext instance for testing action execution.
|
|
|
|
Note: Returns object type since ActionContext structure is opaque to fixtures.
|
|
|
|
Args:
|
|
persona_store: Unused, but required for fixture dependency order.
|
|
"""
|
|
_ = persona_store # Unused but required for fixture dependency
|
|
from guide.app.models.domain.models import ActionContext
|
|
|
|
return ActionContext(
|
|
action_id="test-action",
|
|
persona_id="buyer",
|
|
browser_host_id="demo-extension",
|
|
correlation_id="test-correlation-123",
|
|
shared_state={},
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_page() -> AsyncMock:
|
|
"""Provide mock Playwright page instance."""
|
|
page = AsyncMock()
|
|
page.goto = AsyncMock()
|
|
page.wait_for_selector = AsyncMock()
|
|
page.fill = AsyncMock()
|
|
page.click = AsyncMock()
|
|
page.screenshot = AsyncMock(return_value=b"fake-image-data")
|
|
page.content = AsyncMock(return_value="<html><body>Mock</body></html>")
|
|
page.on = MagicMock()
|
|
return page
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_browser_client() -> AsyncMock:
|
|
"""Provide mock BrowserClient instance."""
|
|
client = AsyncMock()
|
|
client.open_page = AsyncMock()
|
|
return client
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_browser_pool() -> AsyncMock:
|
|
"""Provide mock BrowserPool instance."""
|
|
pool = AsyncMock()
|
|
pool.initialize = AsyncMock()
|
|
pool.close = AsyncMock()
|
|
return pool
|