- Replace all occurrences of 'ss-tools' with 'superset-tools' in 104 files - Rename git bundle file ss-tools.bundle → superset-tools.bundle - Update .gitignore pattern accordingly - Preserve variable names (hasSsTools etc.) and code identifiers
511 lines
21 KiB
Bash
Executable File
511 lines
21 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# #region Coverage.Summary [C:3] [TYPE Module] [SEMANTICS coverage,testing,reporting,ci]
|
|
# @BRIEF Run backend + frontend tests with coverage, generate unified HTML summary report.
|
|
# @LAYER DevOps
|
|
# @RELATION DEPENDS_ON -> [pytest]
|
|
# @RELATION DEPENDS_ON -> [vitest]
|
|
# @RELATION DEPENDS_ON -> [coverage.py]
|
|
# @POST Unified HTML report written to $OUTPUT_DIR/index.html
|
|
# @POST Backend HTML coverage at $OUTPUT_DIR/backend/
|
|
# @POST Frontend HTML coverage at $OUTPUT_DIR/frontend/
|
|
# @SIDE_EFFECT Runs tests; writes coverage artifacts to $OUTPUT_DIR/
|
|
#
|
|
# Usage: ./scripts/coverage-summary.sh [options]
|
|
#
|
|
# Options:
|
|
# --backend-only Run only backend tests
|
|
# --frontend-only Run only frontend tests
|
|
# --unit Run backend unit tests instead of integration (skips Docker)
|
|
# --skip-integration Skip backend integration tests (requires --unit to run backend)
|
|
# --output-dir <dir> Output directory (default: coverage-summary)
|
|
# --help Show this help
|
|
#
|
|
# Examples:
|
|
# ./scripts/coverage-summary.sh # Full run (backend integration + frontend)
|
|
# ./scripts/coverage-summary.sh --unit # Backend unit + frontend
|
|
# ./scripts/coverage-summary.sh --frontend-only # Frontend only
|
|
# ./scripts/coverage-summary.sh --backend-only --unit # Backend unit tests only
|
|
# ./scripts/coverage-summary.sh --skip-integration # Frontend only (backend skipped entirely)
|
|
# #endregion Coverage.Summary
|
|
|
|
# Do NOT use -e: test failures must not abort the script before report generation
|
|
set -uo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
|
|
# ── Defaults ─────────────────────────────────────────────────────────────
|
|
RUN_BACKEND=true
|
|
RUN_FRONTEND=true
|
|
BACKEND_MODE="integration" # integration | unit
|
|
OUTPUT_DIR="${PROJECT_DIR}/coverage-summary"
|
|
TIMESTAMP="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
|
|
|
# ── Parse args ───────────────────────────────────────────────────────────
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--backend-only) RUN_FRONTEND=false; shift ;;
|
|
--frontend-only) RUN_BACKEND=false; shift ;;
|
|
--unit) BACKEND_MODE="unit"; shift ;;
|
|
--skip-integration) RUN_BACKEND=false; shift ;;
|
|
--output-dir) OUTPUT_DIR="$2"; shift 2 ;;
|
|
--help|-h)
|
|
echo "Usage: $(basename "$0") [options]"
|
|
echo ""
|
|
echo "Options:"
|
|
echo " --backend-only Run only backend tests"
|
|
echo " --frontend-only Run only frontend tests"
|
|
echo " --unit Run backend unit tests (SQLite, no Docker)"
|
|
echo " --skip-integration Skip backend integration tests entirely"
|
|
echo " --output-dir <dir> Output directory (default: coverage-summary)"
|
|
echo " --help Show this help"
|
|
echo ""
|
|
echo "Examples:"
|
|
echo " $0 # Full run (integration + frontend)"
|
|
echo " $0 --unit # Backend unit + frontend"
|
|
echo " $0 --frontend-only # Frontend only"
|
|
echo " $0 --backend-only --unit # Backend unit only"
|
|
echo " $0 --skip-integration # Frontend only"
|
|
exit 0
|
|
;;
|
|
*) echo "Error: unknown option $1"; exit 1 ;;
|
|
esac
|
|
done
|
|
|
|
mkdir -p "$OUTPUT_DIR/backend" "$OUTPUT_DIR/frontend"
|
|
|
|
# ── Helpers ──────────────────────────────────────────────────────────────
|
|
green() { echo -e "\033[0;32m$1\033[0m"; }
|
|
blue() { echo -e "\033[0;34m$1\033[0m"; }
|
|
red() { echo -e "\033[0;31m$1\033[0m"; }
|
|
bold() { echo -e "\033[1m$1\033[0m"; }
|
|
|
|
# ── Default coverage values ──────────────────────────────────────────────
|
|
BACKEND_COVERAGE_PCT="N/A"
|
|
BACKEND_STMTS="N/A"
|
|
BACKEND_MISSED="N/A"
|
|
BACKEND_TESTS_PASSED="?"
|
|
BACKEND_TESTS_FAILED="?"
|
|
BACKEND_TESTS_TOTAL="?"
|
|
|
|
FRONTEND_COVERAGE_STMTS="N/A"
|
|
FRONTEND_COVERAGE_BRANCH="N/A"
|
|
FRONTEND_COVERAGE_FUNCS="N/A"
|
|
FRONTEND_COVERAGE_LINES="N/A"
|
|
FRONTEND_TESTS_PASSED="?"
|
|
FRONTEND_TESTS_FAILED="?"
|
|
FRONTEND_TESTS_TOTAL="?"
|
|
|
|
# ── Backend coverage ─────────────────────────────────────────────────────
|
|
run_backend() {
|
|
echo ""
|
|
bold "━━━ Backend ($BACKEND_MODE) ━━━"
|
|
|
|
cd "$PROJECT_DIR/backend"
|
|
|
|
if [ ! -f ".venv/bin/activate" ]; then
|
|
red "Error: backend/.venv not found. Run: cd backend && python3 -m venv .venv"
|
|
exit 1
|
|
fi
|
|
|
|
source .venv/bin/activate
|
|
|
|
local pytest_args=()
|
|
local cov_args=(--cov=src --cov-report=term --cov-report=html:"$OUTPUT_DIR/backend")
|
|
|
|
if [ "$BACKEND_MODE" = "integration" ]; then
|
|
if ! docker ps >/dev/null 2>&1; then
|
|
red "Docker is not running. Integration tests require Testcontainers."
|
|
red " Use --unit for SQLite-based unit tests or start Docker."
|
|
return 1
|
|
fi
|
|
pytest_args=(tests/integration/ --run-integration --ignore=tests/integration/test_superset_tls_custom_ca.py)
|
|
else
|
|
pytest_args=(tests/ --ignore=tests/integration/)
|
|
fi
|
|
|
|
blue "Running: python -m pytest ${pytest_args[*]} ${cov_args[*]}"
|
|
|
|
local out
|
|
set +e
|
|
out=$(python -m pytest "${pytest_args[@]}" "${cov_args[@]}" -q 2>&1)
|
|
set -e
|
|
echo "$out" > "$OUTPUT_DIR/backend/pytest_output.txt"
|
|
|
|
# Parse summary: "X passed" or "X passed, Y failed, Z warnings"
|
|
if echo "$out" | grep -qP '^\d+ passed'; then
|
|
BACKEND_TESTS_PASSED=$(echo "$out" | grep -oP '^\d+(?= passed)')
|
|
if echo "$out" | grep -qP '\d+ failed'; then
|
|
BACKEND_TESTS_FAILED=$(echo "$out" | grep -oP '\d+(?= failed)' | tail -1)
|
|
else
|
|
BACKEND_TESTS_FAILED=0
|
|
fi
|
|
BACKEND_TESTS_TOTAL=$((BACKEND_TESTS_PASSED + BACKEND_TESTS_FAILED))
|
|
else
|
|
local summary
|
|
summary=$(echo "$out" | grep -E "(passed|failed)" | tail -1)
|
|
if echo "$summary" | grep -qP '\d+ passed'; then
|
|
BACKEND_TESTS_PASSED=$(echo "$summary" | grep -oP '\d+(?= passed)')
|
|
BACKEND_TESTS_FAILED=$(echo "$summary" | grep -oP '\d+(?= failed)' || echo "0")
|
|
BACKEND_TESTS_TOTAL=$((BACKEND_TESTS_PASSED + BACKEND_TESTS_FAILED))
|
|
fi
|
|
fi
|
|
|
|
# Parse TOTAL from coverage: "TOTAL 33686 29679 12%"
|
|
local total_line
|
|
total_line=$(echo "$out" | grep "^TOTAL" | tail -1)
|
|
if [ -n "$total_line" ]; then
|
|
BACKEND_STMTS=$(echo "$total_line" | awk '{print $2}')
|
|
BACKEND_MISSED=$(echo "$total_line" | awk '{print $3}')
|
|
BACKEND_COVERAGE_PCT=$(echo "$total_line" | awk '{print $4}' | tr -d '%')
|
|
fi
|
|
|
|
green " Tests: $BACKEND_TESTS_PASSED passed / ${BACKEND_TESTS_FAILED:-0} failed"
|
|
green " Coverage: ${BACKEND_COVERAGE_PCT}% ($BACKEND_STMTS stmts, $BACKEND_MISSED missed)"
|
|
cd "$PROJECT_DIR"
|
|
}
|
|
|
|
# ── Frontend coverage ────────────────────────────────────────────────────
|
|
run_frontend() {
|
|
echo ""
|
|
bold "━━━ Frontend ━━━"
|
|
|
|
cd "$PROJECT_DIR/frontend"
|
|
|
|
if [ ! -d "node_modules" ]; then
|
|
red "Error: frontend/node_modules not found. Run: cd frontend && npm install"
|
|
exit 1
|
|
fi
|
|
|
|
blue "Running: npx vitest run --coverage --coverage.reportOnFailure"
|
|
|
|
local out
|
|
set +e
|
|
out=$(npx vitest run --coverage --coverage.reportOnFailure \
|
|
--coverage.reporter=text \
|
|
--coverage.reporter=html \
|
|
--coverage.reportsDirectory="$OUTPUT_DIR/frontend" 2>&1)
|
|
set -e
|
|
echo "$out" > "$OUTPUT_DIR/frontend/vitest_output.txt"
|
|
|
|
# Parse test results
|
|
local summary_line
|
|
summary_line=$(echo "$out" | grep -E "^\s*Tests\s+" | tail -1)
|
|
if [ -n "$summary_line" ]; then
|
|
FRONTEND_TESTS_PASSED=$(echo "$summary_line" | grep -oP '\d+(?= passed)')
|
|
FRONTEND_TESTS_FAILED=$(echo "$summary_line" | grep -oP '\d+(?= failed)')
|
|
FRONTEND_TESTS_TOTAL=$((FRONTEND_TESTS_PASSED + FRONTEND_TESTS_FAILED))
|
|
fi
|
|
|
|
# Parse coverage: "All files | 99.25 | 87.39 | 98.91 | 99.48"
|
|
local cov_line
|
|
cov_line=$(echo "$out" | grep "All files" | head -1)
|
|
if [ -n "$cov_line" ]; then
|
|
FRONTEND_COVERAGE_STMTS=$(echo "$cov_line" | awk -F'|' '{print $2}' | xargs)
|
|
FRONTEND_COVERAGE_BRANCH=$(echo "$cov_line" | awk -F'|' '{print $3}' | xargs)
|
|
FRONTEND_COVERAGE_FUNCS=$(echo "$cov_line" | awk -F'|' '{print $4}' | xargs)
|
|
FRONTEND_COVERAGE_LINES=$(echo "$cov_line" | awk -F'|' '{print $5}' | xargs)
|
|
fi
|
|
|
|
green " Tests: $FRONTEND_TESTS_PASSED passed / ${FRONTEND_TESTS_FAILED:-0} failed"
|
|
green " Coverage: Stmts ${FRONTEND_COVERAGE_STMTS}% | Branch ${FRONTEND_COVERAGE_BRANCH}% | Funcs ${FRONTEND_COVERAGE_FUNCS}% | Lines ${FRONTEND_COVERAGE_LINES}%"
|
|
cd "$PROJECT_DIR"
|
|
}
|
|
|
|
# ── Generate HTML Report ─────────────────────────────────────────────────
|
|
generate_report() {
|
|
echo ""
|
|
bold "━━━ Generating HTML Report ━━━"
|
|
|
|
# Determine status
|
|
local backend_status="success"
|
|
local frontend_status="success"
|
|
local overall_status="success"
|
|
|
|
if [ "${BACKEND_TESTS_FAILED:-0}" != "?" ] && [ "${BACKEND_TESTS_FAILED:-0}" -gt 0 ]; then
|
|
backend_status="failed"
|
|
overall_status="failed"
|
|
fi
|
|
if [ "${FRONTEND_TESTS_FAILED:-0}" != "?" ] && [ "${FRONTEND_TESTS_FAILED:-0}" -gt 0 ]; then
|
|
frontend_status="failed"
|
|
overall_status="failed"
|
|
fi
|
|
|
|
# Coverage color helper
|
|
cov_color() {
|
|
local pct=$1
|
|
if [ "$pct" = "N/A" ]; then echo "gray"; return; fi
|
|
local int_pct
|
|
int_pct=$(printf "%.0f" "$pct" 2>/dev/null || echo 0)
|
|
if [ "$int_pct" -ge 80 ]; then echo "green"
|
|
elif [ "$int_pct" -ge 50 ]; then echo "orange"
|
|
else echo "red"; fi
|
|
}
|
|
|
|
# Collect which stacks
|
|
local subtitle_parts=()
|
|
[ "$RUN_BACKEND" = true ] && subtitle_parts+=("Backend (${BACKEND_MODE})")
|
|
[ "$RUN_FRONTEND" = true ] && subtitle_parts+=("Frontend")
|
|
local subtitle
|
|
subtitle=$(IFS=" + "; echo "${subtitle_parts[*]}")
|
|
|
|
local backend_cov_color
|
|
backend_cov_color=$(cov_color "$BACKEND_COVERAGE_PCT")
|
|
|
|
# Build status pills
|
|
local status_pills=""
|
|
status_pills+="<span class=\"status-pill ${overall_status}\">Overall: ${overall_status}</span>"
|
|
if [ "$RUN_BACKEND" = true ]; then
|
|
status_pills+="<span class=\"status-pill ${backend_status}\">Backend: ${backend_status}</span>"
|
|
fi
|
|
if [ "$RUN_FRONTEND" = true ]; then
|
|
status_pills+="<span class=\"status-pill ${frontend_status}\">Frontend: ${frontend_status}</span>"
|
|
fi
|
|
|
|
# Build cards
|
|
local cards_html=""
|
|
|
|
if [ "$RUN_BACKEND" = true ]; then
|
|
cards_html+="
|
|
<div class=\"card\">
|
|
<div class=\"card-header\">
|
|
<h2>🐍 Backend</h2>
|
|
<span class=\"status-pill ${backend_status}\">${BACKEND_MODE}</span>
|
|
</div>
|
|
<div class=\"card-body\">
|
|
<div class=\"stat-grid\">
|
|
<div class=\"stat\">
|
|
<div class=\"stat-value cov-value ${backend_cov_color}\">${BACKEND_COVERAGE_PCT}%</div>
|
|
<div class=\"stat-label\">Coverage</div>
|
|
</div>
|
|
<div class=\"stat\">
|
|
<div class=\"stat-value\">${BACKEND_TESTS_PASSED}/${BACKEND_TESTS_TOTAL}</div>
|
|
<div class=\"stat-label\">Tests (pass/total)</div>
|
|
</div>
|
|
<div class=\"stat\">
|
|
<div class=\"stat-value\" style=\"font-size:1rem\">${BACKEND_STMTS}</div>
|
|
<div class=\"stat-label\">Statements</div>
|
|
</div>
|
|
<div class=\"stat\">
|
|
<div class=\"stat-value\" style=\"font-size:1rem\">${BACKEND_MISSED}</div>
|
|
<div class=\"stat-label\">Missed</div>
|
|
</div>
|
|
</div>
|
|
<div class=\"links\">
|
|
<a href=\"backend/index.html\">📄 HTML Report</a>
|
|
<a href=\"backend/pytest_output.txt\">📋 Raw Output</a>
|
|
</div>
|
|
</div>
|
|
</div>"
|
|
fi
|
|
|
|
if [ "$RUN_FRONTEND" = true ]; then
|
|
local f_stmts="${FRONTEND_COVERAGE_STMTS:-0}"
|
|
local f_branch="${FRONTEND_COVERAGE_BRANCH:-0}"
|
|
local f_funcs="${FRONTEND_COVERAGE_FUNCS:-0}"
|
|
local f_lines="${FRONTEND_COVERAGE_LINES:-0}"
|
|
local f_stmts_c
|
|
f_stmts_c=$(cov_color "$f_stmts")
|
|
local f_branch_c
|
|
f_branch_c=$(cov_color "$f_branch")
|
|
local f_funcs_c
|
|
f_funcs_c=$(cov_color "$f_funcs")
|
|
local f_lines_c
|
|
f_lines_c=$(cov_color "$f_lines")
|
|
|
|
cards_html+="
|
|
<div class=\"card\">
|
|
<div class=\"card-header\">
|
|
<h2>🎨 Frontend</h2>
|
|
<span class=\"status-pill ${frontend_status}\">Svelte</span>
|
|
</div>
|
|
<div class=\"card-body\">
|
|
<div class=\"stat-grid\" style=\"grid-template-columns: 1fr 1fr 1fr 1fr;\">
|
|
<div class=\"stat\">
|
|
<div class=\"cov-value ${f_stmts_c}\">${f_stmts}%</div>
|
|
<div class=\"stat-label\">Statements</div>
|
|
</div>
|
|
<div class=\"stat\">
|
|
<div class=\"cov-value ${f_branch_c}\">${f_branch}%</div>
|
|
<div class=\"stat-label\">Branch</div>
|
|
</div>
|
|
<div class=\"stat\">
|
|
<div class=\"cov-value ${f_funcs_c}\">${f_funcs}%</div>
|
|
<div class=\"stat-label\">Functions</div>
|
|
</div>
|
|
<div class=\"stat\">
|
|
<div class=\"cov-value ${f_lines_c}\">${f_lines}%</div>
|
|
<div class=\"stat-label\">Lines</div>
|
|
</div>
|
|
</div>
|
|
<div style=\"margin-top:0.75rem; text-align:center;\">
|
|
<span class=\"stat-value\" style=\"font-size:1rem;\">${FRONTEND_TESTS_PASSED}/${FRONTEND_TESTS_TOTAL}</span>
|
|
<span class=\"stat-label\" style=\"display:inline; margin-left:0.3rem;\">tests passed</span>
|
|
</div>
|
|
<div class=\"links\">
|
|
<a href=\"frontend/index.html\">📄 HTML Report</a>
|
|
<a href=\"frontend/vitest_output.txt\">📋 Raw Output</a>
|
|
</div>
|
|
</div>
|
|
</div>"
|
|
fi
|
|
|
|
# Build links
|
|
local links_html=""
|
|
[ "$RUN_BACKEND" = true ] && links_html+="<a href=\"backend/index.html\">Backend Coverage Details</a>"
|
|
[ "$RUN_FRONTEND" = true ] && links_html+="<a href=\"frontend/index.html\">Frontend Coverage Details</a>"
|
|
|
|
# Build combined table rows
|
|
local table_rows=""
|
|
if [ "$RUN_BACKEND" = true ]; then
|
|
table_rows+="
|
|
<tr>
|
|
<td><strong>Backend</strong> (Python)</td>
|
|
<td>${BACKEND_MODE}</td>
|
|
<td class=\"num\">${BACKEND_TESTS_PASSED}</td>
|
|
<td class=\"num\">${BACKEND_TESTS_FAILED:-0}</td>
|
|
<td class=\"num\">${BACKEND_COVERAGE_PCT}%</td>
|
|
<td class=\"num placeholder\">—</td>
|
|
</tr>"
|
|
fi
|
|
if [ "$RUN_FRONTEND" = true ]; then
|
|
table_rows+="
|
|
<tr>
|
|
<td><strong>Frontend</strong> (Svelte)</td>
|
|
<td>unit</td>
|
|
<td class=\"num\">${FRONTEND_TESTS_PASSED}</td>
|
|
<td class=\"num\">${FRONTEND_TESTS_FAILED:-0}</td>
|
|
<td class=\"num\">${FRONTEND_COVERAGE_STMTS}%</td>
|
|
<td class=\"num\">${FRONTEND_COVERAGE_BRANCH}%</td>
|
|
</tr>"
|
|
fi
|
|
|
|
# ── Write HTML (using quoted heredoc to prevent bash expansion) ─────
|
|
cat > "$OUTPUT_DIR/index.html" << HTML_EOF
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Coverage Summary — superset-tools</title>
|
|
<style>
|
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
background: #f8fafc;
|
|
color: #1e293b;
|
|
line-height: 1.6;
|
|
padding: 2rem;
|
|
}
|
|
.container { max-width: 960px; margin: 0 auto; }
|
|
h1 { font-size: 1.75rem; font-weight: 700; margin-bottom: 0.25rem; }
|
|
.subtitle { color: #64748b; font-size: 0.9rem; margin-bottom: 2rem; }
|
|
.timestamp { color: #94a3b8; font-size: 0.8rem; margin-bottom: 2rem; }
|
|
.status-bar { display: flex; gap: 1rem; margin-bottom: 2rem; flex-wrap: wrap; }
|
|
.status-pill {
|
|
display: inline-flex; align-items: center; gap: 0.5rem;
|
|
padding: 0.4rem 0.9rem; border-radius: 9999px;
|
|
font-size: 0.85rem; font-weight: 600;
|
|
}
|
|
.status-pill.success { background: #dcfce7; color: #166534; }
|
|
.status-pill.failed { background: #fee2e2; color: #991b1b; }
|
|
.status-pill.neutral { background: #f1f5f9; color: #475569; }
|
|
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 1.5rem; margin-bottom: 2rem; }
|
|
.card {
|
|
background: #fff; border-radius: 0.75rem; box-shadow: 0 1px 3px rgba(0,0,0,0.08);
|
|
overflow: hidden; border: 1px solid #e2e8f0;
|
|
}
|
|
.card-header {
|
|
padding: 1rem 1.25rem; border-bottom: 1px solid #e2e8f0;
|
|
display: flex; justify-content: space-between; align-items: center;
|
|
}
|
|
.card-header h2 { font-size: 1.1rem; font-weight: 600; }
|
|
.card-body { padding: 1.25rem; }
|
|
.stat-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
|
|
.stat { text-align: center; }
|
|
.stat-value { font-size: 1.75rem; font-weight: 700; line-height: 1.2; }
|
|
.stat-label { font-size: 0.75rem; color: #64748b; text-transform: uppercase; letter-spacing: 0.04em; margin-top: 0.2rem; }
|
|
.cov-value { font-size: 1.5rem; font-weight: 700; }
|
|
.cov-value.green { color: #16a34a; }
|
|
.cov-value.orange { color: #ea580c; }
|
|
.cov-value.red { color: #dc2626; }
|
|
.cov-value.gray { color: #94a3b8; }
|
|
.table-wrap { overflow-x: auto; }
|
|
table { width: 100%; border-collapse: collapse; font-size: 0.85rem; }
|
|
th, td { padding: 0.6rem 0.75rem; text-align: left; border-bottom: 1px solid #e2e8f0; }
|
|
th { font-weight: 600; color: #475569; background: #f8fafc; }
|
|
.num { text-align: right; font-variant-numeric: tabular-nums; }
|
|
.links { margin-top: 1.5rem; }
|
|
.links a {
|
|
display: inline-block; margin-right: 1rem; margin-bottom: 0.5rem;
|
|
padding: 0.4rem 1rem; background: #fff; border: 1px solid #e2e8f0;
|
|
border-radius: 6px; color: #2563eb; text-decoration: none; font-size: 0.85rem;
|
|
transition: background 0.15s, border-color 0.15s;
|
|
}
|
|
.links a:hover { background: #eff6ff; border-color: #bfdbfe; }
|
|
.placeholder { color: #94a3b8; font-style: italic; }
|
|
.section-title { font-size: 1rem; font-weight: 600; margin-top: 2rem; margin-bottom: 0.75rem; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<h1>Coverage Report — superset-tools</h1>
|
|
<div class="subtitle">${subtitle}</div>
|
|
<div class="timestamp">Generated: ${TIMESTAMP}</div>
|
|
|
|
<div class="status-bar">${status_pills}</div>
|
|
|
|
<div class="cards">${cards_html}</div>
|
|
|
|
<h2 class="section-title">Combined Summary</h2>
|
|
<div class="table-wrap">
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Stack</th>
|
|
<th>Mode</th>
|
|
<th class="num">Tests Passed</th>
|
|
<th class="num">Tests Failed</th>
|
|
<th class="num">Coverage %</th>
|
|
<th class="num">Branch %</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>${table_rows}</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="links" style="margin-top:2rem;">
|
|
${links_html}
|
|
</div>
|
|
|
|
<p style="margin-top:1.5rem; font-size:0.75rem; color:#94a3b8;">
|
|
Generated by <code>scripts/coverage-summary.sh</code>
|
|
</p>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
HTML_EOF
|
|
|
|
green " Report saved: $OUTPUT_DIR/index.html"
|
|
}
|
|
|
|
# ── Main ─────────────────────────────────────────────────────────────────
|
|
echo ""
|
|
bold "╔══════════════════════════════════════════════╗"
|
|
bold "║ superset-tools Coverage Summary Generator ║"
|
|
bold "╚══════════════════════════════════════════════╝"
|
|
|
|
if [ "$RUN_BACKEND" = false ] && [ "$RUN_FRONTEND" = false ]; then
|
|
red "Error: nothing to run. Specify at least one of --backend-only, --frontend-only."
|
|
exit 1
|
|
fi
|
|
|
|
[ "$RUN_BACKEND" = true ] && run_backend
|
|
[ "$RUN_FRONTEND" = true ] && run_frontend
|
|
|
|
generate_report
|
|
|
|
echo ""
|
|
green "Done! Open: file://${OUTPUT_DIR}/index.html"
|
|
echo ""
|