diff --git a/lib/apply/easy_apply.mjs b/lib/apply/easy_apply.mjs index 11d7676..50b213e 100644 --- a/lib/apply/easy_apply.mjs +++ b/lib/apply/easy_apply.mjs @@ -207,7 +207,20 @@ export async function apply(page, job, formFiller) { console.log(` [step ${step}] clicking Submit`); await submitBtn.click({ timeout: APPLY_CLICK_TIMEOUT }).catch(() => {}); await page.waitForTimeout(SUBMIT_WAIT); - return { status: 'submitted', meta }; + + // Verify modal closed or success message appeared + const modalGone = !(await page.$(MODAL)); + const successVisible = await page.$('[class*="success"], [class*="confirmation"], [aria-label*="applied"]').catch(() => null); + if (modalGone || successVisible) { + console.log(` ✅ Submit confirmed — modal closed`); + return { status: 'submitted', meta }; + } + + // Modal still open — submit may have failed + console.log(` [step ${step}] ⚠️ Modal still open after Submit click`); + console.log(` Action: submit may have failed due to validation or network error`); + await dismissModal(page, MODAL); + return { status: 'incomplete', meta }; } // Stuck detection — progress hasn't changed and we've been through a few steps diff --git a/lib/form_filler.mjs b/lib/form_filler.mjs index 75e2bf0..a3614cf 100644 --- a/lib/form_filler.mjs +++ b/lib/form_filler.mjs @@ -148,12 +148,17 @@ export class FormFiller { * Waits for the dropdown to appear, then clicks the first option. * Scoped to the input's nearest container to avoid clicking wrong dropdowns. */ - async selectAutocomplete(page, inp) { - // Wait for dropdown to appear near the input - const option = await page.waitForSelector( - '[role="option"], [role="listbox"] li, ul[class*="autocomplete"] li', - { timeout: AUTOCOMPLETE_TIMEOUT, state: 'visible' } - ).catch(() => null); + async selectAutocomplete(page, container) { + // Wait for dropdown to appear — scope to container (modal) to avoid clicking wrong dropdowns + const selectors = '[role="option"], [role="listbox"] li, ul[class*="autocomplete"] li'; + const option = await container.waitForSelector(selectors, { + timeout: AUTOCOMPLETE_TIMEOUT, state: 'visible', + }).catch(() => { + // Fallback to page-level if container doesn't support waitForSelector (e.g. ElementHandle) + return page.waitForSelector(selectors, { + timeout: AUTOCOMPLETE_TIMEOUT, state: 'visible', + }).catch(() => null); + }); if (option) { await option.click().catch(() => {}); await page.waitForTimeout(AUTOCOMPLETE_WAIT); @@ -198,9 +203,10 @@ export class FormFiller { // Handle city/location autocomplete dropdowns const ll = lbl.toLowerCase(); if (ll.includes('city') || ll.includes('location') || ll.includes('located')) { - await this.selectAutocomplete(page, inp); + await this.selectAutocomplete(page, container); } - } else if (!answer) { + } else { + // No answer, or answer is cover letter (too long for a text input) — check if required if (await this.isRequired(inp)) unknown.push(lbl); } }