Repository navigation
Expand file tree
/
Copy pathvitest.config.ts
More file actions
142 lines (137 loc) · 5.19 KB
/
Copy pathvitest.config.ts
File metadata and controls
142 lines (137 loc) · 5.19 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
import * as path from 'path';
import { tmpdir } from 'os';
import { defineConfig, type Plugin } from 'vitest/config';
const r = (...p: string[]) => path.resolve(__dirname, ...p);
/**
* The source uses NodeNext-style relative imports that carry a `.js` extension
* (e.g. `import { x } from './foo.js'`) while the files on disk are `.ts`/`.tsx`.
* Vite does not strip the extension on its own, so this plugin retries a failed
* `.js` resolution against the matching TypeScript source. It replaces jest's
* `"^(\\.{1,2}/.*)\\.js$": "$1"` moduleNameMapper entry.
*/
function resolveTsForJs(): Plugin {
return {
name: 'resolve-ts-for-js',
enforce: 'pre',
async resolveId(id, importer, options) {
if (!id.endsWith('.js')) return null;
for (const ext of ['.ts', '.tsx'] as const) {
const candidate = `${id.slice(0, -3)}${ext}`;
const resolved = await this.resolve(candidate, importer, {
...options,
skipSelf: true,
});
if (resolved) return resolved;
}
return null;
},
};
}
// Per-surface Vitest projects keyed by today's directories. Each project runs
// alone with `vitest run --project <name>`; `vitest run` runs them all.
const TESTS = '__tests__/**/*.{js,jsx,ts,tsx}';
const AGENT_TESTS = [`src/agent/**/${TESTS}`];
const PROGRAM_TESTS = [`src/programs/**/${TESTS}`];
const TOOL_TESTS = [`src/tools/**/${TESTS}`];
const TUI_TESTS = [`src/tui/**/${TESTS}`];
const HEADLESS_TESTS = [`src/headless/**/${TESTS}`];
const CLI_TESTS = [`src/cli/**/${TESTS}`];
const HARNESS_TESTS = [
`e2e-harness/${TESTS}`,
'e2e-harness/**/*.{test,spec}.{js,jsx,ts,tsx}',
];
const EXCLUDE = [
'**/node_modules/**',
'**/dist/**',
'**/e2e-tests/**',
'**/*.no-jest.*',
'**/*.d.ts',
];
const project = (name: string, include: string[], exclude: string[] = []) => ({
extends: true as const,
test: { name, include, exclude: [...EXCLUDE, ...exclude] },
});
export default defineConfig({
plugins: [resolveTsForJs()],
// The source targets the React 19 automatic JSX runtime (tsconfig
// `"jsx": "react-jsx"`); mirror that here so the TUI `.tsx` tests transform.
esbuild: { jsx: 'automatic' },
resolve: {
alias: [
// Module mocks — parity with jest's moduleNameMapper. Every import of
// these resolves to the hand-written manual mock; a per-test `vi.mock(...)`
// factory still takes precedence within that test file.
{
find: /^@anthropic-ai\/claude-agent-sdk$/,
replacement: r('__mocks__/@anthropic-ai/claude-agent-sdk.ts'),
},
{
find: /^@posthog\/warlock$/,
replacement: r('__mocks__/@posthog/warlock.ts'),
},
{ find: /^ink$/, replacement: r('__mocks__/ink.ts') },
// The real Ink, for the tests that render: `vi.importActual('ink')` gets the stub above.
{
find: /^ink-actual$/,
replacement: r('node_modules/ink/build/index.js'),
},
{ find: /^@shared\/(.*)$/, replacement: `${r('src/shared')}/$1` },
{ find: /^@agent$/, replacement: r('src/agent/index.ts') },
{ find: /^@agent\/types$/, replacement: r('src/agent/types.ts') },
{ find: /^@agent\/(.*)$/, replacement: `${r('src/agent')}/$1` },
{ find: /^@programs$/, replacement: r('src/programs/index.ts') },
{ find: /^@programs\/types$/, replacement: r('src/programs/types.ts') },
{ find: /^@programs\/(.*)$/, replacement: `${r('src/programs')}/$1` },
{ find: /^@tools$/, replacement: r('src/tools/index.ts') },
// Path aliases — mirror tsconfig `paths`.
{ find: /^@env$/, replacement: r('src/env.ts') },
{ find: /^@host\/(.*)$/, replacement: `${r('src/host')}/$1` },
{ find: /^@tui$/, replacement: r('src/tui/index.ts') },
{ find: /^@tui\/(.*)$/, replacement: `${r('src/tui')}/$1` },
{ find: /^@headless$/, replacement: r('src/headless/index.ts') },
{ find: /^@cli$/, replacement: r('src/cli/index.ts') },
{ find: /^@cli\/(.*)$/, replacement: `${r('src/cli')}/$1` },
{ find: /^@e2e-harness\/(.*)$/, replacement: `${r('e2e-harness')}/$1` },
{ find: /^@utils\/(.*)$/, replacement: `${r('src/shared/utils')}/$1` },
],
},
test: {
globals: true,
environment: 'node',
// Tests log to their own file, not the one real runs share.
env: {
POSTHOG_WIZARD_LOG_FILE: path.join(
tmpdir(),
`posthog-wizard-vitest-${process.pid}.log`,
),
},
projects: [
project('agent', AGENT_TESTS),
project('programs', PROGRAM_TESTS),
project('tools', TOOL_TESTS),
project('tui', TUI_TESTS),
project('headless', HEADLESS_TESTS),
project('cli', CLI_TESTS),
project('harness', HARNESS_TESTS),
project(
'shared',
// Shared, the host layer and anything else no layer project claims.
// The second glob runs a test file outside a __tests__ directory here,
// rather than nowhere.
[`src/**/${TESTS}`, 'src/**/*.{test,spec}.{js,jsx,ts,tsx}'],
[
...AGENT_TESTS,
...PROGRAM_TESTS,
...TOOL_TESTS,
...TUI_TESTS,
...HEADLESS_TESTS,
...CLI_TESTS,
],
),
],
coverage: {
provider: 'v8',
exclude: ['dist/**'],
},
},
});