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