@ricsam/isolate-test-environment 0.0.1 → 0.1.1
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 +9 -0
- package/package.json +28 -7
- package/src/expect.test.ts +319 -0
- package/src/hooks.test.ts +303 -0
- package/src/index.ts +655 -0
- package/src/integration.test.ts +343 -0
- package/tsconfig.json +8 -0
- package/README.md +0 -45
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
import { test, describe, beforeEach, afterEach } from "node:test";
|
|
2
|
+
import assert from "node:assert";
|
|
3
|
+
import ivm from "isolated-vm";
|
|
4
|
+
import { setupTestEnvironment, runTests } from "./index.ts";
|
|
5
|
+
|
|
6
|
+
describe("Test Modifiers", () => {
|
|
7
|
+
let isolate: ivm.Isolate;
|
|
8
|
+
let context: ivm.Context;
|
|
9
|
+
|
|
10
|
+
beforeEach(async () => {
|
|
11
|
+
isolate = new ivm.Isolate();
|
|
12
|
+
context = await isolate.createContext();
|
|
13
|
+
await setupTestEnvironment(context);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
afterEach(() => {
|
|
17
|
+
context.release();
|
|
18
|
+
isolate.dispose();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("it.todo marks tests as todo", async () => {
|
|
22
|
+
context.evalSync(`
|
|
23
|
+
describe("Feature tests", () => {
|
|
24
|
+
it.todo("should implement this later");
|
|
25
|
+
|
|
26
|
+
test("regular test", () => {
|
|
27
|
+
expect(true).toBe(true);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
`);
|
|
31
|
+
|
|
32
|
+
const results = await runTests(context);
|
|
33
|
+
|
|
34
|
+
// todo tests should be tracked separately
|
|
35
|
+
assert.strictEqual(results.passed, 1);
|
|
36
|
+
// Check if todo is tracked (may need to look at results structure)
|
|
37
|
+
const todoResult = results.results.find((r: any) => r.name.includes("implement this later"));
|
|
38
|
+
assert.ok(todoResult, "todo test should be in results");
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("describe.only runs only specific suites", async () => {
|
|
42
|
+
context.evalSync(`
|
|
43
|
+
describe.only("critical tests", () => {
|
|
44
|
+
it("this will run", () => {
|
|
45
|
+
expect(true).toBe(true);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe("regular tests", () => {
|
|
50
|
+
it("this won't run because describe.only is used", () => {
|
|
51
|
+
expect(false).toBe(true); // Would fail if run
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
`);
|
|
55
|
+
|
|
56
|
+
const results = await runTests(context);
|
|
57
|
+
|
|
58
|
+
assert.strictEqual(results.passed, 1);
|
|
59
|
+
assert.strictEqual(results.failed, 0);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("describe.only with nested tests", async () => {
|
|
63
|
+
context.evalSync(`
|
|
64
|
+
describe("outer", () => {
|
|
65
|
+
describe.only("inner only", () => {
|
|
66
|
+
it("should run", () => {
|
|
67
|
+
expect(1).toBe(1);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
describe("inner regular", () => {
|
|
72
|
+
it("should not run", () => {
|
|
73
|
+
expect(false).toBe(true);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
`);
|
|
78
|
+
|
|
79
|
+
const results = await runTests(context);
|
|
80
|
+
|
|
81
|
+
assert.strictEqual(results.passed, 1);
|
|
82
|
+
assert.strictEqual(results.failed, 0);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
describe("Test Handle Methods", () => {
|
|
87
|
+
let isolate: ivm.Isolate;
|
|
88
|
+
let context: ivm.Context;
|
|
89
|
+
|
|
90
|
+
beforeEach(async () => {
|
|
91
|
+
isolate = new ivm.Isolate();
|
|
92
|
+
context = await isolate.createContext();
|
|
93
|
+
await setupTestEnvironment(context);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
afterEach(() => {
|
|
97
|
+
context.release();
|
|
98
|
+
isolate.dispose();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test("hasTests returns false when no tests registered", async () => {
|
|
102
|
+
// Assuming hasTests() is exposed or we can check via results
|
|
103
|
+
const results = await runTests(context);
|
|
104
|
+
assert.strictEqual(results.total, 0);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("hasTests returns true when tests are registered", async () => {
|
|
108
|
+
context.evalSync(`
|
|
109
|
+
describe("suite", () => {
|
|
110
|
+
it("test 1", () => {});
|
|
111
|
+
it("test 2", () => {});
|
|
112
|
+
});
|
|
113
|
+
`);
|
|
114
|
+
|
|
115
|
+
const results = await runTests(context);
|
|
116
|
+
assert.ok(results.total > 0);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test("getTestCount returns accurate count", async () => {
|
|
120
|
+
context.evalSync(`
|
|
121
|
+
describe("suite", () => {
|
|
122
|
+
it("test 1", () => {});
|
|
123
|
+
it("test 2", () => {});
|
|
124
|
+
describe("nested", () => {
|
|
125
|
+
it("test 3", () => {});
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
`);
|
|
129
|
+
|
|
130
|
+
const results = await runTests(context);
|
|
131
|
+
assert.strictEqual(results.total, 3);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test("reset clears tests", async () => {
|
|
135
|
+
context.evalSync(`
|
|
136
|
+
it("test", () => {});
|
|
137
|
+
`);
|
|
138
|
+
|
|
139
|
+
// First run should have 1 test
|
|
140
|
+
const results1 = await runTests(context);
|
|
141
|
+
assert.strictEqual(results1.total, 1);
|
|
142
|
+
|
|
143
|
+
// Reset the test environment
|
|
144
|
+
context.evalSync("__resetTestEnvironment()");
|
|
145
|
+
|
|
146
|
+
// After reset, should have 0 tests
|
|
147
|
+
const results2 = await runTests(context);
|
|
148
|
+
assert.strictEqual(results2.total, 0);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
test("reset allows registering new tests", async () => {
|
|
152
|
+
context.evalSync(`it("test 1", () => {});`);
|
|
153
|
+
const results1 = await runTests(context);
|
|
154
|
+
assert.strictEqual(results1.total, 1);
|
|
155
|
+
|
|
156
|
+
context.evalSync("__resetTestEnvironment()");
|
|
157
|
+
|
|
158
|
+
context.evalSync(`it("test 2", () => {}); it("test 3", () => {});`);
|
|
159
|
+
const results2 = await runTests(context);
|
|
160
|
+
assert.strictEqual(results2.total, 2);
|
|
161
|
+
assert.strictEqual(results2.passed, 2);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
describe("Error Scenarios", () => {
|
|
166
|
+
let isolate: ivm.Isolate;
|
|
167
|
+
let context: ivm.Context;
|
|
168
|
+
|
|
169
|
+
beforeEach(async () => {
|
|
170
|
+
isolate = new ivm.Isolate();
|
|
171
|
+
context = await isolate.createContext();
|
|
172
|
+
await setupTestEnvironment(context);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
afterEach(() => {
|
|
176
|
+
context.release();
|
|
177
|
+
isolate.dispose();
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test("syntax error in test code throws", async () => {
|
|
181
|
+
assert.throws(() => {
|
|
182
|
+
context.evalSync(`
|
|
183
|
+
describe("suite", () => {
|
|
184
|
+
it("test", () => {
|
|
185
|
+
invalid syntax here
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
`);
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
test("runtime error in test captures error message", async () => {
|
|
193
|
+
context.evalSync(`
|
|
194
|
+
describe("suite", () => {
|
|
195
|
+
it("throws", () => {
|
|
196
|
+
throw new Error("intentional error");
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
`);
|
|
200
|
+
|
|
201
|
+
const results = await runTests(context);
|
|
202
|
+
|
|
203
|
+
assert.strictEqual(results.failed, 1);
|
|
204
|
+
assert.ok(results.results[0]!.error?.includes("intentional error"));
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test("assertion error includes expected and actual values", async () => {
|
|
208
|
+
context.evalSync(`
|
|
209
|
+
describe("suite", () => {
|
|
210
|
+
it("assertion fails", () => {
|
|
211
|
+
expect(42).toBe(100);
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
`);
|
|
215
|
+
|
|
216
|
+
const results = await runTests(context);
|
|
217
|
+
|
|
218
|
+
assert.strictEqual(results.failed, 1);
|
|
219
|
+
const error = results.results[0]!.error;
|
|
220
|
+
assert.ok(error?.includes("42"), "Error should mention actual value");
|
|
221
|
+
assert.ok(error?.includes("100"), "Error should mention expected value");
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
test("toEqual assertion error shows both values", async () => {
|
|
225
|
+
context.evalSync(`
|
|
226
|
+
describe("suite", () => {
|
|
227
|
+
it("deep equal fails", () => {
|
|
228
|
+
expect({ a: 1 }).toEqual({ a: 2 });
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
`);
|
|
232
|
+
|
|
233
|
+
const results = await runTests(context);
|
|
234
|
+
|
|
235
|
+
assert.strictEqual(results.failed, 1);
|
|
236
|
+
assert.ok(results.results[0]!.error);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
test("multiple test failures are all captured", async () => {
|
|
240
|
+
context.evalSync(`
|
|
241
|
+
describe("suite", () => {
|
|
242
|
+
it("fail 1", () => {
|
|
243
|
+
expect(1).toBe(2);
|
|
244
|
+
});
|
|
245
|
+
it("fail 2", () => {
|
|
246
|
+
throw new Error("error 2");
|
|
247
|
+
});
|
|
248
|
+
it("pass", () => {
|
|
249
|
+
expect(true).toBe(true);
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
`);
|
|
253
|
+
|
|
254
|
+
const results = await runTests(context);
|
|
255
|
+
|
|
256
|
+
assert.strictEqual(results.failed, 2);
|
|
257
|
+
assert.strictEqual(results.passed, 1);
|
|
258
|
+
assert.strictEqual(results.total, 3);
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
describe("Basic Test Execution", () => {
|
|
263
|
+
let isolate: ivm.Isolate;
|
|
264
|
+
let context: ivm.Context;
|
|
265
|
+
|
|
266
|
+
beforeEach(async () => {
|
|
267
|
+
isolate = new ivm.Isolate();
|
|
268
|
+
context = await isolate.createContext();
|
|
269
|
+
await setupTestEnvironment(context);
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
afterEach(() => {
|
|
273
|
+
context.release();
|
|
274
|
+
isolate.dispose();
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
test("SPEC example: basic test execution", async () => {
|
|
278
|
+
context.evalSync(`
|
|
279
|
+
describe("Math operations", () => {
|
|
280
|
+
it("should add numbers", () => {
|
|
281
|
+
expect(1 + 1).toBe(2);
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
it("should multiply numbers", () => {
|
|
285
|
+
expect(2 * 3).toEqual(6);
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
`);
|
|
289
|
+
|
|
290
|
+
const results = await runTests(context);
|
|
291
|
+
|
|
292
|
+
assert.strictEqual(results.passed, 2);
|
|
293
|
+
assert.strictEqual(results.failed, 0);
|
|
294
|
+
assert.ok(results.results.some((r: any) => r.name.includes("should add numbers")));
|
|
295
|
+
assert.ok(results.results.some((r: any) => r.name.includes("should multiply numbers")));
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
test("SPEC example: with all hooks", async () => {
|
|
299
|
+
context.evalSync(`
|
|
300
|
+
describe("Database tests", () => {
|
|
301
|
+
let db;
|
|
302
|
+
|
|
303
|
+
beforeAll(() => {
|
|
304
|
+
db = {
|
|
305
|
+
data: [],
|
|
306
|
+
clear() { this.data = []; },
|
|
307
|
+
insert(r) { this.data.push(r); },
|
|
308
|
+
count() { return this.data.length; },
|
|
309
|
+
findById(id) { return this.data.find(r => r.id === id); }
|
|
310
|
+
};
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
afterAll(() => {
|
|
314
|
+
db = null;
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
beforeEach(() => {
|
|
318
|
+
db.clear();
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
afterEach(() => {
|
|
322
|
+
// cleanup after each test
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
it("should insert record", () => {
|
|
326
|
+
db.insert({ id: 1, name: "test" });
|
|
327
|
+
expect(db.count()).toBe(1);
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it("should query records", () => {
|
|
331
|
+
db.insert({ id: 1, name: "test" });
|
|
332
|
+
const result = db.findById(1);
|
|
333
|
+
expect(result).toEqual({ id: 1, name: "test" });
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
`);
|
|
337
|
+
|
|
338
|
+
const results = await runTests(context);
|
|
339
|
+
|
|
340
|
+
assert.strictEqual(results.passed, 2);
|
|
341
|
+
assert.strictEqual(results.failed, 0);
|
|
342
|
+
});
|
|
343
|
+
});
|
package/tsconfig.json
ADDED
package/README.md
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
# @ricsam/isolate-test-environment
|
|
2
|
-
|
|
3
|
-
## ⚠️ IMPORTANT NOTICE ⚠️
|
|
4
|
-
|
|
5
|
-
**This package is created solely for the purpose of setting up OIDC (OpenID Connect) trusted publishing with npm.**
|
|
6
|
-
|
|
7
|
-
This is **NOT** a functional package and contains **NO** code or functionality beyond the OIDC setup configuration.
|
|
8
|
-
|
|
9
|
-
## Purpose
|
|
10
|
-
|
|
11
|
-
This package exists to:
|
|
12
|
-
1. Configure OIDC trusted publishing for the package name `@ricsam/isolate-test-environment`
|
|
13
|
-
2. Enable secure, token-less publishing from CI/CD workflows
|
|
14
|
-
3. Establish provenance for packages published under this name
|
|
15
|
-
|
|
16
|
-
## What is OIDC Trusted Publishing?
|
|
17
|
-
|
|
18
|
-
OIDC trusted publishing allows package maintainers to publish packages directly from their CI/CD workflows without needing to manage npm access tokens. Instead, it uses OpenID Connect to establish trust between the CI/CD provider (like GitHub Actions) and npm.
|
|
19
|
-
|
|
20
|
-
## Setup Instructions
|
|
21
|
-
|
|
22
|
-
To properly configure OIDC trusted publishing for this package:
|
|
23
|
-
|
|
24
|
-
1. Go to [npmjs.com](https://www.npmjs.com/) and navigate to your package settings
|
|
25
|
-
2. Configure the trusted publisher (e.g., GitHub Actions)
|
|
26
|
-
3. Specify the repository and workflow that should be allowed to publish
|
|
27
|
-
4. Use the configured workflow to publish your actual package
|
|
28
|
-
|
|
29
|
-
## DO NOT USE THIS PACKAGE
|
|
30
|
-
|
|
31
|
-
This package is a placeholder for OIDC configuration only. It:
|
|
32
|
-
- Contains no executable code
|
|
33
|
-
- Provides no functionality
|
|
34
|
-
- Should not be installed as a dependency
|
|
35
|
-
- Exists only for administrative purposes
|
|
36
|
-
|
|
37
|
-
## More Information
|
|
38
|
-
|
|
39
|
-
For more details about npm's trusted publishing feature, see:
|
|
40
|
-
- [npm Trusted Publishing Documentation](https://docs.npmjs.com/generating-provenance-statements)
|
|
41
|
-
- [GitHub Actions OIDC Documentation](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect)
|
|
42
|
-
|
|
43
|
-
---
|
|
44
|
-
|
|
45
|
-
**Maintained for OIDC setup purposes only**
|