@cutleryapp/agent 1.0.9 → 1.0.10

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.
@@ -77,11 +77,16 @@ class TestExecutor {
77
77
  }
78
78
  else if (lower.includes("click")) {
79
79
  const labelMatch = raw.match(/click\s+(?:on\s+)?(?:the\s+)?"?([^"]+?)"?\s*(?:button|link|tab)?$/i);
80
- const label = labelMatch?.[1]?.trim();
80
+ let label = labelMatch?.[1]?.trim();
81
81
  if (label) {
82
- const nameRe = new RegExp(label, 'i');
83
- // Use a short probe timeout so fallbacks are fast
84
- const clicked = await tryClick(page, nameRe, label);
82
+ // Split "Add to cart under Sauce Labs Bike Light product" into target + scope
83
+ const scopeMatch = label.match(/^(.+?)\s+(?:under|inside|within|in the)\s+(.+)$/i);
84
+ const target = scopeMatch ? scopeMatch[1].trim() : label;
85
+ const scope = scopeMatch ? scopeMatch[2].trim() : null;
86
+ const nameRe = new RegExp(escapeRegex(target), 'i');
87
+ const clicked = scope
88
+ ? await tryClickScoped(page, nameRe, target, scope)
89
+ : await tryClick(page, nameRe, target);
85
90
  if (!clicked)
86
91
  throw new Error(`Could not find clickable element: "${label}"`);
87
92
  }
@@ -216,6 +221,26 @@ async function tryClick(page, nameRe, label) {
216
221
  }
217
222
  return false;
218
223
  }
224
+ async function tryClickScoped(page, nameRe, target, scope) {
225
+ const FAST = 3000;
226
+ // Find a container that contains the scope text, then click the target inside it
227
+ const strategies = [
228
+ () => page.locator(`:has-text("${scope}")`).last().getByRole('button', { name: nameRe }).first().click({ timeout: FAST }),
229
+ () => page.locator(`:has-text("${scope}")`).last().getByRole('link', { name: nameRe }).first().click({ timeout: FAST }),
230
+ () => page.locator(`:has-text("${scope}")`).last().getByText(nameRe).first().click({ timeout: FAST }),
231
+ // Fallback: ignore scope and click anywhere
232
+ () => page.getByRole('button', { name: nameRe }).first().click({ timeout: FAST }),
233
+ () => page.getByText(nameRe).first().click({ timeout: FAST }),
234
+ ];
235
+ for (const fn of strategies) {
236
+ try {
237
+ await fn();
238
+ return true;
239
+ }
240
+ catch { /* try next */ }
241
+ }
242
+ return false;
243
+ }
219
244
  async function tryFill(page, label, value) {
220
245
  const FAST = 1500;
221
246
  const labelRe = new RegExp(escapeRegex(label), "i");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cutleryapp/agent",
3
- "version": "1.0.9",
3
+ "version": "1.0.10",
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": {