Пример #1
0
    def __init__(self):
        if not DriverWrappersPool.is_empty():
            # Copy config object and other properties from default driver
            default_wrapper = DriverWrappersPool.get_default_wrapper()
            self.config = default_wrapper.config.deepcopy()
            self.logger = default_wrapper.logger
            self.config_properties_filenames = default_wrapper.config_properties_filenames
            self.config_log_filename = default_wrapper.config_log_filename
            self.output_log_filename = default_wrapper.output_log_filename
            self.visual_baseline_directory = default_wrapper.visual_baseline_directory
            self.baseline_name = default_wrapper.baseline_name

        # Create utils instance and add wrapper to the pool
        self.utils = Utils(self)
        DriverWrappersPool.add_wrapper(self)
Пример #2
0
    def setUp(self):
        # Reset wrappers pool values
        DriverWrappersPool._empty_pool()
        DriverWrapper.config_properties_filenames = None

        # Create a new wrapper
        self.driver_wrapper = DriverWrappersPool.get_default_wrapper()
        self.driver_wrapper.driver = mock.MagicMock()

        # Configure properties
        self.root_path = os.path.dirname(os.path.realpath(__file__))
        config_files = ConfigFiles()
        config_files.set_config_directory(os.path.join(self.root_path, 'conf'))
        config_files.set_config_properties_filenames('properties.cfg')
        config_files.set_output_directory(os.path.join(self.root_path, 'output'))
        self.driver_wrapper.configure(tc_config_files=config_files)

        # Create a new Utils instance
        self.utils = Utils()
Пример #3
0
    def __init__(self):
        if not DriverWrappersPool.is_empty():
            # Copy config object and other properties from default driver
            default_wrapper = DriverWrappersPool.get_default_wrapper()
            self.config = default_wrapper.config.deepcopy()
            self.logger = default_wrapper.logger
            self.config_properties_filenames = default_wrapper.config_properties_filenames
            self.config_log_filename = default_wrapper.config_log_filename
            self.output_log_filename = default_wrapper.output_log_filename
            self.visual_baseline_directory = default_wrapper.visual_baseline_directory
            self.baseline_name = default_wrapper.baseline_name

        # Create utils instance and add wrapper to the pool
        self.utils = Utils(self)
        DriverWrappersPool.add_wrapper(self)
Пример #4
0
    def setUp(self):
        # Reset wrappers pool values
        DriverWrappersPool._empty_pool()
        DriverWrapper.config_properties_filenames = None

        # Create a new wrapper
        self.driver_wrapper = DriverWrappersPool.get_default_wrapper()
        self.driver_wrapper.driver = mock.MagicMock()

        # Configure properties
        self.root_path = os.path.dirname(os.path.realpath(__file__))
        config_files = ConfigFiles()
        config_files.set_config_directory(os.path.join(self.root_path, 'conf'))
        config_files.set_config_properties_filenames('properties.cfg')
        config_files.set_output_directory(os.path.join(self.root_path, 'output'))
        self.driver_wrapper.configure(tc_config_files=config_files)

        # Create a new Utils instance
        self.utils = Utils()
