@hasna/testers 0.0.2 → 0.0.3
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 +72 -1
- package/dist/cli/index.js +1769 -447
- package/dist/db/auth-presets.d.ts +20 -0
- package/dist/db/auth-presets.d.ts.map +1 -0
- package/dist/db/database.d.ts.map +1 -1
- package/dist/db/screenshots.d.ts +3 -0
- package/dist/db/screenshots.d.ts.map +1 -1
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1741 -358
- package/dist/lib/ai-client.d.ts +6 -0
- package/dist/lib/ai-client.d.ts.map +1 -1
- package/dist/lib/costs.d.ts +36 -0
- package/dist/lib/costs.d.ts.map +1 -0
- package/dist/lib/diff.d.ts +25 -0
- package/dist/lib/diff.d.ts.map +1 -0
- package/dist/lib/init.d.ts +28 -0
- package/dist/lib/init.d.ts.map +1 -0
- package/dist/lib/report.d.ts +4 -0
- package/dist/lib/report.d.ts.map +1 -0
- package/dist/lib/runner.d.ts.map +1 -1
- package/dist/lib/screenshotter.d.ts +27 -25
- package/dist/lib/screenshotter.d.ts.map +1 -1
- package/dist/lib/smoke.d.ts +25 -0
- package/dist/lib/smoke.d.ts.map +1 -0
- package/dist/lib/templates.d.ts +5 -0
- package/dist/lib/templates.d.ts.map +1 -0
- package/dist/lib/watch.d.ts +9 -0
- package/dist/lib/watch.d.ts.map +1 -0
- package/dist/lib/webhooks.d.ts +41 -0
- package/dist/lib/webhooks.d.ts.map +1 -0
- package/dist/mcp/index.js +138 -18
- package/dist/server/index.js +138 -18
- package/dist/types/index.d.ts +6 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/lib/ai-client.d.ts
CHANGED
|
@@ -18,6 +18,9 @@ interface ScreenshotResult {
|
|
|
18
18
|
width: number;
|
|
19
19
|
height: number;
|
|
20
20
|
timestamp: string;
|
|
21
|
+
description: string | null;
|
|
22
|
+
pageUrl: string | null;
|
|
23
|
+
thumbnailPath: string | null;
|
|
21
24
|
}
|
|
22
25
|
interface ToolExecutionResult {
|
|
23
26
|
result: string;
|
|
@@ -49,6 +52,9 @@ interface AgentLoopResult {
|
|
|
49
52
|
timestamp: string;
|
|
50
53
|
action: string;
|
|
51
54
|
stepNumber: number;
|
|
55
|
+
description: string | null;
|
|
56
|
+
pageUrl: string | null;
|
|
57
|
+
thumbnailPath: string | null;
|
|
52
58
|
}>;
|
|
53
59
|
}
|
|
54
60
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-client.d.ts","sourceRoot":"","sources":["../../src/lib/ai-client.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAC1C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD,OAAO,KAAK,EAAe,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAI/D;;;GAGG;AACH,wBAAgB,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAKzD;AAID,eAAO,MAAM,aAAa,EAAE,SAAS,CAAC,IAAI,EAyTzC,CAAC;AAIF,UAAU,WAAW;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,gBAAgB;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"ai-client.d.ts","sourceRoot":"","sources":["../../src/lib/ai-client.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAC1C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD,OAAO,KAAK,EAAe,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAI/D;;;GAGG;AACH,wBAAgB,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAKzD;AAID,eAAO,MAAM,aAAa,EAAE,SAAS,CAAC,IAAI,EAyTzC,CAAC;AAIF,UAAU,WAAW;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,gBAAgB;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,UAAU,mBAAmB;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,gBAAgB,CAAC;CAC/B;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,IAAI,EACV,aAAa,EAAE,aAAa,EAC5B,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAClC,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,mBAAmB,CAAC,CA8P9B;AAID,UAAU,gBAAgB;IACxB,MAAM,EAAE,SAAS,CAAC;IAClB,IAAI,EAAE,IAAI,CAAC;IACX,QAAQ,EAAE,QAAQ,CAAC;IACnB,aAAa,EAAE,aAAa,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,eAAe;IACvB,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,KAAK,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;QACnB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;QAC3B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;QACvB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;KAC9B,CAAC,CAAC;CACJ;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,eAAe,CAAC,CAyK1B;AAID;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAQvD"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export interface CostSummary {
|
|
2
|
+
period: string;
|
|
3
|
+
totalCostCents: number;
|
|
4
|
+
totalTokens: number;
|
|
5
|
+
runCount: number;
|
|
6
|
+
byModel: Record<string, {
|
|
7
|
+
costCents: number;
|
|
8
|
+
tokens: number;
|
|
9
|
+
runs: number;
|
|
10
|
+
}>;
|
|
11
|
+
byScenario: Array<{
|
|
12
|
+
scenarioId: string;
|
|
13
|
+
name: string;
|
|
14
|
+
costCents: number;
|
|
15
|
+
tokens: number;
|
|
16
|
+
runs: number;
|
|
17
|
+
}>;
|
|
18
|
+
avgCostPerRun: number;
|
|
19
|
+
estimatedMonthlyCents: number;
|
|
20
|
+
}
|
|
21
|
+
export interface BudgetConfig {
|
|
22
|
+
maxPerRunCents: number;
|
|
23
|
+
maxPerDayCents: number;
|
|
24
|
+
warnAtPercent: number;
|
|
25
|
+
}
|
|
26
|
+
export declare function getCostSummary(options?: {
|
|
27
|
+
projectId?: string;
|
|
28
|
+
period?: "day" | "week" | "month" | "all";
|
|
29
|
+
}): CostSummary;
|
|
30
|
+
export declare function checkBudget(estimatedCostCents: number): {
|
|
31
|
+
allowed: boolean;
|
|
32
|
+
warning?: string;
|
|
33
|
+
};
|
|
34
|
+
export declare function formatCostsTerminal(summary: CostSummary): string;
|
|
35
|
+
export declare function formatCostsJSON(summary: CostSummary): string;
|
|
36
|
+
//# sourceMappingURL=costs.d.ts.map
|
|
@@ -0,0 +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,CAwChE;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAE5D"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Run } from "../types/index.js";
|
|
2
|
+
export interface DiffResult {
|
|
3
|
+
run1: Run;
|
|
4
|
+
run2: Run;
|
|
5
|
+
regressions: ScenarioDiff[];
|
|
6
|
+
fixes: ScenarioDiff[];
|
|
7
|
+
unchanged: ScenarioDiff[];
|
|
8
|
+
newScenarios: ScenarioDiff[];
|
|
9
|
+
removedScenarios: ScenarioDiff[];
|
|
10
|
+
}
|
|
11
|
+
export interface ScenarioDiff {
|
|
12
|
+
scenarioId: string;
|
|
13
|
+
scenarioName: string | null;
|
|
14
|
+
scenarioShortId: string | null;
|
|
15
|
+
status1: string | null;
|
|
16
|
+
status2: string | null;
|
|
17
|
+
duration1: number | null;
|
|
18
|
+
duration2: number | null;
|
|
19
|
+
tokens1: number | null;
|
|
20
|
+
tokens2: number | null;
|
|
21
|
+
}
|
|
22
|
+
export declare function diffRuns(runId1: string, runId2: string): DiffResult;
|
|
23
|
+
export declare function formatDiffTerminal(diff: DiffResult): string;
|
|
24
|
+
export declare function formatDiffJSON(diff: DiffResult): string;
|
|
25
|
+
//# sourceMappingURL=diff.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../src/lib/diff.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,GAAG,EAAU,MAAM,mBAAmB,CAAC;AAErD,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,GAAG,CAAC;IACV,IAAI,EAAE,GAAG,CAAC;IACV,WAAW,EAAE,YAAY,EAAE,CAAC;IAC5B,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,YAAY,EAAE,YAAY,EAAE,CAAC;IAC7B,gBAAgB,EAAE,YAAY,EAAE,CAAC;CAClC;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU,CAuEnE;AA6BD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAiE3D;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAEvD"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { createScenario } from "../db/scenarios.js";
|
|
2
|
+
import { ensureProject } from "../db/projects.js";
|
|
3
|
+
import type { CreateScenarioInput } from "../types/index.js";
|
|
4
|
+
interface FrameworkInfo {
|
|
5
|
+
name: string;
|
|
6
|
+
defaultUrl: string;
|
|
7
|
+
features: string[];
|
|
8
|
+
}
|
|
9
|
+
export declare function detectFramework(dir: string): FrameworkInfo | null;
|
|
10
|
+
export declare function getStarterScenarios(framework: {
|
|
11
|
+
name: string;
|
|
12
|
+
features: string[];
|
|
13
|
+
}, projectId: string): CreateScenarioInput[];
|
|
14
|
+
export interface InitOptions {
|
|
15
|
+
name?: string;
|
|
16
|
+
url?: string;
|
|
17
|
+
path?: string;
|
|
18
|
+
dir?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface InitResult {
|
|
21
|
+
project: ReturnType<typeof ensureProject>;
|
|
22
|
+
scenarios: ReturnType<typeof createScenario>[];
|
|
23
|
+
framework: FrameworkInfo | null;
|
|
24
|
+
url: string;
|
|
25
|
+
}
|
|
26
|
+
export declare function initProject(options: InitOptions): InitResult;
|
|
27
|
+
export {};
|
|
28
|
+
//# sourceMappingURL=init.d.ts.map
|
|
@@ -0,0 +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,CA6DvB;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 @@
|
|
|
1
|
+
{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../src/lib/report.ts"],"names":[],"mappings":"AAOA,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAStD;AA4ED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAiIxD;AAED,wBAAgB,oBAAoB,IAAI,MAAM,CAI7C"}
|
package/dist/lib/runner.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/lib/runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAW/D,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,gBAAgB,GAAG,eAAe,GAAG,eAAe,GAAG,gBAAgB,GAAG,qBAAqB,GAAG,cAAc,CAAC;IACvH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAC;AAIxD,wBAAgB,UAAU,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI,CAEzD;AAMD,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/lib/runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAW/D,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,gBAAgB,GAAG,eAAe,GAAG,eAAe,GAAG,gBAAgB,GAAG,qBAAqB,GAAG,cAAc,CAAC;IACvH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAC;AAIxD,wBAAgB,UAAU,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI,CAEzD;AAMD,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,MAAM,CAAC,CAoFjB;AAED,wBAAsB,QAAQ,CAC5B,SAAS,EAAE,QAAQ,EAAE,EACrB,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC;IAAE,GAAG,EAAE,GAAG,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CA4D1C;AAED,wBAAsB,WAAW,CAC/B,OAAO,EAAE,UAAU,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,GACnF,OAAO,CAAC;IAAE,GAAG,EAAE,GAAG,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAuB1C"}
|
|
@@ -1,59 +1,61 @@
|
|
|
1
1
|
import type { Page } from "playwright";
|
|
2
|
-
/**
|
|
3
|
-
* Convert arbitrary text to a URL/filesystem-safe slug.
|
|
4
|
-
* Lowercases, replaces non-alphanumeric runs with a single hyphen, trims
|
|
5
|
-
* leading/trailing hyphens.
|
|
6
|
-
*/
|
|
7
2
|
export declare function slugify(text: string): string;
|
|
8
|
-
/**
|
|
9
|
-
* Build a zero-padded screenshot filename.
|
|
10
|
-
* Example: stepNumber=1, action="Navigate homepage" → "001-navigate-homepage.png"
|
|
11
|
-
*/
|
|
12
3
|
export declare function generateFilename(stepNumber: number, action: string): string;
|
|
13
4
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*/
|
|
17
|
-
export declare function getScreenshotDir(baseDir: string, runId: string, scenarioSlug: string): string;
|
|
18
|
-
/**
|
|
19
|
-
* Create `dirPath` (and any parents) if it does not already exist.
|
|
5
|
+
* Build the screenshot directory for a run:
|
|
6
|
+
* {baseDir}/{projectName}/{YYYY-MM-DD}/{HH-mm-ss}_{runId-8char}/{scenarioSlug}/
|
|
20
7
|
*/
|
|
8
|
+
export declare function getScreenshotDir(baseDir: string, runId: string, scenarioSlug: string, projectName?: string, timestamp?: Date): string;
|
|
21
9
|
export declare function ensureDir(dirPath: string): void;
|
|
22
10
|
interface ScreenshotterOptions {
|
|
23
11
|
baseDir?: string;
|
|
24
12
|
format?: "png" | "jpeg";
|
|
25
13
|
quality?: number;
|
|
26
14
|
fullPage?: boolean;
|
|
15
|
+
projectName?: string;
|
|
27
16
|
}
|
|
28
17
|
interface CaptureOptions {
|
|
29
18
|
runId: string;
|
|
30
19
|
scenarioSlug: string;
|
|
31
20
|
stepNumber: number;
|
|
32
21
|
action: string;
|
|
22
|
+
description?: string;
|
|
33
23
|
}
|
|
34
|
-
interface CaptureResult {
|
|
24
|
+
export interface CaptureResult {
|
|
35
25
|
filePath: string;
|
|
36
26
|
width: number;
|
|
37
27
|
height: number;
|
|
38
28
|
timestamp: string;
|
|
29
|
+
description: string | null;
|
|
30
|
+
pageUrl: string | null;
|
|
31
|
+
thumbnailPath: string | null;
|
|
39
32
|
}
|
|
33
|
+
export declare function writeRunMeta(dir: string, meta: {
|
|
34
|
+
runId: string;
|
|
35
|
+
url: string;
|
|
36
|
+
model: string;
|
|
37
|
+
status: string;
|
|
38
|
+
startedAt: string;
|
|
39
|
+
scenarioCount: number;
|
|
40
|
+
}): void;
|
|
41
|
+
export declare function writeScenarioMeta(dir: string, meta: {
|
|
42
|
+
scenarioId: string;
|
|
43
|
+
shortId: string;
|
|
44
|
+
name: string;
|
|
45
|
+
status: string;
|
|
46
|
+
reasoning: string | null;
|
|
47
|
+
durationMs: number;
|
|
48
|
+
}): void;
|
|
40
49
|
export declare class Screenshotter {
|
|
41
50
|
private readonly baseDir;
|
|
42
51
|
private readonly format;
|
|
43
52
|
private readonly quality;
|
|
44
53
|
private readonly fullPage;
|
|
54
|
+
private readonly projectName;
|
|
55
|
+
private runTimestamp;
|
|
45
56
|
constructor(options?: ScreenshotterOptions);
|
|
46
|
-
/**
|
|
47
|
-
* Capture a screenshot of the current page state.
|
|
48
|
-
*/
|
|
49
57
|
capture(page: Page, options: CaptureOptions): Promise<CaptureResult>;
|
|
50
|
-
/**
|
|
51
|
-
* Capture a full-page screenshot regardless of the instance default.
|
|
52
|
-
*/
|
|
53
58
|
captureFullPage(page: Page, options: CaptureOptions): Promise<CaptureResult>;
|
|
54
|
-
/**
|
|
55
|
-
* Capture a screenshot of a specific element identified by `selector`.
|
|
56
|
-
*/
|
|
57
59
|
captureElement(page: Page, selector: string, options: CaptureOptions): Promise<CaptureResult>;
|
|
58
60
|
}
|
|
59
61
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"screenshotter.d.ts","sourceRoot":"","sources":["../../src/lib/screenshotter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAOvC
|
|
1
|
+
{"version":3,"file":"screenshotter.d.ts","sourceRoot":"","sources":["../../src/lib/screenshotter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAOvC,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAK5C;AAED,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAI3E;AAUD;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,MAAM,EACpB,WAAW,CAAC,EAAE,MAAM,EACpB,SAAS,CAAC,EAAE,IAAI,GACf,MAAM,CAMR;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAI/C;AAID,UAAU,oBAAoB;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,UAAU,cAAc;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAuBD,wBAAgB,YAAY,CAC1B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,GAC5G,IAAI,CAON;AAED,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GACxH,IAAI,CAON;AAkCD,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiB;IACxC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAU;IACnC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,YAAY,CAAO;gBAEf,OAAO,GAAE,oBAAyB;IASxC,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IAkDpE,eAAe,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IAgD5E,cAAc,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;CA4CpG"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Run, Result } from "../types/index.js";
|
|
2
|
+
export interface SmokeResult {
|
|
3
|
+
run: Run;
|
|
4
|
+
result: Result;
|
|
5
|
+
pagesVisited: number;
|
|
6
|
+
issuesFound: SmokeIssue[];
|
|
7
|
+
}
|
|
8
|
+
export interface SmokeIssue {
|
|
9
|
+
type: "js-error" | "404" | "broken-image" | "broken-link" | "visual" | "performance";
|
|
10
|
+
severity: "critical" | "high" | "medium" | "low";
|
|
11
|
+
description: string;
|
|
12
|
+
url: string;
|
|
13
|
+
screenshot?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function runSmoke(options: {
|
|
16
|
+
url: string;
|
|
17
|
+
model?: string;
|
|
18
|
+
headed?: boolean;
|
|
19
|
+
timeout?: number;
|
|
20
|
+
projectId?: string;
|
|
21
|
+
apiKey?: string;
|
|
22
|
+
}): Promise<SmokeResult>;
|
|
23
|
+
export declare function parseSmokeIssues(reasoning: string): SmokeIssue[];
|
|
24
|
+
export declare function formatSmokeReport(result: SmokeResult): string;
|
|
25
|
+
//# sourceMappingURL=smoke.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"smoke.d.ts","sourceRoot":"","sources":["../../src/lib/smoke.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAOrD,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,GAAG,CAAC;IACT,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,UAAU,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,UAAU,GAAG,KAAK,GAAG,cAAc,GAAG,aAAa,GAAG,QAAQ,GAAG,aAAa,CAAC;IACrF,QAAQ,EAAE,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;IACjD,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAsBD,wBAAsB,QAAQ,CAAC,OAAO,EAAE;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAAC,WAAW,CAAC,CAwEvB;AAwCD,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,UAAU,EAAE,CAiDhE;AA2BD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAuD7D"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { CreateScenarioInput } from "../types/index.js";
|
|
2
|
+
export declare const SCENARIO_TEMPLATES: Record<string, CreateScenarioInput[]>;
|
|
3
|
+
export declare function getTemplate(name: string): CreateScenarioInput[] | null;
|
|
4
|
+
export declare function listTemplateNames(): string[];
|
|
5
|
+
//# sourceMappingURL=templates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../../src/lib/templates.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAE7D,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,EAAE,CAyBpE,CAAC;AAEF,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,mBAAmB,EAAE,GAAG,IAAI,CAEtE;AAED,wBAAgB,iBAAiB,IAAI,MAAM,EAAE,CAE5C"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { RunOptions } from "./runner.js";
|
|
2
|
+
export interface WatchOptions extends RunOptions {
|
|
3
|
+
dir: string;
|
|
4
|
+
debounceMs?: number;
|
|
5
|
+
tags?: string[];
|
|
6
|
+
priority?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function startWatcher(options: WatchOptions): Promise<void>;
|
|
9
|
+
//# sourceMappingURL=watch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watch.d.ts","sourceRoot":"","sources":["../../src/lib/watch.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,MAAM,WAAW,YAAa,SAAQ,UAAU;IAC9C,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAsB,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAsGvE"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Run } from "../types/index.js";
|
|
2
|
+
export interface Webhook {
|
|
3
|
+
id: string;
|
|
4
|
+
url: string;
|
|
5
|
+
events: string[];
|
|
6
|
+
projectId: string | null;
|
|
7
|
+
secret: string | null;
|
|
8
|
+
active: boolean;
|
|
9
|
+
createdAt: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function createWebhook(input: {
|
|
12
|
+
url: string;
|
|
13
|
+
events?: string[];
|
|
14
|
+
projectId?: string;
|
|
15
|
+
secret?: string;
|
|
16
|
+
}): Webhook;
|
|
17
|
+
export declare function getWebhook(id: string): Webhook | null;
|
|
18
|
+
export declare function listWebhooks(projectId?: string): Webhook[];
|
|
19
|
+
export declare function deleteWebhook(id: string): boolean;
|
|
20
|
+
export interface WebhookPayload {
|
|
21
|
+
event: string;
|
|
22
|
+
run: {
|
|
23
|
+
id: string;
|
|
24
|
+
url: string;
|
|
25
|
+
status: string;
|
|
26
|
+
passed: number;
|
|
27
|
+
failed: number;
|
|
28
|
+
total: number;
|
|
29
|
+
};
|
|
30
|
+
schedule?: {
|
|
31
|
+
name: string;
|
|
32
|
+
cronExpression: string;
|
|
33
|
+
};
|
|
34
|
+
timestamp: string;
|
|
35
|
+
}
|
|
36
|
+
export declare function dispatchWebhooks(event: string, run: Run, schedule?: {
|
|
37
|
+
name: string;
|
|
38
|
+
cronExpression: string;
|
|
39
|
+
}): Promise<void>;
|
|
40
|
+
export declare function testWebhook(id: string): Promise<boolean>;
|
|
41
|
+
//# sourceMappingURL=webhooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../../src/lib/webhooks.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAc7C,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAgBD,wBAAgB,aAAa,CAAC,KAAK,EAAE;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAYV;AAED,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAUrD;AAED,wBAAgB,YAAY,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,EAAE,CAW1D;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAMjD;AAID,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE;QACH,EAAE,EAAE,MAAM,CAAC;QACX,GAAG,EAAE,MAAM,CAAC;QACZ,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IACF,QAAQ,CAAC,EAAE;QACT,IAAI,EAAE,MAAM,CAAC;QACb,cAAc,EAAE,MAAM,CAAC;KACxB,CAAC;IACF,SAAS,EAAE,MAAM,CAAC;CACnB;AAwCD,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,GAAG,EACR,QAAQ,CAAC,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,GAClD,OAAO,CAAC,IAAI,CAAC,CAgDf;AAED,wBAAsB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAwB9D"}
|
package/dist/mcp/index.js
CHANGED
|
@@ -4080,7 +4080,10 @@ function screenshotFromRow(row) {
|
|
|
4080
4080
|
filePath: row.file_path,
|
|
4081
4081
|
width: row.width,
|
|
4082
4082
|
height: row.height,
|
|
4083
|
-
timestamp: row.timestamp
|
|
4083
|
+
timestamp: row.timestamp,
|
|
4084
|
+
description: row.description,
|
|
4085
|
+
pageUrl: row.page_url,
|
|
4086
|
+
thumbnailPath: row.thumbnail_path
|
|
4084
4087
|
};
|
|
4085
4088
|
}
|
|
4086
4089
|
function scheduleFromRow(row) {
|
|
@@ -4289,6 +4292,34 @@ var MIGRATIONS = [
|
|
|
4289
4292
|
CREATE INDEX IF NOT EXISTS idx_schedules_project ON schedules(project_id);
|
|
4290
4293
|
CREATE INDEX IF NOT EXISTS idx_schedules_enabled ON schedules(enabled);
|
|
4291
4294
|
CREATE INDEX IF NOT EXISTS idx_schedules_next_run ON schedules(next_run_at);
|
|
4295
|
+
`,
|
|
4296
|
+
`
|
|
4297
|
+
ALTER TABLE screenshots ADD COLUMN description TEXT;
|
|
4298
|
+
ALTER TABLE screenshots ADD COLUMN page_url TEXT;
|
|
4299
|
+
ALTER TABLE screenshots ADD COLUMN thumbnail_path TEXT;
|
|
4300
|
+
`,
|
|
4301
|
+
`
|
|
4302
|
+
CREATE TABLE IF NOT EXISTS auth_presets (
|
|
4303
|
+
id TEXT PRIMARY KEY,
|
|
4304
|
+
name TEXT NOT NULL UNIQUE,
|
|
4305
|
+
email TEXT NOT NULL,
|
|
4306
|
+
password TEXT NOT NULL,
|
|
4307
|
+
login_path TEXT DEFAULT '/login',
|
|
4308
|
+
metadata TEXT DEFAULT '{}',
|
|
4309
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
4310
|
+
);
|
|
4311
|
+
`,
|
|
4312
|
+
`
|
|
4313
|
+
CREATE TABLE IF NOT EXISTS webhooks (
|
|
4314
|
+
id TEXT PRIMARY KEY,
|
|
4315
|
+
url TEXT NOT NULL,
|
|
4316
|
+
events TEXT NOT NULL DEFAULT '["failed"]',
|
|
4317
|
+
project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,
|
|
4318
|
+
secret TEXT,
|
|
4319
|
+
active INTEGER NOT NULL DEFAULT 1,
|
|
4320
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
4321
|
+
);
|
|
4322
|
+
CREATE INDEX IF NOT EXISTS idx_webhooks_active ON webhooks(active);
|
|
4292
4323
|
`
|
|
4293
4324
|
];
|
|
4294
4325
|
function applyMigrations(database) {
|
|
@@ -4686,9 +4717,9 @@ function createScreenshot(input) {
|
|
|
4686
4717
|
const id = uuid();
|
|
4687
4718
|
const timestamp = now();
|
|
4688
4719
|
db2.query(`
|
|
4689
|
-
INSERT INTO screenshots (id, result_id, step_number, action, file_path, width, height, timestamp)
|
|
4690
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
4691
|
-
`).run(id, input.resultId, input.stepNumber, input.action, input.filePath, input.width, input.height, timestamp);
|
|
4720
|
+
INSERT INTO screenshots (id, result_id, step_number, action, file_path, width, height, timestamp, description, page_url, thumbnail_path)
|
|
4721
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
4722
|
+
`).run(id, input.resultId, input.stepNumber, input.action, input.filePath, input.width, input.height, timestamp, input.description ?? null, input.pageUrl ?? null, input.thumbnailPath ?? null);
|
|
4692
4723
|
return getScreenshot(id);
|
|
4693
4724
|
}
|
|
4694
4725
|
function getScreenshot(id) {
|
|
@@ -4805,7 +4836,7 @@ async function closeBrowser(browser) {
|
|
|
4805
4836
|
}
|
|
4806
4837
|
|
|
4807
4838
|
// src/lib/screenshotter.ts
|
|
4808
|
-
import { mkdirSync as mkdirSync2, existsSync as existsSync2 } from "fs";
|
|
4839
|
+
import { mkdirSync as mkdirSync2, existsSync as existsSync2, writeFileSync } from "fs";
|
|
4809
4840
|
import { join as join2 } from "path";
|
|
4810
4841
|
import { homedir as homedir2 } from "os";
|
|
4811
4842
|
function slugify(text) {
|
|
@@ -4814,16 +4845,51 @@ function slugify(text) {
|
|
|
4814
4845
|
function generateFilename(stepNumber, action) {
|
|
4815
4846
|
const padded = String(stepNumber).padStart(3, "0");
|
|
4816
4847
|
const slug = slugify(action);
|
|
4817
|
-
return `${padded}
|
|
4848
|
+
return `${padded}_${slug}.png`;
|
|
4849
|
+
}
|
|
4850
|
+
function formatDate(date) {
|
|
4851
|
+
return date.toISOString().slice(0, 10);
|
|
4818
4852
|
}
|
|
4819
|
-
function
|
|
4820
|
-
return
|
|
4853
|
+
function formatTime(date) {
|
|
4854
|
+
return date.toISOString().slice(11, 19).replace(/:/g, "-");
|
|
4855
|
+
}
|
|
4856
|
+
function getScreenshotDir(baseDir, runId, scenarioSlug, projectName, timestamp) {
|
|
4857
|
+
const now2 = timestamp ?? new Date;
|
|
4858
|
+
const project = projectName ?? "default";
|
|
4859
|
+
const dateDir = formatDate(now2);
|
|
4860
|
+
const timeDir = `${formatTime(now2)}_${runId.slice(0, 8)}`;
|
|
4861
|
+
return join2(baseDir, project, dateDir, timeDir, scenarioSlug);
|
|
4821
4862
|
}
|
|
4822
4863
|
function ensureDir(dirPath) {
|
|
4823
4864
|
if (!existsSync2(dirPath)) {
|
|
4824
4865
|
mkdirSync2(dirPath, { recursive: true });
|
|
4825
4866
|
}
|
|
4826
4867
|
}
|
|
4868
|
+
function writeMetaSidecar(screenshotPath, meta) {
|
|
4869
|
+
const metaPath = screenshotPath.replace(/\.png$/, ".meta.json").replace(/\.jpeg$/, ".meta.json");
|
|
4870
|
+
try {
|
|
4871
|
+
writeFileSync(metaPath, JSON.stringify(meta, null, 2), "utf-8");
|
|
4872
|
+
} catch {}
|
|
4873
|
+
}
|
|
4874
|
+
async function generateThumbnail(page, screenshotDir, filename) {
|
|
4875
|
+
try {
|
|
4876
|
+
const thumbDir = join2(screenshotDir, "_thumbnail");
|
|
4877
|
+
ensureDir(thumbDir);
|
|
4878
|
+
const thumbFilename = filename.replace(/\.(png|jpeg)$/, ".thumb.$1");
|
|
4879
|
+
const thumbPath = join2(thumbDir, thumbFilename);
|
|
4880
|
+
const viewport = page.viewportSize();
|
|
4881
|
+
if (viewport) {
|
|
4882
|
+
await page.screenshot({
|
|
4883
|
+
path: thumbPath,
|
|
4884
|
+
type: "png",
|
|
4885
|
+
clip: { x: 0, y: 0, width: Math.min(viewport.width, 1280), height: Math.min(viewport.height, 720) }
|
|
4886
|
+
});
|
|
4887
|
+
}
|
|
4888
|
+
return thumbPath;
|
|
4889
|
+
} catch {
|
|
4890
|
+
return null;
|
|
4891
|
+
}
|
|
4892
|
+
}
|
|
4827
4893
|
var DEFAULT_BASE_DIR = join2(homedir2(), ".testers", "screenshots");
|
|
4828
4894
|
|
|
4829
4895
|
class Screenshotter {
|
|
@@ -4831,15 +4897,20 @@ class Screenshotter {
|
|
|
4831
4897
|
format;
|
|
4832
4898
|
quality;
|
|
4833
4899
|
fullPage;
|
|
4900
|
+
projectName;
|
|
4901
|
+
runTimestamp;
|
|
4834
4902
|
constructor(options = {}) {
|
|
4835
4903
|
this.baseDir = options.baseDir ?? DEFAULT_BASE_DIR;
|
|
4836
4904
|
this.format = options.format ?? "png";
|
|
4837
4905
|
this.quality = options.quality ?? 90;
|
|
4838
4906
|
this.fullPage = options.fullPage ?? false;
|
|
4907
|
+
this.projectName = options.projectName ?? "default";
|
|
4908
|
+
this.runTimestamp = new Date;
|
|
4839
4909
|
}
|
|
4840
4910
|
async capture(page, options) {
|
|
4841
|
-
const
|
|
4842
|
-
const
|
|
4911
|
+
const action = options.description ?? options.action;
|
|
4912
|
+
const dir = getScreenshotDir(this.baseDir, options.runId, options.scenarioSlug, this.projectName, this.runTimestamp);
|
|
4913
|
+
const filename = generateFilename(options.stepNumber, action);
|
|
4843
4914
|
const filePath = join2(dir, filename);
|
|
4844
4915
|
ensureDir(dir);
|
|
4845
4916
|
await page.screenshot({
|
|
@@ -4849,16 +4920,32 @@ class Screenshotter {
|
|
|
4849
4920
|
quality: this.format === "jpeg" ? this.quality : undefined
|
|
4850
4921
|
});
|
|
4851
4922
|
const viewport = page.viewportSize() ?? { width: 0, height: 0 };
|
|
4923
|
+
const pageUrl = page.url();
|
|
4924
|
+
const timestamp = new Date().toISOString();
|
|
4925
|
+
writeMetaSidecar(filePath, {
|
|
4926
|
+
stepNumber: options.stepNumber,
|
|
4927
|
+
action: options.action,
|
|
4928
|
+
description: options.description ?? null,
|
|
4929
|
+
pageUrl,
|
|
4930
|
+
viewport,
|
|
4931
|
+
timestamp,
|
|
4932
|
+
filePath
|
|
4933
|
+
});
|
|
4934
|
+
const thumbnailPath = await generateThumbnail(page, dir, filename);
|
|
4852
4935
|
return {
|
|
4853
4936
|
filePath,
|
|
4854
4937
|
width: viewport.width,
|
|
4855
4938
|
height: viewport.height,
|
|
4856
|
-
timestamp
|
|
4939
|
+
timestamp,
|
|
4940
|
+
description: options.description ?? null,
|
|
4941
|
+
pageUrl,
|
|
4942
|
+
thumbnailPath
|
|
4857
4943
|
};
|
|
4858
4944
|
}
|
|
4859
4945
|
async captureFullPage(page, options) {
|
|
4860
|
-
const
|
|
4861
|
-
const
|
|
4946
|
+
const action = options.description ?? options.action;
|
|
4947
|
+
const dir = getScreenshotDir(this.baseDir, options.runId, options.scenarioSlug, this.projectName, this.runTimestamp);
|
|
4948
|
+
const filename = generateFilename(options.stepNumber, action);
|
|
4862
4949
|
const filePath = join2(dir, filename);
|
|
4863
4950
|
ensureDir(dir);
|
|
4864
4951
|
await page.screenshot({
|
|
@@ -4868,16 +4955,32 @@ class Screenshotter {
|
|
|
4868
4955
|
quality: this.format === "jpeg" ? this.quality : undefined
|
|
4869
4956
|
});
|
|
4870
4957
|
const viewport = page.viewportSize() ?? { width: 0, height: 0 };
|
|
4958
|
+
const pageUrl = page.url();
|
|
4959
|
+
const timestamp = new Date().toISOString();
|
|
4960
|
+
writeMetaSidecar(filePath, {
|
|
4961
|
+
stepNumber: options.stepNumber,
|
|
4962
|
+
action: options.action,
|
|
4963
|
+
description: options.description ?? null,
|
|
4964
|
+
pageUrl,
|
|
4965
|
+
viewport,
|
|
4966
|
+
timestamp,
|
|
4967
|
+
filePath
|
|
4968
|
+
});
|
|
4969
|
+
const thumbnailPath = await generateThumbnail(page, dir, filename);
|
|
4871
4970
|
return {
|
|
4872
4971
|
filePath,
|
|
4873
4972
|
width: viewport.width,
|
|
4874
4973
|
height: viewport.height,
|
|
4875
|
-
timestamp
|
|
4974
|
+
timestamp,
|
|
4975
|
+
description: options.description ?? null,
|
|
4976
|
+
pageUrl,
|
|
4977
|
+
thumbnailPath
|
|
4876
4978
|
};
|
|
4877
4979
|
}
|
|
4878
4980
|
async captureElement(page, selector, options) {
|
|
4879
|
-
const
|
|
4880
|
-
const
|
|
4981
|
+
const action = options.description ?? options.action;
|
|
4982
|
+
const dir = getScreenshotDir(this.baseDir, options.runId, options.scenarioSlug, this.projectName, this.runTimestamp);
|
|
4983
|
+
const filename = generateFilename(options.stepNumber, action);
|
|
4881
4984
|
const filePath = join2(dir, filename);
|
|
4882
4985
|
ensureDir(dir);
|
|
4883
4986
|
await page.locator(selector).screenshot({
|
|
@@ -4886,11 +4989,25 @@ class Screenshotter {
|
|
|
4886
4989
|
quality: this.format === "jpeg" ? this.quality : undefined
|
|
4887
4990
|
});
|
|
4888
4991
|
const viewport = page.viewportSize() ?? { width: 0, height: 0 };
|
|
4992
|
+
const pageUrl = page.url();
|
|
4993
|
+
const timestamp = new Date().toISOString();
|
|
4994
|
+
writeMetaSidecar(filePath, {
|
|
4995
|
+
stepNumber: options.stepNumber,
|
|
4996
|
+
action: options.action,
|
|
4997
|
+
description: options.description ?? null,
|
|
4998
|
+
pageUrl,
|
|
4999
|
+
viewport,
|
|
5000
|
+
timestamp,
|
|
5001
|
+
filePath
|
|
5002
|
+
});
|
|
4889
5003
|
return {
|
|
4890
5004
|
filePath,
|
|
4891
5005
|
width: viewport.width,
|
|
4892
5006
|
height: viewport.height,
|
|
4893
|
-
timestamp
|
|
5007
|
+
timestamp,
|
|
5008
|
+
description: options.description ?? null,
|
|
5009
|
+
pageUrl,
|
|
5010
|
+
thumbnailPath: null
|
|
4894
5011
|
};
|
|
4895
5012
|
}
|
|
4896
5013
|
}
|
|
@@ -5673,7 +5790,10 @@ async function runSingleScenario(scenario, runId, options) {
|
|
|
5673
5790
|
action: ss.action,
|
|
5674
5791
|
filePath: ss.filePath,
|
|
5675
5792
|
width: ss.width,
|
|
5676
|
-
height: ss.height
|
|
5793
|
+
height: ss.height,
|
|
5794
|
+
description: ss.description,
|
|
5795
|
+
pageUrl: ss.pageUrl,
|
|
5796
|
+
thumbnailPath: ss.thumbnailPath
|
|
5677
5797
|
});
|
|
5678
5798
|
emit({ type: "screenshot:captured", screenshotPath: ss.filePath, scenarioId: scenario.id, runId });
|
|
5679
5799
|
}
|