Package tlib :: Package base :: Module WebDriverTester
[hide private]
[frames] | no frames]

Source Code for Module tlib.base.WebDriverTester

  1  import os 
  2  import inspect 
  3  # noinspection PyPackageRequirements 
  4  import pytest 
  5  import collections 
  6  from time import sleep 
  7  # noinspection PyPackageRequirements 
  8  from _pytest.python import FixtureRequest 
  9  # noinspection PyPackageRequirements 
 10  import jinja2 
 11  from tlib.base import TestHelper 
 12  from tlib.base import FileHelper 
 13  from tlib.base.PytestTester import PytestTester 
 14  from selenium.webdriver.common.by import By 
 15  from selenium.webdriver.support.ui import WebDriverWait 
 16  from selenium.webdriver.support import expected_conditions 
 17  from selenium.common.exceptions import TimeoutException, NoAlertPresentException 
 18  from selenium.webdriver.remote.webelement import WebElement 
 19  from TestHelper import Singleton 
20 21 # noinspection PyMethodParameters 22 # noinspection PyUnresolvedReferences 23 -class WebDriverTester(PytestTester):
24 25 __metaclass__ = Singleton 26 27 _driver = None 28 29 _folder_dest = None # destination of report for the current class 30 _test_case_id = None 31 _test_case_name = None 32 _test_params = None 33 34 _jinja_env = jinja2.Environment(loader=jinja2.FileSystemLoader(TestHelper.tlib_template_folder()), 35 trim_blocks=True) 36 37 _screenshot_report = None # List of tests for which there are screenshots 38 39 # Template for generating screenshot report 40 _screenshot_report_template = _jinja_env.get_template("screenshot_report.html") 41 42 _screenshots = {} # Screenshots generated during a test case 43 44 # Template for generating screenshots for a test case 45 _tc_screenshot_template = _jinja_env.get_template("testcase_screenshots.html") 46 47 _request = None 48
49 - def screenshot_folder(self):
50 """ 51 Returns location of screenshot folder 52 53 @return: str 54 """ 55 screenshot_folder = self._find_screenshot_folder() 56 if screenshot_folder is None: 57 #Couldn't find screenshot folder 58 raise NotImplementedError("Could not find screenshot folder. " 59 "Class should implement method screenshot_folder") 60 else: 61 return screenshot_folder
62
63 - def _find_screenshot_folder(self):
64 """ 65 Will try to find a folder named "screenshot" starting from the file being executed and up to 66 three levels up 67 68 @return: str 69 """ 70 #Get current folder 71 curr_folder = os.path.dirname(self._request.fspath.strpath) 72 73 #Go up to three levels to find screenshot folder 74 for i in range(1, 4): 75 curr_folder = os.path.abspath(os.path.join(curr_folder, os.pardir)) 76 77 #Check if there is a folder 'screenshots' 78 screenshot_folder = os.path.join(curr_folder, 'screenshots') 79 if os.path.exists(screenshot_folder): 80 return screenshot_folder
81 82
83 - def _get_unused_report_name(self, tc_id, tc_params):
84 """ 85 Gets a string based on test case id and name, taking into account if test case has already been run or not 86 @param tc_id: Test case id 87 @type tc_id: str 88 @type tc_name: str 89 """ 90 i = 0 91 while True: 92 if i == 0: 93 filename = "%(tc_id)s_%(tc_params)s" % {"tc_id": tc_id, "tc_params": tc_params} 94 else: 95 filename = "%(tc_id)s_%(tc_params)s [retry %(cnt)d]" % {"tc_id": tc_id, "tc_params": tc_params, "cnt": i} 96 97 if not filename in self._screenshot_report: 98 return filename 99 100 i += 1
101 102 103 @pytest.fixture(scope='class', autouse=True)
104 - def initialize_webdriver_class(self, request):
105 """ 106 @type request: FixtureRequest 107 """ 108 109 #Generates initial HTML page with all test case results for the class 110 self._screenshot_report = collections.OrderedDict() 111 self._folder_dest = os.path.basename(os.path.dirname(os.path.abspath(inspect.getfile(request.cls)))) 112 113 def generate_report(): 114 if len(self._screenshot_report) > 0: 115 #Generate HTML based on template 116 html = self._screenshot_report_template.render(test_class=request.cls.__name__, 117 files=self._screenshot_report) 118 119 htm_file = "%s.htm" % self._folder_dest 120 full_filename = os.path.join(self.screenshot_folder(), htm_file) 121 122 f = open(full_filename, "w") 123 try: 124 f.write(html) 125 finally: 126 f.close()
127 128 request.addfinalizer(generate_report)
129 130 131 @pytest.fixture(scope='function', autouse=True)
132 - def setup_webdriver_test(self, request):
133 """ 134 Goes to homepage before each test, unless marker skipsetup is given 135 136 @type request: FixtureRequest 137 """ 138 #Store an instance of the request object. Required for the generation of screenshots 139 self._request = request 140 141 #Initialization required for screenshot generation 142 # noinspection PyProtectedMember 143 self._test_params = request.keywords.node._genid 144 self._test_case_name = request.keywords.node.name 145 self._screenshots = {} 146 147 #Get marker test case name or use a default if there is none 148 marker = request.node.get_marker("testcasename") 149 if marker is None: 150 self.test_logger.warn("Test case doesn't have marker testcasename") 151 self._test_case_id = "UNKNOWN_TEST_CASE_ID" 152 else: 153 self._test_case_id = marker.args[0] 154 155 def generate_report(): 156 """ 157 Generates HTML page with all the screenshots for a test case 158 Supports generating different files when test cases are run multiple times with the same arguments 159 """ 160 161 if len(self._screenshots) > 0: 162 name = self._get_unused_report_name(self._test_case_id, self._test_params) 163 164 #Generate HTML based on template 165 html = self._tc_screenshot_template.render(test_case_id=self._test_case_id, 166 test_case_name=self._test_case_name, 167 screenshots=self._screenshots) 168 169 #Filename includes a counter in case test case is being run more than once 170 htm_file = FileHelper.get_filename_from_string(name) + '.htm' 171 relative_filename = os.path.join(self._folder_dest, htm_file) 172 full_filename = os.path.join(self.screenshot_folder(), relative_filename) 173 174 self._screenshot_report[name] = {"tc_id": self._test_case_id, 175 "tc_name": self._test_case_name, 176 "filename": relative_filename} 177 178 f = open(full_filename, "w") 179 try: 180 f.write(html) 181 finally: 182 f.close()
183 184 request.addfinalizer(generate_report) 185 186
187 - def save_screenshot(self, description):
188 """ 189 Saves screen shot for the current page 190 """ 191 #Waits until page is loaded 192 self.tlib_logger.debug("Saving screenshot") 193 self.wait_for_page_loaded() 194 195 #Folder containing images for test case 196 name = self._get_unused_report_name(self._test_case_id, self._test_params) 197 name = FileHelper.get_filename_from_string(name) 198 199 test_folder = os.path.join(self.screenshot_folder(), self._folder_dest, name) 200 201 try: 202 os.makedirs(test_folder) 203 except WindowsError: 204 pass 205 206 #Get number of existing images 207 i = len(os.listdir(test_folder)) + 1 208 209 #Get filename 210 img_filename = "%02d.png" % i 211 full_path = os.path.join(test_folder, img_filename) 212 213 self._screenshots[i] = {"filename": "%s/%s" % (name, img_filename), 214 u"description": description} 215 216 self._driver.get_screenshot_as_file(full_path)
217 218
219 - def wait_for_page_loaded(self, timeout=10):
220 raise NotImplementedError("This method should be implemented by derived classes")
221 222
223 - def wait_for_alert(self, timeout=10):
224 """ 225 Waist until an alert is visible 226 @type timeout: Integer 227 @param timeout: Number of seconds before timing out 228 @rtype: bool 229 """ 230 def is_alert_visible(): 231 try: 232 #Try to get alert text to trigger exception if alert is not visible 233 alert = self._driver.switch_to_alert().text 234 return True 235 except NoAlertPresentException as e: 236 return False
237 238 condition = lambda *args: is_alert_visible() 239 try: 240 WebDriverWait(self._driver, timeout).until(condition) 241 return self._driver.switch_to_alert() 242 except TimeoutException: 243 self.test_logger.error('Timeout while waiting for alert to appear') 244 pytest.fail('Timeout while waiting for alert to appear') 245 246
247 - def wait_for_element_to_be_visible(self, locator_strategy, locator_string, error_msg=None, timeout=10):
248 """ 249 Wait until an element becomes visible 250 @param locator_strategy: Location strategy to use 251 @type locator_strategy: By 252 @param locator_string: String used to locate element 253 @type locator_string: str 254 @param error_msg: Error string to show if element is not found 255 @type error_msg: str 256 @param timeout: Maximum time in seconds to wait for the element to be visible 257 @type timeout: int 258 @rtype: WebElement 259 """ 260 try: 261 element = WebDriverWait(self._driver, timeout).until(expected_conditions.visibility_of_element_located((locator_strategy, locator_string))) 262 return element 263 except TimeoutException: 264 if error_msg is None: 265 error_msg = "Timeout while waiting for element '%s' to be visible" % locator_string 266 self.save_screenshot("[ERROR] %s" % error_msg) 267 pytest.fail(error_msg)
268 269
270 - def wait_for_element_to_be_clickable(self, locator_strategy, locator_string, error_msg=None, timeout=10):
271 """ 272 Wait until an element cna be clicked 273 @param locator_strategy: Location strategy to use 274 @type locator_strategy: By 275 @param locator_string: String used to locate element 276 @type locator_string: str 277 @param error_msg: Error string to show if element is not found 278 @type error_msg: str 279 @param timeout: Maximum time in seconds to wait for the element to be clickable 280 @type timeout: int 281 @rtype: WebElement 282 """ 283 try: 284 element = WebDriverWait(self._driver, timeout).until(expected_conditions.element_to_be_clickable((locator_strategy, locator_string))) 285 return element 286 except TimeoutException: 287 if error_msg is None: 288 error_msg = "Timeout while waiting for element '%s' to be clickable" % locator_string 289 self.save_screenshot("[ERROR] %s" % error_msg) 290 pytest.fail(error_msg)
291 292
293 - def wait_for_element_to_be_present(self, locator_strategy, locator_string, error_msg=None, timeout=10):
294 """ 295 Wait until an element is present 296 @param locator_strategy: Location strategy to use 297 @type locator_strategy: By 298 @param locator_string: String used to locate element 299 @type locator_string: str 300 @param error_msg: Error string to show if element is not found 301 @type error_msg: str 302 @param timeout: Maximum time in seconds to wait for the element to be present 303 @type timeout: int 304 @rtype: WebElement 305 """ 306 try: 307 element = WebDriverWait(self._driver, timeout).until(expected_conditions.presence_of_element_located((locator_strategy, locator_string))) 308 return element 309 except TimeoutException: 310 if error_msg is None: 311 error_msg = "Timeout while waiting for element '%s' to be present" % locator_string 312 self.save_screenshot("[ERROR] %s" % error_msg) 313 pytest.fail(error_msg)
314 315
316 - def wait_for_element_to_be_selected(self, locator_strategy, locator_string, error_msg=None, timeout=10):
317 """ 318 Wait until an element is selected 319 @param locator_strategy: Location strategy to use 320 @type locator_strategy: By 321 @param locator_string: String used to locate element 322 @type locator_string: str 323 @param error_msg: Error string to show if element is not found 324 @type error_msg: str 325 @param timeout: Maximum time in seconds to wait for the element to be selected 326 @type timeout: int 327 @rtype: WebElement 328 """ 329 try: 330 element = WebDriverWait(self._driver, timeout).until(expected_conditions.element_located_to_be_selected((locator_strategy, locator_string))) 331 return element 332 except TimeoutException: 333 if error_msg is None: 334 error_msg = "Timeout while waiting for element '%s' to be selected" % locator_string 335 self.save_screenshot("[ERROR] %s" % error_msg) 336 pytest.fail(error_msg)
337 338
339 - def wait_for_element_to_be_invisible(self, locator_strategy, locator_string, error_msg=None, timeout=10):
340 """ 341 Wait until an element becomes invisible 342 @param locator_strategy: Location strategy to use 343 @type locator_strategy: By 344 @param locator_string: String used to locate element 345 @type locator_string: str 346 @param error_msg: Error string to show if element is not found 347 @type error_msg: str 348 @param timeout: Maximum time in seconds to wait for the element to be hidden 349 @type timeout: int 350 @rtype: WebElement 351 """ 352 try: 353 element = WebDriverWait(self._driver, timeout).until(expected_conditions.invisibility_of_element_located((locator_strategy, locator_string))) 354 return element 355 except TimeoutException: 356 if error_msg is None: 357 error_msg = "Timeout while waiting for element '%s' to be invisible" % locator_string 358 self.save_screenshot("[ERROR] %s" % error_msg) 359 pytest.fail(error_msg)
360 361
362 - def wait_for_element_to_be_static(self, locator_strategy, locator_string, error_msg=None, timeout=10):
363 """ 364 Wait until an element that moves on the screen stops moving 365 @param locator_strategy: Location strategy to use 366 @type locator_strategy: By 367 @param locator_string: String used to locate element 368 @type locator_string: str 369 @param error_msg: Error string to show if element is not found 370 @type error_msg: str 371 @param timeout: Maximum time in seconds to wait for the element to be visible 372 @type timeout: int 373 @rtype: WebElement 374 """ 375 try: 376 element = WebDriverWait(self._driver, timeout).until(expected_conditions.visibility_of_element_located((locator_strategy, locator_string))) 377 378 #wait until element is not moving or click will fail 379 old_location = {'x':0, 'y':0} 380 while old_location != element.location: 381 self.tlib_logger.debug("Pop-up is still moving. Previous position: %s, current position: %s" % (old_location, element.location)) 382 old_location = element.location 383 sleep(0.1) 384 element = self._driver.find_element(locator_strategy, locator_string) 385 386 return element 387 except TimeoutException: 388 if error_msg is None: 389 error_msg = "Timeout while waiting for element '%s' to be visible" % locator_string 390 self.save_screenshot("[ERROR] %s" % error_msg) 391 pytest.fail(error_msg)
392 393
394 - def wait_for_text_to_be_present_in_element(self, locator_strategy, locator_string, text, error_msg=None, timeout=10):
395 """ 396 Wait for an element that contains specified text 397 @param locator_strategy: Location strategy to use 398 @type locator_strategy: By 399 @param locator_string: String used to locate element 400 @type locator_string: str 401 @param error_msg: Error string to show if element is not found 402 @type error_msg: str 403 @param timeout: Maximum time in seconds to wait 404 @type timeout: int 405 """ 406 try: 407 WebDriverWait(self._driver, timeout).until(expected_conditions.text_to_be_present_in_element((locator_strategy, locator_string), text)) 408 except TimeoutException: 409 if error_msg is None: 410 error_msg = "Timeout while waiting for text %(text)s to be present in element '%(element)s'" % {"text": text, "element":locator_string} 411 self.save_screenshot("[ERROR] %s" % error_msg) 412 pytest.fail(error_msg)
413 414
415 - def wait_for_text_to_be_present_in_element_value(self, locator_strategy, locator_string, text, error_msg=None, timeout=10):
416 """ 417 Wait for an element's value to contain some test 418 @param locator_strategy: Location strategy to use 419 @type locator_strategy: By 420 @param locator_string: String used to locate element 421 @type locator_string: str 422 @param error_msg: Error string to show if element is not found 423 @type error_msg: str 424 @param timeout: Maximum time in seconds to wait 425 @type timeout: int 426 """ 427 try: 428 WebDriverWait(self._driver, timeout).until(expected_conditions.text_to_be_present_in_element_value((locator_strategy, locator_string), text)) 429 except TimeoutException: 430 if error_msg is None: 431 error_msg = "Timeout while waiting for text %(text)s to be present in the value of element '%(element)s'" % {"text": text, "element":locator_string} 432 self.save_screenshot("[ERROR] %s" % error_msg) 433 pytest.fail(error_msg)
434