Fix missed removals from async ownership state rendering
Add/Remove button state loads asynchronously after page render, so a fixed delay could scan before all buttons updated and skip real candidates (confirmed via Strum's VCV Modules brand page). Add a button-state stabilization wait and multi-pass per-page scanning to catch late-rendering items.
This commit is contained in:
@@ -46,6 +46,30 @@ function pageUrl(page, mode) {
|
|||||||
return base.toString();
|
return base.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function waitForStableActionButtons(page, { stableForMs = 800, maxWaitMs = 8000 } = {}) {
|
||||||
|
function signature() {
|
||||||
|
const buttons = Array.from(document.querySelectorAll('button.library-add, button.library-remove'));
|
||||||
|
return buttons
|
||||||
|
.map((b) => `${b.className.includes('library-add') ? 'add' : 'remove'}:${b.className.includes('hidden') ? 'h' : 'v'}`)
|
||||||
|
.join('|');
|
||||||
|
}
|
||||||
|
|
||||||
|
const start = Date.now();
|
||||||
|
let lastSig = await page.evaluate(signature);
|
||||||
|
let stableSince = Date.now();
|
||||||
|
|
||||||
|
while (Date.now() - start < maxWaitMs) {
|
||||||
|
await page.waitForTimeout(200);
|
||||||
|
const sig = await page.evaluate(signature);
|
||||||
|
if (sig === lastSig) {
|
||||||
|
if (Date.now() - stableSince >= stableForMs) return;
|
||||||
|
} else {
|
||||||
|
lastSig = sig;
|
||||||
|
stableSince = Date.now();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function scanAndRemoveOnPage(page, dryRun) {
|
async function scanAndRemoveOnPage(page, dryRun) {
|
||||||
return page.evaluate(({ dryRunArg }) => {
|
return page.evaluate(({ dryRunArg }) => {
|
||||||
function isVisible(el) {
|
function isVisible(el) {
|
||||||
@@ -114,6 +138,27 @@ async function scanAndRemoveOnPage(page, dryRun) {
|
|||||||
}, { dryRunArg: dryRun });
|
}, { dryRunArg: dryRun });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function scanAndRemoveWithRetries(page, dryRun) {
|
||||||
|
let combined = await scanAndRemoveOnPage(page, dryRun);
|
||||||
|
|
||||||
|
// Extra passes catch modules whose ownership state renders after the initial stabilization wait.
|
||||||
|
for (let i = 0; i < 2; i += 1) {
|
||||||
|
await waitForStableActionButtons(page, { stableForMs: 600, maxWaitMs: 4000 });
|
||||||
|
const again = await scanAndRemoveOnPage(page, dryRun);
|
||||||
|
if (again.visibleRemoveButtons === 0 && again.removed === 0) break;
|
||||||
|
|
||||||
|
combined = {
|
||||||
|
url: again.url,
|
||||||
|
maxPage: again.maxPage,
|
||||||
|
visibleRemoveButtons: combined.visibleRemoveButtons + again.visibleRemoveButtons,
|
||||||
|
removed: combined.removed + again.removed,
|
||||||
|
keptHardwareClone: combined.keptHardwareClone + again.keptHardwareClone
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return combined;
|
||||||
|
}
|
||||||
|
|
||||||
function reportPage(pageNumber, info, dryRun) {
|
function reportPage(pageNumber, info, dryRun) {
|
||||||
const modeText = dryRun ? 'dry-run' : 'apply';
|
const modeText = dryRun ? 'dry-run' : 'apply';
|
||||||
const unchanged = info.removed === 0 ? 'yes' : 'no';
|
const unchanged = info.removed === 0 ? 'yes' : 'no';
|
||||||
@@ -205,7 +250,8 @@ async function main() {
|
|||||||
throw new Error('Not logged in on VCV Library after login detection. Aborting to avoid false no-op run.');
|
throw new Error('Not logged in on VCV Library after login detection. Aborting to avoid false no-op run.');
|
||||||
}
|
}
|
||||||
|
|
||||||
const first = await scanAndRemoveOnPage(page, opts.dryRun);
|
await waitForStableActionButtons(page);
|
||||||
|
const first = await scanAndRemoveWithRetries(page, opts.dryRun);
|
||||||
const lastPage = opts.maxPages
|
const lastPage = opts.maxPages
|
||||||
? Math.min(opts.startPage + opts.maxPages - 1, first.maxPage)
|
? Math.min(opts.startPage + opts.maxPages - 1, first.maxPage)
|
||||||
: first.maxPage;
|
: first.maxPage;
|
||||||
@@ -235,7 +281,8 @@ async function main() {
|
|||||||
const url = pageUrl(p, opts.mode);
|
const url = pageUrl(p, opts.mode);
|
||||||
console.log(`Navigating page ${p}/${lastPage}: ${url}`);
|
console.log(`Navigating page ${p}/${lastPage}: ${url}`);
|
||||||
await navigateWithRetry(page, url, opts.navTimeoutMs, `Page ${p} navigation`);
|
await navigateWithRetry(page, url, opts.navTimeoutMs, `Page ${p} navigation`);
|
||||||
const info = await scanAndRemoveOnPage(page, opts.dryRun);
|
await waitForStableActionButtons(page);
|
||||||
|
const info = await scanAndRemoveWithRetries(page, opts.dryRun);
|
||||||
addStats(info);
|
addStats(info);
|
||||||
reportPage(p, info, opts.dryRun);
|
reportPage(p, info, opts.dryRun);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user