jest-test-lineage-reporter 2.1.1 → 2.1.3

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 CHANGED
@@ -100,17 +100,21 @@ jest-lineage mutate --threshold 90
100
100
  jest-lineage mutate --debug --debug-dir ./mutations
101
101
  ```
102
102
 
103
+ **Note**: Mutation results are saved to `.jest-lineage-mutation-results.json`. Run `jest-lineage report` after mutation testing to generate an HTML report with mutation data included.
104
+
103
105
  #### `jest-lineage report`
104
- Generate HTML report from existing lineage data.
106
+ Generate HTML report from existing lineage data and mutation results.
105
107
 
106
108
  ```bash
107
- # Generate and open report
109
+ # Generate and open report (includes mutation results if available)
108
110
  jest-lineage report --open
109
111
 
110
112
  # Custom output path
111
113
  jest-lineage report --output coverage-report.html
112
114
  ```
113
115
 
116
+ **Tip**: The report command automatically loads mutation results from `.jest-lineage-mutation-results.json` if available.
117
+
114
118
  #### `jest-lineage query <file> [line]`
115
119
  Query which tests cover specific files or lines.
116
120
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jest-test-lineage-reporter",
3
- "version": "2.1.1",
3
+ "version": "2.1.3",
4
4
  "main": "src/TestCoverageReporter.js",
5
5
  "bin": {
6
6
  "jest-lineage": "./bin/jest-lineage.js"
@@ -57,6 +57,7 @@
57
57
  "LICENSE"
58
58
  ],
59
59
  "dependencies": {
60
+ "@babel/runtime": "^7.28.4",
60
61
  "@modelcontextprotocol/sdk": "^0.5.0",
61
62
  "chalk": "^4.1.2",
62
63
  "cli-table3": "^0.6.3",
@@ -3104,7 +3104,7 @@ class TestCoverageReporter {
3104
3104
  // Group mutations by line
3105
3105
  const mutationsByLine = {};
3106
3106
  fileData.mutations.forEach(mutation => {
3107
- const line = mutation.line || 'unknown';
3107
+ const line = mutation.lineNumber || mutation.line || 'unknown';
3108
3108
  if (!mutationsByLine[line]) {
3109
3109
  mutationsByLine[line] = [];
3110
3110
  }
@@ -22,6 +22,20 @@ async function initCommand(options) {
22
22
  process.exit(1);
23
23
  }
24
24
 
25
+ // Detect if jest-test-lineage-reporter is installed locally or globally
26
+ const isLocalInstall = isInstalledLocally(cwd);
27
+
28
+ if (!isLocalInstall) {
29
+ warning('jest-test-lineage-reporter is installed globally.');
30
+ warning('For best results, install it locally in your project:\n');
31
+ console.log(' npm install --save-dev jest-test-lineage-reporter\n');
32
+
33
+ if (!options.force) {
34
+ error('Global installation detected. Use --force to continue with absolute paths.');
35
+ process.exit(1);
36
+ }
37
+ }
38
+
25
39
  // Read package.json to check dependencies
26
40
  const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
27
41
  const allDeps = {
@@ -51,8 +65,11 @@ async function initCommand(options) {
51
65
  if (!options.force) {
52
66
  warning(`jest.config.js already exists. Use --force to overwrite.`);
53
67
  info('Please manually add the following to your jest.config.js:');
68
+ const setupPath = isLocalInstall
69
+ ? 'jest-test-lineage-reporter/src/testSetup.js'
70
+ : getGlobalPackagePath('testSetup.js');
54
71
  console.log(`
55
- setupFilesAfterEnv: ['jest-test-lineage-reporter/src/testSetup.js'],
72
+ setupFilesAfterEnv: ['${setupPath}'],
56
73
 
