--- id: "bbcb2adc-4150-44f2-8128-3546a92bafaa" name: "Lightning form fill via shadow DOM" micro_summary: "SF Lightning form fields live in NESTED shadow roots; deep-walk to lightning-combobox, whose trigger sits one level deeper inside lightning-base-combobox. Reliable recipe: open -> wait ~0.7s -> pick (options render lazily). Helpers in the config-workspace sf_combo.js. Direct URL ?recordTypeId= skips the record-type picker modal." status: "resolved" see_also: [] scope: "Fill and submit Salesforce Lightning record edit forms whose fields live in nested shadow DOM (comboboxes, required picklists), and create records via UI from browser-harness." warnings: "Inline JS inside the old `browser-harness -c \"...\"` runner breaks on escaped quotes; the current stdin runner (see Runtime Invocation) avoids that — load JS from a file and read() it when needed. Combobox options render ~600ms AFTER opening: always open -> wait ~0.7s -> pick, never open+read+click in one call." --- ## The shadow-DOM trap Top-level `document.querySelectorAll("lightning-combobox")` returns 0 — the record edit form is inside nested shadow roots. You must recurse shadow roots. And the clickable trigger is not on `lightning-combobox` directly; it is one level deeper inside its child `lightning-base-combobox` shadowRoot. ## Helpers: config-workspace sf_combo.js Inject once per navigation (`window.*` funcs are wiped on nav): - `__bh_combo(label)` — deep-walk shadow roots, find combobox by label (strips leading `*`). - `__bh_openCombo(label)` / `__bh_pickOption(label,value)` — open, then (after a wait) pick. Options render ~600ms AFTER opening, so never open+read+click in one call. - `__bh_clickBtn(text)` — deep click a button by visible text (e.g. "Save"). - `__bh_requiredFields()` — enumerate required-field labels+tags to know what a record type needs. ## Create a record (Case example) Direct URL SKIPS the record-type picker (more reliable than clicking shadow radios): `/lightning/o/Case/new?nooverride=1&recordTypeId=` Then: inject sf_combo.js -> openCombo -> wait 0.7 -> pickOption -> clickBtn("Save") -> poll url for `/lightning/r/`. ## Quoting trap The old `browser-harness -c "..."` runner broke on escaped quotes; the current stdin runner (see Runtime Invocation) is the safe path. Put JS in a FILE and `open(path).read()` it, then `cdp("Runtime.evaluate", expression=js)`.