Skip to content

Commit 2e2b2a9

Browse files
authored
🎨E2E: add create study, create function, start mmux (#8311)
1 parent 3f7c322 commit 2e2b2a9

File tree

2 files changed

+199
-27
lines changed

2 files changed

+199
-27
lines changed

tests/e2e-playwright/tests/conftest.py

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -492,9 +492,6 @@ def _( # noqa: C901
492492
template_id: str | None,
493493
service_version: str | None,
494494
) -> dict[str, Any]:
495-
assert (
496-
len(created_project_uuids) == 0
497-
), "misuse of this fixture! only 1 study can be opened at a time. Otherwise please modify the fixture"
498495
with log_context(
499496
logging.INFO, f"Open project in {product_url=} as {is_product_billable=}"
500497
) as ctx:
@@ -597,22 +594,29 @@ def wait_for_done(response):
597594
yield _
598595

599596
# go back to dashboard and wait for project to close
600-
with ExitStack() as stack:
601-
for project_uuid in created_project_uuids:
602-
ctx = stack.enter_context(
603-
log_context(logging.INFO, f"Wait for closed project {project_uuid=}")
604-
)
605-
stack.enter_context(
606-
log_in_and_out.expect_event(
607-
"framereceived",
608-
SocketIOProjectClosedWaiter(ctx.logger),
609-
timeout=_PROJECT_CLOSING_TIMEOUT,
610-
)
597+
with log_context(logging.INFO, "Go back to dashboard") as ctx1:
598+
if page.get_by_test_id("dashboardBtn").is_visible():
599+
with ExitStack() as stack:
600+
for project_uuid in created_project_uuids:
601+
ctx = stack.enter_context(
602+
log_context(
603+
logging.INFO, f"Wait for closed project {project_uuid=}"
604+
)
605+
)
606+
stack.enter_context(
607+
log_in_and_out.expect_event(
608+
"framereceived",
609+
SocketIOProjectClosedWaiter(ctx.logger),
610+
timeout=_PROJECT_CLOSING_TIMEOUT,
611+
)
612+
)
613+
if created_project_uuids:
614+
page.get_by_test_id("dashboardBtn").click()
615+
page.get_by_test_id("confirmDashboardBtn").click()
616+
else:
617+
ctx1.logger.warning(
618+
"Cannot go back to dashboard, 'dashboard' button is not visible, we are probably already there"
611619
)
612-
if created_project_uuids:
613-
with log_context(logging.INFO, "Go back to dashboard"):
614-
page.get_by_test_id("dashboardBtn").click()
615-
page.get_by_test_id("confirmDashboardBtn").click()
616620

617621
for project_uuid in created_project_uuids:
618622
with log_context(

tests/e2e-playwright/tests/metamodeling/test_response_surface_modeling.py

Lines changed: 177 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,20 @@
1+
# pylint: disable=logging-fstring-interpolation
2+
# pylint:disable=no-value-for-parameter
3+
# pylint:disable=protected-access
4+
# pylint:disable=redefined-outer-name
5+
# pylint:disable=too-many-arguments
6+
# pylint:disable=too-many-statements
7+
# pylint:disable=unused-argument
8+
# pylint:disable=unused-variable
9+
10+
import json
111
import logging
212
import re
3-
from collections.abc import Callable
13+
from collections.abc import Callable, Iterator
414
from typing import Any, Final
515

6-
from playwright.sync_api import Page
16+
import pytest
17+
from playwright.sync_api import APIRequestContext, Page
718
from pydantic import AnyUrl
819
from pytest_simcore.helpers.logging_tools import log_context
920
from pytest_simcore.helpers.playwright import (
@@ -14,10 +25,71 @@
1425
)
1526

1627
_WAITING_FOR_SERVICE_TO_START: Final[int] = 5 * MINUTE
28+
_WAITING_FOR_SERVICE_TO_APPEAR: Final[int] = 2 * MINUTE
1729
_DEFAULT_RESPONSE_TO_WAIT_FOR: Final[re.Pattern] = re.compile(
1830
r"/flask/list_function_job_collections_for_functionid"
1931
)
2032

33+
_STUDY_FUNCTION_NAME: Final[str] = "playwright_test_study_for_rsm"
34+
_FUNCTION_NAME: Final[str] = "playwright_test_function"
35+
36+
37+
@pytest.fixture
38+
def create_function_from_project(
39+
api_request_context: APIRequestContext,
40+
is_product_billable: bool,
41+
product_url: AnyUrl,
42+
) -> Iterator[Callable[[Page, str], dict[str, Any]]]:
43+
created_function_uuids: list[str] = []
44+
45+
def _create_function_from_project(
46+
page: Page,
47+
project_uuid: str,
48+
) -> dict[str, Any]:
49+
with log_context(
50+
logging.INFO,
51+
f"Convert {project_uuid=} / {_STUDY_FUNCTION_NAME} to a function",
52+
) as ctx:
53+
with page.expect_response(re.compile(rf"/projects/{project_uuid}")):
54+
page.get_by_test_id(f"studyBrowserListItem_{project_uuid}").click()
55+
page.wait_for_timeout(2000)
56+
page.get_by_text("create function").first.click()
57+
page.wait_for_timeout(2000)
58+
59+
with page.expect_response(
60+
lambda response: re.compile(r"/functions").search(response.url)
61+
is not None
62+
and response.request.method == "POST"
63+
) as create_function_response:
64+
page.get_by_test_id("create_function_page_btn").click()
65+
assert (
66+
create_function_response.value.ok
67+
), f"Failed to create function: {create_function_response.value.status}"
68+
function_data = create_function_response.value.json()
69+
70+
ctx.logger.info(
71+
"Created function: %s", f"{json.dumps(function_data['data'], indent=2)}"
72+
)
73+
74+
page.keyboard.press("Escape")
75+
created_function_uuids.append(function_data["data"]["uuid"])
76+
return function_data["data"]
77+
78+
yield _create_function_from_project
79+
80+
# cleanup the functions
81+
for function_uuid in created_function_uuids:
82+
with log_context(
83+
logging.INFO,
84+
f"Delete function with {function_uuid=} in {product_url=} as {is_product_billable=}",
85+
):
86+
response = api_request_context.delete(
87+
f"{product_url}v0/functions/{function_uuid}"
88+
)
89+
assert (
90+
response.status == 204
91+
), f"Unexpected error while deleting project: '{response.json()}'"
92+
2193

2294
def test_response_surface_modeling(
2395
page: Page,
@@ -29,15 +101,88 @@ def test_response_surface_modeling(
29101
service_version: str | None,
30102
product_url: AnyUrl,
31103
is_service_legacy: bool,
104+
create_function_from_project: Callable[[Page, str], dict[str, Any]],
32105
):
106+
# 1. create the initial study with jsonifier
107+
with log_context(logging.INFO, "Create new study for function"):
108+
jsonifier_project_data = create_project_from_service_dashboard(
109+
ServiceType.COMPUTATIONAL, "jsonifier", None, service_version
110+
)
111+
assert (
112+
"workbench" in jsonifier_project_data
113+
), "Expected workbench to be in project data!"
114+
assert isinstance(
115+
jsonifier_project_data["workbench"], dict
116+
), "Expected workbench to be a dict!"
117+
node_ids: list[str] = list(jsonifier_project_data["workbench"])
118+
assert len(node_ids) == 1, "Expected 1 node in the workbench!"
119+
120+
# select the jsonifier, it's the second one as the study has the same name
121+
page.get_by_test_id("nodeTreeItem").filter(has_text="jsonifier").all()[
122+
1
123+
].click()
124+
125+
# create the probe
126+
with page.expect_response(
127+
lambda response: re.compile(
128+
rf"/projects/{jsonifier_project_data['uuid']}"
129+
).search(response.url)
130+
is not None
131+
and response.request.method == "PATCH"
132+
):
133+
page.get_by_test_id("connect_probe_btn_number_3").click()
134+
135+
# # create the parameter
136+
page.get_by_test_id("connect_input_btn_number_1").click()
137+
with page.expect_response(
138+
lambda response: re.compile(
139+
rf"/projects/{jsonifier_project_data['uuid']}"
140+
).search(response.url)
141+
is not None
142+
and response.request.method == "PATCH"
143+
):
144+
page.get_by_text("new parameter").click()
145+
146+
# rename the project to identify it
147+
page.get_by_test_id("studyTitleRenamer").click()
148+
with page.expect_response(
149+
lambda response: re.compile(
150+
rf"/projects/{jsonifier_project_data['uuid']}"
151+
).search(response.url)
152+
is not None
153+
and response.request.method == "PATCH"
154+
):
155+
page.get_by_test_id("studyTitleRenamer").locator("input").fill(
156+
_STUDY_FUNCTION_NAME
157+
)
158+
159+
# 2. go back to dashboard
33160
with (
34-
log_context(
35-
logging.INFO,
36-
f"Waiting for {service_key} to be responsive (waiting for {_DEFAULT_RESPONSE_TO_WAIT_FOR})",
37-
),
38-
page.expect_response(
39-
_DEFAULT_RESPONSE_TO_WAIT_FOR, timeout=_WAITING_FOR_SERVICE_TO_START
40-
),
161+
log_context(logging.INFO, "Go back to dashboard"),
162+
page.expect_response(re.compile(r"/projects\?.+")) as list_projects_response,
163+
):
164+
page.get_by_test_id("dashboardBtn").click()
165+
page.get_by_test_id("confirmDashboardBtn").click()
166+
assert (
167+
list_projects_response.value.ok
168+
), f"Failed to list projects: {list_projects_response.value.status}"
169+
project_listing = list_projects_response.value.json()
170+
assert "data" in project_listing
171+
assert len(project_listing["data"]) > 0
172+
# find the project we just created, it's the first one
173+
our_project = project_listing["data"][0]
174+
assert (
175+
our_project["name"] == _STUDY_FUNCTION_NAME
176+
), f"Expected to find our project named {_STUDY_FUNCTION_NAME} in {project_listing}"
177+
178+
# 3. convert it to a function
179+
create_function_from_project(page, our_project["uuid"])
180+
181+
# 3. start a RSM with that function
182+
183+
with log_context(
184+
logging.INFO,
185+
f"Waiting for {service_key} to be responsive (waiting for {_DEFAULT_RESPONSE_TO_WAIT_FOR})",
41186
):
42187
project_data = create_project_from_service_dashboard(
43188
ServiceType.DYNAMIC, service_key, None, service_version
@@ -58,3 +203,26 @@ def test_response_surface_modeling(
58203
product_url=product_url,
59204
is_service_legacy=is_service_legacy,
60205
)
206+
207+
service_iframe = page.frame_locator("iframe")
208+
with log_context(logging.INFO, "Waiting for the RSM to be ready..."):
209+
service_iframe.get_by_role("grid").wait_for(
210+
state="visible", timeout=_WAITING_FOR_SERVICE_TO_APPEAR
211+
)
212+
213+
page.wait_for_timeout(10000)
214+
215+
# # select the function
216+
# service_iframe.get_by_role("gridcell", name=_FUNCTION_NAME).click()
217+
218+
# # Find the first input field (textbox) in the iframe
219+
# min_input_field = service_iframe.get_by_role("textbox").nth(0)
220+
# min_input_field.fill("1")
221+
# max_input_field = service_iframe.get_by_role("textbox").nth(1)
222+
# max_input_field.fill("10")
223+
224+
# # click on next
225+
# service_iframe.get_by_role("button", name="Next").click()
226+
227+
# # then we wait a long time
228+
# page.wait_for_timeout(1 * MINUTE)

0 commit comments

Comments
 (0)