halo-agent 1.3.3 → 1.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/filler.js +47 -28
- package/package.json +1 -1
package/filler.js
CHANGED
|
@@ -17,22 +17,11 @@ function jitter(base) {
|
|
|
17
17
|
* Fires the full event sequence that frameworks listen for.
|
|
18
18
|
*/
|
|
19
19
|
async function setFieldValue(page, selector, value) {
|
|
20
|
+
// Wrapper that delegates to the React-aware locator variant.
|
|
20
21
|
try {
|
|
21
|
-
|
|
22
|
-
await
|
|
23
|
-
|
|
24
|
-
if (!el) return;
|
|
25
|
-
el.focus();
|
|
26
|
-
// React's synthetic event system requires using the native setter
|
|
27
|
-
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')?.set
|
|
28
|
-
|| Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, 'value')?.set;
|
|
29
|
-
if (nativeInputValueSetter) nativeInputValueSetter.call(el, val);
|
|
30
|
-
el.dispatchEvent(new Event('focus', { bubbles: true }));
|
|
31
|
-
el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
32
|
-
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
33
|
-
el.dispatchEvent(new Event('blur', { bubbles: true }));
|
|
34
|
-
}, { sel: selector, val: value });
|
|
35
|
-
return true;
|
|
22
|
+
const loc = page.locator(selector).first();
|
|
23
|
+
await loc.waitFor({ timeout: 3000 });
|
|
24
|
+
return await setFieldValueByHandle(page, loc, value);
|
|
36
25
|
} catch {
|
|
37
26
|
return false;
|
|
38
27
|
}
|
|
@@ -41,23 +30,53 @@ async function setFieldValue(page, selector, value) {
|
|
|
41
30
|
/**
|
|
42
31
|
* Set a field value by Playwright element handle (used after semantic matching).
|
|
43
32
|
*/
|
|
44
|
-
async function setFieldValueByHandle(page,
|
|
33
|
+
async function setFieldValueByHandle(page, elementOrLocator, value) {
|
|
34
|
+
// Strategy ladder, hardest-React-friendly first.
|
|
35
|
+
// The Reddit-Greenhouse run proved everything below the FIRST strategy
|
|
36
|
+
// fired but the field still appeared empty: React's controlled-input
|
|
37
|
+
// validator rejects programmatic value writes that don't go through a
|
|
38
|
+
// real event pipeline. Playwright's locator.fill() simulates a real
|
|
39
|
+
// user typing (pointerdown → focus → keystroke events → blur) and IS
|
|
40
|
+
// the only thing modern React forms (Greenhouse, Ashby, Lever, Workday)
|
|
41
|
+
// reliably accept. We try it first and fall back to native-setter only
|
|
42
|
+
// if Playwright's path fails (rare; mostly when the element isn't a
|
|
43
|
+
// standard input).
|
|
44
|
+
|
|
45
|
+
// 1. Playwright native fill — works for >99% of React forms
|
|
46
|
+
try {
|
|
47
|
+
await elementOrLocator.fill(String(value), { timeout: 5000 });
|
|
48
|
+
// Verify the value actually stuck (React might have rejected it)
|
|
49
|
+
const got = await elementOrLocator.inputValue({ timeout: 1000 }).catch(() => null);
|
|
50
|
+
if (got && got.trim()) return true;
|
|
51
|
+
// If empty after fill, React rejected it — fall through to manual setter
|
|
52
|
+
} catch {
|
|
53
|
+
// Locator may not support .fill() (custom div pretending to be input).
|
|
54
|
+
// Fall through.
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// 2. Click + keyboard.type — slower but bullets through React harder
|
|
45
58
|
try {
|
|
46
|
-
await
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
59
|
+
await elementOrLocator.click({ timeout: 2000 });
|
|
60
|
+
await elementOrLocator.fill('', { timeout: 2000 }).catch(() => {});
|
|
61
|
+
await page.keyboard.type(String(value), { delay: 20 });
|
|
62
|
+
const got = await elementOrLocator.inputValue({ timeout: 1000 }).catch(() => null);
|
|
63
|
+
if (got && got.trim()) return true;
|
|
64
|
+
} catch {}
|
|
65
|
+
|
|
66
|
+
// 3. Native setter dance — last resort for non-standard elements
|
|
67
|
+
try {
|
|
68
|
+
await elementOrLocator.evaluate((el, val) => {
|
|
51
69
|
el.focus();
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
if (
|
|
55
|
-
el.dispatchEvent(new Event('focus', { bubbles: true }));
|
|
70
|
+
const proto = el.tagName === 'TEXTAREA' ? HTMLTextAreaElement.prototype : HTMLInputElement.prototype;
|
|
71
|
+
const setter = Object.getOwnPropertyDescriptor(proto, 'value')?.set;
|
|
72
|
+
if (setter) setter.call(el, val); else el.value = val;
|
|
56
73
|
el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
57
74
|
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
58
|
-
el.
|
|
59
|
-
}, value);
|
|
60
|
-
|
|
75
|
+
el.blur();
|
|
76
|
+
}, String(value));
|
|
77
|
+
// Verify
|
|
78
|
+
const got = await elementOrLocator.inputValue({ timeout: 1000 }).catch(() => null);
|
|
79
|
+
return !!(got && got.trim());
|
|
61
80
|
} catch {
|
|
62
81
|
return false;
|
|
63
82
|
}
|