Poll for submission success instead of single check
After clicking submit, poll up to 4 times (2.5s + 3x2s = ~8.5s total) for success text or form disappearance. Handles invisible reCAPTCHA verification delay. Stops early on validation errors. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -20,8 +20,11 @@ export async function apply(page, job, formFiller) {
|
||||
await page.$('input[type="file"][name*="resume"]') ||
|
||||
await page.$('input[type="file"]');
|
||||
if (fileInput) {
|
||||
await fileInput.setInputFiles(formFiller.profile.resume_path).catch(() => {});
|
||||
await page.waitForTimeout(1500);
|
||||
const hasFile = await fileInput.evaluate(el => !!el.value);
|
||||
if (!hasFile) {
|
||||
await fileInput.setInputFiles(formFiller.profile.resume_path).catch(() => {});
|
||||
await page.waitForTimeout(1500);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -149,28 +149,34 @@ async function fillAndSubmit(page, job, formFiller, meta, opts) {
|
||||
|
||||
if (hasSubmit) {
|
||||
await submitBtn.click();
|
||||
await page.waitForTimeout(SUBMIT_WAIT);
|
||||
|
||||
// Wait for submission — poll for success or form disappearance
|
||||
// (invisible reCAPTCHA + server round-trip can take several seconds)
|
||||
const verifySelector = opts.verifySelector || 'form button[type="submit"]:not([disabled])';
|
||||
const postSubmit = await page.evaluate((vs) => {
|
||||
const text = (document.body.innerText || '').toLowerCase();
|
||||
// Check for validation errors (red text, error messages)
|
||||
const errorEls = document.querySelectorAll('[class*="error"], [class*="invalid"], [role="alert"]');
|
||||
const validationErrors = Array.from(errorEls)
|
||||
.map(el => el.innerText?.trim())
|
||||
.filter(t => t && t.length < 200)
|
||||
.slice(0, 3);
|
||||
return {
|
||||
hasSuccess: text.includes('application submitted') || text.includes('successfully applied') ||
|
||||
text.includes('thank you') || text.includes('application received') ||
|
||||
text.includes('application has been') || text.includes('we received your'),
|
||||
hasForm: !!document.querySelector(vs),
|
||||
validationErrors,
|
||||
};
|
||||
}, verifySelector).catch(() => ({ hasSuccess: false, hasForm: false, validationErrors: [] }));
|
||||
let postSubmit = { hasSuccess: false, hasForm: true, validationErrors: [] };
|
||||
for (let wait = 0; wait < 4; wait++) {
|
||||
await page.waitForTimeout(wait === 0 ? SUBMIT_WAIT : 2000);
|
||||
postSubmit = await page.evaluate((vs) => {
|
||||
const text = (document.body.innerText || '').toLowerCase();
|
||||
const errorEls = document.querySelectorAll('[class*="error"], [class*="invalid"], [role="alert"]');
|
||||
const validationErrors = Array.from(errorEls)
|
||||
.map(el => el.innerText?.trim())
|
||||
.filter(t => t && t.length < 200)
|
||||
.slice(0, 3);
|
||||
return {
|
||||
hasSuccess: text.includes('application submitted') || text.includes('successfully applied') ||
|
||||
text.includes('thank you') || text.includes('application received') ||
|
||||
text.includes('application has been') || text.includes('we received your'),
|
||||
hasForm: !!document.querySelector(vs),
|
||||
validationErrors,
|
||||
};
|
||||
}, verifySelector).catch(() => ({ hasSuccess: false, hasForm: false, validationErrors: [] }));
|
||||
|
||||
if (postSubmit.hasSuccess || !postSubmit.hasForm) {
|
||||
return { status: 'submitted', meta };
|
||||
if (postSubmit.hasSuccess || !postSubmit.hasForm) {
|
||||
return { status: 'submitted', meta };
|
||||
}
|
||||
// Stop polling if validation errors appeared (form didn't submit)
|
||||
if (postSubmit.validationErrors.length > 0) break;
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user