skopix 2.0.70 → 2.0.71

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.
@@ -1844,23 +1844,32 @@ export async function dashboardCommand(options) {
1844
1844
 
1845
1845
  // ─── STEP LIBRARY ──────────────────────────────────────────────────
1846
1846
  if (pathname.match(/^\/api\/step-library\/[^/]+\/usages$/) && method === 'GET') {
1847
- const id = decodeURIComponent(pathname.split('/')[3]);
1848
- const library = await listLibrarySteps(suitesDir);
1849
- const step = library.find(s => s.id === id);
1850
- if (!step) { sendJSON(res, 404, { error: 'Not found' }); return; }
1851
- const sel = (step.stableSelector || step.selector || '').toLowerCase();
1852
- const allTests = await listAllTests(suitesDir);
1853
- const usages = allTests
1854
- .filter(t => t.steps && t.steps.some(s => {
1855
- const sSel = (s.stableSelector||s.selector||'').toLowerCase();
1856
- if (!sSel) return false;
1857
- // Exact match
1858
- if (sSel === sel) return true;
1859
- // Similarity match — same element different selector format
1860
- return stepSimilarity({ selector: sSel }, { selector: sel }) >= 0.7;
1861
- }))
1862
- .map(t => ({ id: t.id, name: t.name, scope: t.scope }));
1863
- sendJSON(res, 200, usages);
1847
+ try {
1848
+ const id = decodeURIComponent(pathname.split('/')[3]);
1849
+ const library = await listLibrarySteps(suitesDir);
1850
+ const step = library.find(s => s.id === id);
1851
+ if (!step) { sendJSON(res, 404, { error: 'Not found' }); return; }
1852
+ const sel = (step.stableSelector || step.selector || '').toLowerCase();
1853
+ const allTests = await listAllTests(suitesDir);
1854
+ // Extract key tokens from selector (class names, attributes, ids)
1855
+ const keyTokens = sel.match(/\.[a-z][a-z0-9_-]+|\[[\w-]+=["'][^"']+["']\]|#[a-z][a-z0-9_-]+/g) || [];
1856
+ const usages = allTests
1857
+ .filter(t => t.steps && t.steps.some(s => {
1858
+ const sSel = (s.stableSelector||s.selector||'').toLowerCase();
1859
+ if (!sSel) return false;
1860
+ if (sSel === sel) return true;
1861
+ // Check if they share meaningful unique tokens (e.g. same fa-icon class, pi-test-identifier)
1862
+ if (keyTokens.length > 0) {
1863
+ const sharedKey = keyTokens.some(tok => sSel.includes(tok) && tok.length > 5);
1864
+ if (sharedKey) return true;
1865
+ }
1866
+ return stepSimilarity({ selector: sSel }, { selector: sel }) >= 0.6;
1867
+ }))
1868
+ .map(t => ({ id: t.id, name: t.name, scope: t.scope }));
1869
+ sendJSON(res, 200, usages);
1870
+ } catch (err) {
1871
+ sendJSON(res, 500, { error: err.message });
1872
+ }
1864
1873
  return;
1865
1874
  }
1866
1875
  if (pathname === '/api/step-library' && method === 'GET') {
@@ -1923,12 +1932,19 @@ export async function dashboardCommand(options) {
1923
1932
  const { fromSelectors, toSelector, toName } = JSON.parse(await readBody(req));
1924
1933
  const allTests = await listAllTests(suitesDir);
1925
1934
  let updated = 0;
1935
+ // Build key tokens from all fromSelectors for smart matching
1936
+ const allFromTokens = fromSelectors.flatMap(sel =>
1937
+ (sel.match(/\.[a-z][a-z0-9_-]+|\[[\w-]+=["'][^"']+["']\]|#[a-z][a-z0-9_-]+/g) || []).filter(t => t.length > 5)
1938
+ );
1926
1939
  for (const test of allTests) {
1927
1940
  if (!test.steps || !test.steps.length) continue;
1928
1941
  let changed = false;
1929
1942
  const newSteps = test.steps.map(step => {
1930
1943
  const sSel = (step.stableSelector||step.selector||'').toLowerCase();
1931
- if (fromSelectors.includes(sSel)) {
1944
+ const isMatch = fromSelectors.includes(sSel) ||
1945
+ allFromTokens.some(tok => sSel.includes(tok)) ||
1946
+ fromSelectors.some(fSel => stepSimilarity({ selector: sSel }, { selector: fSel }) >= 0.6);
1947
+ if (isMatch) {
1932
1948
  changed = true;
1933
1949
  updated++;
1934
1950
  return { ...step, stableSelector: toSelector, selector: toSelector, description: toName || step.description };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skopix",
3
- "version": "2.0.70",
3
+ "version": "2.0.71",
4
4
  "description": "Browser-based QA tool — record tests by using your app, replay them deterministically, generate Playwright code automatically",
5
5
  "main": "cli/index.js",
6
6
  "bin": {