@hasna/testers 0.0.46 → 0.0.47
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 +70 -9
- package/dist/index.js +14 -1
- package/dist/lib/workflow-runner.d.ts.map +1 -1
- package/dist/mcp/index.js +16 -2
- package/dist/server/index.js +15 -2
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -27262,7 +27262,7 @@ async function runViaSandbox(plan, dependencies) {
|
|
|
27262
27262
|
workflowId: plan.workflow.id,
|
|
27263
27263
|
workflowName: plan.workflow.name
|
|
27264
27264
|
},
|
|
27265
|
-
sandboxEnvVars: plan.sandbox.env,
|
|
27265
|
+
sandboxEnvVars: resolveSandboxEnv(plan.sandbox.env),
|
|
27266
27266
|
cleanup: plan.sandbox.cleanup,
|
|
27267
27267
|
upload: {
|
|
27268
27268
|
localDir: bundle.localDir,
|
|
@@ -27288,6 +27288,19 @@ async function runViaSandbox(plan, dependencies) {
|
|
|
27288
27288
|
bundle.cleanup?.();
|
|
27289
27289
|
}
|
|
27290
27290
|
}
|
|
27291
|
+
function resolveSandboxEnv(env) {
|
|
27292
|
+
if (!env || Object.keys(env).length === 0)
|
|
27293
|
+
return;
|
|
27294
|
+
const resolved = {};
|
|
27295
|
+
for (const [key, value] of Object.entries(env)) {
|
|
27296
|
+
const resolvedValue = resolveCredential(value);
|
|
27297
|
+
if (resolvedValue === null) {
|
|
27298
|
+
throw new Error(`Missing sandbox env value for ${key}`);
|
|
27299
|
+
}
|
|
27300
|
+
resolved[key] = resolvedValue;
|
|
27301
|
+
}
|
|
27302
|
+
return resolved;
|
|
27303
|
+
}
|
|
27291
27304
|
async function resolveSandboxesRuntime(dependencies) {
|
|
27292
27305
|
if (dependencies.sandboxes)
|
|
27293
27306
|
return dependencies.sandboxes;
|
|
@@ -27305,6 +27318,7 @@ var init_workflow_runner = __esm(() => {
|
|
|
27305
27318
|
init_workflows();
|
|
27306
27319
|
init_personas();
|
|
27307
27320
|
init_runner();
|
|
27321
|
+
init_secrets_resolver();
|
|
27308
27322
|
APP_SOURCE_EXCLUDES = [
|
|
27309
27323
|
"node_modules",
|
|
27310
27324
|
".git",
|
|
@@ -94465,7 +94479,7 @@ import chalk6 from "chalk";
|
|
|
94465
94479
|
// package.json
|
|
94466
94480
|
var package_default = {
|
|
94467
94481
|
name: "@hasna/testers",
|
|
94468
|
-
version: "0.0.
|
|
94482
|
+
version: "0.0.47",
|
|
94469
94483
|
description: "AI-powered QA testing CLI \u2014 spawns cheap AI agents to test web apps with headless browsers",
|
|
94470
94484
|
type: "module",
|
|
94471
94485
|
main: "dist/index.js",
|
|
@@ -97365,6 +97379,29 @@ function validateStoredAssertion(value) {
|
|
|
97365
97379
|
}
|
|
97366
97380
|
return describeStoredAssertion(value);
|
|
97367
97381
|
}
|
|
97382
|
+
function envCredentialRef(value) {
|
|
97383
|
+
const trimmed = value?.trim();
|
|
97384
|
+
if (!trimmed)
|
|
97385
|
+
return;
|
|
97386
|
+
return trimmed.startsWith("$") ? trimmed : `$${trimmed}`;
|
|
97387
|
+
}
|
|
97388
|
+
function parseSandboxEnv(values) {
|
|
97389
|
+
if (!values?.length)
|
|
97390
|
+
return;
|
|
97391
|
+
const env = {};
|
|
97392
|
+
for (const value of values) {
|
|
97393
|
+
const trimmed = value.trim();
|
|
97394
|
+
if (!trimmed)
|
|
97395
|
+
continue;
|
|
97396
|
+
const separator = trimmed.indexOf("=");
|
|
97397
|
+
const key = separator >= 0 ? trimmed.slice(0, separator).trim() : trimmed;
|
|
97398
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
|
|
97399
|
+
throw new Error(`Invalid sandbox env var name: ${key || value}`);
|
|
97400
|
+
}
|
|
97401
|
+
env[key] = separator >= 0 ? trimmed.slice(separator + 1) : `$${key}`;
|
|
97402
|
+
}
|
|
97403
|
+
return Object.keys(env).length > 0 ? env : undefined;
|
|
97404
|
+
}
|
|
97368
97405
|
function AddForm({ onComplete }) {
|
|
97369
97406
|
const { exit } = useApp();
|
|
97370
97407
|
const [state, setState] = useState({
|
|
@@ -97669,12 +97706,12 @@ program2.command("add [name]").alias("create").description("Create a new test sc
|
|
|
97669
97706
|
}, []).option("-t, --tag <tag>", "Tag (repeatable)", (val, acc) => {
|
|
97670
97707
|
acc.push(val);
|
|
97671
97708
|
return acc;
|
|
97672
|
-
}, []).option("-p, --priority <level>", "Priority level", "medium").option("-m, --model <model>", "AI model to use").option("--path <path>", "Target path on the URL").option("--auth", "Requires authentication", false).option("--timeout <ms>", "Timeout in milliseconds").option("--project <id>", "Project ID").option("--template <name>", "Seed scenarios from a template (auth, crud, forms, nav, a11y)").option("--assert <assertion>", "Structured assertion (repeatable). Formats: selector:<sel> visible, text:<sel> contains:<text>, no-console-errors, url:contains:<path>, title:contains:<text>, count:<sel> eq:<n>", (val, acc) => {
|
|
97709
|
+
}, []).option("-p, --priority <level>", "Priority level", "medium").option("-m, --model <model>", "AI model to use").option("--path <path>", "Target path on the URL").option("--auth", "Requires authentication", false).option("--auth-preset <name>", "Attach email/password/loginPath from a named auth preset").option("--timeout <ms>", "Timeout in milliseconds").option("--project <id>", "Project ID").option("--template <name>", "Seed scenarios from a template (auth, crud, forms, nav, a11y)").option("--assert <assertion>", "Structured assertion (repeatable). Formats: selector:<sel> visible, text:<sel> contains:<text>, no-console-errors, url:contains:<path>, title:contains:<text>, count:<sel> eq:<n>", (val, acc) => {
|
|
97673
97710
|
acc.push(val);
|
|
97674
97711
|
return acc;
|
|
97675
97712
|
}, []).action(async (name21, opts) => {
|
|
97676
97713
|
try {
|
|
97677
|
-
const hasFlags = opts.description || opts.steps?.length || opts.tag?.length || opts.model || opts.path || opts.auth || opts.timeout || opts.template || opts.assert?.length;
|
|
97714
|
+
const hasFlags = opts.description || opts.steps?.length || opts.tag?.length || opts.model || opts.path || opts.auth || opts.authPreset || opts.timeout || opts.template || opts.assert?.length;
|
|
97678
97715
|
if (!name21 && !hasFlags) {
|
|
97679
97716
|
const projectId2 = resolveProject2(opts.project);
|
|
97680
97717
|
await runInteractiveAdd(projectId2);
|
|
@@ -97699,6 +97736,11 @@ program2.command("add [name]").alias("create").description("Create a new test sc
|
|
|
97699
97736
|
}
|
|
97700
97737
|
const assertions = opts.assert.map(parseAssertionString);
|
|
97701
97738
|
const projectId = resolveProject2(opts.project);
|
|
97739
|
+
const authPreset = opts.authPreset ? getAuthPreset(opts.authPreset) : null;
|
|
97740
|
+
if (opts.authPreset && !authPreset) {
|
|
97741
|
+
logError(chalk6.red(`Auth preset not found: ${opts.authPreset}`));
|
|
97742
|
+
process.exit(1);
|
|
97743
|
+
}
|
|
97702
97744
|
const scenario = createScenario({
|
|
97703
97745
|
name: name21,
|
|
97704
97746
|
description: opts.description || name21,
|
|
@@ -97707,7 +97749,12 @@ program2.command("add [name]").alias("create").description("Create a new test sc
|
|
|
97707
97749
|
priority: opts.priority,
|
|
97708
97750
|
model: opts.model,
|
|
97709
97751
|
targetPath: opts.path,
|
|
97710
|
-
requiresAuth: opts.auth,
|
|
97752
|
+
requiresAuth: opts.auth || Boolean(authPreset),
|
|
97753
|
+
authConfig: authPreset ? {
|
|
97754
|
+
email: authPreset.email,
|
|
97755
|
+
password: authPreset.password,
|
|
97756
|
+
loginPath: authPreset.loginPath
|
|
97757
|
+
} : undefined,
|
|
97711
97758
|
timeoutMs: opts.timeout ? parseInt(opts.timeout, 10) : undefined,
|
|
97712
97759
|
assertions: assertions.length > 0 ? assertions : undefined,
|
|
97713
97760
|
projectId
|
|
@@ -99701,12 +99748,22 @@ program2.command("report [run-id]").description("Generate HTML test report or co
|
|
|
99701
99748
|
}
|
|
99702
99749
|
});
|
|
99703
99750
|
var authCmd = program2.command("auth").description("Manage auth presets");
|
|
99704
|
-
authCmd.command("add <name>").description("Create an auth preset").
|
|
99751
|
+
authCmd.command("add <name>").description("Create an auth preset").option("--email <email>", "Login email or credential reference").option("--password <password>", "Login password or credential reference").option("--email-env <name>", "Environment variable name for the login email").option("--password-env <name>", "Environment variable name for the login password").option("--login-path <path>", "Login page path", "/login").action((name21, opts) => {
|
|
99705
99752
|
try {
|
|
99753
|
+
const email3 = opts.email ?? envCredentialRef(opts.emailEnv);
|
|
99754
|
+
const password = opts.password ?? envCredentialRef(opts.passwordEnv);
|
|
99755
|
+
if (!email3) {
|
|
99756
|
+
logError(chalk6.red("Error: provide --email or --email-env"));
|
|
99757
|
+
process.exit(1);
|
|
99758
|
+
}
|
|
99759
|
+
if (!password) {
|
|
99760
|
+
logError(chalk6.red("Error: provide --password or --password-env"));
|
|
99761
|
+
process.exit(1);
|
|
99762
|
+
}
|
|
99706
99763
|
const preset = createAuthPreset({
|
|
99707
99764
|
name: name21,
|
|
99708
|
-
email:
|
|
99709
|
-
password
|
|
99765
|
+
email: email3,
|
|
99766
|
+
password,
|
|
99710
99767
|
loginPath: opts.loginPath
|
|
99711
99768
|
});
|
|
99712
99769
|
log(chalk6.green(`Created auth preset ${chalk6.bold(preset.name)} (${preset.email})`));
|
|
@@ -100968,7 +101025,10 @@ workflowCmd.command("create <name>").description("Save a reusable testing workfl
|
|
|
100968
101025
|
}, []).option("--priority <level>", "Scenario priority").option("--persona <ids>", "Comma-separated persona IDs").option("--goal <prompt>", "Goal prompt for the agentic testing loop").option("--success <criteria>", "Success criteria (repeatable)", (val, acc) => {
|
|
100969
101026
|
acc.push(val);
|
|
100970
101027
|
return acc;
|
|
100971
|
-
}, []).option("--max-iterations <n>", "Goal-loop iteration cap", "10").option("--target <target>", "Execution target: local or sandbox", "local").option("--sandbox-provider <provider>", "Sandbox provider: e2b, daytona, or modal").option("--sandbox-image <image>", "Sandbox image/template").option("--sandbox-remote-dir <path>", "Remote working directory for sandbox runs").option("--sandbox-cleanup <mode>", "Sandbox cleanup mode: delete, stop, or keep", "delete").option("--sandbox-sync <strategy>", "Sandbox upload sync strategy: rsync or archive", "rsync").option("--sandbox-setup-command <command>", "Shell command to run before testers in the sandbox").option("--sandbox-package <spec>", "Package spec to execute in the sandbox", "@hasna/testers").option("--sandbox-
|
|
101028
|
+
}, []).option("--max-iterations <n>", "Goal-loop iteration cap", "10").option("--target <target>", "Execution target: local or sandbox", "local").option("--sandbox-provider <provider>", "Sandbox provider: e2b, daytona, or modal").option("--sandbox-image <image>", "Sandbox image/template").option("--sandbox-remote-dir <path>", "Remote working directory for sandbox runs").option("--sandbox-cleanup <mode>", "Sandbox cleanup mode: delete, stop, or keep", "delete").option("--sandbox-sync <strategy>", "Sandbox upload sync strategy: rsync or archive", "rsync").option("--sandbox-setup-command <command>", "Shell command to run before testers in the sandbox").option("--sandbox-package <spec>", "Package spec to execute in the sandbox", "@hasna/testers").option("--sandbox-env <assignment>", "Sandbox env var; KEY forwards host KEY, KEY=value stores value (repeatable)", (val, acc) => {
|
|
101029
|
+
acc.push(val);
|
|
101030
|
+
return acc;
|
|
101031
|
+
}, []).option("--sandbox-app-source <path>", "Local app source directory to upload into the sandbox").option("--sandbox-app-remote-dir <path>", "Remote app directory inside the sandbox (default: <sandbox-remote-dir>/app)").option("--sandbox-app-start-command <command>", "Shell command to start the app before testers runs").option("--sandbox-app-url <url>", "URL testers should target inside the sandbox after the app starts").option("--sandbox-app-wait-url <url>", "URL to poll before starting testers (defaults to --sandbox-app-url)").option("--sandbox-app-wait-timeout <ms>", "App readiness wait timeout in milliseconds").option("--e2b-template <name>", "Legacy alias for --sandbox-image").option("--timeout <ms>", "Workflow timeout").option("--json", "Output as JSON", false).action((name21, opts) => {
|
|
100972
101032
|
try {
|
|
100973
101033
|
const workflow = createTestingWorkflow({
|
|
100974
101034
|
name: name21,
|
|
@@ -100994,6 +101054,7 @@ workflowCmd.command("create <name>").description("Save a reusable testing workfl
|
|
|
100994
101054
|
sandboxSyncStrategy: opts.sandboxSync,
|
|
100995
101055
|
setupCommand: opts.sandboxSetupCommand,
|
|
100996
101056
|
packageSpec: opts.sandboxPackage,
|
|
101057
|
+
env: parseSandboxEnv(opts.sandboxEnv),
|
|
100997
101058
|
appSourceDir: opts.sandboxAppSource,
|
|
100998
101059
|
appRemoteDir: opts.sandboxAppRemoteDir,
|
|
100999
101060
|
appStartCommand: opts.sandboxAppStartCommand,
|
package/dist/index.js
CHANGED
|
@@ -17445,7 +17445,7 @@ async function runViaSandbox(plan, dependencies) {
|
|
|
17445
17445
|
workflowId: plan.workflow.id,
|
|
17446
17446
|
workflowName: plan.workflow.name
|
|
17447
17447
|
},
|
|
17448
|
-
sandboxEnvVars: plan.sandbox.env,
|
|
17448
|
+
sandboxEnvVars: resolveSandboxEnv(plan.sandbox.env),
|
|
17449
17449
|
cleanup: plan.sandbox.cleanup,
|
|
17450
17450
|
upload: {
|
|
17451
17451
|
localDir: bundle.localDir,
|
|
@@ -17471,6 +17471,19 @@ async function runViaSandbox(plan, dependencies) {
|
|
|
17471
17471
|
bundle.cleanup?.();
|
|
17472
17472
|
}
|
|
17473
17473
|
}
|
|
17474
|
+
function resolveSandboxEnv(env2) {
|
|
17475
|
+
if (!env2 || Object.keys(env2).length === 0)
|
|
17476
|
+
return;
|
|
17477
|
+
const resolved = {};
|
|
17478
|
+
for (const [key, value] of Object.entries(env2)) {
|
|
17479
|
+
const resolvedValue = resolveCredential(value);
|
|
17480
|
+
if (resolvedValue === null) {
|
|
17481
|
+
throw new Error(`Missing sandbox env value for ${key}`);
|
|
17482
|
+
}
|
|
17483
|
+
resolved[key] = resolvedValue;
|
|
17484
|
+
}
|
|
17485
|
+
return resolved;
|
|
17486
|
+
}
|
|
17474
17487
|
async function resolveSandboxesRuntime(dependencies) {
|
|
17475
17488
|
if (dependencies.sandboxes)
|
|
17476
17489
|
return dependencies.sandboxes;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow-runner.d.ts","sourceRoot":"","sources":["../../src/lib/workflow-runner.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"workflow-runner.d.ts","sourceRoot":"","sources":["../../src/lib/workflow-runner.ts"],"names":[],"mappings":"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"}
|
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.47",
|
|
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",
|
|
@@ -23723,7 +23723,7 @@ async function runViaSandbox(plan, dependencies) {
|
|
|
23723
23723
|
workflowId: plan.workflow.id,
|
|
23724
23724
|
workflowName: plan.workflow.name
|
|
23725
23725
|
},
|
|
23726
|
-
sandboxEnvVars: plan.sandbox.env,
|
|
23726
|
+
sandboxEnvVars: resolveSandboxEnv(plan.sandbox.env),
|
|
23727
23727
|
cleanup: plan.sandbox.cleanup,
|
|
23728
23728
|
upload: {
|
|
23729
23729
|
localDir: bundle.localDir,
|
|
@@ -23749,6 +23749,19 @@ async function runViaSandbox(plan, dependencies) {
|
|
|
23749
23749
|
bundle.cleanup?.();
|
|
23750
23750
|
}
|
|
23751
23751
|
}
|
|
23752
|
+
function resolveSandboxEnv(env2) {
|
|
23753
|
+
if (!env2 || Object.keys(env2).length === 0)
|
|
23754
|
+
return;
|
|
23755
|
+
const resolved = {};
|
|
23756
|
+
for (const [key, value] of Object.entries(env2)) {
|
|
23757
|
+
const resolvedValue = resolveCredential(value);
|
|
23758
|
+
if (resolvedValue === null) {
|
|
23759
|
+
throw new Error(`Missing sandbox env value for ${key}`);
|
|
23760
|
+
}
|
|
23761
|
+
resolved[key] = resolvedValue;
|
|
23762
|
+
}
|
|
23763
|
+
return resolved;
|
|
23764
|
+
}
|
|
23752
23765
|
async function resolveSandboxesRuntime(dependencies) {
|
|
23753
23766
|
if (dependencies.sandboxes)
|
|
23754
23767
|
return dependencies.sandboxes;
|
|
@@ -23766,6 +23779,7 @@ var init_workflow_runner = __esm(() => {
|
|
|
23766
23779
|
init_workflows();
|
|
23767
23780
|
init_personas();
|
|
23768
23781
|
init_runner();
|
|
23782
|
+
init_secrets_resolver();
|
|
23769
23783
|
APP_SOURCE_EXCLUDES = [
|
|
23770
23784
|
"node_modules",
|
|
23771
23785
|
".git",
|
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.47",
|
|
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",
|
|
@@ -51561,7 +51561,7 @@ async function runViaSandbox(plan, dependencies) {
|
|
|
51561
51561
|
workflowId: plan.workflow.id,
|
|
51562
51562
|
workflowName: plan.workflow.name
|
|
51563
51563
|
},
|
|
51564
|
-
sandboxEnvVars: plan.sandbox.env,
|
|
51564
|
+
sandboxEnvVars: resolveSandboxEnv(plan.sandbox.env),
|
|
51565
51565
|
cleanup: plan.sandbox.cleanup,
|
|
51566
51566
|
upload: {
|
|
51567
51567
|
localDir: bundle.localDir,
|
|
@@ -51587,6 +51587,19 @@ async function runViaSandbox(plan, dependencies) {
|
|
|
51587
51587
|
bundle.cleanup?.();
|
|
51588
51588
|
}
|
|
51589
51589
|
}
|
|
51590
|
+
function resolveSandboxEnv(env) {
|
|
51591
|
+
if (!env || Object.keys(env).length === 0)
|
|
51592
|
+
return;
|
|
51593
|
+
const resolved = {};
|
|
51594
|
+
for (const [key, value] of Object.entries(env)) {
|
|
51595
|
+
const resolvedValue = resolveCredential(value);
|
|
51596
|
+
if (resolvedValue === null) {
|
|
51597
|
+
throw new Error(`Missing sandbox env value for ${key}`);
|
|
51598
|
+
}
|
|
51599
|
+
resolved[key] = resolvedValue;
|
|
51600
|
+
}
|
|
51601
|
+
return resolved;
|
|
51602
|
+
}
|
|
51590
51603
|
async function resolveSandboxesRuntime(dependencies) {
|
|
51591
51604
|
if (dependencies.sandboxes)
|
|
51592
51605
|
return dependencies.sandboxes;
|