Пример #5
0
class UtilsTests(unittest.TestCase):
    def setUp(self):
        # Reset wrappers pool values
        DriverWrappersPool._empty_pool()
        DriverWrapper.config_properties_filenames = None

        # Create a new wrapper
        self.driver_wrapper = DriverWrappersPool.get_default_wrapper()
        self.driver_wrapper.driver = mock.MagicMock()

        # Configure properties
        self.root_path = os.path.dirname(os.path.realpath(__file__))
        config_files = ConfigFiles()
        config_files.set_config_directory(os.path.join(self.root_path, 'conf'))
        config_files.set_config_properties_filenames('properties.cfg')
        config_files.set_output_directory(
            os.path.join(self.root_path, 'output'))
        self.driver_wrapper.configure(tc_config_files=config_files)

        # Create a new Utils instance
        self.utils = Utils()

    @classmethod
    def tearDownClass(cls):
        # Reset wrappers pool values
        DriverWrappersPool._empty_pool()
        DriverWrapper.config_properties_filenames = None

    @requests_mock.Mocker()
    def test_get_remote_node(self, req_mock):
        # Configure mock
        self.driver_wrapper.driver.session_id = '5af'
        url = 'http://{}:{}/grid/api/testsession?session={}'.format(
            'localhost', 4444, '5af')
        grid_response_json = {
            'session': 'e2',
            'proxyId': 'http://10.20.30.40:5555',
            'msg': 'slot found !',
            'inactivityTime': 78,
            'success': True,
            'internalKey': '7a'
        }
        req_mock.get(url, json=grid_response_json)

        # Get remote node and check result
        assert_equal(self.utils.get_remote_node(), '10.20.30.40')
        assert_equal(url, req_mock.request_history[0].url)

    @requests_mock.Mocker()
    def test_get_remote_node_non_grid(self, req_mock):
        # Configure mock
        self.driver_wrapper.driver.session_id = '5af'
        url = 'http://{}:{}/grid/api/testsession?session={}'.format(
            'localhost', 4444, '5af')
        req_mock.get(url, text='non_json_response')

        # Get remote node and check result
        assert_equal(self.utils.get_remote_node(), 'localhost')
        assert_equal(url, req_mock.request_history[0].url)

    def test_get_remote_node_local_execution(self):
        self.driver_wrapper.config.set('Server', 'enabled', 'false')
        assert_is_none(self.utils.get_remote_node())

    @requests_mock.Mocker()
    def test_get_remote_video_url(self, req_mock):
        # Configure mock
        url = 'http://{}:{}/video'.format('10.20.30.40', 3000)
        video_url = 'http://{}:{}/download_video/f4.mp4'.format(
            '10.20.30.40', 3000)
        video_response_json = {
            'exit_code':
            1,
            'out': [],
            'error': [
                'Cannot call this endpoint without required parameters: session and action'
            ],
            'available_videos': {
                '5af': {
                    'size': 489701,
                    'session': '5af',
                    'last_modified': 1460041262558,
                    'download_url': video_url,
                    'absolute_path': 'C:\\f4.mp4'
                }
            },
            'current_videos': []
        }
        req_mock.get(url, json=video_response_json)

        # Get remote video url and check result
        assert_equal(self.utils._get_remote_video_url('10.20.30.40', '5af'),
                     video_url)
        assert_equal(url, req_mock.request_history[0].url)

    @requests_mock.Mocker()
    def test_get_remote_video_url_no_videos(self, req_mock):
        # Configure mock
        url = 'http://{}:{}/video'.format('10.20.30.40', 3000)
        video_response_json = {
            'exit_code':
            1,
            'out': [],
            'error': [
                'Cannot call this endpoint without required parameters: session and action'
            ],
            'available_videos': {},
            'current_videos': []
        }
        req_mock.get(url, json=video_response_json)

        # Get remote video url and check result
        assert_is_none(self.utils._get_remote_video_url('10.20.30.40', '5af'))
        assert_equal(url, req_mock.request_history[0].url)

    @requests_mock.Mocker()
    def test_is_remote_video_enabled(self, req_mock):
        # Configure mock
        url = 'http://{}:{}/config'.format('10.20.30.40', 3000)
        config_response_json = {
            'out': [],
            'error': [],
            'exit_code': 0,
            'filename': ['selenium_grid_extras_config.json'],
            'config_runtime': {
                'theConfigMap': {
                    'video_recording_options': {
                        'width': '1024',
                        'videos_to_keep': '5',
                        'frames': '30',
                        'record_test_videos': 'true'
                    }
                }
            }
        }
        req_mock.get(url, json=config_response_json)

        # Get remote video configuration and check result
        assert_equal(self.utils.is_remote_video_enabled('10.20.30.40'), True)
        assert_equal(url, req_mock.request_history[0].url)

    @requests_mock.Mocker()
    def test_is_remote_video_enabled_disabled(self, req_mock):
        # Configure mock
        url = 'http://{}:{}/config'.format('10.20.30.40', 3000)
        config_response_json = {
            'out': [],
            'error': [],
            'exit_code': 0,
            'filename': ['selenium_grid_extras_config.json'],
            'config_runtime': {
                'theConfigMap': {
                    'video_recording_options': {
                        'width': '1024',
                        'videos_to_keep': '5',
                        'frames': '30',
                        'record_test_videos': 'false'
                    }
                }
            }
        }
        req_mock.get(url, json=config_response_json)

        # Get remote video configuration and check result
        assert_equal(self.utils.is_remote_video_enabled('10.20.30.40'), False)
        assert_equal(url, req_mock.request_history[0].url)

    @mock.patch('toolium.utils.requests.get')
    def test_is_remote_video_enabled_non_grid_extras(self, req_get_mock):
        # Configure mock
        req_get_mock.side_effect = ConnectionError('exception error')

        # Get remote video configuration and check result
        assert_equal(self.utils.is_remote_video_enabled('10.20.30.40'), False)

    @data(*navigation_bar_tests)
    @unpack
    def test_get_safari_navigation_bar_height(self, driver_type, appium_app,
                                              appium_browser_name, bar_height):
        self.driver_wrapper.config.set('Driver', 'type', driver_type)
        if appium_app:
            self.driver_wrapper.config.set('AppiumCapabilities', 'app',
                                           appium_app)
        if appium_browser_name:
            self.driver_wrapper.config.set('AppiumCapabilities', 'browserName',
                                           appium_browser_name)
        assert_equal(self.utils.get_safari_navigation_bar_height(), bar_height)

    def test_get_window_size_android_native(self):
        # Configure driver mock
        window_size = {'width': 375, 'height': 667}
        self.driver_wrapper.driver.get_window_size.return_value = window_size
        self.driver_wrapper.config.set('Driver', 'type', 'android')
        self.driver_wrapper.config.set('AppiumCapabilities', 'app',
                                       'C:/Demo.apk')

        assert_equal(self.utils.get_window_size(), window_size)

    def test_get_window_size_android_web(self):
        # Configure driver mock
        window_size = {'width': 375, 'height': 667}
        self.driver_wrapper.driver.current_context = 'WEBVIEW'
        self.driver_wrapper.driver.execute_script.side_effect = [
            window_size['width'], window_size['height']
        ]
        self.driver_wrapper.config.set('Driver', 'type', 'android')
        self.driver_wrapper.config.set('AppiumCapabilities', 'browserName',
                                       'chrome')

        assert_equal(self.utils.get_window_size(), window_size)

    def test_get_native_coords_android_web(self):
        # Configure driver mock
        web_window_size = {'width': 500, 'height': 667}
        native_window_size = {'width': 250, 'height': 450}
        self.driver_wrapper.driver.current_context = 'WEBVIEW'
        self.driver_wrapper.driver.execute_script.side_effect = [
            web_window_size['width'], web_window_size['height'],
            native_window_size['width'], native_window_size['height']
        ]
        self.driver_wrapper.config.set('Driver', 'type', 'android')
        self.driver_wrapper.config.set('AppiumCapabilities', 'browserName',
                                       'chrome')

        web_coords = {'x': 105, 'y': 185}
        native_coords = {'x': 52.5, 'y': 92.5}
        assert_equal(self.utils.get_native_coords(web_coords), native_coords)

    def test_get_native_coords_ios_web(self):
        # Configure driver mock
        web_window_size = {'width': 500, 'height': 667}
        native_window_size = {'width': 250, 'height': 450}
        self.driver_wrapper.driver.get_window_size.side_effect = [
            web_window_size, native_window_size
        ]
        self.driver_wrapper.config.set('Driver', 'type', 'ios')
        self.driver_wrapper.config.set('AppiumCapabilities', 'browserName',
                                       'safari')

        web_coords = {'x': 105, 'y': 185}
        native_coords = {'x': 52.5, 'y': 156.5}
        assert_equal(self.utils.get_native_coords(web_coords), native_coords)

    def test_swipe_android_native(self):
        # Configure driver mock
        web_window_size = {'width': 500, 'height': 667}
        native_window_size = {'width': 250, 'height': 450}
        self.driver_wrapper.driver.get_window_size.side_effect = [
            web_window_size, native_window_size
        ]
        self.driver_wrapper.driver.current_context = 'NATIVE_APP'
        self.driver_wrapper.config.set('Driver', 'type', 'android')
        self.driver_wrapper.config.set('AppiumCapabilities', 'app',
                                       'C:/Demo.apk')

        # Create element mock
        element = get_mock_element(x=250, y=40, height=40, width=300)

        self.utils.swipe(element, 50, 100)
        self.driver_wrapper.driver.swipe.assert_called_once_with(
            400, 60, 450, 160, None)

    def test_swipe_android_web(self):
        # Configure driver mock
        web_window_size = {'width': 500, 'height': 667}
        native_window_size = {'width': 250, 'height': 450}
        self.driver_wrapper.driver.current_context = 'WEBVIEW'
        self.driver_wrapper.driver.execute_script.side_effect = [
            web_window_size['width'], web_window_size['height'],
            native_window_size['width'], native_window_size['height']
        ]
        self.driver_wrapper.config.set('Driver', 'type', 'android')
        self.driver_wrapper.config.set('AppiumCapabilities', 'browserName',
                                       'chrome')

        # Create element mock
        element = get_mock_element(x=250, y=40, height=40, width=300)

        self.utils.swipe(element, 50, 100)
        self.driver_wrapper.driver.swipe.assert_called_once_with(
            200, 30, 250, 130, None)

    def test_swipe_android_hybrid(self):
        # Configure driver mock
        web_window_size = {'width': 500, 'height': 667}
        native_window_size = {'width': 250, 'height': 450}
        self.driver_wrapper.driver.get_window_size.side_effect = [
            web_window_size, native_window_size
        ]
        self.driver_wrapper.driver.current_context = 'WEBVIEW'
        self.driver_wrapper.config.set('Driver', 'type', 'android')
        self.driver_wrapper.config.set('AppiumCapabilities', 'app',
                                       'C:/Demo.apk')

        # Create element mock
        element = get_mock_element(x=250, y=40, height=40, width=300)

        self.utils.swipe(element, 50, 100)
        self.driver_wrapper.driver.swipe.assert_called_once_with(
            200, 30, 250, 130, None)

    def test_swipe_ios_web(self):
        # Configure driver mock
        web_window_size = {'width': 500, 'height': 667}
        native_window_size = {'width': 250, 'height': 450}
        self.driver_wrapper.driver.get_window_size.side_effect = [
            web_window_size, native_window_size
        ]
        self.driver_wrapper.config.set('Driver', 'type', 'ios')
        self.driver_wrapper.config.set('AppiumCapabilities', 'browserName',
                                       'safari')

        # Create element mock
        element = get_mock_element(x=250, y=40, height=40, width=300)

        self.utils.swipe(element, 50, 100)
        self.driver_wrapper.driver.swipe.assert_called_once_with(
            200, 94, 50, 100, None)

    def test_swipe_web(self):
        # Configure driver mock
        self.driver_wrapper.config.set('Driver', 'type', 'firefox')

        # Create element mock
        element = get_mock_element(x=250, y=40, height=40, width=300)

        with assert_raises(Exception) as cm:
            self.utils.swipe(element, 50, 100)
        assert_equal(str(cm.exception),
                     'Swipe method is not implemented in Selenium')

    def test_get_web_element_from_web_element(self):
        element = WebElement(None, 1)
        web_element = self.utils.get_web_element(element)
        assert_equal(element, web_element)

    def test_get_web_element_from_page_element(self):
        element = PageElement(By.ID, 'element_id')
        element._web_element = 'mock_element'

        web_element = self.utils.get_web_element(element)
        assert_equal('mock_element', web_element)

    def test_get_web_element_from_locator(self):
        # Configure driver mock
        self.driver_wrapper.driver.find_element.return_value = 'mock_element'
        element_locator = (By.ID, 'element_id')

        # Get element and assert response
        web_element = self.utils.get_web_element(element_locator)
        assert_equal('mock_element', web_element)
        self.driver_wrapper.driver.find_element.assert_called_once_with(
            *element_locator)

    def test_get_web_element_from_none(self):
        web_element = self.utils.get_web_element(None)
        assert_is_none(web_element)

    def test_get_web_element_from_unknown(self):
        web_element = self.utils.get_web_element(dict())
        assert_is_none(web_element)

    def test_wait_until_first_element_is_found_locator(self):
        # Configure driver mock
        self.driver_wrapper.driver.find_element.return_value = 'mock_element'
        element_locator = (By.ID, 'element_id')

        element = self.utils.wait_until_first_element_is_found(
            [element_locator])

        assert_equal(element_locator, element)
        self.driver_wrapper.driver.find_element.assert_called_once_with(
            *element_locator)

    def test_wait_until_first_element_is_found_page_element(self):
        page_element = PageElement(By.ID, 'element_id')
        page_element._web_element = 'mock_element'

        element = self.utils.wait_until_first_element_is_found([page_element])

        assert_equal(page_element, element)

    def test_wait_until_first_element_is_found_none(self):
        page_element = PageElement(By.ID, 'element_id')
        page_element._web_element = 'mock_element'

        element = self.utils.wait_until_first_element_is_found(
            [None, page_element])

        assert_equal(page_element, element)

    def test_wait_until_first_element_is_found_timeout(self):
        # Configure driver mock
        self.driver_wrapper.driver.find_element.side_effect = NoSuchElementException(
            'Unknown')
        element_locator = (By.ID, 'element_id')

        start_time = time.time()
        with assert_raises(TimeoutException) as cm:
            self.utils.wait_until_first_element_is_found([element_locator],
                                                         timeout=10)
        end_time = time.time()

        assert_in("None of the page elements has been found after 10 seconds",
                  str(cm.exception))
        # find_element has been called more than one time
        self.driver_wrapper.driver.find_element.assert_called_with(
            *element_locator)
        assert_greater(end_time - start_time, 10,
                       'Execution time must be greater than timeout')
