xref: /OK3568_Linux_fs/yocto/poky/bitbake/lib/toaster/tests/browser/selenium_helpers_base.py (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1#! /usr/bin/env python3
2#
3# BitBake Toaster Implementation
4#
5# Copyright (C) 2013-2016 Intel Corporation
6#
7# SPDX-License-Identifier: GPL-2.0-only
8#
9# The Wait class and some of SeleniumDriverHelper and SeleniumTestCase are
10# modified from Patchwork, released under the same licence terms as Toaster:
11# https://github.com/dlespiau/patchwork/blob/master/patchwork/tests.browser.py
12
13"""
14Helper methods for creating Toaster Selenium tests which run within
15the context of Django unit tests.
16"""
17
18import os
19import time
20import unittest
21
22from selenium import webdriver
23from selenium.webdriver.support.ui import WebDriverWait
24from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
25from selenium.common.exceptions import NoSuchElementException, \
26        StaleElementReferenceException, TimeoutException
27
28def create_selenium_driver(cls,browser='chrome'):
29    # set default browser string based on env (if available)
30    env_browser = os.environ.get('TOASTER_TESTS_BROWSER')
31    if env_browser:
32        browser = env_browser
33
34    if browser == 'chrome':
35        return webdriver.Chrome(
36            service_args=["--verbose", "--log-path=selenium.log"]
37        )
38    elif browser == 'firefox':
39        return webdriver.Firefox()
40    elif browser == 'marionette':
41        capabilities = DesiredCapabilities.FIREFOX
42        capabilities['marionette'] = True
43        return webdriver.Firefox(capabilities=capabilities)
44    elif browser == 'ie':
45        return webdriver.Ie()
46    elif browser == 'phantomjs':
47        return webdriver.PhantomJS()
48    elif browser == 'remote':
49        # if we were to add yet another env variable like TOASTER_REMOTE_BROWSER
50        # we could let people pick firefox or chrome, left for later
51        remote_hub= os.environ.get('TOASTER_REMOTE_HUB')
52        driver = webdriver.Remote(remote_hub,
53                                  webdriver.DesiredCapabilities.FIREFOX.copy())
54
55        driver.get("http://%s:%s"%(cls.server_thread.host,cls.server_thread.port))
56        return driver
57    else:
58        msg = 'Selenium driver for browser %s is not available' % browser
59        raise RuntimeError(msg)
60
61class Wait(WebDriverWait):
62    """
63    Subclass of WebDriverWait with predetermined timeout and poll
64    frequency. Also deals with a wider variety of exceptions.
65    """
66    _TIMEOUT = 10
67    _POLL_FREQUENCY = 0.5
68
69    def __init__(self, driver):
70        super(Wait, self).__init__(driver, self._TIMEOUT, self._POLL_FREQUENCY)
71
72    def until(self, method, message=''):
73        """
74        Calls the method provided with the driver as an argument until the
75        return value is not False.
76        """
77
78        end_time = time.time() + self._timeout
79        while True:
80            try:
81                value = method(self._driver)
82                if value:
83                    return value
84            except NoSuchElementException:
85                pass
86            except StaleElementReferenceException:
87                pass
88
89            time.sleep(self._poll)
90            if time.time() > end_time:
91                break
92
93        raise TimeoutException(message)
94
95    def until_not(self, method, message=''):
96        """
97        Calls the method provided with the driver as an argument until the
98        return value is False.
99        """
100
101        end_time = time.time() + self._timeout
102        while True:
103            try:
104                value = method(self._driver)
105                if not value:
106                    return value
107            except NoSuchElementException:
108                return True
109            except StaleElementReferenceException:
110                pass
111
112            time.sleep(self._poll)
113            if time.time() > end_time:
114                break
115
116        raise TimeoutException(message)
117
118class SeleniumTestCaseBase(unittest.TestCase):
119    """
120    NB StaticLiveServerTestCase is used as the base test case so that
121    static files are served correctly in a Selenium test run context; see
122    https://docs.djangoproject.com/en/1.9/ref/contrib/staticfiles/#specialized-test-case-to-support-live-testing
123    """
124
125    @classmethod
126    def setUpClass(cls):
127        """ Create a webdriver driver at the class level """
128
129        super(SeleniumTestCaseBase, cls).setUpClass()
130
131        # instantiate the Selenium webdriver once for all the test methods
132        # in this test case
133        cls.driver = create_selenium_driver(cls)
134        cls.driver.maximize_window()
135
136    @classmethod
137    def tearDownClass(cls):
138        """ Clean up webdriver driver """
139
140        cls.driver.quit()
141        super(SeleniumTestCaseBase, cls).tearDownClass()
142
143    def get(self, url):
144        """
145        Selenium requires absolute URLs, so convert Django URLs returned
146        by resolve() or similar to absolute ones and get using the
147        webdriver instance.
148
149        url: a relative URL
150        """
151        abs_url = '%s%s' % (self.live_server_url, url)
152        self.driver.get(abs_url)
153
154    def find(self, selector):
155        """ Find single element by CSS selector """
156        return self.driver.find_element_by_css_selector(selector)
157
158    def find_all(self, selector):
159        """ Find all elements matching CSS selector """
160        return self.driver.find_elements_by_css_selector(selector)
161
162    def element_exists(self, selector):
163        """
164        Return True if one element matching selector exists,
165        False otherwise
166        """
167        return len(self.find_all(selector)) == 1
168
169    def focused_element(self):
170        """ Return the element which currently has focus on the page """
171        return self.driver.switch_to.active_element
172
173    def wait_until_present(self, selector):
174        """ Wait until element matching CSS selector is on the page """
175        is_present = lambda driver: self.find(selector)
176        msg = 'An element matching "%s" should be on the page' % selector
177        element = Wait(self.driver).until(is_present, msg)
178        return element
179
180    def wait_until_visible(self, selector):
181        """ Wait until element matching CSS selector is visible on the page """
182        is_visible = lambda driver: self.find(selector).is_displayed()
183        msg = 'An element matching "%s" should be visible' % selector
184        Wait(self.driver).until(is_visible, msg)
185        return self.find(selector)
186
187    def wait_until_focused(self, selector):
188        """ Wait until element matching CSS selector has focus """
189        is_focused = \
190            lambda driver: self.find(selector) == self.focused_element()
191        msg = 'An element matching "%s" should be focused' % selector
192        Wait(self.driver).until(is_focused, msg)
193        return self.find(selector)
194
195    def enter_text(self, selector, value):
196        """ Insert text into element matching selector """
197        # note that keyup events don't occur until the element is clicked
198        # (in the case of <input type="text"...>, for example), so simulate
199        # user clicking the element before inserting text into it
200        field = self.click(selector)
201
202        field.send_keys(value)
203        return field
204
205    def click(self, selector):
206        """ Click on element which matches CSS selector """
207        element = self.wait_until_visible(selector)
208        element.click()
209        return element
210
211    def get_page_source(self):
212        """ Get raw HTML for the current page """
213        return self.driver.page_source
214