Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ Button - <https://www.patternfly.org/v4/components/button>

Bullet Chart - <https://www.patternfly.org/v4/charts/bullet-chart>

Calendar month - <https://www.patternfly.org/v4/components/calendar-month>

Card - <https://www.patternfly.org/v4/components/card>

Chip Group - <https://www.patternfly.org/v4/components/chip-group>
Expand Down
2 changes: 2 additions & 0 deletions src/widgetastic_patternfly4/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from .breadcrumb import BreadCrumb
from .bulletchart import BulletChart
from .button import Button
from .calendarmonth import CalendarMonth
from .card import Card
from .card import CardCheckBox
from .card import CardForCardGroup
Expand Down Expand Up @@ -64,6 +65,7 @@
"BreadCrumb",
"Button",
"BulletChart",
"CalendarMonth",
"CheckboxMenu",
"CheckboxSelect",
"Chip",
Expand Down
102 changes: 102 additions & 0 deletions src/widgetastic_patternfly4/calendarmonth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver.common.keys import Keys
from widgetastic.utils import ParametrizedLocator
from widgetastic.widget import Widget
from widgetastic.xpath import quote

from .select import Select


class DisabledDate(Exception):
pass


class BaseCalendarMonth:
"""Represents calendar month component.

https://www.patternfly.org/v4/components/calendar-month
"""

CALENDAR_HEADER = ".//div[@class='pf-c-calendar-month__header']"
MONTH_SELECT_LOCATOR = (
f"{CALENDAR_HEADER}//div[contains(@class, 'header-month')]"
f"//div[@data-ouia-component-type='PF4/Select']"
)
_month_select_widget = Select(locator=MONTH_SELECT_LOCATOR)
YEAR_INPUT_LOCATOR = f"{CALENDAR_HEADER}//div[contains(@class, 'header-year')]/input"
DATE_LOCATOR = ".//button[text()={date}]"

PREV_BUTTON_LOCATOR = f"{CALENDAR_HEADER}//div[contains(@class, 'prev-month')]"
NEXT_BUTTON_LOCATOR = f"{CALENDAR_HEADER}//div[contains(@class, 'next-month')]"

TABLE = ".//table"
SELECTED_DATE_LOCATOR = f"{TABLE}/tbody//td[contains(@class, 'pf-m-selected')]"

def prev(self):
return self.browser.click(self.PREV_BUTTON_LOCATOR)

def next(self):
return self.browser.click(self.NEXT_BUTTON_LOCATOR)

@property
def year(self):
el = self.browser.element(self.YEAR_INPUT_LOCATOR)
return el.get_attribute("value")

@year.setter
def year(self, value):
el = self.browser.element(self.YEAR_INPUT_LOCATOR)
el.send_keys(Keys.CONTROL + "a")
el.send_keys(str(value) + Keys.ENTER)

@property
def month(self):
el = self.browser.element(self.MONTH_SELECT_LOCATOR)
return self.browser.text(el)

@month.setter
def month(self, value):
self._month_select_widget.item_select(value)

@property
def day(self):
try:
el = self.browser.element(self.SELECTED_DATE_LOCATOR)
except NoSuchElementException:
return ""
return self.browser.text(el)

@day.setter
def day(self, value):
el = self.browser.element(self.DATE_LOCATOR.format(date=quote(value)))
if "pf-m-disabled" in el.get_attribute("class") or el.get_attribute("disabled"):
raise DisabledDate(f"Date {value} is disabled for selection")
el.click()

def read(self):
"""Returns the currently selected date in format DD MONTH YYYY."""
return f"{self.day} {self.month} {self.year}"

def fill(self, items):
"""Fills a Calendar with all items.
Example dictionary: {'day': '22', 'month': 'November', 'year': '2023'}"
Comment thread
digitronik marked this conversation as resolved.

Args:
items: A dictionary containing what items to select
"""
if type(items) is not dict:
raise TypeError("'items' value has to be dictionary type. ")
if "day" in items:
self.day = items["day"]
if "month" in items:
self.month = items["month"]
if "year" in items:
self.year = items["year"]


class CalendarMonth(BaseCalendarMonth, Widget):
ROOT = ParametrizedLocator("{@locator}")

def __init__(self, parent, locator, logger=None):
super().__init__(parent, logger=logger)
self.locator = locator
74 changes: 74 additions & 0 deletions testing/test_calendarmonth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import calendar

import pytest
from widgetastic.widget import View

from widgetastic_patternfly4 import CalendarMonth

TESTING_PAGE_URL = "https://patternfly-react.surge.sh/components/calendar-month"

MONTHS_LIST = list(calendar.month_name[1:])


@pytest.fixture
def calendar_month_view(browser):
class TestView(View):
ROOT = ".//div[@id='ws-react-c-calendar-month-selectable-date']"
calendar = CalendarMonth(locator=".//div[@class='pf-c-calendar-month']")

return TestView(browser)


def test_year_selection(calendar_month_view):
assert calendar_month_view.calendar.year
calendar_month_view.calendar.year = "2023"
assert calendar_month_view.calendar.year == "2023"


def test_month_selection(calendar_month_view):
assert calendar_month_view.calendar.month
calendar_month_view.calendar.month = "December"
assert calendar_month_view.calendar.month == "December"


def test_day_selection(calendar_month_view):
calendar_month_view.calendar.day = "20"
assert calendar_month_view.calendar.day == "20"
calendar_month_view.calendar.next()
assert not calendar_month_view.calendar.day


def _get_proper_month_index(index):
max_index = len(MONTHS_LIST) - 1
if index < 0:
index = max_index
elif index > max_index:
index = 0

return index


def test_month_navigation(calendar_month_view):
current_month = calendar_month_view.calendar.month
prev_month_index = _get_proper_month_index(MONTHS_LIST.index(current_month) - 1)
next_month_index = _get_proper_month_index(MONTHS_LIST.index(current_month) + 1)

calendar_month_view.calendar.prev()
prev_month = calendar_month_view.calendar.month
assert prev_month == MONTHS_LIST[prev_month_index]

# reset to current default month
calendar_month_view.calendar.month = current_month

calendar_month_view.calendar.next()
next_month = calendar_month_view.calendar.month
assert next_month == MONTHS_LIST[next_month_index]


def test_fill_and_read(calendar_month_view):
calendar_month_view.calendar.fill({"month": "February", "year": "2023"})
result = calendar_month_view.calendar.read()
assert result == " February 2023"

with pytest.raises(TypeError):
calendar_month_view.calendar.fill("foo")