Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CV UI - Promote, Publish, Add Content #939

Merged
merged 10 commits into from
Oct 17, 2024
142 changes: 120 additions & 22 deletions airgun/entities/contentview_new.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
from asyncio import wait_for
import time

from navmazing import NavigateToSibling
from widgetastic.exceptions import NoSuchElementException

from airgun.entities.base import BaseEntity
from airgun.navigation import NavigateStep, navigator
from airgun.utils import retry_navigation
from airgun.views.contentview_new import (
AddContentViewModal,
AddRPMRuleView,
ContentViewCreateView,
ContentViewEditView,
ContentViewTableView,
ContentViewVersionDetailsView,
ContentViewVersionPromoteView,
ContentViewVersionPublishView,
CreateFilterView,
EditFilterView,
Expand All @@ -19,11 +24,13 @@
class NewContentViewEntity(BaseEntity):
endpoint_path = '/content_views'

def create(self, values):
def create(self, values, composite=False):
"""Create a new content view"""
view = self.navigate_to(self, 'New')
self.browser.plugin.ensure_page_safe(timeout='5s')
view.wait_displayed()
if composite:
view.composite_tile.click()
view.fill(values)
view.submit.click()

Expand All @@ -34,27 +41,72 @@ def search(self, value):
view.wait_displayed()
return view.search(value)

def publish(self, entity_name, values=None):
"""Publishes new version of CV"""
def publish(self, entity_name, values=None, promote=False, lce=None):
"""Publishes new version of CV, optionally allowing for instant promotion"""
view = self.navigate_to(self, 'Publish', entity_name=entity_name)
self.browser.plugin.ensure_page_safe(timeout='5s')
view.wait_displayed()
if values:
view.fill(values)
view.next.click()
view.finish.click()
if promote:
view.promote.click()
view.lce_selector.fill({lce: True})
view.next_button.click()
view.finish_button.click()
wait_for(lambda: view.progressbar.is_displayed, timeout=10)
view.progressbar.wait_for_result()
view = self.navigate_to(self, 'Edit', entity_name=entity_name)
self.browser.plugin.ensure_page_safe(timeout='5s')
view.wait_displayed()
return view.versions.table.read()

def add_content(self, entity_name, content_name):
"""Add specified content to the given Content View"""
view = self.navigate_to(self, 'Edit', entity_name=entity_name)
self.browser.plugin.ensure_page_safe(timeout='5s')
view.wait_displayed()
wait_for(lambda: view.repositories.resources.is_displayed, timeout=10)
view.repositories.resources.add(content_name)
return view.repositories.resources.read()

def add_cv(self, ccv_name, cv_name, always_update=False, version=None):
"""Adds selected CV to selected CCV, optionally with support for always_update and specified version"""
view = self.navigate_to(self, 'Edit', entity_name=ccv_name)
self.browser.plugin.ensure_page_safe(timeout='5s')
view.wait_displayed()
view.content_views.resources.add(cv_name)
view = AddContentViewModal(self.browser)
if always_update:
view.always_update.fill(True)
if version:
view.version_select.item_select(version)
view.submit_button.click()
view = self.navigate_to(self, 'Edit', entity_name=ccv_name)
self.browser.plugin.ensure_page_safe(timeout='5s')
view.wait_displayed()
wait_for(lambda: view.content_views.resources.is_displayed, timeout=10)
return view.content_views.resources.read()

def read_cv(self, entity_name, version_name):
"""Reads the table for a specified Content View's specified Version"""
view = self.navigate_to(self, 'Edit', entity_name=entity_name)
sambible marked this conversation as resolved.
Show resolved Hide resolved
self.browser.plugin.ensure_page_safe(timeout='5s')
view.wait_displayed()
view.versions.search(version_name)
return view.versions.table.row(version=version_name).read()

def read_repositories(self, entity_name):
"""Reads the repositories table for a specified Content View"""
view = self.navigate_to(self, 'Edit', entity_name=entity_name)
return view.repositories.resources.read()

def read_version_table(self, entity_name, version, tab_name, search_param=None):
"""Reads a specific table for a CV Version"""
view = self.navigate_to(self, 'Version', entity_name=entity_name, version=version)
self.browser.plugin.ensure_page_safe(timeout='5s')
view.wait_displayed()
# This allows dynamic access to the proper table
getattr(view, tab_name).table.wait_displayed()
wait_for(lambda: getattr(view, tab_name).table.wait_displayed(), timeout=10)
if search_param:
getattr(view, tab_name).searchbox.search(search_param)
return getattr(view, tab_name).table.read()
Expand Down Expand Up @@ -129,6 +181,31 @@ def read_french_lang_cv(self):
view.wait_displayed()
return view.table.read()

def promote(self, entity_name, version_name, lce_name):
"""Promotes the selected version of content view to given environment.
:return: dict with new content view version table row; contains keys
like 'Version', 'Status', 'Environments' etc.
"""
view = self.navigate_to(self, 'Promote', entity_name=entity_name, version_name=version_name)
modal = ContentViewVersionPromoteView(self.browser)
if modal.is_displayed:
modal.lce.fill({lce_name: True})
modal.promote_btn.click()
view = self.navigate_to(self, 'Edit', entity_name=entity_name)
view.versions.search(version_name)
return view.versions.table.row(version=version_name).read()

def update(self, entity_name, values):
"""Update existing content view"""
view = self.navigate_to(self, 'Edit', entity_name=entity_name)
# need a wait to recognize the loading is complete
# sleep works for now
time.sleep(3)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no hard sleep!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was the old me omkar, I can fix it :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

returning to this: have tried various other things, and the sleep is the only thing consistently working

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say keep it then if there is no other way.
As I believe in many cases there is unfortunately none. :/

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And then I would remove # sleep works for now comment :)

filled_values = view.fill(values)
view.flash.assert_no_error()
view.flash.dismiss()
return filled_values


@navigator.register(NewContentViewEntity, 'All')
class ShowAllContentViewsScreen(NavigateStep):
Expand Down Expand Up @@ -164,22 +241,6 @@ def step(self, *args, **kwargs):
self.parent.create_content_view.click()


@navigator.register(NewContentViewEntity, 'Publish')
class PublishContentViewVersion(NavigateStep):
"""Navigate to Content View Publish screen."""

VIEW = ContentViewVersionPublishView

def prerequisite(self, *args, **kwargs):
"""Open Content View first."""
return self.navigate_to(self.obj, 'Edit', entity_name=kwargs.get('entity_name'))

@retry_navigation
def step(self, *args, **kwargs):
"""Click 'Publish new version' button"""
self.parent.publish.click()


@navigator.register(NewContentViewEntity, 'Edit')
class EditContentView(NavigateStep):
"""Navigate to Edit Content View screen."""
Expand Down Expand Up @@ -208,3 +269,40 @@ def step(self, *args, **kwargs):
version = kwargs.get('version')
self.parent.versions.search(version)
self.parent.versions.table.row(version=version)['Version'].widget.click()


@navigator.register(NewContentViewEntity, 'Publish')
class PublishContentViewVersion(NavigateStep):
"""Navigate to Content View Publish screen.
Args:
entity_name: name of content view
"""

VIEW = ContentViewVersionPublishView

def prerequisite(self, *args, **kwargs):
"""Open Content View first."""
return self.navigate_to(self.obj, 'Edit', entity_name=kwargs.get('entity_name'))

def step(self, *args, **kwargs):
"""Click 'Publish new version' button"""
self.parent.publish.click()


@navigator.register(NewContentViewEntity, 'Promote')
class PromoteContentViewVersion(NavigateStep):
"""Navigate to Content View Promote screen.
Args:
entity_name: name of content view
version_name: name of content view version to promote
"""

VIEW = ContentViewEditView

def prerequisite(self, *args, **kwargs):
return self.navigate_to(self.obj, 'Edit', entity_name=kwargs.get('entity_name'))

def step(self, *args, **kwargs):
version_name = kwargs.get('version_name')
self.parent.versions.search(version_name)
self.parent.versions.table[0][7].widget.item_select('Promote')
121 changes: 69 additions & 52 deletions airgun/views/common.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
import time

import wait_for
from widgetastic.widget import (
Checkbox,
ConditionalSwitchableView,
Expand All @@ -12,9 +9,10 @@
WTMixin,
do_not_read_this_widget,
)
from widgetastic_patternfly import BreadCrumb, Button, Tab, TabWithDropdown
from widgetastic_patternfly import BreadCrumb, Tab, TabWithDropdown
from widgetastic_patternfly4 import Button, Select
from widgetastic_patternfly4.navigation import Navigation
from widgetastic_patternfly4.ouia import Button as PF4Button, Dropdown, PatternflyTable
from widgetastic_patternfly4.ouia import Dropdown, PatternflyTable

from airgun.utils import get_widget_by_name, normalize_dict_values
from airgun.widgets import (
Expand All @@ -25,6 +23,7 @@
ItemsList,
LCESelector,
Pf4ConfirmationDialog,
PF4LCECheckSelector,
PF4LCESelector,
PF4NavSearch,
PF4Search,
Expand Down Expand Up @@ -285,6 +284,30 @@ class PF4LCESelectorGroup(LCESelectorGroup):
)


class PF4LCECheckSelectorGroup(PF4LCESelectorGroup):
"""Checkbox version of PF4 LCE Selector"""

lce = PF4LCECheckSelector(
locator=ParametrizedLocator(
'.//div[@class="env-path" and .//*[contains(normalize-space(.), "{lce_name}")]]'
)
)


class PF4LCEGroup(ParametrizedLocator):
"Group of LCE indicators"
ROOT = './/td and '

PARAMETERS = ('lce_name',)

LAST_ENV = './/div[@class="env-path"][last()]'
lce = PF4LCESelector(
locator=ParametrizedLocator(
'.//div[@class="env-path" and .//*[contains(normalize-space(.), "{lce_name}")]]'
)
)


class ListRemoveTab(SatSecondaryTab):
"""'List/Remove' tab, part of :class:`AddRemoveResourcesView`."""

Expand Down Expand Up @@ -390,38 +413,13 @@ def read(self):
}


