qagentic-reporter 0.1.43 → 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.
- package/README.md +84 -23
- package/dist/cypress/index.js +140 -186
- package/dist/cypress/index.js.map +1 -1
- package/dist/cypress/index.mjs +140 -186
- package/dist/cypress/index.mjs.map +1 -1
- package/dist/cypress/simple-setup.js +132 -184
- package/dist/cypress/simple-setup.js.map +1 -1
- package/dist/cypress/simple-setup.mjs +132 -184
- package/dist/cypress/simple-setup.mjs.map +1 -1
- package/dist/index.js +64 -188
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +64 -185
- package/dist/index.mjs.map +1 -1
- package/dist/jest/index.js +142 -179
- package/dist/jest/index.js.map +1 -1
- package/dist/jest/index.mjs +142 -179
- package/dist/jest/index.mjs.map +1 -1
- package/dist/playwright/index.js +144 -183
- package/dist/playwright/index.js.map +1 -1
- package/dist/playwright/index.mjs +144 -183
- package/dist/playwright/index.mjs.map +1 -1
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
</p>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
|
-
<a href="https://www.npmjs.com/package
|
|
9
|
-
<img src="https://img.shields.io/npm/v
|
|
8
|
+
<a href="https://www.npmjs.com/package/qagentic-reporter">
|
|
9
|
+
<img src="https://img.shields.io/npm/v/qagentic-reporter.svg" alt="npm version">
|
|
10
10
|
</a>
|
|
11
11
|
<a href="https://github.com/qagentic/qagentic-sdk/blob/main/LICENSE">
|
|
12
12
|
<img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License">
|
|
@@ -18,29 +18,26 @@
|
|
|
18
18
|
## 🚀 Installation
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
|
-
npm install
|
|
21
|
+
npm install qagentic-reporter
|
|
22
22
|
# or
|
|
23
|
-
yarn add
|
|
23
|
+
yarn add qagentic-reporter
|
|
24
24
|
# or
|
|
25
|
-
pnpm add
|
|
25
|
+
pnpm add qagentic-reporter
|
|
26
26
|
```
|
|
27
27
|
|
|
28
28
|
## ⚡ Quick Start
|
|
29
29
|
|
|
30
30
|
### Cypress
|
|
31
31
|
|
|
32
|
-
```
|
|
33
|
-
// cypress.config.
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
```typescript
|
|
33
|
+
// cypress.config.ts
|
|
34
|
+
import { defineConfig } from 'cypress';
|
|
35
|
+
import { setupQAgentic } from 'qagentic-reporter/cypress/simple-setup';
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
export default defineConfig({
|
|
38
38
|
e2e: {
|
|
39
39
|
setupNodeEvents(on, config) {
|
|
40
|
-
|
|
41
|
-
projectName: 'my-project',
|
|
42
|
-
apiUrl: 'http://localhost:8080',
|
|
43
|
-
});
|
|
40
|
+
setupQAgentic(on, config); // That's it!
|
|
44
41
|
return config;
|
|
45
42
|
},
|
|
46
43
|
},
|
|
@@ -52,14 +49,14 @@ module.exports = defineConfig({
|
|
|
52
49
|
```typescript
|
|
53
50
|
// playwright.config.ts
|
|
54
51
|
import { defineConfig } from '@playwright/test';
|
|
55
|
-
import { qagenticReporter } from '
|
|
52
|
+
import { qagenticReporter } from 'qagentic-reporter/playwright';
|
|
56
53
|
|
|
57
54
|
export default defineConfig({
|
|
58
55
|
reporter: [
|
|
59
56
|
['html'],
|
|
60
57
|
qagenticReporter({
|
|
61
58
|
projectName: 'my-project',
|
|
62
|
-
apiUrl: 'http://localhost:
|
|
59
|
+
apiUrl: 'http://localhost:8000',
|
|
63
60
|
}),
|
|
64
61
|
],
|
|
65
62
|
});
|
|
@@ -72,9 +69,9 @@ export default defineConfig({
|
|
|
72
69
|
module.exports = {
|
|
73
70
|
reporters: [
|
|
74
71
|
'default',
|
|
75
|
-
['
|
|
72
|
+
['qagentic-reporter/jest', {
|
|
76
73
|
projectName: 'my-project',
|
|
77
|
-
apiUrl: 'http://localhost:
|
|
74
|
+
apiUrl: 'http://localhost:8000',
|
|
78
75
|
}],
|
|
79
76
|
],
|
|
80
77
|
};
|
|
@@ -86,7 +83,7 @@ module.exports = {
|
|
|
86
83
|
|
|
87
84
|
```javascript
|
|
88
85
|
// cypress/e2e/login.cy.js
|
|
89
|
-
import { step, feature, story, severity } from '
|
|
86
|
+
import { step, feature, story, severity } from 'qagentic-reporter/cypress';
|
|
90
87
|
|
|
91
88
|
describe('User Authentication', () => {
|
|
92
89
|
it('should login successfully', () => {
|
|
@@ -112,7 +109,7 @@ describe('User Authentication', () => {
|
|
|
112
109
|
```typescript
|
|
113
110
|
// tests/login.spec.ts
|
|
114
111
|
import { test, expect } from '@playwright/test';
|
|
115
|
-
import { step } from '
|
|
112
|
+
import { step } from 'qagentic-reporter/playwright';
|
|
116
113
|
|
|
117
114
|
test('should login successfully', async ({ page }) => {
|
|
118
115
|
await step('Navigate to login page', async () => {
|
|
@@ -134,7 +131,7 @@ test('should login successfully', async ({ page }) => {
|
|
|
134
131
|
### Attachments
|
|
135
132
|
|
|
136
133
|
```javascript
|
|
137
|
-
import { attach, attachScreenshot, attachJson } from '
|
|
134
|
+
import { attach, attachScreenshot, attachJson } from 'qagentic-reporter';
|
|
138
135
|
|
|
139
136
|
// Attach screenshot
|
|
140
137
|
attachScreenshot('path/to/screenshot.png', 'Login Page');
|
|
@@ -152,7 +149,7 @@ attachText('Log output here...', 'Console Logs');
|
|
|
152
149
|
|
|
153
150
|
```bash
|
|
154
151
|
QAGENTIC_PROJECT_NAME=my-project
|
|
155
|
-
QAGENTIC_API_URL=http://localhost:
|
|
152
|
+
QAGENTIC_API_URL=http://localhost:8000
|
|
156
153
|
QAGENTIC_API_KEY=your-api-key
|
|
157
154
|
QAGENTIC_OUTPUT_DIR=./qagentic-results
|
|
158
155
|
QAGENTIC_AI_ANALYSIS=true
|
|
@@ -169,7 +166,7 @@ project:
|
|
|
169
166
|
reporting:
|
|
170
167
|
api:
|
|
171
168
|
enabled: true
|
|
172
|
-
url: http://localhost:
|
|
169
|
+
url: http://localhost:8000
|
|
173
170
|
key: ${QAGENTIC_API_KEY}
|
|
174
171
|
local:
|
|
175
172
|
enabled: true
|
|
@@ -184,6 +181,36 @@ features:
|
|
|
184
181
|
screenshots: on_failure
|
|
185
182
|
```
|
|
186
183
|
|
|
184
|
+
## 🔌 API Schema
|
|
185
|
+
|
|
186
|
+
The SDK automatically sends a single `POST /ingest/` request at the end of each test run. No manual calls needed. For debugging or custom integrations:
|
|
187
|
+
|
|
188
|
+
```
|
|
189
|
+
POST /ingest/
|
|
190
|
+
Content-Type: application/json
|
|
191
|
+
X-API-Key: <your-api-key>
|
|
192
|
+
|
|
193
|
+
{
|
|
194
|
+
"run_name": "cypress_20260325T120000",
|
|
195
|
+
"framework": "cypress", // cypress | playwright | jest
|
|
196
|
+
"branch": "main", // from CI env or GITHUB_REF_NAME
|
|
197
|
+
"commit_hash": "abc123", // from CI env or GITHUB_SHA
|
|
198
|
+
"environment": "staging",
|
|
199
|
+
"test_cases": [
|
|
200
|
+
{
|
|
201
|
+
"name": "Auth > should login",
|
|
202
|
+
"status": "passed", // passed | failed | skipped
|
|
203
|
+
"duration_ms": 1234,
|
|
204
|
+
"error_message": null,
|
|
205
|
+
"error_type": null,
|
|
206
|
+
"error_stacktrace": null
|
|
207
|
+
}
|
|
208
|
+
]
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Response: `202 Accepted` with `{ "run_id": "uuid" }`
|
|
213
|
+
|
|
187
214
|
## 🔌 CI/CD Integration
|
|
188
215
|
|
|
189
216
|
### GitHub Actions
|
|
@@ -252,6 +279,40 @@ When connected to QAagentic server:
|
|
|
252
279
|
| `severity(level)` | Set severity level |
|
|
253
280
|
| `tag(...tags)` | Add tags |
|
|
254
281
|
|
|
282
|
+
## 🔄 Migration Guide
|
|
283
|
+
|
|
284
|
+
### Upgrading from v0.1.42 and earlier
|
|
285
|
+
|
|
286
|
+
**What changed in v0.1.43:**
|
|
287
|
+
- `APIReporter` (the old 3-call protocol) has been removed
|
|
288
|
+
- All test results are now sent via a single `POST /ingest/` call at the end of the run
|
|
289
|
+
- Jest reporter now correctly sends results to the backend (was broken in previous versions)
|
|
290
|
+
|
|
291
|
+
**What you need to do:**
|
|
292
|
+
|
|
293
|
+
For most users — nothing. If you used the standard setup (`setupQAgentic`, `QAgenticPlaywrightReporter`, or `QAgenticJestReporter`), the transition is automatic:
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
npm install qagentic-reporter@0.1.43
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
**If you explicitly used `APIReporter`** (advanced usage), remove those calls:
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
// BEFORE — remove these
|
|
303
|
+
import { APIReporter } from 'qagentic-reporter';
|
|
304
|
+
const reporter = new APIReporter();
|
|
305
|
+
await reporter.startRun(run);
|
|
306
|
+
// ...
|
|
307
|
+
await reporter.endRun(run);
|
|
308
|
+
|
|
309
|
+
// AFTER — use sendIngestPayload directly if needed
|
|
310
|
+
import { sendIngestPayload } from 'qagentic-reporter/core/ingest-client';
|
|
311
|
+
await sendIngestPayload(run, 'cypress');
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
**Why the old approach failed:** The `/api/v1/runs/*` endpoints never existed in `qagentic-core` — they were part of an old API Gateway that was removed in the platform consolidation (Epic 1). The `/ingest/` endpoint is the only supported ingestion path.
|
|
315
|
+
|
|
255
316
|
## 🤝 Contributing
|
|
256
317
|
|
|
257
318
|
See [CONTRIBUTING.md](../../CONTRIBUTING.md) for guidelines.
|
package/dist/cypress/index.js
CHANGED
|
@@ -42,7 +42,7 @@ var defaultConfig = {
|
|
|
42
42
|
environment: "local",
|
|
43
43
|
api: {
|
|
44
44
|
enabled: true,
|
|
45
|
-
url: "http://localhost:
|
|
45
|
+
url: "http://localhost:8000",
|
|
46
46
|
timeout: 3e4,
|
|
47
47
|
retryCount: 3,
|
|
48
48
|
batchSize: 100
|
|
@@ -77,6 +77,9 @@ function loadFromEnv(config) {
|
|
|
77
77
|
if (env.QAGENTIC_API_ENABLED !== void 0) {
|
|
78
78
|
config.api.enabled = env.QAGENTIC_API_ENABLED.toLowerCase() === "true";
|
|
79
79
|
}
|
|
80
|
+
if (env.QAGENTIC_URL) {
|
|
81
|
+
config.api.url = env.QAGENTIC_URL;
|
|
82
|
+
}
|
|
80
83
|
if (env.QAGENTIC_API_URL) {
|
|
81
84
|
config.api.url = env.QAGENTIC_API_URL;
|
|
82
85
|
}
|
|
@@ -284,31 +287,39 @@ var JSONReporter = class {
|
|
|
284
287
|
this.outputDir = this.config.local.outputDir;
|
|
285
288
|
}
|
|
286
289
|
startRun(run) {
|
|
287
|
-
|
|
288
|
-
fs2__namespace.
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
290
|
+
try {
|
|
291
|
+
if (!fs2__namespace.existsSync(this.outputDir)) {
|
|
292
|
+
fs2__namespace.mkdirSync(this.outputDir, { recursive: true });
|
|
293
|
+
}
|
|
294
|
+
if (this.config.local.cleanOnStart) {
|
|
295
|
+
const files = fs2__namespace.readdirSync(this.outputDir);
|
|
296
|
+
for (const file of files) {
|
|
297
|
+
if (file.endsWith(".json")) {
|
|
298
|
+
fs2__namespace.unlinkSync(path2__namespace.join(this.outputDir, file));
|
|
299
|
+
}
|
|
295
300
|
}
|
|
296
301
|
}
|
|
302
|
+
} catch (err) {
|
|
303
|
+
console.warn("[QAagentic] Warning: could not prepare output directory:", err.message);
|
|
297
304
|
}
|
|
298
305
|
}
|
|
299
306
|
endRun(run) {
|
|
300
307
|
if (!this.config.local.enabled || !this.config.local.formats.includes("json")) {
|
|
301
308
|
return;
|
|
302
309
|
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
fs2__namespace.
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
const
|
|
311
|
-
|
|
310
|
+
try {
|
|
311
|
+
const runFile = path2__namespace.join(this.outputDir, "run.json");
|
|
312
|
+
fs2__namespace.writeFileSync(runFile, JSON.stringify(this.serializeRun(run), null, 2));
|
|
313
|
+
const testsDir = path2__namespace.join(this.outputDir, "tests");
|
|
314
|
+
if (!fs2__namespace.existsSync(testsDir)) {
|
|
315
|
+
fs2__namespace.mkdirSync(testsDir, { recursive: true });
|
|
316
|
+
}
|
|
317
|
+
for (const test of run.tests) {
|
|
318
|
+
const testFile = path2__namespace.join(testsDir, `${test.id}.json`);
|
|
319
|
+
fs2__namespace.writeFileSync(testFile, JSON.stringify(this.serializeTest(test), null, 2));
|
|
320
|
+
}
|
|
321
|
+
} catch (err) {
|
|
322
|
+
console.warn("[QAagentic] Warning: could not write JSON results:", err.message);
|
|
312
323
|
}
|
|
313
324
|
}
|
|
314
325
|
reportTest(_test) {
|
|
@@ -344,17 +355,25 @@ var JUnitReporter = class {
|
|
|
344
355
|
this.outputDir = this.config.local.outputDir;
|
|
345
356
|
}
|
|
346
357
|
startRun(_run) {
|
|
347
|
-
|
|
348
|
-
fs2__namespace.
|
|
358
|
+
try {
|
|
359
|
+
if (!fs2__namespace.existsSync(this.outputDir)) {
|
|
360
|
+
fs2__namespace.mkdirSync(this.outputDir, { recursive: true });
|
|
361
|
+
}
|
|
362
|
+
} catch (err) {
|
|
363
|
+
console.warn("[QAagentic] Warning: could not create JUnit output directory:", err.message);
|
|
349
364
|
}
|
|
350
365
|
}
|
|
351
366
|
endRun(run) {
|
|
352
367
|
if (!this.config.local.enabled || !this.config.local.formats.includes("junit")) {
|
|
353
368
|
return;
|
|
354
369
|
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
370
|
+
try {
|
|
371
|
+
const xml = this.generateXml(run);
|
|
372
|
+
const junitFile = path2__namespace.join(this.outputDir, "junit.xml");
|
|
373
|
+
fs2__namespace.writeFileSync(junitFile, xml);
|
|
374
|
+
} catch (err) {
|
|
375
|
+
console.warn("[QAagentic] Warning: could not write junit.xml:", err.message);
|
|
376
|
+
}
|
|
358
377
|
}
|
|
359
378
|
reportTest(_test) {
|
|
360
379
|
}
|
|
@@ -406,155 +425,6 @@ var JUnitReporter = class {
|
|
|
406
425
|
return xml;
|
|
407
426
|
}
|
|
408
427
|
};
|
|
409
|
-
var APIReporter = class {
|
|
410
|
-
constructor(config) {
|
|
411
|
-
this.batch = [];
|
|
412
|
-
this.config = config || getConfig();
|
|
413
|
-
}
|
|
414
|
-
async startRun(run) {
|
|
415
|
-
if (!this.config.api.enabled) {
|
|
416
|
-
console.log("[APIReporter] API reporting disabled");
|
|
417
|
-
return;
|
|
418
|
-
}
|
|
419
|
-
this.currentRun = run;
|
|
420
|
-
this.batch = [];
|
|
421
|
-
try {
|
|
422
|
-
console.log("[APIReporter] Starting test run:", run.id);
|
|
423
|
-
const url = `${this.config.api.url}/api/v1/runs`;
|
|
424
|
-
console.log("[APIReporter] POST to:", url);
|
|
425
|
-
const response = await axios__default.default.post(
|
|
426
|
-
url,
|
|
427
|
-
{
|
|
428
|
-
id: run.id,
|
|
429
|
-
name: run.name,
|
|
430
|
-
project_name: run.projectName,
|
|
431
|
-
environment: run.environment,
|
|
432
|
-
start_time: run.startTime?.toISOString(),
|
|
433
|
-
labels: run.labels,
|
|
434
|
-
ci_build_id: run.ciBuildId,
|
|
435
|
-
branch: run.branch,
|
|
436
|
-
commit_hash: run.commitHash
|
|
437
|
-
},
|
|
438
|
-
{
|
|
439
|
-
headers: {
|
|
440
|
-
"Content-Type": "application/json",
|
|
441
|
-
"X-API-Key": this.config.api.key || "",
|
|
442
|
-
"X-Project": this.config.projectName
|
|
443
|
-
},
|
|
444
|
-
timeout: this.config.api.timeout
|
|
445
|
-
}
|
|
446
|
-
);
|
|
447
|
-
console.log("[APIReporter] Test run started successfully:", response.status);
|
|
448
|
-
} catch (error) {
|
|
449
|
-
console.warn("[APIReporter] Failed to register run with API:");
|
|
450
|
-
console.warn(" Error:", error.message);
|
|
451
|
-
console.warn(" Status:", error.response?.status);
|
|
452
|
-
console.warn(" Data:", error.response?.data);
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
async endRun(run) {
|
|
456
|
-
if (!this.config.api.enabled) return;
|
|
457
|
-
try {
|
|
458
|
-
console.log("[APIReporter] endRun called, batch size:", this.batch.length);
|
|
459
|
-
if (this.batch.length > 0) {
|
|
460
|
-
console.log("[APIReporter] Flushing remaining", this.batch.length, "test results");
|
|
461
|
-
await this.flushBatch();
|
|
462
|
-
console.log("[APIReporter] Batch flushed, waiting for processing...");
|
|
463
|
-
await new Promise((resolve) => setTimeout(resolve, 1500));
|
|
464
|
-
}
|
|
465
|
-
console.log("[APIReporter] Finalizing run with test counts:", {
|
|
466
|
-
total: run.total,
|
|
467
|
-
passed: run.passed,
|
|
468
|
-
failed: run.failed,
|
|
469
|
-
broken: run.broken,
|
|
470
|
-
skipped: run.skipped
|
|
471
|
-
});
|
|
472
|
-
const patchData = {
|
|
473
|
-
end_time: run.endTime?.toISOString(),
|
|
474
|
-
duration_ms: run.durationMs,
|
|
475
|
-
total: run.total,
|
|
476
|
-
passed: run.passed,
|
|
477
|
-
failed: run.failed,
|
|
478
|
-
broken: run.broken,
|
|
479
|
-
skipped: run.skipped,
|
|
480
|
-
status: "completed"
|
|
481
|
-
};
|
|
482
|
-
console.log("[APIReporter] PATCH to:", `${this.config.api.url}/api/v1/runs/${run.id}`);
|
|
483
|
-
console.log("[APIReporter] PATCH data:", patchData);
|
|
484
|
-
const response = await axios__default.default.patch(
|
|
485
|
-
`${this.config.api.url}/api/v1/runs/${run.id}`,
|
|
486
|
-
patchData,
|
|
487
|
-
{
|
|
488
|
-
headers: {
|
|
489
|
-
"Content-Type": "application/json",
|
|
490
|
-
"X-API-Key": this.config.api.key || "",
|
|
491
|
-
"X-Project": this.config.projectName
|
|
492
|
-
},
|
|
493
|
-
timeout: this.config.api.timeout
|
|
494
|
-
}
|
|
495
|
-
);
|
|
496
|
-
console.log("[APIReporter] Run finalized successfully:", response.status);
|
|
497
|
-
} catch (error) {
|
|
498
|
-
console.warn("[APIReporter] Failed to finalize run with API:");
|
|
499
|
-
console.warn(" Error:", error.message);
|
|
500
|
-
console.warn(" Status:", error.response?.status);
|
|
501
|
-
console.warn(" Data:", error.response?.data);
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
async reportTest(test) {
|
|
505
|
-
console.log("[APIReporter] reportTest called for:", test.name, "API enabled:", this.config.api.enabled);
|
|
506
|
-
if (!this.config.api.enabled) {
|
|
507
|
-
console.log("[APIReporter] API reporting disabled, skipping test report");
|
|
508
|
-
return;
|
|
509
|
-
}
|
|
510
|
-
console.log("[APIReporter] Adding test to batch:", test.name, "- batch size:", this.batch.length + 1);
|
|
511
|
-
this.batch.push(test);
|
|
512
|
-
if (this.batch.length >= this.config.api.batchSize) {
|
|
513
|
-
console.log("[APIReporter] Batch size reached, flushing batch");
|
|
514
|
-
await this.flushBatch();
|
|
515
|
-
} else {
|
|
516
|
-
console.log("[APIReporter] Batch size:", this.batch.length, "/", this.config.api.batchSize);
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
async flushBatch() {
|
|
520
|
-
if (this.batch.length === 0 || !this.currentRun) return;
|
|
521
|
-
try {
|
|
522
|
-
console.log("[APIReporter] Flushing batch of", this.batch.length, "test results");
|
|
523
|
-
const response = await axios__default.default.post(
|
|
524
|
-
`${this.config.api.url}/api/v1/runs/${this.currentRun.id}/results`,
|
|
525
|
-
this.batch.map((t) => ({
|
|
526
|
-
id: t.id,
|
|
527
|
-
name: t.name,
|
|
528
|
-
full_name: t.fullName,
|
|
529
|
-
status: t.status,
|
|
530
|
-
duration_ms: t.durationMs,
|
|
531
|
-
start_time: t.startTime?.toISOString(),
|
|
532
|
-
end_time: t.endTime?.toISOString(),
|
|
533
|
-
file_path: t.filePath,
|
|
534
|
-
error_message: t.errorMessage,
|
|
535
|
-
stack_trace: t.stackTrace,
|
|
536
|
-
error_type: t.errorType,
|
|
537
|
-
attachments: t.attachments
|
|
538
|
-
})),
|
|
539
|
-
{
|
|
540
|
-
headers: {
|
|
541
|
-
"Content-Type": "application/json",
|
|
542
|
-
"X-API-Key": this.config.api.key || ""
|
|
543
|
-
},
|
|
544
|
-
timeout: this.config.api.timeout
|
|
545
|
-
}
|
|
546
|
-
);
|
|
547
|
-
console.log("[APIReporter] Batch flushed successfully:", response.status);
|
|
548
|
-
} catch (error) {
|
|
549
|
-
console.warn("[APIReporter] Failed to send test results to API:");
|
|
550
|
-
console.warn(" Error:", error.message);
|
|
551
|
-
console.warn(" Status:", error.response?.status);
|
|
552
|
-
console.warn(" Data:", error.response?.data);
|
|
553
|
-
} finally {
|
|
554
|
-
this.batch = [];
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
};
|
|
558
428
|
var _QAgenticReporter = class _QAgenticReporter {
|
|
559
429
|
constructor(config) {
|
|
560
430
|
this.reporters = [];
|
|
@@ -578,10 +448,6 @@ var _QAgenticReporter = class _QAgenticReporter {
|
|
|
578
448
|
console.log("[QAgenticReporter] Added JUnitReporter");
|
|
579
449
|
}
|
|
580
450
|
}
|
|
581
|
-
if (this.config.api.enabled) {
|
|
582
|
-
this.reporters.push(new APIReporter(this.config));
|
|
583
|
-
console.log("[QAgenticReporter] Added APIReporter");
|
|
584
|
-
}
|
|
585
451
|
console.log("[QAgenticReporter] Initialization complete with", this.reporters.length, "reporters");
|
|
586
452
|
}
|
|
587
453
|
/**
|
|
@@ -912,8 +778,12 @@ function attach(data, name, type = "text/plain", extension) {
|
|
|
912
778
|
}
|
|
913
779
|
function attachScreenshot(data, name = "Screenshot") {
|
|
914
780
|
if (typeof data === "string" && fs2__namespace.existsSync(data)) {
|
|
915
|
-
|
|
916
|
-
|
|
781
|
+
try {
|
|
782
|
+
const content = fs2__namespace.readFileSync(data);
|
|
783
|
+
return attach(content, name, "image/png", "png");
|
|
784
|
+
} catch (err) {
|
|
785
|
+
console.warn("[QAagentic] Warning: could not read screenshot file:", err.message);
|
|
786
|
+
}
|
|
917
787
|
}
|
|
918
788
|
return attach(data, name, "image/png", "png");
|
|
919
789
|
}
|
|
@@ -924,6 +794,95 @@ function attachJson(data, name = "JSON Data") {
|
|
|
924
794
|
function attachText(text, name = "Text") {
|
|
925
795
|
return attach(text, name, "text/plain", "txt");
|
|
926
796
|
}
|
|
797
|
+
function mapStatus(status) {
|
|
798
|
+
switch (status) {
|
|
799
|
+
case "passed" /* PASSED */:
|
|
800
|
+
return "passed";
|
|
801
|
+
case "failed" /* FAILED */:
|
|
802
|
+
case "broken" /* BROKEN */:
|
|
803
|
+
return "failed";
|
|
804
|
+
case "skipped" /* SKIPPED */:
|
|
805
|
+
case "pending" /* PENDING */:
|
|
806
|
+
case "unknown" /* UNKNOWN */:
|
|
807
|
+
default:
|
|
808
|
+
return "skipped";
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
function mapTestCase(test) {
|
|
812
|
+
return {
|
|
813
|
+
name: test.fullName || test.name,
|
|
814
|
+
status: mapStatus(test.status),
|
|
815
|
+
duration_ms: test.durationMs > 0 ? Math.round(test.durationMs) : void 0,
|
|
816
|
+
error_message: test.errorMessage || void 0,
|
|
817
|
+
error_type: test.errorType || void 0,
|
|
818
|
+
error_stacktrace: test.stackTrace || void 0
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
function getCIEnv() {
|
|
822
|
+
return {
|
|
823
|
+
branch: process.env.GITHUB_REF_NAME || process.env.CI_COMMIT_REF_NAME || process.env.BRANCH_NAME || void 0,
|
|
824
|
+
commit_hash: process.env.GITHUB_SHA || process.env.CI_COMMIT_SHA || void 0,
|
|
825
|
+
repository: process.env.GITHUB_REPOSITORY || process.env.CI_PROJECT_PATH || void 0
|
|
826
|
+
};
|
|
827
|
+
}
|
|
828
|
+
async function sendIngestPayload(run, framework) {
|
|
829
|
+
const config = getConfig();
|
|
830
|
+
if (!config.api.enabled) {
|
|
831
|
+
console.log("[QAagentic] API reporting disabled \u2014 skipping ingest");
|
|
832
|
+
return;
|
|
833
|
+
}
|
|
834
|
+
const apiKey = config.api.key;
|
|
835
|
+
if (!apiKey) {
|
|
836
|
+
console.warn(
|
|
837
|
+
"[QAagentic] No API key configured. Set QAGENTIC_API_KEY environment variable. Skipping ingest."
|
|
838
|
+
);
|
|
839
|
+
return;
|
|
840
|
+
}
|
|
841
|
+
const ci = getCIEnv();
|
|
842
|
+
const payload = {
|
|
843
|
+
run_name: run.name || `${framework}_${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
844
|
+
framework,
|
|
845
|
+
branch: run.branch || ci.branch,
|
|
846
|
+
commit_hash: run.commitHash || ci.commit_hash,
|
|
847
|
+
repository: ci.repository,
|
|
848
|
+
environment: run.environment || config.environment,
|
|
849
|
+
test_cases: run.tests.map(mapTestCase),
|
|
850
|
+
metadata: {
|
|
851
|
+
project_name: run.projectName,
|
|
852
|
+
...run.ciBuildId ? { ci_build_id: run.ciBuildId } : {},
|
|
853
|
+
...run.ciBuildUrl ? { ci_build_url: run.ciBuildUrl } : {}
|
|
854
|
+
}
|
|
855
|
+
};
|
|
856
|
+
const url = `${config.api.url.replace(/\/$/, "")}/ingest/`;
|
|
857
|
+
try {
|
|
858
|
+
const response = await axios__default.default.post(url, payload, {
|
|
859
|
+
headers: {
|
|
860
|
+
"Content-Type": "application/json",
|
|
861
|
+
"X-API-Key": apiKey
|
|
862
|
+
},
|
|
863
|
+
timeout: config.api.timeout
|
|
864
|
+
});
|
|
865
|
+
console.log(
|
|
866
|
+
`[QAagentic] \u2713 ${run.tests.length} results ingested. run_id=${response.data?.run_id}`
|
|
867
|
+
);
|
|
868
|
+
} catch (error) {
|
|
869
|
+
if (axios__default.default.isAxiosError(error)) {
|
|
870
|
+
if (!error.response) {
|
|
871
|
+
console.warn(
|
|
872
|
+
`[QAagentic] Could not reach QAagentic at ${url}. Results not sent. Check QAGENTIC_URL and ensure qagentic-core is running.`
|
|
873
|
+
);
|
|
874
|
+
} else {
|
|
875
|
+
console.warn(
|
|
876
|
+
`[QAagentic] Ingest failed (HTTP ${error.response.status}): ${error.response.data?.error || error.message}`
|
|
877
|
+
);
|
|
878
|
+
}
|
|
879
|
+
} else {
|
|
880
|
+
console.warn("[QAagentic] Unexpected error during ingest:", error.message);
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
// src/cypress/simple-setup.ts
|
|
927
886
|
var QAgenticCypressReporter = class {
|
|
928
887
|
constructor(config) {
|
|
929
888
|
this.currentRun = null;
|
|
@@ -942,11 +901,12 @@ var QAgenticCypressReporter = class {
|
|
|
942
901
|
this.pendingTests = [];
|
|
943
902
|
this.projectName = process.env.QAGENTIC_PROJECT_NAME || config.projectId || "Cypress E2E Tests";
|
|
944
903
|
this.environment = process.env.QAGENTIC_ENVIRONMENT || process.env.NODE_ENV || "e2e";
|
|
945
|
-
const apiUrl = process.env.QAGENTIC_API_URL || "
|
|
904
|
+
const apiUrl = process.env.QAGENTIC_API_URL || "http://localhost:8000";
|
|
946
905
|
configure({
|
|
947
906
|
projectName: this.projectName,
|
|
948
907
|
environment: this.environment,
|
|
949
908
|
apiUrl,
|
|
909
|
+
apiKey: process.env.QAGENTIC_API_KEY,
|
|
950
910
|
outputDir: "./qagentic-results"
|
|
951
911
|
});
|
|
952
912
|
this.reporter = QAgenticReporter.getInstance();
|
|
@@ -1114,13 +1074,7 @@ var QAgenticCypressReporter = class {
|
|
|
1114
1074
|
this.currentRun.passed = this.stats.passes;
|
|
1115
1075
|
this.currentRun.failed = this.stats.failures;
|
|
1116
1076
|
this.currentRun.skipped = this.stats.pending;
|
|
1117
|
-
|
|
1118
|
-
total: this.currentRun.total,
|
|
1119
|
-
passed: this.currentRun.passed,
|
|
1120
|
-
failed: this.currentRun.failed,
|
|
1121
|
-
skipped: this.currentRun.skipped
|
|
1122
|
-
});
|
|
1123
|
-
await this.reporter.endRun();
|
|
1077
|
+
await sendIngestPayload(this.currentRun, "cypress");
|
|
1124
1078
|
console.log("[QAagentic] Run finalized successfully");
|
|
1125
1079
|
} catch (error) {
|
|
1126
1080
|
console.warn("[QAagentic] Failed to finalize run:", error);
|