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