@cutleryapp/agent 1.0.43 → 1.0.44

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.
@@ -85,6 +85,9 @@ class TestExecutor {
85
85
  const stepAttachment = (testCase.step_attachments || {})[String(i)] || null;
86
86
  console.log(` 📎 Step ${i} attachment: ${stepAttachment ? `YES (${stepAttachment.length} chars)` : 'none'}`);
87
87
  let stepError;
88
+ // Dismiss any open overlay (date picker, dropdown, modal) from the previous step
89
+ if (i > 0)
90
+ await page.keyboard.press('Escape').catch(() => { });
88
91
  try {
89
92
  // When a reference image is attached, skip MCP strategies entirely and go
90
93
  // straight to the AI multi-field loop so it can scan the form and fill everything.
@@ -182,6 +185,20 @@ class TestExecutor {
182
185
  }
183
186
  else {
184
187
  await tryFill(page, fieldLabel, value);
188
+ // After filling, check if an autocomplete dropdown appeared and click the option
189
+ try {
190
+ const optSel = [
191
+ `[role="option"]:has-text("${value}")`,
192
+ `[class*="option"]:has-text("${value}")`,
193
+ `[class*="suggestion"]:has-text("${value}")`,
194
+ `li:has-text("${value}")`,
195
+ ].join(', ');
196
+ const opt = page.locator(optSel).first();
197
+ if (await opt.isVisible({ timeout: 400 })) {
198
+ await opt.click({ timeout: 1000 });
199
+ }
200
+ }
201
+ catch { /* no autocomplete dropdown, that's fine */ }
185
202
  }
186
203
  }
187
204
  handled = true;
@@ -1219,42 +1236,47 @@ function parseDate(value) {
1219
1236
  async function tryFillDate(page, label, value) {
1220
1237
  const labelRe = new RegExp(escapeRegex(label), 'i');
1221
1238
  const parsed = parseDate(value);
1222
- // Strategy 1: input[type="date"] needs YYYY-MM-DD format
1239
+ // First close any already-open calendar/overlay before we start
1240
+ await page.keyboard.press('Escape').catch(() => { });
1241
+ await page.waitForTimeout(100);
1242
+ // Strategy 1: native input[type="date"] — fill with ISO format, no calendar involved
1223
1243
  if (parsed) {
1224
- const dateInputSelectors = [
1225
- `input[type="date"]`,
1226
- `input[type="datetime-local"]`,
1227
- ];
1228
- for (const sel of dateInputSelectors) {
1229
- try {
1230
- const loc = page.locator(sel).first();
1231
- if (await loc.isVisible({ timeout: 400 })) {
1232
- await loc.fill(parsed.iso, { timeout: 1000 });
1233
- return;
1234
- }
1244
+ try {
1245
+ const native = page.locator('input[type="date"], input[type="datetime-local"]').first();
1246
+ if (await native.isVisible({ timeout: 300 })) {
1247
+ await native.fill(parsed.iso, { timeout: 1000 });
1248
+ return;
1235
1249
  }
1236
- catch { /* try next */ }
1237
1250
  }
1251
+ catch { /* not a native date input */ }
1238
1252
  }
1239
- // Strategy 2: plain text input — type the value as-is, dismiss any calendar that opens
1240
- try {
1241
- const input = await Promise.any([
1242
- page.getByLabel(labelRe).first().elementHandle({ timeout: 500 }),
1243
- page.getByPlaceholder(labelRe).first().elementHandle({ timeout: 500 }),
1244
- page.getByRole('textbox', { name: labelRe }).first().elementHandle({ timeout: 500 }),
1245
- ]);
1246
- if (input) {
1247
- await input.click();
1248
- await page.keyboard.press('Escape'); // close any calendar that opened
1253
+ // Strategy 2: react-datepicker / custom date input — click, select-all, type, Tab to commit
1254
+ const dateLocators = [
1255
+ page.getByLabel(labelRe).first(),
1256
+ page.getByPlaceholder(labelRe).first(),
1257
+ page.getByRole('textbox', { name: labelRe }).first(),
1258
+ ];
1259
+ for (const loc of dateLocators) {
1260
+ try {
1261
+ if (!await loc.isVisible({ timeout: 400 }))
1262
+ continue;
1263
+ // Click to focus (may open calendar)
1264
+ await loc.click({ timeout: 800 });
1265
+ await page.waitForTimeout(150);
1266
+ // Select all existing text and replace
1267
+ await page.keyboard.press('Control+a');
1268
+ await page.keyboard.press('Meta+a'); // Mac
1269
+ await page.keyboard.type(value, { delay: 40 });
1249
1270
  await page.waitForTimeout(150);
1250
- await input.click({ clickCount: 3 }); // select all existing text
1251
- await page.keyboard.type(value, { delay: 30 });
1252
- await page.keyboard.press('Escape'); // close calendar again after typing
1253
- await page.keyboard.press('Tab'); // commit the value
1271
+ // Tab out to commit this also closes the calendar
1272
+ await page.keyboard.press('Tab');
1273
+ await page.waitForTimeout(200);
1274
+ // If calendar is still open, press Escape
1275
+ await page.keyboard.press('Escape').catch(() => { });
1254
1276
  return;
1255
1277
  }
1278
+ catch { /* try next */ }
1256
1279
  }
1257
- catch { /* fall through */ }
1258
1280
  // Strategy 3: fallback to regular fill
1259
1281
  await tryFill(page, label, value);
1260
1282
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cutleryapp/agent",
3
- "version": "1.0.43",
3
+ "version": "1.0.44",
4
4
  "description": "Local agent that connects your machine to the Cutlery QA platform and runs UI tests via Playwright",
5
5
  "main": "dist/cli.js",
6
6
  "bin": {