| """E2Eテスト用のフィクスチャとユーティリティ。 |
| |
| テスト環境の初期化とページオブジェクトを提供します。 |
| 元々 tests/e2e/steps/conftest.py に分かれていた機能を統合しています。 |
| """ |
|
|
| import os |
| import time |
| from pathlib import Path |
| from typing import Generator |
|
|
| import pytest |
| from playwright.sync_api import Browser, Page, sync_playwright |
|
|
| from tests.utils.logger import test_logger as logger |
| from tests.utils.test_environment import TestEnvironment |
| from yomitalk.components.audio_generator import initialize_global_voicevox_manager |
|
|
| |
| TEST_DATA_DIR = Path(__file__).parent.parent / "data" |
|
|
|
|
| @pytest.fixture(scope="session", autouse=True) |
| def initialize_voicevox(): |
| """Initialize global VOICEVOX manager for all tests.""" |
| try: |
| manager = initialize_global_voicevox_manager() |
| if manager: |
| logger.info("Global VOICEVOX manager initialized successfully for tests") |
| else: |
| logger.warning("VOICEVOX manager initialization returned None") |
| except Exception as e: |
| logger.warning(f"VOICEVOX initialization failed in tests: {e}") |
| yield |
| |
|
|
|
|
| @pytest.fixture(scope="session", autouse=True) |
| def app_environment() -> Generator[TestEnvironment, None, None]: |
| """ |
| バックエンド側のテスト環境を提供するフィクスチャ |
| |
| セッション全体で一度だけアプリケーションを起動し、 |
| 全テスト終了時に自動的に終了する |
| """ |
| test_env = TestEnvironment() |
| try: |
| |
| app_url = test_env.setup() |
| logger.info(f"Application backend is running at {app_url}") |
| yield test_env |
| except Exception as e: |
| |
| logger.error(f"ERROR setting up test environment: {e}") |
| raise |
| finally: |
| |
| test_env.teardown() |
|
|
|
|
| @pytest.fixture(scope="function") |
| def page(browser: Browser) -> Generator[Page, None, None]: |
| """ |
| Provides a test page fixture |
| |
| This creates a new page for each test function but does not navigate to any URL. |
| Navigation should be handled by the 'the application is running' step in the Background |
| section of each feature file. |
| |
| Args: |
| browser: Playwright browser instance |
| |
| Returns: |
| Page: Configured page object |
| """ |
| |
| page = browser.new_page(viewport={"width": 1280, "height": 720}) |
|
|
| |
| page.set_default_timeout(8000) |
|
|
| yield page |
|
|
| |
| page.close() |
|
|
|
|
| @pytest.fixture(scope="session") |
| def browser(): |
| """ |
| ブラウザインスタンスを提供するフィクスチャ |
| |
| Returns: |
| Browser: Playwrightブラウザインスタンス |
| """ |
| with sync_playwright() as playwright: |
| |
| launch_args = [ |
| "--no-sandbox", |
| "--disable-dev-shm-usage", |
| "--disable-gpu", |
| "--disable-extensions", |
| "--disable-background-timer-throttling", |
| "--disable-backgrounding-occluded-windows", |
| "--disable-renderer-backgrounding", |
| "--disable-features=TranslateUI", |
| "--disable-component-extensions-with-background-pages", |
| ] |
|
|
| if os.environ.get("HEADLESS", "true").lower() == "true": |
| browser = playwright.chromium.launch(headless=True, args=launch_args) |
| else: |
| browser = playwright.chromium.launch( |
| headless=False, |
| slow_mo=50, |
| args=launch_args, |
| ) |
|
|
| yield browser |
|
|
| |
| browser.close() |
|
|
|
|
| def pytest_bdd_apply_tag(tag, function): |
| """ |
| タグに基づいてテストをスキップするためのフック |
| |
| Args: |
| tag: BDDタグ |
| function: テスト関数(未使用) |
| |
| Returns: |
| bool: スキップするかどうか |
| """ |
| if tag == "skip": |
| return pytest.mark.skip(reason="明示的にスキップされました") |
|
|
| if tag == "slow" and os.environ.get("SKIP_SLOW_TESTS", "false").lower() == "true": |
| return pytest.mark.skip(reason="遅いテストはスキップします") |
|
|
| return None |
|
|
|
|
| def pytest_bdd_step_error(request, feature, scenario, step, step_func, step_func_args, exception): |
| """ |
| ステップが失敗した場合のフック |
| |
| Args: |
| request: Pytestリクエストオブジェクト |
| feature: BDD機能(未使用) |
| scenario: BDDシナリオ |
| step: BDDステップ |
| step_func: ステップ関数(未使用) |
| step_func_args: ステップ関数引数 |
| exception: 発生した例外(未使用) |
| """ |
| logger.error(f"Error in step: {step}") |
|
|
| |
| page = step_func_args.get("page") |
| if page and hasattr(page, "screenshot"): |
| screenshot_dir = os.path.join("tests", "e2e", "screenshots") |
| os.makedirs(screenshot_dir, exist_ok=True) |
|
|
| scenario_name = scenario.name.replace(" ", "_") |
| step_name = step.name.replace(" ", "_") |
| timestamp = int(time.time()) |
|
|
| screenshot_path = os.path.join(screenshot_dir, f"error_{scenario_name}_{step_name}_{timestamp}.png") |
|
|
| page.screenshot(path=screenshot_path) |
| logger.error(f"スクリーンショットが保存されました: {screenshot_path}") |
|
|