pi-harness-runtime 1.1.181 → 1.1.183

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 +3 -3
  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,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
+ }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * WebMCP E2E Tester for pi-harness-runtime
3
+ *
4
+ * Provides natural language E2E testing using WebMCP protocol.
5
+ * Works with Chrome WebMCP Extension: https://github.com/GoogleChromeLabs/webmcp-extension/
6
+ *
7
+ * Agent Usage:
8
+ * ```
9
+ * // Run E2E test on amos-saas
10
+ * const tester = new WebMCPTester();
11
+ * await tester.test({
12
+ * url: "https://amos-saas.example.com",
13
+ * scenario: "Test registration flow with email test@example.com",
14
+ * });
15
+ * ```
16
+ */
17
+ export interface TestStep {
18
+ /** Action to perform */
19
+ action: "navigate" | "fill" | "click" | "submit" | "assert" | "wait" | "screenshot" | "select";
20
+ /** Target element (CSS selector, text, or natural language) */
21
+ target?: string;
22
+ /** Field name for fill actions */
23
+ field?: string;
24
+ /** Value for fill actions */
25
+ value?: string;
26
+ /** Expected content for assert actions */
27
+ expected?: string;
28
+ /** Wait duration in milliseconds */
29
+ waitMs?: number;
30
+ /** Whether to capture screenshot */
31
+ screenshot?: boolean;
32
+ }
33
+ export interface TestScenario {
34
+ /** Test name */
35
+ name: string;
36
+ /** Starting URL */
37
+ url: string;
38
+ /** Authentication config */
39
+ auth?: {
40
+ type: "form" | "cookie" | "token";
41
+ username?: string;
42
+ password?: string;
43
+ email?: string;
44
+ selectors?: {
45
+ username?: string;
46
+ email?: string;
47
+ password?: string;
48
+ submit?: string;
49
+ };
50
+ };
51
+ /** Test steps */
52
+ steps: TestStep[];
53
+ /** Dummy data for test */
54
+ data?: Record<string, string>;
55
+ /** Screenshot on failure */
56
+ screenshotOnFailure?: boolean;
57
+ }
58
+ export interface TestResult {
59
+ /** Test name */
60
+ name: string;
61
+ /** Whether test passed */
62
+ passed: boolean;
63
+ /** Error message if failed */
64
+ error?: string;
65
+ /** Screenshots captured */
66
+ screenshots: string[];
67
+ /** Steps executed */
68
+ stepsExecuted: number;
69
+ /** Time taken (ms) */
70
+ durationMs: number;
71
+ /** Console logs */
72
+ consoleLogs: Array<{
73
+ type: "log" | "warn" | "error" | "info";
74
+ message: string;
75
+ timestamp: number;
76
+ }>;
77
+ }
78
+ export interface WebMCPTesterConfig {
79
+ /** Chrome path */
80
+ chromePath?: string;
81
+ /** Debugging port */
82
+ port?: number;
83
+ /** Headless mode */
84
+ headless?: boolean;
85
+ /** WebMCP extension path */
86
+ extensionPath?: string;
87
+ /** Test timeout (ms) */
88
+ timeoutMs?: number;
89
+ /** Screenshot output dir */
90
+ screenshotDir?: string;
91
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * WebMCP E2E Tester for pi-harness-runtime
3
+ *
4
+ * Provides natural language E2E testing using WebMCP protocol.
5
+ * Works with Chrome WebMCP Extension: https://github.com/GoogleChromeLabs/webmcp-extension/
6
+ *
7
+ * Agent Usage:
8
+ * ```
9
+ * // Run E2E test on amos-saas
10
+ * const tester = new WebMCPTester();
11
+ * await tester.test({
12
+ * url: "https://amos-saas.example.com",
13
+ * scenario: "Test registration flow with email test@example.com",
14
+ * });
15
+ * ```
16
+ */
17
+ export {};
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@pi-harness/e2e-tester",
3
+ "version": "1.1.183",
4
+ "description": "WebMCP-powered E2E testing for pi-harness - natural language test scenarios",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist/",
16
+ "README.md"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsc --project tsconfig.json",
20
+ "dev": "tsc --watch"
21
+ },
22
+ "keywords": [
23
+ "webmcp",
24
+ "e2e",
25
+ "testing",
26
+ "automation"
27
+ ],
28
+ "peerDependencies": {
29
+ "typescript": "^5.0.0"
30
+ }
31
+ }