forked from vercel/next.js
-
Notifications
You must be signed in to change notification settings - Fork 0
/
run-tests.js
141 lines (125 loc) · 4.24 KB
/
run-tests.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
const path = require('path')
const _glob = require('glob')
const fs = require('fs-extra')
const { promisify } = require('util')
const { Sema } = require('async-sema')
const { spawn, exec: execOrig } = require('child_process')
const glob = promisify(_glob)
const exec = promisify(execOrig)
const NUM_RETRIES = 2
const DEFAULT_CONCURRENCY = 2
const timings = []
;(async () => {
let concurrencyIdx = process.argv.indexOf('-c')
const concurrency =
parseInt(process.argv[concurrencyIdx + 1], 10) || DEFAULT_CONCURRENCY
const outputTimings = process.argv.indexOf('--timings') !== -1
const groupIdx = process.argv.indexOf('-g')
const groupArg = groupIdx !== -1 && process.argv[groupIdx + 1]
console.log('Running tests with concurrency:', concurrency)
let tests = process.argv.filter(arg => arg.endsWith('.test.js'))
if (tests.length === 0) {
tests = await glob('**/*.test.js', {
nodir: true,
cwd: path.join(__dirname, 'test'),
})
}
let testNames = [
...new Set(
tests.map(f => {
let name = `${f.replace(/\\/g, '/').replace(/\/test$/, '')}`
if (!name.startsWith('test/')) name = `test/${name}`
return name
})
),
]
if (groupArg) {
const groupParts = groupArg.split('/')
const groupPos = parseInt(groupParts[0], 10)
const groupTotal = parseInt(groupParts[1], 10)
const numPerGroup = Math.ceil(testNames.length / groupTotal)
let offset = groupPos === 1 ? 0 : (groupPos - 1) * numPerGroup - 1
// if there's an odd number of suites give the first group the extra
if (testNames.length % 2 !== 0 && groupPos !== 1) offset++
testNames = testNames.splice(offset, numPerGroup)
}
console.log('Running tests:', '\n', ...testNames.map(name => `${name}\n`))
const sema = new Sema(concurrency, { capacity: testNames.length })
const jestPath = path.join(
path.dirname(require.resolve('jest-cli/package')),
'bin/jest.js'
)
const children = new Set()
const runTest = (test = '') =>
new Promise((resolve, reject) => {
const start = new Date().getTime()
const child = spawn(
'node',
[jestPath, '--runInBand', '--forceExit', '--verbose', test],
{
stdio: 'inherit',
}
)
children.add(child)
child.on('exit', code => {
children.delete(child)
if (code) reject(new Error(`failed with code: ${code}`))
resolve(new Date().getTime() - start)
})
})
await Promise.all(
testNames.map(async test => {
await sema.acquire()
let passed = false
for (let i = 0; i < NUM_RETRIES + 1; i++) {
try {
const time = await runTest(test)
timings.push({
file: test,
time,
})
passed = true
break
} catch (err) {
if (i < NUM_RETRIES) {
try {
console.log('Cleaning test files for', test)
await exec(`git clean -fdx "${path.join(__dirname, test)}"`)
await exec(`git checkout "${path.join(__dirname, test)}"`)
} catch (err) {}
}
}
}
if (!passed) {
console.error(`${test} failed to pass within ${NUM_RETRIES} retries`)
children.forEach(child => child.kill())
process.exit(1)
}
sema.release()
})
)
if (outputTimings) {
let junitData = `<testsuites name="jest tests">`
/*
<testsuite name="/__tests__/bar.test.js" tests="1" errors="0" failures="0" skipped="0" timestamp="2017-10-10T21:56:49" time="0.323">
<testcase classname="bar-should be bar" name="bar-should be bar" time="0.004">
</testcase>
</testsuite>
*/
for (const timing of timings) {
const timeInSeconds = timing.time / 1000
junitData += `
<testsuite name="${timing.file}" file="${
timing.file
}" tests="1" errors="0" failures="0" skipped="0" timestamp="${new Date().toJSON()}" time="${timeInSeconds}">
<testcase classname="tests suite should pass" name="${
timing.file
}" time="${timeInSeconds}"></testcase>
</testsuite>
`
}
junitData += `</testsuites>`
await fs.writeFile('test/junit.xml', junitData, 'utf8')
console.log('output timing data to junit.xml')
}
})()