first commit
This commit is contained in:
@@ -0,0 +1,963 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>QA Automation Test Plan Report</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<style>
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
:root {
|
||||
--color-automated: #22c55e;
|
||||
--color-partial: #f59e0b;
|
||||
--color-manual: #ef4444;
|
||||
--color-core: #3b82f6;
|
||||
--color-pbx: #8b5cf6;
|
||||
--color-check: #64748b;
|
||||
--color-write: #0ea5e9;
|
||||
--bg-header: #0f172a;
|
||||
--bg-subheader: #1e293b;
|
||||
--bg-body: #f8fafc;
|
||||
--bg-card: #ffffff;
|
||||
--bg-row-alt: #f1f5f9;
|
||||
--border: #e2e8f0;
|
||||
--text-primary: #0f172a;
|
||||
--text-secondary: #475569;
|
||||
--text-muted: #94a3b8;
|
||||
--radius: 8px;
|
||||
--shadow: 0 1px 3px rgba(0,0,0,.08), 0 1px 2px rgba(0,0,0,.06);
|
||||
--shadow-md: 0 4px 6px -1px rgba(0,0,0,.1), 0 2px 4px -1px rgba(0,0,0,.06);
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
background: var(--bg-body);
|
||||
color: var(--text-primary);
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* ── HEADER ── */
|
||||
.site-header {
|
||||
background: var(--bg-header);
|
||||
padding: 32px 48px 28px;
|
||||
border-bottom: 3px solid #334155;
|
||||
}
|
||||
.site-header h1 {
|
||||
font-size: 26px;
|
||||
font-weight: 700;
|
||||
color: #f1f5f9;
|
||||
letter-spacing: -0.4px;
|
||||
}
|
||||
.site-header .subtitle {
|
||||
font-size: 14px;
|
||||
color: #94a3b8;
|
||||
margin-top: 4px;
|
||||
}
|
||||
.site-header .meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
margin-top: 14px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.meta-chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
font-size: 12px;
|
||||
color: #cbd5e1;
|
||||
background: #1e293b;
|
||||
border: 1px solid #334155;
|
||||
border-radius: 20px;
|
||||
padding: 3px 12px;
|
||||
}
|
||||
.meta-chip .dot {
|
||||
width: 7px; height: 7px; border-radius: 50%;
|
||||
background: #22c55e;
|
||||
}
|
||||
|
||||
/* ── MAIN ── */
|
||||
main {
|
||||
max-width: 1500px;
|
||||
margin: 0 auto;
|
||||
padding: 32px 32px 60px;
|
||||
}
|
||||
|
||||
/* ── SECTION TITLES ── */
|
||||
.section-title {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .7px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
/* ── SUMMARY CARDS ── */
|
||||
.cards-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 16px;
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
@media (max-width: 900px) { .cards-row { grid-template-columns: repeat(2,1fr); } }
|
||||
@media (max-width: 500px) { .cards-row { grid-template-columns: 1fr; } }
|
||||
|
||||
.card {
|
||||
background: var(--bg-card);
|
||||
border-radius: var(--radius);
|
||||
box-shadow: var(--shadow-md);
|
||||
padding: 22px 24px 18px;
|
||||
border-top: 4px solid var(--card-accent, #94a3b8);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.card::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -30px; right: -30px;
|
||||
width: 100px; height: 100px;
|
||||
border-radius: 50%;
|
||||
background: var(--card-accent, #94a3b8);
|
||||
opacity: .05;
|
||||
}
|
||||
.card-label {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .8px;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.card-number {
|
||||
font-size: 42px;
|
||||
font-weight: 800;
|
||||
line-height: 1;
|
||||
color: var(--card-accent, var(--text-primary));
|
||||
}
|
||||
.card-pct {
|
||||
font-size: 13px;
|
||||
color: var(--text-muted);
|
||||
margin-top: 4px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.card-bar-track {
|
||||
height: 6px;
|
||||
background: #e2e8f0;
|
||||
border-radius: 3px;
|
||||
margin-top: 14px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.card-bar-fill {
|
||||
height: 100%;
|
||||
border-radius: 3px;
|
||||
background: var(--card-accent, #94a3b8);
|
||||
transition: width .6s ease;
|
||||
}
|
||||
|
||||
/* ── CHARTS ROW ── */
|
||||
.charts-row {
|
||||
display: grid;
|
||||
grid-template-columns: 380px 1fr;
|
||||
gap: 20px;
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
@media (max-width: 900px) { .charts-row { grid-template-columns: 1fr; } }
|
||||
|
||||
.chart-card {
|
||||
background: var(--bg-card);
|
||||
border-radius: var(--radius);
|
||||
box-shadow: var(--shadow-md);
|
||||
padding: 24px;
|
||||
}
|
||||
.chart-card h3 {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .6px;
|
||||
margin-bottom: 18px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
.chart-wrap {
|
||||
position: relative;
|
||||
}
|
||||
.chart-wrap-doughnut {
|
||||
max-width: 280px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.doughnut-legend {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
margin-top: 18px;
|
||||
}
|
||||
.legend-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
.legend-dot {
|
||||
width: 10px; height: 10px; border-radius: 2px; flex-shrink: 0;
|
||||
}
|
||||
.legend-val { margin-left: auto; font-weight: 600; color: var(--text-primary); }
|
||||
|
||||
/* ── FILTER BAR ── */
|
||||
.filter-bar {
|
||||
background: var(--bg-card);
|
||||
border-radius: var(--radius);
|
||||
box-shadow: var(--shadow);
|
||||
padding: 16px 20px;
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
}
|
||||
.filter-bar label {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: var(--text-secondary);
|
||||
margin-right: 4px;
|
||||
}
|
||||
.filter-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
.filter-bar select, .filter-bar input {
|
||||
font-size: 13px;
|
||||
padding: 6px 10px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
background: var(--bg-body);
|
||||
color: var(--text-primary);
|
||||
outline: none;
|
||||
transition: border-color .15s;
|
||||
}
|
||||
.filter-bar select:focus, .filter-bar input:focus {
|
||||
border-color: #3b82f6;
|
||||
box-shadow: 0 0 0 3px rgba(59,130,246,.12);
|
||||
}
|
||||
.filter-bar input[type="text"] { min-width: 220px; }
|
||||
.filter-divider { width: 1px; height: 24px; background: var(--border); }
|
||||
.btn-reset {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
padding: 6px 14px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--border);
|
||||
background: var(--bg-body);
|
||||
color: var(--text-secondary);
|
||||
cursor: pointer;
|
||||
transition: background .15s, color .15s;
|
||||
}
|
||||
.btn-reset:hover { background: #e2e8f0; color: var(--text-primary); }
|
||||
.result-count {
|
||||
margin-left: auto;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
.result-count span { color: var(--text-primary); font-size: 14px; }
|
||||
|
||||
/* ── TABLE ── */
|
||||
.table-wrap {
|
||||
background: var(--bg-card);
|
||||
border-radius: var(--radius);
|
||||
box-shadow: var(--shadow-md);
|
||||
overflow: hidden;
|
||||
}
|
||||
.table-scroll { overflow-x: auto; }
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 13px;
|
||||
}
|
||||
thead th {
|
||||
background: var(--bg-subheader);
|
||||
color: #94a3b8;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .7px;
|
||||
padding: 12px 14px;
|
||||
text-align: left;
|
||||
white-space: nowrap;
|
||||
border-bottom: 2px solid #334155;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
transition: color .15s;
|
||||
}
|
||||
thead th:hover { color: #e2e8f0; }
|
||||
thead th.sorted-asc::after { content: ' ↑'; color: #3b82f6; }
|
||||
thead th.sorted-desc::after { content: ' ↓'; color: #3b82f6; }
|
||||
|
||||
tbody tr {
|
||||
border-left: 3px solid transparent;
|
||||
transition: background .1s;
|
||||
}
|
||||
tbody tr:nth-child(even) { background: var(--bg-row-alt); }
|
||||
tbody tr:hover { background: #e0f2fe !important; }
|
||||
|
||||
tbody tr.status-automated { border-left-color: var(--color-automated); }
|
||||
tbody tr.status-partial { border-left-color: var(--color-partial); }
|
||||
tbody tr.status-manual { border-left-color: var(--color-manual); }
|
||||
|
||||
td {
|
||||
padding: 10px 14px;
|
||||
color: var(--text-primary);
|
||||
vertical-align: middle;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
td.num {
|
||||
color: var(--text-muted);
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
width: 42px;
|
||||
text-align: center;
|
||||
}
|
||||
td.td-id {
|
||||
font-family: "SF Mono", "Consolas", "Liberation Mono", monospace;
|
||||
font-size: 11.5px;
|
||||
color: #334155;
|
||||
max-width: 200px;
|
||||
}
|
||||
.id-text {
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 190px;
|
||||
}
|
||||
td.td-desc {
|
||||
min-width: 260px;
|
||||
max-width: 380px;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
td.td-file {
|
||||
font-family: "SF Mono", "Consolas", monospace;
|
||||
font-size: 11px;
|
||||
color: var(--text-muted);
|
||||
max-width: 200px;
|
||||
}
|
||||
.file-text {
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
/* ── BADGES ── */
|
||||
.badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .4px;
|
||||
padding: 2px 9px;
|
||||
border-radius: 20px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.badge-automated { background: #dcfce7; color: #15803d; }
|
||||
.badge-partial { background: #fef3c7; color: #b45309; }
|
||||
.badge-manual { background: #fee2e2; color: #b91c1c; }
|
||||
.badge-core { background: #dbeafe; color: #1d4ed8; }
|
||||
.badge-pbx { background: #ede9fe; color: #6d28d9; }
|
||||
.badge-check { background: #f1f5f9; color: #475569; }
|
||||
.badge-write { background: #e0f2fe; color: #0369a1; }
|
||||
|
||||
.badge-dot {
|
||||
width: 6px; height: 6px; border-radius: 50%;
|
||||
}
|
||||
.badge-automated .badge-dot { background: #15803d; }
|
||||
.badge-partial .badge-dot { background: #b45309; }
|
||||
.badge-manual .badge-dot { background: #b91c1c; }
|
||||
|
||||
/* ── COPY BUTTON ── */
|
||||
.copy-btn {
|
||||
display: none;
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
padding: 2px 7px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--border);
|
||||
background: #fff;
|
||||
color: var(--text-secondary);
|
||||
cursor: pointer;
|
||||
margin-left: 4px;
|
||||
transition: background .1s, color .1s;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.copy-btn:hover { background: #3b82f6; color: #fff; border-color: #3b82f6; }
|
||||
tbody tr:hover .copy-btn { display: inline; }
|
||||
.copy-btn.copied { background: #22c55e; color: #fff; border-color: #22c55e; }
|
||||
|
||||
/* ── NO RESULTS ── */
|
||||
.no-results {
|
||||
padding: 48px;
|
||||
text-align: center;
|
||||
color: var(--text-muted);
|
||||
font-size: 14px;
|
||||
display: none;
|
||||
}
|
||||
.no-results svg { display: block; margin: 0 auto 12px; opacity: .3; }
|
||||
|
||||
/* ── FOOTER ── */
|
||||
footer {
|
||||
text-align: center;
|
||||
padding: 24px 32px;
|
||||
font-size: 12px;
|
||||
color: var(--text-muted);
|
||||
border-top: 1px solid var(--border);
|
||||
margin-top: 40px;
|
||||
}
|
||||
footer strong { color: var(--text-secondary); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- ══════════════════════════════════ HEADER ══════════════════════════════════ -->
|
||||
<header class="site-header">
|
||||
<h1>QA Automation Test Plan Report</h1>
|
||||
<p class="subtitle">PBX / Core Systems</p>
|
||||
<div class="meta">
|
||||
<span class="meta-chip"><span class="dot"></span> Generated 2026-05-22</span>
|
||||
<span class="meta-chip">Playwright + TypeScript + MCP</span>
|
||||
<span class="meta-chip">86 Test Cases</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- ══════════════════════════════════ MAIN ══════════════════════════════════ -->
|
||||
<main>
|
||||
|
||||
<!-- SUMMARY CARDS -->
|
||||
<p class="section-title">Overview</p>
|
||||
<div class="cards-row">
|
||||
<div class="card" style="--card-accent:#3b82f6;">
|
||||
<div class="card-label">Total Tests</div>
|
||||
<div class="card-number">86</div>
|
||||
<div class="card-pct">Across 18 groups</div>
|
||||
<div class="card-bar-track"><div class="card-bar-fill" style="width:100%;"></div></div>
|
||||
</div>
|
||||
<div class="card" style="--card-accent:#22c55e;">
|
||||
<div class="card-label">Automated</div>
|
||||
<div class="card-number">34</div>
|
||||
<div class="card-pct">39.5% of total</div>
|
||||
<div class="card-bar-track"><div class="card-bar-fill" style="width:39.5%;"></div></div>
|
||||
</div>
|
||||
<div class="card" style="--card-accent:#f59e0b;">
|
||||
<div class="card-label">Partial</div>
|
||||
<div class="card-number">16</div>
|
||||
<div class="card-pct">18.6% of total</div>
|
||||
<div class="card-bar-track"><div class="card-bar-fill" style="width:18.6%;"></div></div>
|
||||
</div>
|
||||
<div class="card" style="--card-accent:#ef4444;">
|
||||
<div class="card-label">Manual</div>
|
||||
<div class="card-number">36</div>
|
||||
<div class="card-pct">41.9% of total</div>
|
||||
<div class="card-bar-track"><div class="card-bar-fill" style="width:41.9%;"></div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- CHARTS -->
|
||||
<p class="section-title">Charts</p>
|
||||
<div class="charts-row">
|
||||
<!-- Doughnut -->
|
||||
<div class="chart-card">
|
||||
<h3>Automation Distribution</h3>
|
||||
<div class="chart-wrap chart-wrap-doughnut">
|
||||
<canvas id="chartDoughnut"></canvas>
|
||||
</div>
|
||||
<div class="doughnut-legend">
|
||||
<div class="legend-item"><span class="legend-dot" style="background:#22c55e;"></span> Automated <span class="legend-val">34</span></div>
|
||||
<div class="legend-item"><span class="legend-dot" style="background:#f59e0b;"></span> Partial <span class="legend-val">16</span></div>
|
||||
<div class="legend-item"><span class="legend-dot" style="background:#ef4444;"></span> Manual <span class="legend-val">36</span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Horizontal stacked bar -->
|
||||
<div class="chart-card">
|
||||
<h3>Automation by Group</h3>
|
||||
<div class="chart-wrap" style="height:520px; position:relative;">
|
||||
<canvas id="chartBar"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- FILTER BAR -->
|
||||
<div class="filter-bar">
|
||||
<div class="filter-group">
|
||||
<label for="fModule">Module</label>
|
||||
<select id="fModule">
|
||||
<option value="">All</option>
|
||||
<option value="core">Core</option>
|
||||
<option value="pbx">PBX</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-group">
|
||||
<label for="fGroup">Group</label>
|
||||
<select id="fGroup">
|
||||
<option value="">All</option>
|
||||
<option value="core-repository">core-repository</option>
|
||||
<option value="core-documentation">core-documentation</option>
|
||||
<option value="core-features">core-features</option>
|
||||
<option value="core-virtualization">core-virtualization</option>
|
||||
<option value="core-security">core-security</option>
|
||||
<option value="core-installation">core-installation</option>
|
||||
<option value="core-upgrade">core-upgrade</option>
|
||||
<option value="core-interoperability">core-interoperability</option>
|
||||
<option value="pbx-configuration">pbx-configuration</option>
|
||||
<option value="pbx-extensions">pbx-extensions</option>
|
||||
<option value="pbx-calls">pbx-calls</option>
|
||||
<option value="pbx-codecs">pbx-codecs</option>
|
||||
<option value="pbx-calls-security">pbx-calls-security</option>
|
||||
<option value="pbx-performance">pbx-performance</option>
|
||||
<option value="pbx-availability">pbx-availability</option>
|
||||
<option value="pbx-management">pbx-management</option>
|
||||
<option value="pbx-logs">pbx-logs</option>
|
||||
<option value="pbx-regression">pbx-regression</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-group">
|
||||
<label for="fAction">Action</label>
|
||||
<select id="fAction">
|
||||
<option value="">All</option>
|
||||
<option value="check">check</option>
|
||||
<option value="write">write</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-group">
|
||||
<label for="fStatus">Status</label>
|
||||
<select id="fStatus">
|
||||
<option value="">All</option>
|
||||
<option value="automated">automated</option>
|
||||
<option value="partial">partial</option>
|
||||
<option value="manual">manual</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-divider"></div>
|
||||
<div class="filter-group">
|
||||
<label for="fSearch">Search</label>
|
||||
<input type="text" id="fSearch" placeholder="ID, description, group, file…" />
|
||||
</div>
|
||||
<button class="btn-reset" id="btnReset">Clear</button>
|
||||
<div class="result-count" id="resultCount"><span id="visibleCount">86</span> / 86 tests</div>
|
||||
</div>
|
||||
|
||||
<!-- TABLE -->
|
||||
<div class="table-wrap">
|
||||
<div class="table-scroll">
|
||||
<table id="testTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-col="idx">#</th>
|
||||
<th data-col="id">Test ID</th>
|
||||
<th data-col="group">Group</th>
|
||||
<th data-col="module">Module</th>
|
||||
<th data-col="action">Action</th>
|
||||
<th data-col="description">Description</th>
|
||||
<th data-col="status">Status</th>
|
||||
<th data-col="file">Spec File</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="tableBody"></tbody>
|
||||
</table>
|
||||
<div class="no-results" id="noResults">
|
||||
<svg width="40" height="40" fill="none" viewBox="0 0 24 24" stroke="currentColor"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg>
|
||||
No tests match the current filters.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
Generated automatically by <strong>QA Automation Framework</strong> · Playwright + TypeScript + MCP
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
/* ══════════════════════════════ DATA ══════════════════════════════ */
|
||||
const TESTS = [
|
||||
// === CORE-REPOSITORY (6) ===
|
||||
{ id: "core-repository-check-presence_of_release_notes_and_changelog", group: "core-repository", module: "core", action: "check", description: "Verify Release Notes and Changelog are present in the software repository", status: "automated", file: "tests/core/repository.spec.ts" },
|
||||
{ id: "core-repository-check-presence_of_trouble_shooting_guide", group: "core-repository", module: "core", action: "check", description: "Verify Troubleshooting Guide is present in the repository", status: "automated", file: "tests/core/repository.spec.ts" },
|
||||
{ id: "core-repository-check-presence_of_all_mandatory_core-documentation_files", group: "core-repository", module: "core", action: "check", description: "Verify all mandatory core documentation files are listed in the repository", status: "automated", file: "tests/core/repository.spec.ts" },
|
||||
{ id: "core-repository-check-presence_of_main_software_version_all_hypervisors", group: "core-repository", module: "core", action: "check", description: "Verify main software version packages are available for all supported hypervisors", status: "automated", file: "tests/core/repository.spec.ts" },
|
||||
{ id: "core-repository-check-presence_of_patch_version_and_its_compatibility", group: "core-repository", module: "core", action: "check", description: "Verify patch version packages and compatibility matrices are present", status: "automated", file: "tests/core/repository.spec.ts" },
|
||||
{ id: "core-repository-check-presence_of_companion_tools_and_supporting_scripts", group: "core-repository", module: "core", action: "check", description: "Verify companion tools and supporting scripts are available in the repository", status: "automated", file: "tests/core/repository.spec.ts" },
|
||||
|
||||
// === CORE-DOCUMENTATION (4) ===
|
||||
{ id: "core-documentation-write-release_notes_feature_changes_and_known_issues", group: "core-documentation", module: "core", action: "write", description: "Extract and record Release Notes, feature changes, and known issues", status: "automated", file: "tests/core/documentation.spec.ts" },
|
||||
{ id: "core-documentation-check-accuracy_installation_and_configuration_guide", group: "core-documentation", module: "core", action: "check", description: "Verify accuracy of the Installation and Configuration Guide against actual system behaviour", status: "manual", manualReason: "Requires expert human review: QA engineer must follow the guide step-by-step on a clean system", file: "tests/core/documentation.spec.ts" },
|
||||
{ id: "core-documentation-check-accuracy_trouble_shooting_guide", group: "core-documentation", module: "core", action: "check", description: "Verify the Troubleshooting Guide procedures are accurate and complete", status: "manual", manualReason: "Requires expert human review to validate each troubleshooting scenario on a real system", file: "tests/core/documentation.spec.ts" },
|
||||
{ id: "core-documentation-write-changed_cli_configuration_commands", group: "core-documentation", module: "core", action: "write", description: "Extract and record changed CLI configuration commands for this release", status: "automated", file: "tests/core/documentation.spec.ts" },
|
||||
|
||||
// === CORE-FEATURES (7) ===
|
||||
{ id: "core-features-write-summary_of_each_new_feature_per_release", group: "core-features", module: "core", action: "write", description: "Extract and record a summary of each new feature included in this release", status: "automated", file: "tests/core/features.spec.ts" },
|
||||
{ id: "core-features-check-integration_of_new_features_with_existing_call_processing_and_modules", group: "core-features", module: "core", action: "check", description: "Verify new features integrate correctly with existing call processing and system modules", status: "partial", partialNotes: "UI config checks automated; actual call-flow integration requires active telephony endpoints", file: "tests/core/features.spec.ts" },
|
||||
{ id: "core-features-check-backward_compatibility_of_new_features", group: "core-features", module: "core", action: "check", description: "Verify new features are backward-compatible with existing configurations and older endpoint firmware", status: "partial", partialNotes: "Configuration import checks automated; functional compatibility with older endpoints requires manual testing", file: "tests/core/features.spec.ts" },
|
||||
{ id: "core-features-check-performance_and_resource_impact_assessment_of_new_features", group: "core-features", module: "core", action: "check", description: "Assess CPU/memory/network resource impact of new features under representative call load", status: "manual", manualReason: "Requires SIPp load generator, performance monitoring infrastructure, and sustained load testing", file: "tests/core/features.spec.ts" },
|
||||
{ id: "core-features-write-analysis_of_new_security_risks", group: "core-features", module: "core", action: "write", description: "Identify, analyse, and document new security risks introduced by features in this release", status: "manual", manualReason: "Security risk analysis requires expert threat modelling, source code review, and human judgment", file: "tests/core/features.spec.ts" },
|
||||
{ id: "core-features-write-ui_ux_and_admin_portal_changes_validation", group: "core-features", module: "core", action: "write", description: "Capture and record all UI/UX changes in the admin portal for this release", status: "automated", file: "tests/core/features.spec.ts" },
|
||||
{ id: "core-features-check-provisioning_changes_for_new_features", group: "core-features", module: "core", action: "check", description: "Verify provisioning settings for new features are accessible in the admin portal", status: "automated", file: "tests/core/features.spec.ts" },
|
||||
|
||||
// === CORE-VIRTUALIZATION (8) ===
|
||||
{ id: "core-virtualization-write-supported_hypervisors_deployment", group: "core-virtualization", module: "core", action: "write", description: "Record the list of supported hypervisors and deployment methods from the download portal", status: "automated", file: "tests/core/virtualization.spec.ts" },
|
||||
{ id: "core-virtualization-write-guest_agent_integration", group: "core-virtualization", module: "core", action: "write", description: "Record guest agent integration status for each supported hypervisor", status: "manual", manualReason: "Requires direct access to hypervisor console (vCenter, Hyper-V Manager, virsh)", file: "tests/core/virtualization.spec.ts" },
|
||||
{ id: "core-virtualization-write-resource_allocation_vcpu_vram_vdisk", group: "core-virtualization", module: "core", action: "write", description: "Record vCPU, vRAM, and vDisk resource allocation for deployment templates", status: "manual", manualReason: "Hypervisor management plane access required; not exposed in PBX admin portal", file: "tests/core/virtualization.spec.ts" },
|
||||
{ id: "core-virtualization-write-virtual_networking_configuration", group: "core-virtualization", module: "core", action: "write", description: "Record virtual networking configuration: virtual switches, VLANs, NIC assignments", status: "manual", manualReason: "Requires access to hypervisor networking configuration and virtual switch management", file: "tests/core/virtualization.spec.ts" },
|
||||
{ id: "core-virtualization-check-live_migration_and_active_call_preservation", group: "core-virtualization", module: "core", action: "check", description: "Verify VM live migration preserves active calls without interruption", status: "manual", manualReason: "Requires HA infrastructure with 2+ hypervisor hosts and active SIP call sessions during migration", file: "tests/core/virtualization.spec.ts" },
|
||||
{ id: "core-virtualization-check-snapshot_backup_restore_and_consistency", group: "core-virtualization", module: "core", action: "check", description: "Verify VM snapshot and restore operations maintain full system consistency", status: "partial", partialNotes: "Post-restore consistency checks via admin portal automated; snapshot/restore operation needs hypervisor access", file: "tests/core/virtualization.spec.ts" },
|
||||
{ id: "core-virtualization-check-hypervisor_level_high_availability", group: "core-virtualization", module: "core", action: "check", description: "Verify hypervisor-level HA restarts the PBX VM after a host failure", status: "manual", manualReason: "Requires physical HA cluster with multiple hypervisor hosts; host failure must be simulated", file: "tests/core/virtualization.spec.ts" },
|
||||
{ id: "core-virtualization-write-storage_thin_vs_thick_provisioning", group: "core-virtualization", module: "core", action: "write", description: "Record storage provisioning type and performance implications for each hypervisor", status: "manual", manualReason: "Storage details only visible in hypervisor storage management interface", file: "tests/core/virtualization.spec.ts" },
|
||||
|
||||
// === CORE-SECURITY (11) ===
|
||||
{ id: "core-security-write-strong_password_policy", group: "core-security", module: "core", action: "write", description: "Navigate to security settings and record the current password policy configuration", status: "automated", file: "tests/core/security.spec.ts" },
|
||||
{ id: "core-security-check-sip_tls_and_srtp_encryption_enabled_by_default", group: "core-security", module: "core", action: "check", description: "Verify SIP TLS and SRTP media encryption are enabled by default", status: "automated", file: "tests/core/security.spec.ts" },
|
||||
{ id: "core-security-check-ip_access_control_lists", group: "core-security", module: "core", action: "check", description: "Verify IP Access Control Lists are properly configured and enforced", status: "automated", file: "tests/core/security.spec.ts" },
|
||||
{ id: "core-security-check-unnecessary_services_and_ports_are_disabled", group: "core-security", module: "core", action: "check", description: "Verify unnecessary services and network ports are disabled", status: "partial", partialNotes: "Service status via admin portal automated; port scanning requires nmap from external host", file: "tests/core/security.spec.ts" },
|
||||
{ id: "core-security-check-security_event_logging_and_audit_trail_is_active", group: "core-security", module: "core", action: "check", description: "Verify security event logging and audit trail are active and recording events", status: "automated", file: "tests/core/security.spec.ts" },
|
||||
{ id: "core-security-check-auto_enrollment_certificate_management_process", group: "core-security", module: "core", action: "check", description: "Verify auto-enrollment certificate management (ACME/Let's Encrypt) is configured and functional", status: "automated", file: "tests/core/security.spec.ts" },
|
||||
{ id: "core-security-check-known_vulnerable_dependencies_in_release_artifacts", group: "core-security", module: "core", action: "check", description: "Verify release artifacts do not contain known vulnerable dependencies (CVE scan)", status: "partial", partialNotes: "Vulnerability report page in portal automated; actual CVE scanning requires Trivy/OWASP-DC in CI", file: "tests/core/security.spec.ts" },
|
||||
{ id: "core-security-check-previous_security_findings_have_been_remediated_before_release", group: "core-security", module: "core", action: "check", description: "Verify all previously identified security findings are remediated before release", status: "manual", manualReason: "Requires access to security issue tracker and cross-referencing CVEs with the release changelog", file: "tests/core/security.spec.ts" },
|
||||
{ id: "core-security-check-obtain_formal_security_team_sign_off_for_the_new_version", group: "core-security", module: "core", action: "check", description: "Obtain formal written sign-off from the Security team approving the release", status: "manual", manualReason: "Governance gate requiring human decision and formal sign-off document; cannot be automated", file: "tests/core/security.spec.ts" },
|
||||
{ id: "core-security-check-verify_role_based_access_control_and_least_privilege_principle", group: "core-security", module: "core", action: "check", description: "Verify RBAC roles are correctly defined and least-privilege principle is enforced", status: "automated", file: "tests/core/security.spec.ts" },
|
||||
{ id: "core-security-check-encryption_of_sensitive_data_at_rest_and_in_transit", group: "core-security", module: "core", action: "check", description: "Verify sensitive data is encrypted at rest (DB) and in transit (HTTPS/TLS)", status: "partial", partialNotes: "HTTPS/TLS configuration verified via portal; DB-level at-rest encryption requires SSH/CLI access", file: "tests/core/security.spec.ts" },
|
||||
|
||||
// === CORE-INSTALLATION (4) ===
|
||||
{ id: "core-installation-check-fresh_installation", group: "core-installation", module: "core", action: "check", description: "Verify complete fresh installation from scratch succeeds on each supported hypervisor", status: "manual", manualReason: "Requires provisioning a clean VM, mounting ISO/OVA, and running the installation wizard", file: "tests/core/installation.spec.ts" },
|
||||
{ id: "core-installation-check-post_install_service_status_and_port_verification", group: "core-installation", module: "core", action: "check", description: "Verify all required services are running and expected ports are open after installation", status: "partial", partialNotes: "Service status via admin portal automated; TCP port verification requires nmap from external host", file: "tests/core/installation.spec.ts" },
|
||||
{ id: "core-installation-check-initial_configuration", group: "core-installation", module: "core", action: "check", description: "Verify the initial configuration wizard completes successfully", status: "automated", file: "tests/core/installation.spec.ts" },
|
||||
{ id: "core-installation-check-license_activation", group: "core-installation", module: "core", action: "check", description: "Verify license activation completes successfully and licensed features are unlocked", status: "automated", file: "tests/core/installation.spec.ts" },
|
||||
|
||||
// === CORE-UPGRADE (6) ===
|
||||
{ id: "core-upgrade-check-in_place_upgrade_from_previous_major_version", group: "core-upgrade", module: "core", action: "check", description: "Verify in-place upgrade from the previous major version completes successfully without data loss", status: "manual", manualReason: "Requires a running instance of the previous version and dedicated upgrade test environment", file: "tests/core/upgrade.spec.ts" },
|
||||
{ id: "core-upgrade-check-configuration_and_database_migration_validation", group: "core-upgrade", module: "core", action: "check", description: "Verify configuration objects and database content migrate correctly after upgrade", status: "partial", partialNotes: "UI configuration checks automated; DB schema integrity validation requires direct database access", file: "tests/core/upgrade.spec.ts" },
|
||||
{ id: "core-upgrade-check-schema_upgrade_and_data_integrity_check", group: "core-upgrade", module: "core", action: "check", description: "Verify database schema is correctly upgraded and all data maintains integrity", status: "manual", manualReason: "Requires direct database access (psql/mysql) and schema comparison tooling", file: "tests/core/upgrade.spec.ts" },
|
||||
{ id: "core-upgrade-check-rollback_procedure_execution_and_verification", group: "core-upgrade", module: "core", action: "check", description: "Verify the rollback procedure successfully reverts the system to the previous version", status: "manual", manualReason: "Requires VM snapshot revert or package downgrade; needs physical/hypervisor-level access", file: "tests/core/upgrade.spec.ts" },
|
||||
{ id: "core-upgrade-check-post_upgrade_regression_of_core_features", group: "core-upgrade", module: "core", action: "check", description: "Run automated regression checks on core portal features after upgrade", status: "automated", file: "tests/core/upgrade.spec.ts" },
|
||||
{ id: "core-upgrade-check-zero_downtime_upgrade_where_supported", group: "core-upgrade", module: "core", action: "check", description: "Verify zero-downtime upgrade keeps the service available throughout the upgrade process", status: "manual", manualReason: "Requires active call sessions monitored during the upgrade window; calls cannot be managed by Playwright", file: "tests/core/upgrade.spec.ts" },
|
||||
|
||||
// === CORE-INTEROPERABILITY (2) ===
|
||||
{ id: "core-interoperability-check-addm", group: "core-interoperability", module: "core", action: "check", description: "Verify ADDM integration is configured and reporting discovery data correctly", status: "automated", file: "tests/core/interoperability.spec.ts" },
|
||||
{ id: "core-interoperability-check-siem", group: "core-interoperability", module: "core", action: "check", description: "Verify SIEM integration is configured and forwarding security events", status: "automated", file: "tests/core/interoperability.spec.ts" },
|
||||
|
||||
// === PBX-CONFIGURATION (4) ===
|
||||
{ id: "pbx-configuration-check-initial_system_setup_and_network_settings", group: "pbx-configuration", module: "pbx", action: "check", description: "Verify initial PBX system setup and network settings are correctly configured", status: "automated", file: "tests/pbx/configuration.spec.ts" },
|
||||
{ id: "pbx-configuration-check-dialplan_route_and_outbound_rule_creation", group: "pbx-configuration", module: "pbx", action: "check", description: "Verify creation and validation of dial plan rules and outbound routes", status: "automated", file: "tests/pbx/configuration.spec.ts" },
|
||||
{ id: "pbx-configuration-check-backup_restore_full_system_configuration", group: "pbx-configuration", module: "pbx", action: "check", description: "Verify full system configuration backup and restore operations work correctly", status: "automated", file: "tests/pbx/configuration.spec.ts" },
|
||||
{ id: "pbx-configuration-check-bulk_csv_import_export_of_settings", group: "pbx-configuration", module: "pbx", action: "check", description: "Verify bulk CSV import and export of settings", status: "automated", file: "tests/pbx/configuration.spec.ts" },
|
||||
|
||||
// === PBX-EXTENSIONS (3) ===
|
||||
{ id: "pbx-extensions-check-create_modify_delete_sip_extensions", group: "pbx-extensions", module: "pbx", action: "check", description: "Verify full CRUD lifecycle for SIP extensions in the admin portal", status: "automated", file: "tests/pbx/extensions.spec.ts" },
|
||||
{ id: "pbx-extensions-check-extension_registration_with_deskphones_softphones", group: "pbx-extensions", module: "pbx", action: "check", description: "Verify SIP extension registers successfully from a desk phone and a softphone client", status: "manual", manualReason: "Requires physical SIP desk phone (Yealink, Polycom) or softphone (Zoiper, MicroSIP)", file: "tests/pbx/extensions.spec.ts" },
|
||||
{ id: "pbx-extensions-check-device_provisioning", group: "pbx-extensions", module: "pbx", action: "check", description: "Verify automatic device provisioning: profile generation and server URL accessible", status: "partial", partialNotes: "Provisioning profile creation via admin portal automated; actual device provisioning requires physical phone", file: "tests/pbx/extensions.spec.ts" },
|
||||
|
||||
// === PBX-CALLS (7) ===
|
||||
{ id: "pbx-calls-check-internal_extension_to_extension_audio_call", group: "pbx-calls", module: "pbx", action: "check", description: "Verify internal extension-to-extension audio call connects with bi-directional audio", status: "manual", manualReason: "Requires two active SIP endpoints and human verification of two-way audio quality", file: "tests/pbx/calls.spec.ts" },
|
||||
{ id: "pbx-calls-check-inbound_call_from_sip_trunk_pstn", group: "pbx-calls", module: "pbx", action: "check", description: "Verify inbound call from a SIP trunk/PSTN routes correctly to the configured destination", status: "manual", manualReason: "Requires live SIP trunk connectivity to PSTN and a real DID number", file: "tests/pbx/calls.spec.ts" },
|
||||
{ id: "pbx-calls-check-outbound_call_to_sip_trunk_pstn_with_caller_id", group: "pbx-calls", module: "pbx", action: "check", description: "Verify outbound call via SIP trunk presents the correct Caller ID on the receiving end", status: "manual", manualReason: "Requires live SIP trunk and verification of Caller ID at receiving PSTN party", file: "tests/pbx/calls.spec.ts" },
|
||||
{ id: "pbx-calls-check-caller_id_presentation_restriction_and_pai", group: "pbx-calls", module: "pbx", action: "check", description: "Verify Caller ID presentation, Anonymous restriction (CLIR), and P-Asserted-Identity header", status: "manual", manualReason: "Requires active call sessions and SIP packet capture to verify PAI header values", file: "tests/pbx/calls.spec.ts" },
|
||||
{ id: "pbx-calls-check-call_hold_resume_and_music_on_hold", group: "pbx-calls", module: "pbx", action: "check", description: "Verify call hold, resume, and Music on Hold playback work correctly", status: "manual", manualReason: "Requires an active two-party call; MoH requires audio monitoring with real SIP phone hardware", file: "tests/pbx/calls.spec.ts" },
|
||||
{ id: "pbx-calls-check-call_waiting_notification_and_switching", group: "pbx-calls", module: "pbx", action: "check", description: "Verify call waiting notification and switching between active and waiting calls", status: "manual", manualReason: "Requires concurrent call sessions on the same extension with physical/softphone capable of call-waiting", file: "tests/pbx/calls.spec.ts" },
|
||||
{ id: "pbx-calls-check-three_way_conference_and_multi_party_bridge", group: "pbx-calls", module: "pbx", action: "check", description: "Verify three-way conference calling and multi-party conference bridge functionality", status: "manual", manualReason: "Requires three or more active SIP endpoints and audio verification by human listeners", file: "tests/pbx/calls.spec.ts" },
|
||||
|
||||
// === PBX-CODECS (3) ===
|
||||
{ id: "pbx-codecs-check-codec_negotiation_g711_g729_opus_g722", group: "pbx-codecs", module: "pbx", action: "check", description: "Verify successful codec negotiation for G.711, G.729, Opus, and G.722", status: "manual", manualReason: "Requires SIP call traffic with codec-constrained endpoints and Wireshark packet capture to verify SDP negotiation", file: "tests/pbx/codecs.spec.ts" },
|
||||
{ id: "pbx-codecs-check-dtmf_transmission_rfc2833_inband_sip_info", group: "pbx-codecs", module: "pbx", action: "check", description: "Verify DTMF digit transmission via RFC2833, in-band audio, and SIP INFO methods", status: "manual", manualReason: "Requires active call sessions with DTMF generation, packet capture, and IVR system for verification", file: "tests/pbx/codecs.spec.ts" },
|
||||
{ id: "pbx-codecs-check-t38_fax_passthrough_and_error_correction", group: "pbx-codecs", module: "pbx", action: "check", description: "Verify T.38 fax passthrough and error correction (ECM) work correctly", status: "manual", manualReason: "Requires fax hardware or fax emulator (HylaFAX) and T.38 stack support", file: "tests/pbx/codecs.spec.ts" },
|
||||
|
||||
// === PBX-CALLS-SECURITY (3) ===
|
||||
{ id: "pbx-calls-security-check-srtp_sdes_or_dtls_media_encryption", group: "pbx-calls-security", module: "pbx", action: "check", description: "Verify SRTP with SDES or DTLS-SRTP is enabled for media encryption on calls", status: "partial", partialNotes: "Encryption config verified via admin portal; confirming live RTP is encrypted requires Wireshark during active call", file: "tests/pbx/calls-security.spec.ts" },
|
||||
{ id: "pbx-calls-security-check-acl_ip_whitelisting_and_rate_limiting", group: "pbx-calls-security", module: "pbx", action: "check", description: "Verify ACL IP whitelisting rules and SIP rate limiting are configured and enforced", status: "partial", partialNotes: "ACL/rate-limit config via portal automated; functional enforcement testing requires network-level testing from non-whitelisted IP", file: "tests/pbx/calls-security.spec.ts" },
|
||||
{ id: "pbx-calls-security-check-toll_fraud_prevention_rules_and_alerting", group: "pbx-calls-security", module: "pbx", action: "check", description: "Verify toll fraud prevention rules and alert notifications are configured", status: "automated", file: "tests/pbx/calls-security.spec.ts" },
|
||||
|
||||
// === PBX-PERFORMANCE (3) ===
|
||||
{ id: "pbx-performance-check-cpu_memory_disk_io_utilization_under_sustained_load", group: "pbx-performance", module: "pbx", action: "check", description: "Verify CPU, memory, and disk I/O remain within limits under sustained call load", status: "manual", manualReason: "Requires SIPp load generator, server-side performance monitoring, and dedicated test environment", file: "tests/pbx/performance.spec.ts" },
|
||||
{ id: "pbx-performance-check-long_duration_stability_72h_test", group: "pbx-performance", module: "pbx", action: "check", description: "Verify system stability under continuous call load over a 72-hour period", status: "manual", manualReason: "72-hour test requires dedicated environment, SIPp continuous load, and automated monitoring with alerting", file: "tests/pbx/performance.spec.ts" },
|
||||
{ id: "pbx-performance-check-memory_leak_and_resource_cleanup_detection", group: "pbx-performance", module: "pbx", action: "check", description: "Detect memory leaks and verify proper resource cleanup over extended operation", status: "manual", manualReason: "Requires extended monitoring with active call load and memory profiling tools (Valgrind, heap dumps)", file: "tests/pbx/performance.spec.ts" },
|
||||
|
||||
// === PBX-AVAILABILITY (4) ===
|
||||
{ id: "pbx-availability-check-active_passive_or_active_active_failover", group: "pbx-availability", module: "pbx", action: "check", description: "Verify Active-Passive or Active-Active failover transitions correctly on node failure", status: "manual", manualReason: "Requires HA cluster with 2+ nodes; failover must be triggered by stopping the active node", file: "tests/pbx/availability.spec.ts" },
|
||||
{ id: "pbx-availability-check-database_replication_and_sync_integrity", group: "pbx-availability", module: "pbx", action: "check", description: "Verify database replication between HA nodes maintains consistency and acceptable lag", status: "manual", manualReason: "Requires HA database cluster; verifying replication lag needs direct database access", file: "tests/pbx/availability.spec.ts" },
|
||||
{ id: "pbx-availability-check-automatic_failover_and_call_preservation", group: "pbx-availability", module: "pbx", action: "check", description: "Verify automatic failover preserves active calls (no drops, no audio interruption > 200ms)", status: "manual", manualReason: "Requires active call sessions during failover and simultaneous call-state monitoring", file: "tests/pbx/availability.spec.ts" },
|
||||
{ id: "pbx-availability-check-geographic_redundancy_and_geo_distribution", group: "pbx-availability", module: "pbx", action: "check", description: "Verify geo-distributed deployment handles site failover without service interruption", status: "manual", manualReason: "Requires multi-site infrastructure across geographic locations or WAN simulation", file: "tests/pbx/availability.spec.ts" },
|
||||
|
||||
// === PBX-MANAGEMENT (2) ===
|
||||
{ id: "pbx-management-check-cli_management", group: "pbx-management", module: "pbx", action: "check", description: "Verify CLI management interface is accessible and key management commands execute correctly", status: "partial", partialNotes: "Web console (if present) verified via Playwright; full CLI coverage requires SSH automation integrated in CI pipeline", file: "tests/pbx/management.spec.ts" },
|
||||
{ id: "pbx-management-check-gui_management", group: "pbx-management", module: "pbx", action: "check", description: "Verify all main admin portal sections load correctly and management actions are functional", status: "automated", file: "tests/pbx/management.spec.ts" },
|
||||
|
||||
// === PBX-LOGS (5) ===
|
||||
{ id: "pbx-logs-check-cdr_generation_accuracy_completeness_and_export", group: "pbx-logs", module: "pbx", action: "check", description: "Verify CDR records contain all required fields and can be exported to CSV/PDF", status: "automated", file: "tests/pbx/logs.spec.ts" },
|
||||
{ id: "pbx-logs-check-real_time_active_call_and_registration_monitoring", group: "pbx-logs", module: "pbx", action: "check", description: "Verify real-time monitoring dashboard shows active calls and SIP registrations", status: "automated", file: "tests/pbx/logs.spec.ts" },
|
||||
{ id: "pbx-logs-check-detailed_logging_levels_rotation_and_search", group: "pbx-logs", module: "pbx", action: "check", description: "Verify logging levels, log rotation, and log search/filter functionality", status: "automated", file: "tests/pbx/logs.spec.ts" },
|
||||
{ id: "pbx-logs-check-threshold_alarms_cpu_channels_disk_memory", group: "pbx-logs", module: "pbx", action: "check", description: "Verify threshold-based alarms for CPU, active channels, disk, and memory usage", status: "automated", file: "tests/pbx/logs.spec.ts" },
|
||||
{ id: "pbx-logs-check-call_quality_metrics_mos_jitter_loss_per_call", group: "pbx-logs", module: "pbx", action: "check", description: "Verify per-call quality metrics (MOS score, jitter, packet loss) are collected and displayed", status: "partial", partialNotes: "QoS metrics display UI automated; actual metric generation requires active call sessions with network impairment", file: "tests/pbx/logs.spec.ts" },
|
||||
|
||||
// === PBX-REGRESSION (4) ===
|
||||
{ id: "pbx-regression-check-core_call_features_after_any_code_change", group: "pbx-regression", module: "pbx", action: "check", description: "Run regression checks on core features after any code change to detect regressions", status: "partial", partialNotes: "Admin portal regression automated; call-flow regression (inbound/outbound/hold/transfer) requires SIP endpoints", file: "tests/pbx/regression.spec.ts" },
|
||||
{ id: "pbx-regression-check-previously_fixed_bugs_and_edge_cases_reverification", group: "pbx-regression", module: "pbx", action: "check", description: "Verify all previously fixed bugs and known edge cases have not regressed", status: "partial", partialNotes: "UI bug regressions automated; telephony-layer bug regressions require SIP client or SSH verification", file: "tests/pbx/regression.spec.ts" },
|
||||
{ id: "pbx-regression-check-performance_baselines_comparison_against_previous_version", group: "pbx-regression", module: "pbx", action: "check", description: "Compare key performance metrics against baselines from the previous version", status: "manual", manualReason: "Requires SIPp load tests and statistical comparison of results against previous version baseline", file: "tests/pbx/regression.spec.ts" },
|
||||
{ id: "pbx-regression-check-security_hardening_and_vulnerability_scan_baseline", group: "pbx-regression", module: "pbx", action: "check", description: "Verify security hardening settings and compare vulnerability scan baseline against previous version", status: "partial", partialNotes: "Security config checks via portal automated; full CVE scan baseline requires Trivy/OpenVAS in CI pipeline", file: "tests/pbx/regression.spec.ts" },
|
||||
];
|
||||
|
||||
/* ══════════════════════════════ STATE ══════════════════════════════ */
|
||||
let sortCol = null;
|
||||
let sortDir = 1; // 1 = asc, -1 = desc
|
||||
|
||||
/* ══════════════════════════════ CHARTS ══════════════════════════════ */
|
||||
// Doughnut
|
||||
new Chart(document.getElementById('chartDoughnut'), {
|
||||
type: 'doughnut',
|
||||
data: {
|
||||
labels: ['Automated', 'Partial', 'Manual'],
|
||||
datasets: [{
|
||||
data: [34, 16, 36],
|
||||
backgroundColor: ['#22c55e', '#f59e0b', '#ef4444'],
|
||||
borderColor: ['#16a34a', '#d97706', '#dc2626'],
|
||||
borderWidth: 2,
|
||||
hoverOffset: 6
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: { display: false },
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: ctx => {
|
||||
const val = ctx.raw;
|
||||
const pct = ((val / 86) * 100).toFixed(1);
|
||||
return ` ${ctx.label}: ${val} (${pct}%)`;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
cutout: '68%'
|
||||
}
|
||||
});
|
||||
|
||||
// Compute group counts
|
||||
const GROUP_ORDER = [
|
||||
'core-repository','core-documentation','core-features','core-virtualization',
|
||||
'core-security','core-installation','core-upgrade','core-interoperability',
|
||||
'pbx-configuration','pbx-extensions','pbx-calls','pbx-codecs',
|
||||
'pbx-calls-security','pbx-performance','pbx-availability','pbx-management',
|
||||
'pbx-logs','pbx-regression'
|
||||
];
|
||||
|
||||
const groupCounts = {};
|
||||
GROUP_ORDER.forEach(g => { groupCounts[g] = { automated: 0, partial: 0, manual: 0 }; });
|
||||
TESTS.forEach(t => { if (groupCounts[t.group]) groupCounts[t.group][t.status]++; });
|
||||
|
||||
const barLabels = GROUP_ORDER.map(g => g);
|
||||
const barAuto = GROUP_ORDER.map(g => groupCounts[g].automated);
|
||||
const barPartial = GROUP_ORDER.map(g => groupCounts[g].partial);
|
||||
const barManual = GROUP_ORDER.map(g => groupCounts[g].manual);
|
||||
|
||||
new Chart(document.getElementById('chartBar'), {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: barLabels,
|
||||
datasets: [
|
||||
{ label: 'Automated', data: barAuto, backgroundColor: '#22c55e', borderRadius: 2 },
|
||||
{ label: 'Partial', data: barPartial, backgroundColor: '#f59e0b', borderRadius: 2 },
|
||||
{ label: 'Manual', data: barManual, backgroundColor: '#ef4444', borderRadius: 2 }
|
||||
]
|
||||
},
|
||||
options: {
|
||||
indexAxis: 'y',
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'top',
|
||||
labels: {
|
||||
font: { size: 11 },
|
||||
color: '#475569',
|
||||
boxWidth: 12,
|
||||
padding: 16
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
mode: 'index',
|
||||
intersect: false
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
stacked: true,
|
||||
grid: { color: '#f1f5f9' },
|
||||
ticks: {
|
||||
font: { size: 11 },
|
||||
color: '#94a3b8',
|
||||
stepSize: 1
|
||||
}
|
||||
},
|
||||
y: {
|
||||
stacked: true,
|
||||
grid: { display: false },
|
||||
ticks: {
|
||||
font: { size: 11, family: 'monospace' },
|
||||
color: '#475569'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/* ══════════════════════════════ TABLE RENDERING ══════════════════════════════ */
|
||||
function escHtml(s) {
|
||||
return String(s)
|
||||
.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>')
|
||||
.replace(/"/g,'"');
|
||||
}
|
||||
|
||||
function renderRows(list) {
|
||||
const tbody = document.getElementById('tableBody');
|
||||
const noRes = document.getElementById('noResults');
|
||||
const countEl = document.getElementById('visibleCount');
|
||||
|
||||
if (list.length === 0) {
|
||||
tbody.innerHTML = '';
|
||||
noRes.style.display = 'block';
|
||||
countEl.textContent = '0';
|
||||
return;
|
||||
}
|
||||
noRes.style.display = 'none';
|
||||
countEl.textContent = list.length;
|
||||
|
||||
const rows = list.map((t, i) => {
|
||||
const statusBadge = `<span class="badge badge-${t.status}"><span class="badge-dot"></span>${t.status}</span>`;
|
||||
const moduleBadge = `<span class="badge badge-${t.module}">${t.module.toUpperCase()}</span>`;
|
||||
const actionBadge = `<span class="badge badge-${t.action}">${t.action}</span>`;
|
||||
const shortId = t.id.length > 40 ? t.id.slice(0, 38) + '…' : t.id;
|
||||
return `<tr class="status-${t.status}" data-id="${escHtml(t.id)}">
|
||||
<td class="num">${i + 1}</td>
|
||||
<td class="td-id">
|
||||
<span class="id-text" title="${escHtml(t.id)}">${escHtml(shortId)}</span>
|
||||
<button class="copy-btn" onclick="copyId(this,'${escHtml(t.id)}')" title="Copy test ID">Copy</button>
|
||||
</td>
|
||||
<td>${escHtml(t.group)}</td>
|
||||
<td>${moduleBadge}</td>
|
||||
<td>${actionBadge}</td>
|
||||
<td class="td-desc">${escHtml(t.description)}</td>
|
||||
<td>${statusBadge}</td>
|
||||
<td class="td-file"><span class="file-text" title="${escHtml(t.file)}">${escHtml(t.file)}</span></td>
|
||||
</tr>`;
|
||||
});
|
||||
tbody.innerHTML = rows.join('');
|
||||
}
|
||||
|
||||
function getFiltered() {
|
||||
const mod = document.getElementById('fModule').value;
|
||||
const grp = document.getElementById('fGroup').value;
|
||||
const act = document.getElementById('fAction').value;
|
||||
const stat = document.getElementById('fStatus').value;
|
||||
const search = document.getElementById('fSearch').value.trim().toLowerCase();
|
||||
|
||||
let list = TESTS.filter(t => {
|
||||
if (mod && t.module !== mod) return false;
|
||||
if (grp && t.group !== grp) return false;
|
||||
if (act && t.action !== act) return false;
|
||||
if (stat && t.status !== stat) return false;
|
||||
if (search) {
|
||||
const hay = (t.id + ' ' + t.description + ' ' + t.group + ' ' + t.file).toLowerCase();
|
||||
if (!hay.includes(search)) return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (sortCol) {
|
||||
list = [...list].sort((a, b) => {
|
||||
let va = a[sortCol] || '';
|
||||
let vb = b[sortCol] || '';
|
||||
if (sortCol === 'idx') { va = TESTS.indexOf(a); vb = TESTS.indexOf(b); }
|
||||
if (va < vb) return -1 * sortDir;
|
||||
if (va > vb) return 1 * sortDir;
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
function refresh() { renderRows(getFiltered()); }
|
||||
|
||||
/* ══════════════════════════════ EVENTS ══════════════════════════════ */
|
||||
['fModule','fGroup','fAction','fStatus','fSearch'].forEach(id => {
|
||||
document.getElementById(id).addEventListener('input', refresh);
|
||||
});
|
||||
|
||||
document.getElementById('btnReset').addEventListener('click', () => {
|
||||
document.getElementById('fModule').value = '';
|
||||
document.getElementById('fGroup').value = '';
|
||||
document.getElementById('fAction').value = '';
|
||||
document.getElementById('fStatus').value = '';
|
||||
document.getElementById('fSearch').value = '';
|
||||
sortCol = null;
|
||||
sortDir = 1;
|
||||
document.querySelectorAll('thead th').forEach(th => th.classList.remove('sorted-asc','sorted-desc'));
|
||||
refresh();
|
||||
});
|
||||
|
||||
// Sorting
|
||||
document.querySelectorAll('thead th').forEach(th => {
|
||||
th.addEventListener('click', () => {
|
||||
const col = th.dataset.col;
|
||||
if (sortCol === col) {
|
||||
sortDir *= -1;
|
||||
} else {
|
||||
sortCol = col;
|
||||
sortDir = 1;
|
||||
}
|
||||
document.querySelectorAll('thead th').forEach(h => h.classList.remove('sorted-asc','sorted-desc'));
|
||||
th.classList.add(sortDir === 1 ? 'sorted-asc' : 'sorted-desc');
|
||||
refresh();
|
||||
});
|
||||
});
|
||||
|
||||
/* ══════════════════════════════ COPY ID ══════════════════════════════ */
|
||||
function copyId(btn, id) {
|
||||
navigator.clipboard.writeText(id).then(() => {
|
||||
btn.textContent = 'Copied!';
|
||||
btn.classList.add('copied');
|
||||
setTimeout(() => {
|
||||
btn.textContent = 'Copy';
|
||||
btn.classList.remove('copied');
|
||||
}, 1800);
|
||||
}).catch(() => {
|
||||
// fallback for non-secure contexts
|
||||
const ta = document.createElement('textarea');
|
||||
ta.value = id;
|
||||
ta.style.position = 'fixed';
|
||||
ta.style.opacity = '0';
|
||||
document.body.appendChild(ta);
|
||||
ta.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(ta);
|
||||
btn.textContent = 'Copied!';
|
||||
btn.classList.add('copied');
|
||||
setTimeout(() => {
|
||||
btn.textContent = 'Copy';
|
||||
btn.classList.remove('copied');
|
||||
}, 1800);
|
||||
});
|
||||
}
|
||||
|
||||
/* ══════════════════════════════ INIT ══════════════════════════════ */
|
||||
refresh();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user