From 229dbebf08dcb2ec8d891d81d12a5f074fd80f74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Thu, 16 Jan 2025 11:58:44 +0100 Subject: [PATCH 01/28] test: Stabilize flaky UI test --- panel/tests/ui/chat/test_chat_interface_ui.py | 1 + panel/tests/ui/layout/test_feed.py | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/panel/tests/ui/chat/test_chat_interface_ui.py b/panel/tests/ui/chat/test_chat_interface_ui.py index b601323be5..0cc009b720 100644 --- a/panel/tests/ui/chat/test_chat_interface_ui.py +++ b/panel/tests/ui/chat/test_chat_interface_ui.py @@ -91,6 +91,7 @@ def edit_callback(content, index, instance): # find the input field and type new message chat_input = page.locator(".bk-input").first + page.wait_for_timeout(200) chat_input.fill("Edited") # click enter diff --git a/panel/tests/ui/layout/test_feed.py b/panel/tests/ui/layout/test_feed.py index ae62b4980f..3a0bdd9409 100644 --- a/panel/tests/ui/layout/test_feed.py +++ b/panel/tests/ui/layout/test_feed.py @@ -129,9 +129,11 @@ def test_feed_scroll_to_latest_within_limit(page): feed.scroll_to_latest(scroll_limit=100) - wait_until(lambda: feed_el.evaluate( - '(el) => el.scrollHeight - el.scrollTop - el.clientHeight' - ) == 0, page) + def test_case(): + cmd = '(el) => el.scrollHeight - el.scrollTop - el.clientHeight' + assert feed_el.evaluate(cmd) == 0 + + wait_until(test_case, page) def test_feed_view_scroll_button(page): From 256f430752acf61c3d7aa2cd70baee279bb4ab35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Thu, 16 Jan 2025 15:08:08 +0100 Subject: [PATCH 02/28] Add expect to test_player_visible_loop_options --- panel/tests/ui/widgets/test_player.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/panel/tests/ui/widgets/test_player.py b/panel/tests/ui/widgets/test_player.py index 44616607e9..c37b5b5258 100644 --- a/panel/tests/ui/widgets/test_player.py +++ b/panel/tests/ui/widgets/test_player.py @@ -104,14 +104,14 @@ def test_player_visible_loop_options(page): player = Player(visible_loop_options=["loop", "once"]) serve_component(page, player) - assert page.is_visible(".loop") - assert page.is_visible(".once") - assert not page.is_visible(".reflect") + expect(page.locator(".loop")).to_be_visible() + expect(page.locator(".once")).to_be_visible() + expect(page.locator(".reflect")).to_be_hidden() player.visible_loop_options = ["reflect"] expect(page.locator(".reflect")).to_be_visible() - assert not page.is_visible(".loop") - assert not page.is_visible(".once") + expect(page.locator(".loop")).to_be_hidden() + expect(page.locator(".once")).to_be_hidden() def test_player_scale_buttons(page): From a0779d13ce2069caa315ed4f824707e155670670 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Thu, 16 Jan 2025 16:29:34 +0100 Subject: [PATCH 03/28] Try to recreate the feed_el --- panel/tests/ui/layout/test_feed.py | 1 + 1 file changed, 1 insertion(+) diff --git a/panel/tests/ui/layout/test_feed.py b/panel/tests/ui/layout/test_feed.py index 3a0bdd9409..6dea5bc2b1 100644 --- a/panel/tests/ui/layout/test_feed.py +++ b/panel/tests/ui/layout/test_feed.py @@ -130,6 +130,7 @@ def test_feed_scroll_to_latest_within_limit(page): feed.scroll_to_latest(scroll_limit=100) def test_case(): + feed_el = page.locator(".bk-panel-models-feed-Feed") cmd = '(el) => el.scrollHeight - el.scrollTop - el.clientHeight' assert feed_el.evaluate(cmd) == 0 From 52e1cbaefd86566136c28e0c9bb231ff53f2d2d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Thu, 16 Jan 2025 17:02:25 +0100 Subject: [PATCH 04/28] Fix to eager int conversion of empty string --- panel/tests/ui/layout/test_feed.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/panel/tests/ui/layout/test_feed.py b/panel/tests/ui/layout/test_feed.py index 6dea5bc2b1..794255c284 100644 --- a/panel/tests/ui/layout/test_feed.py +++ b/panel/tests/ui/layout/test_feed.py @@ -49,7 +49,13 @@ def test_feed_view_latest(page): # Assert scroll is not at 0 (view_latest) wait_until(lambda: feed_el.evaluate('(el) => el.scrollTop') > 0, page) - wait_until(lambda: int(page.locator('pre').last.inner_text()) > 0.9 * ITEMS, page) + def case(): + inner_text = page.locator('pre').last.inner_text() + if inner_text == "": + return False + assert int(inner_text) > 0.9 * ITEMS + + wait_until(case, page) def test_feed_view_scroll_to_latest(page): @@ -129,12 +135,12 @@ def test_feed_scroll_to_latest_within_limit(page): feed.scroll_to_latest(scroll_limit=100) - def test_case(): + def case(): feed_el = page.locator(".bk-panel-models-feed-Feed") cmd = '(el) => el.scrollHeight - el.scrollTop - el.clientHeight' assert feed_el.evaluate(cmd) == 0 - wait_until(test_case, page) + wait_until(case, page) def test_feed_view_scroll_button(page): From ff67b6ca4a5f00a7bb4ede9548aebecc1e05deb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Thu, 16 Jan 2025 19:29:52 +0100 Subject: [PATCH 05/28] Add more to scroll_to_latest --- panel/tests/ui/layout/test_feed.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/panel/tests/ui/layout/test_feed.py b/panel/tests/ui/layout/test_feed.py index 794255c284..b02e2f2132 100644 --- a/panel/tests/ui/layout/test_feed.py +++ b/panel/tests/ui/layout/test_feed.py @@ -133,10 +133,9 @@ def test_feed_scroll_to_latest_within_limit(page): # assert auto scroll works; i.e. distance from bottom is 0 feed.append(Spacer(styles=dict(background='yellow'), width=200, height=200)) - feed.scroll_to_latest(scroll_limit=100) + feed.scroll_to_latest(scroll_limit=1000) def case(): - feed_el = page.locator(".bk-panel-models-feed-Feed") cmd = '(el) => el.scrollHeight - el.scrollTop - el.clientHeight' assert feed_el.evaluate(cmd) == 0 From 832083e22003f1098cc8923f9dbb23d3e4a24f1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Thu, 16 Jan 2025 19:30:34 +0100 Subject: [PATCH 06/28] Try to improve test_tabulator_patching_and_styling --- panel/tests/ui/widgets/test_tabulator.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/panel/tests/ui/widgets/test_tabulator.py b/panel/tests/ui/widgets/test_tabulator.py index 18f51f6ae6..cbc16063a9 100644 --- a/panel/tests/ui/widgets/test_tabulator.py +++ b/panel/tests/ui/widgets/test_tabulator.py @@ -2432,6 +2432,8 @@ def test_tabulator_patching_and_styling(page, df_mixed): serve_component(page, widget) + expect(page.locator('.tabulator-cell')).not_to_have_count(0) + # Changing the highest value in the int column should # update the style so that this cell gets a yellow background widget.patch({'int': [(0, 100)]}, as_index=False) From b8cbcf7246afea4e3cf7e0c63ab7fb7428d05838 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Thu, 16 Jan 2025 20:51:17 +0100 Subject: [PATCH 07/28] Updates to test-feed --- panel/tests/ui/layout/test_feed.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/panel/tests/ui/layout/test_feed.py b/panel/tests/ui/layout/test_feed.py index b02e2f2132..8fe73e3adc 100644 --- a/panel/tests/ui/layout/test_feed.py +++ b/panel/tests/ui/layout/test_feed.py @@ -49,13 +49,7 @@ def test_feed_view_latest(page): # Assert scroll is not at 0 (view_latest) wait_until(lambda: feed_el.evaluate('(el) => el.scrollTop') > 0, page) - def case(): - inner_text = page.locator('pre').last.inner_text() - if inner_text == "": - return False - assert int(inner_text) > 0.9 * ITEMS - - wait_until(case, page) + wait_until(lambda: int(page.locator('pre').last.inner_text() or 0) > 0.9 * ITEMS, page) def test_feed_view_scroll_to_latest(page): @@ -158,7 +152,7 @@ def test_feed_view_scroll_button(page): # Assert scroll is not at 0 (view_latest) wait_until(lambda: feed_el.evaluate('(el) => el.scrollTop') > 0, page) - wait_until(lambda: int(page.locator('pre').last.inner_text()) > 50, page) + wait_until(lambda: int(page.locator('pre').last.inner_text() or 0) > 50, page) def test_feed_dynamic_objects(page): feed = Feed(height=250, load_buffer=10) From 89941d81cd8f28f2f39784caacb1a4ad8bf7737c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Thu, 16 Jan 2025 20:51:33 +0100 Subject: [PATCH 08/28] Update to test_tabulator --- panel/tests/ui/widgets/test_tabulator.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/panel/tests/ui/widgets/test_tabulator.py b/panel/tests/ui/widgets/test_tabulator.py index cbc16063a9..51bcc3d306 100644 --- a/panel/tests/ui/widgets/test_tabulator.py +++ b/panel/tests/ui/widgets/test_tabulator.py @@ -2891,6 +2891,7 @@ def test_tabulator_edit_event_and_header_filters_same_column_pagination(page, pa header = page.locator('input[type="search"]') header.click() + page.wait_for_timeout(200) header.fill('B') header.press('Enter') @@ -2898,6 +2899,7 @@ def test_tabulator_edit_event_and_header_filters_same_column_pagination(page, pa cell = page.locator('text="B"').first cell.click() + page.wait_for_timeout(200) editable_cell = page.locator('input[type="text"]') editable_cell.fill("Q") editable_cell.press('Enter') @@ -2917,6 +2919,7 @@ def test_tabulator_edit_event_and_header_filters_same_column_pagination(page, pa # Edit a cell in the filtered column, from B to X cell = page.locator('text="B"').nth(1) cell.click() + page.wait_for_timeout(200) editable_cell = page.locator('input[type="text"]') editable_cell.fill("X") editable_cell.press('Enter') @@ -2930,6 +2933,7 @@ def test_tabulator_edit_event_and_header_filters_same_column_pagination(page, pa # In the same column, edit X to Y cell = page.locator('text="X"') cell.click() + page.wait_for_timeout(200) editable_cell = page.locator('input[type="text"]') editable_cell.fill("Y") editable_cell.press('Enter') @@ -2942,6 +2946,7 @@ def test_tabulator_edit_event_and_header_filters_same_column_pagination(page, pa # Edit the last B value found in that column, from B to Z cell = page.locator('text="B"') cell.click() + page.wait_for_timeout(200) editable_cell = page.locator('input[type="text"]') editable_cell.fill("Z") editable_cell.press('Enter') @@ -3496,6 +3501,7 @@ def test_tabulator_sorter_default_number(page): widget = Tabulator(df, sorters=[{"field": "x", "dir": "desc"}]) serve_component(page, widget) + expect(page.locator('.tabulator-cell')).to_have_count(0) df2 = pd.DataFrame({'x': [0, 96, 116]}) widget.value = df2 From a778d3b3f09abf8e82249306701e5a24f58057a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Thu, 16 Jan 2025 20:53:19 +0100 Subject: [PATCH 09/28] Try to remove global reruns --- pixi.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pixi.toml b/pixi.toml index 8c36fb47b7..9bf73b7dfe 100644 --- a/pixi.toml +++ b/pixi.toml @@ -180,7 +180,7 @@ packaging = "*" _install-ui = 'playwright install chromium' [feature.test-ui.tasks.test-ui] -cmd = 'pytest panel/tests/ui --ui --browser chromium -n logical --dist loadgroup --reruns 3 --reruns-delay 10' +cmd = 'pytest panel/tests/ui --ui --browser chromium -n logical --dist loadgroup' depends-on = ["_install-ui"] # ============================================= From 7cb5b2291d21080bd15b3583d083dfc1b528dcd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Fri, 17 Jan 2025 08:34:15 +0100 Subject: [PATCH 10/28] player test --- panel/tests/ui/widgets/test_player.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/panel/tests/ui/widgets/test_player.py b/panel/tests/ui/widgets/test_player.py index c37b5b5258..5ee09edf3e 100644 --- a/panel/tests/ui/widgets/test_player.py +++ b/panel/tests/ui/widgets/test_player.py @@ -56,7 +56,7 @@ def test_name(page): player = Player(name='test') serve_component(page, player) - assert page.is_visible('label') + expect(page.locator('label')).to_have_count(3) assert page.query_selector('.pn-player-value') is None name = page.locator('.pn-player-title:has-text("test")') @@ -75,11 +75,12 @@ def test_name_and_show_value(page): player = Player(name='test', show_value=True) serve_component(page, player) - assert page.is_visible('label') + expect(page.locator('label')).to_have_count(3) assert page.query_selector('.pn-player-value') is not None name = page.locator('.pn-player-title:has-text("test")') expect(name).to_have_count(1) + def test_player_visible_buttons(page): player = Player(visible_buttons=["play", "pause"]) serve_component(page, player) From f4055c8aebcebc27991f07c2a3620418234d208a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Fri, 17 Jan 2025 08:34:58 +0100 Subject: [PATCH 11/28] test tabulator --- panel/tests/ui/widgets/test_tabulator.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/panel/tests/ui/widgets/test_tabulator.py b/panel/tests/ui/widgets/test_tabulator.py index 51bcc3d306..39fa475f0e 100644 --- a/panel/tests/ui/widgets/test_tabulator.py +++ b/panel/tests/ui/widgets/test_tabulator.py @@ -586,6 +586,7 @@ def test_tabulator_editors_panel_date(page, df_mixed): cell_edit = page.locator('input[type="date"]') new_date = "1980-01-01" cell_edit.fill(new_date) + page.wait_for_timeout(100) # Need to Enter to validate the change page.locator('input[type="date"]').press('Enter') expect(page.locator(f'text="{new_date}"')).to_have_count(1) @@ -597,6 +598,7 @@ def test_tabulator_editors_panel_date(page, df_mixed): cell_edit = page.locator('input[type="date"]') new_date2 = "1990-01-01" cell_edit.fill(new_date2) + page.wait_for_timeout(100) # Escape invalidates the change page.locator('input[type="date"]').press('Escape') expect(page.locator(f'text="{new_date2}"')).to_have_count(0) @@ -2871,6 +2873,7 @@ def test_tabulator_edit_event_and_header_filters_same_column(page, show_index, i assert len(widget.current_view) == 2 +@pytest.mark.flaky(max_runs=3) @pytest.mark.parametrize('pagination', ['remote', 'local']) def test_tabulator_edit_event_and_header_filters_same_column_pagination(page, pagination): df = pd.DataFrame({ @@ -2891,7 +2894,6 @@ def test_tabulator_edit_event_and_header_filters_same_column_pagination(page, pa header = page.locator('input[type="search"]') header.click() - page.wait_for_timeout(200) header.fill('B') header.press('Enter') @@ -2899,7 +2901,6 @@ def test_tabulator_edit_event_and_header_filters_same_column_pagination(page, pa cell = page.locator('text="B"').first cell.click() - page.wait_for_timeout(200) editable_cell = page.locator('input[type="text"]') editable_cell.fill("Q") editable_cell.press('Enter') @@ -2911,7 +2912,6 @@ def test_tabulator_edit_event_and_header_filters_same_column_pagination(page, pa assert len(widget.current_view) == 4 page.locator('text="Last"').click() - page.wait_for_timeout(200) # Check the table has the right number of rows expect(page.locator('.tabulator-row')).to_have_count(2) @@ -2919,7 +2919,6 @@ def test_tabulator_edit_event_and_header_filters_same_column_pagination(page, pa # Edit a cell in the filtered column, from B to X cell = page.locator('text="B"').nth(1) cell.click() - page.wait_for_timeout(200) editable_cell = page.locator('input[type="text"]') editable_cell.fill("X") editable_cell.press('Enter') @@ -2933,7 +2932,6 @@ def test_tabulator_edit_event_and_header_filters_same_column_pagination(page, pa # In the same column, edit X to Y cell = page.locator('text="X"') cell.click() - page.wait_for_timeout(200) editable_cell = page.locator('input[type="text"]') editable_cell.fill("Y") editable_cell.press('Enter') @@ -2946,7 +2944,6 @@ def test_tabulator_edit_event_and_header_filters_same_column_pagination(page, pa # Edit the last B value found in that column, from B to Z cell = page.locator('text="B"') cell.click() - page.wait_for_timeout(200) editable_cell = page.locator('input[type="text"]') editable_cell.fill("Z") editable_cell.press('Enter') From 6aa8adfaa9f76be7e0b5159daa2b566f931f13d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Fri, 17 Jan 2025 08:39:20 +0100 Subject: [PATCH 12/28] Mark convert and jupyter test as flaky --- panel/tests/ui/io/test_convert.py | 2 +- panel/tests/ui/io/test_jupyter_server_extension.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/panel/tests/ui/io/test_convert.py b/panel/tests/ui/io/test_convert.py index 41df409ed5..d2ad101366 100644 --- a/panel/tests/ui/io/test_convert.py +++ b/panel/tests/ui/io/test_convert.py @@ -23,7 +23,7 @@ allow_module_level=True ) -pytestmark = pytest.mark.ui +pytestmark = [pytest.mark.ui, pytest.mark.flaky(max_runs=3)] if os.name == "wt": diff --git a/panel/tests/ui/io/test_jupyter_server_extension.py b/panel/tests/ui/io/test_jupyter_server_extension.py index 36494154a5..93d7c48624 100644 --- a/panel/tests/ui/io/test_jupyter_server_extension.py +++ b/panel/tests/ui/io/test_jupyter_server_extension.py @@ -10,8 +10,7 @@ from panel.tests.util import wait_until -pytestmark = pytest.mark.jupyter - +pytestmark = [pytest.mark.jupyter, pytest.mark.flaky(max_runs=3)] def test_jupyter_server(page, jupyter_preview): From 3e815e1eb92d6089e817869e3e21f9fdc054b637 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Fri, 17 Jan 2025 09:02:47 +0100 Subject: [PATCH 13/28] chat test --- panel/tests/chat/test_feed.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/panel/tests/chat/test_feed.py b/panel/tests/chat/test_feed.py index 94a868ae87..ab025e9a73 100644 --- a/panel/tests/chat/test_feed.py +++ b/panel/tests/chat/test_feed.py @@ -1172,7 +1172,14 @@ def callback(contents, user, instance): feed = ChatFeed(callback=callback) feed.send("Message", respond=True) - await async_wait_until(lambda: feed.objects[-1].object == "helloooo") + + def case(): + msg = feed.objects[-1].object + if msg == "Message": + return False + assert msg == "helloooo" + + await async_wait_until(case) assert chat_feed._placeholder not in chat_feed._chat_log async def test_callback_one_argument(self, chat_feed): From a834ebbd761b421b8b55939d8ce7f4b99b7d35ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Fri, 17 Jan 2025 09:49:25 +0100 Subject: [PATCH 14/28] Mark feed as flaky --- panel/tests/ui/layout/test_feed.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panel/tests/ui/layout/test_feed.py b/panel/tests/ui/layout/test_feed.py index 8fe73e3adc..b5fbc36b53 100644 --- a/panel/tests/ui/layout/test_feed.py +++ b/panel/tests/ui/layout/test_feed.py @@ -8,7 +8,7 @@ from panel.layout.spacer import Spacer from panel.tests.util import serve_component, wait_until -pytestmark = pytest.mark.ui +pytestmark = [pytest.mark.ui, pytest.mark.flaky(max_runs=3)] ITEMS = 100 # 1000 items make the CI flaky From 5e1a0c6cd47a34163c1d9f3441cbdd83a994b3bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Fri, 17 Jan 2025 09:51:31 +0100 Subject: [PATCH 15/28] Update test_feed --- panel/tests/chat/test_feed.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/panel/tests/chat/test_feed.py b/panel/tests/chat/test_feed.py index ab025e9a73..d1796f1d36 100644 --- a/panel/tests/chat/test_feed.py +++ b/panel/tests/chat/test_feed.py @@ -1172,14 +1172,8 @@ def callback(contents, user, instance): feed = ChatFeed(callback=callback) feed.send("Message", respond=True) - - def case(): - msg = feed.objects[-1].object - if msg == "Message": - return False - assert msg == "helloooo" - - await async_wait_until(case) + await async_wait_until(lambda: len(feed.objects) == 2) + await async_wait_until(lambda: feed.objects[-1].object == "helloooo") assert chat_feed._placeholder not in chat_feed._chat_log async def test_callback_one_argument(self, chat_feed): From 2ec4eb1cd717fe2d95db6cd4ae54c9be84cf5f4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Fri, 17 Jan 2025 09:53:44 +0100 Subject: [PATCH 16/28] Update test_icon flaky --- panel/tests/ui/widgets/test_icon.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/panel/tests/ui/widgets/test_icon.py b/panel/tests/ui/widgets/test_icon.py index 8ef0bfe8a1..448401d0a9 100644 --- a/panel/tests/ui/widgets/test_icon.py +++ b/panel/tests/ui/widgets/test_icon.py @@ -126,9 +126,12 @@ def cb(event): # update size icon.size = "8em" - assert page.locator(".icon-tabler-ad-filled").bounding_box()["width"] >= 96 + wait_until(lambda: page.locator(".icon-tabler-ad-filled").bounding_box() is not None, page) + wait_until(lambda: page.locator(".icon-tabler-ad-filled").bounding_box()["width"] > 96, page) + icon.size = "1em" - wait_until(lambda: page.locator(".icon-tabler-ad-filled").bounding_box()["width"] <= 24, page) + wait_until(lambda: page.locator(".icon-tabler-ad-filled").bounding_box() is not None, page) + wait_until(lambda: page.locator(".icon-tabler-ad-filled").bounding_box()["width"] < 24, page) def test_toggle_icon_svg(page): From 070195a73b69b4bc0473574c59a0cd560cc928f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Fri, 17 Jan 2025 10:34:30 +0100 Subject: [PATCH 17/28] Set timeout to 30 min --- .github/workflows/test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 44e740c106..42da8c341f 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -137,7 +137,7 @@ jobs: strategy: fail-fast: false matrix: ${{ fromJson(needs.setup.outputs.matrix) }} - timeout-minutes: 90 + timeout-minutes: 30 steps: - uses: holoviz-dev/holoviz_tasks/pixi_install@v0 with: From 02e24f9b1cf2a0eb3d7922195f79c72a438b3533 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Fri, 17 Jan 2025 10:34:57 +0100 Subject: [PATCH 18/28] Fix flaky tests --- panel/tests/ui/layout/test_column.py | 1 + panel/tests/ui/pane/test_markup.py | 2 +- panel/tests/ui/widgets/test_player.py | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/panel/tests/ui/layout/test_column.py b/panel/tests/ui/layout/test_column.py index 5ecd26156f..ce8ba6b129 100644 --- a/panel/tests/ui/layout/test_column.py +++ b/panel/tests/ui/layout/test_column.py @@ -238,6 +238,7 @@ def test_column_scroll_position_param_updated(page): expect(column).to_have_js_property('scrollTop', 175) +@pytest.mark.flaky(reruns=3) def test_column_scroll_to(page): col = Column( *list(range(100)), diff --git a/panel/tests/ui/pane/test_markup.py b/panel/tests/ui/pane/test_markup.py index 71399b79d0..cc4cbb3d86 100644 --- a/panel/tests/ui/pane/test_markup.py +++ b/panel/tests/ui/pane/test_markup.py @@ -114,7 +114,7 @@ def test_html_model_no_stylesheet(page): serve_component(page, html) header_element = page.locator('h1:has-text("Header")') - assert header_element.is_visible() + expect(header_element).to_be_visible() assert header_element.text_content() == "Header" def test_anchor_scroll(page): diff --git a/panel/tests/ui/widgets/test_player.py b/panel/tests/ui/widgets/test_player.py index 5ee09edf3e..790bb99109 100644 --- a/panel/tests/ui/widgets/test_player.py +++ b/panel/tests/ui/widgets/test_player.py @@ -85,7 +85,7 @@ def test_player_visible_buttons(page): player = Player(visible_buttons=["play", "pause"]) serve_component(page, player) - assert page.is_visible(".play") + expect(page.locator(".play")).to_be_visible() assert page.is_visible(".pause") assert not page.is_visible(".reverse") assert not page.is_visible(".first") From 17f862f6fc2f627305d9fd4589e4bf14abf8caf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Fri, 17 Jan 2025 11:08:03 +0100 Subject: [PATCH 19/28] Mark all reload test as flaky --- panel/tests/ui/io/test_reload.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/panel/tests/ui/io/test_reload.py b/panel/tests/ui/io/test_reload.py index 09d2fa5a43..28b7652273 100644 --- a/panel/tests/ui/io/test_reload.py +++ b/panel/tests/ui/io/test_reload.py @@ -7,7 +7,7 @@ try: from playwright.sync_api import expect - pytestmark = pytest.mark.ui + pytestmark = [pytest.mark.ui, pytest.mark.flaky(reruns=3, reason="Writing files can sometimes be unpredictable")] except ImportError: pytestmark = pytest.mark.skip("playwright not available") @@ -80,7 +80,6 @@ def test_load_app_with_no_content(page, autoreload, py_file): expect(page.locator('.alert')).to_have_count(1) -@pytest.mark.flaky(reruns=3, reason="Writing files can sometimes be unpredictable") def test_reload_app_on_local_module_change(page, autoreload, py_files): py_file, module = py_files import_name = pathlib.Path(module.name).stem From f44475cf0dc525a4e4238cb40401c0f171d7fc19 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Fri, 17 Jan 2025 11:58:26 +0100 Subject: [PATCH 20/28] Set longer timeout for custom tests --- panel/tests/ui/test_custom.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/panel/tests/ui/test_custom.py b/panel/tests/ui/test_custom.py index 06cf4b64be..a1160a53ae 100644 --- a/panel/tests/ui/test_custom.py +++ b/panel/tests/ui/test_custom.py @@ -20,6 +20,13 @@ pytestmark = pytest.mark.ui +@pytest.fixture(scope="module", autouse=True) +def set_expect_timeout(): + expect.set_options(timeout=10_000) + yield + expect.set_options(timeout=5_000) + + class JSUpdate(JSComponent): text = param.String() From ff95fa6d1a3d3042f231e7070c886fbfa92da8fd Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Fri, 17 Jan 2025 12:00:17 +0100 Subject: [PATCH 21/28] Fix type --- panel/tests/ui/io/test_reload.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panel/tests/ui/io/test_reload.py b/panel/tests/ui/io/test_reload.py index 28b7652273..c30b5e3abf 100644 --- a/panel/tests/ui/io/test_reload.py +++ b/panel/tests/ui/io/test_reload.py @@ -9,7 +9,7 @@ pytestmark = [pytest.mark.ui, pytest.mark.flaky(reruns=3, reason="Writing files can sometimes be unpredictable")] except ImportError: - pytestmark = pytest.mark.skip("playwright not available") + pytestmark = [pytest.mark.skip("playwright not available")] from panel.io.state import state from panel.tests.util import serve_component, wait_until From 727a2267f8993f48068b1f7840b752643fdd2e51 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Fri, 17 Jan 2025 12:39:24 +0100 Subject: [PATCH 22/28] Close handles --- panel/tests/command/test_compile.py | 1 + panel/tests/ui/widgets/test_misc.py | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/panel/tests/command/test_compile.py b/panel/tests/command/test_compile.py index b562b790f6..60ae44755a 100644 --- a/panel/tests/command/test_compile.py +++ b/panel/tests/command/test_compile.py @@ -23,3 +23,4 @@ def test_compile_component(py_file): assert 'function render() {\n console.log("foo");\n}' in bundle.read_text() finally: bundle.unlink() + p.kill() diff --git a/panel/tests/ui/widgets/test_misc.py b/panel/tests/ui/widgets/test_misc.py index f8807c907a..30a87475e5 100644 --- a/panel/tests/ui/widgets/test_misc.py +++ b/panel/tests/ui/widgets/test_misc.py @@ -55,7 +55,10 @@ def create_file(value): download = download_info.value tmp = tempfile.NamedTemporaryFile(suffix='.txt') download.save_as(tmp.name) - assert tmp.file.read().decode('utf-8') == 'abc' + try: + assert tmp.file.read().decode('utf-8') == 'abc' + finally: + tmp.close() page.click('.bk-tab:not(.bk-active)') page.click('.bk-tab:not(.bk-active)') @@ -71,4 +74,7 @@ def create_file(value): download = download_info.value tmp = tempfile.NamedTemporaryFile(suffix='.txt') download.save_as(tmp.name) - assert tmp.file.read().decode('utf-8') == 'abcdef' + try: + assert tmp.file.read().decode('utf-8') == 'abcdef' + finally: + tmp.close() From a5e1e5a0d8a02e54c051be3a8fa8a996c3a7d148 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Fri, 17 Jan 2025 13:05:10 +0100 Subject: [PATCH 23/28] Only render first row to avoid complicating selector --- panel/tests/ui/widgets/test_tabulator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/panel/tests/ui/widgets/test_tabulator.py b/panel/tests/ui/widgets/test_tabulator.py index 39fa475f0e..87d41e0f65 100644 --- a/panel/tests/ui/widgets/test_tabulator.py +++ b/panel/tests/ui/widgets/test_tabulator.py @@ -1083,11 +1083,11 @@ def test_tabulator_patch_no_horizontal_rescroll(page, df_mixed): widths = 100 width = int(((df_mixed.shape[1] + 1) * widths) / 2) df_mixed['tomodify'] = 'target' - widget = Tabulator(df_mixed, width=width, widths=widths) + widget = Tabulator(df_mixed.iloc[:1], width=width, widths=widths) serve_component(page, widget) - cell = page.locator('text="target"').first + cell = page.locator('text="target"') # Scroll to the right cell.scroll_into_view_if_needed() page.wait_for_timeout(200) From 541cb1255232b10531d2f2c96a0b7c2f4b7d9e9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Fri, 17 Jan 2025 14:21:49 +0100 Subject: [PATCH 24/28] revert back to previous state --- panel/tests/ui/test_custom.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/panel/tests/ui/test_custom.py b/panel/tests/ui/test_custom.py index a1160a53ae..bc37cf005e 100644 --- a/panel/tests/ui/test_custom.py +++ b/panel/tests/ui/test_custom.py @@ -22,9 +22,12 @@ @pytest.fixture(scope="module", autouse=True) def set_expect_timeout(): + timeout = expect._timeout expect.set_options(timeout=10_000) - yield - expect.set_options(timeout=5_000) + try: + yield + finally: + expect.set_options(timeout=timeout) class JSUpdate(JSComponent): From 1dc2d90d53e4bd08c421b725f619d572b9822499 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Fri, 17 Jan 2025 14:22:03 +0100 Subject: [PATCH 25/28] Wait for images to load --- panel/tests/ui/pane/test_image.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/panel/tests/ui/pane/test_image.py b/panel/tests/ui/pane/test_image.py index 887e11ea22..8a723a9f3f 100644 --- a/panel/tests/ui/pane/test_image.py +++ b/panel/tests/ui/pane/test_image.py @@ -25,7 +25,13 @@ def get_bbox(page, obj): with page.expect_response(obj.object): page.goto(f"http://localhost:{port}") wait_until(lambda: page.locator("img") is not None, page) - return page.locator("img").bounding_box() + for _ in range(5): + bbox = page.locator("img").bounding_box() + if bbox["width"] and bbox["height"]: + return bbox + page.wait_for_timeout(100) + + raise TimeoutError("Image has not been loaded") @pytest.mark.parametrize('embed', [False, True]) def test_png_native_size(embed, page): From 048b7c72446ed3a6841809ee5875a875440d43ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Fri, 17 Jan 2025 14:52:48 +0100 Subject: [PATCH 26/28] Increase timeout for windows --- panel/tests/ui/io/test_convert.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panel/tests/ui/io/test_convert.py b/panel/tests/ui/io/test_convert.py index d2ad101366..99d7d28053 100644 --- a/panel/tests/ui/io/test_convert.py +++ b/panel/tests/ui/io/test_convert.py @@ -27,7 +27,7 @@ if os.name == "wt": - TIMEOUT = 150_000 + TIMEOUT = 200_000 else: TIMEOUT = 90_000 From 6d8c794418d1a0a96bf10f675894e904619c2089 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Fri, 17 Jan 2025 16:46:28 +0100 Subject: [PATCH 27/28] Some tweaks to Feed test --- panel/tests/ui/layout/test_feed.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/panel/tests/ui/layout/test_feed.py b/panel/tests/ui/layout/test_feed.py index b5fbc36b53..4e6e6fe1ff 100644 --- a/panel/tests/ui/layout/test_feed.py +++ b/panel/tests/ui/layout/test_feed.py @@ -113,27 +113,25 @@ def test_feed_scroll_to_latest_within_limit(page): feed.scroll_to_latest(scroll_limit=100) - # assert scroll location is still at top feed.append(Spacer(styles=dict(background='yellow'), width=200, height=200)) - page.wait_for_timeout(500) - + # assert scroll location is still at top expect(feed_el.locator('div')).to_have_count(5) expect(feed_el).to_have_js_property('scrollTop', 0) # scroll to close to bottom - feed_el.evaluate('(el) => el.scrollTo({top: el.scrollHeight})') + feed_el.evaluate('(el) => el.scrollTo({top: 200})') + expect(feed_el).to_have_js_property('scrollTop', 200) # assert auto scroll works; i.e. distance from bottom is 0 feed.append(Spacer(styles=dict(background='yellow'), width=200, height=200)) - feed.scroll_to_latest(scroll_limit=1000) - def case(): - cmd = '(el) => el.scrollHeight - el.scrollTop - el.clientHeight' - assert feed_el.evaluate(cmd) == 0 - - wait_until(case, page) + def assert_at_bottom(): + assert feed_el.evaluate( + '(el) => el.scrollHeight - el.scrollTop - el.clientHeight' + ) == 0 + wait_until(assert_at_bottom, page) def test_feed_view_scroll_button(page): From fe126dc5ee262642fbd4011c5eba13a4c7beab37 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Fri, 17 Jan 2025 17:01:27 +0100 Subject: [PATCH 28/28] Update panel/tests/ui/io/test_convert.py --- panel/tests/ui/io/test_convert.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panel/tests/ui/io/test_convert.py b/panel/tests/ui/io/test_convert.py index 99d7d28053..c2abd2e8f5 100644 --- a/panel/tests/ui/io/test_convert.py +++ b/panel/tests/ui/io/test_convert.py @@ -26,7 +26,7 @@ pytestmark = [pytest.mark.ui, pytest.mark.flaky(max_runs=3)] -if os.name == "wt": +if os.name == "nt": TIMEOUT = 200_000 else: TIMEOUT = 90_000