diff --git a/lib/apply/easy_apply.mjs b/lib/apply/easy_apply.mjs index 34151af..33d7fa2 100644 --- a/lib/apply/easy_apply.mjs +++ b/lib/apply/easy_apply.mjs @@ -115,22 +115,25 @@ export async function apply(page, job, formFiller) { let eaBtn = await page.waitForSelector(LINKEDIN_APPLY_BUTTON_SELECTOR, { timeout: 12000, state: 'attached' }).catch(() => null); // Fallback: LinkedIn shows plain "Continue" when a draft exists (span > span > a) - // The has href containing /apply/ — click it to resume the draft + // The has href containing /apply/ — find it via evaluateHandle in each frame + // (page.$() may not pierce LinkedIn's specific shadow DOM setup) if (!eaBtn) { - // page.$() pierces shadow DOM; also check via evaluate in each frame - let applyLink = await page.$(`a[href*="/apply/"]`); - if (!applyLink) { - // Search frames directly (shadow DOM may block page.$) - for (const frame of page.frames()) { - applyLink = await frame.$(`a[href*="/apply/"]`).catch(() => null); - if (applyLink) break; - } - } - if (applyLink) { - const text = await applyLink.evaluate(el => (el.innerText || '').trim()).catch(() => ''); - if (/continue/i.test(text)) { - eaBtn = applyLink; - console.log(` ℹ️ Found "Continue" link (draft application)`); + for (const frame of page.frames()) { + const handle = await frame.evaluateHandle(() => { + const links = document.querySelectorAll('a[href*="/apply/"]'); + for (const a of links) { + if (/continue/i.test((a.innerText || '').trim())) return a; + } + return null; + }).catch(() => null); + if (handle) { + const el = handle.asElement(); + if (el) { + eaBtn = el; + console.log(` ℹ️ Found "Continue" link (draft application)`); + break; + } + await handle.dispose().catch(() => {}); } } }