diff --git a/services/static-webserver/client/source/class/osparc/component/widget/PersistentIframe.js b/services/static-webserver/client/source/class/osparc/component/widget/PersistentIframe.js index 40263a6ccb9..44f2eabf5c1 100644 --- a/services/static-webserver/client/source/class/osparc/component/widget/PersistentIframe.js +++ b/services/static-webserver/client/source/class/osparc/component/widget/PersistentIframe.js @@ -57,7 +57,7 @@ qx.Class.define("osparc.component.widget.PersistentIframe", { showZoomButton: { check: "Boolean", init: true, - apply: "__applyShowZoomButton" + event: "changeShowZoomButton" }, /** @@ -66,7 +66,7 @@ qx.Class.define("osparc.component.widget.PersistentIframe", { showRestartButton: { check: "Boolean", init: true, - apply: "__applyShowRestartButton" + event: "changeShowRestartButton" } }, @@ -109,16 +109,22 @@ qx.Class.define("osparc.component.widget.PersistentIframe", { this.fireEvent("restart"); }, this); osparc.utils.Utils.setIdToWidget(restartButton, "iFrameRestartBtn"); + this.bind("showRestartButton", restartButton, "visibility", { + converter: show => show ? "visible" : "excluded" + }); appRoot.add(restartButton, { top: this.self().HIDDEN_TOP }); - let zoomButton = this.__zoomButton = new qx.ui.form.Button(null).set({ + const zoomButton = this.__zoomButton = new qx.ui.form.Button(null).set({ icon: this.self().getZoomIcon(false), zIndex: 20, backgroundColor: "transparent", decorator: null }); osparc.utils.Utils.setIdToWidget(zoomButton, this.self().getMaximizeWidgetId(false)); + this.bind("showZoomButton", zoomButton, "visibility", { + converter: show => show ? "visible" : "excluded" + }); appRoot.add(zoomButton, { top: this.self().HIDDEN_TOP }); @@ -175,14 +181,6 @@ qx.Class.define("osparc.component.widget.PersistentIframe", { qx.event.message.Bus.getInstance().dispatchByName("maximizeIframe", this.hasState("maximized")); }, - __applyShowZoomButton: function(show) { - show ? this.__zoomButton.show() : this.__zoomButton.exclude(); - }, - - __applyShowRestartButton: function(show) { - show ? this.__restartButton.show() : this.__restartButton.exclude(); - }, - __syncIframePos: function() { if (this.__syncScheduled) { return; diff --git a/services/static-webserver/client/source/class/osparc/dashboard/Dashboard.js b/services/static-webserver/client/source/class/osparc/dashboard/Dashboard.js index 368e98df440..57e1f822c20 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/Dashboard.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/Dashboard.js @@ -88,11 +88,13 @@ qx.Class.define("osparc.dashboard.Dashboard", { __createMainViewLayout: function() { const permissions = osparc.data.Permissions.getInstance(); const tabs = [{ + id: "studiesTabBtn", label: osparc.utils.Utils.isProduct("s4llight") ? this.tr("PROJECTS") : this.tr("STUDIES"), buildLayout: this.__createStudyBrowser }]; if (permissions.canDo("dashboard.templates.read")) { const templatesTab = { + id: "templatesTabBtn", label: osparc.utils.Utils.isProduct("s4llight") ? this.tr("TUTORIALS") : this.tr("TEMPLATES"), buildLayout: this.__createTemplateBrowser }; @@ -100,17 +102,19 @@ qx.Class.define("osparc.dashboard.Dashboard", { } if (!osparc.utils.Utils.isProduct("s4llight") && permissions.canDo("dashboard.services.read")) { tabs.push({ + id: "servicesTabBtn", label: this.tr("SERVICES"), buildLayout: this.__createServiceBrowser }); } if (!osparc.utils.Utils.isProduct("s4llight")) { tabs.push({ + id: "dataTabBtn", label: this.tr("DATA"), buildLayout: this.__createDataBrowser} ); } - tabs.forEach(({label, buildLayout}) => { + tabs.forEach(({id, label, buildLayout}) => { const tabPage = new qx.ui.tabview.Page(label).set({ appearance: "dashboard-page" }); @@ -119,7 +123,6 @@ qx.Class.define("osparc.dashboard.Dashboard", { font: "text-16", minWidth: 70 }); - const id = label.getMessageId().toLowerCase() + "TabBtn"; osparc.utils.Utils.setIdToWidget(tabButton, id); tabPage.setLayout(new qx.ui.layout.Grow()); 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 5f53cdd64a2..5fe1b27ddb3 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -201,10 +201,10 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { }); }, - __addNewStudyFromServiceButtons: function(mode, services, serviceKey) { + __addNewStudyFromServiceButtons: function(mode, services, serviceKey, newButtonInfo) { + // Make sure we have access to that service const versions = osparc.utils.Services.getVersions(services, serviceKey); - if (versions.length) { - const newButtonInfo = this.self().EXPECTED_S4L_SERVICE_KEYS[serviceKey]; + if (versions.length && newButtonInfo) { const title = newButtonInfo.title; const desc = newButtonInfo.decription; const newStudyFromServiceButton = (mode === "grid") ? new osparc.dashboard.GridButtonNew(title, desc) : new osparc.dashboard.ListButtonNew(title, desc); @@ -223,8 +223,9 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { store.getServicesOnly(false) .then(services => { // add new plus buttons if key services exists - Object.keys(this.self().EXPECTED_S4L_SERVICE_KEYS).forEach(serviceKey => { - this.__addNewStudyFromServiceButtons(mode, services, serviceKey); + const newButtonsInfo = this.self().EXPECTED_S4L_SERVICE_KEYS; + Object.keys(newButtonsInfo).forEach(serviceKey => { + this.__addNewStudyFromServiceButtons(mode, services, serviceKey, newButtonsInfo[serviceKey]); }); }); }, @@ -234,8 +235,9 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { store.getServicesOnly(false) .then(services => { // add new plus buttons if key services exists - Object.keys(this.self().EXPECTED_S4L_LIGHT_SERVICE_KEYS).forEach(serviceKey => { - this.__addNewStudyFromServiceButtons(mode, services, serviceKey); + const newButtonsInfo = this.self().EXPECTED_S4L_LIGHT_SERVICE_KEYS; + Object.keys(newButtonsInfo).forEach(serviceKey => { + this.__addNewStudyFromServiceButtons(mode, services, serviceKey, newButtonsInfo[serviceKey]); }); }); }, diff --git a/services/static-webserver/client/source/class/osparc/data/model/Node.js b/services/static-webserver/client/source/class/osparc/data/model/Node.js index 2231b92485d..90bb1a2f6f5 100644 --- a/services/static-webserver/client/source/class/osparc/data/model/Node.js +++ b/services/static-webserver/client/source/class/osparc/data/model/Node.js @@ -991,10 +991,8 @@ qx.Class.define("osparc.data.model.Node", { }, __initLoadingPage: function() { - const loadingPage = new osparc.ui.message.Loading(this.__getLoadingPageHeader(), this.__getExtraMessages(), true); - if (osparc.utils.Utils.isProduct("s4llight")) { - loadingPage.setShowZoomButton(false); - } + const showZoomMaximizeButton = !osparc.utils.Utils.isProduct("s4llight"); + const loadingPage = new osparc.ui.message.Loading(this.__getLoadingPageHeader(), this.__getExtraMessages(), showZoomMaximizeButton); this.addListener("changeLabel", () => loadingPage.setHeader(this.__getLoadingPageHeader()), this); this.getStatus().addListener("changeInteractive", () => { loadingPage.setHeader(this.__getLoadingPageHeader()); diff --git a/services/static-webserver/client/source/class/osparc/ui/message/Loading.js b/services/static-webserver/client/source/class/osparc/ui/message/Loading.js index b24bf3a290e..bf8e605bb78 100644 --- a/services/static-webserver/client/source/class/osparc/ui/message/Loading.js +++ b/services/static-webserver/client/source/class/osparc/ui/message/Loading.js @@ -172,7 +172,6 @@ qx.Class.define("osparc.ui.message.Loading", { this.__messages.add(widget); }, - // from osparc.component.widget.PersistentIframe maximizeIFrame: function(maximize) { if (maximize) { diff --git a/services/static-webserver/client/source/resource/osparc/favicon-s4llight.png b/services/static-webserver/client/source/resource/osparc/favicon-s4llight.png new file mode 100644 index 00000000000..eb259a92b53 Binary files /dev/null and b/services/static-webserver/client/source/resource/osparc/favicon-s4llight.png differ diff --git a/tests/e2e/tutorials/sim4life-light.js b/tests/e2e/tutorials/sim4life-light.js new file mode 100644 index 00000000000..0269fae5639 --- /dev/null +++ b/tests/e2e/tutorials/sim4life-light.js @@ -0,0 +1,86 @@ +// node ti-plan.js [url] [user] [password] [timeout] [--demo] + +const utils = require('../utils/utils'); +const tutorialBase = require('./tutorialBase'); + +const args = process.argv.slice(2); +const { + url, + user, + pass, + newUser, + startTimeout, + enableDemoMode +} = utils.parseCommandLineArguments(args) + +const studyName = "S4L_light"; + +async function runTutorial() { + const tutorial = new tutorialBase.TutorialBase(url, studyName, user, pass, newUser, enableDemoMode); + let studyId; + try { + await tutorial.start(); + + // make sure only sim4life-dy is available + const services = tutorial.getReceivedServices(); + if (services.length && services.every(service => service.key === "simcore/services/dynamic/sim4life-dy")) { + console.log("Expected services received"); + } + else { + throw "Check exposed services"; + } + + // start Sim4Life Light + const studyData = await tutorial.startSim4LifeLight(); + studyId = studyData["data"]["uuid"]; + + const workbenchData = utils.extractWorkbenchData(studyData["data"]); + console.log(workbenchData); + const s4lNodeId = workbenchData["nodeIds"][0]; + await tutorial.waitForServices( + workbenchData["studyId"], + [s4lNodeId], + startTimeout, + false + ); + + await tutorial.waitFor(15000, 'Wait for some time'); + + // do some basic interaction + const s4lIframe = await tutorial.getIframe(s4lNodeId); + const modelTree = await s4lIframe.$('.model-tree'); + const modelItems = await modelTree.$$('.MuiTreeItem-label'); + const nLabels = modelItems.length; + if (nLabels > 1) { + modelItems[0].click(); + await tutorial.waitFor(2000, 'Model clicked'); + await tutorial.takeScreenshot('ModelClicked'); + modelItems[1].click(); + await tutorial.waitFor(2000, 'Grid clicked'); + await tutorial.takeScreenshot('GridlClicked'); + } + } + catch (err) { + tutorial.setTutorialFailed(true); + console.log('Tutorial error: ' + err); + throw "Tutorial Failed"; + } + finally { + if (studyId) { + await tutorial.toDashboard() + await tutorial.removeStudy(studyId, 20000); + } + await tutorial.logOut(); + await tutorial.close(); + } + + if (tutorial.getTutorialFailed()) { + throw "Tutorial Failed"; + } +} + +runTutorial() + .catch(error => { + console.log('Puppeteer error: ' + error); + process.exit(1); + }); diff --git a/tests/e2e/tutorials/tutorialBase.js b/tests/e2e/tutorials/tutorialBase.js index 2e61f90f685..c294f5b42ca 100644 --- a/tests/e2e/tutorials/tutorialBase.js +++ b/tests/e2e/tutorials/tutorialBase.js @@ -19,6 +19,8 @@ class TutorialBase { this.__page = null; this.__responsesQueue = null; + this.__services = null; + this.__interval = null; this.__failed = false; @@ -169,6 +171,9 @@ class TutorialBase { const resp = await this.__responsesQueue.waitUntilResponse(resource.request); const respData = resp["data"]; console.log(resource.name + " received:", respData.length); + if (resource.name === "Services") { + this.__services = respData; + } if (resource.listThem) { respData.forEach(item => { console.log(" - ", item.name); @@ -182,6 +187,10 @@ class TutorialBase { } } + getReceivedServices() { + return this.__services; + } + async checkFirstStudyId(studyId) { await this.__page.waitForSelector('[osparc-test-id="studiesList"]'); await this.waitFor(5000, "Wait for studies to be loaded"); @@ -227,6 +236,28 @@ class TutorialBase { return resp; } + async startSim4LifeLight() { + await this.takeScreenshot("startSim4LifeLight_before"); + this.__responsesQueue.addResponseListener("projects?from_study="); + this.__responsesQueue.addResponseListener(":open"); + let resp = null; + try { + await this.waitFor(2000); + await auto.dashboardStartSim4LifeLight(this.__page); + await this.__responsesQueue.waitUntilResponse("projects?from_study="); + resp = await this.__responsesQueue.waitUntilResponse(":open"); + const studyId = resp["data"]["uuid"]; + console.log("Study ID:", studyId); + } + catch (err) { + console.error(`Sim4Life Light could not be started:\n`, err); + throw (err); + } + await this.waitFor(2000); + await this.takeScreenshot("startSim4LifeLight_after"); + return resp; + } + async openStudyLink(openStudyTimeout = 20000) { this.__responsesQueue.addResponseListener(":open"); diff --git a/tests/e2e/utils/auto.js b/tests/e2e/utils/auto.js index c46adc4cadb..f6c4e62f9e8 100644 --- a/tests/e2e/utils/auto.js +++ b/tests/e2e/utils/auto.js @@ -100,6 +100,13 @@ async function dashboardNewPlan(page) { await utils.waitAndClick(page, '[osparc-test-id="newPlanButton"]'); } +async function dashboardStartSim4LifeLight(page) { + console.log("Start Sim4Lide from + button"); + + await __dashboardStudiesBrowser(page); + await utils.waitAndClick(page, '[osparc-test-id="startS4LButton"]'); +} + async function toDashboard(page) { console.log("To Dashboard"); @@ -370,6 +377,7 @@ module.exports = { dashboardAbout, dashboardPreferences, dashboardNewPlan, + dashboardStartSim4LifeLight, dashboardOpenFirstTemplate, dashboardOpenService, showLogger,