Package tlib :: Package base :: Module WebDriverTester
[hide private]
[frames] | no frames]

Source Code for Module tlib.base.WebDriverTester

  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 _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  from TestHelper import Singleton 
21 22 # noinspection PyMethodParameters 23 # noinspection PyUnresolvedReferences 24 -class WebDriverTester(PytestTester):
25 26 __metaclass__ = Singleton 27 28 _driver = 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 screenshot_folder(self):
51 """ 52 Returns location of screenshot folder 53 54 @return: str 55 """ 56 screenshot_folder = self._find_screenshot_folder() 57 if screenshot_folder is None: 58 #Couldn't find screenshot folder 59 raise NotImplementedError("Could not find screenshot folder. " 60 "Class should implement method screenshot_folder") 61 else: 62 return screenshot_folder
63
64 - def _find_screenshot_folder(self):
65 """ 66 Will try to find a folder named "screenshot" starting from the file being executed and up to 67 three levels up 68 69 @return: str 70 """ 71 #Get current folder 72 curr_folder = os.path.dirname(self._request.fspath.strpath) 73 74 #Go up to three levels to find screenshot folder 75 for i in range(1, 4): 76 curr_folder = os.path.abspath(os.path.join(curr_folder, os.pardir)) 77 78 #Check if there is a folder 'screenshots' 79 screenshot_folder = os.path.join(curr_folder, 'screenshots') 80 if os.path.exists(screenshot_folder): 81 return screenshot_folder
82 83
84 - def _get_unused_report_name(self, tc_id, tc_params):
85 """ 86 Gets a string based on test case id and name, taking into account if test case has already been run or not 87 @param tc_id: Test case id 88 @type tc_id: str 89 @type tc_name: str 90 """ 91 i = 0 92 while True: 93 if i == 0: 94 filename = "%(tc_id)s_%(tc_params)s" % {"tc_id": tc_id, "tc_params": tc_params} 95 else: 96 filename = "%(tc_id)s_%(tc_params)s [retry %(cnt)d]" % {"tc_id": tc_id, "tc_params": tc_params, "cnt": i} 97 98 if not filename in self._screenshot_report: 99 return filename 100 101 i += 1
102 103 104 @pytest.fixture(scope='class', autouse=True)
105 - def initialize_webdriver_class(self, request):
106 """ 107 @type request: FixtureRequest 108 """ 109 110 #Generates initial HTML page with all test case results for the class 111 self._screenshot_report = collections.OrderedDict() 112 self._folder_dest = os.path.basename(os.path.dirname(os.path.abspath(inspect.getfile(request.cls)))) 113 114 def generate_report(): 115 if len(self._screenshot_report) > 0: 116 #Generate HTML based on template 117 html = self._screenshot_report_template.render(test_class=request.cls.__name__, 118 files=self._screenshot_report) 119 120 htm_file = "%s.htm" % self._folder_dest 121 full_filename = os.path.join(self.screenshot_folder(), htm_file) 122 123 f = open(full_filename, "w") 124 try: 125 f.write(html) 126 finally: 127 f.close()
128 129 request.addfinalizer(generate_report)
130 131 132 @pytest.fixture(scope='function', autouse=True)
133 - def setup_webdriver_test(self, request):
134 """ 135 Goes to homepage before each test, unless marker skipsetup is given 136 137 @type request: FixtureRequest 138 """ 139 #Store an instance of the request object. Required for the generation of screenshots 140 self._request = request 141 142 #Initialization required for screenshot generation 143 # noinspection PyProtectedMember 144 self._test_params = request.keywords.node._genid 145 self._test_case_name = request.keywords.node.name 146 self._screenshots = {} 147 148 #Get marker test case name or use a default if there is none 149 marker = request.node.get_marker("testcasename") 150 if marker is None: 151 self.test_logger.warn("Test case doesn't have marker testcasename") 152 self._test_case_id = "UNKNOWN_TEST_CASE_ID" 153 else: 154 self._test_case_id = marker.args[0] 155 156 def generate_report(): 157 """ 158 Generates HTML page with all the screenshots for a test case 159 Supports generating different files when test cases are run multiple times with the same arguments 160 """ 161 162 if len(self._screenshots) > 0: 163 name = self._get_unused_report_name(self._test_case_id, self._test_params) 164 165 #Generate HTML based on template 166 html = self._tc_screenshot_template.render(test_case_id=self._test_case_id, 167 test_case_name=self._test_case_name, 168 screenshots=self._screenshots) 169 170 #Filename includes a counter in case test case is being run more than once 171 htm_file = FileHelper.get_filename_from_string(name) + '.htm' 172 relative_filename = os.path.join(self._folder_dest, htm_file) 173 full_filename = os.path.join(self.screenshot_folder(), relative_filename) 174 175 self._screenshot_report[name] = {"tc_id": self._test_case_id, 176 "tc_name": self._test_case_name, 177 "filename": relative_filename} 178 179 f = open(full_filename, "w") 180 try: 181 f.write(html) 182 finally: 183 f.close()
184 185 request.addfinalizer(generate_report) 186 187
188 - def save_screenshot(self, description):
189 """ 190 Saves screen shot for the current page 191 """ 192 #Waits until page is loaded 193 self.tlib_logger.debug("Saving screenshot") 194 self.wait_for_page_loaded() 195 196 #Folder containing images for test case 197 name = self._get_unused_report_name(self._test_case_id, self._test_params) 198 name = FileHelper.get_filename_from_string(name) 199 200 test_folder = os.path.join(self.screenshot_folder(), self._folder_dest, name) 201 202 try: 203 os.makedirs(test_folder) 204 except WindowsError: 205 pass 206 207 #Get number of existing images 208 i = len(os.listdir(test_folder)) + 1 209 210 #Get filename 211 img_filename = "%02d.png" % i 212 full_path = os.path.join(test_folder, img_filename) 213 214 self._screenshots[i] = {"filename": "%s/%s" % (name, img_filename), 215 u"description": description} 216 217 self._driver.get_screenshot_as_file(full_path)
218 219
220 - def wait_for_page_loaded(self, timeout=10):
221 raise NotImplementedError("This method should be implemented by derived classes")
222 223
224 - def wait_for_alert(self, timeout=10):
225 """ 226 Waist until an alert is visible 227 @type timeout: Integer 228 @param timeout: Number of seconds before timing out 229 @rtype: bool 230 """ 231 def is_alert_visible(): 232 try: 233 #Try to get alert text to trigger exception if alert is not visible 234 alert = self._driver.switch_to_alert().text 235 return True 236 except NoAlertPresentException as e: 237 return False
238 239 condition = lambda *args: is_alert_visible() 240 try: 241 WebDriverWait(self._driver, timeout).until(condition) 242 return self._driver.switch_to_alert() 243 except TimeoutException: 244 self.test_logger.error('Timeout while waiting for alert to appear') 245 pytest.fail('Timeout while waiting for alert to appear') 246 247
248 - def wait_for_element_to_be_visible(self, locator_strategy, locator_string, error_msg=None, timeout=10):
249 """ 250 Wait until an element becomes visible 251 @param locator_strategy: Location strategy to use 252 @type locator_strategy: By 253 @param locator_string: String used to locate element 254 @type locator_string: str 255 @param error_msg: Error string to show if element is not found 256 @type error_msg: str 257 @param timeout: Maximum time in seconds to wait for the element to be visible 258 @type timeout: int 259 @rtype: WebElement 260 """ 261 try: 262 element = WebDriverWait(self._driver, timeout).until(expected_conditions.visibility_of_element_located((locator_strategy, locator_string))) 263 return element 264 except TimeoutException: 265 if error_msg is None: 266 error_msg = "Timeout while waiting for element '%s' to be visible" % locator_string 267 self.save_screenshot("[ERROR] %s" % error_msg) 268 pytest.fail(error_msg)
269 270
271 - def wait_for_element_to_be_clickable(self, locator_strategy, locator_string, error_msg=None, timeout=10):
272 """ 273 Wait until an element cna be clicked 274 @param locator_strategy: Location strategy to use 275 @type locator_strategy: By 276 @param locator_string: String used to locate element 277 @type locator_string: str 278 @param error_msg: Error string to show if element is not found 279 @type error_msg: str 280 @param timeout: Maximum time in seconds to wait for the element to be clickable 281 @type timeout: int 282 @rtype: WebElement 283 """ 284 try: 285 element = WebDriverWait(self._driver, timeout).until(expected_conditions.element_to_be_clickable((locator_strategy, locator_string))) 286 return element 287 except TimeoutException: 288 if error_msg is None: 289 error_msg = "Timeout while waiting for element '%s' to be clickable" % locator_string 290 self.save_screenshot("[ERROR] %s" % error_msg) 291 pytest.fail(error_msg)
292 293
294 - def wait_for_element_to_be_present(self, locator_strategy, locator_string, error_msg=None, timeout=10):
295 """ 296 Wait until an element is present 297 @param locator_strategy: Location strategy to use 298 @type locator_strategy: By 299 @param locator_string: String used to locate element 300 @type locator_string: str 301 @param error_msg: Error string to show if element is not found 302 @type error_msg: str 303 @param timeout: Maximum time in seconds to wait for the element to be present 304 @type timeout: int 305 @rtype: WebElement 306 """ 307 try: 308 element = WebDriverWait(self._driver, timeout).until(expected_conditions.alert_is_present((locator_strategy, locator_string))) 309 return element 310 except TimeoutException: 311 if error_msg is None: 312 error_msg = "Timeout while waiting for element '%s' to be present" % locator_string 313 self.save_screenshot("[ERROR] %s" % error_msg) 314 pytest.fail(error_msg)
315 316
317 - def wait_for_element_to_be_selected(self, locator_strategy, locator_string, error_msg=None, timeout=10):
318 """ 319 Wait until an element is selected 320 @param locator_strategy: Location strategy to use 321 @type locator_strategy: By 322 @param locator_string: String used to locate element 323 @type locator_string: str 324 @param error_msg: Error string to show if element is not found 325 @type error_msg: str 326 @param timeout: Maximum time in seconds to wait for the element to be selected 327 @type timeout: int 328 @rtype: WebElement 329 """ 330 try: 331 element = WebDriverWait(self._driver, timeout).until(expected_conditions.element_located_to_be_selected((locator_strategy, locator_string))) 332 return element 333 except TimeoutException: 334 if error_msg is None: 335 error_msg = "Timeout while waiting for element '%s' to be selected" % locator_string 336 self.save_screenshot("[ERROR] %s" % error_msg) 337 pytest.fail(error_msg)
338 339
340 - def wait_for_element_to_be_invisible(self, locator_strategy, locator_string, error_msg=None, timeout=10):
341 """ 342 Wait until an element becomes invisible 343 @param locator_strategy: Location strategy to use 344 @type locator_strategy: By 345 @param locator_string: String used to locate element 346 @type locator_string: str 347 @param error_msg: Error string to show if element is not found 348 @type error_msg: str 349 @param timeout: Maximum time in seconds to wait for the element to be hidden 350 @type timeout: int 351 @rtype: WebElement 352 """ 353 try: 354 element = WebDriverWait(self._driver, timeout).until(expected_conditions.invisibility_of_element_located((locator_strategy, locator_string))) 355 return element 356 except TimeoutException: 357 if error_msg is None: 358 error_msg = "Timeout while waiting for element '%s' to be invisible" % locator_string 359 self.save_screenshot("[ERROR] %s" % error_msg) 360 pytest.fail(error_msg)
361 362
363 - def wait_for_element_to_be_static(self, locator_strategy, locator_string, error_msg=None, timeout=10):
364 """ 365 Wait until an element that moves on the screen stops moving 366 @param locator_strategy: Location strategy to use 367 @type locator_strategy: By 368 @param locator_string: String used to locate element 369 @type locator_string: str 370 @param error_msg: Error string to show if element is not found 371 @type error_msg: str 372 @param timeout: Maximum time in seconds to wait for the element to be visible 373 @type timeout: int 374 @rtype: WebElement 375 """ 376 try: 377 element = WebDriverWait(self._driver, timeout).until(expected_conditions.visibility_of_element_located((locator_strategy, locator_string))) 378 379 #wait until element is not moving or click will fail 380 old_location = {'x':0, 'y':0} 381 while old_location != element.location: 382 self.tlib_logger.debug("Pop-up is still moving. Previous position: %s, current position: %s" % (old_location, element.location)) 383 old_location = element.location 384 sleep(0.1) 385 element = self._driver.find_element(locator_strategy, locator_string) 386 387 return element 388 except TimeoutException: 389 if error_msg is None: 390 error_msg = "Timeout while waiting for element '%s' to be visible" % locator_string 391 self.save_screenshot("[ERROR] %s" % error_msg) 392 pytest.fail(error_msg)
393 394
395 - def wait_for_text_to_be_present_in_element(self, locator_strategy, locator_string, text, error_msg=None, timeout=10):
396 """ 397 Wait for an element that contains specified text 398 @param locator_strategy: Location strategy to use 399 @type locator_strategy: By 400 @param locator_string: String used to locate element 401 @type locator_string: str 402 @param error_msg: Error string to show if element is not found 403 @type error_msg: str 404 @param timeout: Maximum time in seconds to wait 405 @type timeout: int 406 """ 407 try: 408 WebDriverWait(self._driver, timeout).until(expected_conditions.text_to_be_present_in_element((locator_strategy, locator_string), text)) 409 except TimeoutException: 410 if error_msg is None: 411 error_msg = "Timeout while waiting for text %(text)s to be present in element '%(element)s'" % {"text": text, "element":locator_string} 412 self.save_screenshot("[ERROR] %s" % error_msg) 413 pytest.fail(error_msg)
414 415
416 - def wait_for_text_to_be_present_in_element_value(self, locator_strategy, locator_string, text, error_msg=None, timeout=10):
417 """ 418 Wait for an element's value to contain some test 419 @param locator_strategy: Location strategy to use 420 @type locator_strategy: By 421 @param locator_string: String used to locate element 422 @type locator_string: str 423 @param error_msg: Error string to show if element is not found 424 @type error_msg: str 425 @param timeout: Maximum time in seconds to wait 426 @type timeout: int 427 """ 428 try: 429 WebDriverWait(self._driver, timeout).until(expected_conditions.text_to_be_present_in_element_value((locator_strategy, locator_string), text)) 430 except TimeoutException: 431 if error_msg is None: 432 error_msg = "Timeout while waiting for text %(text)s to be present in the value of element '%(element)s'" % {"text": text, "element":locator_string} 433 self.save_screenshot("[ERROR] %s" % error_msg) 434 pytest.fail(error_msg)
435