@hasna/testers 0.0.49 → 0.0.50
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
CHANGED
|
@@ -59184,17 +59184,22 @@ function discoverNextRouteInventory(options) {
|
|
|
59184
59184
|
function scenarioInputForNextRoute(item, projectId) {
|
|
59185
59185
|
const label = item.kind === "page" ? "page" : "API route";
|
|
59186
59186
|
const methodList = item.methods.length > 0 ? item.methods.join(", ") : "discovered methods";
|
|
59187
|
+
const fixtureStep = item.fixtureParams.length > 0 ? `Bind dynamic fixture values for ${item.fixtureParams.map((name) => `:${name}`).join(", ")} before running route actions.` : undefined;
|
|
59187
59188
|
const dynamicStep = item.dynamic ? "Substitute dynamic path parameters with valid fixture values from the target org before opening or calling the route." : undefined;
|
|
59189
|
+
const actionSteps = item.actions.slice(0, 16).map(formatActionStep);
|
|
59188
59190
|
const pageSteps = [
|
|
59191
|
+
fixtureStep,
|
|
59189
59192
|
dynamicStep,
|
|
59190
59193
|
`Open the Next.js ${label} ${item.routePath}.`,
|
|
59191
59194
|
"Wait for the route to finish loading and verify it does not show a blank shell, framework error page, or unexpected auth loop.",
|
|
59192
|
-
"Exercise visible primary navigation, tabs, filters, dialogs, forms, and safe buttons on this route.",
|
|
59195
|
+
...actionSteps.length > 0 ? actionSteps : ["Exercise visible primary navigation, tabs, filters, dialogs, forms, and safe buttons on this route."],
|
|
59193
59196
|
"Verify the route stays within the expected org/workspace context and does not emit console errors."
|
|
59194
59197
|
].filter(Boolean);
|
|
59195
59198
|
const apiSteps = [
|
|
59199
|
+
fixtureStep,
|
|
59196
59200
|
dynamicStep,
|
|
59197
59201
|
`Call the ${methodList} handler(s) for ${item.routePath} using safe fixture data.`,
|
|
59202
|
+
...actionSteps,
|
|
59198
59203
|
"Verify expected authentication, authorization, validation, and tenant isolation behavior.",
|
|
59199
59204
|
"For mutating methods, use harmless test payloads and confirm the response does not create cross-org side effects.",
|
|
59200
59205
|
"Verify response status, JSON shape, and error messages are stable and regression-safe."
|
|
@@ -59215,6 +59220,9 @@ function scenarioInputForNextRoute(item, projectId) {
|
|
|
59215
59220
|
category: item.category,
|
|
59216
59221
|
methods: item.methods,
|
|
59217
59222
|
dynamic: item.dynamic,
|
|
59223
|
+
fixtureParams: item.fixtureParams,
|
|
59224
|
+
actions: item.actions,
|
|
59225
|
+
actionCount: item.actions.length,
|
|
59218
59226
|
groups: item.groups
|
|
59219
59227
|
},
|
|
59220
59228
|
projectId
|
|
@@ -59312,9 +59320,20 @@ function routeItemFromFile(rootDir, appDir, file) {
|
|
|
59312
59320
|
const pathSegments = routeSegments.filter((segment) => !segment.startsWith("(")).filter((segment) => !segment.startsWith("@")).map(normalizeRouteSegment).filter(Boolean);
|
|
59313
59321
|
const routePath = `/${pathSegments.join("/")}`.replace(/\/+/g, "/");
|
|
59314
59322
|
const normalizedRoutePath = routePath === "/" ? "/" : routePath.replace(/\/$/, "");
|
|
59315
|
-
const
|
|
59323
|
+
const sources = collectRouteSources(rootDir, file);
|
|
59324
|
+
const primarySource = sources[0]?.source ?? readFileSync9(file, "utf8");
|
|
59325
|
+
const methods = kind === "api" ? extractRouteMethods(primarySource) : [];
|
|
59316
59326
|
const category = classifyRoute(normalizedRoutePath, groups, relativeFile);
|
|
59317
59327
|
const dynamic = routeSegments.some((segment) => segment.includes("["));
|
|
59328
|
+
const fixtureParams = extractFixtureParams(normalizedRoutePath);
|
|
59329
|
+
const actions = kind === "api" ? methods.map((method) => ({
|
|
59330
|
+
kind: "api-method",
|
|
59331
|
+
label: method,
|
|
59332
|
+
target: normalizedRoutePath,
|
|
59333
|
+
sourceFile: relativeFile,
|
|
59334
|
+
destructive: isDestructiveAction(method, normalizedRoutePath),
|
|
59335
|
+
requiresFixture: fixtureParams.length > 0
|
|
59336
|
+
})) : extractPageActions(rootDir, sources, fixtureParams);
|
|
59318
59337
|
const requiresAuth = inferRequiresAuth(normalizedRoutePath, groups, kind);
|
|
59319
59338
|
return {
|
|
59320
59339
|
kind,
|
|
@@ -59325,6 +59344,8 @@ function routeItemFromFile(rootDir, appDir, file) {
|
|
|
59325
59344
|
methods,
|
|
59326
59345
|
dynamic,
|
|
59327
59346
|
requiresAuth,
|
|
59347
|
+
fixtureParams,
|
|
59348
|
+
actions,
|
|
59328
59349
|
tags: tagsForRoute({ kind, routePath: normalizedRoutePath, category, groups, dynamic, requiresAuth }),
|
|
59329
59350
|
priority: priorityForRoute(normalizedRoutePath, category, kind)
|
|
59330
59351
|
};
|
|
@@ -59341,8 +59362,7 @@ function normalizeRouteSegment(segment) {
|
|
|
59341
59362
|
}
|
|
59342
59363
|
return segment;
|
|
59343
59364
|
}
|
|
59344
|
-
function extractRouteMethods(
|
|
59345
|
-
const source = readFileSync9(file, "utf8");
|
|
59365
|
+
function extractRouteMethods(source) {
|
|
59346
59366
|
const methods = new Set;
|
|
59347
59367
|
const pattern = /\b(?:export\s+)?(?:async\s+)?function\s+(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\b|\bexport\s+const\s+(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\b/g;
|
|
59348
59368
|
for (const match of source.matchAll(pattern)) {
|
|
@@ -59352,6 +59372,193 @@ function extractRouteMethods(file) {
|
|
|
59352
59372
|
}
|
|
59353
59373
|
return [...methods].sort();
|
|
59354
59374
|
}
|
|
59375
|
+
function collectRouteSources(rootDir, entryFile) {
|
|
59376
|
+
const seen = new Set;
|
|
59377
|
+
const sources = [];
|
|
59378
|
+
function visit(file, depth) {
|
|
59379
|
+
if (seen.has(file) || sources.length >= IMPORT_SCAN_LIMIT)
|
|
59380
|
+
return;
|
|
59381
|
+
if (!existsSync18(file) || !statSync4(file).isFile())
|
|
59382
|
+
return;
|
|
59383
|
+
seen.add(file);
|
|
59384
|
+
const source = readFileSync9(file, "utf8");
|
|
59385
|
+
sources.push({ file: relative4(rootDir, file), source });
|
|
59386
|
+
if (depth >= IMPORT_SCAN_DEPTH)
|
|
59387
|
+
return;
|
|
59388
|
+
for (const specifier of localImportSpecifiers(source)) {
|
|
59389
|
+
const resolved = resolveImportFile(file, specifier);
|
|
59390
|
+
if (resolved)
|
|
59391
|
+
visit(resolved, depth + 1);
|
|
59392
|
+
}
|
|
59393
|
+
}
|
|
59394
|
+
visit(entryFile, 0);
|
|
59395
|
+
return sources;
|
|
59396
|
+
}
|
|
59397
|
+
function localImportSpecifiers(source) {
|
|
59398
|
+
const specifiers = [];
|
|
59399
|
+
const pattern = /\bimport\b(?:[\s\S]*?\bfrom\s*)?["'](\.{1,2}\/[^"']+)["']|\bimport\(\s*["'](\.{1,2}\/[^"']+)["']\s*\)/g;
|
|
59400
|
+
for (const match of source.matchAll(pattern)) {
|
|
59401
|
+
const specifier = match[1] ?? match[2];
|
|
59402
|
+
if (specifier)
|
|
59403
|
+
specifiers.push(specifier);
|
|
59404
|
+
}
|
|
59405
|
+
return specifiers;
|
|
59406
|
+
}
|
|
59407
|
+
function resolveImportFile(fromFile, specifier) {
|
|
59408
|
+
const base = resolve3(join20(fromFile, ".."), specifier);
|
|
59409
|
+
const candidates = [
|
|
59410
|
+
base,
|
|
59411
|
+
...SOURCE_EXTENSIONS.map((ext) => `${base}${ext}`),
|
|
59412
|
+
...SOURCE_EXTENSIONS.map((ext) => join20(base, `index${ext}`))
|
|
59413
|
+
];
|
|
59414
|
+
return candidates.find((candidate) => existsSync18(candidate) && statSync4(candidate).isFile()) ?? null;
|
|
59415
|
+
}
|
|
59416
|
+
function extractPageActions(rootDir, sources, fixtureParams) {
|
|
59417
|
+
const actions = [];
|
|
59418
|
+
for (const source of sources) {
|
|
59419
|
+
actions.push(...extractLinkedActions(source, fixtureParams));
|
|
59420
|
+
actions.push(...extractButtonActions(source, fixtureParams));
|
|
59421
|
+
actions.push(...extractFormActions(source, fixtureParams));
|
|
59422
|
+
actions.push(...extractInputActions(source, fixtureParams));
|
|
59423
|
+
}
|
|
59424
|
+
const deduped = new Map;
|
|
59425
|
+
for (const action of actions) {
|
|
59426
|
+
const key = [
|
|
59427
|
+
action.kind,
|
|
59428
|
+
normalizeLabel(action.label),
|
|
59429
|
+
action.target ?? "",
|
|
59430
|
+
action.sourceFile
|
|
59431
|
+
].join("|");
|
|
59432
|
+
if (!deduped.has(key))
|
|
59433
|
+
deduped.set(key, action);
|
|
59434
|
+
}
|
|
59435
|
+
return [...deduped.values()].sort((a2, b2) => `${a2.kind}:${a2.label}:${a2.target ?? ""}`.localeCompare(`${b2.kind}:${b2.label}:${b2.target ?? ""}`)).slice(0, 40).map((action) => ({ ...action, sourceFile: relative4(rootDir, resolve3(rootDir, action.sourceFile)) }));
|
|
59436
|
+
}
|
|
59437
|
+
function extractLinkedActions(source, fixtureParams) {
|
|
59438
|
+
const actions = [];
|
|
59439
|
+
const pattern = /<(Link|a)\b([^>]*)>([\s\S]*?)<\/\1>/g;
|
|
59440
|
+
for (const match of source.source.matchAll(pattern)) {
|
|
59441
|
+
const attrs = match[2] ?? "";
|
|
59442
|
+
const body = match[3] ?? "";
|
|
59443
|
+
const href = attributeValue(attrs, "href");
|
|
59444
|
+
const label = firstNonEmpty(attributeValue(attrs, "aria-label"), attributeValue(attrs, "title"), textFromJsx(body), href);
|
|
59445
|
+
if (!label)
|
|
59446
|
+
continue;
|
|
59447
|
+
actions.push({
|
|
59448
|
+
kind: "link",
|
|
59449
|
+
label: clamp(label),
|
|
59450
|
+
target: href ? clamp(href, 180) : undefined,
|
|
59451
|
+
sourceFile: source.file,
|
|
59452
|
+
destructive: isDestructiveAction(label, href ?? ""),
|
|
59453
|
+
requiresFixture: requiresFixture(href ?? "", fixtureParams)
|
|
59454
|
+
});
|
|
59455
|
+
}
|
|
59456
|
+
return actions;
|
|
59457
|
+
}
|
|
59458
|
+
function extractButtonActions(source, fixtureParams) {
|
|
59459
|
+
const actions = [];
|
|
59460
|
+
const pattern = /<(button|Button|IconButton|DropdownMenuItem|CommandItem|SelectItem|TabsTrigger)\b([^>]*?)(?:>([\s\S]*?)<\/\1>|\/>)/g;
|
|
59461
|
+
for (const match of source.source.matchAll(pattern)) {
|
|
59462
|
+
const attrs = match[2] ?? "";
|
|
59463
|
+
const body = match[3] ?? "";
|
|
59464
|
+
const label = firstNonEmpty(attributeValue(attrs, "aria-label"), attributeValue(attrs, "title"), attributeValue(attrs, "data-testid"), textFromJsx(body), attributeValue(attrs, "value"));
|
|
59465
|
+
if (!label)
|
|
59466
|
+
continue;
|
|
59467
|
+
const target = attributeValue(attrs, "href") ?? attributeValue(attrs, "data-testid");
|
|
59468
|
+
actions.push({
|
|
59469
|
+
kind: "button",
|
|
59470
|
+
label: clamp(label),
|
|
59471
|
+
target: target ? clamp(target, 180) : undefined,
|
|
59472
|
+
sourceFile: source.file,
|
|
59473
|
+
destructive: isDestructiveAction(label, attrs),
|
|
59474
|
+
requiresFixture: requiresFixture(`${label} ${target ?? ""}`, fixtureParams)
|
|
59475
|
+
});
|
|
59476
|
+
}
|
|
59477
|
+
return actions;
|
|
59478
|
+
}
|
|
59479
|
+
function extractFormActions(source, fixtureParams) {
|
|
59480
|
+
const actions = [];
|
|
59481
|
+
const pattern = /<form\b([^>]*)>/g;
|
|
59482
|
+
for (const match of source.source.matchAll(pattern)) {
|
|
59483
|
+
const attrs = match[1] ?? "";
|
|
59484
|
+
const label = firstNonEmpty(attributeValue(attrs, "aria-label"), attributeValue(attrs, "name"), attributeValue(attrs, "data-testid"), attributeValue(attrs, "action"), `form in ${source.file}`);
|
|
59485
|
+
const target = attributeValue(attrs, "action");
|
|
59486
|
+
actions.push({
|
|
59487
|
+
kind: "form",
|
|
59488
|
+
label: clamp(label),
|
|
59489
|
+
target: target ? clamp(target, 180) : undefined,
|
|
59490
|
+
sourceFile: source.file,
|
|
59491
|
+
destructive: isDestructiveAction(label, attrs),
|
|
59492
|
+
requiresFixture: requiresFixture(`${label} ${target ?? ""}`, fixtureParams)
|
|
59493
|
+
});
|
|
59494
|
+
}
|
|
59495
|
+
return actions;
|
|
59496
|
+
}
|
|
59497
|
+
function extractInputActions(source, fixtureParams) {
|
|
59498
|
+
const actions = [];
|
|
59499
|
+
const pattern = /<(input|Input|Textarea|Select|Combobox)\b([^>]*?)(?:\/>|>)/g;
|
|
59500
|
+
for (const match of source.source.matchAll(pattern)) {
|
|
59501
|
+
const attrs = match[2] ?? "";
|
|
59502
|
+
const label = firstNonEmpty(attributeValue(attrs, "aria-label"), attributeValue(attrs, "placeholder"), attributeValue(attrs, "name"), attributeValue(attrs, "data-testid"));
|
|
59503
|
+
if (!label)
|
|
59504
|
+
continue;
|
|
59505
|
+
actions.push({
|
|
59506
|
+
kind: "input",
|
|
59507
|
+
label: clamp(label),
|
|
59508
|
+
target: attributeValue(attrs, "name") ?? attributeValue(attrs, "data-testid") ?? undefined,
|
|
59509
|
+
sourceFile: source.file,
|
|
59510
|
+
destructive: false,
|
|
59511
|
+
requiresFixture: requiresFixture(label, fixtureParams)
|
|
59512
|
+
});
|
|
59513
|
+
}
|
|
59514
|
+
return actions;
|
|
59515
|
+
}
|
|
59516
|
+
function attributeValue(attrs, name) {
|
|
59517
|
+
const pattern = new RegExp(`\\b${name}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|\\{\\s*["']([^"']+)["']\\s*\\})`, "i");
|
|
59518
|
+
const match = attrs.match(pattern);
|
|
59519
|
+
const value = match?.[1] ?? match?.[2] ?? match?.[3];
|
|
59520
|
+
return value?.trim() || undefined;
|
|
59521
|
+
}
|
|
59522
|
+
function textFromJsx(value) {
|
|
59523
|
+
const text = value.replace(/<[^>]+>/g, " ").replace(/\{[^}]*\}/g, " ").replace(/\s+/g, " ").trim();
|
|
59524
|
+
return text || undefined;
|
|
59525
|
+
}
|
|
59526
|
+
function firstNonEmpty(...values) {
|
|
59527
|
+
return values.find((value) => value && value.trim())?.trim() ?? "";
|
|
59528
|
+
}
|
|
59529
|
+
function clamp(value, max = 120) {
|
|
59530
|
+
const compact = value.replace(/\s+/g, " ").trim();
|
|
59531
|
+
return compact.length > max ? `${compact.slice(0, max - 1)}\u2026` : compact;
|
|
59532
|
+
}
|
|
59533
|
+
function normalizeLabel(value) {
|
|
59534
|
+
return value.toLowerCase().replace(/\s+/g, " ").trim();
|
|
59535
|
+
}
|
|
59536
|
+
function extractFixtureParams(routePath) {
|
|
59537
|
+
const params = new Set;
|
|
59538
|
+
for (const match of routePath.matchAll(/:([A-Za-z0-9_]+)(?:\*\??)?/g)) {
|
|
59539
|
+
if (match[1])
|
|
59540
|
+
params.add(match[1]);
|
|
59541
|
+
}
|
|
59542
|
+
return [...params];
|
|
59543
|
+
}
|
|
59544
|
+
function requiresFixture(value, fixtureParams) {
|
|
59545
|
+
if (fixtureParams.length === 0)
|
|
59546
|
+
return false;
|
|
59547
|
+
const normalized = value.toLowerCase();
|
|
59548
|
+
return fixtureParams.some((param) => normalized.includes(param.toLowerCase()) || normalized.includes(`[${param}]`));
|
|
59549
|
+
}
|
|
59550
|
+
function isDestructiveAction(label, context = "") {
|
|
59551
|
+
return /\b(delete|destroy|remove|revoke|refund|void|archive|suspend|pause|disable|cancel|reset|purge|terminate)\b/i.test(`${label} ${context}`);
|
|
59552
|
+
}
|
|
59553
|
+
function formatActionStep(action) {
|
|
59554
|
+
const target = action.target ? ` (${action.target})` : "";
|
|
59555
|
+
const fixture = action.requiresFixture ? " after binding fixture values" : "";
|
|
59556
|
+
const guard = action.destructive ? " without confirming the destructive final action" : "";
|
|
59557
|
+
if (action.kind === "api-method") {
|
|
59558
|
+
return `Exercise API method ${action.label}${target}${fixture}${guard}.`;
|
|
59559
|
+
}
|
|
59560
|
+
return `Exercise ${action.kind} "${action.label}"${target}${fixture}${guard}.`;
|
|
59561
|
+
}
|
|
59355
59562
|
function classifyRoute(routePath, groups, file) {
|
|
59356
59563
|
const haystack = `${routePath} ${groups.join(" ")} ${file}`.toLowerCase();
|
|
59357
59564
|
if (haystack.includes("admin"))
|
|
@@ -59416,7 +59623,7 @@ function priorityForRoute(routePath, category, kind) {
|
|
|
59416
59623
|
return "medium";
|
|
59417
59624
|
return "medium";
|
|
59418
59625
|
}
|
|
59419
|
-
var ROUTE_FILE_NAMES, WALK_EXCLUDES, SAFE_PAGE_ASSERTIONS;
|
|
59626
|
+
var ROUTE_FILE_NAMES, WALK_EXCLUDES, SAFE_PAGE_ASSERTIONS, IMPORT_SCAN_LIMIT = 40, IMPORT_SCAN_DEPTH = 3, SOURCE_EXTENSIONS;
|
|
59420
59627
|
var init_next_route_inventory = __esm(() => {
|
|
59421
59628
|
init_scenarios();
|
|
59422
59629
|
init_workflows();
|
|
@@ -59439,6 +59646,13 @@ var init_next_route_inventory = __esm(() => {
|
|
|
59439
59646
|
"coverage"
|
|
59440
59647
|
]);
|
|
59441
59648
|
SAFE_PAGE_ASSERTIONS = [{ type: "no_console_errors" }];
|
|
59649
|
+
SOURCE_EXTENSIONS = [
|
|
59650
|
+
".tsx",
|
|
59651
|
+
".ts",
|
|
59652
|
+
".jsx",
|
|
59653
|
+
".js",
|
|
59654
|
+
".mdx"
|
|
59655
|
+
];
|
|
59442
59656
|
});
|
|
59443
59657
|
|
|
59444
59658
|
// src/lib/generator.ts
|
|
@@ -94827,7 +95041,7 @@ import chalk6 from "chalk";
|
|
|
94827
95041
|
// package.json
|
|
94828
95042
|
var package_default = {
|
|
94829
95043
|
name: "@hasna/testers",
|
|
94830
|
-
version: "0.0.
|
|
95044
|
+
version: "0.0.50",
|
|
94831
95045
|
description: "AI-powered QA testing CLI \u2014 spawns cheap AI agents to test web apps with headless browsers",
|
|
94832
95046
|
type: "module",
|
|
94833
95047
|
main: "dist/index.js",
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import type { CreateScenarioInput, Scenario, ScenarioPriority, TestingWorkflow, WorkflowExecutionInput } from "../types/index.js";
|
|
2
2
|
export type NextRouteKind = "page" | "api";
|
|
3
|
+
export type NextRouteActionKind = "link" | "button" | "form" | "input" | "api-method";
|
|
4
|
+
export interface NextRouteAction {
|
|
5
|
+
kind: NextRouteActionKind;
|
|
6
|
+
label: string;
|
|
7
|
+
target?: string;
|
|
8
|
+
sourceFile: string;
|
|
9
|
+
destructive: boolean;
|
|
10
|
+
requiresFixture: boolean;
|
|
11
|
+
}
|
|
3
12
|
export interface NextRouteInventoryItem {
|
|
4
13
|
kind: NextRouteKind;
|
|
5
14
|
routePath: string;
|
|
@@ -9,6 +18,8 @@ export interface NextRouteInventoryItem {
|
|
|
9
18
|
methods: string[];
|
|
10
19
|
dynamic: boolean;
|
|
11
20
|
requiresAuth: boolean;
|
|
21
|
+
fixtureParams: string[];
|
|
22
|
+
actions: NextRouteAction[];
|
|
12
23
|
tags: string[];
|
|
13
24
|
priority: ScenarioPriority;
|
|
14
25
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"next-route-inventory.d.ts","sourceRoot":"","sources":["../../src/lib/next-route-inventory.ts"],"names":[],"mappings":"AAIA,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;
|
|
1
|
+
{"version":3,"file":"next-route-inventory.d.ts","sourceRoot":"","sources":["../../src/lib/next-route-inventory.ts"],"names":[],"mappings":"AAIA,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,CAwDrB;AAED,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,+BAA+B,GACvC,8BAA8B,CAuBhC"}
|
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.50",
|
|
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",
|
package/dist/server/index.js
CHANGED
|
@@ -46937,7 +46937,7 @@ import { join as join14 } from "path";
|
|
|
46937
46937
|
// package.json
|
|
46938
46938
|
var package_default = {
|
|
46939
46939
|
name: "@hasna/testers",
|
|
46940
|
-
version: "0.0.
|
|
46940
|
+
version: "0.0.50",
|
|
46941
46941
|
description: "AI-powered QA testing CLI \u2014 spawns cheap AI agents to test web apps with headless browsers",
|
|
46942
46942
|
type: "module",
|
|
46943
46943
|
main: "dist/index.js",
|