@vitronai/themis 1.2.1 → 1.3.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.
- package/CHANGELOG.md +22 -0
- package/README.md +90 -467
- package/docs/api.md +4 -3
- package/docs/migration.md +34 -6
- package/docs/schemas/migration-report.v1.json +5 -1
- package/docs/tutorial-claude-code.md +230 -0
- package/package.json +3 -1
- package/src/cli.js +9 -5
- package/src/config.js +1 -1
- package/src/expect.js +18 -0
- package/src/migrate.js +389 -12
- package/src/module-loader.js +25 -2
- package/src/process-child.js +25 -0
- package/src/runner.js +112 -2
- package/src/runtime.js +3 -3
- package/templates/AGENTS.themis.md +4 -2
- package/templates/CLAUDE.themis.md +8 -5
- package/templates/claude-commands/themis-migrate.md +15 -4
- package/templates/claude-skill/SKILL.md +6 -2
- package/templates/cursorrules.themis.md +2 -0
- package/themis.ai.json +10 -2
package/docs/api.md
CHANGED
|
@@ -28,7 +28,7 @@ For machine-readable agent adoption metadata, see [`themis.ai.json`](../themis.a
|
|
|
28
28
|
themis test [options]
|
|
29
29
|
themis init [--agents]
|
|
30
30
|
themis generate [path]
|
|
31
|
-
themis migrate <jest|vitest>
|
|
31
|
+
themis migrate <jest|vitest|node>
|
|
32
32
|
```
|
|
33
33
|
|
|
34
34
|
## `themis init`
|
|
@@ -203,7 +203,7 @@ Migration options:
|
|
|
203
203
|
| `--reporter spec\|next\|json\|agent\|html` | string | Explicit reporter override. |
|
|
204
204
|
| `--workers <N>` | positive integer | Override worker count. Invalid values fail fast. |
|
|
205
205
|
| `--environment node\|jsdom` | string | Override the configured test environment. |
|
|
206
|
-
| `--isolation worker\|in-process` | string | Select worker
|
|
206
|
+
| `--isolation worker\|in-process\|process` | string | Select isolation model. `worker` (default) = worker thread per file. `in-process` = sequential in the parent (fastest reruns; shares ESM cache + process state across files). `process` = `child_process.fork` per file, mirroring `node --test` (use when tests mutate `process.env`/`process.cwd()` at module load). |
|
|
207
207
|
| `--cache` | flag | Enable file-level result caching for in-process local loops. |
|
|
208
208
|
| `--update-contracts` | flag | Accept updated `captureContract(...)` baselines for the selected tests. |
|
|
209
209
|
| `-w`, `--watch` | flag | Rerun the selected suite when watched project files change. |
|
|
@@ -219,7 +219,7 @@ Migration compatibility:
|
|
|
219
219
|
- imports from `@jest/globals` are supported at runtime
|
|
220
220
|
- imports from `vitest` are supported at runtime
|
|
221
221
|
- imports from `@testing-library/react` are supported via Themis `render`, `screen`, `fireEvent`, `waitFor`, `cleanup`, and `act`
|
|
222
|
-
- `themis migrate <jest|vitest>` also emits `.themis/migration/migration-report.json` with detected files, migration mode details, assistant findings, and recommended next actions
|
|
222
|
+
- `themis migrate <jest|vitest|node>` also emits `.themis/migration/migration-report.json` with detected files, migration mode details, assistant findings, and recommended next actions
|
|
223
223
|
|
|
224
224
|
Additional option:
|
|
225
225
|
|
|
@@ -229,6 +229,7 @@ Execution note:
|
|
|
229
229
|
|
|
230
230
|
- `--watch --isolation in-process --cache` is the fastest local rerun mode
|
|
231
231
|
- `--isolation worker` remains the safer mode for CI and global-heavy suites
|
|
232
|
+
- `--isolation process` is required for tests that mutate `process.env`/`process.cwd()` at module load (matches `node --test`'s isolation model)
|
|
232
233
|
- `--watch` is intended for short edit-run-review loops for both humans and AI agents
|
|
233
234
|
|
|
234
235
|
Snapshot note:
|
package/docs/migration.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Migrating From Jest And
|
|
1
|
+
# Migrating From Jest, Vitest, And node:test
|
|
2
2
|
|
|
3
3
|
Themis is designed for incremental migration. Start by running existing suites under the Themis runtime, then convert touched tests toward native contracts and `intent(...)` flows as you work.
|
|
4
4
|
|
|
@@ -12,14 +12,42 @@ npx themis migrate jest --assist
|
|
|
12
12
|
npx themis test
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
Use `vitest`
|
|
15
|
+
Use `vitest` for Vitest suites or `node` for `node:test` suites in place of `jest`.
|
|
16
16
|
|
|
17
17
|
## Migration modes
|
|
18
18
|
|
|
19
|
-
- `themis migrate <jest|vitest>`: scaffold config
|
|
20
|
-
- `--rewrite-imports`: point framework imports at `themis.compat.js
|
|
21
|
-
- `--convert`: remove common
|
|
22
|
-
- `--assist`: run the safe rewrite and conversion passes together, then report leftover
|
|
19
|
+
- `themis migrate <jest|vitest|node>`: scaffold config and migration report. For `jest`/`vitest`, also writes a setup file and a compat bridge; `node` skips both (Themis provides the same globals natively).
|
|
20
|
+
- `--rewrite-imports`: point framework imports at `themis.compat.js` (jest/vitest only — `node` source has no compat shim, conversion is direct).
|
|
21
|
+
- `--convert`: remove common framework imports and rewrite matcher/test patterns into Themis-native forms.
|
|
22
|
+
- `--assist`: run the safe rewrite and conversion passes together, then report leftover framework-specific helpers that still need manual follow-up.
|
|
23
|
+
|
|
24
|
+
## node:test specifics
|
|
25
|
+
|
|
26
|
+
`themis migrate node` handles the following transforms:
|
|
27
|
+
|
|
28
|
+
| Input (node:test + node:assert/strict) | Output (Themis) |
|
|
29
|
+
| --- | --- |
|
|
30
|
+
| `import test from 'node:test'` | dropped (`test` is a Themis global) |
|
|
31
|
+
| `import assert from 'node:assert/strict'` | dropped (`expect` replaces all asserts) |
|
|
32
|
+
| `assert.equal(a, b)` / `strictEqual` | `expect(a).toBe(b)` |
|
|
33
|
+
| `assert.deepEqual(a, b)` / `deepStrictEqual` | `expect(a).toEqual(b)` |
|
|
34
|
+
| `assert.ok(v)` | `expect(v).toBeTruthy()` |
|
|
35
|
+
| `assert.match(s, /re/)` | `expect(s).toMatch(/re/)` |
|
|
36
|
+
| `await assert.rejects(fn, /re/)` | async try/catch wrapper + `toMatch` on the error message |
|
|
37
|
+
| `test.after(fn)` / `test.afterEach(fn)` | `afterAll(fn)` / `afterEach(fn)` |
|
|
38
|
+
| `test(name, { timeout }, fn)` | `test(name, fn)` (options arg silently dropped) |
|
|
39
|
+
|
|
40
|
+
Not supported in this pass: `t.test()` subtests, `t.context`, `test.only`, `describe`/`it` exported from `node:test` (use Themis globals instead), `assert.throws`/`notEqual`/`fail`/`doesNotReject`, source-map line preservation. The optional 3rd-arg message string on `assert.equal`-family calls is silently dropped.
|
|
41
|
+
|
|
42
|
+
## Process-state isolation
|
|
43
|
+
|
|
44
|
+
`node:test` runs each test file in its own child process. If your suite mutates `process.env`, `process.cwd()`, or other process-level state at module load (e.g. `process.env.HOME = mkdtempSync(...)` before `await import('../dist/index.js')`), pair `themis test` with per-file process isolation:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npx themis test --isolation process
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
This spawns a fresh Node child process per test file via `child_process.fork`, mirroring `node --test`'s isolation model. The default `worker` mode shares process-state (especially `os.homedir()` cached at worker startup) across files and will surface as cross-file leakage for state-mutating tests.
|
|
23
51
|
|
|
24
52
|
## Before And After
|
|
25
53
|
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
},
|
|
13
13
|
"source": {
|
|
14
14
|
"type": "string",
|
|
15
|
-
"enum": ["jest", "vitest"]
|
|
15
|
+
"enum": ["jest", "vitest", "node"]
|
|
16
16
|
},
|
|
17
17
|
"createdAt": {
|
|
18
18
|
"type": "string"
|
|
@@ -35,6 +35,8 @@
|
|
|
35
35
|
"jestGlobals",
|
|
36
36
|
"vitest",
|
|
37
37
|
"testingLibraryReact",
|
|
38
|
+
"nodeTest",
|
|
39
|
+
"nodeAssert",
|
|
38
40
|
"rewrittenFiles",
|
|
39
41
|
"rewrittenImports",
|
|
40
42
|
"convertedFiles",
|
|
@@ -50,6 +52,8 @@
|
|
|
50
52
|
"jestGlobals": { "type": "number" },
|
|
51
53
|
"vitest": { "type": "number" },
|
|
52
54
|
"testingLibraryReact": { "type": "number" },
|
|
55
|
+
"nodeTest": { "type": "number" },
|
|
56
|
+
"nodeAssert": { "type": "number" },
|
|
53
57
|
"rewrittenFiles": { "type": "number" },
|
|
54
58
|
"rewrittenImports": { "type": "number" },
|
|
55
59
|
"convertedFiles": { "type": "number" },
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
# Testing With Claude Code and Themis
|
|
2
|
+
|
|
3
|
+
A step-by-step walkthrough showing how Themis turns Claude Code into a test-writing machine that gets it right on the first try.
|
|
4
|
+
|
|
5
|
+
## The Problem
|
|
6
|
+
|
|
7
|
+
When you ask Claude Code to write unit tests, it reaches for Jest or Vitest by default. The tests it generates are often correct, but just as often they have subtle issues: wrong import paths, misused mocking APIs, snapshot tests where assertions would be better, setup files where the framework handles things natively. You end up in an edit-test-fix loop that burns time and context window.
|
|
8
|
+
|
|
9
|
+
Themis fixes this by shipping structured guidance directly to Claude Code — a skill, slash commands, and a `CLAUDE.md` that tells Claude exactly how to write, run, and fix tests. No copy-pasting docs. No explaining the framework. Claude just knows.
|
|
10
|
+
|
|
11
|
+
## What You'll See
|
|
12
|
+
|
|
13
|
+
By the end of this tutorial you'll have:
|
|
14
|
+
|
|
15
|
+
1. A Node.js project with Themis installed and Claude Code fully wired up
|
|
16
|
+
2. Generated tests that pass on the first run
|
|
17
|
+
3. A structured failure-fix loop where Claude reads machine-parseable repair hints instead of raw stack traces
|
|
18
|
+
4. Slash commands (`/themis-test`, `/themis-generate`, `/themis-fix`) that work out of the box
|
|
19
|
+
|
|
20
|
+
## Step 1: Set Up a Project
|
|
21
|
+
|
|
22
|
+
Start with any Node.js or TypeScript project. For this tutorial we'll use a small utility library.
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
mkdir demo-project && cd demo-project
|
|
26
|
+
npm init -y
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Create a source file at `src/cart.js`:
|
|
30
|
+
|
|
31
|
+
```js
|
|
32
|
+
class Cart {
|
|
33
|
+
constructor() {
|
|
34
|
+
this.items = [];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
add(item) {
|
|
38
|
+
if (!item || !item.name || typeof item.price !== 'number') {
|
|
39
|
+
throw new TypeError('Item must have a name and a numeric price');
|
|
40
|
+
}
|
|
41
|
+
const existing = this.items.find((i) => i.name === item.name);
|
|
42
|
+
if (existing) {
|
|
43
|
+
existing.quantity += item.quantity || 1;
|
|
44
|
+
} else {
|
|
45
|
+
this.items.push({ ...item, quantity: item.quantity || 1 });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
remove(name) {
|
|
50
|
+
const index = this.items.findIndex((i) => i.name === name);
|
|
51
|
+
if (index === -1) throw new Error(`Item "${name}" not in cart`);
|
|
52
|
+
this.items.splice(index, 1);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
total() {
|
|
56
|
+
return this.items.reduce((sum, item) => sum + item.price * item.quantity, 0);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
checkout(paymentMethod) {
|
|
60
|
+
if (this.items.length === 0) throw new Error('Cannot checkout an empty cart');
|
|
61
|
+
const receipt = {
|
|
62
|
+
items: this.items.map((i) => ({ ...i })),
|
|
63
|
+
total: this.total(),
|
|
64
|
+
paymentMethod,
|
|
65
|
+
timestamp: new Date().toISOString()
|
|
66
|
+
};
|
|
67
|
+
this.items = [];
|
|
68
|
+
return receipt;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
module.exports = { Cart };
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Step 2: Install Themis With Claude Code Integration
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
npm install -D @vitronai/themis@latest
|
|
79
|
+
npx themis init --claude-code
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
That one command installs:
|
|
83
|
+
|
|
84
|
+
- `CLAUDE.md` — adoption rules at the repo root that Claude Code reads automatically
|
|
85
|
+
- `.claude/skills/themis/SKILL.md` — a skill that auto-loads when Claude sees a test-related request
|
|
86
|
+
- `.claude/commands/themis-test.md` — `/themis-test` slash command
|
|
87
|
+
- `.claude/commands/themis-generate.md` — `/themis-generate` slash command
|
|
88
|
+
- `.claude/commands/themis-migrate.md` — `/themis-migrate` slash command
|
|
89
|
+
- `.claude/commands/themis-fix.md` — `/themis-fix` slash command
|
|
90
|
+
|
|
91
|
+
You can verify:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
cat CLAUDE.md # Themis adoption rules
|
|
95
|
+
ls .claude/skills/ # themis/SKILL.md
|
|
96
|
+
ls .claude/commands/ # four slash command files
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Step 3: Generate Tests
|
|
100
|
+
|
|
101
|
+
Open Claude Code in the project and type:
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
/themis-generate src
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Claude uses the installed skill context to run `npx themis generate src`. Generated tests land under `__themis__/tests/` as `.generated.test.js` files. These are deterministic, contract-style tests — not LLM-generated guesses.
|
|
108
|
+
|
|
109
|
+
## Step 4: Run the Test Loop
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
/themis-test
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
This runs `npx themis test --reporter agent` and Claude reads the structured JSON output. If everything passes, you're done. If there are failures, Claude sees:
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
{
|
|
119
|
+
"failures": [
|
|
120
|
+
{
|
|
121
|
+
"cluster": "cart-checkout-validation",
|
|
122
|
+
"repairHints": ["checkout() throws when cart is empty — test passes an empty cart but expects success"],
|
|
123
|
+
"sourceFile": "src/cart.js",
|
|
124
|
+
"lineNumber": 32,
|
|
125
|
+
"expected": "Error: Cannot checkout an empty cart",
|
|
126
|
+
"actual": "{ items: [], total: 0 }"
|
|
127
|
+
}
|
|
128
|
+
]
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Instead of re-reading a raw stack trace, Claude acts on the `repairHints` directly. This is the key difference: structured signals instead of unstructured error output.
|
|
133
|
+
|
|
134
|
+
## Step 5: Ask Claude to Write More Tests
|
|
135
|
+
|
|
136
|
+
Now ask Claude to add coverage for edge cases:
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
Write additional tests for the Cart class covering:
|
|
140
|
+
- adding duplicate items increments quantity
|
|
141
|
+
- removing a non-existent item throws
|
|
142
|
+
- checkout clears the cart
|
|
143
|
+
- total with no items returns 0
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Because the Themis skill is loaded, Claude will:
|
|
147
|
+
|
|
148
|
+
1. Use `intent(...)` for behavior tests and `test(...)` for pure unit checks
|
|
149
|
+
2. Follow the four-phase shape: context, run, verify, cleanup
|
|
150
|
+
3. Use `expect(...)` assertions (not snapshots)
|
|
151
|
+
4. Place tests alongside the generated ones, not in a random `tests/` directory
|
|
152
|
+
|
|
153
|
+
Run `/themis-test` again to verify.
|
|
154
|
+
|
|
155
|
+
## Step 6: Fix Failures (When They Happen)
|
|
156
|
+
|
|
157
|
+
If any test fails, use:
|
|
158
|
+
|
|
159
|
+
```
|
|
160
|
+
/themis-fix
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Claude will:
|
|
164
|
+
|
|
165
|
+
1. Run `npx themis test --reporter agent` to get the current failures
|
|
166
|
+
2. Group failures by `cluster` — fixes within a cluster share a root cause
|
|
167
|
+
3. Read `repairHints` before looking at the stack trace
|
|
168
|
+
4. Apply the smallest fix that addresses the root cause
|
|
169
|
+
5. Re-run with `--rerun-failed` to confirm the fix without running the full suite
|
|
170
|
+
|
|
171
|
+
This cluster-based fixing is faster than fixing tests one at a time, and the `--rerun-failed` flag means you don't pay the cost of a full suite run after each fix.
|
|
172
|
+
|
|
173
|
+
## Step 7: Optional — Wire Up the Automated Hook
|
|
174
|
+
|
|
175
|
+
For the tightest possible loop, add a PostToolUse hook that runs Themis automatically after every edit Claude makes:
|
|
176
|
+
|
|
177
|
+
Add this to `.claude/settings.json`:
|
|
178
|
+
|
|
179
|
+
```json
|
|
180
|
+
{
|
|
181
|
+
"hooks": {
|
|
182
|
+
"PostToolUse": [
|
|
183
|
+
{
|
|
184
|
+
"matcher": "Edit|Write|MultiEdit",
|
|
185
|
+
"hooks": [
|
|
186
|
+
{
|
|
187
|
+
"type": "command",
|
|
188
|
+
"command": "node node_modules/@vitronai/themis/scripts/claude-hook.js"
|
|
189
|
+
}
|
|
190
|
+
]
|
|
191
|
+
}
|
|
192
|
+
]
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Now every time Claude edits a `.js`/`.ts`/`.jsx`/`.tsx` file, Themis runs automatically. If tests fail, the structured failure JSON is fed back into the conversation — Claude sees it immediately and can fix it in the next turn without you running anything.
|
|
198
|
+
|
|
199
|
+
The hook is smart about scope:
|
|
200
|
+
|
|
201
|
+
- Skips non-source edits (docs, config, etc.)
|
|
202
|
+
- Uses `--rerun-failed` when there's a prior failure artifact
|
|
203
|
+
- Exits silently when tests pass (no context noise)
|
|
204
|
+
- Set `THEMIS_HOOK_DISABLED=1` to pause it temporarily
|
|
205
|
+
|
|
206
|
+
## Why This Works
|
|
207
|
+
|
|
208
|
+
The magic is not in Themis being a better test runner (though it is faster). The magic is in the **structured agent context**:
|
|
209
|
+
|
|
210
|
+
1. **The skill** tells Claude exactly when and how to use Themis — it auto-loads without you mentioning the framework
|
|
211
|
+
2. **The CLAUDE.md** provides rules about what to avoid (no setup shims, no snapshots as defaults, no ad-hoc test directories)
|
|
212
|
+
3. **The `--reporter agent` output** gives Claude machine-parseable failure data with repair hints, instead of raw stack traces it has to re-parse
|
|
213
|
+
4. **The slash commands** encode the correct workflow so Claude doesn't have to figure out which flags to pass
|
|
214
|
+
|
|
215
|
+
In Tessl evaluations across 10 scenarios, agents scored **37% without** the Themis skill context and **97% with it**. The context is the product.
|
|
216
|
+
|
|
217
|
+
## What's Next
|
|
218
|
+
|
|
219
|
+
- **Migrate from Jest or Vitest**: Run `/themis-migrate` — Claude walks through the four-step incremental migration
|
|
220
|
+
- **Cursor users**: Run `npx themis init --cursor` to install `.cursorrules`
|
|
221
|
+
- **Both at once**: `npx themis init --agents --claude-code --cursor`
|
|
222
|
+
- **Auto-detection**: A bare `npx themis init` detects which agents are present and installs the right assets automatically
|
|
223
|
+
|
|
224
|
+
## Links
|
|
225
|
+
|
|
226
|
+
- npm: [`@vitronai/themis`](https://www.npmjs.com/package/@vitronai/themis)
|
|
227
|
+
- GitHub: [vitron-ai/themis](https://github.com/vitron-ai/themis)
|
|
228
|
+
- Tessl tile: [vitron-ai/themis](https://tessl.io/registry/vitron-ai/themis)
|
|
229
|
+
- Eval results: [37% baseline → 97% with skill](https://tessl.io/eval-runs/019d72a0-8211-74ea-84ef-a8e336ead3d2)
|
|
230
|
+
- Adoption guide: [`docs/agents-adoption.md`](agents-adoption.md)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vitronai/themis",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "A Node.js and TypeScript unit test framework designed for AI coding agents. Drop-in alternative to Jest and Vitest with machine-readable failure output, structured repair hints, and one-command migration.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Vitron AI",
|
|
@@ -97,6 +97,8 @@
|
|
|
97
97
|
"benchmark:first-try": "node scripts/benchmark-first-try.js",
|
|
98
98
|
"benchmark:gate": "node scripts/benchmark-gate.js",
|
|
99
99
|
"proof:migration": "node scripts/verify-migration-fixtures.js",
|
|
100
|
+
"proof:esm": "node scripts/verify-esm-fixtures.js",
|
|
101
|
+
"verify:dogfood": "node scripts/verify-alethia-bridge-dogfood.js",
|
|
100
102
|
"pack:check": "npm pack --dry-run",
|
|
101
103
|
"prepublishOnly": "npm run lint && npm test && npm run typecheck"
|
|
102
104
|
},
|
package/src/cli.js
CHANGED
|
@@ -147,7 +147,11 @@ async function main(argv) {
|
|
|
147
147
|
}
|
|
148
148
|
console.log(`Report: ${formatCliPath(cwd, result.reportPath)}`);
|
|
149
149
|
}
|
|
150
|
-
|
|
150
|
+
if (result.source === 'node') {
|
|
151
|
+
console.log('node:test and node:assert imports are dropped during conversion; Themis provides test/expect/afterAll/afterEach as globals.');
|
|
152
|
+
} else {
|
|
153
|
+
console.log('Runtime compatibility is enabled for @jest/globals, vitest, and @testing-library/react imports.');
|
|
154
|
+
}
|
|
151
155
|
console.log('Next: run npx themis test or npm run test:themis');
|
|
152
156
|
return;
|
|
153
157
|
}
|
|
@@ -778,10 +782,10 @@ function validateWorkerCount(flagValue, configValue) {
|
|
|
778
782
|
}
|
|
779
783
|
|
|
780
784
|
function validateIsolation(value) {
|
|
781
|
-
if (value === 'worker' || value === 'in-process') {
|
|
785
|
+
if (value === 'worker' || value === 'in-process' || value === 'process') {
|
|
782
786
|
return;
|
|
783
787
|
}
|
|
784
|
-
throw new Error(`Unsupported --isolation value: ${value}. Use one of: worker, in-process.`);
|
|
788
|
+
throw new Error(`Unsupported --isolation value: ${value}. Use one of: worker, in-process, process.`);
|
|
785
789
|
}
|
|
786
790
|
|
|
787
791
|
function resolveWorkerCount(flagValue, configValue) {
|
|
@@ -814,8 +818,8 @@ function printUsage() {
|
|
|
814
818
|
console.log(' generate [path] Scan source files and generate Themis contract tests');
|
|
815
819
|
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]');
|
|
816
820
|
console.log(' scan [path] Alias for generate');
|
|
817
|
-
console.log(' migrate <jest|vitest> [--rewrite-imports] [--convert] [--assist] Scaffold an incremental migration bridge for existing suites');
|
|
818
|
-
console.log(' test [--json] [--agent] [--next] [--reporter spec|next|json|agent|html] [--workers N] [--stability N] [--environment node|jsdom] [--isolation worker|in-process] [--cache] [--update-contracts] [--fix] [-w|--watch] [--html-output path] [--match regex] [--rerun-failed] [--no-memes] [--lexicon classic|themis]');
|
|
821
|
+
console.log(' migrate <jest|vitest|node> [--rewrite-imports] [--convert] [--assist] Scaffold an incremental migration bridge for existing suites');
|
|
822
|
+
console.log(' test [--json] [--agent] [--next] [--reporter spec|next|json|agent|html] [--workers N] [--stability N] [--environment node|jsdom] [--isolation worker|in-process|process] [--cache] [--update-contracts] [--fix] [-w|--watch] [--html-output path] [--match regex] [--rerun-failed] [--no-memes] [--lexicon classic|themis]');
|
|
819
823
|
}
|
|
820
824
|
|
|
821
825
|
function printGenerateSummary(summary, cwd) {
|
package/src/config.js
CHANGED
|
@@ -5,7 +5,7 @@ const os = require('os');
|
|
|
5
5
|
const DEFAULT_CONFIG = {
|
|
6
6
|
testDir: 'tests',
|
|
7
7
|
generatedTestsDir: path.join('__themis__', 'tests'),
|
|
8
|
-
testRegex: '\\.(test|spec)\\.(js|jsx|ts|tsx)$',
|
|
8
|
+
testRegex: '\\.(test|spec)\\.(js|jsx|ts|tsx|mjs|cjs)$',
|
|
9
9
|
maxWorkers: Math.max(1, os.cpus().length - 1),
|
|
10
10
|
reporter: 'next',
|
|
11
11
|
environment: 'node',
|
package/src/expect.js
CHANGED
|
@@ -70,6 +70,24 @@ function createExpect(_context = {}) {
|
|
|
70
70
|
|
|
71
71
|
throw new Error('toContain only supports strings and arrays');
|
|
72
72
|
},
|
|
73
|
+
toMatch(expected) {
|
|
74
|
+
if (typeof received !== 'string') {
|
|
75
|
+
throw new Error(`toMatch expects a string, received ${format(received)}`);
|
|
76
|
+
}
|
|
77
|
+
if (typeof expected === 'string') {
|
|
78
|
+
if (!received.includes(expected)) {
|
|
79
|
+
throw new Error(`Expected ${format(received)} to match substring ${format(expected)}`);
|
|
80
|
+
}
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (expected instanceof RegExp) {
|
|
84
|
+
if (!expected.test(received)) {
|
|
85
|
+
throw new Error(`Expected ${format(received)} to match ${String(expected)}`);
|
|
86
|
+
}
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
throw new Error('toMatch expects a string or RegExp');
|
|
90
|
+
},
|
|
73
91
|
toThrow(match) {
|
|
74
92
|
if (typeof received !== 'function') {
|
|
75
93
|
throw new Error('toThrow expects a function');
|