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