Пример #6
0
class DriverWrapper(object):
    """Wrapper with the webdriver and the configuration needed to execute tests

    :type driver: selenium.webdriver.remote.webdriver.WebDriver or appium.webdriver.webdriver.WebDriver
    :type config: toolium.config_parser.ExtendedConfigParser or configparser.ConfigParser
    :type utils: toolium.utils.Utils
    :type app_strings: dict
    :type session_id: str
    :type remote_node: str
    :type remote_node_video_enabled: bool
    :type logger: logging.Logger
    :type config_properties_filenames: str
    :type config_log_filename: str
    :type output_log_filename: str
    :type visual_baseline_directory: str
    :type baseline_name: str
    """
    driver = None  #: webdriver instance
    config = ExtendedConfigParser()  #: driver configuration
    utils = None  #: test utils instance
    app_strings = None  #: mobile application strings
    session_id = None  #: remote webdriver session id
    remote_node = None  #: remote grid node
    remote_node_video_enabled = False  #: True if the remote grid node has the video recorder enabled
    logger = None  #: logger instance

    # Configuration and output files
    config_properties_filenames = None  #: configuration filenames separated by commas
    config_log_filename = None  #: configuration log file
    output_log_filename = None  #: output log file
    visual_baseline_directory = None  #: folder with the baseline images
    baseline_name = None  #: baseline name

    def __init__(self):
        if not DriverWrappersPool.is_empty():
            # Copy config object and other properties from default driver
            default_wrapper = DriverWrappersPool.get_default_wrapper()
            self.config = default_wrapper.config.deepcopy()
            self.logger = default_wrapper.logger
            self.config_properties_filenames = default_wrapper.config_properties_filenames
            self.config_log_filename = default_wrapper.config_log_filename
            self.output_log_filename = default_wrapper.output_log_filename
            self.visual_baseline_directory = default_wrapper.visual_baseline_directory
            self.baseline_name = default_wrapper.baseline_name

        # Create utils instance and add wrapper to the pool
        self.utils = Utils(self)
        DriverWrappersPool.add_wrapper(self)

    def configure_logger(self, tc_config_log_filename=None, tc_output_log_filename=None):
        """Configure selenium instance logger

        :param tc_config_log_filename: test case specific logging config file
        :param tc_output_log_filename: test case specific output logger file
        """
        # Get config logger filename
        config_log_filename = DriverWrappersPool.get_configured_value('Config_log_filename', tc_config_log_filename,
                                                                      'logging.conf')
        config_log_filename = os.path.join(DriverWrappersPool.config_directory, config_log_filename)

        # Configure logger only if logging filename has changed
        if self.config_log_filename != config_log_filename:
            # Get output logger filename
            output_log_filename = DriverWrappersPool.get_configured_value('Output_log_filename', tc_output_log_filename,
                                                                          'toolium.log')
            output_log_filename = os.path.join(DriverWrappersPool.output_directory, output_log_filename)
            output_log_filename = output_log_filename.replace('\\', '\\\\')

            try:
                logging.config.fileConfig(config_log_filename, {'logfilename': output_log_filename}, False)
            except Exception as exc:
                print("[WARN] Error reading logging config file '{}': {}".format(config_log_filename, exc))
            self.config_log_filename = config_log_filename
            self.output_log_filename = output_log_filename
            self.logger = logging.getLogger(__name__)

    def configure_properties(self, tc_config_prop_filenames=None, behave_properties=None):
        """Configure selenium instance properties

        :param tc_config_prop_filenames: test case specific properties filenames
        :param behave_properties: dict with behave user data properties
        """
        prop_filenames = DriverWrappersPool.get_configured_value('Config_prop_filenames', tc_config_prop_filenames,
                                                                 'properties.cfg;local-properties.cfg')
        prop_filenames = [os.path.join(DriverWrappersPool.config_directory, filename) for filename in
                          prop_filenames.split(';')]
        prop_filenames = ';'.join(prop_filenames)

        # Configure config only if properties filename has changed
        if self.config_properties_filenames != prop_filenames:
            # Initialize the config object
            self.config = ExtendedConfigParser.get_config_from_file(prop_filenames)
            self.config_properties_filenames = prop_filenames

        # Override properties with system properties
        self.config.update_properties(os.environ)

        # Override properties with behave userdata properties
        if behave_properties:
            self.config.update_properties(behave_properties)

    def configure_visual_baseline(self):
        """Configure baseline directory"""
        # Get baseline name
        baseline_name = self.config.get_optional('VisualTests', 'baseline_name', '{Driver_type}')
        for section in self.config.sections():
            for option in self.config.options(section):
                option_value = self.config.get(section, option)
                baseline_name = baseline_name.replace('{{{0}_{1}}}'.format(section, option), option_value)

        # Configure baseline directory if baseline name has changed
        if self.baseline_name != baseline_name:
            self.baseline_name = baseline_name
            self.visual_baseline_directory = os.path.join(DriverWrappersPool.visual_baseline_directory,
                                                          get_valid_filename(baseline_name))

    def update_visual_baseline(self):
        """Configure baseline directory after driver is created"""
        # Update baseline with real platformVersion value
        if '{PlatformVersion}' in self.baseline_name:
            try:
                platform_version = self.driver.desired_capabilities['platformVersion']
            except KeyError:
                platform_version = None
            self.baseline_name = self.baseline_name.replace('{PlatformVersion}', str(platform_version))
            self.visual_baseline_directory = os.path.join(DriverWrappersPool.visual_baseline_directory,
                                                          self.baseline_name)

        # Update baseline with real version value
        if '{Version}' in self.baseline_name:
            try:
                splitted_version = self.driver.desired_capabilities['version'].split('.')
                version = '.'.join(splitted_version[:2])
            except KeyError:
                version = None
            self.baseline_name = self.baseline_name.replace('{Version}', str(version))
            self.visual_baseline_directory = os.path.join(DriverWrappersPool.visual_baseline_directory,
                                                          self.baseline_name)

        # Update baseline with remote node value
        if '{RemoteNode}' in self.baseline_name:
            self.baseline_name = self.baseline_name.replace('{RemoteNode}', str(self.remote_node))
            self.visual_baseline_directory = os.path.join(DriverWrappersPool.visual_baseline_directory,
                                                          self.baseline_name)

    def configure(self, tc_config_files, is_selenium_test=True, behave_properties=None):
        """Configure initial selenium instance using logging and properties files for Selenium or Appium tests

        :param tc_config_files: test case specific config files
        :param is_selenium_test: true if test is a selenium or appium test case
        :param behave_properties: dict with behave user data properties
        """
        # Configure config and output directories
        DriverWrappersPool.configure_common_directories(tc_config_files)

        # Configure logger
        self.configure_logger(tc_config_files.config_log_filename, tc_config_files.output_log_filename)

        # Initialize the config object
        self.configure_properties(tc_config_files.config_properties_filenames, behave_properties)

        # Configure visual directories
        if is_selenium_test:
            driver_info = self.config.get('Driver', 'type')
            DriverWrappersPool.configure_visual_directories(driver_info)
            self.configure_visual_baseline()

    def connect(self, maximize=True):
        """Set up the selenium driver and connect to the server

        :param maximize: True if the driver should be maximized
        :returns: selenium driver
        """
        if not self.config.get('Driver', 'type') or self.config.get('Driver', 'type') in ['api', 'no_driver']:
            return None

        self.driver = ConfigDriver(self.config).create_driver()

        # Save session id and remote node to download video after the test execution
        self.session_id = self.driver.session_id
        self.remote_node = self.utils.get_remote_node()
        self.remote_node_video_enabled = self.utils.is_remote_video_enabled(self.remote_node)

        # Save app_strings in mobile tests
        if self.is_mobile_test() and not self.is_web_test() and self.config.getboolean_optional('Driver',
                                                                                                'appium_app_strings'):
            self.app_strings = self.driver.app_strings()

        if self.is_maximizable():
            # Bounds and screen
            bounds_x, bounds_y = self.get_config_window_bounds()
            self.driver.set_window_position(bounds_x, bounds_y)
            self.logger.debug('Window bounds: %s x %s', bounds_x, bounds_y)

            # Maximize browser
            if maximize:
                # Set window size or maximize
                window_width = self.config.get_optional('Driver', 'window_width')
                window_height = self.config.get_optional('Driver', 'window_height')
                if window_width and window_height:
                    self.driver.set_window_size(window_width, window_height)
                else:
                    self.driver.maximize_window()

        # Log window size
        window_size = self.utils.get_window_size()
        self.logger.debug('Window size: %s x %s', window_size['width'], window_size['height'])

        # Update baseline
        self.update_visual_baseline()

        # Discard previous logcat logs
        self.utils.discard_logcat_logs()

        # Set implicitly wait timeout
        self.utils.set_implicitly_wait()

        return self.driver

    def get_config_window_bounds(self):
        """Reads bounds from config and, if monitor is specified, modify the values to match with the specified monitor

        :return: coords X and Y where set the browser window.
        """
        bounds_x = int(self.config.get_optional('Driver', 'bounds_x') or 0)
        bounds_y = int(self.config.get_optional('Driver', 'bounds_y') or 0)

        monitor_index = int(self.config.get_optional('Driver', 'monitor') or -1)
        if monitor_index > -1:
            try:
                monitor = screeninfo.get_monitors()[monitor_index]
                bounds_x += monitor.x
                bounds_y += monitor.y
            except NotImplementedError:
                self.logger.warn('Current environment doesn\'t support get_monitors')

        return bounds_x, bounds_y

    def is_android_test(self):
        """Check if actual test must be executed in an Android mobile

        :returns: True if test must be executed in an Android mobile
        """
        driver_name = self.config.get('Driver', 'type').split('-')[0]
        return driver_name == 'android'

    def is_ios_test(self):
        """Check if actual test must be executed in an iOS mobile

        :returns: True if test must be executed in an iOS mobile
        """
        driver_name = self.config.get('Driver', 'type').split('-')[0]
        return driver_name in ('ios', 'iphone')

    def is_mobile_test(self):
        """Check if actual test must be executed in a mobile

        :returns: True if test must be executed in a mobile
        """
        return self.is_android_test() or self.is_ios_test()

    def is_web_test(self):
        """Check if actual test must be executed in a browser

        :returns: True if test must be executed in a browser
        """
        appium_browser_name = self.config.get_optional('AppiumCapabilities', 'browserName')
        return not self.is_mobile_test() or appium_browser_name not in (None, '')

    def is_android_web_test(self):
        """Check if actual test must be executed in a browser of an Android mobile

        :returns: True if test must be executed in a browser of an Android mobile
        """
        return self.is_android_test() and self.is_web_test()

    def is_ios_web_test(self):
        """Check if actual test must be executed in a browser of an iOS mobile

        :returns: True if test must be executed in a browser of an iOS mobile
        """
        return self.is_ios_test() and self.is_web_test()

    def is_maximizable(self):
        """Check if the browser is maximizable

        :returns: True if the browser is maximizable
        """
        return not self.is_mobile_test()

    def should_reuse_driver(self, scope, test_passed, context=None):
        """Check if the driver should be reused

        :param scope: execution scope (function, module, class or session)
        :param test_passed: True if the test has passed
        :param context: behave context
        :returns: True if the driver should be reused
        """
        reuse_driver = self.config.getboolean_optional('Driver', 'reuse_driver')
        reuse_driver_session = self.config.getboolean_optional('Driver', 'reuse_driver_session')
        restart_driver_after_failure = (self.config.getboolean_optional('Driver', 'restart_driver_after_failure') or
                                        self.config.getboolean_optional('Driver', 'restart_driver_fail'))
        if context and scope == 'function':
            reuse_driver = reuse_driver or (hasattr(context, 'reuse_driver_from_tags')
                                            and context.reuse_driver_from_tags)
        return (((reuse_driver and scope == 'function') or (reuse_driver_session and scope != 'session'))
                and (test_passed or not restart_driver_after_failure))
