Added tests for check volumes management functionality
Tests check that create/edit/delete volumes actions are executed without errors under admin and non-admin user. Volumes page object was defined similar to other pages. 'volume' section is added to horizon.conf Implements blueprint: horizon-integration-tests-coverage Change-Id: Ib2c2bcb98bd010232fea404e565591cf6140383f
This commit is contained in:
parent
595d1ee6be
commit
abad2d3af4
@ -95,6 +95,10 @@ class BaseWebObject(unittest.TestCase):
|
|||||||
actually waiting for a _different_ element with a different text to
|
actually waiting for a _different_ element with a different text to
|
||||||
appear in place of an old element. So a way to avoid capturing stale
|
appear in place of an old element. So a way to avoid capturing stale
|
||||||
element reference should be provided for this use case.
|
element reference should be provided for this use case.
|
||||||
|
|
||||||
|
Better to wrap getting entity status cell in a lambda
|
||||||
|
to avoid problems with cell being replaced with totally different
|
||||||
|
element by Javascript
|
||||||
"""
|
"""
|
||||||
def predicate(_):
|
def predicate(_):
|
||||||
elt = element() if hasattr(element, '__call__') else element
|
elt = element() if hasattr(element, '__call__') else element
|
||||||
|
@ -79,6 +79,15 @@ InstancesGroup = [
|
|||||||
help="Boot Source to be selected for launch Instances"),
|
help="Boot Source to be selected for launch Instances"),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
VolumeGroup = [
|
||||||
|
cfg.StrOpt('volume_type',
|
||||||
|
default='lvmdriver-1',
|
||||||
|
help='Default volume type'),
|
||||||
|
cfg.StrOpt('volume_size',
|
||||||
|
default='1',
|
||||||
|
help='Default volume size ')
|
||||||
|
]
|
||||||
|
|
||||||
PluginGroup = [
|
PluginGroup = [
|
||||||
cfg.BoolOpt('is_plugin',
|
cfg.BoolOpt('is_plugin',
|
||||||
default='False',
|
default='False',
|
||||||
@ -110,5 +119,6 @@ def get_config():
|
|||||||
cfg.CONF.register_opts(ScenarioGroup, group="scenario")
|
cfg.CONF.register_opts(ScenarioGroup, group="scenario")
|
||||||
cfg.CONF.register_opts(InstancesGroup, group="launch_instances")
|
cfg.CONF.register_opts(InstancesGroup, group="launch_instances")
|
||||||
cfg.CONF.register_opts(PluginGroup, group="plugin")
|
cfg.CONF.register_opts(PluginGroup, group="plugin")
|
||||||
|
cfg.CONF.register_opts(VolumeGroup, group="volume")
|
||||||
|
|
||||||
return cfg.CONF
|
return cfg.CONF
|
||||||
|
@ -56,3 +56,7 @@ ssh_user=cirros
|
|||||||
available_zone=nova
|
available_zone=nova
|
||||||
#image_name to launch instances
|
#image_name to launch instances
|
||||||
image_name=cirros-0.3.4-x86_64-uec (24.0 MB)
|
image_name=cirros-0.3.4-x86_64-uec (24.0 MB)
|
||||||
|
|
||||||
|
[volume]
|
||||||
|
volume_type=lvmdriver-1
|
||||||
|
volume_size=1
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
from openstack_dashboard.test.integration_tests.pages.project.compute.volumes\
|
||||||
|
import volumespage
|
||||||
|
|
||||||
|
|
||||||
|
class VolumesPage(volumespage.VolumesPage):
|
||||||
|
pass
|
@ -96,9 +96,6 @@ class ImagesPage(basepage.BaseNavigationPage):
|
|||||||
def is_image_active(self, name):
|
def is_image_active(self, name):
|
||||||
row = self._get_row_with_image_name(name)
|
row = self._get_row_with_image_name(name)
|
||||||
|
|
||||||
# NOTE(tsufiev): better to wrap getting image status cell in a lambda
|
|
||||||
# to avoid problems with cell being replaced with totally different
|
|
||||||
# element by Javascript
|
|
||||||
def cell_getter():
|
def cell_getter():
|
||||||
return row.cells[self.IMAGES_TABLE_STATUS_COLUMN]
|
return row.cells[self.IMAGES_TABLE_STATUS_COLUMN]
|
||||||
try:
|
try:
|
||||||
|
@ -0,0 +1,137 @@
|
|||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
from selenium.common import exceptions
|
||||||
|
|
||||||
|
from openstack_dashboard.test.integration_tests.pages import basepage
|
||||||
|
from openstack_dashboard.test.integration_tests.regions import forms
|
||||||
|
from openstack_dashboard.test.integration_tests.regions import tables
|
||||||
|
|
||||||
|
VOLUME_SOURCE_TYPE = 'Volume'
|
||||||
|
IMAGE_SOURCE_TYPE = 'Image'
|
||||||
|
|
||||||
|
|
||||||
|
class VolumesTable(tables.TableRegion):
|
||||||
|
name = 'volumes'
|
||||||
|
|
||||||
|
# This form is applicable for volume creation from image only.
|
||||||
|
# Volume creation from volume requires additional 'volume_source' field
|
||||||
|
# which is available only in case at least one volume is already present.
|
||||||
|
CREATE_VOLUME_FROM_IMAGE_FORM_FIELDS = (
|
||||||
|
"name", "description", "volume_source_type", "image_source",
|
||||||
|
"type", "size", "availability_zone")
|
||||||
|
|
||||||
|
EDIT_VOLUME_FORM_FIELDS = ("name", "description")
|
||||||
|
|
||||||
|
@tables.bind_table_action('create')
|
||||||
|
def create_volume(self, create_button):
|
||||||
|
create_button.click()
|
||||||
|
return forms.FormRegion(
|
||||||
|
self.driver, self.conf,
|
||||||
|
field_mappings=self.CREATE_VOLUME_FROM_IMAGE_FORM_FIELDS)
|
||||||
|
|
||||||
|
@tables.bind_table_action('delete')
|
||||||
|
def delete_volume(self, delete_button):
|
||||||
|
delete_button.click()
|
||||||
|
return forms.BaseFormRegion(self.driver, self.conf)
|
||||||
|
|
||||||
|
@tables.bind_row_action('edit', primary=True)
|
||||||
|
def edit_volume(self, edit_button, row):
|
||||||
|
edit_button.click()
|
||||||
|
return forms.FormRegion(self.driver, self.conf,
|
||||||
|
field_mappings=self.EDIT_VOLUME_FORM_FIELDS)
|
||||||
|
|
||||||
|
|
||||||
|
class VolumesPage(basepage.BaseNavigationPage):
|
||||||
|
|
||||||
|
VOLUMES_TABLE_NAME_COLUMN = 'name'
|
||||||
|
VOLUMES_TABLE_STATUS_COLUMN = 'status'
|
||||||
|
|
||||||
|
def __init__(self, driver, conf):
|
||||||
|
super(VolumesPage, self).__init__(driver, conf)
|
||||||
|
self._page_title = "Volumes"
|
||||||
|
|
||||||
|
def _get_row_with_volume_name(self, name):
|
||||||
|
return self.volumes_table.get_row(
|
||||||
|
self.VOLUMES_TABLE_NAME_COLUMN, name)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def volumes_table(self):
|
||||||
|
return VolumesTable(self.driver, self.conf)
|
||||||
|
|
||||||
|
def create_volume(self, volume_name, description=None,
|
||||||
|
volume_source_type=IMAGE_SOURCE_TYPE,
|
||||||
|
volume_size=None,
|
||||||
|
volume_source=None):
|
||||||
|
volume_form = self.volumes_table.create_volume()
|
||||||
|
volume_form.name.text = volume_name
|
||||||
|
if description is not None:
|
||||||
|
volume_form.description.text = description
|
||||||
|
volume_form.volume_source_type.text = volume_source_type
|
||||||
|
volume_source_type = self._get_source_name(volume_form,
|
||||||
|
volume_source_type,
|
||||||
|
self.conf.launch_instances,
|
||||||
|
volume_source)
|
||||||
|
volume_source_type[0].text = volume_source_type[1]
|
||||||
|
if volume_size is None:
|
||||||
|
volume_size = self.conf.volume.volume_size
|
||||||
|
volume_form.size.value = volume_size
|
||||||
|
if volume_source_type != "Volume":
|
||||||
|
volume_form.type.value = self.conf.volume.volume_type
|
||||||
|
volume_form.availability_zone.value = \
|
||||||
|
self.conf.launch_instances.available_zone
|
||||||
|
volume_form.submit()
|
||||||
|
|
||||||
|
def delete_volume(self, name):
|
||||||
|
row = self._get_row_with_volume_name(name)
|
||||||
|
row.mark()
|
||||||
|
confirm_delete_volumes_form = self.volumes_table.delete_volume()
|
||||||
|
confirm_delete_volumes_form.submit()
|
||||||
|
|
||||||
|
def edit_volume(self, name, new_name=None, description=None):
|
||||||
|
row = self._get_row_with_volume_name(name)
|
||||||
|
volume_edit_form = self.volumes_table.edit_volume(row)
|
||||||
|
if new_name:
|
||||||
|
volume_edit_form.name.text = new_name
|
||||||
|
if description:
|
||||||
|
volume_edit_form.description.text = description
|
||||||
|
volume_edit_form.submit()
|
||||||
|
|
||||||
|
def is_volume_present(self, name):
|
||||||
|
return bool(self._get_row_with_volume_name(name))
|
||||||
|
|
||||||
|
def is_volume_status(self, name, status):
|
||||||
|
row = self._get_row_with_volume_name(name)
|
||||||
|
|
||||||
|
def cell_getter():
|
||||||
|
return row.cells[self.VOLUMES_TABLE_STATUS_COLUMN]
|
||||||
|
|
||||||
|
try:
|
||||||
|
self._wait_till_text_present_in_element(cell_getter, status)
|
||||||
|
except exceptions.TimeoutException:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def is_volume_deleted(self, name):
|
||||||
|
try:
|
||||||
|
getter = lambda: self._get_row_with_volume_name(name)
|
||||||
|
self.wait_till_element_disappears(getter)
|
||||||
|
except exceptions.TimeoutException:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _get_source_name(self, volume_form, volume_source_type, conf,
|
||||||
|
volume_source):
|
||||||
|
if volume_source_type == IMAGE_SOURCE_TYPE:
|
||||||
|
return volume_form.image_source, conf.image_name
|
||||||
|
if volume_source_type == VOLUME_SOURCE_TYPE:
|
||||||
|
return volume_form.volume_id, volume_source
|
115
openstack_dashboard/test/integration_tests/tests/test_volumes.py
Normal file
115
openstack_dashboard/test/integration_tests/tests/test_volumes.py
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
from openstack_dashboard.test.integration_tests import helpers
|
||||||
|
from openstack_dashboard.test.integration_tests.regions import messages
|
||||||
|
|
||||||
|
|
||||||
|
class TestVolumes(helpers.TestCase):
|
||||||
|
VOLUME_NAME = helpers.gen_random_resource_name("volume")
|
||||||
|
|
||||||
|
def test_volume_create_edit_delete(self):
|
||||||
|
"""This test case checks create, edit, delete volume functionality
|
||||||
|
executed by non-admin user::
|
||||||
|
Steps:
|
||||||
|
1. Login to Horizon Dashboard as horizon user
|
||||||
|
2. Navigate to Project -> Compute -> Volumes page
|
||||||
|
3. Create new volume
|
||||||
|
4. Check that the volume is in the list
|
||||||
|
5. Check that no Error messages present
|
||||||
|
6. Edit the volume
|
||||||
|
7. Check that the volume is still in the list
|
||||||
|
8. Check that no Error messages present
|
||||||
|
9. Delete the volume
|
||||||
|
10. Check that the volume is absent in the list
|
||||||
|
11. Check that no Error messages present
|
||||||
|
"""
|
||||||
|
volumes_page = self.home_pg.go_to_compute_volumes_volumespage()
|
||||||
|
|
||||||
|
volumes_page.create_volume(self.VOLUME_NAME)
|
||||||
|
self.assertTrue(
|
||||||
|
volumes_page.find_message_and_dismiss(messages.INFO))
|
||||||
|
self.assertFalse(
|
||||||
|
volumes_page.find_message_and_dismiss(messages.ERROR))
|
||||||
|
self.assertTrue(volumes_page.is_volume_present(self.VOLUME_NAME))
|
||||||
|
self.assertTrue(volumes_page.is_volume_status(self.VOLUME_NAME,
|
||||||
|
'Available'))
|
||||||
|
|
||||||
|
new_name = "edited_" + self.VOLUME_NAME
|
||||||
|
volumes_page.edit_volume(self.VOLUME_NAME, new_name, "description")
|
||||||
|
self.VOLUME_NAME = new_name
|
||||||
|
self.assertTrue(
|
||||||
|
volumes_page.find_message_and_dismiss(messages.INFO))
|
||||||
|
self.assertFalse(
|
||||||
|
volumes_page.find_message_and_dismiss(messages.ERROR))
|
||||||
|
self.assertTrue(volumes_page.is_volume_present(self.VOLUME_NAME))
|
||||||
|
self.assertTrue(volumes_page.is_volume_status(self.VOLUME_NAME,
|
||||||
|
'Available'))
|
||||||
|
|
||||||
|
volumes_page.delete_volume(self.VOLUME_NAME)
|
||||||
|
self.assertTrue(
|
||||||
|
volumes_page.find_message_and_dismiss(messages.SUCCESS))
|
||||||
|
self.assertFalse(
|
||||||
|
volumes_page.find_message_and_dismiss(messages.ERROR))
|
||||||
|
self.assertTrue(volumes_page.is_volume_deleted(self.VOLUME_NAME))
|
||||||
|
|
||||||
|
|
||||||
|
class TestAdminVolumes(helpers.AdminTestCase):
|
||||||
|
VOLUME_NAME = helpers.gen_random_resource_name("volume")
|
||||||
|
|
||||||
|
def test_volume_create_edit_delete_through_admin(self):
|
||||||
|
"""This test case checks create, edit, delete volume functionality
|
||||||
|
executed by admin user:
|
||||||
|
Steps:
|
||||||
|
1. Login to Horizon Dashboard as admin user
|
||||||
|
2. Navigate to Project -> Compute -> Volumes page
|
||||||
|
3. Create new volume
|
||||||
|
4. Check that the volume is in the list
|
||||||
|
5. Check that no Error messages present
|
||||||
|
6. Edit the volume
|
||||||
|
7. Check that the volume is still in the list
|
||||||
|
8. Check that no Error messages present
|
||||||
|
9. Go to Admin/System/Volumes page
|
||||||
|
10. Delete the volume
|
||||||
|
11. Check that the volume is absent in the list
|
||||||
|
12. Check that no Error messages present
|
||||||
|
"""
|
||||||
|
volumes_page = self.home_pg.go_to_compute_volumes_volumespage()
|
||||||
|
|
||||||
|
volumes_page.create_volume(self.VOLUME_NAME)
|
||||||
|
self.assertTrue(
|
||||||
|
volumes_page.find_message_and_dismiss(messages.INFO))
|
||||||
|
self.assertFalse(
|
||||||
|
volumes_page.find_message_and_dismiss(messages.ERROR))
|
||||||
|
self.assertTrue(volumes_page.is_volume_present(self.VOLUME_NAME))
|
||||||
|
self.assertTrue(volumes_page.is_volume_status(self.VOLUME_NAME,
|
||||||
|
'Available'))
|
||||||
|
|
||||||
|
new_name = "edited_" + self.VOLUME_NAME
|
||||||
|
volumes_page.edit_volume(self.VOLUME_NAME, new_name, "description")
|
||||||
|
self.VOLUME_NAME = new_name
|
||||||
|
self.assertTrue(
|
||||||
|
volumes_page.find_message_and_dismiss(messages.INFO))
|
||||||
|
self.assertFalse(
|
||||||
|
volumes_page.find_message_and_dismiss(messages.ERROR))
|
||||||
|
self.assertTrue(volumes_page.is_volume_present(self.VOLUME_NAME))
|
||||||
|
self.assertTrue(volumes_page.is_volume_status(self.VOLUME_NAME,
|
||||||
|
'Available'))
|
||||||
|
|
||||||
|
volumes_admin_page = self.home_pg.go_to_system_volumes_volumespage()
|
||||||
|
|
||||||
|
volumes_admin_page.delete_volume(self.VOLUME_NAME)
|
||||||
|
self.assertTrue(
|
||||||
|
volumes_admin_page.find_message_and_dismiss(messages.SUCCESS))
|
||||||
|
self.assertFalse(
|
||||||
|
volumes_admin_page.find_message_and_dismiss(messages.ERROR))
|
||||||
|
self.assertTrue(volumes_admin_page.is_volume_deleted(self.VOLUME_NAME))
|
Loading…
Reference in New Issue
Block a user