async function runAllTestCases() { if (isRunning) return; isRunning = true; const outputBox = document.getElementById('output-box'); outputBox.textContent = '⏳ Running test cases...'; const code = editor.getValue(); try { const response = await fetch("{{ route('student.course.verify.testcases') }}", { method: "POST", headers: { "Content-Type": "application/json", "X-CSRF-TOKEN": csrfToken }, body: JSON.stringify({ code: code, language: currentLanguage, question_id: "{{ $id }}" }) }); const result = await response.json(); if (result.success) { for (let i = 1; i <= totalExpectedTests; i++) { const btn = testButtons[i]; if (btn) { btn.classList.remove('bg-green-600', 'bg-red-600', 'bg-gray-400'); btn.classList.add('bg-yellow-300', 'text-black'); } } await pollTestResults(result.data.result_url, 'test_run'); } else { outputBox.textContent = '❌ Error: ' + (result.message || 'Test execution failed'); } } catch (error) { outputBox.textContent = '❌ Error: ' + error.message; } finally { isRunning = false; } } function checkAllTestsPassed() { let allGreen = true; for (let i = 1; i <= 4; i++) { const btn = testButtons[i]; if (btn && !btn.classList.contains('bg-green-600')) { allGreen = false; break; } } return allGreen; } function forceCheckSubmitButton() { const allGreen = checkAllTestsPassed(); if (allGreen) { allTestsPassed = true; enableSubmitButton(); } } async function submitFinalSolution() { let allGreen = checkAllTestsPassed(); if (!allGreen && !allTestsPassed) { alert("Please pass all test cases first before submitting!"); return; } const submitBtn = document.getElementById('submitBtn'); if (!submitBtn) return; submitBtn.disabled = true; submitBtn.textContent = "Submitting..."; const outputBox = document.getElementById('output-box'); outputBox.textContent = '⏳ Submitting final solution...\n'; const code = editor.getValue(); const timerValue = document.getElementById("timer-value").textContent; const [minutes, seconds] = timerValue.split(':').map(Number); const totalTimeSeconds = (minutes * 60) + seconds; try { const response = await fetch("{{ route('student.course.compiler.store.report') }}", { method: "POST", headers: { "Content-Type": "application/json", "X-CSRF-TOKEN": csrfToken }, body: JSON.stringify({ code: code, language: currentLanguage, question_id: "{{ $id }}", submission_type: 'final_submission', total_time_seconds: totalTimeSeconds, course_id: "{{ $course_id ?? $coursename ?? '' }}", module_id: "{{ $module_id ?? $module ?? '' }}", topic_id: "{{ $topic_id ?? $topic ?? '' }}" }) }); const result = await response.json(); if (result.success) { outputBox.textContent = '✅ Final submission queued...\n'; await pollTestResults(result.data.result_url, 'final_submission'); } else { throw new Error(result.message || 'Submission failed'); } } catch (error) { outputBox.textContent = '❌ Error: ' + error.message; submitBtn.disabled = false; submitBtn.textContent = "✅ Submit Final"; } } /* ===== REST OF YOUR CODE UNCHANGED BELOW ===== */ async function pollTestResults(resultUrl, submissionType) { const outputBox = document.getElementById('output-box'); const submitBtn = document.getElementById('submitBtn'); let attempts = 0; const maxAttempts = 30; const pollInterval = 2000; outputBox.textContent = '⏳ Running test cases...'; return new Promise((resolve) => { const poll = async () => { attempts++; if (attempts > maxAttempts) { outputBox.textContent = '❌ Timeout - No response after ' + Math.round((maxAttempts * pollInterval) / 1000) + ' seconds'; if (submitBtn) { submitBtn.disabled = false; submitBtn.textContent = "✅ Submit Final"; } resolve(false); return; } try { const url = `${resultUrl}?_t=${Date.now()}`; const response = await fetch(url, { method: 'GET', headers: { 'Accept': 'application/json', 'Cache-Control': 'no-cache', 'Pragma': 'no-cache' }, credentials: 'same-origin', cache: 'no-store', }); if (response.status === 404) { clearInterval(intervalId); outputBox.textContent = '❌ Result not found (404)'; resolve(false); return; } if (response.status === 401) { clearInterval(intervalId); outputBox.textText = '❌ Session expired. Please login again.'; resolve(false); return; } if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const contentType = response.headers.get('content-type') || ''; if (!contentType.includes('application/json')) { throw new Error(`Expected JSON but got: ${contentType}`); } const data = await response.json(); const status = String(data.status || '').toLowerCase(); if (status === 'completed' || status === 'done') { clearInterval(intervalId); outputBox.textContent = '✅ Test results received!\n'; const testsAllPassed = displayTestResults({ results: data.results || [] }, submissionType); if (submissionType === 'final_submission' && testsAllPassed && data.report_stored) { // ✅ Keep ONLY: "Congratulations! Question Submitted" (already set by displayTestResults) setTimeout(() => autoExitCompiler(), 2000); } resolve(true); return; } if (status === 'error' || data.error) { clearInterval(intervalId); outputBox.textContent = `❌ Error: ${data.error || 'Unknown error'}`; if (submitBtn) { submitBtn.disabled = false; submitBtn.textContent = "✅ Submit Final"; } resolve(false); return; } outputBox.textContent = `⏳ Running test cases... (${attempts}/${maxAttempts})`; } catch (err) { outputBox.textContent = `⏳ Running test cases... (${attempts}/${maxAttempts})\nError: ${err.message}`; if ((attempts >= 5 && err.message.includes('NetworkError')) || err.message.includes('Failed to fetch')) { clearInterval(intervalId); outputBox.textContent = '❌ Network error. Please check your connection.'; resolve(false); } } }; poll(); const intervalId = setInterval(poll, pollInterval); }); } function displayTestResults(results, submissionType) { const outputBox = document.getElementById('output-box'); if (!results || !results.results || !Array.isArray(results.results)) { outputBox.textContent = '❌ Error: Invalid test results'; return false; } const testResults = results.results; // helper: 2nd / 3rd / 4th function ordinalSuffix(n) { if (n % 100 >= 11 && n % 100 <= 13) return 'th'; switch (n % 10) { case 1: return 'st'; case 2: return 'nd'; case 3: return 'rd'; default: return 'th'; } } function failMessage(n) { if (n === 1) return 'Test case 1 failed'; return `${n}${ordinalSuffix(n)} test case failed`; } let validTestCount = 0; let firstFailed = null; // { num, error? } for (let i = 0; i < testResults.length; i++) { const r = testResults[i]; if (r.skipped || !r.is_valid) continue; validTestCount++; const testNum = Number(r.test_num || validTestCount); if (r.passed) { const btn = testButtons[testNum]; if (btn) { btn.classList.remove('bg-yellow-300', 'text-black', 'bg-red-600', 'bg-gray-400'); btn.classList.add('bg-green-600', 'text-white'); } continue; } firstFailed = { num: testNum, error: r.error || '' }; const btn = testButtons[testNum]; if (btn) { btn.classList.remove('bg-yellow-300', 'text-black', 'bg-green-600', 'bg-gray-400'); btn.classList.add('bg-red-600', 'text-white'); } break; } if (validTestCount === 0) { allTestsPassed = false; disableSubmitButton(); outputBox.textContent = '⚠️ No valid test cases to execute'; return false; } if (firstFailed) { allTestsPassed = false; disableSubmitButton(); let msg = failMessage(firstFailed.num); if (firstFailed.error) msg += `\n${firstFailed.error}`; outputBox.textContent = msg; return false; } allTestsPassed = true; if (submissionType === 'final_submission') { outputBox.textContent = 'Congratulations! Question Submitted'; return true; } if (IS_COMPLETED) { outputBox.textContent = 'Congratulation: Problem solved'; return true; } outputBox.textContent = 'Congratulation: Problem solved. You can now submit.'; enableSubmitButton(); return true; } function enableSubmitButton() { const submitBtn = document.getElementById('submitBtn'); if (!submitBtn) return; submitBtn.disabled = false; submitBtn.classList.remove("opacity-50", "cursor-not-allowed"); submitBtn.textContent = "✅ Submit Final"; allTestsPassed = true; for (let i = 1; i <= 4; i++) { const btn = testButtons[i]; if (btn) { btn.classList.remove('bg-yellow-300', 'text-black', 'bg-red-600', 'bg-gray-400'); btn.classList.add('bg-green-600', 'text-white'); } } }