Skip to content

Commit

Permalink
fix typing errors for Target.
Browse files Browse the repository at this point in the history
  • Loading branch information
perrygoy committed May 22, 2024
1 parent e5d92a7 commit c7ddd5c
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 22 deletions.
35 changes: 29 additions & 6 deletions screenpy_playwright/target.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from collections import UserString
from dataclasses import dataclass
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Pattern, TypedDict

from playwright.sync_api import Locator

Expand All @@ -13,7 +13,26 @@

if TYPE_CHECKING:
from screenpy import Actor
from typing_extensions import Self
from typing_extensions import NotRequired, Self, Unpack

_ManipulationArgsType = tuple[str | int | None, ...]

class _ManipulationKwargsType(TypedDict):
"""Types that can be passed to Playwright's Locator or FrameLocator."""

has_text: NotRequired[str | Pattern[str] | None]
has_not_text: NotRequired[str | Pattern[str] | None]
has: NotRequired[Locator | None]
has_not: NotRequired[Locator | None]
exact: NotRequired[bool | None]
checked: NotRequired[bool | None]
disabled: NotRequired[bool | None]
expanded: NotRequired[bool | None]
include_hidden: NotRequired[bool | None]
level: NotRequired[int | None]
name: NotRequired[str | Pattern[str] | None]
pressed: NotRequired[bool | None]
selected: NotRequired[bool | None]


@dataclass
Expand All @@ -29,8 +48,8 @@ class _Manipulation(UserString):

target: Target
name: str
args: tuple | None = None
kwargs: dict | None = None
args: _ManipulationArgsType | None = None
kwargs: _ManipulationKwargsType | None = None

def __hash__(self) -> int:
"""Appear as the name, in case this is an attribute and not a method."""
Expand All @@ -44,7 +63,11 @@ def __getattr__(self, name: str) -> Target | _Manipulation:
"""Defer back to the Target for unknown attributes."""
return getattr(self.target, name)

def __call__(self, *args: str, **kwargs: str) -> Target:
def __call__(
self,
*args: Unpack[_ManipulationArgsType],
**kwargs: Unpack[_ManipulationKwargsType],
) -> Target:
"""Add args and kwargs to the manipulation."""
self.args = args
self.kwargs = kwargs
Expand Down Expand Up @@ -90,7 +113,7 @@ class Target:
)
# Using Playwright strategies directly
Target().frame_locator("#todoframe").get_by_label("todo")
Target("To-Do list").frame_locator("#todoframe").get_by_label("todo")
"""

manipulations: list[_Manipulation]
Expand Down
48 changes: 32 additions & 16 deletions tests/test_target.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,24 @@
if TYPE_CHECKING:
from screenpy import Actor

from screenpy_playwright.target import (
_ManipulationArgsType,
_ManipulationKwargsType,
)


class Test_Manipulations:
def test_proper_display(self) -> None:
name = "viking"
args = ("spam", "eggs")
kwargs = {"sausage": "spam"}
args_str = "'spam', 'eggs'"
kwargs_str = "sausage='spam'"
args: _ManipulationArgsType = ("spam", "eggs", 1, None)
kwargs: _ManipulationKwargsType = {
"has_text": "spam",
"exact": True,
"include_hidden": None,
"level": 1,
}
args_str = "'spam', 'eggs', 1, None"
kwargs_str = "has_text='spam', exact=True, include_hidden=None, level=1"

m_for_attribute = _Manipulation(Target(), name)
m_with_neither = _Manipulation(Target(), name, (), {})
Expand Down Expand Up @@ -46,12 +56,14 @@ def test_can_be_instantiated(self) -> None:
t3 = Target("test")
t4 = Target().located_by("test")
t5 = Target()
t6 = Target("test").get_by_label("test", exact=True)

assert isinstance(t1, Target)
assert isinstance(t2, Target)
assert isinstance(t3, Target)
assert isinstance(t4, Target)
assert isinstance(t5, Target)
assert isinstance(t6, Target)

def test_auto_describe(self) -> None:
t1 = Target().located_by("#yellow")
Expand Down Expand Up @@ -96,29 +108,33 @@ def test_found_by_with_frames(self, Tester: Actor) -> None:

# list from https://playwright.dev/python/docs/locators
@pytest.mark.parametrize(
"strategy",
("strategy", "args", "kwargs"),
[
"get_by_role",
"get_by_text",
"get_by_label",
"get_by_placeholder",
"get_by_alt_text",
"get_by_title",
"get_by_test_id",
("get_by_role", ("button",), {}),
("get_by_text", ("Log In",), {"exact": True}),
("get_by_label", ("spam",), {"level": 1, "exact": None}),
("get_by_placeholder", ("eggs",), {}),
("get_by_alt_text", ("sausage",), {}),
("get_by_title", ("baked beans",), {}),
("get_by_test_id", ("spam",), {}),
("nth", (1,), {}),
],
)
def test_found_by_with_playwright_strategies(
self, Tester: Actor, strategy: str
self,
Tester: Actor,
strategy: str,
args: _ManipulationArgsType,
kwargs: _ManipulationKwargsType,
) -> None:
test_value = "Eeuugh!"
mocked_btws = Tester.ability_to(BrowseTheWebSynchronously)
mocked_btws.current_page = mock.Mock()

target = Target.the("test")
getattr(target, strategy)(test_value).found_by(Tester)
getattr(target, strategy)(*args, **kwargs).found_by(Tester)

func = getattr(mocked_btws.current_page.locator("html"), strategy)
func.assert_called_once_with(test_value)
func.assert_called_once_with(*args, **kwargs)

def test_found_by_chain(self, Tester: Actor) -> None:
test_locator = "#spam>baked-beans>eggs>sausage+spam"
Expand Down

0 comments on commit c7ddd5c

Please sign in to comment.