fragment-ts 1.0.18 → 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.
@@ -17,6 +17,7 @@ export class TestCommand {
17
17
  )
18
18
  .option("--pattern <pattern>", "Test file pattern", "**/*.spec.ts")
19
19
  .option("--coverage", "Generate coverage report")
20
+ .option("--no-color", "Disable colored output")
20
21
  .action(async (options) => {
21
22
  await this.runTests(options);
22
23
  });
@@ -57,7 +58,7 @@ export class TestCommand {
57
58
  useTypeScript = false;
58
59
  mode = "production (dist/)";
59
60
  basePath = "dist";
60
- pattern = options.pattern.replace(".ts", ".js") || "**/*.spec.js";
61
+ pattern = options.pattern.replace(/\.ts$/, ".js") || "**/*.spec.js";
61
62
  } else {
62
63
  // Auto-detect
63
64
  if (hasSource) {
@@ -69,7 +70,7 @@ export class TestCommand {
69
70
  useTypeScript = false;
70
71
  mode = "auto-detected production (dist/)";
71
72
  basePath = "dist";
72
- pattern = options.pattern.replace(".ts", ".js") || "**/*.spec.js";
73
+ pattern = options.pattern.replace(/\.ts$/, ".js") || "**/*.spec.js";
73
74
  } else {
74
75
  console.log(
75
76
  chalk.red("No src/ or dist/ directory found. Run: fragment init"),
@@ -78,189 +79,62 @@ export class TestCommand {
78
79
  }
79
80
  }
80
81
 
81
- console.log(chalk.gray(` Mode: ${mode}\n`));
82
+ console.log(chalk.gray(` Mode: ${mode}`));
83
+ console.log(chalk.gray(` Pattern: ${basePath}/${pattern}`));
82
84
 
83
- const scriptContent = `
84
- ${useTypeScript ? "require('ts-node/register/transpile-only');" : ""}
85
- require('reflect-metadata');
86
-
87
- const { glob } = require('glob');
88
- const path = require('path');
89
-
90
- // Global test state
91
- const testState = {
92
- suites: [],
93
- currentSuite: null,
94
- passed: 0,
95
- failed: 0,
96
- errors: []
97
- };
98
-
99
- // Global test functions
100
- global.describe = function(name, fn) {
101
- const suite = { name, tests: [] };
102
- testState.suites.push(suite);
103
- testState.currentSuite = suite;
104
- fn();
105
- testState.currentSuite = null;
106
- };
107
-
108
- global.it = function(name, fn) {
109
- if (!testState.currentSuite) {
110
- throw new Error('it() must be called inside describe()');
111
- }
112
- testState.currentSuite.tests.push({ name, fn });
113
- };
114
-
115
- global.expect = function(actual) {
116
- return {
117
- toBe(expected) {
118
- if (actual !== expected) {
119
- throw new Error(\`Expected \${actual} to be \${expected}\`);
120
- }
121
- },
122
- toEqual(expected) {
123
- if (JSON.stringify(actual) !== JSON.stringify(expected)) {
124
- throw new Error(\`Expected \${JSON.stringify(actual)} to equal \${JSON.stringify(expected)}\`);
125
- }
126
- },
127
- toBeTruthy() {
128
- if (!actual) {
129
- throw new Error(\`Expected \${actual} to be truthy\`);
130
- }
131
- },
132
- toBeFalsy() {
133
- if (actual) {
134
- throw new Error(\`Expected \${actual} to be falsy\`);
135
- }
136
- },
137
- toThrow(expectedError) {
138
- let threw = false;
139
- let error = null;
140
- try {
141
- actual();
142
- } catch (e) {
143
- threw = true;
144
- error = e;
145
- }
146
- if (!threw) {
147
- throw new Error('Expected function to throw');
148
- }
149
- if (expectedError && error.message !== expectedError) {
150
- throw new Error(\`Expected error message "\${expectedError}" but got "\${error.message}"\`);
151
- }
152
- },
153
- toBeInstanceOf(expected) {
154
- if (!(actual instanceof expected)) {
155
- throw new Error(\`Expected \${actual} to be instance of \${expected.name}\`);
156
- }
157
- },
158
- toContain(expected) {
159
- if (!actual.includes(expected)) {
160
- throw new Error(\`Expected \${actual} to contain \${expected}\`);
161
- }
162
- },
163
- toHaveProperty(property, value) {
164
- if (!(property in actual)) {
165
- throw new Error(\`Expected object to have property \${property}\`);
166
- }
167
- if (value !== undefined && actual[property] !== value) {
168
- throw new Error(\`Expected property \${property} to be \${value} but got \${actual[property]}\`);
169
- }
170
- },
171
- toBeNull() {
172
- if (actual !== null) {
173
- throw new Error(\`Expected \${actual} to be null\`);
174
- }
175
- },
176
- toBeUndefined() {
177
- if (actual !== undefined) {
178
- throw new Error(\`Expected \${actual} to be undefined\`);
179
- }
180
- },
181
- toHaveLength(expected) {
182
- if (actual.length !== expected) {
183
- throw new Error(\`Expected length \${expected} but got \${actual.length}\`);
184
- }
185
- }
186
- };
187
- };
188
-
189
- async function runTests() {
190
- try {
191
- // Find and load test files
192
- const files = await glob('${basePath}/${pattern}', { cwd: process.cwd() });
193
-
194
- if (files.length === 0) {
195
- console.log('No test files found matching pattern: ${basePath}/${pattern}');
196
- process.exit(0);
197
- }
85
+ // Check if we need to use ts-node for TypeScript files
86
+ const tsConfigPath = path.join(cwd, "tsconfig.json");
87
+ const hasTsConfig = fs.existsSync(tsConfigPath);
198
88
 
199
- console.log(\`Found \${files.length} test file(s)\\n\`);
200
-
201
- // Load all test files
202
- for (const file of files) {
203
- require(path.resolve(file));
204
- }
205
-
206
- // Run all tests
207
- for (const suite of testState.suites) {
208
- console.log(\`\\n📦 \${suite.name}\`);
209
-
210
- for (const test of suite.tests) {
211
- try {
212
- await test.fn();
213
- console.log(\` ✓ \${test.name}\`);
214
- testState.passed++;
215
- } catch (error) {
216
- console.log(\` ✗ \${test.name}\`);
217
- console.error(\` \${error.message}\`);
218
- testState.failed++;
219
- testState.errors.push({
220
- suite: suite.name,
221
- test: test.name,
222
- error: error.message,
223
- stack: error.stack
224
- });
225
- }
226
- }
227
- }
228
-
229
- // Print summary
230
- console.log(\`\\n\\n📊 Results: \${testState.passed} passed, \${testState.failed} failed\\n\`);
231
-
232
- if (testState.failed > 0) {
233
- console.log('\\n❌ Failed Tests:\\n');
234
- testState.errors.forEach(err => {
235
- console.log(\` \${err.suite} > \${err.test}\`);
236
- console.log(\` \${err.error}\\n\`);
237
- });
238
- process.exit(1);
239
- } else {
240
- console.log('✅ All tests passed!\\n');
241
- process.exit(0);
242
- }
243
- } catch (error) {
244
- console.error('Error running tests:', error);
245
- process.exit(1);
246
- }
247
- }
248
-
249
- runTests();
250
- `;
89
+ // Create a simple runner script that uses our test runner module
90
+ const scriptContent = this.generateRunnerScript({
91
+ useTypeScript,
92
+ basePath,
93
+ pattern,
94
+ hasTsConfig,
95
+ tsConfigPath,
96
+ watchMode: options.watch,
97
+ coverage: options.coverage,
98
+ noColor: options.color === false,
99
+ });
251
100
 
252
101
  const scriptPath = path.join(cwd, ".fragment-test-runner.js");
253
102
 
254
103
  try {
255
104
  fs.writeFileSync(scriptPath, scriptContent);
256
105
 
106
+ // Make the script executable
107
+ fs.chmodSync(scriptPath, "755");
108
+
257
109
  const nodeArgs = [scriptPath];
258
110
  const env = {
259
111
  ...process.env,
260
112
  TS_NODE_TRANSPILE_ONLY: "true",
261
113
  NODE_ENV: "test",
114
+ // Only set TS_NODE_PROJECT if tsconfig exists
115
+ ...(hasTsConfig && useTypeScript
116
+ ? { TS_NODE_PROJECT: tsConfigPath }
117
+ : {}),
118
+ // Handle color output
119
+ FORCE_COLOR: options.color === false ? "0" : "1",
262
120
  };
263
121
 
122
+ // Add --watch support
123
+ if (options.watch) {
124
+ console.log(
125
+ chalk.yellow(
126
+ "\n⚠️ Watch mode is not yet implemented. Running once.\n",
127
+ ),
128
+ );
129
+ }
130
+
131
+ // Add --coverage support
132
+ if (options.coverage) {
133
+ console.log(
134
+ chalk.yellow("\n⚠️ Coverage reporting is not yet implemented.\n"),
135
+ );
136
+ }
137
+
264
138
  const proc = spawn("node", nodeArgs, {
265
139
  cwd,
266
140
  stdio: "inherit",
@@ -270,7 +144,9 @@ runTests();
270
144
  proc.on("close", (code) => {
271
145
  try {
272
146
  fs.unlinkSync(scriptPath);
273
- } catch {}
147
+ } catch (e) {
148
+ // Ignore cleanup errors
149
+ }
274
150
  process.exit(code || 0);
275
151
  });
276
152
 
@@ -278,7 +154,9 @@ runTests();
278
154
  console.error(chalk.red("Failed to run tests:"), error);
279
155
  try {
280
156
  fs.unlinkSync(scriptPath);
281
- } catch {}
157
+ } catch (e) {
158
+ // Ignore cleanup errors
159
+ }
282
160
  process.exit(1);
283
161
  });
284
162
  } catch (error: any) {
@@ -286,4 +164,141 @@ runTests();
286
164
  process.exit(1);
287
165
  }
288
166
  }