class AddRemoveSubscriptionsView(AddRemoveResourcesView):
"""A variant of :class:`AddRemoveResourcesView` for managing subscriptions.
Subscriptions table has different structure - entity label is located in
separate row apart from checkbox and other cells.
"""

@View.nested
class list_remove_tab(ListRemoveTab):
table = SatSubscriptionsTable(
locator=".//table", column_widgets={0: Checkbox(locator=".//input[@type='checkbox']")}
)

@View.nested
class add_tab(AddTab):
table = SatSubscriptionsTable(
locator=".//table", column_widgets={0: Checkbox(locator=".//input[@type='checkbox']")}
)


class NewAddRemoveResourcesView(View):
searchbox = PF4Search()
type = Dropdown(
locator='.//div[contains(@class, "All repositories") or'
' contains(@aria-haspopup="listbox")]'
)
Status = Dropdown(
locator='.//div[contains(@class, "All") or contains(@aria-haspopup="listbox")]'
)
add_repo = PF4Button('OUIA-Generated-Button-secondary-2')
# Need to add kebab menu
status = Select(locator='.//div[@data-ouia-component-id="select Status"]')
remove_button = Dropdown(locator='.//div[@data-ouia-component-id="repositoies-bulk-actions"]')
add_button = Button(locator='.//button[@data-ouia-component-id="add-repositories"]')
table = PatternflyTable(
component_id='OUIA-Generated-Table-4',
component_id='content-view-repositories-table',
column_widgets={
0: Checkbox(locator='.//input[@type="checkbox"]'),
'Type': Text('.//a'),
Expand All @@ -433,49 +431,68 @@ class NewAddRemoveResourcesView(View):
},
)

