skopix 2.0.78 → 2.0.80
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 +65 -10
- package/package.json +1 -1
|
@@ -3901,28 +3901,78 @@ async function saveSavedUrls(urls) {
|
|
|
3901
3901
|
}
|
|
3902
3902
|
// Internal helper — sync selectors across all tests
|
|
3903
3903
|
async function syncSelectorsInternal(suitesDir, fromSelectors, toSelector, toName) {
|
|
3904
|
-
const
|
|
3904
|
+
const files = await fs.readdir(suitesDir);
|
|
3905
|
+
const suiteFiles = files.filter(f => f.endsWith('.suite.yaml') || f.endsWith('.suite.yml'));
|
|
3905
3906
|
let updated = 0;
|
|
3906
3907
|
const fromLower = fromSelectors.map(s => s.toLowerCase());
|
|
3907
3908
|
const allFromTokens = fromLower.flatMap(sel =>
|
|
3908
3909
|
(sel.match(/\.[a-z][a-z0-9_-]+|\[[\w-]+=["'][^"']+["']\]|#[a-z][a-z0-9_-]+/g) || []).filter(t => t.length > 5)
|
|
3909
3910
|
);
|
|
3910
|
-
|
|
3911
|
-
|
|
3911
|
+
|
|
3912
|
+
function matchesSel(sSel) {
|
|
3913
|
+
if (!sSel) return false;
|
|
3914
|
+
const sLower = sSel.toLowerCase();
|
|
3915
|
+
if (fromLower.includes(sLower)) return true;
|
|
3916
|
+
if (allFromTokens.some(tok => sLower.includes(tok))) return true;
|
|
3917
|
+
return fromLower.some(fSel => stepSimilarity({ selector: sLower }, { selector: fSel }) >= 0.7);
|
|
3918
|
+
}
|
|
3919
|
+
|
|
3920
|
+
function syncSteps(steps) {
|
|
3921
|
+
if (!Array.isArray(steps)) return { steps, changed: false };
|
|
3912
3922
|
let changed = false;
|
|
3913
|
-
const newSteps =
|
|
3914
|
-
const
|
|
3915
|
-
|
|
3916
|
-
allFromTokens.some(tok => sSel.includes(tok)) ||
|
|
3917
|
-
fromLower.some(fSel => stepSimilarity({ selector: sSel }, { selector: fSel }) >= 0.7);
|
|
3918
|
-
if (isMatch && sSel) {
|
|
3923
|
+
const newSteps = steps.map(step => {
|
|
3924
|
+
const sel = step.stableSelector || step.selector || '';
|
|
3925
|
+
if (matchesSel(sel)) {
|
|
3919
3926
|
changed = true;
|
|
3920
3927
|
updated++;
|
|
3921
3928
|
return { ...step, stableSelector: toSelector, selector: toSelector, description: toName || step.description };
|
|
3922
3929
|
}
|
|
3923
3930
|
return step;
|
|
3924
3931
|
});
|
|
3925
|
-
|
|
3932
|
+
return { steps: newSteps, changed };
|
|
3933
|
+
}
|
|
3934
|
+
|
|
3935
|
+
for (const file of suiteFiles) {
|
|
3936
|
+
const filePath = path.join(suitesDir, file);
|
|
3937
|
+
try {
|
|
3938
|
+
const content = await fs.readFile(filePath, 'utf8');
|
|
3939
|
+
const data = yaml.parse(content);
|
|
3940
|
+
if (!data || !Array.isArray(data.tests)) continue;
|
|
3941
|
+
let fileChanged = false;
|
|
3942
|
+
|
|
3943
|
+
data.tests = data.tests.map(test => {
|
|
3944
|
+
let testChanged = false;
|
|
3945
|
+
// Sync structured steps
|
|
3946
|
+
if (Array.isArray(test.steps) && test.steps.length > 0) {
|
|
3947
|
+
const { steps: newSteps, changed } = syncSteps(test.steps);
|
|
3948
|
+
if (changed) { test = { ...test, steps: newSteps }; testChanged = true; }
|
|
3949
|
+
}
|
|
3950
|
+
// Sync playwrightJs code string
|
|
3951
|
+
if (test.playwrightJs && typeof test.playwrightJs === 'string') {
|
|
3952
|
+
let newJs = test.playwrightJs;
|
|
3953
|
+
for (const oldSel of fromSelectors) {
|
|
3954
|
+
if (newJs.includes(oldSel)) {
|
|
3955
|
+
newJs = newJs.split('"' + oldSel + '"').join('"' + toSelector + '"');
|
|
3956
|
+
newJs = newJs.split("'" + oldSel + "'").join("'" + toSelector + "'");
|
|
3957
|
+
// Update comment descriptions
|
|
3958
|
+
if (toName) {
|
|
3959
|
+
fromSelectors.forEach(s => {}); // placeholder — descriptions in comments are harder to match
|
|
3960
|
+
}
|
|
3961
|
+
testChanged = true;
|
|
3962
|
+
}
|
|
3963
|
+
}
|
|
3964
|
+
if (testChanged) test = { ...test, playwrightJs: newJs };
|
|
3965
|
+
}
|
|
3966
|
+
if (testChanged) fileChanged = true;
|
|
3967
|
+
return test;
|
|
3968
|
+
});
|
|
3969
|
+
|
|
3970
|
+
if (fileChanged) {
|
|
3971
|
+
await fs.writeFile(filePath, yaml.stringify(data));
|
|
3972
|
+
}
|
|
3973
|
+
} catch (err) {
|
|
3974
|
+
process.stderr.write('[sync] error processing ' + file + ': ' + err.message + '\n');
|
|
3975
|
+
}
|
|
3926
3976
|
}
|
|
3927
3977
|
return updated;
|
|
3928
3978
|
}
|
|
@@ -3967,6 +4017,9 @@ async function approvePendingStep(suitesDir, id) {
|
|
|
3967
4017
|
const { id: _id, addedAt: _a, ...stepData } = step;
|
|
3968
4018
|
const added = await addLibraryStep(suitesDir, stepData);
|
|
3969
4019
|
await savePendingSteps(pending.filter(s => s.id !== id));
|
|
4020
|
+
// Sync test descriptions to use the approved name
|
|
4021
|
+
const sel = stepData.stableSelector || stepData.selector || '';
|
|
4022
|
+
if (sel) await syncSelectorsInternal(suitesDir, [sel.toLowerCase()], sel, stepData.name || stepData.description || '');
|
|
3970
4023
|
return added;
|
|
3971
4024
|
}
|
|
3972
4025
|
|
|
@@ -3980,6 +4033,8 @@ async function approveAllPending(suitesDir) {
|
|
|
3980
4033
|
for (const step of pending) {
|
|
3981
4034
|
const { id: _id, addedAt: _a, ...stepData } = step;
|
|
3982
4035
|
await addLibraryStep(suitesDir, stepData);
|
|
4036
|
+
const sel = stepData.stableSelector || stepData.selector || '';
|
|
4037
|
+
if (sel) await syncSelectorsInternal(suitesDir, [sel.toLowerCase()], sel, stepData.name || stepData.description || '');
|
|
3983
4038
|
}
|
|
3984
4039
|
await savePendingSteps([]);
|
|
3985
4040
|
return pending.length;
|
package/package.json
CHANGED