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