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

Source Code for Module tlib.base.WebDriverTester

  1  import os 
  2  import inspect 
  3  # noinspection PyPackageRequirements 
  4  import pytest 
  5  import collections 
  6  from time import sleep 
  7  # noinspection PyPackageRequirements 
  8  from _pytest.python import FixtureRequest 
  9  # noinspection PyPackageRequirements 
 10  import jinja2 
 11  from tlib.base import TestHelper 
 12  from tlib.base import FileHelper 
 13  from tlib.base.PytestTester import PytestTester 
 14  from selenium.webdriver.common.by import By 
 15  from selenium.webdriver.support.ui import WebDriverWait 
 16  from selenium.webdriver.support import expected_conditions 
 17  from selenium.common.exceptions import TimeoutException, NoAlertPresentException, NoSuchElementException 
 18  from selenium.webdriver.remote.webelement import WebElement 
 19  from TestHelper import Singleton 
20 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 - def _get_unused_report_name(self, tc_id, tc_params):
84 """ 85 Gets a string based on test case id and name, taking into account if test case has already been run or not 86 @param tc_id: Test case id 87 @type tc_id: str 88 @type tc_name: str 89 """ 90 i = 0 91 while True: 92 if i == 0: 93 filename = "%(tc_id)s_%(tc_params)s" % {"tc_id": tc_id, "tc_params": tc_params} 94 else: 95 filename = "%(tc_id)s_%(tc_params)s [retry %(cnt)d]" % \ 96 {"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 @pytest.fixture(scope='class', autouse=True)
104 - def initialize_webdriver_class(self, request):
105 """ 106 @type request: FixtureRequest 107 """ 108 109 #Generates initial HTML page with all test case results for the class 110 self._screenshot_report = collections.OrderedDict() 111 self._folder_dest = os.path.basename(os.path.dirname(os.path.abspath(inspect.getfile(request.cls)))) 112 113 def generate_report(): 114 if len(self._screenshot_report) > 0: 115 #Generate HTML based on template 116 html = self._screenshot_report_template.render(test_class=request.cls.__name__, 117 files=self._screenshot_report) 118 119 htm_file = "%s.htm" % self._folder_dest 120 full_filename = os.path.join(self.screenshot_folder(), htm_file) 121 122 f = open(full_filename, "w") 123 try: 124 f.write(html) 125 finally: 126 f.close()
127 128 request.addfinalizer(generate_report)
129 130 @pytest.fixture(scope='function', autouse=True)
131 - def setup_webdriver_test(self, request):
132 """ 133 Goes to homepage before each test, unless marker skipsetup is given 134 135 @type request: FixtureRequest 136 """ 137 #Store an instance of the request object. Required for the generation of screenshots 138 self._request = request 139 140 #Initialization required for screenshot generation 141 # noinspection PyProtectedMember 142 self._test_params = request.keywords.node._genid 143 self._test_case_name = request.keywords.node.name 144 self._screenshots = {} 145 146 #Get marker test case name or use a default if there is none 147 marker = request.node.get_marker("testcasename") 148 if marker is None: 149 self.test_logger.warn("Test case doesn't have marker testcasename") 150 self._test_case_id = "UNKNOWN_TEST_CASE_ID" 151 else: 152 self._test_case_id = marker.args[0] 153 154 def generate_report(): 155 """ 156 Generates HTML page with all the screenshots for a test case 157 Supports generating different files when test cases are run multiple times with the same arguments 158 """ 159 160 if len(self._screenshots) > 0: 161 name = self._get_unused_report_name(self._test_case_id, self._test_params) 162 name = self.remove_non_ascii_chars(name) 163 164 test_case_name=self._test_case_name 165 test_case_name = self.remove_non_ascii_chars(test_case_name) 166 167 #Generate HTML based on template 168 html = self._tc_screenshot_template.render(test_case_id=self._test_case_id, 169 test_case_name=test_case_name, 170 screenshots=self._screenshots) 171 172 #Filename includes a counter in case test case is being run more than once 173 htm_file = FileHelper.get_filename_from_string(name) + '.htm' 174 relative_filename = os.path.join(self._folder_dest, htm_file) 175 full_filename = os.path.join(self.screenshot_folder(), relative_filename) 176 full_filename = self.remove_non_ascii_chars(full_filename) 177 178 self._screenshot_report[name] = {"tc_id": self._test_case_id, 179 "tc_name": test_case_name, 180 "filename": relative_filename} 181 182 f = open(full_filename, "w") 183 try: 184 f.write(html) 185 finally: 186 f.close()
187 188 request.addfinalizer(generate_report) 189
190 - def save_screenshot(self, description):
191 """ 192 Saves screen shot for the current page 193 """ 194 #Waits until page is loaded 195 self.tlib_logger.debug("Saving screenshot") 196 self.wait_for_page_loaded() 197 198 #Folder containing images for test case 199 name = self._get_unused_report_name(self._test_case_id, self._test_params) 200 name = FileHelper.get_filename_from_string(name) 201 name = self.remove_non_ascii_chars(name) 202 203 test_folder = os.path.join(self.screenshot_folder(), self._folder_dest, name) 204 205 try: 206 os.makedirs(test_folder) 207 except WindowsError: 208 pass 209 210 #Get number of existing images 211 i = len(os.listdir(test_folder)) + 1 212 213 #Get filename 214 img_filename = "%02d.png" % i 215 full_path = os.path.join(test_folder, img_filename) 216 217 self._screenshots[i] = {"filename": "%s/%s" % (name, img_filename), 218 u"description": description} 219 220 self._driver.get_screenshot_as_file(full_path)
221
222 - def remove_non_ascii_chars(self, str):
223 """ 224 Removes non-ascii characters from a string 225 @type str: string 226 @param str: String with non-ascii characters 227 @rtype: string 228 """ 229 return ''.join([i if ord(i) < 128 else '' for i in str])
230
231 - def wait_for_page_loaded(self, timeout=10):
232 raise NotImplementedError("This method should be implemented by derived classes")
233
234 - def wait_for_alert(self, timeout=10):
235 """ 236 Waist until an alert is visible 237 @type timeout: Integer 238 @param timeout: Number of seconds before timing out 239 @rtype: bool 240 """ 241 def is_alert_visible(): 242 try: 243 #Try to get alert text to trigger exception if alert is not visible 244 alert = self._driver.switch_to_alert().text 245 return True 246 except NoAlertPresentException as e: 247 return False
248 249 condition = lambda *args: is_alert_visible() 250 try: 251 WebDriverWait(self._driver, timeout).until(condition) 252 return self._driver.switch_to_alert() 253 except TimeoutException: 254 self.test_logger.error('Timeout while waiting for alert to appear') 255 raise RuntimeError('Timeout while waiting for alert to appear') 256
257 - def wait_for_element_to_be_visible(self, locator_strategy, locator_string, error_msg=None, timeout=10):
258 """ 259 Wait until an element becomes visible 260 @param locator_strategy: Location strategy to use 261 @type locator_strategy: By 262 @param locator_string: String used to locate element 263 @type locator_string: str 264 @param error_msg: Error string to show if element is not found 265 @type error_msg: str 266 @param timeout: Maximum time in seconds to wait for the element to be visible 267 @type timeout: int 268 @rtype: WebElement 269 """ 270 try: 271 element = WebDriverWait(self._driver, timeout).\ 272 until(expected_conditions.visibility_of_element_located((locator_strategy, locator_string))) 273 return element 274 except TimeoutException: 275 if error_msg is None: 276 error_msg = "Timeout while waiting for element '%s' to be visible" % locator_string 277 self.save_screenshot("[ERROR] %s" % error_msg) 278 raise RuntimeError(error_msg)
279
280 - def wait_for_element_to_be_clickable(self, locator_strategy, locator_string, error_msg=None, timeout=10):
281 """ 282 Wait until an element cna be clicked 283 @param locator_strategy: Location strategy to use 284 @type locator_strategy: By 285 @param locator_string: String used to locate element 286 @type locator_string: str 287 @param error_msg: Error string to show if element is not found 288 @type error_msg: str 289 @param timeout: Maximum time in seconds to wait for the element to be clickable 290 @type timeout: int 291 @rtype: WebElement 292 """ 293 try: 294 element = WebDriverWait(self._driver, timeout).\ 295 until(expected_conditions.element_to_be_clickable((locator_strategy, locator_string))) 296 return element 297 except TimeoutException: 298 if error_msg is None: 299 error_msg = "Timeout while waiting for element '%s' to be clickable" % locator_string 300 self.save_screenshot("[ERROR] %s" % error_msg) 301 raise RuntimeError(error_msg)
302
303 - def wait_for_element_to_be_present(self, locator_strategy, locator_string, error_msg=None, timeout=10):
304 """ 305 Wait until an element is present 306 @param locator_strategy: Location strategy to use 307 @type locator_strategy: By 308 @param locator_string: String used to locate element 309 @type locator_string: str 310 @param error_msg: Error string to show if element is not found 311 @type error_msg: str 312 @param timeout: Maximum time in seconds to wait for the element to be present 313 @type timeout: int 314 @rtype: WebElement 315 """ 316 try: 317 element = WebDriverWait(self._driver, timeout).\ 318 until(expected_conditions.presence_of_element_located((locator_strategy, locator_string))) 319 return element 320 except TimeoutException: 321 if error_msg is None: 322 error_msg = "Timeout while waiting for element '%s' to be present" % locator_string 323 self.save_screenshot("[ERROR] %s" % error_msg) 324 raise RuntimeError(error_msg)
325
326 - def wait_for_element_to_be_selected(self, locator_strategy, locator_string, error_msg=None, timeout=10):
327 """ 328 Wait until an element is selected 329 @param locator_strategy: Location strategy to use 330 @type locator_strategy: By 331 @param locator_string: String used to locate element 332 @type locator_string: str 333 @param error_msg: Error string to show if element is not found 334 @type error_msg: str 335 @param timeout: Maximum time in seconds to wait for the element to be selected 336 @type timeout: int 337 @rtype: WebElement 338 """ 339 try: 340 element = WebDriverWait(self._driver, timeout).\ 341 until(expected_conditions.element_located_to_be_selected((locator_strategy, locator_string))) 342 return element 343 except TimeoutException: 344 if error_msg is None: 345 error_msg = "Timeout while waiting for element '%s' to be selected" % locator_string 346 self.save_screenshot("[ERROR] %s" % error_msg) 347 raise RuntimeError(error_msg)
348
349 - def wait_for_element_to_be_invisible(self, locator_strategy, locator_string, error_msg=None, timeout=10):
350 """ 351 Wait until an element becomes invisible 352 @param locator_strategy: Location strategy to use 353 @type locator_strategy: By 354 @param locator_string: String used to locate element 355 @type locator_string: str 356 @param error_msg: Error string to show if element is not found 357 @type error_msg: str 358 @param timeout: Maximum time in seconds to wait for the element to be hidden 359 @type timeout: int 360 @rtype: WebElement 361 """ 362 try: 363 element = WebDriverWait(self._driver, timeout).\ 364 until(expected_conditions.invisibility_of_element_located((locator_strategy, locator_string))) 365 return element 366 except TimeoutException: 367 if error_msg is None: 368 error_msg = "Timeout while waiting for element '%s' to be invisible" % locator_string 369 self.save_screenshot("[ERROR] %s" % error_msg) 370 raise RuntimeError(error_msg)
371
372 - def wait_for_element_to_be_static(self, locator_strategy, locator_string, error_msg=None, timeout=10):
373 """ 374 Wait until an element that moves on the screen stops moving 375 @param locator_strategy: Location strategy to use 376 @type locator_strategy: By 377 @param locator_string: String used to locate element 378 @type locator_string: str 379 @param error_msg: Error string to show if element is not found 380 @type error_msg: str 381 @param timeout: Maximum time in seconds to wait for the element to be visible 382 @type timeout: int 383 @rtype: WebElement 384 """ 385 try: 386 element = WebDriverWait(self._driver, timeout).\ 387 until(expected_conditions.visibility_of_element_located((locator_strategy, locator_string))) 388 389 #wait until element is not moving or click will fail 390 old_location = {'x': 0, 'y': 0} 391 while old_location != element.location: 392 self.tlib_logger.debug("Pop-up is still moving. Previous position: %s, current position: %s" % 393 (old_location, element.location)) 394 old_location = element.location 395 sleep(0.1) 396 element = self._driver.find_element(locator_strategy, locator_string) 397 398 return element 399 except TimeoutException: 400 if error_msg is None: 401 error_msg = "Timeout while waiting for element '%s' to be visible" % locator_string 402 self.save_screenshot("[ERROR] %s" % error_msg) 403 raise RuntimeError(error_msg)
404
405 - def wait_for_text_to_be_present_in_element(self, locator_strategy, locator_string, text, 406 error_msg=None, timeout=10):
407 """ 408 Wait for an element that contains specified text 409 @param locator_strategy: Location strategy to use 410 @type locator_strategy: By 411 @param locator_string: String used to locate element 412 @type locator_string: str 413 @param error_msg: Error string to show if element is not found 414 @type error_msg: str 415 @param timeout: Maximum time in seconds to wait 416 @type timeout: int 417 """ 418 try: 419 WebDriverWait(self._driver, timeout).\ 420 until(expected_conditions.text_to_be_present_in_element((locator_strategy, locator_string), text)) 421 except TimeoutException: 422 if error_msg is None: 423 error_msg = "Timeout while waiting for text %(text)s to be present in element '%(element)s'" % \ 424 {"text": text, "element": locator_string} 425 self.save_screenshot("[ERROR] %s" % error_msg) 426 raise RuntimeError(error_msg)
427
428 - def wait_for_text_to_be_present_in_element_value(self, locator_strategy, locator_string, text, 429 error_msg=None, timeout=10):
430 """ 431 Wait for an element's value to contain some test 432 @param locator_strategy: Location strategy to use 433 @type locator_strategy: By 434 @param locator_string: String used to locate element 435 @type locator_string: str 436 @param error_msg: Error string to show if element is not found 437 @type error_msg: str 438 @param timeout: Maximum time in seconds to wait 439 @type timeout: int 440 """ 441 try: 442 WebDriverWait(self._driver, timeout).\ 443 until(expected_conditions.text_to_be_present_in_element_value((locator_strategy, locator_string), text)) 444 except TimeoutException: 445 if error_msg is None: 446 error_msg = "Timeout while waiting for text %(text)s to be present " \ 447 "in the value of element '%(element)s'" % {"text": text, "element": locator_string} 448 self.save_screenshot("[ERROR] %s" % error_msg) 449 raise RuntimeError(error_msg)
450 451 466
467 - def get_webelement_by_xpath(self, locator_string):
468 """ 469 Get the webelement by xpath 470 @param locator_string: String used to locate element 471 @type locator_string: str 472 @param error_msg: Error string to show if element is not found 473 @type error_msg: str 474 """ 475 try: 476 self.wait_for_element_to_be_visible(By.XPATH, locator_string) 477 return self.browser.find_element_by_xpath(locator_string) 478 except NoSuchElementException: 479 error_msg="Could not find the xpath: '%s'" 480 self.save_screenshot(error_msg % locator_string) 481 raise RuntimeError(error_msg % locator_string)
482
483 - def get_webelement_by_css(self, locator_string):
484 """ 485 Get the webelement by CSS 486 @param locator_string: String used to locate element 487 @type locator_string: str 488 @param error_msg: Error string to show if element is not found 489 @type error_msg: str 490 """ 491 try: 492 self.wait_for_element_to_be_visible(By.CSS_SELECTOR, locator_string) 493 return self.browser.find_element_by_css_selector(locator_string) 494 except NoSuchElementException: 495 error_msg="Could not find css: '%s'" 496 self.save_screenshot(error_msg % locator_string) 497 raise RuntimeError(error_msg %locator_string)
498
499 - def get_webelements_by_xpath(self, locator_string):
500 """ 501 Get the webelement list by xpath 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 """ 507 try: 508 return self.browser.find_elements_by_xpath(locator_string) 509 except NoSuchElementException: 510 error_msg="Could not find the link: '%s'" 511 self.save_screenshot(error_msg % locator_string) 512 raise RuntimeError(error_msg+ " '%s'" % locator_string)
513
514 - def get_webelements_by_css(self, locator_string):
515 """ 516 Get the webelement list by CSS 517 @param locator_string: String used to locate element 518 @type locator_string: str 519 @param error_msg: Error string to show if element is not found 520 @type error_msg: str 521 """ 522 try: 523 return self.browser.find_elements_by_css_selector(locator_string) 524 except NoSuchElementException: 525 error_msg="Could not find css: '%s'" 526 self.save_screenshot(error_msg % locator_string) 527 raise RuntimeError(error_msg % locator_string)
528