jest-test-lineage-reporter 2.0.2 → 2.1.1

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.
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Output Formatter
3
+ * Format console output with colors and formatting
4
+ */
5
+
6
+ const chalk = require('chalk');
7
+ const ora = require('ora');
8
+
9
+ /**
10
+ * Print success message
11
+ * @param {string} message - Success message
12
+ */
13
+ function success(message) {
14
+ console.log(chalk.green(`✅ ${message}`));
15
+ }
16
+
17
+ /**
18
+ * Print error message
19
+ * @param {string} message - Error message
20
+ */
21
+ function error(message) {
22
+ console.error(chalk.red(`❌ ${message}`));
23
+ }
24
+
25
+ /**
26
+ * Print warning message
27
+ * @param {string} message - Warning message
28
+ */
29
+ function warning(message) {
30
+ console.log(chalk.yellow(`⚠️ ${message}`));
31
+ }
32
+
33
+ /**
34
+ * Print info message
35
+ * @param {string} message - Info message
36
+ */
37
+ function info(message) {
38
+ console.log(chalk.cyan(`ℹ️ ${message}`));
39
+ }
40
+
41
+ /**
42
+ * Print section header
43
+ * @param {string} title - Section title
44
+ */
45
+ function section(title) {
46
+ console.log(chalk.bold.cyan(`\n${title}`));
47
+ console.log(chalk.gray('═'.repeat(title.length + 2)));
48
+ }
49
+
50
+ /**
51
+ * Create a spinner for long operations
52
+ * @param {string} text - Spinner text
53
+ * @returns {object} Ora spinner instance
54
+ */
55
+ function spinner(text) {
56
+ return ora({
57
+ text,
58
+ color: 'cyan',
59
+ spinner: 'dots'
60
+ });
61
+ }
62
+
63
+ /**
64
+ * Print mutation results summary
65
+ * @param {object} results - Mutation test results
66
+ */
67
+ function printMutationSummary(results) {
68
+ section('🧬 Mutation Testing Results');
69
+
70
+ console.log(`📊 ${chalk.bold('Total Mutations:')} ${results.totalMutations}`);
71
+ console.log(`${chalk.green('✅ Killed:')} ${results.killedMutations}`);
72
+ console.log(`${chalk.red('🔴 Survived:')} ${results.survivedMutations}`);
73
+ console.log(`${chalk.yellow('⏰ Timeout:')} ${results.timeoutMutations || 0}`);
74
+ console.log(`${chalk.gray('❌ Error:')} ${results.errorMutations || 0}`);
75
+ console.log(`${chalk.bold.cyan('🎯 Mutation Score:')} ${chalk.bold(results.mutationScore.toFixed(1))}%`);
76
+
77
+ if (results.mutationScore >= 80) {
78
+ console.log(chalk.green('\n✅ Excellent mutation score!'));
79
+ } else if (results.mutationScore >= 60) {
80
+ console.log(chalk.yellow('\n⚠️ Good mutation score, but room for improvement'));
81
+ } else {
82
+ console.log(chalk.red('\n❌ Low mutation score - consider improving test quality'));
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Print lineage data summary
88
+ * @param {object} data - Lineage data
89
+ */
90
+ function printLineageDataSummary(data) {
91
+ const testCount = data.tests.length;
92
+ const failedTests = data.tests.filter(t => t.failed).length;
93
+ const passedTests = testCount - failedTests;
94
+
95
+ console.log(chalk.cyan(`\n📊 Lineage data loaded:`));
96
+ console.log(` ${chalk.green('✓')} ${passedTests} tests passed`);
97
+ if (failedTests > 0) {
98
+ console.log(` ${chalk.red('✗')} ${failedTests} tests failed`);
99
+ }
100
+ console.log(` ${chalk.gray('📅')} Generated: ${new Date(data.timestamp).toLocaleString()}`);
101
+ }
102
+
103
+ /**
104
+ * Format file path for display
105
+ * @param {string} filePath - File path
106
+ * @returns {string} Formatted path
107
+ */
108
+ function formatPath(filePath) {
109
+ const cwd = process.cwd();
110
+ if (filePath.startsWith(cwd)) {
111
+ return chalk.gray(filePath.replace(cwd, '.'));
112
+ }
113
+ return chalk.gray(filePath);
114
+ }
115
+
116
+ module.exports = {
117
+ success,
118
+ error,
119
+ warning,
120
+ info,
121
+ section,
122
+ spinner,
123
+ printMutationSummary,
124
+ printLineageDataSummary,
125
+ formatPath
126
+ };
@@ -0,0 +1,66 @@
1
+ // Example file to demonstrate call depth tracking
2
+
3
+ export function directFunction(x: number): number {
4
+ return x * 2; // This will be depth 1 when called directly from tests
5
+ }
6
+ export function oneLevel(x: number): number {
7
+ return directFunction(x) + 1; // directFunction will be depth 2 here
8
+ }
9
+ export function twoLevels(x: number): number {
10
+ return oneLevel(x) + 1; // directFunction will be depth 3 here
11
+ }
12
+ export function threeLevels(x: number): number {
13
+ return twoLevels(x) + 1; // directFunction will be depth 4 here
14
+ }
15
+
16
+ // Complex function that calls multiple other functions
17
+ export function complexFunction(x: number): number {
18
+ const a = directFunction(x); // depth 2
19
+ const b = oneLevel(x); // directFunction will be depth 3 here
20
+ const c = twoLevels(x); // directFunction will be depth 4 here
21
+ return a + b + c;
22
+ }
23
+
24
+ // Recursive function to test deep call stacks
25
+ export function recursiveFunction(n: number, depth: number = 0): number {
26
+ if (depth >= 3) {
27
+ return directFunction(n); // This will be very deep
28
+ }
29
+ return recursiveFunction(n, depth + 1) + 1;
30
+ }
31
+
32
+ // Global array to hold leaked memory - this will cause actual memory leaks
33
+ const memoryLeakStorage: any[] = [];
34
+ export function memoryLeakFunction(size: number): number {
35
+ // Create large objects and store them globally (this leaks memory!)
36
+ const largeObject = {
37
+ id: Date.now(),
38
+ data: new Array(size).fill(0).map((_, i) => ({
39
+ index: i,
40
+ value: Math.random(),
41
+ timestamp: new Date(),
42
+ largeString: 'x'.repeat(1000),
43
+ // 1KB string per item
44
+ metadata: {
45
+ created: Date.now(),
46
+ processed: false,
47
+ tags: ['memory', 'leak', 'test', 'large'],
48
+ history: new Array(100).fill(0).map(() => Math.random())
49
+ }
50
+ }))
51
+ };
52
+
53
+ // Store in global array - this prevents garbage collection (memory leak!)
54
+ memoryLeakStorage.push(largeObject);
55
+
56
+ // Also call our tracked function
57
+ return directFunction(size);
58
+ }
59
+ export function clearMemoryLeaks(): number {
60
+ const count = memoryLeakStorage.length;
61
+ memoryLeakStorage.length = 0; // Clear the array
62
+ return count;
63
+ }
64
+ export function getMemoryLeakCount(): number {
65
+ return memoryLeakStorage.length;
66
+ }
@@ -0,0 +1,158 @@
1
+ // Examples demonstrating GC pressure and how to fix it
2
+
3
+ // BAD: Creates many small objects (causes GC pressure)
4
+ export function badGCPressureFunction(count: number): number {
5
+ let total = 0;
6
+
7
+ for (let i = 0; i < count; i++) {
8
+ // Creating new objects in a loop - BAD for GC
9
+ const tempObj = {
10
+ id: i,
11
+ value: Math.random(),
12
+ timestamp: Date.now(),
13
+ processed: false
14
+ };
15
+
16
+ // More object creation
17
+ const result = {
18
+ input: tempObj,
19
+ output: tempObj.value * 2,
20
+ metadata: {
21
+ created: new Date(),
22
+ index: i
23
+ }
24
+ };
25
+
26
+ total += result.output;
27
+ }
28
+
29
+ return total;
30
+ }
31
+
32
+ // GOOD: Reuses objects (reduces GC pressure)
33
+ export function goodGCPressureFunction(count: number): number {
34
+ let total = 0;
35
+
36
+ // Reuse objects instead of creating new ones
37
+ const reusableObj = {
38
+ id: 0,
39
+ value: 0,
40
+ timestamp: 0,
41
+ processed: false
42
+ };
43
+
44
+ const reusableResult = {
45
+ input: reusableObj,
46
+ output: 0,
47
+ metadata: {
48
+ created: new Date(),
49
+ index: 0
50
+ }
51
+ };
52
+
53
+ for (let i = 0; i < count; i++) {
54
+ // Reuse existing objects - GOOD for GC
55
+ reusableObj.id = i;
56
+ reusableObj.value = Math.random();
57
+ reusableObj.timestamp = Date.now();
58
+ reusableObj.processed = false;
59
+
60
+ reusableResult.output = reusableObj.value * 2;
61
+ reusableResult.metadata.index = i;
62
+
63
+ total += reusableResult.output;
64
+ }
65
+
66
+ return total;
67
+ }
68
+
69
+ // BAD: String concatenation in loop (creates many temporary strings)
70
+ export function badStringConcatenation(items: string[]): string {
71
+ let result = '';
72
+
73
+ for (const item of items) {
74
+ // Each += creates a new string object - BAD for GC
75
+ result += item + ', ';
76
+ }
77
+
78
+ return result;
79
+ }
80
+
81
+ // GOOD: Use array join (single allocation)
82
+ export function goodStringConcatenation(items: string[]): string {
83
+ // Single allocation - GOOD for GC
84
+ return items.join(', ');
85
+ }
86
+
87
+ // BAD: Array operations that create many intermediate arrays
88
+ export function badArrayOperations(numbers: number[]): number[] {
89
+ return numbers
90
+ .map(x => x * 2) // Creates new array
91
+ .filter(x => x > 10) // Creates another new array
92
+ .map(x => x + 1) // Creates another new array
93
+ .filter(x => x % 2 === 0); // Creates final array
94
+ }
95
+
96
+ // GOOD: Single pass with reduce (one allocation)
97
+ export function goodArrayOperations(numbers: number[]): number[] {
98
+ return numbers.reduce((acc: number[], x: number) => {
99
+ const doubled = x * 2;
100
+ if (doubled > 10) {
101
+ const incremented = doubled + 1;
102
+ if (incremented % 2 === 0) {
103
+ acc.push(incremented);
104
+ }
105
+ }
106
+ return acc;
107
+ }, []);
108
+ }
109
+
110
+ // Object pool pattern for heavy reuse
111
+ class ObjectPool<T> {
112
+ private pool: T[] = [];
113
+ private createFn: () => T;
114
+ private resetFn: (obj: T) => void;
115
+
116
+ constructor(createFn: () => T, resetFn: (obj: T) => void, initialSize: number = 10) {
117
+ this.createFn = createFn;
118
+ this.resetFn = resetFn;
119
+
120
+ // Pre-allocate objects
121
+ for (let i = 0; i < initialSize; i++) {
122
+ this.pool.push(this.createFn());
123
+ }
124
+ }
125
+
126
+ acquire(): T {
127
+ return this.pool.pop() || this.createFn();
128
+ }
129
+
130
+ release(obj: T): void {
131
+ this.resetFn(obj);
132
+ this.pool.push(obj);
133
+ }
134
+ }
135
+
136
+ // Example using object pool
137
+ export function objectPoolExample(count: number): number {
138
+ const pool = new ObjectPool(
139
+ () => ({ id: 0, value: 0, result: 0 }),
140
+ (obj) => { obj.id = 0; obj.value = 0; obj.result = 0; },
141
+ 50
142
+ );
143
+
144
+ let total = 0;
145
+
146
+ for (let i = 0; i < count; i++) {
147
+ const obj = pool.acquire();
148
+ obj.id = i;
149
+ obj.value = Math.random();
150
+ obj.result = obj.value * 2;
151
+
152
+ total += obj.result;
153
+
154
+ pool.release(obj); // Return to pool for reuse
155
+ }
156
+
157
+ return total;
158
+ }
@@ -0,0 +1,7 @@
1
+ declare global {
2
+ var __TRACK_LINE_EXECUTION__: ((filePath: string, lineNumber: number) => void) | undefined;
3
+ var __GET_LINEAGE_RESULTS__: (() => any) | undefined;
4
+ var __TEST_LINEAGE_TRACKER__: any;
5
+ }
6
+
7
+ export {};