Skip to content

Commit

Permalink
Merge pull request #34 from LightOfHeaven1994/ouia-widgets
Browse files Browse the repository at this point in the history
Ouia widgets
  • Loading branch information
digitronik authored Aug 24, 2023
2 parents f4663a2 + c627c15 commit aa6cce4
Show file tree
Hide file tree
Showing 10 changed files with 519 additions and 0 deletions.
112 changes: 112 additions & 0 deletions src/widgetastic_patternfly5/ouia.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
from widgetastic.ouia import OUIAGenericView
from widgetastic.ouia import OUIAGenericWidget
from widgetastic.ouia.input import TextInput as BaseOuiaTextInput
from widgetastic.ouia.text import Text as BaseOuiaText
from widgetastic.widget.table import Table
from widgetastic.xpath import quote

from widgetastic_patternfly5.components.alert import BaseAlert
from widgetastic_patternfly5.components.breadcrumb import BaseBreadCrumb
from widgetastic_patternfly5.components.button import BaseButton
from widgetastic_patternfly5.components.card import BaseCard
from widgetastic_patternfly5.components.forms.formselect import BaseFormSelect
from widgetastic_patternfly5.components.menus.menu import BaseCheckboxMenu
from widgetastic_patternfly5.components.menus.menu import BaseMenu
from widgetastic_patternfly5.components.modal import BaseModal
from widgetastic_patternfly5.components.navigation import BaseNavigation
from widgetastic_patternfly5.components.pagination import BaseCompactPagination
from widgetastic_patternfly5.components.pagination import BasePagination
from widgetastic_patternfly5.components.table import BaseExpandableTable
from widgetastic_patternfly5.components.table import BasePatternflyTable
from widgetastic_patternfly5.components.title import BaseTitle


class Alert(BaseAlert, OUIAGenericWidget):
OUIA_COMPONENT_TYPE = "PF5/Alert"


class BreadCrumb(BaseBreadCrumb, OUIAGenericWidget):
OUIA_COMPONENT_TYPE = "PF5/Breadcrumb"


class Button(BaseButton, OUIAGenericWidget):
OUIA_COMPONENT_TYPE = "PF5/Button"


class Card(BaseCard, OUIAGenericWidget):
OUIA_COMPONENT_TYPE = "PF5/Card"


class FormSelect(BaseFormSelect, OUIAGenericWidget):
OUIA_COMPONENT_TYPE = "PF5/FormSelect"


class Menu(BaseMenu):
OUIA_COMPONENT_TYPE = "PF5/Menu"


class CheckboxMenu(BaseCheckboxMenu):
OUIA_COMPONENT_TYPE = "PF5/Menu"


class Modal(BaseModal, OUIAGenericView):
OUIA_COMPONENT_TYPE = "PF5/ModalContent"


class Navigation(BaseNavigation, OUIAGenericWidget):
OUIA_COMPONENT_TYPE = "PF5/Nav"


class Pagination(BasePagination, OUIAGenericView):
OUIA_COMPONENT_TYPE = "PF5/Pagination"


class CompactPagination(BaseCompactPagination, Pagination):
pass


class PatternflyTable(BasePatternflyTable, Table):
def __init__(
self,
parent,
component_id,
column_widgets=None,
assoc_column=None,
rows_ignore_top=None,
rows_ignore_bottom=None,
top_ignore_fill=False,
bottom_ignore_fill=False,
logger=None,
):
self.component_type = "PF5/Table"
self.component_id = component_id
super().__init__(
parent,
locator=(
f".//*[@data-ouia-component-type={quote(self.component_type)} "
f"and @data-ouia-component-id={quote(self.component_id)}]"
),
column_widgets=column_widgets,
assoc_column=assoc_column,
rows_ignore_top=rows_ignore_top,
rows_ignore_bottom=rows_ignore_bottom,
top_ignore_fill=top_ignore_fill,
bottom_ignore_fill=bottom_ignore_fill,
logger=logger,
)


class ExpandableTable(BaseExpandableTable, PatternflyTable):
pass


class Title(BaseTitle, OUIAGenericWidget):
OUIA_COMPONENT_TYPE = "PF5/Title"


