"""최종 Playwright 원문을 보존하며 첨부 실측값과 검증 요약만 별도 파일로 추출한다.""" import base64 import json from pathlib import Path EVIDENCE = Path(__file__).resolve().parent report = json.loads((EVIDENCE / 'browser-results.json').read_text()) projects = {} def walk(suite): """중첩 suite를 순회한다. 실제 result가 실패하면 성공 요약을 만들지 않는다.""" for spec in suite.get('specs', []): for test in spec['tests']: project = projects.setdefault(test['projectName'], {'passed': 0, 'attachments': {}}) assert len(test['results']) == 1, '재시도 없는 최종 실행만 성공으로 요약한다.' result = test['results'][0] assert result['status'] == 'passed', spec['title'] project['passed'] += 1 for attachment in result.get('attachments', []): if attachment['contentType'] != 'application/json': continue # 파일 첨부와 base64 본문 첨부의 원문을 같은 방식으로 재사용한다. raw = (Path(attachment['path']).read_bytes() if 'path' in attachment else base64.b64decode(attachment['body'])) name = attachment['name'].replace(' ', '-') output = EVIDENCE / f"final-{test['projectName']}-{name}.json" output.write_bytes(raw) project['attachments'][name] = output.name for child in suite.get('suites', []): walk(child) walk(report) for project in projects.values(): gains = json.loads((EVIDENCE / project['attachments']['actual-gain-nodes']).read_text()) audit = json.loads((EVIDENCE / project['attachments']['runtime-audit']).read_text()) resampled = json.loads((EVIDENCE / project['attachments']['all-voices-resampled-bounds']).read_text()) project['resampledPeakMaxima'] = {kind: max(row['normalizedPeak'] for row in resampled['rows'] if row['kind'] == kind) for kind in ('background', 'voice', 'intro')} project['gainCases'] = len(gains['rows']) project['observedBackgroundGainValues'] = sorted({r['sources'][0]['observed'] for r in gains['rows']}) project['scheduledBackgroundGainValues'] = sorted({r['sources'][0]['scheduled'] for r in gains['rows']}) project['normalFlowAudit'] = {key: len(audit[key]) + len(gains[key]) for key in ('errors', 'failed', 'external')} assert project['normalFlowAudit'] == {'errors': 0, 'failed': 0, 'external': 0} project['axeViolations'] = 0 # game/rounds의 실제 axe assertion 통과를 위 status에서 확인했다. summary = {'stats': report['stats'], 'projects': projects, 'scope': 'System Chrome desktop/mobile emulation; native Web Audio; local built app; intentional fault injections excluded from normal-flow error totals.', 'sandboxEvidence': 'chrome-sandbox-final.json'} (EVIDENCE / 'browser-summary.json').write_text(json.dumps(summary, indent=2) + '\n') print(json.dumps(summary, indent=2))