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 3586e7ad2b2..0e837f54a70 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js @@ -710,7 +710,8 @@ qx.Class.define("osparc.dashboard.CardBase", { resourceDetails.addListener("openStudy", e => { const openCB = () => win.close(); const studyId = e.getData()["uuid"]; - this._startStudyById(studyId, openCB, null); + const isStudyCreation = false; + this._startStudyById(studyId, openCB, null, isStudyCreation); }); return resourceDetails; }, diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js index d3f898f82ec..56edaad0fbf 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js @@ -401,9 +401,11 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", { openButton.openResource = true; openButton.addListener("execute", () => { switch (resourceData["resourceType"]) { - case "study": - this._startStudyById(resourceData["uuid"]); + case "study": { + const isStudyCreation = false; + this._startStudyById(resourceData["uuid"], null, null, isStudyCreation); break; + } case "template": this._createStudyFromTemplate(resourceData); break; @@ -428,7 +430,8 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", { resourceDetails.addListener("openStudy", e => { const openCB = () => win.close(); const studyId = e.getData()["uuid"]; - this._startStudyById(studyId, openCB, null); + const isStudyCreation = false; + this._startStudyById(studyId, openCB, null, isStudyCreation); }); resourceDetails.addListener("openTemplate", e => { win.close(); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index fc43ab29393..5f04f9e25bd 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -126,7 +126,8 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { const loadStudyId = osparc.store.Store.getInstance().getCurrentStudyId(); if (loadStudyId) { const cancelCB = () => this.reloadResources(); - this._startStudyById(loadStudyId, null, cancelCB); + const isStudyCreation = false; + this._startStudyById(loadStudyId, null, cancelCB, isStudyCreation); } else { this.reloadResources(); } @@ -807,7 +808,8 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { }; osparc.data.Resources.fetch("studies", "delete", params, studyId); }; - this._startStudyById(studyId, openCB, cancelCB); + const isStudyCreation = true; + this._startStudyById(studyId, openCB, cancelCB, isStudyCreation); }) .catch(err => { this._hideLoadingPage(); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/TemplateBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/TemplateBrowser.js index c7da02e6ae7..a82fdd72bb9 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/TemplateBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/TemplateBrowser.js @@ -351,7 +351,8 @@ qx.Class.define("osparc.dashboard.TemplateBrowser", { }, __editTemplate: function(studyData) { - this._startStudyById(studyData.uuid); + const isStudyCreation = false; + this._startStudyById(studyData.uuid, null, null, isStudyCreation); }, __doDeleteTemplate: function(studyData) { diff --git a/services/static-webserver/client/source/class/osparc/desktop/SlideshowView.js b/services/static-webserver/client/source/class/osparc/desktop/SlideshowView.js index 5eec0bbbe8e..f8197e54005 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/SlideshowView.js +++ b/services/static-webserver/client/source/class/osparc/desktop/SlideshowView.js @@ -388,8 +388,8 @@ qx.Class.define("osparc.desktop.SlideshowView", { const node = this.__nodeView.getNode(); if (node.isDynamic()) { - // Start it. First wait a second because the function depends on the node's state which might not be available yet - setTimeout(() => node.requestStartNode(), 1000); + // Start it. First wait 2 seconds because the function depends on the node's state which might not be available yet + setTimeout(() => node.requestStartNode(), 2000); } }, diff --git a/services/static-webserver/client/source/class/osparc/node/BaseNodeView.js b/services/static-webserver/client/source/class/osparc/node/BaseNodeView.js index be17e8b6928..06c280598f5 100644 --- a/services/static-webserver/client/source/class/osparc/node/BaseNodeView.js +++ b/services/static-webserver/client/source/class/osparc/node/BaseNodeView.js @@ -380,6 +380,7 @@ qx.Class.define("osparc.node.BaseNodeView", { if (node.isDynamic()) { node.attachHandlersToStartButton(this.__nodeStartButton); + osparc.utils.Utils.setIdToWidget(this.__nodeStartButton, "Start_"+node.getNodeId()) node.attachVisibilityHandlerToStopButton(this.__nodeStopButton); node.attachExecuteHandlerToStopButton(this.__nodeStopButton); } diff --git a/tests/e2e-playwright/tests/ti_plan.py b/tests/e2e-playwright/tests/ti_plan.py new file mode 100644 index 00000000000..02c968e9e5b --- /dev/null +++ b/tests/e2e-playwright/tests/ti_plan.py @@ -0,0 +1,139 @@ +# pylint: disable=redefined-outer-name +# pylint: disable=unused-argument +# pylint: disable=unused-variable +# pylint: disable=too-many-arguments +# pylint: disable=too-many-statements +# pylint: disable=unnecessary-lambda + +import re +from http import HTTPStatus +from typing import Final + +from playwright.sync_api import APIRequestContext, Page +from pydantic import AnyUrl +from pytest_simcore.playwright_utils import on_web_socket_default_handler +from tenacity import Retrying +from tenacity.retry import retry_if_exception_type +from tenacity.stop import stop_after_attempt +from tenacity.wait import wait_fixed + +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_tip( + page: Page, + log_in_and_out: None, + api_request_context: APIRequestContext, + product_url: AnyUrl, + product_billable: bool, +): + # connect and listen to websocket + page.on("websocket", on_web_socket_default_handler) + + # open studies tab and filter + page.get_by_test_id("studiesTabBtn").click() + _textbox = page.get_by_test_id("searchBarFilter-textField-study") + _textbox.fill("Classic TI") + _textbox.press("Enter") + + with page.expect_response(re.compile(r"/projects/[^:]+:open")) as response_info: + page.get_by_test_id("newTIPlanButton").click() + if product_billable: + # Open project with default resources + page.get_by_test_id("openWithResources").click() + page.wait_for_timeout(1000) + + project_data = response_info.value.json() + assert project_data + project_uuid = project_data["data"]["uuid"] + print("project uuid: ", project_uuid) + node_ids = [] + for node_id in project_data["data"]["workbench"].keys(): + print("node_id: ", node_id) + node_ids.append(node_id) + + # Electrode Selector + es_page = page.frame_locator(f'[osparc-test-id="iframe_{node_ids[0]}"]') + es_page.get_by_test_id("TargetStructure_Selector").click(timeout=300000) + es_page.get_by_test_id( + "TargetStructure_Target_(Targets_combined) Hypothalamus" + ).click() + electrode_selections = [ + ["E1+", "FT9"], + ["E1-", "FT7"], + ["E2+", "T9"], + ["E2-", "T7"], + ] + for selection in electrode_selections: + group_id = "ElectrodeGroup_" + selection[0] + "_Start" + electrode_id = "Electrode_" + selection[1] + es_page.get_by_test_id(group_id).click() + es_page.get_by_test_id(electrode_id).click() + es_page.get_by_test_id("FinishSetUp").click() + page.wait_for_timeout(10000) + # check outputs + expected_outputs = ["output.json"] + text_on_output_button = f"Outputs ({len(expected_outputs)})" + page.get_by_test_id("outputsBtn").get_by_text(text_on_output_button).click() + page.wait_for_timeout(5000) + + # Move to next step + page.get_by_test_id("AppMode_NextBtn").click() + + # Optimal Configuration Identification + ti_page = page.frame_locator(f'[osparc-test-id="iframe_{node_ids[1]}"]') + ti_page.get_by_role("button", name="Run Optimization").click(timeout=300000) + page.wait_for_timeout(20000) + ti_page.get_by_role("button", name="Load Analysis").click() + page.wait_for_timeout(20000) + ti_page.get_by_role("button", name="Load").nth(1).click() # Load Analysis is first + page.wait_for_timeout(20000) + ti_page.get_by_role("button", name="Add to Report (0)").nth(0).click() + page.wait_for_timeout(20000) + ti_page.get_by_role("button", name="Export to S4L").click() + page.wait_for_timeout(20000) + ti_page.get_by_role("button", name="Add to Report (1)").nth(1).click() + page.wait_for_timeout(20000) + ti_page.get_by_role("button", name="Export Report").click() + page.wait_for_timeout(20000) + # check outputs + expected_outputs = ["output_1.zip", "TIP_report.pdf", "results.csv"] + text_on_output_button = f"Outputs ({len(expected_outputs)})" + page.get_by_test_id("outputsBtn").get_by_text(text_on_output_button).click() + page.wait_for_timeout(5000) + + # Move to next step + page.get_by_test_id("AppMode_NextBtn").click() + + # Sim4Life PostPro + s4l_postpro_page = page.frame_locator(f'[osparc-test-id="iframe_{node_ids[2]}"]') + # click on the mode button + s4l_postpro_page.get_by_test_id("mode-button-postro").click(timeout=300000) + # click on the surface viewer + s4l_postpro_page.get_by_test_id("tree-item-ti_field.cache").click() + s4l_postpro_page.get_by_test_id("tree-item-SurfaceViewer").click() + page.wait_for_timeout(5000) + + # Going back to dashboard + page.get_by_test_id("dashboardBtn").click() + page.get_by_test_id("confirmDashboardBtn").click() + page.wait_for_timeout(1000) + + # Going back to projects/studies view (In Sim4life projects:=studies) + page.get_by_test_id("studiesTabBtn").click() + page.wait_for_timeout(1000) + + # The project is closing, wait until it is closed and delete it (currently waits max=5 minutes) + for attempt in Retrying( + wait=wait_fixed(5), + stop=stop_after_attempt(60), # 5*60= 300 seconds + retry=retry_if_exception_type(AssertionError), + reraise=True, + ): + with attempt: + resp = api_request_context.delete( + f"{product_url}v0/projects/{project_uuid}" + ) + assert resp.status == HTTPStatus.NO_CONTENT diff --git a/tests/e2e/tutorials/ti-plan.js b/tests/e2e/tutorials/ti-plan.js index 122756cab92..d4b18a48ee8 100644 --- a/tests/e2e/tutorials/ti-plan.js +++ b/tests/e2e/tutorials/ti-plan.js @@ -89,7 +89,7 @@ async function runTutorial() { // Click "Load Analysis" button const buttonsLoadAnalysis = await utils.getButtonsWithText(postProIframe, "Load Analysis"); await buttonsLoadAnalysis[0].click(); - await tutorial.waitFor(20000, "Loading anaylsis"); + await tutorial.waitFor(20000, "Loading analysis"); await tutorial.takeScreenshot("postpro_load_analysis"); // Click on the first "Load" button const buttonsLoad = await utils.getButtonsWithText(postProIframe, "Load");