Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tests(dataframe): Add additional tests for dataframe #1487

Merged
merged 5 commits into from
Jul 2, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
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: 0 additions & 6 deletions tests/playwright/shiny/components/data_frame/edit/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,6 @@
# 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

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>")) # type: ignore
schloerke marked this conversation as resolved.
Show resolved Hide resolved


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)

# TODO-karan-test; Enable rows selection and editable.
schloerke marked this conversation as resolved.
Show resolved Hide resolved
# 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)

# TODO-karan-test; Enable rows selection and editable.
schloerke marked this conversation as resolved.
Show resolved Hide resolved
# 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)

# TODO-karan-test; Click outside the table/Press Escape to exit row focus.
schloerke marked this conversation as resolved.
Show resolved Hide resolved
# 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)
Loading