fragment-ts 1.0.19 → 1.0.20

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.
@@ -177,7 +177,7 @@ class InitCommand {
177
177
  "migrate:revert": "fragment migrate:revert",
178
178
  },
179
179
  dependencies: {
180
- "fragment-ts": "^1.0.19",
180
+ "fragment-ts": "^1.0.20",
181
181
  "reflect-metadata": "^0.1.13",
182
182
  },
183
183
  devDependencies: {
@@ -2,5 +2,6 @@ import { Command } from "commander";
2
2
  export declare class TestCommand {
3
3
  static register(program: Command): void;
4
4
  private static runTests;
5
+ private static generateRunnerScript;
5
6
  }
6
7
  //# sourceMappingURL=test.command.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"test.command.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/test.command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,qBAAa,WAAW;IACtB,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;mBAiBlB,QAAQ;CAwQ9B"}
1
+ {"version":3,"file":"test.command.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/test.command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,qBAAa,WAAW;IACtB,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;mBAkBlB,QAAQ;IA8I7B,OAAO,CAAC,MAAM,CAAC,oBAAoB;CAwIpC"}
@@ -50,6 +50,7 @@ class TestCommand {
50
50
  .option("--env <environment>", "Environment mode: dev (use src/) or prod (use dist/)", "auto")
51
51
  .option("--pattern <pattern>", "Test file pattern", "**/*.spec.ts")
52
52
  .option("--coverage", "Generate coverage report")
53
+ .option("--no-color", "Disable colored output")
53
54
  .action(async (options) => {
54
55
  await this.runTests(options);
55
56
  });
@@ -81,7 +82,7 @@ class TestCommand {
81
82
  useTypeScript = false;
82
83
  mode = "production (dist/)";
83
84
  basePath = "dist";
84
- pattern = options.pattern.replace(".ts", ".js") || "**/*.spec.js";
85
+ pattern = options.pattern.replace(/\.ts$/, ".js") || "**/*.spec.js";
85
86
  }
86
87
  else {
87
88
  // Auto-detect
@@ -95,191 +96,54 @@ class TestCommand {
95
96
  useTypeScript = false;
96
97
  mode = "auto-detected production (dist/)";
97
98
  basePath = "dist";
98
- pattern = options.pattern.replace(".ts", ".js") || "**/*.spec.js";
99
+ pattern = options.pattern.replace(/\.ts$/, ".js") || "**/*.spec.js";
99
100
  }
100
101
  else {
101
102
  console.log(chalk_1.default.red("No src/ or dist/ directory found. Run: fragment init"));
102
103
  return;
103
104
  }
104
105
  }
105
- console.log(chalk_1.default.gray(` Mode: ${mode}\n`));
106
- const scriptContent = `
107
- ${useTypeScript ? "require('ts-node/register/transpile-only');" : ""}
108
- require('reflect-metadata');
109
-
110
- const { glob } = require('glob');
111
- const path = require('path');
112
-
113
- // Global test state
114
- const testState = {
115
- suites: [],
116
- currentSuite: null,
117
- passed: 0,
118
- failed: 0,
119
- errors: []
120
- };
121
-
122
- // Global test functions
123
- global.describe = function(name, fn) {
124
- const suite = { name, tests: [] };
125
- testState.suites.push(suite);
126
- testState.currentSuite = suite;
127
- fn();
128
- testState.currentSuite = null;
129
- };
130
-
131
- global.it = function(name, fn) {
132
- if (!testState.currentSuite) {
133
- throw new Error('it() must be called inside describe()');
134
- }
135
- testState.currentSuite.tests.push({ name, fn });
136
- };
137
-
138
- global.expect = function(actual) {
139
- return {
140
- toBe(expected) {
141
- if (actual !== expected) {
142
- throw new Error(\`Expected \${actual} to be \${expected}\`);
143
- }
144
- },
145
- toEqual(expected) {
146
- if (JSON.stringify(actual) !== JSON.stringify(expected)) {
147
- throw new Error(\`Expected \${JSON.stringify(actual)} to equal \${JSON.stringify(expected)}\`);
148
- }
149
- },
150
- toBeTruthy() {
151
- if (!actual) {
152
- throw new Error(\`Expected \${actual} to be truthy\`);
153
- }
154
- },
155
- toBeFalsy() {
156
- if (actual) {
157
- throw new Error(\`Expected \${actual} to be falsy\`);
158
- }
159
- },
160
- toThrow(expectedError) {
161
- let threw = false;
162
- let error = null;
163
- try {
164
- actual();
165
- } catch (e) {
166
- threw = true;
167
- error = e;
168
- }
169
- if (!threw) {
170
- throw new Error('Expected function to throw');
171
- }
172
- if (expectedError && error.message !== expectedError) {
173
- throw new Error(\`Expected error message "\${expectedError}" but got "\${error.message}"\`);
174
- }
175
- },
176
- toBeInstanceOf(expected) {
177
- if (!(actual instanceof expected)) {
178
- throw new Error(\`Expected \${actual} to be instance of \${expected.name}\`);
179
- }
180
- },
181
- toContain(expected) {
182
- if (!actual.includes(expected)) {
183
- throw new Error(\`Expected \${actual} to contain \${expected}\`);
184
- }
185
- },
186
- toHaveProperty(property, value) {
187
- if (!(property in actual)) {
188
- throw new Error(\`Expected object to have property \${property}\`);
189
- }
190
- if (value !== undefined && actual[property] !== value) {
191
- throw new Error(\`Expected property \${property} to be \${value} but got \${actual[property]}\`);
192
- }
193
- },
194
- toBeNull() {
195
- if (actual !== null) {
196
- throw new Error(\`Expected \${actual} to be null\`);
197
- }
198
- },
199
- toBeUndefined() {
200
- if (actual !== undefined) {
201
- throw new Error(\`Expected \${actual} to be undefined\`);
202
- }
203
- },
204
- toHaveLength(expected) {
205
- if (actual.length !== expected) {
206
- throw new Error(\`Expected length \${expected} but got \${actual.length}\`);
207
- }
208
- }
209
- };
210
- };
211
-
212
- async function runTests() {
213
- try {
214
- // Find and load test files
215
- const files = await glob('${basePath}/${pattern}', { cwd: process.cwd() });
216
-
217
- if (files.length === 0) {
218
- console.log('No test files found matching pattern: ${basePath}/${pattern}');
219
- process.exit(0);
220
- }
221
-
222
- console.log(\`Found \${files.length} test file(s)\\n\`);
223
-
224
- // Load all test files
225
- for (const file of files) {
226
- require(path.resolve(file));
227
- }
228
-
229
- // Run all tests
230
- for (const suite of testState.suites) {
231
- console.log(\`\\nšŸ“¦ \${suite.name}\`);
232
-
233
- for (const test of suite.tests) {
234
- try {
235
- await test.fn();
236
- console.log(\` āœ“ \${test.name}\`);
237
- testState.passed++;
238
- } catch (error) {
239
- console.log(\` āœ— \${test.name}\`);
240
- console.error(\` \${error.message}\`);
241
- testState.failed++;
242
- testState.errors.push({
243
- suite: suite.name,
244
- test: test.name,
245
- error: error.message,
246
- stack: error.stack
247
- });
248
- }
249
- }
250
- }
251
-
252
- // Print summary
253
- console.log(\`\\n\\nšŸ“Š Results: \${testState.passed} passed, \${testState.failed} failed\\n\`);
254
-
255
- if (testState.failed > 0) {
256
- console.log('\\nāŒ Failed Tests:\\n');
257
- testState.errors.forEach(err => {
258
- console.log(\` \${err.suite} > \${err.test}\`);
259
- console.log(\` \${err.error}\\n\`);
260
- });
261
- process.exit(1);
262
- } else {
263
- console.log('āœ… All tests passed!\\n');
264
- process.exit(0);
265
- }
266
- } catch (error) {
267
- console.error('Error running tests:', error);
268
- process.exit(1);
269
- }
270
- }
271
-
272
- runTests();
273
- `;
106
+ console.log(chalk_1.default.gray(` Mode: ${mode}`));
107
+ console.log(chalk_1.default.gray(` Pattern: ${basePath}/${pattern}`));
108
+ // Check if we need to use ts-node for TypeScript files
109
+ const tsConfigPath = path.join(cwd, "tsconfig.json");
110
+ const hasTsConfig = fs.existsSync(tsConfigPath);
111
+ // Create a simple runner script that uses our test runner module
112
+ const scriptContent = this.generateRunnerScript({
113
+ useTypeScript,
114
+ basePath,
115
+ pattern,
116
+ hasTsConfig,
117
+ tsConfigPath,
118
+ watchMode: options.watch,
119
+ coverage: options.coverage,
120
+ noColor: options.color === false,
121
+ });
274
122
  const scriptPath = path.join(cwd, ".fragment-test-runner.js");
275
123
  try {
276
124
  fs.writeFileSync(scriptPath, scriptContent);
125
+ // Make the script executable
126
+ fs.chmodSync(scriptPath, "755");
277
127
  const nodeArgs = [scriptPath];
278
128
  const env = {
279
129
  ...process.env,
280
130
  TS_NODE_TRANSPILE_ONLY: "true",
281
131
  NODE_ENV: "test",
132
+ // Only set TS_NODE_PROJECT if tsconfig exists
133
+ ...(hasTsConfig && useTypeScript
134
+ ? { TS_NODE_PROJECT: tsConfigPath }
135
+ : {}),
136
+ // Handle color output
137
+ FORCE_COLOR: options.color === false ? "0" : "1",
282
138
  };
139
+ // Add --watch support
140
+ if (options.watch) {
141
+ console.log(chalk_1.default.yellow("\nāš ļø Watch mode is not yet implemented. Running once.\n"));
142
+ }
143
+ // Add --coverage support
144
+ if (options.coverage) {
145
+ console.log(chalk_1.default.yellow("\nāš ļø Coverage reporting is not yet implemented.\n"));
146
+ }
283
147
  const proc = (0, child_process_1.spawn)("node", nodeArgs, {
284
148
  cwd,
285
149
  stdio: "inherit",
@@ -289,7 +153,9 @@ runTests();
289
153
  try {
290
154
  fs.unlinkSync(scriptPath);
291
155
  }
292
- catch { }
156
+ catch (e) {
157
+ // Ignore cleanup errors
158
+ }
293
159
  process.exit(code || 0);
294
160
  });
295
161
  proc.on("error", (error) => {
@@ -297,7 +163,9 @@ runTests();
297
163
  try {
298
164
  fs.unlinkSync(scriptPath);
299
165
  }
300
- catch { }
166
+ catch (e) {
167
+ // Ignore cleanup errors
168
+ }
301
169
  process.exit(1);
302
170
  });
303
171
  }
@@ -306,6 +174,114 @@ runTests();
306
174
  process.exit(1);
307
175
  }
308
176
  }
