* Sort test to determine order of execution * Sorting is applied after sharding * @param tests * * ```typescript * class CustomSequencer extends Sequencer { * sort(tests) { * const copyTests = Array.from(tests); * return [...tests].sort((a, b) => (a.path > b.path ?
(tests: Array<Test>)
| 173 | * ``` |
| 174 | */ |
| 175 | sort(tests: Array<Test>): Array<Test> | Promise<Array<Test>> { |
| 176 | /** |
| 177 | * Sorting tests is very important because it has a great impact on the |
| 178 | * user-perceived responsiveness and speed of the test run. |
| 179 | * |
| 180 | * If such information is on cache, tests are sorted based on: |
| 181 | * -> Has it failed during the last run ? |
| 182 | * Since it's important to provide the most expected feedback as quickly |
| 183 | * as possible. |
| 184 | * -> How long it took to run ? |
| 185 | * Because running long tests first is an effort to minimize worker idle |
| 186 | * time at the end of a long test run. |
| 187 | * And if that information is not available they are sorted based on file size |
| 188 | * since big test files usually take longer to complete. |
| 189 | * |
| 190 | * Note that a possible improvement would be to analyse other information |
| 191 | * from the file other than its size. |
| 192 | * |
| 193 | */ |
| 194 | const stats: {[path: string]: number} = {}; |
| 195 | const fileSize = ({path, context: {hasteFS}}: Test) => |
| 196 | stats[path] || (stats[path] = hasteFS.getSize(path) ?? 0); |
| 197 | |
| 198 | for (const test of tests) { |
| 199 | test.duration = this.time(test); |
| 200 | } |
| 201 | return tests.sort((testA, testB) => { |
| 202 | const failedA = this.hasFailed(testA); |
| 203 | const failedB = this.hasFailed(testB); |
| 204 | const hasTimeA = testA.duration != null; |
| 205 | const hasTimeB = testB.duration != null; |
| 206 | if (failedA !== failedB) { |
| 207 | return failedA ? -1 : 1; |
| 208 | } else if (hasTimeA !== hasTimeB) { |
| 209 | // If only one of two tests has timing information, run it last |
| 210 | return hasTimeA ? 1 : -1; |
| 211 | } else if (testA.duration != null && testB.duration != null) { |
| 212 | return testA.duration < testB.duration ? 1 : -1; |
| 213 | } else { |
| 214 | return fileSize(testA) < fileSize(testB) ? 1 : -1; |
| 215 | } |
| 216 | }); |
| 217 | } |
| 218 | |
| 219 | allFailedTests(tests: Array<Test>): Array<Test> | Promise<Array<Test>> { |
| 220 | return this.sort(tests.filter(test => this.hasFailed(test))); |