Пример #7
0
class DriverWrapper(object):
    """Wrapper with the webdriver and the configuration needed to execute tests

    :type driver: selenium.webdriver.remote.webdriver.WebDriver or appium.webdriver.webdriver.WebDriver
    :type config: toolium.config_parser.ExtendedConfigParser or configparser.ConfigParser
    :type utils: toolium.utils.Utils
    :type app_strings: dict
    :type session_id: str
    :type remote_node: str
    :type remote_node_video_enabled: bool
    :type logger: logging.Logger
    :type config_properties_filenames: str
    :type config_log_filename: str
    :type output_log_filename: str
    :type visual_baseline_directory: str
    :type baseline_name: str
    """
    driver = None  #: webdriver instance
    config = ExtendedConfigParser()  #: driver configuration
    utils = None  #: test utils instance
    app_strings = None  #: mobile application strings
    session_id = None  #: remote webdriver session id
    remote_node = None  #: remote grid node
    remote_node_video_enabled = False  #: True if the remote grid node has the video recorder enabled
    logger = None  #: logger instance

    # Configuration and output files
    config_properties_filenames = None  #: configuration filenames separated by commas
    config_log_filename = None  #: configuration log file
    output_log_filename = None  #: output log file
    visual_baseline_directory = None  #: folder with the baseline images
    baseline_name = None  #: baseline name

    def __init__(self):
        if not DriverWrappersPool.is_empty():
            # Copy config object and other properties from default driver
            default_wrapper = DriverWrappersPool.get_default_wrapper()
            self.config = default_wrapper.config.deepcopy()
            self.logger = default_wrapper.logger
            self.config_properties_filenames = default_wrapper.config_properties_filenames
            self.config_log_filename = default_wrapper.config_log_filename
            self.output_log_filename = default_wrapper.output_log_filename
            self.visual_baseline_directory = default_wrapper.visual_baseline_directory
            self.baseline_name = default_wrapper.baseline_name

        # Create utils instance and add wrapper to the pool
        self.utils = Utils(self)
        DriverWrappersPool.add_wrapper(self)

    def configure_logger(self, tc_config_log_filename=None, tc_output_log_filename=None):
        """Configure selenium instance logger

        :param tc_config_log_filename: test case specific logging config file
        :param tc_output_log_filename: test case specific output logger file
        """
        # Get config logger filename
        config_log_filename = DriverWrappersPool.get_configured_value('Config_log_filename', tc_config_log_filename,
                                                                      'logging.conf')
        config_log_filename = os.path.join(DriverWrappersPool.config_directory, config_log_filename)

        # Configure logger only if logging filename has changed
        if self.config_log_filename != config_log_filename:
            # Get output logger filename
            output_log_filename = DriverWrappersPool.get_configured_value('Output_log_filename', tc_output_log_filename,
                                                                          'toolium.log')
            output_log_filename = os.path.join(DriverWrappersPool.output_directory, output_log_filename)
            output_log_filename = output_log_filename.replace('\\', '\\\\')

            try:
                logging.config.fileConfig(config_log_filename, {'logfilename': output_log_filename}, False)
            except Exception as exc:
                print("[WARN] Error reading logging config file '{}': {}".format(config_log_filename, exc))
            self.config_log_filename = config_log_filename
            self.output_log_filename = output_log_filename
            self.logger = logging.getLogger(__name__)

    def configure_properties(self, tc_config_prop_filenames=None, behave_properties=None):
        """Configure selenium instance properties

        :param tc_config_prop_filenames: test case specific properties filenames
        :param behave_properties: dict with behave user data properties
        """
        prop_filenames = DriverWrappersPool.get_configured_value('Config_prop_filenames', tc_config_prop_filenames,
                                                                 'properties.cfg;local-properties.cfg')
        prop_filenames = [os.path.join(DriverWrappersPool.config_directory, filename) for filename in
                          prop_filenames.split(';')]
        prop_filenames = ';'.join(prop_filenames)

        # Configure config only if properties filename has changed
        if self.config_properties_filenames != prop_filenames:
            # Initialize the config object
            self.config = ExtendedConfigParser.get_config_from_file(prop_filenames)
            self.config_properties_filenames = prop_filenames

        # Override properties with system properties
        self.config.update_properties(os.environ)

        # Override properties with behave userdata properties
        if behave_properties:
            self.config.update_properties(behave_properties)

    def configure_visual_baseline(self):
        """Configure baseline directory"""
        # Get baseline name
        baseline_name = self.config.get_optional('VisualTests', 'baseline_name', '{Driver_type}')
        for section in self.config.sections():
            for option in self.config.options(section):
                option_value = self.config.get(section, option).replace('-', '_').replace(' ', '_')
                baseline_name = baseline_name.replace('{{{0}_{1}}}'.format(section, option), option_value)

        # Configure baseline directory if baseline name has changed
        if self.baseline_name != baseline_name:
            self.baseline_name = baseline_name
            self.visual_baseline_directory = os.path.join(DriverWrappersPool.visual_baseline_directory, baseline_name)

    def update_visual_baseline(self):
        """Configure baseline directory after driver is created"""
        # Update baseline with real platformVersion value
        if '{PlatformVersion}' in self.baseline_name:
            try:
                platform_version = self.driver.desired_capabilities['platformVersion']
            except KeyError:
                platform_version = None
            self.baseline_name = self.baseline_name.replace('{PlatformVersion}', str(platform_version))
            self.visual_baseline_directory = os.path.join(DriverWrappersPool.visual_baseline_directory,
                                                          self.baseline_name)

        # Update baseline with real version value
        if '{Version}' in self.baseline_name:
            try:
                splitted_version = self.driver.desired_capabilities['version'].split('.')
                version = '.'.join(splitted_version[:2])
            except KeyError:
                version = None
            self.baseline_name = self.baseline_name.replace('{Version}', str(version))
            self.visual_baseline_directory = os.path.join(DriverWrappersPool.visual_baseline_directory,
                                                          self.baseline_name)

        # Update baseline with remote node value
        if '{RemoteNode}' in self.baseline_name:
            self.baseline_name = self.baseline_name.replace('{RemoteNode}', str(self.remote_node))
            self.visual_baseline_directory = os.path.join(DriverWrappersPool.visual_baseline_directory,
                                                          self.baseline_name)

    def configure(self, is_selenium_test=True, tc_config_files=None, behave_properties=None):
        """Configure initial selenium instance using logging and properties files for Selenium or Appium tests

        :param is_selenium_test: true if test is a selenium or appium test case
        :param tc_config_files: test case specific config files
        :param behave_properties: dict with behave user data properties
        """
        # Initialize config files
        tc_config_files = self._initialize_config_files(tc_config_files)

        # Configure config and output directories
        DriverWrappersPool.configure_common_directories(tc_config_files)

        # Configure logger
        self.configure_logger(tc_config_files.config_log_filename, tc_config_files.output_log_filename)

        # Initialize the config object
        self.configure_properties(tc_config_files.config_properties_filenames, behave_properties)

        # Configure visual directories
        if is_selenium_test:
            driver_info = self.config.get('Driver', 'type').replace('-', '_')
            DriverWrappersPool.configure_visual_directories(driver_info)
            self.configure_visual_baseline()

    @staticmethod
    def _initialize_config_files(tc_config_files=None):
        """Initialize config files and update config files names with the environment

        :param tc_config_files: test case specific config files
        :returns: initialized config files object
        """
        # Initialize config files
        if tc_config_files is None:
            tc_config_files = ConfigFiles()

        # Update properties and log file names if an environment is configured
        env = DriverWrappersPool.get_configured_value('Config_environment', None, None)
        if env:
            # Update config properties filenames
            prop_filenames = tc_config_files.config_properties_filenames
            new_prop_filenames_list = prop_filenames.split(';') if prop_filenames else ['properties.cfg']
            base = new_prop_filenames_list[0].split('.')[0]
            ext = new_prop_filenames_list[0].split('.')[1]
            new_prop_filenames_list.append('{}-{}.{}'.format(env, base, ext))
            new_prop_filenames_list.append('local-{}-{}.{}'.format(env, base, ext))
            tc_config_files.set_config_properties_filenames(*new_prop_filenames_list)

            # Update output log filename
            output_log_filename = tc_config_files.output_log_filename
            base = output_log_filename.split('.')[0] if output_log_filename else 'toolium'
            ext = output_log_filename.split('.')[1] if output_log_filename else 'log'
            tc_config_files.set_output_log_filename('{}_{}.{}'.format(base, env, ext))

        return tc_config_files

    def connect(self, maximize=True):
        """Set up the selenium driver and connect to the server

        :param maximize: True if the driver should be maximized
        :returns: selenium driver
        """
        if not self.config.get('Driver', 'type'):
            return None

        self.driver = ConfigDriver(self.config).create_driver()

        # Save session id and remote node to download video after the test execution
        self.session_id = self.driver.session_id
        self.remote_node = self.utils.get_remote_node()
        self.remote_node_video_enabled = self.utils.is_remote_video_enabled(self.remote_node)

        # Save app_strings in mobile tests
        if self.is_mobile_test() and not self.is_web_test() and self.config.getboolean_optional('Driver',
                                                                                                'appium_app_strings'):
            self.app_strings = self.driver.app_strings()

        # Maximize browser
        if maximize and self.is_maximizable():
            # Set window size or maximize
            window_width = self.config.get_optional('Driver', 'window_width')
            window_height = self.config.get_optional('Driver', 'window_height')
            if window_width and window_height:
                self.driver.set_window_size(window_width, window_height)
            else:
                self.driver.maximize_window()

        # Log window size
        window_size = self.utils.get_window_size()
        self.logger.debug('Window size: %s x %s', window_size['width'], window_size['height'])

        # Update baseline
        self.update_visual_baseline()

        # Discard previous logcat logs
        self.utils.discard_logcat_logs()

        # Set implicitly wait timeout
        self.utils.set_implicitly_wait()

        return self.driver

    def is_android_test(self):
        """Check if actual test must be executed in an Android mobile

        :returns: true if test must be executed in an Android mobile
        """
        driver_name = self.config.get('Driver', 'type').split('-')[0]
        return driver_name == 'android'

    def is_ios_test(self):
        """Check if actual test must be executed in an iOS mobile

        :returns: true if test must be executed in an iOS mobile
        """
        driver_name = self.config.get('Driver', 'type').split('-')[0]
        return driver_name in ('ios', 'iphone')

    def is_mobile_test(self):
        """Check if actual test must be executed in a mobile

        :returns: true if test must be executed in a mobile
        """
        return self.is_android_test() or self.is_ios_test()

    def is_web_test(self):
        """Check if actual test must be executed in a browser

        :returns: true if test must be executed in a browser
        """
        appium_browser_name = self.config.get_optional('AppiumCapabilities', 'browserName')
        return not self.is_mobile_test() or appium_browser_name not in (None, '')

    def is_android_web_test(self):
        """Check if actual test must be executed in a browser of an Android mobile

        :returns: true if test must be executed in a browser of an Android mobile
        """
        return self.is_android_test() and self.is_web_test()

    def is_ios_web_test(self):
        """Check if actual test must be executed in a browser of an iOS mobile

        :returns: true if test must be executed in a browser of an iOS mobile
        """
        return self.is_ios_test() and self.is_web_test()

    def is_maximizable(self):
        """Check if the browser is maximizable

        :returns: true if the browser is maximizable
        """
        return not self.is_mobile_test()
