Fix instances integration tests
The aim of this PS is to fix existing skipped instances integration tests listed below: - TestAdminInstances.test_create_delete_instance - TestInstances.test_create_delete_instance This PS also resolves Partial-Bug: #1774697 Change-Id: I9c2385274a2725409f61137ae201d868d24a56b4
This commit is contained in:
@@ -15,4 +15,5 @@ from openstack_dashboard.test.integration_tests.pages.project.compute \
|
|||||||
|
|
||||||
|
|
||||||
class InstancesPage(instancespage.InstancesPage):
|
class InstancesPage(instancespage.InstancesPage):
|
||||||
pass
|
|
||||||
|
INSTANCES_TABLE_NAME_COLUMN = 'Name'
|
||||||
|
|||||||
@@ -10,35 +10,32 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
import netaddr
|
import netaddr
|
||||||
|
from selenium.webdriver.common import by
|
||||||
|
|
||||||
from openstack_dashboard.test.integration_tests.pages import basepage
|
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 forms
|
||||||
|
from openstack_dashboard.test.integration_tests.regions import menus
|
||||||
from openstack_dashboard.test.integration_tests.regions import tables
|
from openstack_dashboard.test.integration_tests.regions import tables
|
||||||
|
|
||||||
|
|
||||||
class LaunchInstanceForm(forms.TabbedFormRegion):
|
|
||||||
field_mappings = ((
|
|
||||||
"availability_zone", "name", "flavor",
|
|
||||||
"count", "source_type", "instance_snapshot_id",
|
|
||||||
"volume_id", "volume_snapshot_id", "image_id", "volume_size",
|
|
||||||
"vol_delete_on_instance_delete"),
|
|
||||||
("keypair", "groups"),
|
|
||||||
("script_source", "script_upload", "script_data"),
|
|
||||||
("disk_config", "config_drive")
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(self, driver, conf):
|
|
||||||
super(LaunchInstanceForm, self).__init__(
|
|
||||||
driver, conf, field_mappings=self.field_mappings)
|
|
||||||
|
|
||||||
|
|
||||||
class InstancesTable(tables.TableRegion):
|
class InstancesTable(tables.TableRegion):
|
||||||
name = "instances"
|
name = "instances"
|
||||||
|
LAUNCH_INSTANCE_FORM_FIELDS = (
|
||||||
|
("name", "count", "availability_zone"),
|
||||||
|
("boot_source_type", "volume_size"),
|
||||||
|
{
|
||||||
|
'flavor': menus.InstanceFlavorMenuRegion
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'network': menus.InstanceAvailableResourceMenuRegion
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
@tables.bind_table_action('launch-ng')
|
@tables.bind_table_action('launch-ng')
|
||||||
def launch_instance(self, launch_button):
|
def launch_instance(self, launch_button):
|
||||||
launch_button.click()
|
launch_button.click()
|
||||||
return LaunchInstanceForm(self.driver, self.conf)
|
return forms.WizardFormRegion(self.driver, self.conf,
|
||||||
|
self.LAUNCH_INSTANCE_FORM_FIELDS)
|
||||||
|
|
||||||
@tables.bind_table_action('delete')
|
@tables.bind_table_action('delete')
|
||||||
def delete_instance(self, delete_button):
|
def delete_instance(self, delete_button):
|
||||||
@@ -50,18 +47,23 @@ class InstancesPage(basepage.BaseNavigationPage):
|
|||||||
|
|
||||||
DEFAULT_FLAVOR = 'm1.tiny'
|
DEFAULT_FLAVOR = 'm1.tiny'
|
||||||
DEFAULT_COUNT = 1
|
DEFAULT_COUNT = 1
|
||||||
DEFAULT_BOOT_SOURCE = 'Boot from image'
|
DEFAULT_BOOT_SOURCE = 'Image'
|
||||||
DEFAULT_VOLUME_NAME = None
|
DEFAULT_VOLUME_NAME = None
|
||||||
DEFAULT_SNAPSHOT_NAME = None
|
DEFAULT_SNAPSHOT_NAME = None
|
||||||
DEFAULT_VOLUME_SNAPSHOT_NAME = None
|
DEFAULT_VOLUME_SNAPSHOT_NAME = None
|
||||||
DEFAULT_VOL_DELETE_ON_INSTANCE_DELETE = False
|
DEFAULT_VOL_DELETE_ON_INSTANCE_DELETE = True
|
||||||
DEFAULT_SECURITY_GROUP = True
|
DEFAULT_SECURITY_GROUP = True
|
||||||
|
DEFAULT_NETWORK_TYPE = 'shared'
|
||||||
|
|
||||||
INSTANCES_TABLE_NAME_COLUMN = 'Instance Name'
|
INSTANCES_TABLE_NAME_COLUMN = 'Instance Name'
|
||||||
INSTANCES_TABLE_STATUS_COLUMN = 'Status'
|
INSTANCES_TABLE_STATUS_COLUMN = 'Status'
|
||||||
INSTANCES_TABLE_IP_COLUMN = 'IP Address'
|
INSTANCES_TABLE_IP_COLUMN = 'IP Address'
|
||||||
INSTANCES_TABLE_IMAGE_NAME_COLUMN = 'Image Name'
|
INSTANCES_TABLE_IMAGE_NAME_COLUMN = 'Image Name'
|
||||||
|
|
||||||
|
SOURCE_STEP_INDEX = 1
|
||||||
|
FLAVOR_STEP_INDEX = 2
|
||||||
|
NETWORKS_STEP_INDEX = 3
|
||||||
|
|
||||||
def __init__(self, driver, conf):
|
def __init__(self, driver, conf):
|
||||||
super(InstancesPage, self).__init__(driver, conf)
|
super(InstancesPage, self).__init__(driver, conf)
|
||||||
self._page_title = "Instances"
|
self._page_title = "Instances"
|
||||||
@@ -71,8 +73,10 @@ class InstancesPage(basepage.BaseNavigationPage):
|
|||||||
name)
|
name)
|
||||||
|
|
||||||
def _get_rows_with_instances_names(self, names):
|
def _get_rows_with_instances_names(self, names):
|
||||||
return [self.instances_table.get_row(
|
return [
|
||||||
self.INSTANCES_TABLE_IMAGE_NAME_COLUMN, n) for n in names]
|
self.instances_table.get_row(
|
||||||
|
self.INSTANCES_TABLE_IMAGE_NAME_COLUMN, n) for n in names
|
||||||
|
]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def instances_table(self):
|
def instances_table(self):
|
||||||
@@ -96,19 +100,35 @@ class InstancesPage(basepage.BaseNavigationPage):
|
|||||||
instance_form = self.instances_table.launch_instance()
|
instance_form = self.instances_table.launch_instance()
|
||||||
instance_form.availability_zone.value = available_zone
|
instance_form.availability_zone.value = available_zone
|
||||||
instance_form.name.text = instance_name
|
instance_form.name.text = instance_name
|
||||||
instance_form.flavor.text = flavor
|
|
||||||
instance_form.count.value = instance_count
|
instance_form.count.value = instance_count
|
||||||
instance_form.source_type.text = boot_source
|
instance_form.switch_to(self.SOURCE_STEP_INDEX)
|
||||||
|
instance_form.boot_source_type.text = boot_source
|
||||||
boot_source = self._get_source_name(instance_form, boot_source,
|
boot_source = self._get_source_name(instance_form, boot_source,
|
||||||
self.conf.launch_instances)
|
self.conf.launch_instances)
|
||||||
if not source_name:
|
if not source_name:
|
||||||
source_name = boot_source[1]
|
source_name = boot_source
|
||||||
boot_source[0].text = source_name
|
menus.InstanceAvailableResourceMenuRegion(
|
||||||
|
self.driver, self.conf).transfer_available_resource(source_name)
|
||||||
if device_size:
|
if device_size:
|
||||||
instance_form.volume_size.value = device_size
|
instance_form.volume_size.value = device_size
|
||||||
if vol_delete_on_instance_delete:
|
if vol_delete_on_instance_delete:
|
||||||
instance_form.vol_delete_on_instance_delete.mark()
|
self.vol_delete_on_instance_delete_click()
|
||||||
|
instance_form.switch_to(self.FLAVOR_STEP_INDEX)
|
||||||
|
instance_form.flavor.transfer_available_resource(flavor)
|
||||||
|
instance_form.switch_to(self.NETWORKS_STEP_INDEX)
|
||||||
|
instance_form.network.transfer_available_resource(
|
||||||
|
self.DEFAULT_NETWORK_TYPE)
|
||||||
instance_form.submit()
|
instance_form.submit()
|
||||||
|
instance_form.wait_till_wizard_disappears()
|
||||||
|
|
||||||
|
def vol_delete_on_instance_delete_click(self):
|
||||||
|
locator = (
|
||||||
|
by.By.XPATH,
|
||||||
|
'//label[contains(@ng-model, "vol_delete_on_instance_delete")]')
|
||||||
|
elements = self._get_elements(*locator)
|
||||||
|
for ele in elements:
|
||||||
|
if ele.text == 'Yes':
|
||||||
|
ele.click()
|
||||||
|
|
||||||
def delete_instance(self, name):
|
def delete_instance(self, name):
|
||||||
row = self._get_row_with_instance_name(name)
|
row = self._get_row_with_instance_name(name)
|
||||||
@@ -139,15 +159,14 @@ class InstancesPage(basepage.BaseNavigationPage):
|
|||||||
('Active', 'Error'))
|
('Active', 'Error'))
|
||||||
return status == 'Active'
|
return status == 'Active'
|
||||||
|
|
||||||
def _get_source_name(self, instance, boot_source,
|
def _get_source_name(self, instance, boot_source, conf):
|
||||||
conf):
|
if 'Image' in boot_source:
|
||||||
if 'image' in boot_source:
|
return conf.image_name
|
||||||
return instance.image_id, conf.image_name
|
elif boot_source == 'Volume':
|
||||||
elif boot_source == 'Boot from volume':
|
|
||||||
return instance.volume_id, self.DEFAULT_VOLUME_NAME
|
return instance.volume_id, self.DEFAULT_VOLUME_NAME
|
||||||
elif boot_source == 'Boot from snapshot':
|
elif boot_source == 'Instance Snapshot':
|
||||||
return instance.instance_snapshot_id, self.DEFAULT_SNAPSHOT_NAME
|
return instance.instance_snapshot_id, self.DEFAULT_SNAPSHOT_NAME
|
||||||
elif 'volume snapshot (creates a new volume)' in boot_source:
|
elif 'Volume Snapshot' in boot_source:
|
||||||
return (instance.volume_snapshot_id,
|
return (instance.volume_snapshot_id,
|
||||||
self.DEFAULT_VOLUME_SNAPSHOT_NAME)
|
self.DEFAULT_VOLUME_SNAPSHOT_NAME)
|
||||||
|
|
||||||
|
|||||||
@@ -77,6 +77,10 @@ class BaseFormFieldRegion(baseregion.BaseRegion,
|
|||||||
def name(self):
|
def name(self):
|
||||||
return self.element.get_attribute('name')
|
return self.element.get_attribute('name')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id(self):
|
||||||
|
return self.element.get_attribute('id')
|
||||||
|
|
||||||
def is_required(self):
|
def is_required(self):
|
||||||
classes = self.driver.get_attribute('class')
|
classes = self.driver.get_attribute('class')
|
||||||
return 'required' in classes
|
return 'required' in classes
|
||||||
@@ -113,7 +117,7 @@ class CheckBoxFormFieldRegion(CheckBoxMixin, BaseFormFieldRegion):
|
|||||||
class ChooseFileFormFieldRegion(BaseFormFieldRegion):
|
class ChooseFileFormFieldRegion(BaseFormFieldRegion):
|
||||||
"""Choose file field."""
|
"""Choose file field."""
|
||||||
|
|
||||||
_element_locator_str_suffix = 'div > input[type=file]'
|
_element_locator_str_suffix = 'input[type=file]'
|
||||||
|
|
||||||
def choose(self, path):
|
def choose(self, path):
|
||||||
self.element.send_keys(os.path.join(os.getcwd(), path))
|
self.element.send_keys(os.path.join(os.getcwd(), path))
|
||||||
@@ -136,31 +140,31 @@ class TextInputFormFieldRegion(BaseTextFormFieldRegion):
|
|||||||
"""Text input box."""
|
"""Text input box."""
|
||||||
|
|
||||||
_element_locator_str_suffix = \
|
_element_locator_str_suffix = \
|
||||||
'div > input[type=text], div > input[type=None]'
|
'input[type=text], input[type=None]'
|
||||||
|
|
||||||
|
|
||||||
class PasswordInputFormFieldRegion(BaseTextFormFieldRegion):
|
class PasswordInputFormFieldRegion(BaseTextFormFieldRegion):
|
||||||
"""Password text input box."""
|
"""Password text input box."""
|
||||||
|
|
||||||
_element_locator_str_suffix = 'div > input[type=password]'
|
_element_locator_str_suffix = 'input[type=password]'
|
||||||
|
|
||||||
|
|
||||||
class EmailInputFormFieldRegion(BaseTextFormFieldRegion):
|
class EmailInputFormFieldRegion(BaseTextFormFieldRegion):
|
||||||
"""Email text input box."""
|
"""Email text input box."""
|
||||||
|
|
||||||
_element_locator_str_suffix = 'div > input[type=email]'
|
_element_locator_str_suffix = 'input[type=email]'
|
||||||
|
|
||||||
|
|
||||||
class TextAreaFormFieldRegion(BaseTextFormFieldRegion):
|
class TextAreaFormFieldRegion(BaseTextFormFieldRegion):
|
||||||
"""Multi-line text input box."""
|
"""Multi-line text input box."""
|
||||||
|
|
||||||
_element_locator_str_suffix = 'div > textarea'
|
_element_locator_str_suffix = 'textarea'
|
||||||
|
|
||||||
|
|
||||||
class IntegerFormFieldRegion(BaseFormFieldRegion):
|
class IntegerFormFieldRegion(BaseFormFieldRegion):
|
||||||
"""Integer input box."""
|
"""Integer input box."""
|
||||||
|
|
||||||
_element_locator_str_suffix = 'div > input[type=number]'
|
_element_locator_str_suffix = 'input[type=number]'
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def value(self):
|
def value(self):
|
||||||
@@ -174,7 +178,7 @@ class IntegerFormFieldRegion(BaseFormFieldRegion):
|
|||||||
class SelectFormFieldRegion(BaseFormFieldRegion):
|
class SelectFormFieldRegion(BaseFormFieldRegion):
|
||||||
"""Select box field."""
|
"""Select box field."""
|
||||||
|
|
||||||
_element_locator_str_suffix = 'div > select.form-control'
|
_element_locator_str_suffix = 'select.form-control'
|
||||||
|
|
||||||
def is_displayed(self):
|
def is_displayed(self):
|
||||||
return self.element._el.is_displayed()
|
return self.element._el.is_displayed()
|
||||||
@@ -201,6 +205,10 @@ class SelectFormFieldRegion(BaseFormFieldRegion):
|
|||||||
def name(self):
|
def name(self):
|
||||||
return self.element._el.get_attribute('name')
|
return self.element._el.get_attribute('name')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id(self):
|
||||||
|
return self.element._el.get_attribute('id')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def text(self):
|
def text(self):
|
||||||
return self.element.first_selected_option.text
|
return self.element.first_selected_option.text
|
||||||
@@ -226,7 +234,7 @@ class SelectFormFieldRegion(BaseFormFieldRegion):
|
|||||||
class ThemableSelectFormFieldRegion(BaseFormFieldRegion):
|
class ThemableSelectFormFieldRegion(BaseFormFieldRegion):
|
||||||
"""Select box field."""
|
"""Select box field."""
|
||||||
|
|
||||||
_element_locator_str_suffix = 'div > .themable-select'
|
_element_locator_str_suffix = '.themable-select'
|
||||||
_raw_select_locator = (by.By.CSS_SELECTOR, 'select')
|
_raw_select_locator = (by.By.CSS_SELECTOR, 'select')
|
||||||
_selected_label_locator = (by.By.CSS_SELECTOR, '.dropdown-title')
|
_selected_label_locator = (by.By.CSS_SELECTOR, '.dropdown-title')
|
||||||
_dropdown_menu_locator = (by.By.CSS_SELECTOR, 'ul.dropdown-menu > li > a')
|
_dropdown_menu_locator = (by.By.CSS_SELECTOR, 'ul.dropdown-menu > li > a')
|
||||||
@@ -332,6 +340,7 @@ class FormRegion(BaseFormRegion):
|
|||||||
_header_locator = (by.By.CSS_SELECTOR, 'div.modal-header > h3')
|
_header_locator = (by.By.CSS_SELECTOR, 'div.modal-header > h3')
|
||||||
_side_info_locator = (by.By.CSS_SELECTOR, 'div.right')
|
_side_info_locator = (by.By.CSS_SELECTOR, 'div.right')
|
||||||
_fields_locator = (by.By.CSS_SELECTOR, 'fieldset')
|
_fields_locator = (by.By.CSS_SELECTOR, 'fieldset')
|
||||||
|
_step_locator = (by.By.CSS_SELECTOR, 'div.step')
|
||||||
|
|
||||||
# private methods
|
# private methods
|
||||||
def __init__(self, driver, conf, src_elem=None, field_mappings=None):
|
def __init__(self, driver, conf, src_elem=None, field_mappings=None):
|
||||||
@@ -359,9 +368,15 @@ class FormRegion(BaseFormRegion):
|
|||||||
|
|
||||||
def _get_form_fields(self):
|
def _get_form_fields(self):
|
||||||
factory = FieldFactory(self.driver, self.conf, self.fields_src_elem)
|
factory = FieldFactory(self.driver, self.conf, self.fields_src_elem)
|
||||||
|
fields = {}
|
||||||
try:
|
try:
|
||||||
self._turn_off_implicit_wait()
|
self._turn_off_implicit_wait()
|
||||||
return {field.name: field for field in factory.fields()}
|
for field in factory.fields():
|
||||||
|
if hasattr(field, 'name') and field.name is not None:
|
||||||
|
fields.update({field.name.replace('-', '_'): field})
|
||||||
|
elif hasattr(field, 'id') and field.id is not None:
|
||||||
|
fields.update({field.id.replace('-', '_'): field})
|
||||||
|
return fields
|
||||||
finally:
|
finally:
|
||||||
self._turn_on_implicit_wait()
|
self._turn_on_implicit_wait()
|
||||||
|
|
||||||
@@ -465,6 +480,61 @@ class TabbedFormRegion(FormRegion):
|
|||||||
src_elem=self.src_elem)
|
src_elem=self.src_elem)
|
||||||
|
|
||||||
|
|
||||||
|
class WizardFormRegion(FormRegion):
|
||||||
|
"""Form consists of sequence of steps."""
|
||||||
|
|
||||||
|
_submit_locator = (by.By.CSS_SELECTOR,
|
||||||
|
'*.btn.btn-primary.finish[type=button]')
|
||||||
|
|
||||||
|
def __init__(self, driver, conf, field_mappings=None, default_step=0):
|
||||||
|
self.current_step = default_step
|
||||||
|
super(WizardFormRegion, self).__init__(driver,
|
||||||
|
conf,
|
||||||
|
field_mappings=field_mappings)
|
||||||
|
|
||||||
|
def _form_getter(self):
|
||||||
|
return self.driver.find_element(*self._default_form_locator)
|
||||||
|
|
||||||
|
def _prepare_mappings(self, field_mappings):
|
||||||
|
return [
|
||||||
|
super(WizardFormRegion, self)._prepare_mappings(step_mappings)
|
||||||
|
for step_mappings in field_mappings
|
||||||
|
]
|
||||||
|
|
||||||
|
def _init_form_fields(self):
|
||||||
|
self.switch_to(self.current_step)
|
||||||
|
|
||||||
|
def _init_step_fields(self, step_index):
|
||||||
|
steps = self._get_elements(*self._step_locator)
|
||||||
|
self.fields_src_elem = steps[step_index]
|
||||||
|
fields = self._get_form_fields()
|
||||||
|
current_step_mappings = self.field_mappings[step_index]
|
||||||
|
for accessor_name, accessor_expr in current_step_mappings.items():
|
||||||
|
if isinstance(accessor_expr, str):
|
||||||
|
self._dynamic_properties[accessor_name] = fields[accessor_expr]
|
||||||
|
else: # it is a class
|
||||||
|
self._dynamic_properties[accessor_name] = accessor_expr(
|
||||||
|
self.driver, self.conf)
|
||||||
|
|
||||||
|
def switch_to(self, step_index=0):
|
||||||
|
self.steps.switch_to(index=step_index)
|
||||||
|
self._init_step_fields(step_index)
|
||||||
|
|
||||||
|
def wait_till_wizard_disappears(self):
|
||||||
|
try:
|
||||||
|
self.wait_till_element_disappears(self._form_getter)
|
||||||
|
except exceptions.StaleElementReferenceException:
|
||||||
|
# The form might be absent already by the time the first check
|
||||||
|
# occurs. So just suppress the exception here.
|
||||||
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
def steps(self):
|
||||||
|
return menus.WizardMenuRegion(self.driver,
|
||||||
|
self.conf,
|
||||||
|
src_elem=self.src_elem)
|
||||||
|
|
||||||
|
|
||||||
class DateFormRegion(BaseFormRegion):
|
class DateFormRegion(BaseFormRegion):
|
||||||
"""Form that queries data to table that is regularly below the form.
|
"""Form that queries data to table that is regularly below the form.
|
||||||
|
|
||||||
|
|||||||
@@ -292,6 +292,15 @@ class TabbedMenuRegion(baseregion.BaseRegion):
|
|||||||
self._get_elements(*self._tab_locator)[index].click()
|
self._get_elements(*self._tab_locator)[index].click()
|
||||||
|
|
||||||
|
|
||||||
|
class WizardMenuRegion(baseregion.BaseRegion):
|
||||||
|
|
||||||
|
_step_locator = (by.By.CSS_SELECTOR, 'li > a')
|
||||||
|
_default_src_locator = (by.By.CSS_SELECTOR, 'div > .nav.nav-pills')
|
||||||
|
|
||||||
|
def switch_to(self, index=0):
|
||||||
|
self._get_elements(*self._step_locator)[index].click()
|
||||||
|
|
||||||
|
|
||||||
class ProjectDropDownRegion(DropDownMenuRegion):
|
class ProjectDropDownRegion(DropDownMenuRegion):
|
||||||
_menu_items_locator = (
|
_menu_items_locator = (
|
||||||
by.By.CSS_SELECTOR, 'ul.context-selection li > a')
|
by.By.CSS_SELECTOR, 'ul.context-selection li > a')
|
||||||
@@ -414,3 +423,30 @@ class MembershipMenuRegion(baseregion.BaseRegion):
|
|||||||
self._switch_member_roles(
|
self._switch_member_roles(
|
||||||
name, roles2remove, self.get_member_allocated_roles,
|
name, roles2remove, self.get_member_allocated_roles,
|
||||||
allocated_members=allocated_members)
|
allocated_members=allocated_members)
|
||||||
|
|
||||||
|
|
||||||
|
class InstanceAvailableResourceMenuRegion(baseregion.BaseRegion):
|
||||||
|
_available_table_locator = (
|
||||||
|
by.By.CSS_SELECTOR,
|
||||||
|
'div.step:not(.ng-hide) div.transfer-available table')
|
||||||
|
_available_table_row_locator = (by.By.CSS_SELECTOR,
|
||||||
|
"tbody > tr.ng-scope:not(.detail-row)")
|
||||||
|
_available_table_column_locator = (by.By.TAG_NAME, "td")
|
||||||
|
_action_column_btn_locator = (by.By.CSS_SELECTOR,
|
||||||
|
"td.actions_column button")
|
||||||
|
|
||||||
|
def transfer_available_resource(self, resource_name):
|
||||||
|
available_table = self._get_element(*self._available_table_locator)
|
||||||
|
rows = available_table.find_elements(
|
||||||
|
*self._available_table_row_locator)
|
||||||
|
for row in rows:
|
||||||
|
cols = row.find_elements(*self._available_table_column_locator)
|
||||||
|
if len(cols) > 1 and cols[1].text.strip() in resource_name:
|
||||||
|
row_selector_btn = row.find_element(
|
||||||
|
*self._action_column_btn_locator)
|
||||||
|
row_selector_btn.click()
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
class InstanceFlavorMenuRegion(InstanceAvailableResourceMenuRegion):
|
||||||
|
_action_column_btn_locator = (by.By.CSS_SELECTOR, "td.action-col button")
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
import functools
|
import functools
|
||||||
|
|
||||||
|
from django.utils import html
|
||||||
from selenium.common import exceptions
|
from selenium.common import exceptions
|
||||||
from selenium.webdriver.common import by
|
from selenium.webdriver.common import by
|
||||||
|
|
||||||
@@ -60,6 +61,7 @@ class TableRegion(baseregion.BaseRegion):
|
|||||||
'div.table_search > .themable-select')
|
'div.table_search > .themable-select')
|
||||||
_cell_progress_bar_locator = (by.By.CSS_SELECTOR, 'div.progress-bar')
|
_cell_progress_bar_locator = (by.By.CSS_SELECTOR, 'div.progress-bar')
|
||||||
_warning_cell_locator = (by.By.CSS_SELECTOR, 'td.warning')
|
_warning_cell_locator = (by.By.CSS_SELECTOR, 'td.warning')
|
||||||
|
_default_form_locator = (by.By.CSS_SELECTOR, 'div.modal-dialog')
|
||||||
marker_name = 'marker'
|
marker_name = 'marker'
|
||||||
prev_marker_name = 'prev_marker'
|
prev_marker_name = 'prev_marker'
|
||||||
|
|
||||||
@@ -84,6 +86,9 @@ class TableRegion(baseregion.BaseRegion):
|
|||||||
def _warning_cell_getter(self):
|
def _warning_cell_getter(self):
|
||||||
return self.driver.find_element(*self._warning_cell_locator)
|
return self.driver.find_element(*self._warning_cell_locator)
|
||||||
|
|
||||||
|
def _form_getter(self):
|
||||||
|
return self.driver.find_element(*self._default_form_locator)
|
||||||
|
|
||||||
def __init__(self, driver, conf):
|
def __init__(self, driver, conf):
|
||||||
self._default_src_locator = self._table_locator(self.__class__.name)
|
self._default_src_locator = self._table_locator(self.__class__.name)
|
||||||
super(TableRegion, self).__init__(driver, conf)
|
super(TableRegion, self).__init__(driver, conf)
|
||||||
@@ -115,12 +120,13 @@ class TableRegion(baseregion.BaseRegion):
|
|||||||
def filter(self, value):
|
def filter(self, value):
|
||||||
self._set_search_field(value)
|
self._set_search_field(value)
|
||||||
self._click_search_btn()
|
self._click_search_btn()
|
||||||
|
self.driver.implicitly_wait(5)
|
||||||
|
|
||||||
def set_filter_value(self, value):
|
def set_filter_value(self, value):
|
||||||
search_menu = self._get_element(*self._search_option_locator)
|
self.wait_till_element_disappears(self._form_getter)
|
||||||
search_menu.click()
|
js_cmd = ("$('ul.dropdown-menu').find(\"a[data-select-value='%s']\")."
|
||||||
item_locator = self._search_menu_value_locator(value)
|
"click();" % (html.escape(value)))
|
||||||
search_menu.find_element(*item_locator).click()
|
self.driver.execute_script(js_cmd)
|
||||||
|
|
||||||
def get_row(self, column_name, text, exact_match=True):
|
def get_row(self, column_name, text, exact_match=True):
|
||||||
"""Get row that contains specified text in specified column.
|
"""Get row that contains specified text in specified column.
|
||||||
@@ -222,7 +228,13 @@ class TableRegion(baseregion.BaseRegion):
|
|||||||
lnk = self._get_element(*self._prev_locator)
|
lnk = self._get_element(*self._prev_locator)
|
||||||
lnk.click()
|
lnk.click()
|
||||||
|
|
||||||
def assert_definition(self, expected_table_definition, sorting=False):
|
def get_column_data(self, name_column='Name'):
|
||||||
|
return [row.cells[name_column].text for row in self.rows]
|
||||||
|
|
||||||
|
def assert_definition(self,
|
||||||
|
expected_table_definition,
|
||||||
|
sorting=False,
|
||||||
|
name_column='Name'):
|
||||||
"""Checks that actual table is expected one.
|
"""Checks that actual table is expected one.
|
||||||
|
|
||||||
Items to compare: 'next' and 'prev' links, count of rows and names of
|
Items to compare: 'next' and 'prev' links, count of rows and names of
|
||||||
@@ -231,7 +243,7 @@ class TableRegion(baseregion.BaseRegion):
|
|||||||
:param sorting: boolean arg specifying whether to sort actual names
|
:param sorting: boolean arg specifying whether to sort actual names
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
names = [row.cells['Name'].text for row in self.rows]
|
names = self.get_column_data(name_column)
|
||||||
if sorting:
|
if sorting:
|
||||||
names.sort()
|
names.sort()
|
||||||
actual_table = {'Next': self.is_next_link_available(),
|
actual_table = {'Next': self.is_next_link_available(),
|
||||||
|
|||||||
@@ -23,7 +23,10 @@ class TestInstances(helpers.TestCase):
|
|||||||
def instances_page(self):
|
def instances_page(self):
|
||||||
return self.home_pg.go_to_project_compute_instancespage()
|
return self.home_pg.go_to_project_compute_instancespage()
|
||||||
|
|
||||||
@pytest.mark.skip(reason="Bug 1774697")
|
@property
|
||||||
|
def instance_table_name_column(self):
|
||||||
|
return 'Instance Name'
|
||||||
|
|
||||||
def test_create_delete_instance(self):
|
def test_create_delete_instance(self):
|
||||||
"""tests the instance creation and deletion functionality:
|
"""tests the instance creation and deletion functionality:
|
||||||
|
|
||||||
@@ -35,16 +38,14 @@ class TestInstances(helpers.TestCase):
|
|||||||
instances_page = self.home_pg.go_to_project_compute_instancespage()
|
instances_page = self.home_pg.go_to_project_compute_instancespage()
|
||||||
|
|
||||||
instances_page.create_instance(self.INSTANCE_NAME)
|
instances_page.create_instance(self.INSTANCE_NAME)
|
||||||
self.assertTrue(
|
self.assertTrue(instances_page.find_message_and_dismiss(messages.INFO))
|
||||||
instances_page.find_message_and_dismiss(messages.SUCCESS))
|
|
||||||
self.assertFalse(
|
self.assertFalse(
|
||||||
instances_page.find_message_and_dismiss(messages.ERROR))
|
instances_page.find_message_and_dismiss(messages.ERROR))
|
||||||
self.assertTrue(instances_page.is_instance_active(self.INSTANCE_NAME))
|
self.assertTrue(instances_page.is_instance_active(self.INSTANCE_NAME))
|
||||||
|
|
||||||
instances_page = self.instances_page
|
instances_page = self.instances_page
|
||||||
instances_page.delete_instance(self.INSTANCE_NAME)
|
instances_page.delete_instance(self.INSTANCE_NAME)
|
||||||
self.assertTrue(
|
self.assertTrue(instances_page.find_message_and_dismiss(messages.INFO))
|
||||||
instances_page.find_message_and_dismiss(messages.SUCCESS))
|
|
||||||
self.assertFalse(
|
self.assertFalse(
|
||||||
instances_page.find_message_and_dismiss(messages.ERROR))
|
instances_page.find_message_and_dismiss(messages.ERROR))
|
||||||
self.assertTrue(instances_page.is_instance_deleted(self.INSTANCE_NAME))
|
self.assertTrue(instances_page.is_instance_deleted(self.INSTANCE_NAME))
|
||||||
@@ -242,8 +243,13 @@ class TestAdminInstances(helpers.AdminTestCase, TestInstances):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def instances_page(self):
|
def instances_page(self):
|
||||||
|
self.home_pg.go_to_admin_overviewpage()
|
||||||
return self.home_pg.go_to_admin_compute_instancespage()
|
return self.home_pg.go_to_admin_compute_instancespage()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def instance_table_name_column(self):
|
||||||
|
return 'Name'
|
||||||
|
|
||||||
@pytest.mark.skip(reason="Bug 1774697")
|
@pytest.mark.skip(reason="Bug 1774697")
|
||||||
def test_instances_pagination_and_filtration(self):
|
def test_instances_pagination_and_filtration(self):
|
||||||
super(TestAdminInstances, self).\
|
super(TestAdminInstances, self).\
|
||||||
|
|||||||
Reference in New Issue
Block a user