From be6199ef1f9e0dbb7eeb8c11af187b3b87c82c9c Mon Sep 17 00:00:00 2001 From: NastyaUrlapova Date: Mon, 17 Feb 2014 18:16:48 +0400 Subject: [PATCH] Move UI tests to main repo Change-Id: I185969eb59922b98f8a7ca8a6b0f44a822ce927e --- fuelweb_test/__init__.py | 2 +- fuelweb_ui_test/.gitignore | 38 ++ fuelweb_ui_test/README.md | 24 + fuelweb_ui_test/__init__.py | 13 + fuelweb_ui_test/browser.py | 39 ++ fuelweb_ui_test/decorators.py | 13 + fuelweb_ui_test/pageobjects/__init__.py | 0 fuelweb_ui_test/pageobjects/actions.py | 28 ++ fuelweb_ui_test/pageobjects/base.py | 104 ++++ fuelweb_ui_test/pageobjects/environments.py | 175 +++++++ fuelweb_ui_test/pageobjects/header.py | 58 +++ fuelweb_ui_test/pageobjects/networks.py | 203 ++++++++ .../pageobjects/node_disks_settings.py | 121 +++++ .../pageobjects/node_interfaces_settings.py | 32 ++ fuelweb_ui_test/pageobjects/nodes.py | 176 +++++++ fuelweb_ui_test/pageobjects/releases.py | 36 ++ fuelweb_ui_test/pageobjects/settings.py | 192 ++++++++ fuelweb_ui_test/pageobjects/support.py | 25 + fuelweb_ui_test/pageobjects/tabs.py | 36 ++ fuelweb_ui_test/requirements.txt | 1 + fuelweb_ui_test/settings.py | 39 ++ fuelweb_ui_test/tests/__init__.py | 0 fuelweb_ui_test/tests/base.py | 91 ++++ fuelweb_ui_test/tests/preconditions.py | 79 +++ fuelweb_ui_test/tests/test_adding_nodes.py | 312 ++++++++++++ fuelweb_ui_test/tests/test_configure_disks.py | 216 +++++++++ .../tests/test_configure_networks.py | 281 +++++++++++ fuelweb_ui_test/tests/test_deploy.py | 133 ++++++ fuelweb_ui_test/tests/test_discard_changes.py | 50 ++ fuelweb_ui_test/tests/test_env_settings.py | 189 ++++++++ fuelweb_ui_test/tests/test_env_wizard.py | 313 ++++++++++++ fuelweb_ui_test/tests/test_environment.py | 207 ++++++++ .../tests/test_environment_actions.py | 40 ++ fuelweb_ui_test/tests/test_networks.py | 449 ++++++++++++++++++ fuelweb_ui_test/tests/test_releases.py | 74 +++ fuelweb_ui_test/tests/test_roles.py | 168 +++++++ fuelweb_ui_test/tests/test_support.py | 59 +++ run_tests.sh | 2 +- 38 files changed, 4016 insertions(+), 2 deletions(-) create mode 100644 fuelweb_ui_test/.gitignore create mode 100644 fuelweb_ui_test/README.md create mode 100644 fuelweb_ui_test/__init__.py create mode 100644 fuelweb_ui_test/browser.py create mode 100644 fuelweb_ui_test/decorators.py create mode 100644 fuelweb_ui_test/pageobjects/__init__.py create mode 100644 fuelweb_ui_test/pageobjects/actions.py create mode 100644 fuelweb_ui_test/pageobjects/base.py create mode 100644 fuelweb_ui_test/pageobjects/environments.py create mode 100644 fuelweb_ui_test/pageobjects/header.py create mode 100644 fuelweb_ui_test/pageobjects/networks.py create mode 100644 fuelweb_ui_test/pageobjects/node_disks_settings.py create mode 100644 fuelweb_ui_test/pageobjects/node_interfaces_settings.py create mode 100644 fuelweb_ui_test/pageobjects/nodes.py create mode 100644 fuelweb_ui_test/pageobjects/releases.py create mode 100644 fuelweb_ui_test/pageobjects/settings.py create mode 100644 fuelweb_ui_test/pageobjects/support.py create mode 100644 fuelweb_ui_test/pageobjects/tabs.py create mode 100644 fuelweb_ui_test/requirements.txt create mode 100644 fuelweb_ui_test/settings.py create mode 100644 fuelweb_ui_test/tests/__init__.py create mode 100644 fuelweb_ui_test/tests/base.py create mode 100644 fuelweb_ui_test/tests/preconditions.py create mode 100644 fuelweb_ui_test/tests/test_adding_nodes.py create mode 100644 fuelweb_ui_test/tests/test_configure_disks.py create mode 100644 fuelweb_ui_test/tests/test_configure_networks.py create mode 100644 fuelweb_ui_test/tests/test_deploy.py create mode 100644 fuelweb_ui_test/tests/test_discard_changes.py create mode 100644 fuelweb_ui_test/tests/test_env_settings.py create mode 100644 fuelweb_ui_test/tests/test_env_wizard.py create mode 100644 fuelweb_ui_test/tests/test_environment.py create mode 100644 fuelweb_ui_test/tests/test_environment_actions.py create mode 100644 fuelweb_ui_test/tests/test_networks.py create mode 100644 fuelweb_ui_test/tests/test_releases.py create mode 100644 fuelweb_ui_test/tests/test_roles.py create mode 100644 fuelweb_ui_test/tests/test_support.py diff --git a/fuelweb_test/__init__.py b/fuelweb_test/__init__.py index 141ca6ab9..ce4c00840 100644 --- a/fuelweb_test/__init__.py +++ b/fuelweb_test/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2013 Mirantis, Inc. +# Copyright 2014 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 diff --git a/fuelweb_ui_test/.gitignore b/fuelweb_ui_test/.gitignore new file mode 100644 index 000000000..cb740582e --- /dev/null +++ b/fuelweb_ui_test/.gitignore @@ -0,0 +1,38 @@ +*.py[cod] + +# C extensions +*.so + +# Packages +*.egg +*.egg-info +dist +build +eggs +parts +bin +var +sdist +develop-eggs +.installed.cfg +lib +lib64 + +# Installer logs +pip-log.txt + +# Unit test / coverage reports +.coverage +.tox +nosetests.xml + +# Translations +*.mo + +# Mr Developer +.mr.developer.cfg +.project +.pydevproject + +# IDEA +.idea diff --git a/fuelweb_ui_test/README.md b/fuelweb_ui_test/README.md new file mode 100644 index 000000000..eca5e9cb1 --- /dev/null +++ b/fuelweb_ui_test/README.md @@ -0,0 +1,24 @@ +fuelweb-test +============ + +# Setting up virtual test machine + +Steps to set up a virtual machine for running fuelweb selenium tests: + +* Create virtual machine with Ubuntu desktop OS. VM should have internet connection. + * install java OpenJDK runtime environment. + * Turn the VM into jenkins node +* Install “git” +* “git clone https://github.com/stackforge/fuel-web.git” repository. It will be required in next step (command “cd nailgun” is about folder in this repository) +* Install “postgresql” and “pip”, setup python virtual environement and install python packages. Follow instructions at https://github.com/stackforge/fuel-web/blob/master/docs/develop/env.rst#setup-for-nailgun-unit-tests Steps #2 - #5. +* Install NodeJS. Follow instructions in step #1 at https://github.com/stackforge/fuel-web/blob/master/docs/develop/env.rst#setup-for-web-ui-tests. +* Try to run fuel-web in fake-ui mode. Follow steps #1 - #3 at https://github.com/stackforge/fuel-web/blob/master/docs/develop/env.rst#running-nailgun-in-fake-mode . Try to navigate to http://localhost:8000 +* Install Chrome web browser. +* Download latest version of Chrome driver from http://chromedriver.storage.googleapis.com/index.html and extract it to home folder at the VM +* “git clone https://github.com/Mirantis/fuelweb-test.git” Checkout a stable branch + * activate fuel virtual environment “workon fuel” + * install pip packages “pip install -r fuelweb-test/requirements.txt“ +* Try to run selenium tests (fake-ui should be launched). + * workon fuel + * export PYTHONPATH=$PYTHONPATH:[path to /fuel-web/nailgun] + * nosetests fuelweb-test.tests diff --git a/fuelweb_ui_test/__init__.py b/fuelweb_ui_test/__init__.py new file mode 100644 index 000000000..141ca6ab9 --- /dev/null +++ b/fuelweb_ui_test/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2013 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. diff --git a/fuelweb_ui_test/browser.py b/fuelweb_ui_test/browser.py new file mode 100644 index 000000000..3265281e0 --- /dev/null +++ b/fuelweb_ui_test/browser.py @@ -0,0 +1,39 @@ +from selenium import webdriver +from selenium.webdriver import DesiredCapabilities +from fuelweb_ui_test.settings import BROWSER +from fuelweb_ui_test.settings import CHROME_EXECUTABLE_PATH +from fuelweb_ui_test.settings import SELENIUM_IMPLICIT_WAIT + +driver = None + + +def start_driver(browser=None): + browser = browser or BROWSER + + def start_chrome(): + return webdriver.Chrome( + executable_path=CHROME_EXECUTABLE_PATH, + desired_capabilities=DesiredCapabilities.CHROME) + + def start_firefox(): + return webdriver.Firefox() + + def start_iexplore(): + return webdriver.Ie() + + global driver + if browser == "iexplore": + driver = start_iexplore() + elif browser == "chrome": + driver = start_chrome() + elif browser == "firefox": + driver = start_firefox() + + #driver.set_window_size(1024, 768) + driver.maximize_window() + driver.implicitly_wait(SELENIUM_IMPLICIT_WAIT) + return driver + + +def quit_driver(): + driver.quit() diff --git a/fuelweb_ui_test/decorators.py b/fuelweb_ui_test/decorators.py new file mode 100644 index 000000000..d8e45e659 --- /dev/null +++ b/fuelweb_ui_test/decorators.py @@ -0,0 +1,13 @@ +import browser +from fuelweb_ui_test.settings import SELENIUM_IMPLICIT_WAIT + + +def implicit_wait(wait_time): + def wrapper(func): + def wrapped(*args, **kwargs): + browser.driver.implicitly_wait(wait_time) + result = func(*args, **kwargs) + browser.driver.implicitly_wait(SELENIUM_IMPLICIT_WAIT) + return result + return wrapped + return wrapper diff --git a/fuelweb_ui_test/pageobjects/__init__.py b/fuelweb_ui_test/pageobjects/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/fuelweb_ui_test/pageobjects/actions.py b/fuelweb_ui_test/pageobjects/actions.py new file mode 100644 index 000000000..f2ca51123 --- /dev/null +++ b/fuelweb_ui_test/pageobjects/actions.py @@ -0,0 +1,28 @@ +from pageobjects.base import PageObject +from pageobjects.base import Popup + + +class Actions(PageObject): + + @property + def name(self): + return self.parent.\ + find_element_by_css_selector('.rename-cluster-form input') + + @property + def rename(self): + return self.parent.\ + find_element_by_css_selector('button.apply-name-btn') + + @property + def delete(self): + return self.parent.\ + find_element_by_css_selector('button.delete-cluster-btn') + + +class DeleteEnvironmentPopup(Popup): + + @property + def delete(self): + return self.parent.\ + find_element_by_css_selector('button.remove-cluster-btn') diff --git a/fuelweb_ui_test/pageobjects/base.py b/fuelweb_ui_test/pageobjects/base.py new file mode 100644 index 000000000..100ef222a --- /dev/null +++ b/fuelweb_ui_test/pageobjects/base.py @@ -0,0 +1,104 @@ +from selenium.common.exceptions import NoSuchElementException +from selenium.common.exceptions import StaleElementReferenceException +from selenium.webdriver.support.wait import WebDriverWait +import browser +import time + + +class PageObject: + + XPATH_RADIO = '//div[@class="custom-tumbler" ' \ + 'and input[@type="radio" and @name="{}" and @value="{}"]]' + + XPATH_CHECKBOX = \ + '//div[@class="custom-tumbler" ' \ + 'and input[@type="checkbox" and @name="{}"]]' + + def __init__(self, parent=None): + self.parent = parent or browser.driver + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + pass + + @staticmethod + def wait_until_moving(element, timeout=10): + class Move: + def __init__(self, elem): + self.element = elem + self.location = elem.location + + def __call__(self, *args, **kwargs): + loc = element.location + res = self.location['x'] == loc['x'] \ + and self.location['y'] == loc['y'] + self.location = loc + return res + + wait = WebDriverWait(browser.driver, timeout) + wait.until(Move(element)) + + @staticmethod + def wait_until_exists(element, timeout=10): + wait = WebDriverWait(browser.driver, timeout) + try: + wait.until(lambda driver: not element.is_displayed()) + except StaleElementReferenceException: + pass + + @staticmethod + def wait_element(page_object, attribute, timeout=10): + class El: + def __init__(self, page_object, attribute): + self.page_object = page_object + self.attribute = attribute + + def __call__(self, *args, **kwargs): + try: + getattr(self.page_object, attribute) + return True + except NoSuchElementException: + return False + + wait = WebDriverWait(browser.driver, timeout) + wait.until(El(page_object, attribute)) + + +class Popup(PageObject): + + def __init__(self): + element = browser.driver.find_element_by_css_selector('div.modal') + PageObject.__init__(self, element) + time.sleep(0.5) + #PageObject.wait_until_moving(self.parent) + + def wait_until_exists(self): + try: + PageObject.wait_until_exists( + browser.driver. + find_element_by_css_selector('div.modal-backdrop')) + except NoSuchElementException: + pass + + @property + def close_cross(self): + return self.parent.find_element_by_css_selector('.close') + + @property + def header(self): + return self.parent.find_element_by_css_selector('.modal-header > h3') + + +class ConfirmPopup(Popup): + + TEXT = 'Settings were modified but not saved' + + @property + def stay_on_page(self): + return self.parent.find_element_by_css_selector('.btn-return') + + @property + def leave_page(self): + return self.parent.find_element_by_css_selector('.proceed-btn') diff --git a/fuelweb_ui_test/pageobjects/environments.py b/fuelweb_ui_test/pageobjects/environments.py new file mode 100644 index 000000000..2e6c566b0 --- /dev/null +++ b/fuelweb_ui_test/pageobjects/environments.py @@ -0,0 +1,175 @@ +from selenium.webdriver.support.select import Select +from pageobjects.base import PageObject +from pageobjects.base import Popup + + +class Environments(PageObject): + + @property + def create_cluster_box(self): + return self.parent.find_element_by_css_selector('div.create-cluster') + + @property + def create_cluster_boxes(self): + return self.parent.find_elements_by_css_selector('a.clusterbox') + + +class RedhatAccountPopup(Popup): + + @property + def license_rhsm(self): + return self.parent.\ + find_element_by_xpath( + self.XPATH_RADIO.format('license-type', 'rhsm')) + + @property + def license_rhn(self): + return self.parent.\ + find_element_by_xpath( + self.XPATH_RADIO.format('license-type', 'rhn')) + + @property + def redhat_username(self): + return self.parent.find_element_by_name('username') + + @property + def redhat_password(self): + return self.parent.find_element_by_name('password') + + @property + def redhat_satellite(self): + return self.parent.find_element_by_name('satellite') + + @property + def redhat_activation_key(self): + return self.parent.find_element_by_name('activation_key') + + @property + def apply(self): + return self.parent.\ + find_element_by_css_selector('button.btn-os-download') + + +class Wizard(Popup, RedhatAccountPopup): + + @property + def name(self): + return self.parent.find_element_by_name('name') + + @property + def release(self): + return Select(self.parent.find_element_by_name('release')) + + @property + def next(self): + return self.parent.find_element_by_css_selector('button.next-pane-btn') + + @property + def create(self): + return self.parent.find_element_by_css_selector('button.finish-btn') + + @property + def cancel(self): + return self.parent.\ + find_element_by_css_selector('button.btn[data-dismiss=modal]') + + @property + def prev(self): + return self.parent.find_element_by_css_selector('button.prev-pane-btn') + + @property + def mode_multinode(self): + return self.parent.\ + find_element_by_xpath(self.XPATH_RADIO.format('mode', 'multinode')) + + @property + def mode_ha_compact(self): + return self.parent.\ + find_element_by_xpath( + self.XPATH_RADIO.format('mode', 'ha_compact')) + + @property + def hypervisor_kvm(self): + return self.parent.\ + find_element_by_xpath(self.XPATH_RADIO.format('hypervisor', 'kvm')) + + @property + def hypervisor_qemu(self): + return self.parent.\ + find_element_by_xpath( + self.XPATH_RADIO.format('hypervisor', 'qemu')) + + @property + def network_nova(self): + return self.parent.\ + find_element_by_xpath( + self.XPATH_RADIO.format('manager', 'nova-network')) + + @property + def network_neutron_gre(self): + return self.parent.\ + find_element_by_xpath( + self.XPATH_RADIO.format('manager', 'neutron-gre')) + + @property + def network_neutron_vlan(self): + return self.parent.\ + find_element_by_xpath( + self.XPATH_RADIO.format('manager', 'neutron-vlan')) + + @property + def storage_cinder_default(self): + return self.parent.\ + find_element_by_xpath( + self.XPATH_RADIO.format('cinder', 'default')) + + @property + def storage_cinder_ceph(self): + return self.parent.\ + find_element_by_xpath( + self.XPATH_RADIO.format('cinder', 'ceph')) + + @property + def storage_glance_default(self): + return self.parent.\ + find_element_by_xpath( + self.XPATH_RADIO.format('glance', 'default')) + + @property + def storage_glance_ceph(self): + return self.parent.\ + find_element_by_xpath( + self.XPATH_RADIO.format('glance', 'ceph')) + + @property + def install_savanna(self): + return self.parent.\ + find_element_by_xpath( + self.XPATH_CHECKBOX.format('savanna')) + + @property + def install_murano(self): + return self.parent.\ + find_element_by_xpath( + self.XPATH_CHECKBOX.format('murano')) + + @property + def install_ceilometer(self): + return self.parent.\ + find_element_by_xpath( + self.XPATH_CHECKBOX.format('ceilometer')) + + +class DeployChangesPopup(Popup): + + @property + def deploy(self): + return self.parent.\ + find_element_by_css_selector('.start-deployment-btn') + + +class DiscardChangesPopup(Popup): + + @property + def discard(self): + return self.parent.find_element_by_css_selector('.discard-btn') diff --git a/fuelweb_ui_test/pageobjects/header.py b/fuelweb_ui_test/pageobjects/header.py new file mode 100644 index 000000000..0c90c8dcd --- /dev/null +++ b/fuelweb_ui_test/pageobjects/header.py @@ -0,0 +1,58 @@ +import browser +from decorators import implicit_wait +from pageobjects.base import PageObject + + +class Header(PageObject): + + @property + def logo(self): + return self.parent.find_element_by_css_selector('div.logo') + + @property + def environments(self): + return self.parent.\ + find_element_by_css_selector('.navigation-bar-ul ' + 'a[href$=clusters]') + + @property + def releases(self): + return self.parent.\ + find_element_by_css_selector('.navigation-bar-ul ' + 'a[href$=releases]') + + @property + def support(self): + return self.parent.\ + find_element_by_css_selector('.navigation-bar-ul a[href$=support]') + + @property + def breadcrumb(self): + return self.parent.find_element_by_css_selector('.breadcrumb') + + @property + def total_nodes(self): + return self.parent.\ + find_element_by_css_selector('div.total-nodes-count') + + @property + def unallocated_nodes(self): + return self.parent.\ + find_element_by_css_selector('div.unallocated-nodes-count') + + +class TaskResultAlert(PageObject): + + @implicit_wait(60) + def __init__(self): + element = browser.driver.\ + find_element_by_css_selector('div.task-result') + PageObject.__init__(self, element) + + @property + def close(self): + return self.parent.find_element_by_css_selector('button.close') + + @property + def header(self): + return self.parent.find_element_by_css_selector('h4') diff --git a/fuelweb_ui_test/pageobjects/networks.py b/fuelweb_ui_test/pageobjects/networks.py new file mode 100644 index 000000000..1792c3d67 --- /dev/null +++ b/fuelweb_ui_test/pageobjects/networks.py @@ -0,0 +1,203 @@ +import browser +from decorators import implicit_wait +from pageobjects.base import PageObject +from selenium.webdriver.support.select import Select + + +class Networks(PageObject): + @property + def flatdhcp_manager(self): + return self.parent.\ + find_element_by_xpath( + self.XPATH_RADIO.format('net-manager', 'FlatDHCPManager')) + + @property + def vlan_manager(self): + return self.parent.\ + find_element_by_xpath( + self.XPATH_RADIO.format('net-manager', 'VlanManager')) + + @property + def segmentation_type(self): + return self.parent.\ + find_element_by_css_selector('span.network-segment-type') + + @property + def public(self): + return Network('Public') + + @property + def floating(self): + return Network('Floating') + + @property + def management(self): + return Network('Management') + + @property + def storage(self): + return Network('Storage') + + @property + def fixed(self): + return Network('VM (Fixed)') + + @property + def neutron(self): + return NeutronParameters() + + @property + def dns1(self): + return self.parent.\ + find_element_by_css_selector('.nameservers-row input[name=range0]') + + @property + def dns2(self): + return self.parent.\ + find_element_by_css_selector('.nameservers-row input[name=range1]') + + @property + @implicit_wait(20) + def verification_alert(self): + return self.parent.\ + find_element_by_css_selector('.verification-control .alert') + + @property + def verify_networks(self): + return self.parent.\ + find_element_by_xpath('//button[text()="Verify Networks"]') + + @property + def cancel_changes(self): + return self.parent.\ + find_element_by_xpath('//button[text()="Cancel Changes"]') + + @property + def save_settings(self): + return self.parent.\ + find_element_by_xpath('//button[text()="Save Settings"]') + + +class Network(PageObject): + XPATH_PARAMETER = './/div[contains(@class,"network-attribute") and ' \ + 'div[contains(@class,"parameter-name")]="{}"]' + + XPATH_PARAMETER_RANGES = \ + XPATH_PARAMETER + \ + '/div[@class="ip-ranges-rows"]/div[contains(@class,"range-row")]' + + def __init__(self, name): + el = browser.driver.\ + find_element_by_xpath('//div[legend="{}"]'.format(name)) + PageObject.__init__(self, el) + + @property + def ip_ranges(self): + elements = self.parent.find_elements_by_xpath( + self.XPATH_PARAMETER_RANGES.format('IP Range')) + return [IpRange(el) for el in elements] + + @property + def vlan_tagging(self): + return self.parent.find_element_by_css_selector('div.custom-tumbler') + + @property + def vlan_id(self): + return self.parent.\ + find_element_by_css_selector('input[name$=vlan_start]') + + @property + def vlan_end(self): + return self.parent.\ + find_element_by_css_selector('input[name$=vlan_end]') + + @property + def netmask(self): + return self.parent.\ + find_element_by_css_selector('input[name$=netmask]') + + @property + def gateway(self): + return self.parent.\ + find_element_by_css_selector('input[name$=gateway]') + + @property + def cidr(self): + return self.parent.find_element_by_css_selector('input[name$=cidr]') + + @property + def number_of_networks(self): + return self.parent.find_element_by_css_selector('input[name$=amount]') + + @property + def network_size(self): + return Select( + self.parent. + find_element_by_css_selector('select[name$=network_size]')) + + +class NeutronParameters(PageObject): + def __init__(self): + el = browser.driver.\ + find_element_by_css_selector('div.neutron-parameters') + PageObject.__init__(self, el) + + @property + def id_start(self): + return self.parent.find_element_by_css_selector('input[name=id0]') + + @property + def id_end(self): + return self.parent.find_element_by_css_selector('input[name=id1]') + + @property + def base_mac(self): + return self.parent.find_element_by_css_selector('input[name=base_mac]') + + @property + def floating_start(self): + return self.parent.\ + find_element_by_css_selector('.floating-row input[name=range0]') + + @property + def floating_end(self): + return self.parent.\ + find_element_by_css_selector('.floating-row input[name=range1]') + + @property + def cidr(self): + return self.parent.find_element_by_css_selector('input[name=cidr-int]') + + @property + def gateway(self): + return self.parent.find_element_by_css_selector('input[name=gateway]') + + @property + def nameserver0(self): + return self.parent.\ + find_element_by_css_selector('.nameservers-row input[name=range0]') + + @property + def nameserver1(self): + return self.parent.\ + find_element_by_css_selector('.nameservers-row input[name=range1]') + + +class IpRange(PageObject): + + @property + def start(self): + return self.parent.find_element_by_css_selector('input[name=range0]') + + @property + def end(self): + return self.parent.find_element_by_css_selector('input[name=range1]') + + @property + def icon_plus(self): + return self.parent.find_element_by_css_selector('button.ip-ranges-add') + + @property + def icon_minus(self): + return self.parent.\ + find_element_by_css_selector('button.ip-ranges-delete') diff --git a/fuelweb_ui_test/pageobjects/node_disks_settings.py b/fuelweb_ui_test/pageobjects/node_disks_settings.py new file mode 100644 index 000000000..7e50943b4 --- /dev/null +++ b/fuelweb_ui_test/pageobjects/node_disks_settings.py @@ -0,0 +1,121 @@ +from pageobjects.base import PageObject +from pageobjects.settings import SettingsFooter + + +class DisksSettings(PageObject, SettingsFooter): + + @property + def disks(self): + elements = self.parent.\ + find_elements_by_css_selector('div.node-disks > div') + return [Disk(el) for el in elements] + + +class Disk(PageObject): + + XPATH_INFORMATION_ITEM = './/div[@class="disk-map-details-item" and ' \ + 'div[@class="disk-map-details-name"]="{}"]' \ + '/div[@class="disk-map-details-parameter"]' + + def __init__(self, element): + PageObject.__init__(self, element) + + @property + def volume_os(self): + return Volume(self.parent.find_element_by_css_selector( + 'div.volume-group.os > .toggle-volume')) + + @property + def volume_image(self): + return Volume(self.parent.find_element_by_css_selector( + 'div.volume-group.image > .toggle-volume')) + + @property + def volume_storage(self): + return Volume(self.parent.find_element_by_css_selector( + 'div.volume-group.vm > .toggle-volume')) + + @property + def volume_unallocated(self): + return Volume(self.parent.find_element_by_css_selector( + 'div.volume-group.unallocated > .toggle-volume')) + + @property + def volume_group_os(self): + return VolumeGroup(self.parent.find_element_by_css_selector( + 'div.volume-group-box[data-volume=os]' + )) + + @property + def volume_group_image(self): + return VolumeGroup(self.parent.find_element_by_css_selector( + 'div.volume-group-box[data-volume=image]' + )) + + @property + def volume_group_storage(self): + return VolumeGroup(self.parent.find_element_by_css_selector( + 'div.volume-group-box[data-volume=vm]' + )) + + @property + def details_panel(self): + return self.parent.find_element_by_css_selector('.disk-map-details') + + @property + def name(self): + return self.parent.find_element_by_xpath( + self.XPATH_INFORMATION_ITEM.format('name')) + + @property + def model(self): + return self.parent.find_element_by_xpath( + self.XPATH_INFORMATION_ITEM.format('model')) + + @property + def disk(self): + return self.parent.find_element_by_xpath( + self.XPATH_INFORMATION_ITEM.format('disk')) + + @property + def size(self): + return self.parent.find_element_by_xpath( + self.XPATH_INFORMATION_ITEM.format('size')) + + +class Volume(PageObject): + + def __init__(self, element): + PageObject.__init__(self, element) + + @property + def name(self): + return self.parent.find_element_by_css_selector('.volume-group-name') + + @property + def size(self): + return self.parent.find_element_by_css_selector('.volume-group-size') + + @property + def close_cross(self): + return self.parent.\ + find_element_by_xpath('./../div[contains(@class, "close-btn")]') + + +class VolumeGroup(PageObject): + + def __init__(self, element): + PageObject.__init__(self, element) + + @property + def name(self): + return self.parent.\ + find_element_by_css_selector('.volume-group-box-name') + + @property + def use_all(self): + return self.parent.find_element_by_css_selector('.use-all-allowed') + + @property + def input(self): + return self.parent.find_element_by_tag_name('input') diff --git a/fuelweb_ui_test/pageobjects/node_interfaces_settings.py b/fuelweb_ui_test/pageobjects/node_interfaces_settings.py new file mode 100644 index 000000000..19aa6fceb --- /dev/null +++ b/fuelweb_ui_test/pageobjects/node_interfaces_settings.py @@ -0,0 +1,32 @@ +from pageobjects.base import PageObject +from pageobjects.settings import SettingsFooter + + +class InterfacesSettings(PageObject, SettingsFooter): + + @property + def interfaces(self): + elements = self.parent.\ + find_elements_by_css_selector('.physical-network-box') + return [Interface(e) for e in elements] + + +class Interface(PageObject): + + def __init__(self, element): + PageObject.__init__(self, element) + + @property + def info(self): + return self.parent.find_element_by_css_selector('.network-info-box') + + @property + def networks_box(self): + return self.parent.find_element_by_css_selector('.logical-network-box') + + @property + def networks(self): + elements = self.parent.\ + find_elements_by_css_selector('.logical-network-item') + return {el.find_element_by_css_selector('.name').text.lower(): el + for el in elements} diff --git a/fuelweb_ui_test/pageobjects/nodes.py b/fuelweb_ui_test/pageobjects/nodes.py new file mode 100644 index 000000000..4d7d82a23 --- /dev/null +++ b/fuelweb_ui_test/pageobjects/nodes.py @@ -0,0 +1,176 @@ +from selenium.webdriver.support.select import Select +from pageobjects.base import PageObject +from pageobjects.base import Popup + + +class Nodes(PageObject): + + @property + def info_icon(self): + return self.parent.find_element_by_css_selector('i.icon-info-circled') + + @property + def env_name(self): + return self.parent.\ + find_element_by_css_selector('span.btn-cluster-details') + + @property + def deploy_changes(self): + return self.parent.find_element_by_css_selector('button.deploy-btn') + + @property + def discard_changes(self): + return self.parent.find_element_by_css_selector('button.rollback') + + @property + def progress_deployment(self): + return self.parent.find_element_by_css_selector('.progress-deploy') + + @property + def env_details(self): + return self.parent.find_element_by_css_selector('ul.cluster-details') + + @property + def group_by(self): + return Select( + self.parent.find_element_by_css_selector('select[name=grouping]')) + + @property + def add_nodes(self): + return self.parent.find_element_by_css_selector('button.btn-add-nodes') + + @property + def delete_nodes(self): + return self.parent.\ + find_element_by_css_selector('button.btn-delete-nodes') + + @property + def edit_roles(self): + return self.parent.\ + find_element_by_css_selector('button.btn-edit-nodes') + + @property + def configure_interfaces(self): + return self.parent.\ + find_element_by_css_selector('button.btn-configure-interfaces') + + @property + def apply_changes(self): + return self.parent.find_element_by_css_selector('button.btn-apply') + + @property + def configure_disks(self): + return self.parent.\ + find_element_by_css_selector('button.btn-configure-disks') + + @property + def nodes(self): + elements = self.parent.find_elements_by_css_selector('.node-container') + return [NodeContainer(el) for el in elements] + + @property + def nodes_discovered(self): + elements = self.parent.\ + find_elements_by_css_selector('.node-container.discover') + return [NodeContainer(el) for el in elements] + + @property + def nodes_offline(self): + elements = self.parent.\ + find_elements_by_css_selector('.node-container.node-offline') + return [NodeContainer(el) for el in elements] + + @property + def nodes_error(self): + elements = self.parent.\ + find_elements_by_css_selector('.node-container.error') + return [NodeContainer(el) for el in elements] + + @property + def select_all(self): + return self.parent.\ + find_element_by_css_selector('[name=select-nodes-common]') + + @property + def select_all_in_group(self): + return self.parent.\ + find_elements_by_css_selector('[name=select-node-group]') + + @property + def node_groups(self): + elements = self.parent.find_elements_by_css_selector('.node-groups') + return [Nodes(el) for el in elements] + + +class NodeContainer(PageObject): + + @property + def name(self): + return self.parent.find_element_by_css_selector('div.name > p') + + @property + def name_input(self): + return self.parent.find_element_by_css_selector('div.name > input') + + @property + def checkbox(self): + return self.parent.find_element_by_css_selector('div.node-checkbox') + + @property + def roles(self): + return self.parent.find_element_by_css_selector('div.role-list') + + @property + def details(self): + return self.parent.find_element_by_css_selector('.node-details') + + @property + def status(self): + return self.parent.find_element_by_css_selector('.node-status-label') + + +class RolesPanel(PageObject): + + XPATH_ROLE = '//label[contains(., "{}")]/input' + + @property + def controller(self): + return self.parent.\ + find_element_by_xpath(self.XPATH_ROLE.format('Controller')) + + @property + def compute(self): + return self.parent.\ + find_element_by_xpath(self.XPATH_ROLE.format('Compute')) + + @property + def cinder(self): + return self.parent.\ + find_element_by_xpath(self.XPATH_ROLE.format('Cinder')) + + @property + def ceph_osd(self): + return self.parent.\ + find_element_by_xpath(self.XPATH_ROLE.format('Ceph OSD')) + + +class NodeInfo(Popup): + + @property + def edit_networks(self): + return self.parent.find_element_by_css_selector('.btn-edit-networks') + + @property + def edit_disks(self): + return self.parent.find_element_by_css_selector('.btn-edit-disks') + + @property + def close(self): + return self.parent.find_element_by_css_selector('.node-modal-close') + + +class DeleteNodePopup(Popup): + + @property + def delete(self): + return self.parent.find_element_by_css_selector('.btn-delete') diff --git a/fuelweb_ui_test/pageobjects/releases.py b/fuelweb_ui_test/pageobjects/releases.py new file mode 100644 index 000000000..a425c5a5c --- /dev/null +++ b/fuelweb_ui_test/pageobjects/releases.py @@ -0,0 +1,36 @@ +from pageobjects.base import PageObject + + +class Releases(PageObject): + + @property + def dict(self): + elements = self.parent.\ + find_elements_by_css_selector('div.table-releases-box tbody > tr') + return {Release(el).name.text: Release(el) for el in elements} + + @property + def rhel_setup(self): + return self.parent.find_element_by_css_selector('.btn-rhel-setup') + + +class Release(PageObject): + + def __init__(self, element): + PageObject.__init__(self, parent=element) + + @property + def name(self): + return self.parent.find_element_by_css_selector('.release-name') + + @property + def version(self): + return self.parent.find_element_by_css_selector('.release-version') + + @property + def status(self): + return self.parent.find_element_by_css_selector('.release-status') + + @property + def download_progress(self): + return self.parent.find_element_by_css_selector('.download_progress') diff --git a/fuelweb_ui_test/pageobjects/settings.py b/fuelweb_ui_test/pageobjects/settings.py new file mode 100644 index 000000000..bc83e39c0 --- /dev/null +++ b/fuelweb_ui_test/pageobjects/settings.py @@ -0,0 +1,192 @@ +from pageobjects.base import PageObject + + +class SettingsFooter(PageObject): + + @property + def back_to_node_list(self): + return self.parent.\ + find_element_by_xpath('//button[text()="Back To Node List"]') + + @property + def load_defaults(self): + return self.parent.\ + find_element_by_xpath('//button[text()="Load Defaults"]') + + @property + def cancel_changes(self): + return self.parent.\ + find_element_by_xpath('//button[text()="Cancel Changes"]') + + @property + def save_settings(self): + return self.parent.\ + find_element_by_xpath('//button[text()="Save Settings"]') + + @property + def apply(self): + return self.parent.find_element_by_xpath('//button[text()="Apply"]') + + +class Settings(PageObject, SettingsFooter): + + @property + def username(self): + return self.parent.find_element_by_name('user') + + @property + def password(self): + return self.parent.find_element_by_name('password') + + @property + def show_password(self): + return self.parent.\ + find_element_by_xpath('//div[input[@name="password"]]/span') + + @property + def tenant(self): + return self.parent.find_element_by_name('tenant') + + @property + def email(self): + return self.parent.find_element_by_name('email') + + @property + def install_savanna(self): + return self.parent.\ + find_element_by_xpath(self.XPATH_CHECKBOX.format('savanna')) + + @property + def install_murano(self): + return self.parent.\ + find_element_by_xpath(self.XPATH_CHECKBOX.format('murano')) + + @property + def install_ceilometer(self): + return self.parent.\ + find_element_by_xpath(self.XPATH_CHECKBOX.format('ceilometer')) + + @property + def debug(self): + return self.parent.\ + find_element_by_xpath(self.XPATH_CHECKBOX.format('debug')) + + @property + def hypervisor_kvm(self): + return self.parent.\ + find_element_by_xpath(self.XPATH_RADIO.format('libvirt_type', + 'kvm')) + + @property + def hypervisor_qemu(self): + return self.parent.\ + find_element_by_xpath(self.XPATH_RADIO.format('libvirt_type', + 'qemu')) + + @property + def assign_ip(self): + return self.parent.\ + find_element_by_xpath( + self.XPATH_CHECKBOX.format('auto_assign_floating_ip')) + + @property + def filter_scheduler(self): + return self.parent.find_element_by_xpath( + self.XPATH_RADIO.format( + 'compute_scheduler_driver', + 'nova.scheduler.filter_scheduler.FilterScheduler')) + + @property + def simple_scheduler(self): + return self.parent.find_element_by_xpath( + self.XPATH_RADIO.format( + 'compute_scheduler_driver', + 'nova.scheduler.simple.SimpleScheduler')) + + @property + def vlan_splinters(self): + return self.parent.\ + find_element_by_xpath(self.XPATH_CHECKBOX.format('vlan_splinters')) + + @property + def vlan_splinters_disabled(self): + return self.parent.\ + find_element_by_xpath( + self.XPATH_RADIO.format('vlan_splinters', 'disabled')) + + @property + def vlan_splinters_soft(self): + return self.parent.\ + find_element_by_xpath( + self.XPATH_RADIO.format('vlan_splinters', 'soft')) + + @property + def vlan_splinters_hard(self): + return self.parent.\ + find_element_by_xpath( + self.XPATH_RADIO.format('vlan_splinters', 'hard')) + + @property + def use_cow_images(self): + return self.parent.\ + find_element_by_xpath( + self.XPATH_CHECKBOX.format('use_cow_images')) + + @property + def start_guests(self): + return self.parent.\ + find_element_by_xpath( + self.XPATH_CHECKBOX.format('start_guests_on_host_boot')) + + @property + def auth_key(self): + return self.parent.find_element_by_name('auth_key') + + @property + def syslog_server(self): + return self.parent.find_element_by_name('syslog_server') + + @property + def syslog_port(self): + return self.parent.find_element_by_name('syslog_port') + + @property + def syslog_udp(self): + return self.parent.find_element_by_xpath( + self.XPATH_RADIO.format( + 'syslog_transport', 'udp')) + + @property + def syslog_tcp(self): + return self.parent.find_element_by_xpath( + self.XPATH_RADIO.format( + 'syslog_transport', 'tcp')) + + @property + def cinder_for_volumes(self): + return self.parent.\ + find_element_by_xpath(self.XPATH_CHECKBOX.format('volumes_lvm')) + + @property + def ceph_for_volumes(self): + return self.parent.\ + find_element_by_xpath(self.XPATH_CHECKBOX.format('volumes_ceph')) + + @property + def ceph_for_images(self): + return self.parent.\ + find_element_by_xpath(self.XPATH_CHECKBOX.format('images_ceph')) + + @property + def ceph_ephemeral(self): + return self.parent.\ + find_element_by_xpath(self.XPATH_CHECKBOX.format('ephemeral_ceph')) + + @property + def ceph_rados_gw(self): + return self.parent.\ + find_element_by_xpath(self.XPATH_CHECKBOX.format('objects_ceph')) + + @property + def ceph_factor(self): + return self.parent.find_element_by_name('osd_pool_size') diff --git a/fuelweb_ui_test/pageobjects/support.py b/fuelweb_ui_test/pageobjects/support.py new file mode 100644 index 000000000..09690b6a5 --- /dev/null +++ b/fuelweb_ui_test/pageobjects/support.py @@ -0,0 +1,25 @@ +from pageobjects.base import PageObject + + +class Support(PageObject): + + @property + def register_fuel(self): + return self.parent.find_element_by_link_text('Register Fuel') + + @property + def contact_support(self): + return self.parent.find_element_by_link_text('Contact Support') + + @property + def generate_snapshot(self): + return self.parent.find_element_by_css_selector('button.download-logs') + + @property + def download_snapshot(self): + return self.parent.\ + find_element_by_css_selector('span.donwload-logs-link > a') + + @property + def view_capacity_audit(self): + return self.parent.find_element_by_link_text('View Capacity Audit') diff --git a/fuelweb_ui_test/pageobjects/tabs.py b/fuelweb_ui_test/pageobjects/tabs.py new file mode 100644 index 000000000..eff71abaa --- /dev/null +++ b/fuelweb_ui_test/pageobjects/tabs.py @@ -0,0 +1,36 @@ +from pageobjects.base import PageObject + + +class Tabs(PageObject): + + XPATH_TAB = '//ul/li/a[div/text()="{}"]' + + @property + def nodes(self): + return self.parent.\ + find_element_by_xpath(self.XPATH_TAB.format('Nodes')) + + @property + def networks(self): + return self.parent.\ + find_element_by_xpath(self.XPATH_TAB.format('Networks')) + + @property + def settings(self): + return self.parent.\ + find_element_by_xpath(self.XPATH_TAB.format('Settings')) + + @property + def logs(self): + return self.parent.\ + find_element_by_xpath(self.XPATH_TAB.format('Logs')) + + @property + def health_check(self): + return self.parent.\ + find_element_by_xpath(self.XPATH_TAB.format('Health Check')) + + @property + def actions(self): + return self.parent.\ + find_element_by_xpath(self.XPATH_TAB.format('Actions')) diff --git a/fuelweb_ui_test/requirements.txt b/fuelweb_ui_test/requirements.txt new file mode 100644 index 000000000..954f0db0b --- /dev/null +++ b/fuelweb_ui_test/requirements.txt @@ -0,0 +1 @@ +selenium \ No newline at end of file diff --git a/fuelweb_ui_test/settings.py b/fuelweb_ui_test/settings.py new file mode 100644 index 000000000..3b8b13644 --- /dev/null +++ b/fuelweb_ui_test/settings.py @@ -0,0 +1,39 @@ +import os +import re + +BROWSER = os.environ.get('BROWSER', 'firefox') + +# download it here http://chromedriver.storage.googleapis.com/index.html +CHROME_EXECUTABLE_PATH = \ + os.environ.get('CHROME_EXECUTABLE_PATH', '/usr/bin/google-chrome') + +NAILGUN_FOLDER = os.environ.get('NAILGUN_FOLDER') + +FOLDER_SCREEN_EXPECTED = os.environ.get( + 'FOLDER_SCREEN_EXPECTED', '/home/nfedotov/testscreens/expected') +FOLDER_SCREEN_CURRENT = os.environ.get( + 'FOLDER_SCREEN_CURRENT', '/home/nfedotov/testscreens/current') + +OPENSTACK_RELEASE_CENTOS = os.environ.get( + 'OPENSTACK_RELEASE_CENTOS', 'Havana on CentOS 6.4 (2013.2.1)') +OPENSTACK_RELEASE_REDHAT = os.environ.get( + 'OPENSTACK_RELEASE_REDHAT', 'RHOS 3.0 for RHEL 6.4 (2013.1.2)') +OPENSTACK_RELEASE_UBUNTU = os.environ.get( + 'OPENSTACK_RELEASE_UBUNTU', 'Havana on Ubuntu 12.04 (2013.2.1)') + +REDHAT_USERNAME = os.environ.get('REDHAT_USERNAME', 'rheltest') +REDHAT_PASSWORD = os.environ.get('REDHAT_PASSWORD', 'password') +REDHAT_SATELLITE = os.environ.get('REDHAT_SATELLITE', 'satellite.example.com') +REDHAT_ACTIVATION_KEY = os.environ.get( + 'REDHAT_ACTIVATION_KEY', '1234567890') + +openstack_name = lambda release: re.sub('\s\\(.*?\\)$', '', release) +OPENSTACK_CENTOS = openstack_name(OPENSTACK_RELEASE_CENTOS) +OPENSTACK_REDHAT = openstack_name(OPENSTACK_RELEASE_REDHAT) +OPENSTACK_UBUNTU = openstack_name(OPENSTACK_RELEASE_UBUNTU) + +NAILGUN_FIXTURES = os.environ.get('NAILGUN_FIXTURES', '') + +URL_HOME = os.environ.get('URL_HOME', 'http://localhost:8000/') + +SELENIUM_IMPLICIT_WAIT = os.environ.get('SELENIUM_IMPLICIT_WAIT', 10) diff --git a/fuelweb_ui_test/tests/__init__.py b/fuelweb_ui_test/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/fuelweb_ui_test/tests/base.py b/fuelweb_ui_test/tests/base.py new file mode 100644 index 000000000..4ac950b8c --- /dev/null +++ b/fuelweb_ui_test/tests/base.py @@ -0,0 +1,91 @@ +import base64 +from io import BytesIO +from unittest import TestCase +from PIL import Image +import operator +import math +import time +from selenium.common.exceptions import NoSuchElementException +import browser +from pageobjects.header import Header +from fuelweb_ui_test.settings import FOLDER_SCREEN_CURRENT +from fuelweb_ui_test.settings import FOLDER_SCREEN_EXPECTED +from fuelweb_ui_test.settings import NAILGUN_FIXTURES +from fuelweb_ui_test.settings import URL_HOME + + +class BaseTestCase(TestCase): + + @classmethod + def setUpClass(cls): + browser.start_driver() + cls.clear_nailgun_database() + + @classmethod + def tearDownClass(cls): + browser.quit_driver() + + def setUp(self): + self.get_home() + + @staticmethod + def get_home(): + for i in range(5): + try: + browser.driver.get(URL_HOME) + Header().logo.is_displayed() + browser.driver.execute_script('jQuery.fx.off = true') + browser.driver.execute_script(''' + $('head').append( + '') + '''.replace('\n', '')) + break + except NoSuchElementException: + pass + + @staticmethod + def refresh(): + for i in range(5): + try: + browser.driver.refresh() + time.sleep(0.5) + Header().logo.is_displayed() + break + except NoSuchElementException: + pass + + @staticmethod + def clear_nailgun_database(): + from nailgun.db import dropdb + from nailgun.db import syncdb + from nailgun.db.sqlalchemy import fixman + dropdb() + syncdb() + fixman.upload_fixtures() + for fixture in NAILGUN_FIXTURES.split(':'): + if fixture == '': + continue + with open(fixture, "r") as fileobj: + fixman.upload_fixture(fileobj) + + def assert_screen(self, name): + img_exp = Image.open('{}/{}.png'.format(FOLDER_SCREEN_EXPECTED, name)) + + img_cur_base64 = browser.driver.get_screenshot_as_base64() + img_cur = Image.open(BytesIO(base64.decodestring(img_cur_base64))) + img_cur.save('{}/{}.png'.format(FOLDER_SCREEN_CURRENT, name)) + + h1 = img_exp.histogram() + h2 = img_cur.histogram() + rms = math.sqrt( + reduce(operator.add, + map(lambda a, b: (a - b) ** 2, h1, h2)) / len(h1)) + + self.assertNotEqual(rms == 0, 'Screen valid') diff --git a/fuelweb_ui_test/tests/preconditions.py b/fuelweb_ui_test/tests/preconditions.py new file mode 100644 index 000000000..90795a4df --- /dev/null +++ b/fuelweb_ui_test/tests/preconditions.py @@ -0,0 +1,79 @@ +import time +from pageobjects.environments import Environments +from pageobjects.environments import Wizard +from pageobjects.environments import DeployChangesPopup +from pageobjects.header import TaskResultAlert +from pageobjects.nodes import Nodes, RolesPanel +from tests.base import BaseTestCase +from fuelweb_ui_test.settings import OPENSTACK_CENTOS +from fuelweb_ui_test.settings import OPENSTACK_RELEASE_CENTOS + + +class Environment: + @staticmethod + def simple_flat(name=OPENSTACK_CENTOS, + release=OPENSTACK_RELEASE_CENTOS): + BaseTestCase.get_home() + Environments().create_cluster_box.click() + with Wizard() as w: + w.name.send_keys(name) + w.release.select_by_visible_text(release) + for i in range(6): + w.next.click() + w.create.click() + w.wait_until_exists() + + @staticmethod + def ha_flat(name=OPENSTACK_CENTOS, + release=OPENSTACK_RELEASE_CENTOS): + BaseTestCase.get_home() + Environments().create_cluster_box.click() + with Wizard() as w: + w.name.send_keys(name) + w.release.select_by_visible_text(release) + w.next.click() + w.mode_ha_compact.click() + for i in range(5): + w.next.click() + w.create.click() + w.wait_until_exists() + + @staticmethod + def simple_neutron_gre(name=OPENSTACK_CENTOS, + release=OPENSTACK_RELEASE_CENTOS): + BaseTestCase.get_home() + Environments().create_cluster_box.click() + with Wizard() as w: + w.name.send_keys(name) + w.release.select_by_visible_text(release) + for i in range(3): + w.next.click() + w.network_neutron_gre.click() + for i in range(3): + w.next.click() + w.create.click() + w.wait_until_exists() + + @staticmethod + def deploy_nodes(controllers=0, computes=0, cinders=0, cephs=0): + def add(role, amount): + if amount < 1: + return + + Nodes().add_nodes.click() + time.sleep(1) + for i in range(amount): + Nodes().nodes_discovered[i].checkbox.click() + getattr(RolesPanel(), role).click() + Nodes().apply_changes.click() + time.sleep(1) + + add('controller', controllers) + add('compute', computes) + add('cinder', cinders) + add('ceph_osd', cephs) + + time.sleep(1) + Nodes().deploy_changes.click() + DeployChangesPopup().deploy.click() + TaskResultAlert().close.click() diff --git a/fuelweb_ui_test/tests/test_adding_nodes.py b/fuelweb_ui_test/tests/test_adding_nodes.py new file mode 100644 index 000000000..dab6ca410 --- /dev/null +++ b/fuelweb_ui_test/tests/test_adding_nodes.py @@ -0,0 +1,312 @@ +import time +from selenium.common.exceptions import NoSuchElementException +from selenium.webdriver.common.keys import Keys +from pageobjects.environments import Environments +from pageobjects.header import Header +from pageobjects.nodes import Nodes, NodeInfo, RolesPanel +from pageobjects.tabs import Tabs +from tests import preconditions +from tests.base import BaseTestCase +from tests.test_roles import ROLE_CONTROLLER, ROLE_CEPH, ROLE_CINDER + + +class TestNodesAddPage(BaseTestCase): + + @classmethod + def setUpClass(cls): + BaseTestCase.setUpClass() + preconditions.Environment.simple_flat() + + def setUp(self): + BaseTestCase.setUp(self) + Environments().create_cluster_boxes[0].click() + Nodes().add_nodes.click() + time.sleep(1) + + def test_discovered_nodes_enabled(self): + with Nodes()as n: + for node in n.nodes_discovered: + self.assertTrue( + node.checkbox. + find_element_by_tag_name('input').is_enabled(), + 'Node enabled') + + def test_offline_nodes_disabled(self): + with Nodes()as n: + for node in n.nodes_offline: + self.assertFalse( + node.checkbox. + find_element_by_tag_name('input').is_enabled(), + 'Node disabled') + + def test_error_nodes_disabled(self): + with Nodes()as n: + for node in n.nodes_error: + self.assertFalse( + node.checkbox. + find_element_by_tag_name('input').is_enabled(), + 'Node disabled') + + def test_select_all(self): + with Nodes()as n: + n.select_all.click() + for selects in n.select_all_in_group: + self.assertTrue(selects.is_selected(), + 'Select all in group is selected') + for node in n.nodes_discovered: + self.assertTrue( + node.checkbox. + find_element_by_tag_name('input').is_selected(), + 'Discovered node is selected') + for node in n.nodes_offline: + self.assertFalse( + node.checkbox. + find_element_by_tag_name('input').is_selected(), + 'Offline node is not selected') + for node in n.nodes_error: + self.assertFalse( + node.checkbox. + find_element_by_tag_name('input').is_selected(), + 'Error node is not selected') + + def test_select_all_in_group(self): + with Nodes()as n: + for i, group in enumerate(n.node_groups): + group.select_all_in_group[0].click() + for node in group.nodes_discovered: + self.assertTrue( + node.checkbox.find_element_by_tag_name('input'). + is_selected(), + 'Discovered node is selected') + self.assertTrue( + n.select_all.is_selected(), '"Select all" is checked') + + def test_select_all_selecting_nodes_one_by_one(self): + with Nodes()as n: + for i, group in enumerate(n.node_groups): + for node in group.nodes_discovered: + node.checkbox.click() + self.assertTrue( + group.select_all_in_group[0].is_selected(), + '"Select all in group" is checked') + self.assertTrue( + n.select_all.is_selected(), '"Select all" is checked') + + def test_selecting_nodes_clicking_them_discovered(self): + with Nodes()as n: + for node in n.nodes_discovered: + node.parent.click() + self.assertTrue( + node.checkbox. + find_element_by_tag_name('input').is_selected(), + 'Discovered node is selected') + + def test_selecting_nodes_clicking_them_offline(self): + with Nodes()as n: + for node in n.nodes_offline: + node.parent.click() + self.assertFalse( + node.checkbox. + find_element_by_tag_name('input').is_selected(), + 'Offline node is not selected') + + def test_selecting_nodes_clicking_them_error(self): + with Nodes()as n: + for node in n.nodes_error: + node.parent.click() + self.assertFalse( + node.checkbox. + find_element_by_tag_name('input').is_selected(), + 'Error node is not selected') + + def test_node_info_popup(self): + def test_popup(node): + node.details.click() + with NodeInfo() as details: + self.assertEqual( + node.name.text, details.header.text, + 'Node name') + details.close.click() + details.wait_until_exists() + + with Nodes()as n: + test_popup(n.nodes_discovered[0]) + test_popup(n.nodes_offline[0]) + test_popup(n.nodes_error[0]) + + def test_renaming_node(self): + name = 'new node name' + with Nodes()as n: + old_name = n.nodes_discovered[0].name.text + n.nodes_discovered[0].name.click() + self.assertTrue( + n.nodes_discovered[0].name_input.is_displayed(), + 'input visible') + n.nodes_discovered[0].name_input.send_keys(name) + n.nodes_discovered[0].parent.click() + self.assertRaises( + NoSuchElementException, getattr, n.nodes_discovered[0], + 'name_input') + self.assertEqual( + old_name, n.nodes_discovered[0].name.text, + 'Node has old name') + n.nodes_discovered[0].name.click() + n.nodes_discovered[0].name_input.send_keys(name) + n.nodes_discovered[0].name_input.send_keys(Keys.ENTER) + time.sleep(2) + self.assertEqual( + name, Nodes().nodes_discovered[0].name.text, + 'New node name') + + +class TestAddingNodes(BaseTestCase): + + @classmethod + def setUpClass(cls): + BaseTestCase.setUpClass() + + def setUp(self): + BaseTestCase.clear_nailgun_database() + preconditions.Environment.simple_flat() + BaseTestCase.setUp(self) + Environments().create_cluster_boxes[0].click() + Nodes().add_nodes.click() + time.sleep(1) + + def test_adding_node_single_role(self): + name = Nodes().nodes_discovered[0].name.text + Nodes().nodes_discovered[0].checkbox.click() + RolesPanel().controller.click() + Nodes().apply_changes.click() + time.sleep(1) + with Nodes() as n: + self.assertTrue(n.env_name.is_displayed()) + self.assertEqual(len(n.nodes), 1, 'Nodes amount') + self.assertEqual(n.nodes[0].name.text, name, 'Node name') + self.assertIn(ROLE_CONTROLLER, n.nodes[0].roles.text, 'Node role') + + def test_adding_node_multiple_roles(self): + Nodes().nodes_discovered[0].checkbox.click() + with RolesPanel() as r: + r.controller.click() + r.cinder.click() + r.ceph_osd.click() + Nodes().apply_changes.click() + time.sleep(1) + with Nodes() as n: + self.assertTrue(n.env_name.is_displayed()) + self.assertIn(ROLE_CONTROLLER, n.nodes[0].roles.text, + 'Node first role') + self.assertIn(ROLE_CINDER, n.nodes[0].roles.text, + 'Node second role') + self.assertIn(ROLE_CEPH, n.nodes[0].roles.text, + 'Node third role') + + def test_edit_role_add_new_role(self): + # Add node with controller role + Nodes().nodes_discovered[0].checkbox.click() + RolesPanel().controller.click() + Nodes().apply_changes.click() + time.sleep(1) + # Add cinder role + with Nodes() as n: + n.nodes[0].checkbox.click() + n.edit_roles.click() + RolesPanel().cinder.click() + Nodes().apply_changes.click() + time.sleep(1) + with Nodes() as n: + self.assertIn(ROLE_CONTROLLER, n.nodes[0].roles.text, + 'Controller role') + self.assertIn(ROLE_CINDER, n.nodes[0].roles.text, + 'Cinder role') + + def test_edit_role_change_role(self): + # Add node with controller role + Nodes().nodes_discovered[0].checkbox.click() + RolesPanel().controller.click() + Nodes().apply_changes.click() + time.sleep(1) + # Remove controller, Add cinder and ceph-osd roles + with Nodes() as n: + n.nodes[0].checkbox.click() + n.edit_roles.click() + with RolesPanel() as r: + r.controller.click() + r.cinder.click() + r.ceph_osd.click() + Nodes().apply_changes.click() + time.sleep(1) + with Nodes() as n: + self.assertNotIn(ROLE_CONTROLLER, n.nodes[0].roles.text, + 'Controller role has been removed') + self.assertIn(ROLE_CINDER, n.nodes[0].roles.text, + 'Cinder role') + self.assertIn(ROLE_CEPH, n.nodes[0].roles.text, + 'Ceph-osd role') + + def test_unallocated_nodes_counter(self): + initial = int(Header().unallocated_nodes.text) + discovered = len(Nodes().nodes_discovered) + + Tabs().nodes.click() + for i in range(discovered): + Nodes().add_nodes.click() + Nodes().nodes_discovered[0].checkbox.click() + RolesPanel().compute.click() + Nodes().apply_changes.click() + time.sleep(1) + + self.assertEqual( + str(initial - i - 1), Header().unallocated_nodes.text, + 'Unallocated nodes amount') + + +class TestGroupBy(BaseTestCase): + + @classmethod + def setUpClass(cls): + BaseTestCase.setUpClass() + BaseTestCase.get_home() + preconditions.Environment().simple_flat() + Environments().create_cluster_boxes[0].click() + + # Add controller + Nodes().add_nodes.click() + time.sleep(1) + Nodes().nodes_discovered[0].checkbox.click() + RolesPanel().controller.click() + Nodes().apply_changes.click() + time.sleep(1) + + # Add other discovered nodes as compute + Nodes().add_nodes.click() + time.sleep(1) + for n in Nodes().nodes_discovered: + n.checkbox.click() + RolesPanel().compute.click() + Nodes().apply_changes.click() + + def setUp(self): + BaseTestCase.setUp(self) + Environments().create_cluster_boxes[0].click() + + def _test_group_by(self, group_by, nodes_in_groups): + with Nodes() as n: + n.group_by.select_by_visible_text(group_by) + time.sleep(1) + self.assertEqual( + len(nodes_in_groups), len(n.node_groups), 'Groups amount') + for i, group in enumerate(n.node_groups): + self.assertEqual( + nodes_in_groups[i], len(group.nodes), + 'Group #{0} has {1} nodes'.format(i, nodes_in_groups[i])) + + def test_group_by_roles(self): + self._test_group_by('Roles', [1, 5]) + + def test_group_by_hardware_info(self): + self._test_group_by('Hardware Info', [1, 1, 2, 1, 1]) + + def test_group_by_roles_and_hardware_info(self): + self._test_group_by('Roles and hardware info', [1, 2, 1, 1, 1]) diff --git a/fuelweb_ui_test/tests/test_configure_disks.py b/fuelweb_ui_test/tests/test_configure_disks.py new file mode 100644 index 000000000..3ff95f539 --- /dev/null +++ b/fuelweb_ui_test/tests/test_configure_disks.py @@ -0,0 +1,216 @@ +import random +import time +from pageobjects.base import ConfirmPopup +from pageobjects.environments import Environments +from pageobjects.node_disks_settings import DisksSettings +from pageobjects.nodes import Nodes +from pageobjects.nodes import RolesPanel +from pageobjects.nodes import NodeInfo +from pageobjects.tabs import Tabs +from tests import preconditions +from tests.base import BaseTestCase + + +class TestConfigureDisks(BaseTestCase): + + @classmethod + def setUpClass(cls): + BaseTestCase.setUpClass() + preconditions.Environment.simple_flat() + Environments().create_cluster_boxes[0].click() + Nodes().add_nodes.click() + time.sleep(1) + Nodes().nodes_discovered[0].checkbox.click() + RolesPanel().controller.click() + Nodes().apply_changes.click() + + def setUp(self): + BaseTestCase.setUp(self) + Environments().create_cluster_boxes[0].click() + Nodes().nodes[0].details.click() + NodeInfo().edit_disks.click() + time.sleep(1) + + def test_volume_animation(self): + with DisksSettings() as s: + s.disks[0].volume_os.parent.click() + time.sleep(1) + self.assertTrue( + s.disks[0].details_panel.is_displayed(), + 'details panel is expanded') + + s.disks[0].volume_os.parent.click() + time.sleep(1) + self.assertFalse( + s.disks[0].details_panel.is_displayed(), + 'details panel is expanded') + + def test_remove_volume_cross(self): + with DisksSettings() as s: + s.disks[0].volume_image.parent.click() + time.sleep(1) + s.disks[0].volume_image.close_cross.click() + self.assertFalse( + s.disks[0].volume_image.close_cross.is_displayed(), + 'Image volume has been removed') + self.assertEqual( + '0', + s.disks[0].volume_group_image.input.get_attribute('value'), + 'image volume size is 0') + + def test_use_all_allowed(self): + with DisksSettings() as s: + s.disks[1].volume_image.parent.click() + time.sleep(1) + s.disks[1].volume_image.close_cross.click() + unallocated = s.disks[1].volume_unallocated.size.text + + s.disks[1].volume_group_os.use_all.click() + self.assertEqual( + unallocated, s.disks[1].volume_os.size.text, + 'Base system uses all allowed space' + ) + s.disks[1].volume_os.close_cross.click() + + s.disks[1].volume_group_image.use_all.click() + self.assertEqual( + unallocated, s.disks[1].volume_image.size.text, + 'Image storage uses all allowed space' + ) + + def test_type_volume_size(self): + values = [random.randint(100000, 200000) for i in range(3)] + with DisksSettings() as s: + s.disks[0].volume_image.parent.click() + time.sleep(1) + for v in values: + s.disks[0].volume_group_image.input.clear() + s.disks[0].volume_group_image.input.send_keys(v) + time.sleep(0.5) + exp = '{0:.1f} GB'.format((float(v) / 1024)) + cur = s.disks[0].volume_image.size.text + self.assertEqual( + exp, cur, + 'Volume size. exp: {0} ({1}), cur {2}'.format(exp, v, cur)) + + def test_save_load_defaults(self): + default = None + value = random.randint(60000, 80000) + with DisksSettings() as s: + s.disks[0].volume_image.parent.click() + time.sleep(1) + default = s.disks[0].\ + volume_group_image.input.get_attribute('value') + s.disks[0].volume_group_image.input.\ + clear() + s.disks[0].volume_group_image.input.\ + send_keys(value) + s.apply.click() + time.sleep(1) + self.refresh() + with DisksSettings() as s: + time.sleep(2) + self.assertEqual( + "{:,}".format(value), + s.disks[0].volume_group_image.input.get_attribute('value'), + 'New value has been saved' + ) + s.load_defaults.click() + time.sleep(1) + self.assertEqual( + default, + s.disks[0].volume_group_image.input.get_attribute('value'), + 'default value has been restored' + ) + + def test_cancel_changes(self): + with DisksSettings() as s: + s.disks[0].volume_image.parent.click() + time.sleep(1) + default = s.disks[0].\ + volume_group_image.input.get_attribute('value') + s.disks[0].volume_group_image.input.\ + clear() + s.disks[0].volume_group_image.input.\ + send_keys(random.randint(60000, 80000)) + s.cancel_changes.click() + time.sleep(1) + self.assertEqual( + default, + s.disks[0].volume_group_image.input.get_attribute('value'), + 'default value has been restored' + ) + + def test_confirm_if_back_to_list(self): + with DisksSettings() as s: + s.disks[0].volume_image.parent.click() + time.sleep(1) + s.disks[0].volume_group_image.input.\ + clear() + s.disks[0].volume_group_image.input.\ + send_keys('0') + + s.back_to_node_list.click() + with ConfirmPopup() as p: + p.stay_on_page.click() + p.wait_until_exists() + self.assertEqual( + '0', s.disks[0].volume_group_image. + input.get_attribute('value'), 'Value is not changed') + + s.back_to_node_list.click() + with ConfirmPopup() as p: + p.leave_page.click() + p.wait_until_exists() + time.sleep(1) + + self.assertTrue( + Nodes().add_nodes.is_displayed(), + 'Backed to nodes page. Add Nodes button is displayed') + + def test_configure_disks_of_several_nodes(self): + values = [random.randint(100000, 500000) for i in range(4)] + + # Go back to nodes page + Tabs().nodes.click() + time.sleep(1) + # Add second node + Nodes().add_nodes.click() + time.sleep(1) + Nodes().select_all_in_group[1].click() + RolesPanel().compute.click() + Nodes().apply_changes.click() + time.sleep(1) + # change volumes size + with Nodes() as n: + n.select_all_in_group[1].click() + n.configure_disks.click() + time.sleep(1) + + with DisksSettings() as s: + for i, v in enumerate(values): + s.disks[i].volume_storage.parent.click() + time.sleep(1) + s.disks[i].volume_group_storage.input.\ + clear() + s.disks[i].volume_group_storage.input.\ + send_keys(v) + s.apply.click() + time.sleep(1) + + for i in range(1, 3): + # Go to nodes page + Tabs().nodes.click() + time.sleep(1) + # Verify disks settings of each node + Nodes().nodes[i].details.click() + NodeInfo().edit_disks.click() + time.sleep(1) + + for j, v in enumerate(values): + self.assertEqual( + "{:,}".format(v), + s.disks[j]. + volume_group_storage.input.get_attribute('value'), + 'Image volume size of disk {0} of node {0} is correct'. + format(j, i)) diff --git a/fuelweb_ui_test/tests/test_configure_networks.py b/fuelweb_ui_test/tests/test_configure_networks.py new file mode 100644 index 000000000..616e0340b --- /dev/null +++ b/fuelweb_ui_test/tests/test_configure_networks.py @@ -0,0 +1,281 @@ +import random +import time +from selenium.webdriver import ActionChains +import browser +from pageobjects.environments import Environments +from pageobjects.networks import Networks +from pageobjects.node_interfaces_settings import InterfacesSettings +from pageobjects.nodes import Nodes +from pageobjects.nodes import RolesPanel +from pageobjects.nodes import NodeInfo +from pageobjects.tabs import Tabs +from tests import preconditions +from tests.base import BaseTestCase + + +class TestConfigureNetworksPage(BaseTestCase): + + @classmethod + def setUpClass(cls): + BaseTestCase.setUpClass() + preconditions.Environment.simple_flat() + Environments().create_cluster_boxes[0].click() + Nodes().add_nodes.click() + Nodes().nodes_discovered[0].checkbox.click() + RolesPanel().controller.click() + Nodes().apply_changes.click() + time.sleep(1) + + def setUp(self): + BaseTestCase.setUp(self) + Environments().create_cluster_boxes[0].click() + Nodes().nodes[0].details.click() + NodeInfo().edit_networks.click() + + def test_drag_and_drop(self): + with InterfacesSettings() as s: + ActionChains(browser.driver).drag_and_drop( + s.interfaces[0].networks['storage'], + s.interfaces[1].networks_box).perform() + ActionChains(browser.driver).drag_and_drop( + s.interfaces[0].networks['management'], + s.interfaces[2].networks_box).perform() + ActionChains(browser.driver).drag_and_drop( + s.interfaces[0].networks['vm (fixed)'], + s.interfaces[2].networks_box).perform() + + self.assertIn( + 'storage', s.interfaces[1].networks, + 'storage at eht1') + self.assertIn( + 'management', s.interfaces[2].networks, + 'management at eht2') + self.assertIn( + 'vm (fixed)', s.interfaces[2].networks, + 'vm (fixed) at eht2') + + def test_public_floating_grouped(self): + with InterfacesSettings() as s: + ActionChains(browser.driver).drag_and_drop( + s.interfaces[0].networks['public'], + s.interfaces[1].networks_box).perform() + self.assertIn( + 'floating', s.interfaces[1].networks, + 'Floating has been moved') + ActionChains(browser.driver).drag_and_drop( + s.interfaces[1].networks['floating'], + s.interfaces[2].networks_box).perform() + self.assertIn( + 'public', s.interfaces[2].networks, + 'Public has been moved') + + def test_admin_pxe_is_not_dragable(self): + with InterfacesSettings() as s: + ActionChains(browser.driver).drag_and_drop( + s.interfaces[2].networks['admin (pxe)'], + s.interfaces[0].networks_box).perform() + self.assertNotIn( + 'admin (pxe)', s.interfaces[1].networks, + 'admin (pxe) has not been moved') + + def test_two_untagged_on_interface(self): + error = 'Untagged networks can not be assigned to one interface' + with InterfacesSettings() as s: + ActionChains(browser.driver).drag_and_drop( + s.interfaces[0].networks['public'], + s.interfaces[2].networks_box).perform() + self.assertIn( + 'nodrag', s.interfaces[2].parent.get_attribute('class'), + 'Red border') + self.assertIn( + error, s.interfaces[2]. + parent.find_element_by_xpath('./..').text, + 'Error message is displayed' + ) + self.assertFalse(s.apply.is_enabled(), 'Apply disabled') + ActionChains(browser.driver).drag_and_drop( + s.interfaces[2].networks['public'], + s.interfaces[1].networks_box).perform() + self.assertNotIn( + 'nodrag', s.interfaces[2].parent.get_attribute('class'), + 'Red border') + self.assertNotIn( + error, s.interfaces[2]. + parent.find_element_by_xpath('./..').text, + 'Error message is displayed' + ) + self.assertTrue(s.apply.is_enabled(), 'Apply enabled') + + def test_cancel_changes(self): + with InterfacesSettings() as s: + ActionChains(browser.driver).drag_and_drop( + s.interfaces[0].networks['public'], + s.interfaces[1].networks_box).perform() + ActionChains(browser.driver).drag_and_drop( + s.interfaces[0].networks['storage'], + s.interfaces[2].networks_box).perform() + + s.cancel_changes.click() + time.sleep(1) + self.assertIn( + 'storage', s.interfaces[0].networks, + 'storage at eht0') + self.assertIn( + 'public', s.interfaces[0].networks, + 'public at eht0') + self.assertIn( + 'floating', s.interfaces[0].networks, + 'floating at eht0') + + +class TestConfigureNetworks(BaseTestCase): + + @classmethod + def setUpClass(cls): + BaseTestCase.setUpClass() + + def setUp(self): + BaseTestCase.clear_nailgun_database() + BaseTestCase.setUp(self) + + preconditions.Environment.simple_flat() + Environments().create_cluster_boxes[0].click() + Nodes().add_nodes.click() + Nodes().nodes_discovered[0].checkbox.click() + RolesPanel().controller.click() + Nodes().apply_changes.click() + time.sleep(1) + Nodes().nodes[0].details.click() + NodeInfo().edit_networks.click() + + def test_save_load_defaults(self): + with InterfacesSettings() as s: + ActionChains(browser.driver).drag_and_drop( + s.interfaces[0].networks['public'], + s.interfaces[1].networks_box).perform() + ActionChains(browser.driver).drag_and_drop( + s.interfaces[0].networks['storage'], + s.interfaces[2].networks_box).perform() + s.apply.click() + time.sleep(1) + self.refresh() + with InterfacesSettings() as s: + self.assertIn( + 'storage', s.interfaces[2].networks, + 'storage at eht2') + self.assertIn( + 'public', s.interfaces[1].networks, + 'public at eht1') + self.assertIn( + 'floating', s.interfaces[1].networks, + 'floating at eht1') + s.load_defaults.click() + time.sleep(1) + self.assertIn( + 'storage', s.interfaces[0].networks, + 'storage at eht0') + self.assertIn( + 'public', s.interfaces[0].networks, + 'public at eht0') + self.assertIn( + 'floating', s.interfaces[0].networks, + 'floating at eht0') + + def test_configure_interfaces_of_several_nodes(self): + # Go back to nodes page + Tabs().nodes.click() + # Add second node + Nodes().add_nodes.click() + Nodes().nodes_discovered[0].checkbox.click() + RolesPanel().compute.click() + Nodes().apply_changes.click() + time.sleep(1) + # rearrange interfaces + with Nodes() as n: + n.select_all.click() + n.configure_interfaces.click() + with InterfacesSettings() as s: + ActionChains(browser.driver).drag_and_drop( + s.interfaces[0].networks['public'], + s.interfaces[1].networks_box).perform() + ActionChains(browser.driver).drag_and_drop( + s.interfaces[0].networks['management'], + s.interfaces[1].networks_box).perform() + ActionChains(browser.driver).drag_and_drop( + s.interfaces[0].networks['storage'], + s.interfaces[2].networks_box).perform() + s.apply.click() + time.sleep(1) + + for i in range(2): + # Go to nodes page + Tabs().nodes.click() + # Verify interfaces settings of each node + Nodes().nodes[i].details.click() + NodeInfo().edit_networks.click() + self.assertIn( + 'public', s.interfaces[1].networks, + 'public at eht1. Node #{0}'.format(i)) + self.assertIn( + 'management', s.interfaces[1].networks, + 'management at eht1. Node #{0}'.format(i)) + self.assertIn( + 'storage', s.interfaces[2].networks, + 'storage at eht2. Node #{0}'.format(i)) + + def test_vlan_id_labels_visibility(self): + label = 'VLAN ID' + Tabs().networks.click() + with Networks() as n: + n.management.vlan_tagging.click() + n.storage.vlan_tagging.click() + n.fixed.vlan_tagging.click() + n.save_settings.click() + time.sleep(1) + Tabs().nodes.click() + Nodes().nodes[0].details.click() + NodeInfo().edit_networks.click() + with InterfacesSettings() as s: + self.assertNotIn( + label, s.interfaces[0].networks['storage'].text, + 'vlan id is visible. Storage network') + self.assertNotIn( + label, s.interfaces[0].networks['management'].text, + 'vlan id is visible. Management network') + self.assertNotIn( + label, s.interfaces[0].networks['vm (fixed)'].text, + 'vlan id is visible. VM (Fixed) network') + + def test_vlan_id_values(self): + label = 'VLAN ID: {0}' + vlans = [random.randint(110, 200) for i in range(3)] + Tabs().networks.click() + with Networks() as n: + n.management.vlan_id.clear() + n.management.vlan_id.send_keys(vlans[0]) + + n.storage.vlan_id.clear() + n.storage.vlan_id.send_keys(vlans[1]) + + n.fixed.vlan_id.clear() + n.fixed.vlan_id.send_keys(vlans[2]) + + n.save_settings.click() + time.sleep(1) + + Tabs().nodes.click() + Nodes().nodes[0].details.click() + NodeInfo().edit_networks.click() + with InterfacesSettings() as s: + self.assertIn( + label.format(vlans[0]), + s.interfaces[0].networks['management'].text, + 'vlan id is correct. Management network') + self.assertIn( + label.format(vlans[1]), + s.interfaces[0].networks['storage'].text, + 'vlan id is correct. Storage network') + self.assertIn( + label.format(vlans[2]), + s.interfaces[0].networks['vm (fixed)'].text, + 'vlan id is correct. VM (Fixed) network') diff --git a/fuelweb_ui_test/tests/test_deploy.py b/fuelweb_ui_test/tests/test_deploy.py new file mode 100644 index 000000000..adaa58554 --- /dev/null +++ b/fuelweb_ui_test/tests/test_deploy.py @@ -0,0 +1,133 @@ +import time +from selenium.webdriver import ActionChains +import browser +from pageobjects.environments import Environments, DeployChangesPopup +from pageobjects.header import TaskResultAlert +from pageobjects.node_disks_settings import DisksSettings +from pageobjects.node_interfaces_settings import InterfacesSettings +from pageobjects.nodes import Nodes, RolesPanel, DeleteNodePopup, NodeInfo +from tests import preconditions +from tests.base import BaseTestCase + + +class TestDeploy(BaseTestCase): + + @classmethod + def setUpClass(cls): + BaseTestCase.setUpClass() + + def setUp(self): + BaseTestCase.clear_nailgun_database() + BaseTestCase.setUp(self) + preconditions.Environment.simple_flat() + Environments().create_cluster_boxes[0].click() + time.sleep(1) + + def test_add_nodes(self): + Nodes().add_nodes.click() + Nodes().nodes_discovered[0].checkbox.click() + RolesPanel().controller.click() + Nodes().apply_changes.click() + time.sleep(2) + Nodes().add_nodes.click() + time.sleep(1) + Nodes().nodes_discovered[0].checkbox.click() + RolesPanel().compute.click() + Nodes().apply_changes.click() + time.sleep(1) + + for node in Nodes().nodes: + self.assertEqual( + 'pending addition', node.status.text.lower(), + 'Node status is Pending Addition') + + Nodes().deploy_changes.click() + DeployChangesPopup().deploy.click() + TaskResultAlert().close.click() + + with Nodes() as n: + self.assertEqual(2, len(n.nodes), 'Nodes amount') + for node in n.nodes: + self.assertEqual('ready', node.status.text.lower(), + 'Node status is READY') + + def test_delete_node(self): + self.test_add_nodes() + + with Nodes() as n: + n.nodes[1].checkbox.click() + n.delete_nodes.click() + with DeleteNodePopup() as p: + p.delete.click() + p.wait_until_exists() + time.sleep(1) + + self.assertEqual( + 'pending deletion', Nodes().nodes[1].status.text.lower(), + 'Node status is Pending Deletion') + + Nodes().deploy_changes.click() + DeployChangesPopup().deploy.click() + TaskResultAlert().close.click() + + with Nodes() as n: + self.assertEqual(1, len(n.nodes), 'Nodes amount') + for node in n.nodes: + self.assertEqual('ready', node.status.text.lower(), + 'Node status is READY') + + def test_node_configure_networks_is_readonly(self): + Nodes().add_nodes.click() + Nodes().nodes_discovered[0].checkbox.click() + RolesPanel().controller.click() + Nodes().apply_changes.click() + time.sleep(2) + Nodes().deploy_changes.click() + DeployChangesPopup().deploy.click() + time.sleep(1) + + Nodes().nodes[0].details.click() + NodeInfo().edit_networks.click() + + with InterfacesSettings() as s: + ActionChains(browser.driver).drag_and_drop( + s.interfaces[0].networks['storage'], + s.interfaces[1].networks_box).perform() + + time.sleep(1) + self.assertNotIn( + 'storage', s.interfaces[1].networks, + 'storage at eht1') + self.assertFalse(s.apply.is_enabled(), 'Apply is disabled') + self.assertFalse(s.load_defaults.is_enabled(), + 'Load defaults is disabled') + self.assertFalse(s.cancel_changes.is_enabled(), + 'Cancel changes is disabled') + + def test_node_configure_disks_is_readonly(self): + Nodes().add_nodes.click() + Nodes().nodes_discovered[0].checkbox.click() + RolesPanel().controller.click() + Nodes().apply_changes.click() + time.sleep(2) + Nodes().deploy_changes.click() + DeployChangesPopup().deploy.click() + time.sleep(1) + + Nodes().nodes[0].details.click() + NodeInfo().edit_disks.click() + time.sleep(1) + + with DisksSettings() as s: + for i in range(2): + self.assertFalse( + s.disks[i].volume_group_os.input.is_enabled(), + 'Base system input is disabled at disk #{0}'.format(i)) + self.assertFalse( + s.disks[i].volume_group_image.input.is_enabled(), + 'Image storage input is disabled at disk #{0}'.format(i)) + self.assertFalse(s.apply.is_enabled(), 'Apply is disabled') + self.assertFalse(s.load_defaults.is_enabled(), + 'Load defaults is disabled') + self.assertFalse(s.cancel_changes.is_enabled(), + 'Cancel changes is disabled') diff --git a/fuelweb_ui_test/tests/test_discard_changes.py b/fuelweb_ui_test/tests/test_discard_changes.py new file mode 100644 index 000000000..54ff5ec0a --- /dev/null +++ b/fuelweb_ui_test/tests/test_discard_changes.py @@ -0,0 +1,50 @@ +import time +from pageobjects.environments import Environments, DiscardChangesPopup +from pageobjects.nodes import Nodes, RolesPanel, DeleteNodePopup +from tests import preconditions +from tests.base import BaseTestCase + + +class TestDiscardEnvironmentChanges(BaseTestCase): + + @classmethod + def setUpClass(cls): + BaseTestCase.setUpClass() + + def setUp(self): + BaseTestCase.clear_nailgun_database() + BaseTestCase.setUp(self) + preconditions.Environment.simple_flat() + Environments().create_cluster_boxes[0].click() + time.sleep(1) + preconditions.Environment().deploy_nodes(1, 2) + + def _discard_changes(self): + Nodes().discard_changes.click() + with DiscardChangesPopup() as p: + p.discard.click() + p.wait_until_exists() + + time.sleep(2) + self.assertEqual(3, len(Nodes().nodes), 'Nodes amount') + for node in Nodes().nodes: + self.assertEqual('ready', node.status.text.lower(), + 'Node status is READY') + + def test_discard_adding_node(self): + Nodes().add_nodes.click() + Nodes().nodes_discovered[0].checkbox.click() + RolesPanel().compute.click() + Nodes().apply_changes.click() + time.sleep(1) + self._discard_changes() + + def test_discard_deleting_node(self): + with Nodes() as n: + n.nodes[1].checkbox.click() + n.delete_nodes.click() + with DeleteNodePopup() as p: + p.delete.click() + p.wait_until_exists() + time.sleep(1) + self._discard_changes() diff --git a/fuelweb_ui_test/tests/test_env_settings.py b/fuelweb_ui_test/tests/test_env_settings.py new file mode 100644 index 000000000..0e8b6cfef --- /dev/null +++ b/fuelweb_ui_test/tests/test_env_settings.py @@ -0,0 +1,189 @@ +import time +import random +from pageobjects.environments import Environments +from pageobjects.environments import Wizard +from pageobjects.settings import Settings +from pageobjects.tabs import Tabs +from tests.base import BaseTestCase +from fuelweb_ui_test.settings import OPENSTACK_CENTOS +from fuelweb_ui_test.settings import OPENSTACK_RELEASE_CENTOS + + +class BaseClass(BaseTestCase): + @classmethod + def setUpClass(cls): + BaseTestCase.setUpClass() + cls.get_home() + Environments().create_cluster_box.click() + with Wizard() as w: + w.name.send_keys(OPENSTACK_CENTOS) + w.release.select_by_visible_text(OPENSTACK_RELEASE_CENTOS) + for i in range(3): + w.next.click() + w.network_neutron_gre.click() + for i in range(3): + w.next.click() + w.create.click() + w.wait_until_exists() + + def setUp(self): + BaseTestCase.setUp(self) + Environments().create_cluster_boxes[0].click() + Tabs().settings.click() + + def _test_text_field(self, text_field, value): + def_value = None + with Settings() as s: + def_value = getattr(s, text_field).get_attribute('value') + getattr(s, text_field).clear() + getattr(s, text_field).send_keys(value) + s.save_settings.click() + time.sleep(1) + self.refresh() + with Settings() as s: + self.assertEqual(getattr(s, text_field).get_attribute('value'), + value) + s.load_defaults.click() + time.sleep(1) + self.assertEqual( + getattr(s, text_field).get_attribute('value'), def_value, + "load defaults value") + + def _test_tumbler_field(self, tumbler_field): + def_value = None + with Settings() as s: + def_value = getattr(s, tumbler_field).\ + find_element_by_tag_name('input').is_selected() + getattr(s, tumbler_field).click() + s.save_settings.click() + time.sleep(1) + self.refresh() + with Settings() as s: + self.assertEqual( + getattr(s, tumbler_field). + find_element_by_tag_name('input').is_selected(), not def_value) + s.load_defaults.click() + time.sleep(1) + self.assertEqual( + getattr(s, tumbler_field). + find_element_by_tag_name('input').is_selected(), def_value, + "load defaults value") + + def _test_radio_group(self, radios): + radios.reverse() + for radio in radios: + with Settings() as s: + getattr(s, radio).click() + s.save_settings.click() + time.sleep(1) + self.refresh() + self.assertTrue( + getattr(Settings(), radio). + find_element_by_tag_name('input').is_selected()) + # Set group to not default state + random_radio = radios[random.randint(0, len(radios) - 2)] + with Settings() as s: + getattr(s, random_radio).click() + s.load_defaults.click() + time.sleep(1) + self.assertTrue( + getattr(s, radios[-1]). + find_element_by_tag_name('input').is_selected(), + "load defaults value") + + +class TestAccess(BaseClass): + + def test_username(self): + self._test_text_field('username', 'newname') + + def test_password(self): + self._test_text_field('password', 'newpassword') + + def test_password_show(self): + with Settings() as s: + s.show_password.click() + self.assertEqual(s.password.get_attribute('type'), 'text') + s.show_password.click() + self.assertEqual(s.password.get_attribute('type'), 'password') + + def test_tenant(self): + self._test_text_field('tenant', 'newtenant') + + def test_email(self): + self._test_text_field('email', 'newemail@example.org') + + +class TestAdditionalComponents(BaseClass): + + def test_savanna(self): + self._test_tumbler_field('install_savanna') + + def test_murano(self): + self._test_tumbler_field('install_murano') + + def test_ceilometer(self): + self._test_tumbler_field('install_ceilometer') + + +class TestCommon(BaseClass): + + def test_debug(self): + self._test_tumbler_field('debug') + + def test_hypervisor_type(self): + self._test_radio_group(['hypervisor_qemu', 'hypervisor_kvm']) + + def test_assign_ip(self): + self._test_tumbler_field('assign_ip') + + def test_scheduler_driver(self): + self._test_radio_group(['filter_scheduler', 'simple_scheduler']) + + def test_vlan_splinters(self): + self._test_radio_group( + ['vlan_splinters_disabled', + 'vlan_splinters_soft', + 'vlan_splinters_hard']) + + def test_use_cow_images(self): + self._test_tumbler_field('use_cow_images') + + def test_start_guests(self): + self._test_tumbler_field('start_guests') + + def test_auth_key(self): + self._test_text_field('auth_key', 'newauthkey') + + +class TestSyslog(BaseClass): + + def test_hostname(self): + self._test_text_field('syslog_server', 'newsyslog_server') + + def test_port(self): + self._test_text_field('syslog_port', '8000') + + def test_syslog_protocol(self): + self._test_radio_group(['syslog_udp', 'syslog_tcp']) + + +class TestStorage(BaseClass): + + def test_cinder_for_volumes(self): + self._test_tumbler_field('cinder_for_volumes') + + def test_ceph_for_volumes(self): + self._test_tumbler_field('ceph_for_volumes') + + def test_ceph_for_images(self): + self._test_tumbler_field('ceph_for_images') + + def test_ceph_ephemeral(self): + self._test_tumbler_field('ceph_ephemeral') + + def test_ceph_rados_gw(self): + self._test_tumbler_field('ceph_rados_gw') + + def test_ceph_factor(self): + self._test_text_field('ceph_factor', '10') diff --git a/fuelweb_ui_test/tests/test_env_wizard.py b/fuelweb_ui_test/tests/test_env_wizard.py new file mode 100644 index 000000000..b74a9ea9a --- /dev/null +++ b/fuelweb_ui_test/tests/test_env_wizard.py @@ -0,0 +1,313 @@ +import time +from pageobjects.base import PageObject +from pageobjects.environments import Environments, Wizard +from pageobjects.header import Header +from pageobjects.releases import Releases +from tests.base import BaseTestCase +from fuelweb_ui_test.settings import OPENSTACK_RELEASE_CENTOS +from fuelweb_ui_test.settings import OPENSTACK_RELEASE_UBUNTU +from fuelweb_ui_test.settings import OPENSTACK_RELEASE_REDHAT +from fuelweb_ui_test.settings import REDHAT_USERNAME +from fuelweb_ui_test.settings import REDHAT_PASSWORD +from fuelweb_ui_test.settings import OPENSTACK_REDHAT +from fuelweb_ui_test.settings import REDHAT_SATELLITE +from fuelweb_ui_test.settings import REDHAT_ACTIVATION_KEY + + +class TestEnvWizard(BaseTestCase): + + def setUp(self): + BaseTestCase.setUp(self) + Environments().create_cluster_box.click() + + def test_name_field(self): + with Wizard() as w: + w.name.send_keys(OPENSTACK_RELEASE_CENTOS) + w.next.click() + w.prev.click() + self.assertEqual(w.name.get_attribute('value'), + OPENSTACK_RELEASE_CENTOS) + w.name.clear() + w.next.click() + self.assertIn( + 'Environment name cannot be empty', + w.name.find_element_by_xpath('..').text) + + def test_name_exists(self): + name = 'test name' + with Wizard() as w: + w.name.send_keys(name) + for i in range(6): + w.next.click() + w.create.click() + w.wait_until_exists() + + Environments().create_cluster_box.click() + with Wizard() as w: + w.name.send_keys(name) + w.next.click() + time.sleep(1) + self.assertIn('Environment with name "{}" already exists'. + format(name), + w.name.find_element_by_xpath('..').text) + + def test_release_field(self): + with Wizard() as w: + w.name.send_keys(OPENSTACK_RELEASE_UBUNTU) + w.release.select_by_visible_text(OPENSTACK_RELEASE_UBUNTU) + w.next.click() + w.prev.click() + self.assertEqual(w.release.first_selected_option.text, + OPENSTACK_RELEASE_UBUNTU) + + def test_rhel_empty_form(self): + with Wizard() as w: + w.name.send_keys(OPENSTACK_RELEASE_REDHAT) + w.release.select_by_visible_text(OPENSTACK_RELEASE_REDHAT) + w.next.click() + self.assertIn('Invalid username', + w.redhat_username.find_element_by_xpath('..').text) + self.assertIn('Invalid password', + w.redhat_password.find_element_by_xpath('..').text) + + w.license_rhn.click() + w.next.click() + self.assertIn('Invalid username', + w.redhat_username.find_element_by_xpath('..').text) + self.assertIn('Invalid password', + w.redhat_password.find_element_by_xpath('..').text) + self.assertIn( + 'Only valid fully qualified domain name is allowed for the ' + 'hostname field', + w.redhat_satellite.find_element_by_xpath('..').text) + self.assertIn( + 'Invalid activation key', + w.redhat_activation_key.find_element_by_xpath('..').text) + + def test_rhel_form(self): + with Wizard() as w: + w.name.send_keys(OPENSTACK_RELEASE_REDHAT) + w.release.select_by_visible_text(OPENSTACK_RELEASE_REDHAT) + self.assertTrue(w.license_rhsm.is_displayed()) + self.assertTrue(w.license_rhn.is_displayed()) + self.assertTrue(w.redhat_username.is_displayed()) + self.assertTrue(w.redhat_password.is_displayed()) + + w.license_rhn.click() + self.assertTrue(w.redhat_satellite.is_displayed()) + self.assertTrue(w.redhat_activation_key.is_displayed()) + + w.license_rhsm.click() + self.assertFalse(w.redhat_satellite.is_displayed()) + self.assertFalse(w.redhat_activation_key.is_displayed()) + + def test_mode_radios(self): + with Wizard() as w: + w.name.send_keys(OPENSTACK_RELEASE_UBUNTU) + w.release.select_by_visible_text(OPENSTACK_RELEASE_UBUNTU) + w.next.click() + w.mode_ha_compact.click() + w.next.click() + w.prev.click() + self.assertTrue( + w.mode_ha_compact. + find_element_by_tag_name('input').is_selected()) + self.assertFalse( + w.mode_multinode. + find_element_by_tag_name('input').is_selected()) + + def test_hypervisor_radios(self): + with Wizard() as w: + w.name.send_keys(OPENSTACK_RELEASE_UBUNTU) + w.release.select_by_visible_text(OPENSTACK_RELEASE_UBUNTU) + w.next.click() + w.next.click() + w.hypervisor_qemu.click() + w.next.click() + w.prev.click() + self.assertTrue( + w.hypervisor_qemu.find_element_by_tag_name('input'). + is_selected()) + self.assertFalse( + w.hypervisor_kvm.find_element_by_tag_name('input'). + is_selected()) + + def test_network_radios(self): + with Wizard() as w: + w.name.send_keys(OPENSTACK_RELEASE_UBUNTU) + w.release.select_by_visible_text(OPENSTACK_RELEASE_UBUNTU) + w.next.click() + w.next.click() + w.next.click() + w.network_neutron_gre.click() + w.next.click() + w.prev.click() + self.assertFalse( + w.network_nova.find_element_by_tag_name('input'). + is_selected()) + self.assertTrue( + w.network_neutron_gre.find_element_by_tag_name('input'). + is_selected()) + self.assertFalse( + w.network_neutron_vlan.find_element_by_tag_name('input'). + is_selected()) + w.network_neutron_vlan.click() + self.assertFalse( + w.network_nova.find_element_by_tag_name('input').is_selected()) + self.assertFalse( + w.network_neutron_gre.find_element_by_tag_name('input'). + is_selected()) + self.assertTrue( + w.network_neutron_vlan.find_element_by_tag_name('input'). + is_selected()) + + def test_storage_radios(self): + with Wizard() as w: + w.name.send_keys(OPENSTACK_RELEASE_UBUNTU) + w.release.select_by_visible_text(OPENSTACK_RELEASE_UBUNTU) + w.next.click() + w.next.click() + w.next.click() + w.next.click() + w.storage_cinder_ceph.click() + w.storage_glance_ceph.click() + w.next.click() + w.prev.click() + self.assertFalse( + w.storage_cinder_default.find_element_by_tag_name('input'). + is_selected()) + self.assertTrue( + w.storage_cinder_ceph.find_element_by_tag_name('input'). + is_selected()) + self.assertFalse( + w.storage_glance_default.find_element_by_tag_name('input'). + is_selected()) + self.assertTrue( + w.storage_glance_ceph.find_element_by_tag_name('input'). + is_selected()) + + def test_services_checkboxes(self): + with Wizard() as w: + w.name.send_keys(OPENSTACK_RELEASE_UBUNTU) + w.release.select_by_visible_text(OPENSTACK_RELEASE_UBUNTU) + w.next.click() + w.next.click() + w.next.click() + w.network_neutron_gre.click() + w.next.click() + w.next.click() + w.install_savanna.click() + w.install_murano.click() + w.install_ceilometer.click() + w.next.click() + w.prev.click() + self.assertTrue( + w.install_savanna.find_element_by_tag_name('input'). + is_selected()) + self.assertTrue( + w.install_murano.find_element_by_tag_name('input'). + is_selected()) + self.assertTrue( + w.install_ceilometer.find_element_by_tag_name('input'). + is_selected()) + + def test_cancel_button(self): + with Wizard() as w: + w.name.send_keys(OPENSTACK_RELEASE_UBUNTU) + w.release.select_by_visible_text(OPENSTACK_RELEASE_UBUNTU) + w.next.click() + w.mode_ha_compact.click() + w.next.click() + w.hypervisor_kvm.click() + w.next.click() + w.network_neutron_gre.click() + w.next.click() + w.storage_cinder_ceph.click() + w.storage_glance_ceph.click() + w.next.click() + w.install_savanna.click() + w.install_murano.click() + w.next.click() + w.cancel.click() + PageObject.wait_until_exists(w.parent) + + Environments().create_cluster_box.click() + with Wizard() as w: + self.assertEqual(w.name.get_attribute('value'), '') + self.assertEqual(w.release.first_selected_option.text, + OPENSTACK_RELEASE_CENTOS) + w.name.send_keys(OPENSTACK_RELEASE_UBUNTU) + w.next.click() + self.assertTrue( + w.mode_multinode.find_element_by_tag_name('input'). + is_selected()) + w.next.click() + self.assertTrue( + w.hypervisor_qemu.find_element_by_tag_name('input'). + is_selected()) + w.next.click() + self.assertTrue( + w.network_nova.find_element_by_tag_name('input'). + is_selected()) + w.next.click() + self.assertTrue( + w.storage_cinder_default.find_element_by_tag_name('input'). + is_selected()) + self.assertTrue( + w.storage_glance_default.find_element_by_tag_name('input'). + is_selected()) + w.next.click() + self.assertFalse( + w.install_savanna.find_element_by_tag_name('input'). + is_selected()) + self.assertFalse( + w.install_murano.find_element_by_tag_name('input'). + is_selected()) + + +class TestEnvWizardRedHat(BaseTestCase): + + def setUp(self): + BaseTestCase.clear_nailgun_database() + BaseTestCase.setUp(self) + Environments().create_cluster_box.click() + + def test_rhsm(self): + with Wizard() as w: + w.name.send_keys(OPENSTACK_RELEASE_REDHAT) + w.release.select_by_visible_text(OPENSTACK_RELEASE_REDHAT) + w.license_rhsm.click() + w.redhat_username.send_keys(REDHAT_USERNAME) + w.redhat_password.send_keys(REDHAT_PASSWORD) + for i in range(6): + w.next.click() + w.create.click() + w.wait_until_exists() + Header().releases.click() + with Releases() as r: + PageObject.wait_until_exists( + r.dict[OPENSTACK_REDHAT].download_progress, timeout=20) + self.assertEqual( + 'Active', r.dict[OPENSTACK_REDHAT].status.text, + 'RHOS status is active') + + def test_rhn_satellite(self): + with Wizard() as w: + w.name.send_keys(OPENSTACK_RELEASE_REDHAT) + w.release.select_by_visible_text(OPENSTACK_RELEASE_REDHAT) + w.license_rhn.click() + w.redhat_username.send_keys(REDHAT_USERNAME) + w.redhat_password.send_keys(REDHAT_PASSWORD) + w.redhat_satellite.send_keys(REDHAT_SATELLITE) + w.redhat_activation_key.send_keys(REDHAT_ACTIVATION_KEY) + for i in range(6): + w.next.click() + w.create.click() + w.wait_until_exists() + Header().releases.click() + with Releases() as r: + PageObject.wait_until_exists( + r.dict[OPENSTACK_REDHAT].download_progress, timeout=20) + self.assertEqual( + 'Active', r.dict[OPENSTACK_REDHAT].status.text, + 'RHOS status is active') diff --git a/fuelweb_ui_test/tests/test_environment.py b/fuelweb_ui_test/tests/test_environment.py new file mode 100644 index 000000000..a32907ce3 --- /dev/null +++ b/fuelweb_ui_test/tests/test_environment.py @@ -0,0 +1,207 @@ +from pageobjects.environments import Environments, Wizard +from pageobjects.networks import Networks, NeutronParameters +from pageobjects.nodes import Nodes +from pageobjects.settings import Settings +from pageobjects.tabs import Tabs +from tests.base import BaseTestCase +from fuelweb_ui_test.settings import OPENSTACK_CENTOS +from fuelweb_ui_test.settings import OPENSTACK_RELEASE_CENTOS + + +class TestEnvironment(BaseTestCase): + + def setUp(self): + self.clear_nailgun_database() + BaseTestCase.setUp(self) + Environments().create_cluster_box.click() + + def test_default_settings(self): + with Wizard() as w: + w.name.send_keys(OPENSTACK_CENTOS) + w.release.select_by_visible_text(OPENSTACK_RELEASE_CENTOS) + for i in range(6): + w.next.click() + w.create.click() + w.wait_until_exists() + + cb = Environments().create_cluster_boxes[0] + self.assertIn(OPENSTACK_CENTOS, cb.text) + cb.click() + + with Nodes() as n: + self.assertEqual(n.env_name.text, OPENSTACK_CENTOS) + n.info_icon.click() + self.assertIn('display: block;', + n.env_details.get_attribute('style')) + self.assertIn(OPENSTACK_CENTOS, n.env_details.text) + self.assertIn('New', n.env_details.text) + self.assertIn('Multi-node', n.env_details.text) + self.assertNotIn('with HA', n.env_details.text) + n.info_icon.click() + self.assertIn('display: none;', + n.env_details.get_attribute('style')) + Tabs().networks.click() + with Networks() as n: + self.assertTrue( + n.flatdhcp_manager.find_element_by_tag_name('input'). + is_selected()) + Tabs().settings.click() + with Settings() as s: + self.assertFalse( + s.install_savanna.find_element_by_tag_name('input'). + is_selected()) + self.assertFalse( + s.install_murano.find_element_by_tag_name('input'). + is_selected()) + self.assertFalse( + s.install_ceilometer.find_element_by_tag_name('input'). + is_selected()) + self.assertTrue( + s.hypervisor_qemu.find_element_by_tag_name('input'). + is_selected()) + pass + + def test_ha_mode(self): + with Wizard() as w: + w.name.send_keys(OPENSTACK_CENTOS) + w.release.select_by_visible_text(OPENSTACK_RELEASE_CENTOS) + w.next.click() + w.mode_ha_compact.click() + for i in range(5): + w.next.click() + w.create.click() + w.wait_until_exists() + + cb = Environments().create_cluster_boxes[0] + cb.click() + + with Nodes() as n: + self.assertEqual(n.env_name.text, OPENSTACK_CENTOS) + n.info_icon.click() + self.assertIn(OPENSTACK_CENTOS, n.env_details.text) + self.assertIn('Multi-node with HA', n.env_details.text) + + def test_hypervisor_kvm(self): + with Wizard() as w: + w.name.send_keys(OPENSTACK_CENTOS) + w.release.select_by_visible_text(OPENSTACK_RELEASE_CENTOS) + w.next.click() + w.next.click() + w.hypervisor_kvm.click() + for i in range(4): + w.next.click() + w.create.click() + w.wait_until_exists() + + cb = Environments().create_cluster_boxes[0] + cb.click() + Tabs().settings.click() + + with Settings() as s: + self.assertTrue( + s.hypervisor_kvm.find_element_by_tag_name('input'). + is_selected()) + + def test_neutron_gre(self): + with Wizard() as w: + w.name.send_keys(OPENSTACK_CENTOS) + w.release.select_by_visible_text(OPENSTACK_RELEASE_CENTOS) + for i in range(3): + w.next.click() + w.network_neutron_gre.click() + for i in range(3): + w.next.click() + w.create.click() + w.wait_until_exists() + + cb = Environments().create_cluster_boxes[0] + cb.click() + Tabs().networks.click() + + with Networks() as n: + self.assertEqual(n.segmentation_type.text, + 'Neutron with gre segmentation') + self.assertTrue(NeutronParameters().parent.is_displayed()) + + def test_neutron_vlan(self): + with Wizard() as w: + w.name.send_keys(OPENSTACK_CENTOS) + w.release.select_by_visible_text(OPENSTACK_RELEASE_CENTOS) + for i in range(3): + w.next.click() + w.network_neutron_vlan.click() + for i in range(3): + w.next.click() + w.create.click() + w.wait_until_exists() + + cb = Environments().create_cluster_boxes[0] + cb.click() + Tabs().networks.click() + + with Networks() as n: + self.assertEqual(n.segmentation_type.text, + 'Neutron with vlan segmentation') + self.assertTrue(NeutronParameters().parent.is_displayed()) + + def test_storage_ceph(self): + with Wizard() as w: + w.name.send_keys(OPENSTACK_CENTOS) + w.release.select_by_visible_text(OPENSTACK_RELEASE_CENTOS) + for i in range(4): + w.next.click() + w.storage_cinder_ceph.click() + w.storage_glance_ceph.click() + w.next.click() + w.next.click() + w.create.click() + w.wait_until_exists() + + cb = Environments().create_cluster_boxes[0] + cb.click() + Tabs().settings.click() + + with Settings() as s: + self.assertTrue( + s.cinder_for_volumes.find_element_by_tag_name('input'). + is_selected()) + self.assertTrue( + s.ceph_for_volumes.find_element_by_tag_name('input'). + is_selected()) + self.assertTrue( + s.ceph_for_images.find_element_by_tag_name('input'). + is_selected()) + self.assertFalse( + s.ceph_rados_gw.find_element_by_tag_name('input'). + is_selected()) + + def test_services(self): + with Wizard() as w: + w.name.send_keys(OPENSTACK_CENTOS) + w.release.select_by_visible_text(OPENSTACK_RELEASE_CENTOS) + for i in range(3): + w.next.click() + w.network_neutron_gre.click() + w.next.click() + w.next.click() + w.install_savanna.click() + w.install_murano.click() + w.install_ceilometer.click() + w.next.click() + w.create.click() + w.wait_until_exists() + + cb = Environments().create_cluster_boxes[0] + cb.click() + Tabs().settings.click() + + with Settings() as s: + self.assertTrue( + s.install_savanna.find_element_by_tag_name('input'). + is_selected()) + self.assertTrue( + s.install_murano.find_element_by_tag_name('input'). + is_selected()) + self.assertTrue( + s.install_ceilometer.find_element_by_tag_name('input'). + is_selected()) diff --git a/fuelweb_ui_test/tests/test_environment_actions.py b/fuelweb_ui_test/tests/test_environment_actions.py new file mode 100644 index 000000000..0ee54710b --- /dev/null +++ b/fuelweb_ui_test/tests/test_environment_actions.py @@ -0,0 +1,40 @@ +import time +from pageobjects.actions import Actions, DeleteEnvironmentPopup +from pageobjects.environments import Environments +from pageobjects.nodes import Nodes +from pageobjects.tabs import Tabs +from tests import preconditions +from tests.base import BaseTestCase + + +class TestEnvironmentActions(BaseTestCase): + + @classmethod + def setUpClass(cls): + BaseTestCase.setUpClass() + + def setUp(self): + BaseTestCase.clear_nailgun_database() + preconditions.Environment.simple_flat() + Environments().create_cluster_boxes[0].click() + Tabs().actions.click() + + def test_rename(self): + value = 'Happy environment' + with Actions() as a: + a.name.clear() + a.name.send_keys(value) + a.rename.click() + time.sleep(1) + Tabs().nodes.click() + self.assertEqual(value, Nodes().env_name.text, + 'Environment has been renamed') + + def test_delete(self): + with Actions() as a: + a.delete.click() + DeleteEnvironmentPopup().delete.click() + time.sleep(1) + self.assertEqual( + 0, len(Environments().create_cluster_boxes), + 'Environment has been deleted') diff --git a/fuelweb_ui_test/tests/test_networks.py b/fuelweb_ui_test/tests/test_networks.py new file mode 100644 index 000000000..44d115849 --- /dev/null +++ b/fuelweb_ui_test/tests/test_networks.py @@ -0,0 +1,449 @@ +import time +from pageobjects.environments import Environments +from pageobjects.networks import Networks +from pageobjects.nodes import Nodes, RolesPanel +from pageobjects.tabs import Tabs +from tests.base import BaseTestCase +import preconditions + +RANGES = [ + ['172.16.0.3', '172.16.0.10'], + ['172.16.0.20', '172.16.0.50'], + ['172.16.0.128', '172.16.0.140'], + ['172.16.0.158', '172.16.0.165'] +] + + +class SimpleFlatNetworks(BaseTestCase): + + @classmethod + def setUpClass(cls): + BaseTestCase.setUpClass() + preconditions.Environment.simple_flat() + + def setUp(self): + BaseTestCase.setUp(self) + Environments().create_cluster_boxes[0].click() + Tabs().networks.click() + time.sleep(1) + + def _assert_save_cancel_disabled(self): + self.assertFalse(Networks().save_settings.is_enabled(), + 'Save settings is disabled') + self.assertFalse(Networks().cancel_changes.is_enabled(), + 'Cancel changes is disabled') + + def _assert_save_verify_disabled(self): + self.assertFalse(Networks().save_settings.is_enabled(), + 'Save settings is disabled') + self.assertFalse(Networks().verify_networks.is_enabled(), + 'Verify networks is disabled') + + def _save_settings(self): + Networks().save_settings.click() + time.sleep(1) + self._assert_save_cancel_disabled() + self.refresh() + + def _test_ranges_plus_icon(self, network): + with getattr(Networks(), network) as n: + n.ip_ranges[0].icon_plus.click() + self.assertEqual(len(n.ip_ranges), 2, 'Plus icon. row 1') + n.ip_ranges[1].icon_plus.click() + self.assertEqual(len(n.ip_ranges), 3, 'Plus icon. row 2') + n.ip_ranges[1].start.send_keys(RANGES[0][0]) + n.ip_ranges[1].end.send_keys(RANGES[0][1]) + n.ip_ranges[0].icon_plus.click() + self.assertEqual(len(n.ip_ranges), 4, 'Plus icon. row 1') + self.assertEqual(n.ip_ranges[1].start.get_attribute('value'), '') + self.assertEqual(n.ip_ranges[1].end.get_attribute('value'), '') + self.assertEqual(n.ip_ranges[2].start.get_attribute('value'), + RANGES[0][0]) + self.assertEqual(n.ip_ranges[2].end.get_attribute('value'), + RANGES[0][1]) + + def _test_ranges_minus_icon(self, network): + with getattr(Networks(), network) as n: + for i in range(3): + n.ip_ranges[i].icon_plus.click() + n.ip_ranges[3].icon_minus.click() + self.assertEqual(len(n.ip_ranges), 3, 'Minus icon. last row') + n.ip_ranges[2].start.send_keys(RANGES[0][0]) + n.ip_ranges[2].end.send_keys(RANGES[0][1]) + n.ip_ranges[1].icon_minus.click() + self.assertEqual(len(n.ip_ranges), 2, 'Minus icon. second row') + self.assertEqual(n.ip_ranges[1].start.get_attribute('value'), + RANGES[0][0]) + self.assertEqual(n.ip_ranges[1].end.get_attribute('value'), + RANGES[0][1]) + + def _test_ranges(self, network, values): + with getattr(Networks(), network) as n: + n.ip_ranges[0].icon_plus.click() + n.ip_ranges[0].start.clear() + n.ip_ranges[0].start.send_keys(values[0][0]) + n.ip_ranges[0].end.clear() + n.ip_ranges[0].end.send_keys(values[0][1]) + n.ip_ranges[1].start.send_keys(values[1][0]) + n.ip_ranges[1].end.send_keys(values[1][1]) + self._save_settings() + with getattr(Networks(), network) as n: + self.assertEqual(n.ip_ranges[0].start.get_attribute('value'), + values[0][0]) + self.assertEqual(n.ip_ranges[0].end.get_attribute('value'), + values[0][1]) + self.assertEqual(n.ip_ranges[1].start.get_attribute('value'), + values[1][0]) + self.assertEqual(n.ip_ranges[1].end.get_attribute('value'), + values[1][1]) + + n.ip_ranges[0].start.clear() + n.ip_ranges[0].start.send_keys(' ') + self.assertIn('Invalid IP range start', + n.ip_ranges[0].start. + find_element_by_xpath('../../..').text) + + n.ip_ranges[1].end.clear() + n.ip_ranges[1].end.send_keys(' ') + self.assertIn('Invalid IP range end', + n.ip_ranges[1].end. + find_element_by_xpath('../../..').text) + + def _test_use_vlan_tagging(self, network, vlan_id, initial_value=False): + def assert_on(): + with getattr(Networks(), network) as n: + self.assertTrue( + n.vlan_tagging. + find_element_by_tag_name('input').is_selected(), + 'use vlan tagging is turned on') + self.assertEqual(n.vlan_id.get_attribute('value'), vlan_id, + 'vlan id value') + + def assert_off(): + with getattr(Networks(), network) as n: + self.assertFalse( + n.vlan_tagging. + find_element_by_tag_name('input').is_selected(), + 'use vlan tagging is turned off') + self.assertFalse(n.vlan_id.is_displayed(), + 'vlan id input is not visible') + + def turn_on(): + with getattr(Networks(), network) as n: + n.vlan_tagging.click() + self.assertTrue(n.vlan_id.is_displayed(), + 'vlan id input is visible') + n.vlan_id.send_keys(vlan_id) + time.sleep(0.5) + self._save_settings() + assert_on() + with getattr(Networks(), network) as n: + n.vlan_id.clear() + n.vlan_id.send_keys(' ') + self.assertIn('Invalid VLAN ID', + n.vlan_id.find_element_by_xpath('../../..').text) + self._assert_save_verify_disabled() + + def turn_off(): + with getattr(Networks(), network) as n: + n.vlan_tagging.click() + self.assertFalse(n.vlan_id.is_displayed(), + 'vlan id input is not visible') + time.sleep(0.5) + self._save_settings() + assert_off() + + if initial_value: + turn_off() + turn_on() + else: + turn_on() + turn_off() + + def _test_text_field(self, network, field, value): + with getattr(Networks(), network) as n: + getattr(n, field).clear() + getattr(n, field).send_keys(value) + self._save_settings() + with getattr(Networks(), network) as n: + self.assertEqual( + getattr(n, field).get_attribute('value'), value, + 'New value') + getattr(n, field).clear() + getattr(n, field).send_keys(' ') + self.assertIn('Invalid', + getattr(n, field). + find_element_by_xpath('../../..').text) + self._assert_save_verify_disabled() + Networks().cancel_changes.click() + time.sleep(1) + with getattr(Networks(), network) as n: + self.assertEqual( + getattr(n, field).get_attribute('value'), value, + "cancel changes") + + def _test_select_field(self, network, field, value): + with getattr(Networks(), network) as n: + getattr(n, field).select_by_visible_text(value) + self._save_settings() + with getattr(Networks(), network) as n: + self.assertEqual( + getattr(n, field).first_selected_option.text, value, + 'New value') + getattr(n, field).options[0].click() + Networks().cancel_changes.click() + time.sleep(1) + with getattr(Networks(), network) as n: + self.assertEqual( + getattr(n, field).first_selected_option.text, value, + "cancel changes") + + +class TestNeutronNetworks(SimpleFlatNetworks): + + @classmethod + def setUpClass(cls): + BaseTestCase.setUpClass() + preconditions.Environment.simple_neutron_gre() + + def test_id_start(self): + self._test_text_field('neutron', 'id_start', '1500') + + def test_id_end(self): + self._test_text_field('neutron', 'id_end', '3500') + + def test_base_mac(self): + self._test_text_field('neutron', 'base_mac', 'aa:bb:3e:14:b4:a3') + + def test_floating_start(self): + self._test_text_field('neutron', 'floating_start', RANGES[3][0]) + + def test_floating_end(self): + self._test_text_field('neutron', 'floating_end', RANGES[3][1]) + + def test_cidr(self): + self._test_text_field('neutron', 'cidr', '192.168.111.0/16') + + def test_gateway(self): + self._test_text_field('neutron', 'gateway', '192.168.111.2') + + def test_nameserver0(self): + self._test_text_field('neutron', 'nameserver0', '5.5.5.5') + + def test_nameserver1(self): + self._test_text_field('neutron', 'nameserver1', '5.5.5.5') + + +class TestSimpleVlanNetworks(SimpleFlatNetworks): + + @classmethod + def setUpClass(cls): + BaseTestCase.setUpClass() + preconditions.Environment.simple_flat() + Environments().create_cluster_boxes[0].click() + Tabs().networks.click() + with Networks() as n: + n.vlan_manager.click() + n.save_settings.click() + time.sleep(1) + + def test_fixed_number_of_networks(self): + self._test_text_field('fixed', 'number_of_networks', '3') + + def test_fixed_size_of_networks(self): + self._test_select_field('fixed', 'network_size', '128') + + def test_fixed_vlan_range_start(self): + self._test_text_field('fixed', 'vlan_id', '120') + + def test_fixed_vlan_range_end_calculation(self): + start_values = [105, 120] + with Networks().fixed as n: + number = int(n.number_of_networks.get_attribute('value')) + for v in start_values: + n.vlan_id.clear() + n.vlan_id.send_keys(v) + self.assertEqual( + n.vlan_end.get_attribute('value'), + str(v + number - 1), 'end value') + + def test_fixed_vlan_range_end_calculation_2(self): + numbers = [5, 20] + with Networks().fixed as n: + start = int(n.vlan_id.get_attribute('value')) + for v in numbers: + n.number_of_networks.clear() + n.number_of_networks.send_keys(v) + self.assertEqual( + n.vlan_end.get_attribute('value'), + str(v + start - 1), 'end value') + + +class TestRangesControls(SimpleFlatNetworks): + + def test_public_plus_icon(self): + self._test_ranges_plus_icon('public') + + def test_public_minus_icon(self): + self._test_ranges_minus_icon('public') + + def test_floating_plus_icon(self): + self._test_ranges_plus_icon('floating') + + def test_floating_minus_icon(self): + self._test_ranges_minus_icon('floating') + + +class TestPublicNetwork(SimpleFlatNetworks): + + def test_ranges(self): + self._test_ranges('public', RANGES[:2]) + + def test_use_vlan_tagging(self): + self._test_use_vlan_tagging('public', '111', False) + + def test_net_mask(self): + self._test_text_field('public', 'netmask', '255.255.0.0') + + def test_gateway(self): + self._test_text_field('public', 'gateway', '172.16.0.2') + + +class TestFloatingNetwork(SimpleFlatNetworks): + + def test_ranges(self): + self._test_ranges('floating', RANGES[2:4]) + + def test_use_vlan_tagging(self): + value = '112' + with Networks().public as n: + n.vlan_tagging.click() + n.vlan_id.send_keys(value) + with Networks().floating as n: + self.assertTrue( + n.vlan_tagging.find_element_by_tag_name('input').is_selected()) + self.assertEqual(n.vlan_id.get_attribute('value'), value) + Networks().save_settings.click() + time.sleep(1) + self.refresh() + with Networks().floating as n: + self.assertTrue( + n.vlan_tagging.find_element_by_tag_name('input').is_selected()) + self.assertEqual(n.vlan_id.get_attribute('value'), value) + + +class TestManagementNetwork(SimpleFlatNetworks): + + def test_cidr(self): + self._test_text_field('management', 'cidr', '192.169.0.0/16') + + def test_use_vlan_tagging(self): + self._test_use_vlan_tagging('management', '111', True) + + +class TestStorageNetwork(SimpleFlatNetworks): + + def test_cidr(self): + self._test_text_field('storage', 'cidr', '192.170.0.0/16') + + def test_use_vlan_tagging(self): + self._test_use_vlan_tagging('storage', '111', True) + + +class TestFixedNetwork(SimpleFlatNetworks): + + def test_cidr(self): + self._test_text_field('fixed', 'cidr', '10.1.0.0/24') + + def test_use_vlan_tagging(self): + self._test_use_vlan_tagging('fixed', '111', True) + + +class TestDnsServers(SimpleFlatNetworks): + + def test_name_servers(self): + v1 = '8.7.7.7' + v2 = '8.6.6.6' + with Networks() as n: + n.dns1.clear() + n.dns1.send_keys(v1) + n.dns2.clear() + n.dns2.send_keys(v2) + self._save_settings() + with Networks() as n: + self.assertEqual(n.dns1.get_attribute('value'), v1, 'dns1') + self.assertEqual(n.dns1.get_attribute('value'), v1, 'dns2') + n.dns1.clear() + n.dns1.send_keys(' ') + self.assertIn('Invalid nameserver', + n.dns1.find_element_by_xpath('../../..').text) + + n.dns2.clear() + n.dns2.send_keys(' ') + self.assertIn('Invalid nameserver', + n.dns2.find_element_by_xpath('../../..').text) + self._assert_save_verify_disabled() + + Networks().cancel_changes.click() + self.assertEqual(n.dns1.get_attribute('value'), v1, + 'cancel changes dns1') + self.assertEqual(n.dns1.get_attribute('value'), v1, + 'cancel changes dns2') + + +class TestFlatVerifyNetworks(BaseTestCase): + + @classmethod + def setUpClass(cls): + BaseTestCase.setUpClass() + + def setUp(self): + BaseTestCase.clear_nailgun_database() + BaseTestCase.setUp(self) + preconditions.Environment.simple_flat() + Environments().create_cluster_boxes[0].click() + Tabs().networks.click() + time.sleep(1) + + def test_no_nodes(self): + with Networks() as n: + n.verify_networks.click() + self.assertIn( + 'At least two nodes are required', + n.verification_alert.text, + 'Alert text contains "At least two nodes are required"') + + def test_one_node(self): + Tabs().nodes.click() + Nodes().add_nodes.click() + time.sleep(1) + Nodes().nodes_discovered[0].checkbox.click() + RolesPanel().controller.click() + Nodes().apply_changes.click() + time.sleep(1) + Tabs().networks.click() + time.sleep(1) + with Networks() as n: + n.verify_networks.click() + self.assertIn( + 'At least two nodes are required', + n.verification_alert.text, + 'Alert text contains "At least two nodes are required"') + + def test_two_nodes(self): + Tabs().nodes.click() + Nodes().add_nodes.click() + time.sleep(1) + Nodes().nodes_discovered[0].checkbox.click() + Nodes().nodes_discovered[1].checkbox.click() + RolesPanel().compute.click() + Nodes().apply_changes.click() + time.sleep(1) + Tabs().networks.click() + time.sleep(1) + with Networks() as n: + n.verify_networks.click() + self.assertIn( + 'Verification succeeded. Your network is configured correctly', + n.verification_alert.text, + 'Verification succeeded') diff --git a/fuelweb_ui_test/tests/test_releases.py b/fuelweb_ui_test/tests/test_releases.py new file mode 100644 index 000000000..e0dcd474f --- /dev/null +++ b/fuelweb_ui_test/tests/test_releases.py @@ -0,0 +1,74 @@ +from pageobjects.base import PageObject +from pageobjects.environments import RedhatAccountPopup +from pageobjects.header import Header +from pageobjects.releases import Releases +from fuelweb_ui_test.settings import OPENSTACK_REDHAT +from fuelweb_ui_test.settings import REDHAT_USERNAME +from fuelweb_ui_test.settings import REDHAT_PASSWORD +from fuelweb_ui_test.settings import REDHAT_SATELLITE +from fuelweb_ui_test.settings import REDHAT_ACTIVATION_KEY +from fuelweb_ui_test.settings import OPENSTACK_CENTOS +from fuelweb_ui_test.settings import OPENSTACK_UBUNTU +from tests.base import BaseTestCase + + +class TestReleases(BaseTestCase): + + @classmethod + def setUpClass(cls): + BaseTestCase.setUpClass() + + def setUp(self): + BaseTestCase.clear_nailgun_database() + BaseTestCase.setUp(self) + Header().releases.click() + + def test_centos_is_active(self): + with Releases() as r: + self.assertEqual( + 'Active', r.dict[OPENSTACK_CENTOS].status.text, + 'CentOS status is active') + + def test_ubuntu_is_active(self): + with Releases() as r: + self.assertEqual( + 'Active', r.dict[OPENSTACK_UBUNTU].status.text, + 'Ubuntu status is active') + + def test_rhos_is_active(self): + with Releases() as r: + self.assertEqual( + 'Not available', r.dict[OPENSTACK_REDHAT].status.text, + 'RHOS status is Not available') + + def test_rhsm(self): + Releases().rhel_setup.click() + with RedhatAccountPopup() as p: + p.license_rhsm.click() + p.redhat_username.send_keys(REDHAT_USERNAME) + p.redhat_password.send_keys(REDHAT_PASSWORD) + p.apply.click() + p.wait_until_exists() + with Releases() as r: + PageObject.wait_until_exists( + r.dict[OPENSTACK_REDHAT].download_progress, timeout=20) + self.assertEqual( + 'Active', r.dict[OPENSTACK_REDHAT].status.text, + 'RHOS status is active') + + def test_rhn_satellite(self): + Releases().rhel_setup.click() + with RedhatAccountPopup() as p: + p.license_rhn.click() + p.redhat_username.send_keys(REDHAT_USERNAME) + p.redhat_password.send_keys(REDHAT_PASSWORD) + p.redhat_satellite.send_keys(REDHAT_SATELLITE) + p.redhat_activation_key.send_keys(REDHAT_ACTIVATION_KEY) + p.apply.click() + p.wait_until_exists() + with Releases() as r: + PageObject.wait_until_exists( + r.dict[OPENSTACK_REDHAT].download_progress, timeout=20) + self.assertEqual( + 'Active', r.dict[OPENSTACK_REDHAT].status.text, + 'RHOS status is active') diff --git a/fuelweb_ui_test/tests/test_roles.py b/fuelweb_ui_test/tests/test_roles.py new file mode 100644 index 000000000..e8eea5c27 --- /dev/null +++ b/fuelweb_ui_test/tests/test_roles.py @@ -0,0 +1,168 @@ +import time +from pageobjects.environments import Environments +from pageobjects.nodes import Nodes, RolesPanel +from tests import preconditions +from tests.base import BaseTestCase + +ERROR_ROLE_CANNOT_COMBINE = 'This role cannot be combined ' \ + 'with the other roles already selected.' +ROLE_UNALLOCATED = 'UNALLOCATED' +ROLE_CONTROLLER = 'CONTROLLER' +ROLE_COMPUTE = 'COMPUTE' +ROLE_CINDER = 'CINDER' +ROLE_CEPH = 'CEPH-OSD' + + +class BaseClass(BaseTestCase): + + def assertNodeInRoles(self, node, roles): + for role in roles: + self.assertIn(role, node.roles.text, "node's roles") + + def setUp(self): + BaseTestCase.setUp(self) + Environments().create_cluster_boxes[0].click() + Nodes().add_nodes.click() + time.sleep(1) + + +class TestRolesSimpleFlat(BaseClass): + + @classmethod + def setUpClass(cls): + BaseTestCase.setUpClass() + preconditions.Environment.simple_flat() + + def test_controller(self): + with Nodes()as n: + n.nodes_discovered[0].checkbox.click() + with RolesPanel() as r: + r.controller.click() + self.assertFalse(r.compute.is_enabled()) + self.assertIn( + ERROR_ROLE_CANNOT_COMBINE, + r.compute.find_element_by_xpath('../..').text, + 'error "{}" is visible'.format(ERROR_ROLE_CANNOT_COMBINE)) + with Nodes()as n: + self.assertNodeInRoles(n.nodes_discovered[0], [ROLE_CONTROLLER]) + self.assertTrue(n.apply_changes.is_enabled()) + n.nodes_discovered[0].checkbox.click() + self.assertFalse(n.apply_changes.is_enabled()) + self.assertNodeInRoles(n.nodes_discovered[0], [ROLE_UNALLOCATED]) + + def test_one_controller_allowed_nodes_disabled(self): + with Nodes()as n: + n.nodes_discovered[0].checkbox.click() + with RolesPanel() as r: + r.controller.click() + for n in Nodes().nodes_discovered[1:]: + self.assertFalse( + n.checkbox.find_element_by_tag_name('input').is_enabled(), + 'Checkbox is disabled') + + def test_one_controller_allowed_controller_role_disabled(self): + with Nodes()as n: + with RolesPanel() as r: + n.nodes_discovered[0].checkbox.click() + self.assertTrue(r.controller.is_enabled()) + for node in n.nodes_discovered[1:]: + node.checkbox.click() + self.assertFalse(r.controller.is_enabled()) + + def test_compute(self): + with Nodes()as n: + n.nodes_discovered[0].checkbox.click() + with RolesPanel() as r: + r.compute.click() + self.assertFalse(r.controller.is_enabled()) + self.assertIn( + ERROR_ROLE_CANNOT_COMBINE, + r.controller.find_element_by_xpath('../..').text, + 'error "{}" is visible'.format(ERROR_ROLE_CANNOT_COMBINE)) + with Nodes()as n: + self.assertNodeInRoles(n.nodes_discovered[0], [ROLE_COMPUTE]) + self.assertTrue(n.apply_changes.is_enabled()) + n.nodes_discovered[0].checkbox.click() + self.assertFalse(n.apply_changes.is_enabled()) + self.assertNodeInRoles(n.nodes_discovered[0], [ROLE_UNALLOCATED]) + + def test_cinder(self): + with Nodes()as n: + n.nodes_discovered[0].checkbox.click() + with RolesPanel() as r: + r.cinder.click() + with Nodes()as n: + self.assertNodeInRoles(n.nodes_discovered[0], [ROLE_CINDER]) + self.assertTrue(n.apply_changes.is_enabled()) + n.nodes_discovered[0].checkbox.click() + self.assertFalse(n.apply_changes.is_enabled()) + self.assertNodeInRoles(n.nodes_discovered[0], [ROLE_UNALLOCATED]) + + def test_ceph(self): + with Nodes()as n: + n.nodes_discovered[0].checkbox.click() + with RolesPanel() as r: + r.ceph_osd.click() + with Nodes()as n: + self.assertNodeInRoles(n.nodes_discovered[0], [ROLE_CEPH]) + self.assertTrue(n.apply_changes.is_enabled()) + n.nodes_discovered[0].checkbox.click() + self.assertFalse(n.apply_changes.is_enabled()) + self.assertNodeInRoles(n.nodes_discovered[0], [ROLE_UNALLOCATED]) + + def test_multiroles(self): + with Nodes()as n: + n.nodes_discovered[0].checkbox.click() + with RolesPanel() as r: + r.controller.click() + r.cinder.click() + r.ceph_osd.click() + with Nodes()as n: + self.assertNodeInRoles( + n.nodes_discovered[0], + [ROLE_CONTROLLER, ROLE_CINDER, ROLE_CEPH]) + + def test_several_nodes(self): + with Nodes()as n: + n.nodes_discovered[0].checkbox.click() + n.nodes_discovered[1].checkbox.click() + n.nodes_discovered[2].checkbox.click() + with RolesPanel() as r: + r.compute.click() + r.cinder.click() + r.ceph_osd.click() + with Nodes()as n: + self.assertNodeInRoles( + n.nodes_discovered[0], + [ROLE_COMPUTE, ROLE_CINDER, ROLE_CEPH]) + self.assertNodeInRoles( + n.nodes_discovered[1], + [ROLE_COMPUTE, ROLE_CINDER, ROLE_CEPH]) + self.assertNodeInRoles( + n.nodes_discovered[2], + [ROLE_COMPUTE, ROLE_CINDER, ROLE_CEPH]) + + +class TestRolesHAFlat(BaseClass): + + @classmethod + def setUpClass(cls): + BaseTestCase.setUpClass() + preconditions.Environment.ha_flat() + + def test_controller_role_always_enabled(self): + with Nodes()as n: + for node in n.nodes_discovered: + node.checkbox.click() + self.assertTrue(RolesPanel().controller.is_enabled()) + RolesPanel().controller.click() + for node in n.nodes_discovered: + self.assertNodeInRoles(node, [ROLE_CONTROLLER]) + + def test_all_nodes_could_be_controller(self): + RolesPanel().controller.click() + with Nodes()as n: + for node in n.nodes_discovered: + node.checkbox.click() + for node in n.nodes_discovered: + self.assertNodeInRoles(node, [ROLE_CONTROLLER]) diff --git a/fuelweb_ui_test/tests/test_support.py b/fuelweb_ui_test/tests/test_support.py new file mode 100644 index 000000000..028d80c42 --- /dev/null +++ b/fuelweb_ui_test/tests/test_support.py @@ -0,0 +1,59 @@ +import re +import time +import browser +from pageobjects.base import PageObject +from pageobjects.header import Header +from pageobjects.support import Support +from tests.base import BaseTestCase + + +class TestSupport(BaseTestCase): + + @classmethod + def setUpClass(cls): + BaseTestCase.setUpClass() + + def setUp(self): + BaseTestCase.clear_nailgun_database() + BaseTestCase.setUp(self) + time.sleep(1) + Header().support.click() + time.sleep(1) + + def test_register_fuel(self): + with Support() as s: + key = re.search( + 'key=(?P.*)', + s.register_fuel.get_attribute('href')).group('key') + self.assertEqual(216, len(key), 'Key not empty') + + s.register_fuel.click() + time.sleep(4) + browser.driver.switch_to_window(browser.driver.window_handles.pop()) + self.assertTrue( + browser.driver.find_element_by_css_selector( + '[value="Register and Activate subscription"]').is_displayed(), + '"Register and Activate subscription" is displayed') + + def test_contact_support(self): + Support().contact_support.click() + time.sleep(4) + browser.driver.switch_to_window(browser.driver.window_handles.pop()) + self.assertIn('http://software.mirantis.com/', + browser.driver.current_url) + + def test_diagnostic_snapshot(self): + Support().generate_snapshot.click() + with Support() as s: + PageObject.wait_element(s, 'download_snapshot') + self.assertTrue( + s.download_snapshot.is_enabled(), + '"Diagnostic Snapshot" is displayed') + + def test_capacity_audit(self): + Support().view_capacity_audit.click() + self.assertEqual( + 'Home/ Support/ Capacity', + Header().breadcrumb.text, + 'Breadcrumb text' + ) diff --git a/run_tests.sh b/run_tests.sh index 1015034f4..e75d08cf2 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -3,5 +3,5 @@ set -e set -x -flake8 --ignore=H302,H802 --exclude bin/,include/,lib/,local/,tmp/ --show-source fuelweb_test +flake8 --ignore=H302,H802 --exclude bin/,include/,lib/,local/,tmp/ --show-source fuelweb_test fuelweb_ui_test