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.
- package/dist/cli/commands/init.command.js +1 -1
- package/dist/cli/commands/test.command.d.ts +1 -0
- package/dist/cli/commands/test.command.d.ts.map +1 -1
- package/dist/cli/commands/test.command.js +149 -173
- package/dist/cli/commands/test.command.js.map +1 -1
- package/dist/testing/runner.d.ts +5 -2
- package/dist/testing/runner.d.ts.map +1 -1
- package/dist/testing/runner.js +110 -23
- package/dist/testing/runner.js.map +1 -1
- package/dist/web/application.d.ts.map +1 -1
- package/dist/web/application.js +1 -0
- package/dist/web/application.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/commands/init.command.ts +1 -1
- package/src/cli/commands/test.command.ts +188 -173
- package/src/testing/runner.ts +138 -23
- package/src/web/application.ts +1 -0
package/src/testing/runner.ts
CHANGED
|
@@ -36,6 +36,12 @@ export class TestRunner {
|
|
|
36
36
|
private suites: TestSuite[] = [];
|
|
37
37
|
private passed = 0;
|
|
38
38
|
private failed = 0;
|
|
39
|
+
private errors: Array<{
|
|
40
|
+
suite: string;
|
|
41
|
+
test: string;
|
|
42
|
+
error: string;
|
|
43
|
+
stack?: string;
|
|
44
|
+
}> = [];
|
|
39
45
|
|
|
40
46
|
describe(name: string, fn: () => void): void {
|
|
41
47
|
const suite: TestSuite = {
|
|
@@ -71,56 +77,144 @@ export class TestRunner {
|
|
|
71
77
|
}
|
|
72
78
|
|
|
73
79
|
async run(): Promise<void> {
|
|
74
|
-
console.log("\
|
|
80
|
+
console.log("\n๐งช Running Fragment Tests\n");
|
|
81
|
+
|
|
82
|
+
if (this.suites.length === 0) {
|
|
83
|
+
console.log("No test suites found");
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
75
86
|
|
|
76
87
|
for (const suite of this.suites) {
|
|
77
|
-
console.log(`\
|
|
88
|
+
console.log(`\n๐ฆ ${suite.name}`);
|
|
89
|
+
|
|
90
|
+
if (suite.tests.length === 0) {
|
|
91
|
+
console.log(" (no tests)");
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
78
94
|
|
|
79
95
|
for (const test of suite.tests) {
|
|
80
96
|
try {
|
|
97
|
+
// Run beforeEach hooks
|
|
81
98
|
for (const hook of suite.beforeEachHooks) {
|
|
82
99
|
await hook();
|
|
83
100
|
}
|
|
84
101
|
|
|
102
|
+
// Run the test
|
|
85
103
|
await test.fn();
|
|
86
104
|
|
|
105
|
+
// Run afterEach hooks
|
|
87
106
|
for (const hook of suite.afterEachHooks) {
|
|
88
107
|
await hook();
|
|
89
108
|
}
|
|
90
109
|
|
|
91
|
-
console.log(`
|
|
110
|
+
console.log(` โ ${test.name}`);
|
|
92
111
|
this.passed++;
|
|
93
112
|
} catch (error: any) {
|
|
94
|
-
console.log(`
|
|
113
|
+
console.log(` โ ${test.name}`);
|
|
95
114
|
console.error(` ${error?.message ?? error}`);
|
|
96
115
|
this.failed++;
|
|
116
|
+
this.errors.push({
|
|
117
|
+
suite: suite.name,
|
|
118
|
+
test: test.name,
|
|
119
|
+
error: error?.message ?? String(error),
|
|
120
|
+
stack: error?.stack,
|
|
121
|
+
});
|
|
97
122
|
}
|
|
98
123
|
}
|
|
99
124
|
}
|
|
100
125
|
|
|
101
|
-
|
|
126
|
+
// Print summary
|
|
127
|
+
console.log(
|
|
128
|
+
`\n\n๐ Results: ${this.passed} passed, ${this.failed} failed\n`,
|
|
129
|
+
);
|
|
102
130
|
|
|
103
131
|
if (this.failed > 0) {
|
|
132
|
+
console.log("โ Failed Tests:\n");
|
|
133
|
+
this.errors.forEach((err) => {
|
|
134
|
+
console.log(` ${err.suite} > ${err.test}`);
|
|
135
|
+
console.log(` ${err.error}\n`);
|
|
136
|
+
});
|
|
104
137
|
process.exit(1);
|
|
138
|
+
} else {
|
|
139
|
+
console.log("โ
All tests passed!\n");
|
|
140
|
+
process.exit(0);
|
|
105
141
|
}
|
|
106
142
|
}
|
|
107
143
|
|
|
108
144
|
async loadTestFiles(pattern: string): Promise<void> {
|
|
109
|
-
const files = await glob(pattern);
|
|
145
|
+
const files = await glob(pattern, { cwd: process.cwd() });
|
|
146
|
+
|
|
147
|
+
if (files.length === 0) {
|
|
148
|
+
console.log(`No test files found matching pattern: ${pattern}`);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
console.log(`Found ${files.length} test file(s)\n`);
|
|
153
|
+
|
|
154
|
+
// Set the global runner before loading any files
|
|
155
|
+
G.__testRunner = this;
|
|
110
156
|
|
|
111
157
|
for (const file of files) {
|
|
112
|
-
|
|
158
|
+
const fullPath = path.resolve(file);
|
|
159
|
+
|
|
160
|
+
// Clear require cache for this file to allow hot reloading
|
|
161
|
+
delete require.cache[fullPath];
|
|
162
|
+
|
|
163
|
+
try {
|
|
164
|
+
require(fullPath);
|
|
165
|
+
} catch (error: any) {
|
|
166
|
+
console.error(`Error loading test file ${file}:`, error.message);
|
|
167
|
+
throw error;
|
|
168
|
+
}
|
|
113
169
|
}
|
|
114
170
|
}
|
|
115
171
|
}
|
|
116
172
|
|
|
173
|
+
/* ======================================================
|
|
174
|
+
* Singleton instance
|
|
175
|
+
* ====================================================== */
|
|
176
|
+
let runnerInstance: TestRunner | null = null;
|
|
177
|
+
|
|
178
|
+
export function getTestRunner(): TestRunner {
|
|
179
|
+
if (!runnerInstance) {
|
|
180
|
+
runnerInstance = new TestRunner();
|
|
181
|
+
G.__testRunner = runnerInstance;
|
|
182
|
+
}
|
|
183
|
+
return runnerInstance;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export function initTestRunner(): TestRunner {
|
|
187
|
+
return getTestRunner();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/* ======================================================
|
|
191
|
+
* Auto-initialization for test environment
|
|
192
|
+
* ====================================================== */
|
|
193
|
+
// Auto-initialize when in test environment
|
|
194
|
+
if (
|
|
195
|
+
process.env.NODE_ENV === "test" ||
|
|
196
|
+
process.argv.some((arg) => arg.includes("test"))
|
|
197
|
+
) {
|
|
198
|
+
if (!G.__testRunner) {
|
|
199
|
+
getTestRunner();
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
117
203
|
/* ======================================================
|
|
118
204
|
* Global test helpers
|
|
119
205
|
* ====================================================== */
|
|
120
206
|
export function describe(name: string, fn: () => void): void {
|
|
121
207
|
if (!G.__testRunner) {
|
|
122
|
-
|
|
208
|
+
// Try to auto-initialize
|
|
209
|
+
getTestRunner();
|
|
123
210
|
}
|
|
211
|
+
|
|
212
|
+
if (!G.__testRunner) {
|
|
213
|
+
throw new Error(
|
|
214
|
+
"TestRunner not initialized. Make sure to call initTestRunner() or run tests via 'fragment test' command",
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
124
218
|
G.__testRunner.describe(name, fn);
|
|
125
219
|
}
|
|
126
220
|
|
|
@@ -176,21 +270,29 @@ export function expect(actual: any) {
|
|
|
176
270
|
}
|
|
177
271
|
},
|
|
178
272
|
|
|
179
|
-
toThrow() {
|
|
273
|
+
toThrow(expectedError?: string) {
|
|
180
274
|
if (typeof actual !== "function") {
|
|
181
275
|
throw new Error("toThrow expects a function");
|
|
182
276
|
}
|
|
183
277
|
|
|
184
278
|
let threw = false;
|
|
279
|
+
let error = null;
|
|
185
280
|
try {
|
|
186
281
|
actual();
|
|
187
|
-
} catch {
|
|
282
|
+
} catch (e: any) {
|
|
188
283
|
threw = true;
|
|
284
|
+
error = e;
|
|
189
285
|
}
|
|
190
286
|
|
|
191
287
|
if (!threw) {
|
|
192
288
|
throw new Error("Expected function to throw an error");
|
|
193
289
|
}
|
|
290
|
+
|
|
291
|
+
if (expectedError && error?.message !== expectedError) {
|
|
292
|
+
throw new Error(
|
|
293
|
+
`Expected error message "${expectedError}" but got "${error?.message}"`,
|
|
294
|
+
);
|
|
295
|
+
}
|
|
194
296
|
},
|
|
195
297
|
|
|
196
298
|
toBeInstanceOf(expected: any) {
|
|
@@ -209,9 +311,15 @@ export function expect(actual: any) {
|
|
|
209
311
|
}
|
|
210
312
|
},
|
|
211
313
|
|
|
212
|
-
toHaveProperty(
|
|
213
|
-
if (actual == null || !(
|
|
214
|
-
throw new Error(`Expected object to have property "${
|
|
314
|
+
toHaveProperty(property: string, value?: any) {
|
|
315
|
+
if (actual == null || !(property in actual)) {
|
|
316
|
+
throw new Error(`Expected object to have property "${property}"`);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (value !== undefined && actual[property] !== value) {
|
|
320
|
+
throw new Error(
|
|
321
|
+
`Expected property "${property}" to be ${value} but got ${actual[property]}`,
|
|
322
|
+
);
|
|
215
323
|
}
|
|
216
324
|
},
|
|
217
325
|
|
|
@@ -240,17 +348,24 @@ export function expect(actual: any) {
|
|
|
240
348
|
}
|
|
241
349
|
|
|
242
350
|
/* ======================================================
|
|
243
|
-
* CLI entry
|
|
351
|
+
* CLI entry (when this file is run directly)
|
|
244
352
|
* ====================================================== */
|
|
245
353
|
if (require.main === module) {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
354
|
+
(async () => {
|
|
355
|
+
try {
|
|
356
|
+
const runner = getTestRunner();
|
|
357
|
+
|
|
358
|
+
// Auto-detect TypeScript based on arguments
|
|
359
|
+
const isTsNode = process.argv.some(
|
|
360
|
+
(arg) => arg.includes("ts-node") || arg.includes("ts-node/register"),
|
|
361
|
+
);
|
|
362
|
+
const pattern = isTsNode ? "src/**/*.spec.ts" : "dist/**/*.spec.js";
|
|
363
|
+
|
|
364
|
+
await runner.loadTestFiles(pattern);
|
|
365
|
+
await runner.run();
|
|
366
|
+
} catch (err: any) {
|
|
367
|
+
console.error("Failed to run tests:", err.message);
|
|
254
368
|
process.exit(1);
|
|
255
|
-
}
|
|
369
|
+
}
|
|
370
|
+
})();
|
|
256
371
|
}
|