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
    163                  #Generate HTML based on template
    164                  html = self._tc_screenshot_template.render(test_case_id=self._test_case_id,
    165                                                             test_case_name=self._test_case_name,
    166                                                             screenshots=self._screenshots)
    167
    168                  #Filename includes a counter in case test case is being run more than once
    169                  htm_file = FileHelper.get_filename_from_string(name) + '.htm'
    170                  relative_filename = os.path.join(self._folder_dest, htm_file)
    171                  full_filename = os.path.join(self.screenshot_folder(), relative_filename)
    172
    173                  self._screenshot_report[name] = {"tc_id": self._test_case_id,
    174                                                   "tc_name": self._test_case_name,
    175                                                   "filename": relative_filename}
    176
    177                  f = open(full_filename, "w")
    178                  try:
    179                      f.write(html)
    180                  finally:
    181                      f.close()
    182
    183          request.addfinalizer(generate_report)
    184
    185 -    def save_screenshot(self, description):
    186          """
    187          Saves screen shot for the current page
    188          """
    189          #Waits until page is loaded
    190          self.tlib_logger.debug("Saving screenshot")
    191          self.wait_for_page_loaded()
    192
    193          #Folder containing images for test case
    194          name = self._get_unused_report_name(self._test_case_id, self._test_params)
    195          name = FileHelper.get_filename_from_string(name)
    196
    197          test_folder = os.path.join(self.screenshot_folder(), self._folder_dest, name)
    198
    199          try:
    200              os.makedirs(test_folder)
    201          except WindowsError:
    202              pass
    203
    204          #Get number of existing images
    205          i = len(os.listdir(test_folder)) + 1
    206
    207          #Get filename
    208          img_filename = "%02d.png" % i
    209          full_path = os.path.join(test_folder, img_filename)
    210
    211          self._screenshots[i] = {"filename": "%s/%s" % (name, img_filename),
    212                                  u"description": description}
    213
    214          self._driver.get_screenshot_as_file(full_path)
    215
    216 -    def wait_for_page_loaded(self, timeout=10):
    217          raise NotImplementedError("This method should be implemented by derived classes")
    218
    219 -    def wait_for_alert(self, timeout=10):
    220          """
    221          Waist until an alert is visible
    222          @type timeout: Integer
    223          @param timeout: Number of seconds before timing out
    224          @rtype: bool
    225          """
    226          def is_alert_visible():
    227              try:
    228                  #Try to get alert text to trigger exception if alert is not visible
    229                  alert = self._driver.switch_to_alert().text
    230                  return True
    231              except NoAlertPresentException as e:
    232                  return False
    233
    234          condition = lambda *args: is_alert_visible()
    235          try:
    236              WebDriverWait(self._driver, timeout).until(condition)
    237              return self._driver.switch_to_alert()
    238          except TimeoutException:
    239              self.test_logger.error('Timeout while waiting for alert to appear')
    240              pytest.fail('Timeout while waiting for alert to appear')
    241
    242 -    def wait_for_element_to_be_visible(self, locator_strategy, locator_string, error_msg=None, timeout=10):
    243          """
    244          Wait until an element becomes visible
    245          @param locator_strategy: Location strategy to use
    246          @type locator_strategy: By
    247          @param locator_string: String used to locate element
    248          @type locator_string: str
    249          @param error_msg: Error string to show if element is not found
    250          @type error_msg: str
    251          @param timeout: Maximum time in seconds to wait for the element to be visible
    252          @type timeout: int
    253          @rtype: WebElement
    254          """
    255          try:
    256              element = WebDriverWait(self._driver, timeout).\
    257                  until(expected_conditions.visibility_of_element_located((locator_strategy, locator_string)))
    258              return element
    259          except TimeoutException:
    260              if error_msg is None:
    261                  error_msg = "Timeout while waiting for element '%s' to be visible" % locator_string
    262              self.save_screenshot("[ERROR] %s" % error_msg)
    263              pytest.fail(error_msg)
    264
    265 -    def wait_for_element_to_be_clickable(self, locator_strategy, locator_string, error_msg=None, timeout=10):
    266          """
    267          Wait until an element cna be clicked
    268          @param locator_strategy: Location strategy to use
    269          @type locator_strategy: By
    270          @param locator_string: String used to locate element
    271          @type locator_string: str
    272          @param error_msg: Error string to show if element is not found
    273          @type error_msg: str
    274          @param timeout: Maximum time in seconds to wait for the element to be clickable
    275          @type timeout: int
    276          @rtype: WebElement
    277          """
    278          try:
    279              element = WebDriverWait(self._driver, timeout).\
    280                  until(expected_conditions.element_to_be_clickable((locator_strategy, locator_string)))
    281              return element
    282          except TimeoutException:
    283              if error_msg is None:
    284                  error_msg = "Timeout while waiting for element '%s' to be clickable" % locator_string
    285              self.save_screenshot("[ERROR] %s" % error_msg)
    286              pytest.fail(error_msg)
    287
    288 -    def wait_for_element_to_be_present(self, locator_strategy, locator_string, error_msg=None, timeout=10):
    289          """
    290          Wait until an element is present
    291          @param locator_strategy: Location strategy to use
    292          @type locator_strategy: By
    293          @param locator_string: String used to locate element
    294          @type locator_string: str
    295          @param error_msg: Error string to show if element is not found
    296          @type error_msg: str
    297          @param timeout: Maximum time in seconds to wait for the element to be present
    298          @type timeout: int
    299          @rtype: WebElement
    300          """
    301          try:
    302              element = WebDriverWait(self._driver, timeout).\
    303                  until(expected_conditions.presence_of_element_located((locator_strategy, locator_string)))
    304              return element
    305          except TimeoutException:
    306              if error_msg is None:
    307                  error_msg = "Timeout while waiting for element '%s' to be present" % locator_string
    308              self.save_screenshot("[ERROR] %s" % error_msg)
    309              pytest.fail(error_msg)
    310
    311 -    def wait_for_element_to_be_selected(self, locator_strategy, locator_string, error_msg=None, timeout=10):
    312          """
    313          Wait until an element is selected
    314          @param locator_strategy: Location strategy to use
    315          @type locator_strategy: By
    316          @param locator_string: String used to locate element
    317          @type locator_string: str
    318          @param error_msg: Error string to show if element is not found
    319          @type error_msg: str
    320          @param timeout: Maximum time in seconds to wait for the element to be selected
    321          @type timeout: int
    322          @rtype: WebElement
    323          """
    324          try:
    325              element = WebDriverWait(self._driver, timeout).\
    326                  until(expected_conditions.element_located_to_be_selected((locator_strategy, locator_string)))
    327              return element
    328          except TimeoutException:
    329              if error_msg is None:
    330                  error_msg = "Timeout while waiting for element '%s' to be selected" % locator_string
    331              self.save_screenshot("[ERROR] %s" % error_msg)
    332              pytest.fail(error_msg)
    333
    334 -    def wait_for_element_to_be_invisible(self, locator_strategy, locator_string, error_msg=None, timeout=10):
    335          """
    336          Wait until an element becomes invisible
    337          @param locator_strategy: Location strategy to use
    338          @type locator_strategy: By
    339          @param locator_string: String used to locate element
    340          @type locator_string: str
    341          @param error_msg: Error string to show if element is not found
    342          @type error_msg: str
    343          @param timeout: Maximum time in seconds to wait for the element to be hidden
    344          @type timeout: int
    345          @rtype: WebElement
    346          """
    347          try:
    348              element = WebDriverWait(self._driver, timeout).\
    349                  until(expected_conditions.invisibility_of_element_located((locator_strategy, locator_string)))
    350              return element
    351          except TimeoutException:
    352              if error_msg is None:
    353                  error_msg = "Timeout while waiting for element '%s' to be invisible" % locator_string
    354              self.save_screenshot("[ERROR] %s" % error_msg)
    355              pytest.fail(error_msg)
    356
    357 -    def wait_for_element_to_be_static(self, locator_strategy, locator_string, error_msg=None, timeout=10):
    358          """
    359          Wait until an element that moves on the screen stops moving
    360          @param locator_strategy: Location strategy to use
    361          @type locator_strategy: By
    362          @param locator_string: String used to locate element
    363          @type locator_string: str
    364          @param error_msg: Error string to show if element is not found
    365          @type error_msg: str
    366          @param timeout: Maximum time in seconds to wait for the element to be visible
    367          @type timeout: int
    368          @rtype: WebElement
    369          """
    370          try:
    371              element = WebDriverWait(self._driver, timeout).\
    372                  until(expected_conditions.visibility_of_element_located((locator_strategy, locator_string)))
    373
    374              #wait until element is not moving or click will fail
    375              old_location = {'x': 0, 'y': 0}
    376              while old_location != element.location:
    377                  self.tlib_logger.debug("Pop-up is still moving. Previous position: %s, current position: %s" %
    378                                         (old_location, element.location))
    379                  old_location = element.location
    380                  sleep(0.1)
    381                  element = self._driver.find_element(locator_strategy, locator_string)
    382
    383              return element
    384          except TimeoutException:
    385              if error_msg is None:
    386                  error_msg = "Timeout while waiting for element '%s' to be visible" % locator_string
    387              self.save_screenshot("[ERROR] %s" % error_msg)
    388              pytest.fail(error_msg)
    389
    390 -    def wait_for_text_to_be_present_in_element(self, locator_strategy, locator_string, text,
    391                                                 error_msg=None, timeout=10):
    392          """
    393          Wait for an element that contains specified text
    394          @param locator_strategy: Location strategy to use
    395          @type locator_strategy: By
    396          @param locator_string: String used to locate element
    397          @type locator_string: str
    398          @param error_msg: Error string to show if element is not found
    399          @type error_msg: str
    400          @param timeout: Maximum time in seconds to wait
    401          @type timeout: int
    402          """
    403          try:
    404              WebDriverWait(self._driver, timeout).\
    405                  until(expected_conditions.text_to_be_present_in_element((locator_strategy, locator_string), text))
    406          except TimeoutException:
    407              if error_msg is None:
    408                  error_msg = "Timeout while waiting for text %(text)s to be present in element '%(element)s'" % \
    409                              {"text": text, "element": locator_string}
    410              self.save_screenshot("[ERROR] %s" % error_msg)
    411              pytest.fail(error_msg)
    412
    413 -    def wait_for_text_to_be_present_in_element_value(self, locator_strategy, locator_string, text,
    414                                                       error_msg=None, timeout=10):
    415          """
    416          Wait for an element's value to contain some test
    417          @param locator_strategy: Location strategy to use
    418          @type locator_strategy: By
    419          @param locator_string: String used to locate element
    420          @type locator_string: str
    421          @param error_msg: Error string to show if element is not found
    422          @type error_msg: str
    423          @param timeout: Maximum time in seconds to wait
    424          @type timeout: int
    425          """
    426          try:
    427              WebDriverWait(self._driver, timeout).\
    428                  until(expected_conditions.text_to_be_present_in_element_value((locator_strategy, locator_string), text))
    429          except TimeoutException:
    430              if error_msg is None:
    431                  error_msg = "Timeout while waiting for text %(text)s to be present " \
    432                              "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
    436
    437 -    def get_webelement_by_link_text(self, locator_string):
    438          """
    439          Get the webelement by link text
    440          @param locator_string: String used to locate element
    441          @type locator_string: str
    442          @param error_msg: Error string to show if element is not found
    443          @type error_msg: str
    444          """
    445          try:
    446              return self.browser.find_element_by_link_text(locator_string)
    447          except NoSuchElementException:
    448              error_msg="Could not find the link: '%s'"
    449              self.save_screenshot(error_msg % locator_string)
    450              pytest.fail(error_msg % locator_string)
    451
    452 -    def get_webelement_by_xpath(self, locator_string):
    453          """
    454          Get the webelement by xpath
    455          @param locator_string: String used to locate element
    456          @type locator_string: str
    457          @param error_msg: Error string to show if element is not found
    458          @type error_msg: str
    459          """
    460          try:
    461              self.wait_for_element_to_be_visible(By.XPATH, locator_string)
    462              return self.browser.find_element_by_xpath(locator_string)
    463          except NoSuchElementException:
    464              error_msg="Could not find the xpath: '%s'"
    465              self.save_screenshot(error_msg % locator_string)
    466              pytest.fail(error_msg % locator_string)
    467      #
    468 -    def get_webelement_by_css(self, locator_string):
    469          """
    470          Get the webelement by CSS
    471          @param locator_string: String used to locate element
    472          @type locator_string: str
    473          @param error_msg: Error string to show if element is not found
    474          @type error_msg: str
    475          """
    476          try:
    477              self.wait_for_element_to_be_visible(By.CSS_SELECTOR, locator_string)
    478              return self.browser.find_element_by_css_selector(locator_string)
    479          except NoSuchElementException:
    480              error_msg="Could not find css: '%s'"
    481              self.save_screenshot(error_msg % locator_string)
    482              pytest.fail(error_msg %locator_string)
    483      #
    484 -    def get_webelements_by_xpath(self, locator_string):
    485          """
    486          Get the webelement list by xpath
    487          @param locator_string: String used to locate element
    488          @type locator_string: str
    489          @param error_msg: Error string to show if element is not found
    490          @type error_msg: str
    491          """
    492          try:
    493              return  self.browser.find_elements_by_xpath(locator_string)
    494          except NoSuchElementException:
    495              error_msg="Could not find the link: '%s'"
    496              self.save_screenshot(error_msg % locator_string)
    497              pytest.fail(error_msg+ " '%s'" % locator_string)
    498      #
    499 -    def get_webelements_by_css(self, locator_string):
    500          """
    501          Get the webelement list by CSS
    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_css_selector(locator_string)
    509          except NoSuchElementException:
    510              error_msg="Could not find css: '%s'"
    511              self.save_screenshot(error_msg % locator_string)
    512              pytest.fail(error_msg % locator_string)
    513