class Text(BaseOuiaText):
OUIA_COMPONENT_TYPE = "PF5/Text"


class TextInput(BaseOuiaTextInput):
OUIA_COMPONENT_TYPE = "PF5/TextInput"
27 changes: 27 additions & 0 deletions testing/ouia/test_alert_ouia.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import pytest
from widgetastic.widget import View

from widgetastic_patternfly5.ouia import Alert as AlertOUIA

TESTING_PAGE_URL = "https://patternfly-react-main.surge.sh/components/alert"

ALERT_TYPES = ["InfoAlert", "SuccessAlert", "WarningAlert", "DangerAlert"]


@pytest.fixture(params=ALERT_TYPES)
def alert(browser, request):
class TestView(View):
ROOT = ".//div[@id='ws-react-c-alert-alert-variants']"
alert = AlertOUIA(request.param)

view = TestView(browser)
return view.alert


def test_alert_is_displayed(alert):
assert alert.is_displayed


def test_alert_title(alert):
alert_type = alert.type if alert.type != "error" else "danger"
assert alert.title == f"{alert_type.capitalize()} alert title"
42 changes: 42 additions & 0 deletions testing/ouia/test_breadcrumb_ouia.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import re

import pytest
from widgetastic.exceptions import WidgetOperationFailed
from widgetastic.widget import View

from widgetastic_patternfly5.ouia import BreadCrumb as BreadCrumbOUIA

TESTING_PAGE_URL = "https://patternfly-react-main.surge.sh/components/breadcrumb/"


def test_breadcrumb(browser):
class TestView(View):
ROOT = ".//div[@id='ws-react-c-breadcrumb-basic']"
breadcrumb = BreadCrumbOUIA("BasicBreadcrumb")

view = TestView(browser)
assert view.breadcrumb.is_displayed
assert len(view.breadcrumb.locations) == 4
assert view.breadcrumb.locations[0].lower() == "section home"
assert view.breadcrumb.read().lower() == "section landing"
view.breadcrumb.click_location(view.breadcrumb.locations[0])
view.breadcrumb.click_location("title", partial=True)

failing_location = "definitely not in the example page"

# exception + message on full match
exception_match = re.escape(
f'Breadcrumb location "{failing_location}" '
f"not found within locations: {view.breadcrumb.locations}"
)
with pytest.raises(WidgetOperationFailed, match=exception_match):
view.breadcrumb.click_location(failing_location)

# exception + message on partial match
exception_match = re.escape(
f'Breadcrumb location "{failing_location}" '
"not found with partial match "
f"within locations: {view.breadcrumb.locations}"
)
with pytest.raises(WidgetOperationFailed, match=exception_match):
view.breadcrumb.click_location(failing_location, partial=True)
26 changes: 26 additions & 0 deletions testing/ouia/test_button_ouia.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import pytest
from widgetastic.widget import View

from widgetastic_patternfly5.ouia import Button as ButtonOUIA

TESTING_PAGE_URL = "https://patternfly-react-main.surge.sh/components/button"

BUTTON_TYPES = ["Primary", "Secondary", "DangerSecondary", "Tertiary", "Danger", "Warning"]


@pytest.fixture(params=BUTTON_TYPES)
def button(browser, request):
class TestView(View):
ROOT = ".//div[@id='ws-react-c-button-variant-examples']"
button = ButtonOUIA(request.param)

view = TestView(browser)
return view.button


def test_button_is_displayed(button):
assert button.is_displayed


def test_button_is_active(button):
assert not button.disabled
19 changes: 19 additions & 0 deletions testing/ouia/test_card_ouia.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import pytest
from widgetastic.widget import View

from widgetastic_patternfly5.ouia import Card as CardOUIA

TESTING_PAGE_URL = "https://patternfly-react-main.surge.sh/components/card"


@pytest.fixture
def view(browser):
class TestView(View):
ROOT = ".//div[@id='ws-react-c-card-basic-cards']"
card = CardOUIA("BasicCard")

return TestView(browser)


def test_card_displayed(view):
assert view.card.is_displayed
48 changes: 48 additions & 0 deletions testing/ouia/test_formselector_ouia.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import pytest
from widgetastic.widget import View

