Mark options of disabled select non-interactable (#540)

This commit is contained in:
Kerem Yilmaz
2024-07-03 01:38:50 -07:00
committed by GitHub
parent 7479918680
commit 22566f01ce
5 changed files with 65 additions and 2 deletions

View File

@@ -109,6 +109,9 @@ class ActionHandler:
if action.action_type in ActionHandler._handled_action_types:
actions_result: list[ActionResult] = []
if invalid_web_action_check := check_for_invalid_web_action(action, page, scraped_page, task, step):
return invalid_web_action_check
# do setup before action handler
if setup := ActionHandler._setup_action_types.get(action.action_type):
results = await setup(action, page, scraped_page, task, step)
@@ -158,6 +161,19 @@ class ActionHandler:
return [ActionFailure(e)]
def check_for_invalid_web_action(
action: actions.Action,
page: Page,
scraped_page: ScrapedPage,
task: Task,
step: Step,
) -> list[ActionResult]:
if isinstance(action, WebAction) and action.element_id not in scraped_page.id_to_element_dict:
return [ActionFailure(MissingElement(element_id=action.element_id), stop_execution_on_failure=False)]
return []
async def handle_solve_captcha_action(
action: actions.SolveCaptchaAction,
page: Page,

View File

@@ -7,6 +7,7 @@ from skyvern.webeye.string_util import remove_whitespace
class ActionResult(BaseModel):
success: bool
stop_execution_on_failure: bool = True
exception_type: str | None = None
exception_message: str | None = None
data: dict[str, Any] | list | str | None = None
@@ -67,6 +68,7 @@ class ActionFailure(ActionResult):
def __init__(
self,
exception: Exception,
stop_execution_on_failure: bool = True,
javascript_triggered: bool = False,
download_triggered: bool | None = None,
interacted_with_sibling: bool = False,
@@ -75,6 +77,7 @@ class ActionFailure(ActionResult):
super().__init__(
success=False,
exception_type=type(exception).__name__,
stop_execution_on_failure=stop_execution_on_failure,
exception_message=remove_whitespace(str(exception)),
javascript_triggered=javascript_triggered,
download_triggered=download_triggered,

View File

@@ -357,6 +357,11 @@ function isInteractable(element) {
return true;
}
// Check if the option's parent (select) is hidden or disabled
if (tagName === "option" && isHiddenOrDisabled(element.parentElement)) {
return false;
}
if (
tagName === "button" ||
tagName === "select" ||
@@ -718,7 +723,11 @@ async function buildTreeFromBody(frame = "main.frame", open_select = false) {
}
if (elementTagNameLower === "input" || elementTagNameLower === "textarea") {
attrs["value"] = element.value;
if (element.type === "radio") {
attrs["value"] = "" + element.checked + "";
} else {
attrs["value"] = element.value;
}
}
let elementObj = {
@@ -906,6 +915,11 @@ async function buildTreeFromBody(frame = "main.frame", open_select = false) {
const children = getChildElements(element);
for (let i = 0; i < children.length; i++) {
const childElement = children[i];
// Skip processing option-children of an non-interactable select element as they are already added to the select.options
if (childElement.tagName.toLowerCase() === "option") {
continue;
}
await processElement(childElement, parentId);
}
}
@@ -1367,3 +1381,21 @@ async function scrollToNextPage(draw_boxes) {
async function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
// Helper method for debugging
function findNodeById(arr, targetId, path = []) {
for (let i = 0; i < arr.length; i++) {
const currentPath = [...path, arr[i].id];
if (arr[i].id === targetId) {
console.log("Lineage:", currentPath.join(" -> "));
return arr[i];
}
if (arr[i].children && arr[i].children.length > 0) {
const result = findNodeById(arr[i].children, targetId, currentPath);
if (result) {
return result;
}
}
}
return null;
}