from pathlib import Path ROOT = Path(__file__).resolve().parents[1] def test_command_input_stays_enabled_while_execution_is_active(): source = (ROOT / "frontend" / "app.js").read_text(encoding="utf-8") lock_body = source.split("function lockInput()", 1)[1].split( "document.getElementById('chat-form')", 1, )[0] assert "chatInput.disabled = false" in lock_body assert "submitBtn.disabled = false" in lock_body assert "setSubmitButtonMode(true)" in lock_body def test_click_stops_but_enter_submits_replacement_command(): source = (ROOT / "frontend" / "app.js").read_text(encoding="utf-8") assert "enterSubmittedFromChatInput" in source assert "e.submitter === submitBtn && !submittedWithEnter" in source assert "JSON.stringify({ type: 'cancel' })" in source assert "type: 'run'" in source cancel_guard = source.index( "if (busy && e.submitter === submitBtn && !submittedWithEnter)" ) prompt_guard = source.index("if (!prompt || !socket", cancel_guard) assert cancel_guard < prompt_guard def test_empty_input_does_not_trigger_native_validation_before_stop(): html = (ROOT / "frontend" / "index.html").read_text(encoding="utf-8") input_markup = html.split('id="chat-input"', 1)[1].split(">", 1)[0] assert "required" not in input_markup def test_interrupt_requested_uses_stopping_button_state(): source = (ROOT / "frontend" / "app.js").read_text(encoding="utf-8") handler = source.split("case 'interrupt_requested':", 1)[1].split( "case 'interrupted':", 1, )[0] assert "setSubmitButtonMode(true, true)" in handler assert "Đang dừng lệnh..." in source def test_execution_button_has_spinner_and_stop_square(): script = (ROOT / "frontend" / "app.js").read_text(encoding="utf-8") styles = (ROOT / "frontend" / "index.css").read_text(encoding="utf-8") assert "stop-square" in script assert "is-executing" in script assert "#submit-btn.is-executing::before" in styles assert "@keyframes submitExecutionSpin" in styles def test_html_cache_busts_live_command_assets(): html = (ROOT / "frontend" / "index.html").read_text(encoding="utf-8") assert "/static/app.js?v=39" in html assert "/static/index.css?v=11" in html def test_clarification_result_is_not_recorded_as_failed_execution(): source = (ROOT / "frontend" / "app.js").read_text(encoding="utf-8") result_handler = source.split("case 'result':", 1)[1].split( "const badge =", 1, )[0] clarification_guard = result_handler.index( "['clarify', 'reject'].includes(msg.response_type)" ) history_write = result_handler.index("historyData.unshift") assert clarification_guard < history_write