skopix 2.0.64 → 2.0.66
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 +24 -0
- package/package.json +1 -1
- package/web/app/index.html +61 -2
|
@@ -1843,6 +1843,19 @@ export async function dashboardCommand(options) {
|
|
|
1843
1843
|
}
|
|
1844
1844
|
|
|
1845
1845
|
// ─── STEP LIBRARY ──────────────────────────────────────────────────
|
|
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 => (s.stableSelector||s.selector||'').toLowerCase() === sel))
|
|
1855
|
+
.map(t => ({ id: t.id, name: t.name, scope: t.scope }));
|
|
1856
|
+
sendJSON(res, 200, usages);
|
|
1857
|
+
return;
|
|
1858
|
+
}
|
|
1846
1859
|
if (pathname === '/api/step-library' && method === 'GET') {
|
|
1847
1860
|
sendJSON(res, 200, await listLibrarySteps(suitesDir));
|
|
1848
1861
|
return;
|
|
@@ -3998,6 +4011,17 @@ async function startStepTester(testerId, url, selector, mode, steps) {
|
|
|
3998
4011
|
// Push page content left to avoid covering elements
|
|
3999
4012
|
document.body.style.marginRight = '320px';
|
|
4000
4013
|
document.body.style.boxSizing = 'border-box';
|
|
4014
|
+
// Also fix any fixed/sticky headers
|
|
4015
|
+
const fixedEls = Array.from(document.querySelectorAll('*')).filter(el => {
|
|
4016
|
+
const s = window.getComputedStyle(el);
|
|
4017
|
+
return (s.position === 'fixed' || s.position === 'sticky') && el.id !== '__skopix_preview';
|
|
4018
|
+
});
|
|
4019
|
+
fixedEls.forEach(el => {
|
|
4020
|
+
const s = window.getComputedStyle(el);
|
|
4021
|
+
const right = parseInt(s.right) || 0;
|
|
4022
|
+
el.style.right = (right + 320) + 'px';
|
|
4023
|
+
el.setAttribute('data-skopix-fixed', '1');
|
|
4024
|
+
});
|
|
4001
4025
|
const tb = document.createElement('div');
|
|
4002
4026
|
tb.id = '__skopix_preview';
|
|
4003
4027
|
tb.style.cssText = [
|
package/package.json
CHANGED
package/web/app/index.html
CHANGED
|
@@ -1825,6 +1825,15 @@ body.viewer-mode .saved-test-row { cursor: default !important; }
|
|
|
1825
1825
|
</div>
|
|
1826
1826
|
</div>
|
|
1827
1827
|
|
|
1828
|
+
<!-- USAGES POPOVER -->
|
|
1829
|
+
<div id="lib-usages-popover" style="display:none;position:fixed;z-index:9999;background:var(--surface);border:1px solid var(--border);border-radius:10px;padding:0;min-width:220px;max-width:320px;box-shadow:0 8px 32px rgba(0,0,0,0.4)">
|
|
1830
|
+
<div style="padding:10px 14px;border-bottom:1px solid var(--border);font-family:var(--mono);font-size:10px;color:var(--muted);letter-spacing:0.1em;display:flex;justify-content:space-between;align-items:center">
|
|
1831
|
+
<span>USED IN TESTS</span>
|
|
1832
|
+
<span style="cursor:pointer;color:var(--muted)" onclick="closeUsagesPopover()">✕</span>
|
|
1833
|
+
</div>
|
|
1834
|
+
<div id="lib-usages-list" style="max-height:240px;overflow-y:auto;padding:6px 0"></div>
|
|
1835
|
+
</div>
|
|
1836
|
+
|
|
1828
1837
|
<div class="view" id="view-suites">
|
|
1829
1838
|
<div class="topbar">
|
|
1830
1839
|
<div>
|
|
@@ -5787,7 +5796,11 @@ function renderLibrarySteps(steps) {
|
|
|
5787
5796
|
${s.defaultAction||'—'}
|
|
5788
5797
|
</span>
|
|
5789
5798
|
</td>
|
|
5790
|
-
<td style="padding:12px 16px;font-family:var(--mono);font-size:12px
|
|
5799
|
+
<td style="padding:12px 16px;font-family:var(--mono);font-size:12px">
|
|
5800
|
+
${(s.usageCount||0) > 0
|
|
5801
|
+
? `<span class="lib-uses-btn" data-id="${escapeAttr(s.id)}" style="color:#22d3ee;cursor:pointer;text-decoration:underline;text-underline-offset:2px">${s.usageCount||0}</span>`
|
|
5802
|
+
: `<span style="color:var(--muted2)">0</span>`}
|
|
5803
|
+
</td>
|
|
5791
5804
|
<td style="padding:12px 16px;text-align:right">
|
|
5792
5805
|
<button class="btn btn-ghost lib-test-btn" style="padding:4px 10px;font-size:11px;margin-right:4px;color:#f59e0b;border-color:rgba(245,158,11,0.3)" data-id="${escapeAttr(s.id)}" data-selector="${escapeAttr(sel)}">Test</button>
|
|
5793
5806
|
<button class="btn btn-ghost lib-edit-btn" style="padding:4px 10px;font-size:11px;margin-right:4px" data-id="${escapeAttr(s.id)}">Edit</button>
|
|
@@ -5907,7 +5920,51 @@ function populateLibraryTagFilter() {
|
|
|
5907
5920
|
sel.onchange = prev;
|
|
5908
5921
|
}
|
|
5909
5922
|
|
|
5910
|
-
// ──
|
|
5923
|
+
// ── USAGES POPOVER ───────────────────────────────────────────────────────────
|
|
5924
|
+
async function showUsagesPopover(id, anchorEl) {
|
|
5925
|
+
const popover = document.getElementById('lib-usages-popover');
|
|
5926
|
+
const list = document.getElementById('lib-usages-list');
|
|
5927
|
+
list.innerHTML = '<div style="padding:12px 14px;font-family:var(--mono);font-size:11px;color:var(--muted)">Loading...</div>';
|
|
5928
|
+
popover.style.display = 'block';
|
|
5929
|
+
|
|
5930
|
+
// Position near the clicked element
|
|
5931
|
+
const rect = anchorEl.getBoundingClientRect();
|
|
5932
|
+
const top = rect.bottom + 8;
|
|
5933
|
+
const left = Math.min(rect.left, window.innerWidth - 340);
|
|
5934
|
+
popover.style.top = top + 'px';
|
|
5935
|
+
popover.style.left = left + 'px';
|
|
5936
|
+
|
|
5937
|
+
try {
|
|
5938
|
+
const res = await fetch(API_BASE + '/api/step-library/' + encodeURIComponent(id) + '/usages');
|
|
5939
|
+
const usages = await res.json();
|
|
5940
|
+
if (!usages.length) {
|
|
5941
|
+
list.innerHTML = '<div style="padding:12px 14px;font-family:var(--mono);font-size:11px;color:var(--muted)">No tests found using this selector</div>';
|
|
5942
|
+
return;
|
|
5943
|
+
}
|
|
5944
|
+
list.innerHTML = usages.map(u => `
|
|
5945
|
+
<div style="padding:8px 14px;display:flex;align-items:center;gap:8px;border-bottom:1px solid var(--border);cursor:pointer"
|
|
5946
|
+
onmouseover="this.style.background='var(--surface2)'" onmouseout="this.style.background=''"
|
|
5947
|
+
onclick="closeUsagesPopover();openTestEditorById('${escapeAttr(u.scope)}','${escapeAttr(u.id)}')">
|
|
5948
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="flex-shrink:0;color:var(--muted)"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
|
|
5949
|
+
<span style="font-size:12px;color:var(--text)">${escapeHtml(u.name)}</span>
|
|
5950
|
+
<span style="font-family:var(--mono);font-size:10px;color:var(--muted);margin-left:auto">${escapeHtml(u.scope)}</span>
|
|
5951
|
+
</div>`).join('');
|
|
5952
|
+
} catch {
|
|
5953
|
+
list.innerHTML = '<div style="padding:12px 14px;color:var(--red);font-size:12px">Failed to load</div>';
|
|
5954
|
+
}
|
|
5955
|
+
}
|
|
5956
|
+
|
|
5957
|
+
function closeUsagesPopover() {
|
|
5958
|
+
document.getElementById('lib-usages-popover').style.display = 'none';
|
|
5959
|
+
}
|
|
5960
|
+
|
|
5961
|
+
// Close popover when clicking outside
|
|
5962
|
+
document.addEventListener('click', (e) => {
|
|
5963
|
+
const popover = document.getElementById('lib-usages-popover');
|
|
5964
|
+
if (popover && !popover.contains(e.target) && !e.target.closest('.lib-uses-btn')) {
|
|
5965
|
+
closeUsagesPopover();
|
|
5966
|
+
}
|
|
5967
|
+
});
|
|
5911
5968
|
let stepTesterState = { testerId: null };
|
|
5912
5969
|
|
|
5913
5970
|
function openLibraryStepTester(id, selector) {
|
|
@@ -5969,9 +6026,11 @@ document.addEventListener('click', (e) => {
|
|
|
5969
6026
|
const testBtn = e.target.closest('.lib-test-btn');
|
|
5970
6027
|
const editBtn = e.target.closest('.lib-edit-btn');
|
|
5971
6028
|
const deleteBtn = e.target.closest('.lib-delete-btn');
|
|
6029
|
+
const usesBtn = e.target.closest('.lib-uses-btn');
|
|
5972
6030
|
if (testBtn) { openLibraryStepTester(testBtn.dataset.id, testBtn.dataset.selector); }
|
|
5973
6031
|
if (editBtn) { editLibraryStep(editBtn.dataset.id); }
|
|
5974
6032
|
if (deleteBtn) { deleteLibraryStepUI(deleteBtn.dataset.id, deleteBtn.dataset.name); }
|
|
6033
|
+
if (usesBtn) { showUsagesPopover(usesBtn.dataset.id, usesBtn); }
|
|
5975
6034
|
});
|
|
5976
6035
|
|
|
5977
6036
|
function openAddLibraryStep() {
|