Skip to content

Commit

Permalink
Add minimum elements for Publish workflow
Browse files Browse the repository at this point in the history
  • Loading branch information
sambible committed Aug 30, 2023
1 parent c840a15 commit 4e365ef
Show file tree
Hide file tree
Showing 4 changed files with 334 additions and 0 deletions.
55 changes: 55 additions & 0 deletions airgun/entities/contentview_new.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
from navmazing import NavigateToSibling
from wait_for import wait_for

from airgun.entities.base import BaseEntity
from airgun.navigation import NavigateStep
from airgun.navigation import navigator
from airgun.utils import retry_navigation
from airgun.views.contentview_new import NewContentViewCreateView
from airgun.views.contentview_new import NewContentViewTableView
from airgun.views.contentview_new import NewContentViewEditView
from airgun.views.contentview_new import NewContentViewVersionPublishView


class NewContentViewEntity(BaseEntity):
Expand All @@ -22,6 +25,21 @@ def search(self, value):
view = self.navigate_to(self, 'All')
return view.search(value)

def publish(self, entity_name, values=None):
"""Publishes to create new version of CV and promotes the contents to
'Library' environment.
:return: dict with new content view version table row; contains keys
like 'Version', 'Status', 'Environments' etc.
"""
view = self.navigate_to(self, 'Publish', entity_name=entity_name)
if values:
view.fill(values)
view.next.click()
view.finish.click()
view.progressbar.wait_for_result()
view = self.navigate_to(self, 'Edit', entity_name=entity_name)
return view.versions.table.read()


@navigator.register(NewContentViewEntity, 'All')
class ShowAllContentViewsScreen(NavigateStep):
Expand All @@ -44,3 +62,40 @@ class CreateContentView(NavigateStep):

def step(self, *args, **kwargs):
self.parent.create_content_view.click()


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

VIEW = NewContentViewEditView

def prerequisite(self, *args, **kwargs):
return self.navigate_to(self.obj, 'All')

def step(self, *args, **kwargs):
entity_name = kwargs.get('entity_name')
self.parent.search(entity_name)
self.parent.table.row(name=entity_name)['Name'].widget.click()


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

VIEW = NewContentViewVersionPublishView

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()

71 changes: 71 additions & 0 deletions airgun/views/common.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import time

from wait_for import wait_for

from widgetastic.widget import Checkbox
from widgetastic.widget import ConditionalSwitchableView
from widgetastic.widget import do_not_read_this_widget
Expand All @@ -13,6 +17,8 @@
from widgetastic_patternfly import TabWithDropdown
from widgetastic_patternfly4.navigation import Navigation
from widgetastic_patternfly4.ouia import Dropdown
from widgetastic_patternfly4.ouia import PatternflyTable
from widgetastic_patternfly4.ouia import Button as PF4Button

from airgun.utils import get_widget_by_name
from airgun.utils import normalize_dict_values
Expand Down Expand Up @@ -383,6 +389,71 @@ class add_tab(AddTab):
)


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
table = PatternflyTable(
component_id='OUIA-Generated-Table-4',
column_widgets={
0: Checkbox(locator='.//input[@type="checkbox"]'),
'Type': Text('.//a'),
'Name': Text('.//a'),
'Product': Text('.//a'),
'Sync State': Text('.//a'),
'Content': Text('.//a'),
'Status': Text('.//a'),
},
)

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()

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

