diff --git a/tests/selenium/__init__.py b/tests/selenium/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/selenium/seleniumtestexceptions.py b/tests/selenium/seleniumtestexceptions.py new file mode 100644 index 00000000..01a4a0de --- /dev/null +++ b/tests/selenium/seleniumtestexceptions.py @@ -0,0 +1,47 @@ +class BadJobException(Exception): + '''Raise when a started job doesn't result in ID''' + def __init__(self, msg=None): + msg = 'Job ID could not be saved after starting the job.' + super(BadJobException, self).__init__(msg) + +class ClientStatusException(Exception): + '''Raise when a client does not have the expected status''' + def __init__(self,client, status, msg=None): + if status=='enabled': + msg = '%s is enabled and cannot be enabled again.' % client + if status=='disabled': + msg = '%s is disabled and cannot be disabled again.' % client + super(ClientStatusException, self).__init__(msg) + +class ClientNotFoundException(Exception): + '''Raise when the expected client is not found''' + def __init__(self, client, msg=None): + msg = 'The client %s was not found.' % client + super(ClientNotFoundException, self).__init__(msg) + +class ElementCoveredException(Exception): + '''Raise when an element is covered by something''' + def __init__(self, value): + msg = 'Click on element %s failed as it was covered by another element.' % value + super(ElementCoveredException, self).__init__(msg) + +class ElementTimeoutException(Exception): + '''Raise when waiting on an element times out''' + def __init__(self, value): + if value != 'spinner': + msg = 'Waiting for element %s returned a TimeoutException.' % value + else: + msg = 'Waiting for the spinner to disappear returned a TimeoutException.' % value + super(ElementTimeoutException, self).__init__(msg) + +class ElementNotFoundException(Exception): + '''Raise when an element is not found''' + def __init__(self, value): + msg = 'Element %s was not found.' % value + super(ElementTimeoutException, self).__init__(msg) + +class FailedClickException(Exception): + '''Raise when wait_and_click fails''' + def __init__(self, value): + msg = 'Waiting and trying to click %s failed.' % value + super(FailedClickException, self).__init__(msg) \ No newline at end of file diff --git a/tests/selenium/webui-selenium-test.py b/tests/selenium/webui-selenium-test.py index 738c5dea..7f7f9a21 100755 --- a/tests/selenium/webui-selenium-test.py +++ b/tests/selenium/webui-selenium-test.py @@ -9,6 +9,7 @@ from selenium import webdriver from selenium.common.exceptions import * #WebDriverException, ElementNotInteractableException, ElementNotVisibleException, TimeoutException, NoAlertPresentException, NoSuchElementException +from seleniumtestexceptions import * from selenium.webdriver.common.by import By from selenium.webdriver.common.desired_capabilities import DesiredCapabilities from selenium.webdriver.common.keys import Keys @@ -25,6 +26,7 @@ class WebuiSeleniumTest(unittest.TestCase): password = 'secret' client = 'bareos-fd' restorefile = '/usr/sbin/bconsole' + chromedriverpath = '/usr/lib/chromium-browser/chromedriver' # path to store logging files logpath = os.getcwd() # slow down test for debugging @@ -36,6 +38,8 @@ class WebuiSeleniumTest(unittest.TestCase): def setUp(self): + # Get environment variables + self.get_env() # Configure the logger, for information about the timings set it to INFO # Selenium driver itself will write additional debug messages when set to DEBUG #logging.basicConfig(filename='webui-selenium-test.log', level=logging.DEBUG) @@ -51,7 +55,12 @@ def setUp(self): #d = DesiredCapabilities.CHROME #d['loggingPrefs'] = { 'browser':'ALL' } # On OS X: Chromedriver path is 'usr/local/lib/chromium-browser/chromedriver' - self.driver = webdriver.Chrome('/usr/local/lib/chromium-browser/chromedriver') + try: + os.path.isfile(self.chromedriverpath) + except FileNotFoundError: + raise DriverNotFoundException + else: + self.driver = webdriver.Chrome(self.chromedriverpath) if self.browser == "firefox": d = DesiredCapabilities.FIREFOX d['loggingPrefs'] = { 'browser':'ALL' } @@ -59,8 +68,6 @@ def setUp(self): fp.set_preference('webdriver.log.file', self.logpath + '/firefox_console.log') self.driver = webdriver.Firefox(capabilities=d, firefox_profile=fp) - self.driver.implicitly_wait(1) - # used as timeout for selenium.webdriver.support.expected_conditions (EC) self.wait = WebDriverWait(self.driver, self.maxwait) @@ -70,6 +77,47 @@ def setUp(self): self.verificationErrors = [] + def get_env(self): + # Get attributes as environment variables, + # if not available or set use defaults. + global chromedriverpath + chromedriverpath = os.environ.get('BAREOS_CHROMEDRIVER_PATH') + if chromedriverpath: + WebuiSeleniumTest.chromedriverpath = chromedriverpath + global browser + browser = os.environ.get('BAREOS_BROWSER') + if browser: + WebuiSeleniumTest.browser = browser + global base_url + base_url = os.environ.get('BAREOS_BASE_URL') + if base_url: + WebuiSeleniumTest.base_url = base_url.rstrip('/') + global username + username = os.environ.get('BAREOS_USERNAME') + if username: + WebuiSeleniumTest.username = username + global password + password = os.environ.get('BAREOS_PASSWORD') + if password: + WebuiSeleniumTest.password = password + global client + client = os.environ.get('BAREOS_CLIENT_NAME') + if client: + WebuiSeleniumTest.client = client + global restorefile + restorefile = os.environ.get('BAREOS_RESTOREFILE') + if restorefile: + WebuiSeleniumTest.restorefile = restorefile + global logpath + logpath = os.environ.get('BAREOS_LOG_PATH') + if logpath: + WebuiSeleniumTest.logpath = logpath + global sleeptime + sleeptime = os.environ.get('BAREOS_DELAY') + if sleeptime: + WebuiSeleniumTest.sleeptime = float(sleeptime) + + def test_login(self): self.login() self.logout() @@ -77,7 +125,6 @@ def test_login(self): def test_menue(self): self.login() - self.wait_for_url_and_click('/director/') self.wait_for_url_and_click('/schedule/') self.wait_for_url_and_click('/schedule/status/') @@ -85,7 +132,6 @@ def test_menue(self): self.wait_for_url_and_click('/client/') self.wait_for_url_and_click('/restore/') self.wait_and_click(By.XPATH, '//a[contains(@href, "/dashboard/")]', By.XPATH, '//div[@id="modal-001"]//button[.="Close"]') - self.close_alert_and_get_its_text() self.logout() @@ -96,27 +142,21 @@ def test_restore(self): self.wait_for_url_and_click('/restore/') # Click on client dropdown menue and close the possible modal self.wait_and_click(By.XPATH, '(//button[@data-id="client"])', By.XPATH, '//div[@id="modal-001"]//button[.="Close"]') - # Select correct client self.wait_and_click(By.LINK_TEXT, self.client) - # Clicks on file and navigates through the tree # by using the arrow-keys. pathlist = self.restorefile.split('/') for i in pathlist[:-1]: self.wait_for_element(By.XPATH, '//a[contains(text(),"%s/")]' % i).send_keys(Keys.ARROW_RIGHT) self.wait_for_element(By.XPATH, '//a[contains(text(),"%s")]' % pathlist[-1]).click() - # Submit restore self.wait_and_click(By.XPATH, '//input[@id="submit"]') - # Confirms alert self.assertRegexpMatches(self.close_alert_and_get_its_text(), r'^Are you sure[\s\S]$') - # switch to dashboard to prevent that modals are open before logout self.wait_and_click(By.XPATH, '//a[contains(@href, "/dashboard/")]', By.XPATH, '//div[@id="modal-002"]//button[.="Close"]') self.close_alert_and_get_its_text() - # Logout self.logout() @@ -134,24 +174,17 @@ def test_run_default_job(self): self.logout() def test_run_configured_job(self): - driver = self.driver - self.login() - job_id = self.job_start_configured() - self.logout() def test_job_canceling(self): driver = self.driver - self.login() - job_id = self.job_start_configured() self.job_cancel(job_id) - self.logout() def job_start_configured(self): @@ -159,14 +192,12 @@ def job_start_configured(self): driver = self.driver self.wait_and_click(By.ID, 'menu-topnavbar-job') self.wait_and_click(By.LINK_TEXT, 'Run') - Select(driver.find_element_by_id('job')).select_by_visible_text('backup-bareos-fd') Select(driver.find_element_by_id('client')).select_by_visible_text(self.client) Select(driver.find_element_by_id('level')).select_by_visible_text('Incremental') # Clears the priority field and enters 5. driver.find_element_by_id('priority').clear() driver.find_element_by_id('priority').send_keys('5') - # Open the calendar self.wait_and_click(By.CSS_SELECTOR, "span.glyphicon.glyphicon-calendar") # Click the icon to delay jobstart by 1min two times @@ -190,7 +221,9 @@ def job_cancel(self, id): # Go to job list self.wait_and_click(By.ID, 'menu-topnavbar-job') # Click on the object that has id in its url - self.wait_for_url_and_click('/job/details/%s' % id) + self.wait_for_url_and_click('/bareos-webui/job/details/%s' % id) + # Wait for the cancel button to load + self.wait_for_element(By.XPATH, '//*[@title="Cancel"]') # Click on cancel button self.wait_and_click(By.XPATH, '//*[@title="Cancel"]') @@ -208,24 +241,31 @@ def test_rerun_job(self): def test_client_disabling(self): self.login() logger = logging.getLogger() + # Clicks on client menue tab + self.wait_and_click(By.ID, 'menu-topnavbar-client') + # Tries to click on client... + try: + self.wait_and_click(By.LINK_TEXT, self.client) + # Raises exception if client not found + except ElementTimeoutException: + raise ClientNotFoundException(self.client) + # And goes back to dashboard tab. + self.wait_and_click(By.ID, 'menu-topnavbar-dashboard') + # Back to the clients # Disables client 1 and goes back to the dashboard. self.wait_and_click(By.ID, 'menu-topnavbar-client') self.wait_and_click(By.LINK_TEXT, self.client) self.wait_and_click(By.ID, 'menu-topnavbar-client') - # Checks if client is enabled if self.client_status(self.client)=='Enabled': # Disables client self.wait_and_click(By.XPATH, '//tr[contains(td[1], "%s")]/td[5]/a[@title="Disable"]' % self.client) # Switches to dashboard, if prevented by open modal: close modal self.wait_and_click(By.ID, 'menu-topnavbar-dashboard',By.CSS_SELECTOR, 'div.modal-footer > button.btn.btn-default') - # Throw exception if client is already disabled else: raise ClientStatusException(self.client, 'disabled') - self.wait_and_click(By.ID, 'menu-topnavbar-client') - # Checks if client is disabled so that it can be enabled if self.client_status(self.client)=='Disabled': # Enables client @@ -240,10 +280,8 @@ def test_client_disabling(self): def login(self): driver = self.driver - driver.get(self.base_url + '/auth/login') Select(driver.find_element_by_name('director')).select_by_visible_text('localhost-dir') - driver.find_element_by_name('consolename').clear() driver.find_element_by_name('consolename').send_keys(self.username) driver.find_element_by_name('password').clear() @@ -261,9 +299,13 @@ def logout(self): sleep(self.sleeptime) def client_status(self, client): - self.client = client - # Checks the clients status on /bareos-webui/clients - status = self.driver.find_element(By.XPATH, '//tr[contains(td[1], "%s")]/td[4]/span' % self.client).text + # Wait until the site and the status element are loaded. + self.wait.until(EC.presence_of_element_located((By.XPATH, '//tr[contains(td[1], "%s")]/td[4]/span' % self.client))) + # Checks the clients status on /bareos-webui/clients, if client not found raise exception + try: + status = self.driver.find_element(By.XPATH, '//tr[contains(td[1], "%s")]/td[4]/span' % self.client).text + except NoSuchElementException: + raise ClientNotFoundException(self.client) return status def wait_for_spinner_absence(self): @@ -283,6 +325,13 @@ def wait_for_element(self, by, value): element = self.wait.until(EC.element_to_be_clickable((by, value))) except TimeoutException: raise ElementTimeoutException(value) + if element==None: + try: + self.driver.find_element(by, value) + except NoSuchElementException: + raise ElementNotFoundException(value) + else: + raise ElementCoveredException(value) return element def wait_for_url_and_click(self, url): @@ -310,14 +359,15 @@ def wait_and_click(self, by, value, modal_by=None, modal_value=None): except WebDriverException as e: logger.info('WebDriverException: %s', e) sleep(self.waittime) - except NoSuchElementException as e: - logger.info("NoSuchElementException while clicking: %s", e) - sleep(self.waittime) + # The case where the element doesn't exist is handled in wait_for_element + # except NoSuchElementException as e: + # logger.info("NoSuchElementException while clicking: %s", e) + # sleep(self.waittime) else: return element seconds = (datetime.now() - starttime).total_seconds() logger.error('failed to click %s %s', by, value) - raise FailedClickException(by, value) + raise FailedClickException(value) def close_alert_and_get_its_text(self, accept=True): logger = logging.getLogger() @@ -327,80 +377,18 @@ def close_alert_and_get_its_text(self, accept=True): alert_text = alert.text except NoAlertPresentException: return - if accept: alert.accept() else: alert.dismiss() - logger.debug( 'alert message: %s' % (alert_text)) - return alert_text def tearDown(self): self.driver.quit() self.assertEqual([], self.verificationErrors) -class ClientStatusException(Exception): - '''Raise when a client does not have the expected status''' - def __init__(self,client, status, msg=None): - if status=='enabled': - msg = '%s is enabled and cannot be enabled again.' % client - if status=='disabled': - msg = '%s is disabled and cannot be disabled again.' % client - super(ClientStatusException, self).__init__(msg) - -class BadJobException(Exception): - '''Raise when a started job doesn't result in ID''' - def __init__(self, msg=None): - msg = 'Job ID could not be saved after starting the job.' - super(BadJobException, self).__init__(msg) - -class ElementTimeoutException(Exception): - '''Raise when waiting on an element times out''' - def __init__(self, value): - msg = 'Waiting for element %s returned a TimeoutException.' % value - super(ElementTimeoutException, self).__init__(msg) - -class FailedClickException(Exception): - '''Raise when wait_and_click fails''' - def __init__(self, value): - msg = 'Waiting and trying to click %s failed.' % value - super(FailedClickException, self).__init__(msg) if __name__ == '__main__': - # Get attributes as environment variables, - # if not available or set use defaults. - browser = os.environ.get('BAREOS_BROWSER') - if browser: - WebuiSeleniumTest.browser = browser - - base_url = os.environ.get('BAREOS_BASE_URL') - if base_url: - WebuiSeleniumTest.base_url = base_url.rstrip('/') - - username = os.environ.get('BAREOS_USERNAME') - if username: - WebuiSeleniumTest.username = username - - password = os.environ.get('BAREOS_PASSWORD') - if password: - WebuiSeleniumTest.password = password - - client = os.environ.get('BAREOS_CLIENT_NAME') - if client: - WebuiSeleniumTest.client = client - - restorefile = os.environ.get('BAREOS_RESTOREFILE') - if restorefile: - WebuiSeleniumTest.restorefile = restorefile - - logpath = os.environ.get('BAREOS_LOG_PATH') - if logpath: - WebuiSeleniumTest.logpath = logpath - - sleeptime = os.environ.get('BAREOS_DELAY') - if sleeptime: - WebuiSeleniumTest.sleeptime = float(sleeptime) unittest.main()