Skip to content

Commit ddf6862

Browse files
Merge pull request #9 from dmberezovskyii/PPA-0005
added ability to scroll down and up by coordinates
2 parents caf3181 + 7ce7f9a commit ddf6862

File tree

5 files changed

+120
-16
lines changed

5 files changed

+120
-16
lines changed

src/locators/locators.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
11
from appium.webdriver.common.appiumby import AppiumBy
22

33

4-
class Common:
5-
text_link = (AppiumBy.ACCESSIBILITY_ID, 'Text')
6-
content_link = (AppiumBy.ACCESSIBILITY_ID, 'Content')
7-
menu_elements = (AppiumBy.XPATH, '//android.widget.TextView')
4+
class Locators:
5+
class main_menu:
6+
TEXT_LINK = (AppiumBy.ACCESSIBILITY_ID, 'Text')
7+
CONTENT_LINK = (AppiumBy.ACCESSIBILITY_ID, 'Content')
8+
VIEWS_LINK = (AppiumBy.ACCESSIBILITY_ID, 'Views')
9+
MENU_ELEMENTS = (AppiumBy.XPATH, '//android.widget.TextView')
10+
11+
class views_menu:
12+
TEXT_FIELDS = (AppiumBy.ACCESSIBILITY_ID, 'TextFields')
13+
14+
class views_fields:
15+
HINT_INPUT = (AppiumBy.ACCESSIBILITY_ID, 'hint')

src/screens/base_screen.py

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import time
2-
from typing import Tuple, Literal
2+
from typing import Tuple, Literal, Optional
33

4+
from selenium.webdriver.common.actions import interaction
45
from selenium.webdriver.common.actions.action_builder import ActionBuilder
56
from selenium.webdriver.common.actions.pointer_actions import PointerActions
7+
from selenium.webdriver.common.actions.pointer_input import PointerInput
68

