skopix 2.0.15 → 2.0.17
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/core/llm.js +1 -0
- package/core/recorder.js +12 -1
- package/package.json +1 -1
- package/web/app/index.html +5 -5
package/core/llm.js
CHANGED
|
@@ -553,6 +553,7 @@ export async function processRecording({ steps, testName, url, provider, apiKey,
|
|
|
553
553
|
+ ' - Class-based selector for well-named classes e.g. .chart-container, .save-btn\n'
|
|
554
554
|
+ ' - Fall back to the original selector if nothing better\n'
|
|
555
555
|
+ ' CRITICAL RULES FOR SELECTORS:\n'
|
|
556
|
+
+ ' - For icon elements (<i>, <span> with fa/icon classes): use the parent element as the anchor if it has a meaningful class/attribute, e.g. .chart-panel__panel-heading__panel-button i, [aria-label="Options"] i. NEVER generate a bare i.fa-something if there are multiple on the page — always scope it to its parent container\n'
|
|
556
557
|
+ ' - NEVER use IDs that look randomly generated (e.g. #highcharts-abc123-58, #ng-view-1, anything with random hex/numbers)\n'
|
|
557
558
|
+ ' - NEVER use :nth-child or :nth-of-type positional selectors\n'
|
|
558
559
|
+ ' - For chart/visualization containers: use class-based selectors like .highcharts-container, .chart-wrapper, [class*="chart"]\n'
|
package/core/recorder.js
CHANGED
|
@@ -87,7 +87,7 @@ export class RecordingSession {
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
function getElementInfo(el) {
|
|
90
|
-
|
|
90
|
+
const info = {
|
|
91
91
|
tag: el.tagName.toLowerCase(),
|
|
92
92
|
id: el.id || null,
|
|
93
93
|
name: el.name || null,
|
|
@@ -96,6 +96,17 @@ export class RecordingSession {
|
|
|
96
96
|
selector: getSelector(el),
|
|
97
97
|
classes: el.className ? el.className.toString().trim().slice(0, 100) : null,
|
|
98
98
|
};
|
|
99
|
+
// 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;
|
|
101
|
+
if (isIcon && el.parentElement) {
|
|
102
|
+
const p = el.parentElement;
|
|
103
|
+
info.parentTag = p.tagName.toLowerCase();
|
|
104
|
+
info.parentClasses = p.className ? p.className.toString().trim().slice(0, 100) : null;
|
|
105
|
+
info.parentSelector = getSelector(p);
|
|
106
|
+
info.parentAriaLabel = p.getAttribute('aria-label') || null;
|
|
107
|
+
info.parentTestId = p.getAttribute('pi-test-identifier') || p.getAttribute('data-testid') || null;
|
|
108
|
+
}
|
|
109
|
+
return info;
|
|
99
110
|
}
|
|
100
111
|
|
|
101
112
|
// ─── Action listeners ─────────────────────────────────────────────────
|
package/package.json
CHANGED
package/web/app/index.html
CHANGED
|
@@ -2240,9 +2240,9 @@ body.viewer-mode .saved-test-row { cursor: default !important; }
|
|
|
2240
2240
|
<div style="display:flex;align-items:center;gap:8px">
|
|
2241
2241
|
<label style="font-family:var(--mono);font-size:12px;color:var(--muted);white-space:nowrap">Browser zoom:</label>
|
|
2242
2242
|
<select class="form-select" id="re-browser-zoom" style="padding:5px 8px;font-size:12px;width:90px">
|
|
2243
|
-
<option value="1">100%</option>
|
|
2243
|
+
<option value="1" selected>100%</option>
|
|
2244
2244
|
<option value="0.9">90%</option>
|
|
2245
|
-
<option value="0.8"
|
|
2245
|
+
<option value="0.8">80%</option>
|
|
2246
2246
|
<option value="0.75">75%</option>
|
|
2247
2247
|
<option value="0.67">67%</option>
|
|
2248
2248
|
</select>
|
|
@@ -5467,7 +5467,7 @@ async function confirmAddToSuite() {
|
|
|
5467
5467
|
}
|
|
5468
5468
|
|
|
5469
5469
|
// ── RECORDED TEST EDITOR ────────────────────────────────────────────────────
|
|
5470
|
-
let reEditorState = { scope: null, testId: null, steps: [], testName: '', url: '', reusable: false, setup: '', credentials: '', tags: [], generateReport: true, browserZoom:
|
|
5470
|
+
let reEditorState = { scope: null, testId: null, steps: [], testName: '', url: '', reusable: false, setup: '', credentials: '', tags: [], generateReport: true, browserZoom: 1 };
|
|
5471
5471
|
let reAssertInsertAfter = -1;
|
|
5472
5472
|
|
|
5473
5473
|
async function openRecordedTestEditor(scope, testId) {
|
|
@@ -5475,7 +5475,7 @@ async function openRecordedTestEditor(scope, testId) {
|
|
|
5475
5475
|
const res = await fetch(API_BASE + '/api/test/' + encodeURIComponent(scope) + '/' + encodeURIComponent(testId));
|
|
5476
5476
|
const test = await res.json();
|
|
5477
5477
|
if (!res.ok) { showToast(test.error || 'Failed to load test'); return; }
|
|
5478
|
-
reEditorState = { scope, testId, steps: JSON.parse(JSON.stringify(test.steps || [])), testName: test.name || '', url: test.url || '', reusable: !!test.reusable, setup: test.setup || '', credentials: test.credentials || '', tags: test.tags || [], generateReport: test.generateReport !== false, browserZoom: test.browserZoom ||
|
|
5478
|
+
reEditorState = { scope, testId, steps: JSON.parse(JSON.stringify(test.steps || [])), testName: test.name || '', url: test.url || '', reusable: !!test.reusable, setup: test.setup || '', credentials: test.credentials || '', tags: test.tags || [], generateReport: test.generateReport !== false, browserZoom: test.browserZoom || 1 };
|
|
5479
5479
|
document.getElementById('re-name').value = reEditorState.testName;
|
|
5480
5480
|
document.getElementById('re-url').value = reEditorState.url;
|
|
5481
5481
|
document.getElementById('re-reusable').checked = reEditorState.reusable;
|
|
@@ -5737,7 +5737,7 @@ async function saveRecordedEdits() {
|
|
|
5737
5737
|
const url = document.getElementById('re-url').value.trim();
|
|
5738
5738
|
const reusable = document.getElementById('re-reusable').checked;
|
|
5739
5739
|
const generateReport = document.getElementById('re-generate-report')?.checked !== false;
|
|
5740
|
-
const browserZoom = parseFloat(document.getElementById('re-browser-zoom')?.value || '
|
|
5740
|
+
const browserZoom = parseFloat(document.getElementById('re-browser-zoom')?.value || '1');
|
|
5741
5741
|
const setup = document.getElementById('re-setup').value || null;
|
|
5742
5742
|
const credentials = document.getElementById('re-credentials')?.value || '';
|
|
5743
5743
|
const tags = (document.getElementById('re-tags')?.value || '').split(',').map(t => t.trim()).filter(Boolean);
|