Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 22 additions & 18 deletions tests/e2e-playwright/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -492,9 +492,6 @@ def _( # noqa: C901
template_id: str | None,
service_version: str | None,
) -> dict[str, Any]:
assert (
len(created_project_uuids) == 0
), "misuse of this fixture! only 1 study can be opened at a time. Otherwise please modify the fixture"
with log_context(
logging.INFO, f"Open project in {product_url=} as {is_product_billable=}"
) as ctx:
Expand Down Expand Up @@ -597,22 +594,29 @@ def wait_for_done(response):
yield _

# go back to dashboard and wait for project to close
with ExitStack() as stack:
for project_uuid in created_project_uuids:
ctx = stack.enter_context(
log_context(logging.INFO, f"Wait for closed project {project_uuid=}")
)
stack.enter_context(
log_in_and_out.expect_event(
"framereceived",
SocketIOProjectClosedWaiter(ctx.logger),
timeout=_PROJECT_CLOSING_TIMEOUT,
)
with log_context(logging.INFO, "Go back to dashboard") as ctx1:
if page.get_by_test_id("dashboardBtn").is_visible():
with ExitStack() as stack:
for project_uuid in created_project_uuids:
ctx = stack.enter_context(
log_context(
logging.INFO, f"Wait for closed project {project_uuid=}"
)
)
stack.enter_context(
log_in_and_out.expect_event(
"framereceived",
SocketIOProjectClosedWaiter(ctx.logger),
timeout=_PROJECT_CLOSING_TIMEOUT,
)
)
if created_project_uuids:
page.get_by_test_id("dashboardBtn").click()
page.get_by_test_id("confirmDashboardBtn").click()
else:
ctx1.logger.warning(
"Cannot go back to dashboard, 'dashboard' button is not visible, we are probably already there"
)
if created_project_uuids:
with log_context(logging.INFO, "Go back to dashboard"):
page.get_by_test_id("dashboardBtn").click()
page.get_by_test_id("confirmDashboardBtn").click()

for project_uuid in created_project_uuids:
with log_context(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
# pylint: disable=logging-fstring-interpolation
# pylint:disable=no-value-for-parameter
# pylint:disable=protected-access
# pylint:disable=redefined-outer-name
# pylint:disable=too-many-arguments
# pylint:disable=too-many-statements
# pylint:disable=unused-argument
# pylint:disable=unused-variable

import json
import logging
import re
from collections.abc import Callable
from collections.abc import Callable, Iterator
from typing import Any, Final

from playwright.sync_api import Page
import pytest
from playwright.sync_api import APIRequestContext, Page
from pydantic import AnyUrl
from pytest_simcore.helpers.logging_tools import log_context
from pytest_simcore.helpers.playwright import (
Expand All @@ -14,10 +25,71 @@
)

_WAITING_FOR_SERVICE_TO_START: Final[int] = 5 * MINUTE
_WAITING_FOR_SERVICE_TO_APPEAR: Final[int] = 2 * MINUTE
_DEFAULT_RESPONSE_TO_WAIT_FOR: Final[re.Pattern] = re.compile(
r"/flask/list_function_job_collections_for_functionid"
)

_STUDY_FUNCTION_NAME: Final[str] = "playwright_test_study_for_rsm"
_FUNCTION_NAME: Final[str] = "playwright_test_function"


@pytest.fixture
def create_function_from_project(
api_request_context: APIRequestContext,
is_product_billable: bool,
product_url: AnyUrl,
) -> Iterator[Callable[[Page, str], dict[str, Any]]]:
created_function_uuids: list[str] = []

def _create_function_from_project(
page: Page,
project_uuid: str,
) -> dict[str, Any]:
with log_context(
logging.INFO,
f"Convert {project_uuid=} / {_STUDY_FUNCTION_NAME} to a function",
) as ctx:
with page.expect_response(re.compile(rf"/projects/{project_uuid}")):
page.get_by_test_id(f"studyBrowserListItem_{project_uuid}").click()
page.wait_for_timeout(2000)
page.get_by_text("create function").first.click()
page.wait_for_timeout(2000)

with page.expect_response(
lambda response: re.compile(r"/functions").search(response.url)
is not None
and response.request.method == "POST"
) as create_function_response:
page.get_by_test_id("create_function_page_btn").click()
assert (
create_function_response.value.ok
), f"Failed to create function: {create_function_response.value.status}"
function_data = create_function_response.value.json()

ctx.logger.info(
"Created function: %s", f"{json.dumps(function_data['data'], indent=2)}"
)

page.keyboard.press("Escape")
created_function_uuids.append(function_data["data"]["uuid"])
return function_data["data"]

yield _create_function_from_project

# cleanup the functions
for function_uuid in created_function_uuids:
with log_context(
logging.INFO,
f"Delete function with {function_uuid=} in {product_url=} as {is_product_billable=}",
):
response = api_request_context.delete(
f"{product_url}v0/functions/{function_uuid}"
)
assert (
response.status == 204
), f"Unexpected error while deleting project: '{response.json()}'"


def test_response_surface_modeling(
page: Page,
Expand All @@ -29,15 +101,88 @@ def test_response_surface_modeling(
service_version: str | None,
product_url: AnyUrl,
is_service_legacy: bool,
create_function_from_project: Callable[[Page, str], dict[str, Any]],
):
# 1. create the initial study with jsonifier
with log_context(logging.INFO, "Create new study for function"):
jsonifier_project_data = create_project_from_service_dashboard(
ServiceType.COMPUTATIONAL, "jsonifier", None, service_version
)
assert (
"workbench" in jsonifier_project_data
), "Expected workbench to be in project data!"
assert isinstance(
jsonifier_project_data["workbench"], dict
), "Expected workbench to be a dict!"
node_ids: list[str] = list(jsonifier_project_data["workbench"])
assert len(node_ids) == 1, "Expected 1 node in the workbench!"

# select the jsonifier, it's the second one as the study has the same name
page.get_by_test_id("nodeTreeItem").filter(has_text="jsonifier").all()[
1
].click()

# create the probe
with page.expect_response(
lambda response: re.compile(
rf"/projects/{jsonifier_project_data['uuid']}"
).search(response.url)
is not None
and response.request.method == "PATCH"
):
page.get_by_test_id("connect_probe_btn_number_3").click()

# # create the parameter
page.get_by_test_id("connect_input_btn_number_1").click()
with page.expect_response(
lambda response: re.compile(
rf"/projects/{jsonifier_project_data['uuid']}"
).search(response.url)
is not None
and response.request.method == "PATCH"
):
page.get_by_text("new parameter").click()

# rename the project to identify it
page.get_by_test_id("studyTitleRenamer").click()
with page.expect_response(
lambda response: re.compile(
rf"/projects/{jsonifier_project_data['uuid']}"
).search(response.url)
is not None
and response.request.method == "PATCH"
):
page.get_by_test_id("studyTitleRenamer").locator("input").fill(
_STUDY_FUNCTION_NAME
)

# 2. go back to dashboard
with (
log_context(
logging.INFO,
f"Waiting for {service_key} to be responsive (waiting for {_DEFAULT_RESPONSE_TO_WAIT_FOR})",
),
page.expect_response(
_DEFAULT_RESPONSE_TO_WAIT_FOR, timeout=_WAITING_FOR_SERVICE_TO_START
),
log_context(logging.INFO, "Go back to dashboard"),
page.expect_response(re.compile(r"/projects\?.+")) as list_projects_response,
):
page.get_by_test_id("dashboardBtn").click()
page.get_by_test_id("confirmDashboardBtn").click()
assert (
list_projects_response.value.ok
), f"Failed to list projects: {list_projects_response.value.status}"
project_listing = list_projects_response.value.json()
assert "data" in project_listing
assert len(project_listing["data"]) > 0
# find the project we just created, it's the first one
our_project = project_listing["data"][0]
assert (
our_project["name"] == _STUDY_FUNCTION_NAME
), f"Expected to find our project named {_STUDY_FUNCTION_NAME} in {project_listing}"

# 3. convert it to a function
create_function_from_project(page, our_project["uuid"])

# 3. start a RSM with that function

with log_context(
logging.INFO,
f"Waiting for {service_key} to be responsive (waiting for {_DEFAULT_RESPONSE_TO_WAIT_FOR})",
):
project_data = create_project_from_service_dashboard(
ServiceType.DYNAMIC, service_key, None, service_version
Expand All @@ -58,3 +203,26 @@ def test_response_surface_modeling(
product_url=product_url,
is_service_legacy=is_service_legacy,
)

service_iframe = page.frame_locator("iframe")
with log_context(logging.INFO, "Waiting for the RSM to be ready..."):
service_iframe.get_by_role("grid").wait_for(
state="visible", timeout=_WAITING_FOR_SERVICE_TO_APPEAR
)

page.wait_for_timeout(10000)

# # select the function
# service_iframe.get_by_role("gridcell", name=_FUNCTION_NAME).click()

# # Find the first input field (textbox) in the iframe
# min_input_field = service_iframe.get_by_role("textbox").nth(0)
# min_input_field.fill("1")
# max_input_field = service_iframe.get_by_role("textbox").nth(1)
# max_input_field.fill("10")

# # click on next
# service_iframe.get_by_role("button", name="Next").click()

# # then we wait a long time
# page.wait_for_timeout(1 * MINUTE)
Loading