177
+ static generateRunnerScript(options) {
178
+ const { useTypeScript, basePath, pattern, hasTsConfig, tsConfigPath, noColor, } = options;
179
+ // Determine the runner path - try to find the actual test runner
180
+ // This assumes the test runner is installed as a dependency
181
+ let runnerImportPath = "fragment-ts/testing";
182
+ // Try to find the local test runner first for development
183
+ const possiblePaths = [
184
+ path.join(process.cwd(), "node_modules", "fragment-ts", "testing"),
185
+ path.join(process.cwd(), "node_modules", "fragment-ts", "dist", "testing"),
186
+ path.join(__dirname, "..", "..", "testing"), // If we're in the fragment-ts package itself
187
+ ];
188
+ let resolvedRunnerPath = runnerImportPath;
189
+ for (const testPath of possiblePaths) {
190
+ if (fs.existsSync(testPath + ".ts") || fs.existsSync(testPath + ".js")) {
191
+ resolvedRunnerPath = testPath;
192
+ break;
193
+ }
194
+ }
195
+ return `
196
+ "use strict";
197
+
198
+ ${useTypeScript ? "require('ts-node/register/transpile-only');" : ""}
199
+ require('reflect-metadata');
200
+
201
+ // Set environment
202
+ process.env.NODE_ENV = 'test';
203
+ ${noColor ? "process.env.FORCE_COLOR = '0';" : "process.env.FORCE_COLOR = '1';"}
204
+
205
+ // Set up TypeScript config if available
206
+ ${hasTsConfig && useTypeScript
207
+ ? `
208
+ try {
209
+ const tsNode = require('ts-node');
210
+ tsNode.register({
211
+ project: '${tsConfigPath}',
212
+ transpileOnly: true,
213
+ compilerOptions: {
214
+ module: 'commonjs',
215
+ target: 'ES2020',
216
+ esModuleInterop: true,
217
+ skipLibCheck: true
218
+ }
219
+ });
220
+ } catch (error) {
221
+ // Ignore, ts-node might not be available
222
+ }`
223
+ : ""}
224
+
225
+ let testRunner;
226
+ try {
227
+ // Try to import the test runner
228
+ const testModule = require('${resolvedRunnerPath}');
229
+
230
+ // Handle different export patterns
231
+ if (testModule.getTestRunner) {
232
+ testRunner = testModule.getTestRunner();
233
+ } else if (testModule.TestRunner) {
234
+ testRunner = new testModule.TestRunner();
235
+ } else if (testModule.default && testModule.default.getTestRunner) {
236
+ testRunner = testModule.default.getTestRunner();
237
+ } else {
238
+ // Fallback to creating a new instance
239
+ testRunner = testModule;
240
+ }
241
+ } catch (error) {
242
+ console.error('Failed to load test runner:', error.message);
243
+ console.error('Make sure fragment-ts is installed as a dependency');
244
+ process.exit(1);
245
+ }
246
+
247
+ async function runTests() {
248
+ try {
249
+ console.log('Looking for test files...');
250
+
251
+ // Load test files with the runner
252
+ await testRunner.loadTestFiles('${basePath}/${pattern}');
253
+
254
+ // Run tests
255
+ await testRunner.run();
256
+ } catch (error) {
257
+ console.error('\\nāŒ Error running tests:', error.message);
258
+
259
+ if (error.stack && process.env.DEBUG) {
260
+ console.error(error.stack);
261
+ }
262
+
263
+ process.exit(1);
264
+ }
265
+ }
266
+
267
+ // Handle process termination
268
+ process.on('SIGINT', () => {
269
+ console.log('\\n\\nTest run interrupted');
270
+ process.exit(130);
271
+ });
272
+
273
+ process.on('SIGTERM', () => {
274
+ console.log('\\n\\nTest run terminated');
275
+ process.exit(143);
276
+ });
277
+
278
+ // Run the tests
279
+ runTests().catch(error => {
280
+ console.error('Unhandled error:', error);
281
+ process.exit(1);
282
+ });
283
+ `;
284
+ }
309
285
  }
