@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.
@@ -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
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "rootDir": "./src"
5
+ },
6
+ "include": ["src/**/*"],
7
+ "exclude": ["node_modules", "dist", "**/*.test.ts"]
8
+ }
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**