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