diff --git a/tests/operator_gui/test_static_workbench.py b/tests/operator_gui/test_static_workbench.py index d933895..e297988 100644 --- a/tests/operator_gui/test_static_workbench.py +++ b/tests/operator_gui/test_static_workbench.py @@ -458,7 +458,10 @@ def test_safety_rules_are_visible_in_ui_contract(): assert 'id="decision-memo"' in html assert 'id="manual-query-provider"' in html - assert 'option value="google_search"' not in html + # 2026-06-12 안전 규칙 개정(설계 승인 2026-06-11): 운영자 수동 검색에 + # 구글 근거 검색(텍스트 쿼리)을 동적 옵션으로 노출한다. 정적 HTML 기본값은 + # 네이버만 유지하고, 이미지 업로드 역검색 금지는 그대로다. + assert '{ id: "google_search", label: providerLabels.google_search }' in script assert "네이버" in html assert "reverse search" not in html.lower() assert "sourceEvidenceIds" in script @@ -601,13 +604,18 @@ def test_operator_gui_exposes_keyword_candidate_collection_workflow(): assert "제출 제목/파일명 기반" in search -def test_google_custom_search_is_not_exposed_as_operator_choice(): +def test_google_custom_search_is_exposed_as_operator_text_query_choice(): html = _read(INDEX) script = _read(APP_JS) + search = _read(OPERATOR_SEARCH_JS) + labels = _read(OPERATOR_LABELS_JS) - assert "구글 맞춤 검색" not in html - assert "구글 맞춤 검색" not in script + # 정적 HTML은 네이버 기본값만 두고 부트스트랩 후 동적으로 채운다. assert 'value="google_search"' not in html + assert '{ id: "google_search", label: providerLabels.google_search }' in script + assert "구글 근거 검색" in labels + assert 'provider === "google_search" ? "google_search" : "naver"' in search + assert "reverse search" not in html.lower() assert "operatorSearchProviders" in script assert "visibleProviderControls" in script diff --git a/web/operator-gui/app.js b/web/operator-gui/app.js index 9038cc5..be5c0fc 100644 --- a/web/operator-gui/app.js +++ b/web/operator-gui/app.js @@ -12,7 +12,10 @@ } = window.OperatorLabels; const retiredProviderIds = new Set(["google_search"]); -const operatorSearchProviders = [{ id: "naver", label: providerLabels.naver }]; +const operatorSearchProviders = [ + { id: "naver", label: providerLabels.naver }, + { id: "google_search", label: providerLabels.google_search }, +]; const submissions = []; @@ -616,27 +619,25 @@ function visibleProviderStateEntries(providerState) { function renderOperatorSearchProviderOptions() { - const optionHtml = operatorSearchProviders - - .map((provider) => ``) - + .map((provider) => { + const runtime = providers.find((item) => item.id === provider.id); + const unavailable = Boolean(runtime) && !runtime.enabled; + const label = unavailable ? `${provider.label} (비활성)` : provider.label; + return ``; + }) .join(""); ["manual-query-provider", "collection-provider"].forEach((elementId) => { - const select = document.getElementById(elementId); - if (!select) return; - const current = select.value; - select.innerHTML = optionHtml; - select.value = operatorSearchProviders.some((provider) => provider.id === current) ? current : operatorSearchProviders[0].id; - + if (select.selectedOptions[0] && select.selectedOptions[0].disabled) { + select.value = operatorSearchProviders[0].id; + } }); - } diff --git a/web/operator-gui/operator-search.js b/web/operator-gui/operator-search.js index c6c355a..8ef97d6 100644 --- a/web/operator-gui/operator-search.js +++ b/web/operator-gui/operator-search.js @@ -26,7 +26,7 @@ } function normalizeManualSearchProvider(provider) { - return "naver"; + return provider === "google_search" ? "google_search" : "naver"; } global.OperatorSearch = {