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

Source Code for Module tlib.base.SeleniumTester

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