@hasna/testers 0.0.50 → 0.0.51
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/index.js +209 -26
- package/dist/db/scenarios.d.ts.map +1 -1
- package/dist/index.js +180 -24
- package/dist/lib/ai-client.d.ts.map +1 -1
- package/dist/lib/next-route-inventory.d.ts.map +1 -1
- package/dist/lib/route-fixtures.d.ts +20 -0
- package/dist/lib/route-fixtures.d.ts.map +1 -0
- package/dist/lib/runner.d.ts.map +1 -1
- package/dist/mcp/index.js +181 -25
- package/dist/server/index.js +181 -25
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -12394,6 +12394,18 @@ __export(exports_scenarios, {
|
|
|
12394
12394
|
createScenario: () => createScenario,
|
|
12395
12395
|
countScenarios: () => countScenarios
|
|
12396
12396
|
});
|
|
12397
|
+
function stableJson(value) {
|
|
12398
|
+
if (value === undefined)
|
|
12399
|
+
return "";
|
|
12400
|
+
if (value === null)
|
|
12401
|
+
return "null";
|
|
12402
|
+
if (Array.isArray(value))
|
|
12403
|
+
return `[${value.map(stableJson).join(",")}]`;
|
|
12404
|
+
if (typeof value === "object") {
|
|
12405
|
+
return `{${Object.entries(value).filter(([, val]) => val !== undefined).sort(([a], [b]) => a.localeCompare(b)).map(([key, val]) => `${JSON.stringify(key)}:${stableJson(val)}`).join(",")}}`;
|
|
12406
|
+
}
|
|
12407
|
+
return JSON.stringify(value);
|
|
12408
|
+
}
|
|
12397
12409
|
function nextShortId(projectId) {
|
|
12398
12410
|
const db2 = getDatabase();
|
|
12399
12411
|
if (projectId) {
|
|
@@ -12653,9 +12665,12 @@ function upsertScenario(input) {
|
|
|
12653
12665
|
}
|
|
12654
12666
|
const existingSteps = JSON.parse(existing.steps);
|
|
12655
12667
|
const existingTags = JSON.parse(existing.tags);
|
|
12668
|
+
const existingMetadata = existing.metadata ? JSON.parse(existing.metadata) : undefined;
|
|
12669
|
+
const existingAssertions = JSON.parse(existing.assertions || "[]");
|
|
12670
|
+
const existingParameters = existing.parameters ? JSON.parse(existing.parameters) : undefined;
|
|
12656
12671
|
const newSteps = input.steps ?? [];
|
|
12657
12672
|
const newTags = input.tags ?? [];
|
|
12658
|
-
const isIdentical = existing.description === (input.description ?? "") && existingSteps.length === newSteps.length && existingSteps.every((s, i) => s === newSteps[i]) && existingTags.length === newTags.length && existingTags.every((t, i) => t === newTags[i]) && existing.priority === (input.priority ?? "medium");
|
|
12673
|
+
const isIdentical = existing.description === (input.description ?? "") && existingSteps.length === newSteps.length && existingSteps.every((s, i) => s === newSteps[i]) && existingTags.length === newTags.length && existingTags.every((t, i) => t === newTags[i]) && existing.priority === (input.priority ?? "medium") && existing.target_path === (input.targetPath ?? null) && Boolean(existing.requires_auth) === Boolean(input.requiresAuth) && stableJson(existingMetadata) === stableJson(input.metadata) && stableJson(existingAssertions) === stableJson(input.assertions ?? []) && stableJson(existingParameters) === stableJson(input.parameters);
|
|
12659
12674
|
if (isIdentical) {
|
|
12660
12675
|
return { scenario: scenarioFromRow(existing), action: "deduped" };
|
|
12661
12676
|
}
|
|
@@ -12705,6 +12720,10 @@ function upsertScenario(input) {
|
|
|
12705
12720
|
sets.push("assertions = ?");
|
|
12706
12721
|
params.push(JSON.stringify(input.assertions));
|
|
12707
12722
|
}
|
|
12723
|
+
if (input.parameters !== undefined) {
|
|
12724
|
+
sets.push("parameters = ?");
|
|
12725
|
+
params.push(JSON.stringify(input.parameters));
|
|
12726
|
+
}
|
|
12708
12727
|
sets.push("version = ?", "updated_at = ?");
|
|
12709
12728
|
params.push(existing.version + 1, now());
|
|
12710
12729
|
params.push(existing.id);
|
|
@@ -13057,6 +13076,159 @@ var init_screenshots = __esm(() => {
|
|
|
13057
13076
|
init_database();
|
|
13058
13077
|
});
|
|
13059
13078
|
|
|
13079
|
+
// src/lib/route-fixtures.ts
|
|
13080
|
+
function isRecord2(value) {
|
|
13081
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
13082
|
+
}
|
|
13083
|
+
function envNameForParam(prefix, param) {
|
|
13084
|
+
return `${prefix}_${param.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^A-Za-z0-9]+/g, "_").toUpperCase()}`;
|
|
13085
|
+
}
|
|
13086
|
+
function readString(value) {
|
|
13087
|
+
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
13088
|
+
}
|
|
13089
|
+
function resolveReference(value, env) {
|
|
13090
|
+
if (value.startsWith("$?"))
|
|
13091
|
+
return env[value.slice(2)]?.trim() || undefined;
|
|
13092
|
+
if (value.startsWith("$"))
|
|
13093
|
+
return env[value.slice(1)]?.trim() || undefined;
|
|
13094
|
+
return value;
|
|
13095
|
+
}
|
|
13096
|
+
function scenarioFixtureValue(params, name, env) {
|
|
13097
|
+
if (!params)
|
|
13098
|
+
return;
|
|
13099
|
+
const routeFixtures = isRecord2(params["routeFixtures"]) ? params["routeFixtures"] : {};
|
|
13100
|
+
const raw = readString(routeFixtures[name]) ?? readString(params[name]);
|
|
13101
|
+
return raw ? resolveReference(raw, env) : undefined;
|
|
13102
|
+
}
|
|
13103
|
+
function envFixtureValue(name, env) {
|
|
13104
|
+
const candidates = [
|
|
13105
|
+
envNameForParam("TESTERS_ROUTE", name),
|
|
13106
|
+
envNameForParam("TESTERS_FIXTURE", name),
|
|
13107
|
+
envNameForParam("ALUMIA_FIXTURE", name),
|
|
13108
|
+
...PARAM_ENV_CANDIDATES[name] ?? []
|
|
13109
|
+
];
|
|
13110
|
+
for (const candidate of candidates) {
|
|
13111
|
+
const value = env[candidate]?.trim();
|
|
13112
|
+
if (value)
|
|
13113
|
+
return value;
|
|
13114
|
+
}
|
|
13115
|
+
return;
|
|
13116
|
+
}
|
|
13117
|
+
function defaultFixtureValue(name) {
|
|
13118
|
+
if (name === "orgSlug")
|
|
13119
|
+
return "test-org";
|
|
13120
|
+
if (name.toLowerCase().endsWith("slug")) {
|
|
13121
|
+
return `test-${name.replace(/Slug$/i, "").replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase() || "slug"}`;
|
|
13122
|
+
}
|
|
13123
|
+
if (name === "id" || name.toLowerCase().endsWith("id"))
|
|
13124
|
+
return DEFAULT_UUID;
|
|
13125
|
+
if (name.toLowerCase().includes("token"))
|
|
13126
|
+
return "test-token";
|
|
13127
|
+
return `test-${name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase()}`;
|
|
13128
|
+
}
|
|
13129
|
+
function routeParamsFromPath(path) {
|
|
13130
|
+
if (!path)
|
|
13131
|
+
return [];
|
|
13132
|
+
const params = new Set;
|
|
13133
|
+
for (const match of path.matchAll(/:([A-Za-z0-9_]+)(?:\*\??)?/g)) {
|
|
13134
|
+
if (match[1])
|
|
13135
|
+
params.add(match[1]);
|
|
13136
|
+
}
|
|
13137
|
+
return [...params];
|
|
13138
|
+
}
|
|
13139
|
+
function defaultRouteFixturesForParams(params) {
|
|
13140
|
+
return Object.fromEntries(params.map((param) => [param, defaultFixtureValue(param)]));
|
|
13141
|
+
}
|
|
13142
|
+
function resolveRouteFixtures(scenario, env = process.env) {
|
|
13143
|
+
const metadataParams = Array.isArray(scenario.metadata?.["fixtureParams"]) ? scenario.metadata["fixtureParams"].filter((value) => typeof value === "string") : [];
|
|
13144
|
+
const params = [...new Set([...metadataParams, ...routeParamsFromPath(scenario.targetPath)])];
|
|
13145
|
+
const values = {};
|
|
13146
|
+
const sources = {};
|
|
13147
|
+
const synthetic = [];
|
|
13148
|
+
for (const param of params) {
|
|
13149
|
+
const scenarioValue = scenarioFixtureValue(scenario.parameters, param, env);
|
|
13150
|
+
if (scenarioValue) {
|
|
13151
|
+
values[param] = scenarioValue;
|
|
13152
|
+
sources[param] = "scenario";
|
|
13153
|
+
continue;
|
|
13154
|
+
}
|
|
13155
|
+
const envValue = envFixtureValue(param, env);
|
|
13156
|
+
if (envValue) {
|
|
13157
|
+
values[param] = envValue;
|
|
13158
|
+
sources[param] = "env";
|
|
13159
|
+
continue;
|
|
13160
|
+
}
|
|
13161
|
+
values[param] = defaultFixtureValue(param);
|
|
13162
|
+
sources[param] = "default";
|
|
13163
|
+
synthetic.push(param);
|
|
13164
|
+
}
|
|
13165
|
+
const resolvedPath = scenario.targetPath ? resolveRoutePath(scenario.targetPath, values) : null;
|
|
13166
|
+
return {
|
|
13167
|
+
originalPath: scenario.targetPath,
|
|
13168
|
+
resolvedPath,
|
|
13169
|
+
params,
|
|
13170
|
+
values,
|
|
13171
|
+
sources,
|
|
13172
|
+
synthetic
|
|
13173
|
+
};
|
|
13174
|
+
}
|
|
13175
|
+
function resolveRoutePath(path, values) {
|
|
13176
|
+
return path.replace(/\/:([A-Za-z0-9_]+)\*\?/g, (_match, name) => {
|
|
13177
|
+
const value = values[name];
|
|
13178
|
+
return value ? `/${encodeRouteFixture(value, true)}` : "";
|
|
13179
|
+
}).replace(/:([A-Za-z0-9_]+)\*/g, (_match, name) => encodeRouteFixture(values[name] ?? defaultFixtureValue(name), true)).replace(/:([A-Za-z0-9_]+)/g, (_match, name) => encodeRouteFixture(values[name] ?? defaultFixtureValue(name), false));
|
|
13180
|
+
}
|
|
13181
|
+
function encodeRouteFixture(value, allowSlash) {
|
|
13182
|
+
if (allowSlash)
|
|
13183
|
+
return value.split("/").filter(Boolean).map(encodeURIComponent).join("/");
|
|
13184
|
+
return encodeURIComponent(value);
|
|
13185
|
+
}
|
|
13186
|
+
function materializeScenarioRoute(scenario, env = process.env) {
|
|
13187
|
+
const resolution = resolveRouteFixtures(scenario, env);
|
|
13188
|
+
if (!resolution.resolvedPath || resolution.resolvedPath === scenario.targetPath) {
|
|
13189
|
+
return { scenario, resolution };
|
|
13190
|
+
}
|
|
13191
|
+
const steps = scenario.steps.map((step) => {
|
|
13192
|
+
let next = step;
|
|
13193
|
+
for (const [name, value] of Object.entries(resolution.values)) {
|
|
13194
|
+
next = next.replaceAll(`:${name}`, value).replaceAll(`[${name}]`, value).replaceAll(`{${name}}`, value);
|
|
13195
|
+
}
|
|
13196
|
+
return next.replaceAll(scenario.targetPath ?? "", resolution.resolvedPath ?? "");
|
|
13197
|
+
});
|
|
13198
|
+
return {
|
|
13199
|
+
scenario: {
|
|
13200
|
+
...scenario,
|
|
13201
|
+
targetPath: resolution.resolvedPath,
|
|
13202
|
+
steps,
|
|
13203
|
+
metadata: {
|
|
13204
|
+
...scenario.metadata ?? {},
|
|
13205
|
+
routeFixtureResolution: resolution
|
|
13206
|
+
}
|
|
13207
|
+
},
|
|
13208
|
+
resolution
|
|
13209
|
+
};
|
|
13210
|
+
}
|
|
13211
|
+
function resolveStartUrl(baseUrl, targetPath) {
|
|
13212
|
+
try {
|
|
13213
|
+
return new URL(targetPath, baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`).toString();
|
|
13214
|
+
} catch {
|
|
13215
|
+
return `${baseUrl.replace(/\/+$/, "")}/${targetPath.replace(/^\/+/, "")}`;
|
|
13216
|
+
}
|
|
13217
|
+
}
|
|
13218
|
+
var DEFAULT_UUID = "00000000-0000-4000-8000-000000000000", PARAM_ENV_CANDIDATES;
|
|
13219
|
+
var init_route_fixtures = __esm(() => {
|
|
13220
|
+
PARAM_ENV_CANDIDATES = {
|
|
13221
|
+
orgSlug: ["TESTERS_ORG_SLUG", "SMOKE_ORG_SLUG", "ORG_SLUG"],
|
|
13222
|
+
orgId: ["TESTERS_ORG_ID", "SMOKE_ORG_ID", "ORG_ID"],
|
|
13223
|
+
projectSlug: ["TESTERS_PROJECT_SLUG", "SMOKE_PROJECT_SLUG", "PROJECT_SLUG"],
|
|
13224
|
+
projectId: ["TESTERS_PROJECT_ID", "SMOKE_PROJECT_ID", "PROJECT_ID"],
|
|
13225
|
+
workspaceId: ["TESTERS_WORKSPACE_ID", "SMOKE_WORKSPACE_ID", "WORKSPACE_ID"],
|
|
13226
|
+
agentId: ["TESTERS_AGENT_ID", "SMOKE_AGENT_ID", "AGENT_ID"],
|
|
13227
|
+
sessionId: ["TESTERS_SESSION_ID", "SMOKE_SESSION_ID", "SESSION_ID"],
|
|
13228
|
+
userId: ["TESTERS_USER_ID", "SMOKE_USER_ID", "USER_ID"]
|
|
13229
|
+
};
|
|
13230
|
+
});
|
|
13231
|
+
|
|
13060
13232
|
// src/lib/browser-lightpanda.ts
|
|
13061
13233
|
var exports_browser_lightpanda = {};
|
|
13062
13234
|
__export(exports_browser_lightpanda, {
|
|
@@ -14777,33 +14949,35 @@ ${filtered.join(`
|
|
|
14777
14949
|
return { result: `Error executing ${toolName}: ${message}` };
|
|
14778
14950
|
}
|
|
14779
14951
|
}
|
|
14780
|
-
function resolveStartUrl(baseUrl, targetPath) {
|
|
14781
|
-
try {
|
|
14782
|
-
return new URL(targetPath, baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`).toString();
|
|
14783
|
-
} catch {
|
|
14784
|
-
return `${baseUrl.replace(/\/+$/, "")}/${targetPath.replace(/^\/+/, "")}`;
|
|
14785
|
-
}
|
|
14786
|
-
}
|
|
14787
14952
|
function buildScenarioUserMessage(scenario, baseUrl) {
|
|
14953
|
+
const { scenario: materializedScenario, resolution } = materializeScenarioRoute(scenario);
|
|
14788
14954
|
const userParts = [
|
|
14789
|
-
`**Scenario:** ${
|
|
14790
|
-
`**Description:** ${
|
|
14955
|
+
`**Scenario:** ${materializedScenario.name}`,
|
|
14956
|
+
`**Description:** ${materializedScenario.description}`
|
|
14791
14957
|
];
|
|
14792
14958
|
if (baseUrl) {
|
|
14793
14959
|
const normalizedBaseUrl = baseUrl.replace(/\/+$/, "");
|
|
14794
14960
|
userParts.push(`**Base URL:** ${normalizedBaseUrl}`);
|
|
14795
|
-
if (
|
|
14796
|
-
userParts.push(`**Start URL:** ${resolveStartUrl(normalizedBaseUrl,
|
|
14961
|
+
if (materializedScenario.targetPath) {
|
|
14962
|
+
userParts.push(`**Start URL:** ${resolveStartUrl(normalizedBaseUrl, materializedScenario.targetPath)}`);
|
|
14797
14963
|
}
|
|
14798
14964
|
userParts.push("**Navigation Boundary:** Treat the Base URL as the application under test. Resolve relative paths and in-app navigation against this origin. Do not navigate to another host unless a step explicitly includes an absolute external URL.");
|
|
14799
14965
|
}
|
|
14800
|
-
if (
|
|
14801
|
-
userParts.push(`**Target Path:** ${
|
|
14966
|
+
if (materializedScenario.targetPath) {
|
|
14967
|
+
userParts.push(`**Target Path:** ${materializedScenario.targetPath}`);
|
|
14968
|
+
}
|
|
14969
|
+
if (resolution.params.length > 0) {
|
|
14970
|
+
userParts.push("**Route Fixtures:**");
|
|
14971
|
+
for (const param of resolution.params) {
|
|
14972
|
+
const source = resolution.sources[param];
|
|
14973
|
+
const synthetic = source === "default" ? " synthetic" : "";
|
|
14974
|
+
userParts.push(`- :${param} = ${resolution.values[param]} (${source}${synthetic})`);
|
|
14975
|
+
}
|
|
14802
14976
|
}
|
|
14803
|
-
if (
|
|
14977
|
+
if (materializedScenario.steps.length > 0) {
|
|
14804
14978
|
userParts.push("**Steps:**");
|
|
14805
|
-
for (let i = 0;i <
|
|
14806
|
-
userParts.push(`${i + 1}. ${
|
|
14979
|
+
for (let i = 0;i < materializedScenario.steps.length; i++) {
|
|
14980
|
+
userParts.push(`${i + 1}. ${materializedScenario.steps[i]}`);
|
|
14807
14981
|
}
|
|
14808
14982
|
}
|
|
14809
14983
|
return userParts.join(`
|
|
@@ -15101,6 +15275,7 @@ function createClientForModel(model, apiKey) {
|
|
|
15101
15275
|
var activeHARs, activeCoverage, BROWSER_TOOLS;
|
|
15102
15276
|
var init_ai_client = __esm(() => {
|
|
15103
15277
|
init_types();
|
|
15278
|
+
init_route_fixtures();
|
|
15104
15279
|
activeHARs = new Map;
|
|
15105
15280
|
activeCoverage = new Map;
|
|
15106
15281
|
BROWSER_TOOLS = [
|
|
@@ -18321,9 +18496,10 @@ function withTimeout(promise, ms, label) {
|
|
|
18321
18496
|
});
|
|
18322
18497
|
}
|
|
18323
18498
|
async function runSingleScenario(scenario, runId, options) {
|
|
18324
|
-
const
|
|
18499
|
+
const { scenario: materializedScenario, resolution: routeFixtureResolution } = materializeScenarioRoute(scenario);
|
|
18500
|
+
const scenarioType = materializedScenario.scenarioType ?? "browser";
|
|
18325
18501
|
if (scenarioType === "eval") {
|
|
18326
|
-
return runEvalScenario(
|
|
18502
|
+
return runEvalScenario(materializedScenario, { runId, baseUrl: options.url });
|
|
18327
18503
|
}
|
|
18328
18504
|
const config = loadConfig();
|
|
18329
18505
|
if (options.selfHeal !== undefined)
|
|
@@ -18364,7 +18540,7 @@ async function runSingleScenario(scenario, runId, options) {
|
|
|
18364
18540
|
runId,
|
|
18365
18541
|
scenarioId: scenario.id,
|
|
18366
18542
|
model,
|
|
18367
|
-
stepsTotal:
|
|
18543
|
+
stepsTotal: materializedScenario.steps.length || 10,
|
|
18368
18544
|
personaId: persona?.id ?? null,
|
|
18369
18545
|
personaName: persona?.name ?? null
|
|
18370
18546
|
});
|
|
@@ -18400,12 +18576,12 @@ async function runSingleScenario(scenario, runId, options) {
|
|
|
18400
18576
|
engine: effectiveOptions.engine
|
|
18401
18577
|
});
|
|
18402
18578
|
}
|
|
18403
|
-
const targetUrl =
|
|
18404
|
-
const scenarioTimeout =
|
|
18579
|
+
const targetUrl = materializedScenario.targetPath ? resolveStartUrl(options.url.replace(/\/$/, ""), materializedScenario.targetPath) : options.url;
|
|
18580
|
+
const scenarioTimeout = materializedScenario.timeoutMs ?? options.timeout ?? config.browser.timeout ?? 60000;
|
|
18405
18581
|
registerSession({
|
|
18406
18582
|
resultId: result.id,
|
|
18407
18583
|
runId,
|
|
18408
|
-
scenarioId:
|
|
18584
|
+
scenarioId: materializedScenario.id,
|
|
18409
18585
|
engine: effectiveOptions.engine ?? "playwright",
|
|
18410
18586
|
startUrl: targetUrl
|
|
18411
18587
|
});
|
|
@@ -18459,7 +18635,7 @@ async function runSingleScenario(scenario, runId, options) {
|
|
|
18459
18635
|
const agentResult = await withTimeout(runAgentLoop({
|
|
18460
18636
|
client,
|
|
18461
18637
|
page,
|
|
18462
|
-
scenario,
|
|
18638
|
+
scenario: materializedScenario,
|
|
18463
18639
|
screenshotter,
|
|
18464
18640
|
model,
|
|
18465
18641
|
runId,
|
|
@@ -18550,7 +18726,7 @@ async function runSingleScenario(scenario, runId, options) {
|
|
|
18550
18726
|
const baseReasoning = agentResult.reasoning ? agentResult.reasoning + lightpandaNote : lightpandaNote || "";
|
|
18551
18727
|
const assertionOutcome = await applyStructuredAssertionsToResult({
|
|
18552
18728
|
page,
|
|
18553
|
-
scenario,
|
|
18729
|
+
scenario: materializedScenario,
|
|
18554
18730
|
consoleErrors,
|
|
18555
18731
|
status: agentResult.status,
|
|
18556
18732
|
reasoning: baseReasoning
|
|
@@ -18571,6 +18747,7 @@ async function runSingleScenario(scenario, runId, options) {
|
|
|
18571
18747
|
costCents: estimateCost(model, agentResult.tokensUsed),
|
|
18572
18748
|
metadata: {
|
|
18573
18749
|
consoleLogs,
|
|
18750
|
+
...routeFixtureResolution.params.length > 0 ? { routeFixtureResolution } : {},
|
|
18574
18751
|
...networkErrors.length > 0 ? networkMeta : {},
|
|
18575
18752
|
...structuredAssertionMeta
|
|
18576
18753
|
}
|
|
@@ -18920,6 +19097,7 @@ var init_runner = __esm(() => {
|
|
|
18920
19097
|
init_browser();
|
|
18921
19098
|
init_screenshotter();
|
|
18922
19099
|
init_ai_client();
|
|
19100
|
+
init_route_fixtures();
|
|
18923
19101
|
init_config2();
|
|
18924
19102
|
init_persona_auth();
|
|
18925
19103
|
init_session_tracker();
|
|
@@ -59225,6 +59403,10 @@ function scenarioInputForNextRoute(item, projectId) {
|
|
|
59225
59403
|
actionCount: item.actions.length,
|
|
59226
59404
|
groups: item.groups
|
|
59227
59405
|
},
|
|
59406
|
+
parameters: item.fixtureParams.length > 0 ? {
|
|
59407
|
+
routeFixtures: defaultRouteFixturesForParams(item.fixtureParams),
|
|
59408
|
+
routeFixtureParams: item.fixtureParams
|
|
59409
|
+
} : undefined,
|
|
59228
59410
|
projectId
|
|
59229
59411
|
};
|
|
59230
59412
|
}
|
|
@@ -59627,6 +59809,7 @@ var ROUTE_FILE_NAMES, WALK_EXCLUDES, SAFE_PAGE_ASSERTIONS, IMPORT_SCAN_LIMIT = 4
|
|
|
59627
59809
|
var init_next_route_inventory = __esm(() => {
|
|
59628
59810
|
init_scenarios();
|
|
59629
59811
|
init_workflows();
|
|
59812
|
+
init_route_fixtures();
|
|
59630
59813
|
ROUTE_FILE_NAMES = new Set([
|
|
59631
59814
|
"page.tsx",
|
|
59632
59815
|
"page.ts",
|
|
@@ -95041,7 +95224,7 @@ import chalk6 from "chalk";
|
|
|
95041
95224
|
// package.json
|
|
95042
95225
|
var package_default = {
|
|
95043
95226
|
name: "@hasna/testers",
|
|
95044
|
-
version: "0.0.
|
|
95227
|
+
version: "0.0.51",
|
|
95045
95228
|
description: "AI-powered QA testing CLI \u2014 spawns cheap AI agents to test web apps with headless browsers",
|
|
95046
95229
|
type: "module",
|
|
95047
95230
|
main: "dist/index.js",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scenarios.d.ts","sourceRoot":"","sources":["../../src/db/scenarios.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,QAAQ,EAEb,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,cAAc,EAGpB,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"scenarios.d.ts","sourceRoot":"","sources":["../../src/db/scenarios.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,QAAQ,EAEb,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,cAAc,EAGpB,MAAM,mBAAmB,CAAC;AA2C3B,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,GAAG,QAAQ,CA+BnE;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAmBvD;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAIrE;AAED,wBAAgB,aAAa,CAAC,MAAM,CAAC,EAAE,cAAc,GAAG,QAAQ,EAAE,CAoFjE;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,mBAAmB,EAAE,OAAO,EAAE,MAAM,GAAG,QAAQ,CAwFhG;AAED,wBAAgB,cAAc,CAAC,MAAM,CAAC,EAAE,cAAc,GAAG,MAAM,CA8B9D;AAED,MAAM,WAAW,aAAc,SAAQ,QAAQ;IAC7C,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,EAAE,CAmBhE;AAED,wBAAgB,yBAAyB,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAIvE;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAOlD;AAED,MAAM,MAAM,oBAAoB,GAAG;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,CAAA;CAAE,CAAC;AAErG;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,oBAAoB,CAgElG"}
|
package/dist/index.js
CHANGED
|
@@ -11567,6 +11567,156 @@ var init_browser = __esm(() => {
|
|
|
11567
11567
|
DEFAULT_VIEWPORT = { width: 1280, height: 720 };
|
|
11568
11568
|
});
|
|
11569
11569
|
|
|
11570
|
+
// src/lib/route-fixtures.ts
|
|
11571
|
+
function isRecord2(value) {
|
|
11572
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
11573
|
+
}
|
|
11574
|
+
function envNameForParam(prefix, param) {
|
|
11575
|
+
return `${prefix}_${param.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^A-Za-z0-9]+/g, "_").toUpperCase()}`;
|
|
11576
|
+
}
|
|
11577
|
+
function readString(value) {
|
|
11578
|
+
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
11579
|
+
}
|
|
11580
|
+
function resolveReference(value, env) {
|
|
11581
|
+
if (value.startsWith("$?"))
|
|
11582
|
+
return env[value.slice(2)]?.trim() || undefined;
|
|
11583
|
+
if (value.startsWith("$"))
|
|
11584
|
+
return env[value.slice(1)]?.trim() || undefined;
|
|
11585
|
+
return value;
|
|
11586
|
+
}
|
|
11587
|
+
function scenarioFixtureValue(params, name, env) {
|
|
11588
|
+
if (!params)
|
|
11589
|
+
return;
|
|
11590
|
+
const routeFixtures = isRecord2(params["routeFixtures"]) ? params["routeFixtures"] : {};
|
|
11591
|
+
const raw = readString(routeFixtures[name]) ?? readString(params[name]);
|
|
11592
|
+
return raw ? resolveReference(raw, env) : undefined;
|
|
11593
|
+
}
|
|
11594
|
+
function envFixtureValue(name, env) {
|
|
11595
|
+
const candidates = [
|
|
11596
|
+
envNameForParam("TESTERS_ROUTE", name),
|
|
11597
|
+
envNameForParam("TESTERS_FIXTURE", name),
|
|
11598
|
+
envNameForParam("ALUMIA_FIXTURE", name),
|
|
11599
|
+
...PARAM_ENV_CANDIDATES[name] ?? []
|
|
11600
|
+
];
|
|
11601
|
+
for (const candidate of candidates) {
|
|
11602
|
+
const value = env[candidate]?.trim();
|
|
11603
|
+
if (value)
|
|
11604
|
+
return value;
|
|
11605
|
+
}
|
|
11606
|
+
return;
|
|
11607
|
+
}
|
|
11608
|
+
function defaultFixtureValue(name) {
|
|
11609
|
+
if (name === "orgSlug")
|
|
11610
|
+
return "test-org";
|
|
11611
|
+
if (name.toLowerCase().endsWith("slug")) {
|
|
11612
|
+
return `test-${name.replace(/Slug$/i, "").replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase() || "slug"}`;
|
|
11613
|
+
}
|
|
11614
|
+
if (name === "id" || name.toLowerCase().endsWith("id"))
|
|
11615
|
+
return DEFAULT_UUID;
|
|
11616
|
+
if (name.toLowerCase().includes("token"))
|
|
11617
|
+
return "test-token";
|
|
11618
|
+
return `test-${name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase()}`;
|
|
11619
|
+
}
|
|
11620
|
+
function routeParamsFromPath(path) {
|
|
11621
|
+
if (!path)
|
|
11622
|
+
return [];
|
|
11623
|
+
const params = new Set;
|
|
11624
|
+
for (const match of path.matchAll(/:([A-Za-z0-9_]+)(?:\*\??)?/g)) {
|
|
11625
|
+
if (match[1])
|
|
11626
|
+
params.add(match[1]);
|
|
11627
|
+
}
|
|
11628
|
+
return [...params];
|
|
11629
|
+
}
|
|
11630
|
+
function resolveRouteFixtures(scenario, env = process.env) {
|
|
11631
|
+
const metadataParams = Array.isArray(scenario.metadata?.["fixtureParams"]) ? scenario.metadata["fixtureParams"].filter((value) => typeof value === "string") : [];
|
|
11632
|
+
const params = [...new Set([...metadataParams, ...routeParamsFromPath(scenario.targetPath)])];
|
|
11633
|
+
const values = {};
|
|
11634
|
+
const sources = {};
|
|
11635
|
+
const synthetic = [];
|
|
11636
|
+
for (const param of params) {
|
|
11637
|
+
const scenarioValue = scenarioFixtureValue(scenario.parameters, param, env);
|
|
11638
|
+
if (scenarioValue) {
|
|
11639
|
+
values[param] = scenarioValue;
|
|
11640
|
+
sources[param] = "scenario";
|
|
11641
|
+
continue;
|
|
11642
|
+
}
|
|
11643
|
+
const envValue = envFixtureValue(param, env);
|
|
11644
|
+
if (envValue) {
|
|
11645
|
+
values[param] = envValue;
|
|
11646
|
+
sources[param] = "env";
|
|
11647
|
+
continue;
|
|
11648
|
+
}
|
|
11649
|
+
values[param] = defaultFixtureValue(param);
|
|
11650
|
+
sources[param] = "default";
|
|
11651
|
+
synthetic.push(param);
|
|
11652
|
+
}
|
|
11653
|
+
const resolvedPath = scenario.targetPath ? resolveRoutePath(scenario.targetPath, values) : null;
|
|
11654
|
+
return {
|
|
11655
|
+
originalPath: scenario.targetPath,
|
|
11656
|
+
resolvedPath,
|
|
11657
|
+
params,
|
|
11658
|
+
values,
|
|
11659
|
+
sources,
|
|
11660
|
+
synthetic
|
|
11661
|
+
};
|
|
11662
|
+
}
|
|
11663
|
+
function resolveRoutePath(path, values) {
|
|
11664
|
+
return path.replace(/\/:([A-Za-z0-9_]+)\*\?/g, (_match, name) => {
|
|
11665
|
+
const value = values[name];
|
|
11666
|
+
return value ? `/${encodeRouteFixture(value, true)}` : "";
|
|
11667
|
+
}).replace(/:([A-Za-z0-9_]+)\*/g, (_match, name) => encodeRouteFixture(values[name] ?? defaultFixtureValue(name), true)).replace(/:([A-Za-z0-9_]+)/g, (_match, name) => encodeRouteFixture(values[name] ?? defaultFixtureValue(name), false));
|
|
11668
|
+
}
|
|
11669
|
+
function encodeRouteFixture(value, allowSlash) {
|
|
11670
|
+
if (allowSlash)
|
|
11671
|
+
return value.split("/").filter(Boolean).map(encodeURIComponent).join("/");
|
|
11672
|
+
return encodeURIComponent(value);
|
|
11673
|
+
}
|
|
11674
|
+
function materializeScenarioRoute(scenario, env = process.env) {
|
|
11675
|
+
const resolution = resolveRouteFixtures(scenario, env);
|
|
11676
|
+
if (!resolution.resolvedPath || resolution.resolvedPath === scenario.targetPath) {
|
|
11677
|
+
return { scenario, resolution };
|
|
11678
|
+
}
|
|
11679
|
+
const steps = scenario.steps.map((step) => {
|
|
11680
|
+
let next = step;
|
|
11681
|
+
for (const [name, value] of Object.entries(resolution.values)) {
|
|
11682
|
+
next = next.replaceAll(`:${name}`, value).replaceAll(`[${name}]`, value).replaceAll(`{${name}}`, value);
|
|
11683
|
+
}
|
|
11684
|
+
return next.replaceAll(scenario.targetPath ?? "", resolution.resolvedPath ?? "");
|
|
11685
|
+
});
|
|
11686
|
+
return {
|
|
11687
|
+
scenario: {
|
|
11688
|
+
...scenario,
|
|
11689
|
+
targetPath: resolution.resolvedPath,
|
|
11690
|
+
steps,
|
|
11691
|
+
metadata: {
|
|
11692
|
+
...scenario.metadata ?? {},
|
|
11693
|
+
routeFixtureResolution: resolution
|
|
11694
|
+
}
|
|
11695
|
+
},
|
|
11696
|
+
resolution
|
|
11697
|
+
};
|
|
11698
|
+
}
|
|
11699
|
+
function resolveStartUrl(baseUrl, targetPath) {
|
|
11700
|
+
try {
|
|
11701
|
+
return new URL(targetPath, baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`).toString();
|
|
11702
|
+
} catch {
|
|
11703
|
+
return `${baseUrl.replace(/\/+$/, "")}/${targetPath.replace(/^\/+/, "")}`;
|
|
11704
|
+
}
|
|
11705
|
+
}
|
|
11706
|
+
var DEFAULT_UUID = "00000000-0000-4000-8000-000000000000", PARAM_ENV_CANDIDATES;
|
|
11707
|
+
var init_route_fixtures = __esm(() => {
|
|
11708
|
+
PARAM_ENV_CANDIDATES = {
|
|
11709
|
+
orgSlug: ["TESTERS_ORG_SLUG", "SMOKE_ORG_SLUG", "ORG_SLUG"],
|
|
11710
|
+
orgId: ["TESTERS_ORG_ID", "SMOKE_ORG_ID", "ORG_ID"],
|
|
11711
|
+
projectSlug: ["TESTERS_PROJECT_SLUG", "SMOKE_PROJECT_SLUG", "PROJECT_SLUG"],
|
|
11712
|
+
projectId: ["TESTERS_PROJECT_ID", "SMOKE_PROJECT_ID", "PROJECT_ID"],
|
|
11713
|
+
workspaceId: ["TESTERS_WORKSPACE_ID", "SMOKE_WORKSPACE_ID", "WORKSPACE_ID"],
|
|
11714
|
+
agentId: ["TESTERS_AGENT_ID", "SMOKE_AGENT_ID", "AGENT_ID"],
|
|
11715
|
+
sessionId: ["TESTERS_SESSION_ID", "SMOKE_SESSION_ID", "SESSION_ID"],
|
|
11716
|
+
userId: ["TESTERS_USER_ID", "SMOKE_USER_ID", "USER_ID"]
|
|
11717
|
+
};
|
|
11718
|
+
});
|
|
11719
|
+
|
|
11570
11720
|
// src/lib/scanners/a11y.ts
|
|
11571
11721
|
var exports_a11y = {};
|
|
11572
11722
|
__export(exports_a11y, {
|
|
@@ -12409,33 +12559,35 @@ ${filtered.join(`
|
|
|
12409
12559
|
return { result: `Error executing ${toolName}: ${message}` };
|
|
12410
12560
|
}
|
|
12411
12561
|
}
|
|
12412
|
-
function resolveStartUrl(baseUrl, targetPath) {
|
|
12413
|
-
try {
|
|
12414
|
-
return new URL(targetPath, baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`).toString();
|
|
12415
|
-
} catch {
|
|
12416
|
-
return `${baseUrl.replace(/\/+$/, "")}/${targetPath.replace(/^\/+/, "")}`;
|
|
12417
|
-
}
|
|
12418
|
-
}
|
|
12419
12562
|
function buildScenarioUserMessage(scenario, baseUrl) {
|
|
12563
|
+
const { scenario: materializedScenario, resolution } = materializeScenarioRoute(scenario);
|
|
12420
12564
|
const userParts = [
|
|
12421
|
-
`**Scenario:** ${
|
|
12422
|
-
`**Description:** ${
|
|
12565
|
+
`**Scenario:** ${materializedScenario.name}`,
|
|
12566
|
+
`**Description:** ${materializedScenario.description}`
|
|
12423
12567
|
];
|
|
12424
12568
|
if (baseUrl) {
|
|
12425
12569
|
const normalizedBaseUrl = baseUrl.replace(/\/+$/, "");
|
|
12426
12570
|
userParts.push(`**Base URL:** ${normalizedBaseUrl}`);
|
|
12427
|
-
if (
|
|
12428
|
-
userParts.push(`**Start URL:** ${resolveStartUrl(normalizedBaseUrl,
|
|
12571
|
+
if (materializedScenario.targetPath) {
|
|
12572
|
+
userParts.push(`**Start URL:** ${resolveStartUrl(normalizedBaseUrl, materializedScenario.targetPath)}`);
|
|
12429
12573
|
}
|
|
12430
12574
|
userParts.push("**Navigation Boundary:** Treat the Base URL as the application under test. Resolve relative paths and in-app navigation against this origin. Do not navigate to another host unless a step explicitly includes an absolute external URL.");
|
|
12431
12575
|
}
|
|
12432
|
-
if (
|
|
12433
|
-
userParts.push(`**Target Path:** ${
|
|
12576
|
+
if (materializedScenario.targetPath) {
|
|
12577
|
+
userParts.push(`**Target Path:** ${materializedScenario.targetPath}`);
|
|
12578
|
+
}
|
|
12579
|
+
if (resolution.params.length > 0) {
|
|
12580
|
+
userParts.push("**Route Fixtures:**");
|
|
12581
|
+
for (const param of resolution.params) {
|
|
12582
|
+
const source = resolution.sources[param];
|
|
12583
|
+
const synthetic = source === "default" ? " synthetic" : "";
|
|
12584
|
+
userParts.push(`- :${param} = ${resolution.values[param]} (${source}${synthetic})`);
|
|
12585
|
+
}
|
|
12434
12586
|
}
|
|
12435
|
-
if (
|
|
12587
|
+
if (materializedScenario.steps.length > 0) {
|
|
12436
12588
|
userParts.push("**Steps:**");
|
|
12437
|
-
for (let i = 0;i <
|
|
12438
|
-
userParts.push(`${i + 1}. ${
|
|
12589
|
+
for (let i = 0;i < materializedScenario.steps.length; i++) {
|
|
12590
|
+
userParts.push(`${i + 1}. ${materializedScenario.steps[i]}`);
|
|
12439
12591
|
}
|
|
12440
12592
|
}
|
|
12441
12593
|
return userParts.join(`
|
|
@@ -12733,6 +12885,7 @@ function createClientForModel(model, apiKey) {
|
|
|
12733
12885
|
var activeHARs, activeCoverage, BROWSER_TOOLS;
|
|
12734
12886
|
var init_ai_client = __esm(() => {
|
|
12735
12887
|
init_types();
|
|
12888
|
+
init_route_fixtures();
|
|
12736
12889
|
activeHARs = new Map;
|
|
12737
12890
|
activeCoverage = new Map;
|
|
12738
12891
|
BROWSER_TOOLS = [
|
|
@@ -15482,6 +15635,7 @@ function savePersonaAuthCookies(id, cookies) {
|
|
|
15482
15635
|
// src/lib/runner.ts
|
|
15483
15636
|
init_browser();
|
|
15484
15637
|
init_ai_client();
|
|
15638
|
+
init_route_fixtures();
|
|
15485
15639
|
init_config2();
|
|
15486
15640
|
|
|
15487
15641
|
// src/lib/secrets-resolver.ts
|
|
@@ -16625,9 +16779,10 @@ function withTimeout(promise, ms, label) {
|
|
|
16625
16779
|
});
|
|
16626
16780
|
}
|
|
16627
16781
|
async function runSingleScenario(scenario, runId, options) {
|
|
16628
|
-
const
|
|
16782
|
+
const { scenario: materializedScenario, resolution: routeFixtureResolution } = materializeScenarioRoute(scenario);
|
|
16783
|
+
const scenarioType = materializedScenario.scenarioType ?? "browser";
|
|
16629
16784
|
if (scenarioType === "eval") {
|
|
16630
|
-
return runEvalScenario(
|
|
16785
|
+
return runEvalScenario(materializedScenario, { runId, baseUrl: options.url });
|
|
16631
16786
|
}
|
|
16632
16787
|
const config = loadConfig();
|
|
16633
16788
|
if (options.selfHeal !== undefined)
|
|
@@ -16668,7 +16823,7 @@ async function runSingleScenario(scenario, runId, options) {
|
|
|
16668
16823
|
runId,
|
|
16669
16824
|
scenarioId: scenario.id,
|
|
16670
16825
|
model,
|
|
16671
|
-
stepsTotal:
|
|
16826
|
+
stepsTotal: materializedScenario.steps.length || 10,
|
|
16672
16827
|
personaId: persona?.id ?? null,
|
|
16673
16828
|
personaName: persona?.name ?? null
|
|
16674
16829
|
});
|
|
@@ -16704,12 +16859,12 @@ async function runSingleScenario(scenario, runId, options) {
|
|
|
16704
16859
|
engine: effectiveOptions.engine
|
|
16705
16860
|
});
|
|
16706
16861
|
}
|
|
16707
|
-
const targetUrl =
|
|
16708
|
-
const scenarioTimeout =
|
|
16862
|
+
const targetUrl = materializedScenario.targetPath ? resolveStartUrl(options.url.replace(/\/$/, ""), materializedScenario.targetPath) : options.url;
|
|
16863
|
+
const scenarioTimeout = materializedScenario.timeoutMs ?? options.timeout ?? config.browser.timeout ?? 60000;
|
|
16709
16864
|
registerSession({
|
|
16710
16865
|
resultId: result.id,
|
|
16711
16866
|
runId,
|
|
16712
|
-
scenarioId:
|
|
16867
|
+
scenarioId: materializedScenario.id,
|
|
16713
16868
|
engine: effectiveOptions.engine ?? "playwright",
|
|
16714
16869
|
startUrl: targetUrl
|
|
16715
16870
|
});
|
|
@@ -16763,7 +16918,7 @@ async function runSingleScenario(scenario, runId, options) {
|
|
|
16763
16918
|
const agentResult = await withTimeout(runAgentLoop({
|
|
16764
16919
|
client,
|
|
16765
16920
|
page,
|
|
16766
|
-
scenario,
|
|
16921
|
+
scenario: materializedScenario,
|
|
16767
16922
|
screenshotter,
|
|
16768
16923
|
model,
|
|
16769
16924
|
runId,
|
|
@@ -16854,7 +17009,7 @@ async function runSingleScenario(scenario, runId, options) {
|
|
|
16854
17009
|
const baseReasoning = agentResult.reasoning ? agentResult.reasoning + lightpandaNote : lightpandaNote || "";
|
|
16855
17010
|
const assertionOutcome = await applyStructuredAssertionsToResult({
|
|
16856
17011
|
page,
|
|
16857
|
-
scenario,
|
|
17012
|
+
scenario: materializedScenario,
|
|
16858
17013
|
consoleErrors,
|
|
16859
17014
|
status: agentResult.status,
|
|
16860
17015
|
reasoning: baseReasoning
|
|
@@ -16875,6 +17030,7 @@ async function runSingleScenario(scenario, runId, options) {
|
|
|
16875
17030
|
costCents: estimateCost(model, agentResult.tokensUsed),
|
|
16876
17031
|
metadata: {
|
|
16877
17032
|
consoleLogs,
|
|
17033
|
+
...routeFixtureResolution.params.length > 0 ? { routeFixtureResolution } : {},
|
|
16878
17034
|
...networkErrors.length > 0 ? networkMeta : {},
|
|
16879
17035
|
...structuredAssertionMeta
|
|
16880
17036
|
}
|
|
@@ -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;
|
|
1
|
+
{"version":3,"file":"ai-client.d.ts","sourceRoot":"","sources":["../../src/lib/ai-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"next-route-inventory.d.ts","sourceRoot":"","sources":["../../src/lib/next-route-inventory.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"next-route-inventory.d.ts","sourceRoot":"","sources":["../../src/lib/next-route-inventory.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAEV,mBAAmB,EACnB,QAAQ,EACR,gBAAgB,EAChB,eAAe,EACf,sBAAsB,EACvB,MAAM,mBAAmB,CAAC;AAE3B,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,KAAK,CAAC;AAC3C,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,YAAY,CAAC;AAEtF,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,mBAAmB,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,OAAO,CAAC;IACrB,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,aAAa,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,OAAO,CAAC;IACtB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,QAAQ,EAAE,gBAAgB,CAAC;CAC5B;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,KAAK,EAAE,sBAAsB,EAAE,CAAC;CACjC;AAED,MAAM,WAAW,+BAA+B;IAC9C,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,cAAc,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACrC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC;CACrD;AAED,MAAM,WAAW,8BAA8B;IAC7C,SAAS,EAAE,kBAAkB,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,SAAS,EAAE,eAAe,EAAE,CAAC;CAC9B;AAkCD,wBAAgB,0BAA0B,CAAC,OAAO,EAAE;IAClD,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,kBAAkB,CA4BrB;AAED,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,sBAAsB,EAC5B,SAAS,CAAC,EAAE,MAAM,GACjB,mBAAmB,CA8DrB;AAED,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,+BAA+B,GACvC,8BAA8B,CAuBhC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Scenario } from "../types/index.js";
|
|
2
|
+
export interface RouteFixtureResolution {
|
|
3
|
+
originalPath: string | null;
|
|
4
|
+
resolvedPath: string | null;
|
|
5
|
+
params: string[];
|
|
6
|
+
values: Record<string, string>;
|
|
7
|
+
sources: Record<string, "scenario" | "env" | "default">;
|
|
8
|
+
synthetic: string[];
|
|
9
|
+
}
|
|
10
|
+
export interface MaterializedRouteScenario {
|
|
11
|
+
scenario: Scenario;
|
|
12
|
+
resolution: RouteFixtureResolution;
|
|
13
|
+
}
|
|
14
|
+
export declare function routeParamsFromPath(path: string | null | undefined): string[];
|
|
15
|
+
export declare function defaultRouteFixturesForParams(params: string[]): Record<string, string>;
|
|
16
|
+
export declare function resolveRouteFixtures(scenario: Scenario, env?: Record<string, string | undefined>): RouteFixtureResolution;
|
|
17
|
+
export declare function resolveRoutePath(path: string, values: Record<string, string>): string;
|
|
18
|
+
export declare function materializeScenarioRoute(scenario: Scenario, env?: Record<string, string | undefined>): MaterializedRouteScenario;
|
|
19
|
+
export declare function resolveStartUrl(baseUrl: string, targetPath: string): string;
|
|
20
|
+
//# sourceMappingURL=route-fixtures.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route-fixtures.d.ts","sourceRoot":"","sources":["../../src/lib/route-fixtures.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAElD,MAAM,WAAW,sBAAsB;IACrC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,GAAG,KAAK,GAAG,SAAS,CAAC,CAAC;IACxD,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,EAAE,QAAQ,CAAC;IACnB,UAAU,EAAE,sBAAsB,CAAC;CACpC;AAoED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,EAAE,CAO7E;AAED,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAEtF;AAED,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,QAAQ,EAClB,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GACpD,sBAAsB,CAoCxB;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAQrF;AAOD,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,QAAQ,EAClB,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GACpD,yBAAyB,CA6B3B;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAM3E"}
|
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,YAAY,EAAE,MAAM,mBAAmB,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,YAAY,EAAE,MAAM,mBAAmB,CAAC;AA8B7E,OAAO,KAAK,EAAW,IAAI,EAAE,MAAM,YAAY,CAAC;AAEhD,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,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,OAAO,mBAAmB,EAAE,aAAa,CAAC;IACnD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,IAAI,CAAC,EAAE,OAAO,GAAG;QAAE,KAAK,CAAC,EAAE,GAAG,GAAG,IAAI,GAAG,KAAK,CAAA;KAAE,CAAC;IAChD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EACA,gBAAgB,GAChB,eAAe,GACf,eAAe,GACf,gBAAgB,GAChB,qBAAqB,GACrB,cAAc,GACd,gBAAgB,GAChB,kBAAkB,GAClB,eAAe,GACf,0BAA0B,CAAC;IAC/B,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;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAC;AAIxD,wBAAgB,UAAU,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI,CAEzD;AAMD,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,MAAM,EACb,cAAc,CAAC,EAAE,MAAM,EACvB,yBAAyB,CAAC,EAAE,MAAM,GACjC,MAAM,GAAG,SAAS,CAEpB;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,IAAI,CAAC,UAAU,EAAE,SAAS,GAAG,UAAU,CAAC,GAAG,MAAM,CAM9F;AAED,KAAK,mBAAmB,GAAG,OAAO,CAAC,YAAY,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC,CAAC;AAEhF,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,mBAAmB,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,gBAAgB,EAAE,KAAK,CAAC;QACtB,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,EAAE,OAAO,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC,CAAC;CACJ;AAiBD,wBAAsB,iCAAiC,CAAC,KAAK,EAAE;IAC7D,IAAI,EAAE,IAAI,CAAC;IACX,QAAQ,EAAE,QAAQ,CAAC;IACnB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,MAAM,EAAE,mBAAmB,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,0BAA0B,CAAC,CAyCtC;AA2BD,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,MAAM,CAAC,CAsWjB;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,CA4M1C;AAUD,wBAAgB,sBAAsB,CACpC,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,QAAQ,EAAE,CAqBZ;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,CAY1C;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,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;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,CAqF1C"}
|
package/dist/mcp/index.js
CHANGED
|
@@ -52,7 +52,7 @@ var package_default;
|
|
|
52
52
|
var init_package = __esm(() => {
|
|
53
53
|
package_default = {
|
|
54
54
|
name: "@hasna/testers",
|
|
55
|
-
version: "0.0.
|
|
55
|
+
version: "0.0.51",
|
|
56
56
|
description: "AI-powered QA testing CLI \u2014 spawns cheap AI agents to test web apps with headless browsers",
|
|
57
57
|
type: "module",
|
|
58
58
|
main: "dist/index.js",
|
|
@@ -15674,6 +15674,156 @@ var init_agents = __esm(() => {
|
|
|
15674
15674
|
init_database();
|
|
15675
15675
|
});
|
|
15676
15676
|
|
|
15677
|
+
// src/lib/route-fixtures.ts
|
|
15678
|
+
function isRecord2(value) {
|
|
15679
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
15680
|
+
}
|
|
15681
|
+
function envNameForParam(prefix, param) {
|
|
15682
|
+
return `${prefix}_${param.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^A-Za-z0-9]+/g, "_").toUpperCase()}`;
|
|
15683
|
+
}
|
|
15684
|
+
function readString(value) {
|
|
15685
|
+
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
15686
|
+
}
|
|
15687
|
+
function resolveReference(value, env) {
|
|
15688
|
+
if (value.startsWith("$?"))
|
|
15689
|
+
return env[value.slice(2)]?.trim() || undefined;
|
|
15690
|
+
if (value.startsWith("$"))
|
|
15691
|
+
return env[value.slice(1)]?.trim() || undefined;
|
|
15692
|
+
return value;
|
|
15693
|
+
}
|
|
15694
|
+
function scenarioFixtureValue(params, name, env) {
|
|
15695
|
+
if (!params)
|
|
15696
|
+
return;
|
|
15697
|
+
const routeFixtures = isRecord2(params["routeFixtures"]) ? params["routeFixtures"] : {};
|
|
15698
|
+
const raw = readString(routeFixtures[name]) ?? readString(params[name]);
|
|
15699
|
+
return raw ? resolveReference(raw, env) : undefined;
|
|
15700
|
+
}
|
|
15701
|
+
function envFixtureValue(name, env) {
|
|
15702
|
+
const candidates = [
|
|
15703
|
+
envNameForParam("TESTERS_ROUTE", name),
|
|
15704
|
+
envNameForParam("TESTERS_FIXTURE", name),
|
|
15705
|
+
envNameForParam("ALUMIA_FIXTURE", name),
|
|
15706
|
+
...PARAM_ENV_CANDIDATES[name] ?? []
|
|
15707
|
+
];
|
|
15708
|
+
for (const candidate of candidates) {
|
|
15709
|
+
const value = env[candidate]?.trim();
|
|
15710
|
+
if (value)
|
|
15711
|
+
return value;
|
|
15712
|
+
}
|
|
15713
|
+
return;
|
|
15714
|
+
}
|
|
15715
|
+
function defaultFixtureValue(name) {
|
|
15716
|
+
if (name === "orgSlug")
|
|
15717
|
+
return "test-org";
|
|
15718
|
+
if (name.toLowerCase().endsWith("slug")) {
|
|
15719
|
+
return `test-${name.replace(/Slug$/i, "").replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase() || "slug"}`;
|
|
15720
|
+
}
|
|
15721
|
+
if (name === "id" || name.toLowerCase().endsWith("id"))
|
|
15722
|
+
return DEFAULT_UUID;
|
|
15723
|
+
if (name.toLowerCase().includes("token"))
|
|
15724
|
+
return "test-token";
|
|
15725
|
+
return `test-${name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase()}`;
|
|
15726
|
+
}
|
|
15727
|
+
function routeParamsFromPath(path) {
|
|
15728
|
+
if (!path)
|
|
15729
|
+
return [];
|
|
15730
|
+
const params = new Set;
|
|
15731
|
+
for (const match of path.matchAll(/:([A-Za-z0-9_]+)(?:\*\??)?/g)) {
|
|
15732
|
+
if (match[1])
|
|
15733
|
+
params.add(match[1]);
|
|
15734
|
+
}
|
|
15735
|
+
return [...params];
|
|
15736
|
+
}
|
|
15737
|
+
function resolveRouteFixtures(scenario, env = process.env) {
|
|
15738
|
+
const metadataParams = Array.isArray(scenario.metadata?.["fixtureParams"]) ? scenario.metadata["fixtureParams"].filter((value) => typeof value === "string") : [];
|
|
15739
|
+
const params = [...new Set([...metadataParams, ...routeParamsFromPath(scenario.targetPath)])];
|
|
15740
|
+
const values = {};
|
|
15741
|
+
const sources = {};
|
|
15742
|
+
const synthetic = [];
|
|
15743
|
+
for (const param of params) {
|
|
15744
|
+
const scenarioValue = scenarioFixtureValue(scenario.parameters, param, env);
|
|
15745
|
+
if (scenarioValue) {
|
|
15746
|
+
values[param] = scenarioValue;
|
|
15747
|
+
sources[param] = "scenario";
|
|
15748
|
+
continue;
|
|
15749
|
+
}
|
|
15750
|
+
const envValue = envFixtureValue(param, env);
|
|
15751
|
+
if (envValue) {
|
|
15752
|
+
values[param] = envValue;
|
|
15753
|
+
sources[param] = "env";
|
|
15754
|
+
continue;
|
|
15755
|
+
}
|
|
15756
|
+
values[param] = defaultFixtureValue(param);
|
|
15757
|
+
sources[param] = "default";
|
|
15758
|
+
synthetic.push(param);
|
|
15759
|
+
}
|
|
15760
|
+
const resolvedPath = scenario.targetPath ? resolveRoutePath(scenario.targetPath, values) : null;
|
|
15761
|
+
return {
|
|
15762
|
+
originalPath: scenario.targetPath,
|
|
15763
|
+
resolvedPath,
|
|
15764
|
+
params,
|
|
15765
|
+
values,
|
|
15766
|
+
sources,
|
|
15767
|
+
synthetic
|
|
15768
|
+
};
|
|
15769
|
+
}
|
|
15770
|
+
function resolveRoutePath(path, values) {
|
|
15771
|
+
return path.replace(/\/:([A-Za-z0-9_]+)\*\?/g, (_match, name) => {
|
|
15772
|
+
const value = values[name];
|
|
15773
|
+
return value ? `/${encodeRouteFixture(value, true)}` : "";
|
|
15774
|
+
}).replace(/:([A-Za-z0-9_]+)\*/g, (_match, name) => encodeRouteFixture(values[name] ?? defaultFixtureValue(name), true)).replace(/:([A-Za-z0-9_]+)/g, (_match, name) => encodeRouteFixture(values[name] ?? defaultFixtureValue(name), false));
|
|
15775
|
+
}
|
|
15776
|
+
function encodeRouteFixture(value, allowSlash) {
|
|
15777
|
+
if (allowSlash)
|
|
15778
|
+
return value.split("/").filter(Boolean).map(encodeURIComponent).join("/");
|
|
15779
|
+
return encodeURIComponent(value);
|
|
15780
|
+
}
|
|
15781
|
+
function materializeScenarioRoute(scenario, env = process.env) {
|
|
15782
|
+
const resolution = resolveRouteFixtures(scenario, env);
|
|
15783
|
+
if (!resolution.resolvedPath || resolution.resolvedPath === scenario.targetPath) {
|
|
15784
|
+
return { scenario, resolution };
|
|
15785
|
+
}
|
|
15786
|
+
const steps = scenario.steps.map((step) => {
|
|
15787
|
+
let next = step;
|
|
15788
|
+
for (const [name, value] of Object.entries(resolution.values)) {
|
|
15789
|
+
next = next.replaceAll(`:${name}`, value).replaceAll(`[${name}]`, value).replaceAll(`{${name}}`, value);
|
|
15790
|
+
}
|
|
15791
|
+
return next.replaceAll(scenario.targetPath ?? "", resolution.resolvedPath ?? "");
|
|
15792
|
+
});
|
|
15793
|
+
return {
|
|
15794
|
+
scenario: {
|
|
15795
|
+
...scenario,
|
|
15796
|
+
targetPath: resolution.resolvedPath,
|
|
15797
|
+
steps,
|
|
15798
|
+
metadata: {
|
|
15799
|
+
...scenario.metadata ?? {},
|
|
15800
|
+
routeFixtureResolution: resolution
|
|
15801
|
+
}
|
|
15802
|
+
},
|
|
15803
|
+
resolution
|
|
15804
|
+
};
|
|
15805
|
+
}
|
|
15806
|
+
function resolveStartUrl(baseUrl, targetPath) {
|
|
15807
|
+
try {
|
|
15808
|
+
return new URL(targetPath, baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`).toString();
|
|
15809
|
+
} catch {
|
|
15810
|
+
return `${baseUrl.replace(/\/+$/, "")}/${targetPath.replace(/^\/+/, "")}`;
|
|
15811
|
+
}
|
|
15812
|
+
}
|
|
15813
|
+
var DEFAULT_UUID = "00000000-0000-4000-8000-000000000000", PARAM_ENV_CANDIDATES;
|
|
15814
|
+
var init_route_fixtures = __esm(() => {
|
|
15815
|
+
PARAM_ENV_CANDIDATES = {
|
|
15816
|
+
orgSlug: ["TESTERS_ORG_SLUG", "SMOKE_ORG_SLUG", "ORG_SLUG"],
|
|
15817
|
+
orgId: ["TESTERS_ORG_ID", "SMOKE_ORG_ID", "ORG_ID"],
|
|
15818
|
+
projectSlug: ["TESTERS_PROJECT_SLUG", "SMOKE_PROJECT_SLUG", "PROJECT_SLUG"],
|
|
15819
|
+
projectId: ["TESTERS_PROJECT_ID", "SMOKE_PROJECT_ID", "PROJECT_ID"],
|
|
15820
|
+
workspaceId: ["TESTERS_WORKSPACE_ID", "SMOKE_WORKSPACE_ID", "WORKSPACE_ID"],
|
|
15821
|
+
agentId: ["TESTERS_AGENT_ID", "SMOKE_AGENT_ID", "AGENT_ID"],
|
|
15822
|
+
sessionId: ["TESTERS_SESSION_ID", "SMOKE_SESSION_ID", "SESSION_ID"],
|
|
15823
|
+
userId: ["TESTERS_USER_ID", "SMOKE_USER_ID", "USER_ID"]
|
|
15824
|
+
};
|
|
15825
|
+
});
|
|
15826
|
+
|
|
15677
15827
|
// src/lib/browser-lightpanda.ts
|
|
15678
15828
|
var exports_browser_lightpanda = {};
|
|
15679
15829
|
__export(exports_browser_lightpanda, {
|
|
@@ -17407,33 +17557,35 @@ ${filtered.join(`
|
|
|
17407
17557
|
return { result: `Error executing ${toolName}: ${message}` };
|
|
17408
17558
|
}
|
|
17409
17559
|
}
|
|
17410
|
-
function resolveStartUrl(baseUrl, targetPath) {
|
|
17411
|
-
try {
|
|
17412
|
-
return new URL(targetPath, baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`).toString();
|
|
17413
|
-
} catch {
|
|
17414
|
-
return `${baseUrl.replace(/\/+$/, "")}/${targetPath.replace(/^\/+/, "")}`;
|
|
17415
|
-
}
|
|
17416
|
-
}
|
|
17417
17560
|
function buildScenarioUserMessage(scenario, baseUrl) {
|
|
17561
|
+
const { scenario: materializedScenario, resolution } = materializeScenarioRoute(scenario);
|
|
17418
17562
|
const userParts = [
|
|
17419
|
-
`**Scenario:** ${
|
|
17420
|
-
`**Description:** ${
|
|
17563
|
+
`**Scenario:** ${materializedScenario.name}`,
|
|
17564
|
+
`**Description:** ${materializedScenario.description}`
|
|
17421
17565
|
];
|
|
17422
17566
|
if (baseUrl) {
|
|
17423
17567
|
const normalizedBaseUrl = baseUrl.replace(/\/+$/, "");
|
|
17424
17568
|
userParts.push(`**Base URL:** ${normalizedBaseUrl}`);
|
|
17425
|
-
if (
|
|
17426
|
-
userParts.push(`**Start URL:** ${resolveStartUrl(normalizedBaseUrl,
|
|
17569
|
+
if (materializedScenario.targetPath) {
|
|
17570
|
+
userParts.push(`**Start URL:** ${resolveStartUrl(normalizedBaseUrl, materializedScenario.targetPath)}`);
|
|
17427
17571
|
}
|
|
17428
17572
|
userParts.push("**Navigation Boundary:** Treat the Base URL as the application under test. Resolve relative paths and in-app navigation against this origin. Do not navigate to another host unless a step explicitly includes an absolute external URL.");
|
|
17429
17573
|
}
|
|
17430
|
-
if (
|
|
17431
|
-
userParts.push(`**Target Path:** ${
|
|
17574
|
+
if (materializedScenario.targetPath) {
|
|
17575
|
+
userParts.push(`**Target Path:** ${materializedScenario.targetPath}`);
|
|
17576
|
+
}
|
|
17577
|
+
if (resolution.params.length > 0) {
|
|
17578
|
+
userParts.push("**Route Fixtures:**");
|
|
17579
|
+
for (const param of resolution.params) {
|
|
17580
|
+
const source = resolution.sources[param];
|
|
17581
|
+
const synthetic = source === "default" ? " synthetic" : "";
|
|
17582
|
+
userParts.push(`- :${param} = ${resolution.values[param]} (${source}${synthetic})`);
|
|
17583
|
+
}
|
|
17432
17584
|
}
|
|
17433
|
-
if (
|
|
17585
|
+
if (materializedScenario.steps.length > 0) {
|
|
17434
17586
|
userParts.push("**Steps:**");
|
|
17435
|
-
for (let i = 0;i <
|
|
17436
|
-
userParts.push(`${i + 1}. ${
|
|
17587
|
+
for (let i = 0;i < materializedScenario.steps.length; i++) {
|
|
17588
|
+
userParts.push(`${i + 1}. ${materializedScenario.steps[i]}`);
|
|
17437
17589
|
}
|
|
17438
17590
|
}
|
|
17439
17591
|
return userParts.join(`
|
|
@@ -17731,6 +17883,7 @@ function createClientForModel(model, apiKey) {
|
|
|
17731
17883
|
var activeHARs, activeCoverage, BROWSER_TOOLS;
|
|
17732
17884
|
var init_ai_client = __esm(() => {
|
|
17733
17885
|
init_types3();
|
|
17886
|
+
init_route_fixtures();
|
|
17734
17887
|
activeHARs = new Map;
|
|
17735
17888
|
activeCoverage = new Map;
|
|
17736
17889
|
BROWSER_TOOLS = [
|
|
@@ -21370,9 +21523,10 @@ function withTimeout(promise, ms, label) {
|
|
|
21370
21523
|
});
|
|
21371
21524
|
}
|
|
21372
21525
|
async function runSingleScenario(scenario, runId, options) {
|
|
21373
|
-
const
|
|
21526
|
+
const { scenario: materializedScenario, resolution: routeFixtureResolution } = materializeScenarioRoute(scenario);
|
|
21527
|
+
const scenarioType = materializedScenario.scenarioType ?? "browser";
|
|
21374
21528
|
if (scenarioType === "eval") {
|
|
21375
|
-
return runEvalScenario(
|
|
21529
|
+
return runEvalScenario(materializedScenario, { runId, baseUrl: options.url });
|
|
21376
21530
|
}
|
|
21377
21531
|
const config = loadConfig();
|
|
21378
21532
|
if (options.selfHeal !== undefined)
|
|
@@ -21413,7 +21567,7 @@ async function runSingleScenario(scenario, runId, options) {
|
|
|
21413
21567
|
runId,
|
|
21414
21568
|
scenarioId: scenario.id,
|
|
21415
21569
|
model,
|
|
21416
|
-
stepsTotal:
|
|
21570
|
+
stepsTotal: materializedScenario.steps.length || 10,
|
|
21417
21571
|
personaId: persona?.id ?? null,
|
|
21418
21572
|
personaName: persona?.name ?? null
|
|
21419
21573
|
});
|
|
@@ -21449,12 +21603,12 @@ async function runSingleScenario(scenario, runId, options) {
|
|
|
21449
21603
|
engine: effectiveOptions.engine
|
|
21450
21604
|
});
|
|
21451
21605
|
}
|
|
21452
|
-
const targetUrl =
|
|
21453
|
-
const scenarioTimeout =
|
|
21606
|
+
const targetUrl = materializedScenario.targetPath ? resolveStartUrl(options.url.replace(/\/$/, ""), materializedScenario.targetPath) : options.url;
|
|
21607
|
+
const scenarioTimeout = materializedScenario.timeoutMs ?? options.timeout ?? config.browser.timeout ?? 60000;
|
|
21454
21608
|
registerSession({
|
|
21455
21609
|
resultId: result.id,
|
|
21456
21610
|
runId,
|
|
21457
|
-
scenarioId:
|
|
21611
|
+
scenarioId: materializedScenario.id,
|
|
21458
21612
|
engine: effectiveOptions.engine ?? "playwright",
|
|
21459
21613
|
startUrl: targetUrl
|
|
21460
21614
|
});
|
|
@@ -21508,7 +21662,7 @@ async function runSingleScenario(scenario, runId, options) {
|
|
|
21508
21662
|
const agentResult = await withTimeout(runAgentLoop({
|
|
21509
21663
|
client,
|
|
21510
21664
|
page,
|
|
21511
|
-
scenario,
|
|
21665
|
+
scenario: materializedScenario,
|
|
21512
21666
|
screenshotter,
|
|
21513
21667
|
model,
|
|
21514
21668
|
runId,
|
|
@@ -21599,7 +21753,7 @@ async function runSingleScenario(scenario, runId, options) {
|
|
|
21599
21753
|
const baseReasoning = agentResult.reasoning ? agentResult.reasoning + lightpandaNote : lightpandaNote || "";
|
|
21600
21754
|
const assertionOutcome = await applyStructuredAssertionsToResult({
|
|
21601
21755
|
page,
|
|
21602
|
-
scenario,
|
|
21756
|
+
scenario: materializedScenario,
|
|
21603
21757
|
consoleErrors,
|
|
21604
21758
|
status: agentResult.status,
|
|
21605
21759
|
reasoning: baseReasoning
|
|
@@ -21620,6 +21774,7 @@ async function runSingleScenario(scenario, runId, options) {
|
|
|
21620
21774
|
costCents: estimateCost(model, agentResult.tokensUsed),
|
|
21621
21775
|
metadata: {
|
|
21622
21776
|
consoleLogs,
|
|
21777
|
+
...routeFixtureResolution.params.length > 0 ? { routeFixtureResolution } : {},
|
|
21623
21778
|
...networkErrors.length > 0 ? networkMeta : {},
|
|
21624
21779
|
...structuredAssertionMeta
|
|
21625
21780
|
}
|
|
@@ -21969,6 +22124,7 @@ var init_runner = __esm(() => {
|
|
|
21969
22124
|
init_browser();
|
|
21970
22125
|
init_screenshotter();
|
|
21971
22126
|
init_ai_client();
|
|
22127
|
+
init_route_fixtures();
|
|
21972
22128
|
init_config2();
|
|
21973
22129
|
init_persona_auth();
|
|
21974
22130
|
init_session_tracker();
|
package/dist/server/index.js
CHANGED
|
@@ -14410,6 +14410,156 @@ var init_results = __esm(() => {
|
|
|
14410
14410
|
init_database();
|
|
14411
14411
|
});
|
|
14412
14412
|
|
|
14413
|
+
// src/lib/route-fixtures.ts
|
|
14414
|
+
function isRecord2(value) {
|
|
14415
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
14416
|
+
}
|
|
14417
|
+
function envNameForParam(prefix, param) {
|
|
14418
|
+
return `${prefix}_${param.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^A-Za-z0-9]+/g, "_").toUpperCase()}`;
|
|
14419
|
+
}
|
|
14420
|
+
function readString(value) {
|
|
14421
|
+
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
14422
|
+
}
|
|
14423
|
+
function resolveReference(value, env) {
|
|
14424
|
+
if (value.startsWith("$?"))
|
|
14425
|
+
return env[value.slice(2)]?.trim() || undefined;
|
|
14426
|
+
if (value.startsWith("$"))
|
|
14427
|
+
return env[value.slice(1)]?.trim() || undefined;
|
|
14428
|
+
return value;
|
|
14429
|
+
}
|
|
14430
|
+
function scenarioFixtureValue(params, name, env) {
|
|
14431
|
+
if (!params)
|
|
14432
|
+
return;
|
|
14433
|
+
const routeFixtures = isRecord2(params["routeFixtures"]) ? params["routeFixtures"] : {};
|
|
14434
|
+
const raw = readString(routeFixtures[name]) ?? readString(params[name]);
|
|
14435
|
+
return raw ? resolveReference(raw, env) : undefined;
|
|
14436
|
+
}
|
|
14437
|
+
function envFixtureValue(name, env) {
|
|
14438
|
+
const candidates = [
|
|
14439
|
+
envNameForParam("TESTERS_ROUTE", name),
|
|
14440
|
+
envNameForParam("TESTERS_FIXTURE", name),
|
|
14441
|
+
envNameForParam("ALUMIA_FIXTURE", name),
|
|
14442
|
+
...PARAM_ENV_CANDIDATES[name] ?? []
|
|
14443
|
+
];
|
|
14444
|
+
for (const candidate of candidates) {
|
|
14445
|
+
const value = env[candidate]?.trim();
|
|
14446
|
+
if (value)
|
|
14447
|
+
return value;
|
|
14448
|
+
}
|
|
14449
|
+
return;
|
|
14450
|
+
}
|
|
14451
|
+
function defaultFixtureValue(name) {
|
|
14452
|
+
if (name === "orgSlug")
|
|
14453
|
+
return "test-org";
|
|
14454
|
+
if (name.toLowerCase().endsWith("slug")) {
|
|
14455
|
+
return `test-${name.replace(/Slug$/i, "").replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase() || "slug"}`;
|
|
14456
|
+
}
|
|
14457
|
+
if (name === "id" || name.toLowerCase().endsWith("id"))
|
|
14458
|
+
return DEFAULT_UUID;
|
|
14459
|
+
if (name.toLowerCase().includes("token"))
|
|
14460
|
+
return "test-token";
|
|
14461
|
+
return `test-${name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase()}`;
|
|
14462
|
+
}
|
|
14463
|
+
function routeParamsFromPath(path) {
|
|
14464
|
+
if (!path)
|
|
14465
|
+
return [];
|
|
14466
|
+
const params = new Set;
|
|
14467
|
+
for (const match of path.matchAll(/:([A-Za-z0-9_]+)(?:\*\??)?/g)) {
|
|
14468
|
+
if (match[1])
|
|
14469
|
+
params.add(match[1]);
|
|
14470
|
+
}
|
|
14471
|
+
return [...params];
|
|
14472
|
+
}
|
|
14473
|
+
function resolveRouteFixtures(scenario, env = process.env) {
|
|
14474
|
+
const metadataParams = Array.isArray(scenario.metadata?.["fixtureParams"]) ? scenario.metadata["fixtureParams"].filter((value) => typeof value === "string") : [];
|
|
14475
|
+
const params = [...new Set([...metadataParams, ...routeParamsFromPath(scenario.targetPath)])];
|
|
14476
|
+
const values = {};
|
|
14477
|
+
const sources = {};
|
|
14478
|
+
const synthetic = [];
|
|
14479
|
+
for (const param of params) {
|
|
14480
|
+
const scenarioValue = scenarioFixtureValue(scenario.parameters, param, env);
|
|
14481
|
+
if (scenarioValue) {
|
|
14482
|
+
values[param] = scenarioValue;
|
|
14483
|
+
sources[param] = "scenario";
|
|
14484
|
+
continue;
|
|
14485
|
+
}
|
|
14486
|
+
const envValue = envFixtureValue(param, env);
|
|
14487
|
+
if (envValue) {
|
|
14488
|
+
values[param] = envValue;
|
|
14489
|
+
sources[param] = "env";
|
|
14490
|
+
continue;
|
|
14491
|
+
}
|
|
14492
|
+
values[param] = defaultFixtureValue(param);
|
|
14493
|
+
sources[param] = "default";
|
|
14494
|
+
synthetic.push(param);
|
|
14495
|
+
}
|
|
14496
|
+
const resolvedPath = scenario.targetPath ? resolveRoutePath(scenario.targetPath, values) : null;
|
|
14497
|
+
return {
|
|
14498
|
+
originalPath: scenario.targetPath,
|
|
14499
|
+
resolvedPath,
|
|
14500
|
+
params,
|
|
14501
|
+
values,
|
|
14502
|
+
sources,
|
|
14503
|
+
synthetic
|
|
14504
|
+
};
|
|
14505
|
+
}
|
|
14506
|
+
function resolveRoutePath(path, values) {
|
|
14507
|
+
return path.replace(/\/:([A-Za-z0-9_]+)\*\?/g, (_match, name) => {
|
|
14508
|
+
const value = values[name];
|
|
14509
|
+
return value ? `/${encodeRouteFixture(value, true)}` : "";
|
|
14510
|
+
}).replace(/:([A-Za-z0-9_]+)\*/g, (_match, name) => encodeRouteFixture(values[name] ?? defaultFixtureValue(name), true)).replace(/:([A-Za-z0-9_]+)/g, (_match, name) => encodeRouteFixture(values[name] ?? defaultFixtureValue(name), false));
|
|
14511
|
+
}
|
|
14512
|
+
function encodeRouteFixture(value, allowSlash) {
|
|
14513
|
+
if (allowSlash)
|
|
14514
|
+
return value.split("/").filter(Boolean).map(encodeURIComponent).join("/");
|
|
14515
|
+
return encodeURIComponent(value);
|
|
14516
|
+
}
|
|
14517
|
+
function materializeScenarioRoute(scenario, env = process.env) {
|
|
14518
|
+
const resolution = resolveRouteFixtures(scenario, env);
|
|
14519
|
+
if (!resolution.resolvedPath || resolution.resolvedPath === scenario.targetPath) {
|
|
14520
|
+
return { scenario, resolution };
|
|
14521
|
+
}
|
|
14522
|
+
const steps = scenario.steps.map((step) => {
|
|
14523
|
+
let next = step;
|
|
14524
|
+
for (const [name, value] of Object.entries(resolution.values)) {
|
|
14525
|
+
next = next.replaceAll(`:${name}`, value).replaceAll(`[${name}]`, value).replaceAll(`{${name}}`, value);
|
|
14526
|
+
}
|
|
14527
|
+
return next.replaceAll(scenario.targetPath ?? "", resolution.resolvedPath ?? "");
|
|
14528
|
+
});
|
|
14529
|
+
return {
|
|
14530
|
+
scenario: {
|
|
14531
|
+
...scenario,
|
|
14532
|
+
targetPath: resolution.resolvedPath,
|
|
14533
|
+
steps,
|
|
14534
|
+
metadata: {
|
|
14535
|
+
...scenario.metadata ?? {},
|
|
14536
|
+
routeFixtureResolution: resolution
|
|
14537
|
+
}
|
|
14538
|
+
},
|
|
14539
|
+
resolution
|
|
14540
|
+
};
|
|
14541
|
+
}
|
|
14542
|
+
function resolveStartUrl(baseUrl, targetPath) {
|
|
14543
|
+
try {
|
|
14544
|
+
return new URL(targetPath, baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`).toString();
|
|
14545
|
+
} catch {
|
|
14546
|
+
return `${baseUrl.replace(/\/+$/, "")}/${targetPath.replace(/^\/+/, "")}`;
|
|
14547
|
+
}
|
|
14548
|
+
}
|
|
14549
|
+
var DEFAULT_UUID = "00000000-0000-4000-8000-000000000000", PARAM_ENV_CANDIDATES;
|
|
14550
|
+
var init_route_fixtures = __esm(() => {
|
|
14551
|
+
PARAM_ENV_CANDIDATES = {
|
|
14552
|
+
orgSlug: ["TESTERS_ORG_SLUG", "SMOKE_ORG_SLUG", "ORG_SLUG"],
|
|
14553
|
+
orgId: ["TESTERS_ORG_ID", "SMOKE_ORG_ID", "ORG_ID"],
|
|
14554
|
+
projectSlug: ["TESTERS_PROJECT_SLUG", "SMOKE_PROJECT_SLUG", "PROJECT_SLUG"],
|
|
14555
|
+
projectId: ["TESTERS_PROJECT_ID", "SMOKE_PROJECT_ID", "PROJECT_ID"],
|
|
14556
|
+
workspaceId: ["TESTERS_WORKSPACE_ID", "SMOKE_WORKSPACE_ID", "WORKSPACE_ID"],
|
|
14557
|
+
agentId: ["TESTERS_AGENT_ID", "SMOKE_AGENT_ID", "AGENT_ID"],
|
|
14558
|
+
sessionId: ["TESTERS_SESSION_ID", "SMOKE_SESSION_ID", "SESSION_ID"],
|
|
14559
|
+
userId: ["TESTERS_USER_ID", "SMOKE_USER_ID", "USER_ID"]
|
|
14560
|
+
};
|
|
14561
|
+
});
|
|
14562
|
+
|
|
14413
14563
|
// src/lib/browser-lightpanda.ts
|
|
14414
14564
|
var exports_browser_lightpanda = {};
|
|
14415
14565
|
__export(exports_browser_lightpanda, {
|
|
@@ -16094,33 +16244,35 @@ ${filtered.join(`
|
|
|
16094
16244
|
return { result: `Error executing ${toolName}: ${message}` };
|
|
16095
16245
|
}
|
|
16096
16246
|
}
|
|
16097
|
-
function resolveStartUrl(baseUrl, targetPath) {
|
|
16098
|
-
try {
|
|
16099
|
-
return new URL(targetPath, baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`).toString();
|
|
16100
|
-
} catch {
|
|
16101
|
-
return `${baseUrl.replace(/\/+$/, "")}/${targetPath.replace(/^\/+/, "")}`;
|
|
16102
|
-
}
|
|
16103
|
-
}
|
|
16104
16247
|
function buildScenarioUserMessage(scenario, baseUrl) {
|
|
16248
|
+
const { scenario: materializedScenario, resolution } = materializeScenarioRoute(scenario);
|
|
16105
16249
|
const userParts = [
|
|
16106
|
-
`**Scenario:** ${
|
|
16107
|
-
`**Description:** ${
|
|
16250
|
+
`**Scenario:** ${materializedScenario.name}`,
|
|
16251
|
+
`**Description:** ${materializedScenario.description}`
|
|
16108
16252
|
];
|
|
16109
16253
|
if (baseUrl) {
|
|
16110
16254
|
const normalizedBaseUrl = baseUrl.replace(/\/+$/, "");
|
|
16111
16255
|
userParts.push(`**Base URL:** ${normalizedBaseUrl}`);
|
|
16112
|
-
if (
|
|
16113
|
-
userParts.push(`**Start URL:** ${resolveStartUrl(normalizedBaseUrl,
|
|
16256
|
+
if (materializedScenario.targetPath) {
|
|
16257
|
+
userParts.push(`**Start URL:** ${resolveStartUrl(normalizedBaseUrl, materializedScenario.targetPath)}`);
|
|
16114
16258
|
}
|
|
16115
16259
|
userParts.push("**Navigation Boundary:** Treat the Base URL as the application under test. Resolve relative paths and in-app navigation against this origin. Do not navigate to another host unless a step explicitly includes an absolute external URL.");
|
|
16116
16260
|
}
|
|
16117
|
-
if (
|
|
16118
|
-
userParts.push(`**Target Path:** ${
|
|
16261
|
+
if (materializedScenario.targetPath) {
|
|
16262
|
+
userParts.push(`**Target Path:** ${materializedScenario.targetPath}`);
|
|
16263
|
+
}
|
|
16264
|
+
if (resolution.params.length > 0) {
|
|
16265
|
+
userParts.push("**Route Fixtures:**");
|
|
16266
|
+
for (const param of resolution.params) {
|
|
16267
|
+
const source = resolution.sources[param];
|
|
16268
|
+
const synthetic = source === "default" ? " synthetic" : "";
|
|
16269
|
+
userParts.push(`- :${param} = ${resolution.values[param]} (${source}${synthetic})`);
|
|
16270
|
+
}
|
|
16119
16271
|
}
|
|
16120
|
-
if (
|
|
16272
|
+
if (materializedScenario.steps.length > 0) {
|
|
16121
16273
|
userParts.push("**Steps:**");
|
|
16122
|
-
for (let i = 0;i <
|
|
16123
|
-
userParts.push(`${i + 1}. ${
|
|
16274
|
+
for (let i = 0;i < materializedScenario.steps.length; i++) {
|
|
16275
|
+
userParts.push(`${i + 1}. ${materializedScenario.steps[i]}`);
|
|
16124
16276
|
}
|
|
16125
16277
|
}
|
|
16126
16278
|
return userParts.join(`
|
|
@@ -16418,6 +16570,7 @@ function createClientForModel(model, apiKey) {
|
|
|
16418
16570
|
var activeHARs, activeCoverage, BROWSER_TOOLS;
|
|
16419
16571
|
var init_ai_client = __esm(() => {
|
|
16420
16572
|
init_types2();
|
|
16573
|
+
init_route_fixtures();
|
|
16421
16574
|
activeHARs = new Map;
|
|
16422
16575
|
activeCoverage = new Map;
|
|
16423
16576
|
BROWSER_TOOLS = [
|
|
@@ -46937,7 +47090,7 @@ import { join as join14 } from "path";
|
|
|
46937
47090
|
// package.json
|
|
46938
47091
|
var package_default = {
|
|
46939
47092
|
name: "@hasna/testers",
|
|
46940
|
-
version: "0.0.
|
|
47093
|
+
version: "0.0.51",
|
|
46941
47094
|
description: "AI-powered QA testing CLI \u2014 spawns cheap AI agents to test web apps with headless browsers",
|
|
46942
47095
|
type: "module",
|
|
46943
47096
|
main: "dist/index.js",
|
|
@@ -48526,6 +48679,7 @@ class Screenshotter {
|
|
|
48526
48679
|
|
|
48527
48680
|
// src/lib/runner.ts
|
|
48528
48681
|
init_ai_client();
|
|
48682
|
+
init_route_fixtures();
|
|
48529
48683
|
init_config2();
|
|
48530
48684
|
|
|
48531
48685
|
// src/lib/secrets-resolver.ts
|
|
@@ -49572,9 +49726,10 @@ function withTimeout(promise, ms, label) {
|
|
|
49572
49726
|
});
|
|
49573
49727
|
}
|
|
49574
49728
|
async function runSingleScenario(scenario, runId, options) {
|
|
49575
|
-
const
|
|
49729
|
+
const { scenario: materializedScenario, resolution: routeFixtureResolution } = materializeScenarioRoute(scenario);
|
|
49730
|
+
const scenarioType = materializedScenario.scenarioType ?? "browser";
|
|
49576
49731
|
if (scenarioType === "eval") {
|
|
49577
|
-
return runEvalScenario(
|
|
49732
|
+
return runEvalScenario(materializedScenario, { runId, baseUrl: options.url });
|
|
49578
49733
|
}
|
|
49579
49734
|
const config = loadConfig();
|
|
49580
49735
|
if (options.selfHeal !== undefined)
|
|
@@ -49615,7 +49770,7 @@ async function runSingleScenario(scenario, runId, options) {
|
|
|
49615
49770
|
runId,
|
|
49616
49771
|
scenarioId: scenario.id,
|
|
49617
49772
|
model,
|
|
49618
|
-
stepsTotal:
|
|
49773
|
+
stepsTotal: materializedScenario.steps.length || 10,
|
|
49619
49774
|
personaId: persona?.id ?? null,
|
|
49620
49775
|
personaName: persona?.name ?? null
|
|
49621
49776
|
});
|
|
@@ -49651,12 +49806,12 @@ async function runSingleScenario(scenario, runId, options) {
|
|
|
49651
49806
|
engine: effectiveOptions.engine
|
|
49652
49807
|
});
|
|
49653
49808
|
}
|
|
49654
|
-
const targetUrl =
|
|
49655
|
-
const scenarioTimeout =
|
|
49809
|
+
const targetUrl = materializedScenario.targetPath ? resolveStartUrl(options.url.replace(/\/$/, ""), materializedScenario.targetPath) : options.url;
|
|
49810
|
+
const scenarioTimeout = materializedScenario.timeoutMs ?? options.timeout ?? config.browser.timeout ?? 60000;
|
|
49656
49811
|
registerSession({
|
|
49657
49812
|
resultId: result.id,
|
|
49658
49813
|
runId,
|
|
49659
|
-
scenarioId:
|
|
49814
|
+
scenarioId: materializedScenario.id,
|
|
49660
49815
|
engine: effectiveOptions.engine ?? "playwright",
|
|
49661
49816
|
startUrl: targetUrl
|
|
49662
49817
|
});
|
|
@@ -49710,7 +49865,7 @@ async function runSingleScenario(scenario, runId, options) {
|
|
|
49710
49865
|
const agentResult = await withTimeout(runAgentLoop({
|
|
49711
49866
|
client,
|
|
49712
49867
|
page,
|
|
49713
|
-
scenario,
|
|
49868
|
+
scenario: materializedScenario,
|
|
49714
49869
|
screenshotter,
|
|
49715
49870
|
model,
|
|
49716
49871
|
runId,
|
|
@@ -49801,7 +49956,7 @@ async function runSingleScenario(scenario, runId, options) {
|
|
|
49801
49956
|
const baseReasoning = agentResult.reasoning ? agentResult.reasoning + lightpandaNote : lightpandaNote || "";
|
|
49802
49957
|
const assertionOutcome = await applyStructuredAssertionsToResult({
|
|
49803
49958
|
page,
|
|
49804
|
-
scenario,
|
|
49959
|
+
scenario: materializedScenario,
|
|
49805
49960
|
consoleErrors,
|
|
49806
49961
|
status: agentResult.status,
|
|
49807
49962
|
reasoning: baseReasoning
|
|
@@ -49822,6 +49977,7 @@ async function runSingleScenario(scenario, runId, options) {
|
|
|
49822
49977
|
costCents: estimateCost(model, agentResult.tokensUsed),
|
|
49823
49978
|
metadata: {
|
|
49824
49979
|
consoleLogs,
|
|
49980
|
+
...routeFixtureResolution.params.length > 0 ? { routeFixtureResolution } : {},
|
|
49825
49981
|
...networkErrors.length > 0 ? networkMeta : {},
|
|
49826
49982
|
...structuredAssertionMeta
|
|
49827
49983
|
}
|