(cmd, args, options, reportStatus, startTime = Date.now(), testAttempts = 2)
| 85 | } |
| 86 | |
| 87 | function spawnTest(cmd, args, options, reportStatus, startTime = Date.now(), testAttempts = 2) { |
| 88 | testAttempts -= 1; |
| 89 | |
| 90 | const testProgressRegex = /Running test[^\(]+\((\d+) of/g; |
| 91 | |
| 92 | return new Promise((resolve, reject) => { |
| 93 | let output = ''; |
| 94 | let state = 'setup'; |
| 95 | let current = 0; |
| 96 | let max = 0; |
| 97 | |
| 98 | const proc = child_process.spawn(cmd, args, { ...options, stdio: 'pipe' }); |
| 99 | const syncStatus = () => reportStatus({ current, max, state, startTime }); |
| 100 | const restartTest = () => { |
| 101 | console.error(output); |
| 102 | console.error(`Test restarted due to failure.`); |
| 103 | resolve(spawnTest(cmd, args, options, reportStatus, startTime, testAttempts)); |
| 104 | }; |
| 105 | const onOutputChange = () => { |
| 106 | // Extract initial status (i.e. how many tests there are in this shard) |
| 107 | if (initialStatusRegex.test(output) && state === 'setup') { |
| 108 | max = Number(output.match(initialStatusRegex)[1]); |
| 109 | } |
| 110 | if (/Running initializer/.test(output) && state === 'setup') { |
| 111 | state = 'initializing'; |
| 112 | } |
| 113 | if (/Running test/.test(output) && state === 'initializing') { |
| 114 | state = 'testing'; |
| 115 | } |
| 116 | if (state === 'testing') { |
| 117 | const oldLastIndex = testProgressRegex.lastIndex; |
| 118 | const newMatch = testProgressRegex.exec(stripVTControlCharacters(output))?.[1]; |
| 119 | // Do not advance the Regex, or more precisely, reset to index `0`. |
| 120 | if (newMatch === undefined) { |
| 121 | testProgressRegex.lastIndex = oldLastIndex; |
| 122 | } else { |
| 123 | current = Number(newMatch); |
| 124 | } |
| 125 | } |
| 126 | syncStatus(); |
| 127 | }; |
| 128 | proc.stdout.on('data', (data) => { |
| 129 | output += data; |
| 130 | onOutputChange(); |
| 131 | }); |
| 132 | proc.stderr.on('data', (data) => { |
| 133 | output += data; |
| 134 | onOutputChange(); |
| 135 | }); |
| 136 | proc.on('error', (err) => { |
| 137 | syncStatus(); |
| 138 | |
| 139 | // If this test failed and there are test attempts remaining, re-run. |
| 140 | if (testAttempts > 0) { |
| 141 | restartTest(); |
| 142 | return; |
| 143 | } |
| 144 |
no test coverage detected