skopix 2.0.20 → 2.0.22
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/cli/commands/agent.js +27 -6
- package/package.json +1 -1
package/cli/commands/agent.js
CHANGED
|
@@ -254,6 +254,14 @@ export async function agentCommand(options) {
|
|
|
254
254
|
deviceScaleFactor: 1,
|
|
255
255
|
...(wantReport ? { recordVideo: { dir: sessionDir, size: { width: 1280, height: 800 } } } : {}),
|
|
256
256
|
});
|
|
257
|
+
// Set zoom via initScript so it persists across page loads and route changes
|
|
258
|
+
if (browserZoom !== 1) {
|
|
259
|
+
await ctx.addInitScript((zoom) => {
|
|
260
|
+
const applyZ = () => { if (document.documentElement) document.documentElement.style.zoom = zoom; };
|
|
261
|
+
applyZ();
|
|
262
|
+
document.addEventListener('DOMContentLoaded', applyZ);
|
|
263
|
+
}, String(browserZoom));
|
|
264
|
+
}
|
|
257
265
|
const page = await ctx.newPage();
|
|
258
266
|
const applyZoom = async () => {
|
|
259
267
|
if (browserZoom !== 1) {
|
|
@@ -272,6 +280,9 @@ export async function agentCommand(options) {
|
|
|
272
280
|
// Apply zoom now if no setup test (otherwise applied at setup→main transition)
|
|
273
281
|
if (!setupTest) await applyZoom();
|
|
274
282
|
|
|
283
|
+
// Listen for future navigations and re-apply zoom
|
|
284
|
+
page.on('load', () => { applyZoom().catch(() => {}); });
|
|
285
|
+
|
|
275
286
|
send({ type: 'stdout', text: '◆ Replaying ' + allSteps.length + ' steps on ' + os.hostname() });
|
|
276
287
|
|
|
277
288
|
for (const step of allSteps) {
|
|
@@ -358,13 +369,15 @@ export async function agentCommand(options) {
|
|
|
358
369
|
try { const ro = new URL(navUrl).origin; const to = new URL(test.url).origin; if (ro !== to) navUrl = navUrl.replace(ro, to); } catch {}
|
|
359
370
|
}
|
|
360
371
|
await page.goto(navUrl, { waitUntil: 'domcontentloaded', timeout: 15000 });
|
|
372
|
+
await applyZoom();
|
|
361
373
|
await page.waitForTimeout(800);
|
|
362
374
|
|
|
363
375
|
} else if (step.action === 'click') {
|
|
376
|
+
await page.waitForTimeout(300);
|
|
364
377
|
let clicked = false;
|
|
365
378
|
const selectors = [step.stableSelector, step.selector].filter(Boolean).map(sanitiseSelector);
|
|
366
|
-
//
|
|
367
|
-
if (step.elementX || step.clickX) {
|
|
379
|
+
// Strategy 1: coordinate-based (best for duplicate elements)
|
|
380
|
+
if (!clicked && (step.elementX || step.clickX)) {
|
|
368
381
|
const tx = step.elementX || step.clickX, ty = step.elementY || step.clickY;
|
|
369
382
|
for (const s of selectors) {
|
|
370
383
|
if (clicked) break;
|
|
@@ -375,17 +388,25 @@ export async function agentCommand(options) {
|
|
|
375
388
|
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
389
|
await page.locator(s).nth(bi).click({ timeout: 5000 }); clicked = true;
|
|
377
390
|
} else if (count === 1) {
|
|
391
|
+
await page.locator(s).first().waitFor({ state: 'visible', timeout: 3000 });
|
|
378
392
|
await page.locator(s).first().click({ timeout: 5000 }); clicked = true;
|
|
379
393
|
}
|
|
380
394
|
} catch {}
|
|
381
395
|
}
|
|
382
396
|
}
|
|
383
|
-
//
|
|
384
|
-
if (!clicked) { for (const s of selectors) { if (clicked) break; try { await page.locator(s).first().click({ timeout: 5000 }); clicked = true; } catch {} } }
|
|
385
|
-
//
|
|
397
|
+
// Strategy 2: normal click
|
|
398
|
+
if (!clicked) { for (const s of selectors) { if (clicked) break; try { await page.locator(s).first().waitFor({ state: 'visible', timeout: 3000 }); await page.locator(s).first().click({ timeout: 5000 }); clicked = true; } catch {} } }
|
|
399
|
+
// Strategy 3: force click
|
|
386
400
|
if (!clicked) { for (const s of selectors) { if (clicked) break; try { await page.locator(s).first().click({ force: true, timeout: 5000 }); clicked = true; } catch {} } }
|
|
401
|
+
// Strategy 4: click clickable ancestor (for icons/SVGs)
|
|
402
|
+
if (!clicked && step.element) {
|
|
403
|
+
const tag = (step.element.tag || '').toLowerCase();
|
|
404
|
+
if (['i', 'svg', 'path', 'span', 'img'].includes(tag)) {
|
|
405
|
+
for (const s of selectors) { if (clicked) break; try { await page.locator(s).first().locator('xpath=ancestor-or-self::*[self::a or self::button or @role="button"][1]').first().click({ timeout: 5000 }); clicked = true; } catch {} }
|
|
406
|
+
}
|
|
407
|
+
}
|
|
387
408
|
if (!clicked) throw new Error('Could not click: ' + selectors.join(', '));
|
|
388
|
-
await page.waitForTimeout(
|
|
409
|
+
await page.waitForTimeout(500);
|
|
389
410
|
|
|
390
411
|
} else if (step.action === 'type') {
|
|
391
412
|
await page.locator(sel).first().click({ timeout: 5000 });
|
package/package.json
CHANGED