Skip to content

Commit

Permalink
added support for boxplot
Browse files Browse the repository at this point in the history
  • Loading branch information
yash2189 committed Aug 26, 2024
1 parent ba32830 commit 97d9f40
Show file tree
Hide file tree
Showing 4 changed files with 152 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ itteration of [widgetastic.patternfly4](https://github.com/RedHatQE/widgetastic.

### Charts:
- [bullet-chart](https://www.patternfly.org/charts/bullet-chart)
- [boxplot-chart](https://patternfly-react-main.surge.sh/charts/box-plot-chart)
- [donut-chart](https://www.patternfly.org/charts/donut-chart)
- [legends](https://www.patternfly.org/charts/legends)
- [line-chart](https://www.patternfly.org/charts/line-chart)
Expand Down
2 changes: 2 additions & 0 deletions src/widgetastic_patternfly5/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .charts.boxplot_chart import BoxPlotChart
from .charts.bullet_chart import BulletChart
from .charts.donut_chart import DonutChart
from .charts.legend import DataPoint
Expand Down Expand Up @@ -72,6 +73,7 @@
__all__ = [
"Alert",
"BreadCrumb",
"BoxPlotChart",
"BulletChart",
"Button",
"CalendarMonth",
Expand Down
105 changes: 105 additions & 0 deletions src/widgetastic_patternfly5/charts/boxplot_chart.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
from widgetastic.widget import ParametrizedLocator
from widgetastic.widget import View
from widgetastic.xpath import quote

from .legend import Legend


class BoxPlotChart(View):
"""Represents the Patternfly Line Chart.
https://patternfly-react-main.surge.sh/charts/box-plot-chart#embedded-legend
Args:
id: If you want to look the input up by id, use this parameter, pass the id.
locator: If you have specific locator else it will take pf-chart.
"""

ROOT = ParametrizedLocator("{@locator}")

X_AXIS_LABELS = "(.//*[name()='g' and *[name()='line'] and *[name()='g']])[1]//*[name()='text']"
Y_AXIS_LABELS = "(.//*[name()='g' and *[name()='line'] and *[name()='g']])[2]//*[name()='text']"

TOOLTIP = (
".//*[name()='g' and .//*[name()='g' and "
".//*[name()='text' and contains(@id, 'legend-labels')]]]"
)

TOOLTIP_X_AXIS_LABLE = ".//*[name()='text']"
TOOLTIP_LABLES = ".//*[name()='g']/*[name()='text' and not(contains(@id, 'legend-label'))]"
TOOLTIP_VALUES = ".//*[name()='g']/*[name()='text' and contains(@id, 'legend-label')]"

_legends = View.nested(Legend)

def __init__(self, parent=None, id=None, locator=None, logger=None):
View.__init__(self, parent=parent, logger=logger)

assert id or locator, "Provide id or locator."

if id:
self.locator = ".//div[@id={}]".format(quote(id))
else:
self.locator = locator

@property
def legends(self):
"""Return object of Legends"""
return [leg for leg in self._legends]

@property
def legend_names(self):
"""Return all legend names."""
return [leg.label for leg in self.legends]

def get_legend(self, label):
"""Get specific Legend object.
Args:
label: Name of legend label.
"""
try:
return next(leg for leg in self.legends if leg.label == label)
except StopIteration:
return None

@property
def _x_axis_labels_map(self):
return {self.browser.text(el): el for el in self.browser.elements(self.X_AXIS_LABELS)}

@property
def labels_x_axis(self):
"""Return X-Axis labels."""
return list(self._x_axis_labels_map.keys())

def read(self, offset=(0, -100)):
"""Read chart data.
Note: This method has some limitations as we are reading the tooltip for x-axis labels
with some offset. So only applicable for the chart which shows all Legend data in a single
tooltip for the respective x-axis label.
Args:
offset: offset to move the cursor from the x-axis label so that the tooltip can appear.
"""
_data = {}

for lab_el in self._x_axis_labels_map.values():
self.browser.move_to_element(lab_el)
self.browser.click(lab_el)
self.browser.move_by_offset(*offset)
tooltip_el = self.browser.wait_for_element(self.TOOLTIP)

x_axis_label = self.browser.text(self.TOOLTIP_X_AXIS_LABLE, parent=tooltip_el)
label_data = {}

for label_el, value_el in zip(
self.browser.elements(self.TOOLTIP_LABLES, parent=tooltip_el),
self.browser.elements(self.TOOLTIP_VALUES, parent=tooltip_el),
):
label_data[self.browser.text(label_el)] = self.browser.text(value_el)

_data[x_axis_label] = label_data

# Just move cursor to avoid mismatch of legend and tooltip text.
self.root_browser.move_to_element(".//body")
return _data
44 changes: 44 additions & 0 deletions testing/charts/test_boxplot_chart.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import pytest
from widgetastic.widget import View

from widgetastic_patternfly5 import BoxPlotChart

TESTING_PAGE_URL = "https://patternfly-react-main.surge.sh/charts/box-plot-chart"

TEST_DATA = {
"2015": {"Cats": "q1: 1.75, q3: 3.5", "Limit": "12"},
"2016": {"Cats": "q1: 2.75, q3: 8.5", "Limit": "12"},
"2017": {"Cats": "q1: 4.25, q3: 6.5", "Limit": "12"},
"2018": {"Cats": "q1: 1.75, q3: 4.5", "Limit": "12"},
}


@pytest.fixture
def view(browser):
class TestView(View):
ROOT = ".//div[@id='ws-react-c-box-plot-chart-embedded-legend']"
chart = BoxPlotChart(locator=".//div[@class='pf-v5-c-chart']")

return TestView(browser)


def test_line_chart(view):
legend_names = view.chart.legend_names

# check chart is displayed
assert view.chart.is_displayed

# validate chart data
assert view.chart.read() == TEST_DATA

expected_legend_names = list(TEST_DATA.values())[0].keys()

# validate legend names
assert set(legend_names) == set(expected_legend_names)

# validate x-axis keys
assert view.chart.labels_x_axis == list(TEST_DATA.keys())

# Validate Legends
cats_legend = view.chart.get_legend("Cats")
assert cats_legend.label == "Cats"

0 comments on commit 97d9f40

Please sign in to comment.