1- import time
21from enum import Enum
32from typing import Tuple , Optional , Literal , List
3+ import time
44from selenium .webdriver .remote .webelement import WebElement
5- from selenium .webdriver .support import expected_conditions as ec
5+ from selenium .webdriver .support import expected_conditions as EC
66from selenium .webdriver .support .wait import WebDriverWait
7- from selenium .common .exceptions import (
8- TimeoutException ,
9- ElementNotVisibleException ,
10- NoSuchElementException ,
11- )
7+ from selenium .common .exceptions import TimeoutException , NoSuchElementException
128
139Locator = Tuple [str , str ]
1410
1511
1612class WaitType (Enum ):
17- """
18- Enumeration for different wait durations used in WebDriverWait.
19- """
20-
2113 DEFAULT = 30
2214 SHORT = 5
2315 LONG = 60
2416 FLUENT = 10
2517
2618
2719class ElementInteractor :
28- """
29- A utility class for interacting with screen elements, waits strategy.
30- """
31-
3220 def __init__ (self , driver ):
33- """
34- Initializes the ElementInteractor with a WebDriver instance and predefined waiters.
35-
36- :param driver: The Selenium WebDriver instance to interact with.
37- :type driver: WebDriver
38- """
3921 self .driver = driver
4022 self .waiters = {
41- WaitType .DEFAULT : WebDriverWait (driver , WaitType .DEFAULT .value ),
42- WaitType .SHORT : WebDriverWait (driver , WaitType .SHORT .value ),
43- WaitType .LONG : WebDriverWait (driver , WaitType .LONG .value ),
44- WaitType .FLUENT : WebDriverWait (
45- driver ,
46- WaitType .FLUENT .value ,
47- poll_frequency = 1 ,
48- ignored_exceptions = [ElementNotVisibleException ],
49- ),
23+ wait_type : WebDriverWait (driver , wait_type .value )
24+ for wait_type in WaitType
25+ if wait_type != WaitType .FLUENT
5026 }
27+ self .waiters [WaitType .FLUENT ] = WebDriverWait (
28+ driver , WaitType .FLUENT .value , poll_frequency = 1
29+ )
5130
5231 def _get_waiter (self , wait_type : Optional [WaitType ] = None ) -> WebDriverWait :
53- """
54- Returns the appropriate WebDriverWait instance based on the specified wait type.
55-
56- :param wait_type: The type of wait (default is `WaitType.DEFAULT`).
57- :type wait_type: Optional[WaitType]
58-
59- :return: The WebDriverWait instance for the specified wait type.
60- :rtype: WebDriverWait
61- """
6232 return self .waiters .get (wait_type , self .waiters [WaitType .DEFAULT ])
6333
6434 def wait_for (
@@ -67,50 +37,47 @@ def wait_for(
6737 condition : Literal ["clickable" , "visible" , "present" ] = "visible" ,
6838 waiter : Optional [WebDriverWait ] = None ,
6939 ) -> WebElement :
70- """
71- Waits for an element to meet the specified condition.
72-
73- :param locator: A tuple containing the strategy and value of the element locator.
74- :param condition: The condition to wait for ("clickable", "visible", or "present").
75- :param waiter: A custom WebDriverWait instance. Defaults to `None`, which uses the default waiter.
76-
77- :return: The located web element once the condition is satisfied.
78- """
7940 waiter = waiter or self ._get_waiter ()
8041 conditions = {
81- "clickable" : ec .element_to_be_clickable (locator ),
82- "visible" : ec .visibility_of_element_located (locator ),
83- "present" : ec .presence_of_element_located (locator ),
42+ "clickable" : EC .element_to_be_clickable (locator ),
43+ "visible" : EC .visibility_of_element_located (locator ),
44+ "present" : EC .presence_of_element_located (locator ),
8445 }
85-
8646 if condition not in conditions :
8747 raise ValueError (f"Unknown condition: { condition } " )
88-
8948 try :
9049 return waiter .until (conditions [condition ])
9150 except TimeoutException as e :
9251 raise TimeoutException (
93- f"Condition '{ condition } ' failed for element { locator } "
94- f"after { waiter ._timeout } seconds"
52+ f"Condition '{ condition } ' failed for element { locator } after { waiter ._timeout } seconds"
9553 ) from e
9654
55+ def element (
56+ self ,
57+ locator : Locator ,
58+ n : int = 3 ,
59+ condition : Literal ["clickable" , "visible" , "present" ] = "visible" ,
60+ wait_type : Optional [WaitType ] = WaitType .DEFAULT ,
61+ ):
62+ for attempt in range (1 , n + 1 ):
63+ try :
64+ self .wait_for (
65+ locator , condition = condition , waiter = self ._get_waiter (wait_type )
66+ )
67+ return self .driver .find_element (* locator )
68+ except NoSuchElementException :
69+ if attempt == n :
70+ raise NoSuchElementException (
71+ f"Could not locate element with value: { locator } "
72+ )
73+
9774 def elements (
9875 self ,
9976 locator : Locator ,
10077 n : int = 3 ,
10178 condition : Literal ["clickable" , "visible" , "present" ] = "visible" ,
10279 wait_type : Optional [WaitType ] = WaitType .DEFAULT ,
10380 ) -> List [WebElement ]:
104- """
105- Attempts to locate a list of elements by polling a maximum of 'n' times.
106-
107- :param locator: A tuple containing the strategy and value of the element locator.
108- :param n: The maximum number of attempts to find the elements. Default is 3.
109- :param condition: The condition to wait for ("clickable", "visible", or "present").
110- :param wait_type: The wait type to use for polling. Defaults to `WaitType.DEFAULT`.
111-
112- :return: A list of located web elements that match the condition.
113- """
11481 for attempt in range (1 , n + 1 ):
11582 try :
11683 self .wait_for (
@@ -122,35 +89,6 @@ def elements(
12289 raise NoSuchElementException (
12390 f"Could not locate element list with value: { locator } "
12491 )
125- except Exception :
126- if attempt == n :
127- raise
128-
129- def _assert_element_displayed (self , element : WebElement , expected : bool ) -> None :
130- """
131- Asserts that the element's displayed status matches the expected value.
132-
133- :param element: The web element to check.
134- :param expected: The expected visibility status of the element (True or False).
135-
136- :raises AssertionError: If the element's visibility does not match the expected value.
137- """
138- assert element .is_displayed () == expected
139-
140- def _check_elements_displayed (
141- self , elements : List [WebElement ], expected : bool , index : Optional [int ] = None
142- ) -> bool :
143- """
144- Checks if the elements are displayed and if applicable, checks a specific element by index.
145-
146- :param elements: The list of web elements to check.
147- :param expected: The expected visibility status of the elements (True or False).
148- :param index: The index of the specific element to check. If `None`, all elements are checked.
149- :return: True if the element(s) are displayed with the expected status, otherwise False.
150- """
151- if index is None :
152- return all (e .is_displayed () == expected for e in elements )
153- return elements [index ].is_displayed () == expected
15492
15593 def is_displayed (
15694 self ,
@@ -160,15 +98,6 @@ def is_displayed(
16098 condition : Literal ["clickable" , "visible" , "present" ] = "visible" ,
16199 wait_type : Optional [WaitType ] = None ,
162100 ) -> None :
163- """Checks for an element to be displayed or not, and asserts the visibility.
164-
165- :param locator: A tuple containing the strategy and value of the element locator.
166- :param expected: The expected visibility status of the element (True or False).
167- :param n: The maximum number of attempts to check visibility. Defaults to 3.
168- :param condition: The condition to wait for ("clickable", "visible", or "present").
169- :param wait_type: The wait type to use for polling. Defaults to `WaitType.DEFAULT`.
170-
171- :raises AssertionError: If the element's visibility does not match the expected value"""
172101 wait_type = wait_type or WaitType .DEFAULT
173102 for _ in range (n ):
174103 try :
@@ -179,7 +108,12 @@ def is_displayed(
179108 return
180109 except Exception :
181110 time .sleep (0.5 )
182- assert False == expected
111+ if expected : # Assert if the element is expected to be displayed but isn't
112+ raise AssertionError (f"Element { locator } was not displayed as expected." )
113+ else : # Assert if the element should not be displayed but is
114+ raise AssertionError (
115+ f"Element { locator } was displayed when it shouldn't be."
116+ )
183117
184118 def is_exist (
185119 self ,
@@ -188,30 +122,17 @@ def is_exist(
188122 n : int = 3 ,
189123 condition : Literal ["clickable" , "visible" , "present" ] = "visible" ,
190124 wait_type : Optional [WaitType ] = WaitType .DEFAULT ,
191- ** kwargs ,
192125 ) -> bool :
193- """
194- Checks for an element's existence and checks if it meets the expected visibility status.
195-
196- :param locator: A tuple containing the strategy and value of the element locator.
197- :param expected: The expected existence status of the element (True or False).
198- :param n: The maximum number of attempts to check existence. Defaults to 3.
199- :param condition: The condition to wait for ("clickable", "visible", or "present").
200- :param wait_type: The wait type to use for polling. Defaults to `WaitType.DEFAULT`.
201- :param **kwargs: Additional keyword arguments, such as `index` for checking a specific element in a list.
202-
203- :return: `True` if the element(s) exist and match the expected visibility status, otherwise `False`.
204- :rtype: bool
205- """
206126 for _ in range (n ):
207127 try :
208- elements = self .wait_for (
209- locator , condition = condition , waiter = self . _get_waiter ( wait_type )
128+ element = self .element (
129+ locator , n = 1 , condition = condition , wait_type = wait_type
210130 )
211- if isinstance ( elements , list ) and self . _check_elements_displayed (
212- elements , expected , kwargs . get ( "index" )
213- ) :
131+ return element . is_displayed () == expected
132+ except NoSuchElementException :
133+ if not expected :
214134 return True
215135 except Exception :
216- if _ == n - 1 :
217- return False
136+ pass
137+ time .sleep (0.5 )
138+ return not expected
0 commit comments