(output)
| 88 | // some frameworks if output was truncated. |
| 89 | |
| 90 | function parsePytest(output) { |
| 91 | const result = { passed: 0, failed: 0, errors: 0, skipped: 0, failures: [] }; |
| 92 | |
| 93 | // Summary line: "3 passed, 1 failed, 2 errors, 4 skipped in 0.5s" |
| 94 | const summaryRe = /(\d+) passed|(\d+) failed|(\d+) error(?:s)?|(\d+) skipped/gi; |
| 95 | const summaryLine = output.split('\n').reverse().find(l => /passed|failed|error/i.test(l) && /in \d/.test(l)); |
| 96 | if (summaryLine) { |
| 97 | let m; |
| 98 | while ((m = summaryRe.exec(summaryLine)) !== null) { |
| 99 | if (m[1]) result.passed = parseInt(m[1], 10); |
| 100 | if (m[2]) result.failed = parseInt(m[2], 10); |
| 101 | if (m[3]) result.errors = parseInt(m[3], 10); |
| 102 | if (m[4]) result.skipped = parseInt(m[4], 10); |
| 103 | } |
| 104 | } |
| 105 | |
| 106 | // Per-test FAILED lines: "FAILED tests/test_foo.py::test_bar - AssertionError: ..." |
| 107 | const failRe = /^FAILED\s+(\S+)\s*(?:-\s*(.+))?$/mg; |
| 108 | let fm; |
| 109 | while ((fm = failRe.exec(output)) !== null) { |
| 110 | result.failures.push({ name: fm[1].trim(), message: (fm[2] || '').trim().slice(0, 200) }); |
| 111 | } |
| 112 | |
| 113 | // Error lines: "ERROR tests/test_foo.py::test_bar" |
| 114 | const errRe = /^ERROR\s+(\S+)/mg; |
| 115 | let em; |
| 116 | while ((em = errRe.exec(output)) !== null) { |
| 117 | result.failures.push({ name: em[1].trim(), message: 'test error (collection/setup/teardown)' }); |
| 118 | } |
| 119 | |
| 120 | return result; |
| 121 | } |
| 122 | |
| 123 | function parseJest(output) { |
| 124 | const result = { passed: 0, failed: 0, errors: 0, skipped: 0, failures: [] }; |
no outgoing calls
no test coverage detected