diff --git a/test/casperjs/anon-history-tests.js b/test/casperjs/anon-history-tests.js index 51c6b8364d4..7e8954a5a49 100644 --- a/test/casperjs/anon-history-tests.js +++ b/test/casperjs/anon-history-tests.js @@ -1,36 +1,14 @@ -// have to handle errors here - or phantom/casper won't bail but _HANG_ -try { - var utils = require( 'utils' ), - xpath = require( 'casper' ).selectXPath, - format = utils.format, - - //...if there's a better way - please let me know, universe - scriptDir = require( 'system' ).args[3] - // remove the script filename - .replace( /[\w|\.|\-|_]*$/, '' ) - // if given rel. path, prepend the curr dir - .replace( /^(?!\/)/, './' ), - spaceghost = require( scriptDir + 'spaceghost' ).create({ - // script options here (can be overridden by CLI) - //verbose: true, - //logLevel: debug, - scriptDir: scriptDir - }); +var require = patchRequire( require ), + spaceghost = require( 'spaceghost' ).fromCasper( casper ), + xpath = require( 'casper' ).selectXPath, + utils = require( 'utils' ), + format = utils.format; +spaceghost.test.begin( 'Testing histories for anonymous users', 0, function suite( test ){ spaceghost.start(); -} catch( error ){ - console.debug( error ); - phantom.exit( 1 ); -} +// =================================================================== - -// ------------------------------------------------------------------- -/* TODO: - run a tool - -*/ -// =================================================================== globals and helpers var email = spaceghost.user.getRandomEmail(), password = '123456'; if( spaceghost.fixtureData.testUser ){ @@ -141,6 +119,5 @@ spaceghost.user.logout().openHomePage( function(){ }); // =================================================================== -spaceghost.run( function(){ - this.test.done(); + spaceghost.run( function(){ test.done(); }); }); diff --git a/test/casperjs/api-anon-history-permission-tests.js b/test/casperjs/api-anon-history-permission-tests.js index 6e883e3305b..b9c7f2755a7 100644 --- a/test/casperjs/api-anon-history-permission-tests.js +++ b/test/casperjs/api-anon-history-permission-tests.js @@ -1,30 +1,14 @@ -/* Utility to load a specific page and output html, page text, or a screenshot - * Optionally wait for some time, text, or dom selector - */ -try { - //...if there's a better way - please let me know, universe - var scriptDir = require( 'system' ).args[3] - // remove the script filename - .replace( /[\w|\.|\-|_]*$/, '' ) - // if given rel. path, prepend the curr dir - .replace( /^(?!\/)/, './' ), - spaceghost = require( scriptDir + 'spaceghost' ).create({ - // script options here (can be overridden by CLI) - //verbose: true, - //logLevel: debug, - scriptDir: scriptDir - }); - -} catch( error ){ - console.debug( error ); - phantom.exit( 1 ); -} -spaceghost.start(); +var require = patchRequire( require ), + spaceghost = require( 'spaceghost' ).fromCasper( casper ), + xpath = require( 'casper' ).selectXPath, + utils = require( 'utils' ), + format = utils.format; +spaceghost.test.begin( 'Test permissions for accessible, published, and inaccessible histories ' + + 'with anonymous users over the API', 0, function suite( test ){ + spaceghost.start(); // =================================================================== SET UP -var utils = require( 'utils' ); - var email = spaceghost.user.getRandomEmail(), password = '123456'; if( spaceghost.fixtureData.testUser ){ @@ -63,9 +47,9 @@ spaceghost.thenOpen( spaceghost.baseUrl ).then( function(){ //// ------------------------------------------------------------------------------------------- upload some files spaceghost.then( function(){ - this.api.tools.thenUpload( inaccessibleHistory.id, { filepath: this.options.scriptDir + '/../../test-data/1.bed' }); - this.api.tools.thenUpload( accessibleHistory.id, { filepath: this.options.scriptDir + '/../../test-data/1.bed' }); - this.api.tools.thenUpload( publishedHistory.id, { filepath: this.options.scriptDir + '/../../test-data/1.bed' }); + this.api.tools.thenUpload( inaccessibleHistory.id, { filepath: '../../test-data/1.bed' }); + this.api.tools.thenUpload( accessibleHistory.id, { filepath: '../../test-data/1.bed' }); + this.api.tools.thenUpload( publishedHistory.id, { filepath: '../../test-data/1.bed' }); }); spaceghost.then( function(){ // check that they're there @@ -220,5 +204,5 @@ spaceghost.thenOpen( spaceghost.baseUrl ).then( function(){ // =================================================================== -spaceghost.run( function(){ + spaceghost.run( function(){ test.done(); }); }); diff --git a/test/casperjs/api-anon-history-tests.js b/test/casperjs/api-anon-history-tests.js index 63e7444cbe2..775c6a1c933 100644 --- a/test/casperjs/api-anon-history-tests.js +++ b/test/casperjs/api-anon-history-tests.js @@ -1,29 +1,11 @@ -/* Utility to load a specific page and output html, page text, or a screenshot - * Optionally wait for some time, text, or dom selector - */ -try { - //...if there's a better way - please let me know, universe - var scriptDir = require( 'system' ).args[3] - // remove the script filename - .replace( /[\w|\.|\-|_]*$/, '' ) - // if given rel. path, prepend the curr dir - .replace( /^(?!\/)/, './' ), - spaceghost = require( scriptDir + 'spaceghost' ).create({ - // script options here (can be overridden by CLI) - //verbose: true, - //logLevel: debug, - scriptDir: scriptDir - }); +var require = patchRequire( require ), + spaceghost = require( 'spaceghost' ).fromCasper( casper ), + xpath = require( 'casper' ).selectXPath, + utils = require( 'utils' ), + format = utils.format; -} catch( error ){ - console.debug( error ); - phantom.exit( 1 ); -} -spaceghost.start(); - - -// =================================================================== SET UP -var utils = require( 'utils' ); +spaceghost.test.begin( 'Test API functions for histories with an anonymous user', 0, function suite( test ){ + spaceghost.start(); // =================================================================== TESTS spaceghost.thenOpen( spaceghost.baseUrl ).waitForSelector( '.history-name' ); @@ -98,7 +80,7 @@ spaceghost.thenOpen( spaceghost.baseUrl ).waitForSelector( '.history-name' ); //TODO: can't use this - get a 400 when tools checks for history: 'logged in to manage' //spaceghost.then( function(){ // this.api.tools.thenUpload( spaceghost.api.histories.show( 'current' ).id, { -// filepath: this.options.scriptDir + '/../../test-data/1.sam' +// filepath: '../../test-data/1.sam' // }); //}); spaceghost.then( function(){ @@ -155,5 +137,6 @@ spaceghost.then( function(){ }); // =================================================================== -spaceghost.run( function(){ + spaceghost.run( function(){ test.done(); }); }); + diff --git a/test/casperjs/api-configuration-tests.js b/test/casperjs/api-configuration-tests.js index cf2677414eb..eb039382972 100644 --- a/test/casperjs/api-configuration-tests.js +++ b/test/casperjs/api-configuration-tests.js @@ -1,29 +1,13 @@ -/* Utility to load a specific page and output html, page text, or a screenshot - * Optionally wait for some time, text, or dom selector - */ -try { - //...if there's a better way - please let me know, universe - var scriptDir = require( 'system' ).args[3] - // remove the script filename - .replace( /[\w|\.|\-|_]*$/, '' ) - // if given rel. path, prepend the curr dir - .replace( /^(?!\/)/, './' ), - spaceghost = require( scriptDir + 'spaceghost' ).create({ - // script options here (can be overridden by CLI) - //verbose: true, - //logLevel: debug, - scriptDir: scriptDir - }); +var require = patchRequire( require ), + spaceghost = require( 'spaceghost' ).fromCasper( casper ), + xpath = require( 'casper' ).selectXPath, + utils = require( 'utils' ), + format = utils.format; -} catch( error ){ - console.debug( error ); - phantom.exit( 1 ); -} -spaceghost.start(); +spaceghost.test.begin( 'Test the Galaxy configuration API', 0, function suite( test ){ + spaceghost.start(); // =================================================================== SET UP -var utils = require( 'utils' ); - var email = spaceghost.user.getRandomEmail(), password = '123456'; if( spaceghost.fixtureData.testUser ){ @@ -32,35 +16,6 @@ if( spaceghost.fixtureData.testUser ){ } spaceghost.user.loginOrRegisterUser( email, password ); -function hasKeys( object, keysArray ){ - if( !utils.isObject( object ) ){ return false; } - for( var i=0; i= 1, 'Has at least one visualization' ); var firstVisualization = visualizationIndex[0]; - this.test.assert( hasKeys( firstVisualization, indexKeys ), 'Has the proper keys' ); + this.test.assert( this.hasKeys( firstVisualization, indexKeys ), 'Has the proper keys' ); this.test.assert( this.api.isEncodedId( firstVisualization.id ), 'Id appears well-formed' ); //TODO: index searching @@ -156,7 +69,7 @@ spaceghost.thenOpen( spaceghost.baseUrl ).then( function(){ this.test.comment( 'show should get a visualization details object' ); var visualizationShow = this.api.visualizations.show( firstVisualization.id ); this.debug( this.jsonStr( visualizationShow ) ); - this.test.assert( hasKeys( visualizationShow, showKeys ), 'Has the proper keys' ); + this.test.assert( this.hasKeys( visualizationShow, showKeys ), 'Has the proper keys' ); this.test.assert( visualizationShow.model_class === 'Visualization', 'Has the proper model_class: ' + visualizationShow.model_class ); @@ -173,7 +86,7 @@ spaceghost.thenOpen( spaceghost.baseUrl ).then( function(){ this.test.comment( 'a visualization details object should contain a subobject of the latest revision' ); var latestRevision = visualizationShow.latest_revision; this.test.assert( utils.isObject( latestRevision ), 'latestRevision is an object' ); - this.test.assert( hasKeys( latestRevision, revisionKeys ), 'latestRevision has the proper keys' ); + this.test.assert( this.hasKeys( latestRevision, revisionKeys ), 'latestRevision has the proper keys' ); this.test.assert( latestRevision.model_class === 'VisualizationRevision', 'Has the proper model_class: ' + latestRevision.model_class ); this.test.assert( latestRevision.visualization_id === visualizationShow.id, @@ -212,18 +125,18 @@ spaceghost.thenOpen( spaceghost.baseUrl ).then( function(){ visualizationShow = this.api.visualizations.show( created.id ); this.debug( 'visualizationShow:\n' + this.jsonStr( visualizationShow ) ); // config is re-located into a revision and won't be there - this.test.assert( compareObjs( visualizationShow, visualizationData, [ 'config' ] ), - "show results seem to match create data" ); + //this.test.assert( compareObjs( visualizationShow, visualizationData, [ 'config' ] ), + // "show results seem to match create data" ); // the following errors are produced within base.controller.UsesVisualizationsMixin._create_visualization this.test.comment( 'Calling create with a non-unique slug will cause an API error' ); - assertRaises( function(){ + this.api.assertRaises( function(){ created = this.api.visualizations.create( visualizationData ); }, 400, 'visualization identifier must be unique' ); this.test.comment( 'Calling create with no title will cause an API error' ); visualizationData.title = ''; - assertRaises( function(){ + this.api.assertRaises( function(){ created = this.api.visualizations.create( visualizationData ); }, 400, 'visualization name is required' ); visualizationData.title = 'Created Visualization'; @@ -231,33 +144,33 @@ spaceghost.thenOpen( spaceghost.baseUrl ).then( function(){ this.test.comment( 'Calling create with improper characters in the slug will cause an API error' ); var oldSlug = visualizationData.slug; visualizationData.slug = '123_()'; - assertRaises( function(){ + this.api.assertRaises( function(){ created = this.api.visualizations.create( visualizationData ); }, 400, "visualization identifier must consist of only lowercase letters, numbers, and the '-' character" ); visualizationData.slug = oldSlug; this.test.comment( 'Calling create with an unrecognized key will cause an API error' ); visualizationData.bler = 'blah'; - assertRaises( function(){ + this.api.assertRaises( function(){ created = this.api.visualizations.create( visualizationData ); }, 400, 'unknown key: bler' ); delete visualizationData.bler; this.test.comment( 'Calling create with an unparsable JSON config will cause an API error' ); visualizationData.config = '3 = nime'; - assertRaises( function(){ + this.api.assertRaises( function(){ created = this.api.visualizations.create( visualizationData ); }, 400, 'config must be a dictionary (JSON)' ); // ------------------------------------------------------------------------------------------ UPDATE // ........................................................................................... idiot proofing this.test.comment( 'updating using a nonsense key should fail with an error' ); - assertRaises( function(){ + this.api.assertRaises( function(){ returned = this.api.visualizations.update( created.id, { bler : 'blah' }); }, 400, 'unknown key: bler' ); this.test.comment( 'updating by attempting to change type should cause an error' ); - assertRaises( function(){ + this.api.assertRaises( function(){ returned = this.api.visualizations.update( created.id, { title : 30 }); }, 400, 'title must be a string or unicode' ); //TODO: the other types... @@ -278,5 +191,5 @@ spaceghost.thenOpen( spaceghost.baseUrl ).then( function(){ }); // =================================================================== -spaceghost.run( function(){ + spaceghost.run( function(){ test.done(); }); }); diff --git a/test/casperjs/api-workflow-tests.js b/test/casperjs/api-workflow-tests.js index f0225835782..b3e5187ee54 100644 --- a/test/casperjs/api-workflow-tests.js +++ b/test/casperjs/api-workflow-tests.js @@ -1,29 +1,13 @@ -/* Utility to load a specific page and output html, page text, or a screenshot - * Optionally wait for some time, text, or dom selector - */ -try { - //...if there's a better way - please let me know, universe - var scriptDir = require( 'system' ).args[3] - // remove the script filename - .replace( /[\w|\.|\-|_]*$/, '' ) - // if given rel. path, prepend the curr dir - .replace( /^(?!\/)/, './' ), - spaceghost = require( scriptDir + 'spaceghost' ).create({ - // script options here (can be overridden by CLI) - //verbose: true, - //logLevel: debug, - scriptDir: scriptDir - }); +var require = patchRequire( require ), + spaceghost = require( 'spaceghost' ).fromCasper( casper ), + xpath = require( 'casper' ).selectXPath, + utils = require( 'utils' ), + format = utils.format; -} catch( error ){ - console.debug( error ); - phantom.exit( 1 ); -} -spaceghost.start(); +spaceghost.test.begin( 'Test the workflows API', 0, function suite( test ){ + spaceghost.start(); // =================================================================== SET UP -var utils = require( 'utils' ); - var email = spaceghost.user.getRandomEmail(), password = '123456'; if( spaceghost.fixtureData.testUser ){ @@ -32,26 +16,6 @@ if( spaceghost.fixtureData.testUser ){ } spaceghost.user.loginOrRegisterUser( email, password ); -function hasKeys( object, keysArray ){ - if( !utils.isObject( object ) ){ return false; } - for( var i=0; i just call the function). -// * NOTE: is more than one Casper step. -// * @param {Function} then function called when in the history frame -// */ -//HistoryPanel.prototype.then = function then( thenFn ){ -// if( this.inFrame() ){ -// thenFn.call( this.spaceghost ); -// } else { -// this.spaceghost.withHistoryPanel( thenFn ); -// } -//}; -// /** Moves into history iframe and waits until hdas are visible or empty message is. * NOTE: is more than one Casper step. * @see Casper@waitFor @@ -127,51 +113,44 @@ HistoryPanel.prototype.waitForHdas = function waitForHdas( then, timeout, maxWai return spaceghost; }; -/** Expands or collapses an HDA by clicking the title (does nothing if already in desired state). +/** Moves into history iframe and waits until hdas are visible or empty message is. * NOTE: is more than one Casper step. - * @param {String} hdaSelector a css or xpath selector for an historyItemWrapper - * @param {Function} then function called when the change is made - * @param {Boolean} desiredClosed true if you want to collapse, false if you want open - * @private + * @see Casper@waitFor */ -HistoryPanel.prototype._thenExpandOrCollapseHda = function _thenExpandOrCollapseHda( hdaSelector, then, desiredClosed ){ - // using a step here (instead of a jump) bc we need the wait for function - this.spaceghost.then( function checkingHda(){ - this.info( (( desiredClosed )?( 'collapsing' ):( 'expanding' )) + ' hda: ' + hdaSelector ); +HistoryPanel.prototype.waitForHda = function waitForHda( hdaName, then, timeout, maxWait ){ + //TODO:?? should this wait until the seletors are in AND they are opaque? + var spaceghost = this.spaceghost, + hdaElement = null, + previousState = null; + spaceghost.then( function waitingForHda(){ + this.waitFor( + function checkHpanel(){ + var wrapperXpath = xpath( '//span[contains(text(),"' + hdaName + '")]/../../..' ); + hdaElement = this.elementInfoOrNull( wrapperXpath ); + //NOTE: this will probably fail if the name was used on a previous HDA + if( !hdaElement ){ return false; } + + var state = hdaElement.attributes[ "class" ].match( /state\-([\w\-_]*)/ )[1]; + if( state !== previousState ){ + spaceghost.info( 'state: ' + state ); + previousState = state; + } + if( state !== 'ok' ){ return false; } - // click to open if the body isn't visible and call wait to account for opening animation - if( this.visible( hdaSelector + ' ' + this.historypanel.data.selectors.hda.body ) === desiredClosed ){ - this.click( hdaSelector + ' ' + this.historypanel.data.selectors.hda.title ); - //NOTE: then is executed in the top frame - //TODO: magic number - this.wait( 500, then ); - - // otherwise, just call then - } else if( then ){ - then.call( this ); - } + var hdaOpacity = this.evaluate( function( name ){ + return $( '.dataset-name:contains("' + name + '")' ).parents( '.hda' ).css( 'opacity' ); + }, hdaName ); + this.debug( 'fading in: ' + hdaOpacity ); + return hdaOpacity >= 1; + }, + function _then(){ then.call( this, hdaElement ); }, + function _timeout(){ timeout.call( this, hdaElement ); }, + maxWait ); }); - return this.spaceghost; -}; - -/** Collapses an HDA by clicking the title (does nothing if already collapsed). - * NOTE: is more than one Casper step. - * @param {String} hdaSelector a css or xpath selector for an historyItemWrapper - * @param {Function} then function called when the change is made - */ -HistoryPanel.prototype.thenCollapseHda = function thenCollapseHda( hdaSelector, then ){ - return this._thenExpandOrCollapseHda( hdaSelector, then, true ); -}; - -/** Expands an HDA by clicking the title (does nothing if already expanded). - * NOTE: is more than one Casper step. - * @param {String} hdaSelector a css or xpath selector for an historyItemWrapper - * @param {Function} then function called when the change is made - */ -HistoryPanel.prototype.thenExpandHda = function thenExpandHda( hdaSelector, then ){ - return this._thenExpandOrCollapseHda( hdaSelector, then, false ); + return spaceghost; }; +//TODO: combine with above /** Wait for the hda with given id to move into the given state. * whenInStateFn and timeoutFn will be passed the hda element info (see Casper#getElementInfo) * NOTE: is more than one Casper step. @@ -233,6 +212,51 @@ HistoryPanel.prototype.waitForHdaState = function waitForHdaState( hdaSelector, return spaceghost; }; +/** Expands or collapses an HDA by clicking the title (does nothing if already in desired state). + * NOTE: is more than one Casper step. + * @param {String} hdaSelector a css or xpath selector for an historyItemWrapper + * @param {Function} then function called when the change is made + * @param {Boolean} desiredClosed true if you want to collapse, false if you want open + * @private + */ +HistoryPanel.prototype._thenExpandOrCollapseHda = function _thenExpandOrCollapseHda( hdaSelector, then, desiredClosed ){ + // using a step here (instead of a jump) bc we need the wait for function + this.spaceghost.then( function checkingHda(){ + this.info( (( desiredClosed )?( 'collapsing' ):( 'expanding' )) + ' hda: ' + hdaSelector ); + + // click to open if the body isn't visible and call wait to account for opening animation + if( this.visible( hdaSelector + ' ' + this.historypanel.data.selectors.hda.body ) === desiredClosed ){ + this.click( hdaSelector + ' ' + this.historypanel.data.selectors.hda.title ); + //NOTE: then is executed in the top frame + //TODO: magic number + this.wait( 500, then ); + + // otherwise, just call then + } else if( then ){ + then.call( this ); + } + }); + return this.spaceghost; +}; + +/** Collapses an HDA by clicking the title (does nothing if already collapsed). + * NOTE: is more than one Casper step. + * @param {String} hdaSelector a css or xpath selector for an historyItemWrapper + * @param {Function} then function called when the change is made + */ +HistoryPanel.prototype.thenCollapseHda = function thenCollapseHda( hdaSelector, then ){ + return this._thenExpandOrCollapseHda( hdaSelector, then, true ); +}; + +/** Expands an HDA by clicking the title (does nothing if already expanded). + * NOTE: is more than one Casper step. + * @param {String} hdaSelector a css or xpath selector for an historyItemWrapper + * @param {Function} then function called when the change is made + */ +HistoryPanel.prototype.thenExpandHda = function thenExpandHda( hdaSelector, then ){ + return this._thenExpandOrCollapseHda( hdaSelector, then, false ); +}; + /** Deletes an hda by finding an hda with the given title and clicking on the delete icon. * NOTE: if more than one is found, the first found will be deleted. * NOTE: is more than one Casper step. diff --git a/test/casperjs/modules/tools.js b/test/casperjs/modules/tools.js index 85d2f84e848..de75997753a 100644 --- a/test/casperjs/modules/tools.js +++ b/test/casperjs/modules/tools.js @@ -29,7 +29,8 @@ Tools.prototype.toString = function toString(){ */ // =================================================================== INTERNAL -var xpath = require( 'casper' ).selectXPath; +var require = patchRequire( require ), + xpath = require( 'casper' ).selectXPath; // ------------------------------------------------------------------- get avail. tools // list available tools @@ -158,9 +159,6 @@ Tools.prototype.uploadFile = function uploadFile( filepath, callback, timeoutAft filename = this.filenameFromFilepath( filepath ), uploadInfo = {}; - // precondition: filepath is relative to scriptDir - filepath = spaceghost.options.scriptDir + filepath; - spaceghost.info( 'uploading file: ' + filepath + ' (timeout after ' + timeoutAfterMs + ')' ); this._uploadFile( filepath ); @@ -183,31 +181,22 @@ Tools.prototype.uploadFile = function uploadFile( filepath, callback, timeoutAft // the hpanel should refresh and display the uploading file, wait for that to go into the ok state // throw if uploaded HDA doesn't appear, or it doesn't move to 'ok' after allotted time - spaceghost.historypanel.waitForHdas( function afterHpanelRefresh(){ + //spaceghost.historypanel.waitForHdas() + spaceghost.historypanel.waitForHda( filename, + // success: update the upload info and run callback + function whenInStateFn( newHdaInfo ){ + this.info( 'Upload complete: ' + newHdaInfo.text ); + uploadInfo.hdaElement = newHdaInfo; + callback.call( spaceghost, uploadInfo ); + }, + function timeoutFn( newHdaInfo ){ + this.warning( 'timeout waiting for upload: ' + filename + ', ' + this.jsonStr( newHdaInfo ) ); + throw new spaceghost.GalaxyError( 'Upload Error: timeout waiting for ok state: ' + + '"' + uploadInfo.filepath + '" (waited ' + timeoutAfterMs + ' ms)' ); + }, + timeoutAfterMs + ); - var hdaElement = this.historypanel.hdaElementInfoByTitle( uploadInfo.filename ); - if( hdaElement === null ){ - var hdaContainer = this.historypanel.data.selectors.hdaContainer; - this.warning( 'Upload Error: ' + hdaContainer + ':\n' + this.getHTML( hdaContainer ) ); - throw new this.GalaxyError( 'Upload Error: uploaded file HDA not found: ' + uploadInfo.filename ); - } - this.debug( 'uploaded HDA element: ' + this.jsonStr( this.quickInfo( hdaElement ) ) ); - uploadInfo.hdaElement = hdaElement; - - this.historypanel.waitForHdaState( '#' + uploadInfo.hdaElement.attributes.id, 'ok', - // success: update the upload info and run callback - function whenInStateFn( newHdaInfo ){ - this.info( 'Upload complete: ' + newHdaInfo.text ); - uploadInfo.hdaElement = newHdaInfo; - callback.call( spaceghost, uploadInfo ); - - }, function timeoutFn( newHdaInfo ){ - this.warning( 'timeout waiting for upload:\n' + this.jsonStr( this.quickInfo( newHdaInfo ) ) ); - throw new spaceghost.GalaxyError( 'Upload Error: timeout waiting for ok state: ' - + '"' + uploadInfo.filepath + '" (waited ' + timeoutAfterMs + ' ms)' ); - - }, timeoutAfterMs ); - }); return spaceghost; }; //TODO: upload via url diff --git a/test/casperjs/modules/user.js b/test/casperjs/modules/user.js index ef4f181875f..f183cc8c5c2 100644 --- a/test/casperjs/modules/user.js +++ b/test/casperjs/modules/user.js @@ -21,7 +21,8 @@ User.prototype.toString = function toString(){ // =================================================================== INTERNAL -var xpath = require( 'casper' ).selectXPath; +var require = patchRequire( require ), + xpath = require( 'casper' ).selectXPath; /** Tests registering a new user on the Galaxy instance by submitting the registration form. * NOTE: this version does NOT throw an error on a bad registration. diff --git a/test/casperjs/registration-tests.js b/test/casperjs/registration-tests.js index 457f9f5f423..45c3e3bffee 100644 --- a/test/casperjs/registration-tests.js +++ b/test/casperjs/registration-tests.js @@ -1,151 +1,137 @@ -try { - var utils = require( 'utils' ), - xpath = require( 'casper' ).selectXPath, - format = utils.format, - - //...if there's a better way - please let me know, universe - scriptDir = require( 'system' ).args[3] - // remove the script filename - .replace( /[\w|\.|\-|_]*$/, '' ) - // if given rel. path, prepend the curr dir - .replace( /^(?!\/)/, './' ), - spaceghost = require( scriptDir + 'spaceghost' ).create({ - // script options here (can be overridden by CLI) - //verbose: true, - //logLevel: debug, - scriptDir: scriptDir - }); +var require = patchRequire( require ), + spaceghost = require( 'spaceghost' ).fromCasper( casper ), + xpath = require( 'casper' ).selectXPath, + utils = require( 'utils' ), + format = utils.format; +spaceghost.test.begin( 'Testing registration of new users', 0, function suite( test ){ spaceghost.start(); -} catch( error ){ - console.debug( error ); - phantom.exit( 1 ); -} + // =================================================================== globals and helpers + var email = spaceghost.user.getRandomEmail(), + password = '123456', + confirm = password, + username = 'test' + Date.now(); -// =================================================================== globals and helpers -var email = spaceghost.user.getRandomEmail(), - password = '123456', - confirm = password, - username = 'test' + Date.now(); - -// =================================================================== TESTS -spaceghost.openHomePage( function(){ - this.test.comment( 'loading galaxy homepage' ); - this.test.assertTitle( 'Galaxy' ); - this.test.assertExists( xpath( "//div[@id='masthead']" ), 'found masthead' ); -}); - -// ------------------------------------------------------------------- register a new user -spaceghost.then( function(){ - this.test.comment( 'registering user: ' + email ); - this.user._submitRegistration( email, password, username, confirm ); -}); -spaceghost.openHomePage( function(){ - this.clickLabel( 'User' ); - var loggedInAs = this.fetchText( xpath( spaceghost.data.selectors.masthead.userMenu.userEmail_xpath ) ); - this.test.assert( loggedInAs.indexOf( email ) !== -1, 'found proper email in user menu: ' + loggedInAs ); -}); - -// ------------------------------------------------------------------- log out that user -spaceghost.user.logout().openHomePage( function(){ - var emailSelector = xpath( this.data.selectors.masthead.userMenu.userEmail_xpath ); - this.test.assert( !this.elementInfoOrNull( emailSelector ), 'user email not found' ); -}); - -// ------------------------------------------------------------------- bad user registrations -spaceghost.then( function(){ - this.test.comment( 'attempting to re-register user: ' + email ); - this.user._submitRegistration( email, password, username, confirm ); -}); -spaceghost.then(function(){ - this.assertErrorMessage( 'User with that email already exists' ); -}); - -// emails must be in the form -@-.- (which is an email on main, btw) -var badEmails = [ 'bob', 'bob@', 'bob@idontwanttocleanup', 'bob.cantmakeme' ]; -spaceghost.each( badEmails, function( self, badEmail ){ - self.then( function(){ - this.test.comment( 'attempting bad email: ' + badEmail ); - this.user._submitRegistration( badEmail, password, username, confirm ); + // =================================================================== TESTS + spaceghost.openHomePage( function(){ + this.test.comment( 'loading galaxy homepage' ); + this.test.assertTitle( 'Galaxy' ); + this.test.assertExists( xpath( "//div[@id='masthead']" ), 'found masthead' ); }); - self.then(function(){ - this.assertErrorMessage( 'Please enter your valid email address' ); - }); -}); -// passwords must be at least 6 chars long -var badPasswords = [ '1234' ]; -spaceghost.each( badPasswords, function( self, badPassword ){ - self.then( function(){ - this.test.comment( 'attempting bad password: ' + badPassword ); - this.user._submitRegistration( spaceghost.user.getRandomEmail(), badPassword, username, badPassword ); + // ------------------------------------------------------------------- register a new user + spaceghost.then( function(){ + this.test.comment( 'registering user: ' + email ); + this.user._submitRegistration( email, password, username, confirm ); }); - self.then(function(){ - this.assertErrorMessage( 'Please use a password of at least 6 characters' ); + spaceghost.openHomePage( function(){ + this.clickLabel( 'User' ); + var loggedInAs = this.fetchText( xpath( spaceghost.data.selectors.masthead.userMenu.userEmail_xpath ) ); + this.test.assert( loggedInAs.indexOf( email ) !== -1, 'found proper email in user menu: ' + loggedInAs ); }); -}); -// and confirm must match -var badConfirms = [ '1234', '12345678', '123456 7', '' ]; -spaceghost.each( badConfirms, function( self, badConfirm ){ - self.then( function(){ - this.test.comment( 'attempting bad password confirmation: ' + badConfirm ); - this.user._submitRegistration( spaceghost.user.getRandomEmail(), password, username, badConfirm ); + // ------------------------------------------------------------------- log out that user + spaceghost.user.logout().openHomePage( function(){ + var emailSelector = xpath( this.data.selectors.masthead.userMenu.userEmail_xpath ); + this.test.assert( !this.elementInfoOrNull( emailSelector ), 'user email not found' ); }); - self.then(function(){ - this.assertErrorMessage( 'Passwords don\'t match' ); + + // ------------------------------------------------------------------- bad user registrations + spaceghost.then( function(){ + this.test.comment( 'attempting to re-register user: ' + email ); + this.user._submitRegistration( email, password, username, confirm ); }); -}); - -// usernames must be >=4 chars... -//NOTE: that short username errors only show AFTER checking for existing/valid emails -// so: we need to generate new emails for each one -spaceghost.then( function(){ - var newEmail = spaceghost.user.getRandomEmail(), - badUsername = 'bob'; - this.test.comment( 'attempting short username: ' + badUsername ); - this.user._submitRegistration( newEmail, password, badUsername, confirm ); -}); -spaceghost.then(function(){ - this.assertErrorMessage( 'Public name must be at least 4 characters in length' ); -}); - -// ...and be lower-case letters, numbers and '-'... -var badUsernames = [ 'BOBERT', 'Robert Paulson', 'bobert!', 'bob_dobbs' ]; -spaceghost.each( badUsernames, function( self, badUsername ){ - self.then( function(){ - var newEmail = spaceghost.user.getRandomEmail(); - this.test.comment( 'attempting bad username: ' + badUsername ); - this.user._submitRegistration( newEmail, password, badUsername, confirm ); + spaceghost.then(function(){ + this.assertErrorMessage( 'User with that email already exists' ); }); - self.then(function(){ - this.assertErrorMessage( 'Public name must contain only lowercase letters, numbers and "-"' ); - }); -}); -// ...and the name can't be used already -spaceghost.then( function(){ - var newEmail = spaceghost.user.getRandomEmail(); - this.test.comment( 'attempting previously used username with new user: ' + newEmail ); - this.user._submitRegistration( newEmail, password, username, confirm ); -}); -spaceghost.then(function(){ - this.assertErrorMessage( 'Public name is taken; please choose another' ); -}); - -// ------------------------------------------------------------------- test the convenience fns -// these versions are for conv. use in other tests, they should throw errors if used improperly -spaceghost.then( function(){ - this.assertStepsRaise( 'GalaxyError: RegistrationError', function(){ - this.then( function(){ - this.test.comment( 'testing (js) error thrown on bad email' ); - this.user.registerUser( '@internet', '123456', 'ignobel' ); + // emails must be in the form -@-.- (which is an email on main, btw) + var badEmails = [ 'bob', 'bob@', 'bob@idontwanttocleanup', 'bob.cantmakeme' ]; + spaceghost.each( badEmails, function( self, badEmail ){ + self.then( function(){ + this.test.comment( 'attempting bad email: ' + badEmail ); + this.user._submitRegistration( badEmail, password, username, confirm ); + }); + self.then(function(){ + this.assertErrorMessage( 'Please enter your valid email address' ); }); }); + + // passwords must be at least 6 chars long + var badPasswords = [ '1234' ]; + spaceghost.each( badPasswords, function( self, badPassword ){ + self.then( function(){ + this.test.comment( 'attempting bad password: ' + badPassword ); + this.user._submitRegistration( spaceghost.user.getRandomEmail(), badPassword, username, badPassword ); + }); + self.then(function(){ + this.assertErrorMessage( 'Please use a password of at least 6 characters' ); + }); + }); + + // and confirm must match + var badConfirms = [ '1234', '12345678', '123456 7', '' ]; + spaceghost.each( badConfirms, function( self, badConfirm ){ + self.then( function(){ + this.test.comment( 'attempting bad password confirmation: ' + badConfirm ); + this.user._submitRegistration( spaceghost.user.getRandomEmail(), password, username, badConfirm ); + }); + self.then(function(){ + this.assertErrorMessage( 'Passwords don\'t match' ); + }); + }); + + // usernames must be >=4 chars... + //NOTE: that short username errors only show AFTER checking for existing/valid emails + // so: we need to generate new emails for each one + spaceghost.then( function(){ + var newEmail = spaceghost.user.getRandomEmail(), + badUsername = 'bob'; + this.test.comment( 'attempting short username: ' + badUsername ); + this.user._submitRegistration( newEmail, password, badUsername, confirm ); + }); + spaceghost.then(function(){ + this.assertErrorMessage( 'Public name must be at least 4 characters in length' ); + }); + + // ...and be lower-case letters, numbers and '-'... + var badUsernames = [ 'BOBERT', 'Robert Paulson', 'bobert!', 'bob_dobbs' ]; + spaceghost.each( badUsernames, function( self, badUsername ){ + self.then( function(){ + var newEmail = spaceghost.user.getRandomEmail(); + this.test.comment( 'attempting bad username: ' + badUsername ); + this.user._submitRegistration( newEmail, password, badUsername, confirm ); + }); + self.then(function(){ + this.assertErrorMessage( 'Public name must contain only lowercase letters, numbers and "-"' ); + }); + }); + + // ...and the name can't be used already + spaceghost.then( function(){ + var newEmail = spaceghost.user.getRandomEmail(); + this.test.comment( 'attempting previously used username with new user: ' + newEmail ); + this.user._submitRegistration( newEmail, password, username, confirm ); + }); + spaceghost.then(function(){ + this.assertErrorMessage( 'Public name is taken; please choose another' ); + }); + + // ------------------------------------------------------------------- test the convenience fns + // these versions are for conv. use in other tests, they should throw errors if used improperly + spaceghost.then( function(){ + this.assertStepsRaise( 'RegistrationError', function(){ + this.then( function(){ + this.test.comment( 'testing (js) error thrown on bad email' ); + this.user.registerUser( '@internet', '123456', 'ignobel' ); + }); + }); + }); + + // =================================================================== + spaceghost.run( function(){ + test.done(); + }); }); -// =================================================================== -spaceghost.run( function(){ - this.test.done(); -}); diff --git a/test/casperjs/spaceghost.js b/test/casperjs/spaceghost.js index 683a2c39325..e8a59db891a 100644 --- a/test/casperjs/spaceghost.js +++ b/test/casperjs/spaceghost.js @@ -32,24 +32,34 @@ */ // ------------------------------------------------------------------- modules -var Casper = require( 'casper' ).Casper, +var require = patchRequire( require ), + Casper = require( 'casper' ).Casper, fs = require( 'fs' ), utils = require( 'utils' ); // ------------------------------------------------------------------- inheritance /** @class An extension of the Casper object with methods and overrides specifically * for interacting with a Galaxy web page. - * @augments Casper */ function SpaceGhost(){ - SpaceGhost.super_.apply( this, arguments ); - this._init.apply( this, arguments ); + // an empty object just to store functions in a prototype for patching onto a casper isntance } -utils.inherits( SpaceGhost, Casper ); -/** String representation - * @returns {String} - */ +exports.fromCasper = function fromCasper( casper, options ){ + "use strict"; + // patch the sg prototype over the casper instance proto + for( var k in SpaceGhost.prototype ){ + if( SpaceGhost.prototype.hasOwnProperty( k ) ){ + // monkey patch directly onto the casper instance - we need the prototype + casper[ k ] = SpaceGhost.prototype[ k ]; + } + } + casper._init( options ); + return casper; +}; + +// =================================================================== METHODS / OVERRIDES +/** String representation */ SpaceGhost.prototype.toString = function(){ var currentUrl = ''; try { @@ -58,61 +68,27 @@ SpaceGhost.prototype.toString = function(){ return 'SpaceGhost(' + currentUrl + ')'; }; - -// ------------------------------------------------------------------- included libs -//??: can we require underscore, etc. from the ../../static/scripts/lib? -// yep! -//var _ = require( '../../static/scripts/libs/underscore' ); -//var stooges = [{name : 'moe', age : 40}, {name : 'larry', age : 50}, {name : 'curly', age : 60}]; -//console.debug( JSON.stringify( _.pluck(stooges, 'name') ) ); -//exports._ = _; - -// ------------------------------------------------------------------- error types -//TODO: change to inheriting from Error -PageError.prototype = new CasperError(); -PageError.prototype.constructor = CasperError; -/** @class Represents a javascript error on the page casper is browsing - * (as opposed to an error in the test script). - */ -function PageError(){ - CasperError.apply( this, arguments ); - this.name = "PageError"; -} -SpaceGhost.prototype.PageError = PageError; - -GalaxyError.prototype = new CasperError(); -GalaxyError.prototype.constructor = CasperError; -/** @class Thrown when Galaxy has (gracefully?) indicated pilot error. */ -function GalaxyError(){ - CasperError.apply( this, arguments ); - this.name = "GalaxyError"; -} -SpaceGhost.prototype.GalaxyError = GalaxyError; - -AlertError.prototype = new CasperError(); -AlertError.prototype.constructor = CasperError; -/** @class Thrown when Galaxy has displayed a javascript alert. */ -function AlertError(){ - CasperError.apply( this, arguments ); - this.name = "AlertError"; -} -SpaceGhost.prototype.AlertError = AlertError; - -// =================================================================== METHODS / OVERRIDES // ------------------------------------------------------------------- set up /** More initialization: cli, event handlers, etc. * @param {Object} options option hash * @private */ SpaceGhost.prototype._init = function _init( options ){ - //console.debug( 'init, options:', JSON.stringify( options, null, 2 ) ); - + ////console.debug( 'init, options:', JSON.stringify( options, null, 2 ) ); + // //NOTE: cli will override in-script options this._setOptionsFromCli(); - // save errors for later output (needs to go before process CLI) - /** cache of errors that have occurred - * @memberOf SpaceGhost */ + this.on( 'step.error', function stepErrorHandler( error ){ + //console.debug( 'step.error: ' + error.name + ', ' + error.message ); + this.errors.push({ msg: error.message, backtrace: error.stackArray }); + //if( error.name !== 'AssertionError' ){ + // throw error; + //} + }); + // save errors for later output + // set this now so ALL errors are processed well (including errors during set up) + /** cache of errors that have occurred */ this.errors = []; this.on( 'error', function pushErrorToStack( msg, backtrace ){ //this.debug( 'adding error to stack: ' + msg + ', trace:' + this.jsonStr( backtrace ) ); @@ -121,14 +97,19 @@ SpaceGhost.prototype._init = function _init( options ){ this._processCLIArguments(); this._setUpEventHandlers(); + /** cache of test failures */ + this.failures = []; + /** cache of test passes */ + this.passes = []; + // inject these scripts by default - this.debug( 'this.options.scriptDir:' + this.options.scriptDir ); this.options.clientScripts = [ - //this.options.scriptDir + '../../static/scripts/libs/jquery/jquery.js' + //'../../static/scripts/libs/jquery/jquery.js' //... ].concat( this.options.clientScripts ); this.debug( 'clientScripts: ' + this.jsonStr( this.options.clientScripts ) ); + this.changeToScriptDir(); this._loadModules(); }; @@ -141,13 +122,29 @@ SpaceGhost.prototype._setOptionsFromCli = function setOptionsFromCli(){ // get and remove any casper options passed on the command line for( var optionName in this.options ){ if( this.cli.has( optionName ) ){ - //console.debug( optionName + ':' + this.options[ optionName ] + ',' + this.cli.get( optionName ) ); + //console.debug( optionName + ': ' + // + '(was) ' + this.options[ optionName ] + // + ', (now) ' + this.cli.get( optionName ) ); this.options[ optionName ] = this.cli.get( optionName ); this.cli.drop( optionName ); } } }; +/** Change the working directory to that of the current script */ +SpaceGhost.prototype.changeToScriptDir = function changeToScriptDir(){ + var fs = require( 'fs' ), + args = require( 'system' ).args, + scriptPathArray = args[4].split( '/' ); + //console.debug( this.jsonStr( scriptPathArray ) ); + + if( scriptPathArray.length > 1 ){ + scriptPathArray.pop(); + //console.debug( this.jsonStr( scriptPathArray.join( '/' ) ) ); + fs.changeWorkingDirectory( scriptPathArray.join( '/' ) ); + } +}; + // ------------------------------------------------------------------- cli args and options /** Set up any SG specific options passed in on the cli. * @private @@ -190,9 +187,10 @@ SpaceGhost.prototype._processCLIArguments = function _processCLIArguments(){ this.options.returnJsonOnly = true; this._redirectOutputToStderr(); + this.test.removeAllListeners( 'tests.complete' ); + // output json on fail-first error this.on( 'error', function outputJSONOnError( msg, backtrace ){ - //console.debug( 'return-json caught error' ); if( spaceghost.options.exitOnError ){ this._sendStopSignal(); this.outputStateAsJson(); @@ -240,9 +238,8 @@ SpaceGhost.prototype._processCLIArguments = function _processCLIArguments(){ // --fail-screen: print the casper browser screen output on an test failure */ - // get any fixture data passed in as JSON in args - // (NOTE: currently the 2nd arg (with the url being 1st?) - this.fixtureData = ( this.cli.has( 0 ) )?( JSON.parse( this.cli.get( 0 ) ) ):( {} ); + // get any fixture data passed in as JSON (e.g. --data='{ "one": 1 }') + this.fixtureData = ( this.cli.has( 'data' ) )?( JSON.parse( this.cli.get( 'data' ) ) ):( {} ); this.debug( 'fixtureData:' + this.jsonStr( this.fixtureData ) ); /** only output log messages with the given namespace */ @@ -284,18 +281,19 @@ SpaceGhost.prototype._redirectOutputToStderr = function _redirectOutputToStderr( spaceghost.stderr( msg ); }; - //this.removeListener( 'error', this.listeners( 'error' )[0] ); // clear the casper listener that outputs formatted error messages - this.removeListener( 'error', this.listeners( 'error' )[1] ); + this.removeListener( 'error', this.listeners( 'error' )[0] ); + //this.removeListener( 'error', this.listeners( 'error' )[1] ); }; /** Outputs logs, test results and errors in a single JSON formatted object to the console. */ SpaceGhost.prototype.outputStateAsJson = function outputStateAsJson(){ var returnedJSON = { - logs: this.result, - testResults: this.test.testResults, - errors: this.errors + logs : this.result, + passes : this.passes, + failures : this.failures, + errors : this.errors }; // use phantomjs console since echo can't be used (suppressed - see init) console.debug( JSON.stringify( returnedJSON, null, 2 ) ); @@ -303,6 +301,34 @@ SpaceGhost.prototype.outputStateAsJson = function outputStateAsJson(){ // ------------------------------------------------------------------- event handling +/** Sets up event handlers. + * @private + */ +SpaceGhost.prototype._setUpEventHandlers = function _setUpEventHandlers(){ + //console.debug( '_setUpEventHandlers' ); + var spaceghost = this; + + // ........................ page errors + this.on( 'page.error', this._pageErrorHandler ); + //this.on( 'load.failed', this._loadFailedHandler ); + + // ........................ page info/debugging + this.on( 'remote.alert', this._alertHandler ); + //this.on( 'remote.message', function( msg ){ this.debug( 'remote: ' + msg ); }); + //this.on( 'navigation.requested', function( url ){ this.debug( 'navigation: ' + url ); }); + + // ........................ timeouts + this._setUpTimeoutHandlers(); + + // ........................ test results + this.test.on( "fail", function( failure ){ + spaceghost.failures.push( failure ); + }); + this.test.on( "success", function( pass ){ + spaceghost.passes.push( pass ); + }); +}; + //note: using non-anon fns to allow removal if needed /** 'load failed' Event handler for failed page loads that only records to the log @@ -415,25 +441,6 @@ SpaceGhost.prototype._setUpTimeoutHandlers = function _setUpTimeoutHandlers(){ }; }; -/** Sets up event handlers. - * @private - */ -SpaceGhost.prototype._setUpEventHandlers = function _setUpEventHandlers(){ - //console.debug( '_setUpEventHandlers' ); - - // ........................ page errors - this.on( 'page.error', this._pageErrorHandler ); - //this.on( 'load.failed', this._loadFailedHandler ); - - // ........................ page info/debugging - this.on( 'remote.alert', this._alertHandler ); - //this.on( 'remote.message', function( msg ){ this.debug( 'remote: ' + msg ); }); - //this.on( 'navigation.requested', function( url ){ this.debug( 'navigation: ' + url ); }); - - // ........................ timeouts - this._setUpTimeoutHandlers(); -}; - // ------------------------------------------------------------------- sub modules /** Load sub modules (similar to casperjs.test) * @requires User modules/user.js @@ -443,11 +450,11 @@ SpaceGhost.prototype._setUpEventHandlers = function _setUpEventHandlers(){ * @private */ SpaceGhost.prototype._loadModules = function _loadModules(){ - this.user = require( this.options.scriptDir + 'modules/user' ).create( this ); - this.tools = require( this.options.scriptDir + 'modules/tools' ).create( this ); - this.historypanel = require( this.options.scriptDir + 'modules/historypanel' ).create( this ); - this.historyoptions = require( this.options.scriptDir + 'modules/historyoptions' ).create( this ); - this.api = require( this.options.scriptDir + 'modules/api' ).create( this ); + this.user = require( './modules/user' ).create( this ); + this.tools = require( './modules/tools' ).create( this ); + this.historypanel = require( './modules/historypanel' ).create( this ); + this.historyoptions = require( './modules/historyoptions' ).create( this ); + this.api = require( './modules/api' ).create( this ); }; // =================================================================== PAGE CONTROL @@ -484,27 +491,32 @@ SpaceGhost.prototype._sendStopSignal = function _sendStopSignal(){ * Exits with 2 if a test has failed. * Exits with 1 if some error has occurred. * Exits with 0 if all tests passed. - * @see Casper#run run, boy, run (doesn't he fly?) */ SpaceGhost.prototype.run = function run( onComplete, time ){ + var oldFn = spaceghost.test.done; + spaceghost.test.done = function(){ + oldFn.call( spaceghost.test ); + } var new_onComplete = function(){ - onComplete.call( this ); - var returnCode = ( this.test.testResults.failed )?( 2 ):( 0 ); + onComplete.call( this ); + //var returnCode = ( this.test.getFailures() )?( 2 ):( 0 ); + var returnCode = ( this.failures.length )?( 2 ):( 0 ); - // if --return-json is used: output json and exit - //NOTE: used by the test runner to gather JSON test info from stdout - if( this.options.returnJsonOnly ){ - // echo a string to indicate that tests are complete (used in casperjs_runner.py to stop process) - this._sendStopSignal(); - this.outputStateAsJson(); - this.exit( returnCode ); + // if --return-json is used: output json and exit + //NOTE: used by the test runner to gather JSON test info from stdout + if( this.options.returnJsonOnly ){ + // echo a string to indicate that tests are complete (used in casperjs_runner.py to stop process) + this._sendStopSignal(); + this.outputStateAsJson(); + this.exit( returnCode ); - // otherwise, render the nice casper output and exit - } else { - this.test.renderResults( true, returnCode ); - } - }; + // otherwise, render the nice casper output and exit + //} else { + // this.test.renderResults( true, returnCode ); + } + }; Casper.prototype.run.call( this, new_onComplete, time ); + //Casper.prototype.run.call( this, onComplete, time ); }; // ------------------------------------------------------------------- home page @@ -550,18 +562,18 @@ SpaceGhost.prototype.tryStepsCatch = function tryStepsCatch( stepsFn, catchFn ){ // create three steps: 1) set up new error handler, 2) try the fn, 3) check for errors and rem. handler var originalExitOnError, originalErrorHandlers = [], - errorMsg = '', errorTrace = [], - recordError = function( msg, trace ){ - errorMsg = msg; errorTrace = trace; + errorCaught, + recordError = function( error ){ + errorCaught = error; }; // dont bail on the error (but preserve option), uninstall other handlers, // and install hndlr to simply record msg, trace - this.then( function(){ + this.then( function replaceHandlers(){ originalExitOnError = this.options.exitOnError; this.options.exitOnError = false; - originalErrorHandlers = this.popAllListeners( 'error' ); - this.on( 'error', recordError ); + originalErrorHandlers = this.popAllListeners( 'step.error' ); + this.on( 'step.error', recordError ); }); // try the step... @@ -569,15 +581,15 @@ SpaceGhost.prototype.tryStepsCatch = function tryStepsCatch( stepsFn, catchFn ){ //TODO: this doesn't work well with wait for (see upload-tests.js) // possibly combine above and below? - this.then( function(){ - // ...and if an error was recorded call the catch with the info - if( errorMsg ){ - catchFn.call( this, errorMsg, errorTrace ); - } + this.then( function catchWrapper(){ // remove that listener either way, restore original handlers, and restore the bail option - this.removeListener( 'error', recordError ); - this.addListeners( 'error', originalErrorHandlers ); + this.removeListener( 'step.error', recordError ); + this.addListeners( 'step.error', originalErrorHandlers ); this.options.exitOnError = originalExitOnError; + // ...and if an error was recorded call the catch with the info + if( errorCaught ){ + catchFn.call( this, errorCaught ); + } }); }; @@ -823,8 +835,12 @@ SpaceGhost.prototype.assertStepsRaise = function assertStepsRaise( msgContains, // casper provides an assertRaises but this doesn't work well with steps //TODO: * @param {Boolean} removeOtherListeners option to remove other listeners while this fires var spaceghost = this; - function testTheError( msg, backtrace ){ - spaceghost.test.assert( msg.indexOf( msgContains ) !== -1, 'Raised correct error: ' + msg ); + function testTheError( errorCaught ){ + if( errorCaught.message.indexOf( msgContains ) !== -1 ){ + spaceghost.test.pass( 'Raised correct error: ' + errorCaught.message ); + } else { + throw errorCaught; + } } this.tryStepsCatch( stepsFn, testTheError ); }; @@ -1085,29 +1101,27 @@ SpaceGhost.prototype.lastError = function(){ // ------------------------------------------------------------------- file system /** Load and parse a JSON file into an object. - * @param {String} filepath filepath relative to the current scriptDir + * @param {String} filepath filepath relative to the current script * @returns {Object} the object parsed */ SpaceGhost.prototype.loadJSONFile = function loadJSONFile( filepath ){ //precondition: filepath is relative to script dir - filepath = this.options.scriptDir + filepath; return JSON.parse( fs.read( filepath ) ); }; /** Write an object to a JSON file. - * @param {String} filepath filepath relative to the current scriptDir + * @param {String} filepath filepath relative to the current script * @param {Object} object the object to write * @param {String} mode 'w' for a new file, 'a' for append */ SpaceGhost.prototype.writeJSONFile = function writeJSONFile( filepath, object, mode ){ mode = mode || 'w'; //precondition: filepath is relative to script dir - filepath = this.options.scriptDir + filepath; return fs.write( filepath, this.jsonStr( object ), mode ); }; /** Save the HTML from the current page to file. - * @param {String} filepath filepath relative to the current scriptDir + * @param {String} filepath filepath relative to the current script * @param {String} selector A DOM CSS3/XPath selector (optional) * @param {Boolean} outer Whether to fetch outer HTML contents (default: false) */ @@ -1116,13 +1130,12 @@ SpaceGhost.prototype.writeHTMLFile = function writeHTMLFile( filepath, selector, }; /** Read and search a file for the given regex. - * @param {String} filepath filepath relative to the current scriptDir + * @param {String} filepath filepath relative to the current script * @param {Regex} searchFor regex to search for * @returns {Object} search results */ SpaceGhost.prototype.searchFile = function searchFile( filepath, regex ){ //precondition: filepath is relative to script dir - filepath = this.options.scriptDir + filepath; var read = fs.read( filepath ); return read.match( regex ); }; @@ -1235,14 +1248,37 @@ SpaceGhost.prototype.data = { }; -// =================================================================== EXPORTS -exports.SpaceGhost = SpaceGhost; +// =================================================================== error types +/** @class Represents a javascript error on the page casper is browsing + * (as opposed to an error in the test script). + */ +function PageError(){ + CasperError.apply( this, arguments ); + this.name = "PageError"; +} +//TODO: change to inheriting from Error +PageError.prototype = new CasperError(); +PageError.prototype.constructor = CasperError; +SpaceGhost.prototype.PageError = PageError; + +/** @class Thrown when Galaxy has (gracefully?) indicated pilot error. */ +function GalaxyError(){ + CasperError.apply( this, arguments ); + this.name = "GalaxyError"; +} +GalaxyError.prototype = new CasperError(); +GalaxyError.prototype.constructor = CasperError; +SpaceGhost.prototype.GalaxyError = GalaxyError; + +/** @class Thrown when Galaxy has displayed a javascript alert. */ +function AlertError(){ + CasperError.apply( this, arguments ); + this.name = "AlertError"; +} +AlertError.prototype = new CasperError(); +AlertError.prototype.constructor = CasperError; +SpaceGhost.prototype.AlertError = AlertError; + exports.PageError = PageError; exports.GalaxyError = GalaxyError; exports.AlertError = AlertError; -/** creation function - */ -exports.create = function create(options) { - "use strict"; - return new SpaceGhost(options); -}; diff --git a/test/casperjs/upload-tests.js b/test/casperjs/upload-tests.js index 12b2ace50a4..8617d524474 100644 --- a/test/casperjs/upload-tests.js +++ b/test/casperjs/upload-tests.js @@ -1,30 +1,12 @@ -// have to handle errors here - or phantom/casper won't bail but _HANG_ -try { - var utils = require( 'utils' ), - xpath = require( 'casper' ).selectXPath, - format = utils.format, - - //...if there's a better way - please let me know, universe - scriptDir = require( 'system' ).args[3] - // remove the script filename - .replace( /[\w|\.|\-|_]*$/, '' ) - // if given rel. path, prepend the curr dir - .replace( /^(?!\/)/, './' ), - spaceghost = require( scriptDir + 'spaceghost' ).create({ - // script options here (can be overridden by CLI) - //verbose: true, - //logLevel: debug, - scriptDir: scriptDir - }); +var require = patchRequire( require ), + spaceghost = require( 'spaceghost' ).fromCasper( casper ), + xpath = require( 'casper' ).selectXPath, + utils = require( 'utils' ), + format = utils.format; +spaceghost.test.begin( 'Test uploading data to a history', 0, function suite( test ){ spaceghost.start(); -} catch( error ){ - console.debug( error ); - phantom.exit( 1 ); -} - - // =================================================================== /* TODO: @@ -54,7 +36,7 @@ spaceghost.then( function(){ this.test.comment( 'Test uploading a file' ); var filename = '1.txt', - filepath = this.options.scriptDir + '/../../test-data/' + filename; + filepath = '../../test-data/' + filename; this.tools._uploadFile( filepath ); // when an upload begins successfully main should reload with a infomessagelarge @@ -70,28 +52,16 @@ spaceghost.then( function(){ }); // ... and move to the history panel and wait for upload to finish -spaceghost.historypanel.waitForHdas( function(){ - this.test.comment( 'Waiting for upload to move to ok state in history' ); - - var hdaInfo = this.historypanel.hdaElementInfoByTitle( testUploadInfo.name ); - if( !hdaInfo ){ - this.test.fail( 'Could not locate new hda: ' + testUploadInfo.name ); - - } else { - spaceghost.debugElement( spaceghost.jsonStr( hdaInfo ) ); - this.historypanel.waitForHdaState( '#' + hdaInfo.attributes.id, 'ok', - function whenInStateFn( newHdaInfo ){ - //this.debug( 'newHdaInfo:\n' + this.jsonStr( newHdaInfo ) ); - this.test.pass( 'Upload completed successfully for: ' + testUploadInfo.name ); - }, - function timeoutFn( newHdaInfo ){ - this.debug( 'newHdaInfo:\n' + this.jsonStr( newHdaInfo ) ); - this.test.fail( 'Test timed out for upload: ' + testUploadInfo.name ); - }, - // wait a maximum of 30 secs - 30 * 1000 ); - } -}); +spaceghost.historypanel.waitForHda( '1.txt', + function uploadComplete( hdaElement ){ + this.test.pass( 'Upload completed successfully for: ' + this.jsonStr( hdaElement.attributes.id ) ); + }, + function timeout( hdaElement ){ + this.debug( 'hdaElement:\n' + this.jsonStr( hdaElement ) ); + this.test.fail( 'Test timed out for upload: ' + testUploadInfo.name ); + }, + 30 * 1000 +); // ------------------------------------------------------------------- short form spaceghost.then( function(){ @@ -118,6 +88,5 @@ spaceghost.then( function(){ */ // =================================================================== -spaceghost.run( function(){ - this.test.done(); + spaceghost.run( function(){ test.done(); }); });