fill method infrastructure ✓ 100.0%
Last updated: 2026-03-04T23:21:38.391Z
Metrics
LOC: 148
Complexity: 17
Params: 2
Coverage: 100.0% (47/47 lines, 0x executed)
Signature
fill(wrapper: HTMLElement, value: string): : Promise<boolean>
Architecture violations
- [warning] max-cyclomatic-complexity: 'fill' has cyclomatic complexity 17 (max 10)
- [warning] max-lines: 'fill' has 148 lines (max 80)
Source Code
async fill(wrapper: HTMLElement, value: string): Promise<boolean> {
const wrapperSelector = getUniqueSelector(wrapper);
const control = wrapper.querySelector<HTMLElement>(
".react-select__control",
);
if (!control) {
log.warn(`Control não encontrado em: ${wrapperSelector}`);
return false;
}
const isMulti =
wrapper.querySelector(".react-select__value-container--is-multi") !==
null;
// Searchable: has .react-select__input (real text input, not dummyInput)
const searchInput = wrapper.querySelector<HTMLInputElement>(
".react-select__input",
);
const isSearchable = searchInput !== null;
// The accessible combobox input for focus/keyboard events
const comboboxInput =
wrapper.querySelector<HTMLInputElement>("input[role='combobox']") ??
searchInput;
// Step 1: Focus the input so react-select sets isFocused=true.
// Without this, the first mousedown only focuses (doesn't open the menu).
comboboxInput?.focus();
await new Promise<void>((r) => setTimeout(r, 30));
// Step 2: Prefer clicking the dropdown indicator — its onMouseDown always
// calls openMenu() directly, unlike the control which first checks isFocused.
const indicator = wrapper.querySelector<HTMLElement>(
".react-select__dropdown-indicator",
);
const trigger = indicator ?? control;
trigger.dispatchEvent(
new MouseEvent("mousedown", { bubbles: true, cancelable: true }),
);
trigger.dispatchEvent(new MouseEvent("mouseup", { bubbles: true }));
trigger.dispatchEvent(new MouseEvent("click", { bubbles: true }));
// Step 3: ArrowDown on the focused input opens the menu in react-select v5
// regardless of onMouseDown outcome — acts as a reliable fallback.
comboboxInput?.dispatchEvent(
new KeyboardEvent("keydown", {
key: "ArrowDown",
keyCode: 40,
bubbles: true,
cancelable: true,
}),
);
// Step 4: Wait for the menu to render (may be portaled to document.body)
const menu = await waitForReactSelectMenu(wrapper, 1500);
if (!menu) {
log.warn(`Menu react-select não apareceu para: ${wrapperSelector}`);
return false;
}
// Step 5: For searchable fields — type the value to filter options
// For async-loaded options (API calls), we need to poll until they appear.
if (isSearchable && searchInput) {
const nativeSetter = Object.getOwnPropertyDescriptor(
window.HTMLInputElement.prototype,
"value",
)?.set;
nativeSetter
? nativeSetter.call(searchInput, value)
: (searchInput.value = value);
searchInput.dispatchEvent(new Event("input", { bubbles: true }));
searchInput.dispatchEvent(new Event("change", { bubbles: true }));
// Poll for options to appear (local or async-loaded)
await waitForAsyncOptions(menu, 2500);
}
let available = Array.from(
menu.querySelectorAll<HTMLElement>(
".react-select__option:not(.react-select__option--is-disabled)",
),
);
// Step 6: If filtering removed all options, clear the input so the full
// list is restored and we can still pick the closest match.
if (available.length === 0 && isSearchable && searchInput) {
log.debug(
`Nenhuma opção após filtro — limpando input para: ${wrapperSelector}`,
);
const nativeSetter = Object.getOwnPropertyDescriptor(
window.HTMLInputElement.prototype,
"value",
)?.set;
nativeSetter
? nativeSetter.call(searchInput, "")
: (searchInput.value = "");
searchInput.dispatchEvent(new Event("input", { bubbles: true }));
// Wait for full list to re-populate (async or local)
await waitForAsyncOptions(menu, 1500);
available = Array.from(
menu.querySelectorAll<HTMLElement>(
".react-select__option:not(.react-select__option--is-disabled)",
),
);
}
if (available.length === 0) {
log.warn(`Nenhuma opção disponível para: ${wrapperSelector}`);
// Close dropdown
document.body.dispatchEvent(
new MouseEvent("mousedown", { bubbles: true }),
);
return false;
}
if (isMulti) {
const count = Math.min(3, available.length);
const shuffled = [...available]
.sort(() => Math.random() - 0.5)
.slice(0, count);
for (const opt of shuffled) {
opt.dispatchEvent(new MouseEvent("mousedown", { bubbles: true }));
opt.dispatchEvent(new MouseEvent("click", { bubbles: true }));
await new Promise<void>((r) => setTimeout(r, 60));
}
// Close the menu if still open after multi selection
document.body.dispatchEvent(
new MouseEvent("mousedown", { bubbles: true }),
);
return true;
}
// Single-select: prefer option whose text matches the desired value
const lower = value.toLowerCase();
const matched =
available.find(
(opt) =>
opt.textContent?.toLowerCase().includes(lower) ||
opt.dataset["value"]?.toLowerCase() === lower,
) ?? available[0];
matched.dispatchEvent(new MouseEvent("mousedown", { bubbles: true }));
matched.dispatchEvent(new MouseEvent("click", { bubbles: true }));
return true;
},
Dependencies (Outgoing)
| Target | Type |
|---|---|
| waitForReactSelectMenu | calls |
| waitForAsyncOptions | calls |
No incoming dependencies.