def select_status(self, value):
"""Set status box to passed in value"""
self.status.fill(value)

def search(self, value):
"""Search for specific available resource and return the results"""
self.searchbox.search(value)
# Tried following ways to wait for table to be displayed, only sleep worked
# Might need a before/after fill
wait_for(
lambda: self.table.is_displayed is True,
timeout=60,
delay=1,
)
time.sleep(3)
self.table.wait_displayed()
return self.table.read()
return self.read()

def add(self, value):
"""Associate specific resource"""
self.select_status("Not added")
self.search(value)
value = self.table.rows()
next(self.table.rows())[0].widget.fill(True)
self.add_repo.click()
self.add_button.click()

def fill(self, values):
"""Associate resource(s)"""
if not isinstance(values, list):
values = [
values,
]
for value in values:
self.add(value)

def remove(self, value):
"""Unassign some resource(s).
:param str or list values: string containing resource name or a list of
such strings.
:param str or list values: string containing resource name or a list of such strings.
"""
self.select_status("Added")
self.search(value)
next(self.table.rows())[0].widget.fill(True)
self.remove_button.click()
self.remove_button.item_select('Remove')

def read(self):
"""Read all table values from both resource tables"""
self.browser.wait_for_element(locator='//h4[text()="Loading"]', exception=False)
self.browser.wait_for_element(
self.table, exception=False, ensure_page_safe=True, timeout=10
)
self.browser.plugin.ensure_page_safe(timeout='60s')
self.table.wait_displayed()
self.select_status("All")
return self.table.read()


class AddRemoveSubscriptionsView(AddRemoveResourcesView):
"""A variant of :class:`AddRemoveResourcesView` for managing subscriptions.
Subscriptions table has different structure - entity label is located in
separate row apart from checkbox and other cells.
"""

@View.nested
class list_remove_tab(ListRemoveTab):
table = SatSubscriptionsTable(
locator=".//table", column_widgets={0: Checkbox(locator=".//input[@type='checkbox']")}
)

@View.nested
class add_tab(AddTab):
table = SatSubscriptionsTable(
locator=".//table", column_widgets={0: Checkbox(locator=".//input[@type='checkbox']")}
)


class TemplateEditor(View):
"""Default view for template entity editor that can be present for example
on provisioning template of partition table pages. It contains from
Expand Down
Loading