Skip to content

Commit

Permalink
using Self now that it's available in typing_extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
bandophahita committed Feb 14, 2024
1 parent dd85ed2 commit f21539a
Show file tree
Hide file tree
Showing 22 changed files with 212 additions and 285 deletions.
24 changes: 11 additions & 13 deletions screenpy_selenium/abilities/browse_the_web.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
from __future__ import annotations

import os
from typing import TYPE_CHECKING, TypeVar
from typing import TYPE_CHECKING

from selenium.webdriver import Chrome, Firefox, Remote, Safari
from typing_extensions import Self

from ..exceptions import BrowsingError

Expand All @@ -15,9 +16,6 @@
DEFAULT_APPIUM_HUB_URL = "http://localhost:4723/wd/hub"


SelfBrowseTheWeb = TypeVar("SelfBrowseTheWeb", bound="BrowseTheWeb")


class BrowseTheWeb:
"""Use Selenium to enable browsing the web with a web browser.
Expand All @@ -35,22 +33,22 @@ class BrowseTheWeb:
browser: WebDriver

@classmethod
def using_chrome(cls: type[SelfBrowseTheWeb]) -> SelfBrowseTheWeb:
def using_chrome(cls) -> Self:
"""Create and use a default Chrome Selenium webdriver instance."""
return cls.using(browser=Chrome())

@classmethod
def using_firefox(cls: type[SelfBrowseTheWeb]) -> SelfBrowseTheWeb:
def using_firefox(cls) -> Self:
"""Create and use a default Firefox Selenium webdriver instance."""
return cls.using(browser=Firefox())

@classmethod
def using_safari(cls: type[SelfBrowseTheWeb]) -> SelfBrowseTheWeb:
def using_safari(cls) -> Self:
"""Create and use a default Safari Selenium webdriver instance."""
return cls.using(browser=Safari())

@classmethod
def using_ios(cls: type[SelfBrowseTheWeb]) -> SelfBrowseTheWeb:
def using_ios(cls) -> Self:
"""
Create and use a default Remote driver instance.
Expand Down Expand Up @@ -83,7 +81,7 @@ def using_ios(cls: type[SelfBrowseTheWeb]) -> SelfBrowseTheWeb:
return cls.using(browser=Remote(hub_url, IOS_CAPABILITIES))

@classmethod
def using_android(cls: type[SelfBrowseTheWeb]) -> SelfBrowseTheWeb:
def using_android(cls) -> Self:
"""
Create and use a default Remote driver instance.
Expand Down Expand Up @@ -116,19 +114,19 @@ def using_android(cls: type[SelfBrowseTheWeb]) -> SelfBrowseTheWeb:
return cls.using(browser=Remote(hub_url, ANDROID_CAPABILITIES))

@classmethod
def using(cls: type[SelfBrowseTheWeb], browser: WebDriver) -> SelfBrowseTheWeb:
def using(cls, browser: WebDriver) -> Self:
"""Provide an already-set-up WebDriver to use to browse the web."""
return cls(browser=browser)

def forget(self: SelfBrowseTheWeb) -> None:
def forget(self) -> None:
"""Quit the attached browser."""
self.browser.quit()

def __repr__(self: SelfBrowseTheWeb) -> str:
def __repr__(self) -> str:
"""Repr."""
return "Browse the Web"

__str__ = __repr__

def __init__(self: SelfBrowseTheWeb, browser: WebDriver) -> None:
def __init__(self, browser: WebDriver) -> None:
self.browser = browser
19 changes: 8 additions & 11 deletions screenpy_selenium/actions/clear.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,18 @@

from __future__ import annotations

from typing import TYPE_CHECKING, TypeVar
from typing import TYPE_CHECKING

from screenpy.exceptions import DeliveryError
from screenpy.pacing import beat
from selenium.common.exceptions import WebDriverException
from typing_extensions import Self

if TYPE_CHECKING:
from screenpy.actor import Actor

from ..target import Target

SelfClear = TypeVar("SelfClear", bound="Clear")


class Clear:
"""Clear the text from an input field.
Expand All @@ -28,7 +27,7 @@ class Clear:
"""

@classmethod
def the_text_from_the(cls: type[SelfClear], target: Target) -> SelfClear:
def the_text_from_the(cls, target: Target) -> Self:
"""
Specify the Target from which to clear the text.
Expand All @@ -39,23 +38,21 @@ def the_text_from_the(cls: type[SelfClear], target: Target) -> SelfClear:
return cls(target=target)

