fix: stuck detection uses progress bar value, not heading

Heading is always "Apply to <Company>" on every step — comparing it
caused false stuck detection on step 2. Now:
- Progress bar selector finds <progress> elements (no explicit role)
- Stuck detection re-reads progress AFTER clicking Next to see if it changed
- Threshold raised to 3 same-progress clicks before exiting

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-06 11:08:35 -08:00
parent 00bcfaca53
commit 51a4231b5d

View File

@@ -164,7 +164,6 @@ export async function apply(page, job, formFiller) {
// Step through modal // Step through modal
let lastProgress = '-1'; let lastProgress = '-1';
let lastHeading = '';
let samePageCount = 0; let samePageCount = 0;
for (let step = 0; step < LINKEDIN_MAX_MODAL_STEPS; step++) { for (let step = 0; step < LINKEDIN_MAX_MODAL_STEPS; step++) {
const modalStillOpen = await page.$(MODAL); const modalStillOpen = await page.$(MODAL);
@@ -173,10 +172,10 @@ export async function apply(page, job, formFiller) {
return { status: 'submitted', meta }; return { status: 'submitted', meta };
} }
// Read progress bar — use page.$() + evaluate on the handle // Read progress bar — LinkedIn uses <progress> element (no explicit role="progressbar")
const progressEl = await page.$(`${MODAL} [role="progressbar"]`); const progressEl = await page.$(`${MODAL} progress, ${MODAL} [role="progressbar"]`);
const progress = progressEl const progress = progressEl
? await progressEl.evaluate(el => el.getAttribute('aria-valuenow') || el.getAttribute('value') || el.style?.width || '').catch(() => '') ? await progressEl.evaluate(el => el.getAttribute('aria-valuenow') || el.value?.toString() || el.getAttribute('value') || el.style?.width || '').catch(() => '')
: ''; : '';
// Debug snapshot using ElementHandle operations (shadow DOM safe) // Debug snapshot using ElementHandle operations (shadow DOM safe)
@@ -229,14 +228,18 @@ export async function apply(page, job, formFiller) {
if (nextBtn) { if (nextBtn) {
console.log(` [step ${step}] clicking Next`); console.log(` [step ${step}] clicking Next`);
await nextBtn.click({ timeout: APPLY_CLICK_TIMEOUT }).catch(() => {}); await nextBtn.click({ timeout: APPLY_CLICK_TIMEOUT }).catch(() => {});
await page.waitForTimeout(CLICK_WAIT);
// Detect if we're stuck — same heading+progress means page didn't advance // Detect if we're stuck — wait for content to change after clicking Next
const curHeading = debugInfo.heading; await page.waitForTimeout(CLICK_WAIT);
if (curHeading === lastHeading && progress === lastProgress) { const newProgress = await (async () => {
const el = await page.$(`${MODAL} progress, ${MODAL} [role="progressbar"]`);
return el ? await el.evaluate(e => e.getAttribute('aria-valuenow') || e.value?.toString() || '').catch(() => '') : '';
})();
if (newProgress === progress) {
samePageCount++; samePageCount++;
if (samePageCount >= 2) { if (samePageCount >= 3) {
console.log(` [step ${step}] stuck — clicked Next but page didn't advance (${samePageCount} times)`); console.log(` [step ${step}] stuck — clicked Next ${samePageCount} times but progress unchanged at ${progress}`);
console.log(` Action: a required field may be unfilled. Check select dropdowns still at "Select an option"`); console.log(` Action: a required field may be unfilled. Check select dropdowns still at "Select an option"`);
await dismissModal(page, MODAL); await dismissModal(page, MODAL);
return { status: 'stuck', meta }; return { status: 'stuck', meta };
@@ -244,8 +247,7 @@ export async function apply(page, job, formFiller) {
} else { } else {
samePageCount = 0; samePageCount = 0;
} }
lastProgress = progress; lastProgress = newProgress;
lastHeading = curHeading;
continue; continue;
} }
@@ -259,7 +261,6 @@ export async function apply(page, job, formFiller) {
await reviewBtn.click({ timeout: APPLY_CLICK_TIMEOUT }).catch(() => {}); await reviewBtn.click({ timeout: APPLY_CLICK_TIMEOUT }).catch(() => {});
await page.waitForTimeout(CLICK_WAIT); await page.waitForTimeout(CLICK_WAIT);
lastProgress = progress; lastProgress = progress;
lastHeading = debugInfo.heading;
samePageCount = 0; samePageCount = 0;
continue; continue;
} }