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