forked from tnicola/cypress-parallel
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy paththread.js
More file actions
161 lines (135 loc) · 4.43 KB
/
Copy paththread.js
File metadata and controls
161 lines (135 loc) · 4.43 KB
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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
const spawn = require('cross-spawn');
const { isYarn } = require('is-npm');
const path = require('path');
const fs = require('fs');
const camelCase = require('lodash.camelcase');
const globEscape = require('glob-escape');
const { settings } = require('./settings');
const { sleep } = require('./utility');
function getPackageManager() {
const pckManager = isYarn
? 'yarn'
: process.platform === 'win32'
? 'npm.cmd'
: 'npm';
return pckManager;
}
function createReporterOptions(string) {
const options = string.split(',');
return options.reduce((result, current) => {
const parts = current.split('=');
const optionName = parts[0].trim();
const optionValue = parts[1].trim();
result[optionName] = optionValue;
return result;
}, {});
}
function createReporterConfigFile(path) {
const reporterEnabled = ['@openx/cypress-parallel/json-stream.reporter.js'];
let reporterName = settings.reporter;
if (settings.reporter) {
reporterEnabled.push(reporterName);
} else {
reporterEnabled.push('@openx/cypress-parallel/simple-spec.reporter.js');
}
const content = {
reporterEnabled: reporterEnabled.join(', ')
};
if (settings.reporterOptions) {
const optionName = `${camelCase(reporterName)}ReporterOptions`;
content[optionName] = createReporterOptions(settings.reporterOptions);
}
content['runnerResults'] = settings.runnerResults;
fs.writeFileSync(path, JSON.stringify(content, null, 2));
}
function createCommandArguments(thread) {
const specFiles = `${thread.list.map((path) => globEscape(path)).join(',')}`;
const childOptions = [
'run',
`${settings.script}`,
isYarn ? '' : '--',
'--spec',
specFiles
];
let reporterConfigPath;
if (settings.reporterOptionsPath) {
reporterConfigPath = settings.reporterOptionsPath;
} else {
reporterConfigPath = path.join(process.cwd(), 'multi-reporter-config.json');
createReporterConfigFile(reporterConfigPath);
}
childOptions.push('--reporter', settings.reporterModulePath);
childOptions.push('--reporter-options', `configFile=${reporterConfigPath}`);
childOptions.push(...settings.scriptArguments);
return childOptions;
}
async function executeThread(thread, index) {
const packageManager = getPackageManager();
const commandArguments = createCommandArguments(thread);
const cypressThreadNumber = (index + 1).toString();
const threadPrefix = `[${cypressThreadNumber}/${settings.threadCount}]`;
// staggered start (when executed in container with xvfb ends up having a race condition causing intermittent failures)
await sleep((index + 1) * 5000);
const timeMap = new Map();
const promise = new Promise((resolve, reject) => {
const processOptions = {
cwd: process.cwd(),
stdio: 'pipe',
env: {
...process.env,
CYPRESS_THREAD: cypressThreadNumber
}
};
const child = spawn(packageManager, commandArguments, processOptions);
let stdoutBuffer = '';
child.stdout.on('data', function (chunk) {
stdoutBuffer += chunk;
const lines = stdoutBuffer.split('\n');
while (lines.length > 1) {
const line = lines.shift();
console.log(threadPrefix, line);
}
stdoutBuffer = lines.shift();
});
child.stdout.on('end', function () {
console.log(threadPrefix, stdoutBuffer);
});
let stderrBuffer = '';
child.stderr.on('data', function (chunk) {
stderrBuffer += chunk;
const lines = stderrBuffer.split('\n');
while (lines.length > 1) {
const line = lines.shift();
console.error(threadPrefix, line);
}
stderrBuffer = lines.shift();
});
child.stderr.on('end', function () {
console.error(threadPrefix, stderrBuffer);
});
child.on('exit', (exitCode) => {
if (settings.isVerbose) {
console.log(
`${threadPrefix} Thread likely finished with failure count: ${exitCode}`
);
}
// should preferably exit earlier, but this is simple and better than nothing
if (settings.shouldBail) {
if (exitCode > 0) {
console.error(
`${threadPrefix} BAIL set and thread exited with errors, exit early with error`
);
process.exit(exitCode);
}
}
if (exitCode === 0) {
resolve(timeMap);
}
reject(new Error(`${threadPrefix} Thread exited with code ${exitCode}`));
});
});
return promise;
}
module.exports = {
executeThread
};