class MonitorOverviewView(BaseLoggedInPage): """ Provide a view for the Monitor->Alerts->Overview page """ # since the status_card depends on the provider of the fired alert, it must be instantiated as: # status_card = view.status_card(<provider_name>) status_card = MonitorStatusCard # Used for Ascending/Descending sort sort_order = Text(".//button[./span[contains(@class,'sort-direction')]]") # Used to select filter_by items like 'Name', 'Severity' filter_by_dropdown = SelectorDropdown('uib-tooltip', 'Filter by') # Used to select sort by options like 'Name', 'Number of Associated Plans' sort_by_dropdown = SelectorDropdown( 'class', 'btn btn-default ng-binding dropdown-toggle') # Used to set group by group_by = BootstrapSelect( locator=('.//div[contains(@class, "bootstrap-select")] ' '/button[normalize-space(@title)="Environment"]/..')) # Used to set display display = BootstrapSelect( locator=('.//div[contains(@class, "bootstrap-select")] ' '/button[normalize-space(@title)="providers"]/..')) @property def in_monitor_alerts_overview(self): return (self.logged_in_as_current_user and self.navigation.currently_selected == ['Monitor', 'Alerts', 'Overview']) @property def is_displayed(self): return self.in_monitor_alerts_overview
class InfrastructureMappingView(BaseLoggedInPage): """This is an entire separate page, with many elements similar to what we had in MigrationPlanRequestDetailsView , so re-using some of those Paginator things by renaming those from v2vPaginator to v2vPaginator, etc.""" infra_mapping_list = InfraMappingList("infra-mappings-list-view") create_infrastructure_mapping = Text( locator='(//a|//button)' '[text()="Create Infrastructure Mapping"]') configure_providers = Text(locator='//a[text()="Configure Providers"]') paginator_view = View.include(v2vPaginatorPane, use_parent=True) search_box = TextInput( locator=".//div[contains(@class,'input-group')]/input") clear_filters = Text(".//a[text()='Clear All Filters']") # Used for Ascending/Descending sort sort_order = Text(".//button[./span[contains(@class,'sort-direction')]]") # Used to select filter_by 'Name' or 'Description' filter_by_dropdown = SelectorDropdown('id', 'filterFieldTypeMenu') # USed to select sort by options like 'Name', 'Number of Associated Plans' sort_by_dropdown = SelectorDropdown('id', 'sortTypeMenu') @property def is_displayed(self): # TODO: Resmove 1st condition, once /manageiq-v2v/issues/768 fix is backported to 510z return ( (self.navigation.currently_selected == ['Compute', 'Migration', 'Overview'] or self.navigation.currently_selected == ['Compute', 'Migration', 'Infrastructure Mappings']) and len( self.browser.elements(".//div[contains(@class,'spinner')]")) == 0 and (self.create_infrastructure_mapping.is_displayed or self.infra_mapping_list.is_displayed or self.configure_providers.is_displayed))
class AdHocMetricsView(BaseLoggedInPage): filter_dropdown = SelectorDropdown('uib-tooltip', 'Filter by') filter_result_header = Text('h5.ng-binding') apply_btn = Button("Apply Filters") selected_filter = None @property def is_displayed(self): return False def wait_for_filter_option_to_load(self): wait_for(lambda: bool(self.filter_dropdown.items), delay=5, num_sec=60) def wait_for_results_to_load(self): wait_for(lambda: bool(int(self.filter_result_header.text.split()[0])), delay=5, num_sec=60) def apply_filter(self): self.apply_btn.click() def set_filter(self, desired_filter): self.selected_filter = desired_filter self.filter_dropdown.fill_with(desired_filter) def get_random_filter(self): return str(random.choice(self.filter_dropdown.items)) def get_total_results_count(self): return int(self.filter_result_header.text.split()[0])
class data_view(View): # noqa table = Table('//*[@id="report_list_div"]//table') field = SelectorDropdown("id", "filterFieldTypeMenu") search_text = SearchBox(locator='//input[contains(@placeholder, "search text")]') # TODO: Write a separate paginator for data view def child_widget_accessed(self, widget): if self.parent.view_selector.selected != "Data View": self.parent.view_selector.select("Data View")
class vms(View): # noqa import_btn = Button('Import') importcsv = Button('Import CSV') hidden_field = HiddenFileInput(locator='.//input[contains(@accept,".csv")]') # TODO: Replace string keys by integer keys after row indexing issue get fixed # TODO: Replace Text by Button or GenericLocatorWidget after button text get added table = Table('.//div[contains(@class, "container-fluid")]/table', column_widgets={ 'Select': Checkbox(locator=".//input"), 1: Text('.//span/button[contains(@class,"btn btn-link")]')}) filter_by_dropdown = SelectorDropdown('id', 'filterFieldTypeMenu') search_box = TextInput(locator='.//div[contains(@class, "modal-content")]' '//div[contains(@class,"input-group")]/input') clear_filters = Text(".//a[text()='Clear All Filters']") error_text = Text('.//h3[contains(@class,"blank-slate-pf-main-action") and ' 'contains(text(), "Error:")]') error_icon = Text(locator='.//span[contains(@class, "pficon-error-circle-o")]') popover_text = Text(locator='.//div[contains(@class, "popover-content")]') @property def is_displayed(self): return (self.table.is_displayed and (len(self.browser.elements(".//div[contains(@class,'spinner')]")) == 0)) def filter_by_name(self, vm_name): try: # remove `if` cond https://github.com/ManageIQ/manageiq-v2v/issues/771 fixed if self.browser.appliance.version < '5.10': # Don't remove nxt line, remove `if` self.filter_by_dropdown.item_select("VM Name") # just unindent except NoSuchElementException: self.logger.info("`VM Name` not present in filter dropdown!") self.search_box.fill(vm_name) self.browser.send_keys(Keys.ENTER, self.search_box) def filter_by_source_cluster(self, cluster_name): try: self.filter_by_dropdown.item_select("Source Cluster") except NoSuchElementException: self.logger.info("`Source Cluster` not present in filter dropdown!") self.search_box.fill(cluster_name) self.browser.send_keys(Keys.ENTER, self.search_box) def filter_by_path(self, path): try: self.filter_by_dropdown.item_select("Path") except NoSuchElementException: self.logger.info("`Path` not present in filter dropdown!") self.search_box.fill(path) self.browser.send_keys(Keys.ENTER, self.search_box) def select_by_name(self, vm_name): self.filter_by_name(vm_name) vms_selected = [] for row in self.table.rows(): if vm_name in row.read()['VM Name']: row.select.fill(True) vms_selected.append(row.read()['VM Name']) return vms_selected
class AllInfraMappingView(InfrastructureMappingView): """All infrastructure mapping View""" infra_mapping_list = InfraMappingList("infra-mappings-list-view") create_infra_mapping = Text(locator='(//a|//button)[text()="Create Infrastructure Mapping"]') configure_providers = Text(locator='//a[text()="Configure Providers"]') paginator_view = View.include(V2VPaginatorPane, use_parent=True) search_box = TextInput(locator=".//div[contains(@class,'input-group')]/input") clear_filters = Text(".//a[text()='Clear All Filters']") sort_order = Text(".//button[./span[contains(@class,'sort-direction')]]") filter_by_dropdown = SelectorDropdown("id", "filterFieldTypeMenu") sort_by_dropdown = SelectorDropdown("id", "sortTypeMenu") @property def is_displayed(self): return len(self.browser.elements(".//div[contains(@class,'spinner')]")) == 0 and ( self.create_infra_mapping.is_displayed or self.infra_mapping_list.is_displayed or self.configure_providers.is_displayed )
class vms(View): # noqa import_btn = Button('Import') importcsv = Button('Import CSV') hidden_field = HiddenFileInput( locator='.//input[contains(@accept,".csv")]') table = Table('.//div[contains(@class, "container-fluid")]/table', column_widgets={"Select": Checkbox(locator=".//input")}) filter_by_dropdown = SelectorDropdown('id', 'filterFieldTypeMenu') search_box = TextInput( locator=".//div[contains(@class,'input-group')]/input") clear_filters = Text(".//a[text()='Clear All Filters']") @property def is_displayed(self): return self.filter_by_dropdown.is_displayed def filter_by_name(self, vm_name): try: self.filter_by_dropdown.item_select("VM Name") except NoSuchElementException: self.logger.info("`VM Name` not present in filter dropdown!") self.search_box.fill(vm_name) self.browser.send_keys(Keys.ENTER, self.search_box) def filter_by_source_cluster(self, cluster_name): try: self.filter_by_dropdown.item_select("Source Cluster") except NoSuchElementException: self.logger.info( "`Source Cluster` not present in filter dropdown!") self.search_box.fill(cluster_name) self.browser.send_keys(Keys.ENTER, self.search_box) def filter_by_path(self, path): try: self.filter_by_dropdown.item_select("Path") except NoSuchElementException: self.logger.info("`Path` not present in filter dropdown!") self.search_box.fill(path) self.browser.send_keys(Keys.ENTER, self.search_box) def select_by_name(self, vm_name): self.filter_by_name(vm_name) vms_selected = [] for row in self.table.rows(): if vm_name in row.read()['VM Name']: row.select.fill(True) vms_selected.append(row.read()['VM Name']) return vms_selected
class OptimizationSavedReportDetailsView(OptimizationView): field = SelectorDropdown("id", "filterFieldTypeMenu") search_text = SearchBox( locator='//input[contains(@placeholder, "search text")]') table = Table('//*[@id="main_div"]//table') @property def is_displayed(self): context_obj = self.context["object"] return (self.logged_in_as_current_user and self.title.text == context_obj.name and self.table.is_displayed and self.navigation.currently_selected == ["Overview", "Optimization"] and self.breadcrumb.locations == [ "Overview", "Optimization", context_obj.parent.parent.menu_name, context_obj.name ])
class MigrationPlanView(MigrationView): dashboard_cards = View.nested(DashboardCards) paginator_view = View.include(V2VPaginatorPane, use_parent=True) title = Text('.//div[contains(@class, "pull-left")]//h3') create_migration_plan = Text(locator='(//a|//button)[text()="Create Migration Plan"]') configure_providers = Text(locator='//a[text()="Configure Providers"]') sort_type_dropdown = SelectorDropdown("id", "sortTypeMenu") sort_direction = Text(locator=".//span[contains(@class,'sort-direction')]") progress_card = MigrationProgressBar() search_box = SearchBox(locator=".//div[contains(@class,'input-group')]/input") clear_filters = Text(".//a[text()='Clear All Filters']") @property def in_migration_plan(self): return ( self.in_explorer and self.create_migration_plan.is_displayed and len(self.browser.elements('.//div[contains(@class,"card-pf")]')) > 0 and len(self.browser.elements(PFIcon.icons.WARNING)) < 1 ) @property def is_displayed(self): return self.in_migration_plan
class MigrationPlanRequestDetailsView(View): migration_request_details_list = MigrationPlanRequestDetailsList( "plan-request-details-list") sort_type = SelectorDropdown('id', 'sortTypeMenu') paginator_view = View.include(v2vPaginatorPane, use_parent=True) search_box = TextInput( locator=".//div[contains(@class,'input-group')]/input") clear_filters = Text(".//a[text()='Clear All Filters']") # Used for Ascending/Descending sort sort_order = Text(".//button[./span[contains(@class,'sort-direction')]]") # Used to select filter_by 'Name' or 'Status' filter_by_dropdown = SelectorDropdown('id', 'filterFieldTypeMenu') # USed to select specific status from dropdown to filter items by filter_by_status_dropdown = SelectorDropdown('id', 'filterCategoryMenu') # USed to select sort by options like 'VM Name', 'Started' or 'Status' sort_by_dropdown = SelectorDropdown('id', 'sortTypeMenu') @property def is_displayed(self): return self.migration_request_details_list.is_displayed def filter_by_vm_name(self, vm_name): """Enter VM Name in search box and hit ENTER to filter the list of VMs. Args: vm_name(str): Takes VM Name as arg. """ try: self.filter_by_dropdown.item_select("VM Name") except NoSuchElementException: self.logger.info( "filter_by_dropdown not present, " "migration plan may not have started yet.Ignoring.") self.search_box.fill(vm_name) self.browser.send_keys(Keys.ENTER, self.search_box) def get_migration_status_by_vm_name(self, vm_name): """Search VM using filter_by_name and return its status. Args: vm_name(str): Takes VM Name as arg. """ try: # Try to clear previously applied filters, if any. self.clear_filters.click() except NoSuchElementException: # Ignore as button won't be visible if there were no filters applied. self.logger.info("Clear Filters button not present, ignoring.") self.filter_by_vm_name(vm_name) status = { "Message": self.migration_request_details_list.get_message_text(vm_name), "Description": self.migration_request_details_list.get_progress_description( vm_name), "Time Elapsed": self.migration_request_details_list.get_clock(vm_name) } return status def filter_by_status(self, status): """Set filter_by_dropdown to 'Status' and uses status arg by user to set status filter. Args: status(str): Takes status string as arg. Valid status options are: ['Pending', 'Validating', 'Pre-migration', 'Migrating', 'VM Transformations Ccompleted', 'VM Transformations Failed'] """ try: self.filter_by_dropdown.item_select("Status") self.filter_by_status_dropdown.item_select(status) except NoSuchElementException: raise ItemNotFound("Migration plan is in Not Started State," " hence filter status dropdown not visible") def sort_by(self, option): """Sort VM list by using one of the 'Started','VM Name' or 'Status' option. Args: status(str): Takes status string as arg. """ try: self.sort_by_dropdown.item_select(option) except NoSuchElementException: raise ItemNotFound("Migration plan is in Not Started State," " hence sort_by dropdown not visible") def plan_in_progress(self, vms_count=5): """Reuturn True or False, depending on migration plan status. If none of the VM migrations are in progress, return True. """ if vms_count > 5: self.items_on_page.item_select("15") migration_plan_in_progress_tracker = [] vms = self.migration_request_details_list.read() for vm in vms: clock_reading1 = self.migration_request_details_list.get_clock(vm) time.sleep(1) # wait 1 sec to see if clock is ticking self.logger.info( "For vm %s, current message is %s", vm, self.migration_request_details_list.get_message_text(vm)) self.logger.info( "For vm %s, current progress description is %s", vm, self.migration_request_details_list.get_progress_description( vm)) clock_reading2 = self.migration_request_details_list.get_clock(vm) self.logger.info("clock_reading1: %s, clock_reading2:%s", clock_reading1, clock_reading2) self.logger.info( "For vm %s, is currently in progress: %s", vm, self.migration_request_details_list.is_in_progress(vm)) migration_plan_in_progress_tracker.append( self.migration_request_details_list.is_in_progress(vm) and (clock_reading1 < clock_reading2)) return not any(migration_plan_in_progress_tracker)
class MigrationDashboardView(BaseLoggedInPage): create_infrastructure_mapping = Text( locator='(//a|//button)' '[text()="Create Infrastructure Mapping"]') create_migration_plan = Text( locator='(//a|//button)[text()="Create Migration Plan"]') configure_providers = Text(locator='//a[text()="Configure Providers"]') migration_plans_not_started_list = MigrationPlansList( "plans-not-started-list") migration_plans_completed_list = MigrationPlansList("plans-complete-list") migration_plans_archived_list = MigrationPlansList("plans-complete-list") sort_type_dropdown = SelectorDropdown('id', 'sortTypeMenu') sort_direction = Text(locator=".//span[contains(@class,'sort-direction')]") # TODO: XPATH requested to devel (repo:miq_v2v_ui_plugin issues:415) progress_card = MigrationProgressBar( locator='.//div[3]/div/div[3]/div[3]/div/div') not_started_plans = MigrationDashboardStatusCard(name="Not Started") in_progress_plans = MigrationDashboardStatusCard(name="In Progress") completed_plans = MigrationDashboardStatusCard(name="Complete") archived_plans = MigrationDashboardStatusCard(name="Archived") # TODO: next 3 lines are specialized only for 510z and inheritance for DashboardView59z # is incorrect. Need to fix it. paginator_view = View.include(v2vPaginatorPane, use_parent=True) search_box = TextInput( locator=".//div[contains(@class,'input-group')]/input") clear_filters = Text(".//a[text()='Clear All Filters']") @property def is_displayed(self): # TODO: Remove next line, after fix for https://github.com/ManageIQ/manageiq-v2v/issues/726 # has been backported to downstream 510z return ( (self.navigation.currently_selected == ['Compute', 'Migration'] or self.navigation.currently_selected == ['Compute', 'Migration', 'Overview']) and (len(self.browser.elements(".//div[contains(@class,'spinner')]")) == 0) and (len(self.browser.elements('.//div[contains(@class,"card-pf")]')) > 0) and len( self.browser.elements( ".//div[contains(@class,'pficon-warning-triangle-o')]")) < 1) def switch_to(self, section): """Switches to Not Started, In Progress, Complete or Archived Plans section.""" sections = { 'Not Started Plans': self.not_started_plans, 'In Progress Plans': self.in_progress_plans, 'Completed Plans': self.completed_plans, 'Archived Plans': self.archived_plans } self.logger.info("Switching to Migration Dashboard section: %s", section) sections[section].click() def plan_in_progress(self, plan_name): """MIQ V2V UI is going through redesign as OSP will be integrated. # TODO: This also means that mappings/plans may be moved to different pages. Once all of that is settled we will need to refactor and also account for notifications. """ try: try: is_plan_visible = self.progress_card.is_plan_visible(plan_name) plan_time_elapsed = self.progress_card.get_clock(plan_name) except ItemNotFound: # This will end the wait_for loop and check the plan under completed_plans section return True if is_plan_visible: # log current status # uncomment following logs after @Yadnyawalk updates the widget for in progress card # logger.info("For plan %s, current migrated size is %s out of total size %s", # migration_plan.name, view.progress_card.get_migrated_size(plan_name), # view.progress_card.get_total_size(migration_plan.name)) # logger.info("For plan %s, current migrated VMs are %s out of total VMs %s", # migration_plan.name, view.progress_card.migrated_vms(migration_plan.name), # view.progress_card.total_vm_to_be_migrated(migration_plan.name)) self.logger.info( "For plan %s, is plan in progress: %s," "time elapsed for migration: %s", plan_name, is_plan_visible, plan_time_elapsed) # return False if plan visible under "In Progress Plans" return not is_plan_visible except StaleElementReferenceException: self.browser.refresh() self.switch_to("In Progress Plans") return False