Skip to content

Commit

Permalink
tests(dataframe): Add additional tests for dataframe (#1487)
Browse files Browse the repository at this point in the history
Co-authored-by: Barret Schloerke <barret@posit.co>
  • Loading branch information
karangattu and schloerke committed Jul 2, 2024
1 parent d1e19b9 commit 83568a2
Show file tree
Hide file tree
Showing 5 changed files with 191 additions and 3 deletions.
4 changes: 4 additions & 0 deletions shiny/_template_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -430,3 +430,7 @@ def {test_name}(page: Page, app: ShinyAppProc):

# Write template to test file
test_file.write_text(template)

# next steps
print("\nNext steps:")
print("- Run `pytest` in your terminal to run all the tests")
87 changes: 87 additions & 0 deletions shiny/playwright/controller/_controls.py
Original file line number Diff line number Diff line change
Expand Up @@ -5747,6 +5747,7 @@ def cell_locator(self, row: int, col: int) -> Locator:
.nth(col)
)

# TODO-barret; Should this be called `expect_row_count()`?
def expect_n_row(self, value: int, *, timeout: Timeout = None):
"""
Expects the number of rows in the data frame.
Expand All @@ -5762,6 +5763,92 @@ def expect_n_row(self, value: int, *, timeout: Timeout = None):
value, timeout=timeout
)

def expect_selected_n_row(self, value: int, *, timeout: Timeout = None):
"""
Expects the number of selected rows in the data frame.
Parameters
----------
value
The expected number of selected rows.
timeout
The maximum time to wait for the expectation to pass. Defaults to `None`.
"""
playwright_expect(
self.loc_body.locator("tr[aria-selected=true]")
).to_have_count(value, timeout=timeout)

def expect_selected_rows(self, rows: list[int], *, timeout: Timeout = None):
"""
Expects the specified rows to be selected.
Parameters
----------
rows
The row numbers.
timeout
The maximum time to wait for the expectation to pass. Defaults to `None`.
"""
# * given container...
# * Add that container has all known rows
# * Verify that selected row count is of size N
big_loc = self.loc_body
assert len(rows) > 0
for row in rows:
big_loc = big_loc.locator(
"xpath=.", # return "self"
has=self.page.locator(
f"> tr[data-index='{row}'][aria-selected='true']"
),
)

try:
playwright_expect(
big_loc.locator("> tr[aria-selected='true']")
).to_have_count(len(rows), timeout=timeout)
except AssertionError as e:
# Debug expections

# Expecting container to exist (count = 1)
playwright_expect(self.loc_body).to_have_count(1, timeout=timeout)

for row in rows:
# Expecting item `{item}` to exist in container
# Perform exact matches on strings.
playwright_expect(
# Simple approach as position is not needed
self.loc_body.locator(
f"> tr[aria-selected='true'][data-index='{row}']",
)
).to_have_count(1, timeout=timeout)

# Could not find the reason why. Raising the original error.
raise e

def expect_row_focus_state(
self, in_focus: bool = True, *, row: int, timeout: Timeout = None
):
"""
Expects the focus state of the specified row.
Parameters
----------
row
The row number.
in_focus
`True` if the row is expected to be in focus, `False` otherwise.
timeout
The maximum time to wait for the expectation to pass. Defaults to `None`.
"""
if in_focus:
playwright_expect(
self.loc_body.locator(f"> tr[data-index='{row}']")
).to_be_focused(timeout=timeout)
else:
playwright_expect(
self.loc_body.locator(f"> tr[data-index='{row}']")
).not_to_be_focused(timeout=timeout)

def expect_cell(
self,
value: PatternOrStr,
Expand Down
6 changes: 3 additions & 3 deletions tests/playwright/shiny/components/data_frame/edit/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
# pyright: reportMissingTypeStubs = false
# pyright: reportArgumentType = false
# pyright: reportUnknownMemberType = false
#
# TODO-barret-render.data_frame; Make an example that uses a dataframe that then updates a higher level reactive, that causes the df to update... which causes the table to render completely
# TODO-barret-render.data_frame; When "updating" data, try to maintain the scroll, filter info when a new `df` is supplied;
#
# TODO-karan-test; Click outside the table. Tab to the column name, hit enter. Verify the table becomes sorted. Tab to an HTML column name, hit enter. Verify the sort does not update.
# TODO-karan-test; Enable rows selection and editable. Select (and verify) a row. Edit a cell content in that row. Verify the row is not focused. Hit escape key. Verify the cell value is not updated. Verify the row is focused. Hit escape key again. Verify the row is not focused. (Possibly verify the container div is focused?)
# TODO-karan-test; Enable rows selection and editable. Select (and verify) a row. Edit a cell content in that row. Click a cell in another row. Verify the new row is selected and focused. Verify the old row is not selected. Verify the old row cell value was updated.
# TODO-karan-test; Enable rows selection and editable. Select (and verify) a row. Hit enter to edit the first cell in that row. Hit escape key. Verify the same row is focused. Scroll right and display an html column in the left part of the view. Hit enter to edit the first visible non-html cell in that row. Verify that cell is editing.
#
# TODO-future; Can we maintain pre-processed value and use it within editing?
# A: Doesn't seem possible for now
import great_tables as gt
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from palmerpenguins import load_penguins_raw # pyright: ignore[reportMissingTypeStubs]

from shiny import App, Inputs, Outputs, Session, render, ui

df = load_penguins_raw()

df["Species"] = df["Species"].apply(lambda x: ui.HTML(f"<u>{x}</u>")) # pyright: ignore


app_ui = ui.page_fluid(
ui.h2("Palmer Penguins"),
ui.output_data_frame("penguins_df"),
)


def server(input: Inputs, output: Outputs, session: Session) -> None:
@render.data_frame
def penguins_df():
return render.DataGrid(
data=df, # pyright: ignore[reportUnknownArgumentType]
editable=True,
selection_mode="rows",
)


app = App(app_ui, server)
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
from playwright.sync_api import Page

from shiny.playwright import controller
from shiny.run import ShinyAppProc


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

# Select (and verify) a row. Edit a cell content in that row.
# Verify the row is not focused. Hit escape key. Verify the cell value is not updated.
# Verify the row is focused. Hit escape key again.
# Verify the row is not focused. (Possibly verify the container div is focused?)
data_frame = controller.OutputDataFrame(page, "penguins_df")

data_frame.expect_cell("N1A2", row=1, col=6)
data_frame.edit_cell("N2A2", row=1, col=6)
data_frame.expect_row_focus_state(False, row=1)
data_frame.expect_class_state("editing", row=1, col=6)
data_frame.expect_selected_n_row(1)
data_frame.expect_selected_rows([1])
data_frame.save_cell("N3A2", row=1, col=6, save_key="Escape")
data_frame.expect_cell("N1A2", row=1, col=6)
data_frame.expect_row_focus_state(True, row=1)
page.keyboard.press("Escape")
data_frame.expect_row_focus_state(False, row=1)

# Enable rows selection and editable.
# Select (and verify) a row. Edit a cell content in that row.
# Click a cell in another row. Verify the new row is selected and focused.
# Verify the old row is not selected. Verify the old row cell value was updated.
data_frame.expect_cell("N1A2", row=1, col=6)
data_frame.edit_cell("N2A2", row=1, col=6)
data_frame.expect_row_focus_state(False, row=1)
data_frame.expect_class_state("editing", row=1, col=6)
data_frame.cell_locator(row=2, col=6).click()
data_frame.expect_row_focus_state(True, row=2)
data_frame.expect_row_focus_state(False, row=1)
data_frame.expect_cell("N2A2", row=1, col=6)

# Enable rows selection and editable.
# Select (and verify) a row. Hit enter to edit the first cell in that row.
# Hit escape key. Verify the same row is focused.
# Scroll right and display an html column in the left part of the view.
# Hit enter to edit the first visible non-html cell in that row.
# Verify that cell is editing.
data_frame.cell_locator(row=1, col=2).click()
page.keyboard.press("Enter")
data_frame.expect_row_focus_state(False, row=1)
page.keyboard.press("Escape")
data_frame.expect_row_focus_state(True, row=1)
page.keyboard.press("Escape")
data_frame.edit_cell("Temp value", row=1, col=16)
page.keyboard.press("Escape")
page.keyboard.press("Enter")
data_frame.expect_class_state("editing", row=1, col=0)

# Click outside the table/Press Escape to exit row focus.
# Tab to the column name, hit enter. Verify the table becomes sorted.
# Tab to an HTML column name, hit enter. Verify the sort does not update.
page.keyboard.press("Escape")
page.keyboard.press("Escape")
page.keyboard.press("Tab")
page.keyboard.press("Tab") # tab to sample number
page.keyboard.press("Enter")
data_frame.expect_cell("152", row=0, col=1)
page.keyboard.press("Tab")
page.keyboard.press("Enter")
data_frame.expect_cell("Adelie Penguin (Pygoscelis adeliae)", row=0, col=2)

0 comments on commit 83568a2

Please sign in to comment.