@mutineerjs/mutineer 0.2.4 → 0.4.0

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.
Files changed (135) hide show
  1. package/README.md +48 -42
  2. package/dist/bin/mutineer.js +0 -0
  3. package/dist/mutators/__tests__/operator.spec.js +169 -0
  4. package/dist/mutators/__tests__/registry.spec.js +6 -0
  5. package/dist/mutators/__tests__/return-value.spec.js +239 -0
  6. package/dist/mutators/__tests__/utils.spec.js +68 -1
  7. package/dist/mutators/operator.d.ts +25 -0
  8. package/dist/mutators/operator.js +50 -0
  9. package/dist/mutators/registry.d.ts +6 -28
  10. package/dist/mutators/registry.js +14 -66
  11. package/dist/mutators/return-value.d.ts +39 -0
  12. package/dist/mutators/return-value.js +104 -0
  13. package/dist/mutators/utils.d.ts +21 -0
  14. package/dist/mutators/utils.js +44 -27
  15. package/dist/runner/__tests__/pool-executor.spec.js +95 -4
  16. package/dist/runner/__tests__/variants.spec.js +3 -1
  17. package/dist/runner/discover.js +2 -1
  18. package/dist/runner/jest/__tests__/adapter.spec.js +1 -1
  19. package/dist/runner/jest/__tests__/pool.spec.js +5 -6
  20. package/dist/runner/jest/__tests__/worker-runtime.spec.js +2 -2
  21. package/dist/runner/jest/adapter.js +1 -1
  22. package/dist/runner/jest/pool.d.ts +1 -1
  23. package/dist/runner/jest/pool.js +6 -6
  24. package/dist/runner/jest/worker.mjs +1 -1
  25. package/dist/runner/pool-executor.js +21 -1
  26. package/dist/runner/shared/__tests__/redirect-state.spec.js +3 -3
  27. package/dist/runner/shared/index.d.ts +1 -1
  28. package/dist/runner/shared/index.js +1 -1
  29. package/dist/runner/shared/redirect-state.d.ts +2 -2
  30. package/dist/runner/shared/redirect-state.js +4 -4
  31. package/dist/runner/types.d.ts +1 -1
  32. package/dist/runner/vitest/__tests__/adapter.spec.js +1 -1
  33. package/dist/runner/vitest/__tests__/pool.spec.js +1 -1
  34. package/dist/runner/vitest/__tests__/redirect-loader.spec.js +83 -1
  35. package/dist/runner/vitest/__tests__/worker-runtime.spec.js +84 -0
  36. package/dist/runner/vitest/adapter.js +1 -1
  37. package/dist/runner/vitest/index.d.ts +0 -1
  38. package/dist/runner/vitest/index.js +0 -1
  39. package/dist/runner/vitest/pool.d.ts +1 -1
  40. package/dist/runner/vitest/pool.js +7 -7
  41. package/dist/runner/vitest/redirect-loader.d.ts +1 -1
  42. package/dist/runner/vitest/redirect-loader.js +1 -1
  43. package/dist/runner/vitest/worker-runtime.js +3 -3
  44. package/dist/runner/vitest/worker.mjs +1 -1
  45. package/dist/types/mutant.d.ts +2 -0
  46. package/dist/utils/__tests__/coverage.spec.js +167 -0
  47. package/dist/utils/__tests__/progress.spec.js +96 -0
  48. package/dist/utils/__tests__/summary.spec.js +28 -0
  49. package/dist/utils/summary.js +7 -1
  50. package/package.json +71 -22
  51. package/dist/admin/assets/index-B7nXq-e7.js +0 -32
  52. package/dist/admin/assets/index-B7nXq-e7.js.map +0 -1
  53. package/dist/admin/assets/index-BDQLkBUE.js +0 -32
  54. package/dist/admin/assets/index-BDQLkBUE.js.map +0 -1
  55. package/dist/admin/assets/index-DVkP-Tc7.css +0 -1
  56. package/dist/admin/index.html +0 -13
  57. package/dist/admin/server/admin.d.ts +0 -6
  58. package/dist/admin/server/admin.js +0 -234
  59. package/dist/bin/mutate-vitest.d.ts +0 -2
  60. package/dist/bin/mutate-vitest.js +0 -90
  61. package/dist/plugin/viteMutate.d.ts +0 -15
  62. package/dist/plugin/viteMutate.js +0 -52
  63. package/dist/plugin/vitest.setup.d.ts +0 -47
  64. package/dist/plugin/vitest.setup.js +0 -118
  65. package/dist/plugin/withVitest.d.ts +0 -13
  66. package/dist/plugin/withVitest.js +0 -30
  67. package/dist/runner/__tests__/orchestrator.spec.js +0 -55
  68. package/dist/runner/adapters/__tests__/jest.spec.js +0 -88
  69. package/dist/runner/adapters/__tests__/vitest-worker-runtime.spec.d.ts +0 -1
  70. package/dist/runner/adapters/__tests__/vitest-worker-runtime.spec.js +0 -59
  71. package/dist/runner/adapters/__tests__/vitest.spec.d.ts +0 -1
  72. package/dist/runner/adapters/__tests__/vitest.spec.js +0 -118
  73. package/dist/runner/adapters/index.d.ts +0 -10
  74. package/dist/runner/adapters/index.js +0 -9
  75. package/dist/runner/adapters/jest/__tests__/index.spec.d.ts +0 -1
  76. package/dist/runner/adapters/jest/__tests__/index.spec.js +0 -88
  77. package/dist/runner/adapters/jest/index.d.ts +0 -24
  78. package/dist/runner/adapters/jest/index.js +0 -216
  79. package/dist/runner/adapters/jest/worker-runtime.d.ts +0 -37
  80. package/dist/runner/adapters/jest/worker-runtime.js +0 -171
  81. package/dist/runner/adapters/jest-worker-runtime.d.ts +0 -37
  82. package/dist/runner/adapters/jest-worker-runtime.js +0 -171
  83. package/dist/runner/adapters/jest.d.ts +0 -24
  84. package/dist/runner/adapters/jest.js +0 -216
  85. package/dist/runner/adapters/types.d.ts +0 -89
  86. package/dist/runner/adapters/types.js +0 -8
  87. package/dist/runner/adapters/vitest/__tests__/index.spec.d.ts +0 -1
  88. package/dist/runner/adapters/vitest/__tests__/index.spec.js +0 -118
  89. package/dist/runner/adapters/vitest/__tests__/worker-runtime.spec.d.ts +0 -1
  90. package/dist/runner/adapters/vitest/__tests__/worker-runtime.spec.js +0 -59
  91. package/dist/runner/adapters/vitest/index.d.ts +0 -33
  92. package/dist/runner/adapters/vitest/index.js +0 -267
  93. package/dist/runner/adapters/vitest/worker-runtime.d.ts +0 -25
  94. package/dist/runner/adapters/vitest/worker-runtime.js +0 -118
  95. package/dist/runner/adapters/vitest-worker-runtime.d.ts +0 -25
  96. package/dist/runner/adapters/vitest-worker-runtime.js +0 -118
  97. package/dist/runner/adapters/vitest.d.ts +0 -33
  98. package/dist/runner/adapters/vitest.js +0 -267
  99. package/dist/runner/pool/__tests__/index.spec.d.ts +0 -1
  100. package/dist/runner/pool/__tests__/index.spec.js +0 -83
  101. package/dist/runner/pool/__tests__/pool-plugin.spec.d.ts +0 -1
  102. package/dist/runner/pool/__tests__/pool-plugin.spec.js +0 -59
  103. package/dist/runner/pool/__tests__/pool-redirect-loader.spec.d.ts +0 -1
  104. package/dist/runner/pool/__tests__/pool-redirect-loader.spec.js +0 -78
  105. package/dist/runner/pool/index.d.ts +0 -8
  106. package/dist/runner/pool/index.js +0 -9
  107. package/dist/runner/pool/jest/pool.d.ts +0 -52
  108. package/dist/runner/pool/jest/pool.js +0 -309
  109. package/dist/runner/pool/jest/worker.d.mts +0 -1
  110. package/dist/runner/pool/jest/worker.mjs +0 -60
  111. package/dist/runner/pool/jest-pool.d.ts +0 -52
  112. package/dist/runner/pool/jest-pool.js +0 -309
  113. package/dist/runner/pool/jest-worker.d.mts +0 -1
  114. package/dist/runner/pool/jest-worker.mjs +0 -60
  115. package/dist/runner/pool/plugin.d.ts +0 -18
  116. package/dist/runner/pool/plugin.js +0 -60
  117. package/dist/runner/pool/pool-plugin.d.ts +0 -18
  118. package/dist/runner/pool/pool-plugin.js +0 -60
  119. package/dist/runner/pool/pool-redirect-loader.d.ts +0 -19
  120. package/dist/runner/pool/pool-redirect-loader.js +0 -116
  121. package/dist/runner/pool/pool-redirect-loader.mjs +0 -146
  122. package/dist/runner/pool/redirect-loader.d.ts +0 -19
  123. package/dist/runner/pool/redirect-loader.js +0 -116
  124. package/dist/runner/pool/vitest/pool.d.ts +0 -70
  125. package/dist/runner/pool/vitest/pool.js +0 -376
  126. package/dist/runner/pool/vitest/worker.d.mts +0 -15
  127. package/dist/runner/pool/vitest/worker.mjs +0 -96
  128. package/dist/runner/pool/vitest-worker.d.mts +0 -15
  129. package/dist/runner/pool/vitest-worker.mjs +0 -96
  130. package/dist/runner/shared-module-redirect.d.ts +0 -56
  131. package/dist/runner/shared-module-redirect.js +0 -84
  132. package/dist/types/api.d.ts +0 -20
  133. package/dist/types/api.js +0 -1
  134. /package/dist/{runner/__tests__/orchestrator.spec.d.ts → mutators/__tests__/operator.spec.d.ts} +0 -0
  135. /package/dist/{runner/adapters/__tests__/jest.spec.d.ts → mutators/__tests__/return-value.spec.d.ts} +0 -0
