Load every failed account before the Tradeify reset loop

The account list loads a screenful at a time, so a reset count larger than the
first page ran out of rows to click.

Counts the reset buttons rather than the rows, so the run log reports the
number that answers the question — are there enough accounts to reset `count`
times. A stretch of non-resettable rows does not stop it early: `stable` only
advances when the scroller was ALREADY at the bottom and nothing new matched,
and every appended batch pushes scrollHeight past scrollTop + clientHeight.
Simulated at 100 rows with the resettables in the middle, at the bottom, and
at the top only — all four layouts walk the whole list.

The container is named rather than guessed. The list scrolls inside
dashboard_body, not the document, and setting scrollTop on the wrong element
scrolls nothing at all — which fails as a low match count rather than an
error. Spelled as a selector list because the live markup is unconfirmed:
querySelector takes either form, and matching neither fails loudly.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Brandon Li
2026-08-31 00:41:30 -05:00
co-authored by Claude Opus 5
parent 159389be35
commit 5538215650
+26 -2
View File
@@ -164,9 +164,14 @@ export const FIRMS: Firm[] = [
// every site build, so they are not safe to select on. // every site build, so they are not safe to select on.
{ action: 'click', selector: 'a.add_account_btn', label: 'Open Add Account' }, { action: 'click', selector: 'a.add_account_btn', label: 'Open Add Account' },
{ action: 'waitFor', selector: 'div.account_types:nth-child(3) > div[role="radiogroup"] > div > div:nth-child(2)', timeoutSeconds: 30, label: 'Wait for form' },
{ action: 'click', selector: 'div.account_types:nth-child(3) > div[role="radiogroup"] > div > div:nth-child(2)'},
{ action: 'click', selector: 'div.account_types:nth-child(7) span:last-child'},
{ action: 'click', selector: 'div.summary_section div.MuiTextField-root input'},
{ action: 'type', selector: 'div.summary_section div.MuiTextField-root input', text: 'MX8'},
{ action: 'click', selector: 'div.summary_section div.MuiTextField-root + button'},
{ action: 'solveCaptcha', timeoutSeconds: 180, label: 'Solve the challenge' }, { action: 'solveCaptcha', timeoutSeconds: 180, label: 'Solve the challenge' },
{ action: 'click', selector: 'div.summary_section > button:last-child', label: 'Open Add Account' },
// TODO: the rest of the purchase flow. Confirm each selector with // TODO: the rest of the purchase flow. Confirm each selector with
// `clicker.py locate` before adding it here. // `clicker.py locate` before adding it here.
@@ -184,6 +189,25 @@ export const FIRMS: Firm[] = [
{ action: 'click', selector: 'div.MuiTabs-scroller button:nth-child(2)', label: 'Click Evaluation Tab'}, { action: 'click', selector: 'div.MuiTabs-scroller button:nth-child(2)', label: 'Click Evaluation Tab'},
{ action: 'click', selector: '.tab-head-right .MuiSwitch-colorPrimary.Mui-checked', skipIfNotFound: true, label: 'Show failed accounts' }, { action: 'click', selector: '.tab-head-right .MuiSwitch-colorPrimary.Mui-checked', skipIfNotFound: true, label: 'Show failed accounts' },
// The account list loads a screenful at a time, so a reset
// count larger than the first page would run out of rows to
// click. Walk it to the bottom first. Counting the reset
// buttons rather than the rows means it stops when the thing
// the loop actually clicks stops appearing.
//
// The list scrolls inside dashboard_body, not the document —
// the automatic guess walks up from a match looking for an
// overflow ancestor, and setting scrollTop on the wrong
// element scrolls nothing at all. Named here so it cannot
// guess wrong. Trim the pair once you know whether the site
// spells it as a class or an id.
{
action: 'scrollToLoad',
selector: '.status-failed button.reset_btn',
containerSelector: '.dashboard_body, #dashboard_body',
label: 'Load every failed account',
},
{ action: 'repeat', timesFrom: 'count', steps: [ { action: 'repeat', timesFrom: 'count', steps: [
// one purchase — the steps you already have // one purchase — the steps you already have
{ action: 'click', selector: '.status-failed button.reset_btn', label: 'Reset Account' }, { action: 'click', selector: '.status-failed button.reset_btn', label: 'Reset Account' },