@@ -235,7 +235,7 @@ test_summary() {
235235 echo " Failed: $test_failed "
236236 echo " "
237237
238- # Write results file for summary aggregation
238+ # Write per-suite results JSON
239239 local results_dir=" ${TEST_RESULTS_DIR:- $REPO_ROOT / reports/ results} "
240240 if [[ ! " $results_dir " = /* ]]; then
241241 results_dir=" $REPO_ROOT /$results_dir "
@@ -247,10 +247,14 @@ test_summary() {
247247 "suite": "${suite_name} ",
248248 "passed": ${test_passed} ,
249249 "failed": ${test_failed} ,
250- "total": ${total}
250+ "total": ${total} ,
251+ "timestamp": "$( date ' +%Y-%m-%d %H:%M:%S' ) "
251252}
252253EOF
253254
255+ # Regenerate combined summary from all suite JSONs
256+ _regenerate_summary " $results_dir "
257+
254258 if [ " $test_failed " -gt 0 ]; then
255259 echo " RESULT: ✗ FAILED"
256260 exit 1
259263 exit 0
260264 fi
261265}
266+
267+ # Regenerate reports/summary.md from all per-suite JSON files.
268+ # Called automatically by test_summary(); can also be called standalone.
269+ _regenerate_summary () {
270+ local results_dir=" ${1:- ${TEST_RESULTS_DIR:- $REPO_ROOT / reports/ results} } "
271+ local reports_dir=" ${REPORTS_DIR:- $REPO_ROOT / reports} "
272+ local logs_dir=" ${TEST_LOGS_DIR:- $reports_dir / logs} "
273+
274+ # Resolve relative paths against REPO_ROOT
275+ [[ " $results_dir " = /* ]] || results_dir=" $REPO_ROOT /$results_dir "
276+ [[ " $reports_dir " = /* ]] || reports_dir=" $REPO_ROOT /$reports_dir "
277+ [[ " $logs_dir " = /* ]] || logs_dir=" $REPO_ROOT /$logs_dir "
278+
279+ local summary_file=" $reports_dir /summary.md"
280+
281+ # Bail if no result files exist yet
282+ local json_files
283+ json_files=$( ls " $results_dir " /* .json 2> /dev/null | sort) || return 0
284+ [ -n " $json_files " ] || return 0
285+
286+ # Aggregate
287+ local _tp=0 _tf=0 _sc=0 _af=0
288+ local _names=() _passed=() _failed=() _totals=() _times=()
289+
290+ for rf in $json_files ; do
291+ [ -f " $rf " ] || continue
292+ local n p f t ts
293+ n=$( jq -r ' .suite // "unknown"' " $rf " 2> /dev/null)
294+ p=$( jq -r ' .passed // 0' " $rf " 2> /dev/null)
295+ f=$( jq -r ' .failed // 0' " $rf " 2> /dev/null)
296+ t=$( jq -r ' .total // 0' " $rf " 2> /dev/null)
297+ ts=$( jq -r ' .timestamp // ""' " $rf " 2> /dev/null)
298+
299+ _names+=(" $n " ); _passed+=(" $p " ); _failed+=(" $f " ); _totals+=(" $t " ); _times+=(" $ts " )
300+ _tp=$(( _tp + p)) ; _tf=$(( _tf + f)) ; _sc=$(( _sc + 1 ))
301+ [ " $f " -gt 0 ] && _af=1
302+ done
303+
304+ local _gt=$(( _tp + _tf))
305+
306+ mkdir -p " $reports_dir " 2> /dev/null || true
307+ {
308+ echo " # Test Suite Summary"
309+ echo " "
310+ echo " **Updated:** $( date ' +%Y-%m-%d %H:%M:%S' ) "
311+ echo " "
312+
313+ if [ " $_af " -gt 0 ]; then
314+ echo " > **SOME TESTS FAILED**"
315+ else
316+ echo " > **ALL ${_sc} SUITES PASSED** (${_gt} tests)"
317+ fi
318+ echo " "
319+
320+ echo " | Suite | Passed | Failed | Total | Result | Ran At |"
321+ echo " |-------|-------:|-------:|------:|--------|--------|"
322+
323+ for i in $( seq 0 $(( _sc - 1 )) ) ; do
324+ local badge=" PASS"
325+ [ " ${_failed[$i]} " -gt 0 ] && badge=" FAIL"
326+ echo " | ${_names[$i]} | ${_passed[$i]} | ${_failed[$i]} | ${_totals[$i]} | ${badge} | ${_times[$i]} |"
327+ done
328+
329+ local total_badge=" PASS"
330+ [ " $_af " -gt 0 ] && total_badge=" FAIL"
331+ echo " | **TOTAL** | **${_tp} ** | **${_tf} ** | **${_gt} ** | **${total_badge} ** | |"
332+ echo " "
333+
334+ # Log files
335+ if ls " $logs_dir " /* .txt > /dev/null 2>&1 ; then
336+ echo " ## Log Files"
337+ echo " "
338+ for log in " $logs_dir " /* .txt; do
339+ echo " - \` $log \` "
340+ done
341+ echo " "
342+ fi
343+
344+ echo " ---"
345+ echo " "
346+ echo " _Run \` devbox run test:fast\` to regenerate this summary_"
347+ } > " $summary_file "
348+ }
0 commit comments