skopix 2.0.19 → 2.0.20

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.
@@ -361,10 +361,10 @@ export async function agentCommand(options) {
361
361
  await page.waitForTimeout(800);
362
362
 
363
363
  } else if (step.action === 'click') {
364
- await page.waitForTimeout(100);
365
364
  let clicked = false;
366
365
  const selectors = [step.stableSelector, step.selector].filter(Boolean).map(sanitiseSelector);
367
- if (!clicked && (step.elementX || step.clickX)) {
366
+ // Try position-matched click first (for elements with coordinates)
367
+ if (step.elementX || step.clickX) {
368
368
  const tx = step.elementX || step.clickX, ty = step.elementY || step.clickY;
369
369
  for (const s of selectors) {
370
370
  if (clicked) break;
@@ -372,7 +372,7 @@ export async function agentCommand(options) {
372
372
  const count = await page.locator(s).count();
373
373
  if (count > 1) {
374
374
  let bi = 0, bd = Infinity;
375
- for (let i = 0; i < count; i++) { try { const box = await page.locator(s).nth(i).boundingBox({ timeout: 2000 }); if (!box) continue; const d = Math.sqrt(Math.pow(box.x + box.width / 2 - tx, 2) + Math.pow(box.y + box.height / 2 - ty, 2)); if (d < bd) { bd = d; bi = i; } } catch {} }
375
+ for (let i = 0; i < count; i++) { try { const box = await page.locator(s).nth(i).boundingBox({ timeout: 2000 }); if (!box) continue; const d = Math.sqrt(Math.pow(box.x+box.width/2-tx,2)+Math.pow(box.y+box.height/2-ty,2)); if (d < bd) { bd=d; bi=i; } } catch {} }
376
376
  await page.locator(s).nth(bi).click({ timeout: 5000 }); clicked = true;
377
377
  } else if (count === 1) {
378
378
  await page.locator(s).first().click({ timeout: 5000 }); clicked = true;
@@ -380,48 +380,12 @@ export async function agentCommand(options) {
380
380
  } catch {}
381
381
  }
382
382
  }
383
+ // Simple click fallback
383
384
  if (!clicked) { for (const s of selectors) { if (clicked) break; try { await page.locator(s).first().click({ timeout: 5000 }); clicked = true; } catch {} } }
384
- if (!clicked) { for (const s of selectors) { if (clicked) break; try { await page.locator(s).first().scrollIntoViewIfNeeded({ timeout: 2000 }).catch(() => {}); await page.locator(s).first().click({ timeout: 5000 }); clicked = true; } catch {} } }
385
+ // Force click (for off-screen elements)
385
386
  if (!clicked) { for (const s of selectors) { if (clicked) break; try { await page.locator(s).first().click({ force: true, timeout: 5000 }); clicked = true; } catch {} } }
386
- if (!clicked) {
387
- // Last resort — JS dispatch for framework-managed elements
388
- for (const s of selectors) {
389
- if (clicked) break;
390
- try {
391
- await page.locator(s).first().evaluate(el => {
392
- el.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, cancelable: true }));
393
- el.dispatchEvent(new MouseEvent('mouseup', { bubbles: true, cancelable: true }));
394
- el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
395
- });
396
- clicked = true;
397
- } catch {}
398
- }
399
- }
400
387
  if (!clicked) throw new Error('Could not click: ' + selectors.join(', '));
401
-
402
- // Special handling for Bootstrap dropdowns — wait for menu to appear
403
- try {
404
- const el = page.locator(selectors[0]).first();
405
- const isDropdown = await el.evaluate(e => e.hasAttribute('data-toggle') && e.getAttribute('data-toggle') === 'dropdown').catch(() => false);
406
- if (isDropdown) {
407
- // Try jQuery dropdown toggle first (Bootstrap 3)
408
- const toggled = await page.evaluate((sel) => {
409
- try {
410
- const el = document.querySelector(sel);
411
- if (el && window.$) { window.$(el).dropdown('toggle'); return true; }
412
- } catch {}
413
- return false;
414
- }, selectors[0]).catch(() => false);
415
- if (!toggled) {
416
- // Fallback: dispatch click on the element
417
- await el.evaluate(e => e.click());
418
- }
419
- await page.waitForSelector('.dropdown-menu', { state: 'visible', timeout: 3000 }).catch(() => {});
420
- await page.waitForTimeout(400);
421
- }
422
- } catch {}
423
-
424
- await page.waitForTimeout(400);
388
+ await page.waitForTimeout(200);
425
389
 
426
390
  } else if (step.action === 'type') {
427
391
  await page.locator(sel).first().click({ timeout: 5000 });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skopix",
3
- "version": "2.0.19",
3
+ "version": "2.0.20",
4
4
  "description": "Browser-based QA tool — record tests by using your app, replay them deterministically, generate Playwright code automatically",
5
5
  "main": "cli/index.js",
6
6
  "bin": {