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:
Serhii Vasheka 2015-10-19 18:29:06 +03:00 committed by Timur Sufiev
parent 595d1ee6be
commit abad2d3af4
8 changed files with 288 additions and 3 deletions

View File

@ -95,6 +95,10 @@ class BaseWebObject(unittest.TestCase):
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
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(_):
elt = element() if hasattr(element, '__call__') else element

View File

@ -79,6 +79,15 @@ InstancesGroup = [
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 = [
cfg.BoolOpt('is_plugin',
default='False',
@ -110,5 +119,6 @@ def get_config():
cfg.CONF.register_opts(ScenarioGroup, group="scenario")
cfg.CONF.register_opts(InstancesGroup, group="launch_instances")
cfg.CONF.register_opts(PluginGroup, group="plugin")
cfg.CONF.register_opts(VolumeGroup, group="volume")
return cfg.CONF

View File

@ -56,3 +56,7 @@ ssh_user=cirros
available_zone=nova
#image_name to launch instances
image_name=cirros-0.3.4-x86_64-uec (24.0 MB)
[volume]
volume_type=lvmdriver-1
volume_size=1

View File

@ -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

View File

@ -96,9 +96,6 @@ class ImagesPage(basepage.BaseNavigationPage):
def is_image_active(self, 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():
return row.cells[self.IMAGES_TABLE_STATUS_COLUMN]
try:

View File

@ -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

View 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))