@classmethod
def the_text_from(cls: type[SelfClear], target: Target) -> SelfClear:
def the_text_from(cls, target: Target) -> Self:
"""Alias for :meth:`~screenpy_selenium.actions.Clear.the_text_from_the`."""
return cls.the_text_from_the(target=target)

@classmethod
def the_text_from_the_first_of_the(
cls: type[SelfClear], target: Target
) -> SelfClear:
def the_text_from_the_first_of_the(cls, target: Target) -> Self:
"""Alias for :meth:`~screenpy_selenium.actions.Clear.the_text_from_the`."""
return cls.the_text_from_the(target=target)

def describe(self: SelfClear) -> str:
def describe(self) -> str:
"""Describe the Action in present tense."""
return f"Clear the text from the {self.target}."

@beat("{} clears text from the {target}.")
def perform_as(self: SelfClear, the_actor: Actor) -> None:
def perform_as(self, the_actor: Actor) -> None:
"""Direct the Actor to clear the text from the input field."""
element = self.target.found_by(the_actor)

Expand All @@ -68,5 +65,5 @@ def perform_as(self: SelfClear, the_actor: Actor) -> None:
)
raise DeliveryError(msg) from e

def __init__(self: SelfClear, target: Target) -> None:
def __init__(self, target: Target) -> None:
self.target = target
21 changes: 9 additions & 12 deletions screenpy_selenium/actions/click.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,19 @@

from __future__ import annotations

from typing import TYPE_CHECKING, TypeVar
from typing import TYPE_CHECKING

from screenpy.exceptions import DeliveryError, UnableToAct
from screenpy.pacing import beat
from selenium.common.exceptions import WebDriverException
from typing_extensions import Self

if TYPE_CHECKING:
from screenpy.actor import Actor
from selenium.webdriver.common.action_chains import ActionChains

from ..target import Target

SelfClick = TypeVar("SelfClick", bound="Click")


class Click:
"""Click on an element!
Expand All @@ -33,7 +32,7 @@ class Click:
"""

@classmethod
def on_the(cls: type[SelfClick], target: Target) -> SelfClick:
def on_the(cls, target: Target) -> Self:
"""
Target the element to click on.
Expand All @@ -44,21 +43,21 @@ def on_the(cls: type[SelfClick], target: Target) -> SelfClick:
return cls(target=target)

@classmethod
def on(cls: type[SelfClick], target: Target) -> SelfClick:
def on(cls, target: Target) -> Self:
"""Alias for :meth:`~screenpy_selenium.actions.Click.on_the`."""
return cls.on_the(target=target)

@classmethod
def on_the_first_of_the(cls: type[SelfClick], target: Target) -> SelfClick:
def on_the_first_of_the(cls, target: Target) -> Self:
"""Alias for :meth:`~screenpy_selenium.actions.Click.on_the`."""
return cls.on_the(target=target)

def describe(self: SelfClick) -> str:
def describe(self) -> str:
"""Describe the Action in present tense."""
return f"Click on the {self.target}."

@beat("{} clicks on the {target}.")
def perform_as(self: SelfClick, the_actor: Actor) -> None:
def perform_as(self, the_actor: Actor) -> None:
"""Direct the Actor to click on the element."""
if self.target is None:
msg = (
Expand All @@ -79,9 +78,7 @@ def perform_as(self: SelfClick, the_actor: Actor) -> None:
raise DeliveryError(msg) from e

@beat("Click{description}!")
def add_to_chain(
self: SelfClick, the_actor: Actor, the_chain: ActionChains
) -> None:
def add_to_chain(self, the_actor: Actor, the_chain: ActionChains) -> None:
"""Add the Click Action to a Chain of Actions."""
if self.target is not None:
the_element = self.target.found_by(the_actor)
Expand All @@ -90,6 +87,6 @@ def add_to_chain(

the_chain.click(on_element=the_element)

def __init__(self: SelfClick, target: Target | None = None) -> None:
def __init__(self, target: Target | None = None) -> None:
self.target = target
self.description = f" on the {target}" if target is not None else ""
27 changes: 10 additions & 17 deletions screenpy_selenium/actions/double_click.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

from __future__ import annotations

from typing import TYPE_CHECKING, TypeVar
from typing import TYPE_CHECKING

from screenpy.pacing import beat
from selenium.webdriver.common.action_chains import ActionChains
from typing_extensions import Self

from ..abilities import BrowseTheWeb

Expand All @@ -14,8 +15,6 @@

from ..target import Target

SelfDoubleClick = TypeVar("SelfDoubleClick", bound="DoubleClick")


class DoubleClick:
"""Double-click on an element, or wherever the cursor currently is.
Expand All @@ -33,7 +32,7 @@ class DoubleClick:
target: Target | None

