From 74bd46f39dfbe73ea20d19a1e62d51f07fb12ddf Mon Sep 17 00:00:00 2001 From: John Chilton Date: Wed, 11 Apr 2018 21:41:46 -0400 Subject: [PATCH] More, better workflow editor Selenium tests - part 2. - More stuff tested: - Connecting and disconnecting nodes. - Rendering simple wfs with output collections and subworkflows. - More screenshots. --- templates/base/base_panels.mako | 8 +- templates/webapps/galaxy/galaxy.panels.mako | 8 +- test/api/test_workflows.py | 131 ++-------------- test/base/workflow_fixtures.py | 158 ++++++++++++++++++++ test/galaxy_selenium/navigation.yml | 6 + test/selenium_tests/_workflow_fixtures.py | 38 ----- test/selenium_tests/test_workflow_editor.py | 101 ++++++++++++- test/selenium_tests/test_workflow_run.py | 3 +- 8 files changed, 285 insertions(+), 168 deletions(-) create mode 100644 test/base/workflow_fixtures.py delete mode 100644 test/selenium_tests/_workflow_fixtures.py diff --git a/templates/base/base_panels.mako b/templates/base/base_panels.mako index c55af74cb2e1..9bc101a334e0 100644 --- a/templates/base/base_panels.mako +++ b/templates/base/base_panels.mako @@ -223,8 +223,8 @@
${self.left_panel()}
%endif @@ -235,8 +235,8 @@ %endif diff --git a/templates/webapps/galaxy/galaxy.panels.mako b/templates/webapps/galaxy/galaxy.panels.mako index 856cbaae62d1..3936dfe1aa90 100644 --- a/templates/webapps/galaxy/galaxy.panels.mako +++ b/templates/webapps/galaxy/galaxy.panels.mako @@ -209,8 +209,8 @@
%endif @@ -237,8 +237,8 @@
%endif diff --git a/test/api/test_workflows.py b/test/api/test_workflows.py index 5f0e3083e9f5..72476642d634 100644 --- a/test/api/test_workflows.py +++ b/test/api/test_workflows.py @@ -20,54 +20,16 @@ wait_on, WorkflowPopulator ) +from base.workflow_fixtures import ( # noqa: I100 + WORKFLOW_NESTED_SIMPLE, + WORKFLOW_WITH_OUTPUT_COLLECTION, + WORKFLOW_WITH_OUTPUT_COLLECTION_MAPPING, + WORKFLOW_WITH_RULES_1, +) from galaxy.exceptions import error_codes # noqa: I201 from galaxy.tools.verify.test_data import TestDataResolver -SIMPLE_NESTED_WORKFLOW_YAML = """ -class: GalaxyWorkflow -inputs: - - id: outer_input -outputs: - - id: outer_output - source: second_cat#out_file1 -steps: - - tool_id: cat1 - label: first_cat - state: - input1: - $link: outer_input - - run: - class: GalaxyWorkflow - inputs: - - id: inner_input - outputs: - - id: workflow_output - source: random_lines#out_file1 - steps: - - tool_id: random_lines1 - label: random_lines - state: - num_lines: 1 - input: - $link: inner_input - seed_source: - seed_source_selector: set_seed - seed: asdf - label: nested_workflow - connect: - inner_input: first_cat#out_file1 - - tool_id: cat1 - label: second_cat - state: - input1: - $link: nested_workflow#workflow_output - queries: - - input2: - $link: nested_workflow#workflow_output -""" - - class BaseWorkflowsApiTestCase(api.ApiTestCase): # TODO: Find a new file for this class. @@ -418,7 +380,7 @@ def get_subworkflow_content_id(workflow_id): subworkflow_step = next(s for s in steps.values() if s["type"] == "subworkflow") return subworkflow_step['content_id'] - workflow_id = self._upload_yaml_workflow(SIMPLE_NESTED_WORKFLOW_YAML, publish=True) + workflow_id = self._upload_yaml_workflow(WORKFLOW_NESTED_SIMPLE, publish=True) subworkflow_content_id = get_subworkflow_content_id(workflow_id) with self._different_user(): other_import_response = self.__import_workflow(workflow_id) @@ -577,21 +539,7 @@ def __run_cat_workflow(self, inputs_by): @skip_without_tool("collection_creates_pair") def test_workflow_run_output_collections(self): - workflow_id = self._upload_yaml_workflow(""" -class: GalaxyWorkflow -steps: - - label: text_input - type: input - - label: split_up - tool_id: collection_creates_pair - state: - input1: - $link: text_input - - tool_id: collection_paired_test - state: - f1: - $link: split_up#paired_output -""") + workflow_id = self._upload_yaml_workflow(WORKFLOW_WITH_OUTPUT_COLLECTION) with self.dataset_populator.test_history() as history_id: hda1 = self.dataset_populator.new_dataset(history_id, content="a\nb\nc\nd\n") inputs = { @@ -723,23 +671,7 @@ def test_workflow_resume_with_mapped_over_input(self): @skip_without_tool("collection_creates_pair") def test_workflow_run_output_collection_mapping(self): - workflow_id = self._upload_yaml_workflow(""" -class: GalaxyWorkflow -steps: - - type: input_collection - - tool_id: collection_creates_pair - state: - input1: - $link: 0 - - tool_id: collection_paired_test - state: - f1: - $link: 1#paired_output - - tool_id: cat_list - state: - input1: - $link: 2#out1 -""") + workflow_id = self._upload_yaml_workflow(WORKFLOW_WITH_OUTPUT_COLLECTION_MAPPING) with self.dataset_populator.test_history() as history_id: hdca1 = self.dataset_collection_populator.create_list_in_history(history_id, contents=["a\nb\nc\nd\n", "e\nf\ng\nh\n"]).json() self.dataset_populator.wait_for_history(history_id, assert_ok=True) @@ -934,7 +866,7 @@ def test_run_subworkflow_simple(self): outer_input: value: 1.bed type: File -""" % SIMPLE_NESTED_WORKFLOW_YAML +""" % WORKFLOW_NESTED_SIMPLE self._run_jobs(workflow_run_description, history_id=history_id) content = self.dataset_populator.get_history_dataset_content(history_id) @@ -984,44 +916,7 @@ def test_workflow_run_zip_collections(self): @skip_without_tool("__APPLY_RULES__") def test_workflow_run_apply_rules(self): with self.dataset_populator.test_history() as history_id: - self._run_jobs(""" -class: GalaxyWorkflow -inputs: - - type: collection - label: input_c -steps: - - label: apply - tool_id: __APPLY_RULES__ - state: - input: - $link: input_c - rules: - rules: - - type: add_column_metadata - value: identifier0 - - type: add_column_metadata - value: identifier0 - mapping: - - type: list_identifiers - columns: [0, 1] - - tool_id: random_lines1 - label: random_lines - state: - num_lines: 1 - input: - $link: apply#output - seed_source: - seed_source_selector: set_seed - seed: asdf -test_data: - input_c: - type: list - elements: - - identifier: i1 - content: "0" - - identifier: i2 - content: "1" -""", history_id=history_id, wait=True, assert_ok=True) + self._run_jobs(WORKFLOW_WITH_RULES_1, history_id=history_id, wait=True, assert_ok=True) output_content = self.dataset_populator.get_history_collection_details(history_id, hid=6) rules_test_data.check_example_2(output_content, self.dataset_populator) @@ -1254,7 +1149,7 @@ def test_workflow_run_input_mapping_with_subworkflows(self): - identifier: el2 value: 1.fastq type: File -""" % SIMPLE_NESTED_WORKFLOW_YAML, history_id=history_id) +""" % WORKFLOW_NESTED_SIMPLE, history_id=history_id) workflow_id = summary.workflow_id invocation_id = summary.invocation_id invocation_response = self._get("workflows/%s/invocations/%s" % (workflow_id, invocation_id)) @@ -1844,7 +1739,7 @@ def test_nested_workflow_rerun_with_use_cached_job(self): outer_input: value: 1.bed type: File -""" % SIMPLE_NESTED_WORKFLOW_YAML +""" % WORKFLOW_NESTED_SIMPLE run_jobs_summary = self._run_jobs(workflow_run_description, history_id=history_id_one) self.dataset_populator.wait_for_history(history_id_one, assert_ok=True) workflow_request = run_jobs_summary.workflow_request diff --git a/test/base/workflow_fixtures.py b/test/base/workflow_fixtures.py new file mode 100644 index 000000000000..5470865badb1 --- /dev/null +++ b/test/base/workflow_fixtures.py @@ -0,0 +1,158 @@ + +WORKFLOW_SIMPLE_CAT_TWICE = """ +class: GalaxyWorkflow +inputs: + - id: input1 +steps: + - tool_id: cat + label: first_cat + state: + input1: + $link: input1 + queries: + - input2: + $link: input1 +""" + +WORKFLOW_WITH_OLD_TOOL_VERSION = """ +class: GalaxyWorkflow +inputs: + - id: input1 +steps: + - tool_id: multiple_versions + tool_version: "0.0.1" + state: + inttest: 8 +""" + + +WORKFLOW_WITH_INVALID_STATE = """ +class: GalaxyWorkflow +inputs: + - id: input1 +steps: + - tool_id: multiple_versions + tool_version: "0.0.1" + state: + inttest: "moocow" +""" + + +WORKFLOW_WITH_OUTPUT_COLLECTION = """ +class: GalaxyWorkflow +steps: + - label: text_input + type: input + - label: split_up + tool_id: collection_creates_pair + state: + input1: + $link: text_input + - tool_id: collection_paired_test + state: + f1: + $link: split_up#paired_output +""" + + +WORKFLOW_WITH_OUTPUT_COLLECTION_MAPPING = """ +class: GalaxyWorkflow +steps: + - type: input_collection + - tool_id: collection_creates_pair + state: + input1: + $link: 0 + - tool_id: collection_paired_test + state: + f1: + $link: 1#paired_output + - tool_id: cat_list + state: + input1: + $link: 2#out1 +""" + + +WORKFLOW_WITH_RULES_1 = """ +class: GalaxyWorkflow +inputs: + - type: collection + label: input_c +steps: + - label: apply + tool_id: __APPLY_RULES__ + state: + input: + $link: input_c + rules: + rules: + - type: add_column_metadata + value: identifier0 + - type: add_column_metadata + value: identifier0 + mapping: + - type: list_identifiers + columns: [0, 1] + - tool_id: random_lines1 + label: random_lines + state: + num_lines: 1 + input: + $link: apply#output + seed_source: + seed_source_selector: set_seed + seed: asdf +test_data: + input_c: + type: list + elements: + - identifier: i1 + content: "0" + - identifier: i2 + content: "1" +""" + + +WORKFLOW_NESTED_SIMPLE = """ +class: GalaxyWorkflow +inputs: + - id: outer_input +outputs: + - id: outer_output + source: second_cat#out_file1 +steps: + - tool_id: cat1 + label: first_cat + state: + input1: + $link: outer_input + - run: + class: GalaxyWorkflow + inputs: + - id: inner_input + outputs: + - id: workflow_output + source: random_lines#out_file1 + steps: + - tool_id: random_lines1 + label: random_lines + state: + num_lines: 1 + input: + $link: inner_input + seed_source: + seed_source_selector: set_seed + seed: asdf + label: nested_workflow + connect: + inner_input: first_cat#out_file1 + - tool_id: cat1 + label: second_cat + state: + input1: + $link: nested_workflow#workflow_output + queries: + - input2: + $link: nested_workflow#workflow_output +""" diff --git a/test/galaxy_selenium/navigation.yml b/test/galaxy_selenium/navigation.yml index 2756344c4e2f..dab44a914c81 100644 --- a/test/galaxy_selenium/navigation.yml +++ b/test/galaxy_selenium/navigation.yml @@ -11,6 +11,10 @@ _: # global stuff selectors: editable_text: '.editable-text' tooltip_balloon: '.tooltip' + left_panel_drag: '#left-panel-drag' + left_panel_collapse: '#left-panel-collapse' + right_panel_drag: '#right-panel-drag' + right_panel_collapse: '#right-panel-collapse' messages: selectors: @@ -248,6 +252,8 @@ workflow_editor: connector_for: "canvas[handle1-id='${source_id}'][handle2-id='${sink_id}']" + connector_destroy_callout: '.callout .fa-times' + tour: popover: selectors: diff --git a/test/selenium_tests/_workflow_fixtures.py b/test/selenium_tests/_workflow_fixtures.py deleted file mode 100644 index 37a4d844e864..000000000000 --- a/test/selenium_tests/_workflow_fixtures.py +++ /dev/null @@ -1,38 +0,0 @@ - -WORKFLOW_SIMPLE_CAT_TWICE = """ -class: GalaxyWorkflow -inputs: - - id: input1 -steps: - - tool_id: cat - label: first_cat - state: - input1: - $link: input1 - queries: - - input2: - $link: input1 -""" - -WORKFLOW_WITH_OLD_TOOL_VERSION = """ -class: GalaxyWorkflow -inputs: - - id: input1 -steps: - - tool_id: multiple_versions - tool_version: "0.0.1" - state: - inttest: 8 -""" - - -WORKFLOW_WITH_INVALID_STATE = """ -class: GalaxyWorkflow -inputs: - - id: input1 -steps: - - tool_id: multiple_versions - tool_version: "0.0.1" - state: - inttest: "moocow" -""" diff --git a/test/selenium_tests/test_workflow_editor.py b/test/selenium_tests/test_workflow_editor.py index e9badc4f4718..f264ee762432 100644 --- a/test/selenium_tests/test_workflow_editor.py +++ b/test/selenium_tests/test_workflow_editor.py @@ -1,8 +1,12 @@ -from ._workflow_fixtures import ( +from base.workflow_fixtures import ( + WORKFLOW_NESTED_SIMPLE, WORKFLOW_SIMPLE_CAT_TWICE, WORKFLOW_WITH_INVALID_STATE, WORKFLOW_WITH_OLD_TOOL_VERSION, + WORKFLOW_WITH_OUTPUT_COLLECTION, + WORKFLOW_WITH_RULES_1, ) + from .framework import ( retry_assertion_during_transitions, selenium_test, @@ -15,11 +19,32 @@ class WorkflowEditorTestCase(SeleniumTestCase): ensure_registered = True @selenium_test - def test_build_workflow(self): + def test_basics(self): + editor = self.components.workflow_editor + name = self.workflow_create_new() edit_name_element = self.components.workflow_editor.edit_name.wait_for_visible() assert name in edit_name_element.text, edit_name_element.text + editor.canvas_body.wait_for_visible() + editor.tool_menu.wait_for_visible() + + self.screenshot("workflow_editor_blank") + + self.components._.left_panel_drag.wait_for_visible() + self.components._.left_panel_collapse.wait_for_and_click() + + self.sleep_for(self.wait_types.UX_RENDER) + + self.screenshot("workflow_editor_left_collapsed") + + self.components._.right_panel_drag.wait_for_visible() + self.components._.right_panel_collapse.wait_for_and_click() + + self.sleep_for(self.wait_types.UX_RENDER) + + self.screenshot("workflow_editor_left_and_right_collapsed") + @selenium_test def test_data_input(self): editor = self.components.workflow_editor @@ -76,11 +101,52 @@ def test_collection_input(self): @selenium_test def test_existing_connections(self): + editor = self.components.workflow_editor name = self.workflow_upload_yaml_with_random_name(WORKFLOW_SIMPLE_CAT_TWICE) self.workflow_index_open() self.workflow_index_open_with_name(name) self.workflow_editor_click_option("Auto Re-layout") self.assert_connected("input1#output", "first_cat#input1") + self.screenshot("workflow_editor_connection_simple") + + cat_node = editor.node._(label="first_cat") + cat_input = cat_node.input_terminal(name="input1") + cat_input.wait_for_and_click() + editor.connector_destroy_callout.wait_for_visible() + self.screenshot("workflow_editor_connection_callout") + editor.connector_destroy_callout.wait_for_and_click() + self.assert_not_connected("input1#output", "first_cat#input1") + self.screenshot("workflow_editor_connection_destroyed") + + self.workflow_editor_connect("input1#output", "first_cat#input1", screenshot_partial="workflow_editor_connection_dragging") + self.assert_connected("input1#output", "first_cat#input1") + + @selenium_test + def test_rendering_output_collection_connections(self): + name = self.workflow_upload_yaml_with_random_name(WORKFLOW_WITH_OUTPUT_COLLECTION) + self.workflow_index_open() + self.workflow_index_open_with_name(name) + self.workflow_editor_click_option("Auto Re-layout") + self.workflow_editor_maximize_center_pane() + self.screenshot("workflow_editor_output_collections") + + @selenium_test + def test_rendering_simple_nested_workflow(self): + name = self.workflow_upload_yaml_with_random_name(WORKFLOW_NESTED_SIMPLE) + self.workflow_index_open() + self.workflow_index_open_with_name(name) + self.workflow_editor_click_option("Auto Re-layout") + self.workflow_editor_maximize_center_pane() + self.screenshot("workflow_editor_simple_nested") + + @selenium_test + def test_rendering_rules_workflow(self): + name = self.workflow_upload_yaml_with_random_name(WORKFLOW_WITH_RULES_1) + self.workflow_index_open() + self.workflow_index_open_with_name(name) + self.workflow_editor_click_option("Auto Re-layout") + self.workflow_editor_maximize_center_pane() + self.screenshot("workflow_editor_rules_1") @selenium_test def test_save_as(self): @@ -132,7 +198,36 @@ def workflow_editor_save_and_close(self): self.workflow_editor_click_option("Save") self.workflow_editor_click_option("Close") + def workflow_editor_maximize_center_pane(self): + self.components._.left_panel_collapse.wait_for_and_click() + self.components._.right_panel_collapse.wait_for_and_click() + self.sleep_for(self.wait_types.UX_RENDER) + + def workflow_editor_connect(self, source, sink, screenshot_partial=None): + source_id, sink_id = self.workflow_editor_source_sink_terminal_ids(source, sink) + source_element = self.driver.find_element_by_css_selector("#" + source_id) + sink_element = self.driver.find_element_by_css_selector("#" + sink_id) + + ac = self.action_chains() + ac = ac.move_to_element(source_element).click_and_hold() + if screenshot_partial: + ac = ac.move_to_element_with_offset(sink_element, -5, 0) + ac.perform() + self.sleep_for(self.wait_types.UX_RENDER) + self.screenshot(screenshot_partial) + ac = self.action_chains() + + ac = ac.move_to_element(sink_element).release().perform() + def assert_connected(self, source, sink): + 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_visible() + + def assert_not_connected(self, source, sink): + 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 workflow_editor_source_sink_terminal_ids(self, source, sink): editor = self.components.workflow_editor source_node_label, source_output = source.split("#", 1) @@ -153,7 +248,7 @@ def assert_connected(self, source, sink): source_id = output_element.get_attribute("id") sink_id = input_element.get_attribute("id") - editor.connector_for(source_id=source_id, sink_id=sink_id).wait_for_visible() + return source_id, sink_id def workflow_index_open_with_name(self, name): self.workflow_index_search_for(name) diff --git a/test/selenium_tests/test_workflow_run.py b/test/selenium_tests/test_workflow_run.py index da9414ee71d4..143fb5e4e7c1 100644 --- a/test/selenium_tests/test_workflow_run.py +++ b/test/selenium_tests/test_workflow_run.py @@ -1,7 +1,8 @@ -from ._workflow_fixtures import ( +from base.workflow_fixtures import ( WORKFLOW_SIMPLE_CAT_TWICE, WORKFLOW_WITH_OLD_TOOL_VERSION, ) + from .framework import ( selenium_test, SeleniumTestCase,