skopix 2.0.81 → 2.0.83
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/dashboard.js +2 -0
- package/core/llm.js +12 -1
- package/core/recorder.js +11 -2
- package/package.json +1 -1
|
@@ -1572,6 +1572,8 @@ export async function dashboardCommand(options) {
|
|
|
1572
1572
|
const data = JSON.parse(body);
|
|
1573
1573
|
try {
|
|
1574
1574
|
const result = await updateTest(suitesDir, scope, testId, data);
|
|
1575
|
+
// Extract any new steps to pending review queue
|
|
1576
|
+
extractStepsToLibrary(suitesDir, data.steps || [], data.name || testId).catch(() => {});
|
|
1575
1577
|
sendJSON(res, 200, result);
|
|
1576
1578
|
} catch (err) {
|
|
1577
1579
|
sendJSON(res, 400, { error: err.message });
|
package/core/llm.js
CHANGED
|
@@ -509,6 +509,13 @@ export async function processRecording({ steps, testName, url, provider, apiKey,
|
|
|
509
509
|
type: s.element.type,
|
|
510
510
|
text: s.element.text,
|
|
511
511
|
classes: s.element.classes,
|
|
512
|
+
title: s.element.title || null,
|
|
513
|
+
ariaLabel: s.element.ariaLabel || null,
|
|
514
|
+
piTestId: s.element.piTestId || null,
|
|
515
|
+
dataTestId: s.element.dataTestId || null,
|
|
516
|
+
parentTitle: s.element.parentTitle || null,
|
|
517
|
+
parentTestId: s.element.parentTestId || null,
|
|
518
|
+
parentClasses: s.element.parentClasses || null,
|
|
512
519
|
} : null,
|
|
513
520
|
value: s.action === 'type' && s.isPassword ? '[password - use process.env.TEST_PASSWORD]' : (s.value || null),
|
|
514
521
|
isPassword: s.isPassword || false,
|
|
@@ -547,13 +554,17 @@ export async function processRecording({ steps, testName, url, provider, apiKey,
|
|
|
547
554
|
+ '\n\nYour jobs:\n'
|
|
548
555
|
+ '1. For each step, write a STABLE SELECTOR. Priority order:\n'
|
|
549
556
|
+ ' - data-testid, data-test, data-cy, data-qa, pi-test-identifier attributes use [attr="value"]\n'
|
|
557
|
+
+ ' - MOST IMPORTANT: If the element data contains piTestId, use [pi-test-identifier="VALUE"] as the selector — this is the most reliable selector possible. Always check piTestId first.\n'
|
|
558
|
+
+ ' - If parentTestId exists and piTestId does not, use [pi-test-identifier="parentTestId"] as anchor\n'
|
|
559
|
+
+ ' - title attribute — if the element has a title, use a[title="..."] or button[title="..."] — this is VERY reliable\n'
|
|
550
560
|
+ ' - Unique meaningful id (NOT random/generated IDs) use #id\n'
|
|
551
561
|
+ ' - Semantic selector e.g. button:has-text("Login"), input[name="email"]\n'
|
|
552
562
|
+ ' - Role + text e.g. [role="button"]:has-text("Submit")\n'
|
|
553
563
|
+ ' - Class-based selector for well-named classes e.g. .chart-container, .save-btn\n'
|
|
554
564
|
+ ' - Fall back to the original selector if nothing better\n'
|
|
555
565
|
+ ' CRITICAL RULES FOR SELECTORS:\n'
|
|
556
|
-
+ ' -
|
|
566
|
+
+ ' - 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'
|
|
567
|
+
+ ' - 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
568
|
+ ' - NEVER use IDs that look randomly generated (e.g. #highcharts-abc123-58, #ng-view-1, anything with random hex/numbers)\n'
|
|
558
569
|
+ ' - NEVER use :nth-child or :nth-of-type positional selectors\n'
|
|
559
570
|
+ ' - 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