@@ -1,267 +0,0 @@
1
- /**
2
- * Vitest Test Runner Adapter
3
- *
4
- * Implements the TestRunnerAdapter interface for Vitest.
5
- * Handles baseline test runs, mutant execution via worker pool,
6
- * and coverage configuration detection.
7
- */
8
- import fs from 'node:fs/promises';
9
- import path from 'node:path';
10
- import { spawn } from 'node:child_process';
11
- import { createRequire } from 'node:module';
12
- import { VitestPool } from '../pool/index.js';
13
- const require = createRequire(import.meta.url);
14
- /**
15
- * Resolve the Vitest CLI entry point.
16
- */
17
- function resolveVitestPath() {
18
- try {
19
- return require.resolve('vitest/vitest.mjs');
20
- }
21
- catch {
22
- const pkgJson = require.resolve('vitest/package.json');
23
- return path.join(path.dirname(pkgJson), 'vitest.mjs');
24
- }
25
- }
26
- /**
27
- * Strip mutineer-specific CLI args that shouldn't be passed to Vitest.
28
- */
29
- function stripMutineerArgs(args) {
30
- const out = [];
31
- const consumeNext = new Set([
32
- '--concurrency',
33
- '--progress',
34
- '--min-kill-percent',
35
- '--config',
36
- '-c',
37
- '--coverage-file',
38
- ]);
39
- const dropExact = new Set([
40
- '-m',
41
- '--mutate',
42
- '--changed',
43
- '--changed-with-deps',
44
- '--only-covered-lines',
45
- '--per-test-coverage',
46
- '--perTestCoverage',
47
- ]);
48
- for (let i = 0; i < args.length; i++) {
49
- const a = args[i];
50
- if (dropExact.has(a))
51
- continue;
52
- if (consumeNext.has(a)) {
53
- i++;
54
- continue;
55
- }
56
- if (a.startsWith('--min-kill-percent='))
57
- continue;
58
- if (a.startsWith('--config=') || a.startsWith('-c='))
59
- continue;
60
- out.push(a);
61
- }
62
- return out;
63
- }
64
- /**
65
- * Ensure the Vitest config arg is included if specified.
66
- */
67
- function ensureConfigArg(args, vitestConfig, cwd) {
68
- if (!vitestConfig)
69
- return args;
70
- if (args.some((a) => a === '--config' || a === '-c' || a.startsWith('--config=') || a.startsWith('-c='))) {
71
- return args;
72
- }
73
- const resolved = cwd ? path.resolve(cwd, vitestConfig) : vitestConfig;
74
- return [...args, '--config', resolved];
75
- }
76
- /**
77
- * Build Vitest CLI arguments for the given mode.
78
- */
79
- function buildVitestArgs(args, mode) {
80
- const result = [...args];
81
- if (!result.includes('run') && !result.includes('--run'))
82
- result.unshift('run');
83
- if (!result.some((a) => a.startsWith('--watch')))
84
- result.push('--watch=false');
85
- if (!result.some((a) => a.startsWith('--passWithNoTests')))
86
- result.push('--passWithNoTests');
87
- if (mode === 'baseline-with-coverage') {
88
- if (!result.some((a) => a.startsWith('--coverage'))) {
89
- result.push('--coverage.enabled=true', '--coverage.reporter=json');
90
- }
91
- if (!result.some((a) => a.startsWith('--coverage.perTest='))) {
92
- result.push('--coverage.perTest=true');
93
- }
94
- }
95
- return result;
96
- }
97
- /**
98
- * Vitest adapter implementation.
99
- */
100
- export class VitestAdapter {
101
- constructor(options) {
102
- this.name = 'vitest';
103
- this.pool = null;
104
- this.baseArgs = [];
105
- this.options = options;
106
- this.vitestPath = resolveVitestPath();
107
- // Prepare base args by stripping mutineer-specific flags
108
- const stripped = stripMutineerArgs(options.cliArgs);
109
- this.baseArgs = ensureConfigArg(stripped, options.config.vitestConfig, options.cwd);
110
- }
111
- async init(concurrencyOverride) {
112
- const workerCount = Math.max(1, concurrencyOverride ?? this.options.concurrency);
113
- this.pool = new VitestPool({
114
- cwd: this.options.cwd,
115
- concurrency: workerCount,
116
- vitestConfig: this.options.config.vitestConfig,
117
- timeoutMs: this.options.timeoutMs,
118
- debug: this.options.debug,
119
- });
120
- await this.pool.init();
121
- }
122
- async runBaseline(tests, options) {
123
- const mode = options.collectCoverage ? 'baseline-with-coverage' : 'baseline';
124
- const args = buildVitestArgs(this.baseArgs, mode);
125
- return new Promise((resolve) => {
126
- const env = { ...process.env };
127
- env.VITEST_WATCH = 'false';
128
- if (!env.CI)
129
- env.CI = '1';
130
- const child = spawn(process.execPath, [this.vitestPath, ...args, ...tests], {
131
- cwd: this.options.cwd,
132
- stdio: ['ignore', 'inherit', 'inherit'],
133
- env,
134
- });
135
- child.on('error', (err) => {
136
- if (this.options.debug)
137
- console.error('Failed to spawn vitest process', err);
138
- resolve(false);
139
- });
140
- child.on('exit', (code) => {
141
- resolve(code === 0);
142
- });
143
- });
144
- }
145
- async runMutant(mutant, tests) {
146
- if (!this.pool) {
147
- throw new Error('VitestAdapter not initialized. Call init() first.');
148
- }
149
- try {
150
- const result = await this.pool.run(mutant, [...tests]);
151
- if (result.error === 'timeout') {
152
- return {
153
- status: 'timeout',
154
- durationMs: result.durationMs,
155
- error: result.error,
156
- };
157
- }
158
- if (result.error) {
159
- return {
160
- status: 'error',
161
- durationMs: result.durationMs,
162
- error: result.error,
163
- };
164
- }
165
- return {
166
- status: result.killed ? 'killed' : 'escaped',
167
- durationMs: result.durationMs,
168
- error: result.error,
169
- };
170
- }
171
- catch (err) {
172
- return {
173
- status: 'error',
174
- durationMs: 0,
175
- error: err instanceof Error ? err.message : String(err),
176
- };
177
- }
178
- }
179
- async shutdown() {
180
- if (this.pool) {
181
- await this.pool.shutdown();
182
- this.pool = null;
183
- }
184
- }
185
- hasCoverageProvider() {
186
- try {
187
- require.resolve('@vitest/coverage-v8/package.json', { paths: [this.options.cwd] });
188
- return true;
189
- }
190
- catch {
191
- return false;
192
- }
193
- }
194
- async detectCoverageConfig() {
195
- const configPath = this.options.config.vitestConfig;
196
- if (!configPath) {
197
- return { perTestEnabled: false, coverageEnabled: false };
198
- }
199
- try {
200
- const abs = path.isAbsolute(configPath) ? configPath : path.join(this.options.cwd, configPath);
201
- const content = await fs.readFile(abs, 'utf8');
202
- const perTestEnabled = /perTest\s*:\s*true/.test(content);
203
- let coverageEnabled = false;
204
- if (!/coverage\s*\.\s*enabled\s*:\s*false/.test(content) && !/coverage\s*:\s*false/.test(content)) {
205
- coverageEnabled = /coverage\s*:/.test(content);
206
- }
207
- return { perTestEnabled, coverageEnabled };
208
- }
209
- catch {
210
- return { perTestEnabled: false, coverageEnabled: false };
211
- }
212
- }
213
- }
214
- /**
215
- * Check if coverage is requested via CLI args.
216
- */
217
- export function isCoverageRequestedInArgs(args) {
218
- let requested = false;
219
- let disabled = false;
220
- const isFalsey = (v) => typeof v === 'string' && /^(false|0|off)$/i.test(v);
221
- for (let i = 0; i < args.length; i++) {
222
- const arg = args[i];
223
- if (arg === '--no-coverage') {
224
- disabled = true;
225
- continue;
226
- }
227
- if (arg === '--coverage') {
228
- requested = true;
229
- continue;
230
- }
231
- if (arg === '--coverage.enabled') {
232
- const next = args[i + 1];
233
- if (isFalsey(next))
234
- disabled = true;
235
- else
236
- requested = true;
237
- continue;
238
- }
239
- if (arg.startsWith('--coverage.enabled=')) {
240
- const val = arg.slice('--coverage.enabled='.length);
241
- if (isFalsey(val))
242
- disabled = true;
243
- else
244
- requested = true;
245
- continue;
246
- }
247
- if (arg.startsWith('--coverage=')) {
248
- const val = arg.slice('--coverage='.length);
249
- if (isFalsey(val))
250
- disabled = true;
251
- else
252
- requested = true;
253
- continue;
254
- }
255
- if (arg.startsWith('--coverage.')) {
256
- requested = true;
257
- continue;
258
- }
259
- }
260
- return requested && !disabled;
261
- }
262
- /**
263
- * Factory function for creating VitestAdapter instances.
264
- */
265
- export function createVitestAdapter(options) {
266
- return new VitestAdapter(options);
267
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,83 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
- import { EventEmitter } from 'node:events';
3
- import * as childProcess from 'node:child_process';
4
- import * as readline from 'node:readline';
5
- import { VitestPool, runWithPool } from '../index.js';
6
- vi.mock('node:child_process', () => ({ spawn: vi.fn() }));
7
- vi.mock('node:readline', () => ({ createInterface: vi.fn() }));
8
- describe('VitestPool', () => {
9
- const mockProcesses = [];
10
- const rlEmitters = [];
11
- const fakeWorkers = [];
12
- beforeEach(() => {
13
- mockProcesses.length = 0;
14
- rlEmitters.length = 0;
15
- vi.mocked(childProcess.spawn).mockImplementation(() => {
16
- const proc = new EventEmitter();
17
- proc.stdout = new EventEmitter();
18
- proc.stderr = new EventEmitter();
19
- proc.stdin = {
20
- writes: [],
21
- write: (chunk) => {
22
- proc.stdin.writes.push(chunk);
23
- }
24
- };
25
- proc.kill = vi.fn();
26
- mockProcesses.push(proc);
27
- return proc;
28
- });
29
- vi.mocked(readline.createInterface).mockImplementation(() => {
30
- const rl = new EventEmitter();
31
- rlEmitters.push(rl);
32
- return rl;
33
- });
34
- });
35
- afterEach(() => {
36
- vi.restoreAllMocks();
37
- });
38
- it('runs a mutant through a worker and returns result', async () => {
39
- const pool = new VitestPool({
40
- cwd: process.cwd(),
41
- concurrency: 1,
42
- timeoutMs: 5000,
43
- createWorker: (id, opts) => {
44
- const worker = new EventEmitter();
45
- worker.id = id;
46
- worker.start = vi.fn().mockResolvedValue(undefined);
47
- worker.isReady = vi.fn().mockReturnValue(true);
48
- worker.isBusy = vi.fn().mockReturnValue(false);
49
- worker.run = vi.fn().mockImplementation(async () => ({ killed: true, durationMs: 42 }));
50
- worker.shutdown = vi.fn().mockResolvedValue(undefined);
51
- worker.kill = vi.fn();
52
- fakeWorkers.push(worker);
53
- return worker;
54
- }
55
- });
56
- await pool.init();
57
- const mutant = { id: '1', name: 'mutant', file: 'foo.ts', code: 'x', line: 1, col: 1 };
58
- const tests = ['foo.spec.ts'];
59
- const runPromise = pool.run(mutant, tests);
60
- const result = await runPromise;
61
- expect(result).toEqual({ killed: true, durationMs: 42, error: undefined });
62
- expect(fakeWorkers[0].run).toHaveBeenCalledWith(mutant, tests, 5000);
63
- await pool.shutdown();
64
- });
65
- it('maps runWithPool results to escaped when not killed', async () => {
66
- const mockPool = {
67
- run: vi.fn().mockResolvedValue({ killed: false, durationMs: 7 })
68
- };
69
- const mutant = { id: '2', name: 'm', file: 'bar.ts', code: 'y', line: 2, col: 3 };
70
- const tests = ['bar.spec.ts'];
71
- const result = await runWithPool(mockPool, mutant, tests);
72
- expect(result).toEqual({ status: 'escaped', durationMs: 7 });
73
- expect(mockPool.run).toHaveBeenCalledWith(mutant, ['bar.spec.ts']);
74
- });
75
- it('maps runWithPool errors to error status', async () => {
76
- const mockPool = {
77
- run: vi.fn().mockRejectedValue(new Error('boom'))
78
- };
79
- const mutant = { id: '3', name: 'err', file: 'baz.ts', code: 'z', line: 3, col: 4 };
80
- const result = await runWithPool(mockPool, mutant, []);
81
- expect(result.status).toBe('error');
82
- });
83
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,59 +0,0 @@
1
- import { describe, it, expect, afterEach } from 'vitest';
2
- import fs from 'node:fs/promises';
3
- import path from 'node:path';
4
- import os from 'node:os';
5
- import { poolMutineerPlugin } from '../plugin.js';
6
- function getLoadFn() {
7
- const plugin = poolMutineerPlugin();
8
- if (Array.isArray(plugin)) {
9
- return plugin[0]?.load;
10
- }
11
- if (plugin && typeof plugin === 'object' && 'load' in plugin) {
12
- return plugin.load;
13
- }
14
- return undefined;
15
- }
16
- describe('poolMutineerPlugin', () => {
17
- afterEach(() => {
18
- ;
19
- globalThis.__mutineer_redirect__ = undefined;
20
- });
21
- it('returns null when no redirect is configured', () => {
22
- const load = getLoadFn();
23
- const result = load?.('/some/file.ts');
24
- expect(result).toBeNull();
25
- });
26
- it('returns the mutated file when the id matches the redirect target', async () => {
27
- const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'mutineer-pool-plugin-'));
28
- const fromPath = path.join(tmpDir, 'source.ts');
29
- const mutatedPath = path.join(tmpDir, 'mutated.ts');
30
- await fs.writeFile(fromPath, 'export const original = true\n', 'utf8');
31
- await fs.writeFile(mutatedPath, 'export const mutated = true\n', 'utf8');
32
- try {
33
- ;
34
- globalThis.__mutineer_redirect__ = { from: fromPath, to: mutatedPath };
35
- const load = getLoadFn();
36
- const result = load?.(fromPath);
37
- expect(result).toBe('export const mutated = true\n');
38
- }
39
- finally {
40
- await fs.rm(tmpDir, { recursive: true, force: true });
41
- }
42
- });
43
- it('ignores modules that do not match the redirect', async () => {
44
- const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'mutineer-pool-plugin-'));
45
- const fromPath = path.join(tmpDir, 'source.ts');
46
- const mutatedPath = path.join(tmpDir, 'mutated.ts');
47
- await fs.writeFile(mutatedPath, 'export const mutated = true\n', 'utf8');
48
- try {
49
- ;
50
- globalThis.__mutineer_redirect__ = { from: fromPath, to: mutatedPath };
51
- const load = getLoadFn();
52
- const result = load?.(path.join(tmpDir, 'other.ts?import'));
53
- expect(result).toBeNull();
54
- }
55
- finally {
56
- await fs.rm(tmpDir, { recursive: true, force: true });
57
- }
58
- });
59
- });
@@ -1,78 +0,0 @@
1
- import { describe, it, expect, vi, afterEach } from 'vitest';
2
- import fs from 'node:fs/promises';
3
- import path from 'node:path';
4
- import os from 'node:os';
5
- import { pathToFileURL } from 'node:url';
6
- import { resolve as poolResolve } from '../redirect-loader.js';
7
- describe('pool-redirect-loader resolve', () => {
8
- afterEach(() => {
9
- ;
10
- globalThis.__mutineer_redirect__ = undefined;
11
- vi.restoreAllMocks();
12
- });
13
- it('resolves .js to .ts in the same directory', async () => {
14
- const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'mutineer-pool-loader-'));
15
- const parentFile = path.join(tmpDir, 'src', 'index.ts');
16
- const tsFile = path.join(tmpDir, 'src', 'foo.ts');
17
- await fs.mkdir(path.dirname(parentFile), { recursive: true });
18
- await fs.writeFile(parentFile, 'export {}', 'utf8');
19
- await fs.writeFile(tsFile, 'export const foo = 1', 'utf8');
20
- try {
21
- const nextResolve = vi.fn();
22
- const result = await poolResolve('./foo.js', { parentURL: pathToFileURL(parentFile).href }, nextResolve);
23
- expect(nextResolve).not.toHaveBeenCalled();
24
- expect(result.shortCircuit).toBe(true);
25
- expect(result.url).toBe(pathToFileURL(tsFile).href);
26
- }
27
- finally {
28
- await fs.rm(tmpDir, { recursive: true, force: true });
29
- }
30
- });
31
- it('redirects to mutated file when target matches', async () => {
32
- const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'mutineer-pool-loader-'));
33
- const parentFile = path.join(tmpDir, 'src', 'index.ts');
34
- const fromPath = path.join(tmpDir, 'src', 'target.ts');
35
- const mutatedPath = path.join(tmpDir, 'mutated.ts');
36
- await fs.mkdir(path.dirname(parentFile), { recursive: true });
37
- await fs.writeFile(parentFile, 'export {}', 'utf8');
38
- await fs.writeFile(fromPath, 'export const target = true', 'utf8');
39
- await fs.writeFile(mutatedPath, 'export const mutated = true', 'utf8');
40
- try {
41
- ;
42
- globalThis.__mutineer_redirect__ = { from: fromPath, to: mutatedPath };
43
- const nextResolve = vi.fn();
44
- const result = await poolResolve('./target.js', { parentURL: pathToFileURL(parentFile).href }, nextResolve);
45
- expect(nextResolve).not.toHaveBeenCalled();
46
- expect(result.shortCircuit).toBe(true);
47
- expect(result.url).toBe(pathToFileURL(mutatedPath).href);
48
- }
49
- finally {
50
- await fs.rm(tmpDir, { recursive: true, force: true });
51
- }
52
- });
53
- it('redirects after delegated resolution', async () => {
54
- const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'mutineer-pool-loader-'));
55
- const parentFile = path.join(tmpDir, 'src', 'index.ts');
56
- const fromPath = path.join(tmpDir, 'src', 'delegated.ts');
57
- const mutatedPath = path.join(tmpDir, 'mutated.ts');
58
- await fs.mkdir(path.dirname(parentFile), { recursive: true });
59
- await fs.writeFile(parentFile, 'export {}', 'utf8');
60
- await fs.writeFile(fromPath, 'export const delegated = true', 'utf8');
61
- await fs.writeFile(mutatedPath, 'export const mutated = true', 'utf8');
62
- try {
63
- ;
64
- globalThis.__mutineer_redirect__ = { from: fromPath, to: mutatedPath };
65
- const nextResolve = vi.fn().mockResolvedValue({
66
- url: pathToFileURL(fromPath).href,
67
- shortCircuit: false
68
- });
69
- const result = await poolResolve('./delegated', { parentURL: pathToFileURL(parentFile).href }, nextResolve);
70
- expect(nextResolve).toHaveBeenCalledOnce();
71
- expect(result.shortCircuit).toBe(true);
72
- expect(result.url).toBe(pathToFileURL(mutatedPath).href);
73
- }
74
- finally {
75
- await fs.rm(tmpDir, { recursive: true, force: true });
76
- }
77
- });
78
- });
@@ -1,8 +0,0 @@
1
- /**
2
- * Test Runner Worker Pools
3
- *
4
- * Exports worker pool implementations for different test runners.
5
- */
6
- export { VitestPool, runWithPool, type VitestPoolOptions } from './vitest/pool.js';
7
- export { JestPool, runWithJestPool, type JestPoolOptions } from './jest/pool.js';
8
- export type { MutantPayload, MutantRunResult, MutantRunSummary } from '../../types/mutant.js';
@@ -1,9 +0,0 @@
1
- /**
2
- * Test Runner Worker Pools
3
- *
4
- * Exports worker pool implementations for different test runners.
5
- */
6
- // Vitest pool
7
- export { VitestPool, runWithPool } from './vitest/pool.js';
8
- // Jest pool
9
- export { JestPool, runWithJestPool } from './jest/pool.js';
@@ -1,52 +0,0 @@
1
- import { EventEmitter } from 'node:events';
2
- import type { MutantPayload, MutantRunResult, MutantRunSummary } from '../../../types/mutant.js';
3
- declare class JestWorker extends EventEmitter {
4
- private readonly cwd;
5
- private readonly jestConfig?;
6
- private readonly debug;
7
- readonly id: string;
8
- private process;
9
- private pendingTask;
10
- private ready;
11
- private shuttingDown;
12
- constructor(id: string, cwd: string, jestConfig?: string | undefined, debug?: boolean);
13
- start(): Promise<void>;
14
- private handleMessage;
15
- private handleExit;
16
- isReady(): boolean;
17
- isBusy(): boolean;
18
- run(mutant: MutantPayload, tests: string[], timeoutMs?: number): Promise<MutantRunSummary>;
19
- shutdown(): Promise<void>;
20
- kill(): void;
21
- private log;
22
- }
23
- export interface JestPoolOptions {
24
- cwd: string;
25
- concurrency: number;
26
- jestConfig?: string;
27
- timeoutMs?: number;
28
- debug?: boolean;
29
- createWorker?: (id: string, opts: {
30
- cwd: string;
31
- jestConfig?: string;
32
- debug: boolean;
33
- }) => JestWorker;
34
- }
35
- export declare class JestPool {
36
- private workers;
37
- private availableWorkers;
38
- private waitingTasks;
39
- private readonly options;
40
- private initialized;
41
- private shuttingDown;
42
- constructor(options: JestPoolOptions);
43
- init(): Promise<void>;
44
- private handleWorkerExit;
45
- private acquireWorker;
46
- private releaseWorker;
47
- run(mutant: MutantPayload, tests: string[]): Promise<MutantRunSummary>;
48
- shutdown(): Promise<void>;
49
- private log;
50
- }
51
- export declare function runWithJestPool(pool: JestPool, mutant: MutantPayload, tests: readonly string[]): Promise<MutantRunResult>;
52
- export type { MutantPayload, MutantRunResult, MutantRunSummary } from '../../../types/mutant.js';