@hasna/testers 0.0.8 → 0.0.11
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/dashboard/dist/assets/index-DyXKnBM8.css +1 -0
- package/dashboard/dist/assets/index-jNG_Nd_Q.js +49 -0
- package/dashboard/dist/index.html +2 -2
- package/dist/cli/index.js +1437 -361
- package/dist/db/results.d.ts +1 -0
- package/dist/db/results.d.ts.map +1 -1
- package/dist/db/runs.d.ts +1 -0
- package/dist/db/runs.d.ts.map +1 -1
- package/dist/db/scenarios.d.ts +5 -0
- package/dist/db/scenarios.d.ts.map +1 -1
- package/dist/db/screenshots.d.ts +1 -0
- package/dist/db/screenshots.d.ts.map +1 -1
- package/dist/index.js +301 -25
- package/dist/lib/costs.d.ts +13 -0
- package/dist/lib/costs.d.ts.map +1 -1
- package/dist/lib/init.d.ts.map +1 -1
- package/dist/lib/logs-integration.d.ts +7 -0
- package/dist/lib/logs-integration.d.ts.map +1 -0
- package/dist/lib/reporter.d.ts +9 -1
- package/dist/lib/reporter.d.ts.map +1 -1
- package/dist/lib/runner.d.ts +7 -1
- package/dist/lib/runner.d.ts.map +1 -1
- package/dist/mcp/index.js +78 -5
- package/dist/server/index.js +4257 -19
- package/dist/types/index.d.ts +5 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/dashboard/dist/assets/index-CDcHt94n.css +0 -1
- package/dashboard/dist/assets/index-DCNDCh61.js +0 -49
package/dist/db/results.d.ts
CHANGED
|
@@ -17,4 +17,5 @@ export declare function updateResult(id: string, updates: Partial<{
|
|
|
17
17
|
costCents: number;
|
|
18
18
|
}>): Result;
|
|
19
19
|
export declare function getResultsByRun(runId: string): Result[];
|
|
20
|
+
export declare function countResultsByRun(runId: string): number;
|
|
20
21
|
//# sourceMappingURL=results.d.ts.map
|
package/dist/db/results.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"results.d.ts","sourceRoot":"","sources":["../../src/db/results.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,MAAM,EAEX,KAAK,YAAY,EAElB,MAAM,mBAAmB,CAAC;AAG3B,wBAAgB,YAAY,CAAC,KAAK,EAAE;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,MAAM,CAkBT;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAanD;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAMnD;AAED,wBAAgB,YAAY,CAC1B,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,YAAY,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC,GACD,MAAM,CA+CR;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAEvD"}
|
|
1
|
+
{"version":3,"file":"results.d.ts","sourceRoot":"","sources":["../../src/db/results.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,MAAM,EAEX,KAAK,YAAY,EAElB,MAAM,mBAAmB,CAAC;AAG3B,wBAAgB,YAAY,CAAC,KAAK,EAAE;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,MAAM,CAkBT;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAanD;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAMnD;AAED,wBAAgB,YAAY,CAC1B,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,YAAY,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC,GACD,MAAM,CA+CR;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAEvD;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMvD"}
|
package/dist/db/runs.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export declare function createRun(input: CreateRunInput & {
|
|
|
4
4
|
}): Run;
|
|
5
5
|
export declare function getRun(id: string): Run | null;
|
|
6
6
|
export declare function listRuns(filter?: RunFilter): Run[];
|
|
7
|
+
export declare function countRuns(filter?: RunFilter): number;
|
|
7
8
|
export declare function updateRun(id: string, updates: Partial<RunRow>): Run;
|
|
8
9
|
export declare function deleteRun(id: string): boolean;
|
|
9
10
|
//# sourceMappingURL=runs.d.ts.map
|
package/dist/db/runs.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runs.d.ts","sourceRoot":"","sources":["../../src/db/runs.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,GAAG,EACR,KAAK,MAAM,EACX,KAAK,cAAc,EACnB,KAAK,SAAS,EAEf,MAAM,mBAAmB,CAAC;AAG3B,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,GAAG,CAoBxE;AAED,wBAAgB,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,GAAG,GAAG,IAAI,CAc7C;AAED,wBAAgB,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,GAAG,GAAG,EAAE,
|
|
1
|
+
{"version":3,"file":"runs.d.ts","sourceRoot":"","sources":["../../src/db/runs.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,GAAG,EACR,KAAK,MAAM,EACX,KAAK,cAAc,EACnB,KAAK,SAAS,EAEf,MAAM,mBAAmB,CAAC;AAG3B,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,GAAG,CAoBxE;AAED,wBAAgB,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,GAAG,GAAG,IAAI,CAc7C;AAED,wBAAgB,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,GAAG,GAAG,EAAE,CAwClD;AAED,wBAAgB,SAAS,CAAC,MAAM,CAAC,EAAE,SAAS,GAAG,MAAM,CAmBpD;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,GAAG,CAmEnE;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAO7C"}
|
package/dist/db/scenarios.d.ts
CHANGED
|
@@ -4,5 +4,10 @@ export declare function getScenario(id: string): Scenario | null;
|
|
|
4
4
|
export declare function getScenarioByShortId(shortId: string): Scenario | null;
|
|
5
5
|
export declare function listScenarios(filter?: ScenarioFilter): Scenario[];
|
|
6
6
|
export declare function updateScenario(id: string, input: UpdateScenarioInput, version: number): Scenario;
|
|
7
|
+
export declare function countScenarios(filter?: ScenarioFilter): number;
|
|
8
|
+
export interface StaleScenario extends Scenario {
|
|
9
|
+
lastRunAt: string | null;
|
|
10
|
+
}
|
|
11
|
+
export declare function findStaleScenarios(days: number): StaleScenario[];
|
|
7
12
|
export declare function deleteScenario(id: string): boolean;
|
|
8
13
|
//# sourceMappingURL=scenarios.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scenarios.d.ts","sourceRoot":"","sources":["../../src/db/scenarios.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,QAAQ,EAEb,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,cAAc,EAGpB,MAAM,mBAAmB,CAAC;AAsB3B,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,GAAG,QAAQ,CA8BnE;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAmBvD;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAIrE;AAED,wBAAgB,aAAa,CAAC,MAAM,CAAC,EAAE,cAAc,GAAG,QAAQ,EAAE,
|
|
1
|
+
{"version":3,"file":"scenarios.d.ts","sourceRoot":"","sources":["../../src/db/scenarios.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,QAAQ,EAEb,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,cAAc,EAGpB,MAAM,mBAAmB,CAAC;AAsB3B,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,GAAG,QAAQ,CA8BnE;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAmBvD;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAIrE;AAED,wBAAgB,aAAa,CAAC,MAAM,CAAC,EAAE,cAAc,GAAG,QAAQ,EAAE,CAqDjE;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,mBAAmB,EAAE,OAAO,EAAE,MAAM,GAAG,QAAQ,CAoFhG;AAED,wBAAgB,cAAc,CAAC,MAAM,CAAC,EAAE,cAAc,GAAG,MAAM,CA8B9D;AAED,MAAM,WAAW,aAAc,SAAQ,QAAQ;IAC7C,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,EAAE,CAmBhE;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAOlD"}
|
package/dist/db/screenshots.d.ts
CHANGED
|
@@ -13,4 +13,5 @@ export declare function createScreenshot(input: {
|
|
|
13
13
|
export declare function getScreenshot(id: string): Screenshot | null;
|
|
14
14
|
export declare function listScreenshots(resultId: string): Screenshot[];
|
|
15
15
|
export declare function getScreenshotsByResult(resultId: string): Screenshot[];
|
|
16
|
+
export declare function countScreenshots(resultId: string): number;
|
|
16
17
|
//# sourceMappingURL=screenshots.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"screenshots.d.ts","sourceRoot":"","sources":["../../src/db/screenshots.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,UAAU,EAGhB,MAAM,mBAAmB,CAAC;AAG3B,wBAAgB,gBAAgB,CAAC,KAAK,EAAE;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B,GAAG,UAAU,CAuBb;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAI3D;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,EAAE,CAM9D;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,EAAE,CAErE"}
|
|
1
|
+
{"version":3,"file":"screenshots.d.ts","sourceRoot":"","sources":["../../src/db/screenshots.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,UAAU,EAGhB,MAAM,mBAAmB,CAAC;AAG3B,wBAAgB,gBAAgB,CAAC,KAAK,EAAE;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B,GAAG,UAAU,CAuBb;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAI3D;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,EAAE,CAM9D;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,EAAE,CAErE;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAMzD"}
|
package/dist/index.js
CHANGED
|
@@ -513,7 +513,8 @@ __export(exports_runs, {
|
|
|
513
513
|
listRuns: () => listRuns,
|
|
514
514
|
getRun: () => getRun,
|
|
515
515
|
deleteRun: () => deleteRun,
|
|
516
|
-
createRun: () => createRun
|
|
516
|
+
createRun: () => createRun,
|
|
517
|
+
countRuns: () => countRuns
|
|
517
518
|
});
|
|
518
519
|
function createRun(input) {
|
|
519
520
|
const db2 = getDatabase();
|
|
@@ -554,7 +555,10 @@ function listRuns(filter) {
|
|
|
554
555
|
if (conditions.length > 0) {
|
|
555
556
|
sql += " WHERE " + conditions.join(" AND ");
|
|
556
557
|
}
|
|
557
|
-
|
|
558
|
+
const sortField = filter?.sort ?? "date";
|
|
559
|
+
const sortDir = filter?.desc === false ? "ASC" : "DESC";
|
|
560
|
+
const orderByCol = sortField === "duration" ? "(CASE WHEN finished_at IS NULL THEN NULL ELSE (julianday(finished_at) - julianday(started_at)) * 86400000 END)" : sortField === "cost" ? "(SELECT COALESCE(SUM(cost_cents), 0) FROM results WHERE run_id = runs.id)" : "started_at";
|
|
561
|
+
sql += ` ORDER BY ${orderByCol} ${sortDir}`;
|
|
558
562
|
if (filter?.limit) {
|
|
559
563
|
sql += " LIMIT ?";
|
|
560
564
|
params.push(filter.limit);
|
|
@@ -566,6 +570,24 @@ function listRuns(filter) {
|
|
|
566
570
|
const rows = db2.query(sql).all(...params);
|
|
567
571
|
return rows.map(runFromRow);
|
|
568
572
|
}
|
|
573
|
+
function countRuns(filter) {
|
|
574
|
+
const db2 = getDatabase();
|
|
575
|
+
const conditions = [];
|
|
576
|
+
const params = [];
|
|
577
|
+
if (filter?.projectId) {
|
|
578
|
+
conditions.push("project_id = ?");
|
|
579
|
+
params.push(filter.projectId);
|
|
580
|
+
}
|
|
581
|
+
if (filter?.status) {
|
|
582
|
+
conditions.push("status = ?");
|
|
583
|
+
params.push(filter.status);
|
|
584
|
+
}
|
|
585
|
+
let sql = "SELECT COUNT(*) as count FROM runs";
|
|
586
|
+
if (conditions.length > 0)
|
|
587
|
+
sql += " WHERE " + conditions.join(" AND ");
|
|
588
|
+
const row = db2.query(sql).get(...params);
|
|
589
|
+
return row.count;
|
|
590
|
+
}
|
|
569
591
|
function updateRun(id, updates) {
|
|
570
592
|
const db2 = getDatabase();
|
|
571
593
|
const existing = getRun(id);
|
|
@@ -1033,7 +1055,10 @@ function listScenarios(filter) {
|
|
|
1033
1055
|
if (conditions.length > 0) {
|
|
1034
1056
|
sql += " WHERE " + conditions.join(" AND ");
|
|
1035
1057
|
}
|
|
1036
|
-
|
|
1058
|
+
const sortField = filter?.sort ?? "date";
|
|
1059
|
+
const sortDir = filter?.desc === false ? "ASC" : "DESC";
|
|
1060
|
+
const orderByCol = sortField === "name" ? "name" : sortField === "priority" ? "CASE priority WHEN 'critical' THEN 0 WHEN 'high' THEN 1 WHEN 'medium' THEN 2 WHEN 'low' THEN 3 ELSE 4 END" : "created_at";
|
|
1061
|
+
sql += ` ORDER BY ${orderByCol} ${sortDir}`;
|
|
1037
1062
|
if (filter?.limit) {
|
|
1038
1063
|
sql += " LIMIT ?";
|
|
1039
1064
|
params.push(filter.limit);
|
|
@@ -2665,6 +2690,38 @@ async function testWebhook(id) {
|
|
|
2665
2690
|
}
|
|
2666
2691
|
}
|
|
2667
2692
|
|
|
2693
|
+
// src/lib/logs-integration.ts
|
|
2694
|
+
async function pushFailedRunToLogs(run, failedResults, scenarios) {
|
|
2695
|
+
const logsUrl = process.env.LOGS_URL;
|
|
2696
|
+
if (!logsUrl)
|
|
2697
|
+
return;
|
|
2698
|
+
const scenarioMap = new Map(scenarios.map((s) => [s.id, s]));
|
|
2699
|
+
const entries = failedResults.map((result) => {
|
|
2700
|
+
const scenario = scenarioMap.get(result.scenarioId);
|
|
2701
|
+
return {
|
|
2702
|
+
level: "error",
|
|
2703
|
+
source: "sdk",
|
|
2704
|
+
service: "testers",
|
|
2705
|
+
message: `[testers] Scenario failed: ${scenario?.name ?? result.scenarioId}${result.error ? ` \u2014 ${result.error}` : ""}`,
|
|
2706
|
+
metadata: {
|
|
2707
|
+
run_id: run.id,
|
|
2708
|
+
scenario_id: result.scenarioId,
|
|
2709
|
+
scenario_name: scenario?.name,
|
|
2710
|
+
url: run.url,
|
|
2711
|
+
status: result.status,
|
|
2712
|
+
duration_ms: result.durationMs
|
|
2713
|
+
}
|
|
2714
|
+
};
|
|
2715
|
+
});
|
|
2716
|
+
try {
|
|
2717
|
+
await fetch(`${logsUrl.replace(/\/$/, "")}/api/logs`, {
|
|
2718
|
+
method: "POST",
|
|
2719
|
+
headers: { "Content-Type": "application/json" },
|
|
2720
|
+
body: JSON.stringify(entries)
|
|
2721
|
+
});
|
|
2722
|
+
} catch {}
|
|
2723
|
+
}
|
|
2724
|
+
|
|
2668
2725
|
// src/lib/runner.ts
|
|
2669
2726
|
var eventHandler = null;
|
|
2670
2727
|
function onRunEvent(handler) {
|
|
@@ -2676,14 +2733,26 @@ function emit(event) {
|
|
|
2676
2733
|
}
|
|
2677
2734
|
function withTimeout(promise, ms, label) {
|
|
2678
2735
|
return new Promise((resolve, reject) => {
|
|
2736
|
+
const warningAt = Math.floor(ms * 0.8);
|
|
2737
|
+
const warningTimer = setTimeout(() => {
|
|
2738
|
+
emit({
|
|
2739
|
+
type: "scenario:timeout_warning",
|
|
2740
|
+
scenarioName: label,
|
|
2741
|
+
timeoutMs: ms,
|
|
2742
|
+
elapsedMs: warningAt
|
|
2743
|
+
});
|
|
2744
|
+
}, warningAt);
|
|
2679
2745
|
const timer = setTimeout(() => {
|
|
2680
|
-
|
|
2746
|
+
clearTimeout(warningTimer);
|
|
2747
|
+
reject(new Error(`Scenario '${label}' timed out after ${ms}ms. Try: testers run --timeout ${ms * 2} or simplify the scenario steps.`));
|
|
2681
2748
|
}, ms);
|
|
2682
2749
|
promise.then((val) => {
|
|
2683
2750
|
clearTimeout(timer);
|
|
2751
|
+
clearTimeout(warningTimer);
|
|
2684
2752
|
resolve(val);
|
|
2685
2753
|
}, (err) => {
|
|
2686
2754
|
clearTimeout(timer);
|
|
2755
|
+
clearTimeout(warningTimer);
|
|
2687
2756
|
reject(err);
|
|
2688
2757
|
});
|
|
2689
2758
|
});
|
|
@@ -2712,6 +2781,7 @@ async function runSingleScenario(scenario, runId, options) {
|
|
|
2712
2781
|
const targetUrl = scenario.targetPath ? `${options.url.replace(/\/$/, "")}${scenario.targetPath}` : options.url;
|
|
2713
2782
|
const scenarioTimeout = scenario.timeoutMs ?? options.timeout ?? config.browser.timeout ?? 60000;
|
|
2714
2783
|
await page.goto(targetUrl, { timeout: Math.min(scenarioTimeout, 30000) });
|
|
2784
|
+
const stepStartTimes = new Map;
|
|
2715
2785
|
const agentResult = await withTimeout(runAgentLoop({
|
|
2716
2786
|
client,
|
|
2717
2787
|
page,
|
|
@@ -2721,6 +2791,16 @@ async function runSingleScenario(scenario, runId, options) {
|
|
|
2721
2791
|
runId,
|
|
2722
2792
|
maxTurns: 30,
|
|
2723
2793
|
onStep: (stepEvent) => {
|
|
2794
|
+
let stepDurationMs;
|
|
2795
|
+
if (stepEvent.type === "tool_call") {
|
|
2796
|
+
stepStartTimes.set(stepEvent.stepNumber, Date.now());
|
|
2797
|
+
} else if (stepEvent.type === "tool_result") {
|
|
2798
|
+
const startTime = stepStartTimes.get(stepEvent.stepNumber);
|
|
2799
|
+
if (startTime !== undefined) {
|
|
2800
|
+
stepDurationMs = Date.now() - startTime;
|
|
2801
|
+
stepStartTimes.delete(stepEvent.stepNumber);
|
|
2802
|
+
}
|
|
2803
|
+
}
|
|
2724
2804
|
emit({
|
|
2725
2805
|
type: `step:${stepEvent.type}`,
|
|
2726
2806
|
scenarioId: scenario.id,
|
|
@@ -2730,7 +2810,8 @@ async function runSingleScenario(scenario, runId, options) {
|
|
|
2730
2810
|
toolInput: stepEvent.toolInput,
|
|
2731
2811
|
toolResult: stepEvent.toolResult,
|
|
2732
2812
|
thinking: stepEvent.thinking,
|
|
2733
|
-
stepNumber: stepEvent.stepNumber
|
|
2813
|
+
stepNumber: stepEvent.stepNumber,
|
|
2814
|
+
stepDurationMs
|
|
2734
2815
|
});
|
|
2735
2816
|
}
|
|
2736
2817
|
}), scenarioTimeout, scenario.name);
|
|
@@ -2810,6 +2891,7 @@ async function runBatch(scenarios, options) {
|
|
|
2810
2891
|
} catch {}
|
|
2811
2892
|
return true;
|
|
2812
2893
|
};
|
|
2894
|
+
const maxRetries = options.retry ?? 0;
|
|
2813
2895
|
if (parallel <= 1) {
|
|
2814
2896
|
for (const scenario of sortedScenarios) {
|
|
2815
2897
|
if (!await canRun(scenario)) {
|
|
@@ -2820,7 +2902,13 @@ async function runBatch(scenarios, options) {
|
|
|
2820
2902
|
emit({ type: "scenario:error", scenarioId: scenario.id, scenarioName: scenario.name, error: "Dependency failed \u2014 skipped", runId: run.id });
|
|
2821
2903
|
continue;
|
|
2822
2904
|
}
|
|
2823
|
-
|
|
2905
|
+
let result = await runSingleScenario(scenario, run.id, options);
|
|
2906
|
+
let attempt = 1;
|
|
2907
|
+
while ((result.status === "failed" || result.status === "error") && attempt <= maxRetries) {
|
|
2908
|
+
emit({ type: "scenario:start", scenarioId: scenario.id, scenarioName: scenario.name, runId: run.id, retryAttempt: attempt + 1, maxRetries: maxRetries + 1 });
|
|
2909
|
+
result = await runSingleScenario(scenario, run.id, options);
|
|
2910
|
+
attempt++;
|
|
2911
|
+
}
|
|
2824
2912
|
results.push(result);
|
|
2825
2913
|
if (result.status === "failed" || result.status === "error") {
|
|
2826
2914
|
failedScenarioIds.add(scenario.id);
|
|
@@ -2867,6 +2955,10 @@ async function runBatch(scenarios, options) {
|
|
|
2867
2955
|
emit({ type: "run:complete", runId: run.id });
|
|
2868
2956
|
const eventType = finalRun.status === "failed" ? "failed" : "completed";
|
|
2869
2957
|
dispatchWebhooks(eventType, finalRun).catch(() => {});
|
|
2958
|
+
if (finalRun.status === "failed") {
|
|
2959
|
+
const failedResults = results.filter((r) => r.status === "failed" || r.status === "error");
|
|
2960
|
+
pushFailedRunToLogs(finalRun, failedResults, scenarios).catch(() => {});
|
|
2961
|
+
}
|
|
2870
2962
|
return { run: finalRun, results };
|
|
2871
2963
|
}
|
|
2872
2964
|
async function runByFilter(options) {
|
|
@@ -3468,13 +3560,28 @@ var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 });
|
|
|
3468
3560
|
var source_default = chalk;
|
|
3469
3561
|
|
|
3470
3562
|
// src/lib/reporter.ts
|
|
3471
|
-
|
|
3563
|
+
init_database();
|
|
3564
|
+
function useEmoji() {
|
|
3565
|
+
return !process.env["NO_COLOR"] && process.argv.indexOf("--no-color") === -1;
|
|
3566
|
+
}
|
|
3567
|
+
function formatTerminal(run, results, options) {
|
|
3472
3568
|
const lines = [];
|
|
3569
|
+
const failedOnly = options?.failedOnly ?? false;
|
|
3473
3570
|
lines.push("");
|
|
3474
3571
|
lines.push(source_default.bold(` Run ${run.id.slice(0, 8)} \u2014 ${run.url}`));
|
|
3475
3572
|
lines.push(source_default.dim(` Model: ${run.model} | Parallel: ${run.parallel} | Headed: ${run.headed ? "yes" : "no"}`));
|
|
3476
3573
|
lines.push("");
|
|
3574
|
+
if (failedOnly) {
|
|
3575
|
+
const passedCount = results.filter((r) => r.status === "passed").length;
|
|
3576
|
+
if (passedCount > 0) {
|
|
3577
|
+
lines.push(source_default.dim(` (${passedCount} passed scenario${passedCount !== 1 ? "s" : ""} hidden \u2014 use without --failed-only to see all)`));
|
|
3578
|
+
lines.push("");
|
|
3579
|
+
}
|
|
3580
|
+
}
|
|
3477
3581
|
for (const result of results) {
|
|
3582
|
+
if (failedOnly && result.status !== "failed" && result.status !== "error") {
|
|
3583
|
+
continue;
|
|
3584
|
+
}
|
|
3478
3585
|
const scenario = getScenario(result.scenarioId);
|
|
3479
3586
|
const name = scenario ? `${scenario.shortId}: ${scenario.name}` : result.scenarioId.slice(0, 8);
|
|
3480
3587
|
const screenshots = listScreenshots(result.id);
|
|
@@ -3482,21 +3589,22 @@ function formatTerminal(run, results) {
|
|
|
3482
3589
|
const screenshotCount = screenshots.length;
|
|
3483
3590
|
let statusIcon;
|
|
3484
3591
|
let statusColor;
|
|
3592
|
+
const emoji = useEmoji();
|
|
3485
3593
|
switch (result.status) {
|
|
3486
3594
|
case "passed":
|
|
3487
|
-
statusIcon = source_default.green("PASS");
|
|
3595
|
+
statusIcon = emoji ? "\u2705" : source_default.green("PASS");
|
|
3488
3596
|
statusColor = source_default.green;
|
|
3489
3597
|
break;
|
|
3490
3598
|
case "failed":
|
|
3491
|
-
statusIcon = source_default.red("FAIL");
|
|
3599
|
+
statusIcon = emoji ? "\u274C" : source_default.red("FAIL");
|
|
3492
3600
|
statusColor = source_default.red;
|
|
3493
3601
|
break;
|
|
3494
3602
|
case "error":
|
|
3495
|
-
statusIcon = source_default.yellow("ERR ");
|
|
3603
|
+
statusIcon = emoji ? "\u26A0\uFE0F " : source_default.yellow("ERR ");
|
|
3496
3604
|
statusColor = source_default.yellow;
|
|
3497
3605
|
break;
|
|
3498
3606
|
default:
|
|
3499
|
-
statusIcon = source_default.dim("SKIP");
|
|
3607
|
+
statusIcon = emoji ? "\u23ED\uFE0F " : source_default.dim("SKIP");
|
|
3500
3608
|
statusColor = source_default.dim;
|
|
3501
3609
|
break;
|
|
3502
3610
|
}
|
|
@@ -3509,7 +3617,7 @@ function formatTerminal(run, results) {
|
|
|
3509
3617
|
}
|
|
3510
3618
|
}
|
|
3511
3619
|
lines.push("");
|
|
3512
|
-
lines.push(
|
|
3620
|
+
lines.push(formatActionableSummary(run, results));
|
|
3513
3621
|
lines.push("");
|
|
3514
3622
|
return lines.join(`
|
|
3515
3623
|
`);
|
|
@@ -3521,6 +3629,30 @@ function formatSummary(run) {
|
|
|
3521
3629
|
const totalStr = source_default.dim(` (${run.total} total)`);
|
|
3522
3630
|
return ` ${passedStr}${failedStr}${totalStr} in ${duration}`;
|
|
3523
3631
|
}
|
|
3632
|
+
function formatActionableSummary(run, results) {
|
|
3633
|
+
const emoji = useEmoji();
|
|
3634
|
+
const passedCount = results.filter((r) => r.status === "passed").length;
|
|
3635
|
+
const failedCount = results.filter((r) => r.status === "failed" || r.status === "error").length;
|
|
3636
|
+
const shortId = run.id.slice(0, 8);
|
|
3637
|
+
const passStr = `${emoji ? "\u2705" : "PASS"} ${passedCount} passed`;
|
|
3638
|
+
const failStr = failedCount > 0 ? ` ${emoji ? "\u274C" : "FAIL"} ${failedCount} failed` : "";
|
|
3639
|
+
const lines = [];
|
|
3640
|
+
lines.push(` ${source_default.bold(passStr)}${failedCount > 0 ? source_default.bold(failStr) : ""}`);
|
|
3641
|
+
if (failedCount > 0) {
|
|
3642
|
+
lines.push(source_default.dim(` retry failed: testers retry ${shortId} | view: testers results ${shortId}`));
|
|
3643
|
+
} else {
|
|
3644
|
+
lines.push(source_default.dim(` view: testers results ${shortId}`));
|
|
3645
|
+
}
|
|
3646
|
+
const totalCostCents = results.reduce((sum, r) => sum + (r.costCents ?? 0), 0);
|
|
3647
|
+
const totalTokens = results.reduce((sum, r) => sum + (r.tokensUsed ?? 0), 0);
|
|
3648
|
+
if (totalTokens > 0) {
|
|
3649
|
+
const costStr = `$${(totalCostCents / 100).toFixed(4)}`;
|
|
3650
|
+
const tokensStr = totalTokens.toLocaleString();
|
|
3651
|
+
lines.push(source_default.dim(` ${emoji ? "\uD83D\uDCB0" : "cost:"} Cost: ${costStr} (${tokensStr} tokens)`));
|
|
3652
|
+
}
|
|
3653
|
+
return lines.join(`
|
|
3654
|
+
`);
|
|
3655
|
+
}
|
|
3524
3656
|
function formatJSON(run, results) {
|
|
3525
3657
|
const output = {
|
|
3526
3658
|
run: {
|
|
@@ -3599,6 +3731,15 @@ function formatRunList(runs) {
|
|
|
3599
3731
|
return lines.join(`
|
|
3600
3732
|
`);
|
|
3601
3733
|
}
|
|
3734
|
+
function getScenarioRunStats(scenarioId) {
|
|
3735
|
+
const db2 = getDatabase();
|
|
3736
|
+
const lastRow = db2.query("SELECT status FROM results WHERE scenario_id = ? ORDER BY created_at DESC LIMIT 1").get(scenarioId);
|
|
3737
|
+
const statsRow = db2.query("SELECT COUNT(*) as total, SUM(CASE WHEN status = 'passed' THEN 1 ELSE 0 END) as passed FROM results WHERE scenario_id = ?").get(scenarioId);
|
|
3738
|
+
return {
|
|
3739
|
+
lastStatus: lastRow ? lastRow.status : null,
|
|
3740
|
+
passRate: statsRow && statsRow.total > 0 ? `${statsRow.passed}/${statsRow.total}` : "\u2014"
|
|
3741
|
+
};
|
|
3742
|
+
}
|
|
3602
3743
|
function formatScenarioList(scenarios) {
|
|
3603
3744
|
const lines = [];
|
|
3604
3745
|
lines.push("");
|
|
@@ -3613,7 +3754,21 @@ function formatScenarioList(scenarios) {
|
|
|
3613
3754
|
for (const s of scenarios) {
|
|
3614
3755
|
const priorityColor = s.priority === "critical" ? source_default.red : s.priority === "high" ? source_default.yellow : s.priority === "medium" ? source_default.blue : source_default.dim;
|
|
3615
3756
|
const tags = s.tags.length > 0 ? source_default.dim(` [${s.tags.join(", ")}]`) : "";
|
|
3616
|
-
|
|
3757
|
+
let lastStatusIcon = source_default.dim("\u2014");
|
|
3758
|
+
let passRateStr = source_default.dim("\u2014");
|
|
3759
|
+
if (s.id) {
|
|
3760
|
+
const stats = getScenarioRunStats(s.id);
|
|
3761
|
+
if (stats.lastStatus === "passed")
|
|
3762
|
+
lastStatusIcon = source_default.green("\u2713");
|
|
3763
|
+
else if (stats.lastStatus === "failed")
|
|
3764
|
+
lastStatusIcon = source_default.red("\u2717");
|
|
3765
|
+
else if (stats.lastStatus === "error")
|
|
3766
|
+
lastStatusIcon = source_default.yellow("!");
|
|
3767
|
+
else if (stats.lastStatus === "skipped")
|
|
3768
|
+
lastStatusIcon = source_default.dim("~");
|
|
3769
|
+
passRateStr = stats.passRate === "\u2014" ? source_default.dim("\u2014") : source_default.dim(stats.passRate);
|
|
3770
|
+
}
|
|
3771
|
+
lines.push(` ${source_default.cyan(s.shortId)} ${s.name} ${priorityColor(s.priority)}${tags} ${lastStatusIcon} ${passRateStr}`);
|
|
3617
3772
|
}
|
|
3618
3773
|
lines.push("");
|
|
3619
3774
|
return lines.join(`
|
|
@@ -4029,26 +4184,146 @@ function detectFramework(dir) {
|
|
|
4029
4184
|
return null;
|
|
4030
4185
|
}
|
|
4031
4186
|
function getStarterScenarios(framework, projectId) {
|
|
4187
|
+
if (framework.name === "Next.js") {
|
|
4188
|
+
const scenarios2 = [
|
|
4189
|
+
{
|
|
4190
|
+
name: "Homepage loads",
|
|
4191
|
+
description: "Navigate to the homepage and verify it loads correctly. Check that the main heading and content are visible, and there are no console errors.",
|
|
4192
|
+
tags: ["smoke"],
|
|
4193
|
+
priority: "high",
|
|
4194
|
+
projectId
|
|
4195
|
+
},
|
|
4196
|
+
{
|
|
4197
|
+
name: "404 page works",
|
|
4198
|
+
description: "Navigate to a non-existent URL (e.g. /this-page-does-not-exist) and verify the Next.js 404 page renders correctly.",
|
|
4199
|
+
tags: ["smoke"],
|
|
4200
|
+
priority: "medium",
|
|
4201
|
+
projectId
|
|
4202
|
+
},
|
|
4203
|
+
{
|
|
4204
|
+
name: "Navigation links work",
|
|
4205
|
+
description: "Click through the main navigation links and verify each page loads without errors. Check that client-side routing is working correctly.",
|
|
4206
|
+
tags: ["smoke"],
|
|
4207
|
+
priority: "medium",
|
|
4208
|
+
projectId
|
|
4209
|
+
}
|
|
4210
|
+
];
|
|
4211
|
+
if (framework.features.includes("hasAuth")) {
|
|
4212
|
+
scenarios2.push({
|
|
4213
|
+
name: "Login flow",
|
|
4214
|
+
description: "Navigate to the login page, enter valid credentials, and verify successful authentication and redirect.",
|
|
4215
|
+
tags: ["auth"],
|
|
4216
|
+
priority: "critical",
|
|
4217
|
+
projectId
|
|
4218
|
+
}, {
|
|
4219
|
+
name: "Protected route redirect",
|
|
4220
|
+
description: "Try to access a protected route without authentication and verify you are redirected to the login page.",
|
|
4221
|
+
tags: ["auth"],
|
|
4222
|
+
priority: "high",
|
|
4223
|
+
projectId
|
|
4224
|
+
});
|
|
4225
|
+
}
|
|
4226
|
+
if (framework.features.includes("hasForms")) {
|
|
4227
|
+
scenarios2.push({
|
|
4228
|
+
name: "Form validation",
|
|
4229
|
+
description: "Submit forms with empty/invalid data and verify validation errors appear correctly.",
|
|
4230
|
+
tags: ["forms"],
|
|
4231
|
+
priority: "medium",
|
|
4232
|
+
projectId
|
|
4233
|
+
});
|
|
4234
|
+
}
|
|
4235
|
+
return scenarios2;
|
|
4236
|
+
}
|
|
4237
|
+
if (framework.name === "Vite" || framework.name === "SvelteKit") {
|
|
4238
|
+
const scenarios2 = [
|
|
4239
|
+
{
|
|
4240
|
+
name: "Homepage loads",
|
|
4241
|
+
description: "Navigate to the homepage and verify it loads correctly with no console errors.",
|
|
4242
|
+
tags: ["smoke"],
|
|
4243
|
+
priority: "high",
|
|
4244
|
+
projectId
|
|
4245
|
+
},
|
|
4246
|
+
{
|
|
4247
|
+
name: "Mobile viewport check",
|
|
4248
|
+
description: "Set the viewport to 375x812 (iPhone) and verify the homepage renders correctly without horizontal scrolling or layout issues.",
|
|
4249
|
+
tags: ["responsive"],
|
|
4250
|
+
priority: "medium",
|
|
4251
|
+
projectId
|
|
4252
|
+
},
|
|
4253
|
+
{
|
|
4254
|
+
name: "No console errors",
|
|
4255
|
+
description: "Navigate through the app and verify there are no JavaScript errors or warnings in the browser console.",
|
|
4256
|
+
tags: ["smoke"],
|
|
4257
|
+
priority: "high",
|
|
4258
|
+
projectId
|
|
4259
|
+
}
|
|
4260
|
+
];
|
|
4261
|
+
if (framework.features.includes("hasAuth")) {
|
|
4262
|
+
scenarios2.push({
|
|
4263
|
+
name: "Login flow",
|
|
4264
|
+
description: "Navigate to the login page, enter valid credentials, and verify successful authentication.",
|
|
4265
|
+
tags: ["auth"],
|
|
4266
|
+
priority: "critical",
|
|
4267
|
+
projectId
|
|
4268
|
+
});
|
|
4269
|
+
}
|
|
4270
|
+
return scenarios2;
|
|
4271
|
+
}
|
|
4272
|
+
if (framework.name === "Nuxt") {
|
|
4273
|
+
const scenarios2 = [
|
|
4274
|
+
{
|
|
4275
|
+
name: "Homepage loads",
|
|
4276
|
+
description: "Navigate to the homepage and verify it loads correctly. Check that the main heading and content are visible.",
|
|
4277
|
+
tags: ["smoke"],
|
|
4278
|
+
priority: "high",
|
|
4279
|
+
projectId
|
|
4280
|
+
},
|
|
4281
|
+
{
|
|
4282
|
+
name: "Navigation works",
|
|
4283
|
+
description: "Click through main navigation links and verify each page loads without errors.",
|
|
4284
|
+
tags: ["smoke"],
|
|
4285
|
+
priority: "medium",
|
|
4286
|
+
projectId
|
|
4287
|
+
},
|
|
4288
|
+
{
|
|
4289
|
+
name: "Mobile viewport check",
|
|
4290
|
+
description: "Set the viewport to 375x812 and verify the homepage renders correctly on mobile.",
|
|
4291
|
+
tags: ["responsive"],
|
|
4292
|
+
priority: "medium",
|
|
4293
|
+
projectId
|
|
4294
|
+
}
|
|
4295
|
+
];
|
|
4296
|
+
if (framework.features.includes("hasAuth")) {
|
|
4297
|
+
scenarios2.push({
|
|
4298
|
+
name: "Login flow",
|
|
4299
|
+
description: "Navigate to the login page, enter valid credentials, and verify successful authentication.",
|
|
4300
|
+
tags: ["auth"],
|
|
4301
|
+
priority: "critical",
|
|
4302
|
+
projectId
|
|
4303
|
+
});
|
|
4304
|
+
}
|
|
4305
|
+
return scenarios2;
|
|
4306
|
+
}
|
|
4032
4307
|
const scenarios = [
|
|
4033
4308
|
{
|
|
4034
|
-
name: "
|
|
4035
|
-
description: "Navigate to the
|
|
4309
|
+
name: "Homepage loads",
|
|
4310
|
+
description: "Navigate to the homepage and verify it loads correctly with no console errors. Check that the main heading, navigation, and primary CTA are visible.",
|
|
4036
4311
|
tags: ["smoke"],
|
|
4037
4312
|
priority: "high",
|
|
4038
4313
|
projectId
|
|
4039
4314
|
},
|
|
4040
4315
|
{
|
|
4041
|
-
name: "
|
|
4042
|
-
description: "
|
|
4316
|
+
name: "Form submit works",
|
|
4317
|
+
description: "Find the main form on the page, fill it in with valid test data, submit it, and verify the success state.",
|
|
4043
4318
|
tags: ["smoke"],
|
|
4044
4319
|
priority: "medium",
|
|
4045
4320
|
projectId
|
|
4046
4321
|
},
|
|
4047
4322
|
{
|
|
4048
|
-
name: "
|
|
4049
|
-
description: "
|
|
4050
|
-
tags: ["
|
|
4051
|
-
priority: "
|
|
4323
|
+
name: "Mobile viewport check",
|
|
4324
|
+
description: "Set the viewport to 375x812 (iPhone) and verify the homepage renders correctly without horizontal scrolling or layout issues.",
|
|
4325
|
+
tags: ["responsive"],
|
|
4326
|
+
priority: "medium",
|
|
4052
4327
|
projectId
|
|
4053
4328
|
}
|
|
4054
4329
|
];
|
|
@@ -4891,12 +5166,13 @@ function formatCostsTerminal(summary) {
|
|
|
4891
5166
|
}
|
|
4892
5167
|
if (summary.byScenario.length > 0) {
|
|
4893
5168
|
lines.push("");
|
|
4894
|
-
lines.push(source_default.bold("
|
|
4895
|
-
lines.push(` ${"Scenario".padEnd(40)} ${"Cost".padEnd(12)} ${"
|
|
4896
|
-
lines.push(` ${"\u2500".repeat(40)} ${"\u2500".repeat(12)} ${"\u2500".repeat(12)} ${"\u2500".repeat(6)}`);
|
|
5169
|
+
lines.push(source_default.bold(" Scenarios by Cost (most expensive first)"));
|
|
5170
|
+
lines.push(` ${"Scenario".padEnd(40)} ${"Total Cost".padEnd(12)} ${"Avg/Run".padEnd(12)} ${"Runs".padEnd(6)} Tokens`);
|
|
5171
|
+
lines.push(` ${"\u2500".repeat(40)} ${"\u2500".repeat(12)} ${"\u2500".repeat(12)} ${"\u2500".repeat(6)} ${"\u2500".repeat(10)}`);
|
|
4897
5172
|
for (const s of summary.byScenario) {
|
|
4898
5173
|
const label = s.name.length > 38 ? s.name.slice(0, 35) + "..." : s.name;
|
|
4899
|
-
|
|
5174
|
+
const avgPerRun = s.runs > 0 ? s.costCents / s.runs : 0;
|
|
5175
|
+
lines.push(` ${label.padEnd(40)} ${formatDollars(s.costCents).padEnd(12)} ${formatDollars(avgPerRun).padEnd(12)} ${String(s.runs).padEnd(6)} ${formatTokens(s.tokens)}`);
|
|
4900
5176
|
}
|
|
4901
5177
|
}
|
|
4902
5178
|
lines.push("");
|
package/dist/lib/costs.d.ts
CHANGED
|
@@ -27,10 +27,23 @@ export declare function getCostSummary(options?: {
|
|
|
27
27
|
projectId?: string;
|
|
28
28
|
period?: "day" | "week" | "month" | "all";
|
|
29
29
|
}): CostSummary;
|
|
30
|
+
export interface ScenarioCostRow {
|
|
31
|
+
scenarioId: string;
|
|
32
|
+
name: string;
|
|
33
|
+
runCount: number;
|
|
34
|
+
totalCostCents: number;
|
|
35
|
+
avgCostPerRunCents: number;
|
|
36
|
+
}
|
|
37
|
+
export declare function getCostsByScenario(options?: {
|
|
38
|
+
projectId?: string;
|
|
39
|
+
period?: "day" | "week" | "month" | "all";
|
|
40
|
+
}): ScenarioCostRow[];
|
|
41
|
+
export declare function formatCostsByScenarioTerminal(rows: ScenarioCostRow[], period: string): string;
|
|
30
42
|
export declare function checkBudget(estimatedCostCents: number): {
|
|
31
43
|
allowed: boolean;
|
|
32
44
|
warning?: string;
|
|
33
45
|
};
|
|
34
46
|
export declare function formatCostsTerminal(summary: CostSummary): string;
|
|
35
47
|
export declare function formatCostsJSON(summary: CostSummary): string;
|
|
48
|
+
export declare function formatCostsCsv(summary: CostSummary): string;
|
|
36
49
|
//# sourceMappingURL=costs.d.ts.map
|
package/dist/lib/costs.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"costs.d.ts","sourceRoot":"","sources":["../../src/lib/costs.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC7E,UAAU,EAAE,KAAK,CAAC;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACzG,aAAa,EAAE,MAAM,CAAC;IACtB,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,YAAY;IAC3B,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;CACvB;AA0CD,wBAAgB,cAAc,CAAC,OAAO,CAAC,EAAE;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC;CAC3C,GAAG,WAAW,CAyFd;AAED,wBAAgB,WAAW,CAAC,kBAAkB,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CA+B9F;AAcD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"costs.d.ts","sourceRoot":"","sources":["../../src/lib/costs.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC7E,UAAU,EAAE,KAAK,CAAC;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACzG,aAAa,EAAE,MAAM,CAAC;IACtB,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,YAAY;IAC3B,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;CACvB;AA0CD,wBAAgB,cAAc,CAAC,OAAO,CAAC,EAAE;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC;CAC3C,GAAG,WAAW,CAyFd;AAID,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED,wBAAgB,kBAAkB,CAAC,OAAO,CAAC,EAAE;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC;CAC3C,GAAG,eAAe,EAAE,CAoCpB;AAED,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,eAAe,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAyB7F;AAED,wBAAgB,WAAW,CAAC,kBAAkB,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CA+B9F;AAcD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAyChE;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAE5D;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAS3D"}
|
package/dist/lib/init.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/lib/init.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAI7D,UAAU,aAAa;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAmDjE;AAID,wBAAgB,mBAAmB,CACjC,SAAS,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,EAC/C,SAAS,EAAE,MAAM,GAChB,mBAAmB,EAAE,
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/lib/init.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAI7D,UAAU,aAAa;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAmDjE;AAID,wBAAgB,mBAAmB,CACjC,SAAS,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,EAC/C,SAAS,EAAE,MAAM,GAChB,mBAAmB,EAAE,CAkNvB;AAID,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;IAC1C,SAAS,EAAE,UAAU,CAAC,OAAO,cAAc,CAAC,EAAE,CAAC;IAC/C,SAAS,EAAE,aAAa,GAAG,IAAI,CAAC;IAChC,GAAG,EAAE,MAAM,CAAC;CACb;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,UAAU,CAuC5D"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Run, Result, Scenario } from "../types/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Push failed test results to open-logs if LOGS_URL env var is set.
|
|
4
|
+
* No-op when LOGS_URL is not configured.
|
|
5
|
+
*/
|
|
6
|
+
export declare function pushFailedRunToLogs(run: Run, failedResults: Result[], scenarios: Scenario[]): Promise<void>;
|
|
7
|
+
//# sourceMappingURL=logs-integration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logs-integration.d.ts","sourceRoot":"","sources":["../../src/lib/logs-integration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAE/D;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,GAAG,EACR,aAAa,EAAE,MAAM,EAAE,EACvB,SAAS,EAAE,QAAQ,EAAE,GACpB,OAAO,CAAC,IAAI,CAAC,CAiCf"}
|
package/dist/lib/reporter.d.ts
CHANGED
|
@@ -2,13 +2,21 @@ import type { Run, Result, Screenshot } from "../types/index.js";
|
|
|
2
2
|
export interface ReportOptions {
|
|
3
3
|
json?: boolean;
|
|
4
4
|
verbose?: boolean;
|
|
5
|
+
failedOnly?: boolean;
|
|
5
6
|
}
|
|
6
|
-
export declare function formatTerminal(run: Run, results: Result[]): string;
|
|
7
|
+
export declare function formatTerminal(run: Run, results: Result[], options?: ReportOptions): string;
|
|
7
8
|
export declare function formatSummary(run: Run): string;
|
|
9
|
+
export declare function formatActionableSummary(run: Run, results: Result[]): string;
|
|
8
10
|
export declare function formatJSON(run: Run, results: Result[]): string;
|
|
9
11
|
export declare function getExitCode(run: Run): number;
|
|
10
12
|
export declare function formatRunList(runs: Run[]): string;
|
|
13
|
+
export interface ScenarioRunStats {
|
|
14
|
+
lastStatus: "passed" | "failed" | "error" | "skipped" | null;
|
|
15
|
+
passRate: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function getScenarioRunStats(scenarioId: string): ScenarioRunStats;
|
|
11
18
|
export declare function formatScenarioList(scenarios: Array<{
|
|
19
|
+
id?: string;
|
|
12
20
|
shortId: string;
|
|
13
21
|
name: string;
|
|
14
22
|
priority: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reporter.d.ts","sourceRoot":"","sources":["../../src/lib/reporter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"reporter.d.ts","sourceRoot":"","sources":["../../src/lib/reporter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAWjE,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,MAAM,CAmE3F;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,CAU9C;AAED,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,CA4B3E;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,CAoD9D;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,CAI5C;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,MAAM,CA6BjD;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,IAAI,CAAC;IAC7D,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,gBAAgB,CAiBxE;AAED,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,KAAK,CAAC;IAAE,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,GAAG,MAAM,CAwC7I;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,GAAG,MAAM,CAqCpF"}
|