skopix 2.0.80 → 2.0.82
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 +2 -0
- package/cli/commands/dashboard.js +1 -0
- package/core/llm.js +3 -1
- package/core/recorder.js +11 -2
- package/package.json +1 -1
package/cli/commands/agent.js
CHANGED
|
@@ -406,6 +406,8 @@ export async function agentCommand(options) {
|
|
|
406
406
|
}
|
|
407
407
|
}
|
|
408
408
|
if (!clicked) throw new Error('Could not click: ' + selectors.join(', '));
|
|
409
|
+
// Wait for any navigation triggered by the click
|
|
410
|
+
await page.waitForLoadState('domcontentloaded', { timeout: 5000 }).catch(() => {});
|
|
409
411
|
await page.waitForTimeout(500);
|
|
410
412
|
|
|
411
413
|
} else if (step.action === 'type') {
|
|
@@ -3288,6 +3288,7 @@ function startReplay(test, setupTest, activeRuns, reportsDir, currentUser, env)
|
|
|
3288
3288
|
if (!clicked) throw new Error('Could not click element. Tried: ' + selectors.join(', '));
|
|
3289
3289
|
const descLower = (step.description||'').toLowerCase(), selLower = selectors.join(' ').toLowerCase();
|
|
3290
3290
|
const isSave = descLower.includes('save')||descLower.includes('submit')||selLower.includes('fa-save')||selLower.includes('fa-floppy');
|
|
3291
|
+
await page.waitForLoadState('domcontentloaded', { timeout: 5000 }).catch(() => {});
|
|
3291
3292
|
await page.waitForTimeout(isSave ? 2000 : 500);
|
|
3292
3293
|
broadcast({ type: 'stdout', text: ' \u2713 Clicked' + (isSave?' (waited for save)':'') });
|
|
3293
3294
|
|
package/core/llm.js
CHANGED
|
@@ -547,13 +547,15 @@ export async function processRecording({ steps, testName, url, provider, apiKey,
|
|
|
547
547
|
+ '\n\nYour jobs:\n'
|
|
548
548
|
+ '1. For each step, write a STABLE SELECTOR. Priority order:\n'
|
|
549
549
|
+ ' - data-testid, data-test, data-cy, data-qa, pi-test-identifier attributes use [attr="value"]\n'
|
|
550
|
+
+ ' - title attribute — if the element has a title, use a[title="..."] or button[title="..."] — this is VERY reliable\n'
|
|
550
551
|
+ ' - Unique meaningful id (NOT random/generated IDs) use #id\n'
|
|
551
552
|
+ ' - Semantic selector e.g. button:has-text("Login"), input[name="email"]\n'
|
|
552
553
|
+ ' - Role + text e.g. [role="button"]:has-text("Submit")\n'
|
|
553
554
|
+ ' - Class-based selector for well-named classes e.g. .chart-container, .save-btn\n'
|
|
554
555
|
+ ' - Fall back to the original selector if nothing better\n'
|
|
555
556
|
+ ' CRITICAL RULES FOR SELECTORS:\n'
|
|
556
|
-
+ ' -
|
|
557
|
+
+ ' - ONLY use attributes that ACTUALLY EXIST on the element from the captured data. NEVER invent aria-label, title, or other attributes that are not in the element info.\n'
|
|
558
|
+
+ ' - For icon elements (<i>, <span> with fa/icon classes): use the PARENT title or pi-test-identifier as the anchor e.g. a[title="Create new chart"] or [pi-test-identifier="x"] i. Check parentTitle and parentTestId fields.\n'
|
|
557
559
|
+ ' - NEVER use IDs that look randomly generated (e.g. #highcharts-abc123-58, #ng-view-1, anything with random hex/numbers)\n'
|
|
558
560
|
+ ' - NEVER use :nth-child or :nth-of-type positional selectors\n'
|
|
559
561
|
+ ' - For chart/visualization containers: use class-based selectors like .highcharts-container, .chart-wrapper, [class*="chart"]\n'
|
package/core/recorder.js
CHANGED
|
@@ -87,23 +87,32 @@ export class RecordingSession {
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
function getElementInfo(el) {
|
|
90
|
+
const piTestId = el.getAttribute('pi-test-identifier') || null;
|
|
91
|
+
const title = el.getAttribute('title') || null;
|
|
92
|
+
const ariaLabel = el.getAttribute('aria-label') || null;
|
|
93
|
+
const dataTestId = el.getAttribute('data-testid') || el.getAttribute('data-test-id') || null;
|
|
90
94
|
const info = {
|
|
91
95
|
tag: el.tagName.toLowerCase(),
|
|
92
96
|
id: el.id || null,
|
|
93
97
|
name: el.name || null,
|
|
94
98
|
type: el.type || null,
|
|
95
|
-
text: (el.innerText || el.value || el.placeholder ||
|
|
99
|
+
text: (el.innerText || el.value || el.placeholder || ariaLabel || title || '').trim().slice(0, 80),
|
|
96
100
|
selector: getSelector(el),
|
|
97
101
|
classes: el.className ? el.className.toString().trim().slice(0, 100) : null,
|
|
102
|
+
title,
|
|
103
|
+
ariaLabel,
|
|
104
|
+
piTestId,
|
|
105
|
+
dataTestId,
|
|
98
106
|
};
|
|
99
107
|
// For icon elements with no meaningful ID/text, capture parent context for better selector generation
|
|
100
|
-
const isIcon = ['i', 'span', 'svg'].includes(info.tag) && !info.id && !info.text;
|
|
108
|
+
const isIcon = ['i', 'span', 'svg'].includes(info.tag) && !info.id && !info.text && !piTestId;
|
|
101
109
|
if (isIcon && el.parentElement) {
|
|
102
110
|
const p = el.parentElement;
|
|
103
111
|
info.parentTag = p.tagName.toLowerCase();
|
|
104
112
|
info.parentClasses = p.className ? p.className.toString().trim().slice(0, 100) : null;
|
|
105
113
|
info.parentSelector = getSelector(p);
|
|
106
114
|
info.parentAriaLabel = p.getAttribute('aria-label') || null;
|
|
115
|
+
info.parentTitle = p.getAttribute('title') || null;
|
|
107
116
|
info.parentTestId = p.getAttribute('pi-test-identifier') || p.getAttribute('data-testid') || null;
|
|
108
117
|
}
|
|
109
118
|
return info;
|
package/package.json
CHANGED