Skip to content

Commit 2d5acbe

Browse files
Add card test (#622)
Co-authored-by: Barret Schloerke <[email protected]>
1 parent 4b32919 commit 2d5acbe

File tree

5 files changed

+299
-0
lines changed

5 files changed

+299
-0
lines changed

e2e/controls.py

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2371,3 +2371,197 @@ def expect_open(self, open: bool, *, timeout: Timeout = None) -> None:
23712371
playwright_expect(self.loc_handle).to_have_attribute(
23722372
"aria-expanded", str(open).lower(), timeout=timeout
23732373
)
2374+
2375+
2376+
class _CardBodyP(_InputBaseP, Protocol):
2377+
loc_body: Locator
2378+
2379+
2380+
class _CardBodyM:
2381+
def expect_body(
2382+
self: _CardBodyP,
2383+
text: PatternOrStr | list[PatternOrStr],
2384+
*,
2385+
timeout: Timeout = None,
2386+
) -> None:
2387+
"""Note: If testing against multiple elements, text should be an array"""
2388+
playwright_expect(self.loc).to_have_text(
2389+
text,
2390+
timeout=timeout,
2391+
)
2392+
2393+
2394+
class _CardFooterLayoutP(_InputBaseP, Protocol):
2395+
loc_footer: Locator
2396+
2397+
2398+
class _CardFooterM:
2399+
def expect_footer(
2400+
self: _CardFooterLayoutP,
2401+
text: PatternOrStr,
2402+
*,
2403+
timeout: Timeout = None,
2404+
) -> None:
2405+
playwright_expect(self.loc_footer).to_have_text(
2406+
text,
2407+
timeout=timeout,
2408+
)
2409+
2410+
2411+
class _CardFullScreenLayoutP(_OutputBaseP, Protocol):
2412+
loc_title: Locator
2413+
_loc_fullscreen: Locator
2414+
_loc_close_button: Locator
2415+
2416+
2417+
class _CardFullScreenM:
2418+
def open_full_screen(
2419+
self: _CardFullScreenLayoutP, *, timeout: Timeout = None
2420+
) -> None:
2421+
self.loc_title.hover(timeout=timeout)
2422+
self._loc_fullscreen.wait_for(state="visible", timeout=timeout)
2423+
self._loc_fullscreen.click(timeout=timeout)
2424+
2425+
def close_full_screen(
2426+
self: _CardFullScreenLayoutP, *, timeout: Timeout = None
2427+
) -> None:
2428+
self._loc_close_button.click(timeout=timeout)
2429+
2430+
def expect_full_screen(
2431+
self: _CardFullScreenLayoutP, open: bool, *, timeout: Timeout = None
2432+
) -> None:
2433+
playwright_expect(self._loc_close_button).to_have_count(
2434+
int(open), timeout=timeout
2435+
)
2436+
2437+
2438+
class ValueBox(
2439+
_WidthLocM,
2440+
_CardBodyM,
2441+
_CardFullScreenM,
2442+
_InputWithContainer,
2443+
):
2444+
# title: TagChild,
2445+
# value: TagChild,
2446+
# *args: TagChild | TagAttrs,
2447+
# showcase: TagChild = None,
2448+
# showcase_layout: ((TagChild, Tag) -> CardItem) | None = None,
2449+
# full_screen: bool = False,
2450+
# theme_color: str | None = "primary",
2451+
# height: CssUnit | None = None,
2452+
# max_height: CssUnit | None = None,
2453+
# fill: bool = True,
2454+
# class_: str | None = None,
2455+
# **kwargs: TagAttrValue
2456+
def __init__(self, page: Page, id: str) -> None:
2457+
super().__init__(
2458+
page,
2459+
id=id,
2460+
loc_container=f"div#{id}.bslib-value-box",
2461+
loc="> div > .value-box-grid",
2462+
)
2463+
value_box_grid = self.loc
2464+
self.loc = value_box_grid.locator(
2465+
"> div > .value-box-area > :not(:first-child)"
2466+
)
2467+
self.loc_showcase = value_box_grid.locator("> div > .value-box-showcase")
2468+
self.loc_title = value_box_grid.locator(
2469+
"> div > .value-box-area > :first-child"
2470+
)
2471+
self.loc_body = self.loc
2472+
self._loc_fullscreen = self.loc_container.locator(
2473+
"> bslib-tooltip > .bslib-full-screen-enter"
2474+
)
2475+
2476+
# an easier approach is using `#bslib-full-screen-overlay:has(+ div#{id}.card) > a`
2477+
# but playwright doesn't allow that
2478+
self._loc_close_button = (
2479+
self.page.locator(f"#bslib-full-screen-overlay + div#{id}.bslib-value-box")
2480+
.locator("..")
2481+
.locator("#bslib-full-screen-overlay > a")
2482+
)
2483+
2484+
def expect_height(self, value: StyleValue, *, timeout: Timeout = None) -> None:
2485+
expect_to_have_style(
2486+
self.loc_container, "--bslib-grid-height", value, timeout=timeout
2487+
)
2488+
2489+
def expect_title(
2490+
self,
2491+
text: PatternOrStr,
2492+
*,
2493+
timeout: Timeout = None,
2494+
) -> None:
2495+
playwright_expect(self.loc_title).to_have_text(
2496+
text,
2497+
timeout=timeout,
2498+
)
2499+
2500+
# hard to test since it can be customized by user
2501+
# def expect_showcase_layout(self, layout, *, timeout: Timeout = None) -> None:
2502+
# raise NotImplementedError()
2503+
2504+
2505+
class Card(_WidthLocM, _CardFooterM, _CardBodyM, _CardFullScreenM, _InputWithContainer):
2506+
# *args: TagChild | TagAttrs | CardItem,
2507+
# full_screen: bool = False,
2508+
# height: CssUnit | None = None,
2509+
# max_height: CssUnit | None = None,
2510+
# min_height: CssUnit | None = None,
2511+
# fill: bool = True,
2512+
# class_: str | None = None,
2513+
# wrapper: WrapperCallable | MISSING_TYPE | None = MISSING,
2514+
# **kwargs: TagAttrValue
2515+
def __init__(self, page: Page, id: str) -> None:
2516+
super().__init__(
2517+
page,
2518+
id=id,
2519+
loc_container=f"div#{id}.card",
2520+
loc="> div.card-body",
2521+
)
2522+
self.loc_title = self.loc_container.locator("> div.card-header")
2523+
self.loc_footer = self.loc_container.locator("> div.card-footer")
2524+
self._loc_fullscreen = self.loc_container.locator(
2525+
"> bslib-tooltip > .bslib-full-screen-enter"
2526+
)
2527+
# an easier approach is using `#bslib-full-screen-overlay:has(+ div#{id}.card) > a`
2528+
# but playwright doesn't allow that
2529+
self._loc_close_button = (
2530+
self.page.locator(f"#bslib-full-screen-overlay + div#{id}")
2531+
.locator("..")
2532+
.locator("#bslib-full-screen-overlay > a")
2533+
)
2534+
self.loc_body = self.loc
2535+
2536+
def expect_header(
2537+
self,
2538+
text: PatternOrStr,
2539+
*,
2540+
timeout: Timeout = None,
2541+
) -> None:
2542+
playwright_expect(self.loc_title).to_have_text(
2543+
text,
2544+
timeout=timeout,
2545+
)
2546+
2547+
# def expect_body(
2548+
# self,
2549+
# text: PatternOrStr,
2550+
# index: int = 0,
2551+
# *,
2552+
# timeout: Timeout = None,
2553+
# ) -> None:
2554+
# """Note: Function requires an index since multiple bodies can exist in loc"""
2555+
# playwright_expect(self.loc.nth(index).locator("> :first-child")).to_have_text(
2556+
# text,
2557+
# timeout=timeout,
2558+
# )
2559+
2560+
def expect_max_height(self, value: StyleValue, *, timeout: Timeout = None) -> None:
2561+
expect_to_have_style(self.loc_container, "max-height", value, timeout=timeout)
2562+
2563+
def expect_min_height(self, value: StyleValue, *, timeout: Timeout = None) -> None:
2564+
expect_to_have_style(self.loc_container, "min-height", value, timeout=timeout)
2565+
2566+
def expect_height(self, value: StyleValue, *, timeout: Timeout = None) -> None:
2567+
expect_to_have_style(self.loc_container, "height", value, timeout=timeout)

e2e/experimental/card/app.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from __future__ import annotations
2+
3+
import shiny.experimental as x
4+
from shiny import App, ui
5+
6+
app_ui = ui.page_fluid(
7+
x.ui.card(
8+
x.ui.card_header("This is the header"),
9+
x.ui.card_title("This is the title"),
10+
ui.p("This is the body."),
11+
x.ui.card_image(
12+
file=None,
13+
src="https://posit.co/wp-content/uploads/2022/10/Posit-logo-h-full-color-RGB-TM.svg",
14+
),
15+
ui.p("This is still the body."),
16+
x.ui.card_footer("This is the footer"),
17+
full_screen=True,
18+
id="card1",
19+
),
20+
)
21+
22+
23+
app = App(app_ui, server=None)

e2e/experimental/card/test_card.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from conftest import ShinyAppProc
2+
from controls import Card
3+
from playwright.sync_api import Page
4+
5+
6+
def test_card(page: Page, local_app: ShinyAppProc) -> None:
7+
page.goto(local_app.url)
8+
9+
card = Card(page, "card1")
10+
card.expect_max_height(None)
11+
card.expect_min_height(None)
12+
card.expect_height(None)
13+
card.expect_header("This is the header")
14+
card.expect_footer("This is the footer")
15+
card.expect_body(
16+
[
17+
"\nThis is the title\nThis is the body.\n",
18+
"\n\n",
19+
"\nThis is still the body.\n",
20+
]
21+
)
22+
card.expect_full_screen(False)
23+
card.open_full_screen()
24+
card.expect_full_screen(True)
25+
card.close_full_screen()
26+
card.expect_full_screen(False)

e2e/experimental/value_box/app.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
from __future__ import annotations
2+
3+
import shiny.experimental as x
4+
from shiny import App, ui
5+
6+
piggy_bank = ui.HTML(
7+
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="bi bi-piggy-bank " style="height:auto;width:100%;fill:currentColor;" aria-hidden="true" role="img" ><path d="M5 6.25a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0zm1.138-1.496A6.613 6.613 0 0 1 7.964 4.5c.666 0 1.303.097 1.893.273a.5.5 0 0 0 .286-.958A7.602 7.602 0 0 0 7.964 3.5c-.734 0-1.441.103-2.102.292a.5.5 0 1 0 .276.962z"></path>\n<path fill-rule="evenodd" d="M7.964 1.527c-2.977 0-5.571 1.704-6.32 4.125h-.55A1 1 0 0 0 .11 6.824l.254 1.46a1.5 1.5 0 0 0 1.478 1.243h.263c.3.513.688.978 1.145 1.382l-.729 2.477a.5.5 0 0 0 .48.641h2a.5.5 0 0 0 .471-.332l.482-1.351c.635.173 1.31.267 2.011.267.707 0 1.388-.095 2.028-.272l.543 1.372a.5.5 0 0 0 .465.316h2a.5.5 0 0 0 .478-.645l-.761-2.506C13.81 9.895 14.5 8.559 14.5 7.069c0-.145-.007-.29-.02-.431.261-.11.508-.266.705-.444.315.306.815.306.815-.417 0 .223-.5.223-.461-.026a.95.95 0 0 0 .09-.255.7.7 0 0 0-.202-.645.58.58 0 0 0-.707-.098.735.735 0 0 0-.375.562c-.024.243.082.48.32.654a2.112 2.112 0 0 1-.259.153c-.534-2.664-3.284-4.595-6.442-4.595zM2.516 6.26c.455-2.066 2.667-3.733 5.448-3.733 3.146 0 5.536 2.114 5.536 4.542 0 1.254-.624 2.41-1.67 3.248a.5.5 0 0 0-.165.535l.66 2.175h-.985l-.59-1.487a.5.5 0 0 0-.629-.288c-.661.23-1.39.359-2.157.359a6.558 6.558 0 0 1-2.157-.359.5.5 0 0 0-.635.304l-.525 1.471h-.979l.633-2.15a.5.5 0 0 0-.17-.534 4.649 4.649 0 0 1-1.284-1.541.5.5 0 0 0-.446-.275h-.56a.5.5 0 0 1-.492-.414l-.254-1.46h.933a.5.5 0 0 0 .488-.393zm12.621-.857a.565.565 0 0 1-.098.21.704.704 0 0 1-.044-.025c-.146-.09-.157-.175-.152-.223a.236.236 0 0 1 .117-.173c.049-.027.08-.021.113.012a.202.202 0 0 1 .064.199z"></path></svg>'
8+
)
9+
arrow_up = ui.HTML(
10+
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="bi bi-arrow-up " style="height:1em;width:1em;fill:currentColor;" aria-hidden="true" role="img" ><path fill-rule="evenodd" d="M8 15a.5.5 0 0 0 .5-.5V2.707l3.146 3.147a.5.5 0 0 0 .708-.708l-4-4a.5.5 0 0 0-.708 0l-4 4a.5.5 0 1 0 .708.708L7.5 2.707V14.5a.5.5 0 0 0 .5.5z"></path></svg>'
11+
)
12+
13+
app_ui = ui.page_fluid(
14+
x.ui.value_box(
15+
"KPI Title",
16+
ui.h1(ui.HTML("$1 <i>Billion</i> Dollars")),
17+
ui.span(arrow_up, " 30% VS PREVIOUS 30 DAYS"),
18+
showcase=piggy_bank,
19+
class_="bg-success",
20+
full_screen=True,
21+
# showcase_layout=x.ui._valuebox.showcase_left_center(),
22+
id="valuebox1",
23+
),
24+
x.ui.value_box(
25+
"KPI Title",
26+
ui.h1(ui.HTML("$1 <i>Billion</i> Dollars")),
27+
ui.span(arrow_up, " 30% VS PREVIOUS 30 DAYS"),
28+
showcase=piggy_bank,
29+
class_="bg-success",
30+
full_screen=True,
31+
showcase_layout=x.ui.showcase_top_right(width="70%"),
32+
id="valuebox2",
33+
),
34+
)
35+
36+
37+
app = App(app_ui, server=None)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import pytest
2+
from conftest import ShinyAppProc
3+
from controls import ValueBox
4+
from playwright.sync_api import Page
5+
6+
7+
@pytest.mark.parametrize("value_box_id", ["valuebox1", "valuebox2"])
8+
def test_valuebox(page: Page, local_app: ShinyAppProc, value_box_id: str) -> None:
9+
page.goto(local_app.url)
10+
11+
value_box = ValueBox(page, value_box_id)
12+
value_box.expect_height(None)
13+
value_box.expect_title("KPI Title")
14+
value_box.expect_full_screen(False)
15+
value_box.open_full_screen()
16+
value_box.expect_full_screen(True)
17+
value_box.expect_body(["$1 Billion Dollars", "30% VS PREVIOUS 30 DAYS"])
18+
value_box.close_full_screen()
19+
value_box.expect_full_screen(False)

0 commit comments

Comments
 (0)