@zohodesk/codestandard-validator 1.2.4-exp-5 → 1.2.4-exp-7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +170 -1
- package/bin/cliCI.js +1 -0
- package/bin/zdcodequality.js +50 -0
- package/build/hooks/hook.js +3 -1
- package/build/mutation/_config.json +55 -0
- package/build/mutation/branchDiff.js +224 -149
- package/build/mutation/fileResolver.js +164 -133
- package/build/mutation/mutationCli.js +162 -100
- package/build/mutation/mutationRunner.js +216 -189
- package/build/mutation/reportGenerator.js +239 -51
- package/build/mutation/strykerConfigBuilder.js +327 -0
- package/build/mutation/strykerWrapper.js +107 -51
- package/build/utils/General/Config.js +4 -0
- package/build/utils/PluginsInstallation/checkIfPluginsAreInstalled.js +1 -0
- package/index.js +2 -1
- package/package.json +12 -6
- package/samples/sample-branch-mode.js +0 -34
- package/samples/sample-cli-entry.js +0 -34
- package/samples/sample-components.js +0 -63
- package/samples/sample-directory-mode.js +0 -30
- package/samples/sample-runner-direct.js +0 -32
- package/samples/sample-with-api.js +0 -44
|
@@ -6,165 +6,196 @@ const glob = require('glob');
|
|
|
6
6
|
const {
|
|
7
7
|
getRootDirectory
|
|
8
8
|
} = require('../utils/General/RootDirectoryUtils/getRootDirectory');
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* FileResolver — resolves source ↔ test file pairs.
|
|
12
|
+
*
|
|
13
|
+
* Uses heuristic strategies (co-located, __tests__/, mirrored dirs)
|
|
14
|
+
* to find a test file for every source file and vice versa.
|
|
15
|
+
*
|
|
16
|
+
* Compatible with Node 16+.
|
|
17
|
+
*/
|
|
18
|
+
function FileResolver(options) {
|
|
19
|
+
options = options || {};
|
|
20
|
+
this._rootdir = getRootDirectory();
|
|
21
|
+
this._cwd = options.cwd || process.cwd();
|
|
22
|
+
this._testSuffixes = options.testSuffixes || ['.test.js', '.test.ts', '.test.jsx', '.test.tsx'];
|
|
23
|
+
this._sourceExtensions = options.sourceExtensions || ['.js', '.ts', '.jsx', '.tsx', '.mjs', '.cjs'];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/* ------------------------------------------------------------------ */
|
|
27
|
+
/* Source → Test */
|
|
28
|
+
/* ------------------------------------------------------------------ */
|
|
29
|
+
|
|
30
|
+
FileResolver.prototype.findTestForSource = function (sourceFile) {
|
|
31
|
+
var parsed = path.parse(sourceFile);
|
|
32
|
+
var dir = parsed.dir;
|
|
33
|
+
var name = parsed.name;
|
|
34
|
+
var candidate, i, j, k;
|
|
35
|
+
|
|
36
|
+
// Strategy 1: co-located (src/foo.js → src/foo.test.js)
|
|
37
|
+
for (i = 0; i < this._testSuffixes.length; i++) {
|
|
38
|
+
candidate = path.join(dir, name + this._testSuffixes[i]);
|
|
39
|
+
if (fs.existsSync(path.resolve(this._rootdir, candidate))) {
|
|
40
|
+
return candidate;
|
|
27
41
|
}
|
|
42
|
+
}
|
|
28
43
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
44
|
+
// Strategy 2: __tests__ subdir (src/foo.js → src/__tests__/foo.test.js)
|
|
45
|
+
for (i = 0; i < this._testSuffixes.length; i++) {
|
|
46
|
+
candidate = path.join(dir, '__tests__', name + this._testSuffixes[i]);
|
|
47
|
+
if (fs.existsSync(path.resolve(this._rootdir, candidate))) {
|
|
48
|
+
return candidate;
|
|
35
49
|
}
|
|
50
|
+
}
|
|
36
51
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
52
|
+
// Strategy 3: mirror structure (src/x/foo.js → test/x/foo.test.js)
|
|
53
|
+
var testDirs = ['test', 'tests', '__tests__'];
|
|
54
|
+
var srcDirs = ['src', 'lib'];
|
|
55
|
+
for (i = 0; i < srcDirs.length; i++) {
|
|
56
|
+
if (dir.indexOf(srcDirs[i]) === 0) {
|
|
57
|
+
var relativePath = dir.substring(srcDirs[i].length);
|
|
58
|
+
for (j = 0; j < testDirs.length; j++) {
|
|
59
|
+
for (k = 0; k < this._testSuffixes.length; k++) {
|
|
60
|
+
candidate = path.join(testDirs[j], relativePath, name + this._testSuffixes[k]);
|
|
61
|
+
if (fs.existsSync(path.resolve(this._rootdir, candidate))) {
|
|
62
|
+
return candidate;
|
|
49
63
|
}
|
|
50
64
|
}
|
|
51
65
|
}
|
|
52
66
|
}
|
|
53
|
-
return null;
|
|
54
67
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
68
|
+
return null;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/* ------------------------------------------------------------------ */
|
|
72
|
+
/* Test → Source */
|
|
73
|
+
/* ------------------------------------------------------------------ */
|
|
74
|
+
|
|
75
|
+
FileResolver.prototype.findSourceForTest = function (testFile) {
|
|
76
|
+
var parsed = path.parse(testFile);
|
|
77
|
+
var dir = parsed.dir;
|
|
78
|
+
var name = parsed.name;
|
|
79
|
+
var candidate, i, j, k;
|
|
80
|
+
|
|
81
|
+
// Strip .test / .spec suffix
|
|
82
|
+
var suffixes = ['.test', '.spec'];
|
|
83
|
+
for (i = 0; i < suffixes.length; i++) {
|
|
84
|
+
if (name.slice(-suffixes[i].length) === suffixes[i]) {
|
|
85
|
+
name = name.slice(0, -suffixes[i].length);
|
|
86
|
+
break;
|
|
66
87
|
}
|
|
88
|
+
}
|
|
67
89
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
90
|
+
// Strategy 1: co-located
|
|
91
|
+
for (i = 0; i < this._sourceExtensions.length; i++) {
|
|
92
|
+
candidate = path.join(dir, name + this._sourceExtensions[i]);
|
|
93
|
+
if (fs.existsSync(path.resolve(this._rootdir, candidate))) {
|
|
94
|
+
return candidate;
|
|
74
95
|
}
|
|
96
|
+
}
|
|
75
97
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
98
|
+
// Strategy 2: parent of __tests__
|
|
99
|
+
if (dir.indexOf('__tests__') !== -1) {
|
|
100
|
+
var parentDir = dir.replace(/__tests__\/?/, '');
|
|
101
|
+
for (i = 0; i < this._sourceExtensions.length; i++) {
|
|
102
|
+
candidate = path.join(parentDir, name + this._sourceExtensions[i]);
|
|
103
|
+
if (fs.existsSync(path.resolve(this._rootdir, candidate))) {
|
|
104
|
+
return candidate;
|
|
84
105
|
}
|
|
85
106
|
}
|
|
107
|
+
}
|
|
86
108
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
109
|
+
// Strategy 3: mirror test → src/lib
|
|
110
|
+
var testDirs = ['test', 'tests', '__tests__'];
|
|
111
|
+
var srcDirs = ['src', 'lib'];
|
|
112
|
+
for (i = 0; i < testDirs.length; i++) {
|
|
113
|
+
if (dir.indexOf(testDirs[i]) === 0) {
|
|
114
|
+
var relativePath = dir.substring(testDirs[i].length);
|
|
115
|
+
for (j = 0; j < srcDirs.length; j++) {
|
|
116
|
+
for (k = 0; k < this._sourceExtensions.length; k++) {
|
|
117
|
+
candidate = path.join(srcDirs[j], relativePath, name + this._sourceExtensions[k]);
|
|
118
|
+
if (fs.existsSync(path.resolve(this._rootdir, candidate))) {
|
|
119
|
+
return candidate;
|
|
99
120
|
}
|
|
100
121
|
}
|
|
101
122
|
}
|
|
102
123
|
}
|
|
103
|
-
return null;
|
|
104
124
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const resolvedSources = new Set();
|
|
108
|
-
const resolvedTests = new Set();
|
|
109
|
-
|
|
110
|
-
// Map source -> test
|
|
111
|
-
for (const source of sourceFiles) {
|
|
112
|
-
const normalizedSource = source.replace(/\\/g, '/');
|
|
113
|
-
const test = this.findTestForSource(normalizedSource);
|
|
114
|
-
if (test) {
|
|
115
|
-
const normalizedTest = test.replace(/\\/g, '/');
|
|
116
|
-
pairs.push({
|
|
117
|
-
source: normalizedSource,
|
|
118
|
-
test: normalizedTest
|
|
119
|
-
});
|
|
120
|
-
resolvedSources.add(normalizedSource);
|
|
121
|
-
resolvedTests.add(normalizedTest);
|
|
122
|
-
} else {
|
|
123
|
-
// Source file with no matching test
|
|
124
|
-
pairs.push({
|
|
125
|
-
source: normalizedSource,
|
|
126
|
-
test: null
|
|
127
|
-
});
|
|
128
|
-
resolvedSources.add(normalizedSource);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
125
|
+
return null;
|
|
126
|
+
};
|
|
131
127
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
128
|
+
/* ------------------------------------------------------------------ */
|
|
129
|
+
/* Pair resolution */
|
|
130
|
+
/* ------------------------------------------------------------------ */
|
|
131
|
+
|
|
132
|
+
FileResolver.prototype.resolveSourceTestPairs = function (sourceFiles, testFiles) {
|
|
133
|
+
var pairs = [];
|
|
134
|
+
var resolvedSources = {};
|
|
135
|
+
var resolvedTests = {};
|
|
136
|
+
var i, normalizedSource, normalizedTest, test, source;
|
|
137
|
+
|
|
138
|
+
// Map source → test
|
|
139
|
+
for (i = 0; i < sourceFiles.length; i++) {
|
|
140
|
+
normalizedSource = sourceFiles[i].replace(/\\/g, '/');
|
|
141
|
+
test = this.findTestForSource(normalizedSource);
|
|
142
|
+
if (test) {
|
|
143
|
+
normalizedTest = test.replace(/\\/g, '/');
|
|
144
|
+
pairs.push({
|
|
145
|
+
source: normalizedSource,
|
|
146
|
+
test: normalizedTest
|
|
147
|
+
});
|
|
148
|
+
resolvedSources[normalizedSource] = true;
|
|
149
|
+
resolvedTests[normalizedTest] = true;
|
|
150
|
+
} else {
|
|
151
|
+
pairs.push({
|
|
152
|
+
source: normalizedSource,
|
|
153
|
+
test: null
|
|
154
|
+
});
|
|
155
|
+
resolvedSources[normalizedSource] = true;
|
|
145
156
|
}
|
|
146
|
-
return pairs;
|
|
147
157
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
158
|
+
|
|
159
|
+
// Map remaining test → source
|
|
160
|
+
for (i = 0; i < testFiles.length; i++) {
|
|
161
|
+
normalizedTest = testFiles[i].replace(/\\/g, '/');
|
|
162
|
+
if (resolvedTests[normalizedTest]) continue;
|
|
163
|
+
source = this.findSourceForTest(normalizedTest);
|
|
164
|
+
if (source && !resolvedSources[source.replace(/\\/g, '/')]) {
|
|
165
|
+
pairs.push({
|
|
166
|
+
source: source.replace(/\\/g, '/'),
|
|
167
|
+
test: normalizedTest
|
|
154
168
|
});
|
|
155
|
-
|
|
156
|
-
} catch (err) {
|
|
157
|
-
throw new Error(`Failed to collect files from '${directory}': ${err.message}`);
|
|
169
|
+
resolvedSources[source.replace(/\\/g, '/')] = true;
|
|
158
170
|
}
|
|
171
|
+
resolvedTests[normalizedTest] = true;
|
|
159
172
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
173
|
+
return pairs;
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
/* ------------------------------------------------------------------ */
|
|
177
|
+
/* File collectors (glob-based) */
|
|
178
|
+
/* ------------------------------------------------------------------ */
|
|
179
|
+
|
|
180
|
+
FileResolver.prototype.collectFiles = function (directory, pattern) {
|
|
181
|
+
var fullPattern = path.join(directory, pattern || '**/*.{js,ts,jsx,tsx}').replace(/\\/g, '/');
|
|
182
|
+
try {
|
|
183
|
+
return glob.sync(fullPattern, {
|
|
184
|
+
cwd: this._rootdir,
|
|
185
|
+
nodir: true
|
|
186
|
+
});
|
|
187
|
+
} catch (err) {
|
|
188
|
+
throw new Error("Failed to collect files from '" + directory + "': " + err.message);
|
|
168
189
|
}
|
|
169
|
-
}
|
|
190
|
+
};
|
|
191
|
+
FileResolver.prototype.collectSourceFiles = function (srcDir) {
|
|
192
|
+
var allFiles = this.collectFiles(srcDir, '**/*.{js,ts,jsx,tsx,mjs,cjs}');
|
|
193
|
+
var testPattern = /\.(test|spec)\.(js|ts|jsx|tsx|mjs|cjs)$/;
|
|
194
|
+
return allFiles.filter(function (f) {
|
|
195
|
+
return !testPattern.test(f);
|
|
196
|
+
});
|
|
197
|
+
};
|
|
198
|
+
FileResolver.prototype.collectTestFiles = function (testDir) {
|
|
199
|
+
return this.collectFiles(testDir, '**/*.{test,spec}.{js,ts,jsx,tsx}');
|
|
200
|
+
};
|
|
170
201
|
module.exports = FileResolver;
|
|
@@ -1,111 +1,173 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
var MutationRunner = require('./mutationRunner');
|
|
4
|
+
var StrykerConfigBuilder = require('./strykerConfigBuilder');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* MutationCli — CLI argument parser and executor for mutation testing.
|
|
8
|
+
*
|
|
9
|
+
* Two modes:
|
|
10
|
+
* local — pre-commit: staged + branch diff → mutate → JSON report
|
|
11
|
+
* ci — CI pipeline: branch diff → mutate → JSON report
|
|
12
|
+
*
|
|
13
|
+
* Usage (zdcodequality):
|
|
14
|
+
* npx zdcodequality mutate --local [--branch=<branch>] (default branch: release)
|
|
15
|
+
* npx zdcodequality mutate --ci [--branch=<branch>] (default branch: release)
|
|
16
|
+
*
|
|
17
|
+
* Legacy (ZDLintFramework):
|
|
18
|
+
* npx ZDLintFramework mutate --mode=local --release=<branch>
|
|
19
|
+
* npx ZDLintFramework mutate --mode=ci --release=<branch>
|
|
20
|
+
*
|
|
21
|
+
* Optional flags:
|
|
22
|
+
* --outputDir=<dir> (default: reports/mutation)
|
|
23
|
+
* --outputFileName=<name> (default: mutation-report.json)
|
|
24
|
+
* --logLevel=<level> (default: info)
|
|
25
|
+
* --concurrency=<n> (default: cpu count - 1)
|
|
26
|
+
* --timeoutMS=<ms> (default: 60000)
|
|
27
|
+
* --strykerConfig=<path> (external stryker.conf.js)
|
|
28
|
+
*
|
|
29
|
+
* Compatible with Node 16+.
|
|
30
|
+
*/
|
|
31
|
+
function MutationCli(options) {
|
|
32
|
+
this._options = options || {};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/* ------------------------------------------------------------------ */
|
|
36
|
+
/* Arg parsing */
|
|
37
|
+
/* ------------------------------------------------------------------ */
|
|
38
|
+
|
|
39
|
+
MutationCli.prototype.parseArgs = function (args) {
|
|
40
|
+
var parsed = {
|
|
41
|
+
command: null,
|
|
42
|
+
mode: null,
|
|
43
|
+
release: null,
|
|
44
|
+
outputDir: null,
|
|
45
|
+
outputFileName: null,
|
|
46
|
+
logLevel: 'info',
|
|
47
|
+
concurrency: null,
|
|
48
|
+
timeoutMS: null,
|
|
49
|
+
strykerConfig: null
|
|
50
|
+
};
|
|
51
|
+
for (var i = 0; i < args.length; i++) {
|
|
52
|
+
var arg = args[i];
|
|
53
|
+
if (arg === 'mutate') {
|
|
54
|
+
parsed.command = 'mutate';
|
|
55
|
+
} else if (arg === '--local') {
|
|
56
|
+
parsed.mode = 'local';
|
|
57
|
+
} else if (arg === '--ci') {
|
|
58
|
+
parsed.mode = 'ci';
|
|
59
|
+
} else if (arg.indexOf('--branch=') === 0) {
|
|
60
|
+
parsed.release = arg.split('=')[1];
|
|
61
|
+
} else if (arg.indexOf('--mode=') === 0) {
|
|
62
|
+
parsed.mode = arg.split('=')[1];
|
|
63
|
+
} else if (arg.indexOf('--release=') === 0) {
|
|
64
|
+
parsed.release = arg.split('=')[1];
|
|
65
|
+
} else if (arg.indexOf('--outputDir=') === 0) {
|
|
66
|
+
parsed.outputDir = arg.split('=')[1];
|
|
67
|
+
} else if (arg.indexOf('--outputFileName=') === 0) {
|
|
68
|
+
parsed.outputFileName = arg.split('=')[1];
|
|
69
|
+
} else if (arg.indexOf('--logLevel=') === 0) {
|
|
70
|
+
parsed.logLevel = arg.split('=')[1];
|
|
71
|
+
} else if (arg.indexOf('--concurrency=') === 0) {
|
|
72
|
+
parsed.concurrency = arg.split('=')[1];
|
|
73
|
+
} else if (arg.indexOf('--timeoutMS=') === 0) {
|
|
74
|
+
parsed.timeoutMS = arg.split('=')[1];
|
|
75
|
+
} else if (arg.indexOf('--strykerConfig=') === 0) {
|
|
76
|
+
parsed.strykerConfig = arg.split('=')[1];
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (parsed.release == null || parsed.release === '') {
|
|
80
|
+
parsed.release = 'release';
|
|
7
81
|
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
82
|
+
return parsed;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
/* ------------------------------------------------------------------ */
|
|
86
|
+
/* Validation */
|
|
87
|
+
/* ------------------------------------------------------------------ */
|
|
88
|
+
|
|
89
|
+
MutationCli.prototype.validate = function (parsed) {
|
|
90
|
+
if (parsed.command !== 'mutate') {
|
|
91
|
+
return {
|
|
92
|
+
valid: false,
|
|
93
|
+
error: 'Unknown command. Expected "mutate".'
|
|
19
94
|
};
|
|
20
|
-
for (const arg of args) {
|
|
21
|
-
if (arg === 'mutate') {
|
|
22
|
-
parsed.command = 'mutate';
|
|
23
|
-
} else if (arg.startsWith('--branch=')) {
|
|
24
|
-
parsed.branch = arg.split('=')[1];
|
|
25
|
-
} else if (arg.startsWith('--src=')) {
|
|
26
|
-
parsed.src = arg.split('=')[1];
|
|
27
|
-
} else if (arg.startsWith('--test=')) {
|
|
28
|
-
parsed.test = arg.split('=')[1];
|
|
29
|
-
} else if (arg === '--useApi') {
|
|
30
|
-
parsed.useApi = true;
|
|
31
|
-
} else if (arg.startsWith('--outputDir=')) {
|
|
32
|
-
parsed.outputDir = arg.split('=')[1];
|
|
33
|
-
} else if (arg.startsWith('--outputFileName=')) {
|
|
34
|
-
parsed.outputFileName = arg.split('=')[1];
|
|
35
|
-
} else if (arg.startsWith('--logLevel=')) {
|
|
36
|
-
parsed.logLevel = arg.split('=')[1];
|
|
37
|
-
} else if (arg.startsWith('--hostname=')) {
|
|
38
|
-
parsed.hostname = arg.split('=')[1];
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
return parsed;
|
|
42
95
|
}
|
|
43
|
-
|
|
44
|
-
if (parsed.command !== 'mutate') {
|
|
45
|
-
return {
|
|
46
|
-
valid: false,
|
|
47
|
-
error: 'Unknown command. Expected "mutate".'
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
const hasBranch = !!parsed.branch;
|
|
51
|
-
const hasSrcTest = !!parsed.src && !!parsed.test;
|
|
52
|
-
if (!hasBranch && !hasSrcTest) {
|
|
53
|
-
return {
|
|
54
|
-
valid: false,
|
|
55
|
-
error: 'Missing arguments. Usage:\n' + ' npx ZDLintFramework mutate --branch=<branch>\n' + ' npx ZDLintFramework mutate --src=<sourcedir> --test=<testdir>'
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
if (hasBranch && hasSrcTest) {
|
|
59
|
-
return {
|
|
60
|
-
valid: false,
|
|
61
|
-
error: 'Cannot use both --branch and --src/--test together. Choose one mode.'
|
|
62
|
-
};
|
|
63
|
-
}
|
|
96
|
+
if (!parsed.mode || parsed.mode !== 'local' && parsed.mode !== 'ci') {
|
|
64
97
|
return {
|
|
65
|
-
valid:
|
|
98
|
+
valid: false,
|
|
99
|
+
error: 'Invalid or missing mode. Use --local or --ci.\n' + 'Usage:\n' + ' npx zdcodequality mutate --local [--branch=<branch>]\n' + ' npx zdcodequality mutate --ci [--branch=<branch>]\n' + '(default branch: release)'
|
|
66
100
|
};
|
|
67
101
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
102
|
+
return {
|
|
103
|
+
valid: true
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
/* ------------------------------------------------------------------ */
|
|
108
|
+
/* Build config via builder from parsed + defaults */
|
|
109
|
+
/* ------------------------------------------------------------------ */
|
|
110
|
+
|
|
111
|
+
MutationCli.prototype._buildConfig = function (parsed) {
|
|
112
|
+
var builder = new StrykerConfigBuilder();
|
|
113
|
+
if (parsed.strykerConfig || this._options.strykerConfigpath) {
|
|
114
|
+
builder.fromConfigFile(parsed.strykerConfig || this._options.strykerConfigpath);
|
|
115
|
+
}
|
|
116
|
+
builder.setLogLevel(parsed.logLevel || this._options.logLevel || 'info');
|
|
117
|
+
var concurrency = parsed.concurrency || this._options.concurrency;
|
|
118
|
+
if (concurrency != null) {
|
|
119
|
+
builder.setConcurrency(concurrency);
|
|
120
|
+
}
|
|
121
|
+
var timeoutMS = parsed.timeoutMS || this._options.timeoutMS;
|
|
122
|
+
if (timeoutMS != null) {
|
|
123
|
+
builder.setTimeoutMS(timeoutMS);
|
|
124
|
+
}
|
|
125
|
+
if (this._options.jestConfig) {
|
|
126
|
+
builder.setJest(Object.assign({
|
|
127
|
+
projectType: 'custom',
|
|
128
|
+
enableFindRelatedTests: true
|
|
129
|
+
}, this._options.jestConfig));
|
|
130
|
+
}
|
|
131
|
+
return builder;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
/* ------------------------------------------------------------------ */
|
|
135
|
+
/* Execution */
|
|
136
|
+
/* ------------------------------------------------------------------ */
|
|
137
|
+
|
|
138
|
+
MutationCli.prototype.execute = function (args) {
|
|
139
|
+
var parsed = this.parseArgs(args);
|
|
140
|
+
var validation = this.validate(parsed);
|
|
141
|
+
if (!validation.valid) {
|
|
142
|
+
return Promise.resolve({
|
|
143
|
+
success: false,
|
|
144
|
+
error: validation.error
|
|
87
145
|
});
|
|
88
|
-
try {
|
|
89
|
-
let result;
|
|
90
|
-
if (parsed.branch) {
|
|
91
|
-
result = await runner.runByBranch(parsed.branch, {
|
|
92
|
-
useApi: parsed.useApi,
|
|
93
|
-
hostname: this._options.hostname,
|
|
94
|
-
projectId: this._options.projectId
|
|
95
|
-
});
|
|
96
|
-
} else {
|
|
97
|
-
result = await runner.runByDirectory(parsed.src, parsed.test);
|
|
98
|
-
}
|
|
99
|
-
return {
|
|
100
|
-
success: true,
|
|
101
|
-
...result
|
|
102
|
-
};
|
|
103
|
-
} catch (err) {
|
|
104
|
-
return {
|
|
105
|
-
success: false,
|
|
106
|
-
error: err.message
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
146
|
}
|
|
110
|
-
|
|
147
|
+
var configBuilder = this._buildConfig(parsed);
|
|
148
|
+
var runner = new MutationRunner({
|
|
149
|
+
...this._options,
|
|
150
|
+
cwd: this._options.cwd,
|
|
151
|
+
configBuilder: configBuilder,
|
|
152
|
+
jestConfig: this._options.jestConfig,
|
|
153
|
+
outputDir: parsed.outputDir || this._options.outputDir,
|
|
154
|
+
outputFileName: parsed.outputFileName || this._options.outputFileName
|
|
155
|
+
});
|
|
156
|
+
var promise;
|
|
157
|
+
if (parsed.mode === 'local') {
|
|
158
|
+
promise = runner.runLocal(parsed.release);
|
|
159
|
+
} else {
|
|
160
|
+
promise = runner.runCI(parsed.release);
|
|
161
|
+
}
|
|
162
|
+
return promise.then(function (result) {
|
|
163
|
+
return Object.assign({
|
|
164
|
+
success: true
|
|
165
|
+
}, result);
|
|
166
|
+
}).catch(function (err) {
|
|
167
|
+
return {
|
|
168
|
+
success: false,
|
|
169
|
+
error: err.message
|
|
170
|
+
};
|
|
171
|
+
});
|
|
172
|
+
};
|
|
111
173
|
module.exports = MutationCli;
|