From f771cbb34015603ba1d2c1cfacbbd4286a6c2277 Mon Sep 17 00:00:00 2001 From: LawyZheng Date: Mon, 29 Jul 2024 15:32:16 +0800 Subject: [PATCH] optimize react select (#654) --- skyvern/webeye/scraper/domUtils.js | 30 +++++++++++++++++++++++++++++- skyvern/webeye/utils/dom.py | 11 ++++++----- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/skyvern/webeye/scraper/domUtils.js b/skyvern/webeye/scraper/domUtils.js index dbf5c068..ec7d1f81 100644 --- a/skyvern/webeye/scraper/domUtils.js +++ b/skyvern/webeye/scraper/domUtils.js @@ -696,6 +696,9 @@ async function getSelect2Options(element) { } async function getReactSelectOptionElements(element) { + var scrollLeft = window.scrollX; + var scrollTop = window.scrollY; + let optionList = []; // wait for 2s until the element is updated with `aria-controls` console.log("wait 2s for the dropdown being updated."); @@ -714,7 +717,7 @@ async function getReactSelectOptionElements(element) { // sometimes need more time to load the options console.log("wait 5s to load all options"); await sleep(5000); // wait 5s - optionList = dropdownDiv.querySelectorAll("div[role='option']"); + optionList = dropdownDiv.querySelectorAll("div[class*='select__option']"); if (optionList.length === 0) { break; } @@ -750,6 +753,12 @@ async function getReactSelectOptionElements(element) { ); } + // scroll back to the original place + window.scroll({ + top: scrollTop, + left: scrollLeft, + behavior: "instant", + }); return optionList; } @@ -887,6 +896,12 @@ async function buildTreeFromBody(frame = "main.frame", open_select = false) { view: window, }), ); + element.dispatchEvent( + new MouseEvent("mousedown", { + bubbles: true, + view: window, + }), + ); selectOptions = await getReactSelectOptions(element); @@ -897,6 +912,19 @@ async function buildTreeFromBody(frame = "main.frame", open_select = false) { view: window, }), ); + element.dispatchEvent( + new MouseEvent("mousedown", { + bubbles: true, + view: window, + }), + ); + element.dispatchEvent( + new KeyboardEvent("keydown", { + keyCode: 27, + bubbles: true, + key: "Escape", + }), + ); } else if (open_select && isComboboxDropdown(element)) { // open combobox dropdown to get options element.click(); diff --git a/skyvern/webeye/utils/dom.py b/skyvern/webeye/utils/dom.py index 10cf2004..06f0c515 100644 --- a/skyvern/webeye/utils/dom.py +++ b/skyvern/webeye/utils/dom.py @@ -440,7 +440,7 @@ class ReactSelectDropdown(AbstractSelectDropdown): locator = self.skyvern_element.get_locator() if tag_name == InteractiveElement.BUTTON: - return locator.locator("..").locator("..").locator("input[class~='select__input']") + return locator.locator("..").locator("..").locator("input[class*='select__input']") return locator @@ -461,17 +461,18 @@ class ReactSelectDropdown(AbstractSelectDropdown): return "react-select" async def open(self, timeout: float = SettingsManager.get_settings().BROWSER_ACTION_TIMEOUT_MS) -> None: - await self.skyvern_element.get_locator().click(timeout=timeout) + await self.skyvern_element.get_locator().focus(timeout=timeout) + await self.skyvern_element.get_locator().press(key="ArrowDown", timeout=timeout) await self.__find_anchor(timeout=timeout) async def close(self, timeout: float = SettingsManager.get_settings().BROWSER_ACTION_TIMEOUT_MS) -> None: await self.__find_anchor(timeout=timeout) - await self.skyvern_element.get_locator().click(timeout=timeout) + await self.skyvern_element.get_locator().press(key="Escape", timeout=timeout) async def get_current_value(self, timeout: float = SettingsManager.get_settings().BROWSER_ACTION_TIMEOUT_MS) -> str: input_locator = self.__find_input_locator() # TODO: only support single value now - value_locator = input_locator.locator("..").locator("..").locator("div[class~='select__single-value']") + value_locator = input_locator.locator("..").locator("..").locator("div[class*='select__single-value']") if await value_locator.count() == 0: return "" try: @@ -491,7 +492,7 @@ class ReactSelectDropdown(AbstractSelectDropdown): self, index: int, timeout: float = SettingsManager.get_settings().BROWSER_ACTION_TIMEOUT_MS ) -> None: anchor = await self.__find_anchor(timeout=timeout) - options = anchor.locator("div[role='option']") + options = anchor.locator("div[class*='select__option']") await options.nth(index).click(timeout=timeout)