4 new commits in galaxy-central: https://bitbucket.org/galaxy/galaxy-central/commits/9d7aa747eb7e/ Changeset: 9d7aa747eb7e User: jmchilton Date: 2014-04-10 20:31:46 Summary: Remove unused variable? Affected #: 1 file diff -r 2eb03ce13018bbd46ddf55b296246b355afd7ba4 -r 9d7aa747eb7ee1f01db0821e51bc80f0271204a5 static/scripts/galaxy.workflow_editor.canvas.js --- a/static/scripts/galaxy.workflow_editor.canvas.js +++ b/static/scripts/galaxy.workflow_editor.canvas.js @@ -422,7 +422,6 @@ // Update input rows var old_body = el.find( "div.inputs" ); var new_body = $("<div class='inputs'></div>"); - var old = old_body.find( "div.input-data-row"); $.each( data.data_inputs, function( i, input ) { var t = node.new_input_terminal( input ); // If already connected save old connection https://bitbucket.org/galaxy/galaxy-central/commits/b51841ba5ce8/ Changeset: b51841ba5ce8 User: jmchilton Date: 2014-04-10 20:31:47 Summary: Refactor interaction between workflows JS and editor JS for adding nodes. Add abstraction into editor for use by galaxy.workflow.js to hide some editor details from workflow code and reduce duplication between add_node_for_tool and add_node_for_module. Affected #: 2 files diff -r 9d7aa747eb7ee1f01db0821e51bc80f0271204a5 -r b51841ba5ce81e0adb416c80123ed28184e4c549 static/scripts/galaxy.workflow_editor.canvas.js --- a/static/scripts/galaxy.workflow_editor.canvas.js +++ b/static/scripts/galaxy.workflow_editor.canvas.js @@ -892,6 +892,18 @@ return node; } +function add_node( type, title_text, tool_id ) { + // Abstraction for use by galaxy.workflow.js to hide + // some editor details from workflow code and reduce duplication + // between add_node_for_tool and add_node_for_module. + var node = prebuild_node( type, title_text, tool_id ); + workflow.add_node( node ); + workflow.fit_canvas_to_nodes(); + canvas_manager.draw_overview(); + workflow.activate_node( node ); + return node; +} + var ext_to_type = null; var type_to_type = null; diff -r 9d7aa747eb7ee1f01db0821e51bc80f0271204a5 -r b51841ba5ce81e0adb416c80123ed28184e4c549 static/scripts/galaxy.workflows.js --- a/static/scripts/galaxy.workflows.js +++ b/static/scripts/galaxy.workflows.js @@ -320,11 +320,7 @@ // Add a new step to the workflow by tool id function add_node_for_tool( id, title ) { - var node = prebuild_node( 'tool', title, id ); - workflow.add_node( node ); - workflow.fit_canvas_to_nodes(); - canvas_manager.draw_overview(); - workflow.activate_node( node ); + node = add_node( 'tool', title, id ); $.ajax( { url: get_new_module_info_url, data: { type: "tool", tool_id: id, "_": "true" }, @@ -344,11 +340,7 @@ } function add_node_for_module( type, title ) { - node = prebuild_node( type, title ); - workflow.add_node( node ); - workflow.fit_canvas_to_nodes(); - canvas_manager.draw_overview(); - workflow.activate_node( node ); + node = add_node( type, title ); $.ajax( { url: get_new_module_info_url, data: { type: type, "_": "true" }, https://bitbucket.org/galaxy/galaxy-central/commits/8f577a1a3901/ Changeset: 8f577a1a3901 User: jmchilton Date: 2014-04-10 20:31:48 Summary: Outline workflow editor tests. Affected #: 2 files diff -r b51841ba5ce81e0adb416c80123ed28184e4c549 -r 8f577a1a390145aaab909ccbfbd31a78241f2e45 test/qunit/tests/workflow_editor_tests.html --- /dev/null +++ b/test/qunit/tests/workflow_editor_tests.html @@ -0,0 +1,10 @@ +<!doctype html> +<!-- Minimal outline test page for a requirejs+qunit testing setup, + test environment is bootstrapped in test-common.js --> +<html> + <head> + <script data-main="../test-common" src="../scripts/libs/require.js"></script> + </head> + <body> + </body> +</html> diff -r b51841ba5ce81e0adb416c80123ed28184e4c549 -r 8f577a1a390145aaab909ccbfbd31a78241f2e45 test/qunit/tests/workflow_editor_tests.js --- /dev/null +++ b/test/qunit/tests/workflow_editor_tests.js @@ -0,0 +1,379 @@ +/* global define, QUnit, module, test, ok, equal, deepEqual, notEqual */ +/* global Workflow, CanvasManager, InputTerminal, Connector, add_node */ +define([ + "galaxy.workflow_editor.canvas", + "jquery", + "sinon-qunit" +], function( + workflowEditor, + $, + sinon +){ + "use strict"; + + // globals cosumed by workflow editor + window.show_form_for_tool = sinon.spy(); + window.workflow = null; + window.canvas_manager = null; + + // Stub used over stubbing issubtype for unit testing datatype comparisons. + var issubtypeStub = null; + + QUnit.moduleStart(function() { + if( issubtypeStub === null) { + issubtypeStub = sinon.stub( window, "issubtype" ); + } + }); + QUnit.moduleDone(function() { + if( issubtypeStub !== null) { + issubtypeStub.restore(); + issubtypeStub = null; + } + }); + + var with_canvas_container = function( f ) { + var canvas_container = $("<div id='canvas-container'>"); + $("body").append( canvas_container ); + f( canvas_container ); + canvas_container.remove(); + }; + + var with_workflow_global = function( f ) { + var overview = $( "<div id='overview'><canvas id='overview-canvas'></canvas><div id='overview-viewport'></div></div>" ); + var canvas_viewport = $( "<div id='canvas-viewport'><div id='canvas-container'></div></div>" ); + + $("body").append( overview, canvas_viewport ); + window.canvas_manager = new CanvasManager( canvas_viewport, overview ); + with_canvas_container( function( canvas_container ) { + window.workflow = new Workflow( canvas_container ); + f( window.workflow ); + window.workflow = null; + } ); + window.canvas_manager = null; + overview.remove(); + canvas_viewport.remove(); + }; + + module( "Input terminal model test", { + setup: function() { + this.node = { }; + this.element = $( "<div>" ); + this.input_terminal = new InputTerminal( this.element, [ "txt" ] ); + this.input_terminal.node = this.node; + }, + test_connector: function( attr ) { + var connector = attr || {}; + this.input_terminal.connectors.push( connector ); + return connector; + }, + with_test_connector: function( attr, f ) { + this.test_connector( attr ); + f(); + this.reset_connectors(); + }, + reset_connectors: function( ) { + this.input_terminal.connectors = []; + }, + test_accept: function( other ) { + other = other || { node: {}, datatypes: [ "txt" ] }; + return this.input_terminal.can_accept( other ); + } + } ); + + test( "test connect", function() { + this.node.changed = sinon.spy(); + + var connector = {}; + this.input_terminal.connect( connector ); + + // Assert node changed called + ok( this.node.changed.called ); + // Assert connectors updated + ok( this.input_terminal.connectors[ 0 ] === connector ); + } ); + + test( "test disconnect", function() { + this.node.changed = sinon.spy(); + + var connector = this.test_connector( {} ); + this.input_terminal.disconnect( connector ); + + // Assert node changed called + ok( this.node.changed.called ); + // Assert connectors updated + equal( this.input_terminal.connectors.length, 0 ); + } ); + + test( "test redraw", function() { + var connector = this.test_connector( { redraw: sinon.spy() } ); + this.input_terminal.redraw(); + // Assert connectors were redrawn + ok( connector.redraw.called ); + } ); + + test( "test destroy", function() { + var connector = this.test_connector( { destroy: sinon.spy() } ); + + this.input_terminal.destroy(); + // Assert connectors were redrawn + ok( connector.destroy.called ); + } ); + + test( "can accept correct datatype", function() { + issubtypeStub.returns(true); + ok( this.test_accept() ) ; + } ); + + test( "cannot accept incorrect datatypes", function() { + issubtypeStub.returns(false); + ok( ! this.test_accept() ); + } ); + + test( "can accept inputs", function() { + issubtypeStub.returns(false); + // Other is data input module - always accept (currently - could be + // more intelligent by looking at what else input is connected to. + var other = { node: {}, datatypes: [ "input" ] }; + ok( this.test_accept( other ) ); + } ); + + test( "cannot accept when already connected", function() { + issubtypeStub.returns(true); + var self = this; + // If other is subtype but already connected, cannot accept + this.with_test_connector( {}, function() { + ok( ! self.test_accept() ); + } ); + } ); + + module( "Connector test", { + + } ); + + test( "connects only if both valid handles", function() { + var input = { connect: sinon.spy() }; + var output = { connect: sinon.spy() }; + new Connector( input, null ); + new Connector( null, output ); + // Not attempts to connect... + ok( ! input.connect.called ); + ok( ! output.connect.called ); + + new Connector( input, output ); + ok( input.connect.called ); + ok( output.connect.called ); + }); + + test( "default attributes", function() { + var input = { connect: sinon.spy() }; + var output = { connect: sinon.spy() }; + var connector = new Connector( input, output ); + + equal( connector.dragging, false ); + equal( connector.canvas, null ); + equal( connector.inner_color, "#FFFFFF" ); + equal( connector.outer_color, "#D8B365" ); + } ); + + test( "destroy", function() { + var input = { connect: sinon.spy(), disconnect: sinon.spy() }; + var output = { connect: sinon.spy(), disconnect: sinon.spy() }; + var connector = new Connector( input, output ); + + connector.destroy(); + ok( input.disconnect.called ); + ok( output.disconnect.called ); + } ); + + test( "initial redraw", function() { + with_canvas_container( function( canvas_container ) { + var input = { connect: sinon.spy(), element: $("<div>") }; + var output = { connect: sinon.spy(), element: $("<div>") }; + var connector = new Connector( input, output ); + + connector.redraw(); + // Ensure canvas gets set + ok( connector.canvas ); + // Ensure it gest added to canvas container + equal( canvas_container.children()[ 0 ], connector.canvas ); + } ); + } ); + + module( "Node unit test", { + setup: function() { + this.input_terminal = { destroy: sinon.spy(), redraw: sinon.spy() }; + this.output_terminal = { destroy: sinon.spy(), redraw: sinon.spy() }; + this.element = $("<div><div class='toolFormBody'></div></div>"); + this.node = new Node( this.element ); + this.node.input_terminals.i1 = this.input_terminal; + this.node.output_terminals.o1 = this.output_terminal; + }, + $: function( selector ) { + return $( this.node.element.find( selector ) ); + }, + expect_workflow_node_changed: function( f ) { + var node = this.node; + with_workflow_global( function( workflow ) { + var node_changed_spy = sinon.spy( workflow, "node_changed" ); + f(); + ok( node_changed_spy.calledWith( node ) ); + } ); + }, + init_field_data_simple: function() { + var data = { + data_inputs: [ {name: "input1", extensions: [ "data" ] } ], + data_outputs: [ {name: "output1", extensions: [ "data" ] } ], + }; + this.node.init_field_data( data ); + }, + update_field_data_with_new_input: function() { + var new_data = { + data_inputs: [ + { name: "input1", extensions: [ "data" ] }, + { name: "extra_0|input1", extensions: [ "data" ] }, + ], + data_outputs: [ {name: "output1", extensions: [ "data" ] } ], + post_job_actions: "{}" + }; + this.node.update_field_data( new_data ); + } + } ); + + test( "make active", function() { + ok( ! this.element.hasClass( "toolForm-active" ) ); + this.node.make_active(); + ok( this.element.hasClass( "toolForm-active" ) ); + } ); + + test( "destroy", function() { + var test = this; + with_workflow_global( function( workflow ) { + var remove_node_spy = sinon.spy( workflow, "remove_node" ); + test.node.destroy(); + ok( test.input_terminal.destroy.called ); + ok( test.output_terminal.destroy.called ); + ok( remove_node_spy.calledWith( test.node ) ); + } ); + } ); + + test( "error", function() { + // Test body of toolFormBody div updated and workflow notified of change. + var test = this; + this.expect_workflow_node_changed( function() { + test.node.error( "TOOL ERROR" ); + equal( $( test.$(".toolFormBody").children()[ 0 ] ).html(), "TOOL ERROR" ); + } ); + } ); + + test( "init_field_data properties", function() { + var node = this.node; + this.expect_workflow_node_changed( function( ) { + var data = { + data_inputs: [], + data_outputs: [], + type: "tool", + name: "cat1", + form_html: "<form>", + tool_state: "ok", + tool_errors: false, + tooltip: "tool tooltip", + annotation: "tool annotation", + workflow_outputs: [ "out1" ], + }; + node.init_field_data( data ); + equal( node.type, "tool" ); + equal( node.name, "cat1" ); + equal( node.form_html, "<form>" ); + equal( node.tool_state, "ok" ); + equal( node.tooltip, "tool tooltip" ); + equal( node.annotation, "tool annotation" ); + deepEqual( node.post_job_actions, {} ); + deepEqual( node.workflow_outputs, [ "out1" ] ); + } ); + } ); + + test( "init_field_data data", function() { + var test = this; + this.expect_workflow_node_changed( function( ) { + // pre-init not tool form body... + equal( test.$( ".output-terminal" ).length, 0 ); + equal( test.$( ".input-terminal" ).length, 0 ); + equal( test.$( ".rule" ).length, 0 ); + + test.init_field_data_simple(); + + // After init tool form should have three "rows"/divs - , inputs div, one output, and rule... + equal( test.$( ".output-terminal" ).length, 1 ); + equal( test.$( ".input-terminal" ).length, 1 ); + equal( test.$( ".rule" ).length, 1 ); + + equal( test.$( ".toolFormBody" ).children().length, 3 ); + } ); + } ); + + test( "update_field_data updated data inputs and outputs", function() { + var test = this; + this.expect_workflow_node_changed( function( ) { + // Call init with one input and output. + test.init_field_data_simple(); + + test.update_field_data_with_new_input(); + + // Now there are 2 inputs... + equal( test.$( ".input-terminal" ).length, 2 ); + equal( test.$( ".output-terminal" ).length, 1 ); + equal( test.$( ".rule" ).length, 1 ); + } ); + } ); + + test( "update_field_data preserves connectors", function() { + var test = this; + var node = this.node; + this.expect_workflow_node_changed( function( ) { + // Call init with one input and output. + test.init_field_data_simple(); + + var connector = new Connector(); + var old_input_terminal = node.input_terminals.input1; + old_input_terminal.connectors.push( connector ); + + test.update_field_data_with_new_input(); + + var new_input_terminal = node.input_terminals.input1; + equal( old_input_terminal, old_input_terminal ); + notEqual( old_input_terminal, new_input_terminal ); + equal( connector, new_input_terminal.connectors[ 0 ] ); + } ); + } ); + + test( "update_field_data destroys old terminals", function() { + var test = this; + var node = this.node; + this.expect_workflow_node_changed( function( ) { + var data = { + data_inputs: [ { name: "input1", extensions: [ "data" ] }, + { name: "willDisappear", extensions: [ "data" ] } ], + data_outputs: [ {name: "output1", extensions: [ "data" ] } ], + }; + node.init_field_data( data ); + + var old_input_terminal = node.input_terminals.willDisappear; + var destroy_spy = sinon.spy( old_input_terminal, "destroy" ); + // Update + test.update_field_data_with_new_input(); + + ok( destroy_spy.called ); + } ); + } ); + + module( "add_node" ); + + test( "node added to workflow", function() { + with_workflow_global( function( workflow ) { + var add_node_spy = sinon.spy( workflow, "add_node" ); + var node = add_node( "tool", "Cat Files", "cat1" ); + ok( add_node_spy.calledWith( node ) ); + } ); + } ); + +}); \ No newline at end of file https://bitbucket.org/galaxy/galaxy-central/commits/bacb1a3c0eaa/ Changeset: bacb1a3c0eaa User: jmchilton Date: 2014-04-10 20:31:48 Summary: Simplify enable_output_terminal and enable_output_terminal in workflow editor. These functions were taking in a single element and then looping through it. I hope this is not some intententional jQuery pattern to change the value of 'this' I don't quite understand and is just a remnant of when some loop over inputs was on the inside of this function or some other such refactoring. Affected #: 1 file diff -r 8f577a1a390145aaab909ccbfbd31a78241f2e45 -r bacb1a3c0eaa0fd21ddc9f5099cb65bd2c826a9c static/scripts/galaxy.workflow_editor.canvas.js --- a/static/scripts/galaxy.workflow_editor.canvas.js +++ b/static/scripts/galaxy.workflow_editor.canvas.js @@ -172,101 +172,98 @@ } $.extend( Node.prototype, { new_input_terminal : function( input ) { - var t = $("<div class='terminal input-terminal'></div>"); + var t = $("<div class='terminal input-terminal'></div>")[ 0 ]; this.enable_input_terminal( t, input.name, input.extensions, input.multiple ); return t; }, - enable_input_terminal : function( elements, name, types, multiple ) { + enable_input_terminal : function( element, name, types, multiple ) { var node = this; - $(elements).each( function() { - var terminal = this.terminal = new InputTerminal( this, types, multiple ); - terminal.node = node; - terminal.name = name; - $(this).bind( "dropinit", function( e, d ) { - // Accept a dragable if it is an output terminal and has a - // compatible type - return $(d.drag).hasClass( "output-terminal" ) && terminal.can_accept( d.drag.terminal ); - }).bind( "dropstart", function( e, d ) { - if (d.proxy.terminal) { - d.proxy.terminal.connectors[0].inner_color = "#BBFFBB"; - } - }).bind( "dropend", function ( e, d ) { - if (d.proxy.terminal) { - d.proxy.terminal.connectors[0].inner_color = "#FFFFFF"; - } - }).bind( "drop", function( e, d ) { - ( new Connector( d.drag.terminal, terminal ) ).redraw(); - }).bind( "hover", function() { - // If connected, create a popup to allow disconnection - if ( terminal.connectors.length > 0 ) { - // Create callout - var t = $("<div class='callout'></div>") - .css( { display: 'none' } ) - .appendTo( "body" ) - .append( - $("<div class='button'></div>").append( - $("<div/>").addClass("fa-icon-button fa fa-times").click( function() { - $.each( terminal.connectors, function( _, x ) { - if (x) { - x.destroy(); - } - }); - t.remove(); - }))) - .bind( "mouseleave", function() { - $(this).remove(); - }); - // Position it and show - t.css({ - top: $(this).offset().top - 2, - left: $(this).offset().left - t.width(), - 'padding-right': $(this).width() - }).show(); - } - }); - node.input_terminals[name] = terminal; + + var terminal = element.terminal = new InputTerminal( element, types, multiple ); + terminal.node = node; + terminal.name = name; + $(element).bind( "dropinit", function( e, d ) { + // Accept a dragable if it is an output terminal and has a + // compatible type + return $(d.drag).hasClass( "output-terminal" ) && terminal.can_accept( d.drag.terminal ); + }).bind( "dropstart", function( e, d ) { + if (d.proxy.terminal) { + d.proxy.terminal.connectors[0].inner_color = "#BBFFBB"; + } + }).bind( "dropend", function ( e, d ) { + if (d.proxy.terminal) { + d.proxy.terminal.connectors[0].inner_color = "#FFFFFF"; + } + }).bind( "drop", function( e, d ) { + ( new Connector( d.drag.terminal, terminal ) ).redraw(); + }).bind( "hover", function() { + // If connected, create a popup to allow disconnection + if ( terminal.connectors.length > 0 ) { + // Create callout + var t = $("<div class='callout'></div>") + .css( { display: 'none' } ) + .appendTo( "body" ) + .append( + $("<div class='button'></div>").append( + $("<div/>").addClass("fa-icon-button fa fa-times").click( function() { + $.each( terminal.connectors, function( _, x ) { + if (x) { + x.destroy(); + } + }); + t.remove(); + }))) + .bind( "mouseleave", function() { + $(this).remove(); + }); + // Position it and show + t.css({ + top: $(element).offset().top - 2, + left: $(element).offset().left - t.width(), + 'padding-right': $(element).width() + }).show(); + } }); + node.input_terminals[name] = terminal; }, - enable_output_terminal : function( elements, name, type ) { + enable_output_terminal : function( element, name, type ) { var node = this; - $(elements).each( function() { - var terminal_element = this; - var terminal = this.terminal = new OutputTerminal( this, type ); - terminal.node = node; - terminal.name = name; - $(this).bind( "dragstart", function( e, d ) { - $( d.available ).addClass( "input-terminal-active" ); - // Save PJAs in the case of change datatype actions. - workflow.check_changes_in_active_form(); - // Drag proxy div - var h = $( '<div class="drag-terminal" style="position: absolute;"></div>' ) - .appendTo( "#canvas-container" ).get(0); - // Terminal and connection to display noodle while dragging - h.terminal = new OutputTerminal( h ); - var c = new Connector(); - c.dragging = true; - c.connect( this.terminal, h.terminal ); - return h; - }).bind( "drag", function ( e, d ) { - var onmove = function() { - var po = $(d.proxy).offsetParent().offset(), - x = d.offsetX - po.left, - y = d.offsetY - po.top; - $(d.proxy).css( { left: x, top: y } ); - d.proxy.terminal.redraw(); - // FIXME: global - canvas_manager.update_viewport_overlay(); - }; - onmove(); - $("#canvas-container").get(0).scroll_panel.test( e, onmove ); - }).bind( "dragend", function ( e, d ) { - d.proxy.terminal.connectors[0].destroy(); - $(d.proxy).remove(); - $( d.available ).removeClass( "input-terminal-active" ); - $("#canvas-container").get(0).scroll_panel.stop(); - }); - node.output_terminals[name] = terminal; + var terminal_element = element; + var terminal = element.terminal = new OutputTerminal( element, type ); + terminal.node = node; + terminal.name = name; + $(element).bind( "dragstart", function( e, d ) { + $( d.available ).addClass( "input-terminal-active" ); + // Save PJAs in the case of change datatype actions. + workflow.check_changes_in_active_form(); + // Drag proxy div + var h = $( '<div class="drag-terminal" style="position: absolute;"></div>' ) + .appendTo( "#canvas-container" ).get(0); + // Terminal and connection to display noodle while dragging + h.terminal = new OutputTerminal( h ); + var c = new Connector(); + c.dragging = true; + c.connect( element.terminal, h.terminal ); + return h; + }).bind( "drag", function ( e, d ) { + var onmove = function() { + var po = $(d.proxy).offsetParent().offset(), + x = d.offsetX - po.left, + y = d.offsetY - po.top; + $(d.proxy).css( { left: x, top: y } ); + d.proxy.terminal.redraw(); + // FIXME: global + canvas_manager.update_viewport_overlay(); + }; + onmove(); + $("#canvas-container").get(0).scroll_panel.test( e, onmove ); + }).bind( "dragend", function ( e, d ) { + d.proxy.terminal.connectors[0].destroy(); + $(d.proxy).remove(); + $( d.available ).removeClass( "input-terminal-active" ); + $("#canvas-container").get(0).scroll_panel.stop(); }); + node.output_terminals[name] = terminal; }, redraw : function () { $.each( this.input_terminals, function( _, t ) { @@ -342,7 +339,7 @@ } $.each( data.data_outputs, function( i, output ) { var t = $( "<div class='terminal output-terminal'></div>" ); - node.enable_output_terminal( t, output.name, output.extensions ); + node.enable_output_terminal( t[ 0 ], output.name, output.extensions ); var label = output.name; if ( output.extensions.indexOf( 'input' ) < 0 ) { label = label + " (" + output.extensions.join(", ") + ")"; @@ -429,8 +426,8 @@ $(this).find( ".input-terminal" ).each( function() { var c = this.terminal.connectors[0]; if ( c ) { - t[0].terminal.connectors[0] = c; - c.handle2 = t[0].terminal; + t.terminal.connectors[0] = c; + c.handle2 = t.terminal; } }); $(this).remove(); Repository URL: https://bitbucket.org/galaxy/galaxy-central/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email.