diff --git a/packages/pytest-simcore/src/pytest_simcore/playwright_utils.py b/packages/pytest-simcore/src/pytest_simcore/playwright_utils.py index 3c593c0f795..ff0e376a80e 100644 --- a/packages/pytest-simcore/src/pytest_simcore/playwright_utils.py +++ b/packages/pytest-simcore/src/pytest_simcore/playwright_utils.py @@ -88,15 +88,18 @@ def __call__(self, message: str) -> bool: @dataclass class SocketIOOsparcMessagePrinter: + include_logger_messages: bool = False + def __call__(self, message: str) -> None: osparc_messages = [ - "logger", "nodeUpdated", "nodeProgress", "projectStateUpdated", "serviceDiskUsage", "walletOsparcCreditsUpdated", ] + if self.include_logger_messages: + osparc_messages.append("logger") if message.startswith("42"): decoded_message: SocketIOEvent = decode_socketio_42_message(message) diff --git a/services/static-webserver/client/source/class/osparc/data/model/Workbench.js b/services/static-webserver/client/source/class/osparc/data/model/Workbench.js index 571a88a9566..ae0f4ffe7c5 100644 --- a/services/static-webserver/client/source/class/osparc/data/model/Workbench.js +++ b/services/static-webserver/client/source/class/osparc/data/model/Workbench.js @@ -68,7 +68,8 @@ qx.Class.define("osparc.data.model.Workbench", { }, statics: { - CANT_ADD_NODE: qx.locale.Manager.tr("Nodes can't be added while the pipeline is running") + CANT_ADD_NODE: qx.locale.Manager.tr("Nodes can't be added while the pipeline is running"), + CANT_DELETE_NODE: qx.locale.Manager.tr("Nodes can't be deleted while the pipeline is running") }, members: { @@ -510,6 +511,10 @@ qx.Class.define("osparc.data.model.Workbench", { if (!osparc.data.Permissions.getInstance().canDo("study.node.delete", true)) { return false; } + if (this.getStudy().isPipelineRunning()) { + osparc.FlashMessenger.getInstance().logAs(this.self().CANT_DELETE_NODE, "ERROR"); + return false; + } let node = this.getNode(nodeId); if (node) { diff --git a/services/static-webserver/client/source/class/osparc/desktop/WorkbenchView.js b/services/static-webserver/client/source/class/osparc/desktop/WorkbenchView.js index 8d853881bd4..4129a945c9e 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/WorkbenchView.js +++ b/services/static-webserver/client/source/class/osparc/desktop/WorkbenchView.js @@ -15,9 +15,7 @@ ************************************************************************ */ -/** - * - */ +/* eslint-disable no-underscore-dangle */ qx.Class.define("osparc.desktop.WorkbenchView", { extend: qx.ui.splitpane.Pane, @@ -99,10 +97,8 @@ qx.Class.define("osparc.desktop.WorkbenchView", { __nodesTree: null, __storagePage: null, __studyOptionsPage: null, - __infoPage: null, - __settingsPage: null, - __outputsPage: null, - __nodeOptionsPage: null, + __fileInfoPage: null, + __serviceOptionsPage: null, __workbenchPanel: null, __workbenchPanelPage: null, __workbenchUI: null, @@ -399,24 +395,13 @@ qx.Class.define("osparc.desktop.WorkbenchView", { studyOptionsPage.exclude(); tabViewSecondary.add(studyOptionsPage); - const infoPage = this.__infoPage = this.__createTabPage("@FontAwesome5Solid/info", this.tr("Information")); - infoPage.exclude(); - tabViewSecondary.add(infoPage); + const fileInfoPage = this.__fileInfoPage = this.__createTabPage("@FontAwesome5Solid/info", this.tr("Information")); + fileInfoPage.exclude(); + tabViewSecondary.add(fileInfoPage); - const settingsPage = this.__settingsPage = this.__createTabPage("@FontAwesome5Solid/sign-in-alt", this.tr("Settings")); - settingsPage.exclude(); - tabViewSecondary.add(settingsPage); - - const outputsPage = this.__outputsPage = this.__createTabPage("@FontAwesome5Solid/sign-out-alt", this.tr("Outputs")); - osparc.utils.Utils.setIdToWidget(outputsPage.getChildControl("button"), "outputsTabButton"); - outputsPage.exclude(); - tabViewSecondary.add(outputsPage); - - const nodeOptionsPage = this.__nodeOptionsPage = this.__createTabPage("@FontAwesome5Solid/cogs", this.tr("Service Options")); - nodeOptionsPage.getLayout().setSpacing(20); - osparc.utils.Utils.setIdToWidget(nodeOptionsPage.getChildControl("button"), "nodeOptionsTabButton"); - nodeOptionsPage.exclude(); - tabViewSecondary.add(nodeOptionsPage); + const serviceOptionsPage = this.__serviceOptionsPage = this.__createTabPage("@FontAwesome5Solid/exchange-alt", this.tr("Service options")); + serviceOptionsPage.exclude(); + tabViewSecondary.add(serviceOptionsPage); this.__addTopBarSpacer(topBar); @@ -884,10 +869,8 @@ qx.Class.define("osparc.desktop.WorkbenchView", { __populateSecondPanel: function(node) { [ this.__studyOptionsPage, - this.__infoPage, - this.__settingsPage, - this.__outputsPage, - this.__nodeOptionsPage + this.__fileInfoPage, + this.__serviceOptionsPage ].forEach(page => { page.removeAll(); page.getChildControl("button").exclude(); @@ -1079,10 +1062,10 @@ qx.Class.define("osparc.desktop.WorkbenchView", { __populateSecondPanelFilePicker: function(filePicker) { const fpView = new osparc.file.FilePicker(filePicker, "workbench"); if (osparc.file.FilePicker.hasOutputAssigned(filePicker.getOutputs())) { - this.__infoPage.getChildControl("button").show(); - this.getChildControl("side-panel-right-tabs").setSelection([this.__infoPage]); + this.__fileInfoPage.getChildControl("button").show(); + this.getChildControl("side-panel-right-tabs").setSelection([this.__fileInfoPage]); - this.__infoPage.add(fpView, { + this.__fileInfoPage.add(fpView, { flex: 1 }); } else { @@ -1090,10 +1073,10 @@ qx.Class.define("osparc.desktop.WorkbenchView", { const tabViewLeftPanel = this.getChildControl("side-panel-left-tabs"); tabViewLeftPanel.setSelection([this.__storagePage]); - this.__settingsPage.getChildControl("button").show(); - this.getChildControl("side-panel-right-tabs").setSelection([this.__settingsPage]); + this.__serviceOptionsPage.getChildControl("button").show(); + this.getChildControl("side-panel-right-tabs").setSelection([this.__serviceOptionsPage]); - this.__settingsPage.add(fpView, { + this.__serviceOptionsPage.add(fpView, { flex: 1 }); } @@ -1105,123 +1088,83 @@ qx.Class.define("osparc.desktop.WorkbenchView", { }, __populateSecondPanelParameter: function(parameter) { - this.__settingsPage.getChildControl("button").show(); - this.getChildControl("side-panel-right-tabs").setSelection([this.__settingsPage]); + this.__serviceOptionsPage.getChildControl("button").show(); + this.getChildControl("side-panel-right-tabs").setSelection([this.__serviceOptionsPage]); const view = new osparc.node.ParameterEditor(parameter); view.buildForm(false); - this.__settingsPage.add(view, { + this.__serviceOptionsPage.add(view, { flex: 1 }); }, __populateSecondPanelNode: async function(node) { - this.__settingsPage.getChildControl("button").show(); - this.__outputsPage.getChildControl("button").show(); - if (![this.__settingsPage, this.__outputsPage].includes(this.getChildControl("side-panel-right-tabs").getSelection()[0])) { - this.getChildControl("side-panel-right-tabs").setSelection([this.__settingsPage]); - } + this.__serviceOptionsPage.getChildControl("button").show(); + this.getChildControl("side-panel-right-tabs").setSelection([this.__serviceOptionsPage]); + + const spacing = 8; + const vBox = new qx.ui.container.Composite(new qx.ui.layout.VBox(spacing*2)); + // INPUTS FORM if (node.isPropertyInitialized("propsForm") && node.getPropsForm()) { - const scrollContainer = new qx.ui.container.Scroll(); - scrollContainer.add(node.getPropsForm()); - this.__settingsPage.add(scrollContainer, { - flex: 1 + const inputsForm = node.getPropsForm().set({ + allowGrowX: false + }); + const inputs = new osparc.desktop.PanelView(this.tr("Inputs"), inputsForm); + inputs._innerContainer.set({ + margin: spacing }); + vBox.add(inputs); } + // OUTPUTS + const outputsBox = new qx.ui.container.Composite(new qx.ui.layout.VBox(spacing)); if (node.hasOutputs()) { const nodeOutputs = new osparc.widget.NodeOutputs(node, node.getMetaData().outputs).set({ offerProbes: true }); - this.__outputsPage.add(nodeOutputs); + outputsBox.add(nodeOutputs); } - - const outputFilesBtn = new qx.ui.form.Button(this.tr("Service data"), "@FontAwesome5Solid/folder-open/14").set({ - allowGrowX: false + const outputs = new osparc.desktop.PanelView(this.tr("Outputs"), outputsBox); + outputs._innerContainer.set({ + margin: spacing }); - osparc.utils.Utils.setIdToWidget(outputFilesBtn, "nodeOutputFilesBtn"); - outputFilesBtn.addListener("execute", () => osparc.node.BaseNodeView.openNodeDataManager(node)); - this.__outputsPage.add(outputFilesBtn); - - const showPage = await this.__populateNodeOptionsPage(node); - // if it's deprecated or retired show the LifeCycleView right away - if (showPage && node.hasOutputs() && node.isDynamic() && (node.isDeprecated() || node.isRetired())) { - this.getChildControl("side-panel-right-tabs").setSelection([this.__nodeOptionsPage]); + vBox.add(outputs); + + // NODE OPTIONS + const nodeOptions = this.__getNodeOptionsPage(node); + if (nodeOptions) { + const options = new osparc.desktop.PanelView(this.tr("Options"), nodeOptions); + options._innerContainer.set({ + margin: spacing + }); + nodeOptions.bind("visibility", options, "visibility"); + vBox.add(options); } + + const scrollContainer = new qx.ui.container.Scroll(); + scrollContainer.add(vBox); + this.__serviceOptionsPage.add(scrollContainer, { + flex: 1 + }); }, - __populateNodeOptionsPage: async function(node) { + __getNodeOptionsPage: function(node) { if (osparc.auth.Data.getInstance().isGuest()) { - return false; - } - - let showPage = false; - let showStartStopButton = false; - - const sections = []; - - // Life Cycle - if ( - node.isDynamic() && - (node.isUpdatable() || node.isDeprecated() || node.isRetired()) - ) { - const lifeCycleView = new osparc.node.LifeCycleView(node); - node.addListener("versionChanged", () => this.__populateSecondPanel(node)); - sections.push(lifeCycleView); - showPage = true; - showStartStopButton = true; + return null; } - // Boot Options - if (node.hasBootModes()) { - const bootOptionsView = new osparc.node.BootOptionsView(node); - node.addListener("bootModeChanged", () => this.__populateSecondPanel(node)); - sections.push(bootOptionsView); - showPage = true; - showStartStopButton = true; - } - - // Update Resource Limits - if ( - await osparc.data.Permissions.getInstance().checkCanDo("override_services_specifications") && - (node.isComputational() || node.isDynamic()) - ) { - const updateResourceLimitsView = new osparc.node.UpdateResourceLimitsView(node); - node.addListener("limitsChanged", () => this.__populateSecondPanel(node)); - sections.push(updateResourceLimitsView); - showPage = true; - showStartStopButton |= node.isDynamic(); - } - - this.__nodeOptionsPage.removeAll(); - if (showPage) { - const introLayout = new qx.ui.container.Composite(new qx.ui.layout.VBox(10)); - const title = new qx.ui.basic.Label(this.tr("Service Options")).set({ - font: "text-14" - }); - introLayout.add(title); - - if (showStartStopButton) { - // Only available to dynamic services - const instructions = new qx.ui.basic.Label(this.tr("To proceed with the following actions, the service needs to be Stopped.")).set({ - font: "text-13", - rich: true, - wrap: true - }); - introLayout.add(instructions); - - const startStopButton = new osparc.node.StartStopButton(); - startStopButton.setNode(node); - introLayout.add(startStopButton); - } - - this.__nodeOptionsPage.add(introLayout); - sections.forEach(section => this.__nodeOptionsPage.add(section)); - this.__nodeOptionsPage.getChildControl("button").setVisibility(showPage ? "visible" : "excluded"); - } + const nodeOptions = new osparc.widget.NodeOptions(node); + nodeOptions.buildLayout(); + [ + "versionChanged", + "bootModeChanged", + "limitsChanged" + ].forEach(eventName => { + nodeOptions.addListener(eventName, () => this.__populateSecondPanel(node)); + }); - return showPage; + return nodeOptions; }, getLogger: function() { diff --git a/services/static-webserver/client/source/class/osparc/form/renderer/PropFormBase.js b/services/static-webserver/client/source/class/osparc/form/renderer/PropFormBase.js index 50a06a6db15..f6110e38204 100644 --- a/services/static-webserver/client/source/class/osparc/form/renderer/PropFormBase.js +++ b/services/static-webserver/client/source/class/osparc/form/renderer/PropFormBase.js @@ -38,14 +38,16 @@ qx.Class.define("osparc.form.renderer.PropFormBase", { this.base(arguments, form); - const fl = this._getLayout(); - fl.setSpacingY(0); // so that the "excluded" rows do not take any space - fl.setColumnFlex(this.self().GRID_POS.LABEL, 0); - fl.setColumnAlign(this.self().GRID_POS.LABEL, "left", "top"); - fl.setColumnFlex(this.self().GRID_POS.INFO, 0); - fl.setColumnAlign(this.self().GRID_POS.INFO, "left", "middle"); - fl.setColumnFlex(this.self().GRID_POS.CTRL_FIELD, 1); - fl.setColumnMinWidth(this.self().GRID_POS.CTRL_FIELD, 50); + // override qx.ui.form.renderer.Single's grid layout + const grid = this.getLayout(); + grid.setSpacingY(0); // so that the "excluded" rows do not take any space + grid.setColumnFlex(this.self().GRID_POS.LABEL, 1); + grid.setColumnFlex(this.self().GRID_POS.INFO, 0); + grid.setColumnFlex(this.self().GRID_POS.CTRL_FIELD, 1); + grid.setColumnFlex(this.self().GRID_POS.UNIT, 0); + grid.setColumnFlex(this.self().GRID_POS.FIELD_LINK_UNLINK, 0); + grid.setColumnMinWidth(this.self().GRID_POS.CTRL_FIELD, 50); + Object.keys(this.self().GRID_POS).forEach((_, idx) => grid.setColumnAlign(idx, "left", "middle")); }, properties: { @@ -97,7 +99,14 @@ qx.Class.define("osparc.form.renderer.PropFormBase", { readWrite: "ReadAndWrite" }, - addItems: function(items, names, title, itemOptions, headerOptions) { + /** + * override + * + * @param items {qx.ui.core.Widget[]} An array of form items to render. + * @param names {String[]} An array of names for the form items. + * @param title {String?} A title of the group you are adding. + */ + addItems: function(items, names, title) { // add the header if (title !== null) { this._add( @@ -115,9 +124,9 @@ qx.Class.define("osparc.form.renderer.PropFormBase", { const item = items[i]; const label = this._createLabel(names[i], item); - // compensate the SpacingY: 0 label.set({ - marginTop: 3 + rich: false, // override, required for showing the vut off ellipses + toolTipText: names[i] }); label.setBuddy(item); this._add(label, { @@ -125,7 +134,7 @@ qx.Class.define("osparc.form.renderer.PropFormBase", { column: this.self().GRID_POS.LABEL }); - const info = this._createInfoWHint(item.description); + const info = this.__createInfoWHint(item.description); this._add(info, { row: this._row, column: this.self().GRID_POS.INFO @@ -142,20 +151,9 @@ qx.Class.define("osparc.form.renderer.PropFormBase", { column: this.self().GRID_POS.UNIT }); - this._connectVisibility(item, label); - // store the names for translation - if (qx.core.Environment.get("qx.dynlocale")) { - this._names.push({ - name: names[i], - label: label, - item: items[i] - }); - } - - // compensate the SpacingY: 0 - this._getLayout().setRowHeight(this._row, this.self().ROW_HEIGHT); - this._row++; + + this._connectVisibility(item, label); } }, @@ -253,7 +251,7 @@ qx.Class.define("osparc.form.renderer.PropFormBase", { throw new Error("Abstract method called!"); }, - _createInfoWHint: function(hint) { + __createInfoWHint: function(hint) { const infoWHint = new osparc.form.PortInfoHint(hint); return infoWHint; }, @@ -276,7 +274,6 @@ qx.Class.define("osparc.form.renderer.PropFormBase", { } const unitLabel = new qx.ui.basic.Label().set({ rich: true, - alignY: "bottom", paddingBottom: 1, value: unitShort || null, toolTipText: unitLong || null, diff --git a/services/static-webserver/client/source/class/osparc/node/NodeView.js b/services/static-webserver/client/source/class/osparc/node/NodeView.js index f993ea06c21..ed9f92f095c 100644 --- a/services/static-webserver/client/source/class/osparc/node/NodeView.js +++ b/services/static-webserver/client/source/class/osparc/node/NodeView.js @@ -96,13 +96,6 @@ qx.Class.define("osparc.node.NodeView", { this._mainView.bind("backgroundColor", nodeOutputs, "backgroundColor"); this._outputsLayout.add(nodeOutputs); - const outputFilesBtn = new qx.ui.form.Button(this.tr("Service data"), "@FontAwesome5Solid/folder-open/14").set({ - allowGrowX: false - }); - osparc.utils.Utils.setIdToWidget(outputFilesBtn, "nodeOutputFilesBtn"); - outputFilesBtn.addListener("execute", () => osparc.node.BaseNodeView.openNodeDataManager(this.getNode())); - this._outputsLayout.add(outputFilesBtn); - this._outputsBtn.set({ value: false, enabled: this.getNode().hasOutputs() > 0 diff --git a/services/static-webserver/client/source/class/osparc/widget/NodeOptions.js b/services/static-webserver/client/source/class/osparc/widget/NodeOptions.js new file mode 100644 index 00000000000..4d7a7a63433 --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/widget/NodeOptions.js @@ -0,0 +1,108 @@ +/* ************************************************************************ + + osparc - the simcore frontend + + https://osparc.io + + Copyright: + 2024 IT'IS Foundation, https://itis.swiss + + License: + MIT: https://opensource.org/licenses/MIT + + Authors: + * Odei Maiz (odeimaiz) + +************************************************************************ */ + + +qx.Class.define("osparc.widget.NodeOptions", { + extend: qx.ui.core.Widget, + + /** + * @param node {osparc.data.model.Node} Node owning the widget + */ + construct: function(node) { + this.base(arguments); + + this._setLayout(new qx.ui.layout.VBox(15)); + + // it will become visible if children are added + this.exclude(); + + this.setNode(node); + }, + + events: { + "versionChanged": "qx.event.type.Event", + "bootModeChanged": "qx.event.type.Event", + "limitsChanged": "qx.event.type.Event" + }, + + properties: { + node: { + check: "osparc.data.model.Node", + nullable: false + } + }, + + members: { + // overridden + _afterAddChild: function() { + this.show(); + }, + + buildLayout: async function() { + const node = this.getNode(); + + const sections = []; + let showStartStopButton = false; + + // Life Cycle + if ( + node.isDynamic() && + (node.isUpdatable() || node.isDeprecated() || node.isRetired()) + ) { + const lifeCycleView = new osparc.node.LifeCycleView(node); + node.addListener("versionChanged", () => this.fireEvent("versionChanged")); + sections.push(lifeCycleView); + showStartStopButton = true; + } + + // Boot Options + if (node.hasBootModes()) { + const bootOptionsView = new osparc.node.BootOptionsView(node); + node.addListener("bootModeChanged", () => this.fireEvent("bootModeChanged")); + sections.push(bootOptionsView); + showStartStopButton = true; + } + + // Update Resource Limits + if ( + await osparc.data.Permissions.getInstance().checkCanDo("override_services_specifications") && + (node.isComputational() || node.isDynamic()) + ) { + const updateResourceLimitsView = new osparc.node.UpdateResourceLimitsView(node); + node.addListener("limitsChanged", () => this.fireEvent("limitsChanged")); + sections.push(updateResourceLimitsView); + showStartStopButton |= node.isDynamic(); + } + + if (showStartStopButton) { + // Only available to dynamic services + const instructions = new qx.ui.basic.Label(this.tr("To proceed with the following actions, the service needs to be Stopped.")).set({ + font: "text-13", + rich: true, + wrap: true + }); + this._add(instructions); + + const startStopButton = new osparc.node.StartStopButton(); + startStopButton.setNode(node); + this._add(startStopButton); + } + + sections.forEach(section => this._add(section)); + } + } +}); diff --git a/services/static-webserver/client/source/class/osparc/widget/NodeOutputs.js b/services/static-webserver/client/source/class/osparc/widget/NodeOutputs.js index be23b0c7a1c..1875d901332 100644 --- a/services/static-webserver/client/source/class/osparc/widget/NodeOutputs.js +++ b/services/static-webserver/client/source/class/osparc/widget/NodeOutputs.js @@ -34,20 +34,36 @@ qx.Class.define("osparc.widget.NodeOutputs", { construct: function(node, ports) { this.base(arguments); + this._setLayout(new qx.ui.layout.VBox(15)); + const grid = new qx.ui.layout.Grid(5, 5); - grid.setColumnMaxWidth(this.self().POS.NAME, 140); + grid.setColumnFlex(this.self().POS.LABEL, 1); + grid.setColumnFlex(this.self().POS.INFO, 0); + grid.setColumnFlex(this.self().POS.ICON, 0); grid.setColumnFlex(this.self().POS.VALUE, 1); - Object.keys(this.self().POS).forEach((_, idx) => { - grid.setColumnAlign(idx, "left", "middle"); + grid.setColumnFlex(this.self().POS.UNIT, 0); + grid.setColumnFlex(this.self().POS.PROBE, 0); + grid.setColumnMinWidth(this.self().POS.VALUE, 50); + Object.keys(this.self().POS).forEach((_, idx) => grid.setColumnAlign(idx, "left", "middle")); + const gridLayout = this.__gridLayout = new qx.ui.container.Composite(grid).set({ + allowGrowX: false }); - this._setLayout(grid); + this._add(gridLayout); this.set({ node, ports }); - node.addListener("changeOutputs", () => this.__populateLayout(), this); + const outputFilesBtn = new qx.ui.form.Button(this.tr("Service data"), "@FontAwesome5Solid/folder-open/14").set({ + allowGrowX: false, + allowGrowY: false + }); + osparc.utils.Utils.setIdToWidget(outputFilesBtn, "nodeOutputFilesBtn"); + outputFilesBtn.addListener("execute", () => osparc.node.BaseNodeView.openNodeDataManager(node)); + this._add(outputFilesBtn); + + node.addListener("changeOutputs", () => this.__populateGrid(), this); }, properties: { @@ -58,7 +74,7 @@ qx.Class.define("osparc.widget.NodeOutputs", { ports: { nullable: false, - apply: "__populateLayout" + apply: "__populateGrid" }, offerProbes: { @@ -74,33 +90,20 @@ qx.Class.define("osparc.widget.NodeOutputs", { statics: { POS: { - KEY: { - col: 0 - }, - NAME: { - col: 1 - }, - INFO: { - col: 2 - }, - ICON: { - col: 3 - }, - VALUE: { - col: 4 - }, - UNIT: { - col: 5 - }, - PROBE: { - col: 6 - } + LABEL: 0, + INFO: 1, + ICON: 2, + VALUE: 3, + UNIT: 4, + PROBE: 5 } }, members: { - __populateLayout: function() { - this._removeAll(); + __gridLayout: null, + + __populateGrid: function() { + this.__gridLayout.removeAll(); const ports = this.getPorts(); const portKeys = Object.keys(ports); @@ -108,32 +111,32 @@ qx.Class.define("osparc.widget.NodeOutputs", { const portKey = portKeys[i]; const port = ports[portKey]; - const name = new qx.ui.basic.Label(port.label).set({ + const label = new qx.ui.basic.Label(port.label + " :").set({ toolTipText: port.label }); - this._add(name, { + this.__gridLayout.add(label, { row: i, - column: this.self().POS.NAME.col + column: this.self().POS.LABEL }); const infoButton = new osparc.ui.hint.InfoHint(port.description); - this._add(infoButton, { + this.__gridLayout.add(infoButton, { row: i, - column: this.self().POS.INFO.col + column: this.self().POS.INFO }); const icon = new qx.ui.basic.Image(osparc.data.Converters.fromTypeToIcon(port.type)); - this._add(icon, { + this.__gridLayout.add(icon, { row: i, - column: this.self().POS.ICON.col + column: this.self().POS.ICON }); const value = port.value || null; if (value && typeof value === "object") { const valueLink = new osparc.ui.basic.LinkLabel(); - this._add(valueLink, { + this.__gridLayout.add(valueLink, { row: i, - column: this.self().POS.VALUE.col + column: this.self().POS.VALUE }); if ("store" in value) { // it's a file @@ -161,16 +164,16 @@ qx.Class.define("osparc.widget.NodeOutputs", { if (value) { valueEntry.setValue(String(value)); } - this._add(valueEntry, { + this.__gridLayout.add(valueEntry, { row: i, - column: this.self().POS.VALUE.col + column: this.self().POS.VALUE }); } const unit = new qx.ui.basic.Label(port.unitShort || ""); - this._add(unit, { + this.__gridLayout.add(unit, { row: i, - column: this.self().POS.UNIT.col + column: this.self().POS.UNIT }); const probeBtn = new qx.ui.form.Button().set({ @@ -186,9 +189,9 @@ qx.Class.define("osparc.widget.NodeOutputs", { portId: portKey, nodeId: this.getNode().getNodeId() })); - this._add(probeBtn, { + this.__gridLayout.add(probeBtn, { row: i, - column: this.self().POS.PROBE.col + column: this.self().POS.PROBE }); } } diff --git a/services/static-webserver/client/source/class/osparc/widget/NodeTreeItem.js b/services/static-webserver/client/source/class/osparc/widget/NodeTreeItem.js index 59b7ef990dc..b479ed86782 100644 --- a/services/static-webserver/client/source/class/osparc/widget/NodeTreeItem.js +++ b/services/static-webserver/client/source/class/osparc/widget/NodeTreeItem.js @@ -150,6 +150,11 @@ qx.Class.define("osparc.widget.NodeTreeItem", { }; node.addListener("changeMarker", () => updateMarker()); updateMarker(); + + const deleteBtn = this.getChildControl("delete-button"); + node.getStudy().bind("pipelineRunning", deleteBtn, "enabled", { + converter: running => !running + }); }, __applyIconColor: function(textColor) { diff --git a/services/static-webserver/client/source/class/osparc/workbench/BaseNodeUI.js b/services/static-webserver/client/source/class/osparc/workbench/BaseNodeUI.js index c464d57ebb4..2a778f12772 100644 --- a/services/static-webserver/client/source/class/osparc/workbench/BaseNodeUI.js +++ b/services/static-webserver/client/source/class/osparc/workbench/BaseNodeUI.js @@ -127,9 +127,11 @@ qx.Class.define("osparc.workbench.BaseNodeUI", { }, members: { - _inputLayout: null, - _outputLayout: null, + __inputLayout: null, + __outputLayout: null, _optionsMenu: null, + _markerBtn: null, + _deleteBtn: null, __nodeMoving: null, __getMenuButton: function() { @@ -159,7 +161,7 @@ qx.Class.define("osparc.workbench.BaseNodeUI", { infoBtn.addListener("execute", () => this.fireDataEvent("infoNode", this.getNodeId())); optionsMenu.add(infoBtn); - const deleteBtn = new qx.ui.menu.Button().set({ + const deleteBtn = this._deleteBtn = new qx.ui.menu.Button().set({ label: this.tr("Delete"), icon: "@FontAwesome5Solid/trash/10" }); @@ -179,11 +181,11 @@ qx.Class.define("osparc.workbench.BaseNodeUI", { }, getInputPort: function() { - return this._inputLayout; + return this.__inputLayout; }, getOutputPort: function() { - return this._outputLayout; + return this.__outputLayout; }, _createPort: function(isInput, placeholder = false) { @@ -219,9 +221,9 @@ qx.Class.define("osparc.workbench.BaseNodeUI", { }); if (isInput) { - this._inputLayout = port; + this.__inputLayout = port; } else { - this._outputLayout = port; + this.__outputLayout = port; } return port; diff --git a/services/static-webserver/client/source/class/osparc/workbench/NodeUI.js b/services/static-webserver/client/source/class/osparc/workbench/NodeUI.js index e9221359214..c58f0af71ee 100644 --- a/services/static-webserver/client/source/class/osparc/workbench/NodeUI.js +++ b/services/static-webserver/client/source/class/osparc/workbench/NodeUI.js @@ -270,7 +270,7 @@ qx.Class.define("osparc.workbench.NodeUI", { this.getNode().bind("marker", this._markerBtn, "label", { converter: val => val ? this.tr("Remove Marker") : this.tr("Add Marker") }); - this._markerBtn.addListener("execute", () => this.getNode().toggleMarker()); + this._markerBtn.addListener("execute", () => node.toggleMarker()); const marker = this.getChildControl("marker"); const updateMarker = () => { @@ -284,6 +284,10 @@ qx.Class.define("osparc.workbench.NodeUI", { node.addListener("changeMarker", () => updateMarker()); updateMarker(); + node.getStudy().bind("pipelineRunning", this._deleteBtn, "enabled", { + converter: running => !running + }); + const evaluateLifeCycleIcon = () => { const deprecatedIcon = this.getChildControl("deprecated-icon"); deprecatedIcon.exclude(); diff --git a/tests/e2e-playwright/tests/sleepers/sleepers.py b/tests/e2e-playwright/tests/sleepers/sleepers.py index d65ac48c659..68864b6130f 100644 --- a/tests/e2e-playwright/tests/sleepers/sleepers.py +++ b/tests/e2e-playwright/tests/sleepers/sleepers.py @@ -220,7 +220,6 @@ def test_sleepers( ) as ctx: for index, sleeper in enumerate(page.get_by_test_id("nodeTreeItem").all()[1:]): sleeper.click() - page.get_by_test_id("outputsTabButton").click() # waiting for this response is not enough, the frontend needs some time to show the files # therefore _get_file_names is wrapped with tenacity with page.expect_response(re.compile(r"files/metadata")): diff --git a/tests/e2e-playwright/tests/ti_plan.py b/tests/e2e-playwright/tests/ti_plan.py index 1d6a6a01a09..f796af9a0c4 100644 --- a/tests/e2e-playwright/tests/ti_plan.py +++ b/tests/e2e-playwright/tests/ti_plan.py @@ -46,8 +46,11 @@ def test_tip( project_uuid = project_data["data"]["uuid"] print("project uuid: ", project_uuid) node_ids = [] - for node_id in project_data["data"]["workbench"].keys(): + workbench = project_data["data"]["workbench"] + for node_id in workbench.keys(): print("node_id: ", node_id) + print("key: ", workbench[node_id]["key"]) + print("version: ", workbench[node_id]["version"]) node_ids.append(node_id) # let it start or force diff --git a/tests/e2e/utils/auto.js b/tests/e2e/utils/auto.js index f4700475271..9f1a29521e2 100644 --- a/tests/e2e/utils/auto.js +++ b/tests/e2e/utils/auto.js @@ -359,7 +359,6 @@ async function restoreIFrame(page) { async function openNodeFiles(page) { console.log("Opening Data produced by Node"); - await utils.waitAndClick(page, '[osparc-test-id="outputsTabButton"]'); await utils.waitAndClick(page, '[osparc-test-id="nodeOutputFilesBtn"]'); }