@classmethod
def on_the(cls: type[SelfDoubleClick], target: Target) -> SelfDoubleClick:
def on_the(cls, target: Target) -> Self:
"""
Target the element to double-click on.
Expand All @@ -44,20 +43,16 @@ def on_the(cls: type[SelfDoubleClick], target: Target) -> SelfDoubleClick:
return cls(target=target)

@classmethod
def on(cls: type[SelfDoubleClick], target: Target) -> SelfDoubleClick:
def on(cls, target: Target) -> Self:
"""Alias for :meth:`~screenpy_selenium.actions.DoubleClick.on_the`."""
return cls.on_the(target=target)

@classmethod
def on_the_first_of_the(
cls: type[SelfDoubleClick], target: Target
) -> SelfDoubleClick:
def on_the_first_of_the(cls, target: Target) -> Self:
"""Alias for :meth:`~screenpy_selenium.actions.DoubleClick.on_the`."""
return cls.on_the(target=target)

def _add_action_to_chain(
self: SelfDoubleClick, the_actor: Actor, the_chain: ActionChains
) -> None:
def _add_action_to_chain(self, the_actor: Actor, the_chain: ActionChains) -> None:
"""Private method to add this Action to the chain."""
if self.target is not None:
the_element = self.target.found_by(the_actor)
Expand All @@ -66,25 +61,23 @@ def _add_action_to_chain(

the_chain.double_click(on_element=the_element)

def describe(self: SelfDoubleClick) -> str:
def describe(self) -> str:
"""Describe the Action in present tense."""
return f"Double-click{self.description}."

@beat("{} double-clicks{description}.")
def perform_as(self: SelfDoubleClick, the_actor: Actor) -> None:
def perform_as(self, the_actor: Actor) -> None:
"""Direct the Actor to double-click on the element."""
browser = the_actor.ability_to(BrowseTheWeb).browser
the_chain = ActionChains(browser) # type: ignore[arg-type]
self._add_action_to_chain(the_actor, the_chain)
the_chain.perform()

@beat("Double-click{description}!")
def add_to_chain(
self: SelfDoubleClick, the_actor: Actor, the_chain: ActionChains
) -> None:
def add_to_chain(self, the_actor: Actor, the_chain: ActionChains) -> None:
"""Add the DoubleClick Action to a Chain of Actions."""
self._add_action_to_chain(the_actor, the_chain)

def __init__(self: SelfDoubleClick, target: Target | None = None) -> None:
def __init__(self, target: Target | None = None) -> None:
self.target = target
self.description = f" on the {target}" if target is not None else ""
Loading

0 comments on commit f21539a

Please sign in to comment.