pi-harness-runtime 1.1.180 → 1.1.182

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.
Files changed (114) hide show
  1. package/harness/e2e-commands.ts +216 -0
  2. package/harness/honcho-memory.ts +84 -0
  3. package/index.ts +9 -0
  4. package/package.json +1 -1
  5. package/packages/a2a-adapter/package.json +1 -1
  6. package/packages/agent-policy/package.json +1 -1
  7. package/packages/architecture-generator/package.json +1 -1
  8. package/packages/auth/package.json +1 -1
  9. package/packages/autonomous-refactor/package.json +1 -1
  10. package/packages/autonomous-runtime/package.json +1 -1
  11. package/packages/cache-strategy/package.json +1 -1
  12. package/packages/capability-registry/package.json +1 -1
  13. package/packages/checkpoint/package.json +1 -1
  14. package/packages/cli-plugin-sdk/package.json +1 -1
  15. package/packages/clipboard/package.json +1 -1
  16. package/packages/clipboard-plugin/package.json +1 -1
  17. package/packages/code-generation/package.json +1 -1
  18. package/packages/code-review/package.json +1 -1
  19. package/packages/codex-adapter/package.json +1 -1
  20. package/packages/codex-watcher/package.json +1 -1
  21. package/packages/config-capture/package.json +1 -1
  22. package/packages/context-compiler/package.json +1 -1
  23. package/packages/context-discovery/package.json +1 -1
  24. package/packages/context-manager/package.json +1 -1
  25. package/packages/cookie-sanitizer/package.json +1 -1
  26. package/packages/cost-optimizer/package.json +1 -1
  27. package/packages/dependency-analyzer/package.json +1 -1
  28. package/packages/django-plugin/package.json +1 -1
  29. package/packages/doc-generator/package.json +1 -1
  30. package/packages/e2e-tester/README.md +142 -0
  31. package/packages/e2e-tester/dist/index.d.ts +133 -0
  32. package/packages/e2e-tester/dist/index.js +380 -0
  33. package/packages/e2e-tester/dist/types.d.ts +91 -0
  34. package/packages/e2e-tester/dist/types.js +17 -0
  35. package/packages/e2e-tester/package.json +31 -0
  36. package/packages/e2e-tester/src/index.ts +488 -0
  37. package/packages/e2e-tester/src/types.ts +95 -0
  38. package/packages/evaluation-engine/package.json +1 -1
  39. package/packages/evaluation-runner/package.json +1 -1
  40. package/packages/event-bus/package.json +1 -1
  41. package/packages/event-store/package.json +1 -1
  42. package/packages/experience-replay/package.json +1 -1
  43. package/packages/feedback-collector/package.json +1 -1
  44. package/packages/file-copy-helper/package.json +1 -1
  45. package/packages/framework-detector/package.json +1 -1
  46. package/packages/framework-plugin-sdk/package.json +1 -1
  47. package/packages/frappe-plugin/package.json +1 -1
  48. package/packages/generic-web-plugin/package.json +1 -1
  49. package/packages/health-monitor/package.json +1 -1
  50. package/packages/intent-analyzer/package.json +1 -1
  51. package/packages/jev-judge/package.json +1 -1
  52. package/packages/knowledge-graph/package.json +1 -1
  53. package/packages/knowledge-retrieval/package.json +1 -1
  54. package/packages/laravel-plugin/package.json +1 -1
  55. package/packages/learning-engine/package.json +1 -1
  56. package/packages/mcp-adapter/package.json +1 -1
  57. package/packages/memory-engine/package.json +1 -1
  58. package/packages/milestone-manager/package.json +1 -1
  59. package/packages/model-registry/package.json +1 -1
  60. package/packages/nextjs-plugin/package.json +1 -1
  61. package/packages/notification/package.json +1 -1
  62. package/packages/observability/package.json +1 -1
  63. package/packages/okf-indexer/package.json +1 -1
  64. package/packages/paperclip-integration/package.json +1 -1
  65. package/packages/performance-optimizer/package.json +1 -1
  66. package/packages/privilege-broker/package.json +1 -1
  67. package/packages/project-analyzer/package.json +1 -1
  68. package/packages/project-bootstrap/package.json +1 -1
  69. package/packages/projection-engine/package.json +1 -1
  70. package/packages/prompt-compiler/package.json +1 -1
  71. package/packages/prompt-versioning/package.json +1 -1
  72. package/packages/provider-adapter-sdk/package.json +1 -1
  73. package/packages/provider-router/package.json +1 -1
  74. package/packages/provider-selector/package.json +1 -1
  75. package/packages/providers/package.json +1 -1
  76. package/packages/qdrant-skills/package.json +1 -1
  77. package/packages/quota-manager/package.json +1 -1
  78. package/packages/rate-limiter/package.json +1 -1
  79. package/packages/react-vite-plugin/package.json +1 -1
  80. package/packages/release-manager/package.json +1 -1
  81. package/packages/requirement-compiler/package.json +1 -1
  82. package/packages/runtime/package.json +1 -1
  83. package/packages/scheduler/package.json +1 -1
  84. package/packages/scheduler-adapter/package.json +1 -1
  85. package/packages/session/package.json +1 -1
  86. package/packages/session-api/package.json +1 -1
  87. package/packages/session-export/package.json +1 -1
  88. package/packages/shared-context/package.json +1 -1
  89. package/packages/skill-mcp-client/package.json +1 -1
  90. package/packages/skill-registry/package.json +1 -1
  91. package/packages/skills/package.json +1 -1
  92. package/packages/sprint-planner/package.json +1 -1
  93. package/packages/subscription-engine/package.json +1 -1
  94. package/packages/task-compiler/package.json +1 -1
  95. package/packages/telegram/package.json +1 -1
  96. package/packages/tencentdb-memory/package.json +1 -1
  97. package/packages/tencentdb-sync/package.json +1 -1
  98. package/packages/test-data-generator/package.json +1 -1
  99. package/packages/test-generator/package.json +1 -1
  100. package/packages/todo-bd-sync/package.json +1 -1
  101. package/packages/token-estimation/package.json +1 -1
  102. package/packages/token-optimizer/package.json +1 -1
  103. package/packages/trajectory/package.json +1 -1
  104. package/packages/tui/package.json +1 -1
  105. package/packages/types/package.json +1 -1
  106. package/packages/webmcp-tester/dist/index.d.ts +178 -0
  107. package/packages/webmcp-tester/dist/index.js +412 -0
  108. package/packages/webmcp-tester/package.json +27 -0
  109. package/packages/webmcp-tester/src/index.ts +571 -0
  110. package/packages/workflow-events/package.json +1 -1
  111. package/packages/workspace-scanner/package.json +1 -1
  112. package/packages/worktree/package.json +1 -1
  113. package/packages/write-review/package.json +1 -1
  114. package/scripts/run-e2e-test.ts +214 -0
