@bbearai/ai-executor 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +591 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.mts +233 -0
- package/dist/index.d.ts +233 -0
- package/dist/index.js +407 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +367 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +60 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
createStagehandSession: () => createStagehandSession,
|
|
34
|
+
generateRunSummary: () => generateRunSummary,
|
|
35
|
+
injectAuth: () => injectAuth,
|
|
36
|
+
runTest: () => runTest
|
|
37
|
+
});
|
|
38
|
+
module.exports = __toCommonJS(index_exports);
|
|
39
|
+
|
|
40
|
+
// src/runner.ts
|
|
41
|
+
var import_sdk = __toESM(require("@anthropic-ai/sdk"));
|
|
42
|
+
var import_zod = require("zod");
|
|
43
|
+
|
|
44
|
+
// src/browser.ts
|
|
45
|
+
var import_stagehand = require("@browserbasehq/stagehand");
|
|
46
|
+
var DEFAULT_MODEL = "anthropic/claude-sonnet-4-20250514";
|
|
47
|
+
async function createStagehandSession(config, anthropicApiKey) {
|
|
48
|
+
const modelName = config.model ?? DEFAULT_MODEL;
|
|
49
|
+
const viewport = config.viewport ?? { width: 1280, height: 720 };
|
|
50
|
+
const stagehand = new import_stagehand.Stagehand({
|
|
51
|
+
env: config.provider === "browserbase" ? "BROWSERBASE" : "LOCAL",
|
|
52
|
+
apiKey: config.provider === "browserbase" ? config.browserbaseApiKey : void 0,
|
|
53
|
+
projectId: config.provider === "browserbase" ? config.browserbaseProjectId : void 0,
|
|
54
|
+
model: {
|
|
55
|
+
modelName,
|
|
56
|
+
apiKey: anthropicApiKey
|
|
57
|
+
},
|
|
58
|
+
localBrowserLaunchOptions: config.provider === "local" ? {
|
|
59
|
+
headless: config.headless ?? true,
|
|
60
|
+
viewport
|
|
61
|
+
} : void 0,
|
|
62
|
+
browserbaseSessionCreateParams: config.provider === "browserbase" ? { projectId: config.browserbaseProjectId } : void 0
|
|
63
|
+
});
|
|
64
|
+
await stagehand.init();
|
|
65
|
+
const page = stagehand.context.activePage();
|
|
66
|
+
await page.setViewportSize(viewport.width, viewport.height);
|
|
67
|
+
let sessionId = `local-${Date.now()}`;
|
|
68
|
+
if (config.provider === "browserbase" && stagehand.browserbaseSessionID) {
|
|
69
|
+
sessionId = stagehand.browserbaseSessionID;
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
stagehand,
|
|
73
|
+
page,
|
|
74
|
+
sessionId,
|
|
75
|
+
close: async () => {
|
|
76
|
+
await stagehand.close().catch(() => {
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
async function injectAuth(page, auth, stagehand) {
|
|
82
|
+
if (auth.type === "cookie") {
|
|
83
|
+
for (const c of auth.cookies) {
|
|
84
|
+
await page.sendCDP("Network.setCookie", {
|
|
85
|
+
name: c.name,
|
|
86
|
+
value: c.value,
|
|
87
|
+
domain: c.domain,
|
|
88
|
+
path: c.path ?? "/",
|
|
89
|
+
secure: c.secure ?? false,
|
|
90
|
+
httpOnly: c.httpOnly ?? false,
|
|
91
|
+
sameSite: c.sameSite ?? "Lax"
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
} else if (auth.type === "localStorage") {
|
|
95
|
+
const currentUrl = page.url();
|
|
96
|
+
if (currentUrl === "about:blank") {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
await page.evaluate((items) => {
|
|
100
|
+
for (const [key, value] of Object.entries(items)) {
|
|
101
|
+
localStorage.setItem(key, value);
|
|
102
|
+
}
|
|
103
|
+
}, auth.items);
|
|
104
|
+
} else if (auth.type === "form-login") {
|
|
105
|
+
await performFormLogin(page, auth, stagehand);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
async function performFormLogin(page, auth, stagehand) {
|
|
109
|
+
await page.goto(auth.loginUrl, { waitUntil: "domcontentloaded" });
|
|
110
|
+
await page.waitForLoadState("networkidle", 15e3).catch(() => {
|
|
111
|
+
});
|
|
112
|
+
if (stagehand) {
|
|
113
|
+
await stagehand.act(
|
|
114
|
+
`Fill in the email/username field with "${auth.email}" and the password field with "${auth.password}", then click the login/sign-in button to submit the form.`
|
|
115
|
+
);
|
|
116
|
+
} else {
|
|
117
|
+
await manualFormLogin(page, auth);
|
|
118
|
+
}
|
|
119
|
+
await page.waitForLoadState("networkidle", 15e3).catch(() => {
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
async function manualFormLogin(page, auth) {
|
|
123
|
+
await page.waitForSelector(
|
|
124
|
+
'input[type="email"], input[type="text"][name*="email"], input[name*="user"], input[type="text"]',
|
|
125
|
+
{ timeout: 15e3 }
|
|
126
|
+
).catch(() => {
|
|
127
|
+
});
|
|
128
|
+
const emailSelectors = [
|
|
129
|
+
'input[type="email"]',
|
|
130
|
+
'input[name="email"]',
|
|
131
|
+
'input[name="username"]',
|
|
132
|
+
'input[autocomplete="email"]',
|
|
133
|
+
'input[autocomplete="username"]',
|
|
134
|
+
'input[type="text"][name*="email"]',
|
|
135
|
+
'input[type="text"][name*="user"]',
|
|
136
|
+
'input[type="text"]'
|
|
137
|
+
];
|
|
138
|
+
let emailFilled = false;
|
|
139
|
+
for (const sel of emailSelectors) {
|
|
140
|
+
const locator = page.locator(sel);
|
|
141
|
+
if (await locator.count() > 0 && await locator.isVisible()) {
|
|
142
|
+
await locator.fill(auth.email);
|
|
143
|
+
emailFilled = true;
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (!emailFilled) {
|
|
148
|
+
throw new Error("Could not find email/username input on login page");
|
|
149
|
+
}
|
|
150
|
+
const passwordLocator = page.locator('input[type="password"]');
|
|
151
|
+
if (await passwordLocator.count() > 0 && await passwordLocator.isVisible()) {
|
|
152
|
+
await passwordLocator.fill(auth.password);
|
|
153
|
+
} else {
|
|
154
|
+
throw new Error("Could not find password input on login page");
|
|
155
|
+
}
|
|
156
|
+
const submitSelectors = [
|
|
157
|
+
'button[type="submit"]',
|
|
158
|
+
'input[type="submit"]'
|
|
159
|
+
];
|
|
160
|
+
let submitted = false;
|
|
161
|
+
for (const sel of submitSelectors) {
|
|
162
|
+
const locator = page.locator(sel);
|
|
163
|
+
if (await locator.count() > 0 && await locator.isVisible()) {
|
|
164
|
+
await locator.click();
|
|
165
|
+
submitted = true;
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (!submitted) {
|
|
170
|
+
await page.locator('input[type="password"]').type("\n");
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// src/evaluator.ts
|
|
175
|
+
async function generateRunSummary(anthropic, testTitle, steps, model) {
|
|
176
|
+
const stepsText = steps.map(
|
|
177
|
+
(s) => `Step ${s.stepNumber}: ${s.action}
|
|
178
|
+
Expected: ${s.expectedResult}
|
|
179
|
+
Actual: ${s.actualResult}
|
|
180
|
+
Result: ${s.passed ? "PASS" : "FAIL"} (confidence: ${Math.round(s.confidence * 100)}%)${s.error ? `
|
|
181
|
+
Error: ${s.error}` : ""}`
|
|
182
|
+
).join("\n\n");
|
|
183
|
+
const passCount = steps.filter((s) => s.passed).length;
|
|
184
|
+
const failCount = steps.filter((s) => !s.passed).length;
|
|
185
|
+
const response = await anthropic.messages.create({
|
|
186
|
+
model,
|
|
187
|
+
max_tokens: 512,
|
|
188
|
+
messages: [
|
|
189
|
+
{
|
|
190
|
+
role: "user",
|
|
191
|
+
content: `Summarize this AI test execution in 2-3 sentences. Focus on what was tested, what passed, and what failed (if anything). Be concise and factual.
|
|
192
|
+
|
|
193
|
+
Test: ${testTitle}
|
|
194
|
+
Results: ${passCount} passed, ${failCount} failed out of ${steps.length} steps
|
|
195
|
+
|
|
196
|
+
${stepsText}`
|
|
197
|
+
}
|
|
198
|
+
]
|
|
199
|
+
});
|
|
200
|
+
return response.content.filter((block) => block.type === "text").map((block) => block.text).join("");
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// src/runner.ts
|
|
204
|
+
async function runTest(config) {
|
|
205
|
+
const anthropic = new import_sdk.default({ apiKey: config.anthropicApiKey });
|
|
206
|
+
const startTime = Date.now();
|
|
207
|
+
const browserConfig = config.browser ?? {
|
|
208
|
+
provider: "local",
|
|
209
|
+
headless: true
|
|
210
|
+
};
|
|
211
|
+
config.onStatusChange?.("initializing");
|
|
212
|
+
const session = await createStagehandSession(browserConfig, config.anthropicApiKey);
|
|
213
|
+
const { stagehand, page } = session;
|
|
214
|
+
const stepResults = [];
|
|
215
|
+
let pendingConsoleLogs = [];
|
|
216
|
+
let pendingNetworkErrors = [];
|
|
217
|
+
let stepStartTime = Date.now();
|
|
218
|
+
const rawPage = page;
|
|
219
|
+
rawPage.on("console", (msg) => {
|
|
220
|
+
const level = msg.type?.() ?? msg.type ?? "log";
|
|
221
|
+
const mappedLevel = level === "error" ? "error" : level === "warn" || level === "warning" ? "warning" : level === "info" ? "info" : level === "debug" ? "debug" : "log";
|
|
222
|
+
pendingConsoleLogs.push({
|
|
223
|
+
level: mappedLevel,
|
|
224
|
+
text: (typeof msg.text === "function" ? msg.text() : String(msg.text ?? msg)).slice(0, 2e3),
|
|
225
|
+
source: typeof msg.location === "function" ? msg.location()?.url : void 0,
|
|
226
|
+
timestamp: Date.now() - stepStartTime
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
rawPage.on("requestfailed", (req) => {
|
|
230
|
+
const url = typeof req.url === "function" ? req.url() : String(req.url ?? "");
|
|
231
|
+
const method = typeof req.method === "function" ? req.method() : String(req.method ?? "GET");
|
|
232
|
+
const failure = typeof req.failure === "function" ? req.failure() : req.failure;
|
|
233
|
+
pendingNetworkErrors.push({
|
|
234
|
+
method,
|
|
235
|
+
url: url.slice(0, 500),
|
|
236
|
+
status: 0,
|
|
237
|
+
statusText: failure?.errorText ?? "Request failed",
|
|
238
|
+
timestamp: Date.now() - stepStartTime
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
rawPage.on("response", (res) => {
|
|
242
|
+
const status = typeof res.status === "function" ? res.status() : Number(res.status ?? 0);
|
|
243
|
+
if (status >= 400) {
|
|
244
|
+
const url = typeof res.url === "function" ? res.url() : String(res.url ?? "");
|
|
245
|
+
const statusText = typeof res.statusText === "function" ? res.statusText() : String(res.statusText ?? "");
|
|
246
|
+
const req = typeof res.request === "function" ? res.request() : res.request;
|
|
247
|
+
const method = req ? typeof req.method === "function" ? req.method() : String(req.method ?? "GET") : "GET";
|
|
248
|
+
pendingNetworkErrors.push({
|
|
249
|
+
method,
|
|
250
|
+
url: url.slice(0, 500),
|
|
251
|
+
status,
|
|
252
|
+
statusText,
|
|
253
|
+
timestamp: Date.now() - stepStartTime
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
try {
|
|
258
|
+
if (config.auth?.type === "form-login") {
|
|
259
|
+
config.onStatusChange?.("authenticating");
|
|
260
|
+
await injectAuth(page, config.auth, stagehand);
|
|
261
|
+
}
|
|
262
|
+
config.onStatusChange?.("navigating");
|
|
263
|
+
const targetUrl = config.testCase.targetRoute ? `${config.targetUrl.replace(/\/$/, "")}${config.testCase.targetRoute}` : config.targetUrl;
|
|
264
|
+
await page.goto(targetUrl, { waitUntil: "domcontentloaded", timeoutMs: 3e4 });
|
|
265
|
+
if (config.auth && config.auth.type !== "form-login") {
|
|
266
|
+
config.onStatusChange?.("authenticating");
|
|
267
|
+
await injectAuth(page, config.auth, stagehand);
|
|
268
|
+
if (config.auth.type === "localStorage") {
|
|
269
|
+
await page.evaluate((items) => {
|
|
270
|
+
for (const [key, value] of Object.entries(items)) {
|
|
271
|
+
localStorage.setItem(key, value);
|
|
272
|
+
}
|
|
273
|
+
}, config.auth.items);
|
|
274
|
+
await page.reload({ waitUntil: "domcontentloaded" });
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
await page.waitForLoadState("networkidle").catch(() => {
|
|
278
|
+
});
|
|
279
|
+
pendingConsoleLogs = [];
|
|
280
|
+
pendingNetworkErrors = [];
|
|
281
|
+
config.onStatusChange?.("executing");
|
|
282
|
+
const steps = config.testCase.steps;
|
|
283
|
+
for (let i = 0; i < steps.length; i++) {
|
|
284
|
+
const step = steps[i];
|
|
285
|
+
stepStartTime = Date.now();
|
|
286
|
+
pendingConsoleLogs = [];
|
|
287
|
+
pendingNetworkErrors = [];
|
|
288
|
+
const screenshotBefore = await page.screenshot({ type: "png" });
|
|
289
|
+
let error;
|
|
290
|
+
let screenshotAfter = screenshotBefore;
|
|
291
|
+
let actSucceeded = false;
|
|
292
|
+
try {
|
|
293
|
+
await stagehand.act(step.action);
|
|
294
|
+
actSucceeded = true;
|
|
295
|
+
await page.waitForLoadState("networkidle").catch(() => {
|
|
296
|
+
});
|
|
297
|
+
await page.waitForTimeout(500);
|
|
298
|
+
screenshotAfter = await page.screenshot({ type: "png" });
|
|
299
|
+
} catch (err) {
|
|
300
|
+
error = err instanceof Error ? err.message : String(err);
|
|
301
|
+
screenshotAfter = await page.screenshot({ type: "png" }).catch(() => screenshotBefore);
|
|
302
|
+
}
|
|
303
|
+
let evaluation = {
|
|
304
|
+
passed: false,
|
|
305
|
+
confidence: 0,
|
|
306
|
+
actualResult: error ?? "Action execution failed"
|
|
307
|
+
};
|
|
308
|
+
if (actSucceeded) {
|
|
309
|
+
try {
|
|
310
|
+
const verificationSchema = import_zod.z.object({
|
|
311
|
+
passed: import_zod.z.boolean().describe("Whether the expected result was achieved"),
|
|
312
|
+
confidence: import_zod.z.number().min(0).max(1).describe("Confidence in the assessment (0.9+ = very sure, 0.7-0.9 = likely, below 0.7 = uncertain)"),
|
|
313
|
+
actualResult: import_zod.z.string().describe("Description of what actually happened on the page")
|
|
314
|
+
});
|
|
315
|
+
const verification = await stagehand.extract(
|
|
316
|
+
`You are evaluating a QA test step. The action "${step.action}" was just performed. Check if this expected result was achieved: "${step.expectedResult}". Look at the current page state and describe what actually happened. Be precise and factual in your assessment.`,
|
|
317
|
+
verificationSchema
|
|
318
|
+
);
|
|
319
|
+
evaluation = {
|
|
320
|
+
passed: verification.passed,
|
|
321
|
+
confidence: verification.confidence,
|
|
322
|
+
actualResult: verification.actualResult
|
|
323
|
+
};
|
|
324
|
+
} catch (evalErr) {
|
|
325
|
+
evaluation = {
|
|
326
|
+
passed: false,
|
|
327
|
+
confidence: 0.2,
|
|
328
|
+
actualResult: `Verification error: ${evalErr instanceof Error ? evalErr.message : String(evalErr)}`
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
const consoleLogs = pendingConsoleLogs.slice(0, 50);
|
|
333
|
+
const networkErrors = pendingNetworkErrors.slice(0, 30);
|
|
334
|
+
const result = {
|
|
335
|
+
stepNumber: step.stepNumber,
|
|
336
|
+
action: step.action,
|
|
337
|
+
expectedResult: step.expectedResult,
|
|
338
|
+
actualResult: evaluation.actualResult,
|
|
339
|
+
passed: evaluation.passed,
|
|
340
|
+
confidence: evaluation.confidence,
|
|
341
|
+
screenshotBefore,
|
|
342
|
+
screenshotAfter,
|
|
343
|
+
actionsTaken: [],
|
|
344
|
+
// Stagehand handles actions internally
|
|
345
|
+
error,
|
|
346
|
+
durationMs: Date.now() - stepStartTime,
|
|
347
|
+
consoleLogs,
|
|
348
|
+
networkErrors
|
|
349
|
+
};
|
|
350
|
+
stepResults.push(result);
|
|
351
|
+
config.onStepComplete?.(result, i, steps.length);
|
|
352
|
+
}
|
|
353
|
+
config.onStatusChange?.("completed");
|
|
354
|
+
const model = config.model ?? "claude-sonnet-4-20250514";
|
|
355
|
+
const summary = await generateRunSummary(anthropic, config.testCase.title, stepResults, model);
|
|
356
|
+
const overallResult = determineOverallResult(stepResults);
|
|
357
|
+
return {
|
|
358
|
+
testCaseId: config.testCase.id,
|
|
359
|
+
testCaseTitle: config.testCase.title,
|
|
360
|
+
overallResult,
|
|
361
|
+
steps: stepResults,
|
|
362
|
+
totalDurationMs: Date.now() - startTime,
|
|
363
|
+
summary,
|
|
364
|
+
screenshotUrls: [],
|
|
365
|
+
tokenUsage: {
|
|
366
|
+
// Stagehand tracks tokens internally; these are approximate
|
|
367
|
+
inputTokens: steps.length * 3e3,
|
|
368
|
+
outputTokens: steps.length * 500
|
|
369
|
+
},
|
|
370
|
+
browserSessionId: session.sessionId
|
|
371
|
+
};
|
|
372
|
+
} catch (err) {
|
|
373
|
+
return {
|
|
374
|
+
testCaseId: config.testCase.id,
|
|
375
|
+
testCaseTitle: config.testCase.title,
|
|
376
|
+
overallResult: "error",
|
|
377
|
+
steps: stepResults,
|
|
378
|
+
totalDurationMs: Date.now() - startTime,
|
|
379
|
+
summary: `Test execution failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
380
|
+
screenshotUrls: [],
|
|
381
|
+
tokenUsage: {
|
|
382
|
+
inputTokens: stepResults.length * 3e3,
|
|
383
|
+
outputTokens: stepResults.length * 500
|
|
384
|
+
},
|
|
385
|
+
browserSessionId: session.sessionId
|
|
386
|
+
};
|
|
387
|
+
} finally {
|
|
388
|
+
await session.close();
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
function determineOverallResult(steps) {
|
|
392
|
+
if (steps.length === 0) return "error";
|
|
393
|
+
const allPassed = steps.every((s) => s.passed);
|
|
394
|
+
const allFailed = steps.every((s) => !s.passed);
|
|
395
|
+
const hasErrors = steps.some((s) => s.error);
|
|
396
|
+
if (allPassed) return "passed";
|
|
397
|
+
if (allFailed || hasErrors) return "failed";
|
|
398
|
+
return "partial";
|
|
399
|
+
}
|
|
400
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
401
|
+
0 && (module.exports = {
|
|
402
|
+
createStagehandSession,
|
|
403
|
+
generateRunSummary,
|
|
404
|
+
injectAuth,
|
|
405
|
+
runTest
|
|
406
|
+
});
|
|
407
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/runner.ts","../src/browser.ts","../src/evaluator.ts"],"sourcesContent":["/**\n * @bbearai/ai-executor\n *\n * AI-powered QA test executor using Stagehand and Claude.\n * Executes natural language test steps in a real browser,\n * using Stagehand for AI-powered element interaction and\n * structured data extraction for result evaluation.\n */\n\n// Core runner\nexport { runTest } from './runner';\n\n// Browser management\nexport { createStagehandSession, injectAuth } from './browser';\nexport type { StagehandSession } from './browser';\n\n// Result summary\nexport { generateRunSummary } from './evaluator';\n\n// Types\nexport type {\n // Browser\n BrowserProvider,\n BrowserConfig,\n // Auth\n CookieAuth,\n LocalStorageAuth,\n FormLoginAuth,\n AuthConfig,\n // Test case\n TestStep,\n TestCaseInput,\n // Results\n StepAction,\n ConsoleEntry,\n NetworkError,\n StepResult,\n TestRunConfig,\n TestRunStatus,\n OverallResult,\n TestRunResult,\n} from './types';\n","/**\n * Test Runner\n *\n * Orchestrates the full test execution lifecycle using Stagehand:\n * 1. Launch Stagehand browser session\n * 2. Navigate to target URL\n * 3. Inject authentication\n * 4. For each step: act() → screenshot → extract() → record\n * 5. Generate summary\n * 6. Return structured results\n */\n\nimport Anthropic from '@anthropic-ai/sdk';\nimport { z } from 'zod';\nimport { createStagehandSession, injectAuth } from './browser';\nimport { generateRunSummary } from './evaluator';\nimport type {\n TestRunConfig,\n TestRunResult,\n StepResult,\n OverallResult,\n BrowserConfig,\n ConsoleEntry,\n NetworkError,\n} from './types';\n\n/**\n * Execute a test case against a target URL using Stagehand.\n */\nexport async function runTest(config: TestRunConfig): Promise<TestRunResult> {\n const anthropic = new Anthropic({ apiKey: config.anthropicApiKey });\n const startTime = Date.now();\n\n const browserConfig: BrowserConfig = config.browser ?? {\n provider: 'local',\n headless: true,\n };\n\n config.onStatusChange?.('initializing');\n\n const session = await createStagehandSession(browserConfig, config.anthropicApiKey);\n const { stagehand, page } = session;\n\n const stepResults: StepResult[] = [];\n\n // Accumulated console logs and network errors — drained per step\n let pendingConsoleLogs: ConsoleEntry[] = [];\n let pendingNetworkErrors: NetworkError[] = [];\n let stepStartTime = Date.now();\n\n // Cast to any for event listeners — Stagehand's Page type only exposes 'console'\n // but the underlying Playwright page supports all standard events\n const rawPage = page as any;\n\n // Listen for console messages\n rawPage.on('console', (msg: any) => {\n const level = msg.type?.() ?? msg.type ?? 'log';\n const mappedLevel =\n level === 'error' ? 'error' :\n level === 'warn' || level === 'warning' ? 'warning' :\n level === 'info' ? 'info' :\n level === 'debug' ? 'debug' : 'log';\n\n pendingConsoleLogs.push({\n level: mappedLevel as ConsoleEntry['level'],\n text: (typeof msg.text === 'function' ? msg.text() : String(msg.text ?? msg)).slice(0, 2000),\n source: typeof msg.location === 'function' ? msg.location()?.url : undefined,\n timestamp: Date.now() - stepStartTime,\n });\n });\n\n // Listen for failed requests (connection errors, CORS, etc.)\n rawPage.on('requestfailed', (req: any) => {\n const url = typeof req.url === 'function' ? req.url() : String(req.url ?? '');\n const method = typeof req.method === 'function' ? req.method() : String(req.method ?? 'GET');\n const failure = typeof req.failure === 'function' ? req.failure() : req.failure;\n pendingNetworkErrors.push({\n method,\n url: url.slice(0, 500),\n status: 0,\n statusText: failure?.errorText ?? 'Request failed',\n timestamp: Date.now() - stepStartTime,\n });\n });\n\n // Listen for HTTP error responses (4xx/5xx)\n rawPage.on('response', (res: any) => {\n const status = typeof res.status === 'function' ? res.status() : Number(res.status ?? 0);\n if (status >= 400) {\n const url = typeof res.url === 'function' ? res.url() : String(res.url ?? '');\n const statusText = typeof res.statusText === 'function' ? res.statusText() : String(res.statusText ?? '');\n const req = typeof res.request === 'function' ? res.request() : res.request;\n const method = req ? (typeof req.method === 'function' ? req.method() : String(req.method ?? 'GET')) : 'GET';\n pendingNetworkErrors.push({\n method,\n url: url.slice(0, 500),\n status,\n statusText,\n timestamp: Date.now() - stepStartTime,\n });\n }\n });\n\n try {\n // Handle form-login auth first (navigates to login page, then we go to target)\n if (config.auth?.type === 'form-login') {\n config.onStatusChange?.('authenticating');\n await injectAuth(page, config.auth, stagehand);\n }\n\n // Navigate to target URL\n config.onStatusChange?.('navigating');\n const targetUrl = config.testCase.targetRoute\n ? `${config.targetUrl.replace(/\\/$/, '')}${config.testCase.targetRoute}`\n : config.targetUrl;\n\n await page.goto(targetUrl, { waitUntil: 'domcontentloaded', timeoutMs: 30_000 });\n\n // Inject non-form authentication after navigation\n if (config.auth && config.auth.type !== 'form-login') {\n config.onStatusChange?.('authenticating');\n await injectAuth(page, config.auth, stagehand);\n\n // If localStorage auth, reload to apply\n if (config.auth.type === 'localStorage') {\n await page.evaluate((items: Record<string, string>) => {\n for (const [key, value] of Object.entries(items)) {\n localStorage.setItem(key, value);\n }\n }, config.auth.items);\n await page.reload({ waitUntil: 'domcontentloaded' });\n }\n }\n\n // Wait for page to stabilize\n await page.waitForLoadState('networkidle').catch(() => {});\n\n // Clear any logs accumulated during navigation/auth\n pendingConsoleLogs = [];\n pendingNetworkErrors = [];\n\n // Execute each step using Stagehand\n config.onStatusChange?.('executing');\n const steps = config.testCase.steps;\n\n for (let i = 0; i < steps.length; i++) {\n const step = steps[i];\n stepStartTime = Date.now();\n\n // Reset per-step collectors\n pendingConsoleLogs = [];\n pendingNetworkErrors = [];\n\n // Capture pre-action screenshot\n const screenshotBefore = await page.screenshot({ type: 'png' });\n\n let error: string | undefined;\n let screenshotAfter: Buffer = screenshotBefore;\n let actSucceeded = false;\n\n // Execute the action using Stagehand\n try {\n await stagehand.act(step.action);\n actSucceeded = true;\n\n // Wait for page to settle after action\n await page.waitForLoadState('networkidle').catch(() => {});\n await page.waitForTimeout(500);\n\n // Capture post-action screenshot\n screenshotAfter = await page.screenshot({ type: 'png' });\n } catch (err) {\n error = err instanceof Error ? err.message : String(err);\n // Still capture screenshot for debugging even after failure\n screenshotAfter = await page.screenshot({ type: 'png' }).catch(() => screenshotBefore);\n }\n\n // Evaluate the result using Stagehand's extract()\n let evaluation = {\n passed: false,\n confidence: 0,\n actualResult: error ?? 'Action execution failed',\n };\n\n if (actSucceeded) {\n try {\n const verificationSchema = z.object({\n passed: z.boolean().describe('Whether the expected result was achieved'),\n confidence: z\n .number()\n .min(0)\n .max(1)\n .describe('Confidence in the assessment (0.9+ = very sure, 0.7-0.9 = likely, below 0.7 = uncertain)'),\n actualResult: z.string().describe('Description of what actually happened on the page'),\n });\n\n const verification = await stagehand.extract(\n `You are evaluating a QA test step. The action \"${step.action}\" was just performed. ` +\n `Check if this expected result was achieved: \"${step.expectedResult}\". ` +\n `Look at the current page state and describe what actually happened. ` +\n `Be precise and factual in your assessment.`,\n verificationSchema,\n );\n\n evaluation = {\n passed: verification.passed,\n confidence: verification.confidence,\n actualResult: verification.actualResult,\n };\n } catch (evalErr) {\n evaluation = {\n passed: false,\n confidence: 0.2,\n actualResult: `Verification error: ${evalErr instanceof Error ? evalErr.message : String(evalErr)}`,\n };\n }\n }\n\n // Snapshot the collected logs for this step (limit to 50 entries each to avoid bloat)\n const consoleLogs = pendingConsoleLogs.slice(0, 50);\n const networkErrors = pendingNetworkErrors.slice(0, 30);\n\n const result: StepResult = {\n stepNumber: step.stepNumber,\n action: step.action,\n expectedResult: step.expectedResult,\n actualResult: evaluation.actualResult,\n passed: evaluation.passed,\n confidence: evaluation.confidence,\n screenshotBefore,\n screenshotAfter,\n actionsTaken: [], // Stagehand handles actions internally\n error,\n durationMs: Date.now() - stepStartTime,\n consoleLogs,\n networkErrors,\n };\n\n stepResults.push(result);\n config.onStepComplete?.(result, i, steps.length);\n }\n\n // Generate summary\n config.onStatusChange?.('completed');\n const model = config.model ?? 'claude-sonnet-4-20250514';\n const summary = await generateRunSummary(anthropic, config.testCase.title, stepResults, model);\n\n // Determine overall result\n const overallResult = determineOverallResult(stepResults);\n\n return {\n testCaseId: config.testCase.id,\n testCaseTitle: config.testCase.title,\n overallResult,\n steps: stepResults,\n totalDurationMs: Date.now() - startTime,\n summary,\n screenshotUrls: [],\n tokenUsage: {\n // Stagehand tracks tokens internally; these are approximate\n inputTokens: steps.length * 3000,\n outputTokens: steps.length * 500,\n },\n browserSessionId: session.sessionId,\n };\n } catch (err) {\n // Fatal error — test couldn't complete\n return {\n testCaseId: config.testCase.id,\n testCaseTitle: config.testCase.title,\n overallResult: 'error',\n steps: stepResults,\n totalDurationMs: Date.now() - startTime,\n summary: `Test execution failed: ${err instanceof Error ? err.message : String(err)}`,\n screenshotUrls: [],\n tokenUsage: {\n inputTokens: stepResults.length * 3000,\n outputTokens: stepResults.length * 500,\n },\n browserSessionId: session.sessionId,\n };\n } finally {\n await session.close();\n }\n}\n\n/**\n * Determine the overall test result from step results.\n */\nfunction determineOverallResult(steps: StepResult[]): OverallResult {\n if (steps.length === 0) return 'error';\n\n const allPassed = steps.every((s) => s.passed);\n const allFailed = steps.every((s) => !s.passed);\n const hasErrors = steps.some((s) => s.error);\n\n if (allPassed) return 'passed';\n if (allFailed || hasErrors) return 'failed';\n return 'partial';\n}\n","/**\n * Browser Provider\n *\n * Uses Stagehand (by Browserbase) for AI-powered browser automation.\n * Supports both Browserbase hosted sessions and local Playwright.\n */\n\nimport { Stagehand } from '@browserbasehq/stagehand';\nimport type { Page } from '@browserbasehq/stagehand';\nimport type { BrowserConfig, AuthConfig } from './types';\n\nconst DEFAULT_MODEL = 'anthropic/claude-sonnet-4-20250514';\n\nexport interface StagehandSession {\n stagehand: Stagehand;\n page: Page;\n sessionId: string;\n close: () => Promise<void>;\n}\n\n/**\n * Create a Stagehand-powered browser session.\n *\n * Stagehand wraps Playwright with AI capabilities (act, extract, observe)\n * and manages Browserbase or local browser sessions.\n */\nexport async function createStagehandSession(\n config: BrowserConfig,\n anthropicApiKey: string,\n): Promise<StagehandSession> {\n const modelName = config.model ?? DEFAULT_MODEL;\n const viewport = config.viewport ?? { width: 1280, height: 720 };\n\n const stagehand = new Stagehand({\n env: config.provider === 'browserbase' ? 'BROWSERBASE' : 'LOCAL',\n apiKey: config.provider === 'browserbase' ? config.browserbaseApiKey : undefined,\n projectId: config.provider === 'browserbase' ? config.browserbaseProjectId : undefined,\n model: {\n modelName: modelName as any,\n apiKey: anthropicApiKey,\n },\n localBrowserLaunchOptions:\n config.provider === 'local'\n ? {\n headless: config.headless ?? true,\n viewport,\n }\n : undefined,\n browserbaseSessionCreateParams:\n config.provider === 'browserbase'\n ? { projectId: config.browserbaseProjectId! }\n : undefined,\n });\n\n await stagehand.init();\n\n const page = stagehand.context.activePage()!;\n await page.setViewportSize(viewport.width, viewport.height);\n\n // Extract session ID\n let sessionId = `local-${Date.now()}`;\n if (config.provider === 'browserbase' && stagehand.browserbaseSessionID) {\n sessionId = stagehand.browserbaseSessionID;\n }\n\n return {\n stagehand,\n page,\n sessionId,\n close: async () => {\n await stagehand.close().catch(() => {});\n },\n };\n}\n\n/**\n * Inject authentication into the browser session.\n * Uses Stagehand's Page API and CDP for cookie injection.\n */\nexport async function injectAuth(page: Page, auth: AuthConfig, stagehand?: Stagehand): Promise<void> {\n if (auth.type === 'cookie') {\n // Use CDP to set cookies (Stagehand's Page doesn't have context().addCookies)\n for (const c of auth.cookies) {\n await page.sendCDP('Network.setCookie', {\n name: c.name,\n value: c.value,\n domain: c.domain,\n path: c.path ?? '/',\n secure: c.secure ?? false,\n httpOnly: c.httpOnly ?? false,\n sameSite: c.sameSite ?? 'Lax',\n });\n }\n } else if (auth.type === 'localStorage') {\n const currentUrl = page.url();\n if (currentUrl === 'about:blank') {\n // We'll inject localStorage after navigation in the runner\n return;\n }\n await page.evaluate((items: Record<string, string>) => {\n for (const [key, value] of Object.entries(items)) {\n localStorage.setItem(key, value);\n }\n }, auth.items);\n } else if (auth.type === 'form-login') {\n await performFormLogin(page, auth, stagehand);\n }\n}\n\n/**\n * Automate login via a web form.\n *\n * Uses Stagehand's act() when available for AI-powered form filling,\n * falling back to manual locator-based login.\n */\nasync function performFormLogin(\n page: Page,\n auth: { loginUrl: string; email: string; password: string },\n stagehand?: Stagehand,\n): Promise<void> {\n await page.goto(auth.loginUrl, { waitUntil: 'domcontentloaded' });\n\n // Wait for the page to settle\n await page.waitForLoadState('networkidle', 15_000).catch(() => {});\n\n if (stagehand) {\n // Use Stagehand's AI to handle login — much more reliable\n await stagehand.act(\n `Fill in the email/username field with \"${auth.email}\" and the password field with \"${auth.password}\", then click the login/sign-in button to submit the form.`,\n );\n } else {\n // Fallback: manual locator-based login\n await manualFormLogin(page, auth);\n }\n\n // Wait for navigation after login\n await page.waitForLoadState('networkidle', 15_000).catch(() => {});\n}\n\n/**\n * Manual form login using locators (fallback when Stagehand instance unavailable).\n */\nasync function manualFormLogin(\n page: Page,\n auth: { email: string; password: string },\n): Promise<void> {\n // Wait for a form or input to appear\n await page\n .waitForSelector(\n 'input[type=\"email\"], input[type=\"text\"][name*=\"email\"], input[name*=\"user\"], input[type=\"text\"]',\n { timeout: 15_000 },\n )\n .catch(() => {});\n\n // Try common email/username selectors in priority order\n const emailSelectors = [\n 'input[type=\"email\"]',\n 'input[name=\"email\"]',\n 'input[name=\"username\"]',\n 'input[autocomplete=\"email\"]',\n 'input[autocomplete=\"username\"]',\n 'input[type=\"text\"][name*=\"email\"]',\n 'input[type=\"text\"][name*=\"user\"]',\n 'input[type=\"text\"]',\n ];\n\n let emailFilled = false;\n for (const sel of emailSelectors) {\n const locator = page.locator(sel);\n if ((await locator.count()) > 0 && (await locator.isVisible())) {\n await locator.fill(auth.email);\n emailFilled = true;\n break;\n }\n }\n\n if (!emailFilled) {\n throw new Error('Could not find email/username input on login page');\n }\n\n // Fill password\n const passwordLocator = page.locator('input[type=\"password\"]');\n if ((await passwordLocator.count()) > 0 && (await passwordLocator.isVisible())) {\n await passwordLocator.fill(auth.password);\n } else {\n throw new Error('Could not find password input on login page');\n }\n\n // Submit the form\n const submitSelectors = [\n 'button[type=\"submit\"]',\n 'input[type=\"submit\"]',\n ];\n\n let submitted = false;\n for (const sel of submitSelectors) {\n const locator = page.locator(sel);\n if ((await locator.count()) > 0 && (await locator.isVisible())) {\n await locator.click();\n submitted = true;\n break;\n }\n }\n\n if (!submitted) {\n // Fallback: press Enter on the password field\n await page.locator('input[type=\"password\"]').type('\\n');\n }\n}\n","/**\n * Result Evaluator\n *\n * Generates AI summaries of test run results.\n * Step-level evaluation is now handled by Stagehand's extract() in the runner.\n */\n\nimport Anthropic from '@anthropic-ai/sdk';\n\n/**\n * Generate an AI summary of the entire test run.\n */\nexport async function generateRunSummary(\n anthropic: Anthropic,\n testTitle: string,\n steps: Array<{\n stepNumber: number;\n action: string;\n expectedResult: string;\n actualResult: string;\n passed: boolean;\n confidence: number;\n error?: string;\n }>,\n model: string,\n): Promise<string> {\n const stepsText = steps\n .map(\n (s) =>\n `Step ${s.stepNumber}: ${s.action}\\n Expected: ${s.expectedResult}\\n Actual: ${s.actualResult}\\n Result: ${s.passed ? 'PASS' : 'FAIL'} (confidence: ${Math.round(s.confidence * 100)}%)${s.error ? `\\n Error: ${s.error}` : ''}`\n )\n .join('\\n\\n');\n\n const passCount = steps.filter((s) => s.passed).length;\n const failCount = steps.filter((s) => !s.passed).length;\n\n const response = await anthropic.messages.create({\n model,\n max_tokens: 512,\n messages: [\n {\n role: 'user',\n content: `Summarize this AI test execution in 2-3 sentences. Focus on what was tested, what passed, and what failed (if anything). Be concise and factual.\n\nTest: ${testTitle}\nResults: ${passCount} passed, ${failCount} failed out of ${steps.length} steps\n\n${stepsText}`,\n },\n ],\n });\n\n return response.content\n .filter((block): block is Anthropic.TextBlock => block.type === 'text')\n .map((block) => block.text)\n .join('');\n}\n"],"mappings":"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","names":["Anthropic"]}
|