From 97c5f8d3cf6ca39a202a538ab82338759936b4ad Mon Sep 17 00:00:00 2001 From: guerler Date: Tue, 18 Jan 2022 14:25:12 -0500 Subject: [PATCH 01/11] Properly replace tool version in tool identifer string --- .../src/components/Workflow/Editor/Forms/FormTool.vue | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/client/src/components/Workflow/Editor/Forms/FormTool.vue b/client/src/components/Workflow/Editor/Forms/FormTool.vue index a364a1e5169..d8a42a337d0 100644 --- a/client/src/components/Workflow/Editor/Forms/FormTool.vue +++ b/client/src/components/Workflow/Editor/Forms/FormTool.vue @@ -171,9 +171,16 @@ export default { }, postChanges(newVersion) { const options = this.node.config_form; + let toolId = options.id; + let toolVersion = options.version; + if (newVersion) { + toolId = toolId.replace(toolVersion, newVersion); + toolVersion = newVersion; + console.debug("FormTool - Tool version changed.", toolId, toolVersion); + } this.$emit("onSetData", this.node.id, { - tool_id: options.id, - tool_version: newVersion || options.version, + tool_id: toolId, + tool_version: toolVersion, type: "tool", inputs: Object.assign({}, this.mainValues, this.sectionValues), }); From d5b31ee55aa9001131079bcf5be57f1d9a2110e1 Mon Sep 17 00:00:00 2001 From: mvdbeek Date: Wed, 19 Jan 2022 14:33:25 +0100 Subject: [PATCH 02/11] Fix tool id not getting updated on tool update --- .../src/components/Workflow/Editor/Node.vue | 2 +- .../galaxy_test/base}/tool_sheds_conf.xml | 0 .../galaxy_test/base}/uses_shed.py | 0 lib/galaxy_test/selenium/framework.py | 8 ++++ .../selenium/test_workflow_editor.py | 36 +++++++---------- packages/test_base/MANIFEST.in | 2 +- packages/test_base/setup.py | 4 +- test/integration/test_data_manager.py | 2 +- .../integration/test_data_manager_refgenie.py | 2 +- .../integration/test_repository_operations.py | 2 +- test/integration/test_shed_tool_tests.py | 2 +- .../test_workflow_repository_tool_update.py | 39 +++++++++++++++++++ 12 files changed, 70 insertions(+), 29 deletions(-) rename {test/integration => lib/galaxy_test/base}/tool_sheds_conf.xml (100%) rename {test/integration => lib/galaxy_test/base}/uses_shed.py (100%) create mode 100644 test/integration_selenium/test_workflow_repository_tool_update.py diff --git a/client/src/components/Workflow/Editor/Node.vue b/client/src/components/Workflow/Editor/Node.vue index b3aebc99b82..898f9b7ff1c 100644 --- a/client/src/components/Workflow/Editor/Node.vue +++ b/client/src/components/Workflow/Editor/Node.vue @@ -314,6 +314,7 @@ export default { }, setData(data) { this.config_form = data.config_form; + this.content_id = data.config_form?.id || data.content_id; this.tool_state = data.tool_state; this.errors = data.errors; this.tooltip = data.tooltip || ""; @@ -326,7 +327,6 @@ export default { }, initData(data) { this.uuid = data.uuid; - this.content_id = data.config_form?.id || data.content_id; this.annotation = data.annotation; this.label = data.label; this.setData(data); diff --git a/test/integration/tool_sheds_conf.xml b/lib/galaxy_test/base/tool_sheds_conf.xml similarity index 100% rename from test/integration/tool_sheds_conf.xml rename to lib/galaxy_test/base/tool_sheds_conf.xml diff --git a/test/integration/uses_shed.py b/lib/galaxy_test/base/uses_shed.py similarity index 100% rename from test/integration/uses_shed.py rename to lib/galaxy_test/base/uses_shed.py diff --git a/lib/galaxy_test/selenium/framework.py b/lib/galaxy_test/selenium/framework.py index c24c734a695..8635799ff92 100644 --- a/lib/galaxy_test/selenium/framework.py +++ b/lib/galaxy_test/selenium/framework.py @@ -384,6 +384,14 @@ class TestWithSeleniumMixin(GalaxyTestSeleniumContext, UsesApiTestCaseMixin): self.assert_no_error_message() return GALAXY_TEST_SELENIUM_ADMIN_USER_EMAIL + @retry_assertion_during_transitions + def assert_workflow_has_changes_and_save(self): + save_button = self.components.workflow_editor.save_button + save_button.wait_for_visible() + assert not save_button.has_class("disabled") + save_button.wait_for_and_click() + self.sleep_for(self.wait_types.UX_RENDER) + def workflow_upload_yaml_with_random_name(self, content, **kwds): workflow_populator = self.workflow_populator name = self._get_random_name() diff --git a/lib/galaxy_test/selenium/test_workflow_editor.py b/lib/galaxy_test/selenium/test_workflow_editor.py index a1fc0942728..ca47f673b9a 100644 --- a/lib/galaxy_test/selenium/test_workflow_editor.py +++ b/lib/galaxy_test/selenium/test_workflow_editor.py @@ -71,7 +71,7 @@ class WorkflowEditorTestCase(SeleniumTestCase): new_annotation = 'look new annotation' edit_annotation.wait_for_and_send_keys(new_annotation) - self.assert_has_changes_and_save() + self.assert_workflow_has_changes_and_save() self.workflow_index_open_with_name(name) self.assert_wf_annotation_is(new_annotation) @@ -84,7 +84,7 @@ class WorkflowEditorTestCase(SeleniumTestCase): edit_name = self.components.workflow_editor.edit_name edit_name.wait_for_and_send_keys(new_name) - self.assert_has_changes_and_save() + self.assert_workflow_has_changes_and_save() self.workflow_index_open_with_name(new_name) self.assert_wf_name_is(name) @@ -104,19 +104,19 @@ class WorkflowEditorTestCase(SeleniumTestCase): self.sleep_for(self.wait_types.UX_RENDER) self.components.tool_form.parameter_input(parameter='select_single').wait_for_and_send_keys('e') self.sleep_for(self.wait_types.UX_RENDER) - self.assert_has_changes_and_save() + self.assert_workflow_has_changes_and_save() workflow = self.workflow_populator.download_workflow(workflow_id) tool_state = json.loads(workflow['steps']['0']['tool_state']) assert tool_state['select_single'] == 'parameter value' # Disable optional button, resets value to null self.components.tool_form.parameter_checkbox(parameter='select_single').wait_for_and_click() - self.assert_has_changes_and_save() + self.assert_workflow_has_changes_and_save() workflow = self.workflow_populator.download_workflow(workflow_id) tool_state = json.loads(workflow['steps']['0']['tool_state']) assert tool_state['select_single'] is None # Enable button but don't provide a value self.components.tool_form.parameter_checkbox(parameter='select_single').wait_for_and_click() - self.assert_has_changes_and_save() + self.assert_workflow_has_changes_and_save() workflow = self.workflow_populator.download_workflow(workflow_id) tool_state = json.loads(workflow['steps']['0']['tool_state']) assert tool_state['select_single'] == "" @@ -192,7 +192,7 @@ steps: assert textarea_column_names.get_attribute('value') == 'a\nb\nc\n' self.set_text_element(columns, '4\n5\n6\n') self.sleep_for(self.wait_types.UX_RENDER) - self.assert_has_changes_and_save() + self.assert_workflow_has_changes_and_save() self.driver.refresh() node.title.wait_for_and_click() textarea_columns = columns.wait_for_visible() @@ -300,7 +300,7 @@ steps: self.workflow_editor_connect("input1#output", "first_cat#input1") self.assert_connected("input1#output", "first_cat#input1") self.sleep_for(self.wait_types.UX_RENDER) - self.assert_has_changes_and_save() + self.assert_workflow_has_changes_and_save() self.workflow_index_open_with_name(name) self.assert_connected("input1#output", "first_cat#input1") @@ -422,7 +422,7 @@ steps: assert self.select_dropdown_item('Switch to 0.2'), 'Switch to tool version dropdown item not found' self.screenshot("workflow_editor_version_update") self.sleep_for(self.wait_types.UX_RENDER) - self.assert_has_changes_and_save() + self.assert_workflow_has_changes_and_save() workflow = self.workflow_populator.download_workflow(workflow_id) assert workflow['steps']['0']['tool_version'] == '0.2' @@ -435,7 +435,7 @@ steps: self.assert_modal_has_text("Using version '0.2' instead of version '0.0.1'") self.screenshot("workflow_editor_tool_upgrade") self.components.workflow_editor.modal_button_continue.wait_for_and_click() - self.assert_has_changes_and_save() + self.assert_workflow_has_changes_and_save() @selenium_test def test_editor_subworkflow_tool_upgrade_message(self): @@ -462,7 +462,7 @@ steps: self.assert_modal_has_text("parameter 'inttest': an integer or workflow parameter is required") self.screenshot("workflow_editor_subworkflow_tool_upgrade") self.components.workflow_editor.modal_button_continue.wait_for_and_click() - self.assert_has_changes_and_save() + self.assert_workflow_has_changes_and_save() @staticmethod def set_text_element(element, value): @@ -483,9 +483,9 @@ steps: self.set_text_element(editor.label_input, 'source label') # Select node using new label, ensures labels are synced between side panel and node cat_node = editor.node._(label="source label") - self.assert_has_changes_and_save() + self.assert_workflow_has_changes_and_save() editor.annotation_input.wait_for_and_send_keys("source annotation") - self.assert_has_changes_and_save() + self.assert_workflow_has_changes_and_save() editor.configure_output(output='out_file1').wait_for_and_click() output_label = editor.label_output(output='out_file1') self.set_text_element(output_label, 'workflow output label') @@ -499,7 +499,7 @@ steps: editor.label_input.wait_for_and_send_keys('cloned label') output_label = editor.label_output(output='out_file1') self.set_text_element(output_label, 'cloned output label') - self.assert_has_changes_and_save() + self.assert_workflow_has_changes_and_save() edited_workflow = self.workflow_populator.download_workflow(workflow_id) source_step = next(iter(step for step in edited_workflow['steps'].values() if step['label'] == 'source label')) cloned_step = next(iter(step for step in edited_workflow['steps'].values() if step['label'] == 'cloned label')) @@ -531,7 +531,7 @@ steps: editor.tool_menu_section_link(section_name="workflows").wait_for_and_click() editor.workflow_link(workflow_title=child_workflow_name).wait_for_and_click() self.sleep_for(self.wait_types.UX_RENDER) - self.assert_has_changes_and_save() + self.assert_workflow_has_changes_and_save() workflow = self.workflow_populator.download_workflow(parent_workflow_id) subworkflow_step = workflow['steps']['1'] assert subworkflow_step['name'] == child_workflow_name @@ -702,14 +702,6 @@ steps: workflow_populator.upload_yaml_workflow(content, name=name) return name - @retry_assertion_during_transitions - def assert_has_changes_and_save(self): - save_button = self.components.workflow_editor.save_button - save_button.wait_for_visible() - assert not save_button.has_class("disabled") - save_button.wait_for_and_click() - self.sleep_for(self.wait_types.UX_RENDER) - @retry_assertion_during_transitions def assert_wf_name_is(self, expected_name): edit_name_element = self.components.workflow_editor.edit_name.wait_for_visible() diff --git a/packages/test_base/MANIFEST.in b/packages/test_base/MANIFEST.in index 8393a3b8e2a..ff8b5f91a9a 100644 --- a/packages/test_base/MANIFEST.in +++ b/packages/test_base/MANIFEST.in @@ -1 +1 @@ -include *.rst *.txt LICENSE +include *.rst *.txt LICENSE galaxy_test/base/tool_sheds_conf.xml diff --git a/packages/test_base/setup.py b/packages/test_base/setup.py index cd5148d83a4..c0154d8c37c 100644 --- a/packages/test_base/setup.py +++ b/packages/test_base/setup.py @@ -38,7 +38,9 @@ ENTRY_POINTS = ''' ''' PACKAGE_DATA = { # Be sure to update MANIFEST.in for source dist. - 'galaxy': [ + 'galaxy': [], + 'galaxy_test': [ + 'galaxy_test/base/tool_sheds_conf.xml', ], } PACKAGE_DIR = { diff --git a/test/integration/test_data_manager.py b/test/integration/test_data_manager.py index cbc85c2f655..dbeca6d6d28 100644 --- a/test/integration/test_data_manager.py +++ b/test/integration/test_data_manager.py @@ -5,8 +5,8 @@ import pytest from nose.plugins.skip import SkipTest from galaxy_test.base.populators import DatasetPopulator, skip_if_toolshed_down +from galaxy_test.base.uses_shed import CONDA_AUTO_INSTALL_JOB_TIMEOUT, UsesShed from galaxy_test.driver import integration_util -from .uses_shed import CONDA_AUTO_INSTALL_JOB_TIMEOUT, UsesShed FETCH_TOOL_ID = 'toolshed.g2.bx.psu.edu/repos/devteam/data_manager_fetch_genome_dbkeys_all_fasta/data_manager_fetch_genome_all_fasta_dbkey/0.0.3' FETCH_GENOME_DBKEYS_ALL_FASTA_INPUT = { diff --git a/test/integration/test_data_manager_refgenie.py b/test/integration/test_data_manager_refgenie.py index 73e4d63f023..44fab7a235e 100644 --- a/test/integration/test_data_manager_refgenie.py +++ b/test/integration/test_data_manager_refgenie.py @@ -5,8 +5,8 @@ import string from nose.plugins.skip import SkipTest from galaxy_test.base.populators import DatasetPopulator +from galaxy_test.base.uses_shed import CONDA_AUTO_INSTALL_JOB_TIMEOUT, UsesShed from galaxy_test.driver import integration_util -from .uses_shed import CONDA_AUTO_INSTALL_JOB_TIMEOUT, UsesShed FETCH_TOOL_ID = 'toolshed.g2.bx.psu.edu/repos/devteam/data_manager_fetch_genome_dbkeys_all_fasta/data_manager_fetch_genome_all_fasta_dbkey/0.0.3' FETCH_GENOME_DBKEYS_ALL_FASTA_INPUT = { diff --git a/test/integration/test_repository_operations.py b/test/integration/test_repository_operations.py index 21af0a6a213..2060c047978 100644 --- a/test/integration/test_repository_operations.py +++ b/test/integration/test_repository_operations.py @@ -2,9 +2,9 @@ import os from collections import namedtuple from galaxy_test.base.populators import DatasetPopulator +from galaxy_test.base.uses_shed import UsesShed from galaxy_test.driver import integration_util from tool_shed.util import hg_util -from .uses_shed import UsesShed REPO_TYPE = namedtuple('REPO_TYPE', 'name owner changeset') REPO = REPO_TYPE( diff --git a/test/integration/test_shed_tool_tests.py b/test/integration/test_shed_tool_tests.py index 46732cc93c4..316722af381 100644 --- a/test/integration/test_shed_tool_tests.py +++ b/test/integration/test_shed_tool_tests.py @@ -1,8 +1,8 @@ import os from galaxy_test.base.populators import skip_if_toolshed_down +from galaxy_test.base.uses_shed import UsesShed from galaxy_test.driver import integration_util -from .uses_shed import UsesShed class ToolShedToolTestIntegrationTestCase(integration_util.IntegrationTestCase, UsesShed): diff --git a/test/integration_selenium/test_workflow_repository_tool_update.py b/test/integration_selenium/test_workflow_repository_tool_update.py new file mode 100644 index 00000000000..214697de515 --- /dev/null +++ b/test/integration_selenium/test_workflow_repository_tool_update.py @@ -0,0 +1,39 @@ +from galaxy_test.base.uses_shed import UsesShed +from .framework import ( + selenium_test, + SeleniumIntegrationTestCase +) + + +class WorkflowEditorToolUpgradeWithToolShedToolTestCase(SeleniumIntegrationTestCase, UsesShed): + requires_admin = True + + @classmethod + def handle_galaxy_config_kwds(cls, config): + cls.configure_shed(config) + + @selenium_test + def test_tool_shed_tool_update_in_workflow_editor(self): + self.install_repository("iuc", "compose_text_param", "feb3acba1e0a") # 0.1.0 + self.install_repository("iuc", "compose_text_param", "e188c9826e0f") # 0.1.1 + self.login() + workflow_populator = self.workflow_populator + workflow_id = workflow_populator.upload_yaml_workflow("""class: GalaxyWorkflow +inputs: [] +steps: + - tool_id: compose_text_param + tool_version: 0.1.0 + label: compose_text_param + """, exact_tools=True) + self.workflow_index_open() + self.workflow_index_click_option("Edit") + editor = self.components.workflow_editor + editor.node._(label="compose_text_param").wait_for_and_click() + editor.tool_version_button.wait_for_and_click() + assert self.select_dropdown_item('Switch to 0.1.1'), 'Switch to tool version dropdown item not found' + self.screenshot("workflow_editor_version_update_tool_shed") + self.sleep_for(self.wait_types.UX_RENDER) + self.assert_workflow_has_changes_and_save() + workflow = self.workflow_populator.download_workflow(workflow_id) + assert workflow['steps']['0']['tool_version'] == '0.1.1' + assert workflow['steps']['0']['tool_id'] == 'toolshed.g2.bx.psu.edu/repos/iuc/compose_text_param/compose_text_param/0.1.1' From d3d6c607055945f9f08be21387e57f51277da69a Mon Sep 17 00:00:00 2001 From: mvdbeek Date: Wed, 19 Jan 2022 15:49:05 +0100 Subject: [PATCH 03/11] Fix embedding subworkflow --- client/src/components/Workflow/Editor/Node.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/components/Workflow/Editor/Node.vue b/client/src/components/Workflow/Editor/Node.vue index 898f9b7ff1c..9e06f0aafad 100644 --- a/client/src/components/Workflow/Editor/Node.vue +++ b/client/src/components/Workflow/Editor/Node.vue @@ -314,7 +314,7 @@ export default { }, setData(data) { this.config_form = data.config_form; - this.content_id = data.config_form?.id || data.content_id; + this.content_id = data.config_form?.id || data.content_id || this.content_id; this.tool_state = data.tool_state; this.errors = data.errors; this.tooltip = data.tooltip || ""; From 2ed8cf644d69b813ca7a2d90517872dbdfe76103 Mon Sep 17 00:00:00 2001 From: guerler Date: Wed, 19 Jan 2022 10:58:28 -0500 Subject: [PATCH 04/11] Consistently parse content id for workflow modules when updating states --- client/src/components/Workflow/Editor/Node.vue | 2 +- lib/galaxy/webapps/galaxy/api/workflows.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/client/src/components/Workflow/Editor/Node.vue b/client/src/components/Workflow/Editor/Node.vue index 9e06f0aafad..7fb08436b22 100644 --- a/client/src/components/Workflow/Editor/Node.vue +++ b/client/src/components/Workflow/Editor/Node.vue @@ -314,7 +314,7 @@ export default { }, setData(data) { this.config_form = data.config_form; - this.content_id = data.config_form?.id || data.content_id || this.content_id; + this.content_id = data.content_id; this.tool_state = data.tool_state; this.errors = data.errors; this.tooltip = data.tooltip || ""; diff --git a/lib/galaxy/webapps/galaxy/api/workflows.py b/lib/galaxy/webapps/galaxy/api/workflows.py index 73c27bdc60c..b520e6eb9ad 100644 --- a/lib/galaxy/webapps/galaxy/api/workflows.py +++ b/lib/galaxy/webapps/galaxy/api/workflows.py @@ -699,6 +699,7 @@ class WorkflowsAPIController(BaseGalaxyAPIController, UsesStoredWorkflowMixin, U 'annotation': inputs.get('__annotation', ''), 'name': module.get_name(), 'tool_state': module.get_state(), + 'content_id': module.get_content_id(), 'inputs': module.get_all_inputs(connectable_only=True), 'outputs': module.get_all_outputs(), 'config_form': module.get_config_form(), From 7f0ce0ec8457ac15273cafd3b8248e2eebfd1d5d Mon Sep 17 00:00:00 2001 From: mvdbeek Date: Thu, 20 Jan 2022 11:31:49 +0100 Subject: [PATCH 05/11] Add selenium test to check map over state for parameter steps --- .../selenium/test_workflow_editor.py | 31 +++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/lib/galaxy_test/selenium/test_workflow_editor.py b/lib/galaxy_test/selenium/test_workflow_editor.py index ca47f673b9a..854a5a41194 100644 --- a/lib/galaxy_test/selenium/test_workflow_editor.py +++ b/lib/galaxy_test/selenium/test_workflow_editor.py @@ -271,6 +271,32 @@ steps: self.workflow_editor_connect("input_int#output", "tool_exec#inttest", screenshot_partial="workflow_editor_parameter_connection_dragging") self.assert_connected("input_int#output", "tool_exec#inttest") + @selenium_test + def test_non_data_map_over_carried_through(self): + # Use auto_layout=false, which prevents placing any + # step outside of the scroll area + # xref: https://github.com/galaxyproject/galaxy/issues/13211 + self.open_in_workflow_editor(""" +class: GalaxyWorkflow +inputs: + input_collection: + type: collection + collection_type: "list" +steps: + param_value_from_file: + tool_id: param_value_from_file + in: + input1: input_collection + text_input_step: + tool_id: param_text_option + in: + text_param: param_value_from_file/text_param + collection_input: + tool_id: identifier_collection +""", auto_layout=False) + self.workflow_editor_connect("text_input_step#out_file1", "collection_input#input1") + self.assert_connected("text_input_step#out_file1", "collection_input#input1") + @selenium_test def test_existing_connections(self): self.open_in_workflow_editor(WORKFLOW_SIMPLE_CAT_TWICE) @@ -628,11 +654,12 @@ steps: source_id, sink_id = self.workflow_editor_source_sink_terminal_ids(source, sink) self.components.workflow_editor.connector_for(source_id=source_id, sink_id=sink_id).wait_for_absent() - def open_in_workflow_editor(self, yaml_content): + def open_in_workflow_editor(self, yaml_content, auto_layout=True): name = self.workflow_upload_yaml_with_random_name(yaml_content) self.workflow_index_open() self.workflow_index_open_with_name(name) - self.workflow_editor_click_option("Auto Layout") + if auto_layout: + self.workflow_editor_click_option("Auto Layout") return name def workflow_editor_source_sink_terminal_ids(self, source, sink): From dce483e7f542d5fb3464d814fc51ee85a7b88143 Mon Sep 17 00:00:00 2001 From: guerler Date: Tue, 18 Jan 2022 23:15:46 -0500 Subject: [PATCH 06/11] Call setMapOver routine in input parameter terminals to detect mapped collections --- .../Workflow/Editor/modules/terminals.js | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/client/src/components/Workflow/Editor/modules/terminals.js b/client/src/components/Workflow/Editor/modules/terminals.js index 51b39ab26c8..359c1c736bb 100644 --- a/client/src/components/Workflow/Editor/modules/terminals.js +++ b/client/src/components/Workflow/Editor/modules/terminals.js @@ -224,6 +224,15 @@ class BaseInputTerminal extends Terminal { this.datatypesMapper = attr.datatypesMapper; this.update(attr.input); // subclasses should implement this... } + setDefaultMapOver(connector) { + var other_output = connector.outputHandle; + if (other_output) { + var otherCollectionType = this._otherCollectionType(other_output); + if (otherCollectionType.isCollection) { + this.setMapOver(otherCollectionType); + } + } + } canAccept(other) { if (this._inputFilled()) { return new ConnectionAcceptable( @@ -385,14 +394,7 @@ class InputTerminal extends BaseInputTerminal { } connect(connector) { super.connect(connector); - var other_output = connector.outputHandle; - if (!other_output) { - return; - } - var otherCollectionType = this._otherCollectionType(other_output); - if (otherCollectionType.isCollection) { - this.setMapOver(otherCollectionType); - } + this.setDefaultMapOver(connector); } attachable(other) { var otherCollectionType = this._otherCollectionType(other); @@ -460,6 +462,10 @@ class InputParameterTerminal extends BaseInputTerminal { this.type = input.type; this.optional = input.optional; } + connect(connector) { + super.connect(connector); + this.setDefaultMapOver(connector); + } effectiveType(parameterType) { return parameterType == "select" ? "text" : parameterType; } From b5f35c76c26ca181cf3b5f07f82b65f052119af2 Mon Sep 17 00:00:00 2001 From: guerler Date: Tue, 18 Jan 2022 23:57:03 -0500 Subject: [PATCH 07/11] Add test to validate correct mapping of parameter input terminals --- .../qunit/tests/workflow_editor_tests.js | 42 ++++++++++++++----- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/client/tests/qunit/tests/workflow_editor_tests.js b/client/tests/qunit/tests/workflow_editor_tests.js index a55604b395f..35c92bb3000 100644 --- a/client/tests/qunit/tests/workflow_editor_tests.js +++ b/client/tests/qunit/tests/workflow_editor_tests.js @@ -889,6 +889,23 @@ QUnit.module("terminal mapping logic", { } return inputTerminal; }, + newInputParameterTerminal: function (mapOver, input, node) { + input = input || {}; + node = node || this.newNode(); + if (!("type" in input)) { + input["type"] = "text"; + } + const inputEl = $("
")[0]; + const inputTerminal = new Terminals.InputParameterTerminal({ + element: inputEl, + input: input, + }); + inputTerminal.node = node; + if (mapOver) { + inputTerminal.setMapOver(new Terminals.CollectionTypeDescription(mapOver)); + } + return inputTerminal; + }, newInputCollectionTerminal: function (input, node) { input = input || {}; node = node || this.newNode(); @@ -1023,6 +1040,15 @@ QUnit.module("terminal mapping logic", { verifyNotMappedOver: function (assert, terminal) { assert.ok(!terminal.mapOver.isCollection); }, + verifyDefaultMapOver: function(assert, inputTerminal1) { + const outputCollectionTerminal1 = this.newOutputCollectionTerminal("list"); + assert.ok(!inputTerminal1.node.mapOver); + const connector = new Connector({}, outputCollectionTerminal1, inputTerminal1); + outputCollectionTerminal1.connect(connector); + assert.ok(inputTerminal1.node.mapOver); + inputTerminal1.disconnect(connector); + assert.ok(!inputTerminal1.node.mapOver); + } }); QUnit.test("unconstrained input can be mapped over", function (assert) { @@ -1280,13 +1306,9 @@ QUnit.test("simple mapping over collection outputs works correctly", function (a this.verifyNotAttachable(assert, testTerminal1, connectedOutput); }); -QUnit.test("node mapping state over collection outputs works correctly", function (assert) { - const inputTerminal1 = this.newInputTerminal(); - const outputCollectionTerminal1 = this.newOutputCollectionTerminal("list"); - assert.ok(!inputTerminal1.node.mapOver); - const connector = new Connector({}, outputCollectionTerminal1, inputTerminal1); - outputCollectionTerminal1.connect(connector); - assert.ok(inputTerminal1.node.mapOver); - inputTerminal1.disconnect(connector); - assert.ok(!inputTerminal1.node.mapOver); -}); \ No newline at end of file +QUnit.test("node input parameter mapping state over collection outputs works correctly", function (assert) { + const inputTerminal = this.newInputTerminal(); + this.verifyDefaultMapOver(assert, inputTerminal); + const inputParameterTerminal = this.newInputParameterTerminal(); + this.verifyDefaultMapOver(assert, inputParameterTerminal); +}); From f7fb4ede5502b329b5902b7c4bc125c9a780b97c Mon Sep 17 00:00:00 2001 From: guerler Date: Wed, 19 Jan 2022 00:58:35 -0500 Subject: [PATCH 08/11] Remove currently unused options from qunit terminal creation helpers --- .../qunit/tests/workflow_editor_tests.js | 60 +++++++------------ 1 file changed, 21 insertions(+), 39 deletions(-) diff --git a/client/tests/qunit/tests/workflow_editor_tests.js b/client/tests/qunit/tests/workflow_editor_tests.js index 35c92bb3000..23317d11793 100644 --- a/client/tests/qunit/tests/workflow_editor_tests.js +++ b/client/tests/qunit/tests/workflow_editor_tests.js @@ -871,9 +871,9 @@ QUnit.test("resetMapping", function (assert) { }); QUnit.module("terminal mapping logic", { - newInputTerminal: function (mapOver, input, node) { + newInputTerminal: function (mapOver, input) { input = input || {}; - node = node || this.newNode(); + const node = this.newNode(); if (!("extensions" in input)) { input["extensions"] = ["data"]; } @@ -889,26 +889,19 @@ QUnit.module("terminal mapping logic", { } return inputTerminal; }, - newInputParameterTerminal: function (mapOver, input, node) { - input = input || {}; - node = node || this.newNode(); - if (!("type" in input)) { - input["type"] = "text"; - } + newInputParameterTerminal: function () { + const node = this.newNode(); const inputEl = $("
")[0]; const inputTerminal = new Terminals.InputParameterTerminal({ element: inputEl, - input: input, + input: {}, }); inputTerminal.node = node; - if (mapOver) { - inputTerminal.setMapOver(new Terminals.CollectionTypeDescription(mapOver)); - } return inputTerminal; }, - newInputCollectionTerminal: function (input, node) { + newInputCollectionTerminal: function (input) { input = input || {}; - node = node || this.newNode(); + const node = this.newNode(); if (!("extensions" in input)) { input["extensions"] = ["data"]; } @@ -921,16 +914,12 @@ QUnit.module("terminal mapping logic", { }); return inputTerminal; }, - newOutputTerminal: function (mapOver, output, node) { - output = output || {}; - node = node || this.newNode(); - if (!("extensions" in output)) { - output["extensions"] = ["data"]; - } + newOutputTerminal: function (mapOver) { + const node = this.newNode(); const outputEl = $("
")[0]; const outputTerminal = new Terminals.OutputTerminal({ element: outputEl, - datatypes: output.extensions, + datatypes: ["data"], node: {}, }); outputTerminal.node = node; @@ -939,24 +928,17 @@ QUnit.module("terminal mapping logic", { } return outputTerminal; }, - newOutputCollectionTerminal: function (collectionType, output, node, mapOver) { + newOutputCollectionTerminal: function (collectionType) { collectionType = collectionType || "list"; - output = output || {}; - node = node || this.newNode(); - if (!("extensions" in output)) { - output["extensions"] = ["data"]; - } + const node = this.newNode(); const outputEl = $("
")[0]; const outputTerminal = new Terminals.OutputCollectionTerminal({ element: outputEl, - datatypes: output.extensions, + datatypes: ["data"], collection_type: collectionType, node: {}, }); outputTerminal.node = node; - if (mapOver) { - outputTerminal.setMapOver(new Terminals.CollectionTypeDescription(mapOver)); - } return outputTerminal; }, newNode: function () { @@ -1040,14 +1022,14 @@ QUnit.module("terminal mapping logic", { verifyNotMappedOver: function (assert, terminal) { assert.ok(!terminal.mapOver.isCollection); }, - verifyDefaultMapOver: function(assert, inputTerminal1) { - const outputCollectionTerminal1 = this.newOutputCollectionTerminal("list"); - assert.ok(!inputTerminal1.node.mapOver); - const connector = new Connector({}, outputCollectionTerminal1, inputTerminal1); - outputCollectionTerminal1.connect(connector); - assert.ok(inputTerminal1.node.mapOver); - inputTerminal1.disconnect(connector); - assert.ok(!inputTerminal1.node.mapOver); + verifyDefaultMapOver: function(assert, terminal) { + const outputCollectionTerminal = this.newOutputCollectionTerminal("list"); + assert.ok(!terminal.node.mapOver); + const connector = new Connector({}, outputCollectionTerminal, terminal); + outputCollectionTerminal.connect(connector); + assert.ok(terminal.node.mapOver); + terminal.disconnect(connector); + assert.ok(!terminal.node.mapOver); } }); From 2a005df01873b84bbaad093d500c4165db67d43c Mon Sep 17 00:00:00 2001 From: guerler Date: Wed, 19 Jan 2022 22:16:04 -0500 Subject: [PATCH 09/11] Separate qunit test cases for data and parameter input terminals --- client/tests/qunit/tests/workflow_editor_tests.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/tests/qunit/tests/workflow_editor_tests.js b/client/tests/qunit/tests/workflow_editor_tests.js index 23317d11793..bf4f43b7317 100644 --- a/client/tests/qunit/tests/workflow_editor_tests.js +++ b/client/tests/qunit/tests/workflow_editor_tests.js @@ -1288,9 +1288,12 @@ QUnit.test("simple mapping over collection outputs works correctly", function (a this.verifyNotAttachable(assert, testTerminal1, connectedOutput); }); -QUnit.test("node input parameter mapping state over collection outputs works correctly", function (assert) { +QUnit.test("node input terminal mapping state over collection outputs works correctly", function (assert) { const inputTerminal = this.newInputTerminal(); this.verifyDefaultMapOver(assert, inputTerminal); +}); + +QUnit.test("node input parameter terminal mapping state over collection outputs works correctly", function (assert) { const inputParameterTerminal = this.newInputParameterTerminal(); this.verifyDefaultMapOver(assert, inputParameterTerminal); }); From 4f104cfbd7cb10ba612115db0a2742dad12b3754 Mon Sep 17 00:00:00 2001 From: davelopez <46503462+davelopez@users.noreply.github.com> Date: Thu, 20 Jan 2022 19:25:45 +0100 Subject: [PATCH 10/11] Add API test for dataset collections batch update When merging forward replace `_raw_update_batch` to `_update_batch` --- lib/galaxy_test/api/test_history_contents.py | 26 ++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/lib/galaxy_test/api/test_history_contents.py b/lib/galaxy_test/api/test_history_contents.py index 2fc5d8c8b95..7cebc0cbbac 100644 --- a/lib/galaxy_test/api/test_history_contents.py +++ b/lib/galaxy_test/api/test_history_contents.py @@ -249,6 +249,32 @@ class HistoryContentsApiTestCase(ApiTestCase): assert objects[0]["deleted"] is False assert objects[0]["visible"] is True + def test_update_batch_collections(self): + hdca = self._create_pair_collection() + assert hdca["deleted"] is False + assert hdca["visible"] is True + + # update deleted flag => true + payload = dict(items=[{"history_content_type": "dataset_collection", "id": hdca["id"]}], deleted=True) + update_response = self._raw_update_batch(payload) + objects = update_response.json() + assert objects[0]["deleted"] is True + assert objects[0]["visible"] is True + + # update visibility flag => false + payload = dict(items=[{"history_content_type": "dataset_collection", "id": hdca["id"]}], visible=False) + update_response = self._raw_update_batch(payload) + objects = update_response.json() + assert objects[0]["deleted"] is True + assert objects[0]["visible"] is False + + # update both flags + payload = dict(items=[{"history_content_type": "dataset_collection", "id": hdca["id"]}], deleted=False, visible=True) + update_response = self._raw_update_batch(payload) + objects = update_response.json() + assert objects[0]["deleted"] is False + assert objects[0]["visible"] is True + def test_update_type_failures(self): hda1 = self._wait_for_new_hda() update_response = self._raw_update(hda1["id"], dict(deleted='not valid')) From 0bc41e0b8978f40655c23f3786f4ed0bda7c558e Mon Sep 17 00:00:00 2001 From: davelopez <46503462+davelopez@users.noreply.github.com> Date: Thu, 20 Jan 2022 19:30:00 +0100 Subject: [PATCH 11/11] Fix dataset collections not hiding when updating in batch NOTE: this commit can be dropped when merging forward as this is already refactored and fixed in the dev branch --- lib/galaxy/schema/schema.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/galaxy/schema/schema.py b/lib/galaxy/schema/schema.py index 78d2e9e6149..c45da53dc8a 100644 --- a/lib/galaxy/schema/schema.py +++ b/lib/galaxy/schema/schema.py @@ -646,7 +646,7 @@ class UpdateHistoryContentsBatchPayload(BaseModel): description="A list of content items to update with the changes.", ) deleted: Optional[bool] = Field( - default=False, + default=None, title="Deleted", description=( "This will check the uploading state if not deleting (i.e: deleted=False), " @@ -654,7 +654,7 @@ class UpdateHistoryContentsBatchPayload(BaseModel): ), ) visible: Optional[bool] = Field( - default=False, + default=None, title="Visible", description=( "Show or hide history contents"