from widgetastic_patternfly5 import FormSelectOptionNotFound
from widgetastic_patternfly5.ouia import FormSelect as FormSelectOUIA

TESTING_PAGE_URL = "https://patternfly-react-main.surge.sh/components/forms/form-select"


@pytest.fixture
def view(browser):
class FormSelectTestView(View):
ROOT = ".//div[@id='ws-react-c-form-select-basic']"
input = FormSelectOUIA("BasicFormSelect")

return FormSelectTestView(browser)


def test_formselect_visibility(view):
assert view.input.is_displayed


def test_formselect_enablement(view):
assert view.input.is_enabled


def test_formselect_validity(view):
assert view.input.is_valid


def test_formselect_value(view):
assert len(view.input.all_options) == 7
assert len(view.input.all_enabled_options) == 6
assert "Mrs" in view.input.all_options
view.input.fill("Mrs")
assert view.input.read() == "Mrs"


def test_formselect_option_enablement(view):
expected_enabled_options = {"Mr", "Miss", "Mrs", "Ms", "Dr", "Other"}
expected_disabled_options = {"Please Choose"}
assert set(view.input.all_enabled_options) == expected_enabled_options
assert expected_disabled_options not in set(view.input.all_enabled_options)


def test_formselect_fill_nonexistent_option(view):
with pytest.raises(FormSelectOptionNotFound):
view.input.fill("foo")
71 changes: 71 additions & 0 deletions testing/ouia/test_modal_ouia.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import pytest
from widgetastic.widget import Text
from widgetastic.widget import View

from widgetastic_patternfly5 import ModalItemNotFound
from widgetastic_patternfly5.ouia import Button as ButtonOUIA
from widgetastic_patternfly5.ouia import Modal as ModalOUIA

TESTING_PAGE_URL = "https://patternfly-react-main.surge.sh/components/modal"


@pytest.fixture()
def modal(browser):
class ModalTestView(View):
ROOT = ".//div[@id='ws-react-c-modal-basic-modals']"
show_modal = ButtonOUIA("ShowBasicModal")

modal = ModalOUIA(browser, "BasicModal")

view = ModalTestView(browser)
view.show_modal.click()
yield modal
if modal.is_displayed:
modal.close()


class CustomModal(ModalOUIA):
"""Model use as view and enhance with widgets"""

custom_body = Text(".//div[contains(@class, 'pf-v5-c-modal-box__body')]")


def test_title(modal):
assert modal.title


def test_body(modal):
body = modal.body
assert body.text.startswith("Lorem")


def test_close(modal):
modal.close()
assert not modal.is_displayed


def test_footer_items(modal):
items = modal.footer_items
assert len(items) == 2
assert "Cancel" in items
assert "Confirm" in items


def test_footer_item(modal):
item = modal.footer_item("Confirm")
assert item.text == "Confirm"
item.click()
assert not modal.is_displayed


def test_footer_item_invalid(modal):
items = modal.footer_items
with pytest.raises(ModalItemNotFound) as e:
modal.footer_item("INVALID")
assert str(e.value) == f"Item INVALID not found. Available items: {items}"


def test_modal_as_view(browser, modal):
view = CustomModal(browser, "BasicModal")
assert view.is_displayed
assert view.custom_body.text == modal.body.text
25 changes: 25 additions & 0 deletions testing/ouia/test_navigation_ouia.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import pytest
from widgetastic.widget import View

from widgetastic_patternfly5.ouia import Navigation as NavigationOUIA

TESTING_PAGE_URL = "https://patternfly-react-main.surge.sh/components/navigation"


@pytest.fixture
def view(browser):
class TestView(View):
ROOT = ".//div[@id='ws-react-c-navigation-default']"
nav = NavigationOUIA("DefaultNav")

return TestView(browser)


def test_navigation(browser, view):
assert view.nav.currently_selected == ["Default Link 1"]
assert view.nav.nav_item_tree() == [
"Default Link 1",
"Default Link 2",
"Default Link 3",
"Default Link 4",
]
Loading

0 comments on commit aa6cce4

Please sign in to comment.