(interfaceName)
| 8969 | const overall = d.success |
| 8970 | ? '<div class="mb-2 px-3 py-2 rounded border bg-green-950/40 border-green-900 text-green-400 text-sm">✓ All detector self-tests passed' + (d.scapy_available ? ' (including Scapy end-to-end)' : ' — install Scapy for the end-to-end leg') + '</div>' |
| 8971 | : '<div class="mb-2 px-3 py-2 rounded border bg-red-950/60 border-red-800 text-red-300 text-sm">🛑 A detector self-test FAILED — see below</div>'; |
| 8972 | let html = overall + |
| 8973 | '<table class="min-w-full text-xs text-gray-300 whitespace-nowrap"><thead>' + |
| 8974 | '<tr class="text-left text-gray-500"><th class="px-2 py-1">Scanner</th><th class="px-2 py-1">Scenarios</th><th class="px-2 py-1">End-to-end</th><th class="px-2 py-1">Result</th></tr>' + |
| 8975 | '</thead><tbody>'; |
| 8976 | ['igmp', 'ipv6', 'ndp', 'raguard', 'ntp', 'icmp', 'snmp', 'cert', 'tls', 'stp', 'smb', 'relay', 'dtp', 'cdp', 'vtp', 'eigrp', 'isis', 'fhrp', 'ospf', 'bgp', 'bgp_speaker', 'path_asymmetry'].forEach(k => { |
| 8977 | const s = d.suites[k]; if (!s) return; |
| 8978 | const okAll = s.success; |
| 8979 | html += `<tr class="border-t border-slate-800"> |
| 8980 | <td class="px-2 py-1">${names[k]}</td> |
| 8981 | <td class="px-2 py-1 font-mono ${okAll ? 'text-gray-300' : 'text-red-300'}">${s.passed}/${s.total}</td> |
| 8982 | <td class="px-2 py-1">${_scapyLegLabel(s.scapy)}</td> |
| 8983 | <td class="px-2 py-1">${okAll ? '<span class="text-green-400">PASS</span>' : '<span class="text-red-300">FAIL</span>'}</td> |
| 8984 | </tr>`; |
| 8985 | // list any failing scenarios |
| 8986 | (s.scenarios || []).filter(x => !x.pass).forEach(x => { |
| 8987 | html += `<tr class="border-t border-slate-900"><td class="px-2 py-1 text-red-300 text-xs" colspan="4">↳ ${escapeHtml(x.name)}: expected ${escapeHtml(String(x.expect))}, got ${escapeHtml(String(x.got))}</td></tr>`; |
| 8988 | }); |
| 8989 | }); |
| 8990 | html += '</tbody></table>'; |
| 8991 | out.innerHTML = html; |
| 8992 | } catch (e) { |
no test coverage detected