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