skopix 2.0.88 → 2.0.90
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 +16 -0
- package/core/llm.js +19 -19
- package/package.json +1 -1
- package/web/app/index.html +21 -1
|
@@ -3885,6 +3885,9 @@ async function matchStepsToLibrary(suitesDir, steps) {
|
|
|
3885
3885
|
const matched = steps.map(step => {
|
|
3886
3886
|
const sSel = (step.stableSelector || step.selector || '').toLowerCase().trim();
|
|
3887
3887
|
if (!sSel) return step;
|
|
3888
|
+
// Never replace steps that already have stable selectors
|
|
3889
|
+
const isStable = /pi-test-identifier|aria-label|data-testid|\[title=|has-text\(/.test(sSel);
|
|
3890
|
+
if (isStable) return step;
|
|
3888
3891
|
|
|
3889
3892
|
const match = library.find(lib => {
|
|
3890
3893
|
const lSel = (lib.stableSelector || lib.selector || '').toLowerCase().trim();
|
|
@@ -3981,8 +3984,21 @@ async function syncSelectorsInternal(suitesDir, fromSelectors, toSelector, toNam
|
|
|
3981
3984
|
(sel.match(/\.[a-z][a-z0-9_-]+|\[[\w-]+=["'][^"']+["']\]|#[a-z][a-z0-9_-]+/g) || []).filter(t => t.length > 5)
|
|
3982
3985
|
);
|
|
3983
3986
|
|
|
3987
|
+
function isStableSel(sel) {
|
|
3988
|
+
if (!sel) return false;
|
|
3989
|
+
if (/pi-test-identifier/.test(sel)) return true;
|
|
3990
|
+
if (/aria-label/.test(sel)) return true;
|
|
3991
|
+
if (/data-testid/.test(sel)) return true;
|
|
3992
|
+
if (/\[title=/.test(sel)) return true;
|
|
3993
|
+
if (/has-text\(/.test(sel)) return true;
|
|
3994
|
+
if (/^#[a-z][a-z0-9_-]+$/i.test(sel)) return true;
|
|
3995
|
+
return false;
|
|
3996
|
+
}
|
|
3997
|
+
|
|
3984
3998
|
function matchesSel(sSel) {
|
|
3985
3999
|
if (!sSel) return false;
|
|
4000
|
+
// Never touch steps that already have stable selectors — prevents false matches
|
|
4001
|
+
if (isStableSel(sSel)) return false;
|
|
3986
4002
|
const sLower = sSel.toLowerCase();
|
|
3987
4003
|
if (fromLower.includes(sLower)) return true;
|
|
3988
4004
|
if (allFromTokens.some(tok => sLower.includes(tok))) return true;
|
package/core/llm.js
CHANGED
|
@@ -552,25 +552,25 @@ export async function processRecording({ steps, testName, url, provider, apiKey,
|
|
|
552
552
|
+ JSON.stringify(stepsContext, null, 2)
|
|
553
553
|
+ setupSection
|
|
554
554
|
+ '\n\nYour jobs:\n'
|
|
555
|
-
+ '1. For each step, write a STABLE SELECTOR
|
|
556
|
-
+ '
|
|
557
|
-
+ '
|
|
558
|
-
+ '
|
|
559
|
-
+ '
|
|
560
|
-
+ '
|
|
561
|
-
+ '
|
|
562
|
-
+ '
|
|
563
|
-
+ '
|
|
564
|
-
+ '
|
|
565
|
-
+ '
|
|
566
|
-
+ '
|
|
567
|
-
+ '
|
|
568
|
-
+ '
|
|
569
|
-
+ ' - NEVER use
|
|
570
|
-
+ ' -
|
|
571
|
-
+ ' -
|
|
572
|
-
+ ' -
|
|
573
|
-
+ ' - For
|
|
555
|
+
+ '1. For each step, write a STABLE SELECTOR using this STRICT priority order — always use the HIGHEST priority option available:\n'
|
|
556
|
+
+ ' PRIORITY 1 (ALWAYS USE IF AVAILABLE): piTestId field → [pi-test-identifier="VALUE"]\n'
|
|
557
|
+
+ ' PRIORITY 2: parentTestId field (when clicking icon inside element) → [pi-test-identifier="parentTestId"]\n'
|
|
558
|
+
+ ' PRIORITY 3: dataTestId field → [data-testid="VALUE"]\n'
|
|
559
|
+
+ ' PRIORITY 4: Unique non-random id → #id\n'
|
|
560
|
+
+ ' PRIORITY 5: title attribute (from element.title field) → a[title="VALUE"] or button[title="VALUE"]\n'
|
|
561
|
+
+ ' PRIORITY 6: Semantic selector → button:has-text("Login"), input[name="email"]\n'
|
|
562
|
+
+ ' PRIORITY 7: Class-based → .meaningful-class-name\n'
|
|
563
|
+
+ ' PRIORITY 8: Original selector as fallback\n'
|
|
564
|
+
+ '\n'
|
|
565
|
+
+ ' EXAMPLE: If element has piTestId="ChartEditor.saveChartButton" AND title="Save" → use [pi-test-identifier="ChartEditor.saveChartButton"] NOT a[title="Save"]\n'
|
|
566
|
+
+ ' EXAMPLE: If element has piTestId=null AND title="Create new chart" → use a[title="Create new chart"]\n'
|
|
567
|
+
+ '\n'
|
|
568
|
+
+ ' HARD RULES:\n'
|
|
569
|
+
+ ' - NEVER use IDs with random hex/numbers (e.g. #chart-abc123, #ng-view-1)\n'
|
|
570
|
+
+ ' - NEVER use :nth-child or :nth-of-type\n'
|
|
571
|
+
+ ' - NEVER invent attributes — only use what is in the captured element data\n'
|
|
572
|
+
+ ' - pi-test-identifier values ending in 5+ digits are DYNAMIC — use *= e.g. [pi-test-identifier*="ChartColumn.sort.desc"]\n'
|
|
573
|
+
+ ' - For icon elements (<i>, <svg>): use piTestId or parentTestId as anchor, never bare i.fa-something\n\n'
|
|
574
574
|
+ '2. For each step, write a SHORT human-readable description (max 10 words). Examples:\n'
|
|
575
575
|
+ ' - "Click the Login button"\n'
|
|
576
576
|
+ ' - "Type username into email field"\n'
|
package/package.json
CHANGED
package/web/app/index.html
CHANGED
|
@@ -6777,10 +6777,30 @@ async function saveBuiltTest() {
|
|
|
6777
6777
|
}));
|
|
6778
6778
|
|
|
6779
6779
|
try {
|
|
6780
|
+
showToast('Processing steps...');
|
|
6781
|
+
|
|
6782
|
+
// Run through LLM to generate Playwright code
|
|
6783
|
+
let processedSteps = steps;
|
|
6784
|
+
let playwrightJs = '';
|
|
6785
|
+
let playwrightTs = '';
|
|
6786
|
+
try {
|
|
6787
|
+
const procRes = await fetch(API_BASE + '/api/record/process', {
|
|
6788
|
+
method: 'POST',
|
|
6789
|
+
headers: { 'Content-Type': 'application/json' },
|
|
6790
|
+
body: JSON.stringify({ steps, testName: name, url }),
|
|
6791
|
+
});
|
|
6792
|
+
if (procRes.ok) {
|
|
6793
|
+
const proc = await procRes.json();
|
|
6794
|
+
processedSteps = proc.steps || steps;
|
|
6795
|
+
playwrightJs = proc.playwrightJs || '';
|
|
6796
|
+
playwrightTs = proc.playwrightTs || '';
|
|
6797
|
+
}
|
|
6798
|
+
} catch {}
|
|
6799
|
+
|
|
6780
6800
|
const res = await fetch(API_BASE + '/api/record/save', {
|
|
6781
6801
|
method: 'POST',
|
|
6782
6802
|
headers: { 'Content-Type': 'application/json' },
|
|
6783
|
-
body: JSON.stringify({ scope: 'saved', name, url, steps, tags: [] }),
|
|
6803
|
+
body: JSON.stringify({ scope: 'saved', name, url, steps: processedSteps, playwrightJs, playwrightTs, tags: [] }),
|
|
6784
6804
|
});
|
|
6785
6805
|
if (!res.ok) throw new Error(await res.text());
|
|
6786
6806
|
showToast(`Test "${name}" saved`);
|