Пример #8
0
class UtilsTests(unittest.TestCase):
    def setUp(self):
        # Reset wrappers pool values
        DriverWrappersPool._empty_pool()
        DriverWrapper.config_properties_filenames = None

        # Create a new wrapper
        self.driver_wrapper = DriverWrappersPool.get_default_wrapper()
        self.driver_wrapper.driver = mock.MagicMock()

        # Configure properties
        self.root_path = os.path.dirname(os.path.realpath(__file__))
        config_files = ConfigFiles()
        config_files.set_config_directory(os.path.join(self.root_path, 'conf'))
        config_files.set_config_properties_filenames('properties.cfg')
        config_files.set_output_directory(os.path.join(self.root_path, 'output'))
        self.driver_wrapper.configure(tc_config_files=config_files)

        # Create a new Utils instance
        self.utils = Utils()

    @classmethod
    def tearDownClass(cls):
        # Reset wrappers pool values
        DriverWrappersPool._empty_pool()
        DriverWrapper.config_properties_filenames = None

    @requests_mock.Mocker()
    def test_get_remote_node(self, req_mock):
        # Configure mock
        self.driver_wrapper.driver.session_id = '5af'
        url = 'http://{}:{}/grid/api/testsession?session={}'.format('localhost', 4444, '5af')
        grid_response_json = {'session': 'e2', 'proxyId': 'http://10.20.30.40:5555', 'msg': 'slot found !',
                              'inactivityTime': 78, 'success': True, 'internalKey': '7a'}
        req_mock.get(url, json=grid_response_json)

        # Get remote node and check result
        assert_equal(self.utils.get_remote_node(), '10.20.30.40')
        assert_equal(url, req_mock.request_history[0].url)

    @requests_mock.Mocker()
    def test_get_remote_node_non_grid(self, req_mock):
        # Configure mock
        self.driver_wrapper.driver.session_id = '5af'
        url = 'http://{}:{}/grid/api/testsession?session={}'.format('localhost', 4444, '5af')
        req_mock.get(url, text='non_json_response')

        # Get remote node and check result
        assert_equal(self.utils.get_remote_node(), 'localhost')
        assert_equal(url, req_mock.request_history[0].url)

    def test_get_remote_node_local_execution(self):
        self.driver_wrapper.config.set('Server', 'enabled', 'false')
        assert_is_none(self.utils.get_remote_node())

    @requests_mock.Mocker()
    def test_get_remote_video_url(self, req_mock):
        # Configure mock
        url = 'http://{}:{}/video'.format('10.20.30.40', 3000)
        video_url = 'http://{}:{}/download_video/f4.mp4'.format('10.20.30.40', 3000)
        video_response_json = {'exit_code': 1, 'out': [],
                               'error': ['Cannot call this endpoint without required parameters: session and action'],
                               'available_videos': {'5af': {'size': 489701,
                                                            'session': '5af',
                                                            'last_modified': 1460041262558,
                                                            'download_url': video_url,
                                                            'absolute_path': 'C:\\f4.mp4'}},
                               'current_videos': []}
        req_mock.get(url, json=video_response_json)

        # Get remote video url and check result
        assert_equal(self.utils._get_remote_video_url('10.20.30.40', '5af'), video_url)
        assert_equal(url, req_mock.request_history[0].url)

    @requests_mock.Mocker()
    def test_get_remote_video_url_no_videos(self, req_mock):
        # Configure mock
        url = 'http://{}:{}/video'.format('10.20.30.40', 3000)
        video_response_json = {'exit_code': 1, 'out': [],
                               'error': ['Cannot call this endpoint without required parameters: session and action'],
                               'available_videos': {},
                               'current_videos': []}
        req_mock.get(url, json=video_response_json)

        # Get remote video url and check result
        assert_is_none(self.utils._get_remote_video_url('10.20.30.40', '5af'))
        assert_equal(url, req_mock.request_history[0].url)

    @requests_mock.Mocker()
    def test_is_remote_video_enabled(self, req_mock):
        # Configure mock
        url = 'http://{}:{}/config'.format('10.20.30.40', 3000)
        config_response_json = {'out': [], 'error': [], 'exit_code': 0,
                                'filename': ['selenium_grid_extras_config.json'],
                                'config_runtime': {'theConfigMap': {
                                    'video_recording_options': {'width': '1024', 'videos_to_keep': '5',
                                                                'frames': '30',
                                                                'record_test_videos': 'true'}}}}
        req_mock.get(url, json=config_response_json)

        # Get remote video configuration and check result
        assert_equal(self.utils.is_remote_video_enabled('10.20.30.40'), True)
        assert_equal(url, req_mock.request_history[0].url)

    @requests_mock.Mocker()
    def test_is_remote_video_enabled_disabled(self, req_mock):
        # Configure mock
        url = 'http://{}:{}/config'.format('10.20.30.40', 3000)
        config_response_json = {'out': [], 'error': [], 'exit_code': 0,
                                'filename': ['selenium_grid_extras_config.json'],
                                'config_runtime': {'theConfigMap': {
                                    'video_recording_options': {'width': '1024', 'videos_to_keep': '5',
                                                                'frames': '30',
                                                                'record_test_videos': 'false'}}}}
        req_mock.get(url, json=config_response_json)

        # Get remote video configuration and check result
        assert_equal(self.utils.is_remote_video_enabled('10.20.30.40'), False)
        assert_equal(url, req_mock.request_history[0].url)

    @mock.patch('toolium.utils.requests.get')
    def test_is_remote_video_enabled_non_grid_extras(self, req_get_mock):
        # Configure mock
        req_get_mock.side_effect = ConnectionError('exception error')

        # Get remote video configuration and check result
        assert_equal(self.utils.is_remote_video_enabled('10.20.30.40'), False)

    @data(*navigation_bar_tests)
    @unpack
    def test_get_safari_navigation_bar_height(self, driver_type, appium_app, appium_browser_name, bar_height):
        self.driver_wrapper.config.set('Driver', 'type', driver_type)
        if appium_app:
            self.driver_wrapper.config.set('AppiumCapabilities', 'app', appium_app)
        if appium_browser_name:
            self.driver_wrapper.config.set('AppiumCapabilities', 'browserName', appium_browser_name)
        assert_equal(self.utils.get_safari_navigation_bar_height(), bar_height)

    def test_get_window_size_android_native(self):
        # Configure driver mock
        window_size = {'width': 375, 'height': 667}
        self.driver_wrapper.driver.get_window_size.return_value = window_size
        self.driver_wrapper.config.set('Driver', 'type', 'android')
        self.driver_wrapper.config.set('AppiumCapabilities', 'app', 'C:/Demo.apk')

        assert_equal(self.utils.get_window_size(), window_size)

    def test_get_window_size_android_web(self):
        # Configure driver mock
        window_size = {'width': 375, 'height': 667}
        self.driver_wrapper.driver.current_context = 'WEBVIEW'
        self.driver_wrapper.driver.execute_script.side_effect = [window_size['width'], window_size['height']]
        self.driver_wrapper.config.set('Driver', 'type', 'android')
        self.driver_wrapper.config.set('AppiumCapabilities', 'browserName', 'chrome')

        assert_equal(self.utils.get_window_size(), window_size)

    def test_get_native_coords_android_web(self):
        # Configure driver mock
        web_window_size = {'width': 500, 'height': 667}
        native_window_size = {'width': 250, 'height': 450}
        self.driver_wrapper.driver.current_context = 'WEBVIEW'
        self.driver_wrapper.driver.execute_script.side_effect = [web_window_size['width'], web_window_size['height'],
                                                                 native_window_size['width'],
                                                                 native_window_size['height']]
        self.driver_wrapper.config.set('Driver', 'type', 'android')
        self.driver_wrapper.config.set('AppiumCapabilities', 'browserName', 'chrome')

        web_coords = {'x': 105, 'y': 185}
        native_coords = {'x': 52.5, 'y': 92.5}
        assert_equal(self.utils.get_native_coords(web_coords), native_coords)

    def test_get_native_coords_ios_web(self):
        # Configure driver mock
        web_window_size = {'width': 500, 'height': 667}
        native_window_size = {'width': 250, 'height': 450}
        self.driver_wrapper.driver.get_window_size.side_effect = [web_window_size, native_window_size]
        self.driver_wrapper.config.set('Driver', 'type', 'ios')
        self.driver_wrapper.config.set('AppiumCapabilities', 'browserName', 'safari')

        web_coords = {'x': 105, 'y': 185}
        native_coords = {'x': 52.5, 'y': 156.5}
        assert_equal(self.utils.get_native_coords(web_coords), native_coords)

    def test_swipe_android_native(self):
        # Configure driver mock
        web_window_size = {'width': 500, 'height': 667}
        native_window_size = {'width': 250, 'height': 450}
        self.driver_wrapper.driver.get_window_size.side_effect = [web_window_size, native_window_size]
        self.driver_wrapper.driver.current_context = 'NATIVE_APP'
        self.driver_wrapper.config.set('Driver', 'type', 'android')
        self.driver_wrapper.config.set('AppiumCapabilities', 'app', 'C:/Demo.apk')

        # Create element mock
        element = get_mock_element(x=250, y=40, height=40, width=300)

        self.utils.swipe(element, 50, 100)
        self.driver_wrapper.driver.swipe.assert_called_once_with(400, 60, 450, 160, None)

    def test_swipe_android_web(self):
        # Configure driver mock
        web_window_size = {'width': 500, 'height': 667}
        native_window_size = {'width': 250, 'height': 450}
        self.driver_wrapper.driver.current_context = 'WEBVIEW'
        self.driver_wrapper.driver.execute_script.side_effect = [web_window_size['width'], web_window_size['height'],
                                                                 native_window_size['width'],
                                                                 native_window_size['height']]
        self.driver_wrapper.config.set('Driver', 'type', 'android')
        self.driver_wrapper.config.set('AppiumCapabilities', 'browserName', 'chrome')

        # Create element mock
        element = get_mock_element(x=250, y=40, height=40, width=300)

        self.utils.swipe(element, 50, 100)
        self.driver_wrapper.driver.swipe.assert_called_once_with(200, 30, 250, 130, None)

    def test_swipe_android_hybrid(self):
        # Configure driver mock
        web_window_size = {'width': 500, 'height': 667}
        native_window_size = {'width': 250, 'height': 450}
        self.driver_wrapper.driver.get_window_size.side_effect = [web_window_size, native_window_size]
        self.driver_wrapper.driver.current_context = 'WEBVIEW'
        self.driver_wrapper.config.set('Driver', 'type', 'android')
        self.driver_wrapper.config.set('AppiumCapabilities', 'app', 'C:/Demo.apk')

        # Create element mock
        element = get_mock_element(x=250, y=40, height=40, width=300)

        self.utils.swipe(element, 50, 100)
        self.driver_wrapper.driver.swipe.assert_called_once_with(200, 30, 250, 130, None)

    def test_swipe_ios_web(self):
        # Configure driver mock
        web_window_size = {'width': 500, 'height': 667}
        native_window_size = {'width': 250, 'height': 450}
        self.driver_wrapper.driver.get_window_size.side_effect = [web_window_size, native_window_size]
        self.driver_wrapper.config.set('Driver', 'type', 'ios')
        self.driver_wrapper.config.set('AppiumCapabilities', 'browserName', 'safari')

        # Create element mock
        element = get_mock_element(x=250, y=40, height=40, width=300)

        self.utils.swipe(element, 50, 100)
        self.driver_wrapper.driver.swipe.assert_called_once_with(200, 94, 50, 100, None)

    def test_swipe_web(self):
        # Configure driver mock
        self.driver_wrapper.config.set('Driver', 'type', 'firefox')

        # Create element mock
        element = get_mock_element(x=250, y=40, height=40, width=300)

        with assert_raises(Exception) as cm:
            self.utils.swipe(element, 50, 100)
        assert_equal(str(cm.exception), 'Swipe method is not implemented in Selenium')

    def test_get_web_element_from_web_element(self):
        element = WebElement(None, 1)
        web_element = self.utils.get_web_element(element)
        assert_equal(element, web_element)

    def test_get_web_element_from_page_element(self):
        element = PageElement(By.ID, 'element_id')
        element._web_element = 'mock_element'

        web_element = self.utils.get_web_element(element)
        assert_equal('mock_element', web_element)

    def test_get_web_element_from_locator(self):
        # Configure driver mock
        self.driver_wrapper.driver.find_element.return_value = 'mock_element'
        element_locator = (By.ID, 'element_id')

        # Get element and assert response
        web_element = self.utils.get_web_element(element_locator)
        assert_equal('mock_element', web_element)
        self.driver_wrapper.driver.find_element.assert_called_once_with(*element_locator)

    def test_get_web_element_from_none(self):
        web_element = self.utils.get_web_element(None)
        assert_is_none(web_element)

    def test_get_web_element_from_unknown(self):
        web_element = self.utils.get_web_element(dict())
        assert_is_none(web_element)

    def test_wait_until_first_element_is_found_locator(self):
        # Configure driver mock
        self.driver_wrapper.driver.find_element.return_value = 'mock_element'
        element_locator = (By.ID, 'element_id')

        element = self.utils.wait_until_first_element_is_found([element_locator])

        assert_equal(element_locator, element)
        self.driver_wrapper.driver.find_element.assert_called_once_with(*element_locator)

    def test_wait_until_first_element_is_found_page_element(self):
        page_element = PageElement(By.ID, 'element_id')
        page_element._web_element = 'mock_element'

        element = self.utils.wait_until_first_element_is_found([page_element])

        assert_equal(page_element, element)

    def test_wait_until_first_element_is_found_none(self):
        page_element = PageElement(By.ID, 'element_id')
        page_element._web_element = 'mock_element'

        element = self.utils.wait_until_first_element_is_found([None, page_element])

        assert_equal(page_element, element)

    def test_wait_until_first_element_is_found_timeout(self):
        # Configure driver mock
        self.driver_wrapper.driver.find_element.side_effect = NoSuchElementException('Unknown')
        element_locator = (By.ID, 'element_id')

        start_time = time.time()
        with assert_raises(TimeoutException) as cm:
            self.utils.wait_until_first_element_is_found([element_locator], timeout=10)
        end_time = time.time()

        assert_in("None of the page elements has been found after 10 seconds", str(cm.exception))
        # find_element has been called more than one time
        self.driver_wrapper.driver.find_element.assert_called_with(*element_locator)
        assert_greater(end_time - start_time, 10, 'Execution time must be greater than timeout')
