@cutleryapp/agent 1.0.25 → 1.0.27
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/mcp-executor.js +58 -1
- package/package.json +1 -1
package/dist/mcp-executor.js
CHANGED
|
@@ -14,11 +14,35 @@ class TestExecutor {
|
|
|
14
14
|
this.activeBrowser = null;
|
|
15
15
|
}
|
|
16
16
|
async execute(testCase) {
|
|
17
|
+
// Data-driven: run once per row, merging row into test_variables
|
|
18
|
+
const dataRows = testCase.test_data || [];
|
|
19
|
+
if (dataRows.length > 1) {
|
|
20
|
+
console.log(`[executor] 🗂️ Data-driven: ${dataRows.length} rows`);
|
|
21
|
+
const allResults = [];
|
|
22
|
+
for (let i = 0; i < dataRows.length; i++) {
|
|
23
|
+
console.log(`[executor] 🗂️ Row ${i + 1}/${dataRows.length}:`, JSON.stringify(dataRows[i]));
|
|
24
|
+
const rowResult = await this.execute({
|
|
25
|
+
...testCase,
|
|
26
|
+
test_variables: { ...(testCase.test_variables || {}), ...dataRows[i] },
|
|
27
|
+
test_data: [],
|
|
28
|
+
});
|
|
29
|
+
allResults.push(rowResult);
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
success: allResults.every(r => r.success),
|
|
33
|
+
steps: allResults.flatMap((r, i) => r.steps.map(s => ({ ...s, step: `[Row ${i + 1}] ${s.step}` }))),
|
|
34
|
+
screenshots: allResults.flatMap(r => r.screenshots),
|
|
35
|
+
error: allResults.find(r => r.error)?.error,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
17
38
|
const result = { success: true, steps: [], screenshots: [] };
|
|
18
39
|
if (!(0, fs_1.existsSync)(this.options.outputDir)) {
|
|
19
40
|
(0, fs_1.mkdirSync)(this.options.outputDir, { recursive: true });
|
|
20
41
|
}
|
|
21
|
-
|
|
42
|
+
// Merge single data row into variables if present
|
|
43
|
+
const variables = dataRows.length === 1
|
|
44
|
+
? { ...(testCase.test_variables || {}), ...dataRows[0] }
|
|
45
|
+
: testCase.test_variables || {};
|
|
22
46
|
const baseUrlValue = this.options.baseUrl;
|
|
23
47
|
console.log('[executor] test_variables received:', JSON.stringify(variables));
|
|
24
48
|
console.log('[executor] baseUrl option:', baseUrlValue);
|
|
@@ -101,6 +125,39 @@ class TestExecutor {
|
|
|
101
125
|
handled = true;
|
|
102
126
|
}
|
|
103
127
|
}
|
|
128
|
+
// 3a. Multi-field fill: "Fill firstname, lastname" → fill each with inferred value
|
|
129
|
+
if (!handled && /^(?:fill|type)\s+/i.test(raw) && !/\s+(?:in|into|with)\s+/i.test(raw)) {
|
|
130
|
+
const fieldsPart = raw.replace(/^(?:fill|type)\s+/i, "").trim();
|
|
131
|
+
const fields = fieldsPart.split(/,\s*/).map((f) => f.trim().replace(/^["']|["']$/g, "")).filter(Boolean);
|
|
132
|
+
if (fields.length > 1) {
|
|
133
|
+
const valueMap = {
|
|
134
|
+
firstname: "John", first: "John", fname: "John",
|
|
135
|
+
lastname: "Smith", last: "Smith", lname: "Smith", surname: "Smith",
|
|
136
|
+
name: "John Smith", fullname: "John Smith",
|
|
137
|
+
email: "john.smith@example.com", emailaddress: "john.smith@example.com",
|
|
138
|
+
phone: "9876543210", mobile: "9876543210", phonenumber: "9876543210", mobilenumber: "9876543210",
|
|
139
|
+
address: "123 Test Street", currentaddress: "123 Test Street", streetaddress: "123 Test Street",
|
|
140
|
+
city: "New York", state: "New York",
|
|
141
|
+
zip: "10001", zipcode: "10001", postalcode: "10001",
|
|
142
|
+
dob: "01/01/1990", dateofbirth: "01/01/1990", birthdate: "01/01/1990",
|
|
143
|
+
age: "30", username: "john.smith",
|
|
144
|
+
password: "Test@1234", company: "Acme Corp",
|
|
145
|
+
subject: "Mathematics", subjects: "Mathematics",
|
|
146
|
+
message: "This is a test message.", comment: "Test comment.",
|
|
147
|
+
description: "Test description.",
|
|
148
|
+
};
|
|
149
|
+
for (const field of fields) {
|
|
150
|
+
const key = field.toLowerCase().replace(/[\s_-]+/g, "");
|
|
151
|
+
const value = valueMap[key] || "Test Value";
|
|
152
|
+
console.log(` ⌨️ Multi-fill: "${field}" → "${value}"`);
|
|
153
|
+
try {
|
|
154
|
+
await tryFill(page, field, value);
|
|
155
|
+
}
|
|
156
|
+
catch { /* ignore individual failures */ }
|
|
157
|
+
}
|
|
158
|
+
handled = true;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
104
161
|
// 3. Fill — smart selector strategies via MCP/Playwright
|
|
105
162
|
if (!handled && (lower.includes("fill") || lower.includes("type") || lower.includes("enter"))) {
|
|
106
163
|
const match = raw.match(/(?:enter|fill|type)\s+"([^"]+)"\s+(?:in|into)\s+(?:the\s+)?"?([^"]+?)"?\s*(?:field|input|box|area)?\s*$/i) ||
|