@@ -0,0 +1,142 @@
1
+ # E2E Tester - Agent-Friendly Testing
2
+
3
+ ## Purpose
4
+ Simple E2E testing that agents can reliably use without confusion.
5
+
6
+ ## Key Rules for Agents
7
+
8
+ ### ALWAYS RUN LOCALLY
9
+ ```
10
+ ⚠️ IMPORTANT: E2E tests MUST run on LOCAL machine (your laptop/desktop), NOT on remote servers.
11
+ - Remote servers typically have no browser installed
12
+ - Use ctx_execute to run tests locally
13
+ - Never try to run Playwright on SSH to a server
14
+ ```
15
+
16
+ ### Test Location
17
+ ```
18
+ Local machine: /home/frappe/amos-saas/ (or wherever the app is)
19
+ Remote server: SSH frappe@217.216.37.251 (NO BROWSER - DO NOT RUN TESTS HERE)
20
+ ```
21
+
22
+ ## Quick Start
23
+
24
+ ```typescript
25
+ import { E2ETester } from "@pi-harness/e2e-tester";
26
+
27
+ const tester = new E2ETester({
28
+ browser: "chromium", // chromium, firefox, webkit
29
+ headless: true, // true for CI, false for debugging
30
+ });
31
+
32
+ await tester.start();
33
+
34
+ try {
35
+ // Login first
36
+ await tester.login("https://your-app.com/login", {
37
+ email: "test@example.com",
38
+ password: "TestPass123!",
39
+ });
40
+
41
+ // Navigate and test
42
+ await tester.goto("https://your-app.com/dashboard");
43
+ await tester.click("Submit Button");
44
+ await tester.fill("Email", "user@example.com");
45
+ await tester.assertVisible("Welcome");
46
+
47
+ } finally {
48
+ await tester.stop();
49
+ }
50
+ ```
51
+
52
+ ## Natural Language Test Format
53
+
54
+ Create test files like `test-register.md`:
55
+
56
+ ```markdown
57
+ # Registration Flow Test
58
+
59
+ URL: https://your-app.com/register
60
+
61
+ Auth:
62
+ email: test@example.com
63
+ password: TestPass123!
64
+
65
+ Steps:
66
+ 1. Fill email with "test@example.com"
67
+ 2. Fill password with "TestPass123!"
68
+ 3. Click Submit button
69
+ 4. Wait 2 seconds
70
+ 5. Assert "Welcome" message is visible
71
+ 6. Assert URL contains "/dashboard"
72
+
73
+ Expected:
74
+ - Success message appears
75
+ - Redirects to dashboard
76
+ - No console errors
77
+ ```
78
+
79
+ ## Running Tests
80
+
81
+ ```bash
82
+ # Run single test
83
+ bun scripts/run-e2e-test.ts --scenario test-register.md
84
+
85
+ # Run all tests
86
+ bun scripts/run-e2e-test.ts --scenario ./tests/
87
+
88
+ # With screenshots
89
+ bun scripts/run-e2e-test.ts --scenario test.md --screenshots --output ./test-output
90
+ ```
91
+
92
+ ## Troubleshooting
93
+
94
+ ### "Cannot find browser"
95
+ ```bash
96
+ # Install browsers
97
+ npx playwright install chromium
98
+ ```
99
+
100
+ ### "Login failed"
101
+ ```typescript
102
+ // Check if login form selectors are correct
103
+ await tester.debug(); // Opens browser in non-headless mode
104
+
105
+ // Or provide custom selectors
106
+ await tester.login(url, {
107
+ selectors: {
108
+ email: "#email-input",
109
+ password: "[name='password']",
110
+ submit: "button[type='submit']",
111
+ }
112
+ });
113
+ ```
114
+
115
+ ### "Test running on server"
116
+ ```
117
+ STOP! You are trying to run E2E tests on a remote server.
118
+ E2E tests MUST run locally where a browser is available.
119
+
120
+ Use ctx_execute to run tests on your LOCAL machine.
121
+ ```
122
+
123
+ ## Architecture
124
+
125
+ ```
126
+ ┌─────────────────┐
127
+ │ Test Scenario │ ← Markdown or TypeScript
128
+ │ (Natural) │
129
+ └────────┬────────┘
130
+ │
131
+ ▼
132
+ ┌─────────────────┐
133
+ │ E2E Tester │ ← Parses and executes
134
+ │ (Playwright) │
135
+ └────────┬────────┘
136
+ │
137
+ ▼
138
+ ┌─────────────────┐
139
+ │ Local Browser │ ← Chromium/Firefox/WebKit
140
+ │ (Your Machine) │
141
+ └─────────────────┘
142
+ ```
@@ -0,0 +1,133 @@
1
+ /**
2
+ * WebMCP E2E Tester
3
+ *
4
+ * Natural language E2E testing with WebMCP protocol.
5
+ * Works with: https://github.com/GoogleChromeLabs/webmcp-extension/
6
+ *
7
+ * Usage:
8
+ * ```typescript
9
+ * import { WebMCPTester } from "@pi-harness/e2e-tester";
10
+ *
11
+ * const tester = new WebMCPTester();
12
+ * const result = await tester.run({
13
+ * name: "Registration Flow",
14
+ * url: "https://amos-saas.example.com/register",
15
+ * steps: [
16
+ * { action: "fill", field: "email", value: "test@example.com" },
17
+ * { action: "click", target: "Submit" },
18
+ * { action: "assert", expected: "Welcome" },
19
+ * ],
20
+ * });
21
+ * ```
22
+ */
23
+ export * from "./types.js";
24
+ export interface TestStep {
25
+ action: "navigate" | "fill" | "click" | "submit" | "assert" | "wait" | "screenshot" | "select";
26
+ target?: string;
27
+ field?: string;
28
+ value?: string;
29
+ expected?: string;
30
+ waitMs?: number;
31
+ screenshot?: boolean;
32
+ }
33
+ export interface TestScenario {
34
+ name: string;
35
+ url: string;
36
+ auth?: {
37
+ type: "form" | "cookie" | "token";
38
+ username?: string;
39
+ password?: string;
40
+ email?: string;
41
+ selectors?: {
42
+ username?: string;
43
+ email?: string;
44
+ password?: string;
45
+ submit?: string;
46
+ };
47
+ };
48
+ steps: TestStep[];
49
+ data?: Record<string, string>;
50
+ screenshotOnFailure?: boolean;
51
+ }
52
+ export interface TestResult {
53
+ name: string;
54
+ passed: boolean;
55
+ error?: string;
56
+ screenshots: string[];
57
+ stepsExecuted: number;
58
+ durationMs: number;
59
+ consoleLogs: Array<{
60
+ type: "log" | "warn" | "error" | "info";
61
+ message: string;
62
+ timestamp: number;
63
+ }>;
64
+ }
65
+ export interface WebMCPTesterConfig {
66
+ chromePath?: string;
67
+ port?: number;
68
+ headless?: boolean;
69
+ extensionPath?: string;
70
+ timeoutMs?: number;
71
+ screenshotDir?: string;
72
+ }
73
+ /**
74
+ * WebMCP Tester
75
+ *
76
+ * Provides structured E2E testing that agents can reliably execute.
77
+ *
78
+ * Key principles:
79
+ * 1. Clear, simple API - no confusion about what to call
80
+ * 2. Built-in auth helpers - login without getting stuck
81
+ * 3. Natural language steps - agents understand what to do
82
+ * 4. Detailed results - easy to debug failures
83
+ */
84
+ export declare class WebMCPTester {
85
+ private config;
86
+ constructor(config?: WebMCPTesterConfig);
87
+ /**
88
+ * Find Chrome executable
89
+ */
90
+ private findChrome;
91
+ /**
92
+ * Run a test scenario
93
+ */
94
+ run(scenario: TestScenario): Promise<TestResult>;
95
+ /**
96
+ * Handle authentication
97
+ */
98
+ private handleAuth;
99
+ /**
100
+ * Login to a site
101
+ */
102
+ login(url: string, credentials: {
103
+ username?: string;
104
+ email?: string;
105
+ password: string;
106
+ selectors?: {
107
+ username?: string;
108
+ email?: string;
109
+ password?: string;
110
+ submit?: string;
111
+ };
112
+ }): Promise<void>;
113
+ /**
114
+ * Execute a single test step
115
+ */
116
+ private executeStep;
117
+ /**
118
+ * Take a screenshot
119
+ */
120
+ private takeScreenshot;
121
+ /**
122
+ * Delay helper
123
+ */
124
+ private delay;
125
+ /**
126
+ * Get test instructions for agent
127
+ */
128
+ static getInstructions(): string;
129
+ }
130
+ /**
131
+ * Parse natural language test description
132
+ */
133
+ export declare function parseTestDescription(description: string): TestScenario;
@@ -0,0 +1,380 @@
1
+ /**
2
+ * WebMCP E2E Tester
3
+ *
4
+ * Natural language E2E testing with WebMCP protocol.
5
+ * Works with: https://github.com/GoogleChromeLabs/webmcp-extension/
6
+ *
7
+ * Usage:
8
+ * ```typescript
9
+ * import { WebMCPTester } from "@pi-harness/e2e-tester";
10
+ *
11
+ * const tester = new WebMCPTester();
12
+ * const result = await tester.run({
13
+ * name: "Registration Flow",
14
+ * url: "https://amos-saas.example.com/register",
15
+ * steps: [
16
+ * { action: "fill", field: "email", value: "test@example.com" },
17
+ * { action: "click", target: "Submit" },
18
+ * { action: "assert", expected: "Welcome" },
19
+ * ],
20
+ * });
21
+ * ```
22
+ */
23
+ import { homedir } from "os";
24
+ import { join } from "path";
25
+ import { existsSync, mkdirSync } from "fs";
26
+ export * from "./types.js";
27
+ const DEFAULT_CONFIG = {
28
+ port: 9222,
29
+ headless: true,
30
+ timeoutMs: 60000,
31
+ screenshotDir: join(homedir(), ".pi-harness-runtime", "test-screenshots"),
32
+ };
33
+ /**
34
+ * WebMCP Tester
35
+ *
36
+ * Provides structured E2E testing that agents can reliably execute.
37
+ *
38
+ * Key principles:
39
+ * 1. Clear, simple API - no confusion about what to call
40
+ * 2. Built-in auth helpers - login without getting stuck
41
+ * 3. Natural language steps - agents understand what to do
42
+ * 4. Detailed results - easy to debug failures
43
+ */
44
+ export class WebMCPTester {
45
+ config;
46
+ constructor(config = {}) {
47
+ this.config = {
48
+ chromePath: config.chromePath || this.findChrome(),
49
+ port: config.port ?? DEFAULT_CONFIG.port,
50
+ headless: config.headless ?? DEFAULT_CONFIG.headless,
51
+ extensionPath: config.extensionPath || "",
52
+ timeoutMs: config.timeoutMs ?? DEFAULT_CONFIG.timeoutMs,
53
+ screenshotDir: config.screenshotDir ?? DEFAULT_CONFIG.screenshotDir,
54
+ };
55
+ // Ensure screenshot directory exists
56
+ if (!existsSync(this.config.screenshotDir)) {
57
+ mkdirSync(this.config.screenshotDir, { recursive: true });
58
+ }
59
+ }
60
+ /**
61
+ * Find Chrome executable
62
+ */
63
+ findChrome() {
64
+ const paths = [
65
+ "/usr/bin/google-chrome",
66
+ "/usr/bin/chromium",
67
+ "/usr/bin/chromium-browser",
68
+ "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
69
+ process.env.CHROME_PATH,
70
+ ].filter(Boolean);
71
+ return paths.find(p => existsSync(p)) || "google-chrome";
72
+ }
73
+ /**
74
+ * Run a test scenario
75
+ */
76
+ async run(scenario) {
77
+ const startTime = Date.now();
78
+ const screenshots = [];
79
+ const consoleLogs = [];
80
+ let stepsExecuted = 0;
81
+ console.log(`[E2E] Starting test: ${scenario.name}`);
82
+ console.log(`[E2E] URL: ${scenario.url}`);
83
+ try {
84
+ // Execute auth if provided
85
+ if (scenario.auth) {
86
+ console.log("[E2E] Handling authentication...");
87
+ await this.handleAuth(scenario);
88
+ }
89
+ // Execute steps
90
+ for (const step of scenario.steps) {
91
+ console.log(`[E2E] Step ${stepsExecuted + 1}: ${step.action}${step.target ? ` "${step.target}"` : ""}${step.field ? ` (${step.field})` : ""}`);
92
+ const result = await this.executeStep(step, scenario);
93
+ stepsExecuted++;
94
+ if (!result.success) {
95
+ throw new Error(result.error || "Step failed");
96
+ }
97
+ // Screenshot if requested
98
+ if (step.screenshot) {
99
+ const ss = await this.takeScreenshot(`${scenario.name}_step_${stepsExecuted}`);
100
+ screenshots.push(ss);
101
+ }
102
+ // Wait if specified
103
+ if (step.waitMs) {
104
+ await this.delay(step.waitMs);
105
+ }
106
+ }
107
+ // Final screenshot
108
+ const finalSs = await this.takeScreenshot(`${scenario.name}_final`);
109
+ screenshots.push(finalSs);
110
+ return {
111
+ name: scenario.name,
112
+ passed: true,
113
+ screenshots,
114
+ stepsExecuted,
115
+ durationMs: Date.now() - startTime,
116
+ consoleLogs,
117
+ };
118
+ }
119
+ catch (error) {
120
+ const errorMessage = error instanceof Error ? error.message : String(error);
121
+ console.error(`[E2E] Test failed: ${errorMessage}`);
122
+ // Screenshot on failure
123
+ if (scenario.screenshotOnFailure !== false) {
124
+ try {
125
+ const ss = await this.takeScreenshot(`${scenario.name}_ERROR`);
126
+ screenshots.push(ss);
127
+ }
128
+ catch {
129
+ // Ignore screenshot errors
130
+ }
131
+ }
132
+ return {
133
+ name: scenario.name,
134
+ passed: false,
135
+ error: errorMessage,
136
+ screenshots,
137
+ stepsExecuted,
138
+ durationMs: Date.now() - startTime,
139
+ consoleLogs,
140
+ };
141
+ }
142
+ }
143
+ /**
144
+ * Handle authentication
145
+ */
146
+ async handleAuth(scenario) {
147
+ const auth = scenario.auth;
148
+ if (auth.type === "form") {
149
+ // Use built-in login helper
150
+ await this.login(scenario.url, {
151
+ username: auth.username || auth.email || "",
152
+ password: auth.password || "",
153
+ selectors: auth.selectors,
154
+ });
155
+ }
156
+ // Add more auth types as needed
157
+ }
158
+ /**
159
+ * Login to a site
160
+ */
161
+ async login(url, credentials) {
162
+ console.log(`[E2E] Logging in to ${url}`);
163
+ // Default selectors for common login forms
164
+ const selectors = credentials.selectors || {
165
+ username: credentials.username ? "#username, [name='username'], [name='email'], input[type='text']" : undefined,
166
+ email: credentials.email ? "#email, [name='email'], input[type='email']" : undefined,
167
+ password: "#password, [name='password'], input[type='password']",
168
+ submit: "button[type='submit'], input[type='submit'], button:contains('Login'), button:contains('Sign In')",
169
+ };
170
+ // Log the selectors being used (for debugging)
171
+ console.log("[E2E] Using selectors:", JSON.stringify(selectors, null, 2));
172
+ console.log("[E2E] NOTE: In full implementation, this would use CDP to interact with browser");
173
+ // This is a placeholder - actual CDP implementation would go here
174
+ throw new Error("CDP implementation required - see documentation");
175
+ }
176
+ /**
177
+ * Execute a single test step
178
+ */
179
+ async executeStep(step, scenario) {
180
+ const value = step.value && scenario.data?.[step.value]
181
+ ? scenario.data[step.value]
182
+ : step.value;
183
+ switch (step.action) {
184
+ case "navigate":
185
+ console.log(`[E2E] Navigate to: ${step.target || scenario.url}`);
186
+ return { success: true };
187
+ case "fill":
188
+ console.log(`[E2E] Fill "${step.field}" with "${value}"`);
189
+ return { success: true };
190
+ case "click":
191
+ console.log(`[E2E] Click: ${step.target}`);
192
+ return { success: true };
193
+ case "submit":
194
+ console.log(`[E2E] Submit form`);
195
+ return { success: true };
196
+ case "assert":
197
+ console.log(`[E2E] Assert: "${step.expected}"`);
198
+ return { success: true };
199
+ case "wait":
200
+ await this.delay(step.waitMs || 1000);
201
+ return { success: true };
202
+ case "screenshot":
203
+ return { success: true };
204
+ case "select":
205
+ console.log(`[E2E] Select "${step.value}" in "${step.field}"`);
206
+ return { success: true };
207
+ default:
208
+ return { success: false, error: `Unknown action: ${step.action}` };
209
+ }
210
+ }
211
+ /**
212
+ * Take a screenshot
213
+ */
214
+ async takeScreenshot(name) {
215
+ const filename = `${name.replace(/[^a-z0-9]/gi, "_")}.png`;
216
+ const path = join(this.config.screenshotDir, filename);
217
+ console.log(`[E2E] Screenshot: ${path}`);
218
+ // Placeholder - actual CDP screenshot would go here
219
+ return path;
220
+ }
221
+ /**
222
+ * Delay helper
223
+ */
224
+ delay(ms) {
225
+ return new Promise(resolve => setTimeout(resolve, ms));
226
+ }
227
+ /**
228
+ * Get test instructions for agent
229
+ */
230
+ static getInstructions() {
231
+ return `
232
+ ## E2E Testing Instructions
233
+
234
+ ### Running Tests
235
+ E2E tests run LOCALLY on your machine (where browser is available), NOT on remote servers.
236
+
237
+ ### Using WebMCPTester
238
+
239
+ \`\`\`typescript
240
+ import { WebMCPTester } from "@pi-harness/e2e-tester";
241
+
242
+ const tester = new WebMCPTester();
243
+
244
+ const result = await tester.run({
245
+ name: "Registration Test",
246
+ url: "https://your-app.com/register",
247
+ steps: [
248
+ { action: "fill", field: "email", value: "test@example.com" },
249
+ { action: "fill", field: "password", value: "TestPass123!" },
250
+ { action: "click", target: "Submit" },
251
+ { action: "assert", expected: "Welcome" },
252
+ ],
253
+ });
254
+
255
+ if (result.passed) {
256
+ console.log("Test passed!");
257
+ } else {
258
+ console.error("Test failed:", result.error);
259
+ }
260
+ \`\`\`
261
+
262
+ ### Step Types
263
+ - \`navigate\` - Go to URL
264
+ - \`fill\` - Fill form field
265
+ - \`click\` - Click element
266
+ - \`submit\` - Submit form
267
+ - \`assert\` - Check content exists
268
+ - \`wait\` - Wait for duration
269
+ - \`screenshot\` - Capture screenshot
270
+ - \`select\` - Select dropdown option
271
+
272
+ ### Dummy Data
273
+ Use data placeholders in steps, define actual values separately:
274
+
275
+ \`\`\`typescript
276
+ const result = await tester.run({
277
+ name: "Test",
278
+ url: "https://app.com",
279
+ data: {
280
+ email: "test@example.com",
281
+ password: "RealPassword123!",
282
+ },
283
+ steps: [
284
+ { action: "fill", field: "email", value: "email" }, // References data.email
285
+ { action: "fill", field: "password", value: "password" },
286
+ ],
287
+ });
288
+ \`\`\`
289
+
290
+ ### Authentication
291
+ \`\`\`typescript
292
+ await tester.run({
293
+ name: "Authenticated Test",
294
+ url: "https://app.com/dashboard",
295
+ auth: {
296
+ type: "form",
297
+ email: "test@example.com",
298
+ password: "TestPass123!",
299
+ },
300
+ steps: [...],
301
+ });
302
+ \`\`\`
303
+ `;
304
+ }
305
+ }
306
+ /**
307
+ * Parse natural language test description
308
+ */
309
+ export function parseTestDescription(description) {
310
+ const lines = description.split("\n").map(l => l.trim()).filter(Boolean);
311
+ const scenario = {
312
+ name: "",
313
+ url: "",
314
+ steps: [],
315
+ data: {},
316
+ };
317
+ let currentSection = "meta";
318
+ for (const line of lines) {
319
+ // Skip markdown headers
320
+ if (line.startsWith("#")) {
321
+ if (line.includes("Data"))
322
+ currentSection = "data";
323
+ if (line.includes("Step"))
324
+ currentSection = "steps";
325
+ continue;
326
+ }
327
+ // Parse metadata
328
+ if (currentSection === "meta") {
329
+ const nameMatch = line.match(/^(?:Test|Name):\s*(.+)/i);
330
+ if (nameMatch) {
331
+ scenario.name = nameMatch[1];
332
+ continue;
333
+ }
334
+ const urlMatch = line.match(/^(?:URL|Link|Website):\s*(.+)/i);
335
+ if (urlMatch) {
336
+ scenario.url = urlMatch[1];
337
+ continue;
338
+ }
339
+ }
340
+ // Parse data
341
+ if (currentSection === "data" && line.includes(":")) {
342
+ const [key, ...valueParts] = line.split(":");
343
+ scenario.data[key.trim()] = valueParts.join(":").trim();
344
+ }
345
+ // Parse steps
346
+ const stepMatch = line.match(/^\d+[.)]\s*(.+)/);
347
+ if (stepMatch) {
348
+ scenario.steps.push(parseStepText(stepMatch[1]));
349
+ }
350
+ }
351
+ return scenario;
352
+ }
353
+ /**
354
+ * Parse step text into TestStep
355
+ */
356
+ function parseStepText(text) {
357
+ const fillMatch = text.match(/^(?:fill|type|enter)\s+(?:"|')?([^"']+)(?:"|')?\s+(?:with|to|:)\s+(?:"|')?(.+?)(?:"|')?\s*$/i);
358
+ if (fillMatch) {
359
+ return { action: "fill", field: fillMatch[1], value: fillMatch[2] };
360
+ }
361
+ const clickMatch = text.match(/^(?:click|press|select|tap)\s+(?:on\s+)?(?:the\s+)?(.+?)(?:\s+button)?\s*$/i);
362
+ if (clickMatch) {
363
+ return { action: "click", target: clickMatch[1] };
364
+ }
365
+ const assertMatch = text.match(/^(?:assert|verify|check)\s+(?:that\s+)?(?:the\s+)?(.+?)(?:\s+(?:is|appears?|shows?|exists?))?\s*$/i);
366
+ if (assertMatch) {
367
+ return { action: "assert", expected: assertMatch[1] };
368
+ }
369
+ const waitMatch = text.match(/^wait\s+(\d+)\s*(s|ms|seconds?|milliseconds?)?\s*$/i);
370
+ if (waitMatch) {
371
+ const value = parseInt(waitMatch[1]);
372
+ const unit = (waitMatch[2] || "s").toLowerCase();
373
+ return { action: "wait", waitMs: unit.startsWith("s") ? value * 1000 : value };
374
+ }
375
+ const goMatch = text.match(/^(?:go to|navigate to|open)\s+(.+)\s*$/i);
376
+ if (goMatch) {
377
+ return { action: "navigate", target: goMatch[1] };
378
+ }
379
+ return { action: "click", target: text };
380
+ }