Пример #9
0
class DriverWrapper(object):
    """Wrapper with the webdriver and the configuration needed to execute tests

    :type driver: selenium.webdriver.remote.webdriver.WebDriver or appium.webdriver.webdriver.WebDriver
    :type config: toolium.config_parser.ExtendedConfigParser or configparser.ConfigParser
    :type utils: toolium.utils.Utils
    :type app_strings: dict
    :type session_id: str
    :type remote_node: str
    :type remote_node_video_enabled: bool
    :type logger: logging.Logger
    :type config_properties_filenames: str
    :type config_log_filename: str
    :type output_log_filename: str
    :type visual_baseline_directory: str
    :type baseline_name: str
    """
    driver = None  #: webdriver instance
    config = ExtendedConfigParser()  #: driver configuration
    utils = None  #: test utils instance
    app_strings = None  #: mobile application strings
    session_id = None  #: remote webdriver session id
    remote_node = None  #: remote grid node
    remote_node_video_enabled = False  #: True if the remote grid node has the video recorder enabled
    logger = None  #: logger instance

    # Configuration and output files
    config_properties_filenames = None  #: configuration filenames separated by commas
    config_log_filename = None  #: configuration log file
    output_log_filename = None  #: output log file
    visual_baseline_directory = None  #: folder with the baseline images
    baseline_name = None  #: baseline name

    def __init__(self):
        if not DriverWrappersPool.is_empty():
            # Copy config object and other properties from default driver
            default_wrapper = DriverWrappersPool.get_default_wrapper()
            self.config = default_wrapper.config.deepcopy()
            self.logger = default_wrapper.logger
            self.config_properties_filenames = default_wrapper.config_properties_filenames
            self.config_log_filename = default_wrapper.config_log_filename
            self.output_log_filename = default_wrapper.output_log_filename
            self.visual_baseline_directory = default_wrapper.visual_baseline_directory
            self.baseline_name = default_wrapper.baseline_name

        # Create utils instance and add wrapper to the pool
        self.utils = Utils(self)
        DriverWrappersPool.add_wrapper(self)

    def configure_logger(self, tc_config_log_filename=None, tc_output_log_filename=None):
        """Configure selenium instance logger

        :param tc_config_log_filename: test case specific logging config file
        :param tc_output_log_filename: test case specific output logger file
        """
        # Get config logger filename
        config_log_filename = DriverWrappersPool.get_configured_value('Config_log_filename', tc_config_log_filename,
                                                                      'logging.conf')
        config_log_filename = os.path.join(DriverWrappersPool.config_directory, config_log_filename)

        # Configure logger only if logging filename has changed
        if self.config_log_filename != config_log_filename:
            # Get output logger filename
            output_log_filename = DriverWrappersPool.get_configured_value('Output_log_filename', tc_output_log_filename,
                                                                          'toolium.log')
            output_log_filename = os.path.join(DriverWrappersPool.output_directory, output_log_filename)
            output_log_filename = output_log_filename.replace('\\', '\\\\')

            try:
                logging.config.fileConfig(config_log_filename, {'logfilename': output_log_filename}, False)
            except Exception as exc:
                print("[WARN] Error reading logging config file '{}': {}".format(config_log_filename, exc))
            self.config_log_filename = config_log_filename
            self.output_log_filename = output_log_filename
            self.logger = logging.getLogger(__name__)

    def configure_properties(self, tc_config_prop_filenames=None):
        """Configure selenium instance properties

        :param tc_config_prop_filenames: test case specific properties filenames
        """
        prop_filenames = DriverWrappersPool.get_configured_value('Config_prop_filenames', tc_config_prop_filenames,
                                                                 'properties.cfg;local-properties.cfg')
        prop_filenames = [os.path.join(DriverWrappersPool.config_directory, filename) for filename in
                          prop_filenames.split(';')]
        prop_filenames = ';'.join(prop_filenames)

        # Configure config only if properties filename has changed
        if self.config_properties_filenames != prop_filenames:
            # Initialize the config object
            self.config = ExtendedConfigParser.get_config_from_file(prop_filenames)
            self.config_properties_filenames = prop_filenames

        # Override properties with system properties
        self.config.update_from_system_properties()

    def configure_visual_baseline(self):
        """Configure baseline directory"""
        # Get baseline name
        baseline_name = self.config.get_optional('VisualTests', 'baseline_name', '{Driver_type}')
        for section in self.config.sections():
            for option in self.config.options(section):
                option_value = self.config.get(section, option).replace('-', '_').replace(' ', '_')
                baseline_name = baseline_name.replace('{{{0}_{1}}}'.format(section, option), option_value)

        # Configure baseline directory if baseline name has changed
        if self.baseline_name != baseline_name:
            self.baseline_name = baseline_name
            self.visual_baseline_directory = os.path.join(DriverWrappersPool.output_directory, 'visualtests',
                                                          'baseline', baseline_name)

    def update_visual_baseline(self):
        """Configure baseline directory after driver is created"""
        # Update baseline with real platformVersion value
        if '{PlatformVersion}' in self.baseline_name:
            try:
                platform_version = self.driver.desired_capabilities['platformVersion']
            except KeyError:
                platform_version = None
            self.baseline_name = self.baseline_name.replace('{PlatformVersion}', str(platform_version))
            self.visual_baseline_directory = os.path.join(DriverWrappersPool.output_directory, 'visualtests',
                                                          'baseline', self.baseline_name)

        # Update baseline with real version value
        if '{Version}' in self.baseline_name:
            try:
                splitted_version = self.driver.desired_capabilities['version'].split('.')
                version = '.'.join(splitted_version[:2])
            except KeyError:
                version = None
            self.baseline_name = self.baseline_name.replace('{Version}', str(version))
            self.visual_baseline_directory = os.path.join(DriverWrappersPool.output_directory, 'visualtests',
                                                          'baseline', self.baseline_name)

        # Update baseline with remote node value
        if '{RemoteNode}' in self.baseline_name:
            self.baseline_name = self.baseline_name.replace('{RemoteNode}', str(self.remote_node))
            self.visual_baseline_directory = os.path.join(DriverWrappersPool.output_directory, 'visualtests',
                                                          'baseline', self.baseline_name)

    def configure(self, is_selenium_test=True, tc_config_files=ConfigFiles()):
        """Configure initial selenium instance using logging and properties files for Selenium or Appium tests

        :param is_selenium_test: true if test is a selenium or appium test case
        :param tc_config_files: test case specific config files
        """
        # Configure config and output directories
        DriverWrappersPool.configure_common_directories(tc_config_files)

        # Configure logger
        self.configure_logger(tc_config_files.config_log_filename, tc_config_files.output_log_filename)

        # Initialize the config object
        self.configure_properties(tc_config_files.config_properties_filenames)

        # Configure visual directories
        if is_selenium_test:
            driver_info = self.config.get('Driver', 'type').replace('-', '_')
            DriverWrappersPool.configure_visual_directories(driver_info)
            self.configure_visual_baseline()

    def connect(self, maximize=True):
        """Set up the selenium driver and connect to the server

        :param maximize: True if the driver should be maximized
        :returns: selenium driver
        """
        if not self.config.get('Driver', 'type'):
            return None

        self.driver = ConfigDriver(self.config).create_driver()

        # Save session id and remote node to download video after the test execution
        self.session_id = self.driver.session_id
        self.remote_node = self.utils.get_remote_node()
        self.remote_node_video_enabled = self.utils.is_remote_video_enabled(self.remote_node)

        # Save app_strings in mobile tests
        if self.is_mobile_test() and not self.is_web_test() and self.config.getboolean_optional('Driver',
                                                                                                'appium_app_strings'):
            self.app_strings = self.driver.app_strings()

        # Maximize browser
        if maximize and self.is_maximizable():
            # Set window size or maximize
            window_width = self.config.get_optional('Driver', 'window_width')
            window_height = self.config.get_optional('Driver', 'window_height')
            if window_width and window_height:
                self.driver.set_window_size(window_width, window_height)
            else:
                self.driver.maximize_window()

        # Log window size
        window_size = self.utils.get_window_size()
        self.logger.debug('Window size: {} x {}'.format(window_size['width'], window_size['height']))

        # Update baseline
        self.update_visual_baseline()

        # Discard previous logcat logs
        self.utils.discard_logcat_logs()

        return self.driver

    def is_android_test(self):
        """Check if actual test must be executed in an Android mobile

        :returns: true if test must be executed in an Android mobile
        """
        driver_name = self.config.get('Driver', 'type').split('-')[0]
        return driver_name == 'android'

    def is_ios_test(self):
        """Check if actual test must be executed in an iOS mobile

        :returns: true if test must be executed in an iOS mobile
        """
        driver_name = self.config.get('Driver', 'type').split('-')[0]
        return driver_name in ('ios', 'iphone')

    def is_mobile_test(self):
        """Check if actual test must be executed in a mobile

        :returns: true if test must be executed in a mobile
        """
        return self.is_android_test() or self.is_ios_test()

    def is_web_test(self):
        """Check if actual test must be executed in a browser

        :returns: true if test must be executed in a browser
        """
        appium_browser_name = self.config.get_optional('AppiumCapabilities', 'browserName')
        return not self.is_mobile_test() or appium_browser_name not in (None, '')

    def is_android_web_test(self):
        """Check if actual test must be executed in a browser of an Android mobile

        :returns: true if test must be executed in a browser of an Android mobile
        """
        return self.is_android_test() and self.is_web_test()

    def is_ios_web_test(self):
        """Check if actual test must be executed in a browser of an iOS mobile

        :returns: true if test must be executed in a browser of an iOS mobile
        """
        return self.is_ios_test() and self.is_web_test()

    def is_maximizable(self):
        """Check if the browser is maximizable

        :returns: true if the browser is maximizable
        """
        return not self.is_mobile_test()
