@mutineerjs/mutineer 0.2.3 → 0.3.2
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 +48 -42
- package/dist/bin/mutineer.js +0 -0
- package/dist/core/__tests__/module.spec.js +66 -3
- package/dist/core/__tests__/sfc.spec.js +76 -0
- package/dist/core/__tests__/variant-utils.spec.js +93 -0
- package/dist/mutators/__tests__/operator.spec.js +169 -0
- package/dist/mutators/__tests__/registry.spec.js +6 -0
- package/dist/mutators/__tests__/return-value.spec.js +239 -0
- package/dist/mutators/__tests__/utils.spec.js +68 -1
- package/dist/mutators/operator.d.ts +25 -0
- package/dist/mutators/operator.js +50 -0
- package/dist/mutators/registry.d.ts +6 -28
- package/dist/mutators/registry.js +14 -66
- package/dist/mutators/return-value.d.ts +39 -0
- package/dist/mutators/return-value.js +104 -0
- package/dist/mutators/utils.d.ts +21 -0
- package/dist/mutators/utils.js +44 -27
- package/dist/runner/__tests__/args.spec.js +225 -0
- package/dist/runner/__tests__/cache.spec.js +180 -0
- package/dist/runner/__tests__/changed.spec.js +227 -0
- package/dist/runner/__tests__/cleanup.spec.js +41 -0
- package/dist/runner/__tests__/config.spec.js +71 -0
- package/dist/runner/__tests__/coverage-resolver.spec.js +171 -0
- package/dist/runner/__tests__/pool-executor.spec.js +211 -0
- package/dist/runner/__tests__/tasks.spec.js +95 -0
- package/dist/runner/__tests__/variants.spec.js +261 -0
- package/dist/runner/args.d.ts +5 -0
- package/dist/runner/args.js +7 -0
- package/dist/runner/config.js +2 -2
- package/dist/runner/coverage-resolver.d.ts +21 -0
- package/dist/runner/coverage-resolver.js +96 -0
- package/dist/runner/discover.js +2 -1
- package/dist/runner/jest/__tests__/adapter.spec.js +1 -1
- package/dist/runner/jest/__tests__/pool.spec.d.ts +1 -0
- package/dist/runner/jest/__tests__/pool.spec.js +211 -0
- package/dist/runner/jest/__tests__/worker-runtime.spec.d.ts +1 -0
- package/dist/runner/jest/__tests__/worker-runtime.spec.js +148 -0
- package/dist/runner/jest/adapter.js +1 -1
- package/dist/runner/jest/pool.d.ts +1 -1
- package/dist/runner/jest/pool.js +6 -6
- package/dist/runner/jest/worker.mjs +1 -1
- package/dist/runner/orchestrator.js +43 -295
- package/dist/runner/pool-executor.d.ts +17 -0
- package/dist/runner/pool-executor.js +143 -0
- package/dist/runner/shared/__tests__/mutant-paths.spec.d.ts +1 -0
- package/dist/runner/shared/__tests__/mutant-paths.spec.js +66 -0
- package/dist/runner/shared/__tests__/redirect-state.spec.d.ts +1 -0
- package/dist/runner/shared/__tests__/redirect-state.spec.js +56 -0
- package/dist/runner/shared/index.d.ts +1 -1
- package/dist/runner/shared/index.js +1 -1
- package/dist/runner/shared/redirect-state.d.ts +2 -2
- package/dist/runner/shared/redirect-state.js +4 -4
- package/dist/runner/tasks.d.ts +12 -0
- package/dist/runner/tasks.js +25 -0
- package/dist/runner/types.d.ts +1 -1
- package/dist/runner/variants.d.ts +17 -2
- package/dist/runner/variants.js +33 -0
- package/dist/runner/vitest/__tests__/adapter.spec.js +1 -1
- package/dist/runner/vitest/__tests__/pool.spec.js +1 -1
- package/dist/runner/vitest/__tests__/redirect-loader.spec.js +87 -1
- package/dist/runner/vitest/__tests__/worker-runtime.spec.js +84 -0
- package/dist/runner/vitest/adapter.js +1 -1
- package/dist/runner/vitest/index.d.ts +0 -1
- package/dist/runner/vitest/index.js +0 -1
- package/dist/runner/vitest/pool.d.ts +1 -1
- package/dist/runner/vitest/pool.js +7 -7
- package/dist/runner/vitest/redirect-loader.d.ts +1 -1
- package/dist/runner/vitest/redirect-loader.js +1 -1
- package/dist/runner/vitest/worker-runtime.js +3 -3
- package/dist/runner/vitest/worker.mjs +1 -1
- package/dist/utils/__tests__/coverage.spec.js +167 -0
- package/dist/utils/__tests__/logger.spec.d.ts +1 -0
- package/dist/utils/__tests__/logger.spec.js +61 -0
- package/dist/utils/__tests__/normalizePath.spec.d.ts +1 -0
- package/dist/utils/__tests__/normalizePath.spec.js +22 -0
- package/dist/utils/__tests__/progress.spec.js +96 -0
- package/package.json +71 -22
- package/dist/admin/assets/index-B7nXq-e7.js +0 -32
- package/dist/admin/assets/index-B7nXq-e7.js.map +0 -1
- package/dist/admin/assets/index-BDQLkBUE.js +0 -32
- package/dist/admin/assets/index-BDQLkBUE.js.map +0 -1
- package/dist/admin/assets/index-DVkP-Tc7.css +0 -1
- package/dist/admin/index.html +0 -13
- package/dist/admin/server/admin.d.ts +0 -6
- package/dist/admin/server/admin.js +0 -234
- package/dist/bin/mutate-vitest.d.ts +0 -2
- package/dist/bin/mutate-vitest.js +0 -90
- package/dist/plugin/viteMutate.d.ts +0 -15
- package/dist/plugin/viteMutate.js +0 -52
- package/dist/plugin/vitest.setup.d.ts +0 -47
- package/dist/plugin/vitest.setup.js +0 -118
- package/dist/plugin/withVitest.d.ts +0 -13
- package/dist/plugin/withVitest.js +0 -30
- package/dist/runner/__tests__/orchestrator.spec.js +0 -55
- package/dist/runner/adapters/__tests__/jest.spec.js +0 -88
- package/dist/runner/adapters/__tests__/vitest-worker-runtime.spec.js +0 -59
- package/dist/runner/adapters/__tests__/vitest.spec.js +0 -118
- package/dist/runner/adapters/index.d.ts +0 -10
- package/dist/runner/adapters/index.js +0 -9
- package/dist/runner/adapters/jest/__tests__/index.spec.js +0 -88
- package/dist/runner/adapters/jest/index.d.ts +0 -24
- package/dist/runner/adapters/jest/index.js +0 -216
- package/dist/runner/adapters/jest/worker-runtime.d.ts +0 -37
- package/dist/runner/adapters/jest/worker-runtime.js +0 -171
- package/dist/runner/adapters/jest-worker-runtime.d.ts +0 -37
- package/dist/runner/adapters/jest-worker-runtime.js +0 -171
- package/dist/runner/adapters/jest.d.ts +0 -24
- package/dist/runner/adapters/jest.js +0 -216
- package/dist/runner/adapters/types.d.ts +0 -89
- package/dist/runner/adapters/types.js +0 -8
- package/dist/runner/adapters/vitest/__tests__/index.spec.js +0 -118
- package/dist/runner/adapters/vitest/__tests__/worker-runtime.spec.js +0 -59
- package/dist/runner/adapters/vitest/index.d.ts +0 -33
- package/dist/runner/adapters/vitest/index.js +0 -267
- package/dist/runner/adapters/vitest/worker-runtime.d.ts +0 -25
- package/dist/runner/adapters/vitest/worker-runtime.js +0 -118
- package/dist/runner/adapters/vitest-worker-runtime.d.ts +0 -25
- package/dist/runner/adapters/vitest-worker-runtime.js +0 -118
- package/dist/runner/adapters/vitest.d.ts +0 -33
- package/dist/runner/adapters/vitest.js +0 -267
- package/dist/runner/pool/__tests__/index.spec.js +0 -83
- package/dist/runner/pool/__tests__/pool-plugin.spec.js +0 -59
- package/dist/runner/pool/__tests__/pool-redirect-loader.spec.js +0 -78
- package/dist/runner/pool/index.d.ts +0 -8
- package/dist/runner/pool/index.js +0 -9
- package/dist/runner/pool/jest/pool.d.ts +0 -52
- package/dist/runner/pool/jest/pool.js +0 -309
- package/dist/runner/pool/jest/worker.mjs +0 -60
- package/dist/runner/pool/jest-pool.d.ts +0 -52
- package/dist/runner/pool/jest-pool.js +0 -309
- package/dist/runner/pool/jest-worker.mjs +0 -60
- package/dist/runner/pool/plugin.d.ts +0 -18
- package/dist/runner/pool/plugin.js +0 -60
- package/dist/runner/pool/pool-plugin.d.ts +0 -18
- package/dist/runner/pool/pool-plugin.js +0 -60
- package/dist/runner/pool/pool-redirect-loader.d.ts +0 -19
- package/dist/runner/pool/pool-redirect-loader.js +0 -116
- package/dist/runner/pool/pool-redirect-loader.mjs +0 -146
- package/dist/runner/pool/redirect-loader.d.ts +0 -19
- package/dist/runner/pool/redirect-loader.js +0 -116
- package/dist/runner/pool/vitest/pool.d.ts +0 -70
- package/dist/runner/pool/vitest/pool.js +0 -376
- package/dist/runner/pool/vitest/worker.d.mts +0 -15
- package/dist/runner/pool/vitest/worker.mjs +0 -96
- package/dist/runner/pool/vitest-worker.d.mts +0 -15
- package/dist/runner/pool/vitest-worker.mjs +0 -96
- package/dist/runner/shared-module-redirect.d.ts +0 -56
- package/dist/runner/shared-module-redirect.js +0 -84
- package/dist/types/api.d.ts +0 -20
- /package/dist/{runner/__tests__/orchestrator.spec.d.ts → core/__tests__/sfc.spec.d.ts} +0 -0
- /package/dist/{runner/adapters/__tests__/jest.spec.d.ts → core/__tests__/variant-utils.spec.d.ts} +0 -0
- /package/dist/{runner/adapters/__tests__/vitest-worker-runtime.spec.d.ts → mutators/__tests__/operator.spec.d.ts} +0 -0
- /package/dist/{runner/adapters/__tests__/vitest.spec.d.ts → mutators/__tests__/return-value.spec.d.ts} +0 -0
- /package/dist/runner/{adapters/jest/__tests__/index.spec.d.ts → __tests__/args.spec.d.ts} +0 -0
- /package/dist/runner/{adapters/vitest/__tests__/index.spec.d.ts → __tests__/cache.spec.d.ts} +0 -0
- /package/dist/runner/{adapters/vitest/__tests__/worker-runtime.spec.d.ts → __tests__/changed.spec.d.ts} +0 -0
- /package/dist/runner/{pool/__tests__/index.spec.d.ts → __tests__/cleanup.spec.d.ts} +0 -0
- /package/dist/runner/{pool/__tests__/pool-plugin.spec.d.ts → __tests__/config.spec.d.ts} +0 -0
- /package/dist/runner/{pool/__tests__/pool-redirect-loader.spec.d.ts → __tests__/coverage-resolver.spec.d.ts} +0 -0
- /package/dist/runner/{pool/jest-worker.d.mts → __tests__/pool-executor.spec.d.ts} +0 -0
- /package/dist/runner/{pool/jest/worker.d.mts → __tests__/tasks.spec.d.ts} +0 -0
- /package/dist/{types/api.js → runner/__tests__/variants.spec.d.ts} +0 -0
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { readMutantCache } from '../orchestrator.js';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import fs from 'node:fs/promises';
|
|
5
|
-
import os from 'node:os';
|
|
6
|
-
describe('Orchestrator', () => {
|
|
7
|
-
describe.skip('readMutantCache', () => {
|
|
8
|
-
it('should return empty object when cache file does not exist', async () => {
|
|
9
|
-
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'mutineer-test-'));
|
|
10
|
-
try {
|
|
11
|
-
const cache = await readMutantCache(tmpDir);
|
|
12
|
-
expect(cache).toEqual({});
|
|
13
|
-
}
|
|
14
|
-
finally {
|
|
15
|
-
await fs.rm(tmpDir, { recursive: true, force: true });
|
|
16
|
-
}
|
|
17
|
-
});
|
|
18
|
-
it('should parse valid cache file', async () => {
|
|
19
|
-
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'mutineer-test-'));
|
|
20
|
-
const cacheFile = path.join(tmpDir, '.mutate-cache.json');
|
|
21
|
-
try {
|
|
22
|
-
const cacheData = {
|
|
23
|
-
'test:hash:file.ts:1,0:mutator': 'killed',
|
|
24
|
-
'other:hash:file2.ts:2,5:flipStrictEQ': {
|
|
25
|
-
status: 'escaped',
|
|
26
|
-
file: '/path/to/file2.ts',
|
|
27
|
-
line: 2,
|
|
28
|
-
col: 5,
|
|
29
|
-
mutator: 'flipStrictEQ'
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
|
-
await fs.writeFile(cacheFile, JSON.stringify(cacheData), 'utf8');
|
|
33
|
-
const cache = await readMutantCache(tmpDir);
|
|
34
|
-
expect(cache).toBeDefined();
|
|
35
|
-
expect(Object.keys(cache).length).toBeGreaterThan(0);
|
|
36
|
-
expect(cache['test:hash:file.ts:1,0:mutator']).toBeDefined();
|
|
37
|
-
}
|
|
38
|
-
finally {
|
|
39
|
-
await fs.rm(tmpDir, { recursive: true, force: true });
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
it('should handle malformed JSON gracefully', async () => {
|
|
43
|
-
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'mutineer-test-'));
|
|
44
|
-
const cacheFile = path.join(tmpDir, '.mutate-cache.json');
|
|
45
|
-
try {
|
|
46
|
-
await fs.writeFile(cacheFile, 'invalid json {', 'utf8');
|
|
47
|
-
const cache = await readMutantCache(tmpDir);
|
|
48
|
-
expect(cache).toEqual({});
|
|
49
|
-
}
|
|
50
|
-
finally {
|
|
51
|
-
await fs.rm(tmpDir, { recursive: true, force: true });
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
});
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import fs from 'node:fs/promises';
|
|
4
|
-
import os from 'node:os';
|
|
5
|
-
import { createJestAdapter } from '../jest.js';
|
|
6
|
-
// Mock JestPool to avoid real processes
|
|
7
|
-
let poolInstance = null;
|
|
8
|
-
let poolCtorOpts = null;
|
|
9
|
-
vi.mock('../../pool/jest-pool.js', () => {
|
|
10
|
-
class MockPool {
|
|
11
|
-
constructor(opts) {
|
|
12
|
-
this.init = vi.fn();
|
|
13
|
-
this.run = vi.fn();
|
|
14
|
-
this.shutdown = vi.fn();
|
|
15
|
-
poolCtorOpts = opts;
|
|
16
|
-
poolInstance = this;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
return { JestPool: MockPool };
|
|
20
|
-
});
|
|
21
|
-
// Mock runCLI from jest
|
|
22
|
-
const runCLIMock = vi.fn();
|
|
23
|
-
vi.mock('@jest/core', () => ({
|
|
24
|
-
runCLI: runCLIMock,
|
|
25
|
-
}));
|
|
26
|
-
function makeAdapter(opts = {}) {
|
|
27
|
-
return createJestAdapter({
|
|
28
|
-
cwd: opts.cwd ?? process.cwd(),
|
|
29
|
-
concurrency: opts.concurrency ?? 2,
|
|
30
|
-
timeoutMs: opts.timeoutMs ?? 1000,
|
|
31
|
-
debug: opts.debug ?? false,
|
|
32
|
-
config: opts.config ?? { jestConfig: undefined },
|
|
33
|
-
cliArgs: opts.cliArgs ?? ['--changed'],
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
describe('Jest adapter', () => {
|
|
37
|
-
beforeEach(() => {
|
|
38
|
-
vi.clearAllMocks();
|
|
39
|
-
});
|
|
40
|
-
afterEach(() => {
|
|
41
|
-
vi.useRealTimers();
|
|
42
|
-
});
|
|
43
|
-
it('initializes pool with override concurrency', async () => {
|
|
44
|
-
const adapter = makeAdapter();
|
|
45
|
-
await adapter.init(4);
|
|
46
|
-
expect(poolInstance?.init).toHaveBeenCalledTimes(1);
|
|
47
|
-
expect(poolCtorOpts.concurrency).toBe(4);
|
|
48
|
-
});
|
|
49
|
-
it('runs baseline via runCLI with coverage when requested', async () => {
|
|
50
|
-
const adapter = makeAdapter({ config: { jestConfig: 'jest.config.ts' } });
|
|
51
|
-
runCLIMock.mockResolvedValueOnce({ results: { success: true } });
|
|
52
|
-
const ok = await adapter.runBaseline(['test-a'], { collectCoverage: true, perTestCoverage: false });
|
|
53
|
-
expect(ok).toBe(true);
|
|
54
|
-
expect(runCLIMock).toHaveBeenCalledTimes(1);
|
|
55
|
-
const args = runCLIMock.mock.calls[0][0];
|
|
56
|
-
expect(args.collectCoverage).toBe(true);
|
|
57
|
-
expect(args.coverageProvider).toBe('v8');
|
|
58
|
-
expect(args.config).toBe('jest.config.ts');
|
|
59
|
-
});
|
|
60
|
-
it('maps pool result to mutant status', async () => {
|
|
61
|
-
const adapter = makeAdapter();
|
|
62
|
-
await adapter.init();
|
|
63
|
-
poolInstance.run.mockResolvedValueOnce({ killed: true, durationMs: 10 });
|
|
64
|
-
const res = await adapter.runMutant({ id: '1', name: 'm', file: 'f', code: 'c', line: 1, col: 1 }, ['t']);
|
|
65
|
-
expect(res).toEqual({ status: 'killed', durationMs: 10, error: undefined });
|
|
66
|
-
});
|
|
67
|
-
it('maps pool errors to error status', async () => {
|
|
68
|
-
const adapter = makeAdapter();
|
|
69
|
-
await adapter.init();
|
|
70
|
-
poolInstance.run.mockResolvedValueOnce({ killed: true, durationMs: 12, error: 'crash' });
|
|
71
|
-
const res = await adapter.runMutant({ id: '1', name: 'm', file: 'f', code: 'c', line: 1, col: 1 }, ['t']);
|
|
72
|
-
expect(res).toEqual({ status: 'error', durationMs: 12, error: 'crash' });
|
|
73
|
-
});
|
|
74
|
-
it('detects coverage config from jest config file', async () => {
|
|
75
|
-
const tmp = await fs.mkdtemp(path.join(os.tmpdir(), 'mutineer-jest-'));
|
|
76
|
-
const cfgPath = path.join(tmp, 'jest.config.ts');
|
|
77
|
-
await fs.writeFile(cfgPath, 'module.exports = { collectCoverage: true }');
|
|
78
|
-
try {
|
|
79
|
-
const adapter = makeAdapter({ cwd: tmp, config: { jestConfig: 'jest.config.ts' } });
|
|
80
|
-
const coverage = await adapter.detectCoverageConfig();
|
|
81
|
-
expect(coverage.coverageEnabled).toBe(true);
|
|
82
|
-
expect(coverage.perTestEnabled).toBe(false);
|
|
83
|
-
}
|
|
84
|
-
finally {
|
|
85
|
-
await fs.rm(tmp, { recursive: true, force: true });
|
|
86
|
-
}
|
|
87
|
-
});
|
|
88
|
-
});
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import fs from 'node:fs';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import os from 'node:os';
|
|
5
|
-
import { createVitestWorkerRuntime } from '../vitest-worker-runtime.js';
|
|
6
|
-
const initFn = vi.fn();
|
|
7
|
-
const closeFn = vi.fn();
|
|
8
|
-
const runSpecsFn = vi.fn();
|
|
9
|
-
const invalidateFn = vi.fn();
|
|
10
|
-
const getProjectByNameFn = vi.fn();
|
|
11
|
-
vi.mock('vitest/node', () => ({
|
|
12
|
-
createVitest: vi.fn(async () => ({
|
|
13
|
-
init: initFn,
|
|
14
|
-
close: closeFn,
|
|
15
|
-
runTestSpecifications: runSpecsFn,
|
|
16
|
-
invalidateFile: invalidateFn,
|
|
17
|
-
getProjectByName: getProjectByNameFn,
|
|
18
|
-
}))
|
|
19
|
-
}));
|
|
20
|
-
describe('VitestWorkerRuntime', () => {
|
|
21
|
-
const tmpFiles = [];
|
|
22
|
-
beforeEach(() => {
|
|
23
|
-
vi.clearAllMocks();
|
|
24
|
-
getProjectByNameFn.mockReturnValue({
|
|
25
|
-
createSpecification: (file) => ({ moduleId: file })
|
|
26
|
-
});
|
|
27
|
-
runSpecsFn.mockResolvedValue({ testModules: [{ moduleId: 'a', ok: () => false }] });
|
|
28
|
-
});
|
|
29
|
-
afterEach(() => {
|
|
30
|
-
for (const f of tmpFiles.splice(0)) {
|
|
31
|
-
try {
|
|
32
|
-
fs.rmSync(f, { recursive: true, force: true });
|
|
33
|
-
}
|
|
34
|
-
catch { }
|
|
35
|
-
}
|
|
36
|
-
});
|
|
37
|
-
it('runs specs and reports kill based on results', async () => {
|
|
38
|
-
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'mutineer-worker-'));
|
|
39
|
-
tmpFiles.push(tmp);
|
|
40
|
-
const runtime = createVitestWorkerRuntime({ workerId: 'w1', cwd: tmp });
|
|
41
|
-
await runtime.init();
|
|
42
|
-
const result = await runtime.run({ id: 'mut#1', name: 'm', file: path.join(tmp, 'src.ts'), code: 'export const x=1', line: 1, col: 1 }, [path.join(tmp, 'test.ts')]);
|
|
43
|
-
expect(initFn).toHaveBeenCalled();
|
|
44
|
-
expect(runSpecsFn).toHaveBeenCalled();
|
|
45
|
-
expect(result.killed).toBe(true);
|
|
46
|
-
expect(fs.existsSync(path.join(tmp, 'src', '__mutineer__'))).toBe(false);
|
|
47
|
-
await runtime.shutdown();
|
|
48
|
-
});
|
|
49
|
-
it('returns escaped when no specs produced', async () => {
|
|
50
|
-
getProjectByNameFn.mockReturnValue({ createSpecification: () => null });
|
|
51
|
-
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'mutineer-worker-'));
|
|
52
|
-
tmpFiles.push(tmp);
|
|
53
|
-
const runtime = createVitestWorkerRuntime({ workerId: 'w2', cwd: tmp });
|
|
54
|
-
await runtime.init();
|
|
55
|
-
const result = await runtime.run({ id: 'mut#2', name: 'm', file: path.join(tmp, 'src.ts'), code: 'export const x=1', line: 1, col: 1 }, [path.join(tmp, 'test.ts')]);
|
|
56
|
-
expect(result.killed).toBe(false);
|
|
57
|
-
await runtime.shutdown();
|
|
58
|
-
});
|
|
59
|
-
});
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import fs from 'node:fs/promises';
|
|
4
|
-
import os from 'node:os';
|
|
5
|
-
import { createVitestAdapter, isCoverageRequestedInArgs } from '../vitest.js';
|
|
6
|
-
// Mock VitestPool to avoid spinning processes
|
|
7
|
-
var poolInstance = null;
|
|
8
|
-
var poolCtorOpts = null;
|
|
9
|
-
vi.mock('../../pool/index.js', () => {
|
|
10
|
-
class MockPool {
|
|
11
|
-
constructor(opts) {
|
|
12
|
-
this.init = vi.fn();
|
|
13
|
-
this.run = vi.fn();
|
|
14
|
-
this.shutdown = vi.fn();
|
|
15
|
-
poolCtorOpts = opts;
|
|
16
|
-
poolInstance = this;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
return { VitestPool: MockPool };
|
|
20
|
-
});
|
|
21
|
-
// Mock spawn for baseline runs
|
|
22
|
-
var spawnMock;
|
|
23
|
-
vi.mock('node:child_process', async () => {
|
|
24
|
-
const actual = await vi.importActual('node:child_process');
|
|
25
|
-
spawnMock = vi.fn();
|
|
26
|
-
return { ...actual, spawn: spawnMock };
|
|
27
|
-
});
|
|
28
|
-
function makeAdapter(opts = {}) {
|
|
29
|
-
return createVitestAdapter({
|
|
30
|
-
cwd: opts.cwd ?? process.cwd(),
|
|
31
|
-
concurrency: opts.concurrency ?? 2,
|
|
32
|
-
timeoutMs: opts.timeoutMs ?? 1000,
|
|
33
|
-
debug: opts.debug ?? false,
|
|
34
|
-
config: opts.config ?? { vitestConfig: undefined },
|
|
35
|
-
cliArgs: opts.cliArgs ?? ['--changed']
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
describe('Vitest adapter', () => {
|
|
39
|
-
beforeEach(() => {
|
|
40
|
-
vi.clearAllMocks();
|
|
41
|
-
});
|
|
42
|
-
afterEach(() => {
|
|
43
|
-
vi.useRealTimers();
|
|
44
|
-
});
|
|
45
|
-
it('initializes pool with override concurrency', async () => {
|
|
46
|
-
const adapter = makeAdapter();
|
|
47
|
-
await adapter.init(5);
|
|
48
|
-
expect(poolInstance?.init).toHaveBeenCalledTimes(1);
|
|
49
|
-
expect(poolCtorOpts.concurrency).toBe(5);
|
|
50
|
-
});
|
|
51
|
-
it('maps pool result to mutant status', async () => {
|
|
52
|
-
poolInstance = null;
|
|
53
|
-
poolCtorOpts = null;
|
|
54
|
-
const adapter = makeAdapter();
|
|
55
|
-
await adapter.init();
|
|
56
|
-
poolInstance.run.mockResolvedValueOnce({ killed: true, durationMs: 10 });
|
|
57
|
-
const res = await adapter.runMutant({ id: '1', name: 'm', file: 'f', code: 'c', line: 1, col: 1 }, ['t']);
|
|
58
|
-
expect(res).toEqual({ status: 'killed', durationMs: 10, error: undefined });
|
|
59
|
-
});
|
|
60
|
-
it('maps pool timeout errors to timeout status', async () => {
|
|
61
|
-
const adapter = makeAdapter();
|
|
62
|
-
await adapter.init();
|
|
63
|
-
poolInstance.run.mockResolvedValueOnce({ killed: true, durationMs: 15, error: 'timeout' });
|
|
64
|
-
const res = await adapter.runMutant({ id: '1', name: 'm', file: 'f', code: 'c', line: 1, col: 1 }, ['t']);
|
|
65
|
-
expect(res).toEqual({ status: 'timeout', durationMs: 15, error: 'timeout' });
|
|
66
|
-
});
|
|
67
|
-
it('maps pool errors to error status', async () => {
|
|
68
|
-
const adapter = makeAdapter();
|
|
69
|
-
await adapter.init();
|
|
70
|
-
poolInstance.run.mockResolvedValueOnce({ killed: true, durationMs: 12, error: 'crash' });
|
|
71
|
-
const res = await adapter.runMutant({ id: '1', name: 'm', file: 'f', code: 'c', line: 1, col: 1 }, ['t']);
|
|
72
|
-
expect(res).toEqual({ status: 'error', durationMs: 12, error: 'crash' });
|
|
73
|
-
});
|
|
74
|
-
it('returns error status on pool throw', async () => {
|
|
75
|
-
const adapter = makeAdapter();
|
|
76
|
-
await adapter.init();
|
|
77
|
-
poolInstance.run.mockRejectedValueOnce(new Error('boom'));
|
|
78
|
-
const res = await adapter.runMutant({ id: '1', name: 'm', file: 'f', code: 'c', line: 1, col: 1 }, ['t']);
|
|
79
|
-
expect(res.status).toBe('error');
|
|
80
|
-
});
|
|
81
|
-
it('includes coverage args for baseline when requested', async () => {
|
|
82
|
-
const adapter = makeAdapter({ cliArgs: [] });
|
|
83
|
-
spawnMock.mockImplementationOnce(() => ({
|
|
84
|
-
on: (evt, cb) => {
|
|
85
|
-
if (evt === 'exit')
|
|
86
|
-
cb(0);
|
|
87
|
-
}
|
|
88
|
-
}));
|
|
89
|
-
const ok = await adapter.runBaseline(['test-a'], { collectCoverage: true, perTestCoverage: true });
|
|
90
|
-
expect(ok).toBe(true);
|
|
91
|
-
const call = spawnMock.mock.calls[0];
|
|
92
|
-
const args = call[1];
|
|
93
|
-
expect(args.join(' ')).toContain('--coverage.enabled=true');
|
|
94
|
-
expect(args.join(' ')).toContain('--coverage.perTest=true');
|
|
95
|
-
});
|
|
96
|
-
it('detects coverage config from vitest config file', async () => {
|
|
97
|
-
const tmp = await fs.mkdtemp(path.join(os.tmpdir(), 'mutineer-vitest-'));
|
|
98
|
-
const cfgPath = path.join(tmp, 'vitest.config.ts');
|
|
99
|
-
await fs.writeFile(cfgPath, 'export default { coverage: { enabled: true }, test: { coverage: { perTest: true } } }');
|
|
100
|
-
try {
|
|
101
|
-
const adapter = makeAdapter({ cwd: tmp, config: { vitestConfig: 'vitest.config.ts' } });
|
|
102
|
-
const coverage = await adapter.detectCoverageConfig();
|
|
103
|
-
expect(coverage.coverageEnabled).toBe(true);
|
|
104
|
-
expect(coverage.perTestEnabled).toBe(true);
|
|
105
|
-
}
|
|
106
|
-
finally {
|
|
107
|
-
await fs.rm(tmp, { recursive: true, force: true });
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
});
|
|
111
|
-
describe('isCoverageRequestedInArgs', () => {
|
|
112
|
-
it('detects enabled coverage flags', () => {
|
|
113
|
-
expect(isCoverageRequestedInArgs(['--coverage'])).toBe(true);
|
|
114
|
-
expect(isCoverageRequestedInArgs(['--coverage.enabled=true'])).toBe(true);
|
|
115
|
-
expect(isCoverageRequestedInArgs(['--coverage.enabled=false'])).toBe(false);
|
|
116
|
-
expect(isCoverageRequestedInArgs(['--no-coverage'])).toBe(false);
|
|
117
|
-
});
|
|
118
|
-
});
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Test Runner Adapters
|
|
3
|
-
*
|
|
4
|
-
* This module exports the adapter interface and available implementations.
|
|
5
|
-
* Currently supports Vitest and Jest, with the architecture ready for additional
|
|
6
|
-
* test runners (Mocha, etc.).
|
|
7
|
-
*/
|
|
8
|
-
export type { TestRunnerAdapter, TestRunnerAdapterOptions, TestRunnerAdapterFactory, MutantPayload, MutantRunResult, BaselineOptions, CoverageConfig, } from './types.js';
|
|
9
|
-
export { VitestAdapter, createVitestAdapter, isCoverageRequestedInArgs } from './vitest/index.js';
|
|
10
|
-
export { JestAdapter, createJestAdapter } from './jest/index.js';
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Test Runner Adapters
|
|
3
|
-
*
|
|
4
|
-
* This module exports the adapter interface and available implementations.
|
|
5
|
-
* Currently supports Vitest and Jest, with the architecture ready for additional
|
|
6
|
-
* test runners (Mocha, etc.).
|
|
7
|
-
*/
|
|
8
|
-
export { VitestAdapter, createVitestAdapter, isCoverageRequestedInArgs } from './vitest/index.js';
|
|
9
|
-
export { JestAdapter, createJestAdapter } from './jest/index.js';
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import fs from 'node:fs/promises';
|
|
4
|
-
import os from 'node:os';
|
|
5
|
-
import { createJestAdapter } from '../index.js';
|
|
6
|
-
// Mock JestPool to avoid real processes
|
|
7
|
-
let poolInstance = null;
|
|
8
|
-
let poolCtorOpts = null;
|
|
9
|
-
vi.mock('../../../pool/jest/pool.js', () => {
|
|
10
|
-
class MockPool {
|
|
11
|
-
constructor(opts) {
|
|
12
|
-
this.init = vi.fn();
|
|
13
|
-
this.run = vi.fn();
|
|
14
|
-
this.shutdown = vi.fn();
|
|
15
|
-
poolCtorOpts = opts;
|
|
16
|
-
poolInstance = this;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
return { JestPool: MockPool };
|
|
20
|
-
});
|
|
21
|
-
// Mock runCLI from jest
|
|
22
|
-
const runCLIMock = vi.fn();
|
|
23
|
-
vi.mock('@jest/core', () => ({
|
|
24
|
-
runCLI: runCLIMock,
|
|
25
|
-
}));
|
|
26
|
-
function makeAdapter(opts = {}) {
|
|
27
|
-
return createJestAdapter({
|
|
28
|
-
cwd: opts.cwd ?? process.cwd(),
|
|
29
|
-
concurrency: opts.concurrency ?? 2,
|
|
30
|
-
timeoutMs: opts.timeoutMs ?? 1000,
|
|
31
|
-
debug: opts.debug ?? false,
|
|
32
|
-
config: opts.config ?? { jestConfig: undefined },
|
|
33
|
-
cliArgs: opts.cliArgs ?? ['--changed'],
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
describe('Jest adapter', () => {
|
|
37
|
-
beforeEach(() => {
|
|
38
|
-
vi.clearAllMocks();
|
|
39
|
-
});
|
|
40
|
-
afterEach(() => {
|
|
41
|
-
vi.useRealTimers();
|
|
42
|
-
});
|
|
43
|
-
it('initializes pool with override concurrency', async () => {
|
|
44
|
-
const adapter = makeAdapter();
|
|
45
|
-
await adapter.init(4);
|
|
46
|
-
expect(poolInstance?.init).toHaveBeenCalledTimes(1);
|
|
47
|
-
expect(poolCtorOpts.concurrency).toBe(4);
|
|
48
|
-
});
|
|
49
|
-
it('runs baseline via runCLI with coverage when requested', async () => {
|
|
50
|
-
const adapter = makeAdapter({ config: { jestConfig: 'jest.config.ts' } });
|
|
51
|
-
runCLIMock.mockResolvedValueOnce({ results: { success: true } });
|
|
52
|
-
const ok = await adapter.runBaseline(['test-a'], { collectCoverage: true, perTestCoverage: false });
|
|
53
|
-
expect(ok).toBe(true);
|
|
54
|
-
expect(runCLIMock).toHaveBeenCalledTimes(1);
|
|
55
|
-
const args = runCLIMock.mock.calls[0][0];
|
|
56
|
-
expect(args.collectCoverage).toBe(true);
|
|
57
|
-
expect(args.coverageProvider).toBe('v8');
|
|
58
|
-
expect(args.config).toBe('jest.config.ts');
|
|
59
|
-
});
|
|
60
|
-
it('maps pool result to mutant status', async () => {
|
|
61
|
-
const adapter = makeAdapter();
|
|
62
|
-
await adapter.init();
|
|
63
|
-
poolInstance.run.mockResolvedValueOnce({ killed: true, durationMs: 10 });
|
|
64
|
-
const res = await adapter.runMutant({ id: '1', name: 'm', file: 'f', code: 'c', line: 1, col: 1 }, ['t']);
|
|
65
|
-
expect(res).toEqual({ status: 'killed', durationMs: 10, error: undefined });
|
|
66
|
-
});
|
|
67
|
-
it('maps pool errors to error status', async () => {
|
|
68
|
-
const adapter = makeAdapter();
|
|
69
|
-
await adapter.init();
|
|
70
|
-
poolInstance.run.mockResolvedValueOnce({ killed: true, durationMs: 12, error: 'crash' });
|
|
71
|
-
const res = await adapter.runMutant({ id: '1', name: 'm', file: 'f', code: 'c', line: 1, col: 1 }, ['t']);
|
|
72
|
-
expect(res).toEqual({ status: 'error', durationMs: 12, error: 'crash' });
|
|
73
|
-
});
|
|
74
|
-
it('detects coverage config from jest config file', async () => {
|
|
75
|
-
const tmp = await fs.mkdtemp(path.join(os.tmpdir(), 'mutineer-jest-'));
|
|
76
|
-
const cfgPath = path.join(tmp, 'jest.config.ts');
|
|
77
|
-
await fs.writeFile(cfgPath, 'module.exports = { collectCoverage: true }');
|
|
78
|
-
try {
|
|
79
|
-
const adapter = makeAdapter({ cwd: tmp, config: { jestConfig: 'jest.config.ts' } });
|
|
80
|
-
const coverage = await adapter.detectCoverageConfig();
|
|
81
|
-
expect(coverage.coverageEnabled).toBe(true);
|
|
82
|
-
expect(coverage.perTestEnabled).toBe(false);
|
|
83
|
-
}
|
|
84
|
-
finally {
|
|
85
|
-
await fs.rm(tmp, { recursive: true, force: true });
|
|
86
|
-
}
|
|
87
|
-
});
|
|
88
|
-
});
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Jest Test Runner Adapter
|
|
3
|
-
*
|
|
4
|
-
* Implements the TestRunnerAdapter interface for Jest using runCLI.
|
|
5
|
-
* Baseline runs are executed directly via runCLI; mutant runs are delegated
|
|
6
|
-
* to a pool of long-lived worker processes that also use runCLI with a
|
|
7
|
-
* redirect resolver to swap in mutated code.
|
|
8
|
-
*/
|
|
9
|
-
import type { TestRunnerAdapter, TestRunnerAdapterOptions, MutantPayload, MutantRunResult, BaselineOptions, CoverageConfig } from '../types.js';
|
|
10
|
-
export declare class JestAdapter implements TestRunnerAdapter {
|
|
11
|
-
readonly name = "jest";
|
|
12
|
-
private readonly options;
|
|
13
|
-
private jestConfigPath?;
|
|
14
|
-
private pool;
|
|
15
|
-
private readonly requireFromCwd;
|
|
16
|
-
constructor(options: TestRunnerAdapterOptions);
|
|
17
|
-
init(concurrencyOverride?: number): Promise<void>;
|
|
18
|
-
runBaseline(tests: readonly string[], options: BaselineOptions): Promise<boolean>;
|
|
19
|
-
runMutant(mutant: MutantPayload, tests: readonly string[]): Promise<MutantRunResult>;
|
|
20
|
-
shutdown(): Promise<void>;
|
|
21
|
-
hasCoverageProvider(): boolean;
|
|
22
|
-
detectCoverageConfig(): Promise<CoverageConfig>;
|
|
23
|
-
}
|
|
24
|
-
export declare function createJestAdapter(options: TestRunnerAdapterOptions): JestAdapter;
|
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Jest Test Runner Adapter
|
|
3
|
-
*
|
|
4
|
-
* Implements the TestRunnerAdapter interface for Jest using runCLI.
|
|
5
|
-
* Baseline runs are executed directly via runCLI; mutant runs are delegated
|
|
6
|
-
* to a pool of long-lived worker processes that also use runCLI with a
|
|
7
|
-
* redirect resolver to swap in mutated code.
|
|
8
|
-
*/
|
|
9
|
-
import fs from 'node:fs/promises';
|
|
10
|
-
import path from 'node:path';
|
|
11
|
-
import { createRequire } from 'node:module';
|
|
12
|
-
// import os from 'node:os'
|
|
13
|
-
import { JestPool } from '../../pool/jest/pool.js';
|
|
14
|
-
const require = createRequire(import.meta.url);
|
|
15
|
-
/**
|
|
16
|
-
* Strip mutineer-specific CLI args that shouldn't be passed to Jest.
|
|
17
|
-
*/
|
|
18
|
-
function stripMutineerArgs(args) {
|
|
19
|
-
const out = [];
|
|
20
|
-
const consumeNext = new Set([
|
|
21
|
-
'--concurrency',
|
|
22
|
-
'--progress',
|
|
23
|
-
'--min-kill-percent',
|
|
24
|
-
'--config',
|
|
25
|
-
'-c',
|
|
26
|
-
'--coverage-file',
|
|
27
|
-
'--runner',
|
|
28
|
-
]);
|
|
29
|
-
const dropExact = new Set([
|
|
30
|
-
'-m',
|
|
31
|
-
'--mutate',
|
|
32
|
-
'--changed',
|
|
33
|
-
'--changed-with-deps',
|
|
34
|
-
'--only-covered-lines',
|
|
35
|
-
'--per-test-coverage',
|
|
36
|
-
'--perTestCoverage',
|
|
37
|
-
]);
|
|
38
|
-
for (let i = 0; i < args.length; i++) {
|
|
39
|
-
const a = args[i];
|
|
40
|
-
if (dropExact.has(a))
|
|
41
|
-
continue;
|
|
42
|
-
if (consumeNext.has(a)) {
|
|
43
|
-
i++;
|
|
44
|
-
continue;
|
|
45
|
-
}
|
|
46
|
-
if (a.startsWith('--min-kill-percent='))
|
|
47
|
-
continue;
|
|
48
|
-
if (a.startsWith('--config=') || a.startsWith('-c='))
|
|
49
|
-
continue;
|
|
50
|
-
out.push(a);
|
|
51
|
-
}
|
|
52
|
-
return out;
|
|
53
|
-
}
|
|
54
|
-
// function getMutantFilePath(originalFile: string, mutantId: string): string {
|
|
55
|
-
// const dir = path.dirname(originalFile)
|
|
56
|
-
// const ext = path.extname(originalFile)
|
|
57
|
-
// const basename = path.basename(originalFile, ext)
|
|
58
|
-
// const mutineerDir = path.join(dir, '__mutineer__')
|
|
59
|
-
// return path.join(mutineerDir, `${basename}_${mutantId.replace(/[^a-zA-Z0-9]/g, '_').slice(0, 20)}${ext}`)
|
|
60
|
-
// }
|
|
61
|
-
// function ensureMutineerDir(file: string): void {
|
|
62
|
-
// const dir = path.dirname(file)
|
|
63
|
-
// if (!fs) return
|
|
64
|
-
// }
|
|
65
|
-
// function createResolverModule(workerId: string): string {
|
|
66
|
-
// const resolverPath = path.join(os.tmpdir(), `mutineer-jest-resolver-${workerId}.cjs`)
|
|
67
|
-
// const resolverCode = `
|
|
68
|
-
// const path = require('path');
|
|
69
|
-
// const { defaultResolver } = require('jest-resolve');
|
|
70
|
-
// module.exports = (request, options) => {
|
|
71
|
-
// const from = process.env.MUTINEER_REDIRECT_FROM;
|
|
72
|
-
// const to = process.env.MUTINEER_REDIRECT_TO;
|
|
73
|
-
// if (!from || !to) {
|
|
74
|
-
// return defaultResolver(request, options);
|
|
75
|
-
// }
|
|
76
|
-
// const normalizedFrom = path.resolve(from);
|
|
77
|
-
// const baseDir = options?.basedir ?? process.cwd();
|
|
78
|
-
// const candidate = path.resolve(baseDir, request);
|
|
79
|
-
// if (candidate === normalizedFrom || request === normalizedFrom) {
|
|
80
|
-
// return defaultResolver(to, options);
|
|
81
|
-
// }
|
|
82
|
-
// return defaultResolver(request, options);
|
|
83
|
-
// };
|
|
84
|
-
// `
|
|
85
|
-
// fs.writeFile(resolverPath, resolverCode, 'utf8')
|
|
86
|
-
// .catch(() => {
|
|
87
|
-
// /* ignore */
|
|
88
|
-
// })
|
|
89
|
-
// return resolverPath
|
|
90
|
-
// }
|
|
91
|
-
async function loadRunCLI(requireFromCwd) {
|
|
92
|
-
try {
|
|
93
|
-
return requireFromCwd('@jest/core');
|
|
94
|
-
}
|
|
95
|
-
catch {
|
|
96
|
-
return import('@jest/core');
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
/**
|
|
100
|
-
* Build Jest runCLI options for the given mode.
|
|
101
|
-
*/
|
|
102
|
-
function buildJestCliOptions(tests, mode, jestConfigPath) {
|
|
103
|
-
const opts = {
|
|
104
|
-
_: [...tests],
|
|
105
|
-
$0: 'mutineer',
|
|
106
|
-
runInBand: true,
|
|
107
|
-
runTestsByPath: true,
|
|
108
|
-
watch: false,
|
|
109
|
-
passWithNoTests: true,
|
|
110
|
-
testPathPattern: [...tests],
|
|
111
|
-
};
|
|
112
|
-
if (jestConfigPath) {
|
|
113
|
-
opts.config = jestConfigPath;
|
|
114
|
-
}
|
|
115
|
-
if (mode === 'baseline-with-coverage') {
|
|
116
|
-
opts.coverage = true;
|
|
117
|
-
opts.collectCoverage = true;
|
|
118
|
-
opts.coverageProvider = 'v8';
|
|
119
|
-
}
|
|
120
|
-
return opts;
|
|
121
|
-
}
|
|
122
|
-
export class JestAdapter {
|
|
123
|
-
constructor(options) {
|
|
124
|
-
this.name = 'jest';
|
|
125
|
-
this.pool = null;
|
|
126
|
-
this.options = options;
|
|
127
|
-
this.jestConfigPath = options.config.jestConfig;
|
|
128
|
-
stripMutineerArgs(options.cliArgs);
|
|
129
|
-
this.requireFromCwd = createRequire(path.join(options.cwd, 'package.json'));
|
|
130
|
-
}
|
|
131
|
-
async init(concurrencyOverride) {
|
|
132
|
-
const workerCount = Math.max(1, concurrencyOverride ?? this.options.concurrency);
|
|
133
|
-
this.pool = new JestPool({
|
|
134
|
-
cwd: this.options.cwd,
|
|
135
|
-
concurrency: workerCount,
|
|
136
|
-
jestConfig: this.options.config.jestConfig,
|
|
137
|
-
timeoutMs: this.options.timeoutMs,
|
|
138
|
-
debug: this.options.debug,
|
|
139
|
-
});
|
|
140
|
-
await this.pool.init();
|
|
141
|
-
}
|
|
142
|
-
async runBaseline(tests, options) {
|
|
143
|
-
const mode = options.collectCoverage ? 'baseline-with-coverage' : 'baseline';
|
|
144
|
-
const cliOptions = buildJestCliOptions(tests, mode, this.jestConfigPath);
|
|
145
|
-
try {
|
|
146
|
-
const { runCLI } = await loadRunCLI(this.requireFromCwd);
|
|
147
|
-
const { results } = await runCLI(cliOptions, [this.options.cwd]);
|
|
148
|
-
return results.success;
|
|
149
|
-
}
|
|
150
|
-
catch (err) {
|
|
151
|
-
if (this.options.debug) {
|
|
152
|
-
console.error('Failed to run Jest baseline', err);
|
|
153
|
-
}
|
|
154
|
-
return false;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
async runMutant(mutant, tests) {
|
|
158
|
-
if (!this.pool) {
|
|
159
|
-
throw new Error('JestAdapter not initialized. Call init() first.');
|
|
160
|
-
}
|
|
161
|
-
try {
|
|
162
|
-
const result = await this.pool.run(mutant, [...tests]);
|
|
163
|
-
if (result.error === 'timeout') {
|
|
164
|
-
return { status: 'timeout', durationMs: result.durationMs, error: result.error };
|
|
165
|
-
}
|
|
166
|
-
if (result.error) {
|
|
167
|
-
return { status: 'error', durationMs: result.durationMs, error: result.error };
|
|
168
|
-
}
|
|
169
|
-
return {
|
|
170
|
-
status: result.killed ? 'killed' : 'escaped',
|
|
171
|
-
durationMs: result.durationMs,
|
|
172
|
-
error: result.error,
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
catch (err) {
|
|
176
|
-
return {
|
|
177
|
-
status: 'error',
|
|
178
|
-
durationMs: 0,
|
|
179
|
-
error: err instanceof Error ? err.message : String(err),
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
async shutdown() {
|
|
184
|
-
if (this.pool) {
|
|
185
|
-
await this.pool.shutdown();
|
|
186
|
-
this.pool = null;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
hasCoverageProvider() {
|
|
190
|
-
try {
|
|
191
|
-
require.resolve('jest/package.json', { paths: [this.options.cwd] });
|
|
192
|
-
return true;
|
|
193
|
-
}
|
|
194
|
-
catch {
|
|
195
|
-
return false;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
async detectCoverageConfig() {
|
|
199
|
-
const configPath = this.options.config.jestConfig;
|
|
200
|
-
if (!configPath) {
|
|
201
|
-
return { perTestEnabled: false, coverageEnabled: false };
|
|
202
|
-
}
|
|
203
|
-
try {
|
|
204
|
-
const abs = path.isAbsolute(configPath) ? configPath : path.join(this.options.cwd, configPath);
|
|
205
|
-
const content = await fs.readFile(abs, 'utf8');
|
|
206
|
-
const coverageEnabled = /collectCoverage\s*:\s*true/.test(content) || /coverageProvider\s*:/.test(content);
|
|
207
|
-
return { perTestEnabled: false, coverageEnabled };
|
|
208
|
-
}
|
|
209
|
-
catch {
|
|
210
|
-
return { perTestEnabled: false, coverageEnabled: false };
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
export function createJestAdapter(options) {
|
|
215
|
-
return new JestAdapter(options);
|
|
216
|
-
}
|