@fasttest-ai/qa-agent 0.4.0 → 0.4.2
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/LICENSE +190 -0
- package/README.md +188 -0
- package/dist/actions.d.ts +0 -1
- package/dist/actions.js +0 -1
- package/dist/actions.js.map +1 -1
- package/dist/browser.d.ts +3 -1
- package/dist/browser.js +33 -10
- package/dist/browser.js.map +1 -1
- package/dist/cli.js +16 -3
- package/dist/cli.js.map +1 -1
- package/dist/cloud.d.ts +16 -0
- package/dist/cloud.js +35 -0
- package/dist/cloud.js.map +1 -1
- package/dist/healer.d.ts +1 -0
- package/dist/healer.js +4 -3
- package/dist/healer.js.map +1 -1
- package/dist/index.js +191 -24
- package/dist/index.js.map +1 -1
- package/dist/install.d.ts +5 -5
- package/dist/install.js +367 -92
- package/dist/install.js.map +1 -1
- package/dist/runner.d.ts +21 -0
- package/dist/runner.js +181 -18
- package/dist/runner.js.map +1 -1
- package/package.json +24 -3
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,UAAU,EAAmB,MAAM,aAAa,CAAC;AAE1D,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC1D,MAAM,WAAW,GAAW,CAAC,GAAG,EAAE;IAChC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,cAAc,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;QACrF,OAAO,GAAG,CAAC,OAAO,IAAI,OAAO,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,OAAO,CAAC;IACjB,CAAC;AACH,CAAC,CAAC,EAAE,CAAC;AAiBL,SAAS,YAAY;IACnB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACnC,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,IAAI,OAAO,GAAG,EAAE,CAAC;IACjB,IAAI,OAAO,GAAG,yBAAyB,CAAC;IACxC,IAAI,MAA0B,CAAC;IAC/B,IAAI,KAAyB,CAAC;IAC9B,IAAI,WAAW,GAAsC,UAAU,CAAC;IAChE,IAAI,WAAiC,CAAC;IACtC,IAAI,IAAI,GAAG,KAAK,CAAC;IAEjB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,QAAQ,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YAChB,KAAK,WAAW;gBACd,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;gBACzB,MAAM;YACR,KAAK,YAAY;gBACf,OAAO,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;gBAC1B,MAAM;YACR,KAAK,YAAY;gBACf,OAAO,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,OAAO,CAAC;gBAC/B,MAAM;YACR,KAAK,WAAW;gBACd,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;gBACnB,MAAM;YACR,KAAK,UAAU;gBACb,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;gBAClB,MAAM;YACR,KAAK,WAAW;gBACd,WAAW,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,UAAU,CAAuB,CAAC;gBAC9D,MAAM;YACR,KAAK,iBAAiB;gBACpB,WAAW,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;gBAChF,MAAM;YACR,KAAK,QAAQ;gBACX,IAAI,GAAG,IAAI,CAAC;gBACZ,MAAM;QACV,CAAC;IACH,CAAC;IAED,IAAI,CAAC,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QACxB,OAAO,CAAC,KAAK,CACX,wEAAwE;YACxE,yEAAyE;YACzE,oCAAoC,CACrC,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;AAC9F,CAAC;AAED,8EAA8E;AAC9E,mBAAmB;AACnB,8EAA8E;AAE9E,SAAS,qBAAqB,CAAC,OAAmB;IAChD,MAAM,WAAW,GAAG,OAAO,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC;IACtE,OAAO,CAAC,GAAG,CAAC,gBAAgB,WAAW,MAAM,CAAC,CAAC;IAC/C,OAAO,CAAC,GAAG,CAAC,cAAc,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;IAClD,OAAO,CAAC,GAAG,CACT,UAAU,OAAO,CAAC,KAAK,cAAc,OAAO,CAAC,MAAM,cAAc,OAAO,CAAC,MAAM,eAAe,OAAO,CAAC,OAAO,EAAE,CAChH,CAAC;IACF,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACrE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAEhB,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QAChC,MAAM,IAAI,GAAG,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;QACtF,OAAO,CAAC,GAAG,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,WAAW,KAAK,CAAC,CAAC;QAC1D,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;YACZ,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9B,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,oBAAoB,OAAO,CAAC,MAAM,CAAC,MAAM,kBAAkB,CAAC,CAAC;QACzE,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YAC/B,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,SAAS,UAAU,CAAC,CAAC,UAAU,GAAG,CAAC,EAAE,CAAC,CAAC;YAC3D,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,iBAAiB,OAAO,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC;YAC/D,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,QAAQ,KAAK,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,GAAG,GAAG,CAAC,eAAe,CAAC,CAAC;QAC7F,CAAC;IACH,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,OAAO;AACP,8EAA8E;AAE9E,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;IAC9B,MAAM,OAAO,GAAG,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,SAAS,CAAC,CAAC;IAEvE,MAAM,UAAU,GAAG,IAAI,cAAc,CAAC;QACpC,WAAW,EAAE,MAAM,CAAC,OAAO;QAC3B,QAAQ,EAAE,IAAI;QACd,OAAO;KACR,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,IAAI,WAAW,CAAC;QAC5B,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,OAAO,EAAE,MAAM,CAAC,OAAO;KACxB,CAAC,CAAC;IAEH,MAAM,WAAW,GAAa,EAAE,CAAC;IAEjC,OAAO,CAAC,GAAG,CAAC,uBAAuB,WAAW,EAAE,CAAC,CAAC;IAClD,OAAO,CAAC,GAAG,CAAC,UAAU,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;IACxC,OAAO,CAAC,GAAG,CAAC,YAAY,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;IAC1C,IAAI,MAAM,CAAC,MAAM;QAAE,OAAO,CAAC,GAAG,CAAC,YAAY,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IAC5D,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAEhB,IAAI,OAAmB,CAAC;IACxB,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,UAAU,CACxB,UAAU,EACV,KAAK,EACL;YACE,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,WAAW,EAAE,MAAM,CAAC,WAAW;YAC/B,cAAc,EAAE,MAAM,CAAC,MAAM;SAC9B,EACD,WAAW,CACZ,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,UAAU,GAAG,EAAE,CAAC,CAAC;QAC/B,MAAM,SAAS,CAAC,UAAU,CAAC,CAAC;QAC5B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,iBAAiB;IACjB,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAChD,CAAC;SAAM,CAAC;QACN,qBAAqB,CAAC,OAAO,CAAC,CAAC;IACjC,CAAC;IAED,+BAA+B;IAC/B,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,aAAa,CAAC;gBACzC,MAAM,EAAE,MAAM,CAAC,KAAK;gBACpB,YAAY,EAAE,OAAO,CAAC,YAAY;gBAClC,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,gBAAgB,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;gBACxD,YAAY,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;oBACxC,IAAI,EAAE,CAAC,CAAC,IAAI;oBACZ,MAAM,EAAE,CAAC,CAAC,MAAM;oBAChB,KAAK,EAAE,CAAC,CAAC,KAAK;iBACf,CAAC,CAAC;gBACH,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;oBACjC,iBAAiB,EAAE,CAAC,CAAC,iBAAiB;oBACtC,YAAY,EAAE,CAAC,CAAC,YAAY;oBAC5B,QAAQ,EAAE,CAAC,CAAC,QAAQ;oBACpB,UAAU,EAAE,CAAC,CAAC,UAAU;iBACzB,CAAC,CAAC;aACJ,CAAC,CAAC;YACH,MAAM,UAAU,GAAI,QAAmC,CAAC,WAAW,CAAC;YACpE,OAAO,CAAC,GAAG,CAAC,wBAAwB,UAAU,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;QACpE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,gCAAgC,GAAG,EAAE,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;IAED,MAAM,SAAS,CAAC,UAAU,CAAC,CAAC;IAC5B,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACpD,CAAC;AAED,uDAAuD;AACvD,KAAK,UAAU,SAAS,CAAC,UAA0B;IACjD,MAAM,OAAO,CAAC,IAAI,CAAC;QACjB,UAAU,CAAC,KAAK,EAAE;QAClB,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;KACpD,CAAC,CAAC;AACL,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAC7B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
package/dist/cloud.d.ts
CHANGED
|
@@ -23,6 +23,8 @@ export interface RunResponse {
|
|
|
23
23
|
base_url: string | null;
|
|
24
24
|
test_cases: TestCasePayload[];
|
|
25
25
|
previous_execution_id: string | null;
|
|
26
|
+
/** Map of test_case_id → status from the previous execution (for fail-first ordering). */
|
|
27
|
+
previous_statuses?: Record<string, string>;
|
|
26
28
|
}
|
|
27
29
|
export interface RegressionItem {
|
|
28
30
|
test_case_id: string;
|
|
@@ -58,6 +60,7 @@ export interface TestCasePayload {
|
|
|
58
60
|
test_data: Record<string, unknown>[];
|
|
59
61
|
timeout_seconds: number;
|
|
60
62
|
retry_count: number;
|
|
63
|
+
depends_on?: string[];
|
|
61
64
|
}
|
|
62
65
|
export interface TestStep {
|
|
63
66
|
action: string;
|
|
@@ -65,6 +68,8 @@ export interface TestStep {
|
|
|
65
68
|
value?: string;
|
|
66
69
|
url?: string;
|
|
67
70
|
description?: string;
|
|
71
|
+
/** Semantic intent — what this step is trying to accomplish (e.g. "Click the submit button"). */
|
|
72
|
+
intent?: string;
|
|
68
73
|
timeout?: number;
|
|
69
74
|
fields?: Record<string, string>;
|
|
70
75
|
condition?: string;
|
|
@@ -90,6 +95,13 @@ export interface TestAssertion {
|
|
|
90
95
|
expected_value?: string;
|
|
91
96
|
description?: string;
|
|
92
97
|
}
|
|
98
|
+
/** Thrown when the org has exceeded its monthly run quota (HTTP 402). */
|
|
99
|
+
export declare class QuotaExceededError extends Error {
|
|
100
|
+
plan: string;
|
|
101
|
+
used: number;
|
|
102
|
+
limit: number;
|
|
103
|
+
constructor(plan: string, used: number, limit: number);
|
|
104
|
+
}
|
|
93
105
|
export declare class CloudClient {
|
|
94
106
|
private apiKey;
|
|
95
107
|
private baseUrl;
|
|
@@ -162,6 +174,10 @@ export declare class CloudClient {
|
|
|
162
174
|
id: string;
|
|
163
175
|
name: string;
|
|
164
176
|
}>;
|
|
177
|
+
/** Apply a healed selector to a test case, replacing the original in all steps. */
|
|
178
|
+
applyHealing(testCaseId: string, originalSelector: string, healedSelector: string): Promise<Record<string, unknown>>;
|
|
179
|
+
/** Detect repeated step sequences across test cases. */
|
|
180
|
+
detectSharedSteps(projectId?: string, autoCreate?: boolean): Promise<Record<string, unknown>>;
|
|
165
181
|
/** Resolve an environment by name within a suite's project. */
|
|
166
182
|
resolveEnvironment(suiteId: string, name: string): Promise<{
|
|
167
183
|
id: string;
|
package/dist/cloud.js
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* HTTP client to Cloud API — authenticates with the org's API key.
|
|
3
3
|
*/
|
|
4
|
+
/** Thrown when the org has exceeded its monthly run quota (HTTP 402). */
|
|
5
|
+
export class QuotaExceededError extends Error {
|
|
6
|
+
plan;
|
|
7
|
+
used;
|
|
8
|
+
limit;
|
|
9
|
+
constructor(plan, used, limit) {
|
|
10
|
+
super(`Monthly run limit reached (${used}/${limit}). Current plan: ${plan}. Upgrade at https://fasttest.ai to continue.`);
|
|
11
|
+
this.plan = plan;
|
|
12
|
+
this.used = used;
|
|
13
|
+
this.limit = limit;
|
|
14
|
+
this.name = "QuotaExceededError";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
4
17
|
export class CloudClient {
|
|
5
18
|
apiKey;
|
|
6
19
|
baseUrl;
|
|
@@ -58,6 +71,11 @@ export class CloudClient {
|
|
|
58
71
|
await new Promise((r) => setTimeout(r, baseDelay * 2 ** attempt));
|
|
59
72
|
continue;
|
|
60
73
|
}
|
|
74
|
+
if (resp.status === 402) {
|
|
75
|
+
// Parse "Monthly run limit reached (50/50). Current plan: free."
|
|
76
|
+
const match = text.match(/\((\d+)\/(\d+)\).*plan:\s*(\w+)/i);
|
|
77
|
+
throw new QuotaExceededError(match?.[3] ?? "unknown", match ? parseInt(match[1]) : 0, match ? parseInt(match[2]) : 0);
|
|
78
|
+
}
|
|
61
79
|
throw new Error(`Cloud API ${method} ${path} → ${resp.status}: ${text}`);
|
|
62
80
|
}
|
|
63
81
|
return (await resp.json());
|
|
@@ -127,6 +145,23 @@ export class CloudClient {
|
|
|
127
145
|
async updateTestCase(testCaseId, body) {
|
|
128
146
|
return this.request("PUT", `/qa/test-cases/${testCaseId}`, body);
|
|
129
147
|
}
|
|
148
|
+
/** Apply a healed selector to a test case, replacing the original in all steps. */
|
|
149
|
+
async applyHealing(testCaseId, originalSelector, healedSelector) {
|
|
150
|
+
return this.post(`/qa/test-cases/${testCaseId}/apply-healing`, {
|
|
151
|
+
original_selector: originalSelector,
|
|
152
|
+
healed_selector: healedSelector,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
/** Detect repeated step sequences across test cases. */
|
|
156
|
+
async detectSharedSteps(projectId, autoCreate) {
|
|
157
|
+
const params = new URLSearchParams();
|
|
158
|
+
if (projectId)
|
|
159
|
+
params.set("project_id", projectId);
|
|
160
|
+
if (autoCreate)
|
|
161
|
+
params.set("auto_create", "true");
|
|
162
|
+
const qs = params.toString() ? `?${params.toString()}` : "";
|
|
163
|
+
return this.post(`/qa/shared-steps/detect${qs}`, {});
|
|
164
|
+
}
|
|
130
165
|
// --- Environments ---
|
|
131
166
|
/** Resolve an environment by name within a suite's project. */
|
|
132
167
|
async resolveEnvironment(suiteId, name) {
|
package/dist/cloud.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloud.js","sourceRoot":"","sources":["../src/cloud.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"cloud.js","sourceRoot":"","sources":["../src/cloud.ts"],"names":[],"mappings":"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"}
|
package/dist/healer.d.ts
CHANGED
package/dist/healer.js
CHANGED
|
@@ -144,9 +144,10 @@ async function validateCandidate(page, selector, context) {
|
|
|
144
144
|
return false;
|
|
145
145
|
}
|
|
146
146
|
}
|
|
147
|
-
// Check description text similarity (if
|
|
148
|
-
|
|
149
|
-
|
|
147
|
+
// Check description/intent text similarity (if they contain quoted text)
|
|
148
|
+
const textSources = [context.description, context.intent].filter(Boolean);
|
|
149
|
+
for (const src of textSources) {
|
|
150
|
+
const quotedMatch = src.match(/['"]([^'"]+)['"]/);
|
|
150
151
|
if (quotedMatch) {
|
|
151
152
|
const expectedText = quotedMatch[1].toLowerCase();
|
|
152
153
|
const elementText = (info.text + " " + info.ariaLabel).toLowerCase();
|
package/dist/healer.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"healer.js","sourceRoot":"","sources":["../src/healer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;
|
|
1
|
+
{"version":3,"file":"healer.js","sourceRoot":"","sources":["../src/healer.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -14,10 +14,11 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
14
14
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
15
15
|
import { z } from "zod";
|
|
16
16
|
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
|
17
|
-
import { join } from "node:path";
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
17
|
+
import { join, dirname } from "node:path";
|
|
18
|
+
import { spawn } from "node:child_process";
|
|
19
|
+
import { fileURLToPath } from "node:url";
|
|
20
|
+
import { BrowserManager, sanitizePath } from "./browser.js";
|
|
21
|
+
import { CloudClient, QuotaExceededError } from "./cloud.js";
|
|
21
22
|
import * as actions from "./actions.js";
|
|
22
23
|
import { executeRun } from "./runner.js";
|
|
23
24
|
import { healSelector } from "./healer.js";
|
|
@@ -85,7 +86,9 @@ After testing, provide a clear summary:
|
|
|
85
86
|
selectors
|
|
86
87
|
|
|
87
88
|
If cloud is connected (setup completed), ask if the user wants to save \
|
|
88
|
-
passing tests as a reusable suite via \`save_suite\` for CI/CD replay.
|
|
89
|
+
passing tests as a reusable suite via \`save_suite\` for CI/CD replay. \
|
|
90
|
+
If tests span multiple features (e.g. auth, navigation, forms), organize \
|
|
91
|
+
them into separate suites by feature rather than one big suite.
|
|
89
92
|
|
|
90
93
|
## Saving tests for CI/CD
|
|
91
94
|
|
|
@@ -100,7 +103,18 @@ The test runner resolves these from environment variables at execution time. \
|
|
|
100
103
|
In CI, they are set as GitHub repository secrets.
|
|
101
104
|
|
|
102
105
|
Do NOT use placeholders for non-sensitive data like URLs, button labels, or \
|
|
103
|
-
page content — only for credentials, tokens, and secrets
|
|
106
|
+
page content — only for credentials, tokens, and secrets.
|
|
107
|
+
|
|
108
|
+
## Step intent for self-healing
|
|
109
|
+
|
|
110
|
+
For each step, include an \`intent\` field describing what the step is trying \
|
|
111
|
+
to accomplish in plain English. This is critical for self-healing: when a \
|
|
112
|
+
selector breaks, the runner uses the intent to find the right replacement \
|
|
113
|
+
element. Good intents describe the WHAT, not the HOW:
|
|
114
|
+
- Good: \`"Click the 'Add to Cart' button"\`
|
|
115
|
+
- Good: \`"Fill the email input in the login form"\`
|
|
116
|
+
- Bad: \`"Click #add-to-cart"\` (just restates the selector)
|
|
117
|
+
- Bad: \`"Click"\` (too vague)`;
|
|
104
118
|
const LOCAL_EXPLORE_PROMPT = `\
|
|
105
119
|
You are autonomously exploring a web application to discover testable flows. \
|
|
106
120
|
The page snapshot and screenshot above show your starting point.
|
|
@@ -225,6 +239,8 @@ For EACH testable flow you discovered, construct a test case with:
|
|
|
225
239
|
- A navigate step to the starting URL
|
|
226
240
|
- The exact interaction steps (click, fill, etc.) using the most stable selectors \
|
|
227
241
|
from your snapshots (data-testid > aria-label > role > text > CSS)
|
|
242
|
+
- An \`intent\` field on EVERY step describing what it does in plain English \
|
|
243
|
+
(e.g. "Click the 'Sign In' button", "Fill the email field with test credentials")
|
|
228
244
|
- At least one assertion per flow verifying the expected outcome
|
|
229
245
|
|
|
230
246
|
Cover these flow types (in priority order):
|
|
@@ -236,18 +252,25 @@ Cover these flow types (in priority order):
|
|
|
236
252
|
|
|
237
253
|
## Step 3: Save (persist the safety net)
|
|
238
254
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
-
|
|
255
|
+
Group test cases by feature area and save MULTIPLE suites — one per feature. \
|
|
256
|
+
For example, if the app has auth, a dashboard, and settings, create:
|
|
257
|
+
- \`save_suite(suite_name: "{suite_name}: Auth", ...)\` for login/logout/signup tests
|
|
258
|
+
- \`save_suite(suite_name: "{suite_name}: Dashboard", ...)\` for dashboard tests
|
|
259
|
+
- \`save_suite(suite_name: "{suite_name}: Settings", ...)\` for settings tests
|
|
260
|
+
|
|
261
|
+
Use project: "{project}" for all suites. If the app is very simple (1-2 pages), \
|
|
262
|
+
a single suite is fine.
|
|
242
263
|
|
|
243
264
|
IMPORTANT: Replace any credentials with \`{{VAR_NAME}}\` placeholders:
|
|
244
265
|
- Passwords: \`{{TEST_USER_PASSWORD}}\`
|
|
245
266
|
- Emails: \`{{TEST_USER_EMAIL}}\`
|
|
246
267
|
- API keys: \`{{STRIPE_TEST_KEY}}\`
|
|
247
268
|
|
|
269
|
+
Include an \`intent\` field on every step for self-healing.
|
|
270
|
+
|
|
248
271
|
## Step 4: Run baseline (establish the starting point)
|
|
249
272
|
|
|
250
|
-
Call \`run\`
|
|
273
|
+
Call \`run\` for each suite to execute all tests.
|
|
251
274
|
This establishes the baseline. Future runs will show what changed.
|
|
252
275
|
|
|
253
276
|
Present the results clearly — this is the first Vibe Shield report for this app.`;
|
|
@@ -257,6 +280,10 @@ Running regression check to see what changed since the last run...
|
|
|
257
280
|
|
|
258
281
|
Call the \`run\` tool with suite_name="{suite_name}".
|
|
259
282
|
|
|
283
|
+
Also check for other Vibe Shield suites for this app using \`list_suites\` with \
|
|
284
|
+
search="{suite_name}". If there are multiple feature suites (e.g. "{suite_name}: Auth", \
|
|
285
|
+
"{suite_name}: Dashboard"), run all of them.
|
|
286
|
+
|
|
260
287
|
The results will include a regression diff showing:
|
|
261
288
|
- **Regressions**: Tests that were passing but now fail (something broke)
|
|
262
289
|
- **Fixes**: Tests that were failing but now pass (something was fixed)
|
|
@@ -369,15 +396,38 @@ function parseArgs() {
|
|
|
369
396
|
// ---------------------------------------------------------------------------
|
|
370
397
|
const consoleLogs = [];
|
|
371
398
|
const MAX_LOGS = 500;
|
|
399
|
+
const recordedSteps = [];
|
|
400
|
+
let recording = false;
|
|
401
|
+
function recordStep(step) {
|
|
402
|
+
if (!recording)
|
|
403
|
+
return;
|
|
404
|
+
recordedSteps.push({ ...step, timestamp: Date.now() });
|
|
405
|
+
}
|
|
406
|
+
function startRecording() {
|
|
407
|
+
recordedSteps.length = 0;
|
|
408
|
+
recording = true;
|
|
409
|
+
}
|
|
410
|
+
function stopRecording() {
|
|
411
|
+
recording = false;
|
|
412
|
+
return [...recordedSteps];
|
|
413
|
+
}
|
|
372
414
|
// ---------------------------------------------------------------------------
|
|
373
415
|
// Boot — resolve auth from CLI > config file > null (local-only mode)
|
|
374
416
|
// ---------------------------------------------------------------------------
|
|
375
417
|
const cliArgs = parseArgs();
|
|
376
418
|
const globalCfg = loadGlobalConfig();
|
|
377
|
-
// Resolution: CLI --api-key wins, then config file, then undefined
|
|
378
|
-
|
|
419
|
+
// Resolution: CLI --api-key wins, then env var, then config file, then undefined
|
|
420
|
+
// Filter out unresolved ${...} placeholders (e.g. from .mcp.json when env var is unset)
|
|
421
|
+
function isRealKey(v) {
|
|
422
|
+
if (!v)
|
|
423
|
+
return undefined;
|
|
424
|
+
if (/^\$\{.+\}$/.test(v))
|
|
425
|
+
return undefined;
|
|
426
|
+
return v;
|
|
427
|
+
}
|
|
428
|
+
const resolvedApiKey = isRealKey(cliArgs.apiKey) || isRealKey(process.env.FASTTEST_API_KEY) || isRealKey(globalCfg.api_key) || undefined;
|
|
379
429
|
const resolvedBaseUrl = cliArgs.baseUrl || globalCfg.base_url || "https://api.fasttest.ai";
|
|
380
|
-
const orgSlug = resolvedApiKey ? (resolvedApiKey.split("_")[1] ?? "default") : "default";
|
|
430
|
+
const orgSlug = sanitizePath(resolvedApiKey ? (resolvedApiKey.split("_")[1] ?? "default") : "default");
|
|
381
431
|
const browserMgr = new BrowserManager({
|
|
382
432
|
browserType: cliArgs.browser,
|
|
383
433
|
headless: cliArgs.headless,
|
|
@@ -444,9 +494,20 @@ async function resolveProjectId(projectName) {
|
|
|
444
494
|
}
|
|
445
495
|
return undefined;
|
|
446
496
|
}
|
|
497
|
+
// Read version from package.json at startup
|
|
498
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
499
|
+
const PKG_VERSION = (() => {
|
|
500
|
+
try {
|
|
501
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8"));
|
|
502
|
+
return pkg.version ?? "0.0.0";
|
|
503
|
+
}
|
|
504
|
+
catch {
|
|
505
|
+
return "0.0.0";
|
|
506
|
+
}
|
|
507
|
+
})();
|
|
447
508
|
const server = new McpServer({
|
|
448
509
|
name: "fasttest",
|
|
449
|
-
version:
|
|
510
|
+
version: PKG_VERSION,
|
|
450
511
|
}, {
|
|
451
512
|
instructions: "FastTest is a QA testing platform. When the user wants to test, QA, explore, or break a web app, " +
|
|
452
513
|
"use FastTest tools (test, explore, chaos, vibe_shield) instead of other browser tools. " +
|
|
@@ -459,6 +520,7 @@ server.tool("browser_navigate", "Navigate to a URL in the browser", { url: z.str
|
|
|
459
520
|
const page = await browserMgr.ensureBrowser();
|
|
460
521
|
attachConsoleListener(page);
|
|
461
522
|
const result = await actions.navigate(page, url);
|
|
523
|
+
recordStep({ action: "navigate", url });
|
|
462
524
|
const snapshot = await actions.getSnapshot(page);
|
|
463
525
|
return {
|
|
464
526
|
content: [{ type: "text", text: JSON.stringify({ ...result, snapshot }, null, 2) }],
|
|
@@ -467,6 +529,7 @@ server.tool("browser_navigate", "Navigate to a URL in the browser", { url: z.str
|
|
|
467
529
|
server.tool("browser_click", "Click an element on the page", { selector: z.string().describe("CSS selector of the element to click") }, async ({ selector }) => {
|
|
468
530
|
const page = await browserMgr.getPage();
|
|
469
531
|
const result = await actions.click(page, selector);
|
|
532
|
+
recordStep({ action: "click", selector });
|
|
470
533
|
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
471
534
|
});
|
|
472
535
|
server.tool("browser_fill", "Fill a form field with a value", {
|
|
@@ -475,6 +538,7 @@ server.tool("browser_fill", "Fill a form field with a value", {
|
|
|
475
538
|
}, async ({ selector, value }) => {
|
|
476
539
|
const page = await browserMgr.getPage();
|
|
477
540
|
const result = await actions.fill(page, selector, value);
|
|
541
|
+
recordStep({ action: "fill", selector, value });
|
|
478
542
|
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
479
543
|
});
|
|
480
544
|
server.tool("browser_screenshot", "Capture a screenshot of the current page", { full_page: z.boolean().optional().describe("Capture full page (default false)") }, async ({ full_page }) => {
|
|
@@ -534,16 +598,19 @@ server.tool("browser_restore_session", "Restore a previously saved browser sessi
|
|
|
534
598
|
server.tool("browser_go_back", "Navigate back in the browser history", {}, async () => {
|
|
535
599
|
const page = await browserMgr.getPage();
|
|
536
600
|
const result = await actions.goBack(page);
|
|
601
|
+
recordStep({ action: "go_back" });
|
|
537
602
|
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
538
603
|
});
|
|
539
604
|
server.tool("browser_go_forward", "Navigate forward in the browser history", {}, async () => {
|
|
540
605
|
const page = await browserMgr.getPage();
|
|
541
606
|
const result = await actions.goForward(page);
|
|
607
|
+
recordStep({ action: "go_forward" });
|
|
542
608
|
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
543
609
|
});
|
|
544
610
|
server.tool("browser_press_key", "Press a keyboard key (Enter, Tab, Escape, ArrowDown, etc.)", { key: z.string().describe("Key to press (e.g. 'Enter', 'Tab', 'Escape', 'ArrowDown', 'Control+a')") }, async ({ key }) => {
|
|
545
611
|
const page = await browserMgr.getPage();
|
|
546
612
|
const result = await actions.pressKey(page, key);
|
|
613
|
+
recordStep({ action: "press_key", key });
|
|
547
614
|
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
548
615
|
});
|
|
549
616
|
server.tool("browser_file_upload", "Upload file(s) to a file input element", {
|
|
@@ -552,6 +619,7 @@ server.tool("browser_file_upload", "Upload file(s) to a file input element", {
|
|
|
552
619
|
}, async ({ selector, paths }) => {
|
|
553
620
|
const page = await browserMgr.getPage();
|
|
554
621
|
const result = await actions.uploadFile(page, selector, paths);
|
|
622
|
+
recordStep({ action: "upload_file", selector, value: paths.join(",") });
|
|
555
623
|
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
556
624
|
});
|
|
557
625
|
server.tool("browser_handle_dialog", "Accept or dismiss a JavaScript dialog (alert, confirm, prompt)", {
|
|
@@ -637,6 +705,7 @@ server.tool("browser_fill_form", "Fill multiple form fields at once (batch opera
|
|
|
637
705
|
}, async ({ fields }) => {
|
|
638
706
|
const page = await browserMgr.getPage();
|
|
639
707
|
const result = await actions.fillForm(page, fields);
|
|
708
|
+
recordStep({ action: "fill_form", fields });
|
|
640
709
|
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
641
710
|
});
|
|
642
711
|
server.tool("browser_network_requests", "List captured network requests from the current session. Shows API calls, failed requests, and document loads (static assets are filtered out).", {
|
|
@@ -666,15 +735,25 @@ server.tool("browser_network_requests", "List captured network requests from the
|
|
|
666
735
|
// ---------------------------------------------------------------------------
|
|
667
736
|
function openBrowser(url) {
|
|
668
737
|
try {
|
|
738
|
+
// Validate URL to prevent command injection (especially on Windows where
|
|
739
|
+
// cmd.exe interprets special characters like & | > in arguments).
|
|
740
|
+
const parsed = new URL(url);
|
|
741
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:")
|
|
742
|
+
return;
|
|
669
743
|
const platform = process.platform;
|
|
670
744
|
if (platform === "darwin") {
|
|
671
|
-
|
|
745
|
+
spawn("open", [url], { stdio: "ignore", detached: true }).unref();
|
|
672
746
|
}
|
|
673
747
|
else if (platform === "win32") {
|
|
674
|
-
|
|
748
|
+
// Use PowerShell Start-Process which doesn't interpret shell metacharacters
|
|
749
|
+
spawn("powershell", ["-NoProfile", "-Command", `Start-Process '${url.replace(/'/g, "''")}'`], {
|
|
750
|
+
stdio: "ignore",
|
|
751
|
+
detached: true,
|
|
752
|
+
windowsHide: true,
|
|
753
|
+
}).unref();
|
|
675
754
|
}
|
|
676
755
|
else {
|
|
677
|
-
|
|
756
|
+
spawn("xdg-open", [url], { stdio: "ignore", detached: true }).unref();
|
|
678
757
|
}
|
|
679
758
|
}
|
|
680
759
|
catch {
|
|
@@ -787,6 +866,8 @@ server.tool("test", "PRIMARY TOOL for testing web applications. Use this when th
|
|
|
787
866
|
// Always use local mode: host AI drives browser tools directly.
|
|
788
867
|
// Cloud LLM is never used from the MCP server — the host AI (Claude Code,
|
|
789
868
|
// Codex, etc.) follows our prompt with its own reasoning capability.
|
|
869
|
+
// Start recording browser actions for auto-capture
|
|
870
|
+
startRecording();
|
|
790
871
|
const lines = [];
|
|
791
872
|
if (url) {
|
|
792
873
|
const page = await browserMgr.ensureBrowser();
|
|
@@ -812,6 +893,7 @@ server.tool("test", "PRIMARY TOOL for testing web applications. Use this when th
|
|
|
812
893
|
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
813
894
|
});
|
|
814
895
|
server.tool("save_suite", "Save test cases as a reusable test suite in the cloud. Use this after running tests to persist them for CI/CD replay. " +
|
|
896
|
+
"If you just ran the `test` tool, browser actions were recorded automatically — use them as the basis for your test steps. " +
|
|
815
897
|
"IMPORTANT: For sensitive values (passwords, API keys, tokens), use {{VAR_NAME}} placeholders instead of literal values. " +
|
|
816
898
|
"Example: use {{TEST_USER_PASSWORD}} instead of the actual password. " +
|
|
817
899
|
"The runner resolves these from environment variables at execution time. Variable names must be UPPER_SNAKE_CASE.", {
|
|
@@ -822,13 +904,29 @@ server.tool("save_suite", "Save test cases as a reusable test suite in the cloud
|
|
|
822
904
|
name: z.string().describe("Test case name"),
|
|
823
905
|
description: z.string().optional().describe("What this test verifies"),
|
|
824
906
|
priority: z.enum(["high", "medium", "low"]).optional().describe("Test priority"),
|
|
825
|
-
steps: z.array(z.record(z.string(), z.unknown())).describe("Test steps: [{action, selector?, value?, url?, description?}]. " +
|
|
907
|
+
steps: z.array(z.record(z.string(), z.unknown())).describe("Test steps: [{action, selector?, value?, url?, description?, intent?}]. " +
|
|
908
|
+
"Include 'intent' on every step — a plain-English description of WHAT the step does (e.g. \"Click the 'Submit' button\"). " +
|
|
826
909
|
"Use {{VAR_NAME}} placeholders for sensitive values (e.g. value: '{{TEST_PASSWORD}}')"),
|
|
827
910
|
assertions: z.array(z.record(z.string(), z.unknown())).describe("Assertions: [{type, selector?, text?, url?, count?}]"),
|
|
828
911
|
tags: z.array(z.string()).optional().describe("Tags for categorization"),
|
|
829
912
|
})).describe("Array of test cases to save"),
|
|
830
913
|
}, async ({ suite_name, description, project, test_cases }) => {
|
|
914
|
+
// Stop recording and capture any auto-recorded steps
|
|
915
|
+
const captured = stopRecording();
|
|
831
916
|
if (!test_cases || test_cases.length === 0) {
|
|
917
|
+
if (captured.length > 0) {
|
|
918
|
+
// Return recorded steps so the host AI can build test cases from them
|
|
919
|
+
const stepsJson = JSON.stringify(captured.map(({ timestamp: _, ...s }) => s), null, 2);
|
|
920
|
+
return {
|
|
921
|
+
content: [{
|
|
922
|
+
type: "text",
|
|
923
|
+
text: `No test cases provided, but ${captured.length} browser actions were recorded during testing:\n\n` +
|
|
924
|
+
"```json\n" + stepsJson + "\n```\n\n" +
|
|
925
|
+
"Use these as the basis for your test cases and call `save_suite` again with the test_cases array populated. " +
|
|
926
|
+
"Add an `intent` field to each step and replace sensitive values with `{{VAR_NAME}}` placeholders.",
|
|
927
|
+
}],
|
|
928
|
+
};
|
|
929
|
+
}
|
|
832
930
|
return { content: [{ type: "text", text: "Cannot save an empty suite. Provide at least one test case." }] };
|
|
833
931
|
}
|
|
834
932
|
const c = requireCloud();
|
|
@@ -890,6 +988,24 @@ server.tool("save_suite", "Save test cases as a reusable test suite in the cloud
|
|
|
890
988
|
lines.push(` - ${v}`);
|
|
891
989
|
}
|
|
892
990
|
}
|
|
991
|
+
// Auto-detect shared steps across test cases in this project
|
|
992
|
+
try {
|
|
993
|
+
const detection = await c.detectSharedSteps(finalProjectId, true);
|
|
994
|
+
if (detection.created && detection.created.length > 0) {
|
|
995
|
+
lines.push("");
|
|
996
|
+
lines.push("Shared steps auto-extracted:");
|
|
997
|
+
for (const ss of detection.created) {
|
|
998
|
+
lines.push(` - ${ss.name} (${ss.step_count} steps, used in ${ss.used_in} test cases)`);
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
else if (detection.suggestions && detection.suggestions.length > 0) {
|
|
1002
|
+
lines.push("");
|
|
1003
|
+
lines.push(`Detected ${detection.suggestions.length} repeated step sequence(s) across test cases.`);
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
catch {
|
|
1007
|
+
// Non-fatal — detection failure shouldn't block save
|
|
1008
|
+
}
|
|
893
1009
|
return {
|
|
894
1010
|
content: [{ type: "text", text: lines.join("\n") }],
|
|
895
1011
|
};
|
|
@@ -903,7 +1019,7 @@ server.tool("update_suite", "Update test cases in an existing suite. Use this wh
|
|
|
903
1019
|
name: z.string().describe("Test case name"),
|
|
904
1020
|
description: z.string().optional(),
|
|
905
1021
|
priority: z.enum(["high", "medium", "low"]).optional(),
|
|
906
|
-
steps: z.array(z.record(z.string(), z.unknown())).describe("Updated test steps"),
|
|
1022
|
+
steps: z.array(z.record(z.string(), z.unknown())).describe("Updated test steps — include 'intent' on every step for self-healing"),
|
|
907
1023
|
assertions: z.array(z.record(z.string(), z.unknown())).describe("Updated assertions"),
|
|
908
1024
|
tags: z.array(z.string()).optional(),
|
|
909
1025
|
})).describe("Test cases to update or add"),
|
|
@@ -1225,11 +1341,37 @@ server.tool("run", "Run a test suite. Executes all test cases in a real browser
|
|
|
1225
1341
|
};
|
|
1226
1342
|
}
|
|
1227
1343
|
}
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1344
|
+
let summary;
|
|
1345
|
+
try {
|
|
1346
|
+
summary = await executeRun(browserMgr, cloudClient, {
|
|
1347
|
+
suiteId: resolvedSuiteId,
|
|
1348
|
+
environmentId,
|
|
1349
|
+
testCaseIds: test_case_ids,
|
|
1350
|
+
aiFallback: true,
|
|
1351
|
+
}, consoleLogs);
|
|
1352
|
+
}
|
|
1353
|
+
catch (err) {
|
|
1354
|
+
if (err instanceof QuotaExceededError) {
|
|
1355
|
+
const upgrade = err.plan === "free"
|
|
1356
|
+
? "Upgrade to Pro ($15/mo) for 1,000 runs/month"
|
|
1357
|
+
: err.plan === "pro"
|
|
1358
|
+
? "Upgrade to Team ($99/mo) for unlimited runs"
|
|
1359
|
+
: "Contact support for higher limits";
|
|
1360
|
+
return {
|
|
1361
|
+
content: [{
|
|
1362
|
+
type: "text",
|
|
1363
|
+
text: [
|
|
1364
|
+
`## Monthly run limit reached`,
|
|
1365
|
+
``,
|
|
1366
|
+
`You've used **${err.used}/${err.limit} runs** this month on the **${err.plan.toUpperCase()}** plan.`,
|
|
1367
|
+
``,
|
|
1368
|
+
`${upgrade} at https://fasttest.ai`,
|
|
1369
|
+
].join("\n"),
|
|
1370
|
+
}],
|
|
1371
|
+
};
|
|
1372
|
+
}
|
|
1373
|
+
throw err;
|
|
1374
|
+
}
|
|
1233
1375
|
// Format a human-readable summary
|
|
1234
1376
|
const lines = [
|
|
1235
1377
|
`# Vibe Shield Report ${summary.status === "passed" ? "✅ PASSED" : "❌ FAILED"}`,
|
|
@@ -1322,6 +1464,31 @@ server.tool("run", "Run a test suite. Executes all test cases in a real browser
|
|
|
1322
1464
|
}
|
|
1323
1465
|
lines.push("");
|
|
1324
1466
|
}
|
|
1467
|
+
// AI fallback: if a step failed and we have diagnostic context, give the host AI
|
|
1468
|
+
// instructions to intervene using browser tools
|
|
1469
|
+
if (summary.ai_fallback) {
|
|
1470
|
+
const fb = summary.ai_fallback;
|
|
1471
|
+
lines.push("## AI Fallback — Manual Intervention Needed");
|
|
1472
|
+
lines.push("");
|
|
1473
|
+
lines.push(`Test **"${fb.test_case_name}"** failed at step ${fb.step_index + 1}.`);
|
|
1474
|
+
if (fb.intent) {
|
|
1475
|
+
lines.push(`**Intent**: ${fb.intent}`);
|
|
1476
|
+
}
|
|
1477
|
+
lines.push(`**Error**: ${fb.error}`);
|
|
1478
|
+
lines.push(`**Page URL**: ${fb.page_url}`);
|
|
1479
|
+
lines.push("");
|
|
1480
|
+
lines.push("The browser is still open on the failing page. You can use browser tools to:");
|
|
1481
|
+
lines.push("1. Take a `browser_snapshot` to see the current page state");
|
|
1482
|
+
lines.push("2. Use `heal` with the broken selector to find a replacement");
|
|
1483
|
+
lines.push("3. Manually execute the failing step with the correct selector");
|
|
1484
|
+
lines.push("4. If the element is genuinely missing, this may be a real bug in the app");
|
|
1485
|
+
lines.push("");
|
|
1486
|
+
lines.push("### Page Snapshot at failure");
|
|
1487
|
+
lines.push("```json");
|
|
1488
|
+
lines.push(JSON.stringify(fb.snapshot, null, 2));
|
|
1489
|
+
lines.push("```");
|
|
1490
|
+
lines.push("");
|
|
1491
|
+
}
|
|
1325
1492
|
// Post PR comment if pr_url was provided
|
|
1326
1493
|
if (pr_url) {
|
|
1327
1494
|
try {
|