79
from screens.element_interactor import ElementInteractor
810
from appium.webdriver.extensions.action_helpers import ActionHelpers, ActionChains
@@ -23,15 +25,20 @@ def click(
2325
element = self.element(locator, condition=condition)
2426
element.click()
2527

26-
def tap(self, locator, **kwargs):
27-
"""Taps on an element using ActionHelpers."""
28+
def tap(self, locator: Locator, duration: float = 500, **kwargs):
29+
"""Taps on an element using ActionHelpers.
30+
Taps on an particular place with up to five fingers, holding for a
31+
certain duration
32+
33+
:param locator: locator of an element
34+
:param duration: length of time to tap, in ms"""
2835
try:
2936
element = self.element(locator, condition="clickable", **kwargs)
3037
location = element.location
3138
size = element.size
3239
x = location["x"] + size["width"] // 2
3340
y = location["y"] + size["height"] // 2
34-
self.driver.tap([(x, y)])
41+
self.driver.tap([(x, y)], duration=duration)
3542
except Exception as e:
3643
print(f"Error during tap action: {e}")
3744

@@ -43,7 +50,7 @@ def swipe(
4350
relative_end_y: float,
4451
duration_ms: int = 200,
4552
) -> None:
46-
size = self.driver.get_window_size()
53+
size = self.get_screen_size()
4754
width = size["width"]
4855
height = size["height"]
4956
start_x = int(width * relative_start_x)
@@ -58,6 +65,41 @@ def swipe(
5865
duration_ms=duration_ms,
5966
)
6067

68+
def scroll(
69+
self,
70+
directions: Literal["down", "up"] = "down",
71+
start_ratio: float = 0.7,
72+
end_ratio: float = 0.3,
73+
):
74+
"""
75+
Scrolls down the screen with customizable scroll size.
76+
77+
:param directions: up or down:
78+
:param start_ratio: Percentage (0-1) from where the scroll starts
79+
:type end_ratio: Percentage (0-1) where the scroll ends.
80+
USAGE:
81+
DOWN example
82+
start_y = int(height * 0.7)
83+
end_y = int(height * 0.3)
84+
UP example
85+
start_y = int(height * 0.3)
86+
end_y = int(height * 0.7)
87+
"""
88+
size = self.get_screen_size()
89+
width = size["width"]
90+
height = size["height"]
91+
start_x = width // 2
92+
if directions == "down":
93+
start_y = int(height * start_ratio)
94+
end_y = int(height * end_ratio)
95+
elif directions == "up":
96+
start_y = int(height * end_ratio)
97+
end_y = int(height * start_ratio)
98+
else:
99+
raise ValueError("Direction must be 'down' or 'up'")
100+
101+
self.scroll_by_coordinates(start_x, start_y, start_x, end_y)
102+
61103
def type(self, locator: Locator, text: str):
62104
element = self.element(locator)
63105
element.send_keys(text)
@@ -71,8 +113,7 @@ def double_tap(
71113
"""Double taps on an element."""
72114
try:
73115
element = self.element(locator, condition=condition, **kwargs)
74-
action = ActionHelpers()
75-
action.double_tap(element).perform()
116+
# TODO
76117
except Exception as e:
77118
print(f"Error during double tap action: {e}")
78119

src/screens/element_interactor.py

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
from enum import Enum
2-
from typing import Tuple, Optional, Literal, List
2+
from typing import Tuple, Optional, Literal, List, cast
33
import time
4+
5+
from selenium.webdriver import ActionChains
6+
from selenium.webdriver.common.actions import interaction
7+
from selenium.webdriver.common.actions.action_builder import ActionBuilder
8+
from selenium.webdriver.common.actions.pointer_input import PointerInput
49
from selenium.webdriver.remote.webelement import WebElement
510
from selenium.webdriver.support import expected_conditions as EC
611
from selenium.webdriver.support.wait import WebDriverWait
@@ -136,3 +141,40 @@ def is_exist(
136141
pass
137142
time.sleep(0.5)
138143
return not expected
144+
145+
def scroll_by_coordinates(
146+
self,
147+
start_x: int,
148+
start_y: int,
149+
end_x: int,
150+
end_y: int,
151+
duration: Optional[int] = None,
152+
):
153+
"""Scrolls from one set of coordinates to another.
154+
155+
Args:
156+
start_x: X coordinate to start scrolling from.
157+
start_y: Y coordinate to start scrolling from.
158+
end_x: X coordinate to scroll to.
159+
end_y: Y coordinate to scroll to.
160+
duration: Defines speed of scroll action. Default is 600 ms.
161+
162+
Returns:
163+
Self instance.
164+
"""
165+
if duration is None:
166+
duration = 1000
167+
168+
touch_input = PointerInput(interaction.POINTER_TOUCH, "touch")
169+
actions = ActionChains(self.driver)
170+
171+
actions.w3c_actions = ActionBuilder(self.driver, mouse = touch_input)
172+
actions.w3c_actions.pointer_action.move_to_location(start_x, start_y)
173+
actions.w3c_actions.pointer_action.pointer_down()
174+
actions.w3c_actions = ActionBuilder(self.driver, mouse=touch_input, duration=duration)
175+
176+
actions.w3c_actions.pointer_action.move_to_location(end_x, end_y)
177+
actions.w3c_actions.pointer_action.release()
178+
179+
actions.perform()
180+
Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,23 @@
1-
from locators.locators import Common
1+
from typing import Literal
2+
3+
from locators.locators import Locators
24
from screens.base_screen import Screen
35

46

57
class MainScreen(Screen):
68

79
def __init__(self, driver):
810
super().__init__(driver)
11+
self.locators = Locators()
912

1013
def click_on_text_link(self):
11-
self.click(locator = Common.text_link)
14+
self.click(locator = self.locators.main_menu.TEXT_LINK)
1215

1316
def tap_on_text_link(self):
14-
self.tap(locator = Common.text_link)
17+
self.tap(locator = self.locators.main_menu.TEXT_LINK)
18+
19+
def scroll_view_by_coordinates(self, direction: Literal['down', 'up'] = 'down'):
20+
self.tap(locator = self.locators.main_menu.VIEWS_LINK)
21+
self.scroll(directions = direction)
22+
23+

tests/test_p1/test_actions.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,8 @@ def test_click(self, setup):
1212
self.main_screen.click_on_text_link()
1313

1414
def test_tap(self, setup):
15-
self.main_screen.tap_on_text_link()
15+
self.main_screen.tap_on_text_link()
16+
17+
def test_scroll_by_coordinates(self, setup):
18+
self.main_screen.scroll_view_by_coordinates(direction = "down")
19+
self.main_screen.scroll('up')

0 commit comments

Comments
 (0)