Merge "Provide integration test for managing a project's members"

This commit is contained in:
Jenkins 2016-05-04 10:11:23 +00:00 committed by Gerrit Code Review
commit c116704394
6 changed files with 200 additions and 10 deletions

View File

@ -47,6 +47,13 @@ IdentityGroup = [
cfg.StrOpt('admin_home_project',
default='admin',
help="Project to keep all objects belonging to an admin user."),
cfg.StrOpt('default_keystone_role',
default='Member',
help="Name of default role every user gets in his new project"),
cfg.StrOpt('default_keystone_admin_role',
default='admin',
help="Name of the role that grants admin rights to a user in "
"his project"),
]
ImageGroup = [

View File

@ -60,6 +60,12 @@ admin_password=secretadmin
# Project in which an admin user creates everything by default
admin_home_project=admin
# Name of default role every user gets in his new project
default_keystone_role=Member
# Name of the role that grants admin rights to a user in his project
default_keystone_admin_role=admin
[network]
# The cidr block to allocate tenant ipv4 subnets from (string value)
tenant_network_cidr=10.100.0.0/16

View File

@ -12,19 +12,31 @@
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 menus
from openstack_dashboard.test.integration_tests.regions import tables
class ProjectForm(forms.TabbedFormRegion):
FIELDS = (("name", "description", "enabled"),
{'members': menus.MembershipMenuRegion})
def __init__(self, driver, conf, tab=0):
super(ProjectForm, self).__init__(
driver, conf, field_mappings=self.FIELDS, default_tab=tab)
class ProjectsTable(tables.TableRegion):
name = 'tenants'
CREATE_PROJECT_FORM_FIELDS = (("name", "description", "enabled"),)
@tables.bind_table_action('create')
def create_project(self, create_button):
create_button.click()
return forms.TabbedFormRegion(
self.driver, self.conf,
field_mappings=self.CREATE_PROJECT_FORM_FIELDS)
return ProjectForm(self.driver, self.conf)
@tables.bind_row_action('update')
def update_members(self, members_button, row):
members_button.click()
return ProjectForm(self.driver, self.conf, tab=1)
@tables.bind_table_action('delete')
def delete_project(self, delete_button):
@ -72,3 +84,17 @@ class ProjectsPage(basepage.BaseNavigationPage):
def get_project_id_from_row(self, name):
row = self._get_row_with_project_name(name)
return row.cells[self.PROJECT_ID_TABLE_NAME_COLUMN].text
def allocate_user_to_project(self, user_name, roles, project_name):
row = self._get_row_with_project_name(project_name)
members_form = self.projects_table.update_members(row)
members_form.members.allocate_member(user_name)
members_form.members.allocate_member_roles(user_name, roles)
members_form.submit()
def get_user_roles_at_project(self, user_name, project_name):
row = self._get_row_with_project_name(project_name)
members_form = self.projects_table.update_members(row)
roles = members_form.members.get_member_allocated_roles(user_name)
members_form.cancel()
return set(roles)

View File

@ -29,11 +29,14 @@ class PageObject(basewebobject.BaseWebObject):
def page_title(self):
return self.driver.title
def is_the_current_page(self):
self.assertIn(self._page_title, self.page_title,
"Expected to find %s in page title, instead found: %s"
% (self._page_title, self.page_title))
return True
def is_the_current_page(self, do_assert=False):
found_expected_title = self.page_title.startswith(self._page_title)
if do_assert:
self.assertTrue(
found_expected_title,
"Expected to find %s in page title, instead found: %s"
% (self._page_title, self.page_title))
return found_expected_title
@property
def login_url(self):
@ -87,4 +90,4 @@ class PageObject(basewebobject.BaseWebObject):
def go_to_login_page(self):
self.driver.get(self.login_url)
self.is_the_current_page()
self.is_the_current_page(do_assert=True)

View File

@ -262,3 +262,116 @@ class ProjectDropDownRegion(DropDownMenuRegion):
else:
raise exceptions.NoSuchElementException(
"Not found element with text: %s" % name)
class MembershipMenuRegion(baseregion.BaseRegion):
_available_members_locator = (
by.By.CSS_SELECTOR, 'ul.available_members > ul.btn-group')
_allocated_members_locator = (
by.By.CSS_SELECTOR, 'ul.members > ul.btn-group')
_add_remove_member_sublocator = (
by.By.CSS_SELECTOR, 'li > a[href="#add_remove"]')
_member_name_sublocator = (
by.By.CSS_SELECTOR, 'li.member > span.display_name')
_member_roles_widget_sublocator = (by.By.CSS_SELECTOR, 'li.role_options')
_member_roles_widget_open_subsublocator = (by.By.CSS_SELECTOR, 'a.btn')
_member_roles_widget_roles_subsublocator = (
by.By.CSS_SELECTOR, 'ul.role_dropdown > li')
def _get_member_name(self, element):
return element.find_element(*self._member_name_sublocator).text
@property
def available_members(self):
return {self._get_member_name(el): el for el in
self._get_elements(*self._available_members_locator)}
@property
def allocated_members(self):
return {self._get_member_name(el): el for el in
self._get_elements(*self._allocated_members_locator)}
def allocate_member(self, name, available_members=None):
# NOTE(tsufiev): available_members here (and allocated_members below)
# are meant to be used for performance optimization to reduce the
# amount of calls to selenium by reusing still valid element reference
if available_members is None:
available_members = self.available_members
available_members[name].find_element(
*self._add_remove_member_sublocator).click()
def deallocate_member(self, name, allocated_members=None):
if allocated_members is None:
allocated_members = self.allocated_members
allocated_members[name].find_element(
*self._add_remove_member_sublocator).click()
def _get_member_roles_widget(self, name, allocated_members=None):
if allocated_members is None:
allocated_members = self.allocated_members
return allocated_members[name].find_element(
*self._member_roles_widget_sublocator)
def _get_member_all_roles(self, name, allocated_members=None):
roles_widget = self._get_member_roles_widget(name, allocated_members)
return roles_widget.find_elements(
*self._member_roles_widget_roles_subsublocator)
@staticmethod
def _is_role_selected(role):
return 'selected' in role.get_attribute('class').split()
@staticmethod
def _get_hidden_text(role):
return role.get_attribute('textContent')
def get_member_available_roles(self, name, allocated_members=None,
strip=True):
roles = self._get_member_all_roles(name, allocated_members)
return [(self._get_hidden_text(role).strip() if strip else role)
for role in roles if not self._is_role_selected(role)]
def get_member_allocated_roles(self, name, allocated_members=None,
strip=True):
roles = self._get_member_all_roles(name, allocated_members)
return [(self._get_hidden_text(role).strip() if strip else role)
for role in roles if self._is_role_selected(role)]
def open_member_roles_dropdown(self, name, allocated_members=None):
widget = self._get_member_roles_widget(name, allocated_members)
button = widget.find_element(
*self._member_roles_widget_open_subsublocator)
button.click()
def _switch_member_roles(self, name, roles2toggle, method,
allocated_members=None):
self.open_member_roles_dropdown(name, allocated_members)
roles = method(name, allocated_members, False)
roles2toggle = set(roles2toggle)
for role in roles:
role_name = role.text.strip()
if role_name in roles2toggle:
role.click()
roles2toggle.remove(role_name)
if not roles2toggle:
break
def allocate_member_roles(self, name, roles2add, allocated_members=None):
self._switch_member_roles(
name, roles2add, self.get_member_available_roles,
allocated_members=allocated_members)
def deallocate_member_roles(self, name, roles2remove,
allocated_members=None):
self._switch_member_roles(
name, roles2remove, self.get_member_allocated_roles,
allocated_members=allocated_members)

View File

@ -9,6 +9,7 @@
# 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
@ -36,3 +37,37 @@ class TestCreateDeleteProject(helpers.AdminTestCase):
self.assertFalse(
self.projects_page.find_message_and_dismiss(messages.ERROR))
self.assertFalse(self.projects_page.is_project_present(PROJECT_NAME))
class TestModifyProject(helpers.AdminTestCase):
def setUp(self):
super(TestModifyProject, self).setUp()
self.projects_page = self.home_pg.go_to_identity_projectspage()
self.projects_page.create_project(PROJECT_NAME)
self.assertTrue(
self.projects_page.find_message_and_dismiss(messages.SUCCESS))
def test_add_member(self):
admin_name = self.CONFIG.identity.admin_username
regular_role_name = self.CONFIG.identity.default_keystone_role
admin_role_name = self.CONFIG.identity.default_keystone_admin_role
roles2add = {regular_role_name, admin_role_name}
self.projects_page.allocate_user_to_project(
admin_name, roles2add, PROJECT_NAME)
self.assertTrue(
self.projects_page.find_message_and_dismiss(messages.SUCCESS))
self.assertFalse(
self.projects_page.find_message_and_dismiss(messages.ERROR))
user_roles = self.projects_page.get_user_roles_at_project(
admin_name, PROJECT_NAME)
self.assertEqual(roles2add, user_roles,
"The requested roles haven't been set for the user!")
def tearDown(self):
if not self.projects_page.is_the_current_page():
self.home_pg.go_to_identity_projectspage()
self.projects_page.delete_project(PROJECT_NAME)
super(TestModifyProject, self).tearDown()