diff --git a/tests/e2e-playwright/Makefile b/tests/e2e-playwright/Makefile index dfee6eb774a..4b684cfa823 100644 --- a/tests/e2e-playwright/Makefile +++ b/tests/e2e-playwright/Makefile @@ -161,6 +161,12 @@ $(SLEEPERS_INPUT_FILE) $(JUPYTER_LAB_INPUT_FILE) $(CLASSIC_TIP_INPUT_FILE) $(S4L if [ "$@" = "$(JUPYTER_LAB_INPUT_FILE)" ]; then \ read -p "Enter the size of the large file (human readable form e.g. 3Gib): " LARGE_FILE_SIZE; \ echo "--service-key=jupyter-math --large-file-size=$$LARGE_FILE_SIZE" >> $@; \ + read -p "Enter the service version (default to latest): " SERVICE_VERSION; \ + if [ -z "$$SERVICE_VERSION" ]; then \ + echo "No service version specified, using default."; \ + else \ + echo "--service-version=$$SERVICE_VERSION" >> $@; \ + fi; \ elif [ "$@" = "$(S4L_INPUT_FILE)" ]; then \ read -p "Do you want to check the videostreaming ? (requires to run with chrome/msedge) [y/n]: " VIDEOSTREAM; \ if [ "$$VIDEOSTREAM" = "y" ]; then \ @@ -173,6 +179,12 @@ $(SLEEPERS_INPUT_FILE) $(JUPYTER_LAB_INPUT_FILE) $(CLASSIC_TIP_INPUT_FILE) $(S4L else \ read -p "Enter the service key: " SERVICE_KEY; \ echo "--service-key=$$SERVICE_KEY" >> $@; \ + read -p "Enter the service version (default to latest): " SERVICE_VERSION; \ + if [ -z "$$SERVICE_VERSION" ]; then \ + echo "No service version specified, using default."; \ + else \ + echo "--service-version=$$SERVICE_VERSION" >> $@; \ + fi; \ fi; \ elif [ "$@" = "$(SLEEPERS_INPUT_FILE)" ]; then \ read -p "Enter the number of sleepers: " NUM_SLEEPERS; \ diff --git a/tests/e2e-playwright/tests/conftest.py b/tests/e2e-playwright/tests/conftest.py index a3402b7746d..5bf3d71a932 100644 --- a/tests/e2e-playwright/tests/conftest.py +++ b/tests/e2e-playwright/tests/conftest.py @@ -112,6 +112,13 @@ def pytest_addoption(parser: pytest.Parser) -> None: default=False, help="Whether service is a legacy service (no sidecar)", ) + group.addoption( + "--service-version", + action="store", + type=str, + default=None, + help="The service version option defines a service specific version", + ) group.addoption( "--template-id", action="store", @@ -272,6 +279,14 @@ def is_service_legacy(request: pytest.FixtureRequest) -> bool: return TypeAdapter(bool).validate_python(autoscaled) +@pytest.fixture(scope="session") +def service_version(request: pytest.FixtureRequest) -> str | None: + if key := request.config.getoption("--service-version"): + assert isinstance(key, str) + return key + return None + + @pytest.fixture(scope="session") def template_id(request: pytest.FixtureRequest) -> str | None: if key := request.config.getoption("--template-id"): @@ -438,6 +453,22 @@ def _open_with_resources(page: Page, *, click_it: bool): return open_with_resources_button +def _select_service_version(page: Page, *, version: str) -> None: + try: + # since https://github.com/ITISFoundation/osparc-simcore/pull/7060 + with log_context(logging.INFO, msg=f"selecting version {version}"): + page.get_by_test_id("serviceSelectBox").click(timeout=5 * SECOND) + with page.expect_response( + re.compile(r"/catalog/services/.+/resources"), timeout=1.5 * 5 * SECOND + ): + page.get_by_test_id(f"serviceVersionItem_{version}").click( + timeout=5 * SECOND + ) + except TimeoutError: + # we try the non robust way + page.get_by_label("Version").select_option(version) + + @pytest.fixture def create_new_project_and_delete( page: Page, @@ -445,16 +476,19 @@ def create_new_project_and_delete( is_product_billable: bool, api_request_context: APIRequestContext, product_url: AnyUrl, -) -> Iterator[Callable[[tuple[RunningState], bool], dict[str, Any]]]: +) -> Iterator[ + Callable[[tuple[RunningState], bool, str | None, str | None], dict[str, Any]] +]: """The first available service currently displayed in the dashboard will be opened NOTE: cannot be used multiple times or going back to dashboard will fail!! """ created_project_uuids = [] def _( - expected_states: tuple[RunningState] = (RunningState.NOT_STARTED,), - press_open: bool = True, - template_id: str | None = None, + expected_states: tuple[RunningState], + press_open: bool, + template_id: str | None, + service_version: str | None, ) -> dict[str, Any]: assert ( len(created_project_uuids) == 0 @@ -527,6 +561,8 @@ def wait_for_done(response): # not expected in the sim4life context though ... else: + if service_version is not None: + _select_service_version(page, version=service_version) open_button.click() if is_product_billable: _open_with_resources(page, click_it=True) @@ -618,7 +654,9 @@ def find_and_start_service_in_dashboard( page: Page, ) -> Callable[[ServiceType, str, str | None], None]: def _( - service_type: ServiceType, service_name: str, service_key_prefix: str | None + service_type: ServiceType, + service_name: str, + service_key_prefix: str | None, ) -> None: with log_context(logging.INFO, f"Finding {service_name=} in dashboard"): page.get_by_test_id("servicesTabBtn").click() @@ -638,13 +676,13 @@ def _( def create_project_from_new_button( start_study_from_plus_button: Callable[[str], None], create_new_project_and_delete: Callable[ - [tuple[RunningState], bool], dict[str, Any] + [tuple[RunningState], bool, str | None, str | None], dict[str, Any] ], ) -> Callable[[str], dict[str, Any]]: def _(plus_button_test_id: str) -> dict[str, Any]: start_study_from_plus_button(plus_button_test_id) expected_states = (RunningState.UNKNOWN,) - return create_new_project_and_delete(expected_states, False) + return create_new_project_and_delete(expected_states, False, None, None) return _ @@ -652,12 +690,14 @@ 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]], -) -> Callable[[ServiceType, str, str | None], dict[str, Any]]: + create_new_project_and_delete: Callable[ + [tuple[RunningState], bool, str | None, str | None], dict[str, Any] + ], +) -> Callable[[str], dict[str, Any]]: def _(template_id: str) -> dict[str, Any]: find_and_click_template_in_dashboard(template_id) expected_states = (RunningState.UNKNOWN,) - return create_new_project_and_delete(expected_states, True, template_id) + return create_new_project_and_delete(expected_states, True, template_id, None) return _ @@ -665,10 +705,15 @@ def _(template_id: str) -> dict[str, Any]: @pytest.fixture def create_project_from_service_dashboard( find_and_start_service_in_dashboard: Callable[[ServiceType, str, str | None], None], - create_new_project_and_delete: Callable[[tuple[RunningState]], dict[str, Any]], -) -> Callable[[ServiceType, str, str | None], dict[str, Any]]: + create_new_project_and_delete: Callable[ + [tuple[RunningState], bool, str | None, str | None], dict[str, Any] + ], +) -> Callable[[ServiceType, str, str | None, str | None], dict[str, Any]]: def _( - service_type: ServiceType, service_name: str, service_key_prefix: str | None + service_type: ServiceType, + service_name: str, + service_key_prefix: str | None, + service_version: str | None, ) -> dict[str, Any]: find_and_start_service_in_dashboard( service_type, service_name, service_key_prefix @@ -676,7 +721,9 @@ def _( expected_states = (RunningState.UNKNOWN,) if service_type is ServiceType.COMPUTATIONAL: expected_states = (RunningState.NOT_STARTED,) - return create_new_project_and_delete(expected_states, True) + return create_new_project_and_delete( + expected_states, True, None, service_version + ) return _ diff --git a/tests/e2e-playwright/tests/jupyterlabs/test_jupyterlab.py b/tests/e2e-playwright/tests/jupyterlabs/test_jupyterlab.py index fcd20bbbd04..f61d510b09b 100644 --- a/tests/e2e-playwright/tests/jupyterlabs/test_jupyterlab.py +++ b/tests/e2e-playwright/tests/jupyterlabs/test_jupyterlab.py @@ -64,9 +64,10 @@ def test_jupyterlab( page: Page, log_in_and_out: RestartableWebSocket, create_project_from_service_dashboard: Callable[ - [ServiceType, str, str | None], dict[str, Any] + [ServiceType, str, str | None, str | None], dict[str, Any] ], service_key: str, + service_version: str | None, large_file_size: ByteSize, large_file_block_size: ByteSize, product_url: AnyUrl, @@ -86,7 +87,7 @@ def test_jupyterlab( ), ): project_data = create_project_from_service_dashboard( - ServiceType.DYNAMIC, service_key, None + ServiceType.DYNAMIC, service_key, None, service_version ) assert "workbench" in project_data, "Expected workbench to be in project data!" assert isinstance( diff --git a/tests/e2e-playwright/tests/sim4life/test_sim4life.py b/tests/e2e-playwright/tests/sim4life/test_sim4life.py index 10a9ddcf97e..b3747da27b1 100644 --- a/tests/e2e-playwright/tests/sim4life/test_sim4life.py +++ b/tests/e2e-playwright/tests/sim4life/test_sim4life.py @@ -23,11 +23,12 @@ def test_sim4life( page: Page, create_project_from_service_dashboard: Callable[ - [ServiceType, str, str | None], dict[str, Any] + [ServiceType, str, str | None, str | None], dict[str, Any] ], create_project_from_new_button: Callable[[str], dict[str, Any]], log_in_and_out: RestartableWebSocket, service_key: str, + service_version: str | None, use_plus_button: bool, is_autoscaled: bool, check_videostreaming: bool, @@ -38,7 +39,7 @@ def test_sim4life( project_data = create_project_from_new_button(service_key) else: project_data = create_project_from_service_dashboard( - ServiceType.DYNAMIC, service_key, None + ServiceType.DYNAMIC, service_key, None, service_version ) assert "workbench" in project_data, "Expected workbench to be in project data!" diff --git a/tests/e2e-playwright/tests/sleepers/test_sleepers.py b/tests/e2e-playwright/tests/sleepers/test_sleepers.py index 72a506ef979..570511b158d 100644 --- a/tests/e2e-playwright/tests/sleepers/test_sleepers.py +++ b/tests/e2e-playwright/tests/sleepers/test_sleepers.py @@ -37,9 +37,9 @@ _WAITING_FOR_CLUSTER_MAX_WAITING_TIME: Final[int] = 5 * MINUTE _WAITING_FOR_STARTED_MAX_WAITING_TIME: Final[int] = 5 * MINUTE _WAITING_FOR_SUCCESS_MAX_WAITING_TIME_PER_SLEEPER: Final[int] = 1 * MINUTE -_WAITING_FOR_FILE_NAMES_MAX_WAITING_TIME: Final[ - datetime.timedelta -] = datetime.timedelta(seconds=30) +_WAITING_FOR_FILE_NAMES_MAX_WAITING_TIME: Final[datetime.timedelta] = ( + datetime.timedelta(seconds=30) +) _WAITING_FOR_FILE_NAMES_WAIT_INTERVAL: Final[datetime.timedelta] = datetime.timedelta( seconds=1 ) @@ -82,14 +82,14 @@ def test_sleepers( page: Page, log_in_and_out: RestartableWebSocket, create_project_from_service_dashboard: Callable[ - [ServiceType, str, str | None], dict[str, Any] + [ServiceType, str, str | None, str | None], dict[str, Any] ], start_and_stop_pipeline: Callable[..., SocketIOEvent], num_sleepers: int, input_sleep_time: int | None, ): project_data = create_project_from_service_dashboard( - ServiceType.COMPUTATIONAL, "sleeper", "itis" + ServiceType.COMPUTATIONAL, "sleeper", "itis", None ) # we are now in the workbench diff --git a/tests/e2e-playwright/tests/tip/conftest.py b/tests/e2e-playwright/tests/tip/conftest.py index b0d979921ed..094c8b8f78e 100644 --- a/tests/e2e-playwright/tests/tip/conftest.py +++ b/tests/e2e-playwright/tests/tip/conftest.py @@ -27,12 +27,12 @@ def _( def create_tip_plan_from_dashboard( find_and_start_tip_plan_in_dashboard: Callable[[str], None], create_new_project_and_delete: Callable[ - [tuple[RunningState], bool], dict[str, Any] + [tuple[RunningState], bool, str | None, str | None], dict[str, Any] ], ) -> Callable[[str], dict[str, Any]]: def _(plan_name_test_id: str) -> dict[str, Any]: find_and_start_tip_plan_in_dashboard(plan_name_test_id) expected_states = (RunningState.UNKNOWN,) - return create_new_project_and_delete(expected_states, press_open=False) + return create_new_project_and_delete(expected_states, False, None, None) return _