Skip to content

Fix typing errors with Target, Enter. #15

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
May 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 30 additions & 31 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion screenpy_playwright/actions/enter.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,10 @@ def the_secret(cls, text: str, **kwargs: Unpack[EnterTypes]) -> Self:
"""
return cls(text, mask=True, **kwargs)

the_password = the_secret
@classmethod
def the_password(cls, text: str, **kwargs: Unpack[EnterTypes]) -> Self:
"""Alias for ``the_secret``, recreated for mypy."""
return cls.the_secret(text, **kwargs)

def into_the(self, target: Target, **kwargs: Unpack[EnterTypes]) -> Enter:
"""Target the element to enter text into.
Expand Down
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, Tuple, TypedDict, Union

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[Union[str, int, None], ...]

class _ManipulationKwargsType(TypedDict):
"""Types for kwargs that are passed to Playwright's locator methods."""

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
2 changes: 2 additions & 0 deletions tests/test_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,13 @@ def test_can_be_instantiated(self) -> None:
e2 = Enter.the_text("")
e3 = Enter.the_secret("")
e4 = Enter.the_text("").into_the(TARGET)
e5 = Enter.the_password("").into_the(TARGET)

assert isinstance(e1, Enter)
assert isinstance(e2, Enter)
assert isinstance(e3, Enter)
assert isinstance(e4, Enter)
assert isinstance(e5, Enter)

def test_implements_protocol(self) -> None:
e = Enter("")
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