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

Source Code for Module tlib.base.SeleniumTester

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