From 2854170be16191895ea7138adbc82d24805abb32 Mon Sep 17 00:00:00 2001 From: Karan Gathani Date: Tue, 19 Mar 2024 10:10:53 -0700 Subject: [PATCH 1/8] Add kitchensink tests for valuebox --- tests/playwright/controls.py | 192 ++++++++++++++++-- .../components/value_box/kitchensink/app.py | 50 +++++ .../value_box/kitchensink/test_valuebox.py | 94 +++++++++ 3 files changed, 318 insertions(+), 18 deletions(-) create mode 100644 tests/playwright/shiny/components/value_box/kitchensink/app.py create mode 100644 tests/playwright/shiny/components/value_box/kitchensink/test_valuebox.py diff --git a/tests/playwright/controls.py b/tests/playwright/controls.py index 9a92db482..c2ee8ec82 100644 --- a/tests/playwright/controls.py +++ b/tests/playwright/controls.py @@ -2552,17 +2552,36 @@ def toggle(self, *, timeout: Timeout = None) -> None: class _CardBodyP(_InputBaseP, Protocol): + """ + Represents the body of a card control. + + Parameters + ---------- + loc_body + The locator for the body element of the card control. + """ + loc_body: Locator class _CardBodyM: + """Represents a card body element with additional methods for testing.""" + def expect_body( self: _CardBodyP, text: PatternOrStr | list[PatternOrStr], *, timeout: Timeout = None, ) -> None: - """Note: If testing against multiple elements, text should be an array""" + """Expect the card body element to have the specified text. + + Parameters + ---------- + text + The expected text or a list of expected texts. + timeout + The maximum time to wait for the text to appear. Defaults to None. + """ playwright_expect(self.loc).to_have_text( text, timeout=timeout, @@ -2570,16 +2589,39 @@ def expect_body( class _CardFooterLayoutP(_InputBaseP, Protocol): + """ + Represents the layout of the footer in a card. + + Parameters + ---------- + loc_footer + The locator for the footer element. + """ + loc_footer: Locator class _CardFooterM: + """ + Represents the footer section of a card. + """ + def expect_footer( self: _CardFooterLayoutP, text: PatternOrStr, *, timeout: Timeout = None, ) -> None: + """ + Asserts that the footer section of the card has the expected text. + + Parameters + ---------- + text + The expected text in the footer section. + timeout + The maximum time to wait for the footer text to appear. Defaults to None. + """ playwright_expect(self.loc_footer).to_have_text( text, timeout=timeout, @@ -2587,15 +2629,40 @@ def expect_footer( class _CardFullScreenLayoutP(_OutputBaseP, Protocol): + """ + Represents a card full-screen layout for the Playwright controls. + + Parameters + ---------- + loc_title + The locator for the title element. + _loc_fullscreen + The locator for the full-screen element. + _loc_close_button + The locator for the close button element. + """ + loc_title: Locator _loc_fullscreen: Locator _loc_close_button: Locator class _CardFullScreenM: + """ + Represents a class for managing full screen functionality of a card. + """ + def open_full_screen( self: _CardFullScreenLayoutP, *, timeout: Timeout = None ) -> None: + """ + Opens the card in full screen mode. + + Parameters + ---------- + timeout + The maximum time to wait for the card to open in full screen mode. Defaults to None. + """ self.loc_title.hover(timeout=timeout) self._loc_fullscreen.wait_for(state="visible", timeout=timeout) self._loc_fullscreen.click(timeout=timeout) @@ -2603,11 +2670,29 @@ def open_full_screen( def close_full_screen( self: _CardFullScreenLayoutP, *, timeout: Timeout = None ) -> None: + """ + Closes the card from full screen mode. + + Parameters + ---------- + timeout + The maximum time to wait for the card to close from full screen mode. Defaults to None. + """ self._loc_close_button.click(timeout=timeout) def expect_full_screen( self: _CardFullScreenLayoutP, open: bool, *, timeout: Timeout = None ) -> None: + """ + Verifies if the card is expected to be in full screen mode. + + Parameters + ---------- + open + True if the card is expected to be in full screen mode, False otherwise. + timeout + The maximum time to wait for the verification. Defaults to None. + """ playwright_expect(self._loc_close_button).to_have_count( int(open), timeout=timeout ) @@ -2618,24 +2703,41 @@ class ValueBox( _CardFullScreenM, _InputWithContainer, ): - # title: TagChild, - # value: TagChild, - # *args: TagChild | TagAttrs, - # showcase: TagChild = None, - # showcase_layout: ((TagChild, Tag) -> CardItem) | None = None, - # full_screen: bool = False, - # theme_color: str | None = "primary", - # height: CssUnit | None = None, - # max_height: CssUnit | None = None, - # fill: bool = True, - # class_: str | None = None, - # **kwargs: TagAttrValue + + loc: Locator + """ + Locator for the value box's value + """ + loc_showcase: Locator + """ + Locator for the value box showcase + """ + loc_title: Locator + """ + Locator for the value box title + """ + loc_body: Locator + """ + Locator for the value box body + """ + def __init__(self, page: Page, id: str) -> None: + """ + Initializes a new instance of the ValueBox class. + + Parameters + ---------- + page + The Playwright page object. + id + The ID of the value box. + + """ super().__init__( page, id=id, loc_container=f"div#{id}.bslib-value-box", - loc="> div > .value-box-grid", + loc="> div.card-body > .value-box-grid, > div.card-body", ) value_box_grid = self.loc self.loc_showcase = value_box_grid.locator("> .value-box-showcase") @@ -2657,7 +2759,17 @@ def __init__(self, page: Page, id: str) -> None: ) def expect_height(self, value: StyleValue, *, timeout: Timeout = None) -> None: - expect_to_have_style(self.loc_container, "height", value, timeout=timeout) + """ + Expects the value box to have a specific height. + + Parameters + ---------- + value + The expected height value. + timeout + The maximum time to wait for the expectation to pass. Defaults to None. + """ + expect_to_have_style(self.loc_container, "max-height", value, timeout=timeout) def expect_title( self, @@ -2665,6 +2777,17 @@ def expect_title( *, timeout: Timeout = None, ) -> None: + """ + Expects the value box title to have a specific text. + + Parameters + ---------- + text + The expected text pattern or string. + timeout + The maximum time to wait for the expectation to pass. Defaults to None. + + """ playwright_expect(self.loc_title).to_have_text( text, timeout=timeout, @@ -2676,6 +2799,16 @@ def expect_value( *, timeout: Timeout = None, ) -> None: + """ + Expects the value box value to have a specific text. + + Parameters + ---------- + text + The expected text pattern or string. + timeout + The maximum time to wait for the expectation to pass. Defaults to None. + """ playwright_expect(self.loc).to_have_text( text, timeout=timeout, @@ -2687,15 +2820,38 @@ def expect_body( *, timeout: Timeout = None, ) -> None: + """ + Expects the value box body to have specific text. + + Parameters + ---------- + text + The expected text pattern or list of patterns/strings. + timeout + The maximum time to wait for the expectation to pass. Defaults to None. + """ """Note: If testing against multiple elements, text should be an array""" playwright_expect(self.loc_body).to_have_text( text, timeout=timeout, ) - # hard to test since it can be customized by user - # def expect_showcase_layout(self, layout, *, timeout: Timeout = None) -> None: - # raise NotImplementedError() + def expect_full_screen_available( + self, available: bool, *, timeout: Timeout = None + ) -> None: + """ + Expects the value box to be available for full screen mode. + + Parameters + ---------- + available + True if the value box is expected to be available for full screen mode, False otherwise. + timeout + The maximum time to wait for the expectation to pass. Defaults to None. + """ + playwright_expect(self._loc_fullscreen).to_have_count( + int(available), timeout=timeout + ) class Card(_WidthLocM, _CardFooterM, _CardBodyM, _CardFullScreenM, _InputWithContainer): diff --git a/tests/playwright/shiny/components/value_box/kitchensink/app.py b/tests/playwright/shiny/components/value_box/kitchensink/app.py new file mode 100644 index 000000000..a94e9e34f --- /dev/null +++ b/tests/playwright/shiny/components/value_box/kitchensink/app.py @@ -0,0 +1,50 @@ +from shiny import App, ui +import faicons as fa + + +app_ui = ui.page_fluid( + ui.value_box( + ui.span("Red Color theme w/ Fullscreen"), + ui.h1("Showcase top right"), + ui.span("Inside the fullscreen"), + showcase=fa.icon_svg("faucet-drip"), + showcase_layout=ui.showcase_top_right(), + theme="red", + full_screen=True, + id="valuebox1", + ), + ui.value_box( + "Primary theme w/o Fullscreen", + ui.h5(ui.HTML("Showcase left center")), + showcase=fa.icon_svg("faucet-drip"), + showcase_layout=ui.showcase_left_center(), + theme="primary", + full_screen=False, + id="valuebox2", + ), + ui.value_box( + ui.span("No theme w/ Fullscreen"), + ui.h3("Showcase bottom"), + showcase=fa.icon_svg("faucet-drip"), + showcase_layout=ui.showcase_bottom(), + full_screen=True, + id="valuebox3", + ), + ui.value_box( + "No showcase - w/o Fullscreen (default)", + "No theme - only defaults", + id="valuebox4", + ), + ui.value_box( + "No showcase w/ showcase layout", + "Red text - fill is False", + max_height="500px", + showcase_layout=ui.showcase_left_center(), + fill=False, + theme="text-red", + id="valuebox5", + ), +) + + +app = App(app_ui, server=None) diff --git a/tests/playwright/shiny/components/value_box/kitchensink/test_valuebox.py b/tests/playwright/shiny/components/value_box/kitchensink/test_valuebox.py new file mode 100644 index 000000000..e38ea7a2e --- /dev/null +++ b/tests/playwright/shiny/components/value_box/kitchensink/test_valuebox.py @@ -0,0 +1,94 @@ +from conftest import ShinyAppProc +from controls import ValueBox, expect_to_have_class +from playwright.sync_api import Page + + +def get_value_box_bg_color(value_box: ValueBox) -> str: + value_box_bg_color = value_box.loc_container.evaluate( + "el => window.getComputedStyle(el).getPropertyValue('background-color');" + ) + return value_box_bg_color + +def get_value_box_fg_color(value_box: ValueBox) -> str: + value_box_fg_color = value_box.loc_container.evaluate( + "el => window.getComputedStyle(el).getPropertyValue('color');" + ) + return value_box_fg_color + +def get_title_tag_name(value_box: ValueBox) -> str: + title_tag_name = ( + value_box.loc_title.locator("*") + .nth(0) + .evaluate("el => el.tagName.toLowerCase()") + ) + return title_tag_name + + +def get_value_tag_name(value_box: ValueBox) -> str: + value_tag_name = ( + value_box.loc.locator("*").nth(0).evaluate("el => el.tagName.toLowerCase()") + ) + return value_tag_name + + +def test_valuebox(page: Page, local_app: ShinyAppProc) -> None: + page.goto(local_app.url) + + value_box1 = ValueBox(page, "valuebox1") + value_box1.expect_height(None) + # verify showcase layout uses 'showcase_top_right()' + expect_to_have_class(value_box1.loc_container, "showcase-top-right") + assert get_title_tag_name(value_box1) == "span" + value_box1.expect_title("Red Color theme w/ Fullscreen") + value_box1.expect_value("Showcase top right") + assert get_value_tag_name(value_box1) == "h1" + assert get_value_box_bg_color(value_box1) == "rgb(193, 0, 0)" + assert get_value_box_fg_color(value_box1) == "rgb(255, 255, 255)" + value_box1.expect_full_screen_available(True) + value_box1.expect_full_screen(False) + value_box1.open_full_screen() + value_box1.expect_full_screen(True) + value_box1.expect_body(["Inside the fullscreen"]) + value_box1.close_full_screen() + value_box1.expect_full_screen(False) + + value_box2 = ValueBox(page, "valuebox2") + value_box2.expect_height(None) + # verify showcase layout uses 'showcase_left_center()' + expect_to_have_class(value_box2.loc_container, "showcase-left-center") + assert get_title_tag_name(value_box2) == "p" + value_box2.expect_title("Primary theme w/o Fullscreen") + value_box2.expect_value("Showcase left center") + assert get_value_tag_name(value_box2) == "h5" + assert get_value_box_bg_color(value_box2) == "rgb(0, 123, 194)" + value_box2.expect_full_screen_available(False) + + value_box3 = ValueBox(page, "valuebox3") + value_box3.expect_height(None) + # verify showcase layout uses 'showcase_bottom()' + expect_to_have_class(value_box3.loc_container, "showcase-bottom") + assert get_title_tag_name(value_box3) == "span" + value_box3.expect_title("No theme w/ Fullscreen") + value_box3.expect_value("Showcase bottom") + assert get_value_tag_name(value_box3) == "h3" + value_box3.expect_full_screen_available(True) + assert get_value_box_bg_color(value_box3) == "rgb(255, 255, 255)" + + value_box4 = ValueBox(page, "valuebox4") + value_box4.expect_height(None) + value_box4.expect_title("No showcase - w/o Fullscreen (default)") + value_box4.expect_value("No theme - only defaults") + assert get_title_tag_name(value_box4) == "p" + assert get_title_tag_name(value_box4) == "p" + value_box4.expect_full_screen_available(False) + assert get_value_box_bg_color(value_box4) == "rgb(255, 255, 255)" + + value_box5 = ValueBox(page, "valuebox5") + value_box5.expect_height("500px") + assert get_title_tag_name(value_box5) == "p" + value_box5.expect_title("No showcase w/ showcase layout") + value_box5.expect_value("Red text - fill is False") + assert get_value_tag_name(value_box5) == "p" + value_box5.expect_full_screen_available(False) + assert get_value_box_bg_color(value_box5) == "rgb(255, 255, 255)" + assert get_value_box_fg_color(value_box5) == "rgb(193, 0, 0)" From c1190a1cb6c3cac73870edf7c0ecf4d6703008e4 Mon Sep 17 00:00:00 2001 From: Karan Gathani Date: Tue, 19 Mar 2024 10:46:50 -0700 Subject: [PATCH 2/8] linting issues --- .../shiny/components/value_box/kitchensink/test_valuebox.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/playwright/shiny/components/value_box/kitchensink/test_valuebox.py b/tests/playwright/shiny/components/value_box/kitchensink/test_valuebox.py index e38ea7a2e..0387e071a 100644 --- a/tests/playwright/shiny/components/value_box/kitchensink/test_valuebox.py +++ b/tests/playwright/shiny/components/value_box/kitchensink/test_valuebox.py @@ -9,12 +9,14 @@ def get_value_box_bg_color(value_box: ValueBox) -> str: ) return value_box_bg_color + def get_value_box_fg_color(value_box: ValueBox) -> str: value_box_fg_color = value_box.loc_container.evaluate( "el => window.getComputedStyle(el).getPropertyValue('color');" ) return value_box_fg_color + def get_title_tag_name(value_box: ValueBox) -> str: title_tag_name = ( value_box.loc_title.locator("*") From bcb7b7fae85671fa2df71133705419a512f70cec Mon Sep 17 00:00:00 2001 From: Karan Gathani Date: Tue, 19 Mar 2024 11:10:54 -0700 Subject: [PATCH 3/8] fix isort --- tests/playwright/shiny/components/value_box/kitchensink/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/playwright/shiny/components/value_box/kitchensink/app.py b/tests/playwright/shiny/components/value_box/kitchensink/app.py index a94e9e34f..cde97329f 100644 --- a/tests/playwright/shiny/components/value_box/kitchensink/app.py +++ b/tests/playwright/shiny/components/value_box/kitchensink/app.py @@ -1,6 +1,6 @@ -from shiny import App, ui import faicons as fa +from shiny import App, ui app_ui = ui.page_fluid( ui.value_box( From 41aaaeee6d622a74fd7f5d192e4ba4d9c5a78413 Mon Sep 17 00:00:00 2001 From: Karan Gathani Date: Tue, 19 Mar 2024 11:45:57 -0700 Subject: [PATCH 4/8] remove faicon dependency --- .../shiny/components/value_box/kitchensink/app.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/playwright/shiny/components/value_box/kitchensink/app.py b/tests/playwright/shiny/components/value_box/kitchensink/app.py index cde97329f..3cc4989d4 100644 --- a/tests/playwright/shiny/components/value_box/kitchensink/app.py +++ b/tests/playwright/shiny/components/value_box/kitchensink/app.py @@ -1,13 +1,15 @@ -import faicons as fa - from shiny import App, ui +piggy_bank = ui.HTML( + '' +) + app_ui = ui.page_fluid( ui.value_box( ui.span("Red Color theme w/ Fullscreen"), ui.h1("Showcase top right"), ui.span("Inside the fullscreen"), - showcase=fa.icon_svg("faucet-drip"), + showcase=piggy_bank, showcase_layout=ui.showcase_top_right(), theme="red", full_screen=True, @@ -16,7 +18,7 @@ ui.value_box( "Primary theme w/o Fullscreen", ui.h5(ui.HTML("Showcase left center")), - showcase=fa.icon_svg("faucet-drip"), + showcase=piggy_bank, showcase_layout=ui.showcase_left_center(), theme="primary", full_screen=False, @@ -25,7 +27,7 @@ ui.value_box( ui.span("No theme w/ Fullscreen"), ui.h3("Showcase bottom"), - showcase=fa.icon_svg("faucet-drip"), + showcase=piggy_bank, showcase_layout=ui.showcase_bottom(), full_screen=True, id="valuebox3", From 5f6df4e826abba5c1bedc21eb47185580c0252f6 Mon Sep 17 00:00:00 2001 From: Karan Gathani Date: Thu, 21 Mar 2024 09:07:09 -0700 Subject: [PATCH 5/8] Address code review comments --- tests/playwright/controls.py | 34 ++++++++----------- .../value_box/kitchensink/test_valuebox.py | 11 ++++++ 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/tests/playwright/controls.py b/tests/playwright/controls.py index c2ee8ec82..bca4fe124 100644 --- a/tests/playwright/controls.py +++ b/tests/playwright/controls.py @@ -2554,14 +2554,12 @@ def toggle(self, *, timeout: Timeout = None) -> None: class _CardBodyP(_InputBaseP, Protocol): """ Represents the body of a card control. - - Parameters - ---------- - loc_body - The locator for the body element of the card control. """ loc_body: Locator + """ + The locator for the body element of the card control. + """ class _CardBodyM: @@ -2591,14 +2589,12 @@ def expect_body( class _CardFooterLayoutP(_InputBaseP, Protocol): """ Represents the layout of the footer in a card. - - Parameters - ---------- - loc_footer - The locator for the footer element. """ loc_footer: Locator + """ + The locator for the footer element. + """ class _CardFooterM: @@ -2631,20 +2627,20 @@ def expect_footer( class _CardFullScreenLayoutP(_OutputBaseP, Protocol): """ Represents a card full-screen layout for the Playwright controls. - - Parameters - ---------- - loc_title - The locator for the title element. - _loc_fullscreen - The locator for the full-screen element. - _loc_close_button - The locator for the close button element. """ loc_title: Locator + """ + The locator for the title element. + """ _loc_fullscreen: Locator + """ + The locator for the full-screen element. + """ _loc_close_button: Locator + """ + The locator for the close button element. + """ class _CardFullScreenM: diff --git a/tests/playwright/shiny/components/value_box/kitchensink/test_valuebox.py b/tests/playwright/shiny/components/value_box/kitchensink/test_valuebox.py index 0387e071a..9f0b759da 100644 --- a/tests/playwright/shiny/components/value_box/kitchensink/test_valuebox.py +++ b/tests/playwright/shiny/components/value_box/kitchensink/test_valuebox.py @@ -33,6 +33,17 @@ def get_value_tag_name(value_box: ValueBox) -> str: return value_tag_name +""" +For each value box we want to test +Layout and Positioning(e.g., showcase-top-right, showcase-left-center, showcase-bottom). +Title and Value Elements: The tests assert the tag names used for the title and value elements within each ValueBox (e.g., ,

,

,

,

). +Fullscreen Availability and State: The tests check whether the fullscreen feature is available for a particular ValueBox and verify its initial state (fullscreen or not). For ValueBoxes with fullscreen support, the tests open and close the fullscreen mode and assert the expected behavior. +Background and Foreground Colors: The tests assert the background and foreground (text) colors applied to the ValueBox components. +Height: In some cases, the tests expect a specific height value for the ValueBox. +Content: The tests verify the expected title and value text displayed within each ValueBox. Additionally, for ValueBoxes with fullscreen support, the tests check the content displayed in fullscreen mode. +""" + + def test_valuebox(page: Page, local_app: ShinyAppProc) -> None: page.goto(local_app.url) From 2b03b2b7af73b6b28dca7a1746a008445334a2b8 Mon Sep 17 00:00:00 2001 From: Karan Gathani Date: Thu, 21 Mar 2024 14:25:58 -0700 Subject: [PATCH 6/8] Address code review suggestions --- tests/playwright/controls.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/playwright/controls.py b/tests/playwright/controls.py index bca4fe124..33bfc8998 100644 --- a/tests/playwright/controls.py +++ b/tests/playwright/controls.py @@ -2699,7 +2699,9 @@ class ValueBox( _CardFullScreenM, _InputWithContainer, ): - + """ + ValueBox control for shiny.ui.value_box - https://shiny.posit.co/py/api/core/ui.value_box.html + """ loc: Locator """ Locator for the value box's value @@ -2823,10 +2825,10 @@ def expect_body( ---------- text The expected text pattern or list of patterns/strings. + Note: If testing against multiple elements, text should be an array timeout The maximum time to wait for the expectation to pass. Defaults to None. """ - """Note: If testing against multiple elements, text should be an array""" playwright_expect(self.loc_body).to_have_text( text, timeout=timeout, From 5aeaa2e83e4b23520e9ea7ca4907c24acaa6fb65 Mon Sep 17 00:00:00 2001 From: Karan Gathani Date: Thu, 21 Mar 2024 14:29:08 -0700 Subject: [PATCH 7/8] linting issues --- tests/playwright/controls.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/playwright/controls.py b/tests/playwright/controls.py index 33bfc8998..72ebdb3b6 100644 --- a/tests/playwright/controls.py +++ b/tests/playwright/controls.py @@ -2702,6 +2702,7 @@ class ValueBox( """ ValueBox control for shiny.ui.value_box - https://shiny.posit.co/py/api/core/ui.value_box.html """ + loc: Locator """ Locator for the value box's value From 35289502e25ad98dc65401b0c72f6c854aa62c20 Mon Sep 17 00:00:00 2001 From: Karan Gathani Date: Fri, 22 Mar 2024 12:44:21 -0700 Subject: [PATCH 8/8] Move legacy valuebox tests to smoke dir --- .../kitchensink/{test_valuebox.py => test_valuebox_ks.py} | 0 tests/playwright/shiny/components/value_box/{ => smoke}/app.py | 0 .../shiny/components/value_box/{ => smoke}/test_valuebox.py | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename tests/playwright/shiny/components/value_box/kitchensink/{test_valuebox.py => test_valuebox_ks.py} (100%) rename tests/playwright/shiny/components/value_box/{ => smoke}/app.py (100%) rename tests/playwright/shiny/components/value_box/{ => smoke}/test_valuebox.py (100%) diff --git a/tests/playwright/shiny/components/value_box/kitchensink/test_valuebox.py b/tests/playwright/shiny/components/value_box/kitchensink/test_valuebox_ks.py similarity index 100% rename from tests/playwright/shiny/components/value_box/kitchensink/test_valuebox.py rename to tests/playwright/shiny/components/value_box/kitchensink/test_valuebox_ks.py diff --git a/tests/playwright/shiny/components/value_box/app.py b/tests/playwright/shiny/components/value_box/smoke/app.py similarity index 100% rename from tests/playwright/shiny/components/value_box/app.py rename to tests/playwright/shiny/components/value_box/smoke/app.py diff --git a/tests/playwright/shiny/components/value_box/test_valuebox.py b/tests/playwright/shiny/components/value_box/smoke/test_valuebox.py similarity index 100% rename from tests/playwright/shiny/components/value_box/test_valuebox.py rename to tests/playwright/shiny/components/value_box/smoke/test_valuebox.py