Add navigation among pages
Add navigation module that provides navigation across pages. Modify existing test cases so they can benefit from the navigation module. * Navigation class - navigation mixin that adds navigation functionality to PageObject class * BaseNavigationPage - base class for pages that want to use navigation Partially implements blueprint: selenium-integration-testing Change-Id: I7a2145a599fe22613a6d5bfd6feaabd116ec0279
This commit is contained in:
parent
56341524ec
commit
bace1f573f
@ -13,7 +13,7 @@
|
||||
from openstack_dashboard.test.integration_tests.pages import basepage
|
||||
|
||||
|
||||
class OverviewPage(basepage.BasePage):
|
||||
class OverviewPage(basepage.BaseNavigationPage):
|
||||
def __init__(self, driver, conf):
|
||||
super(OverviewPage, self).__init__(driver, conf)
|
||||
self._page_title = "Usage Overview"
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
from selenium.webdriver.common import by
|
||||
|
||||
from openstack_dashboard.test.integration_tests.pages import navigation
|
||||
from openstack_dashboard.test.integration_tests.pages import pageobject
|
||||
from openstack_dashboard.test.integration_tests.regions import bars
|
||||
from openstack_dashboard.test.integration_tests.regions import menus
|
||||
@ -40,6 +41,7 @@ class BasePage(pageobject.PageObject):
|
||||
def navaccordion(self):
|
||||
return menus.NavigationAccordionRegion(self.driver, self.conf)
|
||||
|
||||
@property
|
||||
def error_message(self):
|
||||
src_elem = self._get_element(*self._error_msg_locator)
|
||||
return messages.ErrorMessageRegion(self.driver, self.conf, src_elem)
|
||||
@ -58,4 +60,8 @@ class BasePage(pageobject.PageObject):
|
||||
return self.go_to_login_page()
|
||||
|
||||
def go_to_help_page(self):
|
||||
self.topbar.user_dropdown_menu.click_on_help()
|
||||
self.topbar.user_dropdown_menu.click_on_help()
|
||||
|
||||
|
||||
class BaseNavigationPage(BasePage, navigation.Navigation):
|
||||
pass
|
||||
|
@ -21,7 +21,6 @@ from openstack_dashboard.test.integration_tests.pages.project.compute import \
|
||||
|
||||
|
||||
class LoginPage(pageobject.PageObject):
|
||||
|
||||
_login_username_field_locator = (by.By.CSS_SELECTOR, '#id_username')
|
||||
_login_password_field_locator = (by.By.CSS_SELECTOR, '#id_password')
|
||||
_login_submit_button_locator = (by.By.CSS_SELECTOR,
|
||||
@ -32,8 +31,8 @@ class LoginPage(pageobject.PageObject):
|
||||
self._page_title = "Login"
|
||||
|
||||
def is_login_page(self):
|
||||
return self.is_the_current_page() and \
|
||||
self._is_element_visible(*self._login_submit_button_locator)
|
||||
return (self.is_the_current_page() and
|
||||
self.is_element_visible(*self._login_submit_button_locator))
|
||||
|
||||
@property
|
||||
def username(self):
|
||||
|
331
openstack_dashboard/test/integration_tests/pages/navigation.py
Normal file
331
openstack_dashboard/test/integration_tests/pages/navigation.py
Normal file
@ -0,0 +1,331 @@
|
||||
# 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.
|
||||
|
||||
import importlib
|
||||
import types
|
||||
|
||||
|
||||
class Navigation(object):
|
||||
"""Provide basic navigation among pages.
|
||||
|
||||
* allows navigation among pages without need to import pageobjects
|
||||
* shortest path possible should be always used for navigation to
|
||||
specific page as user would navigate in the same manner
|
||||
* go_to_*some_page* method respects following pattern:
|
||||
* go_to_{sub_menu}_{pagename}page
|
||||
* go_to_*some_page* methods are generated at runtime at module import
|
||||
* go_to_*some_page* method returns pageobject
|
||||
* pages must be located in the correct directories for the navigation
|
||||
to work correctly
|
||||
* pages modules and class names must respect following pattern
|
||||
to work correctly with navigation module:
|
||||
* module name consist of menu item in lower case without spaces and '&'
|
||||
* page class begins with capital letter and ends with 'Page'
|
||||
|
||||
Examples:
|
||||
In order to go to Project/Compute/Overview page, one would have to call
|
||||
method go_to_compute_overviewpage()
|
||||
|
||||
In order to go to Admin/System/Overview page, one would have to call
|
||||
method go_to_system_overviewpage()
|
||||
|
||||
"""
|
||||
# constants
|
||||
MAX_SUB_LEVEL = 4
|
||||
MIN_SUB_LEVEL = 2
|
||||
SIDE_MENU_MAX_LEVEL = 3
|
||||
PAGES_IMPORT_PATH = "openstack_dashboard.test.integration_tests.pages.%s"
|
||||
ITEMS = "__items__"
|
||||
|
||||
PAGE_STRUCTURE = \
|
||||
{
|
||||
"Project":
|
||||
{
|
||||
"Compute":
|
||||
{
|
||||
"Access & Security":
|
||||
{
|
||||
ITEMS:
|
||||
(
|
||||
"Security Groups",
|
||||
"Key Pairs",
|
||||
"Floating IPs",
|
||||
"API Access"
|
||||
),
|
||||
},
|
||||
"Volumes":
|
||||
{
|
||||
ITEMS:
|
||||
(
|
||||
"Volumes",
|
||||
"Volume Snapshots"
|
||||
)
|
||||
},
|
||||
ITEMS:
|
||||
(
|
||||
"Overview",
|
||||
"Instances",
|
||||
"Images",
|
||||
)
|
||||
},
|
||||
"Network":
|
||||
{
|
||||
ITEMS:
|
||||
(
|
||||
"Network Topology",
|
||||
"Networks",
|
||||
"Routers"
|
||||
)
|
||||
},
|
||||
"Object Store":
|
||||
{
|
||||
ITEMS:
|
||||
(
|
||||
"Containers",
|
||||
)
|
||||
},
|
||||
"Data Processing":
|
||||
{
|
||||
ITEMS:
|
||||
(
|
||||
"Clusters",
|
||||
"Cluster Templates",
|
||||
"Node Group Templates",
|
||||
"Job Executions",
|
||||
"Jobs",
|
||||
"Job Binaries",
|
||||
"Data Sources",
|
||||
"Image Registry",
|
||||
"Plugins"
|
||||
),
|
||||
},
|
||||
"Orchestration":
|
||||
{
|
||||
ITEMS:
|
||||
(
|
||||
"Stacks",
|
||||
)
|
||||
}
|
||||
},
|
||||
"Admin":
|
||||
{
|
||||
"System":
|
||||
{
|
||||
"Resource Usage":
|
||||
{
|
||||
ITEMS:
|
||||
(
|
||||
"Daily Report",
|
||||
"Stats"
|
||||
)
|
||||
},
|
||||
"System info":
|
||||
{
|
||||
ITEMS:
|
||||
(
|
||||
"Services",
|
||||
"Compute Services",
|
||||
"Block Storage Services",
|
||||
"Network Agents",
|
||||
"Default Quotas"
|
||||
)
|
||||
},
|
||||
"Volumes":
|
||||
{
|
||||
ITEMS:
|
||||
(
|
||||
"Volumes",
|
||||
"Volume Types",
|
||||
"Volume Snapshots"
|
||||
)
|
||||
},
|
||||
ITEMS:
|
||||
(
|
||||
"Overview",
|
||||
"Hypervisors",
|
||||
"Host Aggregates",
|
||||
"Instances",
|
||||
"Flavors",
|
||||
"Images",
|
||||
"Networks",
|
||||
"Routers"
|
||||
)
|
||||
},
|
||||
},
|
||||
"Settings":
|
||||
{
|
||||
ITEMS:
|
||||
(
|
||||
"User Settings",
|
||||
"Change Password"
|
||||
)
|
||||
},
|
||||
"Identity":
|
||||
{
|
||||
ITEMS:
|
||||
(
|
||||
"Projects",
|
||||
"Users",
|
||||
"Groups",
|
||||
"Domains",
|
||||
"Roles"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
# protected methods
|
||||
def _go_to_page(self, path, page_class=None):
|
||||
"""Go to page specified via path parameter.
|
||||
|
||||
* page_class parameter overrides basic process for receiving
|
||||
pageobject
|
||||
"""
|
||||
path_len = len(path)
|
||||
if path_len < self.MIN_SUB_LEVEL or path_len > self.MAX_SUB_LEVEL:
|
||||
raise ValueError("Navigation path length should be in the interval"
|
||||
" between %s and %s, but its length is %s" %
|
||||
(self.MIN_SUB_LEVEL, self.MAX_SUB_LEVEL,
|
||||
path_len))
|
||||
|
||||
if path_len == self.MIN_SUB_LEVEL:
|
||||
# menu items that do not contain second layer of menu
|
||||
if path[0] == "Settings":
|
||||
self._go_to_settings_page(path[1])
|
||||
else:
|
||||
self._go_to_side_menu_page([path[0], None, path[1]])
|
||||
else:
|
||||
# side menu contains only three sub-levels
|
||||
self._go_to_side_menu_page(path[:self.SIDE_MENU_MAX_LEVEL])
|
||||
|
||||
if path_len == self.MAX_SUB_LEVEL:
|
||||
# apparently there is tabbed menu,
|
||||
# because another extra sub level is present
|
||||
self._go_to_tab_menu_page(path[self.MAX_SUB_LEVEL - 1])
|
||||
|
||||
# if there is some nonstandard pattern in page object naming
|
||||
return self._get_page_class(path, page_class)(self.driver, self.conf)
|
||||
|
||||
def _go_to_tab_menu_page(self, item_text):
|
||||
self.driver.find_element_by_link_text(item_text).click()
|
||||
|
||||
def _go_to_settings_page(self, item_text):
|
||||
"""Go to page that is located under the settings tab."""
|
||||
self.topbar.user_dropdown_menu.click_on_settings()
|
||||
self.navaccordion.click_on_menu_items(third_level=item_text)
|
||||
|
||||
def _go_to_side_menu_page(self, menu_items):
|
||||
"""Go to page that is located in the side menu (navaccordion)."""
|
||||
self.navaccordion.click_on_menu_items(*menu_items)
|
||||
|
||||
def _get_page_cls_name(self, filename):
|
||||
"""Gather page class name from path.
|
||||
|
||||
* take last item from path (should be python filename
|
||||
without extension)
|
||||
* make the first letter capital
|
||||
* append 'Page'
|
||||
"""
|
||||
cls_name = "".join((filename.capitalize(), "Page"))
|
||||
return cls_name
|
||||
|
||||
def _get_page_class(self, path, page_cls_name):
|
||||
|
||||
# last module name does not contain '_'
|
||||
final_module = self.unify_page_path(path[-1],
|
||||
preserve_spaces=False)
|
||||
page_cls_path = ".".join(path[:-1] + (final_module,))
|
||||
page_cls_path = self.unify_page_path(page_cls_path)
|
||||
# append 'page' as every page module ends with this keyword
|
||||
page_cls_path += "page"
|
||||
|
||||
page_cls_name = page_cls_name or self._get_page_cls_name(final_module)
|
||||
|
||||
# return imported class
|
||||
module = importlib.import_module(self.PAGES_IMPORT_PATH %
|
||||
page_cls_path)
|
||||
return getattr(module, page_cls_name)
|
||||
|
||||
class GoToMethodFactory(object):
|
||||
"""Represent the go_to_some_page method."""
|
||||
|
||||
METHOD_NAME_PREFIX = "go_to_"
|
||||
METHOD_NAME_SUFFIX = "page"
|
||||
METHOD_NAME_DELIMITER = "_"
|
||||
|
||||
# private methods
|
||||
def __init__(self, path, page_class=None):
|
||||
self.path = path
|
||||
self.page_class = page_class
|
||||
self._name = self._create_name()
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
return Navigation._go_to_page(args[0], self.path, self.page_class)
|
||||
|
||||
# protected methods
|
||||
def _create_name(self):
|
||||
"""Create method name.
|
||||
|
||||
* consist of 'go_to_subsubmenu_menuitem_page'
|
||||
"""
|
||||
submenu, menu_item = self.path[-2:]
|
||||
|
||||
name = "".join((self.METHOD_NAME_PREFIX, submenu,
|
||||
self.METHOD_NAME_DELIMITER, menu_item,
|
||||
self.METHOD_NAME_SUFFIX))
|
||||
name = Navigation.unify_page_path(name, preserve_spaces=False)
|
||||
return name
|
||||
|
||||
# properties
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
# classmethods
|
||||
@classmethod
|
||||
def _initialize_go_to_methods(cls):
|
||||
"""Create all navigation methods based on the PAGE_STRUCTURE."""
|
||||
|
||||
def rec(items, sub_menus):
|
||||
if isinstance(items, dict):
|
||||
for sub_menu, sub_item in items.iteritems():
|
||||
rec(sub_item, sub_menus + (sub_menu,))
|
||||
elif isinstance(items, tuple):
|
||||
# exclude ITEMS element from sub_menus
|
||||
paths = (sub_menus[:-1] + (menu_item,) for menu_item in items)
|
||||
for path in paths:
|
||||
cls._create_go_to_method(path)
|
||||
|
||||
rec(cls.PAGE_STRUCTURE, ())
|
||||
|
||||
@classmethod
|
||||
def _create_go_to_method(cls, path, class_name=None):
|
||||
go_to_method = Navigation.GoToMethodFactory(path, class_name)
|
||||
inst_method = types.MethodType(go_to_method, None, Navigation)
|
||||
setattr(Navigation, inst_method.name, inst_method)
|
||||
|
||||
@classmethod
|
||||
def unify_page_path(cls, path, preserve_spaces=True):
|
||||
"""Unify path to page.
|
||||
|
||||
Replace '&' in path with 'and', remove spaces (if not specified
|
||||
otherwise) and convert path to lower case.
|
||||
"""
|
||||
path = path.replace("&", "and")
|
||||
path = path.lower()
|
||||
if preserve_spaces:
|
||||
path = path.replace(" ", "_")
|
||||
else:
|
||||
path = path.replace(" ", "")
|
||||
return path
|
||||
|
||||
|
||||
Navigation._initialize_go_to_methods()
|
@ -1,42 +0,0 @@
|
||||
# Copyright 2014 Hewlett-Packard Development Company, L.P
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.webdriver.common import by
|
||||
|
||||
from openstack_dashboard.test.integration_tests.pages import basepage
|
||||
from openstack_dashboard.test.integration_tests.pages.project.compute.\
|
||||
access_and_security import keypairpage
|
||||
|
||||
|
||||
class AccessSecurityPage(basepage.BasePage):
|
||||
|
||||
_keypair_link_locator = (
|
||||
by.By.CSS_SELECTOR,
|
||||
'a[href*="?tab=access_security_tabs__keypairs_tab"]')
|
||||
|
||||
def __init__(self, driver, conf):
|
||||
super(AccessSecurityPage, self).__init__(driver, conf)
|
||||
self._page_title = "Access & Security"
|
||||
|
||||
@property
|
||||
def keypair_link(self):
|
||||
return self._get_element(*self._keypair_link_locator)
|
||||
|
||||
def _click_on_keypair_link(self):
|
||||
self.keypair_link.click()
|
||||
|
||||
def go_to_keypair_page(self):
|
||||
self._click_on_keypair_link()
|
||||
return keypairpage.KeypairPage(self.driver, self.conf)
|
@ -20,7 +20,7 @@ from openstack_dashboard.test.integration_tests.regions import forms
|
||||
from openstack_dashboard.test.integration_tests.regions import tables
|
||||
|
||||
|
||||
class KeypairPage(basepage.BasePage):
|
||||
class KeypairsPage(basepage.BaseNavigationPage):
|
||||
|
||||
_key_pairs_table_locator = (by.By.CSS_SELECTOR, 'table#keypairs')
|
||||
|
||||
@ -32,7 +32,7 @@ class KeypairPage(basepage.BasePage):
|
||||
CREATE_KEY_PAIR_FORM_FIELDS = ('name',)
|
||||
|
||||
def __init__(self, driver, conf):
|
||||
super(KeypairPage, self).__init__(driver, conf)
|
||||
super(KeypairsPage, self).__init__(driver, conf)
|
||||
self._page_title = "Access & Security"
|
||||
|
||||
def _get_row_with_keypair_name(self, name):
|
@ -12,15 +12,11 @@
|
||||
from selenium.webdriver.common import by
|
||||
|
||||
from openstack_dashboard.test.integration_tests.pages import basepage
|
||||
from openstack_dashboard.test.integration_tests.pages.project.compute.\
|
||||
access_and_security import accesssecuritypage
|
||||
from openstack_dashboard.test.integration_tests.pages.settings import \
|
||||
settingspage
|
||||
from openstack_dashboard.test.integration_tests.regions import forms
|
||||
from openstack_dashboard.test.integration_tests.regions import tables
|
||||
|
||||
|
||||
class OverviewPage(basepage.BasePage):
|
||||
class OverviewPage(basepage.BaseNavigationPage):
|
||||
_usage_table_locator = (by.By.CSS_SELECTOR, 'table#project_usage')
|
||||
_date_form_locator = (by.By.CSS_SELECTOR, 'form#date_form')
|
||||
|
||||
@ -30,19 +26,6 @@ class OverviewPage(basepage.BasePage):
|
||||
super(OverviewPage, self).__init__(driver, conf)
|
||||
self._page_title = 'Instance Overview'
|
||||
|
||||
def go_to_settings_page(self):
|
||||
self.topbar.user_dropdown_menu.click_on_settings()
|
||||
return settingspage.SettingsPage(self.driver, self.conf)
|
||||
|
||||
def go_to_accesssecurity_page(self):
|
||||
access_security_locator_flag = self._is_element_visible(
|
||||
*self.navaccordion._project_access_security_locator)
|
||||
if not access_security_locator_flag:
|
||||
self.navaccordion.project_bar.click()
|
||||
self.navaccordion.access_security.click()
|
||||
return accesssecuritypage.AccessSecurityPage(
|
||||
self.driver, self.conf)
|
||||
|
||||
@property
|
||||
def usage_table(self):
|
||||
src_elem = self._get_element(*self._usage_table_locator)
|
||||
|
@ -16,7 +16,7 @@ from openstack_dashboard.test.integration_tests.pages import basepage
|
||||
from openstack_dashboard.test.integration_tests.regions import forms
|
||||
|
||||
|
||||
class ChangePasswordPage(basepage.BasePage):
|
||||
class ChangepasswordPage(basepage.BaseNavigationPage):
|
||||
|
||||
_password_form_locator = (by.By.CSS_SELECTOR,
|
||||
'div#change_password_modal')
|
||||
|
@ -18,7 +18,7 @@ from openstack_dashboard.test.integration_tests.pages.settings import \
|
||||
from openstack_dashboard.test.integration_tests.regions import forms
|
||||
|
||||
|
||||
class SettingsPage(basepage.BasePage):
|
||||
class UsersettingsPage(basepage.BaseNavigationPage):
|
||||
DEFAULT_LANGUAGE = "en"
|
||||
DEFAULT_TIMEZONE = "UTC"
|
||||
DEFAULT_PAGESIZE = "20"
|
||||
@ -35,7 +35,7 @@ class SettingsPage(basepage.BasePage):
|
||||
'a[href*="/settings/password/"]')
|
||||
|
||||
def __init__(self, driver, conf):
|
||||
super(SettingsPage, self).__init__(driver, conf)
|
||||
super(UsersettingsPage, self).__init__(driver, conf)
|
||||
self._page_title = "User Settings"
|
||||
|
||||
@property
|
@ -9,12 +9,15 @@
|
||||
# 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.webdriver.common import by
|
||||
|
||||
from openstack_dashboard.test.integration_tests.regions import baseregion
|
||||
|
||||
|
||||
class CannotClickMenuItemException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class NavigationAccordionRegion(baseregion.BaseRegion):
|
||||
"""Navigation menu located in the left."""
|
||||
_project_access_security_locator = (
|
||||
@ -25,10 +28,43 @@ class NavigationAccordionRegion(baseregion.BaseRegion):
|
||||
".//*[@id='main_content']//div[contains(text(),"
|
||||
"'Project')]")
|
||||
|
||||
MAX_MENU_ITEM_CLICK_TRIES = 100
|
||||
|
||||
@property
|
||||
def project_bar(self):
|
||||
return self._get_element(*self._project_bar_locator)
|
||||
|
||||
_first_level_item_selected_locator = (by.By.CSS_SELECTOR, 'dt.active')
|
||||
_second_level_item_selected_locator = (by.By.CSS_SELECTOR, 'h4.active')
|
||||
|
||||
_first_level_item_xpath_template = '//dt[contains(text(),\'%s\')]'
|
||||
_second_level_item_xpath_template = '//h4[contains(text(),\'%s\')]'
|
||||
_third_level_item_xpath_template = '//li/a[text()=\'%s\']'
|
||||
|
||||
def _get_first_level_item_locator(self, text):
|
||||
return (by.By.XPATH,
|
||||
self._first_level_item_xpath_template % text)
|
||||
|
||||
def _get_second_level_item_locator(self, text):
|
||||
return (by.By.XPATH,
|
||||
self._second_level_item_xpath_template % text)
|
||||
|
||||
def _get_third_level_item_locator(self, text):
|
||||
return (by.By.XPATH,
|
||||
self._third_level_item_xpath_template % text)
|
||||
|
||||
def get_first_level_selected_item(self):
|
||||
if self._is_element_present(*self._first_level_item_selected_locator):
|
||||
return self._get_element(*self._first_level_item_selected_locator)
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_second_level_selected_item(self):
|
||||
if self._is_element_present(*self._second_level_item_selected_locator):
|
||||
return self._get_element(*self._second_level_item_selected_locator)
|
||||
else:
|
||||
return None
|
||||
|
||||
@property
|
||||
def access_security(self):
|
||||
return self._get_element(*self._project_access_security_locator)
|
||||
@ -37,6 +73,58 @@ class NavigationAccordionRegion(baseregion.BaseRegion):
|
||||
def change_password(self):
|
||||
return self._get_element(*self._settings_change_password_locator)
|
||||
|
||||
def _click_menu_item(self, text, loc_craft_func, get_selected_func=None):
|
||||
"""Click on menu item if not selected.
|
||||
|
||||
Menu animation that visualize transition from one selection to
|
||||
another take some time - if clicked on item during this animation
|
||||
nothing happens, therefore it is necessary to do this in a loop.
|
||||
"""
|
||||
|
||||
if not get_selected_func:
|
||||
self._click_item(text, loc_craft_func)
|
||||
else:
|
||||
for _ in xrange(self.MAX_MENU_ITEM_CLICK_TRIES):
|
||||
selected_item = get_selected_func()
|
||||
if selected_item and text == selected_item.text:
|
||||
break
|
||||
|
||||
# In case different item was chosen previously scroll it,
|
||||
# because otherwise selenium will complain with
|
||||
# MoveTargetOutOfBoundsException
|
||||
if selected_item:
|
||||
selected_item.click()
|
||||
self._click_item(text, loc_craft_func)
|
||||
else:
|
||||
|
||||
# One should never get in here,
|
||||
# this suggest that something is wrong
|
||||
raise CannotClickMenuItemException()
|
||||
|
||||
def _click_item(self, text, loc_craft_func):
|
||||
"""Click on item."""
|
||||
item_locator = loc_craft_func(text)
|
||||
item = self._get_element(*item_locator)
|
||||
item.click()
|
||||
|
||||
def click_on_menu_items(self, first_level=None,
|
||||
second_level=None,
|
||||
third_level=None):
|
||||
if first_level:
|
||||
self._click_menu_item(first_level,
|
||||
self._get_first_level_item_locator,
|
||||
self.get_first_level_selected_item)
|
||||
if second_level:
|
||||
self._click_menu_item(second_level,
|
||||
self._get_second_level_item_locator,
|
||||
self.get_second_level_selected_item)
|
||||
|
||||
# it is not checked that third level item is clicked because behaviour
|
||||
# of the third menu layer is buggy => always click
|
||||
if third_level:
|
||||
self._click_menu_item(third_level,
|
||||
self._get_third_level_item_locator)
|
||||
|
||||
|
||||
class DropDownMenuRegion(baseregion.BaseRegion):
|
||||
"""Drop down menu region."""
|
||||
|
@ -23,12 +23,10 @@ class TestKeypair(helpers.TestCase):
|
||||
KEYPAIR_NAME = 'horizonkeypair_' + str(uuid.uuid4())
|
||||
|
||||
def test_keypair(self):
|
||||
accesssecurity_page = self.home_pg.go_to_accesssecurity_page()
|
||||
keypair_page = accesssecurity_page.go_to_keypair_page()
|
||||
|
||||
keypair_page = self.home_pg.go_to_accessandsecurity_keypairspage()
|
||||
keypair_page.create_keypair(self.KEYPAIR_NAME)
|
||||
accesssecurity_page = self.home_pg.go_to_accesssecurity_page()
|
||||
keypair_page = accesssecurity_page.go_to_keypair_page()
|
||||
|
||||
keypair_page = self.home_pg.go_to_accessandsecurity_keypairspage()
|
||||
self.assertTrue(keypair_page.is_keypair_present(self.KEYPAIR_NAME))
|
||||
|
||||
keypair_page.delete_keypair(self.KEYPAIR_NAME)
|
||||
|
@ -21,8 +21,7 @@ class TestPasswordChange(helpers.TestCase):
|
||||
"""Changes the password, verifies it was indeed changed and resets to
|
||||
default password.
|
||||
"""
|
||||
settings_page = self.home_pg.go_to_settings_page()
|
||||
passwordchange_page = settings_page.go_to_change_password_page()
|
||||
passwordchange_page = self.home_pg.go_to_settings_changepasswordpage()
|
||||
|
||||
try:
|
||||
passwordchange_page.change_password(self.conf.identity.password,
|
||||
@ -32,8 +31,9 @@ class TestPasswordChange(helpers.TestCase):
|
||||
user=self.conf.identity.username, password=NEW_PASSWORD)
|
||||
self.assertTrue(self.home_pg.is_logged_in,
|
||||
"Failed to login with new password")
|
||||
settings_page = self.home_pg.go_to_settings_page()
|
||||
passwordchange_page = settings_page.go_to_change_password_page()
|
||||
settings_page = self.home_pg.go_to_settings_usersettingspage()
|
||||
passwordchange_page = settings_page.\
|
||||
go_to_settings_changepasswordpage()
|
||||
finally:
|
||||
passwordchange_page.reset_to_default_password(NEW_PASSWORD)
|
||||
self.login_pg.login()
|
||||
|
@ -36,7 +36,7 @@ class TestUserSettings(helpers.TestCase):
|
||||
* changes the number of items per page (page size)
|
||||
* verifies all changes were successfully executed
|
||||
"""
|
||||
self.settings_page = self.home_pg.go_to_settings_page()
|
||||
self.settings_page = self.home_pg.go_to_settings_usersettingspage()
|
||||
|
||||
self.settings_page.change_language("es")
|
||||
self.settings_page.change_timezone("Asia/Jerusalem")
|
||||
|
Loading…
x
Reference in New Issue
Block a user