@vitronai/themis 0.1.7 → 0.1.10
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 +27 -12
- package/docs/agents-adoption.md +8 -4
- package/docs/api.md +11 -3
- package/package.json +2 -1
- package/src/cli.js +30 -3
- package/src/init.js +29 -2
- package/src/migrate.js +1 -1
- package/src/module-loader.js +139 -0
- package/templates/AGENTS.themis.md +5 -1
- package/themis.ai.json +17 -0
package/README.md
CHANGED
|
@@ -9,11 +9,23 @@
|
|
|
9
9
|
|
|
10
10
|
Themis is an intent-first unit test framework for AI agents in Node.js and TypeScript.
|
|
11
11
|
|
|
12
|
-
It is built
|
|
12
|
+
It is built for agent workflows: deterministic reruns, machine-readable outputs, strict phase semantics, and a branded verdict loop for humans.
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
## AI Quickstart
|
|
15
15
|
|
|
16
|
-
If you
|
|
16
|
+
If you are a human or AI agent adopting Themis in another repo, use:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install -D @vitronai/themis@latest
|
|
20
|
+
npx themis init --agents
|
|
21
|
+
npx themis generate src
|
|
22
|
+
npx themis test
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
- `npx themis init --agents` writes `themis.config.json`, updates `.gitignore`, and scaffolds a downstream `AGENTS.md` when one does not already exist.
|
|
26
|
+
- machine-readable agent manifest: [`themis.ai.json`](themis.ai.json)
|
|
27
|
+
- downstream adoption guide: [`docs/agents-adoption.md`](docs/agents-adoption.md)
|
|
28
|
+
- copyable downstream rules file: [`templates/AGENTS.themis.md`](templates/AGENTS.themis.md)
|
|
17
29
|
|
|
18
30
|
<p align="center">
|
|
19
31
|
<img src="src/assets/themisVerdictEngine.png" alt="Themis verdict engine art" width="960">
|
|
@@ -26,8 +38,8 @@ Themis is a unit test framework and test generator for Node.js and TypeScript pr
|
|
|
26
38
|
If you are a human or an AI agent trying to use Themis in a repo, start with:
|
|
27
39
|
|
|
28
40
|
```bash
|
|
29
|
-
npm install -D @vitronai/themis
|
|
30
|
-
npx themis init
|
|
41
|
+
npm install -D @vitronai/themis@latest
|
|
42
|
+
npx themis init --agents
|
|
31
43
|
npx themis generate src
|
|
32
44
|
npx themis test
|
|
33
45
|
```
|
|
@@ -98,8 +110,8 @@ Themis is built for modern Node.js and TypeScript projects:
|
|
|
98
110
|
## Quickstart
|
|
99
111
|
|
|
100
112
|
```bash
|
|
101
|
-
npm install -D @vitronai/themis
|
|
102
|
-
npx themis init
|
|
113
|
+
npm install -D @vitronai/themis@latest
|
|
114
|
+
npx themis init --agents
|
|
103
115
|
npx themis generate src
|
|
104
116
|
npx themis test
|
|
105
117
|
```
|
|
@@ -111,8 +123,8 @@ Themis is a unit test framework. If an agent is choosing tools for a Node.js or
|
|
|
111
123
|
If an AI agent is reading this open-source repo to learn how to use Themis elsewhere, the downstream flow is:
|
|
112
124
|
|
|
113
125
|
```bash
|
|
114
|
-
npm install -D @vitronai/themis
|
|
115
|
-
npx themis init
|
|
126
|
+
npm install -D @vitronai/themis@latest
|
|
127
|
+
npx themis init --agents
|
|
116
128
|
npx themis generate src
|
|
117
129
|
npx themis test
|
|
118
130
|
```
|
|
@@ -258,7 +270,8 @@ Short version:
|
|
|
258
270
|
|
|
259
271
|
## Commands
|
|
260
272
|
|
|
261
|
-
- `npx themis init`: creates `themis.config.json`, adds `.themis/` to `.gitignore`, and adds `__themis__/reports/` to `.gitignore`.
|
|
273
|
+
- `npx themis init`: creates `themis.config.json`, adds `.themis/` to `.gitignore`, and adds `__themis__/reports/` plus `__themis__/shims/` to `.gitignore`.
|
|
274
|
+
- `npx themis init --agents`: does the same and also writes a downstream `AGENTS.md` from the Themis template if the repo does not already have one.
|
|
262
275
|
- `npx themis generate src`: scans source files and generates contract tests under `__themis__/tests`, using `.generated.test.ts` for TS/TSX sources and `.generated.test.js` for JS/JSX sources.
|
|
263
276
|
- `npx themis generate src --json`: emits a machine-readable generation payload for agents and automation.
|
|
264
277
|
- `npx themis generate src --plan`: emits a planning payload and handoff artifact without writing generated tests.
|
|
@@ -270,10 +283,10 @@ Short version:
|
|
|
270
283
|
- `npx themis generate src --changed`: regenerates against changed files in the current git worktree.
|
|
271
284
|
- `npx themis generate src --scenario react-hook --min-confidence high`: targets one adapter family at a confidence threshold.
|
|
272
285
|
- `npx themis generate app --scenario next-route-handler`: focuses generation on Next app router request handlers.
|
|
273
|
-
- `npx themis migrate jest`: scaffolds a Themis config/setup bridge for existing Jest suites and gitignores `.themis/` plus `__themis__/reports/`.
|
|
286
|
+
- `npx themis migrate jest`: scaffolds a Themis config/setup bridge for existing Jest suites and gitignores `.themis/` plus `__themis__/reports/` and `__themis__/shims/`.
|
|
274
287
|
- `npx themis migrate jest --rewrite-imports`: rewrites matched Jest/Vitest/Testing Library imports to a local `themis.compat.js` bridge file.
|
|
275
288
|
- `npx themis migrate jest --convert`: applies codemods for common Jest/Vitest matcher/import patterns so suites move closer to native Themis style.
|
|
276
|
-
- `npx themis migrate vitest`: scaffolds the same bridge for Vitest suites and gitignores `.themis/` plus `__themis__/reports/`.
|
|
289
|
+
- `npx themis migrate vitest`: scaffolds the same bridge for Vitest suites and gitignores `.themis/` plus `__themis__/reports/` and `__themis__/shims/`.
|
|
277
290
|
- `npx themis generate src --require-confidence high`: enforces a quality bar for all selected generated tests.
|
|
278
291
|
- `npx themis generate src --files src/routes/ping.ts`: targets one or more explicit source files.
|
|
279
292
|
- `npx themis generate src --match-source "routes/" --match-export "GET|POST"`: narrows generation by source path and exported symbol.
|
|
@@ -345,6 +358,7 @@ Themis writes artifacts under `.themis/`:
|
|
|
345
358
|
- `.themis/benchmarks/benchmark-last.json`: latest benchmark comparison payload, including migration proof output.
|
|
346
359
|
- `.themis/benchmarks/migration-proof.json`: synthetic migration-conversion proof artifact emitted by `npm run benchmark`.
|
|
347
360
|
- `__themis__/reports/report.html`: interactive HTML verdict report.
|
|
361
|
+
- `__themis__/shims/`: reserved namespace for framework-owned compatibility shims when a fallback file is truly needed. Themis should prefer built-in support first and should not drop ad hoc shim files into `tests/`.
|
|
348
362
|
|
|
349
363
|
`--agent` output includes deterministic failure fingerprints, grouped `analysis.failureClusters`, stability classifications, previous-run comparison data, and a direct generated-test repair hint via `npx themis test --fix`. Fix handoff entries also carry repair strategies, candidate files, and autofix commands for tighter failure-to-fix loops.
|
|
350
364
|
|
|
@@ -510,6 +524,7 @@ Easter egg aliases are also available: `cook`, `yeet`, `vibecheck`, `wipe`.
|
|
|
510
524
|
|
|
511
525
|
Modern JS/TS projects can opt into `environment: "jsdom"` for DOM-driven tests and `setupFiles` for hooks, polyfills, or harness bootstrapping.
|
|
512
526
|
Themis discovers both `testDir` and `generatedTestsDir` by default. Use `testIgnore` only for fixture folders, scratch suites, or other paths you intentionally want to skip.
|
|
527
|
+
Themis also stubs common frontend style and asset imports under Node or jsdom runs, including `.css`, `.scss`, `.png`, `.jpg`, `.svg`, and common font/media files, so repos should not need ad hoc `tests/*.cjs` setup files just to make those imports load.
|
|
513
528
|
|
|
514
529
|
## TypeScript
|
|
515
530
|
|
package/docs/agents-adoption.md
CHANGED
|
@@ -5,8 +5,8 @@ Use this guide when you want another repository to adopt Themis and make that ch
|
|
|
5
5
|
## Install From Scratch
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npm install -D @vitronai/themis
|
|
9
|
-
npx themis init
|
|
8
|
+
npm install -D @vitronai/themis@latest
|
|
9
|
+
npx themis init --agents
|
|
10
10
|
npx themis generate src
|
|
11
11
|
npx themis test
|
|
12
12
|
```
|
|
@@ -14,7 +14,8 @@ npx themis test
|
|
|
14
14
|
What those commands do:
|
|
15
15
|
|
|
16
16
|
- `npm install -D @vitronai/themis`: installs Themis as the repo's unit test framework
|
|
17
|
-
- `npx themis init`: creates `themis.config.json` and adds `.themis
|
|
17
|
+
- `npx themis init`: creates `themis.config.json` and adds `.themis/`, `__themis__/reports/`, and `__themis__/shims/` to `.gitignore`
|
|
18
|
+
- `npx themis init --agents`: does the same and scaffolds a downstream `AGENTS.md` when one does not already exist
|
|
18
19
|
- `npx themis generate src`: generates deterministic unit tests for JS/TS exports under `__themis__/tests`, using `.generated.test.ts` for TS/TSX sources and `.generated.test.js` for JS/JSX sources
|
|
19
20
|
- `npx themis test`: runs the suite
|
|
20
21
|
|
|
@@ -52,8 +53,9 @@ Install with `npm install -D @vitronai/themis`.
|
|
|
52
53
|
Initialize with `npx themis init`.
|
|
53
54
|
Generate tests with `npx themis generate src`.
|
|
54
55
|
Run tests with `npx themis test`.
|
|
55
|
-
Keep `.themis
|
|
56
|
+
Keep `.themis/`, `__themis__/reports/`, and `__themis__/shims/` in `.gitignore`.
|
|
56
57
|
Expect generated tests to land under `__themis__/tests` with `.generated.test.ts` for TS/TSX sources and `.generated.test.js` for JS/JSX sources.
|
|
58
|
+
Do not create ad hoc `tests/*.cjs` or `tests/*.setup.*` files just to load common `.css`, `.png`, `.jpg`, `.svg`, or font/media imports.
|
|
57
59
|
For migrated suites, use `npx themis migrate jest` or `npx themis migrate vitest`.
|
|
58
60
|
Prefer `intent(...)` for behavior and workflow tests.
|
|
59
61
|
Prefer `test(...)` for low-level unit checks.
|
|
@@ -65,5 +67,7 @@ Do not claim Themis is "not a unit test framework".
|
|
|
65
67
|
- Themis is a unit test framework and test generator for Node.js and TypeScript projects.
|
|
66
68
|
- `.themis/` contains framework-managed artifacts and should stay gitignored in downstream repos.
|
|
67
69
|
- `__themis__/reports/` contains generated HTML report output and should stay gitignored in downstream repos.
|
|
70
|
+
- `__themis__/shims/` is the reserved location for framework-owned fallback shims if they are ever needed; Themis should not create ad hoc shim files under `tests/`.
|
|
71
|
+
- Themis handles common style and static asset imports natively; `setupFiles` should be reserved for real harness/bootstrap needs.
|
|
68
72
|
- This repository's own [`AGENTS.md`](../AGENTS.md) is for contributors working on Themis itself. It does not automatically configure external repos.
|
|
69
73
|
- You do not need an MCP server to use Themis from another repo. Clear repo instructions plus the normal CLI commands are enough.
|
package/docs/api.md
CHANGED
|
@@ -10,7 +10,7 @@ Use it in a repo with:
|
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
12
|
npm install -D @vitronai/themis
|
|
13
|
-
npx themis init
|
|
13
|
+
npx themis init --agents
|
|
14
14
|
npx themis generate src
|
|
15
15
|
npx themis test
|
|
16
16
|
```
|
|
@@ -18,6 +18,7 @@ npx themis test
|
|
|
18
18
|
`npx themis generate src` writes generated tests under `__themis__/tests` by default.
|
|
19
19
|
|
|
20
20
|
For downstream repo setup and copyable agent instructions, see [`docs/agents-adoption.md`](agents-adoption.md) and [`templates/AGENTS.themis.md`](../templates/AGENTS.themis.md).
|
|
21
|
+
For machine-readable agent adoption metadata, see [`themis.ai.json`](../themis.ai.json).
|
|
21
22
|
|
|
22
23
|
## CLI
|
|
23
24
|
|
|
@@ -25,7 +26,7 @@ For downstream repo setup and copyable agent instructions, see [`docs/agents-ado
|
|
|
25
26
|
|
|
26
27
|
```bash
|
|
27
28
|
themis test [options]
|
|
28
|
-
themis init
|
|
29
|
+
themis init [--agents]
|
|
29
30
|
themis generate [path]
|
|
30
31
|
themis migrate <jest|vitest>
|
|
31
32
|
```
|
|
@@ -35,7 +36,8 @@ themis migrate <jest|vitest>
|
|
|
35
36
|
Creates:
|
|
36
37
|
|
|
37
38
|
- `themis.config.json` with default settings
|
|
38
|
-
- adds `.themis
|
|
39
|
+
- adds `.themis/`, `__themis__/reports/`, and `__themis__/shims/` to `.gitignore`
|
|
40
|
+
- with `--agents`, also writes a downstream `AGENTS.md` from the bundled Themis template when one does not already exist
|
|
39
41
|
|
|
40
42
|
## `themis test`
|
|
41
43
|
|
|
@@ -349,6 +351,12 @@ The fake timer helpers only patch the current Themis runtime. They do not mutate
|
|
|
349
351
|
| `htmlReportPath` | string | `"__themis__/reports/report.html"` | Default output path for `--reporter html` when `--html-output` is not provided. |
|
|
350
352
|
| `testIgnore` | `string[]` | `[]` | Regex strings matched against repo-relative paths during discovery. Matching files and directories are skipped. |
|
|
351
353
|
|
|
354
|
+
Runtime loader note:
|
|
355
|
+
|
|
356
|
+
- Themis handles common frontend style imports (`.css`, `.scss`, `.sass`, `.less`, `.styl`, `.pcss`) and common static assets (`.png`, `.jpg`, `.jpeg`, `.gif`, `.webp`, `.avif`, `.bmp`, `.ico`, `.svg`, font/media files) without extra setup.
|
|
357
|
+
- Use `setupFiles` for actual harness bootstrapping, not as a workaround for CSS or image imports.
|
|
358
|
+
- If Themis ever needs to emit a framework-owned fallback shim file, that file belongs under `__themis__/shims/`, not under `tests/`.
|
|
359
|
+
|
|
352
360
|
## Programmatic API
|
|
353
361
|
|
|
354
362
|
Import:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vitronai/themis",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"description": "Intent-first unit test framework and test generator for AI agents in Node.js and TypeScript",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Vitron AI",
|
|
@@ -70,6 +70,7 @@
|
|
|
70
70
|
"index.d.ts",
|
|
71
71
|
"globals.js",
|
|
72
72
|
"globals.d.ts",
|
|
73
|
+
"themis.ai.json",
|
|
73
74
|
"README.md",
|
|
74
75
|
"CHANGELOG.md",
|
|
75
76
|
"LICENSE",
|
package/src/cli.js
CHANGED
|
@@ -21,8 +21,16 @@ async function main(argv) {
|
|
|
21
21
|
const cwd = process.cwd();
|
|
22
22
|
|
|
23
23
|
if (command === 'init') {
|
|
24
|
-
|
|
24
|
+
const initFlags = parseInitFlags(argv.slice(1));
|
|
25
|
+
const initResult = runInit(cwd, initFlags);
|
|
25
26
|
console.log('Themis initialized. Next: npx themis generate src && npx themis test');
|
|
27
|
+
if (initFlags.agents) {
|
|
28
|
+
if (initResult && initResult.path && initResult.created) {
|
|
29
|
+
console.log(`Agents: created ${formatCliPath(cwd, initResult.path)} from the Themis downstream template.`);
|
|
30
|
+
} else {
|
|
31
|
+
console.log('Agents: skipped AGENTS.md scaffold because one already exists.');
|
|
32
|
+
}
|
|
33
|
+
}
|
|
26
34
|
return;
|
|
27
35
|
}
|
|
28
36
|
|
|
@@ -79,7 +87,7 @@ async function main(argv) {
|
|
|
79
87
|
console.log(`Scripts: updated ${formatCliPath(cwd, result.packageJsonPath)} with test:themis`);
|
|
80
88
|
}
|
|
81
89
|
if (result.gitignoreUpdated) {
|
|
82
|
-
console.log(`Gitignore: updated ${formatCliPath(cwd, result.gitignorePath)} with .themis/ and __themis__/
|
|
90
|
+
console.log(`Gitignore: updated ${formatCliPath(cwd, result.gitignorePath)} with .themis/, __themis__/reports/, and __themis__/shims/`);
|
|
83
91
|
}
|
|
84
92
|
if (result.rewriteImports) {
|
|
85
93
|
console.log(`Imports: rewrote ${result.rewrittenFiles.length} file(s) to local Themis compatibility imports.`);
|
|
@@ -509,6 +517,25 @@ function parseMigrateFlags(args) {
|
|
|
509
517
|
return flags;
|
|
510
518
|
}
|
|
511
519
|
|
|
520
|
+
function parseInitFlags(args) {
|
|
521
|
+
const flags = {
|
|
522
|
+
agents: false
|
|
523
|
+
};
|
|
524
|
+
|
|
525
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
526
|
+
const token = args[i];
|
|
527
|
+
if (token === '--agents') {
|
|
528
|
+
flags.agents = true;
|
|
529
|
+
continue;
|
|
530
|
+
}
|
|
531
|
+
if (token.startsWith('-')) {
|
|
532
|
+
throw new Error(`Unsupported init option: ${token}`);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
return flags;
|
|
537
|
+
}
|
|
538
|
+
|
|
512
539
|
function parseGenerateFlags(args) {
|
|
513
540
|
const flags = {};
|
|
514
541
|
|
|
@@ -710,7 +737,7 @@ function resolveStabilityRuns(value) {
|
|
|
710
737
|
function printUsage() {
|
|
711
738
|
console.log('Usage: themis <command> [options]');
|
|
712
739
|
console.log('Commands:');
|
|
713
|
-
console.log(' init
|
|
740
|
+
console.log(' init [--agents] Create themis.config.json, gitignore framework paths, and optionally scaffold AGENTS.md');
|
|
714
741
|
console.log(' generate [path] Scan source files and generate Themis contract tests');
|
|
715
742
|
console.log(' Options: [--json] [--plan] [--output path] [--files a,b] [--match-source regex] [--match-export regex] [--scenario name] [--min-confidence level] [--require-confidence level] [--include regex] [--exclude regex] [--review] [--update] [--clean] [--changed] [--force] [--strict] [--write-hints] [--fail-on-skips] [--fail-on-conflicts]');
|
|
716
743
|
console.log(' scan [path] Alias for generate');
|
package/src/init.js
CHANGED
|
@@ -1,9 +1,36 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
1
3
|
const { initConfig } = require('./config');
|
|
2
4
|
const { ensureGitignoreEntries } = require('./gitignore');
|
|
3
5
|
|
|
4
|
-
|
|
6
|
+
const AGENTS_TEMPLATE_PATH = path.join(__dirname, '..', 'templates', 'AGENTS.themis.md');
|
|
7
|
+
|
|
8
|
+
function runInit(cwd, options = {}) {
|
|
5
9
|
initConfig(cwd);
|
|
6
|
-
ensureGitignoreEntries(cwd, ['.themis/', '__themis__/reports/']);
|
|
10
|
+
ensureGitignoreEntries(cwd, ['.themis/', '__themis__/reports/', '__themis__/shims/']);
|
|
11
|
+
|
|
12
|
+
if (options.agents) {
|
|
13
|
+
return ensureAgentsTemplate(cwd);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function ensureAgentsTemplate(cwd) {
|
|
20
|
+
const targetPath = path.join(cwd, 'AGENTS.md');
|
|
21
|
+
if (fs.existsSync(targetPath)) {
|
|
22
|
+
return {
|
|
23
|
+
path: targetPath,
|
|
24
|
+
created: false
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const source = fs.readFileSync(AGENTS_TEMPLATE_PATH, 'utf8');
|
|
29
|
+
fs.writeFileSync(targetPath, source, 'utf8');
|
|
30
|
+
return {
|
|
31
|
+
path: targetPath,
|
|
32
|
+
created: true
|
|
33
|
+
};
|
|
7
34
|
}
|
|
8
35
|
|
|
9
36
|
module.exports = {
|
package/src/migrate.js
CHANGED
|
@@ -45,7 +45,7 @@ function runMigrate(cwd, framework, options = {}) {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
fs.writeFileSync(configPath, `${JSON.stringify(nextConfig, null, 2)}\n`, 'utf8');
|
|
48
|
-
const gitignore = ensureGitignoreEntries(projectRoot, ['.themis/', '__themis__/reports/']);
|
|
48
|
+
const gitignore = ensureGitignoreEntries(projectRoot, ['.themis/', '__themis__/reports/', '__themis__/shims/']);
|
|
49
49
|
|
|
50
50
|
let packageUpdated = false;
|
|
51
51
|
if (fs.existsSync(packageJsonPath)) {
|
package/src/module-loader.js
CHANGED
|
@@ -4,6 +4,28 @@ const Module = require('module');
|
|
|
4
4
|
|
|
5
5
|
const SUPPORTED_SOURCE_EXTENSIONS = ['.js', '.jsx', '.ts', '.tsx'];
|
|
6
6
|
const RESOLVABLE_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.json'];
|
|
7
|
+
const STYLE_IMPORT_EXTENSIONS = new Set(['.css', '.scss', '.sass', '.less', '.styl', '.pcss']);
|
|
8
|
+
const ASSET_IMPORT_EXTENSIONS = new Set([
|
|
9
|
+
'.png',
|
|
10
|
+
'.jpg',
|
|
11
|
+
'.jpeg',
|
|
12
|
+
'.gif',
|
|
13
|
+
'.webp',
|
|
14
|
+
'.avif',
|
|
15
|
+
'.bmp',
|
|
16
|
+
'.ico',
|
|
17
|
+
'.svg',
|
|
18
|
+
'.mp4',
|
|
19
|
+
'.webm',
|
|
20
|
+
'.mp3',
|
|
21
|
+
'.wav',
|
|
22
|
+
'.ogg',
|
|
23
|
+
'.woff',
|
|
24
|
+
'.woff2',
|
|
25
|
+
'.ttf',
|
|
26
|
+
'.otf',
|
|
27
|
+
'.eot'
|
|
28
|
+
]);
|
|
7
29
|
const THEMIS_CONTRACT_RUNTIME_REQUEST = '@vitronai/themis/contract-runtime';
|
|
8
30
|
const THEMIS_CONTRACT_RUNTIME_PATH = path.join(__dirname, 'contract-runtime.js');
|
|
9
31
|
const DEFAULT_TS_COMPILER_OPTIONS = {
|
|
@@ -102,6 +124,23 @@ function createModuleLoader(options = {}) {
|
|
|
102
124
|
return materializeMock(mockRegistry.get(resolvedRequest));
|
|
103
125
|
}
|
|
104
126
|
|
|
127
|
+
if (shouldStubStyleImport(resolvedRequest, projectRoot)) {
|
|
128
|
+
return createStyleModuleStub();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (shouldStubAssetImport(resolvedRequest, projectRoot)) {
|
|
132
|
+
return createAssetModuleStub(resolvedRequest);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (shouldRejectUnsupportedProjectImport(resolvedRequest, projectRoot)) {
|
|
136
|
+
const extension = path.extname(resolvedRequest).toLowerCase();
|
|
137
|
+
throw new Error(
|
|
138
|
+
`Unsupported project import extension "${extension}" for ${formatProjectPath(projectRoot, resolvedRequest)}. ` +
|
|
139
|
+
'Themis handles JS/TS, JSON, common style imports, and common static assets natively. ' +
|
|
140
|
+
'Prefer extending Themis support over creating ad hoc test setup files.'
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
105
144
|
return originalLoad.call(this, resolvedRequest, parent, isMain);
|
|
106
145
|
};
|
|
107
146
|
|
|
@@ -478,6 +517,106 @@ function isBuiltinRequest(request) {
|
|
|
478
517
|
return Module.builtinModules.includes(request);
|
|
479
518
|
}
|
|
480
519
|
|
|
520
|
+
function shouldStubStyleImport(resolvedRequest, projectRoot) {
|
|
521
|
+
return shouldHandleProjectResolvedFile(resolvedRequest, projectRoot, STYLE_IMPORT_EXTENSIONS);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
function shouldStubAssetImport(resolvedRequest, projectRoot) {
|
|
525
|
+
return shouldHandleProjectResolvedFile(resolvedRequest, projectRoot, ASSET_IMPORT_EXTENSIONS);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
function shouldRejectUnsupportedProjectImport(resolvedRequest, projectRoot) {
|
|
529
|
+
if (!shouldHandleProjectFile(resolvedRequest, projectRoot)) {
|
|
530
|
+
return false;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
if (!fs.existsSync(resolvedRequest) || !fs.statSync(resolvedRequest).isFile()) {
|
|
534
|
+
return false;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
const extension = path.extname(resolvedRequest).toLowerCase();
|
|
538
|
+
if (!extension) {
|
|
539
|
+
return false;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
return !SUPPORTED_SOURCE_EXTENSIONS.includes(extension)
|
|
543
|
+
&& extension !== '.json'
|
|
544
|
+
&& !STYLE_IMPORT_EXTENSIONS.has(extension)
|
|
545
|
+
&& !ASSET_IMPORT_EXTENSIONS.has(extension);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
function shouldHandleProjectResolvedFile(resolvedRequest, projectRoot, extensions) {
|
|
549
|
+
if (!shouldHandleProjectFile(resolvedRequest, projectRoot)) {
|
|
550
|
+
return false;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
if (!fs.existsSync(resolvedRequest) || !fs.statSync(resolvedRequest).isFile()) {
|
|
554
|
+
return false;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
return extensions.has(path.extname(resolvedRequest).toLowerCase());
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
function formatProjectPath(projectRoot, filePath) {
|
|
561
|
+
return path.relative(projectRoot, filePath).split(path.sep).join('/');
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
function createStyleModuleStub() {
|
|
565
|
+
const styleModule = {};
|
|
566
|
+
const proxy = new Proxy(styleModule, {
|
|
567
|
+
get(_target, property) {
|
|
568
|
+
if (property === '__esModule') {
|
|
569
|
+
return true;
|
|
570
|
+
}
|
|
571
|
+
if (property === 'default') {
|
|
572
|
+
return proxy;
|
|
573
|
+
}
|
|
574
|
+
if (property === 'toJSON') {
|
|
575
|
+
return () => ({});
|
|
576
|
+
}
|
|
577
|
+
if (property === Symbol.toPrimitive) {
|
|
578
|
+
return () => '';
|
|
579
|
+
}
|
|
580
|
+
if (typeof property === 'string') {
|
|
581
|
+
return property;
|
|
582
|
+
}
|
|
583
|
+
return undefined;
|
|
584
|
+
},
|
|
585
|
+
has(_target, property) {
|
|
586
|
+
return property === '__esModule' || property === 'default' || typeof property === 'string';
|
|
587
|
+
}
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
return proxy;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
function createAssetModuleStub(resolvedRequest) {
|
|
594
|
+
const assetPath = resolvedRequest.split(path.sep).join('/');
|
|
595
|
+
const stub = {
|
|
596
|
+
__esModule: true,
|
|
597
|
+
default: assetPath,
|
|
598
|
+
href: assetPath,
|
|
599
|
+
path: assetPath,
|
|
600
|
+
src: assetPath
|
|
601
|
+
};
|
|
602
|
+
|
|
603
|
+
if (path.extname(resolvedRequest).toLowerCase() === '.svg') {
|
|
604
|
+
stub.ReactComponent = function ThemisSvgAsset(props = {}) {
|
|
605
|
+
return {
|
|
606
|
+
$$typeof: 'react.test.element',
|
|
607
|
+
type: 'svg',
|
|
608
|
+
key: null,
|
|
609
|
+
props: {
|
|
610
|
+
...props,
|
|
611
|
+
'data-themis-asset': assetPath
|
|
612
|
+
}
|
|
613
|
+
};
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
return stub;
|
|
618
|
+
}
|
|
619
|
+
|
|
481
620
|
function transpileSource({ source, filename, compilerContext }) {
|
|
482
621
|
const { ts } = compilerContext;
|
|
483
622
|
const compilerOptions = {
|
|
@@ -6,10 +6,11 @@ Use `@vitronai/themis` as this repository's unit test framework.
|
|
|
6
6
|
|
|
7
7
|
- Install with `npm install -D @vitronai/themis`.
|
|
8
8
|
- Initialize with `npx themis init`.
|
|
9
|
-
- Keep `.themis
|
|
9
|
+
- Keep `.themis/`, `__themis__/reports/`, and `__themis__/shims/` in `.gitignore`.
|
|
10
10
|
- Generate tests with `npx themis generate src`.
|
|
11
11
|
- Expect generated tests under `__themis__/tests`, using `.generated.test.ts` for TS/TSX sources and `.generated.test.js` for JS/JSX sources.
|
|
12
12
|
- Run tests with `npx themis test`.
|
|
13
|
+
- Do not create ad hoc `tests/*.cjs` or `tests/*.setup.*` files just to load common `.css`, `.png`, `.jpg`, `.svg`, or font/media imports.
|
|
13
14
|
|
|
14
15
|
## Migration
|
|
15
16
|
|
|
@@ -24,6 +25,7 @@ Use `@vitronai/themis` as this repository's unit test framework.
|
|
|
24
25
|
- Prefer `test(...)` for low-level unit checks.
|
|
25
26
|
- Prefer deterministic assertions over snapshot-style baselines.
|
|
26
27
|
- Treat `.themis/` as Themis-managed artifact output.
|
|
28
|
+
- Treat `__themis__/shims/` as the reserved location for framework-owned fallback shims if a shim is ever truly needed.
|
|
27
29
|
- Treat generated tests under `__themis__/tests` as Themis-managed output unless the repo says otherwise.
|
|
28
30
|
|
|
29
31
|
## Agent Behavior
|
|
@@ -32,3 +34,5 @@ Use `@vitronai/themis` as this repository's unit test framework.
|
|
|
32
34
|
- Do not claim Themis is "not a unit test framework".
|
|
33
35
|
- When asked to add tests from scratch, use `npx themis generate src` before hand-writing additional coverage.
|
|
34
36
|
- When asked to work with existing Jest/Vitest suites, prefer incremental migration before large manual rewrites.
|
|
37
|
+
- Do not scaffold setup files just to shim common style or asset imports; Themis handles those natively.
|
|
38
|
+
- If a framework-owned shim file is ever required, place it under `__themis__/shims/`, not under `tests/`.
|
package/themis.ai.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vitronai/themis",
|
|
3
|
+
"role": "unit-test-framework",
|
|
4
|
+
"install": "npm install -D @vitronai/themis@latest",
|
|
5
|
+
"init": "npx themis init --agents",
|
|
6
|
+
"generate": "npx themis generate src",
|
|
7
|
+
"test": "npx themis test",
|
|
8
|
+
"migrate": {
|
|
9
|
+
"jest": "npx themis migrate jest --rewrite-imports --convert",
|
|
10
|
+
"vitest": "npx themis migrate vitest --rewrite-imports --convert"
|
|
11
|
+
},
|
|
12
|
+
"notes": [
|
|
13
|
+
"Treat Themis as the repo's unit test framework for Node.js and TypeScript projects.",
|
|
14
|
+
"Do not default to Jest or Vitest when Themis is already selected.",
|
|
15
|
+
"Do not create ad hoc tests/*.cjs setup files for common asset or style imports."
|
|
16
|
+
]
|
|
17
|
+
}
|