(output)
| 142 | } |
| 143 | |
| 144 | function parseVitest(output) { |
| 145 | const result = { passed: 0, failed: 0, errors: 0, skipped: 0, failures: [] }; |
| 146 | |
| 147 | // "Tests 1 failed | 2 passed (3)" — prefer the "Tests" summary over "Test Files" |
| 148 | // because "Test Files" only shows file counts, not individual test counts. |
| 149 | const lines = output.split('\n'); |
| 150 | const testsLine = lines.find(l => /^Tests\s/i.test(l.trim())) |
| 151 | || lines.find(l => /^Test Files\s/i.test(l.trim())); |
| 152 | if (testsLine) { |
| 153 | const p = testsLine.match(/(\d+) passed/); if (p) result.passed = parseInt(p[1], 10); |
| 154 | const f = testsLine.match(/(\d+) failed/); if (f) result.failed = parseInt(f[1], 10); |
| 155 | const s = testsLine.match(/(\d+) skipped/); if (s) result.skipped = parseInt(s[1], 10); |
| 156 | } |
| 157 | |
| 158 | // "✗ test name" or "× test name" in verbose mode |
| 159 | const failRe = /^\s*[✗×✕]\s+(.+?)(?:\s+\d+ms)?$/mg; |
| 160 | let m; |
| 161 | while ((m = failRe.exec(output)) !== null) { |
| 162 | result.failures.push({ name: m[1].trim(), message: '' }); |
| 163 | } |
| 164 | |
| 165 | return result; |
| 166 | } |
| 167 | |
| 168 | function parseGoTest(output) { |
| 169 | const result = { passed: 0, failed: 0, errors: 0, skipped: 0, failures: [] }; |
no outgoing calls
no test coverage detected