commit/galaxy-central: carlfeberhard: Browser tests: first draft at running a workflow from the API, remove screen-caps from tools, fix POST method in API interface
1 new commit in galaxy-central: https://bitbucket.org/galaxy/galaxy-central/commits/6b996d66ce37/ Changeset: 6b996d66ce37 User: carlfeberhard Date: 2013-04-30 17:05:18 Summary: Browser tests: first draft at running a workflow from the API, remove screen-caps from tools, fix POST method in API interface Affected #: 3 files diff -r a27856f42a616378bf5b152a06707d1d8c24e66d -r 6b996d66ce37e20de68d6f02f32355c026a5324b test/casperjs/api-workflow-tests.js --- a/test/casperjs/api-workflow-tests.js +++ b/test/casperjs/api-workflow-tests.js @@ -43,57 +43,245 @@ return true; } -function compareObjs( obj1, where ){ - for( var key in where ){ - if( where.hasOwnProperty( key ) ){ - if( !obj1.hasOwnProperty( key ) ){ return false; } - if( obj1[ key ] !== where[ key ] ){ return false; } - } +function countKeys( object ){ + if( !utils.isObject( object ) ){ return 0; } + var count = 0; + for( var key in object ){ + if( object.hasOwnProperty( key ) ){ count += 1; } } - return true; -} - -function findObject( objectArray, where, start ){ - start = start || 0; - for( var i=start; i<objectArray.length; i += 1 ){ - if( compareObjs( objectArray[i], where ) ){ return objectArray[i]; } - } - return null; + return count; } // =================================================================== TESTS -var workflowSummaryKeys = [ +var workflowJSONFilepath = 'test-data/Bed_interval_lengths.ga', + workflowModelClass = 'StoredWorkflow', + workflowSummaryKeys = [ 'id', 'model_class', 'name', 'published', 'tags', 'url' ], workflowDetailKeys = workflowSummaryKeys.concat([ 'inputs', 'steps' - ]); + ]), + stepKeys = [ + 'id', 'input_steps', 'tool_id', 'type' + ], + simpleBedFilepath = 'test-data/simple.bed', + uploadedFile = null, + workflowCreateKeys = [ 'history', 'outputs' ]; spaceghost.thenOpen( spaceghost.baseUrl ).then( function(){ + // ------------------------------------------------------------------------------------------- UPLOAD + // upload first or we have no data to test + this.test.comment( 'upload should allow importing a new workflow given in JSON form' ); + var workflowToUpload = this.loadJSONFile( workflowJSONFilepath ); + //this.debug( this.jsonStr( workflowToUpload ) ); + var returned = this.api.workflows.upload( workflowToUpload ); + + this.test.comment( 'upload should return a summary object of what we uploaded' ); + //this.debug( this.jsonStr( returned ) ); + this.test.assert( utils.isObject( returned ), "upload returned an object" ); + this.test.assert( hasKeys( returned, workflowSummaryKeys ), "upload's return has the proper keys" ); + this.test.assert( this.api.isEncodedId( returned.id ), + "id is of the proper form: " + returned.id ); + this.test.assert( returned.model_class === workflowModelClass, + "has the proper model_class: " + returned.model_class ); + this.test.assert( returned.name === workflowToUpload.name + ' ' + '(imported from API)', + "has the proper, modified name: " + returned.name ); + this.test.assert( !returned.published, + "uploaded workflow defaults to un-published: " + returned.published ); + this.test.assert( utils.isArray( returned.tags ) && returned.tags.length === 0, + "upload returned an empty tag array: " + this.jsonStr( returned.tags ) ); + this.test.assert( returned.url === '/' + utils.format( this.api.workflows.urlTpls.show, returned.id ), + "url matches the show url: " + returned.url ); + + // ------------------------------------------------------------------------------------------- INDEX this.test.comment( 'index should get a list of workflows' ); var workflowIndex = this.api.workflows.index(); this.debug( this.jsonStr( workflowIndex ) ); this.test.assert( utils.isArray( workflowIndex ), "index returned an array: length " + workflowIndex.length ); + this.test.assert( workflowIndex.length >= 1, "index returned at least one job" ); - // need a way to import/create a workflow here for testing - if( workflowIndex.length <= 0 ){ - log.warn( 'No workflows available' ); - return; - } - this.test.assert( workflowIndex.length >= 1, 'Has at least one workflow' ); + this.test.comment( 'index should have returned an object matching the workflow uploaded' ); + var firstWorkflow = workflowIndex[0]; + this.test.assert( hasKeys( firstWorkflow, workflowSummaryKeys ), "index has the proper keys" ); + this.test.assert( this.api.isEncodedId( firstWorkflow.id ), + "id is of the proper form: " + firstWorkflow.id ); + this.test.assert( firstWorkflow.model_class === workflowModelClass, + "has the proper model_class: " + firstWorkflow.model_class ); + this.test.assert( firstWorkflow.name === workflowToUpload.name + ' ' + '(imported from API)', + "has the proper, modified name: " + firstWorkflow.name ); + this.test.assert( !firstWorkflow.published, + "workflow is un-published: " + firstWorkflow.published ); + this.test.assert( utils.isArray( firstWorkflow.tags ) && firstWorkflow.tags.length === 0, + "tag array is empty: " + this.jsonStr( firstWorkflow.tags ) ); + this.test.assert( firstWorkflow.url === '/' + utils.format( this.api.workflows.urlTpls.show, firstWorkflow.id ), + "url matches the show url: " + firstWorkflow.url ); + // ------------------------------------------------------------------------------------------- SHOW this.test.comment( 'show should get detailed data about the workflow with the given id' ); - var workflowShow = this.api.workflows.show( workflowIndex[0].id ); + var workflowShow = this.api.workflows.show( firstWorkflow.id ); this.debug( this.jsonStr( workflowShow ) ); + this.test.assert( utils.isObject( workflowShow ), "show returned an object" ); + this.test.assert( hasKeys( workflowShow, workflowDetailKeys ), "show has the proper keys" ); + this.test.assert( this.api.isEncodedId( workflowShow.id ), + "id is of the proper form: " + workflowShow.id ); + this.test.assert( workflowShow.model_class === workflowModelClass, + "has the proper model_class: " + workflowShow.model_class ); + this.test.assert( workflowShow.name === workflowToUpload.name + ' ' + '(imported from API)', + "has the proper, modified name: " + workflowShow.name ); + this.test.assert( !workflowShow.published, + "workflow is un-published: " + workflowShow.published ); + this.test.assert( utils.isArray( workflowShow.tags ) && workflowShow.tags.length === 0, + "tag array is empty: " + this.jsonStr( workflowShow.tags ) ); + this.test.assert( workflowShow.url === '/' + utils.format( this.api.workflows.urlTpls.show, workflowShow.id ), + "url matches the show url: " + workflowShow.url ); + this.test.comment( 'inputs from show should be an object (and, in this case, empty)' ); + var inputs = workflowShow.inputs; + //this.debug( 'inputs:\n' + this.jsonStr( inputs ) ); + this.test.assert( utils.isObject( workflowShow.inputs ), "inputs is an object" ); + //for( var inputKey in inputs ){ + // if( inputs.hasOwnProperty( inputKey ) ){ + // } + //} + this.test.assert( countKeys( workflowShow.inputs ) === 0, "inputs is empty" ); + + this.test.comment( 'steps from show should be an object containing each tool defined as a step' ); + var steps = workflowShow.steps; + //this.debug( 'steps:\n' + this.jsonStr( steps ) ); + this.test.assert( utils.isObject( workflowShow.steps ), "steps is an object" ); + //! ids for steps (and the keys used) are un-encoded (and in strings) + for( var stepKey in steps ){ + if( steps.hasOwnProperty( stepKey ) ){ + // any way to match this up with the workflowToUpload? + + this.test.assert( utils.isString( stepKey ), "step key is a string: " + stepKey ); + var step = steps[ stepKey ]; + this.debug( 'step:\n' + this.jsonStr( step ) ); + this.test.assert( hasKeys( step, stepKeys ), "step has the proper keys" ); + + this.test.assert( utils.isNumber( step.id ), + "step id is a number: " + step.id ); + try { + this.test.assert( parseInt( stepKey, 10 ) === step.id, + "step id matches step key: " + step.id ); + } catch( err ){ + this.test.fail( 'couldnt parse stepKey: ' + stepKey + ',' + err ); + } + + this.test.assert( utils.isObject( step.input_steps ), "input_steps is an object" ); + if( countKeys( step.input_steps ) !== 0 ){ + this.test.assert( hasKeys( step.input_steps, [ 'input' ] ), "input_steps has the proper keys" ); + } + + this.test.assert( step.type === 'tool', + "step type is a tool: " + step.type ); + + // check for tools in this wf with the api + this.test.assert( utils.isString( step.tool_id ), + "step tool_id is a string: " + step.tool_id ); + var tool_used = this.api.tools.show( step.tool_id ); + //this.debug( this.jsonStr( tool_used ) ) + this.test.assert( countKeys( step.input_steps ) !== 0, "found tool in api.tools for: " + step.tool_id ); + + // trace the path through input_steps, source_steps + } + } + + + + // ------------------------------------------------------------------------------------------- MISC +}); + +// now run the uploaded workflow +spaceghost.tools.uploadFile( simpleBedFilepath, function( uploadInfo ){ + uploadedFile = uploadInfo; +}); +spaceghost.then( function(){ + var currentHistory = this.api.histories.index()[0], + firstWorkflow = this.api.workflows.show( this.api.workflows.index()[0].id ); + + //this.debug( this.jsonStr( uploadedFile ) ); + var uploadedFileId = uploadedFile.hdaElement.attributes.id.split( '-' )[1]; + this.debug( this.jsonStr( uploadedFileId ) ); + this.debug( this.jsonStr( this.api.hdas.show( currentHistory.id, uploadedFileId ) ) ); + + //this.debug( this.jsonStr( firstWorkflow ) ); + // find the input step by looking for a step where input_steps == {} + var input_step = null; + for( var stepKey in firstWorkflow.steps ){ + if( firstWorkflow.steps.hasOwnProperty( stepKey ) ){ + var step = firstWorkflow.steps[ stepKey ]; + if( countKeys( step.input_steps ) === 0 ){ + input_step = stepKey; + this.debug( 'input step: ' + this.jsonStr( step ) ) + break; + } + } + } // ------------------------------------------------------------------------------------------- CREATE + this.test.comment( 'create should allow running an existing workflow' ); + // needs workflow_id, history, ds_map + var executionData = { + workflow_id : firstWorkflow.id, + history : 'hist_id=' + currentHistory.id, + ds_map : {} + }; + executionData.ds_map[ input_step ] = { + src: 'hda', + id: uploadedFileId + }; + var returned = this.api.workflows.create( executionData ); + this.debug( this.jsonStr( returned ) ); + this.test.assert( utils.isObject( returned ), "create returned an object" ); + this.test.assert( hasKeys( returned, workflowCreateKeys ), "create returned the proper keys" ); + this.test.assert( this.api.isEncodedId( returned.history ), + "history id is proper: " + returned.history ); + this.test.assert( utils.isArray( returned.outputs ), + "create.outputs is an array: length " + returned.outputs.length ); + this.test.assert( returned.outputs.length >= 1, "there is at least one output" ); + for( var i=0; i<returned.outputs.length; i+=1 ){ + this.test.assert( this.api.isEncodedId( returned.outputs[i] ), + "output id is proper: " + returned.outputs[i] ); + } - // ------------------------------------------------------------------------------------------- MISC + var counter = 0; + this.waitFor( + function checkHdas(){ + if( counter % 4 !== 0 ){ + counter += 1; + return false; + } + counter += 1; + + var outputs = this.api.hdas.index( currentHistory.id, returned.outputs ); + //this.debug( 'outputs:\n' + this.jsonStr( outputs ) ); + for( var i=0; i<outputs.length; i+=1 ){ + var output = outputs[i]; + this.debug( utils.format( 'name: %s, state: %s', output.name, output.state ) ); + if( output.state === 'queued' || output.state === 'running' ){ + return false; + } + } + return true; + }, + function allDone(){ + this.debug( 'DONE' ); + var outputs = this.api.hdas.index( currentHistory.id, returned.outputs ); + this.debug( 'outputs:\n' + this.jsonStr( outputs ) ); + }, + function timeout(){ + this.debug( 'timeout' ); + + }, + 45 * 1000 + ); +/* +*/ }); // =================================================================== diff -r a27856f42a616378bf5b152a06707d1d8c24e66d -r 6b996d66ce37e20de68d6f02f32355c026a5324b test/casperjs/modules/api.js --- a/test/casperjs/modules/api.js +++ b/test/casperjs/modules/api.js @@ -58,7 +58,8 @@ // PUT data needs to be stringified in jq.ajax and the content changed //TODO: server side handling could change this? - if( ( options.type && options.type === 'PUT' ) && ( options.data ) ){ + if( ( options.type && [ 'PUT', 'POST' ].indexOf( options.type ) !== -1 ) + && ( options.data ) ){ options.contentType = 'application/json'; options.data = JSON.stringify( options.data ); } @@ -384,39 +385,23 @@ }); }; -//WorkflowsAPI.prototype.create = function create( payload ){ -// this.api.spaceghost.info( 'workflows.create: ' + [ this.api.spaceghost.jsonStr( payload ) ] ); -// -// // py.payload <-> ajax.data -// payload = this.api.ensureObject( payload ); -// return this.api._ajax( utils.format( this.urlTpls.create ), { -// type : 'POST', -// data : payload -// }); -//}; -// -//WorkflowsAPI.prototype.update = function create( id, payload ){ -// this.api.spaceghost.info( 'workflows.update: ' + [ id, this.api.spaceghost.jsonStr( payload ) ] ); -// -// // py.payload <-> ajax.data -// historyId = this.api.ensureId( historyId ); -// id = this.api.ensureId( id ); -// payload = this.api.ensureObject( payload ); -// url = utils.format( this.urlTpls.update, id ); -// -// return this.api._ajax( url, { -// type : 'PUT', -// data : payload -// }); -//}; +WorkflowsAPI.prototype.create = function create( payload ){ + this.api.spaceghost.info( 'workflows.create: ' + [ this.api.spaceghost.jsonStr( payload ) ] ); -WorkflowsAPI.prototype.upload = function upload( filepath ){ - this.api.spaceghost.info( 'workflows.show: ' + [ id ] ); - var data = {}; + // py.payload <-> ajax.data + payload = this.api.ensureObject( payload ); + return this.api._ajax( utils.format( this.urlTpls.create ), { + type : 'POST', + data : payload + }); +}; - id = ( id === 'most_recently_used' )?( id ):( this.api.ensureId( id ) ); - return this.api._ajax( utils.format( this.urlTpls.show, this.api.ensureId( id ) ), { - data : data +WorkflowsAPI.prototype.upload = function create( workflowJSON ){ + this.api.spaceghost.info( 'workflows.upload: ' + [ this.api.spaceghost.jsonStr( workflowJSON ) ] ); + + return this.api._ajax( utils.format( this.urlTpls.upload ), { + type : 'POST', + data : { 'workflow': this.api.ensureObject( workflowJSON ) } }); }; diff -r a27856f42a616378bf5b152a06707d1d8c24e66d -r 6b996d66ce37e20de68d6f02f32355c026a5324b test/casperjs/modules/tools.js --- a/test/casperjs/modules/tools.js +++ b/test/casperjs/modules/tools.js @@ -118,7 +118,6 @@ }); }, function timeoutWaitingForUploadRefreshes( urlsStillWaitingOn ){ - this.capture( 'upload-error.png' ) throw new this.GalaxyError( 'Upload Error: ' + 'timeout waiting for upload "' + filepath + '" refreshes: ' + urlsStillWaitingOn ); }, @@ -178,7 +177,6 @@ // capture any other messages on the page var otherInfo = spaceghost.elementInfoOrNull( this.data.selectors.messages.all ), message = ( otherInfo && otherInfo.text )?( otherInfo.text ):( '' ); - this.capture( 'upload-error.png' ) throw new this.GalaxyError( 'Upload Error: no success message uploading "' + filepath + '": ' + message ); } }); @@ -191,7 +189,6 @@ if( hdaElement === null ){ var hdaContainer = this.historypanel.data.selectors.hdaContainer; this.warning( 'Upload Error: ' + hdaContainer + ':\n' + this.getHTML( hdaContainer ) ); - this.capture( 'upload-error.png' ) throw new this.GalaxyError( 'Upload Error: uploaded file HDA not found: ' + uploadInfo.filename ); } this.debug( 'uploaded HDA element: ' + this.jsonStr( this.quickInfo( hdaElement ) ) ); @@ -206,7 +203,6 @@ }, function timeoutFn( newHdaInfo ){ this.warning( 'timeout waiting for upload:\n' + this.jsonStr( this.quickInfo( newHdaInfo ) ) ); - this.capture( 'upload-error.png' ) throw new spaceghost.GalaxyError( 'Upload Error: timeout waiting for ok state: ' + '"' + uploadInfo.filepath + '" (waited ' + timeoutAfterMs + ' ms)' ); 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.
participants (1)
-
commits-noreply@bitbucket.org