289
- }
167
+
168
+ private static generateRunnerScript(options: {
169
+ useTypeScript: boolean;
170
+ basePath: string;
171
+ pattern: string;
172
+ hasTsConfig: boolean;
173
+ tsConfigPath: string;
174
+ watchMode: boolean;
175
+ coverage: boolean;
176
+ noColor: boolean;
177
+ }): string {
178
+ const {
179
+ useTypeScript,
180
+ basePath,
181
+ pattern,
182
+ hasTsConfig,
183
+ tsConfigPath,
184
+ noColor,
185
+ } = options;
186
+
187
+ // Determine the runner path - try to find the actual test runner
188
+ // This assumes the test runner is installed as a dependency
189
+ let runnerImportPath = "fragment-ts/testing";
190
+
191
+ // Try to find the local test runner first for development
192
+ const possiblePaths = [
193
+ path.join(process.cwd(), "node_modules", "fragment-ts", "testing"),
194
+ path.join(
195
+ process.cwd(),
196
+ "node_modules",
197
+ "fragment-ts",
198
+ "dist",
199
+ "testing",
200
+ ),
201
+ path.join(__dirname, "..", "..", "testing"), // If we're in the fragment-ts package itself
202
+ ];
203
+
204
+ let resolvedRunnerPath = runnerImportPath;
205
+ for (const testPath of possiblePaths) {
206
+ if (fs.existsSync(testPath + ".ts") || fs.existsSync(testPath + ".js")) {
207
+ resolvedRunnerPath = testPath;
208
+ break;
209
+ }
210
+ }
211
+
212
+ return `
213
+ "use strict";
214
+
215
+ ${useTypeScript ? "require('ts-node/register/transpile-only');" : ""}
216
+ require('reflect-metadata');
217
+
218
+ // Set environment
219
+ process.env.NODE_ENV = 'test';
220
+ ${noColor ? "process.env.FORCE_COLOR = '0';" : "process.env.FORCE_COLOR = '1';"}
221
+
222
+ // Set up TypeScript config if available
223
+ ${
224
+ hasTsConfig && useTypeScript
225
+ ? `
226
+ try {
227
+ const tsNode = require('ts-node');
228
+ tsNode.register({
229
+ project: '${tsConfigPath}',
230
+ transpileOnly: true,
231
+ compilerOptions: {
232
+ module: 'commonjs',
233
+ target: 'ES2020',
234
+ esModuleInterop: true,
235
+ skipLibCheck: true
236
+ }
237
+ });
238
+ } catch (error) {
239
+ // Ignore, ts-node might not be available
240
+ }`
241
+ : ""
242
+ }
243
+
244
+ let testRunner;
245
+ try {
246
+ // Try to import the test runner
247
+ const testModule = require('${resolvedRunnerPath}');
248
+
249
+ // Handle different export patterns
250
+ if (testModule.getTestRunner) {
251
+ testRunner = testModule.getTestRunner();
252
+ } else if (testModule.TestRunner) {
253
+ testRunner = new testModule.TestRunner();
254
+ } else if (testModule.default && testModule.default.getTestRunner) {
255
+ testRunner = testModule.default.getTestRunner();
256
+ } else {
257
+ // Fallback to creating a new instance
258
+ testRunner = testModule;
259
+ }
260
+ } catch (error) {
261
+ console.error('Failed to load test runner:', error.message);
262
+ console.error('Make sure fragment-ts is installed as a dependency');
263
+ process.exit(1);
264
+ }
265
+
266
+ async function runTests() {
267
+ try {
268
+ console.log('Looking for test files...');
269
+
270
+ // Load test files with the runner
271
+ await testRunner.loadTestFiles('${basePath}/${pattern}');
272
+
273
+ // Run tests
274
+ await testRunner.run();
275
+ } catch (error) {
276
+ console.error('\\n❌ Error running tests:', error.message);
277
+
278
+ if (error.stack && process.env.DEBUG) {
279
+ console.error(error.stack);
280
+ }
281
+
282
+ process.exit(1);
283
+ }
284
+ }
285
+
286
+ // Handle process termination
287
+ process.on('SIGINT', () => {
288
+ console.log('\\n\\nTest run interrupted');
289
+ process.exit(130);
290
+ });
291
+
292
+ process.on('SIGTERM', () => {
293
+ console.log('\\n\\nTest run terminated');
294
+ process.exit(143);
295
+ });
296
+
297
+ // Run the tests
298
+ runTests().catch(error => {
299
+ console.error('Unhandled error:', error);
300
+ process.exit(1);
301
+ });
302
+ `;
303
+ }
304
+ }