@ricsam/isolate-test-environment 0.1.3 → 0.1.6
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 +145 -24
- package/dist/cjs/index.cjs +402 -104
- package/dist/cjs/index.cjs.map +3 -3
- package/dist/cjs/package.json +1 -1
- package/dist/mjs/index.mjs +388 -103
- package/dist/mjs/index.mjs.map +3 -3
- package/dist/mjs/package.json +1 -1
- package/dist/types/index.d.ts +83 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,18 +2,67 @@
|
|
|
2
2
|
|
|
3
3
|
Test primitives for running tests in sandboxed V8. Provides a Jest/Vitest-compatible API.
|
|
4
4
|
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm add @ricsam/isolate-test-environment
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage with isolate-runtime (Recommended)
|
|
12
|
+
|
|
13
|
+
The easiest way to use this package is through `@ricsam/isolate-runtime`:
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { createRuntime } from "@ricsam/isolate-runtime";
|
|
17
|
+
|
|
18
|
+
const runtime = await createRuntime({
|
|
19
|
+
testEnvironment: {
|
|
20
|
+
onEvent: (event) => {
|
|
21
|
+
// Receive lifecycle events during test execution
|
|
22
|
+
if (event.type === "testEnd") {
|
|
23
|
+
const icon = event.test.status === "pass" ? "✓" : "✗";
|
|
24
|
+
console.log(`${icon} ${event.test.fullName}`);
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
await runtime.eval(`
|
|
31
|
+
describe("math", () => {
|
|
32
|
+
it("adds numbers", () => {
|
|
33
|
+
expect(1 + 1).toBe(2);
|
|
34
|
+
});
|
|
35
|
+
it.todo("subtract numbers");
|
|
36
|
+
});
|
|
37
|
+
`);
|
|
38
|
+
|
|
39
|
+
const results = await runtime.testEnvironment.runTests();
|
|
40
|
+
console.log(`${results.passed}/${results.total} passed, ${results.todo} todo`);
|
|
41
|
+
|
|
42
|
+
// Check if tests exist before running
|
|
43
|
+
if (runtime.testEnvironment.hasTests()) {
|
|
44
|
+
console.log(`Found ${runtime.testEnvironment.getTestCount()} tests`);
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Low-level Usage (Direct ivm)
|
|
49
|
+
|
|
50
|
+
For advanced use cases with direct isolated-vm access:
|
|
51
|
+
|
|
5
52
|
```typescript
|
|
6
53
|
import { setupTestEnvironment, runTests } from "@ricsam/isolate-test-environment";
|
|
7
54
|
|
|
8
55
|
const handle = await setupTestEnvironment(context);
|
|
9
56
|
```
|
|
10
57
|
|
|
11
|
-
|
|
58
|
+
## Injected Globals
|
|
59
|
+
|
|
12
60
|
- `describe`, `it`, `test` (with `.skip`, `.only`, `.todo` modifiers)
|
|
13
61
|
- `beforeAll`, `afterAll`, `beforeEach`, `afterEach`
|
|
14
62
|
- `expect` with matchers and `.not` modifier
|
|
15
63
|
|
|
16
|
-
|
|
64
|
+
## Expect Matchers
|
|
65
|
+
|
|
17
66
|
- `toBe(expected)` - Strict equality (`===`)
|
|
18
67
|
- `toEqual(expected)` - Deep equality
|
|
19
68
|
- `toStrictEqual(expected)` - Strict deep equality (includes prototype checks)
|
|
@@ -26,7 +75,7 @@ const handle = await setupTestEnvironment(context);
|
|
|
26
75
|
- `toMatch(pattern)` - String/regex match
|
|
27
76
|
- `toHaveProperty(path, value?)` - Object property check
|
|
28
77
|
|
|
29
|
-
|
|
78
|
+
## Usage in Isolate
|
|
30
79
|
|
|
31
80
|
```javascript
|
|
32
81
|
describe("Math operations", () => {
|
|
@@ -57,35 +106,107 @@ expect(1).not.toBe(2);
|
|
|
57
106
|
expect([1, 2]).not.toContain(3);
|
|
58
107
|
```
|
|
59
108
|
|
|
60
|
-
|
|
109
|
+
## Running Tests from Host
|
|
61
110
|
|
|
62
111
|
```typescript
|
|
63
|
-
import { setupTestEnvironment, runTests } from "@ricsam/isolate-test-environment";
|
|
64
|
-
|
|
65
|
-
// Setup test environment
|
|
66
|
-
const handle = await setupTestEnvironment(context
|
|
112
|
+
import { setupTestEnvironment, runTests, hasTests, getTestCount } from "@ricsam/isolate-test-environment";
|
|
113
|
+
|
|
114
|
+
// Setup test environment with optional event callback
|
|
115
|
+
const handle = await setupTestEnvironment(context, {
|
|
116
|
+
onEvent: (event) => {
|
|
117
|
+
switch (event.type) {
|
|
118
|
+
case "runStart":
|
|
119
|
+
console.log(`Running ${event.testCount} tests in ${event.suiteCount} suites`);
|
|
120
|
+
break;
|
|
121
|
+
case "testEnd":
|
|
122
|
+
console.log(`${event.test.status}: ${event.test.fullName}`);
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
});
|
|
67
127
|
|
|
68
128
|
// Load test code
|
|
69
129
|
await context.eval(userProvidedTestCode, { promise: true });
|
|
70
130
|
|
|
131
|
+
// Check if any tests were registered
|
|
132
|
+
if (hasTests(context)) {
|
|
133
|
+
console.log(`Found ${getTestCount(context)} tests`);
|
|
134
|
+
}
|
|
135
|
+
|
|
71
136
|
// Run all registered tests
|
|
72
137
|
const results = await runTests(context);
|
|
73
138
|
console.log(`${results.passed}/${results.total} passed`);
|
|
74
139
|
|
|
75
|
-
// Results structure:
|
|
76
|
-
// {
|
|
77
|
-
// passed: number,
|
|
78
|
-
// failed: number,
|
|
79
|
-
// skipped: number,
|
|
80
|
-
// total: number,
|
|
81
|
-
// results: Array<{
|
|
82
|
-
// name: string,
|
|
83
|
-
// passed: boolean,
|
|
84
|
-
// error?: string,
|
|
85
|
-
// duration: number,
|
|
86
|
-
// skipped?: boolean
|
|
87
|
-
// }>
|
|
88
|
-
// }
|
|
89
|
-
|
|
90
140
|
handle.dispose();
|
|
91
|
-
```
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Types
|
|
144
|
+
|
|
145
|
+
### RunResults
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
interface RunResults {
|
|
149
|
+
passed: number;
|
|
150
|
+
failed: number;
|
|
151
|
+
skipped: number;
|
|
152
|
+
todo: number;
|
|
153
|
+
total: number;
|
|
154
|
+
duration: number;
|
|
155
|
+
success: boolean; // true if no failures
|
|
156
|
+
suites: SuiteResult[]; // suite-level results
|
|
157
|
+
tests: TestResult[]; // individual test results
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### TestResult
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
interface TestResult {
|
|
165
|
+
name: string;
|
|
166
|
+
suitePath: string[]; // suite ancestry
|
|
167
|
+
fullName: string; // "suite > nested > test name"
|
|
168
|
+
status: "pass" | "fail" | "skip" | "todo";
|
|
169
|
+
duration: number;
|
|
170
|
+
error?: TestError;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
interface TestError {
|
|
174
|
+
message: string;
|
|
175
|
+
stack?: string;
|
|
176
|
+
expected?: unknown; // for assertion failures
|
|
177
|
+
actual?: unknown;
|
|
178
|
+
matcherName?: string; // e.g., "toBe", "toEqual"
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### SuiteResult
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
interface SuiteResult {
|
|
186
|
+
name: string;
|
|
187
|
+
path: string[]; // ancestry path
|
|
188
|
+
fullName: string; // "outer > inner"
|
|
189
|
+
depth: number; // nesting level (0 for root)
|
|
190
|
+
passed: number;
|
|
191
|
+
failed: number;
|
|
192
|
+
skipped: number;
|
|
193
|
+
todo: number;
|
|
194
|
+
duration: number;
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### TestEvent
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
type TestEvent =
|
|
202
|
+
| { type: "runStart"; testCount: number; suiteCount: number }
|
|
203
|
+
| { type: "suiteStart"; suite: SuiteInfo }
|
|
204
|
+
| { type: "suiteEnd"; suite: SuiteResult }
|
|
205
|
+
| { type: "testStart"; test: TestInfo }
|
|
206
|
+
| { type: "testEnd"; test: TestResult }
|
|
207
|
+
| { type: "runEnd"; results: RunResults };
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## License
|
|
211
|
+
|
|
212
|
+
MIT
|