"""Page Object Model for the operator workbench single-page app. Encapsulates the operator-GUI selectors and interactions so browser specs read as user intent ("select case", "use suggested query") instead of raw locators. Mirrors the existing app structure in ``web/operator-gui/index.html``. All locator accessors return Playwright ``Locator`` objects, which auto-wait; prefer them over arbitrary timeouts (see the e2e-testing skill guidance). """ from __future__ import annotations import json from typing import Any # Status string the app shows after a suggested query is loaded but not yet run. SUGGESTED_QUERY_LOADED_STATUS = "추천 쿼리를 입력했습니다. 실행 버튼을 눌러 검색하세요." # Client-side guard message when a reject/correct decision is missing its memo. MEMO_REQUIRED_ERROR = "반려 또는 보정 결정에는 메모가 필요합니다." class OperatorWorkbench: """Drives the operator GUI for browser-level (E2E) assertions.""" def __init__(self, page: Any, base_url: str) -> None: self.page = page self.base_url = base_url.rstrip("/") self.console_errors: list[str] = [] page.on("console", lambda message: self.console_errors.append(f"console:{message.type}:{message.text}")) page.on("pageerror", lambda error: self.console_errors.append(f"pageerror:{error}")) # -- Locators --------------------------------------------------------- @property def queue_body(self) -> Any: return self.page.locator("#queue-body") @property def workbench_view(self) -> Any: return self.page.locator("#workbench-view") @property def case_title(self) -> Any: return self.page.locator("#case-title") @property def manual_query(self) -> Any: return self.page.locator("#manual-query") @property def manual_query_status(self) -> Any: return self.page.locator("#manual-query-status") @property def submission_image_name(self) -> Any: return self.page.locator("#submission-image-name") @property def submission_import_status(self) -> Any: return self.page.locator("#submission-import-status") @property def recommendation_box(self) -> Any: """Selected-case summary; includes the "현재 운영 결정: