diff --git a/client/src/components/Workflow/Editor/Lint.vue b/client/src/components/Workflow/Editor/Lint.vue index 3cb265c9d90c..1fafce2c2e4b 100644 --- a/client/src/components/Workflow/Editor/Lint.vue +++ b/client/src/components/Workflow/Editor/Lint.vue @@ -4,6 +4,7 @@ -
+
{{ successMessage }} @@ -16,7 +16,12 @@ @mouseover="onMouseOver(item)" @focusout="onMouseLeave(item)" @mouseleave="onMouseLeave(item)"> - + {{ item.stepLabel }}: {{ item.warningLabel }} @@ -37,6 +42,8 @@ import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome"; import BootstrapVue from "bootstrap-vue"; import Vue from "vue"; +import { dataAttributes } from "./modules/linting"; + Vue.use(BootstrapVue); library.add(faMagic); @@ -80,6 +87,7 @@ export default { }, }, methods: { + dataAttributes: dataAttributes, onMouseOver(id) { this.$emit("onMouseOver", id); }, diff --git a/client/src/components/Workflow/Editor/modules/linting.ts b/client/src/components/Workflow/Editor/modules/linting.ts index 21d02cafff88..062183acf29b 100644 --- a/client/src/components/Workflow/Editor/modules/linting.ts +++ b/client/src/components/Workflow/Editor/modules/linting.ts @@ -13,6 +13,7 @@ interface LintState { name?: string; inputName?: string; autofix?: boolean; + data?: Record; } export function getDisconnectedInputs( @@ -50,18 +51,27 @@ export function getMissingMetadata(steps: Steps) { const noAnnotation = !step.annotation; const noLabel = !step.label; let warningLabel = null; + const data = { + "missing-label": "false", + "missing-annotation": "false", + }; if (noLabel && noAnnotation) { warningLabel = "Missing a label and annotation"; + data["missing-label"] = "true"; + data["missing-annotation"] = "true"; } else if (noLabel) { warningLabel = "Missing a label"; + data["missing-label"] = "true"; } else if (noAnnotation) { warningLabel = "Missing an annotation"; + data["missing-annotation"] = "true"; } if (warningLabel) { inputs.push({ stepId: step.id, stepLabel: step.label || step.content_id || step.name, warningLabel: warningLabel, + data: data, }); } } @@ -69,6 +79,15 @@ export function getMissingMetadata(steps: Steps) { return inputs; } +export function dataAttributes(action: LintState): Record { + const result: Record = {}; + for (const [key, value] of Object.entries(action.data || {})) { + result[`data-${key}`] = value; + } + + return result; +} + export function getUnlabeledOutputs(steps: Steps) { const outputs: LintState[] = []; Object.values(steps).forEach((step) => { diff --git a/client/src/utils/navigation/navigation.yml b/client/src/utils/navigation/navigation.yml index 3876199f845c..a4ae98ef00b3 100644 --- a/client/src/utils/navigation/navigation.yml +++ b/client/src/utils/navigation/navigation.yml @@ -750,6 +750,13 @@ workflow_editor: attributes: "#activity-workflow-editor-attributes" changes: "#activity-workflow-undo-redo" upgrade_all: "#activity-workflow-upgrade" + best_practices: "#activity-workflow-best-practices" + + best_practices: + selectors: + section_input_metadata: '[data-description="linting input metadata"]' + item_input_metadata: '[data-description="linting input metadata"] [data-item-index="${index}"]' + comment: selectors: _: ".workflow-editor-comment" diff --git a/lib/galaxy/selenium/navigates_galaxy.py b/lib/galaxy/selenium/navigates_galaxy.py index 241d95babeec..42d9a7702da0 100644 --- a/lib/galaxy/selenium/navigates_galaxy.py +++ b/lib/galaxy/selenium/navigates_galaxy.py @@ -1242,6 +1242,16 @@ def workflow_editor_set_tool_vesrion(self, version: str, node: Optional[EditorNo editor.tool_version_button.wait_for_and_click() assert self.select_dropdown_item(f"Switch to {version}"), "Switch to tool version dropdown item not found" + def workflow_editor_set_node_label(self, label: str, node: Optional[EditorNodeReference] = None): + self.workflow_editor_ensure_tool_form_open(node) + editor = self.components.workflow_editor + editor.label_input.wait_for_and_clear_and_send_keys(label) + + def workflow_editor_set_node_annotation(self, annotation: str, node: Optional[EditorNodeReference] = None): + self.workflow_editor_ensure_tool_form_open(node) + editor = self.components.workflow_editor + editor.annotation_input.wait_for_and_clear_and_send_keys(annotation) + def workflow_editor_ensure_tool_form_open(self, node: Optional[EditorNodeReference] = None): # if node is_empty just assume current tool step is open editor = self.components.workflow_editor diff --git a/lib/galaxy_test/selenium/test_workflow_editor.py b/lib/galaxy_test/selenium/test_workflow_editor.py index 3132bfb6dd57..37c9ef657d68 100644 --- a/lib/galaxy_test/selenium/test_workflow_editor.py +++ b/lib/galaxy_test/selenium/test_workflow_editor.py @@ -210,8 +210,8 @@ def test_data_input(self): name = self.workflow_create_new() self.workflow_editor_add_input(item_name="data_input") self.screenshot("workflow_editor_data_input_new") - editor.label_input.wait_for_and_send_keys("input1") - editor.annotation_input.wait_for_and_send_keys("my cool annotation") + self.workflow_editor_set_node_label("input1") + self.workflow_editor_set_node_annotation("my cool annotation") self.sleep_for(self.wait_types.UX_RENDER) self.screenshot("workflow_editor_data_input_filled_in") self.workflow_editor_click_save() @@ -542,6 +542,38 @@ def test_rendering_simple_nested_workflow(self): self.workflow_editor_maximize_center_pane() self.screenshot("workflow_editor_simple_nested") + @selenium_test + def test_best_practices_input_label(self): + editor = self.components.workflow_editor + annotation = "best_practices_input_label" + self.workflow_create_new(annotation=annotation) + self.workflow_editor_add_input(item_name="data_input") + editor.tool_bar.best_practices.wait_for_and_click() + best_practices = editor.best_practices + section_element = best_practices.section_input_metadata.wait_for_present() + assert section_element.get_attribute("data-lint-status") == "warning" + item = best_practices.item_input_metadata(index=0) + element = item.wait_for_present() + assert element.get_attribute("data-missing-label") == "true" + assert element.get_attribute("data-missing-annotation") == "true" + + item.wait_for_and_click() + self.workflow_editor_set_node_label("best practice input") + + # editor.tool_bar.best_practices.wait_for_and_click() + element = item.wait_for_present() + assert element.get_attribute("data-missing-label") == "false" + assert element.get_attribute("data-missing-annotation") == "true" + + self.workflow_editor_set_node_annotation("informative annotation") + + @retry_assertion_during_transitions + def assert_linting_input_metadata_okay(): + section_element = best_practices.section_input_metadata.wait_for_present() + assert section_element.get_attribute("data-lint-status") == "ok" + + assert_linting_input_metadata_okay() + @selenium_test def test_rendering_rules_workflow_1(self): self.open_in_workflow_editor(WORKFLOW_WITH_RULES_1)