310
286
  exports.TestCommand = TestCommand;
311
287
  //# sourceMappingURL=test.command.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"test.command.js","sourceRoot":"","sources":["../../../src/cli/commands/test.command.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"test.command.js","sourceRoot":"","sources":["../../../src/cli/commands/test.command.ts"],"names":[],"mappings":"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"}
@@ -12,10 +12,13 @@ export declare class TestRunner {
12
12
  private suites;
13
13
  private passed;
14
14
  private failed;
15
+ private errors;
15
16
  describe(name: string, fn: () => void): void;
16
17
  run(): Promise<void>;
17
18
  loadTestFiles(pattern: string): Promise<void>;
18
19
  }
20
+ export declare function getTestRunner(): TestRunner;
21
+ export declare function initTestRunner(): TestRunner;
19
22
  export declare function describe(name: string, fn: () => void): void;
20
23
  export declare function it(name: string, fn: () => void | Promise<void>): void;
21
24
  export declare function beforeEach(fn: () => void | Promise<void>): void;
@@ -25,10 +28,10 @@ export declare function expect(actual: any): {
25
28
  toEqual(expected: any): void;
26
29
  toBeTruthy(): void;
27
30
  toBeFalsy(): void;
28
- toThrow(): void;
31
+ toThrow(expectedError?: string): void;
29
32
  toBeInstanceOf(expected: any): void;
30
33
  toContain(expected: any): void;
31
- toHaveProperty(prop: string): void;
34
+ toHaveProperty(property: string, value?: any): void;
32
35
  toBeNull(): void;
33
36
  toBeUndefined(): void;
34
37
  toHaveLength(expected: number): void;
@@ -1 +1 @@
1
- {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/testing/runner.ts"],"names":[],"mappings":"AAmBA,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,eAAe,EAAE,KAAK,CAAC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACnD,cAAc,EAAE,KAAK,CAAC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;CACnD;AAED,MAAM,WAAW,IAAI;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAChC;AAKD,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,MAAM,CAAK;IAEnB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI;IAiCtC,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;IAmCpB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAOpD;AAKD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI,CAK3D;AAED,wBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAKrE;AAED,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAK/D;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAK9D;AAKD,wBAAgB,MAAM,CAAC,MAAM,EAAE,GAAG;mBAEf,GAAG;sBAMA,GAAG;;;;6BAqCI,GAAG;wBAMR,GAAG;yBAUF,MAAM;;;2BAkBJ,MAAM;EAUhC"}
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/testing/runner.ts"],"names":[],"mappings":"AAmBA,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,eAAe,EAAE,KAAK,CAAC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACnD,cAAc,EAAE,KAAK,CAAC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;CACnD;AAED,MAAM,WAAW,IAAI;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAChC;AAKD,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,MAAM,CAKN;IAER,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI;IAiCtC,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;IAiEpB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CA2BpD;AAOD,wBAAgB,aAAa,IAAI,UAAU,CAM1C;AAED,wBAAgB,cAAc,IAAI,UAAU,CAE3C;AAkBD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI,CAa3D;AAED,wBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAKrE;AAED,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAK/D;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAK9D;AAKD,wBAAgB,MAAM,CAAC,MAAM,EAAE,GAAG;mBAEf,GAAG;sBAMA,GAAG;;;4BAoBG,MAAM;6BAyBL,GAAG;wBAMR,GAAG;6BAUE,MAAM,UAAU,GAAG;;;2BAwBrB,MAAM;EAUhC"}
@@ -34,6 +34,8 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.TestRunner = void 0;
37
+ exports.getTestRunner = getTestRunner;
38
+ exports.initTestRunner = initTestRunner;
37
39
  exports.describe = describe;
38
40
  exports.it = it;
39
41
  exports.beforeEach = beforeEach;
@@ -50,6 +52,7 @@ class TestRunner {
50
52
  this.suites = [];
51
53
  this.passed = 0;
52
54
  this.failed = 0;
55
+ this.errors = [];
53
56
  }
54
57
  describe(name, fn) {
55
58
  const suite = {
@@ -77,47 +80,118 @@ class TestRunner {
77
80
  G.afterEach = originalAfterEach;
78
81
  }
79
82
  async run() {
80
- console.log("\nRunning Fragment Tests\n");
83
+ console.log("\n🧪 Running Fragment Tests\n");
84
+ if (this.suites.length === 0) {
85
+ console.log("No test suites found");
86
+ return;
87
+ }
81
88
  for (const suite of this.suites) {
82
- console.log(`\nSuite: ${suite.name}`);
89
+ console.log(`\nšŸ“¦ ${suite.name}`);
90
+ if (suite.tests.length === 0) {
91
+ console.log(" (no tests)");
92
+ continue;
93
+ }
83
94
  for (const test of suite.tests) {
84
95
  try {
96
+ // Run beforeEach hooks
85
97
  for (const hook of suite.beforeEachHooks) {
86
98
  await hook();
87
99
  }
100
+ // Run the test
88
101
  await test.fn();
102
+ // Run afterEach hooks
89
103
  for (const hook of suite.afterEachHooks) {
90
104
  await hook();
91
105
  }
92
- console.log(` PASS ${test.name}`);
106
+ console.log(` āœ“ ${test.name}`);
93
107
  this.passed++;
94
108
  }
95
109
  catch (error) {
96
- console.log(` FAIL ${test.name}`);
110
+ console.log(` āœ— ${test.name}`);
97
111
  console.error(` ${error?.message ?? error}`);
98
112
  this.failed++;
113
+ this.errors.push({
114
+ suite: suite.name,
115
+ test: test.name,
116
+ error: error?.message ?? String(error),
117
+ stack: error?.stack,
118
+ });
99
119
  }
100
120
  }
101
121
  }
102
- console.log(`\nResults: ${this.passed} passed, ${this.failed} failed\n`);
122
+ // Print summary
123
+ console.log(`\n\nšŸ“Š Results: ${this.passed} passed, ${this.failed} failed\n`);
103
124
  if (this.failed > 0) {
125
+ console.log("āŒ Failed Tests:\n");
126
+ this.errors.forEach((err) => {
127
+ console.log(` ${err.suite} > ${err.test}`);
128
+ console.log(` ${err.error}\n`);
129
+ });
104
130
  process.exit(1);
105
131
  }
132
+ else {
133
+ console.log("āœ… All tests passed!\n");
134
+ process.exit(0);
135
+ }
106
136
  }
107
137
  async loadTestFiles(pattern) {
108
- const files = await (0, glob_1.glob)(pattern);
138
+ const files = await (0, glob_1.glob)(pattern, { cwd: process.cwd() });
139
+ if (files.length === 0) {
140
+ console.log(`No test files found matching pattern: ${pattern}`);
141
+ return;
142
+ }
143
+ console.log(`Found ${files.length} test file(s)\n`);
144
+ // Set the global runner before loading any files
145
+ G.__testRunner = this;
109
146
  for (const file of files) {
110
- require(path.resolve(file));
147
+ const fullPath = path.resolve(file);
148
+ // Clear require cache for this file to allow hot reloading
149
+ delete require.cache[fullPath];
150
+ try {
151
+ require(fullPath);
152
+ }
153
+ catch (error) {
154
+ console.error(`Error loading test file ${file}:`, error.message);
155
+ throw error;
156
+ }
111
157
  }
112
158
  }
113
159
  }
114
160
  exports.TestRunner = TestRunner;
161
+ /* ======================================================
162
+ * Singleton instance
163
+ * ====================================================== */
164
+ let runnerInstance = null;
165
+ function getTestRunner() {
166
+ if (!runnerInstance) {
167
+ runnerInstance = new TestRunner();
168
+ G.__testRunner = runnerInstance;
169
+ }
170
+ return runnerInstance;
171
+ }
172
+ function initTestRunner() {
173
+ return getTestRunner();
174
+ }
175
+ /* ======================================================
176
+ * Auto-initialization for test environment
177
+ * ====================================================== */
178
+ // Auto-initialize when in test environment
179
+ if (process.env.NODE_ENV === "test" ||
180
+ process.argv.some((arg) => arg.includes("test"))) {
181
+ if (!G.__testRunner) {
182
+ getTestRunner();
183
+ }
184
+ }
115
185
  /* ======================================================
116
186
  * Global test helpers
117
187
  * ====================================================== */
118
188
  function describe(name, fn) {
119
189
  if (!G.__testRunner) {
120
- throw new Error("TestRunner not initialized");
190
+ // Try to auto-initialize
191
+ getTestRunner();
192
+ }
193
+ if (!G.__testRunner) {
194
+ throw new Error("TestRunner not initialized. Make sure to call initTestRunner() or run tests via 'fragment test' command");
121
195
  }
122
196
  G.__testRunner.describe(name, fn);
123
197
  }
@@ -166,20 +240,25 @@ function expect(actual) {
166
240
  throw new Error(`Expected ${actual} to be falsy`);
167
241
  }
168
242
  },
169
- toThrow() {
243
+ toThrow(expectedError) {
170
244
  if (typeof actual !== "function") {
171
245
  throw new Error("toThrow expects a function");
172
246
  }
173
247
  let threw = false;
248
+ let error = null;
174
249
  try {
175
250
  actual();
176
251
  }
177
- catch {
252
+ catch (e) {
178
253
  threw = true;
254
+ error = e;
179
255
  }
180
256
  if (!threw) {
181
257
  throw new Error("Expected function to throw an error");
182
258
  }
259
+ if (expectedError && error?.message !== expectedError) {
260
+ throw new Error(`Expected error message "${expectedError}" but got "${error?.message}"`);
261
+ }
183
262
  },
184
263
  toBeInstanceOf(expected) {
185
264
  if (!(actual instanceof expected)) {
@@ -194,9 +273,12 @@ function expect(actual) {
194
273
  throw new Error(`Expected ${actual} to contain ${expected}`);
195
274
  }
196
275
  },
197
- toHaveProperty(prop) {
198
- if (actual == null || !(prop in actual)) {
199
- throw new Error(`Expected object to have property "${prop}"`);
276
+ toHaveProperty(property, value) {
277
+ if (actual == null || !(property in actual)) {
278
+ throw new Error(`Expected object to have property "${property}"`);
279
+ }
280
+ if (value !== undefined && actual[property] !== value) {
281
+ throw new Error(`Expected property "${property}" to be ${value} but got ${actual[property]}`);
200
282
  }
201
283
  },
202
284
  toBeNull() {
@@ -220,17 +302,22 @@ function expect(actual) {
220
302
  };
221
303
  }
222
304
  /* ======================================================
223
- * CLI entry
305
+ * CLI entry (when this file is run directly)
224
306
  * ====================================================== */
225
307
  if (require.main === module) {
226
- const runner = new TestRunner();
227
- G.__testRunner = runner;
228
- runner
229
- .loadTestFiles("dist/**/*.spec.js")
230
- .then(() => runner.run())
231
- .catch((err) => {
232
- console.error("Failed to run tests:", err);
233
- process.exit(1);
234
- });
308
+ (async () => {
309
+ try {
310
+ const runner = getTestRunner();
311
+ // Auto-detect TypeScript based on arguments
312
+ const isTsNode = process.argv.some((arg) => arg.includes("ts-node") || arg.includes("ts-node/register"));
313
+ const pattern = isTsNode ? "src/**/*.spec.ts" : "dist/**/*.spec.js";
314
+ await runner.loadTestFiles(pattern);
315
+ await runner.run();
316
+ }
317
+ catch (err) {
318
+ console.error("Failed to run tests:", err.message);
319
+ process.exit(1);
320
+ }
321
+ })();
235
322
  }
236
323
  //# sourceMappingURL=runner.js.map