diff --git a/skyvern/exceptions.py b/skyvern/exceptions.py index b74687f8..917ae630 100644 --- a/skyvern/exceptions.py +++ b/skyvern/exceptions.py @@ -521,3 +521,10 @@ class InvalidWorkflowParameter(SkyvernHTTPException): message, status_code=status.HTTP_400_BAD_REQUEST, ) + + +class InteractWithDisabledElement(SkyvernException): + def __init__(self, element_id: str): + super().__init__( + f"The element(id={element_id}) now is disabled, try to interact with it later when it's enabled." + ) diff --git a/skyvern/webeye/actions/handler.py b/skyvern/webeye/actions/handler.py index 47dd6d51..b50964da 100644 --- a/skyvern/webeye/actions/handler.py +++ b/skyvern/webeye/actions/handler.py @@ -25,6 +25,7 @@ from skyvern.exceptions import ( FailToSelectByValue, IllegitComplete, ImaginaryFileUrl, + InteractWithDisabledElement, InvalidElementForTextInput, MissingElement, MissingFileUrl, @@ -347,6 +348,18 @@ async def handle_click_action( dom = DomUtil(scraped_page=scraped_page, page=page) skyvern_element = await dom.get_skyvern_element_by_id(action.element_id) await asyncio.sleep(0.3) + + # dynamically validate the attr, since it could change into enabled after the previous actions + if await skyvern_element.is_disabled(dynamic=True): + LOG.warning( + "Try to click on a disabled element", + action_type=action.action_type, + task_id=task.task_id, + step_id=step.step_id, + element_id=skyvern_element.get_id(), + ) + return [ActionFailure(InteractWithDisabledElement(skyvern_element.get_id()))] + if action.download: results = await handle_click_to_download_file_action(action, page, scraped_page, task) else: @@ -417,6 +430,17 @@ async def handle_input_text_action( if text is None: return [ActionFailure(FailedToFetchSecret())] + # dynamically validate the attr, since it could change into enabled after the previous actions + if await skyvern_element.is_disabled(dynamic=True): + LOG.warning( + "Try to input text on a disabled element", + action_type=action.action_type, + task_id=task.task_id, + step_id=step.step_id, + element_id=skyvern_element.get_id(), + ) + return [ActionFailure(InteractWithDisabledElement(skyvern_element.get_id()))] + incremental_element: list[dict] = [] # check if it's selectable if skyvern_element.get_tag_name() == InteractiveElement.INPUT and not await skyvern_element.is_raw_input(): @@ -572,6 +596,18 @@ async def handle_upload_file_action( dom = DomUtil(scraped_page=scraped_page, page=page) skyvern_element = await dom.get_skyvern_element_by_id(action.element_id) + + # dynamically validate the attr, since it could change into enabled after the previous actions + if await skyvern_element.is_disabled(dynamic=True): + LOG.warning( + "Try to upload file on a disabled element", + action_type=action.action_type, + task_id=task.task_id, + step_id=step.step_id, + element_id=skyvern_element.get_id(), + ) + return [ActionFailure(InteractWithDisabledElement(skyvern_element.get_id()))] + locator = skyvern_element.locator file_path = await download_file(file_url) @@ -717,6 +753,17 @@ async def handle_select_option_action( ) return await handle_select_option_action(select_action, page, scraped_page, task, step) + # dynamically validate the attr, since it could change into enabled after the previous actions + if await skyvern_element.is_disabled(dynamic=True): + LOG.warning( + "Try to select on a disabled element", + action_type=action.action_type, + task_id=task.task_id, + step_id=step.step_id, + element_id=skyvern_element.get_id(), + ) + return [ActionFailure(InteractWithDisabledElement(skyvern_element.get_id()))] + if tag_name == InteractiveElement.SELECT: LOG.info( "SelectOptionAction is on