Пример #10
0
def utils():
    # Create a new Utils instance
    return Utils()
Пример #11
0
class DriverWrapper(object):
    """Wrapper with the webdriver and the configuration needed to execute tests

    :type driver: selenium.webdriver.remote.webdriver.WebDriver or appium.webdriver.webdriver.WebDriver
    :type config: toolium.config_parser.ExtendedConfigParser or configparser.ConfigParser
    :type utils: toolium.utils.Utils
    :type app_strings: dict
    :type session_id: str
    :type remote_node: str
    :type remote_node_video_enabled: bool
    :type logger: logging.Logger
    :type config_properties_filenames: str
    :type config_log_filename: str
    :type output_log_filename: str
    :type visual_baseline_directory: str
    :type baseline_name: str
    """
    driver = None  #: webdriver instance
    config = ExtendedConfigParser()  #: driver configuration
    utils = None  #: test utils instance
    app_strings = None  #: mobile application strings
    session_id = None  #: remote webdriver session id
    remote_node = None  #: remote grid node
    remote_node_video_enabled = False  #: True if the remote grid node has the video recorder enabled
    logger = None  #: logger instance

    # Configuration and output files
    config_properties_filenames = None  #: configuration filenames separated by commas
    config_log_filename = None  #: configuration log file
    output_log_filename = None  #: output log file
    visual_baseline_directory = None  #: folder with the baseline images
    baseline_name = None  #: baseline name

    def __init__(self):
        if not DriverWrappersPool.is_empty():
            # Copy config object and other properties from default driver
            default_wrapper = DriverWrappersPool.get_default_wrapper()
            self.config = default_wrapper.config.deepcopy()
            self.logger = default_wrapper.logger
            self.config_properties_filenames = default_wrapper.config_properties_filenames
            self.config_log_filename = default_wrapper.config_log_filename
            self.output_log_filename = default_wrapper.output_log_filename
            self.visual_baseline_directory = default_wrapper.visual_baseline_directory
            self.baseline_name = default_wrapper.baseline_name

        # Create utils instance and add wrapper to the pool
        self.utils = Utils(self)
        DriverWrappersPool.add_wrapper(self)

    def configure_logger(self,
                         tc_config_log_filename=None,
                         tc_output_log_filename=None):
        """Configure selenium instance logger

        :param tc_config_log_filename: test case specific logging config file
        :param tc_output_log_filename: test case specific output logger file
        """
        # Get config logger filename
        config_log_filename = DriverWrappersPool.get_configured_value(
            'Config_log_filename', tc_config_log_filename, 'logging.conf')
        config_log_filename = os.path.join(DriverWrappersPool.config_directory,
                                           config_log_filename)

        # Configure logger only if logging filename has changed
        if self.config_log_filename != config_log_filename:
            # Get output logger filename
            output_log_filename = DriverWrappersPool.get_configured_value(
                'Output_log_filename', tc_output_log_filename, 'toolium.log')
            output_log_filename = os.path.join(
                DriverWrappersPool.output_directory, output_log_filename)
            output_log_filename = output_log_filename.replace('\\', '\\\\')

            try:
                logging.config.fileConfig(config_log_filename,
                                          {'logfilename': output_log_filename},
                                          False)
            except Exception as exc:
                print(
                    "[WARN] Error reading logging config file '{}': {}".format(
                        config_log_filename, exc))
            self.config_log_filename = config_log_filename
            self.output_log_filename = output_log_filename
            self.logger = logging.getLogger(__name__)

    def configure_properties(self,
                             tc_config_prop_filenames=None,
                             behave_properties=None):
        """Configure selenium instance properties

        :param tc_config_prop_filenames: test case specific properties filenames
        :param behave_properties: dict with behave user data properties
        """
        prop_filenames = DriverWrappersPool.get_configured_value(
            'Config_prop_filenames', tc_config_prop_filenames,
            'properties.cfg;local-properties.cfg')
        prop_filenames = [
            os.path.join(DriverWrappersPool.config_directory, filename)
            for filename in prop_filenames.split(';')
        ]
        prop_filenames = ';'.join(prop_filenames)

        # Configure config only if properties filename has changed
        if self.config_properties_filenames != prop_filenames:
            # Initialize the config object
            self.config = ExtendedConfigParser.get_config_from_file(
                prop_filenames)
            self.config_properties_filenames = prop_filenames

        # Override properties with system properties
        self.config.update_properties(os.environ)

        # Override properties with behave userdata properties
        if behave_properties:
            self.config.update_properties(behave_properties)

    def configure_visual_baseline(self):
        """Configure baseline directory"""
        # Get baseline name
        baseline_name = self.config.get_optional('VisualTests',
                                                 'baseline_name',
                                                 '{Driver_type}')
        for section in self.config.sections():
            for option in self.config.options(section):
                option_value = self.config.get(section, option)
                baseline_name = baseline_name.replace(
                    '{{{0}_{1}}}'.format(section, option), option_value)

        # Configure baseline directory if baseline name has changed
        if self.baseline_name != baseline_name:
            self.baseline_name = baseline_name
            self.visual_baseline_directory = os.path.join(
                DriverWrappersPool.visual_baseline_directory,
                get_valid_filename(baseline_name))

    def update_visual_baseline(self):
        """Configure baseline directory after driver is created"""
        # Update baseline with real platformVersion value
        if '{PlatformVersion}' in self.baseline_name:
            try:
                platform_version = self.driver.desired_capabilities[
                    'platformVersion']
            except KeyError:
                platform_version = None
            self.baseline_name = self.baseline_name.replace(
                '{PlatformVersion}', str(platform_version))
            self.visual_baseline_directory = os.path.join(
                DriverWrappersPool.visual_baseline_directory,
                self.baseline_name)

        # Update baseline with real version value
        if '{Version}' in self.baseline_name:
            try:
                splitted_version = self.driver.desired_capabilities[
                    'version'].split('.')
                version = '.'.join(splitted_version[:2])
            except KeyError:
                version = None
            self.baseline_name = self.baseline_name.replace(
                '{Version}', str(version))
            self.visual_baseline_directory = os.path.join(
                DriverWrappersPool.visual_baseline_directory,
                self.baseline_name)

        # Update baseline with remote node value
        if '{RemoteNode}' in self.baseline_name:
            self.baseline_name = self.baseline_name.replace(
                '{RemoteNode}', str(self.remote_node))
            self.visual_baseline_directory = os.path.join(
                DriverWrappersPool.visual_baseline_directory,
                self.baseline_name)

    def configure(self,
                  tc_config_files,
                  is_selenium_test=True,
                  behave_properties=None):
        """Configure initial selenium instance using logging and properties files for Selenium or Appium tests

        :param tc_config_files: test case specific config files
        :param is_selenium_test: true if test is a selenium or appium test case
        :param behave_properties: dict with behave user data properties
        """
        # Configure config and output directories
        DriverWrappersPool.configure_common_directories(tc_config_files)

        # Configure logger
        self.configure_logger(tc_config_files.config_log_filename,
                              tc_config_files.output_log_filename)

        # Initialize the config object
        self.configure_properties(tc_config_files.config_properties_filenames,
                                  behave_properties)

        # Configure visual directories
        if is_selenium_test:
            driver_info = self.config.get('Driver', 'type')
            DriverWrappersPool.configure_visual_directories(driver_info)
            self.configure_visual_baseline()

    def connect(self, maximize=True):
        """Set up the selenium driver and connect to the server

        :param maximize: True if the driver should be maximized
        :returns: selenium driver
        """
        if not self.config.get('Driver', 'type') or self.config.get(
                'Driver', 'type') in ['api', 'no_driver']:
            return None

        self.driver = ConfigDriver(self.config).create_driver()

        # Save session id and remote node to download video after the test execution
        self.session_id = self.driver.session_id
        self.remote_node = self.utils.get_remote_node()
        self.remote_node_video_enabled = self.utils.is_remote_video_enabled(
            self.remote_node)

        # Save app_strings in mobile tests
        if self.is_mobile_test(
        ) and not self.is_web_test() and self.config.getboolean_optional(
                'Driver', 'appium_app_strings'):
            self.app_strings = self.driver.app_strings()

        if self.is_maximizable():
            # Bounds and screen
            bounds_x, bounds_y = self.get_config_window_bounds()
            self.driver.set_window_position(bounds_x, bounds_y)
            self.logger.debug('Window bounds: %s x %s', bounds_x, bounds_y)

            # Maximize browser
            if maximize:
                # Set window size or maximize
                window_width = self.config.get_optional(
                    'Driver', 'window_width')
                window_height = self.config.get_optional(
                    'Driver', 'window_height')
                if window_width and window_height:
                    self.driver.set_window_size(window_width, window_height)
                else:
                    self.driver.maximize_window()

        # Log window size
        window_size = self.utils.get_window_size()
        self.logger.debug('Window size: %s x %s', window_size['width'],
                          window_size['height'])

        # Update baseline
        self.update_visual_baseline()

        # Discard previous logcat logs
        self.utils.discard_logcat_logs()

        # Set implicitly wait timeout
        self.utils.set_implicitly_wait()

        return self.driver

    def get_config_window_bounds(self):
        """Reads bounds from config and, if monitor is specified, modify the values to match with the specified monitor

        :return: coords X and Y where set the browser window.
        """
        bounds_x = int(self.config.get_optional('Driver', 'bounds_x') or 0)
        bounds_y = int(self.config.get_optional('Driver', 'bounds_y') or 0)

        monitor_index = int(
            self.config.get_optional('Driver', 'monitor') or -1)
        if monitor_index > -1:
            try:
                monitor = screeninfo.get_monitors()[monitor_index]
                bounds_x += monitor.x
                bounds_y += monitor.y
            except NotImplementedError:
                self.logger.warn(
                    'Current environment doesn\'t support get_monitors')

        return bounds_x, bounds_y

    def is_android_test(self):
        """Check if actual test must be executed in an Android mobile

        :returns: True if test must be executed in an Android mobile
        """
        driver_name = self.config.get('Driver', 'type').split('-')[0]
        return driver_name == 'android'

    def is_ios_test(self):
        """Check if actual test must be executed in an iOS mobile

        :returns: True if test must be executed in an iOS mobile
        """
        driver_name = self.config.get('Driver', 'type').split('-')[0]
        return driver_name in ('ios', 'iphone')

    def is_mobile_test(self):
        """Check if actual test must be executed in a mobile

        :returns: True if test must be executed in a mobile
        """
        return self.is_android_test() or self.is_ios_test()

    def is_web_test(self):
        """Check if actual test must be executed in a browser

        :returns: True if test must be executed in a browser
        """
        appium_browser_name = self.config.get_optional('AppiumCapabilities',
                                                       'browserName')
        return not self.is_mobile_test() or appium_browser_name not in (None,
                                                                        '')

    def is_android_web_test(self):
        """Check if actual test must be executed in a browser of an Android mobile

        :returns: True if test must be executed in a browser of an Android mobile
        """
        return self.is_android_test() and self.is_web_test()

    def is_ios_web_test(self):
        """Check if actual test must be executed in a browser of an iOS mobile

        :returns: True if test must be executed in a browser of an iOS mobile
        """
        return self.is_ios_test() and self.is_web_test()

    def is_maximizable(self):
        """Check if the browser is maximizable

        :returns: True if the browser is maximizable
        """
        return not self.is_mobile_test()

    def should_reuse_driver(self, scope, test_passed, context=None):
        """Check if the driver should be reused

        :param scope: execution scope (function, module, class or session)
        :param test_passed: True if the test has passed
        :param context: behave context
        :returns: True if the driver should be reused
        """
        reuse_driver = self.config.getboolean_optional('Driver',
                                                       'reuse_driver')
        reuse_driver_session = self.config.getboolean_optional(
            'Driver', 'reuse_driver_session')
        restart_driver_after_failure = (self.config.getboolean_optional(
            'Driver', 'restart_driver_after_failure')
                                        or self.config.getboolean_optional(
                                            'Driver', 'restart_driver_fail'))
        if context and scope == 'function':
            reuse_driver = reuse_driver or (hasattr(context,
                                                    'reuse_driver_from_tags')
                                            and context.reuse_driver_from_tags)
        return (((reuse_driver and scope == 'function') or
                 (reuse_driver_session and scope != 'session'))
                and (test_passed or not restart_driver_after_failure))