57
74
  reporters: [
58
75
  'default',
@@ -70,11 +87,11 @@ async function initCommand(options) {
70
87
  },
71
88
  `);
72
89
  } else {
73
- createJestConfig(jestConfigPath, options);
90
+ createJestConfig(jestConfigPath, options, isLocalInstall);
74
91
  jestConfigCreated = true;
75
92
  }
76
93
  } else {
77
- createJestConfig(jestConfigPath, options);
94
+ createJestConfig(jestConfigPath, options, isLocalInstall);
78
95
  jestConfigCreated = true;
79
96
  }
80
97
 
@@ -84,17 +101,20 @@ async function initCommand(options) {
84
101
  if (!options.force) {
85
102
  warning(`babel.config.js already exists. Use --force to overwrite.`);
86
103
  info('Please manually add the lineage tracker plugin to your babel.config.js:');
104
+ const pluginPath = isLocalInstall
105
+ ? 'jest-test-lineage-reporter/src/babel-plugin-lineage-tracker.js'
106
+ : getGlobalPackagePath('babel-plugin-lineage-tracker.js');
87
107
  console.log(`
88
108
  plugins: [
89
- 'jest-test-lineage-reporter/src/babel-plugin-lineage-tracker.js',
109
+ '${pluginPath}',
90
110
  ],
91
111
  `);
92
112
  } else {
93
- createBabelConfig(babelConfigPath, options);
113
+ createBabelConfig(babelConfigPath, options, isLocalInstall);
94
114
  babelConfigCreated = true;
95
115
  }
96
116
  } else {
97
- createBabelConfig(babelConfigPath, options);
117
+ createBabelConfig(babelConfigPath, options, isLocalInstall);
98
118
  babelConfigCreated = true;
99
119
  }
100
120
 
@@ -137,14 +157,17 @@ async function initCommand(options) {
137
157
  }
138
158
  }
139
159
 
140
- function createJestConfig(filePath, options) {
160
+ function createJestConfig(filePath, options, isLocalInstall) {
141
161
  const isTypeScript = options.typescript || hasTypeScriptFiles();
162
+ const setupPath = isLocalInstall
163
+ ? 'jest-test-lineage-reporter/src/testSetup.js'
164
+ : getGlobalPackagePath('testSetup.js');
142
165
 
143
166
  const config = `module.exports = {
144
167
  testEnvironment: 'node',
145
168
 
146
169
  // Required: Setup file for lineage tracking
147
- setupFilesAfterEnv: ['jest-test-lineage-reporter/src/testSetup.js'],
170
+ setupFilesAfterEnv: ['${setupPath}'],
148
171
 
149
172
  // Add the lineage reporter
150
173
  reporters: [
@@ -179,8 +202,11 @@ function createJestConfig(filePath, options) {
179
202
  fs.writeFileSync(filePath, config, 'utf8');
180
203
  }
181
204
 
182
- function createBabelConfig(filePath, options) {
205
+ function createBabelConfig(filePath, options, isLocalInstall) {
183
206
  const isTypeScript = options.typescript || hasTypeScriptFiles();
207
+ const pluginPath = isLocalInstall
208
+ ? 'jest-test-lineage-reporter/src/babel-plugin-lineage-tracker.js'
209
+ : getGlobalPackagePath('babel-plugin-lineage-tracker.js');
184
210
 
185
211
  const config = `module.exports = {
186
212
  presets: [
@@ -189,7 +215,7 @@ function createBabelConfig(filePath, options) {
189
215
  ],
190
216
  plugins: [
191
217
  // Required: Lineage tracker plugin for instrumentation
192
- 'jest-test-lineage-reporter/src/babel-plugin-lineage-tracker.js',
218
+ '${pluginPath}',
193
219
  ],
194
220
  };
195
221
  `;
@@ -211,4 +237,18 @@ function hasTypeScriptFiles() {
211
237
  }
212
238
  }
213
239
 
240
+ function isInstalledLocally(cwd) {
241
+ const localPath = path.join(cwd, 'node_modules', 'jest-test-lineage-reporter');
242
+ return fs.existsSync(localPath);
243
+ }
244
+
245
+ function getGlobalPackagePath(filename) {
246
+ // Get the directory where this script is running from
247
+ const scriptDir = path.dirname(path.dirname(path.dirname(__dirname)));
248
+ const srcPath = path.join(scriptDir, 'src', filename);
249
+
250
+ // Return absolute path
251
+ return srcPath;
252
+ }
253
+
214
254
  module.exports = initCommand;
@@ -55,6 +55,13 @@ async function mutateCommand(options) {
55
55
  spin.succeed('Mutation testing completed!');
56
56
  }
57
57
 
58
+ // Save results to file for HTML report
59
+ const fs = require('fs');
60
+ const path = require('path');
61
+ const resultsPath = path.join(process.cwd(), '.jest-lineage-mutation-results.json');
62
+ fs.writeFileSync(resultsPath, JSON.stringify(results, null, 2));
63
+ info(`Mutation results saved to: ${chalk.yellow(resultsPath)}`);
64
+
58
65
  // Print results
59
66
  printMutationSummary(results);
60
67
 
@@ -26,6 +26,19 @@ async function reportCommand(options) {
26
26
  // Process lineage data
27
27
  const lineageData = processLineageDataForMutation(rawData);
28
28
 
29
+ // Load mutation results if available
30
+ const fs = require('fs');
31
+ const mutationResultsPath = path.join(process.cwd(), '.jest-lineage-mutation-results.json');
32
+ let mutationResults = null;
33
+ if (fs.existsSync(mutationResultsPath)) {
34
+ try {
35
+ mutationResults = JSON.parse(fs.readFileSync(mutationResultsPath, 'utf8'));
36
+ info(`Loaded mutation results: ${chalk.cyan(mutationResults.totalMutations)} mutations tested`);
37
+ } catch (e) {
38
+ // Ignore errors loading mutation results
39
+ }
40
+ }
41
+
29
42
  // Create reporter instance with minimal config
30
43
  const reporter = new TestCoverageReporter(
31
44
  { rootDir: process.cwd() },
@@ -35,6 +48,11 @@ async function reportCommand(options) {
35
48
  // Load data into reporter
36
49
  reporter.processLineageResults(lineageData, 'unknown');
37
50
 
51
+ // Set mutation results if available
52
+ if (mutationResults) {
53
+ reporter.mutationResults = mutationResults;
54
+ }
55
+
38
56
  // Generate HTML report
39
57
  const spin = spinner('Generating HTML report...');
40
58
  spin.start();
@@ -39,6 +39,12 @@ async function runJest(options = {}) {
39
39
  jestArgs.push('--coverage');
40
40
  }
41
41
 
42
+ // Run tests serially when lineage tracking is enabled to avoid race conditions
43
+ // with file writes from parallel workers
44
+ if (enableLineage && !jestArgs.includes('--runInBand') && !jestArgs.includes('--maxWorkers')) {
45
+ jestArgs.push('--runInBand');
46
+ }
47
+
42
48
  // Set environment variables for lineage tracking
43
49
  const env = {
44
50
  ...process.env,
package/src/testSetup.js CHANGED
@@ -278,7 +278,31 @@ function createTestWrapper(originalFn, testType) {
278
278
  if (process.env.JEST_LINEAGE_MUTATION !== 'true') {
279
279
  // Also store in a more persistent way for the reporter
280
280
  if (!global.__LINEAGE_PERSISTENT_DATA__) {
281
- global.__LINEAGE_PERSISTENT_DATA__ = [];
281
+ // Initialize array - load existing tests from file if merging is enabled
282
+ // This ensures data persists across test files running in separate workers
283
+ const shouldMerge = process.env.JEST_LINEAGE_MERGE !== 'false';
284
+ if (shouldMerge) {
285
+ const fs = require('fs');
286
+ const path = require('path');
287
+ const filePath = path.join(process.cwd(), '.jest-lineage-data.json');
288
+
289
+ if (fs.existsSync(filePath)) {
290
+ try {
291
+ const existingData = JSON.parse(fs.readFileSync(filePath, 'utf8'));
292
+ // Convert coverage objects back to Maps
293
+ global.__LINEAGE_PERSISTENT_DATA__ = existingData.tests.map(test => ({
294
+ ...test,
295
+ coverage: new Map(Object.entries(test.coverage || {}))
296
+ }));
297
+ } catch (e) {
298
+ global.__LINEAGE_PERSISTENT_DATA__ = [];
299
+ }
300
+ } else {
301
+ global.__LINEAGE_PERSISTENT_DATA__ = [];
302
+ }
303
+ } else {
304
+ global.__LINEAGE_PERSISTENT_DATA__ = [];
305
+ }
282
306
  }
283
307
  global.__LINEAGE_PERSISTENT_DATA__.push(testData);
284
308
 
@@ -645,8 +669,9 @@ function writeTrackingDataToFile() {
645
669
  try {
646
670
  const filePath = path.join(process.cwd(), '.jest-lineage-data.json');
647
671
 
648
- // Check if we should merge with existing data (default: false - recreate from scratch)
649
- const shouldMerge = process.env.JEST_LINEAGE_MERGE === 'true';
672
+ // Check if we should merge with existing data (default: true for multiple test files)
673
+ // Set JEST_LINEAGE_MERGE=false to disable merging and recreate from scratch
674
+ const shouldMerge = process.env.JEST_LINEAGE_MERGE !== 'false';
650
675
 
651
676
  let existingData = { timestamp: Date.now(), tests: [] };
652
677
  if (shouldMerge && fs.existsSync(filePath)) {
@@ -717,8 +742,6 @@ function writeTrackingDataToFile() {
717
742
  }
718
743
 
719
744
  fs.writeFileSync(filePath, JSON.stringify(dataToWrite, null, 2));
720
-
721
- // console.log(`📝 Wrote tracking data: ${dataToWrite.tests.length} total tests to ${filePath}`);
722
745
  } catch (error) {
723
746
  console.warn('Warning: Could not write tracking data to file:', error.message);
724
747
  }