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.support.ui import WebDriverWait 
 17  from selenium.webdriver.support import expected_conditions 
 18  from selenium.common.exceptions import TimeoutException 
 19  import logging 
20 21 22 # noinspection PyMethodParameters 23 # noinspection PyUnresolvedReferences 24 -class SeleniumTester(PytestTester):
25 26 _homepage = "" 27 _test_logger = None # Reference to test logger 28 _tlib_logger = None # Reference to tlib logger 29 _browser = None 30 31 _folder_dest = None # destination of report for the current class 32 _test_case_id = None 33 _test_case_name = None 34 _test_params = None 35 36 _jinja_env = jinja2.Environment(loader = jinja2.FileSystemLoader(TestHelper.tlib_template_folder()), trim_blocks = True) 37 38 _screenshot_report = None # List of tests for which there are screenshots 39 _screenshot_report_template = _jinja_env.get_template("screenshot_report.html") # Template for generating screenshot report 40 41 _screenshots = {} # Screenshots generated during a test case 42 _tc_screenshot_template = _jinja_env.get_template("testcase_screenshots.html") # Template for generating screenshots for a test case 43 44
45 - def _get_browser(self):
46 """ 47 Returns Instance of the WebDriver browser 48 """ 49 return self.__class__._browser
50 51
52 - def _set_browser(self, browser):
53 """ 54 Instance of the WebDriver browser 55 @type: webdriver.Remote 56 """ 57 self.__class__._browser = browser
58 59 browser = property(_get_browser, _set_browser) 60
61 - def _get_homepage(self):
62 """ 63 Instance of the WebDriver homepage 64 @type: webdriver.Remote 65 """ 66 return self.__class__._homepage
67 68
69 - def _set_homepage(self, homepage):
70 """ 71 Instance of the WebDriver homepage 72 @type: webdriver.Remote 73 """ 74 self.__class__._homepage = homepage
75 76 homepage = property(_get_homepage, _set_homepage) 77 78 79 @property
80 - def test_logger(self):
81 """ 82 Logger object to log test information 83 @type: logging 84 """ 85 return self.__class__._test_logger
86 87 88 @staticmethod
89 - def screenshot_folder():
90 """ 91 Returns project's screenshot folder 92 This method must be defined in subclasses 93 """ 94 raise NotImplementedError("Subclasses should implement this!")
95 96 97 @pytest.fixture(scope='class', autouse=True)
98 - def initialize_class(self, test_logger, tlib_logger, browser, base_url):
99 """ 100 @type test_logger: logging 101 @type browser: webdriver.Remote 102 Stores an instance of browser and test_logger 103 """ 104 self.__class__._test_logger = test_logger 105 self.__class__._tlib_logger = tlib_logger 106 self.browser = browser 107 # Only initialize homepage if it wasn't already initialized 108 if self.homepage is None: 109 self.homepage = base_url
110 111
112 - def get_unused_report_name(self, tc_id, tc_name):
113 """ 114 Gets a string based on test case id and name, taking into account if test case has already been run or not 115 @param tc_id: Test case id 116 @type tc_id: str 117 @param tc_name: Test case name 118 @type tc_name: str 119 """ 120 i = 0 121 while True: 122 if i == 0: 123 # filename = "%(tc_id)s - %(tc_name)s" % { "tc_id": tc_id, "tc_name": tc_name} 124 filename = "%(tc_id)s" % { "tc_id": tc_id} 125 else: 126 # filename = "%(tc_id)s - %(tc_name)s[retry %(cnt)d]" % { "tc_id": tc_id, "tc_name": tc_name, "cnt": i} 127 filename = "%(tc_id)s [retry %(cnt)d]" % { "tc_id": tc_id, "cnt": i} 128 129 if not filename in self.__class__._screenshot_report: 130 return filename 131 132 i += 1
133 134 135 @pytest.fixture(scope='class', autouse=True)
136 - def generate_class_report(self, request):
137 """ 138 Generates HTML page with all test case results for the class 139 @type request: FixtureRequest 140 """ 141 self.__class__._screenshot_report = collections.OrderedDict() 142 self.__class__._folder_dest = re.sub('\.py$', '', os.path.relpath(inspect.getfile(request.cls))) 143 144 def generate_report(): 145 if len(self.__class__._screenshot_report) > 0: 146 #Generate HTML based on template 147 html = self.__class__._screenshot_report_template.render(test_class=self.__class__.__name__, 148 files=self.__class__._screenshot_report) 149 150 htm_file = "%s.htm" % self.__class__._folder_dest 151 full_filename = os.path.join(self.screenshot_folder(), htm_file) 152 153 f = open(full_filename, "w") 154 try: 155 f.write(html) 156 finally: 157 f.close()
158 159 request.addfinalizer(generate_report)
160 161 162 @pytest.fixture(scope='function', autouse=True)
163 - def generate_test_report(self, request):
164 """ 165 Generates HTML page with all the screenshots for a test case 166 Supports generating different When test cases are run multiple times with the same arguments 167 @type request: FixtureRequest 168 """ 169 # noinspection PyProtectedMember 170 self.__class__._test_params = request.keywords.node._genid 171 self.__class__._test_case_name = request.keywords.node.name 172 self.__class__._screenshots = {} 173 174 #Get marker test case name or use a default if there is none 175 marker = request.node.get_marker("testcasename") 176 if marker is None: 177 self._test_logger.warn("Test case doesn't have marker testcasename") 178 self.__class__._test_case_id = "UNKNOWN_TEST_CASE_ID" 179 else: 180 self.__class__._test_case_id = marker.args[0] 181 182 183 def generate_report(): 184 185 if len(self.__class__._screenshots) > 0: 186 name = self.get_unused_report_name(self.__class__._test_case_id, self.__class__._test_case_name) 187 188 #Generate HTML based on template 189 html = self.__class__._tc_screenshot_template.render(test_case_id=self.__class__._test_case_id, 190 test_case_name=self.__class__._test_case_name, 191 screenshots=self.__class__._screenshots) 192 193 #Filename includes a counter in case test case is being run more than once 194 htm_file = FileHelper.get_filename_from_string(name) + '.htm' 195 relative_filename = os.path.join(self.__class__._folder_dest, htm_file) 196 full_filename = os.path.join(self.screenshot_folder(), relative_filename) 197 198 self.__class__._screenshot_report[name] = {"tc_id": self.__class__._test_case_id, 199 "tc_name": self.__class__._test_case_name, 200 "filename": relative_filename} 201 202 f = open(full_filename, "w") 203 try: 204 f.write(html) 205 finally: 206 f.close()
207 208 209 request.addfinalizer(generate_report) 210 211
212 - def navigate(self, page, save_screenshot=True):
213 """ 214 Navigate to "page" 215 """ 216 self.test_logger.info("Navigating to %s" % page) 217 self.browser.get(page) 218 if save_screenshot: 219 self.save_screenshot("Navigate to %s" % page)
220 221
222 - def go_home(self, save_screenshot=True):
223 """ 224 Navigate to home page 225 """ 226 self.navigate(self.homepage, save_screenshot=False) 227 if save_screenshot: 228 self.save_screenshot("Homepage")
229 230
231 - def save_screenshot(self, description):
232 """ 233 Saves screen shot for the current page 234 """ 235 236 def get_params(): 237 if self.__class__._test_params is None: 238 return "" 239 else: 240 return self.__class__._test_params
241 242 #Waits until page is loaded 243 self._tlib_logger.debug("Saving screenshot") 244 self.wait_for_page_loaded() 245 246 #Folder containing images for test case 247 name = self.get_unused_report_name(self.__class__._test_case_id, self.__class__._test_case_name) 248 name = FileHelper.get_filename_from_string(name) 249 250 test_folder = os.path.join(self.screenshot_folder(), self.__class__._folder_dest, name) 251 252 try: 253 os.makedirs(test_folder) 254 except WindowsError: 255 pass 256 257 #Get number of existing images 258 i = len(os.listdir(test_folder)) + 1 259 260 #Get filename 261 img_filename = "%02d.jpg" % i 262 full_path = os.path.join(test_folder, img_filename) 263 264 self.__class__._screenshots[i] = {"filename": "%s/%s" % (name, img_filename), 265 "description": description} 266 267 self.browser.get_screenshot_as_file(full_path) 268 269
270 - def wait_for_page_loaded(self, timeout=10):
271 """ 272 Waist until document.readyState is equal to complete 273 @type timeout: Integer 274 @param timeout: Number of seconds before timing out 275 """ 276 if self.browser.execute_script("return document.readyState") == "complete": 277 return 278 else: 279 self._tlib_logger.debug("Waiting for '%s' to load" % self.browser.current_url) 280 281 condition = lambda *args: self.browser.execute_script("return document.readyState") == "complete" 282 try: 283 WebDriverWait(self.browser, timeout).until(condition) 284 except TimeoutException: 285 self.test_logger.error('Timeout while waiting for page to load') 286 pytest.fail('Timeout while waiting for page to load') 287 288 self._tlib_logger.debug("Page '%s' finished loading" % self.browser.current_url)
289 290 @pytest.fixture(scope='function', autouse=True)
291 - def setup_method(self, request):
292 """ 293 Goes to homepage before each test, unless marker skipsetup is given 294 """ 295 #Get all markers 296 marks = {} 297 for k,v in request.node.function.__dict__.iteritems(): 298 if isinstance(v, MarkInfo): 299 marks[k] = v 300 301 #Don't go to home is skipsetup marker was given 302 if not marks.has_key('skipsetup'): 303 #Before clearing cookies, need to go to home screen, to ensure it's the current domain 304 self.go_home(save_screenshot=False) 305 self.browser.delete_all_cookies() 306 self.go_home() 307 else: 308 self.test_logger.info("Skipping setup")
309 310
311 - def wait_for_element_to_be_visible(self, locator_strategy, locator_string, error_msg=None, timeout=10):
312 try: 313 WebDriverWait(self.browser, timeout).until(expected_conditions.visibility_of_element_located((locator_strategy, locator_string))) 314 except TimeoutException: 315 if error_msg is None: 316 error_msg = "Timeout while waiting for element '%s' to be visible" % locator_string 317 self.save_screenshot("[ERROR] %s" % error_msg) 318 pytest.fail(error_msg)
319 320
321 - def wait_for_element_to_be_clickable(self, locator_strategy, locator_string, error_msg=None, timeout=10):
322 try: 323 WebDriverWait(self.browser, timeout).until(expected_conditions.element_to_be_clickable((locator_strategy, locator_string))) 324 except TimeoutException: 325 if error_msg is None: 326 error_msg = "Timeout while waiting for element '%s' to be clickable" % locator_string 327 self.save_screenshot("[ERROR] %s" % error_msg) 328 pytest.fail(error_msg)
329 330
331 - def wait_for_element_to_be_present(self, locator_strategy, locator_string, error_msg=None, timeout=10):
332 try: 333 WebDriverWait(self.browser, timeout).until(expected_conditions.alert_is_present((locator_strategy, locator_string))) 334 except TimeoutException: 335 if error_msg is None: 336 error_msg = "Timeout while waiting for element '%s' to be present" % locator_string 337 self.save_screenshot("[ERROR] %s" % error_msg) 338 pytest.fail(error_msg)
339 340
341 - def wait_for_element_to_be_selected(self, locator_strategy, locator_string, error_msg=None, timeout=10):
342 try: 343 WebDriverWait(self.browser, timeout).until(expected_conditions.element_located_to_be_selected((locator_strategy, locator_string))) 344 except TimeoutException: 345 if error_msg is None: 346 error_msg = "Timeout while waiting for element '%s' to be selected" % locator_string 347 self.save_screenshot("[ERROR] %s" % error_msg) 348 pytest.fail(error_msg)
349 350
351 - def wait_for_element_to_be_invisible(self, locator_strategy, locator_string, error_msg=None, timeout=10):
352 try: 353 WebDriverWait(self.browser, timeout).until(expected_conditions.invisibility_of_element_located((locator_strategy, locator_string))) 354 except TimeoutException: 355 if error_msg is None: 356 error_msg = "Timeout while waiting for element '%s' to be invisible" % locator_string 357 self.save_screenshot("[ERROR] %s" % error_msg) 358 pytest.fail(error_msg)
359 360
361 - def wait_for_test_to_be_present_in_element(self, locator_strategy, locator_string, text, error_msg=None, timeout=10):
362 try: 363 WebDriverWait(self.browser, timeout).until(expected_conditions.text_to_be_present_in_element((locator_strategy, locator_string), text)) 364 except TimeoutException: 365 if error_msg is None: 366 error_msg = "Timeout while waiting for text %(text)s to be present in element '%(element)s'" % {"text": text, "element":locator_string} 367 self.save_screenshot("[ERROR] %s" % error_msg) 368 pytest.fail(error_msg)
369 370
371 - def wait_for_test_to_be_present_in_element_value(self, locator_strategy, locator_string, text, error_msg=None, timeout=10):
372 try: 373 WebDriverWait(self.browser, timeout).until(expected_conditions.text_to_be_present_in_element_value((locator_strategy, locator_string), text)) 374 except TimeoutException: 375 if error_msg is None: 376 error_msg = "Timeout while waiting for text %(text)s to be present in the value of element '%(element)s'" % {"text": text, "element":locator_string} 377 self.save_screenshot("[ERROR] %s" % error_msg) 378 pytest.fail(error_msg)
379