@oalacea/demon 1.0.0

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,297 @@
1
+ /**
2
+ * Demon - Reporter
3
+ *
4
+ * Generates test reports and summaries.
5
+ */
6
+
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+
10
+ /**
11
+ * Generate test report
12
+ */
13
+ function generateReport(results, options = {}) {
14
+ const report = {
15
+ summary: generateSummary(results),
16
+ details: results,
17
+ recommendations: generateRecommendations(results),
18
+ timestamp: new Date().toISOString(),
19
+ };
20
+
21
+ if (options.output) {
22
+ const outputPath = path.resolve(options.output);
23
+ fs.writeFileSync(outputPath, JSON.stringify(report, null, 2));
24
+ }
25
+
26
+ return report;
27
+ }
28
+
29
+ /**
30
+ * Generate summary
31
+ */
32
+ function generateSummary(results) {
33
+ const summary = {
34
+ totalTests: 0,
35
+ passing: 0,
36
+ failing: 0,
37
+ skipped: 0,
38
+ coverage: results.coverage || null,
39
+ performance: results.performance || null,
40
+ };
41
+
42
+ // Count unit tests
43
+ if (results.unit) {
44
+ summary.totalTests += results.unit.total || 0;
45
+ summary.passing += results.unit.passing || 0;
46
+ summary.failing += results.unit.failing || 0;
47
+ summary.skipped += results.unit.skipped || 0;
48
+ }
49
+
50
+ // Count integration tests
51
+ if (results.integration) {
52
+ summary.totalTests += results.integration.total || 0;
53
+ summary.passing += results.integration.passing || 0;
54
+ summary.failing += results.integration.failing || 0;
55
+ summary.skipped += results.integration.skipped || 0;
56
+ }
57
+
58
+ // Count E2E tests
59
+ if (results.e2e) {
60
+ summary.totalTests += results.e2e.total || 0;
61
+ summary.passing += results.e2e.passing || 0;
62
+ summary.failing += results.e2e.failing || 0;
63
+ summary.skipped += results.e2e.skipped || 0;
64
+ }
65
+
66
+ summary.passRate = summary.totalTests > 0
67
+ ? ((summary.passing / summary.totalTests) * 100).toFixed(2) + '%'
68
+ : 'N/A';
69
+
70
+ return summary;
71
+ }
72
+
73
+ /**
74
+ * Generate recommendations
75
+ */
76
+ function generateRecommendations(results) {
77
+ const recommendations = [];
78
+
79
+ // Coverage recommendations
80
+ if (results.coverage) {
81
+ const coverage = results.coverage;
82
+ if (coverage.lines && coverage.lines < 80) {
83
+ recommendations.push({
84
+ type: 'coverage',
85
+ priority: 'high',
86
+ message: `Line coverage is ${coverage.lines}%. Aim for at least 80%.`,
87
+ });
88
+ }
89
+ if (coverage.branches && coverage.branches < 80) {
90
+ recommendations.push({
91
+ type: 'coverage',
92
+ priority: 'medium',
93
+ message: `Branch coverage is ${coverage.branches}%. Add tests for conditional logic.`,
94
+ });
95
+ }
96
+ }
97
+
98
+ // Performance recommendations
99
+ if (results.performance) {
100
+ const perf = results.performance;
101
+ if (perf.p95 && perf.p95 > 200) {
102
+ recommendations.push({
103
+ type: 'performance',
104
+ priority: 'high',
105
+ message: `API p95 is ${perf.p95}ms. Target is <200ms. Consider optimization.`,
106
+ });
107
+ }
108
+ if (perf.failureRate && perf.failureRate > 1) {
109
+ recommendations.push({
110
+ type: 'performance',
111
+ priority: 'high',
112
+ message: `Failure rate is ${perf.failureRate}%. Check for infrastructure issues.`,
113
+ });
114
+ }
115
+ }
116
+
117
+ // Test failure recommendations
118
+ if (results.unit && results.unit.failing > 0) {
119
+ recommendations.push({
120
+ type: 'tests',
121
+ priority: 'high',
122
+ message: `${results.unit.failing} unit test(s) failing. Review and fix.`,
123
+ });
124
+ }
125
+
126
+ if (results.e2e && results.e2e.failing > 0) {
127
+ recommendations.push({
128
+ type: 'tests',
129
+ priority: 'medium',
130
+ message: `${results.e2e.failing} E2E test(s) failing. Check for flaky tests.`,
131
+ });
132
+ }
133
+
134
+ return recommendations;
135
+ }
136
+
137
+ /**
138
+ * Format report as markdown
139
+ */
140
+ function formatMarkdown(report) {
141
+ const lines = [];
142
+
143
+ lines.push('# Demon Test Report');
144
+ lines.push('');
145
+ lines.push(`**Generated:** ${new Date(report.timestamp).toLocaleString()}`);
146
+ lines.push('');
147
+
148
+ // Summary
149
+ lines.push('## Summary');
150
+ lines.push('');
151
+ lines.push('| Metric | Value |');
152
+ lines.push('|--------|-------|');
153
+ lines.push(`| Total Tests | ${report.summary.totalTests} |`);
154
+ lines.push(`| Passing | ${report.summary.passing} |`);
155
+ lines.push(`| Failing | ${report.summary.failing} |`);
156
+ lines.push(`| Skipped | ${report.summary.skipped} |`);
157
+ lines.push(`| Pass Rate | ${report.summary.passRate} |`);
158
+
159
+ if (report.summary.coverage) {
160
+ lines.push(`| Coverage | ${report.summary.coverage.lines || 'N/A'}% (lines) |`);
161
+ }
162
+
163
+ if (report.summary.performance) {
164
+ const perf = report.summary.performance;
165
+ lines.push(`| API p95 | ${perf.p95 || 'N/A'}ms |`);
166
+ lines.push(`| API p99 | ${perf.p99 || 'N/A'}ms |`);
167
+ }
168
+
169
+ lines.push('');
170
+
171
+ // Recommendations
172
+ if (report.recommendations.length > 0) {
173
+ lines.push('## Recommendations');
174
+ lines.push('');
175
+
176
+ for (const rec of report.recommendations) {
177
+ const icon = rec.priority === 'high' ? '🔴' : rec.priority === 'medium' ? '🟡' : '🟢';
178
+ lines.push(`### ${icon} ${rec.type}`);
179
+ lines.push('');
180
+ lines.push(rec.message);
181
+ lines.push('');
182
+ }
183
+ }
184
+
185
+ // Details by layer
186
+ lines.push('## Details by Layer');
187
+ lines.push('');
188
+
189
+ for (const [layer, data] of Object.entries(report.details)) {
190
+ if (!data || typeof data !== 'object') continue;
191
+
192
+ lines.push(`### ${layer.charAt(0).toUpperCase() + layer.slice(1)}`);
193
+ lines.push('');
194
+ lines.push(`| Tests | Pass | Fail | Skip |`);
195
+ lines.push(`|-------|------|------|------|`);
196
+ lines.push(
197
+ `| ${data.total || 0} | ${data.passing || 0} | ${data.failing || 0} | ${data.skipped || 0} |`
198
+ );
199
+ lines.push('');
200
+ }
201
+
202
+ return lines.join('\n');
203
+ }
204
+
205
+ /**
206
+ * Format report as JSON
207
+ */
208
+ function formatJSON(report) {
209
+ return JSON.stringify(report, null, 2);
210
+ }
211
+
212
+ /**
213
+ * Format report as console output
214
+ */
215
+ function formatConsole(report) {
216
+ const lines = [];
217
+
218
+ lines.push('');
219
+ lines.push('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
220
+ lines.push(' Demon Test Report');
221
+ lines.push('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
222
+ lines.push('');
223
+
224
+ const summary = report.summary;
225
+
226
+ lines.push(` Total Tests: ${summary.totalTests}`);
227
+ lines.push(` Passing: ${summary.passing} ✓`);
228
+ lines.push(` Failing: ${summary.failing} ✗`);
229
+ lines.push(` Skipped: ${summary.skipped} ○`);
230
+ lines.push(` Pass Rate: ${summary.passRate}`);
231
+
232
+ if (summary.coverage) {
233
+ lines.push(` Coverage: ${summary.coverage.lines || 'N/A'}%`);
234
+ }
235
+
236
+ if (summary.performance) {
237
+ lines.push(` API p95: ${summary.performance.p95 || 'N/A'}ms`);
238
+ }
239
+
240
+ lines.push('');
241
+
242
+ if (report.recommendations.length > 0) {
243
+ lines.push(' Recommendations:');
244
+ for (const rec of report.recommendations) {
245
+ const icon = rec.priority === 'high' ? '✗' : rec.priority === 'medium' ? '⚠' : 'ℹ';
246
+ lines.push(` ${icon} ${rec.message}`);
247
+ }
248
+ lines.push('');
249
+ }
250
+
251
+ lines.push('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
252
+ lines.push('');
253
+
254
+ return lines.join('\n');
255
+ }
256
+
257
+ /**
258
+ * Save report to file
259
+ */
260
+ function saveReport(report, format, outputPath) {
261
+ let content;
262
+
263
+ switch (format) {
264
+ case 'markdown':
265
+ content = formatMarkdown(report);
266
+ break;
267
+ case 'json':
268
+ content = formatJSON(report);
269
+ break;
270
+ case 'console':
271
+ content = formatConsole(report);
272
+ break;
273
+ default:
274
+ throw new Error(`Unknown format: ${format}`);
275
+ }
276
+
277
+ fs.writeFileSync(outputPath, content);
278
+ return outputPath;
279
+ }
280
+
281
+ /**
282
+ * Print report to console
283
+ */
284
+ function printReport(report) {
285
+ console.log(formatConsole(report));
286
+ }
287
+
288
+ module.exports = {
289
+ generateReport,
290
+ generateSummary,
291
+ generateRecommendations,
292
+ formatMarkdown,
293
+ formatJSON,
294
+ formatConsole,
295
+ saveReport,
296
+ printReport,
297
+ };
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@oalacea/demon",
3
+ "version": "1.0.0",
4
+ "description": "AI-powered automated test generation and remediation for web applications",
5
+ "author": "Yanis",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/Pamacea/demon"
10
+ },
11
+ "bin": {
12
+ "demon": "./bin/cli.js"
13
+ },
14
+ "scripts": {
15
+ "test": "echo \"Run: npx @oalacea/demon\" && exit 0"
16
+ },
17
+ "keywords": [
18
+ "testing",
19
+ "automation",
20
+ "ai",
21
+ "e2e",
22
+ "unit-tests",
23
+ "integration-tests",
24
+ "performance",
25
+ "vitest",
26
+ "playwright",
27
+ "k6"
28
+ ],
29
+ "dependencies": {},
30
+ "devDependencies": {},
31
+ "engines": {
32
+ "node": ">=18.0.0"
33
+ }
34
+ }