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  from _pytest.mark import MarkInfo 
 12  # noinspection PyPackageRequirements 
 13  import jinja2 
 14  from tlib.base import TestHelper 
 15  from tlib.base import FileHelper 
 16  from tlib.base.PytestTester import PytestTester 
 17  from selenium.webdriver.common.by import By 
 18  from selenium.webdriver.support.ui import WebDriverWait 
 19  from selenium.webdriver.support import expected_conditions 
 20  from selenium.common.exceptions import TimeoutException, NoAlertPresentException 
 21  from selenium.webdriver.remote.webelement import WebElement 
 22  import logging 
23 24 25 # noinspection PyMethodParameters 26 # noinspection PyUnresolvedReferences 27 -class SeleniumTester(PytestTester):
28 29 _homepage = None 30 _test_logger = None # Reference to test logger 31 _tlib_logger = None # Reference to tlib logger 32 _browser = None 33 34 _folder_dest = None # destination of report for the current class 35 _test_case_id = None 36 _test_case_name = None 37 _test_params = None 38 39 _jinja_env = jinja2.Environment(loader = jinja2.FileSystemLoader(TestHelper.tlib_template_folder()), trim_blocks = True) 40 41 _screenshot_report = None # List of tests for which there are screenshots 42 _screenshot_report_template = _jinja_env.get_template("screenshot_report.html") # Template for generating screenshot report 43 44 _screenshots = {} # Screenshots generated during a test case 45 _tc_screenshot_template = _jinja_env.get_template("testcase_screenshots.html") # Template for generating screenshots for a test case 46 47
48 - def _get_browser(self):
49 """ 50 Returns Instance of the WebDriver browser 51 @rtype: webdriver.Remote 52 """ 53 return self.__class__._browser
54 55
56 - def _set_browser(self, browser):
57 """ 58 Instance of the WebDriver browser 59 @type browser: webdriver.Remote 60 """ 61 self.__class__._browser = browser
62 63 browser = property(_get_browser, _set_browser) 64
65 - def _get_homepage(self):
66 """ 67 Instance of the WebDriver homepage 68 """ 69 return self.__class__._homepage
70 71
72 - def _set_homepage(self, homepage):
73 """ 74 Instance of the WebDriver homepage 75 @type homepage: webdriver.Remote 76 """ 77 self.__class__._homepage = homepage
78 79 homepage = property(_get_homepage, _set_homepage) 80 81 82 @property
83 - def test_logger(self):
84 """ 85 Logger object to log test information 86 """ 87 return self.__class__._test_logger
88 89 90 @staticmethod
91 - def screenshot_folder():
92 """ 93 Returns project's screenshot folder 94 This method must be defined in subclasses 95 """ 96 raise NotImplementedError("Subclasses should implement this!")
97 98 99 @pytest.fixture(scope='class', autouse=True)
100 - def initialize_class(self, test_logger, tlib_logger, browser, base_url):
101 """ 102 @type test_logger: logging 103 @type browser: webdriver.Remote 104 Stores an instance of browser and test_logger 105 """ 106 self.__class__._test_logger = test_logger 107 self.__class__._tlib_logger = tlib_logger 108 self.browser = browser 109 # Only initialize homepage if it wasn't already initialized 110 if self.homepage is None: 111 self.homepage = base_url
112 113
114 - def get_unused_report_name(self, tc_id, tc_name):
115 """ 116 Gets a string based on test case id and name, taking into account if test case has already been run or not 117 @param tc_id: Test case id 118 @type tc_id: str 119 @param tc_name: Test case name 120 @type tc_name: str 121 """ 122 i = 0 123 while True: 124 if i == 0: 125 # filename = "%(tc_id)s - %(tc_name)s" % { "tc_id": tc_id, "tc_name": tc_name} 126 filename = "%(tc_id)s" % { "tc_id": tc_id} 127 else: 128 # filename = "%(tc_id)s - %(tc_name)s[retry %(cnt)d]" % { "tc_id": tc_id, "tc_name": tc_name, "cnt": i} 129 filename = "%(tc_id)s [retry %(cnt)d]" % { "tc_id": tc_id, "cnt": i} 130 131 if not filename in self.__class__._screenshot_report: 132 return filename 133 134 i += 1
135 136 137 @pytest.fixture(scope='class', autouse=True)
138 - def generate_class_report(self, request):
139 """ 140 Generates HTML page with all test case results for the class 141 @type request: FixtureRequest 142 """ 143 self.__class__._screenshot_report = collections.OrderedDict() 144 self.__class__._folder_dest = re.sub('\.py$', '', os.path.relpath(inspect.getfile(request.cls))) 145 146 def generate_report(): 147 if len(self.__class__._screenshot_report) > 0: 148 #Generate HTML based on template 149 html = self.__class__._screenshot_report_template.render(test_class=self.__class__.__name__, 150 files=self.__class__._screenshot_report) 151 152 htm_file = "%s.htm" % self.__class__._folder_dest 153 full_filename = os.path.join(self.screenshot_folder(), htm_file) 154 155 f = open(full_filename, "w") 156 try: 157 f.write(html) 158 finally: 159 f.close()
160 161 request.addfinalizer(generate_report)
162 163 164 @pytest.fixture(scope='function', autouse=True)
165 - def generate_test_report(self, request):
166 """ 167 Generates HTML page with all the screenshots for a test case 168 Supports generating different When test cases are run multiple times with the same arguments 169 @type request: FixtureRequest 170 """ 171 # noinspection PyProtectedMember 172 self.__class__._test_params = request.keywords.node._genid 173 self.__class__._test_case_name = request.keywords.node.name 174 self.__class__._screenshots = {} 175 176 #Get marker test case name or use a default if there is none 177 marker = request.node.get_marker("testcasename") 178 if marker is None: 179 self._test_logger.warn("Test case doesn't have marker testcasename") 180 self.__class__._test_case_id = "UNKNOWN_TEST_CASE_ID" 181 else: 182 self.__class__._test_case_id = marker.args[0] 183 184 185 def generate_report(): 186 187 if len(self.__class__._screenshots) > 0: 188 name = self.get_unused_report_name(self.__class__._test_case_id, self.__class__._test_case_name) 189 190 #Generate HTML based on template 191 html = self.__class__._tc_screenshot_template.render(test_case_id=self.__class__._test_case_id, 192 test_case_name=self.__class__._test_case_name, 193 screenshots=self.__class__._screenshots) 194 195 #Filename includes a counter in case test case is being run more than once 196 htm_file = FileHelper.get_filename_from_string(name) + '.htm' 197 relative_filename = os.path.join(self.__class__._folder_dest, htm_file) 198 full_filename = os.path.join(self.screenshot_folder(), relative_filename) 199 200 self.__class__._screenshot_report[name] = {"tc_id": self.__class__._test_case_id, 201 "tc_name": self.__class__._test_case_name, 202 "filename": relative_filename} 203 204 f = open(full_filename, "w") 205 try: 206 f.write(html) 207 finally: 208 f.close()
209 210 211 request.addfinalizer(generate_report) 212 213
214 - def navigate(self, page, save_screenshot=True):
215 """ 216 Navigate to "page" 217 """ 218 self.test_logger.info(u"Navigating to %s" % page) 219 self.browser.get(page) 220 if save_screenshot: 221 self.save_screenshot(u"Navigate to %s" % page)
222 223
224 - def go_home(self, save_screenshot=True):
225 """ 226 Navigate to home page 227 """ 228 self.navigate(self.homepage, save_screenshot=False) 229 if save_screenshot: 230 self.save_screenshot("Homepage")
231 232
233 - def save_screenshot(self, description):
234 """ 235 Saves screen shot for the current page 236 """ 237 238 def get_params(): 239 if self.__class__._test_params is None: 240 return "" 241 else: 242 return self.__class__._test_params
243 244 #Waits until page is loaded 245 self._tlib_logger.debug("Saving screenshot") 246 self.wait_for_page_loaded() 247 248 #Folder containing images for test case 249 name = self.get_unused_report_name(self.__class__._test_case_id, self.__class__._test_case_name) 250 name = FileHelper.get_filename_from_string(name) 251 252 test_folder = os.path.join(self.screenshot_folder(), self.__class__._folder_dest, name) 253 254 try: 255 os.makedirs(test_folder) 256 except WindowsError: 257 pass 258 259 #Get number of existing images 260 i = len(os.listdir(test_folder)) + 1 261 262 #Get filename 263 img_filename = "%02d.jpg" % i 264 full_path = os.path.join(test_folder, img_filename) 265 266 self.__class__._screenshots[i] = {"filename": "%s/%s" % (name, img_filename), 267 u"description": description} 268 269 self.browser.get_screenshot_as_file(full_path) 270 271
272 - def wait_for_page_loaded(self, timeout=10):
273 """ 274 Waist until document.readyState is equal to complete 275 @type timeout: Integer 276 @param timeout: Number of seconds before timing out 277 """ 278 if self.browser.execute_script("return document.readyState") == "complete": 279 return 280 else: 281 self._tlib_logger.debug("Waiting for '%s' to load" % self.browser.current_url) 282 283 condition = lambda *args: self.browser.execute_script("return document.readyState") == "complete" 284 try: 285 WebDriverWait(self.browser, timeout).until(condition) 286 except TimeoutException: 287 self.test_logger.error('Timeout while waiting for page to load') 288 pytest.fail('Timeout while waiting for page to load') 289 290 self._tlib_logger.debug("Page '%s' finished loading" % self.browser.current_url)
291
292 - def wait_for_alert(self, timeout=10):
293 """ 294 Waist until an alert is visible 295 @type timeout: Integer 296 @param timeout: Number of seconds before timing out 297 @rtype: bool 298 """ 299 def is_alert_visible(): 300 try: 301 #Try to get alert text to trigger exception if alert is not visible 302 alert = self.browser.switch_to_alert().text 303 return True 304 except NoAlertPresentException as e: 305 return False
306 307 condition = lambda *args: is_alert_visible() 308 try: 309 WebDriverWait(self.browser, timeout).until(condition) 310 return self.browser.switch_to_alert() 311 except TimeoutException: 312 self.test_logger.error('Timeout while waiting for alert to appear') 313 pytest.fail('Timeout while waiting for alert to appear') 314 315 @pytest.fixture(scope='function', autouse=True)
316 - def setup_method(self, request):
317 """ 318 Goes to homepage before each test, unless marker skipsetup is given 319 """ 320 #Get all markers 321 marks = {} 322 for k,v in request.node.function.__dict__.iteritems(): 323 if isinstance(v, MarkInfo): 324 marks[k] = v 325 326 #Don't go to home is skipsetup marker was given 327 if not marks.has_key('skipsetup'): 328 #Before clearing cookies, need to go to home screen, to ensure it's the current domain 329 self.go_home(save_screenshot=False) 330 self.browser.delete_all_cookies() 331 self.go_home() 332 else: 333 self.test_logger.info("Skipping setup")
334 335
336 - def wait_for_element_to_be_visible(self, locator_strategy, locator_string, error_msg=None, timeout=10):
337 """ 338 Wait until an element becomes visible 339 @param locator_strategy: Location strategy to use 340 @type locator_strategy: By 341 @param locator_string: String used to locate element 342 @type locator_string: str 343 @param error_msg: Error string to show if element is not found 344 @type error_msg: str 345 @param timeout: Maximum time in seconds to wait for the element to be visible 346 @type timeout: int 347 @rtype: WebElement 348 """ 349 try: 350 element = WebDriverWait(self.browser, timeout).until(expected_conditions.visibility_of_element_located((locator_strategy, locator_string))) 351 return element 352 except TimeoutException: 353 if error_msg is None: 354 error_msg = "Timeout while waiting for element '%s' to be visible" % locator_string 355 self.save_screenshot("[ERROR] %s" % error_msg) 356 pytest.fail(error_msg)
357 358
359 - def wait_for_element_to_be_clickable(self, locator_strategy, locator_string, error_msg=None, timeout=10):
360 """ 361 Wait until an element cna be clicked 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 clickable 369 @type timeout: int 370 @rtype: WebElement 371 """ 372 try: 373 element = WebDriverWait(self.browser, timeout).until(expected_conditions.element_to_be_clickable((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 clickable" % locator_string 378 self.save_screenshot("[ERROR] %s" % error_msg) 379 pytest.fail(error_msg)
380 381
382 - def wait_for_element_to_be_present(self, locator_strategy, locator_string, error_msg=None, timeout=10):
383 """ 384 Wait until an element is present 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 present 392 @type timeout: int 393 @rtype: WebElement 394 """ 395 try: 396 element = WebDriverWait(self.browser, timeout).until(expected_conditions.alert_is_present((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 present" % locator_string 401 self.save_screenshot("[ERROR] %s" % error_msg) 402 pytest.fail(error_msg)
403 404
405 - def wait_for_element_to_be_selected(self, locator_strategy, locator_string, error_msg=None, timeout=10):
406 """ 407 Wait until an element is selected 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 selected 415 @type timeout: int 416 @rtype: WebElement 417 """ 418 try: 419 element = WebDriverWait(self.browser, timeout).until(expected_conditions.element_located_to_be_selected((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 selected" % locator_string 424 self.save_screenshot("[ERROR] %s" % error_msg) 425 pytest.fail(error_msg)
426 427
428 - def wait_for_element_to_be_invisible(self, locator_strategy, locator_string, error_msg=None, timeout=10):
429 """ 430 Wait until an element becomes invisible 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 hidden 438 @type timeout: int 439 @rtype: WebElement 440 """ 441 try: 442 element = WebDriverWait(self.browser, timeout).until(expected_conditions.invisibility_of_element_located((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 invisible" % locator_string 447 self.save_screenshot("[ERROR] %s" % error_msg) 448 pytest.fail(error_msg)
449 450
451 - def wait_for_element_to_be_static(self, locator_strategy, locator_string, error_msg=None, timeout=10):
452 """ 453 Wait until an element that moves on the screen stops moving 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 visible 461 @type timeout: int 462 @rtype: WebElement 463 """ 464 try: 465 element = WebDriverWait(self.browser, timeout).until(expected_conditions.visibility_of_element_located((locator_strategy, locator_string))) 466 467 #wait until element is not moving or click will fail 468 old_location = {'x':0, 'y':0} 469 while old_location != element.location: 470 self._tlib_logger.debug("Pop-up is still moving. Previous position: %s, current position: %s" % (old_location, element.location)) 471 old_location = element.location 472 sleep(0.1) 473 element = self.browser.find_element(locator_strategy, locator_string) 474 475 return element 476 except TimeoutException: 477 if error_msg is None: 478 error_msg = "Timeout while waiting for element '%s' to be visible" % locator_string 479 self.save_screenshot("[ERROR] %s" % error_msg) 480 pytest.fail(error_msg)
481 482
483 - def wait_for_text_to_be_present_in_element(self, locator_strategy, locator_string, text, error_msg=None, timeout=10):
484 """ 485 Wait for an element that contains specified text 486 @param locator_strategy: Location strategy to use 487 @type locator_strategy: By 488 @param locator_string: String used to locate element 489 @type locator_string: str 490 @param error_msg: Error string to show if element is not found 491 @type error_msg: str 492 @param timeout: Maximum time in seconds to wait 493 @type timeout: int 494 """ 495 try: 496 WebDriverWait(self.browser, timeout).until(expected_conditions.text_to_be_present_in_element((locator_strategy, locator_string), text)) 497 except TimeoutException: 498 if error_msg is None: 499 error_msg = "Timeout while waiting for text %(text)s to be present in element '%(element)s'" % {"text": text, "element":locator_string} 500 self.save_screenshot("[ERROR] %s" % error_msg) 501 pytest.fail(error_msg)
502 503
504 - def wait_for_text_to_be_present_in_element_value(self, locator_strategy, locator_string, text, error_msg=None, timeout=10):
505 """ 506 Wait for an element's value to contain some test 507 @param locator_strategy: Location strategy to use 508 @type locator_strategy: By 509 @param locator_string: String used to locate element 510 @type locator_string: str 511 @param error_msg: Error string to show if element is not found 512 @type error_msg: str 513 @param timeout: Maximum time in seconds to wait 514 @type timeout: int 515 """ 516 try: 517 WebDriverWait(self.browser, timeout).until(expected_conditions.text_to_be_present_in_element_value((locator_strategy, locator_string), text)) 518 except TimeoutException: 519 if error_msg is None: 520 error_msg = "Timeout while waiting for text %(text)s to be present in the value of element '%(element)s'" % {"text": text, "element":locator_string} 521 self.save_screenshot("[ERROR] %s" % error_msg) 522 pytest.fail(error_msg)
523