def fill(self, values):
"""Associate resource(s)"""
if not isinstance(values, list):
values = list((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.
"""
self.search(value)
next(self.table.rows())[0].widget.fill(True)
self.remove_button.click()

def read(self):
"""Read all table values from both resource tables"""
return self.table.read()


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
173 changes: 173 additions & 0 deletions airgun/views/contentview_new.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,37 @@
from wait_for import wait_for
from widgetastic.utils import ParametrizedLocator
from widgetastic.widget import Checkbox
from widgetastic.widget import ParametrizedView
from widgetastic.widget import Text
from widgetastic.widget import TextInput
from widgetastic.widget import View
from widgetastic_patternfly import BreadCrumb
from widgetastic_patternfly import Tab
from widgetastic_patternfly4 import Button
from widgetastic_patternfly4 import Dropdown
from widgetastic_patternfly4.ouia import Button as PF4Button
from widgetastic_patternfly4.ouia import ExpandableTable
from widgetastic_patternfly4.ouia import Switch
from widgetastic_patternfly4.ouia import PatternflyTable

from airgun.views.common import BaseLoggedInView
from airgun.views.common import NewAddRemoveResourcesView
from airgun.views.common import SearchableViewMixinPF4
from airgun.widgets import ActionsDropdown
from airgun.widgets import ConfirmationDialog
from airgun.widgets import EditableEntry
from airgun.widgets import PF4Search
from airgun.widgets import ProgressBarPF4
from airgun.widgets import ReadOnlyEntry
from airgun.views.common import BaseLoggedInView
from airgun.views.common import NewAddRemoveResourcesView
from airgun.views.common import SearchableViewMixinPF4
from airgun.widgets import ActionsDropdown
from airgun.widgets import ConfirmationDialog
from airgun.widgets import EditableEntry
from airgun.widgets import PF4Search
from airgun.widgets import ProgressBarPF4
from airgun.widgets import ReadOnlyEntry

from airgun.views.common import BaseLoggedInView
from airgun.views.common import SearchableViewMixinPF4
Expand Down Expand Up @@ -61,3 +89,148 @@ def is_displayed(self):
def after_fill(self, value):
"""Ensure 'Create content view' button is enabled after filling out the required fields"""
self.submit.wait_displayed()


class NewContentViewEditView(BaseLoggedInView):
breadcrumb = BreadCrumb()
search = PF4Search()
title = Text("//h2[contains(., 'Publish) or contains(@id, 'pf-wizard-title-0')]")
actions = ActionsDropdown(
"//div[contains(@data-ouia-component-id, 'OUIA-Generated-Dropdown-2')]"
)
publish = PF4Button('cv-details-publish-button')
# not sure if this is needed
dialog = ConfirmationDialog()

@property
def is_displayed(self):
breadcrumb_loaded = self.browser.wait_for_element(self.breadcrumb, exception=False)
return (
breadcrumb_loaded
and len(self.breadcrumb.locations) <= 3
and self.breadcrumb.locations[0] == 'Content Views'
and self.breadcrumb.read() != 'New Content View'
and self.publish.is_displayed
)

@View.nested
class details(Tab):
TAB_LOCATOR = ParametrizedLocator('//a[contains(@href, "#/details")]')
name = EditableEntry(name='Name')
label = ReadOnlyEntry(name='Label')
type = ReadOnlyEntry(name='Composite?')
description = EditableEntry(name='Description')
# depSolv is maybe a conditionalswitch
solve_dependencies = Switch(name='solve_dependencies switch')
import_only = Switch(name='import_only_switch')

@View.nested
class versions(Tab):
TAB_LOCATOR = ParametrizedLocator('//a[contains(@href, "#/versions")]')
searchbox = PF4Search()
table = PatternflyTable(
component_id="content-view-versions-table",
column_widgets={
0: Checkbox(locator='.//input[@type="checkbox"]'),
'Version': Text('.//a'),
'Environments': Text('.//a'),
'Packages': Text('.//a'),
'Errata': Text('.//a'),
'Additional content': Text('.//a'),
'Description': Text('.//a'),
7: Dropdown(locator='.//div[contains(@class, "pf-c-dropdown")]'),
},
)
publishButton = PF4Button('cv-details-publish-button')

def search(self, version_name):
"""Searches for content view version.
Searchbox can't search by version name, only by id, that's why in
case version name was passed, it's transformed into recognizable
value before filling, for example::
'Version 1.0' -> 'version = 1'
"""
search_phrase = version_name
if version_name.startswith('V') and '.' in version_name:
search_phrase = f'version = {version_name.split()[1].split(".")[0]}'
self.searchbox.search(search_phrase)
return self.table.read()

@View.nested
class content_views(Tab):
TAB_LOCATOR = ParametrizedLocator('//a[contains(@href, "#/contentviews")]')

resources = View.nested(NewAddRemoveResourcesView)

@View.nested
class repositories(Tab):
TAB_LOCATOR = ParametrizedLocator('//a[contains(@href, "#/repositories")]')
resources = View.nested(NewAddRemoveResourcesView)

@View.nested
class filters(Tab):
TAB_LOCATOR = ParametrizedLocator('//a[contains(@href, "#/filters")]')
new_filter = Text(".//button[@ui-sref='content-view.yum.filters.new']")


class NewContentViewVersionPublishView(BaseLoggedInView):
# publishing view is a popup so adding all navigation within the same context
breadcrumb = BreadCrumb()
ROOT = './/div[contains(@class,"pf-c-wizard")]'
title = Text("//h2[contains(., 'Publish' or contains(@id, 'pf-wizard-title-0')]")
# publishing screen
description = TextInput(id='description')
promote = Switch('promote-switch')

# review screen only has info to review
# shared buttons at bottom for popup for both push and review section
next = Button('Next')
finish = Button('Finish')
back = Button('Back')
cancel = Button('Cancel')
close_button = Button('Close')
progressbar = ProgressBarPF4()

@property
def is_displayed(self):
breadcrumb_loaded = self.browser.wait_for_element(self.breadcrumb, exception=False)
return (
breadcrumb_loaded
and self.breadcrumb.locations[0] == 'Content Views'
and self.breadcrumb.read() == 'Versions'
)

def wait_animation_end(self):
wait_for(
lambda: 'in' in self.browser.classes(self),
handle_exception=True,
logger=self.logger,
timeout=10,
)

def before_fill(self, values=None):
"""If we don't want to break view.fill() procedure flow, we need to
push 'Edit' button to open necessary dialog to be able to fill values
"""
self.promote.click()
wait_for(
lambda: self.lce.is_displayed is True,
timeout=30,
delay=1,
logger=self.logger,
)


class NewContentViewVersionDetailsView(BaseLoggedInView):
breadcrumb = BreadCrumb()

@property
def is_displayed(self):
breadcrumb_loaded = self.browser.wait_for_element(self.breadcrumb, exception=False)
return (
breadcrumb_loaded
and len(self.breadcrumb.locations) > 3
and self.breadcrumb.locations[0] == 'Content Views'
and self.breadcrumb.locations[2] == 'Versions'
)

35 changes: 35 additions & 0 deletions airgun/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -2034,6 +2034,41 @@ def read(self):
return self.progress


class ProgressBarPF4(ProgressBar):
"""Generic progress bar widget.
Example html representation::
<div class="progress ng-isolate-scope" type="success" ...>
<div class="progress-bar progress-bar-success" aria-valuenow="0"
aria-valuemin="0" aria-valuemax="100" aria-valuetext="0%" ...></div>
</div>
Locator example::
.//div[contains(@class, "progress progress-striped")]
"""

PROGRESSBAR = '//div[contains(@role, "progressbar") or contains(@class, "pf-c-progress__bar")]'

def __init__(self, parent, locator=None, logger=None):
"""Provide common progress bar locator if it wasn't specified."""
Widget.__init__(self, parent, logger=logger)
if not locator:
locator = self.PROGRESSBAR
self.locator = locator

@property
def progress(self):
"""String value with current flow rate in percent."""
return self.browser.get_attribute(
'pf-c-progress__measure', self.PROGRESSBAR, check_safe=False
)

@property
def is_completed(self):
"""Boolean value whether progress bar is finished or not"""
if not self.is_active and self.progress == '100%':
return True
return False


class PublishPromoteProgressBar(ProgressBar):
"""Progress bar for Publish and Promote procedures. They contain status
message and link to associated task. Also the progress is displayed
Expand Down

0 comments on commit 4e365ef

Please sign in to comment.