Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
9e2fb1f
added the first batch of tests
karangattu Jun 27, 2023
960dcbd
add some more assertions for accordion component
karangattu Jun 29, 2023
01b536c
Add tests with conventional locators
karangattu Jul 7, 2023
829a860
run formatter on code
karangattu Jul 7, 2023
fb85594
Merge branch 'main' into add-accordion-test
karangattu Jul 7, 2023
a6e037a
Add more fixtures and use test helpers
karangattu Jul 7, 2023
4ba86d5
Merge branch 'main' into add-accordion-test
karangattu Jul 10, 2023
dd7830b
Use the expect_height to verify autoresize functionality
karangattu Jul 13, 2023
a979d0d
Merge branch 'main' into add-accordion-test
karangattu Jul 13, 2023
9222958
Merge branch 'main' into add-accordion-test
karangattu Jul 13, 2023
ec2f027
Merge branch 'main' into add-accordion-test
karangattu Jul 18, 2023
d8f44da
Remove unnecessary comments
karangattu Jul 19, 2023
9b3b43c
change the new changes to example file paths
karangattu Jul 19, 2023
c3c4a20
Merge branch 'main' into add-accordion-test
karangattu Jul 19, 2023
6eb2d9e
use bounding box since hardcoding height was fickle
karangattu Jul 19, 2023
21ffedd
Update e2e/controls.py
karangattu Jul 19, 2023
59b9a07
Remove all_panels_to_have_attribute
karangattu Jul 19, 2023
dda3fbc
Merge branch 'main' into add-accordion-test
karangattu Jul 20, 2023
c18bc1c
accordion requested changes
karangattu Jul 20, 2023
bd55bdf
Merge branch 'main' into add-accordion-test
karangattu Jul 21, 2023
7b80674
use accordion panel function instead of class instance
karangattu Jul 21, 2023
a20ff59
Add support for `expect_locator_values_in_list(el_type)` to be a Locator
karangattu Jul 21, 2023
c484c28
User resize_number to determine `\n` count
schloerke Jul 21, 2023
72e7de3
address linting issues in test_autoresize file
karangattu Jul 21, 2023
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
146 changes: 138 additions & 8 deletions e2e/controls.py
Original file line number Diff line number Diff line change
Expand Up @@ -938,7 +938,7 @@ def expect_locator_values_in_list(
*,
page: Page,
loc_container: Locator,
el_type: str,
el_type: Locator | str,
arr_name: str,
arr: ListPatternOrStr,
is_checked: bool | MISSING_TYPE = MISSING,
Expand All @@ -949,13 +949,20 @@ def expect_locator_values_in_list(

# Make sure the locator has len(uniq_arr) input elements
_MultipleDomItems.assert_arr_is_unique(arr, f"`{arr_name}` must be unique")
is_checked_str = _MultipleDomItems.checked_css_str(is_checked)

item_selector = f"{el_type}{is_checked_str}"
if isinstance(el_type, Locator):
if not isinstance(is_checked, MISSING_TYPE):
raise RuntimeError(
"`is_checked` cannot be specified if `el_type` is a Locator"
)
loc_item = el_type
else:
is_checked_str = _MultipleDomItems.checked_css_str(is_checked)
loc_item = page.locator(f"{el_type}{is_checked_str}")

# If there are no items, then we should not have any elements
if len(arr) == 0:
playwright_expect(loc_container.locator(item_selector)).to_have_count(
playwright_expect(loc_container.locator(el_type)).to_have_count(
0, timeout=timeout
)
return
Expand All @@ -964,7 +971,7 @@ def expect_locator_values_in_list(
# Find all items in set
for item, i in zip(arr, range(len(arr))):
# Get all elements of type
has_locator = page.locator(item_selector)
has_locator = loc_item
# Get the `n`th matching element
has_locator = has_locator.nth(i)
# Make sure that element has the correct attribute value
Expand All @@ -982,7 +989,7 @@ def expect_locator_values_in_list(
# Make sure other items are not in set
# If we know all elements are contained in the container,
# and all elements all unique, then it should have a count of `len(arr)`
loc_inputs = loc_container.locator(item_selector)
loc_inputs = loc_container.locator(loc_item)
try:
playwright_expect(loc_inputs).to_have_count(len(arr), timeout=timeout)
except AssertionError as e:
Expand All @@ -992,14 +999,14 @@ def expect_locator_values_in_list(
playwright_expect(loc_container_orig).to_have_count(1, timeout=timeout)

# Expecting the container to contain {len(arr)} items
playwright_expect(loc_container_orig.locator(item_selector)).to_have_count(
playwright_expect(loc_container_orig.locator(loc_item)).to_have_count(
len(arr), timeout=timeout
)

for item, i in zip(arr, range(len(arr))):
# Expecting item `{i}` to be `{item}`
playwright_expect(
loc_container_orig.locator(item_selector).nth(i)
loc_container_orig.locator(loc_item).nth(i)
).to_have_attribute(key, item, timeout=timeout)

# Could not find the reason why. Raising the original error.
Expand Down Expand Up @@ -2096,6 +2103,7 @@ def expect_value(
*,
timeout: Timeout = None,
) -> None:
"""Note this function will trim value and output text value before comparing them"""
self.expect.to_have_text(value, timeout=timeout)


Expand Down Expand Up @@ -2565,3 +2573,125 @@ def expect_min_height(self, value: StyleValue, *, timeout: Timeout = None) -> No

def expect_height(self, value: StyleValue, *, timeout: Timeout = None) -> None:
expect_to_have_style(self.loc_container, "height", value, timeout=timeout)


### Experimental below


class Accordion(
_WidthLocM,
_InputWithContainer,
):
# *args: AccordionPanel | TagAttrs,
# id: Optional[str] = None,
# open: Optional[bool | str | list[str]] = None,
# multiple: bool = True,
# class_: Optional[str] = None,
# width: Optional[CssUnit] = None,
# height: Optional[CssUnit] = None,
# **kwargs: TagAttrValue,
def __init__(self, page: Page, id: str) -> None:
super().__init__(
page,
id=id,
loc="> div.accordion-item",
loc_container=f"div#{id}.accordion.shiny-bound-input",
)
self.loc_open = self.loc.locator(
# Return self
"xpath=.",
# Simple approach as position is not needed
has=page.locator(
"> div.accordion-collapse.show",
),
)

def expect_height(self, value: StyleValue, *, timeout: Timeout = None) -> None:
expect_to_have_style(self.loc_container, "height", value, timeout=timeout)

def expect_width(self, value: StyleValue, *, timeout: Timeout = None) -> None:
expect_to_have_style(self.loc_container, "width", value, timeout=timeout)

def expect_open(
self,
value: list[PatternOrStr],
*,
timeout: Timeout = None,
) -> None:
_MultipleDomItems.expect_locator_values_in_list(
page=self.page,
loc_container=self.loc_container,
el_type=self.page.locator(
"> div.accordion-item",
has=self.page.locator("> div.accordion-collapse.show"),
),
# el_type="> div.accordion-item:has(> div.accordion-collapse.show)",
arr_name="value",
arr=value,
key="data-value",
timeout=timeout,
)

def expect_panels(
self,
value: list[PatternOrStr],
*,
timeout: Timeout = None,
) -> None:
_MultipleDomItems.expect_locator_values_in_list(
page=self.page,
loc_container=self.loc_container,
el_type="> div.accordion-item",
arr_name="value",
arr=value,
key="data-value",
timeout=timeout,
)

def accordion_panel(
self,
data_value: str,
) -> AccordionPanel:
return AccordionPanel(self.page, self.id, data_value)


class AccordionPanel(
_WidthLocM,
_InputWithContainer,
):
# self,
# *args: TagChild | TagAttrs,
# data_value: str,
# icon: TagChild | None,
# title: TagChild | None,
# id: str | None,
# **kwargs: TagAttrValue,
def __init__(self, page: Page, id: str, data_value: str) -> None:
super().__init__(
page,
id=id,
loc=f"> div.accordion-item[data-value='{data_value}']",
loc_container=f"div#{id}.accordion.shiny-bound-input",
)

self.loc_label = self.loc.locator(
"> .accordion-header > .accordion-button > .accordion-title"
)

self.loc_icon = self.loc.locator(
"> .accordion-header > .accordion-button > .accordion-icon"
)

self.loc_body = self.loc.locator("> .accordion-collapse")

def expect_label(self, value: PatternOrStr, *, timeout: Timeout = None) -> None:
playwright_expect(self.loc_label).to_have_text(value, timeout=timeout)

def expect_body(self, value: PatternOrStr, *, timeout: Timeout = None) -> None:
playwright_expect(self.loc_body).to_have_text(value, timeout=timeout)

def expect_icon(self, value: PatternOrStr, *, timeout: Timeout = None) -> None:
playwright_expect(self.loc_icon).to_have_text(value, timeout=timeout)

def expect_open(self, is_open: bool, *, timeout: Timeout = None) -> None:
_expect_class_value(self.loc_body, "show", is_open, timeout=timeout)
78 changes: 78 additions & 0 deletions e2e/experimental/accordion/test_accordion.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
from conftest import ShinyAppProc
from controls import Accordion, InputActionButton, OutputTextVerbatim
from playwright.sync_api import Page


def test_accordion(page: Page, local_app: ShinyAppProc) -> None:
page.goto(local_app.url)

acc = Accordion(page, "acc")
acc_panel_A = acc.accordion_panel("Section A")
output_txt_verbatim = OutputTextVerbatim(page, "acc_txt")
alternate_button = InputActionButton(page, "alternate")
open_all_button = InputActionButton(page, "open_all")
close_all_button = InputActionButton(page, "close_all")
toggle_b_button = InputActionButton(page, "toggle_b")
toggle_updates_button = InputActionButton(page, "toggle_updates")
toggle_efg_button = InputActionButton(page, "toggle_efg")
acc.expect_width(None)
acc.expect_height(None)

# initial state - by default only A is open
acc.expect_panels(["Section A", "Section B", "Section C", "Section D"])
output_txt_verbatim.expect_value("input.acc(): ('Section A',)")
acc.expect_open(["Section A"])
acc_panel_A.expect_label("Section A")
acc_panel_A.expect_body("Some narrative for section A")
acc_panel_A.expect_open(True)

alternate_button.click()
acc.expect_open(["Section B", "Section D"])
output_txt_verbatim.expect_value("input.acc(): ('Section B', 'Section D')")

alternate_button.click()
acc.expect_open(["Section A", "Section C"])
output_txt_verbatim.expect_value("input.acc(): ('Section A', 'Section C')")

open_all_button.click()
acc.expect_open(["Section A", "Section B", "Section C", "Section D"])
output_txt_verbatim.expect_value(
"input.acc(): ('Section A', 'Section B', 'Section C', 'Section D')"
)

close_all_button.click()
acc.expect_open([])
output_txt_verbatim.expect_value("input.acc(): None")

toggle_b_button.click()
acc.expect_open(["Section B"])
output_txt_verbatim.expect_value("input.acc(): ('Section B',)")

acc_panel_updated_A = acc.accordion_panel("updated_section_a")
toggle_updates_button.click()
acc_panel_updated_A.expect_label("Updated title")
acc_panel_updated_A.expect_body("Updated body")
acc_panel_updated_A.expect_icon("Look! An icon! -->")

acc.expect_panels(["updated_section_a", "Section B", "Section C", "Section D"])
output_txt_verbatim.expect_value("input.acc(): ('updated_section_a', 'Section B')")

toggle_efg_button.click()
acc.expect_panels(
[
"updated_section_a",
"Section B",
"Section C",
"Section D",
"Section E",
"Section F",
"Section G",
]
)
acc.expect_open(
["updated_section_a", "Section B", "Section E", "Section F", "Section G"]
)
# will be uncommented once https://github.com/rstudio/bslib/issues/565 is fixed
# output_txt_verbatim.expect_value(
# "input.acc(): ('updated_section_a', 'Section B', 'Section E', 'Section F', 'Section G')"
# )
34 changes: 34 additions & 0 deletions e2e/experimental/test_autoresize.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from conftest import ShinyAppProc, x_create_doc_example_fixture
from controls import InputTextArea, OutputTextVerbatim
from playwright.sync_api import Locator, Page

app = x_create_doc_example_fixture("input_text_area")

resize_number = 6


def get_box_height(locator: Locator) -> float:
bounding_box = locator.bounding_box()
if bounding_box is not None:
return bounding_box["height"]
else:
return 0


def test_autoresize(page: Page, app: ShinyAppProc) -> None:
page.goto(app.url)

input_area = InputTextArea(page, "caption")
output_txt_verbatim = OutputTextVerbatim(page, "value")
input_area.expect_height(None)
input_area.expect_width(None)
input_area.set("test value")
# use bounding box approach since height is dynamic
initial_height = get_box_height(input_area.loc)
output_txt_verbatim.expect_value("test value")
for _ in range(resize_number):
input_area.loc.press("Enter")
input_area.loc.type("end value")
return_txt = "\n" * resize_number
output_txt_verbatim.expect_value(f"test value{return_txt}end value")
assert get_box_height(input_area.loc) > initial_height