diff --git a/client/src/mvc/tool/tool-form.js b/client/src/mvc/tool/tool-form.js index 43b89ccfca2..e1bb04265ec 100644 --- a/client/src/mvc/tool/tool-form.js +++ b/client/src/mvc/tool/tool-form.js @@ -356,7 +356,7 @@ const View = Backbone.View.extend({ continue; } if (!input_def.optional && input_value == null && input_def.type != "hidden") { - this.form.highlight(input_id); + this.form.highlight(input_id, "This input requires a value."); return false; } if (input_field.validate) { diff --git a/lib/galaxy/tool_util/linters/inputs.py b/lib/galaxy/tool_util/linters/inputs.py index ff905a1a0df..7446653410b 100644 --- a/lib/galaxy/tool_util/linters/inputs.py +++ b/lib/galaxy/tool_util/linters/inputs.py @@ -44,6 +44,11 @@ def lint_inputs(tool_xml, lint_ctx): if dynamic_options is None and len(select_options) == 0: lint_ctx.warn(f"No options defined for select [{param_name}]") + if param_attrib.get("display") == "checkboxes": + if not string_as_bool(param_attrib.get("multiple", "false")): + lint_ctx.error(f'Select [{param_name}] `display="checkboxes"` is incompatible with `multiple="false"`, remove the `display` attribute') + if not string_as_bool(param_attrib.get("optional", "false")): + lint_ctx.error(f'Select [{param_name}] `display="checkboxes"` is incompatible with `optional="false"`, remove the `display` attribute') if param_attrib.get("display") == "radio": if string_as_bool(param_attrib.get("multiple", "false")): lint_ctx.error(f'Select [{param_name}] display="radio" is incompatible with multiple="true"') diff --git a/lib/galaxy/tool_util/xsd/galaxy.xsd b/lib/galaxy/tool_util/xsd/galaxy.xsd index 86e696ef871..269f9ba45d8 100644 --- a/lib/galaxy/tool_util/xsd/galaxy.xsd +++ b/lib/galaxy/tool_util/xsd/galaxy.xsd @@ -2888,7 +2888,8 @@ is still the column index. Used only when the ``type`` attribute value is Render a select list as a set of -checkboxes (``checkboxes``) or radio buttons (``radio``; note this is +checkboxes (``checkboxes``; note this is incompatible with ``multiple="false"`` +and ``optional="false"``) or radio buttons (``radio``; note this is incompatible with ``multiple="true"`` and ``optional="true"``). Defaults to a drop-down menu select list. Used only when the ``type`` attribute value is ``select``. diff --git a/test/unit/tool_util/test_tool_linters.py b/test/unit/tool_util/test_tool_linters.py index 16e64a4b000..99036d459fc 100644 --- a/test/unit/tool_util/test_tool_linters.py +++ b/test/unit/tool_util/test_tool_linters.py @@ -31,7 +31,14 @@ RADIO_SELECT_INCOMPATIBILITIES = """ The BWA Mapper bwa.py --version - + + + + + + + + """ @@ -51,13 +58,35 @@ SELECT_DUPLICATED_OPTIONS = """ TESTS = [ (NO_SECTIONS_XML, inputs.lint_inputs, lambda x: 'Found no input parameters.' in x.warn_messages), - (NO_WHEN_IN_CONDITIONAL_XML, inputs.lint_inputs, lambda x: "Conditional [labels] no block found for select option 'none'" in x.warn_messages), - (RADIO_SELECT_INCOMPATIBILITIES, inputs.lint_inputs, lambda x: 'Select [radio_select] display="radio" is incompatible with optional="true"' in x.error_messages and 'Select [radio_select] display="radio" is incompatible with multiple="true"' in x.error_messages), - (SELECT_DUPLICATED_OPTIONS, inputs.lint_inputs, lambda x: 'Select [select] has multiple options with the same text content' in x.error_messages and 'Select [select] has multiple options with the same value' in x.error_messages), + ( + NO_WHEN_IN_CONDITIONAL_XML, inputs.lint_inputs, + lambda x: "Conditional [labels] no block found for select option 'none'" in x.warn_messages + ), + ( + RADIO_SELECT_INCOMPATIBILITIES, inputs.lint_inputs, + lambda x: + 'Select [radio_select] display="radio" is incompatible with optional="true"' in x.error_messages + and 'Select [radio_select] display="radio" is incompatible with multiple="true"' in x.error_messages + and 'Select [radio_checkboxes] `display="checkboxes"` is incompatible with `optional="false"`, remove the `display` attribute' in x.error_messages + and 'Select [radio_checkboxes] `display="checkboxes"` is incompatible with `multiple="false"`, remove the `display` attribute' in x.error_messages + ), + ( + SELECT_DUPLICATED_OPTIONS, inputs.lint_inputs, + lambda x: + 'Select [select] has multiple options with the same text content' in x.error_messages + and 'Select [select] has multiple options with the same value' in x.error_messages + ), +] + +TEST_IDS = [ + 'Lint no sections', + 'lint no when', + 'radio select incompatibilities', + 'select duplicated options' ] -@pytest.mark.parametrize('tool_xml,lint_func,assert_func', TESTS, ids=['Lint no sections', 'lint no when', 'radio select incompatibilities', 'select duplicated options']) +@pytest.mark.parametrize('tool_xml,lint_func,assert_func', TESTS, ids=TEST_IDS) def test_tool_xml(tool_xml, lint_func, assert_func): lint_ctx = LintContext('all') tree = etree.ElementTree(element=etree.fromstring(tool_xml))