Provide integration test for managing a project's members
A new MembershipMenuRegion was implemented specifically for the task of dealing with Users being assigned to a Projects. It may be re-used in other tests as well, which deal with the same membership concept (e.g., Flavor Access control). The checker method .is_the_current_page() is changed to act either as a boolean method, or as an internal assert method. Co-Authored-By: Timur Sufiev <tsufiev@mirantis.com> Implements blueprint: horizon-integration-tests-coverage Change-Id: Ie661a1522951e1e86c461c6ec284fcf4a3e6d6fb
This commit is contained in:
parent
5d6003971f
commit
f623281b29
|
@ -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 = [
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -259,3 +259,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)
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue