Add Grafana dashboards UI test
Add common function to run selenium in headless mode. Change-Id: Ia48cea1c3e956aab49ca498e736dceda51598f9e
This commit is contained in:
parent
5a0720cddc
commit
c7127bc49f
@ -24,7 +24,11 @@ for the detailed procedure).
|
|||||||
|
|
||||||
. $VENV_PATH/bin/activate
|
. $VENV_PATH/bin/activate
|
||||||
|
|
||||||
4. Run the tests:
|
4. If you want to run UI test in headless mode, install these packages:
|
||||||
|
|
||||||
|
sudo apt-get install xvfb firefox -y
|
||||||
|
|
||||||
|
5. Run the tests:
|
||||||
|
|
||||||
./utils/jenkins/system_tests.sh -k -K -j fuelweb_test -t test -w $(pwd) -o --group=<your_test_group_to_run>
|
./utils/jenkins/system_tests.sh -k -K -j fuelweb_test -t test -w $(pwd) -o --group=<your_test_group_to_run>
|
||||||
|
|
||||||
|
@ -18,3 +18,9 @@ Destructive
|
|||||||
|
|
||||||
.. automodule:: stacklight_tests.influxdb_grafana.test_destructive
|
.. automodule:: stacklight_tests.influxdb_grafana.test_destructive
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
Functional
|
||||||
|
==========
|
||||||
|
|
||||||
|
.. automodule:: stacklight_tests.influxdb_grafana.test_functional
|
||||||
|
:members:
|
||||||
|
@ -16,3 +16,9 @@ export LMA_COLLECTOR_PLUGIN_PATH=$HOME/plugins/lma_collector-0.9-0.9.0-1.noarch.
|
|||||||
export LMA_INFRA_ALERTING_PLUGIN_PATH=$HOME/plugins/lma_infrastructure_alerting-0.9-0.9.0-1.noarch.rpm
|
export LMA_INFRA_ALERTING_PLUGIN_PATH=$HOME/plugins/lma_infrastructure_alerting-0.9-0.9.0-1.noarch.rpm
|
||||||
export ELASTICSEARCH_KIBANA_PLUGIN_PATH=$HOME/plugins/elasticsearch_kibana-0.9-0.9.0-1.noarch.rpm
|
export ELASTICSEARCH_KIBANA_PLUGIN_PATH=$HOME/plugins/elasticsearch_kibana-0.9-0.9.0-1.noarch.rpm
|
||||||
export INFLUXDB_GRAFANA_PLUGIN_PATH=$HOME/plugins/influxdb_grafana-0.9-0.9.0-1.noarch.rpm
|
export INFLUXDB_GRAFANA_PLUGIN_PATH=$HOME/plugins/influxdb_grafana-0.9-0.9.0-1.noarch.rpm
|
||||||
|
|
||||||
|
# UI Tests settings
|
||||||
|
export SELENIUM_HEADLESS=True
|
||||||
|
export SELENIUM_MAXIMIZE=True
|
||||||
|
export IMPLICIT_WAIT=5
|
||||||
|
# export DRIVER_PROXY=localhost:8080
|
||||||
|
@ -3,3 +3,4 @@ requests
|
|||||||
selenium
|
selenium
|
||||||
six
|
six
|
||||||
tox
|
tox
|
||||||
|
xvfbwrapper
|
||||||
|
0
stacklight_tests/helpers/ui/__init__.py
Normal file
0
stacklight_tests/helpers/ui/__init__.py
Normal file
163
stacklight_tests/helpers/ui/base_pages.py
Normal file
163
stacklight_tests/helpers/ui/base_pages.py
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import contextlib
|
||||||
|
|
||||||
|
from proboscis import asserts
|
||||||
|
import selenium.common.exceptions as Exceptions
|
||||||
|
from selenium.webdriver.remote import webelement
|
||||||
|
import selenium.webdriver.support.ui as Support
|
||||||
|
|
||||||
|
|
||||||
|
class BaseWebObject(object):
|
||||||
|
def __init__(self, driver, timeout=5):
|
||||||
|
self.driver = driver
|
||||||
|
self.timeout = timeout
|
||||||
|
|
||||||
|
def _turn_off_implicit_wait(self):
|
||||||
|
self.driver.implicitly_wait(0)
|
||||||
|
|
||||||
|
def _turn_on_implicit_wait(self):
|
||||||
|
self.driver.implicitly_wait(self.timeout)
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def waits_disabled(self):
|
||||||
|
try:
|
||||||
|
self._turn_off_implicit_wait()
|
||||||
|
yield
|
||||||
|
finally:
|
||||||
|
self._turn_on_implicit_wait()
|
||||||
|
|
||||||
|
def _is_element_present(self, *locator):
|
||||||
|
with self.waits_disabled():
|
||||||
|
try:
|
||||||
|
self._get_element(*locator)
|
||||||
|
return True
|
||||||
|
except Exceptions.NoSuchElementException:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _is_element_visible(self, *locator):
|
||||||
|
try:
|
||||||
|
return self._get_element(*locator).is_displayed()
|
||||||
|
except (Exceptions.NoSuchElementException,
|
||||||
|
Exceptions.ElementNotVisibleException):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _is_element_displayed(self, element):
|
||||||
|
if element is None:
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
if isinstance(element, webelement.WebElement):
|
||||||
|
return element.is_displayed()
|
||||||
|
else:
|
||||||
|
return element.src_elem.is_displayed()
|
||||||
|
except (Exceptions.ElementNotVisibleException,
|
||||||
|
Exceptions.StaleElementReferenceException):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _is_text_visible(self, element, text, strict=True):
|
||||||
|
if not hasattr(element, 'text'):
|
||||||
|
return False
|
||||||
|
if strict:
|
||||||
|
return element.text == text
|
||||||
|
else:
|
||||||
|
return text in element.text
|
||||||
|
|
||||||
|
def _get_element(self, *locator):
|
||||||
|
return self.driver.find_element(*locator)
|
||||||
|
|
||||||
|
def _get_elements(self, *locator):
|
||||||
|
return self.driver.find_elements(*locator)
|
||||||
|
|
||||||
|
def _fill_field_element(self, data, field_element):
|
||||||
|
field_element.clear()
|
||||||
|
field_element.send_keys(data)
|
||||||
|
return field_element
|
||||||
|
|
||||||
|
def _select_dropdown(self, value, element):
|
||||||
|
select = Support.Select(element)
|
||||||
|
select.select_by_visible_text(value)
|
||||||
|
|
||||||
|
def _select_dropdown_by_value(self, value, element):
|
||||||
|
select = Support.Select(element)
|
||||||
|
select.select_by_value(value)
|
||||||
|
|
||||||
|
def _get_dropdown_options(self, element):
|
||||||
|
select = Support.Select(element)
|
||||||
|
return select.options
|
||||||
|
|
||||||
|
|
||||||
|
class PageObject(BaseWebObject):
|
||||||
|
"""Base class for page objects."""
|
||||||
|
|
||||||
|
PARTIAL_LOGIN_URL = 'login'
|
||||||
|
|
||||||
|
def __init__(self, driver):
|
||||||
|
"""Constructor."""
|
||||||
|
super(PageObject, self).__init__(driver)
|
||||||
|
self._page_title = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def page_title(self):
|
||||||
|
return self.driver.title
|
||||||
|
|
||||||
|
def is_the_current_page(self, do_assert=False):
|
||||||
|
found_expected_title = self.page_title.startswith(self._page_title)
|
||||||
|
if do_assert:
|
||||||
|
asserts.assert_true(
|
||||||
|
found_expected_title,
|
||||||
|
"Expected to find %s in page title, instead found: %s"
|
||||||
|
% (self._page_title, self.page_title))
|
||||||
|
return found_expected_title
|
||||||
|
|
||||||
|
def get_current_page_url(self):
|
||||||
|
return self.driver.current_url
|
||||||
|
|
||||||
|
def close_window(self):
|
||||||
|
return self.driver.close()
|
||||||
|
|
||||||
|
def is_nth_window_opened(self, n):
|
||||||
|
return len(self.driver.window_handles) == n
|
||||||
|
|
||||||
|
def switch_window(self, name=None, index=None):
|
||||||
|
"""Switches focus between the webdriver windows.
|
||||||
|
Args:
|
||||||
|
- name: The name of the window to switch to.
|
||||||
|
- index: The index of the window handle to switch to.
|
||||||
|
If the method is called without arguments it switches to the
|
||||||
|
last window in the driver window_handles list.
|
||||||
|
In case only one window exists nothing effectively happens.
|
||||||
|
Usage:
|
||||||
|
page.switch_window(name='_new')
|
||||||
|
page.switch_window(index=2)
|
||||||
|
page.switch_window()
|
||||||
|
"""
|
||||||
|
|
||||||
|
if name is not None and index is not None:
|
||||||
|
raise ValueError("switch_window receives the window's name or "
|
||||||
|
"the window's index, not both.")
|
||||||
|
if name is not None:
|
||||||
|
self.driver.switch_to.window(name)
|
||||||
|
elif index is not None:
|
||||||
|
self.driver.switch_to.window(
|
||||||
|
self.driver.window_handles[index])
|
||||||
|
else:
|
||||||
|
self.driver.switch_to.window(self.driver.window_handles[-1])
|
||||||
|
|
||||||
|
def go_to_previous_page(self):
|
||||||
|
self.driver.back()
|
||||||
|
|
||||||
|
def go_to_next_page(self):
|
||||||
|
self.driver.forward()
|
||||||
|
|
||||||
|
def refresh_page(self):
|
||||||
|
self.driver.refresh()
|
35
stacklight_tests/helpers/ui/ui_settings.py
Normal file
35
stacklight_tests/helpers/ui/ui_settings.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# Copyright 2016 Mirantis, Inc.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
# Use a virtual display server for running the tests headless or not
|
||||||
|
headless_mode = os.environ.get('SELENIUM_HEADLESS', False)
|
||||||
|
|
||||||
|
# The browser session will be started with given proxy,
|
||||||
|
# can be useful if you try to start UI tests on developer machine,
|
||||||
|
# but environment is on remote server
|
||||||
|
proxy_address = os.environ.get('DRIVER_PROXY', None)
|
||||||
|
|
||||||
|
# Maximize the current window that webdriver is using or not
|
||||||
|
maximize_window = os.environ.get('SELENIUM_MAXIMIZE', True)
|
||||||
|
|
||||||
|
# Sets a sticky timeout to implicitly wait for an element to be found,
|
||||||
|
# or a command to complete.
|
||||||
|
implicit_wait = os.environ.get('IMPLICIT_WAIT', 5)
|
||||||
|
|
||||||
|
# Set the amount of time to wait for a page load to complete
|
||||||
|
# before throwing an error.
|
||||||
|
page_timeout = os.environ.get('PAGE_TIMEOUT', 15)
|
@ -12,21 +12,71 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from proboscis import asserts
|
import contextlib
|
||||||
|
import socket
|
||||||
|
|
||||||
from selenium import webdriver
|
from selenium import webdriver
|
||||||
from selenium.webdriver.common.by import By
|
from selenium.webdriver.common import by
|
||||||
from selenium.webdriver.support import expected_conditions as EC
|
from selenium.webdriver.common import proxy
|
||||||
from selenium.webdriver.support.ui import WebDriverWait
|
import xvfbwrapper
|
||||||
|
|
||||||
|
from stacklight_tests.helpers.ui import ui_settings
|
||||||
|
|
||||||
|
|
||||||
def get_driver(ip, anchor, title):
|
@contextlib.contextmanager
|
||||||
driver = webdriver.Firefox()
|
def ui_driver(url, wait_element, title):
|
||||||
driver.get(ip)
|
vdisplay = None
|
||||||
WebDriverWait(driver, 120).until(
|
# Start a virtual display server for running the tests headless.
|
||||||
EC.presence_of_element_located((By.XPATH, anchor)))
|
if ui_settings.headless_mode:
|
||||||
asserts.assert_equal(True, title in driver.title,
|
vdisplay = xvfbwrapper.Xvfb(width=1920, height=1080)
|
||||||
"Title {0} was not found in {1}!".format(
|
args = []
|
||||||
title, driver.title))
|
|
||||||
|
# workaround for memory leak in Xvfb taken from:
|
||||||
|
# http://blog.jeffterrace.com/2012/07/xvfb-memory-leak-workaround.html
|
||||||
|
args.append("-noreset")
|
||||||
|
|
||||||
|
# disables X access control
|
||||||
|
args.append("-ac")
|
||||||
|
|
||||||
|
if hasattr(vdisplay, 'extra_xvfb_args'):
|
||||||
|
# xvfbwrapper 0.2.8 or newer
|
||||||
|
vdisplay.extra_xvfb_args.extend(args)
|
||||||
|
else:
|
||||||
|
vdisplay.xvfb_cmd.extend(args)
|
||||||
|
vdisplay.start()
|
||||||
|
driver = get_driver(url, wait_element, title)
|
||||||
|
try:
|
||||||
|
yield driver
|
||||||
|
finally:
|
||||||
|
driver.quit()
|
||||||
|
if vdisplay is not None:
|
||||||
|
vdisplay.stop()
|
||||||
|
|
||||||
|
|
||||||
|
def get_driver(url, anchor, title, by_selector_type=by.By.XPATH):
|
||||||
|
proxy_address = ui_settings.proxy_address
|
||||||
|
# Increase the default Python socket timeout from nothing
|
||||||
|
# to something that will cope with slow webdriver startup times.
|
||||||
|
# This *just* affects the communication between this test process
|
||||||
|
# and the webdriver.
|
||||||
|
socket.setdefaulttimeout(60)
|
||||||
|
# Start the Selenium webdriver and setup configuration.
|
||||||
|
proxy_ex = None
|
||||||
|
if proxy_address is not None:
|
||||||
|
proxy_ex = proxy.Proxy(
|
||||||
|
{
|
||||||
|
'proxyType': proxy.ProxyType.MANUAL,
|
||||||
|
'socksProxy': proxy_address,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
driver = webdriver.Firefox(proxy=proxy_ex)
|
||||||
|
if ui_settings.maximize_window:
|
||||||
|
driver.maximize_window()
|
||||||
|
driver.implicitly_wait(ui_settings.implicit_wait)
|
||||||
|
driver.set_page_load_timeout(ui_settings.page_timeout)
|
||||||
|
driver.get(url)
|
||||||
|
driver.find_element(by_selector_type, anchor)
|
||||||
|
assert title in driver.title
|
||||||
return driver
|
return driver
|
||||||
|
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ from proboscis import asserts
|
|||||||
import requests
|
import requests
|
||||||
|
|
||||||
from stacklight_tests import base_test
|
from stacklight_tests import base_test
|
||||||
|
from stacklight_tests.influxdb_grafana.grafana_ui import api as ui_api
|
||||||
from stacklight_tests.influxdb_grafana import plugin_settings
|
from stacklight_tests.influxdb_grafana import plugin_settings
|
||||||
|
|
||||||
|
|
||||||
@ -137,3 +138,7 @@ class InfluxdbPluginApi(base_test.PluginApi):
|
|||||||
def check_uninstall_failure(self):
|
def check_uninstall_failure(self):
|
||||||
return self.helpers.check_plugin_cannot_be_uninstalled(
|
return self.helpers.check_plugin_cannot_be_uninstalled(
|
||||||
self.settings.name, self.settings.version)
|
self.settings.name, self.settings.version)
|
||||||
|
|
||||||
|
def check_grafana_dashboards(self):
|
||||||
|
grafana_url = self.get_grafana_url()
|
||||||
|
ui_api.check_grafana_dashboards(grafana_url)
|
||||||
|
48
stacklight_tests/influxdb_grafana/grafana_ui/api.py
Normal file
48
stacklight_tests/influxdb_grafana/grafana_ui/api.py
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
# Copyright 2016 Mirantis, Inc.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
from proboscis import asserts
|
||||||
|
|
||||||
|
from stacklight_tests.helpers.ui_tester import ui_driver
|
||||||
|
from stacklight_tests.influxdb_grafana.grafana_ui import pages
|
||||||
|
|
||||||
|
|
||||||
|
def check_grafana_dashboards(grafana_url):
|
||||||
|
|
||||||
|
login_key_xpath = '/html/body/div/div[2]/div/div/div[2]/form/div[2]/button'
|
||||||
|
|
||||||
|
with ui_driver(grafana_url, login_key_xpath, "Grafana") as driver:
|
||||||
|
login_page = pages.LoginPage(driver)
|
||||||
|
login_page.is_login_page()
|
||||||
|
home_page = login_page.login("grafana", "grafanapass")
|
||||||
|
home_page.is_main_page()
|
||||||
|
dashboard_names = {
|
||||||
|
"Apache", "Cinder", "Elasticsearch", "Glance", "HAProxy", "Heat",
|
||||||
|
"Hypervisor", "InfluxDB", "Keystone", "LMA self-monitoring",
|
||||||
|
"Memcached", "MySQL", "Neutron", "Nova", "RabbitMQ", "System"
|
||||||
|
}
|
||||||
|
dashboard_names = {
|
||||||
|
panel_name.lower() for panel_name in dashboard_names}
|
||||||
|
available_dashboards_names = {
|
||||||
|
dashboard.text.lower() for dashboard in home_page.dashboards}
|
||||||
|
msg = ("There is not enough panels in available panels, "
|
||||||
|
"panels that are not presented: {}")
|
||||||
|
# NOTE(rpromyshlennikov): should there be 'elasticsearch'
|
||||||
|
# and 'influxdb' dashboards?
|
||||||
|
asserts.assert_true(
|
||||||
|
dashboard_names.issubset(available_dashboards_names),
|
||||||
|
msg.format(dashboard_names - available_dashboards_names))
|
||||||
|
for name in available_dashboards_names:
|
||||||
|
dashboard_page = home_page.open_dashboard(name)
|
||||||
|
dashboard_page.get_back_to_home()
|
127
stacklight_tests/influxdb_grafana/grafana_ui/pages.py
Normal file
127
stacklight_tests/influxdb_grafana/grafana_ui/pages.py
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
from selenium.webdriver.common import by
|
||||||
|
from selenium.webdriver.common import keys
|
||||||
|
|
||||||
|
|
||||||
|
from stacklight_tests.helpers.ui import base_pages
|
||||||
|
|
||||||
|
|
||||||
|
class DashboardPage(base_pages.PageObject):
|
||||||
|
_submenu_controls_locator = (
|
||||||
|
by.By.CLASS_NAME,
|
||||||
|
"submenu-controls"
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, driver, dashboard_name):
|
||||||
|
super(DashboardPage, self).__init__(driver)
|
||||||
|
self._page_title = "Grafana - {}".format(dashboard_name)
|
||||||
|
|
||||||
|
def is_dashboards_page(self):
|
||||||
|
return (self.is_the_current_page() and
|
||||||
|
self._is_element_visible(*self._submenu_controls_locator))
|
||||||
|
|
||||||
|
def get_back_to_home(self):
|
||||||
|
self.go_to_previous_page()
|
||||||
|
return MainPage(self.driver)
|
||||||
|
|
||||||
|
|
||||||
|
class MainPage(base_pages.PageObject):
|
||||||
|
_dropdown_menu_locator = (
|
||||||
|
by.By.CLASS_NAME,
|
||||||
|
'top-nav-dashboards-btn')
|
||||||
|
|
||||||
|
_dashboards_list_locator = (
|
||||||
|
by.By.CLASS_NAME,
|
||||||
|
'search-results-container'
|
||||||
|
)
|
||||||
|
|
||||||
|
_dashboard_locator = (by.By.CLASS_NAME,
|
||||||
|
"search-item-dash-db")
|
||||||
|
|
||||||
|
def __init__(self, driver):
|
||||||
|
super(MainPage, self).__init__(driver)
|
||||||
|
self._page_title = "Grafana - Home"
|
||||||
|
|
||||||
|
def is_main_page(self):
|
||||||
|
return (self.is_the_current_page() and
|
||||||
|
self._is_element_visible(*self._dropdown_menu_locator))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def dropdown_menu(self):
|
||||||
|
return self._get_element(*self._dropdown_menu_locator)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def dashboards_list(self):
|
||||||
|
self.open_dropdown_menu()
|
||||||
|
return self._get_element(*self._dashboards_list_locator)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def dashboards(self):
|
||||||
|
return self.dashboards_list.find_elements(*self._dashboard_locator)
|
||||||
|
|
||||||
|
def is_dropdown_menu_opened(self):
|
||||||
|
return self._is_element_present(*self._dashboards_list_locator)
|
||||||
|
|
||||||
|
def open_dropdown_menu(self):
|
||||||
|
if not self.is_dropdown_menu_opened():
|
||||||
|
self.dropdown_menu.click()
|
||||||
|
|
||||||
|
def open_dashboard(self, dashboard_name):
|
||||||
|
dashboards_mapping = {dashboard.text.lower(): dashboard
|
||||||
|
for dashboard in self.dashboards}
|
||||||
|
dashboards_mapping[dashboard_name.lower()].click()
|
||||||
|
dashboard_page = DashboardPage(self.driver, dashboard_name)
|
||||||
|
dashboard_page.is_dashboards_page()
|
||||||
|
return dashboard_page
|
||||||
|
|
||||||
|
|
||||||
|
class LoginPage(base_pages.PageObject):
|
||||||
|
_login_username_field_locator = (by.By.NAME, 'username')
|
||||||
|
_login_password_field_locator = (by.By.NAME, 'password')
|
||||||
|
_login_submit_button_locator = (
|
||||||
|
by.By.CLASS_NAME,
|
||||||
|
'login-submit-button-row')
|
||||||
|
|
||||||
|
def __init__(self, driver):
|
||||||
|
super(LoginPage, self).__init__(driver)
|
||||||
|
self._page_title = "Grafana"
|
||||||
|
|
||||||
|
def is_login_page(self):
|
||||||
|
return (self.is_the_current_page() and
|
||||||
|
self._is_element_visible(*self._login_submit_button_locator))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def username(self):
|
||||||
|
return self._get_element(*self._login_username_field_locator)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def password(self):
|
||||||
|
return self._get_element(*self._login_password_field_locator)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def login_button(self):
|
||||||
|
return self._get_element(*self._login_submit_button_locator)
|
||||||
|
|
||||||
|
def _click_on_login_button(self):
|
||||||
|
self.login_button.click()
|
||||||
|
|
||||||
|
def _press_enter_on_login_button(self):
|
||||||
|
self.login_button.send_keys(keys.Keys.RETURN)
|
||||||
|
|
||||||
|
def login(self, user, password):
|
||||||
|
return self.login_with_mouse_click(user, password)
|
||||||
|
|
||||||
|
def login_with_mouse_click(self, user, password):
|
||||||
|
return self._do_login(user, password, self._click_on_login_button)
|
||||||
|
|
||||||
|
def login_with_enter_key(self, user, password):
|
||||||
|
return self._do_login(user, password,
|
||||||
|
self._press_enter_on_login_button)
|
||||||
|
|
||||||
|
def _do_login(self, user, password, login_method):
|
||||||
|
return self.login_as_user(user, password, login_method)
|
||||||
|
|
||||||
|
def login_as_user(self, user, password, login_method):
|
||||||
|
self._fill_field_element(user, self.username)
|
||||||
|
self._fill_field_element(password, self.password)
|
||||||
|
login_method()
|
||||||
|
return MainPage(self.driver)
|
@ -39,7 +39,6 @@ class TestDestructiveInfluxdbPlugin(api.InfluxdbPluginApi):
|
|||||||
6. Check that the cluster's state is okay
|
6. Check that the cluster's state is okay
|
||||||
|
|
||||||
Duration 40m
|
Duration 40m
|
||||||
Snapshot check_cluster_outage_influxdb_grafana
|
|
||||||
"""
|
"""
|
||||||
self.env.revert_snapshot("deploy_ha_influxdb_grafana")
|
self.env.revert_snapshot("deploy_ha_influxdb_grafana")
|
||||||
|
|
||||||
@ -52,8 +51,6 @@ class TestDestructiveInfluxdbPlugin(api.InfluxdbPluginApi):
|
|||||||
|
|
||||||
self.helpers.run_ostf()
|
self.helpers.run_ostf()
|
||||||
|
|
||||||
self.env.make_snapshot("check_cluster_outage_influxdb_grafana")
|
|
||||||
|
|
||||||
@test(depends_on_groups=["deploy_influxdb_grafana"],
|
@test(depends_on_groups=["deploy_influxdb_grafana"],
|
||||||
groups=["check_disaster_influxdb_grafana", "influxdb_grafana",
|
groups=["check_disaster_influxdb_grafana", "influxdb_grafana",
|
||||||
"destructive", "check_node_outage_influxdb_grafana"])
|
"destructive", "check_node_outage_influxdb_grafana"])
|
||||||
@ -66,11 +63,10 @@ class TestDestructiveInfluxdbPlugin(api.InfluxdbPluginApi):
|
|||||||
1. Revert the snapshot with 3 deployed nodes
|
1. Revert the snapshot with 3 deployed nodes
|
||||||
2. Simulate network interruption on the InfluxDB/Grafana node
|
2. Simulate network interruption on the InfluxDB/Grafana node
|
||||||
3. Wait for at least 30 seconds before recover network availability
|
3. Wait for at least 30 seconds before recover network availability
|
||||||
5. Run OSTF
|
4. Run OSTF
|
||||||
6. Check that plugin is working
|
5. Check that plugin is working
|
||||||
|
|
||||||
Duration 20m
|
Duration 20m
|
||||||
Snapshot check_node_outage_influxdb_grafana
|
|
||||||
"""
|
"""
|
||||||
self.env.revert_snapshot("deploy_influxdb_grafana")
|
self.env.revert_snapshot("deploy_influxdb_grafana")
|
||||||
|
|
||||||
@ -83,5 +79,3 @@ class TestDestructiveInfluxdbPlugin(api.InfluxdbPluginApi):
|
|||||||
self.check_plugin_online()
|
self.check_plugin_online()
|
||||||
|
|
||||||
self.helpers.run_ostf()
|
self.helpers.run_ostf()
|
||||||
|
|
||||||
self.env.make_snapshot("check_node_outage_influxdb_grafana")
|
|
||||||
|
64
stacklight_tests/influxdb_grafana/test_functional.py
Normal file
64
stacklight_tests/influxdb_grafana/test_functional.py
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
# Copyright 2016 Mirantis, Inc.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
from fuelweb_test.helpers.decorators import log_snapshot_after_test
|
||||||
|
from proboscis import test
|
||||||
|
|
||||||
|
from stacklight_tests.influxdb_grafana import api
|
||||||
|
|
||||||
|
|
||||||
|
@test(groups=["plugins"])
|
||||||
|
class TestFunctionalInfluxdbPlugin(api.InfluxdbPluginApi):
|
||||||
|
"""Class for functional testing of plugin."""
|
||||||
|
|
||||||
|
@test(depends_on_groups=["deploy_influxdb_grafana"],
|
||||||
|
groups=["check_display_dashboards_influxdb_grafana",
|
||||||
|
"influxdb_grafana", "functional"])
|
||||||
|
@log_snapshot_after_test
|
||||||
|
def check_display_dashboards_influxdb_grafana(self):
|
||||||
|
"""Verify that the dashboards show up in the Grafana UI.
|
||||||
|
|
||||||
|
Scenario:
|
||||||
|
1. Revert snapshot with 9 deployed nodes in HA configuration
|
||||||
|
2. Open the Grafana URL (
|
||||||
|
open the "Dashboard" tab and click the "Grafana" link)
|
||||||
|
3. Sign-in using the credentials provided
|
||||||
|
during the configuration of the environment
|
||||||
|
4. Go to the Main dashboard and verify that everything is ok
|
||||||
|
5. Repeat the previous step for the following dashboards:
|
||||||
|
* Apache
|
||||||
|
* Cinder
|
||||||
|
* Elasticsearch
|
||||||
|
* Glance
|
||||||
|
* HAProxy
|
||||||
|
* Heat
|
||||||
|
* Hypervisor
|
||||||
|
* InfluxDB
|
||||||
|
* Keystone
|
||||||
|
* LMA self-monitoring
|
||||||
|
* Memcached
|
||||||
|
* MySQL
|
||||||
|
* Neutron
|
||||||
|
* Nova
|
||||||
|
* RabbitMQ
|
||||||
|
* System
|
||||||
|
|
||||||
|
Duration 40m
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.env.revert_snapshot("deploy_influxdb_grafana_plugin")
|
||||||
|
|
||||||
|
self.check_plugin_online()
|
||||||
|
|
||||||
|
self.check_grafana_dashboards()
|
@ -66,8 +66,6 @@ class TestNodesInfluxdbPlugin(api.InfluxdbPluginApi):
|
|||||||
|
|
||||||
self.helpers.run_ostf(should_fail=1)
|
self.helpers.run_ostf(should_fail=1)
|
||||||
|
|
||||||
self.env.make_snapshot("add_remove_controller_influxdb_grafana")
|
|
||||||
|
|
||||||
@test(depends_on_groups=["deploy_ha_influxdb_grafana"],
|
@test(depends_on_groups=["deploy_ha_influxdb_grafana"],
|
||||||
groups=["check_scaling_influxdb_grafana", "scaling",
|
groups=["check_scaling_influxdb_grafana", "scaling",
|
||||||
"influxdb_grafana", "system",
|
"influxdb_grafana", "system",
|
||||||
@ -112,8 +110,6 @@ class TestNodesInfluxdbPlugin(api.InfluxdbPluginApi):
|
|||||||
|
|
||||||
self.helpers.run_ostf(should_fail=1)
|
self.helpers.run_ostf(should_fail=1)
|
||||||
|
|
||||||
self.env.make_snapshot("add_remove_compute_influxdb_grafana")
|
|
||||||
|
|
||||||
@test(depends_on_groups=["deploy_ha_influxdb_grafana"],
|
@test(depends_on_groups=["deploy_ha_influxdb_grafana"],
|
||||||
groups=["check_scaling_influxdb_grafana", "scaling",
|
groups=["check_scaling_influxdb_grafana", "scaling",
|
||||||
"influxdb_grafana", "system",
|
"influxdb_grafana", "system",
|
||||||
@ -162,8 +158,6 @@ class TestNodesInfluxdbPlugin(api.InfluxdbPluginApi):
|
|||||||
|
|
||||||
self.helpers.run_ostf()
|
self.helpers.run_ostf()
|
||||||
|
|
||||||
self.env.make_snapshot("add_remove_influxdb_grafana_node")
|
|
||||||
|
|
||||||
@test(depends_on_groups=["deploy_ha_influxdb_grafana"],
|
@test(depends_on_groups=["deploy_ha_influxdb_grafana"],
|
||||||
groups=["check_failover_influxdb_grafana" "failover",
|
groups=["check_failover_influxdb_grafana" "failover",
|
||||||
"influxdb_grafana", "system", "destructive",
|
"influxdb_grafana", "system", "destructive",
|
||||||
@ -197,5 +191,3 @@ class TestNodesInfluxdbPlugin(api.InfluxdbPluginApi):
|
|||||||
# TODO(rpromyshlennikov): check no data lost
|
# TODO(rpromyshlennikov): check no data lost
|
||||||
|
|
||||||
self.helpers.run_ostf()
|
self.helpers.run_ostf()
|
||||||
|
|
||||||
self.env.make_snapshot("shutdown_influxdb_grafana_node")
|
|
||||||
|
@ -44,6 +44,7 @@ def import_tests():
|
|||||||
from stacklight_tests.elasticsearch_kibana import test_smoke_bvt # noqa
|
from stacklight_tests.elasticsearch_kibana import test_smoke_bvt # noqa
|
||||||
from stacklight_tests.elasticsearch_kibana import test_system # noqa
|
from stacklight_tests.elasticsearch_kibana import test_system # noqa
|
||||||
from stacklight_tests.influxdb_grafana import test_destructive # noqa
|
from stacklight_tests.influxdb_grafana import test_destructive # noqa
|
||||||
|
from stacklight_tests.influxdb_grafana import test_functional # noqa
|
||||||
from stacklight_tests.influxdb_grafana import test_smoke_bvt # noqa
|
from stacklight_tests.influxdb_grafana import test_smoke_bvt # noqa
|
||||||
from stacklight_tests.influxdb_grafana import test_system # noqa
|
from stacklight_tests.influxdb_grafana import test_system # noqa
|
||||||
from stacklight_tests.lma_collector import test_smoke_bvt # noqa
|
from stacklight_tests.lma_collector import test_smoke_bvt # noqa
|
||||||
|
Loading…
Reference in New Issue
Block a user