From bb69444feec107b62dc394f405de6d7da32050d2 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Fri, 23 Aug 2024 09:21:14 +0200 Subject: [PATCH 01/31] create_project_from_template_dashboard --- tests/e2e-playwright/tests/conftest.py | 31 ++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/e2e-playwright/tests/conftest.py b/tests/e2e-playwright/tests/conftest.py index cdae590ad8e..69814ef8a3a 100644 --- a/tests/e2e-playwright/tests/conftest.py +++ b/tests/e2e-playwright/tests/conftest.py @@ -466,6 +466,22 @@ def _(plus_button_test_id: str) -> None: return _ +@pytest.fixture +def find_and_start_template_in_dashboard( + page: Page, +) -> Callable[[str], None]: + def _(template_name: str) -> None: + with log_context(logging.INFO, f"Finding {template_name=} in dashboard"): + page.get_by_test_id("templatesTabBtn").click() + _textbox = page.get_by_test_id("searchBarFilter-textField-template") + _textbox.fill(template_name) + _textbox.press("Enter") + # OM: press first card + page.get_by_test_id(test_id).click() + + return _ + + @pytest.fixture def find_and_start_service_in_dashboard( page: Page, @@ -502,6 +518,21 @@ def _(plus_button_test_id: str) -> dict[str, Any]: return _ +@pytest.fixture +def create_project_from_template_dashboard( + find_and_start_template_in_dashboard: Callable[[str], None], + create_new_project_and_delete: Callable[ + [tuple[RunningState]], dict[str, Any] + ], +) -> Callable[[ServiceType, str, str | None], dict[str, Any]]: + def _(template_name: str) -> dict[str, Any]: + find_and_start_template_in_dashboard(template_name) + expected_states = (RunningState.UNKNOWN,) + return create_new_project_and_delete(expected_states) + + return _ + + @pytest.fixture def create_project_from_service_dashboard( find_and_start_service_in_dashboard: Callable[[ServiceType, str, str | None], None], From c8c085de1e1526abd55222dc91c6327cf42dcc0a Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Fri, 23 Aug 2024 09:21:24 +0200 Subject: [PATCH 02/31] test_template added --- .../tests/sim4life/test_template.py | 181 ++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 tests/e2e-playwright/tests/sim4life/test_template.py diff --git a/tests/e2e-playwright/tests/sim4life/test_template.py b/tests/e2e-playwright/tests/sim4life/test_template.py new file mode 100644 index 00000000000..3ce08a12ccd --- /dev/null +++ b/tests/e2e-playwright/tests/sim4life/test_template.py @@ -0,0 +1,181 @@ +# pylint: disable=logging-fstring-interpolation +# pylint: disable=redefined-outer-name +# pylint: disable=too-many-arguments +# pylint: disable=too-many-statements +# pylint: disable=unnecessary-lambda +# pylint: disable=unused-argument +# pylint: disable=unused-variable + + +import datetime +import logging +import re +from collections.abc import Callable +from dataclasses import dataclass +from typing import Any, Final + +import arrow +from playwright.sync_api import Page, WebSocket, expect +from pytest_simcore.helpers.logging_tools import log_context +from pytest_simcore.helpers.playwright import ( + MINUTE, + SECOND, + SOCKETIO_MESSAGE_PREFIX, + ServiceType, + SocketIOEvent, + decode_socketio_42_message, + wait_for_service_running, +) + +projects_uuid_pattern: Final[re.Pattern] = re.compile( + r"/projects/([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})" +) +_EC2_STARTUP_MAX_WAIT_TIME: Final[int] = 1 * MINUTE +_S4L_MAX_STARTUP_TIME: Final[int] = 1 * MINUTE +_S4L_DOCKER_PULLING_MAX_TIME: Final[int] = 10 * MINUTE +_S4L_AUTOSCALED_MAX_STARTUP_TIME: Final[int] = ( + _EC2_STARTUP_MAX_WAIT_TIME + _S4L_DOCKER_PULLING_MAX_TIME + _S4L_MAX_STARTUP_TIME +) + +_S4L_STARTUP_SCREEN_MAX_TIME: Final[int] = 45 * SECOND +_S4L_STREAMING_ESTABLISHMENT_MAX_TIME: Final[int] = 15 * SECOND + + +_S4L_SOCKETIO_REGEX: Final[re.Pattern] = re.compile( + r"^(?P[^:]+)://(?P[^\.]+)\.services\.(?P[^\/]+)\/socket\.io\/.+$" +) + + +@dataclass(kw_only=True) +class _S4LWaitForWebsocket: + logger: logging.Logger + + def __call__(self, new_websocket: WebSocket) -> bool: + if re.match(_S4L_SOCKETIO_REGEX, new_websocket.url): + self.logger.info("found S4L websocket!") + return True + + return False + + +@dataclass +class _S4LSocketIOMessagePrinter: + def __call__(self, message: str) -> None: + if message.startswith(SOCKETIO_MESSAGE_PREFIX): + decoded_message: SocketIOEvent = decode_socketio_42_message(message) + print("S4L WS Message:", decoded_message.name, decoded_message.obj) + + +@dataclass(kw_only=True) +class _S4LSocketIOCheckBitRateIncreasesMessagePrinter: + observation_time: datetime.timedelta + logger: logging.Logger + _initial_bit_rate: float = 0 + _initial_bit_rate_time: datetime.datetime = arrow.utcnow().datetime + + def __call__(self, message: str) -> bool: + if message.startswith(SOCKETIO_MESSAGE_PREFIX): + decoded_message: SocketIOEvent = decode_socketio_42_message(message) + if ( + decoded_message.name == "server.video_stream.bitrate_data" + and "bitrate" in decoded_message.obj + ): + current_bitrate = decoded_message.obj["bitrate"] + if self._initial_bit_rate == 0: + self._initial_bit_rate = current_bitrate + self._initial_bit_rate_time = arrow.utcnow().datetime + self.logger.info( + "%s", + f"{self._initial_bit_rate=} at {self._initial_bit_rate_time.isoformat()}", + ) + return False + + # NOTE: MaG says the value might also go down, but it shall definitely change, + # if this code proves unsafe we should change it. + elapsed_time = arrow.utcnow().datetime - self._initial_bit_rate_time + if ( + elapsed_time > self.observation_time + and "bitrate" in decoded_message.obj + ): + current_bitrate = decoded_message.obj["bitrate"] + bitrate_test = bool(self._initial_bit_rate != current_bitrate) + self.logger.info( + "%s", + f"{current_bitrate=} after {elapsed_time=}: {'good!' if bitrate_test else 'failed! bitrate did not change! TIP: talk with MaG about underwater cables!'}", + ) + return bitrate_test + + return False + + +def test_template( + page: Page, + create_project_from_template_dashboard: Callable[[str], dict[str, Any]], + log_in_and_out: WebSocket, + template_name: str, + autoscaled: bool, + check_videostreaming: bool, +): + project_data = create_project_from_template_dashboard(template_name) + assert "workbench" in project_data, "Expected workbench to be in project data!" + assert isinstance( + project_data["workbench"], dict + ), "Expected workbench to be a dict!" + node_ids: list[str] = list(project_data["workbench"]) + assert len(node_ids) == 1, "Expected 1 node in the workbench!" + + with log_context(logging.INFO, "launch S4L") as ctx: + predicate = _S4LWaitForWebsocket(logger=ctx.logger) + with page.expect_websocket( + predicate, + timeout=_S4L_STARTUP_SCREEN_MAX_TIME + + ( + _S4L_AUTOSCALED_MAX_STARTUP_TIME + if autoscaled + else _S4L_MAX_STARTUP_TIME + ) + + 10 * SECOND, + ) as ws_info: + s4l_iframe = wait_for_service_running( + page=page, + node_id=node_ids[0], + websocket=log_in_and_out, + timeout=( + _S4L_AUTOSCALED_MAX_STARTUP_TIME + if autoscaled + else _S4L_MAX_STARTUP_TIME + ), + press_start_button=False, + ) + s4l_websocket = ws_info.value + ctx.logger.info("acquired S4L websocket!") + + # Wait until grid is shown + # NOTE: the startup screen should disappear very fast after the websocket was acquired + with log_context(logging.INFO, "Interact with S4l"): + s4l_iframe.get_by_test_id("tree-item-Grid").nth(0).click() + page.wait_for_timeout(3000) + + if check_videostreaming: + with log_context(logging.INFO, "Check videostreaming works") as ctx: + waiter = _S4LSocketIOCheckBitRateIncreasesMessagePrinter( + observation_time=datetime.timedelta( + milliseconds=_S4L_STREAMING_ESTABLISHMENT_MAX_TIME / 2.0, + ), + logger=ctx.logger, + ) + with s4l_websocket.expect_event( + "framereceived", + waiter, + timeout=_S4L_STREAMING_ESTABLISHMENT_MAX_TIME, + ): + ... + + expect( + s4l_iframe.locator("video"), + "videostreaming is not established. " + "TIP: if using playwright integrated open source chromIUM, " + "webkit or firefox this is expected, switch to chrome/msedge!!", + ).to_be_visible() + s4l_iframe.locator("video").click() + page.wait_for_timeout(3000) From 32e8831682ed2ea2c9f8db38147b9d9f0062ab3e Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Fri, 23 Aug 2024 10:11:40 +0200 Subject: [PATCH 03/31] template_id --- tests/e2e-playwright/tests/conftest.py | 12 ++++++------ tests/e2e-playwright/tests/sim4life/test_template.py | 5 ++--- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/tests/e2e-playwright/tests/conftest.py b/tests/e2e-playwright/tests/conftest.py index 69814ef8a3a..d1cc4a544cc 100644 --- a/tests/e2e-playwright/tests/conftest.py +++ b/tests/e2e-playwright/tests/conftest.py @@ -470,13 +470,13 @@ def _(plus_button_test_id: str) -> None: def find_and_start_template_in_dashboard( page: Page, ) -> Callable[[str], None]: - def _(template_name: str) -> None: - with log_context(logging.INFO, f"Finding {template_name=} in dashboard"): + def _(template_id: str) -> None: + with log_context(logging.INFO, f"Finding {template_id=} in dashboard"): page.get_by_test_id("templatesTabBtn").click() _textbox = page.get_by_test_id("searchBarFilter-textField-template") - _textbox.fill(template_name) + _textbox.fill(template_id) _textbox.press("Enter") - # OM: press first card + test_id = "studyBrowserListItem_" + template_id page.get_by_test_id(test_id).click() return _ @@ -525,8 +525,8 @@ def create_project_from_template_dashboard( [tuple[RunningState]], dict[str, Any] ], ) -> Callable[[ServiceType, str, str | None], dict[str, Any]]: - def _(template_name: str) -> dict[str, Any]: - find_and_start_template_in_dashboard(template_name) + def _(template_id: str) -> dict[str, Any]: + find_and_start_template_in_dashboard(template_id) expected_states = (RunningState.UNKNOWN,) return create_new_project_and_delete(expected_states) diff --git a/tests/e2e-playwright/tests/sim4life/test_template.py b/tests/e2e-playwright/tests/sim4life/test_template.py index 3ce08a12ccd..65f4edb26fb 100644 --- a/tests/e2e-playwright/tests/sim4life/test_template.py +++ b/tests/e2e-playwright/tests/sim4life/test_template.py @@ -21,7 +21,6 @@ MINUTE, SECOND, SOCKETIO_MESSAGE_PREFIX, - ServiceType, SocketIOEvent, decode_socketio_42_message, wait_for_service_running, @@ -112,11 +111,11 @@ def test_template( page: Page, create_project_from_template_dashboard: Callable[[str], dict[str, Any]], log_in_and_out: WebSocket, - template_name: str, + template_id: str, autoscaled: bool, check_videostreaming: bool, ): - project_data = create_project_from_template_dashboard(template_name) + project_data = create_project_from_template_dashboard(template_id) assert "workbench" in project_data, "Expected workbench to be in project data!" assert isinstance( project_data["workbench"], dict From 6f51087cb73e8e53bc603c4b8bb33e188fd62bf7 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Fri, 23 Aug 2024 10:23:03 +0200 Subject: [PATCH 04/31] fix card id --- .../client/source/class/osparc/dashboard/CardBase.js | 3 ++- tests/e2e-playwright/tests/conftest.py | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js b/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js index 9b5e902c281..42e8b8d45c6 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js @@ -449,7 +449,8 @@ qx.Class.define("osparc.dashboard.CardBase", { }, __applyUuid: function(value, old) { - osparc.utils.Utils.setIdToWidget(this, "studyBrowserListItem_"+value); + const resourceType = this.getResourceType() || "study"; + osparc.utils.Utils.setIdToWidget(this, resourceType + "BrowserListItem_" + value); this.setCardKey(value); }, diff --git a/tests/e2e-playwright/tests/conftest.py b/tests/e2e-playwright/tests/conftest.py index d1cc4a544cc..7af9ff2e22a 100644 --- a/tests/e2e-playwright/tests/conftest.py +++ b/tests/e2e-playwright/tests/conftest.py @@ -476,7 +476,8 @@ def _(template_id: str) -> None: _textbox = page.get_by_test_id("searchBarFilter-textField-template") _textbox.fill(template_id) _textbox.press("Enter") - test_id = "studyBrowserListItem_" + template_id + test_id = "templateBrowserListItem_" + template_id + # OM: use this: test_id = "studyBrowserListItem_" + template_id page.get_by_test_id(test_id).click() return _ @@ -494,7 +495,7 @@ def _( _textbox = page.get_by_test_id("searchBarFilter-textField-service") _textbox.fill(service_name) _textbox.press("Enter") - test_id = f"studyBrowserListItem_simcore/services/{'dynamic' if service_type is ServiceType.DYNAMIC else 'comp'}" + test_id = f"serviceBrowserListItem_simcore/services/{'dynamic' if service_type is ServiceType.DYNAMIC else 'comp'}" if service_key_prefix: test_id = f"{test_id}/{service_key_prefix}" test_id = f"{test_id}/{service_name}" From c27b3e59f58c190f657d9cdcadd8d2ddc1781965 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Fri, 23 Aug 2024 13:39:12 +0200 Subject: [PATCH 05/31] LongRunningTaskWaiter --- .../src/pytest_simcore/helpers/playwright.py | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/packages/pytest-simcore/src/pytest_simcore/helpers/playwright.py b/packages/pytest-simcore/src/pytest_simcore/helpers/playwright.py index bec851c4d33..d102da6122c 100644 --- a/packages/pytest-simcore/src/pytest_simcore/helpers/playwright.py +++ b/packages/pytest-simcore/src/pytest_simcore/helpers/playwright.py @@ -235,6 +235,29 @@ def __call__(self, message: str) -> bool: return False +@dataclass +class LongRunningTaskWaiter: + page: Page + long_running_task_data: dict + logger: logging.Logger + + def __call__(self) -> bool: + assert "status_href" in self.long_running_task_data + assert "result_href" in self.long_running_task_data + done = False + while not done: + with self.page.expect_response(self.long_running_task_data["status_href"]) as status_info: + assert "done" in status_info + assert "task_progress" in status_info + done = status_info["done"] + self.logger.info( + "task progress: %s", + status_info["task_progress"], + ) + with self.page.expect_response(self.long_running_task_data["result_href"]) as result_info: + return result_info + + def wait_for_pipeline_state( current_state: RunningState, *, From 043ff8e70fdf7df2caa39a6f0a04c084abdb5d96 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Fri, 23 Aug 2024 14:25:47 +0200 Subject: [PATCH 06/31] wait on Waiter --- tests/e2e-playwright/tests/conftest.py | 27 ++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/tests/e2e-playwright/tests/conftest.py b/tests/e2e-playwright/tests/conftest.py index 7af9ff2e22a..05dfa433a63 100644 --- a/tests/e2e-playwright/tests/conftest.py +++ b/tests/e2e-playwright/tests/conftest.py @@ -26,6 +26,7 @@ from pytest_simcore.helpers.playwright import ( MINUTE, AutoRegisteredUser, + LongRunningTaskWaiter, RunningState, ServiceType, SocketIOEvent, @@ -381,6 +382,7 @@ def create_new_project_and_delete( def _( expected_states: tuple[RunningState] = (RunningState.NOT_STARTED,), press_open: bool = True, + template_id: str = None, ) -> dict[str, Any]: assert ( len(created_project_uuids) == 0 @@ -398,7 +400,16 @@ def _( ): # Project detail view pop-ups shows if press_open: - page.get_by_test_id("openResource").click() + open_button = page.get_by_test_id("openResource") + if template_id: + # wait until the template data is copied + with page.expect_response( + re.compile(r"/projects?from_study="+template_id) + ) as long_running_task: + open_button.click() + waiterOM = LongRunningTaskWaiter(page=page, long_running_task_data=long_running_task, logger=ctx.logger) + else: + open_button.click() if product_billable: # Open project with default resources page.get_by_test_id("openWithResources").click() @@ -467,7 +478,7 @@ def _(plus_button_test_id: str) -> None: @pytest.fixture -def find_and_start_template_in_dashboard( +def find_and_click_template_in_dashboard( page: Page, ) -> Callable[[str], None]: def _(template_id: str) -> None: @@ -476,8 +487,8 @@ def _(template_id: str) -> None: _textbox = page.get_by_test_id("searchBarFilter-textField-template") _textbox.fill(template_id) _textbox.press("Enter") - test_id = "templateBrowserListItem_" + template_id - # OM: use this: test_id = "studyBrowserListItem_" + template_id + # OM: use this: test_id = "templateBrowserListItem_" + template_id + test_id = "studyBrowserListItem_" + template_id page.get_by_test_id(test_id).click() return _ @@ -521,15 +532,15 @@ def _(plus_button_test_id: str) -> dict[str, Any]: @pytest.fixture def create_project_from_template_dashboard( - find_and_start_template_in_dashboard: Callable[[str], None], + find_and_click_template_in_dashboard: Callable[[str], None], create_new_project_and_delete: Callable[ [tuple[RunningState]], dict[str, Any] ], ) -> Callable[[ServiceType, str, str | None], dict[str, Any]]: def _(template_id: str) -> dict[str, Any]: - find_and_start_template_in_dashboard(template_id) + find_and_click_template_in_dashboard(template_id) expected_states = (RunningState.UNKNOWN,) - return create_new_project_and_delete(expected_states) + return create_new_project_and_delete(expected_states, True, template_id) return _ @@ -548,7 +559,7 @@ def _( expected_states = (RunningState.UNKNOWN,) if service_type is ServiceType.COMPUTATIONAL: expected_states = (RunningState.NOT_STARTED,) - return create_new_project_and_delete(expected_states) + return create_new_project_and_delete(expected_states, True) return _ From 881be4932486110d8660f29c34245535c57fa8a8 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Fri, 23 Aug 2024 14:30:34 +0200 Subject: [PATCH 07/31] minor --- .../src/pytest_simcore/helpers/playwright.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/pytest-simcore/src/pytest_simcore/helpers/playwright.py b/packages/pytest-simcore/src/pytest_simcore/helpers/playwright.py index d102da6122c..06c560aef88 100644 --- a/packages/pytest-simcore/src/pytest_simcore/helpers/playwright.py +++ b/packages/pytest-simcore/src/pytest_simcore/helpers/playwright.py @@ -241,7 +241,7 @@ class LongRunningTaskWaiter: long_running_task_data: dict logger: logging.Logger - def __call__(self) -> bool: + def __call__(self) -> None: assert "status_href" in self.long_running_task_data assert "result_href" in self.long_running_task_data done = False @@ -255,7 +255,10 @@ def __call__(self) -> bool: status_info["task_progress"], ) with self.page.expect_response(self.long_running_task_data["result_href"]) as result_info: - return result_info + self.logger.info( + "task completed: %s", + result_info["data"], + ) def wait_for_pipeline_state( From 49080231070dadfa275e334d5d7927d370ab1d2c Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Mon, 26 Aug 2024 11:23:34 +0200 Subject: [PATCH 08/31] fix regex --- tests/e2e-playwright/tests/conftest.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/tests/e2e-playwright/tests/conftest.py b/tests/e2e-playwright/tests/conftest.py index 05dfa433a63..8f33a0dec27 100644 --- a/tests/e2e-playwright/tests/conftest.py +++ b/tests/e2e-playwright/tests/conftest.py @@ -402,12 +402,16 @@ def _( if press_open: open_button = page.get_by_test_id("openResource") if template_id: - # wait until the template data is copied with page.expect_response( - re.compile(r"/projects?from_study="+template_id) + re.compile(rf"/projects\?from_study\={template_id}") ) as long_running_task: open_button.click() - waiterOM = LongRunningTaskWaiter(page=page, long_running_task_data=long_running_task, logger=ctx.logger) + # wait until the template data is copied, and log times + waiterOM = LongRunningTaskWaiter( + page=page, + long_running_task_data=long_running_task, + logger=ctx.logger, + ) else: open_button.click() if product_billable: @@ -533,9 +537,7 @@ def _(plus_button_test_id: str) -> dict[str, Any]: @pytest.fixture def create_project_from_template_dashboard( find_and_click_template_in_dashboard: Callable[[str], None], - create_new_project_and_delete: Callable[ - [tuple[RunningState]], dict[str, Any] - ], + create_new_project_and_delete: Callable[[tuple[RunningState]], dict[str, Any]], ) -> Callable[[ServiceType, str, str | None], dict[str, Any]]: def _(template_id: str) -> dict[str, Any]: find_and_click_template_in_dashboard(template_id) From a37baabe972cf0ddb689ffbb93a77e0f554d4edb Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Mon, 26 Aug 2024 11:42:49 +0200 Subject: [PATCH 09/31] hardcode template_id --- tests/e2e-playwright/tests/sim4life/test_template.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/e2e-playwright/tests/sim4life/test_template.py b/tests/e2e-playwright/tests/sim4life/test_template.py index 65f4edb26fb..d5cc688b707 100644 --- a/tests/e2e-playwright/tests/sim4life/test_template.py +++ b/tests/e2e-playwright/tests/sim4life/test_template.py @@ -111,10 +111,13 @@ def test_template( page: Page, create_project_from_template_dashboard: Callable[[str], dict[str, Any]], log_in_and_out: WebSocket, - template_id: str, + # OM Fix this + # template_id: str, autoscaled: bool, check_videostreaming: bool, ): + # OM Fix this + template_id = "776cdc9a-6125-11ef-abcf-02420a00f199" project_data = create_project_from_template_dashboard(template_id) assert "workbench" in project_data, "Expected workbench to be in project data!" assert isinstance( From 028a71f6b2f04bc7e8f907f7918304b398b21a4c Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Tue, 27 Aug 2024 10:35:17 +0200 Subject: [PATCH 10/31] create_template in predicate --- .../src/pytest_simcore/helpers/playwright.py | 26 --------------- tests/e2e-playwright/tests/conftest.py | 32 +++++++++++++------ 2 files changed, 23 insertions(+), 35 deletions(-) diff --git a/packages/pytest-simcore/src/pytest_simcore/helpers/playwright.py b/packages/pytest-simcore/src/pytest_simcore/helpers/playwright.py index 06c560aef88..bec851c4d33 100644 --- a/packages/pytest-simcore/src/pytest_simcore/helpers/playwright.py +++ b/packages/pytest-simcore/src/pytest_simcore/helpers/playwright.py @@ -235,32 +235,6 @@ def __call__(self, message: str) -> bool: return False -@dataclass -class LongRunningTaskWaiter: - page: Page - long_running_task_data: dict - logger: logging.Logger - - def __call__(self) -> None: - assert "status_href" in self.long_running_task_data - assert "result_href" in self.long_running_task_data - done = False - while not done: - with self.page.expect_response(self.long_running_task_data["status_href"]) as status_info: - assert "done" in status_info - assert "task_progress" in status_info - done = status_info["done"] - self.logger.info( - "task progress: %s", - status_info["task_progress"], - ) - with self.page.expect_response(self.long_running_task_data["result_href"]) as result_info: - self.logger.info( - "task completed: %s", - result_info["data"], - ) - - def wait_for_pipeline_state( current_state: RunningState, *, diff --git a/tests/e2e-playwright/tests/conftest.py b/tests/e2e-playwright/tests/conftest.py index 8f33a0dec27..a8e74f6fc73 100644 --- a/tests/e2e-playwright/tests/conftest.py +++ b/tests/e2e-playwright/tests/conftest.py @@ -26,7 +26,6 @@ from pytest_simcore.helpers.playwright import ( MINUTE, AutoRegisteredUser, - LongRunningTaskWaiter, RunningState, ServiceType, SocketIOEvent, @@ -392,10 +391,12 @@ def _( f"Open project in {product_url=} as {product_billable=}", ) as ctx: waiter = SocketIOProjectStateUpdatedWaiter(expected_states=expected_states) + timeout = 30000 if template_id else 120000 with ( - log_in_and_out.expect_event("framereceived", waiter), + log_in_and_out.expect_event("framereceived", waiter, timeout=timeout), page.expect_response( - re.compile(r"/projects/[^:]+:open") + re.compile(r"/projects/[^:]+:open"), + timeout=timeout ) as response_info, ): # Project detail view pop-ups shows @@ -406,12 +407,25 @@ def _( re.compile(rf"/projects\?from_study\={template_id}") ) as long_running_task: open_button.click() - # wait until the template data is copied, and log times - waiterOM = LongRunningTaskWaiter( - page=page, - long_running_task_data=long_running_task, - logger=ctx.logger, - ) + with log_context( + logging.INFO, + f"Copying template data", + ) as my_logger: + def wait_for_done(response): + if response.url == long_running_task["status_href"]: + assert "done" in response["status_info"] + data = response["data"] + my_logger.logger.info( + "task progress: %s", + data["task_progress"], + ) + return False + if response.url == long_running_task["result_href"]: + assert "done" in response["status_info"] + my_logger.logger.info("project created") + return True + return False + page.expect_response(wait_for_done, timeout=timeout) else: open_button.click() if product_billable: From d054bef25957301cca79ce77fb55cf366d5b34a8 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Tue, 27 Aug 2024 10:56:44 +0200 Subject: [PATCH 11/31] working again --- tests/e2e-playwright/tests/conftest.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/tests/e2e-playwright/tests/conftest.py b/tests/e2e-playwright/tests/conftest.py index a8e74f6fc73..5c3f637bcc1 100644 --- a/tests/e2e-playwright/tests/conftest.py +++ b/tests/e2e-playwright/tests/conftest.py @@ -403,27 +403,30 @@ def _( if press_open: open_button = page.get_by_test_id("openResource") if template_id: + # it returns a Long Running Task with page.expect_response( re.compile(rf"/projects\?from_study\={template_id}") - ) as long_running_task: + ) as lrt: open_button.click() + lrt_data = lrt.value.json() + lrt_data = lrt_data["data"] with log_context( logging.INFO, f"Copying template data", ) as my_logger: def wait_for_done(response): - if response.url == long_running_task["status_href"]: - assert "done" in response["status_info"] - data = response["data"] + resp = response.json() + if response.url == lrt_data["status_href"]: + resp_data = response.json() + assert "task_progress" in resp_data my_logger.logger.info( "task progress: %s", - data["task_progress"], + resp_data["task_progress"], ) return False - if response.url == long_running_task["result_href"]: - assert "done" in response["status_info"] + if response.url == lrt_data["result_href"]: my_logger.logger.info("project created") - return True + return response.status == 201 return False page.expect_response(wait_for_done, timeout=timeout) else: From 0cfbe6c4a3baa1abb5c7f57dc7465651493b8a14 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Tue, 27 Aug 2024 10:57:42 +0200 Subject: [PATCH 12/31] relax timeouts --- tests/e2e-playwright/tests/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e-playwright/tests/conftest.py b/tests/e2e-playwright/tests/conftest.py index 5c3f637bcc1..f61b356b9f3 100644 --- a/tests/e2e-playwright/tests/conftest.py +++ b/tests/e2e-playwright/tests/conftest.py @@ -391,7 +391,7 @@ def _( f"Open project in {product_url=} as {product_billable=}", ) as ctx: waiter = SocketIOProjectStateUpdatedWaiter(expected_states=expected_states) - timeout = 30000 if template_id else 120000 + timeout = 60000 if template_id else 180000 with ( log_in_and_out.expect_event("framereceived", waiter, timeout=timeout), page.expect_response( From 4ca580344ac1268f7581a68d5bff0a3a089b888d Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Tue, 27 Aug 2024 11:35:55 +0200 Subject: [PATCH 13/31] working and cleaned up --- tests/e2e-playwright/tests/conftest.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/tests/e2e-playwright/tests/conftest.py b/tests/e2e-playwright/tests/conftest.py index f61b356b9f3..3efa8a2de98 100644 --- a/tests/e2e-playwright/tests/conftest.py +++ b/tests/e2e-playwright/tests/conftest.py @@ -11,6 +11,7 @@ import os import random import re +import urllib.parse from collections.abc import Callable, Iterator from contextlib import ExitStack from typing import Any, Final @@ -414,17 +415,22 @@ def _( logging.INFO, f"Copying template data", ) as my_logger: + # For the long running tasks, only the path is relevant + def url_to_path(url): + return urllib.parse.urlparse(url).path def wait_for_done(response): - resp = response.json() - if response.url == lrt_data["status_href"]: + if url_to_path(response.url) == url_to_path(lrt_data["status_href"]): resp_data = response.json() + resp_data = resp_data["data"] assert "task_progress" in resp_data + task_progress = resp_data["task_progress"] my_logger.logger.info( - "task progress: %s", - resp_data["task_progress"], + "task progress: %s %s", + task_progress["percent"], + task_progress["message"], ) return False - if response.url == lrt_data["result_href"]: + if url_to_path(response.url) == url_to_path(lrt_data["result_href"]): my_logger.logger.info("project created") return response.status == 201 return False From cc7da70d4c476ae9e77cd249f3df4174256b61bd Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Tue, 27 Aug 2024 11:37:13 +0200 Subject: [PATCH 14/31] comment --- tests/e2e-playwright/tests/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e-playwright/tests/conftest.py b/tests/e2e-playwright/tests/conftest.py index 3efa8a2de98..3a0303f5247 100644 --- a/tests/e2e-playwright/tests/conftest.py +++ b/tests/e2e-playwright/tests/conftest.py @@ -415,7 +415,7 @@ def _( logging.INFO, f"Copying template data", ) as my_logger: - # For the long running tasks, only the path is relevant + # From the long running tasks response's urls, only their path is relevant def url_to_path(url): return urllib.parse.urlparse(url).path def wait_for_done(response): From 74444996e779ca443b31bfc2dc0db7672b6ac3d7 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Tue, 27 Aug 2024 11:50:43 +0200 Subject: [PATCH 15/31] template_id as cli argument --- tests/e2e-playwright/tests/conftest.py | 15 +++++++++++++++ .../tests/sim4life/test_template.py | 5 +---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/tests/e2e-playwright/tests/conftest.py b/tests/e2e-playwright/tests/conftest.py index 3a0303f5247..74b46ea10a3 100644 --- a/tests/e2e-playwright/tests/conftest.py +++ b/tests/e2e-playwright/tests/conftest.py @@ -95,6 +95,13 @@ def pytest_addoption(parser: pytest.Parser) -> None: default=None, help="Service Key", ) + group.addoption( + "--template-id", + action="store", + type=str, + default=None, + help="Template uuid", + ) group.addoption( "--user-agent", action="store", @@ -233,6 +240,14 @@ def service_key(request: pytest.FixtureRequest) -> str: return os.environ["SERVICE_KEY"] +@pytest.fixture(scope="session") +def template_id(request: pytest.FixtureRequest) -> str: + if key := request.config.getoption("--template-id"): + assert isinstance(key, str) + return key + return os.environ["TEMPLATE_ID"] + + @pytest.fixture(scope="session") def auto_register(request: pytest.FixtureRequest) -> bool: return bool(request.config.getoption("--autoregister")) diff --git a/tests/e2e-playwright/tests/sim4life/test_template.py b/tests/e2e-playwright/tests/sim4life/test_template.py index d5cc688b707..65f4edb26fb 100644 --- a/tests/e2e-playwright/tests/sim4life/test_template.py +++ b/tests/e2e-playwright/tests/sim4life/test_template.py @@ -111,13 +111,10 @@ def test_template( page: Page, create_project_from_template_dashboard: Callable[[str], dict[str, Any]], log_in_and_out: WebSocket, - # OM Fix this - # template_id: str, + template_id: str, autoscaled: bool, check_videostreaming: bool, ): - # OM Fix this - template_id = "776cdc9a-6125-11ef-abcf-02420a00f199" project_data = create_project_from_template_dashboard(template_id) assert "workbench" in project_data, "Expected workbench to be in project data!" assert isinstance( From f7b4409255f7173549150c175c9593fc0601b5bc Mon Sep 17 00:00:00 2001 From: sanderegg <35365065+sanderegg@users.noreply.github.com> Date: Tue, 27 Aug 2024 11:52:37 +0200 Subject: [PATCH 16/31] use common code --- .../helpers/playwright_sim4life.py | 100 ++++++++++++++++ .../tests/sim4life/test_sim4life.py | 105 ++--------------- .../tests/sim4life/test_template.py | 109 ++---------------- 3 files changed, 114 insertions(+), 200 deletions(-) create mode 100644 packages/pytest-simcore/src/pytest_simcore/helpers/playwright_sim4life.py diff --git a/packages/pytest-simcore/src/pytest_simcore/helpers/playwright_sim4life.py b/packages/pytest-simcore/src/pytest_simcore/helpers/playwright_sim4life.py new file mode 100644 index 00000000000..96f05a979ee --- /dev/null +++ b/packages/pytest-simcore/src/pytest_simcore/helpers/playwright_sim4life.py @@ -0,0 +1,100 @@ +import datetime +import logging +import re +from dataclasses import dataclass +from typing import Final + +import arrow +from playwright.sync_api import Page, WebSocket, expect + +from .logging_tools import log_context +from .playwright import ( + SECOND, + SOCKETIO_MESSAGE_PREFIX, + SocketIOEvent, + decode_socketio_42_message, +) + +_S4L_STREAMING_ESTABLISHMENT_MAX_TIME: Final[int] = 15 * SECOND +_S4L_SOCKETIO_REGEX: Final[re.Pattern] = re.compile( + r"^(?P[^:]+)://(?P[^\.]+)\.services\.(?P[^\/]+)\/socket\.io\/.+$" +) + + +@dataclass(kw_only=True) +class S4LWaitForWebsocket: + logger: logging.Logger + + def __call__(self, new_websocket: WebSocket) -> bool: + if re.match(_S4L_SOCKETIO_REGEX, new_websocket.url): + self.logger.info("found S4L websocket!") + return True + + return False + + +@dataclass(kw_only=True) +class _S4LSocketIOCheckBitRateIncreasesMessagePrinter: + observation_time: datetime.timedelta + logger: logging.Logger + _initial_bit_rate: float = 0 + _initial_bit_rate_time: datetime.datetime = arrow.utcnow().datetime + + def __call__(self, message: str) -> bool: + if message.startswith(SOCKETIO_MESSAGE_PREFIX): + decoded_message: SocketIOEvent = decode_socketio_42_message(message) + if ( + decoded_message.name == "server.video_stream.bitrate_data" + and "bitrate" in decoded_message.obj + ): + current_bitrate = decoded_message.obj["bitrate"] + if self._initial_bit_rate == 0: + self._initial_bit_rate = current_bitrate + self._initial_bit_rate_time = arrow.utcnow().datetime + self.logger.info( + "%s", + f"{self._initial_bit_rate=} at {self._initial_bit_rate_time.isoformat()}", + ) + return False + + # NOTE: MaG says the value might also go down, but it shall definitely change, + # if this code proves unsafe we should change it. + elapsed_time = arrow.utcnow().datetime - self._initial_bit_rate_time + if ( + elapsed_time > self.observation_time + and "bitrate" in decoded_message.obj + ): + current_bitrate = decoded_message.obj["bitrate"] + bitrate_test = bool(self._initial_bit_rate != current_bitrate) + self.logger.info( + "%s", + f"{current_bitrate=} after {elapsed_time=}: {'good!' if bitrate_test else 'failed! bitrate did not change! TIP: talk with MaG about underwater cables!'}", + ) + return bitrate_test + + return False + + +def check_video_streaming(page: Page, s4l_iframe, s4l_websocket: WebSocket) -> None: + with log_context(logging.INFO, "Check videostreaming works") as ctx: + waiter = _S4LSocketIOCheckBitRateIncreasesMessagePrinter( + observation_time=datetime.timedelta( + milliseconds=_S4L_STREAMING_ESTABLISHMENT_MAX_TIME / 2.0, + ), + logger=ctx.logger, + ) + with s4l_websocket.expect_event( + "framereceived", + waiter, + timeout=_S4L_STREAMING_ESTABLISHMENT_MAX_TIME, + ): + ... + + expect( + s4l_iframe.locator("video"), + "videostreaming is not established. " + "TIP: if using playwright integrated open source chromIUM, " + "webkit or firefox this is expected, switch to chrome/msedge!!", + ).to_be_visible() + s4l_iframe.locator("video").click() + page.wait_for_timeout(3000) diff --git a/tests/e2e-playwright/tests/sim4life/test_sim4life.py b/tests/e2e-playwright/tests/sim4life/test_sim4life.py index 3fca6f36b54..e7982c8803a 100644 --- a/tests/e2e-playwright/tests/sim4life/test_sim4life.py +++ b/tests/e2e-playwright/tests/sim4life/test_sim4life.py @@ -7,25 +7,23 @@ # pylint: disable=unused-variable -import datetime import logging import re from collections.abc import Callable -from dataclasses import dataclass from typing import Any, Final -import arrow -from playwright.sync_api import Page, WebSocket, expect +from playwright.sync_api import Page, WebSocket from pytest_simcore.helpers.logging_tools import log_context from pytest_simcore.helpers.playwright import ( MINUTE, SECOND, - SOCKETIO_MESSAGE_PREFIX, ServiceType, - SocketIOEvent, - decode_socketio_42_message, wait_for_service_running, ) +from pytest_simcore.helpers.playwright_sim4life import ( + S4LWaitForWebsocket, + check_video_streaming, +) projects_uuid_pattern: Final[re.Pattern] = re.compile( r"/projects/([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})" @@ -38,74 +36,6 @@ ) _S4L_STARTUP_SCREEN_MAX_TIME: Final[int] = 45 * SECOND -_S4L_STREAMING_ESTABLISHMENT_MAX_TIME: Final[int] = 15 * SECOND - - -_S4L_SOCKETIO_REGEX: Final[re.Pattern] = re.compile( - r"^(?P[^:]+)://(?P[^\.]+)\.services\.(?P[^\/]+)\/socket\.io\/.+$" -) - - -@dataclass(kw_only=True) -class _S4LWaitForWebsocket: - logger: logging.Logger - - def __call__(self, new_websocket: WebSocket) -> bool: - if re.match(_S4L_SOCKETIO_REGEX, new_websocket.url): - self.logger.info("found S4L websocket!") - return True - - return False - - -@dataclass -class _S4LSocketIOMessagePrinter: - def __call__(self, message: str) -> None: - if message.startswith(SOCKETIO_MESSAGE_PREFIX): - decoded_message: SocketIOEvent = decode_socketio_42_message(message) - print("S4L WS Message:", decoded_message.name, decoded_message.obj) - - -@dataclass(kw_only=True) -class _S4LSocketIOCheckBitRateIncreasesMessagePrinter: - observation_time: datetime.timedelta - logger: logging.Logger - _initial_bit_rate: float = 0 - _initial_bit_rate_time: datetime.datetime = arrow.utcnow().datetime - - def __call__(self, message: str) -> bool: - if message.startswith(SOCKETIO_MESSAGE_PREFIX): - decoded_message: SocketIOEvent = decode_socketio_42_message(message) - if ( - decoded_message.name == "server.video_stream.bitrate_data" - and "bitrate" in decoded_message.obj - ): - current_bitrate = decoded_message.obj["bitrate"] - if self._initial_bit_rate == 0: - self._initial_bit_rate = current_bitrate - self._initial_bit_rate_time = arrow.utcnow().datetime - self.logger.info( - "%s", - f"{self._initial_bit_rate=} at {self._initial_bit_rate_time.isoformat()}", - ) - return False - - # NOTE: MaG says the value might also go down, but it shall definitely change, - # if this code proves unsafe we should change it. - elapsed_time = arrow.utcnow().datetime - self._initial_bit_rate_time - if ( - elapsed_time > self.observation_time - and "bitrate" in decoded_message.obj - ): - current_bitrate = decoded_message.obj["bitrate"] - bitrate_test = bool(self._initial_bit_rate != current_bitrate) - self.logger.info( - "%s", - f"{current_bitrate=} after {elapsed_time=}: {'good!' if bitrate_test else 'failed! bitrate did not change! TIP: talk with MaG about underwater cables!'}", - ) - return bitrate_test - - return False def test_sim4life( @@ -134,7 +64,7 @@ def test_sim4life( assert len(node_ids) == 1, "Expected 1 node in the workbench!" with log_context(logging.INFO, "launch S4L") as ctx: - predicate = _S4LWaitForWebsocket(logger=ctx.logger) + predicate = S4LWaitForWebsocket(logger=ctx.logger) with page.expect_websocket( predicate, timeout=_S4L_STARTUP_SCREEN_MAX_TIME @@ -166,25 +96,4 @@ def test_sim4life( page.wait_for_timeout(3000) if check_videostreaming: - with log_context(logging.INFO, "Check videostreaming works") as ctx: - waiter = _S4LSocketIOCheckBitRateIncreasesMessagePrinter( - observation_time=datetime.timedelta( - milliseconds=_S4L_STREAMING_ESTABLISHMENT_MAX_TIME / 2.0, - ), - logger=ctx.logger, - ) - with s4l_websocket.expect_event( - "framereceived", - waiter, - timeout=_S4L_STREAMING_ESTABLISHMENT_MAX_TIME, - ): - ... - - expect( - s4l_iframe.locator("video"), - "videostreaming is not established. " - "TIP: if using playwright integrated open source chromIUM, " - "webkit or firefox this is expected, switch to chrome/msedge!!", - ).to_be_visible() - s4l_iframe.locator("video").click() - page.wait_for_timeout(3000) + check_video_streaming(page, s4l_iframe, s4l_websocket) diff --git a/tests/e2e-playwright/tests/sim4life/test_template.py b/tests/e2e-playwright/tests/sim4life/test_template.py index 65f4edb26fb..6e51214caa7 100644 --- a/tests/e2e-playwright/tests/sim4life/test_template.py +++ b/tests/e2e-playwright/tests/sim4life/test_template.py @@ -7,23 +7,17 @@ # pylint: disable=unused-variable -import datetime import logging import re from collections.abc import Callable -from dataclasses import dataclass from typing import Any, Final -import arrow -from playwright.sync_api import Page, WebSocket, expect +from playwright.sync_api import Page, WebSocket from pytest_simcore.helpers.logging_tools import log_context -from pytest_simcore.helpers.playwright import ( - MINUTE, - SECOND, - SOCKETIO_MESSAGE_PREFIX, - SocketIOEvent, - decode_socketio_42_message, - wait_for_service_running, +from pytest_simcore.helpers.playwright import MINUTE, SECOND, wait_for_service_running +from pytest_simcore.helpers.playwright_sim4life import ( + S4LWaitForWebsocket, + check_video_streaming, ) projects_uuid_pattern: Final[re.Pattern] = re.compile( @@ -37,74 +31,6 @@ ) _S4L_STARTUP_SCREEN_MAX_TIME: Final[int] = 45 * SECOND -_S4L_STREAMING_ESTABLISHMENT_MAX_TIME: Final[int] = 15 * SECOND - - -_S4L_SOCKETIO_REGEX: Final[re.Pattern] = re.compile( - r"^(?P[^:]+)://(?P[^\.]+)\.services\.(?P[^\/]+)\/socket\.io\/.+$" -) - - -@dataclass(kw_only=True) -class _S4LWaitForWebsocket: - logger: logging.Logger - - def __call__(self, new_websocket: WebSocket) -> bool: - if re.match(_S4L_SOCKETIO_REGEX, new_websocket.url): - self.logger.info("found S4L websocket!") - return True - - return False - - -@dataclass -class _S4LSocketIOMessagePrinter: - def __call__(self, message: str) -> None: - if message.startswith(SOCKETIO_MESSAGE_PREFIX): - decoded_message: SocketIOEvent = decode_socketio_42_message(message) - print("S4L WS Message:", decoded_message.name, decoded_message.obj) - - -@dataclass(kw_only=True) -class _S4LSocketIOCheckBitRateIncreasesMessagePrinter: - observation_time: datetime.timedelta - logger: logging.Logger - _initial_bit_rate: float = 0 - _initial_bit_rate_time: datetime.datetime = arrow.utcnow().datetime - - def __call__(self, message: str) -> bool: - if message.startswith(SOCKETIO_MESSAGE_PREFIX): - decoded_message: SocketIOEvent = decode_socketio_42_message(message) - if ( - decoded_message.name == "server.video_stream.bitrate_data" - and "bitrate" in decoded_message.obj - ): - current_bitrate = decoded_message.obj["bitrate"] - if self._initial_bit_rate == 0: - self._initial_bit_rate = current_bitrate - self._initial_bit_rate_time = arrow.utcnow().datetime - self.logger.info( - "%s", - f"{self._initial_bit_rate=} at {self._initial_bit_rate_time.isoformat()}", - ) - return False - - # NOTE: MaG says the value might also go down, but it shall definitely change, - # if this code proves unsafe we should change it. - elapsed_time = arrow.utcnow().datetime - self._initial_bit_rate_time - if ( - elapsed_time > self.observation_time - and "bitrate" in decoded_message.obj - ): - current_bitrate = decoded_message.obj["bitrate"] - bitrate_test = bool(self._initial_bit_rate != current_bitrate) - self.logger.info( - "%s", - f"{current_bitrate=} after {elapsed_time=}: {'good!' if bitrate_test else 'failed! bitrate did not change! TIP: talk with MaG about underwater cables!'}", - ) - return bitrate_test - - return False def test_template( @@ -124,7 +50,7 @@ def test_template( assert len(node_ids) == 1, "Expected 1 node in the workbench!" with log_context(logging.INFO, "launch S4L") as ctx: - predicate = _S4LWaitForWebsocket(logger=ctx.logger) + predicate = S4LWaitForWebsocket(logger=ctx.logger) with page.expect_websocket( predicate, timeout=_S4L_STARTUP_SCREEN_MAX_TIME @@ -156,25 +82,4 @@ def test_template( page.wait_for_timeout(3000) if check_videostreaming: - with log_context(logging.INFO, "Check videostreaming works") as ctx: - waiter = _S4LSocketIOCheckBitRateIncreasesMessagePrinter( - observation_time=datetime.timedelta( - milliseconds=_S4L_STREAMING_ESTABLISHMENT_MAX_TIME / 2.0, - ), - logger=ctx.logger, - ) - with s4l_websocket.expect_event( - "framereceived", - waiter, - timeout=_S4L_STREAMING_ESTABLISHMENT_MAX_TIME, - ): - ... - - expect( - s4l_iframe.locator("video"), - "videostreaming is not established. " - "TIP: if using playwright integrated open source chromIUM, " - "webkit or firefox this is expected, switch to chrome/msedge!!", - ).to_be_visible() - s4l_iframe.locator("video").click() - page.wait_for_timeout(3000) + check_video_streaming(page, s4l_iframe, s4l_websocket) From 9f59a26f6b85f9480c324c62fd72be9a9b7a8911 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Tue, 27 Aug 2024 13:14:12 +0200 Subject: [PATCH 17/31] More refactoring --- .../helpers/playwright_sim4life.py | 56 ++++++++++++++++++- .../tests/sim4life/test_template.py | 50 +++-------------- 2 files changed, 60 insertions(+), 46 deletions(-) diff --git a/packages/pytest-simcore/src/pytest_simcore/helpers/playwright_sim4life.py b/packages/pytest-simcore/src/pytest_simcore/helpers/playwright_sim4life.py index 96f05a979ee..0243aab8077 100644 --- a/packages/pytest-simcore/src/pytest_simcore/helpers/playwright_sim4life.py +++ b/packages/pytest-simcore/src/pytest_simcore/helpers/playwright_sim4life.py @@ -2,23 +2,33 @@ import logging import re from dataclasses import dataclass -from typing import Final +from typing import Dict, Final, Union import arrow -from playwright.sync_api import Page, WebSocket, expect +from playwright.sync_api import FrameLocator, Page, WebSocket, expect from .logging_tools import log_context from .playwright import ( SECOND, + MINUTE, SOCKETIO_MESSAGE_PREFIX, SocketIOEvent, decode_socketio_42_message, + wait_for_service_running, ) + _S4L_STREAMING_ESTABLISHMENT_MAX_TIME: Final[int] = 15 * SECOND _S4L_SOCKETIO_REGEX: Final[re.Pattern] = re.compile( r"^(?P[^:]+)://(?P[^\.]+)\.services\.(?P[^\/]+)\/socket\.io\/.+$" ) +_EC2_STARTUP_MAX_WAIT_TIME: Final[int] = 1 * MINUTE +_S4L_MAX_STARTUP_TIME: Final[int] = 1 * MINUTE +_S4L_DOCKER_PULLING_MAX_TIME: Final[int] = 10 * MINUTE +_S4L_AUTOSCALED_MAX_STARTUP_TIME: Final[int] = ( + _EC2_STARTUP_MAX_WAIT_TIME + _S4L_DOCKER_PULLING_MAX_TIME + _S4L_MAX_STARTUP_TIME +) +_S4L_STARTUP_SCREEN_MAX_TIME: Final[int] = 45 * SECOND @dataclass(kw_only=True) @@ -75,7 +85,47 @@ def __call__(self, message: str) -> bool: return False -def check_video_streaming(page: Page, s4l_iframe, s4l_websocket: WebSocket) -> None: +def launch_S4L(page: Page, node_id, log_in_and_out: WebSocket, autoscaled: bool) -> Dict[str, Union[WebSocket, FrameLocator]]: + with log_context(logging.INFO, "launch S4L") as ctx: + predicate = S4LWaitForWebsocket(logger=ctx.logger) + with page.expect_websocket( + predicate, + timeout=_S4L_STARTUP_SCREEN_MAX_TIME + + ( + _S4L_AUTOSCALED_MAX_STARTUP_TIME + if autoscaled + else _S4L_MAX_STARTUP_TIME + ) + + 10 * SECOND, + ) as ws_info: + s4l_iframe = wait_for_service_running( + page=page, + node_id=node_id, + websocket=log_in_and_out, + timeout=( + _S4L_AUTOSCALED_MAX_STARTUP_TIME + if autoscaled + else _S4L_MAX_STARTUP_TIME + ), + press_start_button=False, + ) + s4l_websocket = ws_info.value + ctx.logger.info("acquired S4L websocket!") + return { + "websocket": s4l_websocket, + "iframe" : s4l_iframe, + } + + +def interact_with_S4L(page: Page, s4l_iframe: FrameLocator) -> None: + # Wait until grid is shown + # NOTE: the startup screen should disappear very fast after the websocket was acquired + with log_context(logging.INFO, "Interact with S4l"): + s4l_iframe.get_by_test_id("tree-item-Grid").nth(0).click() + page.wait_for_timeout(3000) + + +def check_video_streaming(page: Page, s4l_iframe: FrameLocator, s4l_websocket: WebSocket) -> None: with log_context(logging.INFO, "Check videostreaming works") as ctx: waiter = _S4LSocketIOCheckBitRateIncreasesMessagePrinter( observation_time=datetime.timedelta( diff --git a/tests/e2e-playwright/tests/sim4life/test_template.py b/tests/e2e-playwright/tests/sim4life/test_template.py index 6e51214caa7..3ef9f692c3d 100644 --- a/tests/e2e-playwright/tests/sim4life/test_template.py +++ b/tests/e2e-playwright/tests/sim4life/test_template.py @@ -7,30 +7,20 @@ # pylint: disable=unused-variable -import logging import re from collections.abc import Callable from typing import Any, Final from playwright.sync_api import Page, WebSocket -from pytest_simcore.helpers.logging_tools import log_context -from pytest_simcore.helpers.playwright import MINUTE, SECOND, wait_for_service_running from pytest_simcore.helpers.playwright_sim4life import ( - S4LWaitForWebsocket, + launch_S4L, + interact_with_S4L, check_video_streaming, ) projects_uuid_pattern: Final[re.Pattern] = re.compile( r"/projects/([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})" ) -_EC2_STARTUP_MAX_WAIT_TIME: Final[int] = 1 * MINUTE -_S4L_MAX_STARTUP_TIME: Final[int] = 1 * MINUTE -_S4L_DOCKER_PULLING_MAX_TIME: Final[int] = 10 * MINUTE -_S4L_AUTOSCALED_MAX_STARTUP_TIME: Final[int] = ( - _EC2_STARTUP_MAX_WAIT_TIME + _S4L_DOCKER_PULLING_MAX_TIME + _S4L_MAX_STARTUP_TIME -) - -_S4L_STARTUP_SCREEN_MAX_TIME: Final[int] = 45 * SECOND def test_template( @@ -42,6 +32,7 @@ def test_template( check_videostreaming: bool, ): project_data = create_project_from_template_dashboard(template_id) + assert "workbench" in project_data, "Expected workbench to be in project data!" assert isinstance( project_data["workbench"], dict @@ -49,37 +40,10 @@ def test_template( node_ids: list[str] = list(project_data["workbench"]) assert len(node_ids) == 1, "Expected 1 node in the workbench!" - with log_context(logging.INFO, "launch S4L") as ctx: - predicate = S4LWaitForWebsocket(logger=ctx.logger) - with page.expect_websocket( - predicate, - timeout=_S4L_STARTUP_SCREEN_MAX_TIME - + ( - _S4L_AUTOSCALED_MAX_STARTUP_TIME - if autoscaled - else _S4L_MAX_STARTUP_TIME - ) - + 10 * SECOND, - ) as ws_info: - s4l_iframe = wait_for_service_running( - page=page, - node_id=node_ids[0], - websocket=log_in_and_out, - timeout=( - _S4L_AUTOSCALED_MAX_STARTUP_TIME - if autoscaled - else _S4L_MAX_STARTUP_TIME - ), - press_start_button=False, - ) - s4l_websocket = ws_info.value - ctx.logger.info("acquired S4L websocket!") - - # Wait until grid is shown - # NOTE: the startup screen should disappear very fast after the websocket was acquired - with log_context(logging.INFO, "Interact with S4l"): - s4l_iframe.get_by_test_id("tree-item-Grid").nth(0).click() - page.wait_for_timeout(3000) + resp = launch_S4L(page, node_ids[0], log_in_and_out, autoscaled) + s4l_websocket = resp["websocket"] + s4l_iframe = resp["iframe"] + interact_with_S4L(page, s4l_iframe) if check_videostreaming: check_video_streaming(page, s4l_iframe, s4l_websocket) From 62fe3e1198d111a6adaca7adf7fead6f38cdade5 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Tue, 27 Aug 2024 13:19:10 +0200 Subject: [PATCH 18/31] more refactoring --- .../helpers/playwright_sim4life.py | 1 - .../tests/sim4life/test_sim4life.py | 63 +++---------------- .../tests/sim4life/test_template.py | 7 +-- 3 files changed, 10 insertions(+), 61 deletions(-) diff --git a/packages/pytest-simcore/src/pytest_simcore/helpers/playwright_sim4life.py b/packages/pytest-simcore/src/pytest_simcore/helpers/playwright_sim4life.py index 0243aab8077..cb0d4089c3e 100644 --- a/packages/pytest-simcore/src/pytest_simcore/helpers/playwright_sim4life.py +++ b/packages/pytest-simcore/src/pytest_simcore/helpers/playwright_sim4life.py @@ -17,7 +17,6 @@ wait_for_service_running, ) - _S4L_STREAMING_ESTABLISHMENT_MAX_TIME: Final[int] = 15 * SECOND _S4L_SOCKETIO_REGEX: Final[re.Pattern] = re.compile( r"^(?P[^:]+)://(?P[^\.]+)\.services\.(?P[^\/]+)\/socket\.io\/.+$" diff --git a/tests/e2e-playwright/tests/sim4life/test_sim4life.py b/tests/e2e-playwright/tests/sim4life/test_sim4life.py index e7982c8803a..39b62039fbd 100644 --- a/tests/e2e-playwright/tests/sim4life/test_sim4life.py +++ b/tests/e2e-playwright/tests/sim4life/test_sim4life.py @@ -7,36 +7,17 @@ # pylint: disable=unused-variable -import logging -import re from collections.abc import Callable -from typing import Any, Final +from typing import Any from playwright.sync_api import Page, WebSocket -from pytest_simcore.helpers.logging_tools import log_context -from pytest_simcore.helpers.playwright import ( - MINUTE, - SECOND, - ServiceType, - wait_for_service_running, -) +from pytest_simcore.helpers.playwright import ServiceType from pytest_simcore.helpers.playwright_sim4life import ( - S4LWaitForWebsocket, + launch_S4L, + interact_with_S4L, check_video_streaming, ) -projects_uuid_pattern: Final[re.Pattern] = re.compile( - r"/projects/([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})" -) -_EC2_STARTUP_MAX_WAIT_TIME: Final[int] = 1 * MINUTE -_S4L_MAX_STARTUP_TIME: Final[int] = 1 * MINUTE -_S4L_DOCKER_PULLING_MAX_TIME: Final[int] = 10 * MINUTE -_S4L_AUTOSCALED_MAX_STARTUP_TIME: Final[int] = ( - _EC2_STARTUP_MAX_WAIT_TIME + _S4L_DOCKER_PULLING_MAX_TIME + _S4L_MAX_STARTUP_TIME -) - -_S4L_STARTUP_SCREEN_MAX_TIME: Final[int] = 45 * SECOND - def test_sim4life( page: Page, @@ -56,6 +37,7 @@ def test_sim4life( project_data = create_project_from_service_dashboard( ServiceType.DYNAMIC, service_key, None ) + assert "workbench" in project_data, "Expected workbench to be in project data!" assert isinstance( project_data["workbench"], dict @@ -63,37 +45,10 @@ def test_sim4life( node_ids: list[str] = list(project_data["workbench"]) assert len(node_ids) == 1, "Expected 1 node in the workbench!" - with log_context(logging.INFO, "launch S4L") as ctx: - predicate = S4LWaitForWebsocket(logger=ctx.logger) - with page.expect_websocket( - predicate, - timeout=_S4L_STARTUP_SCREEN_MAX_TIME - + ( - _S4L_AUTOSCALED_MAX_STARTUP_TIME - if autoscaled - else _S4L_MAX_STARTUP_TIME - ) - + 10 * SECOND, - ) as ws_info: - s4l_iframe = wait_for_service_running( - page=page, - node_id=node_ids[0], - websocket=log_in_and_out, - timeout=( - _S4L_AUTOSCALED_MAX_STARTUP_TIME - if autoscaled - else _S4L_MAX_STARTUP_TIME - ), - press_start_button=False, - ) - s4l_websocket = ws_info.value - ctx.logger.info("acquired S4L websocket!") - - # Wait until grid is shown - # NOTE: the startup screen should disappear very fast after the websocket was acquired - with log_context(logging.INFO, "Interact with S4l"): - s4l_iframe.get_by_test_id("tree-item-Grid").nth(0).click() - page.wait_for_timeout(3000) + resp = launch_S4L(page, node_ids[0], log_in_and_out, autoscaled) + s4l_websocket = resp["websocket"] + s4l_iframe = resp["iframe"] + interact_with_S4L(page, s4l_iframe) if check_videostreaming: check_video_streaming(page, s4l_iframe, s4l_websocket) diff --git a/tests/e2e-playwright/tests/sim4life/test_template.py b/tests/e2e-playwright/tests/sim4life/test_template.py index 3ef9f692c3d..21f7387c2b2 100644 --- a/tests/e2e-playwright/tests/sim4life/test_template.py +++ b/tests/e2e-playwright/tests/sim4life/test_template.py @@ -7,9 +7,8 @@ # pylint: disable=unused-variable -import re from collections.abc import Callable -from typing import Any, Final +from typing import Any from playwright.sync_api import Page, WebSocket from pytest_simcore.helpers.playwright_sim4life import ( @@ -18,10 +17,6 @@ check_video_streaming, ) -projects_uuid_pattern: Final[re.Pattern] = re.compile( - r"/projects/([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})" -) - def test_template( page: Page, From c1735a3c5731f4f2316455f80fb765f3b9e7484f Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Tue, 27 Aug 2024 13:29:48 +0200 Subject: [PATCH 19/31] minor --- tests/e2e-playwright/tests/conftest.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/e2e-playwright/tests/conftest.py b/tests/e2e-playwright/tests/conftest.py index 74b46ea10a3..8e1095225e0 100644 --- a/tests/e2e-playwright/tests/conftest.py +++ b/tests/e2e-playwright/tests/conftest.py @@ -429,7 +429,7 @@ def _( with log_context( logging.INFO, f"Copying template data", - ) as my_logger: + ) as copying_logger: # From the long running tasks response's urls, only their path is relevant def url_to_path(url): return urllib.parse.urlparse(url).path @@ -439,14 +439,14 @@ def wait_for_done(response): resp_data = resp_data["data"] assert "task_progress" in resp_data task_progress = resp_data["task_progress"] - my_logger.logger.info( + copying_logger.logger.info( "task progress: %s %s", task_progress["percent"], task_progress["message"], ) return False if url_to_path(response.url) == url_to_path(lrt_data["result_href"]): - my_logger.logger.info("project created") + copying_logger.logger.info("project created") return response.status == 201 return False page.expect_response(wait_for_done, timeout=timeout) From befeb97db2cce4273000a5c32c3ce199e785b064 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Tue, 27 Aug 2024 15:01:31 +0200 Subject: [PATCH 20/31] with context --- tests/e2e-playwright/tests/conftest.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/e2e-playwright/tests/conftest.py b/tests/e2e-playwright/tests/conftest.py index 8e1095225e0..43e95e2870e 100644 --- a/tests/e2e-playwright/tests/conftest.py +++ b/tests/e2e-playwright/tests/conftest.py @@ -449,7 +449,10 @@ def wait_for_done(response): copying_logger.logger.info("project created") return response.status == 201 return False - page.expect_response(wait_for_done, timeout=timeout) + with page.expect_response(wait_for_done, timeout=timeout): + # if the above calls go to fast, this test could fail + # not expected in the sim4life context though + ... else: open_button.click() if product_billable: From 00a18855ad8acfb91feb1438b82f4997dce88dc1 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Tue, 27 Aug 2024 15:05:05 +0200 Subject: [PATCH 21/31] minor --- tests/e2e-playwright/tests/conftest.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/e2e-playwright/tests/conftest.py b/tests/e2e-playwright/tests/conftest.py index 43e95e2870e..de355fe052a 100644 --- a/tests/e2e-playwright/tests/conftest.py +++ b/tests/e2e-playwright/tests/conftest.py @@ -532,8 +532,7 @@ def _(template_id: str) -> None: _textbox = page.get_by_test_id("searchBarFilter-textField-template") _textbox.fill(template_id) _textbox.press("Enter") - # OM: use this: test_id = "templateBrowserListItem_" + template_id - test_id = "studyBrowserListItem_" + template_id + test_id = "templateBrowserListItem_" + template_id page.get_by_test_id(test_id).click() return _ From 57f79a819d7d3783d95ad9d5f3792934b08d7cf4 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Tue, 27 Aug 2024 15:26:21 +0200 Subject: [PATCH 22/31] make linter happy --- tests/e2e-playwright/tests/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e-playwright/tests/conftest.py b/tests/e2e-playwright/tests/conftest.py index de355fe052a..a4a57373004 100644 --- a/tests/e2e-playwright/tests/conftest.py +++ b/tests/e2e-playwright/tests/conftest.py @@ -428,7 +428,7 @@ def _( lrt_data = lrt_data["data"] with log_context( logging.INFO, - f"Copying template data", + "Copying template data", ) as copying_logger: # From the long running tasks response's urls, only their path is relevant def url_to_path(url): From 9d2177d8e4f537d0a3c6e58ab81daf5a07356868 Mon Sep 17 00:00:00 2001 From: Odei Maiz <33152403+odeimaiz@users.noreply.github.com> Date: Tue, 27 Aug 2024 15:32:55 +0200 Subject: [PATCH 23/31] Update tests/e2e-playwright/tests/conftest.py Co-authored-by: Sylvain <35365065+sanderegg@users.noreply.github.com> --- tests/e2e-playwright/tests/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e-playwright/tests/conftest.py b/tests/e2e-playwright/tests/conftest.py index a4a57373004..fb639038b3c 100644 --- a/tests/e2e-playwright/tests/conftest.py +++ b/tests/e2e-playwright/tests/conftest.py @@ -412,7 +412,7 @@ def _( log_in_and_out.expect_event("framereceived", waiter, timeout=timeout), page.expect_response( re.compile(r"/projects/[^:]+:open"), - timeout=timeout + timeout=timeout + 5 * SECOND ) as response_info, ): # Project detail view pop-ups shows From d8bb4dbc247073d35859363517c147fe97934edc Mon Sep 17 00:00:00 2001 From: Odei Maiz <33152403+odeimaiz@users.noreply.github.com> Date: Tue, 27 Aug 2024 15:33:20 +0200 Subject: [PATCH 24/31] Update tests/e2e-playwright/tests/conftest.py Co-authored-by: Sylvain <35365065+sanderegg@users.noreply.github.com> --- tests/e2e-playwright/tests/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e-playwright/tests/conftest.py b/tests/e2e-playwright/tests/conftest.py index fb639038b3c..8dc382e1038 100644 --- a/tests/e2e-playwright/tests/conftest.py +++ b/tests/e2e-playwright/tests/conftest.py @@ -245,7 +245,7 @@ def template_id(request: pytest.FixtureRequest) -> str: if key := request.config.getoption("--template-id"): assert isinstance(key, str) return key - return os.environ["TEMPLATE_ID"] + return None @pytest.fixture(scope="session") From eb3dd633c5fbc13c9cd8f43d14c5c7a78f53262b Mon Sep 17 00:00:00 2001 From: Odei Maiz <33152403+odeimaiz@users.noreply.github.com> Date: Tue, 27 Aug 2024 15:33:31 +0200 Subject: [PATCH 25/31] Update tests/e2e-playwright/tests/conftest.py Co-authored-by: Sylvain <35365065+sanderegg@users.noreply.github.com> --- tests/e2e-playwright/tests/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e-playwright/tests/conftest.py b/tests/e2e-playwright/tests/conftest.py index 8dc382e1038..1e1c4ce1e6e 100644 --- a/tests/e2e-playwright/tests/conftest.py +++ b/tests/e2e-playwright/tests/conftest.py @@ -241,7 +241,7 @@ def service_key(request: pytest.FixtureRequest) -> str: @pytest.fixture(scope="session") -def template_id(request: pytest.FixtureRequest) -> str: +def template_id(request: pytest.FixtureRequest) -> str | None: if key := request.config.getoption("--template-id"): assert isinstance(key, str) return key From c45dea588884f9b764ee84f8bc6605bc41e7c42b Mon Sep 17 00:00:00 2001 From: Odei Maiz <33152403+odeimaiz@users.noreply.github.com> Date: Tue, 27 Aug 2024 15:33:41 +0200 Subject: [PATCH 26/31] Update tests/e2e-playwright/tests/conftest.py Co-authored-by: Sylvain <35365065+sanderegg@users.noreply.github.com> --- tests/e2e-playwright/tests/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e-playwright/tests/conftest.py b/tests/e2e-playwright/tests/conftest.py index 1e1c4ce1e6e..6ff6cdadcf2 100644 --- a/tests/e2e-playwright/tests/conftest.py +++ b/tests/e2e-playwright/tests/conftest.py @@ -397,7 +397,7 @@ def create_new_project_and_delete( def _( expected_states: tuple[RunningState] = (RunningState.NOT_STARTED,), press_open: bool = True, - template_id: str = None, + template_id: str | None = None, ) -> dict[str, Any]: assert ( len(created_project_uuids) == 0 From 4ccb3b8ad2f08b104c2c4dd8e3bd20aefc270590 Mon Sep 17 00:00:00 2001 From: Odei Maiz <33152403+odeimaiz@users.noreply.github.com> Date: Tue, 27 Aug 2024 15:34:00 +0200 Subject: [PATCH 27/31] Update tests/e2e-playwright/tests/conftest.py Co-authored-by: Sylvain <35365065+sanderegg@users.noreply.github.com> --- tests/e2e-playwright/tests/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e-playwright/tests/conftest.py b/tests/e2e-playwright/tests/conftest.py index 6ff6cdadcf2..b2d447b9b2d 100644 --- a/tests/e2e-playwright/tests/conftest.py +++ b/tests/e2e-playwright/tests/conftest.py @@ -409,7 +409,7 @@ def _( waiter = SocketIOProjectStateUpdatedWaiter(expected_states=expected_states) timeout = 60000 if template_id else 180000 with ( - log_in_and_out.expect_event("framereceived", waiter, timeout=timeout), + log_in_and_out.expect_event("framereceived", waiter, timeout=timeout + 10 * SECOND), page.expect_response( re.compile(r"/projects/[^:]+:open"), timeout=timeout + 5 * SECOND From 3678470c51a2b3b383e54e2392c8578bd9017862 Mon Sep 17 00:00:00 2001 From: Odei Maiz <33152403+odeimaiz@users.noreply.github.com> Date: Tue, 27 Aug 2024 15:34:06 +0200 Subject: [PATCH 28/31] Update tests/e2e-playwright/tests/conftest.py Co-authored-by: Sylvain <35365065+sanderegg@users.noreply.github.com> --- tests/e2e-playwright/tests/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e-playwright/tests/conftest.py b/tests/e2e-playwright/tests/conftest.py index b2d447b9b2d..8bbaa85d5ce 100644 --- a/tests/e2e-playwright/tests/conftest.py +++ b/tests/e2e-playwright/tests/conftest.py @@ -418,7 +418,7 @@ def _( # Project detail view pop-ups shows if press_open: open_button = page.get_by_test_id("openResource") - if template_id: + if template_id is not None: # it returns a Long Running Task with page.expect_response( re.compile(rf"/projects\?from_study\={template_id}") From 184974371f4b49f4232d1fe2f0b83324f3902bfc Mon Sep 17 00:00:00 2001 From: Odei Maiz <33152403+odeimaiz@users.noreply.github.com> Date: Tue, 27 Aug 2024 15:34:39 +0200 Subject: [PATCH 29/31] Update tests/e2e-playwright/tests/conftest.py Co-authored-by: Sylvain <35365065+sanderegg@users.noreply.github.com> --- tests/e2e-playwright/tests/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e-playwright/tests/conftest.py b/tests/e2e-playwright/tests/conftest.py index 8bbaa85d5ce..457f5be92d0 100644 --- a/tests/e2e-playwright/tests/conftest.py +++ b/tests/e2e-playwright/tests/conftest.py @@ -407,7 +407,7 @@ def _( f"Open project in {product_url=} as {product_billable=}", ) as ctx: waiter = SocketIOProjectStateUpdatedWaiter(expected_states=expected_states) - timeout = 60000 if template_id else 180000 + timeout = _OPENING_TUTORIAL_MAX_WAIT_TIME if template_id is not None else _OPENING_NEW_EMPTY_PROJECT_MAX_WAIT_TIME with ( log_in_and_out.expect_event("framereceived", waiter, timeout=timeout + 10 * SECOND), page.expect_response( From 635193f0cc3ab72db85da712f863e25a543c6ca9 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Tue, 27 Aug 2024 15:36:09 +0200 Subject: [PATCH 30/31] consts --- tests/e2e-playwright/tests/conftest.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/e2e-playwright/tests/conftest.py b/tests/e2e-playwright/tests/conftest.py index 457f5be92d0..fc7f0be060d 100644 --- a/tests/e2e-playwright/tests/conftest.py +++ b/tests/e2e-playwright/tests/conftest.py @@ -25,6 +25,7 @@ from pytest import Item from pytest_simcore.helpers.logging_tools import log_context from pytest_simcore.helpers.playwright import ( + SECOND, MINUTE, AutoRegisteredUser, RunningState, @@ -36,6 +37,8 @@ ) _PROJECT_CLOSING_TIMEOUT: Final[int] = 10 * MINUTE +_OPENING_NEW_EMPTY_PROJECT_MAX_WAIT_TIME: Final[int] = 30 * SECOND # or 60 as you set it but I think the default is 30 +_OPENING_TUTORIAL_MAX_WAIT_TIME: Final[int] = 3 * MINUTE def pytest_addoption(parser: pytest.Parser) -> None: From 191b79f426bdedd23e9d180d373d7c7a2f5e532a Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Tue, 27 Aug 2024 15:44:27 +0200 Subject: [PATCH 31/31] minor --- tests/e2e-playwright/tests/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e-playwright/tests/conftest.py b/tests/e2e-playwright/tests/conftest.py index fc7f0be060d..d7104c6fe70 100644 --- a/tests/e2e-playwright/tests/conftest.py +++ b/tests/e2e-playwright/tests/conftest.py @@ -37,7 +37,7 @@ ) _PROJECT_CLOSING_TIMEOUT: Final[int] = 10 * MINUTE -_OPENING_NEW_EMPTY_PROJECT_MAX_WAIT_TIME: Final[int] = 30 * SECOND # or 60 as you set it but I think the default is 30 +_OPENING_NEW_EMPTY_PROJECT_MAX_WAIT_TIME: Final[int] = 30 * SECOND _OPENING_TUTORIAL_MAX_WAIT_TIME: Final[int] = 3 * MINUTE