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(sidebar): Add tests for sidebar #1715

Merged
merged 8 commits into from
Oct 4, 2024
Merged
Show file tree
Hide file tree
Changes from 6 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 CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

* Small improvements to the default pulse busy indicator to better blend with any background. It's also now slightly smaller by default.(#1707)

* Added `.expect_class()`, `.expect_gap()`, `.expect_bg_color()`, `.expect_desktop_state()`, `.expect_mobile_state()`, `.expect_mobile_max_height()`, `.expect_title()`, `.expect_width()` and `.expect_padding()` for `Sidebar` in `shiny.playwright.controllers` (#1715)
schloerke marked this conversation as resolved.
Show resolved Hide resolved

* Modified `.expect_text()` for `Sidebar` in `shiny.playwright.controllers` to use `loc_content` instead of `loc` for text. (#1715)
schloerke marked this conversation as resolved.
Show resolved Hide resolved

### Bug fixes

* A few fixes for `ui.Chat()`, including:
Expand Down
179 changes: 176 additions & 3 deletions shiny/playwright/controller/_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@
from playwright.sync_api import expect as playwright_expect

from .._types import PatternOrStr, Timeout
from ..expect._internal import (
expect_attribute_to_have_value as _expect_attribute_to_have_value,
)
from ..expect._internal import expect_class_to_have_value as _expect_class_to_have_value
from ._base import UiWithContainer, WidthLocM
from ..expect._internal import expect_style_to_have_value as _expect_style_to_have_value
from ._base import UiWithContainer


class Sidebar(
WidthLocM,
UiWithContainer,
):
"""Controller for :func:`shiny.ui.sidebar`."""
Expand All @@ -32,6 +35,14 @@ class Sidebar(
"""
Playwright `Locator` for the position of the sidebar.
"""
loc_content: Locator
"""
Playwright `Locator` for the content of the sidebar.
"""
loc_title: Locator
"""
Playwright `Locator` for the title of the sidebar.
"""

def __init__(self, page: Page, id: str) -> None:
"""
Expand All @@ -52,6 +63,8 @@ def __init__(self, page: Page, id: str) -> None:
)
self.loc_handle = self.loc_container.locator("button.collapse-toggle")
self.loc_position = self.loc.locator("..")
self.loc_content = self.loc.locator("> div.sidebar-content")
self.loc_title = self.loc_content.locator("> header.sidebar-title")

def expect_text(self, value: PatternOrStr, *, timeout: Timeout = None) -> None:
"""
Expand All @@ -64,7 +77,167 @@ def expect_text(self, value: PatternOrStr, *, timeout: Timeout = None) -> None:
timeout
The maximum time to wait for the text to appear. Defaults to `None`.
"""
playwright_expect(self.loc).to_have_text(value, timeout=timeout)
playwright_expect(self.loc_content).to_have_text(value, timeout=timeout)

def expect_class(
self,
class_name: str,
*,
has_class: bool = True,
timeout: Timeout = None,
) -> None:
"""
Asserts that the sidebar has or does not have a CSS class.

Parameters
----------
class_name
The CSS class to check for.
has_class
`True` if the sidebar should have the CSS class, `False` otherwise.
timeout
The maximum time to wait for the sidebar to appear. Defaults to `None`.
"""
_expect_class_to_have_value(
self.loc,
class_name,
has_class=has_class,
timeout=timeout,
)

def expect_width(self, value: PatternOrStr, *, timeout: Timeout = None) -> None:
"""
Asserts that the sidebar has the expected width.

Parameters
----------
value
The expected width of the sidebar.
timeout
The maximum time to wait for the width to appear. Defaults to `None`.
"""
_expect_style_to_have_value(
self.loc_container, "--_sidebar-width", value, timeout=timeout
)

def expect_gap(self, value: PatternOrStr, *, timeout: Timeout = None) -> None:
"""
Asserts that the sidebar has the expected gap.

Parameters
----------
value
The expected gap of the sidebar.
timeout
The maximum time to wait for the gap to appear. Defaults to `None`.
"""
_expect_style_to_have_value(self.loc_content, "gap", value, timeout=timeout)

def expect_bg_color(self, value: PatternOrStr, *, timeout: Timeout = None) -> None:
"""
Asserts that the sidebar has the expected background color.

Parameters
----------
value
The expected background color of the sidebar.
timeout
The maximum time to wait for the background color to appear. Defaults to `None`.
"""
_expect_style_to_have_value(
self.loc_container, "--_sidebar-bg", value, timeout=timeout
)

def expect_desktop_state(
self, value: Literal["open", "closed", "always"], *, timeout: Timeout = None
) -> None:
"""
Asserts that the sidebar has the expected state on desktop.

Parameters
----------
value
The expected state of the sidebar on desktop.
timeout
The maximum time to wait for the state to appear. Defaults to `None`.
"""
_expect_attribute_to_have_value(
self.loc_container,
name="data-open-desktop",
value=value,
timeout=timeout,
)

def expect_mobile_state(
self, value: Literal["open", "closed", "always"], *, timeout: Timeout = None
) -> None:
"""
Asserts that the sidebar has the expected state on mobile.

Parameters
----------
value
The expected state of the sidebar on mobile.
timeout
The maximum time to wait for the state to appear. Defaults to `None`.
"""
_expect_attribute_to_have_value(
self.loc_container,
name="data-open-mobile",
value=value,
timeout=timeout,
)

def expect_mobile_max_height(
self, value: PatternOrStr, *, timeout: Timeout = None
) -> None:
"""
Asserts that the sidebar has the expected maximum height on mobile.

Parameters
----------
value
The expected maximum height of the sidebar on mobile.
timeout
The maximum time to wait for the maximum height to appear. Defaults to `None`.
"""
self.expect_mobile_state("always", timeout=timeout)
_expect_style_to_have_value(
self.loc_container, "--_mobile-max-height", value, timeout=timeout
)

def expect_title(self, value: PatternOrStr, *, timeout: Timeout = None) -> None:
"""
Asserts that the sidebar has the expected title.

Parameters
----------
value
The expected title of the sidebar.
timeout
The maximum time to wait for the title to appear. Defaults to `None`.
"""
playwright_expect(self.loc_title).to_have_text(value, timeout=timeout)

def expect_padding(
self, value: str | list[str], *, timeout: Timeout = None
) -> None:
"""
Asserts that the sidebar has the expected padding.

Parameters
----------
value
The expected padding of the sidebar.
timeout
The maximum time to wait for the padding to appear. Defaults to `None`.
"""
if not isinstance(value, list):
value = [value]
padding_val = " ".join(value)
_expect_style_to_have_value(
self.loc_content, "padding", padding_val, timeout=timeout
)

def expect_position(
self,
Expand Down
68 changes: 68 additions & 0 deletions tests/playwright/shiny/inputs/sidebar_kitchensink/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
from shiny.express import input, render, ui

ui.page_opts(fillable=True)

with ui.card():
with ui.layout_sidebar():
with ui.sidebar(
id="sidebar_left",
open="desktop",
title="Left sidebar",
bg="dodgerBlue",
class_="text-white",
gap="20px",
padding="10px",
width="200px",
):
"Left sidebar content"

@render.code
def state_left():
return f"input.sidebar_left(): {input.sidebar_left()}"


with ui.card():
with ui.layout_sidebar():
with ui.sidebar(
id="sidebar_right",
position="right",
open={"desktop": "closed", "mobile": "open"},
padding=["10px", "20px"],
bg="SlateBlue",
):
"Right sidebar content"

@render.code
def state_right():
return f"input.sidebar_right(): {input.sidebar_right()}"


with ui.card():
with ui.layout_sidebar():
with ui.sidebar(
id="sidebar_closed",
open="closed",
bg="LightCoral",
padding=["10px", "20px", "30px"],
):
"Closed sidebar content"

@render.code
def state_closed():
return f"input.sidebar_closed(): {input.sidebar_closed()}"


with ui.card():
with ui.layout_sidebar():
with ui.sidebar(
id="sidebar_always",
open="always",
bg="PeachPuff",
padding=["10px", "20px", "30px", "40px"],
max_height_mobile="175px",
):
"Always sidebar content"

@render.code
def state_always():
return f"input.sidebar_always(): {input.sidebar_always()}"
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from playwright.sync_api import Page

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


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

left_sidebar = controller.Sidebar(page, "sidebar_left")
output_txt_left = controller.OutputTextVerbatim(page, "state_left")
left_sidebar.set(True)
left_sidebar.expect_padding("10px")
left_sidebar.expect_padding(["10px"])
left_sidebar.expect_title("Left sidebar")
left_sidebar.expect_gap("20px")
left_sidebar.expect_class("text-white", has_class=True)
left_sidebar.expect_bg_color("dodgerBlue")
left_sidebar.expect_desktop_state("open")
left_sidebar.expect_mobile_state("closed")
left_sidebar.expect_width("200px")
output_txt_left.expect_value("input.sidebar_left(): True")
left_sidebar.expect_open(True)
left_sidebar.set(False)
output_txt_left.expect_value("input.sidebar_left(): False")
left_sidebar.expect_handle(True)
left_sidebar.expect_open(False)
left_sidebar.loc_handle.click()
left_sidebar.expect_open(True)
output_txt_left.expect_value("input.sidebar_left(): True")

right_sidebar = controller.Sidebar(page, "sidebar_right")
right_sidebar.expect_padding(["10px", "20px"])
right_sidebar.expect_bg_color("SlateBlue")
right_sidebar.expect_mobile_state("open")
right_sidebar.expect_desktop_state("closed")

closed_sidebar = controller.Sidebar(page, "sidebar_closed")
closed_sidebar.expect_padding(["10px", "20px", "30px"])
closed_sidebar.expect_bg_color("LightCoral")
closed_sidebar.expect_mobile_state("closed")
closed_sidebar.expect_desktop_state("closed")

always_sidebar = controller.Sidebar(page, "sidebar_always")
always_sidebar.expect_padding(["10px", "20px", "30px", "40px"])
always_sidebar.expect_bg_color("PeachPuff")
always_sidebar.expect_open(True)
always_sidebar.expect_desktop_state("always")
always_sidebar.expect_mobile_state("always")
always_sidebar.expect_mobile_max_height("175px")
Loading