diff --git a/static/scripts/galaxy.workflow_editor.canvas.js b/static/scripts/galaxy.workflow_editor.canvas.js index 83a1397d8e0..9414eb090af 100644 --- a/static/scripts/galaxy.workflow_editor.canvas.js +++ b/static/scripts/galaxy.workflow_editor.canvas.js @@ -302,19 +302,12 @@ var BaseInputTerminal = Terminal.extend( { inputFilled = false; } else { if( this.multiple ) { - if( ! this.connected() ) { - inputFilled = false; + if(this._collectionAttached()) { + // Can only attach one collection to multiple input + // data parameter. + inputsFilled = true; } else { - var firstOutput = this.connectors[ 0 ].handle1; - if( ! firstOutput ){ - inputFilled = false; - } else { - if( firstOutput.isDataCollectionInput || firstOutput.isMappedOver() || firstOutput.datatypes.indexOf( "input_collection" ) > 0 ) { - inputFilled = true; - } else { - inputFilled = false; - } - } + inputFilled = false; } } else { inputFilled = true; @@ -322,6 +315,22 @@ var BaseInputTerminal = Terminal.extend( { } return inputFilled; }, + _collectionAttached: function( ) { + if( ! this.connected() ) { + return false; + } else { + var firstOutput = this.connectors[ 0 ].handle1; + if( ! firstOutput ){ + return false; + } else { + if( firstOutput.isDataCollectionInput || firstOutput.isMappedOver() || firstOutput.datatypes.indexOf( "input_collection" ) > 0 ) { + return true; + } else { + return false; + } + } + } + }, _mappingConstraints: function( ) { // If this is a connected terminal, return list of collection types // other terminals connected to node are constraining mapping to. @@ -407,7 +416,9 @@ var InputTerminal = BaseInputTerminal.extend( { var thisMapOver = this.mapOver(); if( otherCollectionType.isCollection ) { if( this.multiple ) { - if( this.connected() ) { + if( this.connected() && ! this._collectionAttached() ) { + // if single inputs attached, cannot also attach a + // collection (yet...) return false; } if( otherCollectionType.rank == 1 ) { diff --git a/test/qunit/tests/workflow_editor_tests.js b/test/qunit/tests/workflow_editor_tests.js index 37f32e0bed2..f30b8f71829 100644 --- a/test/qunit/tests/workflow_editor_tests.js +++ b/test/qunit/tests/workflow_editor_tests.js @@ -91,7 +91,9 @@ define([ }, test_accept: function( other ) { other = other || { node: {}, datatypes: [ "txt" ] }; - other.mapOver = function() { return NULL_COLLECTION_TYPE_DESCRIPTION; }; + if( ! other.mapOver ) { + other.mapOver = function() { return NULL_COLLECTION_TYPE_DESCRIPTION; }; + } return this.input_terminal.canAccept( other ); }, pja_change_datatype_node: function( output_name, newtype ) { @@ -230,6 +232,22 @@ define([ ok( self.test_accept() ); } ); + test( "can accept list collection for empty multiple inputs", function() { + var other = { node: {}, datatypes: [ "tabular" ], mapOver: function() { return new CollectionTypeDescription( "list" ) } }; + var self = this; + this.multiple(); + ok( self.test_accept( other ) ); + } ); + + test( "cannot accept list collection for multiple input if collection already connected", function() { + var other = { node: {}, datatypes: [ "tabular" ], mapOver: function() { return new CollectionTypeDescription( "list" ) } }; + var self = this; + this.multiple(); + this.with_test_connector( function() { + ok( ! self.test_accept( other ) ); + } ); + } ); + module( "Connector test", { } ); @@ -483,6 +501,17 @@ define([ return c; }, + connectAttachedMultiInputTerminal: function( inputType, outputType ) { + this.view.addDataInput( { name: "TestName", extensions: [ inputType ], multiple: true } ); + var terminal = this.view.node.input_terminals[ "TestName" ]; + + var outputTerminal = new OutputTerminal( { name: "TestOuptut", datatypes: [ "txt" ] } ); + outputTerminal.node = { markChanged: function() {}, post_job_actions: [], hasMappedOverInputTerminals: function() { return false; }, hasConnectedOutputTerminals: function() { return true; } }; + outputTerminal.terminalMapping = { disableMapOver: function() {}, mapOver: new CollectionTypeDescription( "list" ) }; + var c = new Connector( outputTerminal, terminal ); + + return c; + }, connectAttachedMappedOutput: function( ) { this.view.addDataInput( { name: "TestName", extensions: [ "txt" ], input_type: "dataset_collection" } ); var terminal = this.view.node.input_terminals[ "TestName" ]; @@ -530,6 +559,14 @@ define([ ok( connector.handle2 === terminal ); } ); + test( "replacing terminal on data multiple input update preserves collection connections", function() { + var connector = this.connectAttachedMultiInputTerminal( "txt", "txt" ); + var connector_destroy_spy = sinon.spy( connector, "destroy" ); + var newElement = $("
"); + this.view.addDataInput( { name: "TestName", extensions: ["txt"], multiple: true }, newElement ); + ok( ! connector_destroy_spy.called ); + } ); + test( "replacing mapped terminal on data collection input update preserves connections", function() { var connector = this.connectAttachedMappedOutput(); var newElement = $(""); @@ -922,12 +959,11 @@ define([ this.verifyAttachable( inputTerminal1, "list" ); } ); - test( "connected multiple input cannot be connected to collections", function() { + test( "multiple input attachable by collections", function() { var inputTerminal1 = this.newInputTerminal( null, { multiple: true } ); var connectedInput1 = this.addConnectedInput( inputTerminal1 ); this.addConnectedOutput( connectedInput1 ); - // Normally could do this reduction, but cannot because input already connected. - this.verifyNotAttachable( connectedInput1, "list" ); + this.verifyAttachable( inputTerminal1, "list" ); } ); test( "unconnected multiple inputs cannot be connected to rank > 1 collections (yet...)", function() {