From da9d7a3ce732785e619e15ad18998f29d7c99450 Mon Sep 17 00:00:00 2001 From: oootttyyy Date: Fri, 15 Sep 2023 13:02:15 -0400 Subject: [PATCH 1/7] add support for os agnostic meta/control+a --- browser_env/actions.py | 4 ++++ tests/test_browser_env/test_action_functionalities.py | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/browser_env/actions.py b/browser_env/actions.py index 6dbc21c..52c0181 100644 --- a/browser_env/actions.py +++ b/browser_env/actions.py @@ -802,12 +802,16 @@ async def aexecute_scroll(direction: str, page: APage) -> None: @beartype def execute_key_press(key: str, page: Page) -> None: """Press a key.""" + if 'Meta' in key and "Mac" not in page.evaluate("navigator.platform"): + key = key.replace('Meta','Control') page.keyboard.press(key) @beartype async def aexecute_key_press(key: str, page: APage) -> None: """Press a key.""" + if 'Meta' in key and "Mac" not in page.evaluate("navigator.platform"): + key = key.replace('Meta','Control') await page.keyboard.press(key) diff --git a/tests/test_browser_env/test_action_functionalities.py b/tests/test_browser_env/test_action_functionalities.py index 6452fa7..b019b6f 100644 --- a/tests/test_browser_env/test_action_functionalities.py +++ b/tests/test_browser_env/test_action_functionalities.py @@ -212,7 +212,16 @@ def test_key_press( assert success expect(env.page.get_by_label("Full name")).to_be_focused() + expect(env.page.get_by_label("Full name")).to_have_value(s) + obs, success, _, _, info = env.step( + create_id_based_action("press [meta+a]") + ) + assert success + + env.page.get_by_label("Full name").type(s) + expect(env.page.get_by_label("Full name")).to_have_value(s) + obs, success, _, _, info = env.step(create_key_press_action("Enter")) assert success expect(env.page.get_by_label("Email")).to_be_focused() From 676b580be32a212fc6860e1de2f6ad65ed5e6a61 Mon Sep 17 00:00:00 2001 From: oootttyyy Date: Fri, 15 Sep 2023 13:15:19 -0400 Subject: [PATCH 2/7] add clear textbox test --- .../test_action_functionalities.py | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/tests/test_browser_env/test_action_functionalities.py b/tests/test_browser_env/test_action_functionalities.py index b019b6f..5983288 100644 --- a/tests/test_browser_env/test_action_functionalities.py +++ b/tests/test_browser_env/test_action_functionalities.py @@ -280,3 +280,47 @@ def test_e2e_id_based_actions( x[-1]["page"].url == "https://russmaxdesign.github.io/exercise/#link-one" ) + +def test_id_delete_input( + accessibility_tree_current_viewport_script_browser_env: ScriptBrowserEnv, +) -> None: + env = accessibility_tree_current_viewport_script_browser_env + env.reset() + obs, success, _, _, info = env.step( + create_playwright_action( + 'page.goto("https://russmaxdesign.github.io/exercise/")' + ) + ) + assert success + assert "textbox 'Full name'" in obs["text"] + s = "My Name IS XYZ" + element_id = re.search(r"\[(\d+)\] textbox 'Full name'", obs["text"]).group(1) # type: ignore + + obs, success, _, _, info = env.step( + create_id_based_action(f"type [{element_id}] [{s}]") + ) + assert success + locator = env.page.get_by_label("Full name") + expect(locator).to_have_value(s) + + obs, success, _, _, info = env.step( + create_id_based_action(f"click [{element_id}]") + ) + assert success + + obs, success, _, _, info = env.step( + create_id_based_action(f"press [Meta+a]") + ) + assert success + + obs, success, _, _, info = env.step( + create_id_based_action("press [backspace]") + ) + assert success + + new_s = "NEW" + obs, success, _, _, info = env.step( + create_id_based_action(f"type [{element_id}] [{new_s}]") + ) + locator = env.page.get_by_label("Full name") + expect(locator).to_have_value(new_s) From 5af6100be461341deb1b7b4f73b85b190ecf7c86 Mon Sep 17 00:00:00 2001 From: oootttyyy Date: Fri, 15 Sep 2023 13:15:19 -0400 Subject: [PATCH 3/7] add clear textbox test --- browser_env/actions.py | 145 +++++------------- .../test_action_functionalities.py | 86 ++++++----- 2 files changed, 92 insertions(+), 139 deletions(-) diff --git a/browser_env/actions.py b/browser_env/actions.py index 52c0181..9ec643b 100644 --- a/browser_env/actions.py +++ b/browser_env/actions.py @@ -112,9 +112,7 @@ class Action(TypedDict): @beartype -def action2str( - action: Action, action_set_tag: str, semantic_element: str = "" -) -> str: +def action2str(action: Action, action_set_tag: str, semantic_element: str = "") -> str: """Return the string representation of an action sementic_element: the semantic information of the element @@ -125,12 +123,16 @@ def action2str( match action["action_type"]: case ActionTypes.CLICK: # [ID=X] xxxxx - action_str = f"click [{element_id}] where [{element_id}] is {semantic_element}" + action_str = ( + f"click [{element_id}] where [{element_id}] is {semantic_element}" + ) case ActionTypes.TYPE: text = "".join([_id2key[i] for i in action["text"]]) action_str = f"type [{element_id}] [{text}] where [{element_id}] is {semantic_element}" case ActionTypes.HOVER: - action_str = f"hover [{element_id}] where [{element_id}] is {semantic_element}" + action_str = ( + f"hover [{element_id}] where [{element_id}] is {semantic_element}" + ) case ActionTypes.SCROLL: action_str = f"scroll [{action['direction']}]" case ActionTypes.KEY_PRESS: @@ -152,9 +154,7 @@ def action2str( case ActionTypes.NONE: action_str = "none" case _: - raise ValueError( - f"Unknown action type {action['action_type']}" - ) + raise ValueError(f"Unknown action type {action['action_type']}") else: raise NotImplementedError(f"Unknown action set tag {action_set_tag}") @@ -197,9 +197,7 @@ def action2create_function(action: Action) -> str: case ActionTypes.CLICK: args = [] args.append(f"element_id={repr(action['element_id'])}") - args.append( - f"element_role={repr(_id2role[action['element_role']])}" - ) + args.append(f"element_role={repr(_id2role[action['element_role']])}") args.append(f"element_name={repr(action['element_name'])}") args.append(f"pw_code={repr(action['pw_code'])}") args_str = ", ".join(args) @@ -207,9 +205,7 @@ def action2create_function(action: Action) -> str: case ActionTypes.HOVER: args = [] args.append(f"element_id={repr(action['element_id'])}") - args.append( - f"element_role={repr(_id2role[action['element_role']])}" - ) + args.append(f"element_role={repr(_id2role[action['element_role']])}") args.append(f"element_name={repr(action['element_name'])}") args.append(f"pw_code={repr(action['pw_code'])}") args_str = ", ".join(args) @@ -219,9 +215,7 @@ def action2create_function(action: Action) -> str: text = "".join(map(lambda x: _id2key[x], action["text"])) args.append(f"text={repr(text)}") args.append(f"element_id={repr(action['element_id'])}") - args.append( - f"element_role={repr(_id2role[action['element_role']])}" - ) + args.append(f"element_role={repr(_id2role[action['element_role']])}") args.append(f"element_name={repr(action['element_name'])}") args.append(f"pw_code={repr(action['pw_code'])}") args_str = ", ".join(args) @@ -332,8 +326,7 @@ def is_equivalent(a: Action, b: Action) -> bool: } _id2key: list[str] = sorted(_key2id, key=_key2id.get) # type: ignore[arg-type] _role2id: dict[RolesType, int] = { - cast(RolesType, role): i - for i, role in enumerate(chain(ROLES, SPECIAL_LOCATORS)) + cast(RolesType, role): i for i, role in enumerate(chain(ROLES, SPECIAL_LOCATORS)) } _id2role: list[RolesType] = sorted(_role2id, key=_role2id.get) # type: ignore[arg-type] @@ -342,9 +335,7 @@ def is_equivalent(a: Action, b: Action) -> bool: def _keys2ids(keys: list[int | str] | str) -> list[int]: return list( map( - lambda key: _key2id[str(key)] - if is_bearable(key, str) - else int(key), + lambda key: _key2id[str(key)] if is_bearable(key, str) else int(key), keys, ) ) @@ -361,19 +352,13 @@ def get_action_space() -> spaces.Dict: np.array([1.0, 1.0], dtype=np.float32), ), # element role is used for FOCUS_AND_CLICK and FOCUS_AND_TYPE - "element_role": spaces.Discrete( - len(ROLES) + len(SPECIAL_LOCATORS) - ), + "element_role": spaces.Discrete(len(ROLES) + len(SPECIAL_LOCATORS)), # element name is used with element role "element_name": spaces.Text(TEXT_MAX_LENGTH), "element_id": spaces.Text(TEXT_MAX_LENGTH), # text is only used for TYPE and FOCUS_AND_TYPE "text": spaces.MultiDiscrete( - [ - len(ASCII_CHARSET) - + len(SPECIAL_KEYS) - + len(FREQ_UNICODE_CHARSET) - ] + [len(ASCII_CHARSET) + len(SPECIAL_KEYS) + len(FREQ_UNICODE_CHARSET)] * TYPING_MAX_LENGTH ), "page_number": spaces.Discrete(MAX_PAGE_NUMBER), @@ -409,9 +394,7 @@ def create_random_action() -> Action: ), "nth": np.random.randint(MAX_ELEMENT_INDEX_IN_VIEWPORT), "element_id": str(np.random.randint(MAX_ELEMENT_ID)), - "key_comb": "+".join( - random.choices(SPECIAL_KEYS, k=np.random.randint(3)) - ), + "key_comb": "+".join(random.choices(SPECIAL_KEYS, k=np.random.randint(3))), "direction": random.choice(["up", "down"]), "pw_code": "".join( random.choices( @@ -802,16 +785,16 @@ async def aexecute_scroll(direction: str, page: APage) -> None: @beartype def execute_key_press(key: str, page: Page) -> None: """Press a key.""" - if 'Meta' in key and "Mac" not in page.evaluate("navigator.platform"): - key = key.replace('Meta','Control') + if "Meta" in key and "Mac" not in page.evaluate("navigator.platform"): + key = key.replace("Meta", "Control") page.keyboard.press(key) @beartype async def aexecute_key_press(key: str, page: APage) -> None: """Press a key.""" - if 'Meta' in key and "Mac" not in page.evaluate("navigator.platform"): - key = key.replace('Meta','Control') + if "Meta" in key and "Mac" not in page.evaluate("navigator.platform"): + key = key.replace("Meta", "Control") await page.keyboard.press(key) @@ -820,9 +803,7 @@ def execute_mouse_hover(left: float, top: float, page: Page) -> None: """Click at coordinates (left, top).""" viewport_size = page.viewport_size assert viewport_size - page.mouse.move( - left * viewport_size["width"], top * viewport_size["height"] - ) + page.mouse.move(left * viewport_size["width"], top * viewport_size["height"]) @beartype @@ -830,18 +811,14 @@ async def aexecute_mouse_hover(left: float, top: float, page: APage) -> None: """Click at coordinates (left, top).""" viewport_size = page.viewport_size assert viewport_size - await page.mouse.move( - left * viewport_size["width"], top * viewport_size["height"] - ) + await page.mouse.move(left * viewport_size["width"], top * viewport_size["height"]) def execute_mouse_click(left: float, top: float, page: Page) -> None: """Click at coordinates (left, top).""" viewport_size = page.viewport_size assert viewport_size - page.mouse.click( - left * viewport_size["width"], top * viewport_size["height"] - ) + page.mouse.click(left * viewport_size["width"], top * viewport_size["height"]) @beartype @@ -849,9 +826,7 @@ async def aexecute_mouse_click(left: float, top: float, page: APage) -> None: """Click at coordinates (left, top).""" viewport_size = page.viewport_size assert viewport_size - await page.mouse.click( - left * viewport_size["width"], top * viewport_size["height"] - ) + await page.mouse.click(left * viewport_size["width"], top * viewport_size["height"]) @beartype @@ -908,9 +883,7 @@ async def aexecute_type(keys: list[int], page: APage) -> None: @beartype -def execute_focus( - element_role: int, element_name: str, nth: int, page: Page -) -> None: +def execute_focus(element_role: int, element_name: str, nth: int, page: Page) -> None: """Click the specified DOM element.""" element_role_str = _id2role[element_role] if page.viewport_size is None: @@ -925,9 +898,7 @@ def execute_focus( case "placeholder": locators = frame.get_by_placeholder(element_name) case _: - locators = frame.get_by_role( - role=element_role_str, name=element_name - ) + locators = frame.get_by_role(role=element_role_str, name=element_name) for locator_idx in range(locators.count()): locator = locators.nth(locator_idx) if is_in_viewport(locator, page.viewport_size): @@ -962,9 +933,7 @@ async def aexecute_focus( case "placeholder": locators = frame.get_by_placeholder(element_name) case _: - locators = frame.get_by_role( - role=element_role_str, name=element_name - ) + locators = frame.get_by_role(role=element_role_str, name=element_name) for locator_idx in range(await locators.count()): locator = locators.nth(locator_idx) if await async_is_in_viewport(locator, page.viewport_size): @@ -993,9 +962,7 @@ def locate(locator_calls: list[ParsedPlaywrightCode], page: Page) -> Locator: @beartype -async def alocate( - locator_calls: list[ParsedPlaywrightCode], page: APage -) -> ALocator: +async def alocate(locator_calls: list[ParsedPlaywrightCode], page: APage) -> ALocator: locator = page for call in locator_calls: function_name = call["function_name"] @@ -1184,9 +1151,7 @@ def execute_action( # [shuyanzh], don't support action args and kwargs now execute_playwright_hover(locator_code=locator_code, page=page) else: - raise NotImplementedError( - "No proper locator found for hover action" - ) + raise NotImplementedError("No proper locator found for hover action") case ActionTypes.TYPE: if action["element_id"]: element_id = action["element_id"] @@ -1204,13 +1169,9 @@ def execute_action( locator_code = parsed_code[:-1] text = parsed_code[-1]["arguments"][0] # [shuyanzh], don't support action args and kwargs now - execute_playwright_type( - text=text, locator_code=locator_code, page=page - ) + execute_playwright_type(text=text, locator_code=locator_code, page=page) else: - raise NotImplementedError( - "No proper locator found for type action" - ) + raise NotImplementedError("No proper locator found for type action") case ActionTypes.PAGE_FOCUS: page = browser_ctx.pages[action["page_number"]] @@ -1273,13 +1234,9 @@ async def aexecute_action( await aexecute_key_press(keys, page) case ActionTypes.MOUSE_CLICK: - await aexecute_mouse_click( - action["coords"][0], action["coords"][1], page - ) + await aexecute_mouse_click(action["coords"][0], action["coords"][1], page) case ActionTypes.MOUSE_HOVER: - await aexecute_mouse_hover( - action["coords"][0], action["coords"][1], page - ) + await aexecute_mouse_hover(action["coords"][0], action["coords"][1], page) case ActionTypes.KEYBOARD_TYPE: await aexecute_type(action["text"], page) @@ -1298,9 +1255,7 @@ async def aexecute_action( parsed_code = parse_playwright_code(action["pw_code"]) locator_code = parsed_code[:-1] # [shuyanzh], don't support action args and kwargs now - await aexecute_playwright_click( - locator_code=locator_code, page=page - ) + await aexecute_playwright_click(locator_code=locator_code, page=page) else: raise ValueError("No proper locator found for click action") case ActionTypes.HOVER: @@ -1315,13 +1270,9 @@ async def aexecute_action( parsed_code = parse_playwright_code(action["pw_code"]) locator_code = parsed_code[:-1] # [shuyanzh], don't support action args and kwargs now - await aexecute_playwright_hover( - locator_code=locator_code, page=page - ) + await aexecute_playwright_hover(locator_code=locator_code, page=page) else: - raise NotImplementedError( - "No proper locator found for hover action" - ) + raise NotImplementedError("No proper locator found for hover action") case ActionTypes.TYPE: if action["element_id"]: raise NotImplementedError @@ -1340,9 +1291,7 @@ async def aexecute_action( text=text, locator_code=locator_code, page=page ) else: - raise NotImplementedError( - "No proper locator found for type action" - ) + raise NotImplementedError("No proper locator found for type action") case ActionTypes.PAGE_FOCUS: page = browser_ctx.pages[action["page_number"]] @@ -1391,9 +1340,7 @@ async def aexecute_action( def parse_playwright_code(code: str) -> list[ParsedPlaywrightCode]: # extract function calls if not code.startswith("page."): - raise ValueError( - f'Playwright action must start with "page.", but got {code}' - ) + raise ValueError(f'Playwright action must start with "page.", but got {code}') regex = r"\.(?![^\(\)]*\))" chain = re.split(regex, code)[1:] @@ -1411,8 +1358,7 @@ def parse_playwright_code(code: str) -> list[ParsedPlaywrightCode]: for arg in node.args ] keywords = { - str(kw.arg): ast.literal_eval(kw.value) - for kw in node.keywords + str(kw.arg): ast.literal_eval(kw.value) for kw in node.keywords } funcs.append( ParsedPlaywrightCode( @@ -1427,10 +1373,7 @@ def parse_playwright_code(code: str) -> list[ParsedPlaywrightCode]: if len(funcs) != 1: raise ValueError(f"Fail to parse {item} in {code}") - if ( - funcs[0]["function_name"] - not in PLAYWRIGHT_LOCATORS + PLAYWRIGHT_ACTIONS - ): + if funcs[0]["function_name"] not in PLAYWRIGHT_LOCATORS + PLAYWRIGHT_ACTIONS: raise ValueError( f"Invalid playwright code {item}, ", f"the function needs to be one of {PLAYWRIGHT_LOCATORS + PLAYWRIGHT_ACTIONS}", @@ -1555,9 +1498,7 @@ def create_id_based_action(action_str: str) -> Action: if not (action_str.endswith("[0]") or action_str.endswith("[1]")): action_str += " [1]" - match = re.search( - r"type ?\[(\d+)\] ?\[(.+)\] ?\[(\d+)\]", action_str - ) + match = re.search(r"type ?\[(\d+)\] ?\[(.+)\] ?\[(\d+)\]", action_str) if not match: raise ActionParsingError(f"Invalid type action {action_str}") element_id, text, enter_flag = ( @@ -1596,9 +1537,7 @@ def create_id_based_action(action_str: str) -> Action: case "tab_focus": match = re.search(r"tab_focus ?\[(\d+)\]", action_str) if not match: - raise ActionParsingError( - f"Invalid tab_focus action {action_str}" - ) + raise ActionParsingError(f"Invalid tab_focus action {action_str}") page_number = int(match.group(1)) return create_page_focus_action(page_number) case "close_tab": diff --git a/tests/test_browser_env/test_action_functionalities.py b/tests/test_browser_env/test_action_functionalities.py index b019b6f..d5ac8c2 100644 --- a/tests/test_browser_env/test_action_functionalities.py +++ b/tests/test_browser_env/test_action_functionalities.py @@ -138,33 +138,21 @@ def test_id_click( # get the id of the link element_id = re.search(r"\[(\d+)\] link 'McKenna/Bell'", obs["text"]).group(1) # type: ignore - obs, success, _, _, info = env.step( - create_id_based_action(f"click [{element_id}]") - ) + obs, success, _, _, info = env.step(create_id_based_action(f"click [{element_id}]")) assert success - assert ( - info["page"].url - == "https://russmaxdesign.github.io/exercise/#link-four" - ) + assert info["page"].url == "https://russmaxdesign.github.io/exercise/#link-four" obs, success, _, _, info = env.step(create_scroll_action("down")) assert "link 'Classification'" in obs["text"] element_id = re.search(r"\[(\d+)\] link 'Classification'", obs["text"]).group(1) # type: ignore - obs, success, _, _, info = env.step( - create_id_based_action(f"click [{element_id}]") - ) + obs, success, _, _, info = env.step(create_id_based_action(f"click [{element_id}]")) assert success - assert ( - info["page"].url - == "https://russmaxdesign.github.io/exercise/#link-two" - ) + assert info["page"].url == "https://russmaxdesign.github.io/exercise/#link-two" assert "radio 'Weekly'" in obs["text"] element_id = re.search(r"\[(\d+)\] radio 'Weekly'", obs["text"]).group(1) # type: ignore - obs, success, _, _, info = env.step( - create_id_based_action(f"click [{element_id}]") - ) + obs, success, _, _, info = env.step(create_id_based_action(f"click [{element_id}]")) assert success assert "radio 'Weekly'" in obs["text"] @@ -176,17 +164,13 @@ def test_id_hover( env.reset() obs, success, _, _, info = env.step( - create_playwright_action( - 'page.goto("https://ianlunn.github.io/Hover/")' - ) + create_playwright_action('page.goto("https://ianlunn.github.io/Hover/")') ) assert success assert "link 'Download on GitHub'" in obs["text"] element_id = re.search(r"\[(\d+)\] link 'Download on GitHub'", obs["text"]).group(1) # type: ignore - obs, success, _, _, info = env.step( - create_id_based_action(f"hover [{element_id}]") - ) + obs, success, _, _, info = env.step(create_id_based_action(f"hover [{element_id}]")) assert success @@ -214,14 +198,12 @@ def test_key_press( expect(env.page.get_by_label("Full name")).to_be_focused() expect(env.page.get_by_label("Full name")).to_have_value(s) - obs, success, _, _, info = env.step( - create_id_based_action("press [meta+a]") - ) + obs, success, _, _, info = env.step(create_id_based_action("press [meta+a]")) assert success env.page.get_by_label("Full name").type(s) expect(env.page.get_by_label("Full name")).to_have_value(s) - + obs, success, _, _, info = env.step(create_key_press_action("Enter")) assert success expect(env.page.get_by_label("Email")).to_be_focused() @@ -256,16 +238,12 @@ def test_e2e_id_based_actions( env = accessibility_tree_script_browser_env env.reset() obs, *_ = env.step( - create_id_based_action( - "goto [https://russmaxdesign.github.io/exercise/]" - ) + create_id_based_action("goto [https://russmaxdesign.github.io/exercise/]") ) element_id = re.search(r"\[(\d+)\] link 'What are mammals\?'", obs["text"]).group(1) # type: ignore obs, *_ = env.step(create_id_based_action(f"click [{element_id}]")) element_id = re.search(r"\[(\d+)\] textbox 'Email'", obs["text"]).group(1) # type: ignore - env.step( - create_id_based_action(f"type [{element_id}] [test@gmail.com] [0]") - ) + env.step(create_id_based_action(f"type [{element_id}] [test@gmail.com] [0]")) env.step(create_id_based_action("scroll [down]")) env.step(create_id_based_action("scroll [up]")) env.step(create_id_based_action("new_tab")) @@ -276,7 +254,43 @@ def test_e2e_id_based_actions( x = env.step(create_id_based_action("go_forward")) assert x[-1]["page"].url == "https://example.com/" x = env.step(create_id_based_action("tab_focus [0]")) - assert ( - x[-1]["page"].url - == "https://russmaxdesign.github.io/exercise/#link-one" + assert x[-1]["page"].url == "https://russmaxdesign.github.io/exercise/#link-one" + + +def test_id_delete_input( + accessibility_tree_current_viewport_script_browser_env: ScriptBrowserEnv, +) -> None: + env = accessibility_tree_current_viewport_script_browser_env + env.reset() + obs, success, _, _, info = env.step( + create_playwright_action( + 'page.goto("https://russmaxdesign.github.io/exercise/")' + ) + ) + assert success + assert "textbox 'Full name'" in obs["text"] + s = "My Name IS XYZ" + element_id = re.search(r"\[(\d+)\] textbox 'Full name'", obs["text"]).group(1) # type: ignore + + obs, success, _, _, info = env.step( + create_id_based_action(f"type [{element_id}] [{s}]") + ) + assert success + locator = env.page.get_by_label("Full name") + expect(locator).to_have_value(s) + + obs, success, _, _, info = env.step(create_id_based_action(f"click [{element_id}]")) + assert success + + obs, success, _, _, info = env.step(create_id_based_action(f"press [Meta+a]")) + assert success + + obs, success, _, _, info = env.step(create_id_based_action("press [backspace]")) + assert success + + new_s = "NEW" + obs, success, _, _, info = env.step( + create_id_based_action(f"type [{element_id}] [{new_s}]") ) + locator = env.page.get_by_label("Full name") + expect(locator).to_have_value(new_s) From 9ccc2dc5ec479fa8553cdb6d53f26f9ff6a4b3ea Mon Sep 17 00:00:00 2001 From: oootttyyy Date: Sat, 16 Sep 2023 01:13:19 -0400 Subject: [PATCH 4/7] fix black formatting --- .../test_browser_env/test_action_functionalities.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/tests/test_browser_env/test_action_functionalities.py b/tests/test_browser_env/test_action_functionalities.py index 5293a6d..d5ac8c2 100644 --- a/tests/test_browser_env/test_action_functionalities.py +++ b/tests/test_browser_env/test_action_functionalities.py @@ -256,6 +256,7 @@ def test_e2e_id_based_actions( x = env.step(create_id_based_action("tab_focus [0]")) assert x[-1]["page"].url == "https://russmaxdesign.github.io/exercise/#link-one" + def test_id_delete_input( accessibility_tree_current_viewport_script_browser_env: ScriptBrowserEnv, ) -> None: @@ -278,19 +279,13 @@ def test_id_delete_input( locator = env.page.get_by_label("Full name") expect(locator).to_have_value(s) - obs, success, _, _, info = env.step( - create_id_based_action(f"click [{element_id}]") - ) + obs, success, _, _, info = env.step(create_id_based_action(f"click [{element_id}]")) assert success - obs, success, _, _, info = env.step( - create_id_based_action(f"press [Meta+a]") - ) + obs, success, _, _, info = env.step(create_id_based_action(f"press [Meta+a]")) assert success - obs, success, _, _, info = env.step( - create_id_based_action("press [backspace]") - ) + obs, success, _, _, info = env.step(create_id_based_action("press [backspace]")) assert success new_s = "NEW" From 772a5391b901c00dc058d07ce94cace428552e81 Mon Sep 17 00:00:00 2001 From: oootttyyy Date: Sat, 16 Sep 2023 01:37:27 -0400 Subject: [PATCH 5/7] fix black formatting --- browser_env/actions.py | 317 +++++++++++++----- .../test_action_functionalities.py | 97 ++++-- 2 files changed, 305 insertions(+), 109 deletions(-) diff --git a/browser_env/actions.py b/browser_env/actions.py index 9ec643b..c2e2cc8 100644 --- a/browser_env/actions.py +++ b/browser_env/actions.py @@ -68,9 +68,9 @@ def is_in_viewport( boxy1 = box["y"] + box["height"] viewportx0, viewporty0 = 0, 0 viewportx1, viewporty1 = viewport["width"], viewport["height"] - inter = max(0, min(boxx1, viewportx1) - max(boxx0, viewportx0)) * max( - 0, min(boxy1, viewporty1) - max(boxy0, viewporty0) - ) + inter = max( + 0, min(boxx1, viewportx1) - max(boxx0, viewportx0) + ) * max(0, min(boxy1, viewporty1) - max(boxy0, viewporty0)) ratio = inter / (box["width"] * box["height"]) return ratio > threshold @@ -87,9 +87,9 @@ async def async_is_in_viewport( boxy1 = box["y"] + box["height"] viewportx0, viewporty0 = 0, 0 viewportx1, viewporty1 = viewport["width"], viewport["height"] - inter = max(0, min(boxx1, viewportx1) - max(boxx0, viewportx0)) * max( - 0, min(boxy1, viewporty1) - max(boxy0, viewporty0) - ) + inter = max( + 0, min(boxx1, viewportx1) - max(boxx0, viewportx0) + ) * max(0, min(boxy1, viewporty1) - max(boxy0, viewporty0)) ratio = inter / (box["width"] * box["height"]) return ratio > threshold @@ -112,7 +112,9 @@ class Action(TypedDict): @beartype -def action2str(action: Action, action_set_tag: str, semantic_element: str = "") -> str: +def action2str( + action: Action, action_set_tag: str, semantic_element: str = "" +) -> str: """Return the string representation of an action sementic_element: the semantic information of the element @@ -123,16 +125,12 @@ def action2str(action: Action, action_set_tag: str, semantic_element: str = "") match action["action_type"]: case ActionTypes.CLICK: # [ID=X] xxxxx - action_str = ( - f"click [{element_id}] where [{element_id}] is {semantic_element}" - ) + action_str = f"click [{element_id}] where [{element_id}] is {semantic_element}" case ActionTypes.TYPE: text = "".join([_id2key[i] for i in action["text"]]) action_str = f"type [{element_id}] [{text}] where [{element_id}] is {semantic_element}" case ActionTypes.HOVER: - action_str = ( - f"hover [{element_id}] where [{element_id}] is {semantic_element}" - ) + action_str = f"hover [{element_id}] where [{element_id}] is {semantic_element}" case ActionTypes.SCROLL: action_str = f"scroll [{action['direction']}]" case ActionTypes.KEY_PRESS: @@ -154,9 +152,13 @@ def action2str(action: Action, action_set_tag: str, semantic_element: str = "") case ActionTypes.NONE: action_str = "none" case _: - raise ValueError(f"Unknown action type {action['action_type']}") + raise ValueError( + f"Unknown action type {action['action_type']}" + ) else: - raise NotImplementedError(f"Unknown action set tag {action_set_tag}") + raise NotImplementedError( + f"Unknown action set tag {action_set_tag}" + ) return action_str @@ -170,7 +172,9 @@ def action2create_function(action: Action) -> str: direction = "up" if "up" in action["direction"] else "down" return f"create_scroll_action({repr(direction)})" case ActionTypes.KEY_PRESS: - return f"create_key_press_action({repr(action['key_comb'])})" + return ( + f"create_key_press_action({repr(action['key_comb'])})" + ) # inter-page actions case ActionTypes.PAGE_FOCUS: return f"create_page_focus_action({action['page_number']})" @@ -197,7 +201,9 @@ def action2create_function(action: Action) -> str: case ActionTypes.CLICK: args = [] args.append(f"element_id={repr(action['element_id'])}") - args.append(f"element_role={repr(_id2role[action['element_role']])}") + args.append( + f"element_role={repr(_id2role[action['element_role']])}" + ) args.append(f"element_name={repr(action['element_name'])}") args.append(f"pw_code={repr(action['pw_code'])}") args_str = ", ".join(args) @@ -205,7 +211,9 @@ def action2create_function(action: Action) -> str: case ActionTypes.HOVER: args = [] args.append(f"element_id={repr(action['element_id'])}") - args.append(f"element_role={repr(_id2role[action['element_role']])}") + args.append( + f"element_role={repr(_id2role[action['element_role']])}" + ) args.append(f"element_name={repr(action['element_name'])}") args.append(f"pw_code={repr(action['pw_code'])}") args_str = ", ".join(args) @@ -215,7 +223,9 @@ def action2create_function(action: Action) -> str: text = "".join(map(lambda x: _id2key[x], action["text"])) args.append(f"text={repr(text)}") args.append(f"element_id={repr(action['element_id'])}") - args.append(f"element_role={repr(_id2role[action['element_role']])}") + args.append( + f"element_role={repr(_id2role[action['element_role']])}" + ) args.append(f"element_name={repr(action['element_name'])}") args.append(f"pw_code={repr(action['pw_code'])}") args_str = ", ".join(args) @@ -326,7 +336,8 @@ def is_equivalent(a: Action, b: Action) -> bool: } _id2key: list[str] = sorted(_key2id, key=_key2id.get) # type: ignore[arg-type] _role2id: dict[RolesType, int] = { - cast(RolesType, role): i for i, role in enumerate(chain(ROLES, SPECIAL_LOCATORS)) + cast(RolesType, role): i + for i, role in enumerate(chain(ROLES, SPECIAL_LOCATORS)) } _id2role: list[RolesType] = sorted(_role2id, key=_role2id.get) # type: ignore[arg-type] @@ -335,7 +346,9 @@ def is_equivalent(a: Action, b: Action) -> bool: def _keys2ids(keys: list[int | str] | str) -> list[int]: return list( map( - lambda key: _key2id[str(key)] if is_bearable(key, str) else int(key), + lambda key: _key2id[str(key)] + if is_bearable(key, str) + else int(key), keys, ) ) @@ -352,13 +365,19 @@ def get_action_space() -> spaces.Dict: np.array([1.0, 1.0], dtype=np.float32), ), # element role is used for FOCUS_AND_CLICK and FOCUS_AND_TYPE - "element_role": spaces.Discrete(len(ROLES) + len(SPECIAL_LOCATORS)), + "element_role": spaces.Discrete( + len(ROLES) + len(SPECIAL_LOCATORS) + ), # element name is used with element role "element_name": spaces.Text(TEXT_MAX_LENGTH), "element_id": spaces.Text(TEXT_MAX_LENGTH), # text is only used for TYPE and FOCUS_AND_TYPE "text": spaces.MultiDiscrete( - [len(ASCII_CHARSET) + len(SPECIAL_KEYS) + len(FREQ_UNICODE_CHARSET)] + [ + len(ASCII_CHARSET) + + len(SPECIAL_KEYS) + + len(FREQ_UNICODE_CHARSET) + ] * TYPING_MAX_LENGTH ), "page_number": spaces.Discrete(MAX_PAGE_NUMBER), @@ -378,9 +397,13 @@ def create_random_action() -> Action: return { "action_type": np.random.randint(len(ActionTypes)), "coords": np.random.rand(2).astype(np.float32), - "element_role": np.random.randint(len(ROLES) + len(SPECIAL_LOCATORS)), + "element_role": np.random.randint( + len(ROLES) + len(SPECIAL_LOCATORS) + ), "element_name": "".join( - random.choices(ASCII_CHARSET, k=np.random.randint(TEXT_MAX_LENGTH)) + random.choices( + ASCII_CHARSET, k=np.random.randint(TEXT_MAX_LENGTH) + ) ), "text": list( random.choices( @@ -390,11 +413,15 @@ def create_random_action() -> Action: ), "page_number": np.random.randint(MAX_PAGE_NUMBER), "url": "".join( - random.choices(ASCII_CHARSET, k=np.random.randint(URL_MAX_LENGTH)) + random.choices( + ASCII_CHARSET, k=np.random.randint(URL_MAX_LENGTH) + ) ), "nth": np.random.randint(MAX_ELEMENT_INDEX_IN_VIEWPORT), "element_id": str(np.random.randint(MAX_ELEMENT_ID)), - "key_comb": "+".join(random.choices(SPECIAL_KEYS, k=np.random.randint(3))), + "key_comb": "+".join( + random.choices(SPECIAL_KEYS, k=np.random.randint(3)) + ), "direction": random.choice(["up", "down"]), "pw_code": "".join( random.choices( @@ -581,7 +608,9 @@ def create_mouse_click_action( } ) else: - raise ValueError("left and top must be both None or both not None") + raise ValueError( + "left and top must be both None or both not None" + ) return action @@ -785,7 +814,9 @@ async def aexecute_scroll(direction: str, page: APage) -> None: @beartype def execute_key_press(key: str, page: Page) -> None: """Press a key.""" - if "Meta" in key and "Mac" not in page.evaluate("navigator.platform"): + if "Meta" in key and "Mac" not in page.evaluate( + "navigator.platform" + ): key = key.replace("Meta", "Control") page.keyboard.press(key) @@ -793,7 +824,9 @@ def execute_key_press(key: str, page: Page) -> None: @beartype async def aexecute_key_press(key: str, page: APage) -> None: """Press a key.""" - if "Meta" in key and "Mac" not in page.evaluate("navigator.platform"): + if "Meta" in key and "Mac" not in page.evaluate( + "navigator.platform" + ): key = key.replace("Meta", "Control") await page.keyboard.press(key) @@ -803,30 +836,42 @@ def execute_mouse_hover(left: float, top: float, page: Page) -> None: """Click at coordinates (left, top).""" viewport_size = page.viewport_size assert viewport_size - page.mouse.move(left * viewport_size["width"], top * viewport_size["height"]) + page.mouse.move( + left * viewport_size["width"], top * viewport_size["height"] + ) @beartype -async def aexecute_mouse_hover(left: float, top: float, page: APage) -> None: +async def aexecute_mouse_hover( + left: float, top: float, page: APage +) -> None: """Click at coordinates (left, top).""" viewport_size = page.viewport_size assert viewport_size - await page.mouse.move(left * viewport_size["width"], top * viewport_size["height"]) + await page.mouse.move( + left * viewport_size["width"], top * viewport_size["height"] + ) def execute_mouse_click(left: float, top: float, page: Page) -> None: """Click at coordinates (left, top).""" viewport_size = page.viewport_size assert viewport_size - page.mouse.click(left * viewport_size["width"], top * viewport_size["height"]) + page.mouse.click( + left * viewport_size["width"], top * viewport_size["height"] + ) @beartype -async def aexecute_mouse_click(left: float, top: float, page: APage) -> None: +async def aexecute_mouse_click( + left: float, top: float, page: APage +) -> None: """Click at coordinates (left, top).""" viewport_size = page.viewport_size assert viewport_size - await page.mouse.click(left * viewport_size["width"], top * viewport_size["height"]) + await page.mouse.click( + left * viewport_size["width"], top * viewport_size["height"] + ) @beartype @@ -883,11 +928,15 @@ async def aexecute_type(keys: list[int], page: APage) -> None: @beartype -def execute_focus(element_role: int, element_name: str, nth: int, page: Page) -> None: +def execute_focus( + element_role: int, element_name: str, nth: int, page: Page +) -> None: """Click the specified DOM element.""" element_role_str = _id2role[element_role] if page.viewport_size is None: - raise ValueError("Viewport size is not set for the current page") + raise ValueError( + "Viewport size is not set for the current page" + ) element_location_list: list[tuple[Locator, float, float]] = [] for frame in page.frames: match element_role_str: @@ -898,7 +947,9 @@ def execute_focus(element_role: int, element_name: str, nth: int, page: Page) -> case "placeholder": locators = frame.get_by_placeholder(element_name) case _: - locators = frame.get_by_role(role=element_role_str, name=element_name) + locators = frame.get_by_role( + role=element_role_str, name=element_name + ) for locator_idx in range(locators.count()): locator = locators.nth(locator_idx) if is_in_viewport(locator, page.viewport_size): @@ -911,7 +962,9 @@ def execute_focus(element_role: int, element_name: str, nth: int, page: Page) -> raise ValueError( f"There are only {len(element_location_list)} elements found in viewport, but {nth + 1} is requested" ) - element_location_list.sort(key=lambda x: (x[2], x[1])) # row major order + element_location_list.sort( + key=lambda x: (x[2], x[1]) + ) # row major order element_location_list[nth][0].focus() @@ -922,7 +975,9 @@ async def aexecute_focus( """Click the specified DOM element.""" element_role_str = _id2role[element_role] if page.viewport_size is None: - raise ValueError("Viewport size is not set for the current page") + raise ValueError( + "Viewport size is not set for the current page" + ) element_location_list: list[tuple[ALocator, float, float]] = [] for frame in page.frames: match element_role_str: @@ -933,7 +988,9 @@ async def aexecute_focus( case "placeholder": locators = frame.get_by_placeholder(element_name) case _: - locators = frame.get_by_role(role=element_role_str, name=element_name) + locators = frame.get_by_role( + role=element_role_str, name=element_name + ) for locator_idx in range(await locators.count()): locator = locators.nth(locator_idx) if await async_is_in_viewport(locator, page.viewport_size): @@ -946,29 +1003,39 @@ async def aexecute_focus( raise ValueError( f"There are only {len(element_location_list)} elements found in viewport, but {nth + 1} is requested" ) - element_location_list.sort(key=lambda x: (x[2], x[1])) # row major order + element_location_list.sort( + key=lambda x: (x[2], x[1]) + ) # row major order await element_location_list[nth][0].focus() @beartype -def locate(locator_calls: list[ParsedPlaywrightCode], page: Page) -> Locator: +def locate( + locator_calls: list[ParsedPlaywrightCode], page: Page +) -> Locator: locator = page for call in locator_calls: function_name = call["function_name"] arguments = call["arguments"] keywords = call["keywords"] - locator = getattr(locator, function_name)(*arguments, **keywords) + locator = getattr(locator, function_name)( + *arguments, **keywords + ) return locator # type: ignore[return-value] @beartype -async def alocate(locator_calls: list[ParsedPlaywrightCode], page: APage) -> ALocator: +async def alocate( + locator_calls: list[ParsedPlaywrightCode], page: APage +) -> ALocator: locator = page for call in locator_calls: function_name = call["function_name"] arguments = call["arguments"] keywords = call["keywords"] - locator = await getattr(locator, function_name)(*arguments, **keywords) + locator = await getattr(locator, function_name)( + *arguments, **keywords + ) return locator # type: ignore[return-value] @@ -1028,7 +1095,9 @@ def execute_playwright_type( ) -> None: locator = locate(locator_code, page) # perform the action - pw_action_args = [text] + pw_action_args # text is the first argument + pw_action_args = [ + text + ] + pw_action_args # text is the first argument locator.type(*pw_action_args, **pw_action_kwargs) @@ -1042,7 +1111,9 @@ async def aexecute_playwright_type( ) -> None: locator = await alocate(locator_code, page) # perform the action - pw_action_args = [text] + pw_action_args # text is the first argument + pw_action_args = [ + text + ] + pw_action_args # text is the first argument await locator.type(*pw_action_args, **pw_action_kwargs) @@ -1109,9 +1180,13 @@ def execute_action( execute_key_press(keys, page) case ActionTypes.MOUSE_CLICK: - execute_mouse_click(action["coords"][0], action["coords"][1], page) + execute_mouse_click( + action["coords"][0], action["coords"][1], page + ) case ActionTypes.MOUSE_HOVER: - execute_mouse_hover(action["coords"][0], action["coords"][1], page) + execute_mouse_hover( + action["coords"][0], action["coords"][1], page + ) case ActionTypes.KEYBOARD_TYPE: execute_type(action["text"], page) @@ -1121,7 +1196,9 @@ def execute_action( if action["element_id"]: element_id = action["element_id"] element_center = obseration_processor.get_element_center(element_id) # type: ignore[attr-defined] - execute_mouse_click(element_center[0], element_center[1], page) + execute_mouse_click( + element_center[0], element_center[1], page + ) elif action["element_role"] and action["element_name"]: element_role = int(action["element_role"]) element_name = action["element_name"] @@ -1132,14 +1209,20 @@ def execute_action( parsed_code = parse_playwright_code(action["pw_code"]) locator_code = parsed_code[:-1] # [shuyanzh], don't support action args and kwargs now - execute_playwright_click(locator_code=locator_code, page=page) + execute_playwright_click( + locator_code=locator_code, page=page + ) else: - raise ValueError("No proper locator found for click action") + raise ValueError( + "No proper locator found for click action" + ) case ActionTypes.HOVER: if action["element_id"]: element_id = action["element_id"] element_center = obseration_processor.get_element_center(element_id) # type: ignore[attr-defined] - execute_mouse_hover(element_center[0], element_center[1], page) + execute_mouse_hover( + element_center[0], element_center[1], page + ) elif action["element_role"] and action["element_name"]: element_role = int(action["element_role"]) element_name = action["element_name"] @@ -1149,14 +1232,20 @@ def execute_action( parsed_code = parse_playwright_code(action["pw_code"]) locator_code = parsed_code[:-1] # [shuyanzh], don't support action args and kwargs now - execute_playwright_hover(locator_code=locator_code, page=page) + execute_playwright_hover( + locator_code=locator_code, page=page + ) else: - raise NotImplementedError("No proper locator found for hover action") + raise NotImplementedError( + "No proper locator found for hover action" + ) case ActionTypes.TYPE: if action["element_id"]: element_id = action["element_id"] element_center = obseration_processor.get_element_center(element_id) # type: ignore[attr-defined] - execute_mouse_click(element_center[0], element_center[1], page) + execute_mouse_click( + element_center[0], element_center[1], page + ) execute_type(action["text"], page) elif action["element_role"] and action["element_name"]: element_role = int(action["element_role"]) @@ -1169,9 +1258,13 @@ def execute_action( locator_code = parsed_code[:-1] text = parsed_code[-1]["arguments"][0] # [shuyanzh], don't support action args and kwargs now - execute_playwright_type(text=text, locator_code=locator_code, page=page) + execute_playwright_type( + text=text, locator_code=locator_code, page=page + ) else: - raise NotImplementedError("No proper locator found for type action") + raise NotImplementedError( + "No proper locator found for type action" + ) case ActionTypes.PAGE_FOCUS: page = browser_ctx.pages[action["page_number"]] @@ -1234,9 +1327,13 @@ async def aexecute_action( await aexecute_key_press(keys, page) case ActionTypes.MOUSE_CLICK: - await aexecute_mouse_click(action["coords"][0], action["coords"][1], page) + await aexecute_mouse_click( + action["coords"][0], action["coords"][1], page + ) case ActionTypes.MOUSE_HOVER: - await aexecute_mouse_hover(action["coords"][0], action["coords"][1], page) + await aexecute_mouse_hover( + action["coords"][0], action["coords"][1], page + ) case ActionTypes.KEYBOARD_TYPE: await aexecute_type(action["text"], page) @@ -1249,15 +1346,21 @@ async def aexecute_action( element_role = int(action["element_role"]) element_name = action["element_name"] nth = action["nth"] - await aexecute_focus(element_role, element_name, nth, page) + await aexecute_focus( + element_role, element_name, nth, page + ) await aexecute_click_current(page) elif action["pw_code"]: parsed_code = parse_playwright_code(action["pw_code"]) locator_code = parsed_code[:-1] # [shuyanzh], don't support action args and kwargs now - await aexecute_playwright_click(locator_code=locator_code, page=page) + await aexecute_playwright_click( + locator_code=locator_code, page=page + ) else: - raise ValueError("No proper locator found for click action") + raise ValueError( + "No proper locator found for click action" + ) case ActionTypes.HOVER: if action["element_id"]: raise NotImplementedError @@ -1265,14 +1368,20 @@ async def aexecute_action( element_role = int(action["element_role"]) element_name = action["element_name"] nth = action["nth"] - await aexecute_focus(element_role, element_name, nth, page) + await aexecute_focus( + element_role, element_name, nth, page + ) elif action["pw_code"]: parsed_code = parse_playwright_code(action["pw_code"]) locator_code = parsed_code[:-1] # [shuyanzh], don't support action args and kwargs now - await aexecute_playwright_hover(locator_code=locator_code, page=page) + await aexecute_playwright_hover( + locator_code=locator_code, page=page + ) else: - raise NotImplementedError("No proper locator found for hover action") + raise NotImplementedError( + "No proper locator found for hover action" + ) case ActionTypes.TYPE: if action["element_id"]: raise NotImplementedError @@ -1280,7 +1389,9 @@ async def aexecute_action( element_role = int(action["element_role"]) element_name = action["element_name"] nth = action["nth"] - await aexecute_focus(element_role, element_name, nth, page) + await aexecute_focus( + element_role, element_name, nth, page + ) await aexecute_type(action["text"], page) elif action["pw_code"]: parsed_code = parse_playwright_code(action["pw_code"]) @@ -1291,7 +1402,9 @@ async def aexecute_action( text=text, locator_code=locator_code, page=page ) else: - raise NotImplementedError("No proper locator found for type action") + raise NotImplementedError( + "No proper locator found for type action" + ) case ActionTypes.PAGE_FOCUS: page = browser_ctx.pages[action["page_number"]] @@ -1315,7 +1428,9 @@ async def aexecute_action( if action["pw_code"]: parsed_code = parse_playwright_code(action["pw_code"]) locator_code = parsed_code[:-1] - await aexecute_playwright_select_option(locator_code, page) + await aexecute_playwright_select_option( + locator_code, page + ) else: raise NotImplementedError( "No proper locator found for select option action" @@ -1340,7 +1455,9 @@ async def aexecute_action( def parse_playwright_code(code: str) -> list[ParsedPlaywrightCode]: # extract function calls if not code.startswith("page."): - raise ValueError(f'Playwright action must start with "page.", but got {code}') + raise ValueError( + f'Playwright action must start with "page.", but got {code}' + ) regex = r"\.(?![^\(\)]*\))" chain = re.split(regex, code)[1:] @@ -1354,11 +1471,14 @@ def parse_playwright_code(code: str) -> list[ParsedPlaywrightCode]: if isinstance(node, ast.Call): function_name = node.func.id # type: ignore[attr-defined] arguments = [ - ast.literal_eval(arg) if isinstance(arg, ast.Str) else arg + ast.literal_eval(arg) + if isinstance(arg, ast.Str) + else arg for arg in node.args ] keywords = { - str(kw.arg): ast.literal_eval(kw.value) for kw in node.keywords + str(kw.arg): ast.literal_eval(kw.value) + for kw in node.keywords } funcs.append( ParsedPlaywrightCode( @@ -1373,7 +1493,10 @@ def parse_playwright_code(code: str) -> list[ParsedPlaywrightCode]: if len(funcs) != 1: raise ValueError(f"Fail to parse {item} in {code}") - if funcs[0]["function_name"] not in PLAYWRIGHT_LOCATORS + PLAYWRIGHT_ACTIONS: + if ( + funcs[0]["function_name"] + not in PLAYWRIGHT_LOCATORS + PLAYWRIGHT_ACTIONS + ): raise ValueError( f"Invalid playwright code {item}, ", f"the function needs to be one of {PLAYWRIGHT_LOCATORS + PLAYWRIGHT_ACTIONS}", @@ -1429,7 +1552,9 @@ def create_playwright_action(playwright_code: str) -> Action: f"Invalid type/fill action, required to be page.type(TEXT)" ) text = match.group(1) - return create_type_action(text=text, pw_code=playwright_code) + return create_type_action( + text=text, pw_code=playwright_code + ) case "select_option": return create_select_option_action(pw_code=playwright_code) case "check": @@ -1448,7 +1573,9 @@ def create_playwright_action(playwright_code: str) -> Action: p = r"page_focus\((\d+)\)" match = re.search(p, playwright_code) if not match: - raise ActionParsingError("page focus requires a page number") + raise ActionParsingError( + "page focus requires a page number" + ) page_num = int(match.group(1)) return create_page_focus_action(page_num) case "new_tab": @@ -1484,23 +1611,33 @@ def create_id_based_action(action_str: str) -> Action: case "click": match = re.search(r"click ?\[(\d+)\]", action_str) if not match: - raise ActionParsingError(f"Invalid click action {action_str}") + raise ActionParsingError( + f"Invalid click action {action_str}" + ) element_id = match.group(1) return create_click_action(element_id=element_id) case "hover": match = re.search(r"hover ?\[(\d+)\]", action_str) if not match: - raise ActionParsingError(f"Invalid hover action {action_str}") + raise ActionParsingError( + f"Invalid hover action {action_str}" + ) element_id = match.group(1) return create_hover_action(element_id=element_id) case "type": # add default enter flag - if not (action_str.endswith("[0]") or action_str.endswith("[1]")): + if not ( + action_str.endswith("[0]") or action_str.endswith("[1]") + ): action_str += " [1]" - match = re.search(r"type ?\[(\d+)\] ?\[(.+)\] ?\[(\d+)\]", action_str) + match = re.search( + r"type ?\[(\d+)\] ?\[(.+)\] ?\[(\d+)\]", action_str + ) if not match: - raise ActionParsingError(f"Invalid type action {action_str}") + raise ActionParsingError( + f"Invalid type action {action_str}" + ) element_id, text, enter_flag = ( match.group(1), match.group(2), @@ -1512,20 +1649,26 @@ def create_id_based_action(action_str: str) -> Action: case "press": match = re.search(r"press ?\[(.+)\]", action_str) if not match: - raise ActionParsingError(f"Invalid press action {action_str}") + raise ActionParsingError( + f"Invalid press action {action_str}" + ) key_comb = match.group(1) return create_key_press_action(key_comb=key_comb) case "scroll": # up or down match = re.search(r"scroll ?\[?(up|down)\]?", action_str) if not match: - raise ActionParsingError(f"Invalid scroll action {action_str}") + raise ActionParsingError( + f"Invalid scroll action {action_str}" + ) direction = match.group(1) return create_scroll_action(direction=direction) case "goto": match = re.search(r"goto ?\[(.+)\]", action_str) if not match: - raise ActionParsingError(f"Invalid goto action {action_str}") + raise ActionParsingError( + f"Invalid goto action {action_str}" + ) url = match.group(1) return create_goto_url_action(url=url) case "new_tab": @@ -1537,7 +1680,9 @@ def create_id_based_action(action_str: str) -> Action: case "tab_focus": match = re.search(r"tab_focus ?\[(\d+)\]", action_str) if not match: - raise ActionParsingError(f"Invalid tab_focus action {action_str}") + raise ActionParsingError( + f"Invalid tab_focus action {action_str}" + ) page_number = int(match.group(1)) return create_page_focus_action(page_number) case "close_tab": diff --git a/tests/test_browser_env/test_action_functionalities.py b/tests/test_browser_env/test_action_functionalities.py index d5ac8c2..22f538c 100644 --- a/tests/test_browser_env/test_action_functionalities.py +++ b/tests/test_browser_env/test_action_functionalities.py @@ -24,7 +24,9 @@ def test_frame_locator(script_browser_env: ScriptBrowserEnv) -> None: env.reset() for action in seq.split("\n"): action = action.strip() - _, success, _, _, info = env.step(create_playwright_action(action)) + _, success, _, _, info = env.step( + create_playwright_action(action) + ) assert success @@ -47,7 +49,9 @@ def test_basic(script_browser_env: ScriptBrowserEnv) -> None: env.reset() for action in seq.split("\n"): action = action.strip() - _, success, _, _, info = env.step(create_playwright_action(action)) + _, success, _, _, info = env.step( + create_playwright_action(action) + ) assert success @@ -59,7 +63,9 @@ def test_hover(script_browser_env: ScriptBrowserEnv) -> None: env.reset() for action in seq.split("\n"): action = action.strip() - _, success, _, _, info = env.step(create_playwright_action(action)) + _, success, _, _, info = env.step( + create_playwright_action(action) + ) assert success @@ -70,7 +76,9 @@ def test_select_option(script_browser_env: ScriptBrowserEnv) -> None: env.reset() for action in seq.split("\n"): action = action.strip() - _, success, _, _, info = env.step(create_playwright_action(action)) + _, success, _, _, info = env.step( + create_playwright_action(action) + ) assert success @@ -90,11 +98,15 @@ def test_xpath(script_browser_env: ScriptBrowserEnv) -> None: env.reset() for action in seq.split("\n"): action = action.strip() - _, success, _, _, info = env.step(create_playwright_action(action)) + _, success, _, _, info = env.step( + create_playwright_action(action) + ) assert success -def test_inter_page_actions(script_browser_env: ScriptBrowserEnv) -> None: +def test_inter_page_actions( + script_browser_env: ScriptBrowserEnv, +) -> None: env = script_browser_env seq = """page.goto("https://demo.playwright.dev/todomvc/") browser.new_tab() @@ -108,12 +120,16 @@ def test_inter_page_actions(script_browser_env: ScriptBrowserEnv) -> None: env.reset() for action in seq.split("\n"): action = action.strip() - _, success, _, _, info = env.step(create_playwright_action(action)) + _, success, _, _, info = env.step( + create_playwright_action(action) + ) assert success assert "https://demo.playwright.dev/todomvc" in info["page"].url -def test_scroll(current_viewport_script_browser_env: ScriptBrowserEnv) -> None: +def test_scroll( + current_viewport_script_browser_env: ScriptBrowserEnv, +) -> None: env = current_viewport_script_browser_env env.reset() _, success, _, _, _ = env.step(create_scroll_action("down")) @@ -138,21 +154,33 @@ def test_id_click( # get the id of the link element_id = re.search(r"\[(\d+)\] link 'McKenna/Bell'", obs["text"]).group(1) # type: ignore - obs, success, _, _, info = env.step(create_id_based_action(f"click [{element_id}]")) + obs, success, _, _, info = env.step( + create_id_based_action(f"click [{element_id}]") + ) assert success - assert info["page"].url == "https://russmaxdesign.github.io/exercise/#link-four" + assert ( + info["page"].url + == "https://russmaxdesign.github.io/exercise/#link-four" + ) obs, success, _, _, info = env.step(create_scroll_action("down")) assert "link 'Classification'" in obs["text"] element_id = re.search(r"\[(\d+)\] link 'Classification'", obs["text"]).group(1) # type: ignore - obs, success, _, _, info = env.step(create_id_based_action(f"click [{element_id}]")) + obs, success, _, _, info = env.step( + create_id_based_action(f"click [{element_id}]") + ) assert success - assert info["page"].url == "https://russmaxdesign.github.io/exercise/#link-two" + assert ( + info["page"].url + == "https://russmaxdesign.github.io/exercise/#link-two" + ) assert "radio 'Weekly'" in obs["text"] element_id = re.search(r"\[(\d+)\] radio 'Weekly'", obs["text"]).group(1) # type: ignore - obs, success, _, _, info = env.step(create_id_based_action(f"click [{element_id}]")) + obs, success, _, _, info = env.step( + create_id_based_action(f"click [{element_id}]") + ) assert success assert "radio 'Weekly'" in obs["text"] @@ -164,13 +192,17 @@ def test_id_hover( env.reset() obs, success, _, _, info = env.step( - create_playwright_action('page.goto("https://ianlunn.github.io/Hover/")') + create_playwright_action( + 'page.goto("https://ianlunn.github.io/Hover/")' + ) ) assert success assert "link 'Download on GitHub'" in obs["text"] element_id = re.search(r"\[(\d+)\] link 'Download on GitHub'", obs["text"]).group(1) # type: ignore - obs, success, _, _, info = env.step(create_id_based_action(f"hover [{element_id}]")) + obs, success, _, _, info = env.step( + create_id_based_action(f"hover [{element_id}]") + ) assert success @@ -198,13 +230,17 @@ def test_key_press( expect(env.page.get_by_label("Full name")).to_be_focused() expect(env.page.get_by_label("Full name")).to_have_value(s) - obs, success, _, _, info = env.step(create_id_based_action("press [meta+a]")) + obs, success, _, _, info = env.step( + create_id_based_action("press [meta+a]") + ) assert success env.page.get_by_label("Full name").type(s) expect(env.page.get_by_label("Full name")).to_have_value(s) - obs, success, _, _, info = env.step(create_key_press_action("Enter")) + obs, success, _, _, info = env.step( + create_key_press_action("Enter") + ) assert success expect(env.page.get_by_label("Email")).to_be_focused() @@ -238,12 +274,18 @@ def test_e2e_id_based_actions( env = accessibility_tree_script_browser_env env.reset() obs, *_ = env.step( - create_id_based_action("goto [https://russmaxdesign.github.io/exercise/]") + create_id_based_action( + "goto [https://russmaxdesign.github.io/exercise/]" + ) ) element_id = re.search(r"\[(\d+)\] link 'What are mammals\?'", obs["text"]).group(1) # type: ignore obs, *_ = env.step(create_id_based_action(f"click [{element_id}]")) element_id = re.search(r"\[(\d+)\] textbox 'Email'", obs["text"]).group(1) # type: ignore - env.step(create_id_based_action(f"type [{element_id}] [test@gmail.com] [0]")) + env.step( + create_id_based_action( + f"type [{element_id}] [test@gmail.com] [0]" + ) + ) env.step(create_id_based_action("scroll [down]")) env.step(create_id_based_action("scroll [up]")) env.step(create_id_based_action("new_tab")) @@ -254,7 +296,10 @@ def test_e2e_id_based_actions( x = env.step(create_id_based_action("go_forward")) assert x[-1]["page"].url == "https://example.com/" x = env.step(create_id_based_action("tab_focus [0]")) - assert x[-1]["page"].url == "https://russmaxdesign.github.io/exercise/#link-one" + assert ( + x[-1]["page"].url + == "https://russmaxdesign.github.io/exercise/#link-one" + ) def test_id_delete_input( @@ -279,13 +324,19 @@ def test_id_delete_input( locator = env.page.get_by_label("Full name") expect(locator).to_have_value(s) - obs, success, _, _, info = env.step(create_id_based_action(f"click [{element_id}]")) + obs, success, _, _, info = env.step( + create_id_based_action(f"click [{element_id}]") + ) assert success - obs, success, _, _, info = env.step(create_id_based_action(f"press [Meta+a]")) + obs, success, _, _, info = env.step( + create_id_based_action(f"press [Meta+a]") + ) assert success - obs, success, _, _, info = env.step(create_id_based_action("press [backspace]")) + obs, success, _, _, info = env.step( + create_id_based_action("press [backspace]") + ) assert success new_s = "NEW" From fe58b550e01060081cbffd1a794d3633c3a81dfe Mon Sep 17 00:00:00 2001 From: oootttyyy Date: Sat, 16 Sep 2023 01:40:57 -0400 Subject: [PATCH 6/7] fix black formatting --- browser_env/actions.py | 180 +++++------------- .../test_action_functionalities.py | 32 +--- 2 files changed, 56 insertions(+), 156 deletions(-) diff --git a/browser_env/actions.py b/browser_env/actions.py index c2e2cc8..8376270 100644 --- a/browser_env/actions.py +++ b/browser_env/actions.py @@ -68,9 +68,9 @@ def is_in_viewport( boxy1 = box["y"] + box["height"] viewportx0, viewporty0 = 0, 0 viewportx1, viewporty1 = viewport["width"], viewport["height"] - inter = max( - 0, min(boxx1, viewportx1) - max(boxx0, viewportx0) - ) * max(0, min(boxy1, viewporty1) - max(boxy0, viewporty0)) + inter = max(0, min(boxx1, viewportx1) - max(boxx0, viewportx0)) * max( + 0, min(boxy1, viewporty1) - max(boxy0, viewporty0) + ) ratio = inter / (box["width"] * box["height"]) return ratio > threshold @@ -87,9 +87,9 @@ async def async_is_in_viewport( boxy1 = box["y"] + box["height"] viewportx0, viewporty0 = 0, 0 viewportx1, viewporty1 = viewport["width"], viewport["height"] - inter = max( - 0, min(boxx1, viewportx1) - max(boxx0, viewportx0) - ) * max(0, min(boxy1, viewporty1) - max(boxy0, viewporty0)) + inter = max(0, min(boxx1, viewportx1) - max(boxx0, viewportx0)) * max( + 0, min(boxy1, viewporty1) - max(boxy0, viewporty0) + ) ratio = inter / (box["width"] * box["height"]) return ratio > threshold @@ -156,9 +156,7 @@ def action2str( f"Unknown action type {action['action_type']}" ) else: - raise NotImplementedError( - f"Unknown action set tag {action_set_tag}" - ) + raise NotImplementedError(f"Unknown action set tag {action_set_tag}") return action_str @@ -172,9 +170,7 @@ def action2create_function(action: Action) -> str: direction = "up" if "up" in action["direction"] else "down" return f"create_scroll_action({repr(direction)})" case ActionTypes.KEY_PRESS: - return ( - f"create_key_press_action({repr(action['key_comb'])})" - ) + return f"create_key_press_action({repr(action['key_comb'])})" # inter-page actions case ActionTypes.PAGE_FOCUS: return f"create_page_focus_action({action['page_number']})" @@ -397,13 +393,9 @@ def create_random_action() -> Action: return { "action_type": np.random.randint(len(ActionTypes)), "coords": np.random.rand(2).astype(np.float32), - "element_role": np.random.randint( - len(ROLES) + len(SPECIAL_LOCATORS) - ), + "element_role": np.random.randint(len(ROLES) + len(SPECIAL_LOCATORS)), "element_name": "".join( - random.choices( - ASCII_CHARSET, k=np.random.randint(TEXT_MAX_LENGTH) - ) + random.choices(ASCII_CHARSET, k=np.random.randint(TEXT_MAX_LENGTH)) ), "text": list( random.choices( @@ -413,9 +405,7 @@ def create_random_action() -> Action: ), "page_number": np.random.randint(MAX_PAGE_NUMBER), "url": "".join( - random.choices( - ASCII_CHARSET, k=np.random.randint(URL_MAX_LENGTH) - ) + random.choices(ASCII_CHARSET, k=np.random.randint(URL_MAX_LENGTH)) ), "nth": np.random.randint(MAX_ELEMENT_INDEX_IN_VIEWPORT), "element_id": str(np.random.randint(MAX_ELEMENT_ID)), @@ -608,9 +598,7 @@ def create_mouse_click_action( } ) else: - raise ValueError( - "left and top must be both None or both not None" - ) + raise ValueError("left and top must be both None or both not None") return action @@ -814,9 +802,7 @@ async def aexecute_scroll(direction: str, page: APage) -> None: @beartype def execute_key_press(key: str, page: Page) -> None: """Press a key.""" - if "Meta" in key and "Mac" not in page.evaluate( - "navigator.platform" - ): + if "Meta" in key and "Mac" not in page.evaluate("navigator.platform"): key = key.replace("Meta", "Control") page.keyboard.press(key) @@ -824,9 +810,7 @@ def execute_key_press(key: str, page: Page) -> None: @beartype async def aexecute_key_press(key: str, page: APage) -> None: """Press a key.""" - if "Meta" in key and "Mac" not in page.evaluate( - "navigator.platform" - ): + if "Meta" in key and "Mac" not in page.evaluate("navigator.platform"): key = key.replace("Meta", "Control") await page.keyboard.press(key) @@ -842,9 +826,7 @@ def execute_mouse_hover(left: float, top: float, page: Page) -> None: @beartype -async def aexecute_mouse_hover( - left: float, top: float, page: APage -) -> None: +async def aexecute_mouse_hover(left: float, top: float, page: APage) -> None: """Click at coordinates (left, top).""" viewport_size = page.viewport_size assert viewport_size @@ -863,9 +845,7 @@ def execute_mouse_click(left: float, top: float, page: Page) -> None: @beartype -async def aexecute_mouse_click( - left: float, top: float, page: APage -) -> None: +async def aexecute_mouse_click(left: float, top: float, page: APage) -> None: """Click at coordinates (left, top).""" viewport_size = page.viewport_size assert viewport_size @@ -934,9 +914,7 @@ def execute_focus( """Click the specified DOM element.""" element_role_str = _id2role[element_role] if page.viewport_size is None: - raise ValueError( - "Viewport size is not set for the current page" - ) + raise ValueError("Viewport size is not set for the current page") element_location_list: list[tuple[Locator, float, float]] = [] for frame in page.frames: match element_role_str: @@ -962,9 +940,7 @@ def execute_focus( raise ValueError( f"There are only {len(element_location_list)} elements found in viewport, but {nth + 1} is requested" ) - element_location_list.sort( - key=lambda x: (x[2], x[1]) - ) # row major order + element_location_list.sort(key=lambda x: (x[2], x[1])) # row major order element_location_list[nth][0].focus() @@ -975,9 +951,7 @@ async def aexecute_focus( """Click the specified DOM element.""" element_role_str = _id2role[element_role] if page.viewport_size is None: - raise ValueError( - "Viewport size is not set for the current page" - ) + raise ValueError("Viewport size is not set for the current page") element_location_list: list[tuple[ALocator, float, float]] = [] for frame in page.frames: match element_role_str: @@ -1003,24 +977,18 @@ async def aexecute_focus( raise ValueError( f"There are only {len(element_location_list)} elements found in viewport, but {nth + 1} is requested" ) - element_location_list.sort( - key=lambda x: (x[2], x[1]) - ) # row major order + element_location_list.sort(key=lambda x: (x[2], x[1])) # row major order await element_location_list[nth][0].focus() @beartype -def locate( - locator_calls: list[ParsedPlaywrightCode], page: Page -) -> Locator: +def locate(locator_calls: list[ParsedPlaywrightCode], page: Page) -> Locator: locator = page for call in locator_calls: function_name = call["function_name"] arguments = call["arguments"] keywords = call["keywords"] - locator = getattr(locator, function_name)( - *arguments, **keywords - ) + locator = getattr(locator, function_name)(*arguments, **keywords) return locator # type: ignore[return-value] @@ -1033,9 +1001,7 @@ async def alocate( function_name = call["function_name"] arguments = call["arguments"] keywords = call["keywords"] - locator = await getattr(locator, function_name)( - *arguments, **keywords - ) + locator = await getattr(locator, function_name)(*arguments, **keywords) return locator # type: ignore[return-value] @@ -1095,9 +1061,7 @@ def execute_playwright_type( ) -> None: locator = locate(locator_code, page) # perform the action - pw_action_args = [ - text - ] + pw_action_args # text is the first argument + pw_action_args = [text] + pw_action_args # text is the first argument locator.type(*pw_action_args, **pw_action_kwargs) @@ -1111,9 +1075,7 @@ async def aexecute_playwright_type( ) -> None: locator = await alocate(locator_code, page) # perform the action - pw_action_args = [ - text - ] + pw_action_args # text is the first argument + pw_action_args = [text] + pw_action_args # text is the first argument await locator.type(*pw_action_args, **pw_action_kwargs) @@ -1180,13 +1142,9 @@ def execute_action( execute_key_press(keys, page) case ActionTypes.MOUSE_CLICK: - execute_mouse_click( - action["coords"][0], action["coords"][1], page - ) + execute_mouse_click(action["coords"][0], action["coords"][1], page) case ActionTypes.MOUSE_HOVER: - execute_mouse_hover( - action["coords"][0], action["coords"][1], page - ) + execute_mouse_hover(action["coords"][0], action["coords"][1], page) case ActionTypes.KEYBOARD_TYPE: execute_type(action["text"], page) @@ -1196,9 +1154,7 @@ def execute_action( if action["element_id"]: element_id = action["element_id"] element_center = obseration_processor.get_element_center(element_id) # type: ignore[attr-defined] - execute_mouse_click( - element_center[0], element_center[1], page - ) + execute_mouse_click(element_center[0], element_center[1], page) elif action["element_role"] and action["element_name"]: element_role = int(action["element_role"]) element_name = action["element_name"] @@ -1209,20 +1165,14 @@ def execute_action( parsed_code = parse_playwright_code(action["pw_code"]) locator_code = parsed_code[:-1] # [shuyanzh], don't support action args and kwargs now - execute_playwright_click( - locator_code=locator_code, page=page - ) + execute_playwright_click(locator_code=locator_code, page=page) else: - raise ValueError( - "No proper locator found for click action" - ) + raise ValueError("No proper locator found for click action") case ActionTypes.HOVER: if action["element_id"]: element_id = action["element_id"] element_center = obseration_processor.get_element_center(element_id) # type: ignore[attr-defined] - execute_mouse_hover( - element_center[0], element_center[1], page - ) + execute_mouse_hover(element_center[0], element_center[1], page) elif action["element_role"] and action["element_name"]: element_role = int(action["element_role"]) element_name = action["element_name"] @@ -1232,9 +1182,7 @@ def execute_action( parsed_code = parse_playwright_code(action["pw_code"]) locator_code = parsed_code[:-1] # [shuyanzh], don't support action args and kwargs now - execute_playwright_hover( - locator_code=locator_code, page=page - ) + execute_playwright_hover(locator_code=locator_code, page=page) else: raise NotImplementedError( "No proper locator found for hover action" @@ -1243,9 +1191,7 @@ def execute_action( if action["element_id"]: element_id = action["element_id"] element_center = obseration_processor.get_element_center(element_id) # type: ignore[attr-defined] - execute_mouse_click( - element_center[0], element_center[1], page - ) + execute_mouse_click(element_center[0], element_center[1], page) execute_type(action["text"], page) elif action["element_role"] and action["element_name"]: element_role = int(action["element_role"]) @@ -1346,9 +1292,7 @@ async def aexecute_action( element_role = int(action["element_role"]) element_name = action["element_name"] nth = action["nth"] - await aexecute_focus( - element_role, element_name, nth, page - ) + await aexecute_focus(element_role, element_name, nth, page) await aexecute_click_current(page) elif action["pw_code"]: parsed_code = parse_playwright_code(action["pw_code"]) @@ -1358,9 +1302,7 @@ async def aexecute_action( locator_code=locator_code, page=page ) else: - raise ValueError( - "No proper locator found for click action" - ) + raise ValueError("No proper locator found for click action") case ActionTypes.HOVER: if action["element_id"]: raise NotImplementedError @@ -1368,9 +1310,7 @@ async def aexecute_action( element_role = int(action["element_role"]) element_name = action["element_name"] nth = action["nth"] - await aexecute_focus( - element_role, element_name, nth, page - ) + await aexecute_focus(element_role, element_name, nth, page) elif action["pw_code"]: parsed_code = parse_playwright_code(action["pw_code"]) locator_code = parsed_code[:-1] @@ -1389,9 +1329,7 @@ async def aexecute_action( element_role = int(action["element_role"]) element_name = action["element_name"] nth = action["nth"] - await aexecute_focus( - element_role, element_name, nth, page - ) + await aexecute_focus(element_role, element_name, nth, page) await aexecute_type(action["text"], page) elif action["pw_code"]: parsed_code = parse_playwright_code(action["pw_code"]) @@ -1428,9 +1366,7 @@ async def aexecute_action( if action["pw_code"]: parsed_code = parse_playwright_code(action["pw_code"]) locator_code = parsed_code[:-1] - await aexecute_playwright_select_option( - locator_code, page - ) + await aexecute_playwright_select_option(locator_code, page) else: raise NotImplementedError( "No proper locator found for select option action" @@ -1471,9 +1407,7 @@ def parse_playwright_code(code: str) -> list[ParsedPlaywrightCode]: if isinstance(node, ast.Call): function_name = node.func.id # type: ignore[attr-defined] arguments = [ - ast.literal_eval(arg) - if isinstance(arg, ast.Str) - else arg + ast.literal_eval(arg) if isinstance(arg, ast.Str) else arg for arg in node.args ] keywords = { @@ -1552,9 +1486,7 @@ def create_playwright_action(playwright_code: str) -> Action: f"Invalid type/fill action, required to be page.type(TEXT)" ) text = match.group(1) - return create_type_action( - text=text, pw_code=playwright_code - ) + return create_type_action(text=text, pw_code=playwright_code) case "select_option": return create_select_option_action(pw_code=playwright_code) case "check": @@ -1573,9 +1505,7 @@ def create_playwright_action(playwright_code: str) -> Action: p = r"page_focus\((\d+)\)" match = re.search(p, playwright_code) if not match: - raise ActionParsingError( - "page focus requires a page number" - ) + raise ActionParsingError("page focus requires a page number") page_num = int(match.group(1)) return create_page_focus_action(page_num) case "new_tab": @@ -1611,33 +1541,25 @@ def create_id_based_action(action_str: str) -> Action: case "click": match = re.search(r"click ?\[(\d+)\]", action_str) if not match: - raise ActionParsingError( - f"Invalid click action {action_str}" - ) + raise ActionParsingError(f"Invalid click action {action_str}") element_id = match.group(1) return create_click_action(element_id=element_id) case "hover": match = re.search(r"hover ?\[(\d+)\]", action_str) if not match: - raise ActionParsingError( - f"Invalid hover action {action_str}" - ) + raise ActionParsingError(f"Invalid hover action {action_str}") element_id = match.group(1) return create_hover_action(element_id=element_id) case "type": # add default enter flag - if not ( - action_str.endswith("[0]") or action_str.endswith("[1]") - ): + if not (action_str.endswith("[0]") or action_str.endswith("[1]")): action_str += " [1]" match = re.search( r"type ?\[(\d+)\] ?\[(.+)\] ?\[(\d+)\]", action_str ) if not match: - raise ActionParsingError( - f"Invalid type action {action_str}" - ) + raise ActionParsingError(f"Invalid type action {action_str}") element_id, text, enter_flag = ( match.group(1), match.group(2), @@ -1649,26 +1571,20 @@ def create_id_based_action(action_str: str) -> Action: case "press": match = re.search(r"press ?\[(.+)\]", action_str) if not match: - raise ActionParsingError( - f"Invalid press action {action_str}" - ) + raise ActionParsingError(f"Invalid press action {action_str}") key_comb = match.group(1) return create_key_press_action(key_comb=key_comb) case "scroll": # up or down match = re.search(r"scroll ?\[?(up|down)\]?", action_str) if not match: - raise ActionParsingError( - f"Invalid scroll action {action_str}" - ) + raise ActionParsingError(f"Invalid scroll action {action_str}") direction = match.group(1) return create_scroll_action(direction=direction) case "goto": match = re.search(r"goto ?\[(.+)\]", action_str) if not match: - raise ActionParsingError( - f"Invalid goto action {action_str}" - ) + raise ActionParsingError(f"Invalid goto action {action_str}") url = match.group(1) return create_goto_url_action(url=url) case "new_tab": diff --git a/tests/test_browser_env/test_action_functionalities.py b/tests/test_browser_env/test_action_functionalities.py index 22f538c..0bdfc0d 100644 --- a/tests/test_browser_env/test_action_functionalities.py +++ b/tests/test_browser_env/test_action_functionalities.py @@ -24,9 +24,7 @@ def test_frame_locator(script_browser_env: ScriptBrowserEnv) -> None: env.reset() for action in seq.split("\n"): action = action.strip() - _, success, _, _, info = env.step( - create_playwright_action(action) - ) + _, success, _, _, info = env.step(create_playwright_action(action)) assert success @@ -49,9 +47,7 @@ def test_basic(script_browser_env: ScriptBrowserEnv) -> None: env.reset() for action in seq.split("\n"): action = action.strip() - _, success, _, _, info = env.step( - create_playwright_action(action) - ) + _, success, _, _, info = env.step(create_playwright_action(action)) assert success @@ -63,9 +59,7 @@ def test_hover(script_browser_env: ScriptBrowserEnv) -> None: env.reset() for action in seq.split("\n"): action = action.strip() - _, success, _, _, info = env.step( - create_playwright_action(action) - ) + _, success, _, _, info = env.step(create_playwright_action(action)) assert success @@ -76,9 +70,7 @@ def test_select_option(script_browser_env: ScriptBrowserEnv) -> None: env.reset() for action in seq.split("\n"): action = action.strip() - _, success, _, _, info = env.step( - create_playwright_action(action) - ) + _, success, _, _, info = env.step(create_playwright_action(action)) assert success @@ -98,9 +90,7 @@ def test_xpath(script_browser_env: ScriptBrowserEnv) -> None: env.reset() for action in seq.split("\n"): action = action.strip() - _, success, _, _, info = env.step( - create_playwright_action(action) - ) + _, success, _, _, info = env.step(create_playwright_action(action)) assert success @@ -120,9 +110,7 @@ def test_inter_page_actions( env.reset() for action in seq.split("\n"): action = action.strip() - _, success, _, _, info = env.step( - create_playwright_action(action) - ) + _, success, _, _, info = env.step(create_playwright_action(action)) assert success assert "https://demo.playwright.dev/todomvc" in info["page"].url @@ -238,9 +226,7 @@ def test_key_press( env.page.get_by_label("Full name").type(s) expect(env.page.get_by_label("Full name")).to_have_value(s) - obs, success, _, _, info = env.step( - create_key_press_action("Enter") - ) + obs, success, _, _, info = env.step(create_key_press_action("Enter")) assert success expect(env.page.get_by_label("Email")).to_be_focused() @@ -282,9 +268,7 @@ def test_e2e_id_based_actions( obs, *_ = env.step(create_id_based_action(f"click [{element_id}]")) element_id = re.search(r"\[(\d+)\] textbox 'Email'", obs["text"]).group(1) # type: ignore env.step( - create_id_based_action( - f"type [{element_id}] [test@gmail.com] [0]" - ) + create_id_based_action(f"type [{element_id}] [test@gmail.com] [0]") ) env.step(create_id_based_action("scroll [down]")) env.step(create_id_based_action("scroll [up]")) From 017a73597ccc6801b11cc4172ff25cab6510af22 Mon Sep 17 00:00:00 2001 From: oootttyyy Date: Sat, 16 Sep 2023 01:57:46 -0400 Subject: [PATCH 7/7] fix async key press --- browser_env/actions.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/browser_env/actions.py b/browser_env/actions.py index 8376270..1f9db76 100644 --- a/browser_env/actions.py +++ b/browser_env/actions.py @@ -810,7 +810,9 @@ def execute_key_press(key: str, page: Page) -> None: @beartype async def aexecute_key_press(key: str, page: APage) -> None: """Press a key.""" - if "Meta" in key and "Mac" not in page.evaluate("navigator.platform"): + if "Meta" in key and "Mac" not in await page.evaluate( + "navigator.platform" + ): key = key.replace("Meta", "Control") await page.keyboard.press(key)